www.pudn.com > sn068s.zip > SOUND.C


#include  
#include  
 
#include  
 
#define SPC2MHz 
 
#include "helper.h" 
#include "sound.h" 
#include "spc.h" 
 
//#define WIDE_BRR_SAMPLES 
#define NO_PITCH_MODULATION /* do not remove - needs to be fixed */ 
//#define FAULT_ON_PITCH_MODULATION_USE 
 
#define DSP_SPEED_HACK 
 
//#define SET_ENDX_ON_VOICE_OFF 
//#define SET_ENDX_ON_INVALID_BRR 
 
#define ZERO_ENVX_ON_VOICE_OFF 
#define ZERO_OUTX_ON_VOICE_OFF 
 
//#define LOG_SOUND_DSP_READ 
//#define LOG_SOUND_DSP_WRITE 
//#define LOG_SOUND_VOICE_OFF 
 
//#define NO_UPDATE_ON_DSP_READ 
//#define NO_UPDATE_ON_DSP_WRITE 
 
#define MASK_PITCH_H 
 
//#define DISALLOW_ENVX_OUTX_WRITE 
 
#define ATTACK_15_IS_INSTANT 
 
typedef enum { 
 ATTACK, 
 DECAY, 
 SUSTAIN, 
 RELEASE, 
 DECREASE, 
 EXP, 
 INCREASE, 
 BENT, 
 DIRECT 
} ENVSTATE; 
/* 
#define ATTACK 0    // A of ADSR 
#define DECAY 1     // D of ADSR 
#define SUSTAIN 2   // S of ADSR 
#define RELEASE 3   // R of ADSR 
#define DECREASE 4  // GAIN linear decrease mode 
#define EXP 5       // GAIN exponential decrease mode 
#define INCREASE 6  // GAIN linear increase mode 
#define BENT 7      // GAIN bent line increase mode 
#define DIRECT 8    // Directly specify ENVX 
*/ 
 
extern unsigned char SPCRAM[65536]; 
 
unsigned char SPC_MASK; 
unsigned SPC_DSP_DATA; 
signed char sound_enabled; 
int sound_bits; 
 
unsigned sound_cycle_latch; 
unsigned sound_output_position; 
 
static int voice_handle = -1; 
static SAMPLE *sound_buffer = NULL; 
static void *sound_buffer_preload = NULL; 
static output_sample_16 *noise_buffer = NULL; 
static signed char *outx_buffer = NULL;    /* for pitch modulation */ 
static int *mix_buffer = NULL; 
static signed char block_written[2]; 
 
#ifdef ADSR_OPTIONAL 
signed char ADSR_ENABLED = -1; 
#endif 
signed char ENVX_ENABLED = -1; 
 
unsigned char SPC_DSP[256]; 
 
// INTERNAL (static) STUFF 
 
static struct voice_state { 
#ifdef WIDE_BRR_SAMPLES 
    int buf[16]; 
    int last1, last2; 
#else 
    short buf[16]; 
    short last1, last2; 
#endif 
    int envx; 
    unsigned env_cycle_latch, voice_cycle_latch, env_update_time; 
    unsigned ar, dr, sl, sr; 
    int gain_update_time; 
    int lvol, rvol; 
    int out_lvol, out_rvol; 
    int outx; 
    unsigned step; 
    unsigned bufptr; 
    unsigned brrptr; 
    char envstate, gn; 
    unsigned char last_header; 
} SNDvoices[8]; 
 
unsigned char SNDkeys; 
 
/* How many cycles till ADSR/GAIN adjustment 
   Educated guesses on most of these 
 */ 
 
/* 
attack[]*2+1 
0x20000,0x14000,0xC000,0x8000, 
0x5000,0x3000,0x2000,0x1400, 
0xC00,0x800,0x500,0x300, 
0x200,0x140,0xC0,0x40(0.002) 
 
decay[]*2+16 
0x1000,0xA00,0x600,0x400, 
0x280,0x180,0x100,0x80 
 
sustain_gain[]= 
0x0,0x20000,0x18000,0x14000, 
0x10000,0xC000,0xA000,0x8000, 
0x6000,0x5000,0x4000,0x3000, 
0x2800,0x2000,0x1800,0x1400, 
0x1000,0xC00,0xA00,0x800, 
0x600,0x500,0x400,0x300, 
0x280,0x200,0x180,0x140, 
0x100,0xC0,0x80,0x40};    //How many cycles till adjust ADSR/GAIN 
 
 */ 
 
static const unsigned attack_time[16] = 
{ 
 (unsigned) (SPC_CLOCK_HZ * 4.096 / 64), (unsigned) (SPC_CLOCK_HZ * 2.560 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 1.536 / 64), (unsigned) (SPC_CLOCK_HZ * 1.024 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.640 / 64), (unsigned) (SPC_CLOCK_HZ * 0.384 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.256 / 64), (unsigned) (SPC_CLOCK_HZ * 0.160 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.096 / 64), (unsigned) (SPC_CLOCK_HZ * 0.064 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.040 / 64), (unsigned) (SPC_CLOCK_HZ * 0.024 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.016 / 64), (unsigned) (SPC_CLOCK_HZ * 0.010 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.006 / 64), 
#ifdef ATTACK_15_IS_INSTANT 
 0 
#else 
// (unsigned) (SPC_CLOCK_HZ * 0.00025 / 64) 
 (unsigned) (SPC_CLOCK_HZ * 0.002 / 64) 
#endif 
}; 
 
static const unsigned decay_time[16] = 
{ 
 (unsigned) (SPC_CLOCK_HZ * 1.2000 / 600), (unsigned) (SPC_CLOCK_HZ * 0.7500 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 0.4500 / 600), (unsigned) (SPC_CLOCK_HZ * 0.3000 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 0.1875 / 600), (unsigned) (SPC_CLOCK_HZ * 0.1125 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 0.0750 / 600), (unsigned) (SPC_CLOCK_HZ * 0.0375 / 600) 
}; 
 
static const unsigned linear_time[32] = 
{ 
 0 - 1                                 , (unsigned) (SPC_CLOCK_HZ * 4.096 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 3.072 / 64), (unsigned) (SPC_CLOCK_HZ * 2.560 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 2.048 / 64), (unsigned) (SPC_CLOCK_HZ * 1.536 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 1.280 / 64), (unsigned) (SPC_CLOCK_HZ * 1.024 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.768 / 64), (unsigned) (SPC_CLOCK_HZ * 0.640 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.512 / 64), (unsigned) (SPC_CLOCK_HZ * 0.384 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.320 / 64), (unsigned) (SPC_CLOCK_HZ * 0.256 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.192 / 64), (unsigned) (SPC_CLOCK_HZ * 0.160 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.128 / 64), (unsigned) (SPC_CLOCK_HZ * 0.096 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.080 / 64), (unsigned) (SPC_CLOCK_HZ * 0.064 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.048 / 64), (unsigned) (SPC_CLOCK_HZ * 0.040 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.032 / 64), (unsigned) (SPC_CLOCK_HZ * 0.024 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.020 / 64), (unsigned) (SPC_CLOCK_HZ * 0.016 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.012 / 64), (unsigned) (SPC_CLOCK_HZ * 0.010 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.008 / 64), (unsigned) (SPC_CLOCK_HZ * 0.006 / 64), 
 (unsigned) (SPC_CLOCK_HZ * 0.004 / 64), (unsigned) (SPC_CLOCK_HZ * 0.002 / 64) 
}; 
 
static const unsigned exp_time[32] = 
{ 
 0 - 1                                    , (unsigned) (SPC_CLOCK_HZ * 38.4000 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 28.8000 / 600), (unsigned) (SPC_CLOCK_HZ * 24.0000 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 19.2000 / 600), (unsigned) (SPC_CLOCK_HZ * 14.4000 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 12.0000 / 600), (unsigned) (SPC_CLOCK_HZ * 9.60000 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 7.20000 / 600), (unsigned) (SPC_CLOCK_HZ * 6.00000 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 4.80000 / 600), (unsigned) (SPC_CLOCK_HZ * 3.60000 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 3.00000 / 600), (unsigned) (SPC_CLOCK_HZ * 2.40000 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 1.80000 / 600), (unsigned) (SPC_CLOCK_HZ * 1.50000 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 1.20000 / 600), (unsigned) (SPC_CLOCK_HZ * 0.90000 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 0.75000 / 600), (unsigned) (SPC_CLOCK_HZ * 0.60000 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 0.45000 / 600), (unsigned) (SPC_CLOCK_HZ * 0.37500 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 0.30000 / 600), (unsigned) (SPC_CLOCK_HZ * 0.22500 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 0.18750 / 600), (unsigned) (SPC_CLOCK_HZ * 0.15000 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 0.11250 / 600), (unsigned) (SPC_CLOCK_HZ * 0.09375 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 0.07500 / 600), (unsigned) (SPC_CLOCK_HZ * 0.05625 / 600), 
 (unsigned) (SPC_CLOCK_HZ * 0.03750 / 600), (unsigned) (SPC_CLOCK_HZ * 0.01875 / 600) 
}; 
 
