spu: rewrite adsr according to current docs
authornotaz <notasas@gmail.com>
Thu, 11 Dec 2025 00:23:04 +0000 (02:23 +0200)
committernotaz <notasas@gmail.com>
Thu, 11 Dec 2025 00:44:24 +0000 (02:44 +0200)
nocash mostly

plugins/dfsound/adsr.c
plugins/dfsound/externals.h
plugins/dfsound/freeze.c
plugins/dfsound/registers.c
plugins/dfsound/spu.c

index 23ff3df..77a9e21 100644 (file)
@@ -1,10 +1,3 @@
-/***************************************************************************\r
-                          adsr.c  -  description\r
-                             -------------------\r
-    begin                : Wed May 15 2002\r
-    copyright            : (C) 2002 by Pete Bernert\r
-    email                : BlackDove@addcom.de\r
- ***************************************************************************/\r
 /***************************************************************************\r
  *                                                                         *\r
  *   This program is free software; you can redistribute it and/or modify  *\r
  ***************************************************************************/\r
 \r
 #include "stdafx.h"\r
-\r
-#define _IN_ADSR\r
+#include "externals.h"\r
 \r
 // will be included from spu.c\r
-#ifdef _IN_SPU\r
+#if 1 //def _IN_SPU\r
 \r
 ////////////////////////////////////////////////////////////////////////\r
 // ADSR func\r
 ////////////////////////////////////////////////////////////////////////\r
 \r
-static int RateTableAdd[128];\r
-static int RateTableSub[128];\r
-\r
-void InitADSR(void)                                    // INIT ADSR\r
-{\r
- int lcv, denom;\r
-\r
- // Optimize table - Dr. Hell ADSR math\r
- for (lcv = 0; lcv < 48; lcv++)\r
- {\r
-  RateTableAdd[lcv] = (7 - (lcv&3)) << (11 + 16 - (lcv >> 2));\r
-  RateTableSub[lcv] = (-8 + (lcv&3)) << (11 + 16 - (lcv >> 2));\r
- }\r
-\r
- for (; lcv < 128; lcv++)\r
- {\r
-  denom = 1 << ((lcv>>2) - 11);\r
-\r
-  RateTableAdd[lcv] = ((7 - (lcv&3)) << 16) / denom;\r
-  RateTableSub[lcv] = ((-8 + (lcv&3)) << 16) / denom;\r
-\r
-  // XXX: this is wrong, we need more bits..\r
-  if (RateTableAdd[lcv] == 0)\r
-    RateTableAdd[lcv] = 1;\r
- }\r
-}\r
-\r
-////////////////////////////////////////////////////////////////////////\r
-\r
 INLINE void StartADSR(int ch)                          // MIX ADSR\r
 {\r
- spu.s_chan[ch].ADSRX.State = ADSR_ATTACK;             // and init some adsr vars\r
- spu.s_chan[ch].ADSRX.EnvelopeVol = 0;\r
+  ADSRInfoEx *adsr = &spu.s_chan[ch].ADSRX;\r
+  adsr->State = ADSR_ATTACK;                            // and init some adsr vars\r
+  adsr->EnvelopeVol = 0;\r
+  adsr->StepCounter = 0;\r
 }\r
 \r
 ////////////////////////////////////////////////////////////////////////\r
 \r
