/***************************************************************************
adsr.c  -  description
-------------------
begin                : Wed May 15 2002
copyright            : (C) 2002 by Pete Bernert
email                : BlackDove@addcom.de
***************************************************************************/

/***************************************************************************
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version. See also the license.txt file for *
*   additional informations.                                              *
*                                                                         *
***************************************************************************/

//*************************************************************************//
// History of changes:
//
// 2004/09/18 - LDChen
// - Speed optimized ADSR mixing
//
// 2003/05/14 - xodnizel
// - removed stopping of reverb on sample end
//
// 2003/01/06 - Pete
// - added Neill's ADSR timings
//
// 2002/05/15 - Pete
// - generic cleanup for the Peops release
//
//*************************************************************************//

#include "stdafx.h"

#define _IN_ADSR

// will be included from spu.c
#ifdef _IN_SPU


#include <stdio.h>
extern FILE *fp_spu_log;


//#define SPU_LOG
extern int spu_ch;



////////////////////////////////////////////////////////////////////////
// ADSR func
////////////////////////////////////////////////////////////////////////

/*
ADSR
- Dr. Hell (Xebra PS1 emu)
- http://drhell.web.fc2.com

	
Envelope increase
0-47: (7 - (RATE & 3)) <<(11 - (RATE>> 2))
48+:  7 - (RATE & 3) / (1 <<((RATE>> 2) - 11))
		
Envelope decrease
0-47: (-8 + (RATE & 3)) <<(11 - (RATE>> 2))
48+:  -8 + (RATE & 3) / (1 <<((RATE>> 2) - 11))
			
				
Exponential increase
0000-5FFF = (rate + 0)
6000+     = (rate + 8)
					
Exponential decrease
(molecules (decrease) * level)>> 15
						
-----------------------------------
							
Fraction (release rate)
1<<((4*32>>2)-11) = 1<<21
								

	// RE3 - 60-14-67-13
									
Increase
40 = (7-0)<<(11-10) = 7<<1 = 14
41 = (7-1)<<(11-10) = 6<<1 = 12
42 = (7-2)<<(11-10) = 5<<1 = 10
43 = (7-3)<<(11-10) = 4<<1 = 8
										
44 = (7-0)<<(11-11) = 7<<0 = 7
45 = (7-1)<<(11-11) = 6<<0 = 6
46 = (7-2)<<(11-11) = 5<<0 = 5
47 = (7-3)<<(11-11) = 4<<0 = 4
--
48 = (7-0) / 1<<(12-11) = 7 / 2
49 = (7-1) / 1<<(12-11) = 6 / 2
50 = (7-2) / 1<<(12-11) = 5 / 2
51 = (7-3) / 1<<(12-11) = 4 / 2
											
52 = (7-0) / 1<<(13-11) = 7 / 4
56 = (7-0) / 1<<(14-11) = 7 / 8
60 = (7-0) / 1<<(15-11) = 7 / 16
												
													
Decrease
40 = (-8+0)<<(11-10) = -8<<1 = -16
41 = (-8+1)<<(11-10) = -7<<1 = -14
42 = (-8+2)<<(11-10) = -6<<1 = -12
43 = (-8+3)<<(11-10) = -5<<1 = -10
														
44 = (-8+0)<<(11-11) = -8<<0 = -8
45 = (-8+1)<<(11-11) = -7<<0 = -7
46 = (-8+2)<<(11-11) = -6<<0 = -6
47 = (-8+3)<<(11-11) = -5<<0 = -5
--
48 = (-8+0) / 1<<(12-11) = -8 / 2
49 = (-8+1) / 1<<(12-11) = -7 / 2
50 = (-8+2) / 1<<(12-11) = -6 / 2
51 = (-8+3) / 1<<(12-11) = -5 / 2

52 = (-8+0) / 1<<(13-11) = -8 / 4
53 = (-8+1) / 1<<(13-11) = -7 / 4
54 = (-8+2) / 1<<(13-11) = -6 / 4
55 = (-8+3) / 1<<(13-11) = -5 / 4
*/

#ifdef VIOPSF_ADSR_REMOVED
static int RateTableAdd[128];
static int RateTableSub[128];
static int RateTable_denom[128];
#else
static int RateTable[32 + 128];
static const int ADSR_MAX_VOL = 0x7fffffff;
static const int InvExpOffsets[] = { 0, 4, 6, 8, 9, 10, 11, 12 };
#endif