static const unsigned bent_time[32] = 
{ 
 0 - 1                                   , (unsigned) (SPC_CLOCK_HZ * 7.1680 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 5.3760 / 112), (unsigned) (SPC_CLOCK_HZ * 4.4800 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 3.5840 / 112), (unsigned) (SPC_CLOCK_HZ * 2.6880 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 2.2400 / 112), (unsigned) (SPC_CLOCK_HZ * 1.7920 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 1.3440 / 112), (unsigned) (SPC_CLOCK_HZ * 1.1200 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 0.8960 / 112), (unsigned) (SPC_CLOCK_HZ * 0.6720 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 0.5600 / 112), (unsigned) (SPC_CLOCK_HZ * 0.4480 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 0.3360 / 112), (unsigned) (SPC_CLOCK_HZ * 0.2800 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 0.2240 / 112), (unsigned) (SPC_CLOCK_HZ * 0.1680 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 0.1400 / 112), (unsigned) (SPC_CLOCK_HZ * 0.1120 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 0.0840 / 112), (unsigned) (SPC_CLOCK_HZ * 0.0700 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 0.0560 / 112), (unsigned) (SPC_CLOCK_HZ * 0.0420 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 0.0350 / 112), (unsigned) (SPC_CLOCK_HZ * 0.0280 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 0.0210 / 112), (unsigned) (SPC_CLOCK_HZ * 0.0175 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 0.0140 / 112), (unsigned) (SPC_CLOCK_HZ * 0.0105 / 112), 
 (unsigned) (SPC_CLOCK_HZ * 0.0070 / 112), (unsigned) (SPC_CLOCK_HZ * 0.0035 / 112) 
}; 
 
static const unsigned noise_frequencies[32] = 
{ 
     0,    16,    21,    25,    31,    42,    50,    63, 
    84,   100,   125,   167,   200,   250,   333,   400, 
   500,   667,   800,  1000,  1333,  1667,  2000,  2667, 
  3200,  4000,  5333,  6400,  8000, 10667, 16000, 32000 
}; 
static const unsigned noise_clocks[32] = 
{ 
/* 
    0 << 13, 2048 << 13, 1536 << 13, 1280 << 13, 
 1024 << 13,  768 << 13,  640 << 13,  512 << 13, 
  384 << 13,  320 << 13,  256 << 13,  192 << 13, 
  160 << 13,  128 << 13,   96 << 13,   80 << 13, 
   64 << 13,   48 << 13,   40 << 13,   32 << 13, 
   24 << 13,   20 << 13,   16 << 13,   12 << 13, 
   10 << 13,    8 << 13,    6 << 13,    5 << 13, 
    4 << 13,    3 << 13,    2 << 13,    1 << 13 
 */ 
    0       , 8192 / 2048, 8192 / 1536, 8192 / 1280, 
 8192 / 1024, 8192 /  768, 8192 /  640, 8192 /  512, 
 8192 /  384, 8192 /  320, 8192 /  256, 8192 /  192, 
 8192 /  160, 8192 /  128, 8192 /   96, 8192 /   80, 
 8192 /   64, 8192 /   48, 8192 /   40, 8192 /   32, 
 8192 /   24, 8192 /   20, 8192 /   16, 8192 /   12, 
 8192 /   10, 8192 /    8, 8192 /    6, 8192 /    5, 
 8192 /    4, 8192 /    3, 8192 /    2, 8192 /    1 
}; 
static const unsigned noise_freq_factors[32] = 
{ 
    0, 2048, 1536, 1280, 1024,  768,  640,  512, 
  384,  320,  256,  192,  160,  128,   96,   80, 
   64,   48,   40,   32,   24,   20,   16,   12, 
   10,    8,    6,    5,    4,    3,    2,    1 
}; 
 
#define NOISE_FEEDBACK 0x00040001 
int noise; 
int noise_vol; 
 
unsigned noise_freq_factor, noise_freq_latch; 
 
// OLD STUFF 
 
/* Timers 0/1 must be updated once every 10M, 2 every 1.28M SPC cycles */ 
void Update_SPC_Timer_0() 
{ 
 if (!(SPC_CTRL & 1)) return; 
 SPC_T0_position += (TotalCycles - SPC_T0_cycle_latch) / TIMER_0_CYCLES_PER_TICK; 
 SPC_T0_cycle_latch += (TotalCycles - SPC_T0_cycle_latch) & ~(TIMER_0_CYCLES_PER_TICK - 1); 
 
 if (SPC_T0_position < SPC_T0_target) return; 
 SPC_T0_counter = (SPC_T0_counter + (SPC_T0_position / SPC_T0_target)) & 0xF; 
 SPC_T0_position %= SPC_T0_target; 
} 
 
void Update_SPC_Timer_1() 
{ 
 if (!(SPC_CTRL & 2)) return; 
 SPC_T1_position += (TotalCycles - SPC_T1_cycle_latch) / TIMER_1_CYCLES_PER_TICK; 
 SPC_T1_cycle_latch += (TotalCycles - SPC_T1_cycle_latch) & ~(TIMER_1_CYCLES_PER_TICK - 1); 
 
 if (SPC_T1_position < SPC_T1_target) return; 
 SPC_T1_counter = (SPC_T1_counter + (SPC_T1_position / SPC_T1_target)) & 0xF; 
 SPC_T1_position %= SPC_T1_target; 
} 
 
void Update_SPC_Timer_2() 
{ 
 if (!(SPC_CTRL & 4)) return; 
 SPC_T2_position += (TotalCycles - SPC_T2_cycle_latch) / TIMER_2_CYCLES_PER_TICK; 
 SPC_T2_cycle_latch += (TotalCycles - SPC_T2_cycle_latch) & ~(TIMER_2_CYCLES_PER_TICK - 1); 
 
 if (SPC_T2_position < SPC_T2_target) return; 
 SPC_T2_counter = (SPC_T2_counter + (SPC_T2_position / SPC_T2_target)) & 0xF; 
 SPC_T2_position %= SPC_T2_target; 
} 
 
void Wrap_SPC_Cyclecounter(){ 
 int c; 
 
 for (c = 0; c < 8; c++) 
 { 
  SNDvoices[c].env_cycle_latch -= 0xF0000000; 
  SNDvoices[c].voice_cycle_latch -= 0xF0000000; 
 } 
 TotalCycles -= 0xF0000000; 
 SPC_Cycles -= 0xF0000000; 
 SPC_T0_cycle_latch -= 0xF0000000; 
 SPC_T1_cycle_latch -= 0xF0000000; 
 SPC_T2_cycle_latch -= 0xF0000000; 
 sound_cycle_latch -= 0xF0000000; 
} 
 
#ifdef __GNUC__ 
#define INLINE inline 
#else 
#define INLINE 
#endif 
 