-static int MixADSR(int *samples, ADSRInfoEx *adsr, int ns_to)\r
-{\r
- unsigned int EnvelopeVol = adsr->EnvelopeVol;\r
- int ns = 0, val, rto, level;\r
-\r
- if (adsr->State == ADSR_RELEASE)\r
- {\r
-   val = RateTableSub[adsr->ReleaseRate * 4];\r
-\r
-   if (adsr->ReleaseModeExp)\r
-   {\r
-     for (; ns < ns_to; ns++)\r
-     {\r
-       EnvelopeVol += ((long long)val * EnvelopeVol) >> (15+16);\r
-       if ((signed int)EnvelopeVol <= 0)\r
-         break;\r
-\r
-       samples[ns] *= (signed int)EnvelopeVol >> 21;\r
-       samples[ns] >>= 10;\r
-     }\r
-   }\r
-   else\r
-   {\r
-     for (; ns < ns_to; ns++)\r
-     {\r
-       EnvelopeVol += val;\r
-       if ((signed int)EnvelopeVol <= 0)\r
-         break;\r
-\r
-       samples[ns] *= (signed int)EnvelopeVol >> 21;\r
-       samples[ns] >>= 10;\r
-     }\r
-   }\r
-\r
-   goto done;\r
- }\r
-\r
- switch (adsr->State)\r
- {\r
-   case ADSR_ATTACK:                                   // -> attack\r
-     rto = 0;\r
-     if (adsr->AttackModeExp && EnvelopeVol >= 0x60000000)\r
-       rto = 8;\r
-     val = RateTableAdd[adsr->AttackRate + rto];\r
-\r
-     for (; ns < ns_to; ns++)\r
-     {\r
-       EnvelopeVol += val;\r
-       if ((signed int)EnvelopeVol < 0) // overflow\r
-        break;\r
-\r
-       samples[ns] *= (signed int)EnvelopeVol >> 21;\r
-       samples[ns] >>= 10;\r
-     }\r
+enum ADSRtype {\r
+  adsr_noexp,\r
+  adsr_expinc,\r
+  adsr_expdec,\r
+};\r
 \r
-     if ((signed int)EnvelopeVol < 0) // overflow\r
-     {\r
-       EnvelopeVol = 0x7fffffff;\r
-       adsr->State = ADSR_DECAY;\r
-       ns++; // sample is good already\r
-       goto decay;\r
-     }\r
-     break;\r
-\r
-   //--------------------------------------------------//\r
-   decay:\r
-   case ADSR_DECAY:                                    // -> decay\r
-     val = RateTableSub[adsr->DecayRate * 4];\r
-     level = adsr->SustainLevel;\r
-\r
-     for (; ns < ns_to; )\r
-     {\r
-       EnvelopeVol += ((long long)val * EnvelopeVol) >> (15+16);\r
-       if ((signed int)EnvelopeVol < 0)\r
-         EnvelopeVol = 0;\r
-\r
-       samples[ns] *= EnvelopeVol >> 21;\r
-       samples[ns] >>= 10;\r
-       ns++;\r
-\r
-       if (((EnvelopeVol >> 27) & 0xf) <= level)\r
-       {\r
-         adsr->State = ADSR_SUSTAIN;\r
-         goto sustain;\r
-       }\r
-     }\r
-     break;\r
-\r
-   //--------------------------------------------------//\r
-   sustain:\r
-   case ADSR_SUSTAIN:                                  // -> sustain\r
-     if (adsr->SustainIncrease)\r
-     {\r
-       if (EnvelopeVol >= 0x7fff0000)\r
-       {\r
-         ns = ns_to;\r
-         break;\r
-       }\r
-\r
-       rto = 0;\r
-       if (adsr->SustainModeExp && EnvelopeVol >= 0x60000000)\r
-         rto = 8;\r
-       val = RateTableAdd[adsr->SustainRate + rto];\r
-\r
-       for (; ns < ns_to; ns++)\r
-       {\r
-         EnvelopeVol += val;\r
-         if (EnvelopeVol >= 0x7fe00000)\r
-         {\r
-           EnvelopeVol = 0x7fffffff;\r
-           ns = ns_to;\r
-           break;\r
-         }\r
-\r
-         samples[ns] *= (signed int)EnvelopeVol >> 21;\r
-         samples[ns] >>= 10;\r
-       }\r
-     }\r
-     else\r
-     {\r
-       val = RateTableSub[adsr->SustainRate];\r
-       if (adsr->SustainModeExp)\r
-       {\r
-         for (; ns < ns_to; ns++)\r
-         {\r
-           EnvelopeVol += ((long long)val * EnvelopeVol) >> (15+16);\r
-           if ((signed int)EnvelopeVol < 0)\r
-             break;\r
-\r
-           samples[ns] *= (signed int)EnvelopeVol >> 21;\r
-           samples[ns] >>= 10;\r
-         }\r
-       }\r
-       else\r
-       {\r
-         for (; ns < ns_to; ns++)\r
-         {\r
-           EnvelopeVol += val;\r
-           if ((signed int)EnvelopeVol < 0)\r
-             break;\r
-\r
-           samples[ns] *= (signed int)EnvelopeVol >> 21;\r
-           samples[ns] >>= 10;\r
-         }\r
-       }\r
-     }\r
-     break;\r
- }\r
-\r
-done:\r
- adsr->EnvelopeVol = EnvelopeVol;\r
- return ns;\r
-}\r
-\r
-static int SkipADSR(ADSRInfoEx *adsr, int ns_to)\r
+INLINE forceinline int adsr_do_inner(int *samples, int ns, int ns_to,\r
+    int *EnvelopeVol_, int env_step, int *cycles_, int cycles_step,\r
+    int do_samples, enum ADSRtype type)\r
 {\r
- unsigned int EnvelopeVol = adsr->EnvelopeVol;\r
- int ns = 0, val, rto, level;\r
- int64_t v64;\r
-\r
- if (adsr->State == ADSR_RELEASE)\r
- {\r
-   val = RateTableSub[adsr->ReleaseRate * 4];\r
-   if (adsr->ReleaseModeExp)\r
-   {\r
-     for (; ns < ns_to; ns++)\r
-     {\r
-       EnvelopeVol += ((long long)val * EnvelopeVol) >> (15+16);\r
-       if ((signed int)EnvelopeVol <= 0)\r
-         break;\r
-     }\r
-   }\r
-   else\r
-   {\r
-     v64 = EnvelopeVol;\r
-     v64 += (int64_t)val * ns_to;\r
-     EnvelopeVol = (int)v64;\r
-     if (v64 > 0)\r
-       ns = ns_to;\r
-   }\r
-   goto done;\r
- }\r
-\r
- switch (adsr->State)\r
- {\r
-   case ADSR_ATTACK:                                   // -> attack\r
-     rto = 0;\r
-     if (adsr->AttackModeExp && EnvelopeVol >= 0x60000000)\r
-       rto = 8;\r
-     val = RateTableAdd[adsr->AttackRate + rto];\r
-\r
-     for (; ns < ns_to; ns++)\r
-     {\r
-       EnvelopeVol += val;\r
-       if ((signed int)EnvelopeVol < 0)\r
-        break;\r
-     }\r
-     if ((signed int)EnvelopeVol < 0) // overflow\r
-     {\r
-       EnvelopeVol = 0x7fffffff;\r
-       adsr->State = ADSR_DECAY;\r
-       ns++;\r
-       goto decay;\r
-     }\r
-     break;\r
-\r
-   //--------------------------------------------------//\r
-   decay:\r
-   case ADSR_DECAY:                                    // -> decay\r
-     val = RateTableSub[adsr->DecayRate * 4];\r
-     level = adsr->SustainLevel;\r
-\r
-     for (; ns < ns_to; )\r
-     {\r
-       EnvelopeVol += ((long long)val * EnvelopeVol) >> (15+16);\r
-       if ((signed int)EnvelopeVol < 0)\r
-         EnvelopeVol = 0;\r
-\r
-       ns++;\r
-\r
-       if (((EnvelopeVol >> 27) & 0xf) <= level)\r
-       {\r
-         adsr->State = ADSR_SUSTAIN;\r
-         goto sustain;\r
-       }\r
-     }\r
-     break;\r
-\r
-   //--------------------------------------------------//\r
-   sustain:\r
-   case ADSR_SUSTAIN:                                  // -> sustain\r
-     if (adsr->SustainIncrease)\r
-     {\r
-       ns = ns_to;\r
-\r
-       if (EnvelopeVol >= 0x7fff0000)\r
-         break;\r
-\r
-       rto = 0;\r
-       if (adsr->SustainModeExp && EnvelopeVol >= 0x60000000)\r
-         rto = 8;\r
-       val = RateTableAdd[adsr->SustainRate + rto];\r
-\r
-       v64 = EnvelopeVol;\r
-       v64 += (int64_t)val * (ns_to - ns);\r
-       EnvelopeVol = (int)v64;\r
-       if (v64 >= 0x7fe00000ll)\r
-         EnvelopeVol = 0x7fffffff;\r
-     }\r
-     else\r
-     {\r
-       val = RateTableSub[adsr->SustainRate];\r
-       if (adsr->SustainModeExp)\r
-       {\r
-         for (; ns < ns_to; ns++)\r
-         {\r
-           EnvelopeVol += ((long long)val * EnvelopeVol) >> (15+16);\r
-           if ((signed int)EnvelopeVol < 0)\r
-             break;\r
-         }\r
-       }\r
-       else\r
-       {\r
-         v64 = EnvelopeVol;\r
-         v64 += (int64_t)val * (ns_to - ns);\r
-         EnvelopeVol = (int)v64;\r
-         if (v64 > 0)\r
-           ns = ns_to;\r
-       }\r
-     }\r
-     break;\r
- }\r
-\r
-done:\r
- adsr->EnvelopeVol = EnvelopeVol;\r
- return ns;\r
-}\r
-\r
-#endif\r
-\r
-/*\r
-James Higgs ADSR investigations:\r
-\r
-PSX SPU Envelope Timings\r
-~~~~~~~~~~~~~~~~~~~~~~~~\r
-\r
-First, here is an extract from doomed's SPU doc, which explains the basics\r
-of the SPU "volume envelope": \r
-\r
-*** doomed doc extract start ***\r
-\r
---------------------------------------------------------------------------\r
-Voices.\r
---------------------------------------------------------------------------\r
-The SPU has 24 hardware voices. These voices can be used to reproduce sample\r
-data, noise or can be used as frequency modulator on the next voice.\r
-Each voice has it's own programmable ADSR envelope filter. The main volume\r
-can be programmed independently for left and right output.\r
-\r
-The ADSR envelope filter works as follows:\r
-Ar = Attack rate, which specifies the speed at which the volume increases\r
-     from zero to it's maximum value, as soon as the note on is given. The\r
-     slope can be set to lineair or exponential.\r
-Dr = Decay rate specifies the speed at which the volume decreases to the\r
-     sustain level. Decay is always decreasing exponentially.\r
-Sl = Sustain level, base level from which sustain starts.\r
-Sr = Sustain rate is the rate at which the volume of the sustained note\r
-     increases or decreases. This can be either lineair or exponential.\r
-Rr = Release rate is the rate at which the volume of the note decreases\r
-     as soon as the note off is given.\r
-\r
-     lvl |\r
-       ^ |     /\Dr     __\r
-     Sl _| _  / _ \__---  \\r
-         |   /       ---__ \ Rr\r
-         |  /Ar       Sr  \ \\r
-         | /                \\\r
-         |/___________________\________\r
-                                  ->time\r
-\r
-The overal volume can also be set to sweep up or down lineairly or\r
-exponentially from it's current value. This can be done seperately\r
-for left and right.\r
-\r
-Relevant SPU registers:\r
--------------------------------------------------------------\r
-$1f801xx8         Attack/Decay/Sustain level\r
-bit  |0f|0e 0d 0c 0b 0a 09 08|07 06 05 04|03 02 01 00|\r
-desc.|Am|         Ar         |Dr         |Sl         |\r
-\r
-Am       0        Attack mode Linear\r
-         1                    Exponential\r
-\r
-Ar       0-7f     attack rate\r
-Dr       0-f      decay rate\r
-Sl       0-f      sustain level\r
--------------------------------------------------------------\r
-$1f801xxa         Sustain rate, Release Rate.\r
-bit  |0f|0e|0d|0c 0b 0a 09 08 07 06|05|04 03 02 01 00|\r
-desc.|Sm|Sd| 0|   Sr               |Rm|Rr            |\r
-\r
-Sm       0        sustain rate mode linear\r
-         1                          exponential\r
-Sd       0        sustain rate mode increase\r
-         1                          decrease\r
-Sr       0-7f     Sustain Rate\r
-Rm       0        Linear decrease\r
-         1        Exponential decrease\r
-Rr       0-1f     Release Rate\r
-\r
-Note: decay mode is always Expontial decrease, and thus cannot\r
-be set.\r
--------------------------------------------------------------\r
-$1f801xxc         Current ADSR volume\r
-bit  |0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00|\r
-desc.|ADSRvol                                        |\r
-\r
-ADSRvol           Returns the current envelope volume when\r
-                  read.\r
--- James' Note: return range: 0 -> 32767\r
-\r
-*** doomed doc extract end *** \r
-\r
-By using a small PSX proggie to visualise the envelope as it was played,\r
-the following results for envelope timing were obtained:\r
-\r
-1. Attack rate value (linear mode)\r
-\r
-   Attack value range: 0 -> 127\r
-\r
-   Value  | 48 | 52 | 56 | 60 | 64 | 68 | 72 |    | 80 |\r
-   -----------------------------------------------------------------\r
-   Frames | 11 | 21 | 42 | 84 | 169| 338| 676|    |2890|\r
-\r
-   Note: frames is no. of PAL frames to reach full volume (100%\r
-   amplitude)\r
-\r
-   Hmm, noticing that the time taken to reach full volume doubles\r
-   every time we add 4 to our attack value, we know the equation is\r
-   of form:\r
-             frames = k * 2 ^ (value / 4)\r
-\r
-   (You may ponder about envelope generator hardware at this point,\r
-   or maybe not... :)\r
-\r
-   By substituting some stuff and running some checks, we get:\r
-\r
-       k = 0.00257              (close enuf)\r
-\r
-   therefore,\r
-             frames = 0.00257 * 2 ^ (value / 4)\r
-   If you just happen to be writing an emulator, then you can probably\r
-   use an equation like:\r
-\r
-       %volume_increase_per_tick = 1 / frames\r
-\r
-\r
-   ------------------------------------\r
-   Pete:\r
-   ms=((1<<(value>>2))*514)/10000\r
-   ------------------------------------\r
-\r
-2. Decay rate value (only has log mode)\r
-\r
-   Decay value range: 0 -> 15\r
-\r
-   Value  |  8 |  9 | 10 | 11 | 12 | 13 | 14 | 15 |\r
-   ------------------------------------------------\r
-   frames |    |    |    |    |  6 | 12 | 24 | 47 |\r
-\r
-   Note: frames here is no. of PAL frames to decay to 50% volume.\r
-\r
-   formula: frames = k * 2 ^ (value)\r
-\r
-   Substituting, we get: k = 0.00146\r
-\r
-   Further info on logarithmic nature:\r
-   frames to decay to sustain level 3  =  3 * frames to decay to \r
-   sustain level 9\r
-\r
-   Also no. of frames to 25% volume = roughly 1.85 * no. of frames to\r
-   50% volume.\r
-\r
-   Frag it - just use linear approx.\r
-\r
-   ------------------------------------\r
-   Pete:\r
-   ms=((1<<value)*292)/10000\r
-   ------------------------------------\r
-\r
-\r
-3. Sustain rate value (linear mode)\r
-\r
-   Sustain rate range: 0 -> 127\r
-\r
-   Value  | 48 | 52 | 56 | 60 | 64 | 68 | 72 |\r
-   -------------------------------------------\r
-   frames |  9 | 19 | 37 | 74 | 147| 293| 587|\r
-\r
-   Here, frames = no. of PAL frames for volume amplitude to go from 100%\r
-   to 0% (or vice-versa).\r
-\r
-   Same formula as for attack value, just a different value for k:\r
-\r
-   k = 0.00225\r
-\r
-   ie: frames = 0.00225 * 2 ^ (value / 4)\r
-\r
-   For emulation purposes:\r
-\r
-   %volume_increase_or_decrease_per_tick = 1 / frames\r
-\r
-   ------------------------------------\r
-   Pete:\r
-   ms=((1<<(value>>2))*450)/10000\r
-   ------------------------------------\r
-\r
-\r
-4. Release rate (linear mode)\r
-\r
-   Release rate range: 0 -> 31\r
-\r
-   Value  | 13 | 14 | 15 | 16 | 17 |\r
-   ---------------------------------------------------------------\r
-   frames | 18 | 36 | 73 | 146| 292|\r
-\r
-   Here, frames = no. of PAL frames to decay from 100% vol to 0% vol\r
-   after "note-off" is triggered.\r
-\r
-   Formula: frames = k * 2 ^ (value)\r
-\r
-   And so: k = 0.00223\r
-\r
-   ------------------------------------\r
-   Pete:\r
-   ms=((1<<value)*446)/10000\r
-   ------------------------------------\r
-\r
-\r
-Other notes:   \r
-\r
-Log stuff not figured out. You may get some clues from the "Decay rate"\r
-stuff above. For emu purposes it may not be important - use linear\r
-approx.\r
-\r
-To get timings in millisecs, multiply frames by 20.\r
-\r
-\r
-\r
-- James Higgs 17/6/2000\r
-james7780@yahoo.com\r
-\r
-//---------------------------------------------------------------\r
-\r
-OLD adsr mixing according to james' rules... has to be called\r
-every one millisecond\r
-\r
-\r
- long v,v2,lT,l1,l2,l3;\r
-\r
- if(s_chan[ch].bStop)                                  // psx wants to stop? -> release phase\r
+  int EnvelopeVol = *EnvelopeVol_;\r
+  int cycles = *cycles_;\r
+  int cycles_step_exp;\r
+  switch (type)\r
   {\r
-   if(s_chan[ch].ADSR.ReleaseVal!=0)                   // -> release not 0: do release (if 0: stop right now)\r
+  case adsr_noexp:\r
+    if (cycles_step == 0x8000)\r
+    {\r
+      for (; ns < ns_to && !(EnvelopeVol & 0x8000); ns++, EnvelopeVol += env_step)\r
+        if (do_samples)\r
+          samples[ns] = samples[ns] * EnvelopeVol >> 15;\r
+    }\r
+    else\r
     {\r
-     if(!s_chan[ch].ADSR.ReleaseVol)                   // --> release just started? set up the release stuff\r
+      while (ns < ns_to)\r
       {\r
-       s_chan[ch].ADSR.ReleaseStartTime=s_chan[ch].ADSR.lTime;\r
-       s_chan[ch].ADSR.ReleaseVol=s_chan[ch].ADSR.lVolume;\r
-       s_chan[ch].ADSR.ReleaseTime =                   // --> calc how long does it take to reach the wanted sus level\r
-         (s_chan[ch].ADSR.ReleaseTime*\r
-          s_chan[ch].ADSR.ReleaseVol)/1024;\r
+        if (do_samples)\r
+          samples[ns] = samples[ns] * EnvelopeVol >> 15;\r
+        ns++;\r
+        if ((cycles += cycles_step) & 0x8000)\r
+        {\r
+          cycles = 0;\r
+          EnvelopeVol += env_step;\r
+          if (EnvelopeVol & 0x8000)\r
+            break;\r
+        }\r
       }\r
-                                                       // -> NO release exp mode used (yet)\r
-     v=s_chan[ch].ADSR.ReleaseVol;                     // -> get last volume\r
-     lT=s_chan[ch].ADSR.lTime-                         // -> how much time is past?\r
-        s_chan[ch].ADSR.ReleaseStartTime;\r
-     l1=s_chan[ch].ADSR.ReleaseTime;\r
-                                                       \r
-     if(lT<l1)                                         // -> we still have to release\r
+    }\r
+    break;\r
+\r
+  case adsr_expinc:\r
+    cycles_step_exp = cycles_step;\r
+    if (EnvelopeVol >= 0x6000)\r
+      cycles_step_exp = max(1, (cycles_step >> 2));\r
+    while (ns < ns_to)\r
+    {\r
+      if (do_samples)\r
+        samples[ns] = samples[ns] * EnvelopeVol >> 15;\r
+      ns++;\r
+      if ((cycles += cycles_step_exp) & 0x8000)\r
       {\r
-       v=v-((v*lT)/l1);                                // --> calc new volume\r
+        cycles = 0;\r
+        cycles_step_exp = cycles_step;\r
+        if (EnvelopeVol >= 0x6000)\r
+          cycles_step_exp = max(1, (cycles_step >> 2));\r
+        EnvelopeVol += env_step;\r
+        if (EnvelopeVol >= 0x8000)\r
+          break;\r
       }\r
-     else                                              // -> release is over: now really stop that sample\r
-      {v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;}\r
     }\r
-   else                                                // -> release IS 0: release at once\r
+    break;\r
+\r
+  case adsr_expdec:\r
+    while (ns < ns_to)\r
     {\r
-     v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;\r
+      if (do_samples)\r
+        samples[ns] = samples[ns] * EnvelopeVol >> 15;\r
+      ns++;\r
+      if ((cycles += cycles_step) & 0x8000)\r
+      {\r
+        cycles = 0;\r
+        EnvelopeVol += env_step * EnvelopeVol >> 15;\r
+        if (EnvelopeVol <= 0)\r
+          break;\r
+      }\r
     }\r
   }\r
- else                                               \r
-  {//--------------------------------------------------// not in release phase:\r
-   v=1024;\r
-   lT=s_chan[ch].ADSR.lTime;\r
-   l1=s_chan[ch].ADSR.AttackTime;\r
-                                                       \r
-   if(lT<l1)                                           // attack\r
-    {                                                  // no exp mode used (yet)\r
-//     if(s_chan[ch].ADSR.AttackModeExp)\r
-//      {\r
-//       v=(v*lT)/l1;\r
-//      }\r
-//     else\r
+  *EnvelopeVol_ = EnvelopeVol;\r
+  *cycles_ = cycles;\r
+  return ns;\r
+}\r
+\r
+INLINE forceinline int adsr_do(int *samples, ADSRInfoEx *adsr, int ns_to,\r
+    int do_samples)\r
+{\r
+  int EnvelopeVol = adsr->EnvelopeVol;\r
+  int cycles_step, cycles = adsr->StepCounter;\r
+  int ns = 0, level, env_step, rate;\r
+\r
+  switch (adsr->State)\r
+  {\r
+  case ADSR_ATTACK:                                   // -> attack\r
+    if (adsr->AttackRate == 0x7f)\r
+      goto frozen;\r
+    rate = adsr->AttackRate >> 2;\r
+    cycles_step = max(1, 0x8000 >> max(0, rate - 11));\r
+    env_step = (7 - (adsr->AttackRate & 3)) << max(0, 11 - rate);\r
+\r
+    if (adsr->AttackModeExp)\r
+      ns = adsr_do_inner(samples, ns, ns_to, &EnvelopeVol, env_step,\r
+             &cycles, cycles_step, do_samples, adsr_expinc);\r
+    else\r
+      ns = adsr_do_inner(samples, ns, ns_to, &EnvelopeVol, env_step,\r
+             &cycles, cycles_step, do_samples, adsr_noexp);\r
+    if (EnvelopeVol < 0x7fff)\r
+      break;\r
+    EnvelopeVol = 0x7fff;\r
+    adsr->State = ADSR_DECAY;\r
+    // fallthrough to decay\r
+\r
+  //--------------------------------------------------//\r
+  case ADSR_DECAY:                                    // -> decay\r
+    level = (adsr->SustainLevel + 1) << 11;\r
+    cycles_step = 0x8000 >> max(0, adsr->DecayRate - 11);\r
+    env_step = (uint32_t)-8 << max(0, 11 - (int)adsr->DecayRate);\r
+\r
+    while (ns < ns_to)\r
+    {\r
+      if (do_samples)\r
+        samples[ns] = samples[ns] * EnvelopeVol >> 15;\r
+      ns++;\r
+      if ((cycles += cycles_step) & 0x8000)\r
       {\r
-       v=(v*lT)/l1;\r
+        cycles = 0;\r
+        EnvelopeVol += env_step * EnvelopeVol >> 15;\r
+        if (EnvelopeVol < level)\r
+          break;\r
       }\r
-     if(v==0) v=1;\r
     }\r
-   else                                                // decay\r
-    {                                                  // should be exp, but who cares? ;)\r
-     l2=s_chan[ch].ADSR.DecayTime;\r
-     v2=s_chan[ch].ADSR.SustainLevel;\r
+    if (EnvelopeVol >= level)\r
+      break;\r
+    adsr->State = ADSR_SUSTAIN;\r
+    adsr->EnvelopeVol = EnvelopeVol;\r
+    // fallthrough to sustain\r
+\r
+  //--------------------------------------------------//\r
+  case ADSR_SUSTAIN:                                  // -> sustain\r
+    if (adsr->SustainIncrease && EnvelopeVol >= 0x7fff)\r
+    {\r
+      EnvelopeVol = 0x7fff;\r
+      ns = ns_to;\r
+      break;\r
+    }\r
+    if (adsr->SustainRate == 0x7f)\r
+      goto frozen;\r
+    rate = adsr->SustainRate >> 2;\r
+    cycles_step = max(1, 0x8000 >> max(0, rate - 11));\r
+    env_step = (7 - (adsr->SustainRate & 3)) << max(0, 11 - rate);\r
 \r
-     lT-=l1;\r
-     if(lT<l2)\r
+    if (adsr->SustainIncrease)\r
+    {\r
+      if (adsr->SustainModeExp)\r
+        ns = adsr_do_inner(samples, ns, ns_to, &EnvelopeVol, env_step,\r
+               &cycles, cycles_step, do_samples, adsr_expinc);\r
+      else\r
+        ns = adsr_do_inner(samples, ns, ns_to, &EnvelopeVol, env_step,\r
+               &cycles, cycles_step, do_samples, adsr_noexp);\r
+      if (EnvelopeVol >= 0x7fff)\r
       {\r
-       v-=(((v-v2)*lT)/l2);\r
+        EnvelopeVol = 0x7fff;\r
+        ns = ns_to;\r
       }\r
-     else                                              // sustain\r
-      {                                                // no exp mode used (yet)\r
-       l3=s_chan[ch].ADSR.SustainTime;\r
-       lT-=l2;\r
-       if(s_chan[ch].ADSR.SustainModeDec>0)\r
-        {\r
-         if(l3!=0) v2+=((v-v2)*lT)/l3;\r
-         else      v2=v;\r
-        }\r
-       else\r
-        {\r
-         if(l3!=0) v2-=(v2*lT)/l3;\r
-         else      v2=v;\r
-        }\r
-\r
-       if(v2>v)  v2=v;\r
-       if(v2<=0) {v2=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;}\r
-\r
-       v=v2;\r
+    }\r
+    else\r
+    {\r
+      env_step = ~env_step;\r
+      if (adsr->SustainModeExp)\r
+        ns = adsr_do_inner(samples, ns, ns_to, &EnvelopeVol, env_step,\r
+               &cycles, cycles_step, do_samples, adsr_expdec);\r
+      else\r
+        ns = adsr_do_inner(samples, ns, ns_to, &EnvelopeVol, env_step,\r
+               &cycles, cycles_step, do_samples, adsr_noexp);\r
+      if (EnvelopeVol <= 0)\r
+      {\r
+        EnvelopeVol = 0;\r
+        if (do_samples)\r
+          for (; ns < ns_to; ns++)\r
+            samples[ns] = 0;\r
       }\r
     }\r
+    break;\r
+\r
+  //--------------------------------------------------//\r
+  case ADSR_RELEASE:\r
+    if (adsr->ReleaseRate == 0x1f)\r
+      goto frozen;\r
+    cycles_step = max(1, 0x8000 >> max(0, (int)adsr->ReleaseRate - 11));\r
+    env_step = (uint32_t)-8 << max(0, 11 - (int)adsr->ReleaseRate);\r
+\r
+    if (adsr->ReleaseModeExp)\r
+      ns = adsr_do_inner(samples, ns, ns_to, &EnvelopeVol, env_step,\r
+             &cycles, cycles_step, do_samples, adsr_expdec);\r
+    else\r
+      ns = adsr_do_inner(samples, ns, ns_to, &EnvelopeVol, env_step,\r
+             &cycles, cycles_step, do_samples, adsr_noexp);\r
+\r
+    EnvelopeVol = max(0, EnvelopeVol);\r
+    break;\r
   }\r
 \r
- //----------------------------------------------------// \r
- // ok, done for this channel, so increase time\r
-\r
- s_chan[ch].ADSR.lTime+=1;                             // 1 = 1.020408f ms;      \r
-\r
- if(v>1024)     v=1024;                                // adjust volume\r
- if(v<0)        v=0;                                  \r
- s_chan[ch].ADSR.lVolume=v;                            // store act volume\r
-\r
- return v;                                             // return the volume factor\r
-*/\r
-\r
-\r
-//-----------------------------------------------------------------------------\r
-//-----------------------------------------------------------------------------\r
-//-----------------------------------------------------------------------------\r
-\r
-\r
-/*\r
------------------------------------------------------------------------------\r
-Neill Corlett\r
-Playstation SPU envelope timing notes\r
------------------------------------------------------------------------------\r
-\r
-This is preliminary.  This may be wrong.  But the model described herein fits\r
-all of my experimental data, and it's just simple enough to sound right.\r
-\r
-ADSR envelope level ranges from 0x00000000 to 0x7FFFFFFF internally.\r
-The value returned by channel reg 0xC is (envelope_level>>16).\r
-\r
-Each sample, an increment or decrement value will be added to or\r
-subtracted from this envelope level.\r
+  adsr->EnvelopeVol = EnvelopeVol;\r
+  adsr->StepCounter = cycles;\r
+  return ns;\r
 \r
-Create the rate log table.  The values double every 4 entries.\r
-   entry #0 = 4\r
-\r
-    4, 5, 6, 7,\r
-    8,10,12,14,\r
-   16,20,24,28, ...\r
-\r
-   entry #40 = 4096...\r
-   entry #44 = 8192...\r
-   entry #48 = 16384...\r
-   entry #52 = 32768...\r
-   entry #56 = 65536...\r
-\r
-increments and decrements are in terms of ratelogtable[n]\r
-n may exceed the table bounds (plan on n being between -32 and 127).\r
-table values are all clipped between 0x00000000 and 0x3FFFFFFF\r
-\r
-when you "voice on", the envelope is always fully reset.\r
-(yes, it may click. the real thing does this too.)\r
-\r
-envelope level begins at zero.\r
-\r
-each state happens for at least 1 cycle\r
-(transitions are not instantaneous)\r
-this may result in some oddness: if the decay rate is uberfast, it will cut\r
-the envelope from full down to half in one sample, potentially skipping over\r
-the sustain level\r
-\r
-ATTACK\r
-------\r
-- if the envelope level has overflowed past the max, clip to 0x7FFFFFFF and\r
-  proceed to DECAY.\r
-\r
-Linear attack mode:\r
-- line extends upward to 0x7FFFFFFF\r
-- increment per sample is ratelogtable[(Ar^0x7F)-0x10]\r
-\r
-Logarithmic attack mode:\r
-if envelope_level < 0x60000000:\r
-  - line extends upward to 0x60000000\r
-  - increment per sample is ratelogtable[(Ar^0x7F)-0x10]\r
-else:\r
-  - line extends upward to 0x7FFFFFFF\r
-  - increment per sample is ratelogtable[(Ar^0x7F)-0x18]\r
-\r
-DECAY\r
------\r
-- if ((envelope_level>>27)&0xF) <= Sl, proceed to SUSTAIN.\r
-  Do not clip to the sustain level.\r
-- current line ends at (envelope_level & 0x07FFFFFF)\r
-- decrement per sample depends on (envelope_level>>28)&0x7\r
-  0: ratelogtable[(4*(Dr^0x1F))-0x18+0]\r
-  1: ratelogtable[(4*(Dr^0x1F))-0x18+4]\r
-  2: ratelogtable[(4*(Dr^0x1F))-0x18+6]\r
-  3: ratelogtable[(4*(Dr^0x1F))-0x18+8]\r
-  4: ratelogtable[(4*(Dr^0x1F))-0x18+9]\r
-  5: ratelogtable[(4*(Dr^0x1F))-0x18+10]\r
-  6: ratelogtable[(4*(Dr^0x1F))-0x18+11]\r
-  7: ratelogtable[(4*(Dr^0x1F))-0x18+12]\r
-  (note that this is the same as the release rate formula, except that\r
-   decay rates 10-1F aren't possible... those would be slower in theory)\r
-\r
-SUSTAIN\r
--------\r
-- no terminating condition except for voice off\r
-- Sd=0 (increase) behavior is identical to ATTACK for both log and linear.\r
-- Sd=1 (decrease) behavior:\r
-Linear sustain decrease:\r
-- line extends to 0x00000000\r
-- decrement per sample is ratelogtable[(Sr^0x7F)-0x0F]\r
-Logarithmic sustain decrease:\r
-- current line ends at (envelope_level & 0x07FFFFFF)\r
-- decrement per sample depends on (envelope_level>>28)&0x7\r
-  0: ratelogtable[(Sr^0x7F)-0x1B+0]\r
-  1: ratelogtable[(Sr^0x7F)-0x1B+4]\r
-  2: ratelogtable[(Sr^0x7F)-0x1B+6]\r
-  3: ratelogtable[(Sr^0x7F)-0x1B+8]\r
-  4: ratelogtable[(Sr^0x7F)-0x1B+9]\r
-  5: ratelogtable[(Sr^0x7F)-0x1B+10]\r
-  6: ratelogtable[(Sr^0x7F)-0x1B+11]\r
-  7: ratelogtable[(Sr^0x7F)-0x1B+12]\r
-\r
-RELEASE\r
--------\r
-- if the envelope level has overflowed to negative, clip to 0 and QUIT.\r
+frozen:\r
+  if (EnvelopeVol < 0x7ff0 && do_samples)\r
+  {\r
+    for (; ns < ns_to; ns++)\r
+      samples[ns] = samples[ns] * EnvelopeVol >> 15;\r
+  }\r
+  return ns_to;\r
+}\r
 \r
-Linear release mode:\r
-- line extends to 0x00000000\r
-- decrement per sample is ratelogtable[(4*(Rr^0x1F))-0x0C]\r
+static int MixADSR(int *samples, ADSRInfoEx *adsr, int ns_to)\r
+{\r
+  return adsr_do(samples, adsr, ns_to, 1);\r
+}\r
 \r
-Logarithmic release mode:\r
-- line extends to (envelope_level & 0x0FFFFFFF)\r
-- decrement per sample depends on (envelope_level>>28)&0x7\r
-  0: ratelogtable[(4*(Rr^0x1F))-0x18+0]\r
-  1: ratelogtable[(4*(Rr^0x1F))-0x18+4]\r
-  2: ratelogtable[(4*(Rr^0x1F))-0x18+6]\r
-  3: ratelogtable[(4*(Rr^0x1F))-0x18+8]\r
-  4: ratelogtable[(4*(Rr^0x1F))-0x18+9]\r
-  5: ratelogtable[(4*(Rr^0x1F))-0x18+10]\r
-  6: ratelogtable[(4*(Rr^0x1F))-0x18+11]\r
-  7: ratelogtable[(4*(Rr^0x1F))-0x18+12]\r
+static int SkipADSR(ADSRInfoEx *adsr, int ns_to)\r
+{\r
+  return adsr_do(NULL, adsr, ns_to, 0);\r
+}\r
 \r
------------------------------------------------------------------------------\r
-*/\r
+#endif\r
 \r
