-/***************************************************************************\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