static int get_brr_block(int voice, struct voice_state *pvs) 
{ 
    int i, output; 
    unsigned brrptr; 
#ifdef WIDE_LAST_SAMPLES 
    int last1, last2; 
#else 
    short last1, last2; 
#endif 
    unsigned char header, last_header; 
    unsigned char range, filter, input; 
 
    unsigned bufptr = pvs->bufptr; 
 
    brrptr = pvs->brrptr; 
 
    last_header = pvs->last_header; 
    last1 = pvs->last1; 
    last2 = pvs->last2; 
 
    while (bufptr >= 0x20000) 
    { 
     if (brrptr >= 0x10000 - 9) 
     { 
      SNDkeys &= ~(1 << voice); 
#ifdef LOG_SOUND_VOICE_OFF 
      printf("Voice off: %d (BRR address overflow)\n", voice); 
#endif 
#ifdef SET_ENDX_ON_INVALID_BRR 
      SPC_DSP[DSP_ENDX] |= 1 << voice; 
#endif 
#ifdef ZERO_OUTX_ON_VOICE_OFF 
      SPC_DSP[(voice << 4) + DSP_VOICE_OUTX] = 0; 
#endif 
#ifdef ZERO_ENVX_ON_VOICE_OFF 
      SNDvoices[voice].envx = 0; 
#endif 
      return 1; 
     } 
 
     header = SPCRAM[brrptr]; 
 
     range = header >> 4; 
     if (range > 12) 
     { 
#ifdef IGNORE_BAD_BRR_HEADER_ENTIRELY 
      header = last_header; 
#else 
      header = (header & 0x0F) | (last_header & 0xF0); 
#endif 
      range = last_header >> 4; 
     } 
     filter = (header >> 2) & 3; 
 
     last_header = header; 
     brrptr++; 
     i = 0; 
 
     while (i < 16) 
     { 
 
      input = SPCRAM[brrptr++]; 
 
      output = (input >> 4) & 0x0F; 
      output = (output ^ 8) - 8; 
      output = (output << range) & ~1; 
 
      if (filter) 
      { 
       switch (filter) 
       { 
        case 1: 
/*       output += last1 * 15 / 16; */ 
         output = output + 
          (last1 + ((-last1 >> 4) & ~1)); 
          break; 
         case 2: 
/*        output += (last1 * 61 / 32) - (last2 * 15 / 16); */ 
          output = output + 
           ((last1 << 1) + ((-last1 >> 4) & ~1) + ((-last1 >> 5) & ~1)) - 
           (last2 + ((-last2 >> 4) & ~1)); 
          break; 
         case 3: default: 
/*        output += (last1 * 115 / 64) - (last2 * 13 / 16); */ 
          output = output + 
           ((last1 << 1) + ((-last1 >> 3) & ~1) + ((-last1 >> 4) & ~1) + 
           ((-last1 >> 6) & ~1)) - 
           (last2 + ((-last2 >> 3) & ~1) + ((-last2 >> 4) & ~1)); 
          break; 
       } 
 
#ifndef WIDE_BRR_SAMPLES 
       // Clip underflow/overflow (saturation) 
       if (output > 0x7FFE) 
        output = 0x7FFE; 
       else if (output < -0x8000) 
        output = -0x8000; 
#endif 
      } 
 
      last2 = last1; 
      last1 = output; 
      pvs->buf[i++] = output; 
 
      output = input & 0x0F; 
      output = (output ^ 8) - 8; 
      output = (output << range) & ~1; 
 
      if (filter) 
      { 
       switch (filter) 
       { 
        case 1: 
/*       output += last1 * 15 / 16; */ 
         output = output + 
          (last1 + ((-last1 >> 4) & ~1)); 
         break; 
        case 2: 
/*       output += (last1 * 61 / 32) - (last2 * 15 / 16); */ 
         output = output + 
          ((last1 << 1) + ((-last1 >> 4) & ~1) + ((-last1 >> 5) & ~1)) - 
          (last2 + ((-last2 >> 4) & ~1)); 
         break; 
        case 3: default: 
/*       output += (last1 * 115 / 64) - (last2 * 13 / 16); */ 
         output = output + 
          ((last1 << 1) + ((-last1 >> 3) & ~1) + ((-last1 >> 4) & ~1) + 
          ((-last1 >> 6) & ~1)) - 
          (last2 + ((-last2 >> 3) & ~1) + ((-last2 >> 4) & ~1)); 
         break; 
       } 
 
#ifndef WIDE_BRR_SAMPLES 
       // Clip underflow/overflow (saturation) 
       if (output > 0x7FFE) 
        output = 0x7FFE; 
       else if (output < -0x8000) 
        output = -0x8000; 
#endif 
      } 
 
      last2 = last1; 
      last1 = output; 
      pvs->buf[i++] = output; 
     } 
 
     if (header & BRR_PACKET_END) 
     { 
      if (header & BRR_PACKET_LOOP) 
      { 
       typedef struct { unsigned short start, loop; } SAMP_DIR; 
 
       SAMP_DIR *samp_dir = (SAMP_DIR *) 
        &SPCRAM[(int) SPC_DSP[DSP_DIR] << 8]; 
       int cursamp = SPC_DSP[(voice << 4) + DSP_VOICE_SRCN]; 
 
       brrptr = samp_dir[cursamp].loop; 
      } 
      else 
       brrptr = 0x10000; 
 
      SPC_DSP[DSP_ENDX] |= 1 << voice; 
     } 
 
     bufptr -= 0x20000; 
    } 
 
    pvs->brrptr = brrptr; 
    pvs->bufptr = bufptr; 
    pvs->last1 = last1; 
    pvs->last2 = last2; 
    pvs->last_header = last_header; 
    return 0; 
} 
 
#define SoundGetEnvelopeHeightADSR(voice) ((pvs->voice_cycle_latch - pvs->env_cycle_latch < pvs->env_update_time) ? pvs->envx : UpdateEnvelopeHeight(voice)) 
#ifdef ADSR_OPTIONAL 
#define SoundGetEnvelopeHeight(voice) ((ADSR_ENABLED) ? SoundGetEnvelopeHeightADSR(voice) : ENVX_MAX) 
#else 
#ifdef ADSR 
#define SoundGetEnvelopeHeight(voice) SoundGetEnvelopeHeightADSR(voice) 
#else 
#define SoundGetEnvelopeHeight(voice) ENVX_MAX 
#endif 
#endif 
 
INLINE static int UpdateEnvelopeHeight(int voice) 
{ 
    struct voice_state *pvs; 
    unsigned envx; 
    unsigned voice_cycle_latch; 
 
    pvs = &SNDvoices[voice]; 
    envx = pvs->envx; 
    voice_cycle_latch = pvs->voice_cycle_latch; 
 
    for (;;) 
    { 
        unsigned cyc, env_update_time; 
 
        env_update_time = pvs->env_update_time; 
        cyc = voice_cycle_latch - pvs->env_cycle_latch; 
 
        /* Is it time to adjust envelope? */ 
        if (cyc < env_update_time) 
        { 
         /* Should we ever adjust envelope? */ 
         if (env_update_time == (0 - 1)) 
          pvs->env_cycle_latch = voice_cycle_latch; 
         break; 
        } 
 
        switch (pvs->envstate) 
        { 
            case ATTACK: 
#ifdef ATTACK_15_IS_INSTANT 
            if (env_update_time != 0) 
            { 
#endif 
                pvs->env_cycle_latch += env_update_time; 
                envx += ENVX_MAX / 64;      //add 1/64th 
#ifdef ATTACK_15_IS_INSTANT 
            } 
#endif 
 
#ifdef ATTACK_15_IS_INSTANT 
            if (env_update_time == 0 || envx >= ENVX_MAX) 
#else 
            if (envx >= ENVX_MAX) 
#endif 
            { 
                envx = ENVX_MAX; 
                if (pvs->sl != ENVX_MAX) 
                { 
                 pvs->envstate = DECAY; 
                 pvs->env_update_time = pvs->dr; 
                } 
                else 
                { 
                 pvs->envstate = SUSTAIN; 
                 pvs->env_update_time = pvs->sr; 
                } 
            } 
            continue; 
 
            case DECAY: 
            pvs->env_cycle_latch += env_update_time; 
            envx -= (envx >> 8);    //mult by 1-1/256 
 
            if (envx <= pvs->sl) 
            { 
                pvs->envstate = SUSTAIN; 
                pvs->env_update_time = pvs->sr; 
            } 
 
            continue; 
 
            case SUSTAIN: 
            pvs->env_cycle_latch += env_update_time; 
            envx -= (envx >> 8);    //mult by 1-1/256 
            continue; 
 
            case RELEASE: 
            //says add 1/256??  That won't release, must be subtract. 
            //But how often?  Oh well, who cares, I'll just 
            //pick a number. :) 
            pvs->env_cycle_latch += env_update_time; 
            envx -= (ENVX_MAX >> 8);    //sub 1/256th 
            if ((envx == 0) || (envx > ENVX_MAX)) 
            { 
                SNDkeys &= ~(1 << voice); 
#ifdef LOG_SOUND_VOICE_OFF 
     printf("Voice off: %d (release)\n", voice); 
#endif 
#ifdef SET_ENDX_ON_VOICE_OFF 
                SPC_DSP[DSP_ENDX] |= 1 << voice; 
#endif 
#ifdef ZERO_OUTX_ON_VOICE_OFF 
                SPC_DSP[(voice << 4) + DSP_VOICE_OUTX] = 0; 
#endif 
                envx = 0; 
                break; 
            } 
            continue; 
 
            case INCREASE: 
            pvs->env_cycle_latch += env_update_time; 
            envx += (ENVX_MAX >> 6);    //add 1/64th 
            if (envx >= ENVX_MAX) 
            { 
                pvs->env_cycle_latch = voice_cycle_latch; 
                envx = ENVX_MAX; 
                break; 
            } 
            continue; 
 
            case DECREASE: 
            pvs->env_cycle_latch += env_update_time; 
            envx -= (ENVX_MAX >> 6);    //sub 1/64th 
            if (envx == 0 || envx > ENVX_MAX)    //underflow 
            { 
                pvs->env_cycle_latch = voice_cycle_latch; 
                envx = 0; 
                break; 
            } 
            continue; 
 
            case EXP: 
            pvs->env_cycle_latch += env_update_time; 
            if (envx >> 8) envx -= (envx >> 8);    //mult by 1-1/256 
            else envx--; 
 
            if (envx == 0 || envx > ENVX_MAX)   //underflow 
            { 
                pvs->env_cycle_latch = voice_cycle_latch; 
                envx = 0; 
                break; 
            } 
 
            continue; 
 
            case BENT: 
            pvs->env_cycle_latch += env_update_time; 
            if (envx < (ENVX_MAX / 4 * 3)) 
             envx += ENVX_MAX / 64;     //add 1/64th 
            else 
             envx += ENVX_MAX / 256;    //add 1/256th 
            if (envx >= ENVX_MAX) 
            { 
                pvs->env_cycle_latch = voice_cycle_latch; 
                envx = ENVX_MAX; 
                break; 
            } 
            continue; 
 
            //case DIRECT: 
            //break; 
        } 
        break; 
    } 
    pvs->envx = envx; 
 
    return envx; 
} 
 
