Mercurial > hg > dmlib
view minijss/jssmix.c @ 2373:f35ee6f8d994
Improve the help text in argShowC64PaletteHelp().
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Wed, 08 Jan 2020 12:29:35 +0200 |
parents | 6aa0897265e8 |
children | 69a5af2eb1ea |
line wrap: on
line source
/* * miniJSS - Mixing device and channel handling * Programmed and designed by Matti 'ccr' Hamalainen * (C) Copyright 2006-2015 Tecnic Software productions (TNSP) */ #include "jssmix.h" #ifdef DM_USE_C #define JMIXER_HEADER #include "jmix_c_in.c" #undef JMIXER_HEADER #endif #undef DM_USE_SIMD #ifdef DM_USE_SIMD #define JMIXER_HEADER #include "jmix_mmx_in.c" #undef JMIXER_HEADER #endif typedef struct { int mixerID; int outFormat; int outChannels; int (*jvmMixChannel_FW)(JSSMixer *, JSSChannel *, JMIXER_ADDBUF_TYPE *, const int, const DMFixedPoint); int (*jvmMixChannel_BW)(JSSMixer *, JSSChannel *, JMIXER_ADDBUF_TYPE *, const int, const DMFixedPoint); void (*jvmPostProcess)(JMIXER_ADDBUF_TYPE *, 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) { for (int 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; // Sanity check if (jvmNMixRoutines == 0) { JSSERROR(DMERR_INTERNAL, NULL, "No mixing routines compiled in?\n"); return NULL; } // Check settings if (outChannels < 1) { JSSERROR(DMERR_INVALID_ARGS, NULL, "Invalid number of channels %d\n", outChannels); } if (outFreq < 1000) { 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->outChannels = outChannels; mixer->outFreq = outFreq; 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 * 2; mixer->addBuffer = dmMalloc(mixer->addBufSize * sizeof(JMIXER_ADDBUF_TYPE)); if (mixer->addBuffer == NULL) { JSSERROR(DMERR_MALLOC, NULL, "Could not allocate mixing addition buffer.\n"); } 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); dmMemset(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 = 0; 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, JMIXER_ADDBUF_TYPE *addBuffer, const int mixLength) { int mixDone = mixLength, mixResult; JMIXER_ADDBUF_TYPE *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 (chn->chPos.dw >= chn->chLoopE.dw) { DMFixedPoint end; FP_ADD_R(end, chn->chLoopE, chn->chLoopE); FP_SUB_R(chn->chPos, end, chn->chPos); chn->chDirection = FALSE; } } else { // Normal forward loop if (chn->chPos.dw >= chn->chLoopE.dw) { DMFixedPoint diff; FP_SUB_R(diff, chn->chPos, chn->chLoopE); FP_ADD_R(chn->chPos, chn->chLoopS, diff); } } } else { // Normal (non-looped) sample if (chn->chPos.dw >= chn->chSize.dw) { chn->chPlaying = FALSE; return; } } } else { // Channel is playing BACKWARDS if (chn->chFlags & jsfLooped) { // Sample is looped if (chn->chFlags & jsfBiDi) { // Bi-directional loop if (chn->chPos.dw <= chn->chLoopS.dw) { DMFixedPoint start; FP_ADD_R(start, chn->chLoopS, chn->chLoopS); FP_SUB_R(chn->chPos, start, chn->chPos); chn->chDirection = TRUE; } } else { // Normal forward loop if (chn->chPos.dw <= chn->chLoopS.dw) { DMFixedPoint diff; FP_SUB_R(diff, chn->chLoopE, chn->chLoopS); FP_ADD(chn->chPos, diff); } } } else { // Normal (non-looped) sample if (chn->chPos.dw <= 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", FP_GETH32(chn->chLoopE), FP_GETH32(chn->chLoopE)); mixResult = mixer->jvmMixChannel_FW((void *) mixer, chn, ab, mixDone, chn->chLoopE); } else { DBG("%d (%x)]\n", FP_GETH32(chn->chSize), FP_GETH32(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 { static const DMFixedPoint zero = { 0 }; DBG("%d (%x)]\n", 0, 0); mixResult = mixer->jvmMixChannel_BW(mixer, chn, ab, mixDone, zero); } } 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 blockLength, mixLeft; JMIXER_ADDBUF_TYPE *ab; JSS_LOCK(mixer); assert(mixer != NULL); assert(mixBuffer != NULL); assert(mixLength > 0); assert(mixLength * mixer->outChannels <= mixer->addBufSize); // Clear mixer->addBuffer dmMemset(mixer->addBuffer, 0, mixLength * mixer->outChannels * sizeof(JMIXER_ADDBUF_TYPE)); 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 (int 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); JSS_LOCK(mixer); mixer->cbFreq = 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 jvmReset(JSSMixer * mixer, const int channel) { JSSChannel *c; JSS_LOCK(mixer); c = &mixer->channels[channel]; c->chDirection = TRUE; c->chPos.dw = c->chDeltaO.dw = 0; JSS_UNLOCK(mixer); } void jvmSetSample(JSSMixer * mixer, const int channel, void *data, const Sint32 size, const Sint32 loopS, const Sint32 loopE, const int flags) { JSSChannel *c; JSS_LOCK(mixer); c = &mixer->channels[channel]; FP_SETHL(c->chSize, size, 0); FP_SETHL(c->chLoopS, loopS, 0); FP_SETHL(c->chLoopE, loopE, 0); c->chData = data; c->chFlags = flags; c->chDirection = TRUE; 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); FP_SETHL(mixer->channels[channel].chVolume, volume, 0); mixer->channels[channel].chVolumeD = 0; mixer->channels[channel].chDeltaV.dw = 0; JSS_UNLOCK(mixer); } void jvmSetVolumeRamp(JSSMixer * mixer, const int channel, const int start, const int end, const int len) { int tmp; DMFixedPoint a, b; JSS_LOCK(mixer); FP_SETHL(mixer->channels[channel].chVolume, start, 0); tmp = mixer->channels[channel].chVolumeD = len > 0 ? ((mixer->outFreq * len) / 1000) : mixer->cbFreq; FP_SETHL(a, (end - start), 0); FP_CONV(b, tmp); FP_DIV_R(mixer->channels[channel].chDeltaV, a, b); JSS_UNLOCK(mixer); } int jvmGetVolume(JSSMixer * mixer, const int channel) { int tmp; JSS_LOCK(mixer); tmp = FP_GETH32(mixer->channels[channel].chVolume); JSS_UNLOCK(mixer); return tmp; } void jvmSetPos(JSSMixer * mixer, const int channel, const Sint32 pos) { JSS_LOCK(mixer); FP_SETHL(mixer->channels[channel].chPos, pos, 0); JSS_UNLOCK(mixer); } Sint32 jvmGetPos(JSSMixer * mixer, const int channel) { Sint32 tmp; JSS_LOCK(mixer); tmp = FP_GETH32(mixer->channels[channel].chPos); JSS_UNLOCK(mixer); return tmp; } void jvmSetPan(JSSMixer * mixer, const int channel, const int panning) { JSS_LOCK(mixer); FP_SETHL(mixer->channels[channel].chPanning, panning, 0); mixer->channels[channel].chPanningD = 0; mixer->channels[channel].chDeltaP.dw = 0; JSS_UNLOCK(mixer); } void jvmSetPanRamp(JSSMixer * mixer, const int channel, const int start, const int end, const int len) { int tmp; DMFixedPoint a, b; JSS_LOCK(mixer); FP_SETHL(mixer->channels[channel].chPanning, start, 0); tmp = mixer->channels[channel].chPanningD = len > 0 ? ((mixer->outFreq * len) / 1000) : mixer->cbFreq; FP_SETHL(a, (end - start), 0); FP_CONV(b, tmp); FP_DIV_R(mixer->channels[channel].chDeltaP, a, b); JSS_UNLOCK(mixer); } int jvmGetPan(JSSMixer * mixer, const int channel) { int tmp; JSS_LOCK(mixer); tmp = FP_GETH32(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); } BOOL jvmGetMute(JSSMixer * mixer, const int channel) { BOOL tmp; JSS_LOCK(mixer); tmp = mixer->channels[channel].chMute; JSS_UNLOCK(mixer); return tmp; } void jvmClear(JSSMixer * mixer, const int channel) { JSS_LOCK(mixer); dmMemset(&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; }