-// vim:shiftwidth=1:expandtab\r
+// vim:shiftwidth=2:expandtab\r
index 1ccd605..3303853 100644 (file)
 \r
 #ifdef __GNUC__\r
 #define noinline __attribute__((noinline))\r
+#define forceinline __attribute__((always_inline))\r
 #define unlikely(x) __builtin_expect((x), 0)\r
 #else\r
 #define noinline\r
+#define forceinline\r
 #define unlikely(x) x\r
 #endif\r
 #if defined(__GNUC__) && !defined(_TMS320C6X)\r
@@ -85,6 +87,7 @@ typedef struct
  unsigned char  SustainRate;\r
  unsigned char  ReleaseRate;\r
  int            EnvelopeVol;\r
+ int            StepCounter;   // 0 -> 32k\r
 } ADSRInfoEx;\r
               \r
 ///////////////////////////////////////////////////////////\r
index 2b50c1f..13ee2a5 100644 (file)
@@ -61,7 +61,7 @@ typedef struct
  int            ReleaseRate;\r
  int            EnvelopeVol;\r
  int            lVolume;\r
- int            lDummy1;\r
+ int            StepCounter;\r
  int            lDummy2;\r
 } ADSRInfoEx_orig;\r
 \r
@@ -187,7 +187,8 @@ static void save_channel(SPUCHAN_orig *d, const SPUCHAN *s, int ch)
  d->ADSRX.SustainRate = s->ADSRX.SustainRate;\r
  d->ADSRX.ReleaseModeExp = s->ADSRX.ReleaseModeExp;\r
  d->ADSRX.ReleaseRate = s->ADSRX.ReleaseRate;\r