INLINE static void SPC_KeyOn(int voices) 
{ 
    int voice, cursamp, adsr1, adsr2, gain; 
    typedef struct { unsigned short start, loop; } SAMP_DIR; 
    SAMP_DIR *samp_dir = (SAMP_DIR *) &SPCRAM[(int) SPC_DSP[DSP_DIR] << 8]; 
 
    voices &= SPC_MASK; 
 
    voices &= ~SPC_DSP[DSP_KOF];   
 
    for (voice = 0; voice < 8; voice++) 
    { 
        if (voices & (1 << voice)) 
        { 
            struct voice_state *pvs; 
 
            pvs = &SNDvoices[voice]; 
            cursamp = SPC_DSP[(voice << 4) + DSP_VOICE_SRCN]; 
            pvs->brrptr = samp_dir[cursamp].start; 
            pvs->bufptr = 0x20000; 
            pvs->last_header = BRR_PACKET_END; 
            pvs->voice_cycle_latch = TotalCycles & ~(SOUND_CYCLES_PER_SAMPLE - 1); 
            pvs->env_cycle_latch = TotalCycles & ~(SOUND_CYCLES_PER_SAMPLE - 1); 
 
            pvs->lvol = ((int) ((signed char) SPC_DSP[DSP_MAIN_LVOL])) 
             * ((int) ((signed char) SPC_DSP[(voice << 4) + DSP_VOICE_LVOL])); 
            pvs->rvol = ((int) ((signed char) SPC_DSP[DSP_MAIN_RVOL])) 
             * ((int) ((signed char) SPC_DSP[(voice << 4) + DSP_VOICE_RVOL])); 
 
            adsr1 = SPC_DSP[(voice << 4) + DSP_VOICE_ADSR1]; 
            if (adsr1 & 0x80) 
            { 
             //ADSR mode 
             adsr2 = SPC_DSP[(voice << 4) + DSP_VOICE_ADSR2]; 
 
             // Don't set envelope to zero if sound was playing 
             if (!(SNDkeys & (1 << voice))) 
             { 
              pvs->envx = 0; 
              pvs->outx = 0; 
             } 
 
#ifdef ATTACK_15_IS_INSTANT 
             if (pvs->ar != 0) 
             { 
              pvs->envstate = ATTACK; 
              pvs->env_update_time = pvs->ar; 
             } 
             else 
             { 
              pvs->envx = ENVX_MAX; 
              if (pvs->sl != ENVX_MAX) 
              { 
               pvs->envstate = DECAY; 
               pvs->env_update_time = pvs->dr; 
              } 
              else 
              { 
               pvs->envstate = SUSTAIN; 
               pvs->env_update_time = pvs->sr; 
              } 
             } 
#else 
             pvs->envstate = ATTACK; 
             pvs->env_update_time = pvs->ar; 
#endif 
            } 
            else 
            { 
             //GAIN mode 
             gain = SPC_DSP[(voice << 4) + DSP_VOICE_GAIN]; 
 
             pvs->env_update_time = pvs->gain_update_time; 
 
             if (gain & 0x80) 
             { 
              // Don't set envelope to zero if sound was playing 
              if (!(SNDkeys & (1 << voice))) 
              { 
               pvs->envx = 0; 
               pvs->outx = 0; 
              } 
 
              pvs->envstate = gain >> 5; 
 
             } 
             else 
             { 
              pvs->envx = (gain & 0x7F) << 24; 
              pvs->envstate = DIRECT; 
             } 
            } 
        } 
    } 
    SPC_DSP[DSP_ENDX] &= ~voices; 
    SNDkeys |= voices; 
} 
 
INLINE static void SPC_KeyOff(int voices) 
{ 
    int voice; 
 
    voices &= SNDkeys; 
 
    for (voice = 0; voice < 8; voice++) 
    { 
        if (voices & (1 << voice)) 
        { 
#ifdef ADSR_OPTIONAL 
            if(ADSR_ENABLED) 
            { 
#endif 
#if defined(ADSR) || defined(ADSR_OPTIONAL) 
            SNDvoices[voice].envstate = RELEASE; 
            SNDvoices[voice].env_update_time = SOUND_CYCLES_PER_SAMPLE; /*8 64*/ 
#endif 
#ifdef ADSR_OPTIONAL 
            } else { 
#endif 
#if !defined(ADSR) || defined(ADSR_OPTIONAL) 
            SNDkeys &= ~(1 << voice); 
#ifdef LOG_SOUND_VOICE_OFF 
     printf("Voice off: %d (key off)\n", voice); 
#endif 
#ifdef ZERO_OUTX_ON_VOICE_OFF 
            SPC_DSP[(voice << 4) + DSP_VOICE_OUTX] = 0; 
#endif 
#endif 
#ifdef ADSR_OPTIONAL 
            } 
#endif 
        } 
    } 
} 
 
static int sound_enable_mode = 0; 
 
static int last_position; 
 
void Reset_Sound_DSP() 
{ 
 int i, samples; 
 
 samples = 2 * SOUND_FREQ / SOUND_LAG; 
 
 if (sound_enable_mode == 2) samples <<= 1; 
 
 memset(SPC_DSP, 0, 256); 
 
 SPC_MASK = 0xFF; 
 SNDkeys = 0; 
 sound_output_position = 0; 
 block_written[0] = block_written[1] = -1; 
 
 noise = NOISE_FEEDBACK; 
 noise_vol = 0; 
 noise_freq_factor = 0; 
 noise_freq_latch = 0; 
 
 for (i = 0; i < 8; i++) 
 { 
  SNDvoices[i].last2 = SNDvoices[i].last1 = 0; 
  SNDvoices[i].envx = 0; 
  SNDvoices[i].outx = 0; 
  SNDvoices[i].env_cycle_latch = TotalCycles; 
  SNDvoices[i].ar = attack_time[0]; 
  SNDvoices[i].dr = decay_time[0]; 
  SNDvoices[i].sr = exp_time[0]; 
  SNDvoices[i].sl = ENVX_MAX / 8; 
  SNDvoices[i].gn = 0; 
  SNDvoices[i].lvol = 0; 
  SNDvoices[i].rvol = 0; 
//SNDvoices[i].envstate = DIRECT; 
  SNDvoices[i].env_update_time = 
   SNDvoices[i].gain_update_time = (0 - 1); 
 } 
 
 if (!sound_enable_mode) return; 
 
 last_position = 0; 
 
 if (sound_bits == 8) 
 { 
  for (i = 0; i < samples; i++) 
  { 
   ((output_sample_8 *) sound_buffer->data)[i] = OUTPUT_ZERO_BASE_8; 
   ((output_sample_8 *) sound_buffer_preload)[i] = OUTPUT_ZERO_BASE_8; 
  } 
 } 
 else 
 { 
  for (i = 0; i < samples; i++) 
  { 
   ((output_sample_16 *) sound_buffer->data)[i] = OUTPUT_ZERO_BASE_16; 
   ((output_sample_16 *) sound_buffer_preload)[i] = OUTPUT_ZERO_BASE_16; 
  } 
 } 
 
} 
 