void InitADSR(void)                                    // INIT ADSR
{
	int lcv;
	
#ifdef VIOPSF_ADSR_REMOVED
	memset(RateTableAdd,0,sizeof(int)*128);
	memset(RateTableSub,0,sizeof(int)*128);
#else
	memset(RateTable, 0, sizeof(int) * (32+128));
#endif

#ifdef VIOPSF_ADSR_REMOVED
	// Optimize table - Dr. Hell ADSR math
	for( lcv=0; lcv<48; lcv++ ) {
		RateTableAdd[lcv] = (7 - (lcv&3)) << (11 - (lcv >> 2));
		RateTableSub[lcv] = (-8 + (lcv&3)) << (11 - (lcv >> 2));


		// sampling time
		RateTable_denom[lcv] = 1;
	}


	for( lcv=48; lcv<128; lcv++ ) {
		// sampling time
		RateTable_denom[lcv] = 1 << ((lcv>>2) - 11);


		RateTableAdd[lcv] = 7 - (lcv&3);
		RateTableSub[lcv] = -8 + (lcv&3);
	}
#else
	//From Highly Experimental
	RateTable[32 - 8] = 1;
	RateTable[32 - 7] = 1;
	RateTable[32 - 6] = 1;
	RateTable[32 - 5] = 1;
	RateTable[32 - 4] = 2;
	RateTable[32 - 3] = 2;
	RateTable[32 - 2] = 3;
	RateTable[32 - 1] = 3;
	RateTable[32 + 0] = 4;
	RateTable[32 + 1] = 5;
	RateTable[32 + 2] = 6;
	RateTable[32 + 3] = 7;
	for (int lcv = 4; lcv < 128; lcv++)
	{
		unsigned int n = 2 * RateTable[32 + lcv - 4];
		if (n > 0x3fffffff) n = 0x3fffffff;
		RateTable[32 + lcv] = n;
	}
#endif
}

////////////////////////////////////////////////////////////////////////

INLINE void StartADSR(SPUCHAN * pChannel)              // MIX ADSR
{
	pChannel->ADSRX.lVolume=1;                            // and init some adsr vars
	pChannel->ADSRX.State=0;
	pChannel->ADSRX.EnvelopeVol=0;
	pChannel->ADSRX.EnvelopeVol_f=0;
}

////////////////////////////////////////////////////////////////////////

