Mercurial > hg > dmlib
diff jssmix.c @ 0:32250b436bca
Initial re-import.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 28 Sep 2012 01:54:23 +0300 |
parents | |
children | 7908061da010 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jssmix.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,654 @@ +/* + * 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; +}