Mercurial > hg > dmlib
view jssmix.c @ 2:9a8355b47e1c
Various improvements in the build system to add granularity
for building different parts of the engine and tools.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 28 Sep 2012 02:20:31 +0300 |
parents | 32250b436bca |
children | 7908061da010 |
line wrap: on
line source
/* * miniJSS - Mixing device and channel handling * Programmed and designed by Matti 'ccr' Hamalainen * (C) Copyright 2006-2012 Tecnic Software productions (TNSP) */ #include "jssmix.h" #include <string.h> #ifdef DM_USE_C #define JMIXER_HEADER #include "jmix_c.c" #undef JMIXER_HEADER #endif #undef DM_USE_SIMD #ifdef DM_USE_SIMD #define JMIXER_HEADER #include "jmix_mmx.h" #undef JMIXER_HEADER #endif typedef struct { int mixerID; int outFormat; int outChannels; int (*jvmMixChannel_FW)(JSSMixer *, JSSChannel *, Sint32 *, const int, const Uint32); int (*jvmMixChannel_BW)(JSSMixer *, JSSChannel *, Sint32 *, const int, const Uint32); void (*jvmPostProcess)(Sint32 *, void *, const int); } JSSMixingRoutine; /* This table should be sorted from fastest to slowest, e.g. MMX/x86 * optimized routines first, pure C versions last. */ static JSSMixingRoutine jvmMixRoutines[] = { #ifdef DM_USE_SIMD { JMIX_MMX, JSS_AUDIO_U8, JSS_AUDIO_MONO, jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_U8_MMX }, { JMIX_MMX, JSS_AUDIO_S8, JSS_AUDIO_MONO, jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_S8_MMX }, { JMIX_MMX, JSS_AUDIO_U8, JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_U8_MMX }, { JMIX_MMX, JSS_AUDIO_S8, JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_S8_MMX }, { JMIX_MMX, JSS_AUDIO_U16, JSS_AUDIO_MONO, jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_U16_MMX }, { JMIX_MMX, JSS_AUDIO_S16, JSS_AUDIO_MONO, jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_S16_MMX }, { JMIX_MMX, JSS_AUDIO_U16, JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_U16_MMX }, { JMIX_MMX, JSS_AUDIO_S16, JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_S16_MMX }, #endif #ifdef DM_USE_C { JMIX_C, JSS_AUDIO_U8, JSS_AUDIO_MONO, jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_U8_C }, { JMIX_C, JSS_AUDIO_S8, JSS_AUDIO_MONO, jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_S8_C }, { JMIX_C, JSS_AUDIO_U8, JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_U8_C }, { JMIX_C, JSS_AUDIO_S8, JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_S8_C }, { JMIX_C, JSS_AUDIO_U16, JSS_AUDIO_MONO, jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_U16_C }, { JMIX_C, JSS_AUDIO_S16, JSS_AUDIO_MONO, jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_S16_C }, { JMIX_C, JSS_AUDIO_U16, JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_U16_C }, { JMIX_C, JSS_AUDIO_S16, JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_S16_C }, #endif }; static const int jvmNMixRoutines = sizeof(jvmMixRoutines) / sizeof(jvmMixRoutines[0]); static int jvmFindMixRoutine(int outFormat, int outChannels, int mixerID) { int i; for (i = 0; i < jvmNMixRoutines; i++) { if (jvmMixRoutines[i].outFormat == outFormat && jvmMixRoutines[i].outChannels == outChannels && (mixerID == JMIX_AUTO || jvmMixRoutines[i].mixerID == mixerID)) return i; } return -1; } JSSMixer *jvmInit(const int outFormat, const int outChannels, const int outFreq, const int mixerID) { JSSMixer *mixer; int mixerIdx, v, i; // Check settings if (outChannels < 1) { JSSERROR(DMERR_INVALID_ARGS, NULL, "Invalid number of channels %d\n", outChannels); } if (outFreq < 4000) { JSSERROR(DMERR_INVALID_ARGS, NULL, "Invalid mixing frequency %d\n", outFreq); } /* Select mixing routines: * Here we try to choose the most fitting mixing routines * from the compiled in routines, unless caller is forcing * us to select specific ones. */ if (mixerID == JMIX_AUTO) { mixerIdx = jvmFindMixRoutine(outFormat, outChannels, JMIX_SSE); if (mixerIdx < 0) mixerIdx = jvmFindMixRoutine(outFormat, outChannels, JMIX_MMX); if (mixerIdx < 0) mixerIdx = jvmFindMixRoutine(outFormat, outChannels, JMIX_AUTO); } else { mixerIdx = jvmFindMixRoutine(outFormat, outChannels, mixerID); } if (mixerIdx < 0) { JSSERROR(DMERR_INVALID_ARGS, NULL, "Could not find mixing routine for outFormat=%d, outChannels=%d, outFreq=%d.\n", outFormat, outChannels, outFreq); return NULL; } // Allocate a mixer device structure mixer = dmMalloc0(sizeof(JSSMixer)); if (mixer == NULL) { JSSERROR(DMERR_MALLOC, NULL, "Could not allocate mixing device structure.\n"); } // Initialize variables #ifdef JSS_SUP_THREADS mixer->mutex = dmCreateMutex(); #endif mixer->outFormat = outFormat; mixer->outFreq = outFreq; mixer->outChannels = outChannels; mixer->jvmMixChannel_FW = jvmMixRoutines[mixerIdx].jvmMixChannel_FW; mixer->jvmMixChannel_BW = jvmMixRoutines[mixerIdx].jvmMixChannel_BW; mixer->jvmPostProcess = jvmMixRoutines[mixerIdx].jvmPostProcess; // Allocate addBuffer mixer->addBufSize = outChannels * outFreq; mixer->addBuffer = dmCalloc(mixer->addBufSize, sizeof(Sint32)); if (mixer->addBuffer == NULL) { JSSERROR(DMERR_MALLOC, NULL, "Could not allocate mixing addition buffer.\n"); } // Initialize 8-bit volume table for (v = jsetMinVol; v < jsetMaxVol; v++) { for (i = 0; i < 256; i++) { mixer->volTab8[v][i] = (v * (i - 128)); } } return mixer; } int jvmClose(JSSMixer * mixer) { if (mixer == NULL) return DMERR_NULLPTR; // Deallocate resources #ifdef JSS_SUP_THREADS dmDestroyMutex(mixer->mutex); #endif dmFree(mixer->addBuffer); memset(mixer, 0, sizeof(JSSMixer)); dmFree(mixer); return DMERR_OK; } int jvmGetSampleSize(JSSMixer *mixer) { int sampSize = 1; assert(mixer); switch (mixer->outChannels) { case JSS_AUDIO_STEREO: case JSS_AUDIO_MONO: sampSize = mixer->outChannels; break; default: JSSERROR(DMERR_INVALID_ARGS, -1, "outChannels=%d not stereo or mono!\n", mixer->outChannels); break; } switch (mixer->outFormat) { case JSS_AUDIO_U16: sampSize *= sizeof(Uint16); break; case JSS_AUDIO_S16: sampSize *= sizeof(Sint16); break; case JSS_AUDIO_U8: sampSize *= sizeof(Uint8); break; case JSS_AUDIO_S8: sampSize *= sizeof(Sint8); break; default: JSSERROR(DMERR_INVALID_ARGS, -1, "outFormat=%d is not supported!\n", mixer->outFormat); } return sampSize; } int jvmGetSampleRes(JSSMixer *mixer) { int sampRes = 1; assert(mixer); switch (mixer->outFormat) { case JSS_AUDIO_U16: case JSS_AUDIO_S16: sampRes = 16; break; case JSS_AUDIO_U8: case JSS_AUDIO_S8: sampRes = 8; break; default: JSSERROR(DMERR_INVALID_ARGS, -1, "outFormat=%d is not supported!\n", mixer->outFormat); } return sampRes; } static void jvmMixChannel(JSSMixer *mixer, JSSChannel *chn, Sint32 *addBuffer, const int mixLength) { int mixDone = mixLength, mixResult; Sint32 *ab = addBuffer; if (!chn->chPlaying || chn->chMute) return; DBG("%s(%p, %d)\n", __FUNCTION__, chn, mixLength); while (mixDone > 0) { if (chn->chDirection) { // Channel is playing FORWARDS if (chn->chFlags & jsfLooped) { // Sample is looped if (chn->chFlags & jsfBiDi) { // Bi-directional loop if (FP_GETH(chn->chPos) >= chn->chLoopE) { FP_SETH(chn->chPos, chn->chLoopE - 1); FP_SETL(chn->chPos, 0); chn->chDirection = FALSE; } } else { // Normal forward loop if (FP_GETH(chn->chPos) >= chn->chLoopE) { FP_SETH(chn->chPos, chn->chLoopS /* + (FP_GETH(chn->chPos) - chn->chLoopE) */); FP_SETL(chn->chPos, 0); } } } else { // Normal (non-looped) sample if (FP_GETH(chn->chPos) >= chn->chSize) { chn->chPlaying = FALSE; return; } } } else { // Channel is playing BACKWARDS if (chn->chFlags & jsfLooped) { // Sample is looped if (chn->chFlags & jsfBiDi) { // Bi-directional loop if (FP_GETH(chn->chPos) <= chn->chLoopS) { FP_SETH(chn->chPos, chn->chLoopS); FP_SETL(chn->chPos, 0); chn->chDirection = TRUE; } } else { // Normal forward loop if (FP_GETH(chn->chPos) <= chn->chLoopS) { FP_SETH(chn->chPos, chn->chLoopE - 1); FP_SETL(chn->chPos, 0); } } } else { // Normal (non-looped) sample if (FP_GETH(chn->chPos) <= 0) { chn->chPlaying = FALSE; return; } } } // Call the mixing innerloop functions if (chn->chDirection) { DBG("MIX_FW[%p : %d : ", ab, mixDone); if (chn->chFlags & jsfLooped) { DBG("%d (%x)] {loop}\n", chn->chLoopE, chn->chLoopE); mixResult = mixer->jvmMixChannel_FW((void *) mixer, chn, ab, mixDone, chn->chLoopE); } else { DBG("%d (%x)]\n", chn->chSize, chn->chSize); mixResult = mixer->jvmMixChannel_FW((void *) mixer, chn, ab, mixDone, chn->chSize); } } else { DBG("MIX_BW[%p : %d : ", ab, mixDone); if (chn->chFlags & jsfLooped) { DBG("%d (%x)] {loop}\n", chn->chLoopS, chn->chLoopS); mixResult = mixer->jvmMixChannel_BW(mixer, chn, ab, mixDone, chn->chLoopS); } else { DBG("%d (%x)]\n", 0, 0); mixResult = mixer->jvmMixChannel_BW(mixer, chn, ab, mixDone, 0); } } mixDone -= mixResult; ab += mixResult * mixer->outChannels; } #ifdef JSS_DEBUG if (mixDone < 0) JSSWARNING(DMERR_BOUNDS,, "mixDone < 0 in mixing logic loop.\n"); #endif } void jvmRenderAudio(JSSMixer *mixer, void *mixBuffer, const int mixLength) { int i, blockLength, mixLeft; Sint32 *ab; JSS_LOCK(mixer); assert(mixer != NULL); assert(mixBuffer != NULL); assert(mixLength > 0); assert(mixLength * mixer->outChannels < mixer->addBufSize); // Clear mixer->addBuffer memset(mixer->addBuffer, 0, mixLength * mixer->outChannels * sizeof(Sint32)); ab = mixer->addBuffer; mixLeft = mixLength; while (mixLeft > 0) { // Check for callbacks blockLength = mixLeft; if (mixer->cbFunction) { if (mixer->cbCounter <= 0) { mixer->cbFunction(mixer, mixer->cbData); mixer->cbCounter = mixer->cbFreq; } if (mixer->cbCounter < blockLength) blockLength = mixer->cbCounter; } // Do mixing for (i = 0; i < jsetNChannels; i++) { JSSChannel *chn = &(mixer->channels[i]); if (chn->chPlaying && !chn->chMute) jvmMixChannel(mixer, chn, ab, blockLength); } /* if (chn->chPlaying) { if (!chn->chMute) jvmMixChannel(mixer, chn, ab, blockLength); else jvmAdvanceChannel(mixer, chn, blockLength); } */ ab += blockLength * mixer->outChannels; mixLeft -= blockLength; mixer->cbCounter -= blockLength; } // Post-process mixer->jvmPostProcess(mixer->addBuffer, mixBuffer, mixLength * mixer->outChannels); JSS_UNLOCK(mixer); } int jvmSetCallback(JSSMixer * mixer, void (*cbFunction) (void *, void *), void *cbData) { assert(mixer); if (cbFunction == NULL) JSSERROR(DMERR_NULLPTR, DMERR_NULLPTR, "NULL pointer given as cbFunction"); JSS_LOCK(mixer); mixer->cbFunction = cbFunction; mixer->cbData = cbData; JSS_UNLOCK(mixer); return DMERR_OK; } void jvmRemoveCallback(JSSMixer * mixer) { assert(mixer); JSS_LOCK(mixer); mixer->cbFunction = NULL; mixer->cbData = NULL; mixer->cbFreq = mixer->cbCounter = 0; JSS_UNLOCK(mixer); } int jvmSetCallbackFreq(JSSMixer * mixer, const int cbFreq) { assert(mixer); if ((cbFreq < 1) || (cbFreq >= mixer->outFreq)) JSSERROR(DMERR_INVALID_ARGS, DMERR_INVALID_ARGS, "Invalid callback frequency given (%i / %i)\n", cbFreq, mixer->outFreq); JSS_LOCK(mixer); mixer->cbFreq = (mixer->outFreq / cbFreq); mixer->cbCounter = 0; //fprintf(stderr, "set(outFreq = %d, cbFreq = %d) = %d\n", mixer->outFreq, cbFreq, mixer->cbFreq); JSS_UNLOCK(mixer); return DMERR_OK; } /* Channel manipulation routines */ void jvmPlay(JSSMixer * mixer, const int channel) { JSS_LOCK(mixer); mixer->channels[channel].chPlaying = TRUE; JSS_UNLOCK(mixer); } void jvmStop(JSSMixer * mixer, const int channel) { JSS_LOCK(mixer); mixer->channels[channel].chPlaying = FALSE; JSS_UNLOCK(mixer); } void jvmSetSample(JSSMixer * mixer, const int channel, void *data, const Uint32 size, const Uint32 loopS, const Uint32 loopE, const int flags) { JSSChannel *c; JSS_LOCK(mixer); c = &mixer->channels[channel]; c->chData = data; c->chSize = size; c->chLoopS = loopS; c->chLoopE = loopE; c->chFlags = flags; c->chDirection = TRUE; c->chPrevL = c->chPrevR = 0; c->chPos.dw = c->chDeltaO.dw = 0; JSS_UNLOCK(mixer); } void jvmSetFreq(JSSMixer * mixer, const int channel, const int freq) { JSS_LOCK(mixer); mixer->channels[channel].chFreq = freq; if (mixer->outFreq > 0) { DMFixedPoint a, b; FP_SETHL(a, freq, 0); FP_CONV(b, mixer->outFreq); FP_DIV_R(mixer->channels[channel].chDeltaO, a, b); } else { FP_SET(mixer->channels[channel].chDeltaO, 0); } JSS_UNLOCK(mixer); } int jvmGetFreq(JSSMixer * mixer, const int channel) { int tmp; JSS_LOCK(mixer); tmp = mixer->channels[channel].chFreq; JSS_UNLOCK(mixer); return tmp; } void jvmSetVolume(JSSMixer * mixer, const int channel, const int volume) { JSS_LOCK(mixer); mixer->channels[channel].chVolume = volume; JSS_UNLOCK(mixer); } int jvmGetVolume(JSSMixer * mixer, const int channel) { int tmp; JSS_LOCK(mixer); tmp = mixer->channels[channel].chVolume; JSS_UNLOCK(mixer); return tmp; } void jvmSetPos(JSSMixer * mixer, const int channel, const Uint32 pos) { JSS_LOCK(mixer); FP_SETH(mixer->channels[channel].chPos, pos); FP_SETL(mixer->channels[channel].chPos, 0); JSS_UNLOCK(mixer); } Uint32 jvmGetPos(JSSMixer * mixer, const int channel) { Uint32 tmp; JSS_LOCK(mixer); tmp = FP_GETH(mixer->channels[channel].chPos); JSS_UNLOCK(mixer); return tmp; } void jvmSetPan(JSSMixer * mixer, const int channel, const int panning) { JSS_LOCK(mixer); mixer->channels[channel].chPanning = panning; JSS_UNLOCK(mixer); } int jvmGetPan(JSSMixer * mixer, const int channel) { int tmp; JSS_LOCK(mixer); tmp = mixer->channels[channel].chPanning; JSS_UNLOCK(mixer); return tmp; } void jvmMute(JSSMixer * mixer, const int channel, const BOOL mute) { JSS_LOCK(mixer); mixer->channels[channel].chMute = mute; JSS_UNLOCK(mixer); } void jvmClear(JSSMixer * mixer, const int channel) { JSS_LOCK(mixer); memset(&mixer->channels[channel], 0, sizeof(JSSChannel)); JSS_UNLOCK(mixer); } void jvmSetGlobalVol(JSSMixer * mixer, const int volume) { JSS_LOCK(mixer); mixer->globalVol = volume; JSS_UNLOCK(mixer); } int jvmGetGlobalVol(JSSMixer * mixer) { int tmp; JSS_LOCK(mixer); tmp = mixer->globalVol; JSS_UNLOCK(mixer); return tmp; }