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;
+}