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