- d->ADSRX.EnvelopeVol = s->ADSRX.EnvelopeVol;\r
+ d->ADSRX.EnvelopeVol = (uint32_t)s->ADSRX.EnvelopeVol << 16;\r
+ d->ADSRX.StepCounter = s->ADSRX.StepCounter;\r
  d->ADSRX.lVolume = d->bOn; // hmh\r
 }\r
 \r
@@ -222,7 +223,8 @@ static void load_channel(SPUCHAN *d, const SPUCHAN_orig *s, int ch)
  d->ADSRX.SustainRate = s->ADSRX.SustainRate;\r
  d->ADSRX.ReleaseModeExp = s->ADSRX.ReleaseModeExp;\r
  d->ADSRX.ReleaseRate = s->ADSRX.ReleaseRate;\r
- d->ADSRX.EnvelopeVol = s->ADSRX.EnvelopeVol;\r
+ d->ADSRX.EnvelopeVol = s->ADSRX.EnvelopeVol >> 16;\r
+ d->ADSRX.StepCounter = s->ADSRX.StepCounter;\r
  if (s->bOn) spu.dwChannelsAudible |= 1<<ch;\r
  else d->ADSRX.EnvelopeVol = 0;\r
 }\r
index 5565317..8e33d7e 100644 (file)
@@ -341,11 +341,11 @@ unsigned short CALLBACK SPUreadRegister(unsigned long reg, unsigned int cycles)
       {\r
        // this used to return 1 immediately after keyon to deal with\r
        // some poor timing, but that causes Rayman 2 to lose track of\r
-       // it's channels on busy scenes and start looping some of them forever\r
+       // its channels on busy scenes and start looping some of them forever\r
        const int ch = (r>>4) - 0xc0;\r
        if (spu.s_chan[ch].bStarting)\r
         do_samples_if_needed(cycles, 0, 2);\r
-       return (unsigned short)(spu.s_chan[ch].ADSRX.EnvelopeVol >> 16);\r
+       return spu.s_chan[ch].ADSRX.EnvelopeVol;\r
       }\r
 \r
      case 14:                                          // get loop address\r
@@ -448,6 +448,7 @@ static void SoundOff(int start,int end,unsigned short val)
    if(val&1)\r
     {\r
      spu.s_chan[ch].ADSRX.State = ADSR_RELEASE;\r
+     spu.s_chan[ch].ADSRX.StepCounter = 0;\r
 \r
      // Jungle Book - Rhythm 'n Groove\r
      // - turns off buzzing sound (loop hangs)\r
index c14f646..51b970e 100644 (file)
@@ -1592,8 +1592,6 @@ long CALLBACK SPUinit(void)
  // a guard for runaway channels - End+Mute
  spu.spuMemC[512 * 1024 + 1] = 1;
 
- InitADSR();
-
  spu.s_chan = calloc(MAXCHAN+1, sizeof(spu.s_chan[0])); // channel + 1 infos (1 is security for fmod handling)
  spu.rvb = calloc(1, sizeof(REVERBInfo));