diff jssplr.c @ 144:22e1be63f6af

Various improvements in XM replay: implement the 5ms volume fadein and improved volume ramping, etc.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 05 Oct 2012 09:37:39 +0300
parents 69e3041032e9
children e4d6947c2606
line wrap: on
line diff
--- a/jssplr.c	Fri Oct 05 09:33:42 2012 +0300
+++ b/jssplr.c	Fri Oct 05 09:37:39 2012 +0300
@@ -111,7 +111,7 @@
 }
 
 
-static void jmpCSetVolume(JSSPlayer * mp, JSSPlayerChannel *chn, int channel, int volume)
+static void jmpCSetVolume(JSSPlayer * mp, JSSPlayerChannel *chn, int channel, int volume, int init)
 {
     assert(mp != NULL);
     assert(mp->device != NULL);
@@ -120,12 +120,29 @@
     if (volume > mpMaxVol) volume = mpMaxVol;
 
 //fprintf(stderr, "chn %d: vol=%d, fad=%d, env=%d\n", channel, volume, chn->iCFadeOutVol, chn->iCVolEnv);
-/*
+#if 0
     jvmSetVolume(mp->device, channel,
         (chn->iCFadeOutVol * chn->iCVolEnv * volume) / (16 * 65536));
-*/
-    jvmSetVolumeRamp(mp->device, channel, jvmGetVolume(mp->device, channel),
-        (chn->iCFadeOutVol * chn->iCVolEnv * volume) / (16 * 65536));
+#else
+    if (mp->future == NULL)
+    {
+        jvmSetVolume(mp->device, channel,
+            (chn->iCFadeOutVol * chn->iCVolEnv * volume) / (16 * 65536));
+    }
+    else
+    if (init)
+    {
+        jvmSetVolumeRamp(mp->device, channel,
+            0,
+            jvmGetVolume(mp->future->device, channel), 5);
+    }
+    else
+    {
+        jvmSetVolumeRamp(mp->device, channel,
+            (chn->iCFadeOutVol * chn->iCVolEnv * volume) / (16 * 65536),
+            jvmGetVolume(mp->future->device, channel), 0);
+    }
+#endif
 }
 
 
@@ -134,8 +151,17 @@
     assert(mp != NULL);
     assert(mp->device != NULL);
 
-    jvmSetPan(mp->device, channel,
-        panning + (((chn->iCPanEnv - 32) * (128 - abs(panning - 128))) / 32));
+    if (mp->future == NULL)
+    {
+        jvmSetPan(mp->device, channel,
+            panning + (((chn->iCPanEnv - 32) * (128 - abs(panning - 128))) / 32));
+    }
+    else
+    {
+        jvmSetPanRamp(mp->device, channel,
+            panning + (((chn->iCPanEnv - 32) * (128 - abs(panning - 128))) / 32),
+            jvmGetPan(mp->future->device, channel), 0);
+    }
 }
 
 
@@ -304,13 +330,22 @@
 
     // Allocate a player structure
     mp = dmMalloc0(sizeof(JSSPlayer));
-    if (!mp)
+    if (mp == NULL)
+        JSSERROR(DMERR_MALLOC, NULL, "Could not allocate memory for player structure.\n");
+
+    mp->future = dmMalloc0(sizeof(JSSPlayer));
+    if (mp->future == NULL)
         JSSERROR(DMERR_MALLOC, NULL, "Could not allocate memory for player structure.\n");
 
     // Set variables
     mp->device = pDevice;
+    mp->future->device = jvmInit(pDevice->outFormat, pDevice->outChannels, pDevice->outFreq, JMIX_AUTO);
+    if (mp->future->device == NULL)
+        JSSERROR(DMERR_INIT_FAIL, NULL, "Could not initialize shadow mixing device.\n");
+
 #ifdef JSS_SUP_THREADS
     mp->mutex = dmCreateMutex();
+    mp->future->mutex = dmCreateMutex();
 #endif
     
     return mp;
@@ -330,6 +365,15 @@
     dmDestroyMutex(mp->mutex);
 #endif
 
+    if (mp->future != NULL)
+    {
+#ifdef JSS_SUP_THREADS
+        dmDestroyMutex(mp->future->mutex);
+#endif
+        jvmClose(mp->future->device);
+        dmFree(mp->future);
+    }
+
     // Clear structure
     memset(mp, 0, sizeof(JSSPlayer));
     dmFree(mp);
@@ -393,7 +437,7 @@
     jmpStop(mp);
     jmpClearPlayer(mp);
 
-    mp->module = module;
+    mp->future->module = mp->module = module;
 
     JSS_UNLOCK(mp);
 }
@@ -410,6 +454,9 @@
     {
         jvmRemoveCallback(mp->device);
         mp->isPlaying = FALSE;
+
+        if (mp->future != NULL)
+            jmpStop(mp->future);
     }
     
     JSS_UNLOCK(mp);