INLINE int MixADSR(SPUCHAN *ch)
{    
	int rate;

#ifdef VIOPSF_ADSR_REMOVED
	int EnvelopeVol = ch->ADSRX.EnvelopeVol;
	int EnvelopeVol_f = ch->ADSRX.EnvelopeVol_f;
#else
	long long int EnvelopeVol = ch->ADSRX.EnvelopeVol;
#endif
	
	
	if(ch->bStop)                                  // should be stopped:
  {                                                    // do release
		// debug
		ch->ADSRX.State = 3;


		// OPTIMIZE: fast finish
#ifdef VIOPSF_ADSR_REMOVED
		if( EnvelopeVol == 0 && EnvelopeVol_f == 0 ) {
#else
		if (EnvelopeVol == 0 ) {
#endif
			return 0;
		}

#ifdef VIOPSF_ADSR_REMOVED
		rate = ch->ADSRX.ReleaseRate * 4;
#else
		rate = ch->ADSRX.ReleaseRate;
#endif

#ifdef VIOPSF_ADSR_REMOVED
		EnvelopeVol_f++;
		if( EnvelopeVol_f >= RateTable_denom[ rate ] ) {
			EnvelopeVol_f = 0;

			if(ch->ADSRX.ReleaseModeExp) {
				EnvelopeVol += ( RateTableSub[ rate ] * EnvelopeVol ) >> 15;
			}
			else
				EnvelopeVol += RateTableSub[ rate ];
		}
#else
		if (ch->ADSRX.ReleaseModeExp) {
			int off = InvExpOffsets[(EnvelopeVol >> 28) & 7];
			EnvelopeVol -= RateTable[((rate ^ 0x1f) * 4) - 0x18 + off + 32];
		}
		else {
			EnvelopeVol -= RateTable[((rate ^ 0x1f) * 4) - 0xc + 32];
		}
#endif

		if(EnvelopeVol <= 0) 
    {
			EnvelopeVol = 0;
#ifdef VIOPSF_ADSR_REMOVED
			EnvelopeVol_f = 0;
#endif


			/*
			Misadventures of Tron Bonne
			- notaz: allow channel to keep playing (IRQ generation)
			*/
			//ch->bOn=0;
			ch->iSilent = 2;
		}

		ch->ADSRX.EnvelopeVol=EnvelopeVol;
#ifdef VIOPSF_ADSR_REMOVED
		ch->ADSRX.EnvelopeVol_f=EnvelopeVol_f;
		ch->ADSRX.lVolume=EnvelopeVol>>0;
		return EnvelopeVol>>0;
#else
		ch->ADSRX.lVolume = (long)(EnvelopeVol >> 16);
		return int (EnvelopeVol >> 16);
#endif
  }
	else                                                  // not stopped yet?
  {
		if(ch->ADSRX.State==0)                       // -> attack
    {
			rate = ch->ADSRX.AttackRate;

#ifdef VIOPSF_ADSR_REMOVED
			if(ch->ADSRX.AttackModeExp && EnvelopeVol>=0x6000)
				rate += 8;


			EnvelopeVol_f++;
			if( EnvelopeVol_f >= RateTable_denom[ rate ] ) {
				EnvelopeVol_f = 0;

				EnvelopeVol += RateTableAdd[ rate ];
			}
#else
			if (ch->ADSRX.AttackModeExp && EnvelopeVol >= 0x60000000)
				EnvelopeVol += RateTable[(rate ^ 0x7f) - 0x18 + 32];
			else
				EnvelopeVol += RateTable[(rate ^ 0x7f) - 0x10 + 32];
#endif

			
#ifdef SPU_LOG
			if( !fp_spu_log ){
				fp_spu_log = fopen( "spu-log.txt", "w" );
			}
			//if( spu_ch+1 >= 27 )
			fprintf( fp_spu_log, "ADSR-A %d = %X [%X] [%X]\n", spu_ch+1, EnvelopeVol, ch->ADSRX.AttackRate, ch->ADSRX.SustainLevel );
#endif


#ifdef VIOPSF_ADSR_REMOVED
			if(EnvelopeVol>0x7fff) 
#else
			if (EnvelopeVol>ADSR_MAX_VOL)
#endif
      {
#ifdef SPU_LOG
				if( !fp_spu_log ){
					fp_spu_log = fopen( "spu-log.txt", "w" );
				}
				//if( spu_ch+1 >= 27 )
				
				fprintf( fp_spu_log, "ADSR-A-D\n" );
#endif
				
#ifdef VIOPSF_ADSR_REMOVED
				EnvelopeVol = 0x7fff;
				EnvelopeVol_f = 0;
#else
				EnvelopeVol = ADSR_MAX_VOL;
#endif
				ch->ADSRX.State = 1;
			}
			
			ch->ADSRX.EnvelopeVol=EnvelopeVol;
#ifdef VIOPSF_ADSR_REMOVED
			ch->ADSRX.EnvelopeVol_f=EnvelopeVol_f;
			ch->ADSRX.lVolume=EnvelopeVol>>0;
			return EnvelopeVol>>0;
#else
			ch->ADSRX.lVolume = (long)(EnvelopeVol >> 16);
			return int(EnvelopeVol >> 16);
#endif
    }
		//--------------------------------------------------//
		if(ch->ADSRX.State==1)                       // -> decay
    {
#ifdef VIOPSF_ADSR_REMOVED
			rate = ch->ADSRX.DecayRate * 4;


			EnvelopeVol_f++;
			if( EnvelopeVol_f >= RateTable_denom[ rate ] ) {
				EnvelopeVol_f = 0;

				EnvelopeVol += ( RateTableSub[ rate ] * EnvelopeVol ) >> 15;
			}
#else
		rate = ch->ADSRX.DecayRate;
		int off = InvExpOffsets[(EnvelopeVol >> 28) & 7];
		EnvelopeVol -= RateTable[((rate ^ 0x1f) * 4) - 0x18 + off + 32];
#endif
				
#ifdef SPU_LOG
			if( !fp_spu_log ){
				fp_spu_log = fopen( "spu-log.txt", "w" );
			}
			//if( spu_ch+1 >= 27 )
			fprintf( fp_spu_log, "ADSR-D %d = %X [%X] [%X]\n", spu_ch+1, EnvelopeVol, ch->ADSRX.DecayRate, ch->ADSRX.SustainLevel );
#endif



			// FF7 cursor - use Neill's 4-bit accuracy
#ifdef VIOPSF_ADSR_REMOVED
			if( ((EnvelopeVol>>11)&0xf) <= ch->ADSRX.SustainLevel)
#else
			if (((EnvelopeVol >> 27) & 0xf) <= ch->ADSRX.SustainLevel)
#endif
      {
				ch->ADSRX.State=2;

				
				
#ifdef SPU_LOG
				if( !fp_spu_log ){
					fp_spu_log = fopen( "spu-log.txt", "w" );
				}
				//if( spu_ch+1 >= 27 )
				fprintf( fp_spu_log, "ADSR-D-S\n" );
#endif
			}
			
			ch->ADSRX.EnvelopeVol=EnvelopeVol;
#ifdef VIOPSF_ADSR_REMOVED
			ch->ADSRX.EnvelopeVol_f=EnvelopeVol_f;
			ch->ADSRX.lVolume=EnvelopeVol>>0;
			return EnvelopeVol>>0;
#else
			ch->ADSRX.lVolume = (long)(EnvelopeVol >> 16);
			return int(EnvelopeVol >> 16);
#endif
    }
		//--------------------------------------------------//
		if(ch->ADSRX.State==2)                       // -> sustain
    {
			if(ch->ADSRX.SustainIncrease)
      {
				// OPTIMIZE: fast finish
#ifdef VIOPSF_ADSR_REMOVED
				if( EnvelopeVol == 0x7fff && EnvelopeVol_f == 0 )
					return 0x7fff;
#else
		  if (EnvelopeVol == ADSR_MAX_VOL)
			  return (int)(ADSR_MAX_VOL >> 16);
#endif

				rate = ch->ADSRX.SustainRate;

#ifdef VIOPSF_ADSR_REMOVED
				if( ch->ADSRX.SustainModeExp && EnvelopeVol>=0x6000 )
					rate += 8;


				EnvelopeVol_f++;
				if( EnvelopeVol_f >= RateTable_denom[ rate ] ) {
					EnvelopeVol_f = 0;

					EnvelopeVol += RateTableAdd[ rate ];
				}
#else
				if(ch->ADSRX.SustainModeExp && EnvelopeVol >= 0x60000000)
					EnvelopeVol += RateTable[(rate ^ 0x7f) - 0x18 + 32];
				else
					EnvelopeVol += RateTable[(rate ^ 0x7f) - 0x10 + 32];
#endif

#ifdef VIOPSF_ADSR_REMOVED
				if(EnvelopeVol > 0x7fff) {
					EnvelopeVol = 0x7fff;
					EnvelopeVol_f = 0;
        }
#else
				if (EnvelopeVol > ADSR_MAX_VOL) {
					EnvelopeVol = ADSR_MAX_VOL;
				}
#endif
      }
			else
      {
				// OPTIMIZE: fast finish
#ifdef VIOPSF_ADSR_REMOVED
				if( EnvelopeVol == 0 && EnvelopeVol_f == 0 )
#else
		  if (EnvelopeVol == 0)
#endif
					return 0;


				rate = ch->ADSRX.SustainRate;
				
#ifdef VIOPSF_ADSR_REMOVED
				EnvelopeVol_f++;
				if( EnvelopeVol_f >= RateTable_denom[ rate ] ) {
					EnvelopeVol_f = 0;


					if( ch->ADSRX.SustainModeExp ) {
						EnvelopeVol += ( RateTableSub[ rate ] * EnvelopeVol ) >> 15;
					}
					else
						EnvelopeVol += RateTableSub[ rate ];
				}
#else
				if (ch->ADSRX.SustainModeExp) {
					int off = InvExpOffsets[(EnvelopeVol >> 28) & 7];
					EnvelopeVol -= RateTable[(rate ^ 0x7f) - 0x1b + off + 32];
				} else
					EnvelopeVol -= RateTable[(rate ^ 0x7f) - 0xf + 32];
#endif
				
				if(EnvelopeVol < 0) {
					EnvelopeVol = 0;
#ifdef VIOPSF_ADSR_REMOVED
					EnvelopeVol_f = 0;
#endif
        }
      }
			
			
#ifdef SPU_LOG
			if( !fp_spu_log ){
				fp_spu_log = fopen( "spu-log.txt", "w" );
			}
			//if( spu_ch+1 >= 27 )
			fprintf( fp_spu_log, "ADSR-S %d = %X [%X] [%X]\n", spu_ch+1, EnvelopeVol, ch->ADSRX.SustainRate, ch->ADSRX.SustainLevel );
#endif
			
			
			ch->ADSRX.EnvelopeVol=EnvelopeVol;
#ifdef VIOPSF_ADSR_REMOVED
			ch->ADSRX.EnvelopeVol_f=EnvelopeVol_f;
			ch->ADSRX.lVolume=EnvelopeVol>>0;
			return EnvelopeVol>>0;
#else
			ch->ADSRX.lVolume = (long)(EnvelopeVol >> 16);
			return int(EnvelopeVol >> 16);
#endif
    }
  }
	return 0;
}

#endif

/*
James Higgs ADSR investigations:

	PSX SPU Envelope Timings
	~~~~~~~~~~~~~~~~~~~~~~~~
	
		First, here is an extract from doomed's SPU doc, which explains the basics
		of the SPU "volume envelope": 
		
			*** doomed doc extract start ***
			
				--------------------------------------------------------------------------
				Voices.
				--------------------------------------------------------------------------
				The SPU has 24 hardware voices. These voices can be used to reproduce sample
				data, noise or can be used as frequency modulator on the next voice.
				Each voice has it's own programmable ADSR envelope filter. The main volume
				can be programmed independently for left and right output.
				
					The ADSR envelope filter works as follows:
					Ar = Attack rate, which specifies the speed at which the volume increases
					from zero to it's maximum value, as soon as the note on is given. The
					slope can be set to lineair or exponential.
					Dr = Decay rate specifies the speed at which the volume decreases to the
					sustain level. Decay is always decreasing exponentially.
					Sl = Sustain level, base level from which sustain starts.
					Sr = Sustain rate is the rate at which the volume of the sustained note
					increases or decreases. This can be either lineair or exponential.
					Rr = Release rate is the rate at which the volume of the note decreases
					as soon as the note off is given.
					
						lvl |
						^ |     /\Dr     __
						Sl _| _  / _ \__---  \
						|   /       ---__ \ Rr
						|  /Ar       Sr  \ \
						| /                \\
						|/___________________\________
						->time
						
							The overal volume can also be set to sweep up or down lineairly or
							exponentially from it's current value. This can be done seperately
							for left and right.
							
								Relevant SPU registers:
								-------------------------------------------------------------
								$1f801xx8         Attack/Decay/Sustain level
								bit  |0f|0e 0d 0c 0b 0a 09 08|07 06 05 04|03 02 01 00|
								desc.|Am|         Ar         |Dr         |Sl         |
								
									Am       0        Attack mode Linear
									1                    Exponential
									
										Ar       0-7f     attack rate
										Dr       0-f      decay rate
										Sl       0-f      sustain level
										-------------------------------------------------------------
										$1f801xxa         Sustain rate, Release Rate.
										bit  |0f|0e|0d|0c 0b 0a 09 08 07 06|05|04 03 02 01 00|
										desc.|Sm|Sd| 0|   Sr               |Rm|Rr            |
										
											Sm       0        sustain rate mode linear
											1                          exponential
											Sd       0        sustain rate mode increase
											1                          decrease
											Sr       0-7f     Sustain Rate
											Rm       0        Linear decrease
											1        Exponential decrease
											Rr       0-1f     Release Rate
											
												Note: decay mode is always Expontial decrease, and thus cannot
												be set.
												-------------------------------------------------------------
												$1f801xxc         Current ADSR volume
												bit  |0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00|
												desc.|ADSRvol                                        |
												
													ADSRvol           Returns the current envelope volume when
													read.
													-- James' Note: return range: 0 -> 32767
													
														*** doomed doc extract end *** 
														
															By using a small PSX proggie to visualise the envelope as it was played,
															the following results for envelope timing were obtained:
															
																1. Attack rate value (linear mode)
																
																	Attack value range: 0 -> 127
																	
																		Value  | 48 | 52 | 56 | 60 | 64 | 68 | 72 |    | 80 |
																		-----------------------------------------------------------------
																		Frames | 11 | 21 | 42 | 84 | 169| 338| 676|    |2890|
																		
																			Note: frames is no. of PAL frames to reach full volume (100%
																			amplitude)
																			
																				Hmm, noticing that the time taken to reach full volume doubles
																				every time we add 4 to our attack value, we know the equation is
																				of form:
																				frames = k * 2 ^ (value / 4)
																				
																					(You may ponder about envelope generator hardware at this point,
																					or maybe not... :)
																					
																						By substituting some stuff and running some checks, we get:
																						
																							k = 0.00257              (close enuf)
																							
																								therefore,
																								frames = 0.00257 * 2 ^ (value / 4)
																								If you just happen to be writing an emulator, then you can probably
																								use an equation like:
																								
																									%volume_increase_per_tick = 1 / frames
																									
																										
																											------------------------------------
																											Pete:
																											ms=((1<<(value>>2))*514)/10000
																											------------------------------------
																											
																												2. Decay rate value (only has log mode)
																												
																													Decay value range: 0 -> 15
																													
																														Value  |  8 |  9 | 10 | 11 | 12 | 13 | 14 | 15 |
																														------------------------------------------------
																														frames |    |    |    |    |  6 | 12 | 24 | 47 |
																														
																															Note: frames here is no. of PAL frames to decay to 50% volume.
																															
																																formula: frames = k * 2 ^ (value)
																																
																																	Substituting, we get: k = 0.00146
																																	
																																		Further info on logarithmic nature:
																																		frames to decay to sustain level 3  =  3 * frames to decay to 
																																		sustain level 9
																																		
																																			Also no. of frames to 25% volume = roughly 1.85 * no. of frames to
																																			50% volume.
																																			
																																				Frag it - just use linear approx.
																																				
																																					------------------------------------
																																					Pete:
																																					ms=((1<<value)*292)/10000
																																					------------------------------------
																																					
																																						
																																							3. Sustain rate value (linear mode)
																																							
																																								Sustain rate range: 0 -> 127
																																								
																																									Value  | 48 | 52 | 56 | 60 | 64 | 68 | 72 |
																																									-------------------------------------------
																																									frames |  9 | 19 | 37 | 74 | 147| 293| 587|
																																									
																																										Here, frames = no. of PAL frames for volume amplitude to go from 100%
																																										to 0% (or vice-versa).
																																										
																																											Same formula as for attack value, just a different value for k:
																																											
																																												k = 0.00225
																																												
																																													ie: frames = 0.00225 * 2 ^ (value / 4)
																																													
																																														For emulation purposes:
																																														
																																															%volume_increase_or_decrease_per_tick = 1 / frames
																																															
																																																------------------------------------
																																																Pete:
																																																ms=((1<<(value>>2))*450)/10000
																																																------------------------------------
																																																
																																																	
																																																		4. Release rate (linear mode)
																																																		
																																																			Release rate range: 0 -> 31
																																																			
																																																				Value  | 13 | 14 | 15 | 16 | 17 |
																																																				---------------------------------------------------------------
																																																				frames | 18 | 36 | 73 | 146| 292|
																																																				
																																																					Here, frames = no. of PAL frames to decay from 100% vol to 0% vol
																																																					after "note-off" is triggered.
																																																					
																																																						Formula: frames = k * 2 ^ (value)
																																																						
																																																							And so: k = 0.00223
																																																							
																																																								------------------------------------
																																																								Pete:
																																																								ms=((1<<value)*446)/10000
																																																								------------------------------------
																																																								
																																																									
																																																										Other notes:   
																																																										
																																																											Log stuff not figured out. You may get some clues from the "Decay rate"
																																																											stuff above. For emu purposes it may not be important - use linear
																																																											approx.
																																																											
																																																												To get timings in millisecs, multiply frames by 20.
																																																												
																																																													
																																																														
																																																															- James Higgs 17/6/2000
																																																															james7780@yahoo.com
																																																															
																																																																//---------------------------------------------------------------
																																																																
																																																																	OLD adsr mixing according to james' rules... has to be called
																																																																	every one millisecond
																																																																	
																																																																		
																																																																			long v,v2,lT,l1,l2,l3;
																																																																			
																																																																				if(s_chan[ch].bStop)                                  // psx wants to stop? -> release phase
																																																																				{
																																																																				if(s_chan[ch].ADSR.ReleaseVal!=0)                   // -> release not 0: do release (if 0: stop right now)
																																																																				{
																																																																				if(!s_chan[ch].ADSR.ReleaseVol)                   // --> release just started? set up the release stuff
																																																																				{
																																																																				s_chan[ch].ADSR.ReleaseStartTime=s_chan[ch].ADSR.lTime;
																																																																				s_chan[ch].ADSR.ReleaseVol=s_chan[ch].ADSR.lVolume;
																																																																				s_chan[ch].ADSR.ReleaseTime =                   // --> calc how long does it take to reach the wanted sus level
																																																																				(s_chan[ch].ADSR.ReleaseTime*
																																																																				s_chan[ch].ADSR.ReleaseVol)/1024;
																																																																				}
																																																																				// -> NO release exp mode used (yet)
																																																																				v=s_chan[ch].ADSR.ReleaseVol;                     // -> get last volume
																																																																				lT=s_chan[ch].ADSR.lTime-                         // -> how much time is past?
																																																																				s_chan[ch].ADSR.ReleaseStartTime;
																																																																				l1=s_chan[ch].ADSR.ReleaseTime;
																																																																				
																																																																					if(lT<l1)                                         // -> we still have to release
																																																																					{
																																																																					v=v-((v*lT)/l1);                                // --> calc new volume
																																																																					}
																																																																					else                                              // -> release is over: now really stop that sample
																																																																					{v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;}
																																																																					}
																																																																					else                                                // -> release IS 0: release at once
																																																																					{
																																																																					v=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;
																																																																					}
																																																																					}
																																																																					else                                               
																																																																					{//--------------------------------------------------// not in release phase:
																																																																					v=1024;
																																																																					lT=s_chan[ch].ADSR.lTime;
																																																																					l1=s_chan[ch].ADSR.AttackTime;
																																																																					
																																																																						if(lT<l1)                                           // attack
																																																																						{                                                  // no exp mode used (yet)
																																																																						//     if(s_chan[ch].ADSR.AttackModeExp)
																																																																						//      {
																																																																						//       v=(v*lT)/l1;
																																																																						//      }
																																																																						//     else
																																																																						{
																																																																						v=(v*lT)/l1;
																																																																						}
																																																																						if(v==0) v=1;
																																																																						}
																																																																						else                                                // decay
																																																																						{                                                  // should be exp, but who cares? ;)
																																																																						l2=s_chan[ch].ADSR.DecayTime;
																																																																						v2=s_chan[ch].ADSR.SustainLevel;
																																																																						
																																																																							lT-=l1;
																																																																							if(lT<l2)
																																																																							{
																																																																							v-=(((v-v2)*lT)/l2);
																																																																							}
																																																																							else                                              // sustain
																																																																							{                                                // no exp mode used (yet)
																																																																							l3=s_chan[ch].ADSR.SustainTime;
																																																																							lT-=l2;
																																																																							if(s_chan[ch].ADSR.SustainModeDec>0)
																																																																							{
																																																																							if(l3!=0) v2+=((v-v2)*lT)/l3;
																																																																							else      v2=v;
																																																																							}
																																																																							else
																																																																							{
																																																																							if(l3!=0) v2-=(v2*lT)/l3;
																																																																							else      v2=v;
																																																																							}
																																																																							
																																																																								if(v2>v)  v2=v;
																																																																								if(v2<=0) {v2=0;s_chan[ch].bOn=0;s_chan[ch].ADSR.ReleaseVol=0;s_chan[ch].bNoise=0;}
																																																																								
																																																																									v=v2;
																																																																									}
																																																																									}
																																																																									}
																																																																									
																																																																										//----------------------------------------------------// 
																																																																										// ok, done for this channel, so increase time
																																																																										
																																																																											s_chan[ch].ADSR.lTime+=1;                             // 1 = 1.020408f ms;      
																																																																											
																																																																												if(v>1024)     v=1024;                                // adjust volume
																																																																												if(v<0)        v=0;                                  
																																																																												s_chan[ch].ADSR.lVolume=v;                            // store act volume
																																																																												
																																																																													return v;                                             // return the volume factor
*/


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


/*
-----------------------------------------------------------------------------
Neill Corlett
Playstation SPU envelope timing notes
-----------------------------------------------------------------------------

	This is preliminary.  This may be wrong.  But the model described herein fits
	all of my experimental data, and it's just simple enough to sound right.
	
		ADSR envelope level ranges from 0x00000000 to 0x7FFFFFFF internally.
		The value returned by channel reg 0xC is (envelope_level>>16).
		
			Each sample, an increment or decrement value will be added to or
			subtracted from this envelope level.
			
				Create the rate log table.  The values double every 4 entries.
				entry #0 = 4
				
					4, 5, 6, 7,
					8,10,12,14,
					16,20,24,28, ...
					
						entry #40 = 4096...
						entry #44 = 8192...
						entry #48 = 16384...
						entry #52 = 32768...
						entry #56 = 65536...
						
							increments and decrements are in terms of ratelogtable[n]
							n may exceed the table bounds (plan on n being between -32 and 127).
							table values are all clipped between 0x00000000 and 0x3FFFFFFF
							
								when you "voice on", the envelope is always fully reset.
								(yes, it may click. the real thing does this too.)
								
									envelope level begins at zero.
									
										each state happens for at least 1 cycle
										(transitions are not instantaneous)
										this may result in some oddness: if the decay rate is uberfast, it will cut
										the envelope from full down to half in one sample, potentially skipping over
										the sustain level
										
											ATTACK
											------
											- if the envelope level has overflowed past the max, clip to 0x7FFFFFFF and
											proceed to DECAY.
											
												Linear attack mode:
												- line extends upward to 0x7FFFFFFF
												- increment per sample is ratelogtable[(Ar^0x7F)-0x10]
												
													Logarithmic attack mode:
													if envelope_level < 0x60000000:
													- line extends upward to 0x60000000
													- increment per sample is ratelogtable[(Ar^0x7F)-0x10]
													else:
													- line extends upward to 0x7FFFFFFF
													- increment per sample is ratelogtable[(Ar^0x7F)-0x18]
													
														DECAY
														-----
														- if ((envelope_level>>27)&0xF) <= Sl, proceed to SUSTAIN.
														Do not clip to the sustain level.
														- current line ends at (envelope_level & 0x07FFFFFF)
														- decrement per sample depends on (envelope_level>>28)&0x7
														0: ratelogtable[(4*(Dr^0x1F))-0x18+0]
														1: ratelogtable[(4*(Dr^0x1F))-0x18+4]
														2: ratelogtable[(4*(Dr^0x1F))-0x18+6]
														3: ratelogtable[(4*(Dr^0x1F))-0x18+8]
														4: ratelogtable[(4*(Dr^0x1F))-0x18+9]
														5: ratelogtable[(4*(Dr^0x1F))-0x18+10]
														6: ratelogtable[(4*(Dr^0x1F))-0x18+11]
														7: ratelogtable[(4*(Dr^0x1F))-0x18+12]
														(note that this is the same as the release rate formula, except that
														decay rates 10-1F aren't possible... those would be slower in theory)
														
															SUSTAIN
															-------
															- no terminating condition except for voice off
															- Sd=0 (increase) behavior is identical to ATTACK for both log and linear.
															- Sd=1 (decrease) behavior:
															Linear sustain decrease:
															- line extends to 0x00000000
															- decrement per sample is ratelogtable[(Sr^0x7F)-0x0F]
															Logarithmic sustain decrease:
															- current line ends at (envelope_level & 0x07FFFFFF)
															- decrement per sample depends on (envelope_level>>28)&0x7
															0: ratelogtable[(Sr^0x7F)-0x1B+0]
															1: ratelogtable[(Sr^0x7F)-0x1B+4]
															2: ratelogtable[(Sr^0x7F)-0x1B+6]
															3: ratelogtable[(Sr^0x7F)-0x1B+8]
															4: ratelogtable[(Sr^0x7F)-0x1B+9]
															5: ratelogtable[(Sr^0x7F)-0x1B+10]
															6: ratelogtable[(Sr^0x7F)-0x1B+11]
															7: ratelogtable[(Sr^0x7F)-0x1B+12]
															
																RELEASE
																-------
																- if the envelope level has overflowed to negative, clip to 0 and QUIT.
																
																	Linear release mode:
																	- line extends to 0x00000000
																	- decrement per sample is ratelogtable[(4*(Rr^0x1F))-0x0C]
																	
																		Logarithmic release mode:
																		- line extends to (envelope_level & 0x0FFFFFFF)
																		- decrement per sample depends on (envelope_level>>28)&0x7
																		0: ratelogtable[(4*(Rr^0x1F))-0x18+0]
																		1: ratelogtable[(4*(Rr^0x1F))-0x18+4]
																		2: ratelogtable[(4*(Rr^0x1F))-0x18+6]
																		3: ratelogtable[(4*(Rr^0x1F))-0x18+8]
																		4: ratelogtable[(4*(Rr^0x1F))-0x18+9]
																		5: ratelogtable[(4*(Rr^0x1F))-0x18+10]
																		6: ratelogtable[(4*(Rr^0x1F))-0x18+11]
																		7: ratelogtable[(4*(Rr^0x1F))-0x18+12]
																		
																			-----------------------------------------------------------------------------
*/