void Remove_Sound() 
{ 
 if (noise_buffer) 
 { 
  free(noise_buffer); 
  noise_buffer = 0; 
 } 
 if (outx_buffer) 
 { 
  free(outx_buffer); 
  outx_buffer = 0; 
 } 
 if (mix_buffer) 
 { 
  free(mix_buffer); 
  mix_buffer = 0; 
 } 
 if (sound_buffer_preload) 
 { 
  free(sound_buffer_preload); 
  sound_buffer_preload = 0; 
 } 
 if (sound_buffer) 
 { 
  if (voice_handle != -1) 
  { 
   deallocate_voice(voice_handle); 
   voice_handle = -1; 
  } 
  destroy_sample(sound_buffer); 
  sound_buffer = 0; 
 } 
 
 remove_sound(); 
 sound_enabled = sound_enable_mode = 0; 
} 
 
int Install_Sound(int stereo) 
{ 
 int samples; 
 if (sound_enable_mode) Remove_Sound(); 
 srand(0); 
 
 set_volume_per_voice(0); 
 if (install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL)) 
 { 
  return sound_enabled = sound_enable_mode = 0; 
 } 
 
 if (digi_driver->id == DIGI_NONE) 
 { 
  Remove_Sound(); 
  return sound_enabled = sound_enable_mode = 0; 
 } 
 
 samples = 2 * SOUND_FREQ / SOUND_LAG; 
 
 if (sound_bits == 8) 
 { 
  sound_buffer = create_sample(8, stereo ? TRUE : FALSE, SOUND_FREQ, samples); 
  sound_buffer_preload = 
   malloc(sizeof(output_sample_8[(stereo ? 2 : 1) * samples])); 
 } 
 else 
 { 
  sound_buffer = create_sample(16, stereo ? TRUE : FALSE, SOUND_FREQ, samples); 
  sound_buffer_preload = 
   malloc(sizeof(output_sample_16[(stereo ? 2 : 1) * samples])); 
 } 
 
 noise_buffer = (output_sample_16 *) 
  malloc(sizeof(output_sample_16 [samples])); 
 
 outx_buffer = (char *) malloc(sizeof(char [samples])); 
 
 mix_buffer = (int *) malloc(sizeof(int [(stereo ? 2 : 1) * samples])); 
 
 if (!sound_buffer || !sound_buffer_preload || !noise_buffer || !outx_buffer 
  || !mix_buffer) 
 { 
  Remove_Sound(); 
  return sound_enabled = sound_enable_mode = 0; 
 } 
 
 voice_handle = allocate_voice(sound_buffer); 
 
 if (voice_handle == -1) 
 { 
  Remove_Sound(); 
  return sound_enabled = sound_enable_mode = 0; 
 } 
 
 voice_set_playmode(voice_handle, PLAYMODE_LOOP); 
 voice_set_volume(voice_handle, 255); 
 voice_set_pan(voice_handle, 128); 
 sound_enabled = sound_enable_mode = stereo + 1; 
 
 return sound_enabled; 
 
} 
 
void update_sound_block(void) 
{ 
 int samples, play_samples, sample_bytes; 
 int data_block, write_block; 
 
 if (!SPC_ENABLED || !sound_enabled) return; 
 
 samples = SOUND_FREQ / SOUND_LAG; 
 play_samples = samples; 
 
 if (sound_enable_mode == 2) play_samples <<= 1; 
 
 if (sound_bits == 8) 
 { 
  sample_bytes = sizeof(output_sample_8[play_samples]); 
 } 
 else 
 { 
  sample_bytes = sizeof(output_sample_16[play_samples]); 
 } 
 
 /* Write to block that is not playing */ 
 write_block = voice_get_position(voice_handle) >= samples ? 0 : 1; 
 
 /* But only if we haven't already */ 
 if (block_written[write_block]) return; 
 block_written[write_block] = -1; 
 
 /* Write from block not being written to */ 
 data_block = sound_output_position >= samples ? 0 : play_samples; 
 
 if (write_block) write_block = play_samples; 
 
 if (sound_bits == 8) 
 { 
  memcpy(((output_sample_8 *) sound_buffer->data) + write_block, 
   ((output_sample_8 *) sound_buffer_preload) + data_block, sample_bytes); 
 } 
 else 
 { 
  memcpy(((output_sample_16 *) sound_buffer->data) + write_block, 
   ((output_sample_16 *) sound_buffer_preload) + data_block, sample_bytes); 
 } 
} 
 
unsigned samples_output = 0; 
 
INLINE static int get_brr_blocks(int voice, struct voice_state *pvs) 
{ 
 if (pvs->bufptr >= 0x20000) 
 { 
  return get_brr_block(voice, pvs); 
 } 
 return 0; 
} 
 
INLINE static void update_voice_pitch(int voice, struct voice_state *pvs, 
 unsigned char pitch_modulation_enable, unsigned char voice_bit) 
{ 
#ifndef NO_PITCH_MODULATION 
 if (!(pitch_modulation_enable & voice_bit)) 
 { 
  pvs->bufptr += pvs->step; 
 } 
 else 
 { 
  pvs->bufptr += 
   pvs->step * (((SNDvoices[voice - 1].outx >> 8) & 0xFF) ^ 128) / 128; 
 } 
#else 
 pvs->bufptr += pvs->step; 
#endif 
} 
 
#ifndef NO_PRELOAD_OUTPUT 
#define STEREO_PRELOAD_OUTPUT asm volatile("movb (%%eax),%%al" : : "a" (buf + i * 2)); 
#define MONO_PRELOAD_OUTPUT asm volatile("movb (%%eax),%%al" : : "a" (buf + i)); 
#else 
#define STEREO_PRELOAD_OUTPUT 
#define MONO_PRELOAD_OUTPUT 
#endif 
 
#define STEREO_DECLARE_SAMPLES int lsample, rsample; 
#define STEREO_INIT_SAMPLES (lsample = rsample = 0); 
 
#define MONO_DECLARE_SAMPLES int sample; 
#define MONO_INIT_SAMPLES (sample = 0); 
 
#define SAMPLE_WRITE(S,D) ((S) = (D)) 
#define SAMPLE_ADD(S,D) ((S) += (D)) 
 
#define MONO_VOICE_VOLUME(OP) \ 
 { \ 
  if (pvs->out_lvol != 0) \ 
  { \ 
   OP(sample, (pvs->outx * (int) pvs->out_lvol)); \ 
  } \ 
 } 
 
#define STEREO_VOICE_VOLUME_SAME(OP) \ 
 { \ 
  if (pvs->out_lvol != 0) \ 
  { \ 
   int sample = (pvs->outx * (int) pvs->out_lvol); \ 
   OP(lsample, sample); \ 
   OP(rsample, sample); \ 
  } \ 
 } \ 
 
#define STEREO_VOICE_VOLUME(OP) \ 
 { \ 
  if (pvs->out_lvol != 0) \ 
  { \ 
    OP(lsample, (pvs->outx * (int) pvs->out_lvol)); \ 
  } \ 
  if (pvs->out_rvol != 0) \ 
  { \ 
    OP(rsample, (pvs->outx * (int) pvs->out_rvol)); \ 
  } \ 
 } 
 
#define SAMPLE_WRITE_ZERO(BITS,A) \ 
 { \ 
  (A) = OUTPUT_ZERO_BASE_##BITS; \ 
 } 
 
#define MONO_SAMPLE_WRITE_ZERO(BITS) \ 
 { \ 
  SAMPLE_WRITE_ZERO(BITS,buf[i]) \ 
 } 
 