@@ -430,6 +477,8 @@
             JSSERROR(result,, "Could not initialize callback for player.\n");
 
         mp->isPlaying = TRUE;
+        if (mp->future != NULL)
+            jmpResume(mp->future);
     }
 
     JSS_UNLOCK(mp);
@@ -560,6 +609,12 @@
 
     mp->isPlaying = TRUE;
 
+    if (mp->future != NULL)
+    {
+        jmpPlayOrder(mp->future, order);
+        jmpExec(mp->future->device, mp->future);
+    }
+
     JSS_UNLOCK(mp);
     return 0;
 }
@@ -595,6 +650,12 @@
 
     mp->isPlaying = TRUE;
 
+    if (mp->future != NULL)
+    {
+        jmpPlayPattern(mp->future, pattern);
+        jmpExec(mp->future->device, mp->future);
+    }
+
     JSS_UNLOCK(mp);
     return 0;
 }
@@ -705,7 +766,7 @@
     }
 
     // Set the new volume
-    jmpCSetVolume(mp, chn, channel, chn->iCVolume + delta);
+    jmpCSetVolume(mp, chn, channel, chn->iCVolume + delta, FALSE);
 
     // Advance tremolo waveform position
     chn->iTremoloPos += chn->iTremoloSpeed;
@@ -1348,9 +1409,9 @@
             paramY++;
             tmp = (chn->iTremorCount % (paramX + paramY));
             if (tmp < paramX)
-                jmpCSetVolume(mp, chn, channel, chn->iCVolume);
+                jmpCSetVolume(mp, chn, channel, chn->iCVolume, FALSE);
             else
-                jmpCSetVolume(mp, chn, channel, jsetMinVol);
+                jmpCSetVolume(mp, chn, channel, jsetMinVol, FALSE);
 
             chn->iTremorCount = (tmp + 1);
             break;
@@ -1369,13 +1430,16 @@
     int channel, flags;
 
     // Check some things via assert()
-    assert(pMP != NULL);
     mp = (JSSPlayer *) pMP;
     JSS_LOCK(mp);
 
     dev = (JSSMixer *) pDEV;
-    assert(mp->device == dev);
-    assert(mp->module != NULL);
+
+    // Futures
+    if (mp->future != NULL)
+    {
+        jmpExec(mp->future->device, mp->future);
+    }
 
     // Check if we are playing
     if (!mp->isPlaying)
@@ -1492,44 +1556,44 @@
         
         // Check NDFlags and update channel data
         flags = chn->iCNewDataFlags;
-        if (flags)
+        if (!flags)
+            continue;
+
+        // Check if we stop?
+        if (flags & cdfStop)
         {
-            // Check if we stop?
-            if (flags & cdfStop)
+            jvmStop(mp->device, channel);
+        }
+        else
+        {
+            // No, handle other flags
+            if (flags & cdfNewInstr)
             {
-                jvmStop(mp->device, channel);
-            }
-            else
-            {
-                // No, handle other flags
-                if (flags & cdfNewInstr)
+                JSSInstrument *instr = chn->iCInstrument;
+                if (instr != NULL)
                 {
-                    JSSInstrument *instr = chn->iCInstrument;
-                    if (instr != NULL)
-                    {
-                        jvmSetSample(mp->device, channel,
-                            instr->data, instr->size,
-                            instr->loopS, instr->loopE,
-                            instr->flags);
-                    }
-                    jvmPlay(mp->device, channel);
+                    jvmSetSample(mp->device, channel,
+                        instr->data, instr->size,
+                        instr->loopS, instr->loopE,
+                        instr->flags);
                 }
+                jvmPlay(mp->device, channel);
+            }
+
+            if (flags & cdfNewPitch)
+                jmpCSetPitch(mp, channel, chn->iCPitch);
 
-                if (flags & cdfNewPitch)
-                    jmpCSetPitch(mp, channel, chn->iCPitch);
+            if (flags & cdfNewPos)
+                jmpCSetPosition(mp, channel, chn->iCPosition);
 
-                if (flags & cdfNewPos)
-                    jmpCSetPosition(mp, channel, chn->iCPosition);
+            if (flags & cdfNewVolume)
+                jmpCSetVolume(mp, chn, channel, chn->iCVolume, flags & cdfNewInstr);
 
-                if (flags & cdfNewVolume)
-                    jmpCSetVolume(mp, chn, channel, chn->iCVolume);
+            if (flags & cdfNewPanPos)
+                jmpCSetPanning(mp, chn, channel, chn->iCPanning);
 
-                if (flags & cdfNewPanPos)
-                    jmpCSetPanning(mp, chn, channel, chn->iCPanning);
-
-                if (flags & cdfNewGlobalVol)
-                    jvmSetGlobalVol(mp->device, mp->globalVol);
-            }
+            if (flags & cdfNewGlobalVol)
+                jvmSetGlobalVol(mp->device, mp->globalVol);
         }
     }