#define STEREO_SAMPLE_WRITE_ZERO(BITS) \ 
 { \ 
  SAMPLE_WRITE_ZERO(BITS,buf[i * 2]) \ 
  SAMPLE_WRITE_ZERO(BITS,buf[i * 2 + 1]) \ 
 } 
 
#define SAMPLE_CLIP_AND_WRITE(BITS,A,S) \ 
 { \ 
  if ((S) <= PREMIX_LOWER_LIMIT_##BITS) \ 
   (S) = OUTPUT_LOWER_LIMIT_##BITS; \ 
  else if ((S) >= PREMIX_UPPER_LIMIT_##BITS) \ 
   (S) = OUTPUT_UPPER_LIMIT_##BITS; \ 
  else (S) = OUTPUT_ZERO_BASE_##BITS + \ 
   ((S) >> PREMIX_SHIFT_##BITS); \ 
 \ 
 (A) = (S); \ 
 } 
 
#define MONO_SAMPLE_CLIP_AND_WRITE(BITS) \ 
 SAMPLE_CLIP_AND_WRITE(BITS,buf[i],sample) 
 
#define STEREO_SAMPLE_CLIP_AND_WRITE(BITS) \ 
 SAMPLE_CLIP_AND_WRITE(BITS,buf[i * 2],lsample) \ 
 SAMPLE_CLIP_AND_WRITE(BITS,buf[i * 2 + 1],rsample) 
 
#define MIX(BITS,CHANNELS) \ 
     { \ 
/*    int voice_samples_generated[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; */ \ 
/*    int max_samples_generated; */ \ 
 \ 
      output_sample_##BITS *buf = \ 
       (output_sample_##BITS *) sound_buffer_preload; \ 
 \ 
      unsigned i; \ 
      int samples_left = samples; \ 
 \ 
      for (i = first; samples_left && SNDkeys; samples_left--) \ 
      { \ 
       CHANNELS##_DECLARE_SAMPLES \ 
 \ 
       CHANNELS##_PRELOAD_OUTPUT \ 
 \ 
       CHANNELS##_INIT_SAMPLES \ 
       for (voice = 0, voice_bit = 1; voice < 8; voice++, voice_bit <<= 1) \ 
       { \ 
        struct voice_state *pvs; \ 
        if (!(SNDkeys & voice_bit)) continue; \ 
 \ 
        pvs = &SNDvoices[voice]; \ 
        if (get_brr_blocks(voice,pvs)) continue; \ 
 \ 
        if (SPC_DSP[DSP_NON] & voice_bit) \ 
         pvs->outx = noise_buffer [i]; \ 
        else \ 
         pvs->outx = pvs->buf[pvs->bufptr >> 13]; \ 
 \ 
        update_voice_pitch(voice, pvs, pitch_modulation_enable, voice_bit); \ 
 \ 
        if (pvs->outx != 0) \ 
        { \ 
         pvs->outx = (pvs->outx \ 
          * (int) (SoundGetEnvelopeHeight(voice) >> 24)) >> 7; \ 
 \ 
         CHANNELS##_VOICE_VOLUME(SAMPLE_ADD) \ 
        } \ 
 \ 
        pvs->voice_cycle_latch += SOUND_CYCLES_PER_SAMPLE; \ 
 \ 
       } \ 
 \ 
       if (!(SPC_DSP[DSP_FLG] & DSP_FLG_MUTE)) \ 
       { \ 
        CHANNELS##_SAMPLE_CLIP_AND_WRITE(BITS) \ 
       } \ 
       else \ 
       { \ 
        CHANNELS##_SAMPLE_WRITE_ZERO(BITS) \ 
       } \ 
 \ 
       if (++i >= buffer_size) \ 
        i = 0; \ 
      } \ 
 \ 
      for (; samples_left; samples_left--) \ 
      { \ 
       CHANNELS##_PRELOAD_OUTPUT \ 
       CHANNELS##_SAMPLE_WRITE_ZERO(BITS) \ 
 \ 
       if (++i >= buffer_size) \ 
        i = 0; \ 
      } \ 
 \ 
      sound_output_position = i; \ 
     } 
 
void mix_voices(unsigned first, unsigned samples, unsigned buffer_size, 
 int sound_bits, int stereo) 
{ 
    int voice; 
    unsigned char voice_bit; 
    unsigned char pitch_modulation_enable; 
 
    // pitch modulation is not available for noise channels or channel 0 
    pitch_modulation_enable = SPC_DSP[DSP_PMON] & ~SPC_DSP[DSP_NON] & ~1; 
#ifdef FAULT_ON_PITCH_MODULATION_USE 
    if (SPC_DSP[DSP_PMON] & ~1 & SNDkeys) asm("ud2"); 
#endif 
 
    if (sound_bits == 8) 
    /* 8-bit */ 
    { 
     if (stereo) 
     /* stereo */ 
     { 
      MIX(8,STEREO) 
     } 
     else 
     /* mono */ 
     { 
      MIX(8,MONO) 
     } 
    } 
    else 
    /* 16-bit */ 
    { 
     if (stereo) 
     /* stereo */ 
     { 
      MIX(16,STEREO) 
     } 
     else 
     /* mono */ 
     { 
      MIX(16,MONO) 
     } 
    } 
} 
 
/* update_sound() 
 * This function is called to synchronize the sound DSP sample 
 *  generation to the current SPC700 CPU timing. 
 * Active voices are processed and mixed into the sound buffer. 
 * This function MUST be called often enough that no more than one 
 *  complete buffer of samples are processed per call. 
 */ 
void update_sound(void) 
{ 
    unsigned first, samples, buffer_size; 
    int voice; 
    unsigned char voices_were_on; 
    unsigned char voice_bit; 
 
    if (!SPC_ENABLED || !sound_enabled) return; 
 
    samples = (TotalCycles - sound_cycle_latch) / SOUND_CYCLES_PER_SAMPLE; 
    if (!samples) return; 
    samples_output += samples; 
 
    sound_cycle_latch += (TotalCycles - sound_cycle_latch) & ~(SOUND_CYCLES_PER_SAMPLE - 1); 
    first = sound_output_position; 
 
    buffer_size = 2 * SOUND_FREQ / SOUND_LAG; 
 
    // Are we completing a block? 
    if (((first % (SOUND_FREQ / SOUND_LAG)) + samples) >= 
     (SOUND_FREQ / SOUND_LAG)) 
    { 
     block_written[0] = block_written[1] = 0; 
    } 
 
#ifdef LOG_SOUND_VOICE_OFF 
    if (SNDkeys & ~SPC_MASK) 
     printf("Voices off: %d (SPC_MASK)\n", SNDkeys & ~SPC_MASK); 
#endif 
    SNDkeys &= SPC_MASK; 
 
    if (SNDkeys & SPC_DSP[DSP_NON]) 
    { 
     unsigned i; 
     int samples_left = samples; 
 
     for (i = first; samples_left; samples_left--) 
     { 
      if (!--noise_freq_latch) 
      { 
       noise_freq_latch = noise_freq_factor; 
       noise_vol = rand(); 
      } 
 
      noise_buffer [i++] = noise_vol; 
      if (i >= buffer_size) 
       i = 0; 
     } 
    } 
 
    { 
     unsigned i; 
     int samples_left = (sound_enabled == 2) ? samples * 2 : samples; 
 
     for (i = first; samples_left > 0; samples_left -= 4, i += 4) 
     { 
//     asm volatile("movb (%%eax),%%al" : : "a" (mix_buffer + i)); 
     } 
     memset(mix_buffer + first, sizeof(int [samples]), 0); 
    } 
 
    voices_were_on = SNDkeys; 
 
    for (voice = 0, voice_bit = 1; voice < 8; voice++, voice_bit <<= 1) 
    { 
        struct voice_state *pvs; 
 
        if (!(SNDkeys & voice_bit)) continue; 
 
        pvs = &SNDvoices[voice]; 
 
        if (!(SPC_DSP[DSP_FLG] & DSP_FLG_MUTE)) 
        { 
         int lvol, rvol; 
 
         lvol = pvs->lvol; 
         rvol = pvs->rvol; 
          
         if (sound_enabled == 2) 
         { 
          pvs->out_lvol = lvol >> 7; 
          pvs->out_rvol = rvol >> 7; 
         } 
         else 
         { 
          if (lvol < 0) lvol = -lvol; 
          if (rvol < 0) rvol = -rvol; 
 
          pvs->out_lvol = (lvol + rvol) >> 8; 
         } 
        } 
        else 
        { 
         pvs->out_lvol = pvs->out_rvol = 0; 
        } 
 
        if (0 && SPC_DSP[DSP_NON] & voice_bit) 
        { 
         pvs->step = noise_clocks[SPC_DSP[DSP_FLG] & 0x1F]; 
        } 
        else 
        { 
//       unsigned freq; 
 
//       freq = ((unsigned) *(unsigned short *)&SPC_DSP[(voice << 4) + DSP_VOICE_PITCH_L]) * 32000 / 4096; 
//       pvs->step = freq * 8192 / SOUND_FREQ; 
         pvs->step = ((unsigned) *(unsigned short *)&SPC_DSP[(voice << 4) + DSP_VOICE_PITCH_L]) * 2; 
 
        } 
 
    } 
 
/* MMX mixing notes 
 *  PMADDWD takes two sets of four 16-bit words. abcd(r), efgh(r/m). 
 *  Each 16-bit word in source is multiplied by its respective word in 
 *  destination. Then the high 2 results are added and stored as the 
 *  high 32-bit result, and the low 2 results are added and stored 
 *  as the low 32-bit result. 
 * 
 *  For audio channel mixing, propose that source contain channel volumes 
 *  and destination contain channel sample height. 
 * 
 *  In Stereo, high pair of 16-bit words are for two channels, left side, 
 *  and low pair of 16-bit words are for two channels, right side. 
 * 
 *  In Mono, instead of left and right side, two independent samples 
 *  are processed at once. 
 * 
 *  After all channels have been volume-adjusted, PADDD is used to combine 
 *  them, and PACKSSDW converts them back to 16-bit samples for output. 
 */ 
 
    mix_voices(first, samples, buffer_size, sound_bits, 
     sound_enabled == 2 ? 1 : 0); 
 
    for (voice = 0, voice_bit = 1; voice < 8; voice++, voice_bit <<= 1) 
    { 
        if (voices_were_on & ~SNDkeys & voice_bit) 
        { 
#ifdef SET_ENDX_ON_VOICE_OFF 
         SPC_DSP[DSP_ENDX] |= voice_bit; 
#endif 
         SNDkeys &= ~voice_bit; 
#ifdef LOG_SOUND_VOICE_OFF 
     printf("Voice off: %d (?)\n", voice); 
#endif 
#ifdef ZERO_ENVX_ON_VOICE_OFF 
         SNDvoices[voice].envx = 0; 
#endif 
#ifdef ZERO_OUTX_ON_VOICE_OFF 
         SPC_DSP[(voice << 4) + DSP_VOICE_OUTX] = 0; 
#else 
         SPC_DSP[(voice << 4) + DSP_VOICE_OUTX] = 
          (SNDvoices[voice].outx >> 8); 
#endif 
        } 
        else 
        { 
         SPC_DSP[(voice << 4) + DSP_VOICE_OUTX] = 
          (SNDvoices[voice].outx >> 8); 
        } 
    } 
 
    update_sound_block(); 
} 
 
void SPC_READ_DSP() 
{ 
    int addr_lo = SPC_DSP_ADDR & 0xF; 
    int addr_hi = SPC_DSP_ADDR >> 4; 
 
#ifdef LOG_SOUND_DSP_READ 
    printf("\nread @ %08X,%04X: %02X", TotalCycles, (unsigned) _SPC_PC, (unsigned) SPC_DSP_ADDR); 
#endif 
 
#ifdef DSP_SPEED_HACK 
    /* if we're not reading endx */ 
    if (SPC_DSP_ADDR != DSP_ENDX) 
    { 
     /* if we're reading envx or outx but voice is off */ 
     if (addr_lo == DSP_VOICE_ENVX || addr_lo == DSP_VOICE_OUTX) 
     { 
      if (!(SNDkeys & (1 << addr_hi))) 
       return; 
     } 
     else 
     { 
       return; 
     } 
    } 
#endif 
 
//  printf("\nSound update"); 
#ifndef NO_UPDATE_ON_DSP_READ 
    update_sound(); 
#endif 
    switch(addr_lo) 
    { 
    case DSP_VOICE_ENVX: 
                if (!sound_enabled) SNDvoices[addr_hi].voice_cycle_latch = TotalCycles; 
#ifdef ZERO_ENVX_ON_VOICE_OFF 
                if (ENVX_ENABLED && (SNDkeys & (1 << addr_hi))) 
#else 
                if (ENVX_ENABLED) 
#endif 
                 SPC_DSP[SPC_DSP_ADDR] = UpdateEnvelopeHeight(addr_hi); 
                else 
                 SPC_DSP[SPC_DSP_ADDR] = 0; 
 
                break; 
    } 
#ifdef LOG_SOUND_DSP_READ 
    printf(" %02X", SPC_DSP[SPC_DSP_ADDR]); 
#endif 
} 
 
void SPC_WRITE_DSP() 
{ 
 int i; 
 int addr_lo = SPC_DSP_ADDR & 0xF; 
 int addr_hi = SPC_DSP_ADDR >> 4; 
 
 if (addr_hi > 7) 
 { 
  SPC_DSP[SPC_DSP_ADDR] = SPC_DSP_DATA; 
  return; 
 } 
 
#ifdef LOG_SOUND_DSP_WRITE 
 printf("\nwrite @ %08X,%04X: %02X %02X", TotalCycles, (unsigned) _SPC_PC, 
  (unsigned) SPC_DSP_ADDR, (unsigned) SPC_DSP_DATA); 
#endif 
 
#ifdef DSP_SPEED_HACK 
 /* if we're not writing to key on, key off, flg, or endx */ 
 if (addr_lo != 0x0C || addr_hi < 4) 
 /* and write would not change data, return */ 
 if (SPC_DSP[SPC_DSP_ADDR] == SPC_DSP_DATA) return; 
 
 /* if it's not a voice register for a voice that's not on */ 
 if (addr_lo > 9 || (SNDkeys & (1 << addr_hi))) 
 { 
#endif 
#ifdef NO_UPDATE_ON_DSP_WRITE 
  update_sound(); 
#endif 
//printf("\nSound update"); 
#ifdef DSP_SPEED_HACK 
 } 
#endif 
 
 switch (addr_lo) 
 { 
 // break just means nothing needs to be done right now 
 // Commented cases are unsupported registers, or do-nothing cases 
 
 // Channel - volume left 
 case DSP_VOICE_LVOL: 
  SNDvoices[addr_hi].lvol = ((int) ((signed char) SPC_DSP_DATA)) 
   * ((int) ((signed char) SPC_DSP[DSP_MAIN_LVOL])); 
  break; 
 
 // Channel - volume right 
 case DSP_VOICE_RVOL: 
  SNDvoices[addr_hi].rvol = ((int) ((signed char) SPC_DSP_DATA)) 
   * ((int) ((signed char) SPC_DSP[DSP_MAIN_RVOL])); 
  break; 
/* 
 // Channel - pitch low bits (0-7) 
 case DSP_VOICE_PITCH_L: 
  break; 
 */ 
 
#ifdef MASK_PITCH_H 
 // Channel - pitch high bits (8-13) 
 case DSP_VOICE_PITCH_H: 
  SPC_DSP_DATA &= 0x3F; 
  break; 
#endif 
/* 
 // Channel - source number 
 case DSP_VOICE_SRCN: 
  break; 
 */ 
 // Channel - ADSR 1 
 case DSP_VOICE_ADSR1: 
  if (!sound_enabled) SNDvoices[addr_hi].voice_cycle_latch = TotalCycles; 
  if (SNDkeys & (1 << addr_hi)) UpdateEnvelopeHeight(addr_hi); 
 
  SNDvoices[addr_hi].ar = attack_time[SPC_DSP_DATA & 0xF]; 
  SNDvoices[addr_hi].dr = decay_time[(SPC_DSP_DATA >> 4) & 7]; 
 
  if (SNDvoices[addr_hi].envstate == ATTACK) 
   SNDvoices[addr_hi].env_update_time = SNDvoices[addr_hi].ar; 
  else if (SNDvoices[addr_hi].envstate == DECAY) 
   SNDvoices[addr_hi].env_update_time = SNDvoices[addr_hi].dr; 
 
  if (!(SNDkeys & (1 << addr_hi))) break; 
 
  if (SPC_DSP_DATA & 0x80) 
  { 
   // switch to ADSR? not sure what to do 
   if (!(SPC_DSP[SPC_DSP_ADDR] & 0x80)) 
   { 
    SNDvoices[addr_hi].envstate = ATTACK; 
    SNDvoices[addr_hi].env_update_time = SNDvoices[addr_hi].ar; 
   } 
  } 
  else 
  { 
   if (SNDvoices[addr_hi].envstate != RELEASE) 
   { 
    // switch to a GAIN mode 
    i = SPC_DSP[(addr_hi << 4) + DSP_VOICE_GAIN]; 
 
    SNDvoices[addr_hi].env_update_time = 
     SNDvoices[addr_hi].gain_update_time; 
 
    if (i & 0x80) 
    { 
     SNDvoices[addr_hi].envstate = i >> 5; 
    } 
    else 
    { 
     SNDvoices[addr_hi].envstate = DIRECT; 
     SNDvoices[addr_hi].envx = (i & 0x7F) << 24; 
    } 
   } 
  } 
  break; 
 
 // Channel - ADSR 2 
 case DSP_VOICE_ADSR2: 
  if (!sound_enabled) SNDvoices[addr_hi].voice_cycle_latch = TotalCycles; 
  if (SNDkeys & (1 << addr_hi)) UpdateEnvelopeHeight(addr_hi); 
 
  SNDvoices[addr_hi].sr = exp_time[SPC_DSP_DATA & 0x1F]; 
  SNDvoices[addr_hi].sl = (ENVX_MAX / 8) * ((SPC_DSP_DATA >> 5) + 1); 
 
  if (SNDvoices[addr_hi].envstate == SUSTAIN) 
   SNDvoices[addr_hi].env_update_time = SNDvoices[addr_hi].sr; 
 
  break; 
 
 // Channel - GAIN 
 case DSP_VOICE_GAIN: 
  if (!sound_enabled) SNDvoices[addr_hi].voice_cycle_latch = TotalCycles; 
  if (SNDkeys & (1 << addr_hi)) UpdateEnvelopeHeight(addr_hi); 
 
  if (SPC_DSP_DATA & 0x80) 
  { 
   SNDvoices[addr_hi].gn = SPC_DSP_DATA & 0x1F; 
 
   if (SNDvoices[addr_hi].envstate == INCREASE 
    || SNDvoices[addr_hi].envstate == DECREASE) 
    SNDvoices[addr_hi].gain_update_time = 
     linear_time[(int) SNDvoices[addr_hi].gn]; 
   else if (SNDvoices[addr_hi].envstate == BENT) 
    SNDvoices[addr_hi].gain_update_time = 
     bent_time[(int) SNDvoices[addr_hi].gn]; 
   else /*if (SNDvoices[addr_hi].envstate == EXP)*/ 
    SNDvoices[addr_hi].gain_update_time = 
     exp_time[(int) SNDvoices[addr_hi].gn]; 
  } 
  else 
  { 
   SNDvoices[addr_hi].gain_update_time = (0 - 1); 
  } 
 
  /* is voice keyed on? */ 
  if (!(SNDkeys & (1 << addr_hi))) break; 
 
  /* is gain enabled? */ 
  if (!(SPC_DSP[(addr_hi << 4) + DSP_VOICE_ADSR1] & 0x80)) 
  { 
   SNDvoices[addr_hi].env_update_time = 
    SNDvoices[addr_hi].gain_update_time; 
 
   if (SPC_DSP_DATA & 0x80) 
   { 
    SNDvoices[addr_hi].envstate = SPC_DSP_DATA >> 5; 
   } 
   else 
   { 
    SNDvoices[addr_hi].envstate = DIRECT; 
    SNDvoices[addr_hi].envx = (SPC_DSP_DATA & 0x7F) << 24; 
   } 
  } 
 
  break; 
 
 case DSP_VOICE_ENVX: 
 case DSP_VOICE_OUTX: 
#ifdef DISALLOW_ENVX_OUTX_WRITE 
  return; 
#else 
  break; 
#endif 
 
 // These are general registers 
 case 0xC: 
  switch (addr_hi) 
  { 
  int voice, voice_bit; 
  // Main volume - left 
  case DSP_MAIN_LVOL >> 4: 
   for (voice = 0, voice_bit = 1; voice < 8; voice++, voice_bit <<= 1) 
   { 
    if (!(SNDkeys & voice_bit)) continue; 
 
    SNDvoices[voice].lvol = ((int) ((signed char) SPC_DSP_DATA)) 
     * ((int) ((signed char) SPC_DSP[(voice << 4) + DSP_VOICE_LVOL])); 
   } 
   break; 
 
  // Main volume - right 
  case DSP_MAIN_RVOL >> 4: 
   for (voice = 0, voice_bit = 1; voice < 8; voice++, voice_bit <<= 1) 
   { 
    if (!(SNDkeys & voice_bit)) continue; 
 
    SNDvoices[voice].rvol = ((int) ((signed char) SPC_DSP_DATA)) 
     * ((int) ((signed char) SPC_DSP[(voice << 4) + DSP_VOICE_RVOL])); 
   } 
   break; 
/* 
  // Echo volume - left 
  case DSP_ECHO_LVOL >> 4: 
  // Echo volume - right 
  case DSP_ECHO_RVOL >> 4: 
   break; 
 */ 
  // Key on 
  case DSP_KON >> 4: 
   if (sound_enabled && SPC_DSP_DATA) SPC_KeyOn(SPC_DSP_DATA); 
   break; 
 
  // Key off 
  case DSP_KOF >> 4: 
   if (sound_enabled && SPC_DSP_DATA) SPC_KeyOff(SPC_DSP_DATA); 
   break; 
 
  // Reset, mute, echo enable, noise clock select 
  case DSP_FLG >> 4: 
   noise_freq_factor = noise_freq_factors[SPC_DSP_DATA & 0x1F]; 
   if (noise_freq_latch > noise_freq_factor) 
    noise_freq_latch = noise_freq_factor; 
   if (SPC_DSP_DATA & DSP_FLG_RESET) 
   { 
    int voice; 
    for (voice = 0; voice < 7; voice++) 
    { 
     SPC_DSP[(voice << 4) + DSP_VOICE_ENVX] = 0; 
     SPC_DSP[(voice << 4) + DSP_VOICE_OUTX] = 0; 
    } 
    SPC_DSP[DSP_ENDX] = 0; 
    SPC_DSP[DSP_KON] = 0; 
    SPC_DSP[DSP_KOF] = 0; 
#ifdef LOG_SOUND_VOICE_OFF 
   if (SNDkeys) 
     printf("Voices off: %02X (BRR address overflow)\n", (unsigned) SNDkeys); 
#endif 
    SNDkeys = 0; 
   } 
   break; 
 
  // Sample end-block decoded 
  case DSP_ENDX >> 4: 
   SPC_DSP_DATA = 0; 
   break; 
  } 
 
  break; 
 
 case 0xD: 
  switch (addr_hi) 
  { 
  // Echo Feedback 
//case 0: 
// break; 
  // Invalid register 
//case 1: 
// break; 
  // Pitch modulation 
//case 2: 
// break; 
  // Noise enable 
//case 3: 
// break; 
  // Echo enable 
//case 4: 
// break; 
/* 
  // Source directory address 
  case 5: 
   break; 
 */ 
  // Echo start address 
//case 6: 
// break; 
  // Echo delay 
//case 7: 
// break; 
  } 
  break; 
 
 // FIR echo filter 
/* 
 case 0xF: 
  break; 
 */ 
 } 
 SPC_DSP[SPC_DSP_ADDR] = SPC_DSP_DATA; 
} 
 
void sound_pause(void) 
{ 
 if (sound_enabled) voice_stop(voice_handle); 
} 
 
#define VOLUME 255 
 
void sound_resume(void) 
{ 
    if (sound_enabled) 
    { 
     set_volume(VOLUME, -1); 
     voice_start(voice_handle); 
    } 
} 
 
BEGIN_DIGI_DRIVER_LIST 
 DIGI_DRIVER_SOUNDSCAPE 
 DIGI_DRIVER_AUDIODRIVE 
 DIGI_DRIVER_WINSOUNDSYS 
 DIGI_DRIVER_SB 
END_DIGI_DRIVER_LIST 
 
BEGIN_MIDI_DRIVER_LIST 
END_MIDI_DRIVER_LIST