diff --git a/src/GBA.cpp b/src/GBA.cpp index 53b79908..a2bbb828 100644 --- a/src/GBA.cpp +++ b/src/GBA.cpp @@ -3722,6 +3722,15 @@ void CPULoop(int ticks) } } + // we shouldn't be doing sound in stop state, but we loose synchronization + // if sound is disabled, so in stop state, soundTick will just produce + // mute sound + soundTicks -= clockTicks; + if(soundTicks <= 0) { + psoundTickfn(); + soundTicks += SOUND_CLOCK_TICKS; + } + if(!stopState) { if(timer0On) { timer0Ticks -= clockTicks; @@ -3828,14 +3837,7 @@ void CPULoop(int ticks) timerOverflow = 0; - // we shouldn't be doing sound in stop state, but we loose synchronization - // if sound is disabled, so in stop state, soundTick will just produce - // mute sound - soundTicks -= clockTicks; - if(soundTicks <= 0) { - psoundTickfn(); - soundTicks += SOUND_CLOCK_TICKS; - } + #ifdef PROFILING profilingTicks -= clockTicks; diff --git a/src/Sound.cpp b/src/Sound.cpp index ca3f63df..24f23903 100644 --- a/src/Sound.cpp +++ b/src/Sound.cpp @@ -2,6 +2,7 @@ // Copyright (C) 1999-2003 Forgotten // Copyright (C) 2004 Forgotten and the VBA development team // Copyright (C) 2004-2006 VBA development team +// Copyright (C) 2007 Shay Green (blargg) // 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 @@ -24,1481 +25,520 @@ #include "GBA.h" #include "Globals.h" #include "Util.h" +#include "Port.h" -#include "snd_interp.h" - - +#include "gb/gb_apu/Gb_Apu.h" +#include "gb/gb_apu/Multi_Buffer.h" #define USE_TICKS_AS 380 -#define SOUND_MAGIC 0x60000000 -#define SOUND_MAGIC_2 0x30000000 -#define NOISE_MAGIC 5 -extern bool stopState; +extern bool stopState; // TODO: silence sound when true -u8 soundWavePattern[4][32] = { - {0x01,0x01,0x01,0x01, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff}, - {0x01,0x01,0x01,0x01, - 0x01,0x01,0x01,0x01, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff}, - {0x01,0x01,0x01,0x01, - 0x01,0x01,0x01,0x01, - 0x01,0x01,0x01,0x01, - 0x01,0x01,0x01,0x01, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff}, - {0x01,0x01,0x01,0x01, - 0x01,0x01,0x01,0x01, - 0x01,0x01,0x01,0x01, - 0x01,0x01,0x01,0x01, - 0x01,0x01,0x01,0x01, - 0x01,0x01,0x01,0x01, - 0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff} -}; +int soundQuality = 2; // sample rate = 44100 / soundQuality +int soundInterpolation = 0; // 1 if PCM should have low-pass filtering +int soundVolume = 0; // emulator volume code (not linear) +static int soundVolume_; +bool soundEcho = false; +bool soundLowPass = false; +bool soundReverse = false; +int soundEnableFlag = 0x3ff; // emulator channels enabled -int soundFreqRatio[8] = { - 1048576, // 0 - 524288, // 1 - 262144, // 2 - 174763, // 3 - 131072, // 4 - 104858, // 5 - 87381, // 6 - 74898 // 7 -}; - -int soundShiftClock[16]= { - 2, // 0 - 4, // 1 - 8, // 2 - 16, // 3 - 32, // 4 - 64, // 5 - 128, // 6 - 256, // 7 - 512, // 8 - 1024, // 9 - 2048, // 10 - 4096, // 11 - 8192, // 12 - 16384, // 13 - 1, // 14 - 1 // 15 -}; - -int soundVolume = 0; - -u8 soundBuffer[6][735]; -u16 directBuffer[2][735]; -u16 soundFinalWave[1470]; - -int soundBufferLen = 1470; -int soundBufferTotalLen = 14700; -int soundQuality = 2; -int soundInterpolation = 0; -bool soundPaused = true; -int soundPlay = 0; +// Number of 16.8 MHz clocks until soundTick() will be called int soundTicks = soundQuality * USE_TICKS_AS; + +// Number of 16.8 MHz clocks between calls to soundTick() int SOUND_CLOCK_TICKS = soundQuality * USE_TICKS_AS; + +u16 soundFinalWave [1470]; // 16-bit SIGNED stereo sample buffer +int soundBufferLen = 1470; // size of sound buffer in BYTES +int soundBufferTotalLen = 14700; + + void interp_rate() { /* empty for now */ } + +// Unknown purpose +int soundDebug = 0; u32 soundNextPosition = 0; +bool soundOffFlag = false; +bool soundPaused = true; -int soundLevel1 = 0; -int soundLevel2 = 0; -int soundBalance = 0; -int soundMasterOn = 0; -int soundIndex = 0; -int soundBufferIndex = 0; -int soundDebug = 0; -bool soundOffFlag = false; - -int sound1On = 0; -int sound1ATL = 0; -int sound1Skip = 0; -int sound1Index = 0; -int sound1Continue = 0; -int sound1EnvelopeVolume = 0; -int sound1EnvelopeATL = 0; -int sound1EnvelopeUpDown = 0; -int sound1EnvelopeATLReload = 0; -int sound1SweepATL = 0; -int sound1SweepATLReload = 0; -int sound1SweepSteps = 0; -int sound1SweepUpDown = 0; -int sound1SweepStep = 0; -u8 *sound1Wave = soundWavePattern[2]; - -int sound2On = 0; -int sound2ATL = 0; -int sound2Skip = 0; -int sound2Index = 0; -int sound2Continue = 0; -int sound2EnvelopeVolume = 0; -int sound2EnvelopeATL = 0; -int sound2EnvelopeUpDown = 0; -int sound2EnvelopeATLReload = 0; -u8 *sound2Wave = soundWavePattern[2]; - -int sound3On = 0; -int sound3ATL = 0; -int sound3Skip = 0; -int sound3Index = 0; -int sound3Continue = 0; -int sound3OutputLevel = 0; -int sound3Last = 0; -u8 sound3WaveRam[0x20]; -int sound3Bank = 0; -int sound3DataSize = 0; -int sound3ForcedOutput = 0; - -int sound4On = 0; -int sound4Clock = 0; -int sound4ATL = 0; -int sound4Skip = 0; -int sound4Index = 0; -int sound4ShiftRight = 0x7f; -int sound4ShiftSkip = 0; -int sound4ShiftIndex = 0; -int sound4NSteps = 0; -int sound4CountDown = 0; -int sound4Continue = 0; -int sound4EnvelopeVolume = 0; -int sound4EnvelopeATL = 0; -int sound4EnvelopeUpDown = 0; -int sound4EnvelopeATLReload = 0; - -int soundControl = 0; - -int soundDSFifoAIndex = 0; -int soundDSFifoACount = 0; -int soundDSFifoAWriteIndex = 0; -bool soundDSAEnabled = false; -int soundDSATimer = 0; -u8 soundDSFifoA[32]; -u8 soundDSAValue = 0; - -int soundDSFifoBIndex = 0; -int soundDSFifoBCount = 0; -int soundDSFifoBWriteIndex = 0; -bool soundDSBEnabled = false; -int soundDSBTimer = 0; -u8 soundDSFifoB[32]; -u8 soundDSBValue = 0; - -int soundEnableFlag = 0x3ff; - -s16 soundFilter[4000]; -s16 soundRight[5] = { 0, 0, 0, 0, 0 }; -s16 soundLeft[5] = { 0, 0, 0, 0, 0 }; -int soundEchoIndex = 0; -bool soundEcho = false; -bool soundLowPass = false; -bool soundReverse = false; - -variable_desc soundSaveStruct[] = { - { &soundPaused, sizeof(int) }, - { &soundPlay, sizeof(int) }, - { &soundTicks, sizeof(int) }, - { &SOUND_CLOCK_TICKS, sizeof(int) }, - { &soundLevel1, sizeof(int) }, - { &soundLevel2, sizeof(int) }, - { &soundBalance, sizeof(int) }, - { &soundMasterOn, sizeof(int) }, - { &soundIndex, sizeof(int) }, - { &sound1On, sizeof(int) }, - { &sound1ATL, sizeof(int) }, - { &sound1Skip, sizeof(int) }, - { &sound1Index, sizeof(int) }, - { &sound1Continue, sizeof(int) }, - { &sound1EnvelopeVolume, sizeof(int) }, - { &sound1EnvelopeATL, sizeof(int) }, - { &sound1EnvelopeATLReload, sizeof(int) }, - { &sound1EnvelopeUpDown, sizeof(int) }, - { &sound1SweepATL, sizeof(int) }, - { &sound1SweepATLReload, sizeof(int) }, - { &sound1SweepSteps, sizeof(int) }, - { &sound1SweepUpDown, sizeof(int) }, - { &sound1SweepStep, sizeof(int) }, - { &sound2On, sizeof(int) }, - { &sound2ATL, sizeof(int) }, - { &sound2Skip, sizeof(int) }, - { &sound2Index, sizeof(int) }, - { &sound2Continue, sizeof(int) }, - { &sound2EnvelopeVolume, sizeof(int) }, - { &sound2EnvelopeATL, sizeof(int) }, - { &sound2EnvelopeATLReload, sizeof(int) }, - { &sound2EnvelopeUpDown, sizeof(int) }, - { &sound3On, sizeof(int) }, - { &sound3ATL, sizeof(int) }, - { &sound3Skip, sizeof(int) }, - { &sound3Index, sizeof(int) }, - { &sound3Continue, sizeof(int) }, - { &sound3OutputLevel, sizeof(int) }, - { &sound4On, sizeof(int) }, - { &sound4ATL, sizeof(int) }, - { &sound4Skip, sizeof(int) }, - { &sound4Index, sizeof(int) }, - { &sound4Clock, sizeof(int) }, - { &sound4ShiftRight, sizeof(int) }, - { &sound4ShiftSkip, sizeof(int) }, - { &sound4ShiftIndex, sizeof(int) }, - { &sound4NSteps, sizeof(int) }, - { &sound4CountDown, sizeof(int) }, - { &sound4Continue, sizeof(int) }, - { &sound4EnvelopeVolume, sizeof(int) }, - { &sound4EnvelopeATL, sizeof(int) }, - { &sound4EnvelopeATLReload, sizeof(int) }, - { &sound4EnvelopeUpDown, sizeof(int) }, - { &soundEnableFlag, sizeof(int) }, - { &soundControl, sizeof(int) }, - { &soundDSFifoAIndex, sizeof(int) }, - { &soundDSFifoACount, sizeof(int) }, - { &soundDSFifoAWriteIndex, sizeof(int) }, - { &soundDSAEnabled, sizeof(bool) }, - { &soundDSATimer, sizeof(int) }, - { &soundDSFifoA[0], 32 }, - { &soundDSAValue, sizeof(u8) }, - { &soundDSFifoBIndex, sizeof(int) }, - { &soundDSFifoBCount, sizeof(int) }, - { &soundDSFifoBWriteIndex, sizeof(int) }, - { &soundDSBEnabled, sizeof(int) }, - { &soundDSBTimer, sizeof(int) }, - { &soundDSFifoB[0], 32 }, - { &soundDSBValue, sizeof(u8) }, - { &soundBuffer[0][0], 6*735 }, - { &soundFinalWave[0], 2*735 }, - { NULL, 0 } -}; - -variable_desc soundSaveStructV2[] = { - { &sound3WaveRam[0], 0x20 }, - { &sound3Bank, sizeof(int) }, - { &sound3DataSize, sizeof(int) }, - { &sound3ForcedOutput, sizeof(int) }, - { NULL, 0 } -}; - - -// *** Begin snd_interp code - - -// and here is the implementation specific code, in a messier state than the stuff above - -extern bool timer0On; -extern int timer0Reload; -extern int timer0ClockReload; -extern bool timer1On; -extern int timer1Reload; -extern int timer1ClockReload; - -extern int SOUND_CLOCK_TICKS; -extern int soundInterpolation; - -inline double calc_rate(int timer) -{ - if (timer ? timer1On : timer0On) - { - return double(SOUND_CLOCK_TICKS) / - double((0x10000 - (timer ? timer1Reload : timer0Reload)) * - 1 << (timer ? timer1ClockReload : timer0ClockReload)); - } - else - { - return 1.; - } -} - -static foo_interpolate * interp[2]; - -class foo_interpolate_setup -{ +class Gba_Pcm { public: - foo_interpolate_setup() - { - for (int i = 0; i < 2; i++) - { - interp[i] = get_filter(0); - } - } + void init(); + void apply_control( int idx ); + void update( int dac ); + void end_frame( blip_time_t ); - ~foo_interpolate_setup() - { - for (int i = 0; i < 2; i++) - { - delete interp[i]; - } - } +private: + Blip_Buffer* output; + blip_time_t last_time; + int last_amp; + int shift; }; -static foo_interpolate_setup blah; +class Gba_Pcm_Fifo { +public: + int which; + Gba_Pcm pcm; + + void write_control( int data ); + void write_fifo( int data ); + void timer_overflowed( int which_timer ); -static int interpolation = 0; +private: + int readIndex; + int count; + int writeIndex; + bool enabled; + int timer; + u8 fifo [32]; + int dac; +}; -void interp_reset(int ch) +static Gba_Pcm_Fifo pcm [2]; +static Gb_Apu* gb_apu; +static Stereo_Buffer* stereo_buffer; + +static Blip_Synth pcm_synth [3]; // 32 kHz, 16 kHz, 8 kHz + +static inline blip_time_t blip_time() { - setSoundFn(); - interp[ch]->reset(); - interp_rate(); + return SOUND_CLOCK_TICKS - soundTicks; } -void interp_switch(int which) +void Gba_Pcm::init() { - for (int i = 0; i < 2; i++) + output = 0; + last_time = 0; + last_amp = 0; + shift = 0; +} + +void Gba_Pcm::apply_control( int idx ) +{ + shift = ~ioMem [SGCNT0_H] >> (2 + idx) & 1; + + int ch = 0; + if ( (soundEnableFlag >> idx & 0x100) && (ioMem [NR52] & 0x80) ) + ch = ioMem [SGCNT0_H+1] >> (idx * 4) & 3; + + Blip_Buffer* out = 0; + switch ( ch ) { - delete interp[i]; - interp[i] = get_filter(which); + case 1: out = stereo_buffer->right(); break; + case 2: out = stereo_buffer->left(); break; + case 3: out = stereo_buffer->center(); break; + } + + if ( output != out ) + { + if ( output ) + { + output->set_modified(); + pcm_synth [0].offset( blip_time(), -last_amp, output ); + } + last_amp = 0; + output = out; } - interpolation = which; - interp_rate(); } - -void interp_rate() +void Gba_Pcm::end_frame( blip_time_t time ) { - interp[0]->rate(calc_rate(soundDSATimer)); - interp[1]->rate(calc_rate(soundDSBTimer)); + last_time -= time; + if ( last_time < -2048 ) + last_time = -2048; + + if ( output ) + output->set_modified(); } - - -inline void interp_push(int ch, int sample) +void Gba_Pcm::update( int dac ) { - interp[ch]->push(sample); + if ( output ) + { + blip_time_t time = blip_time(); + + dac >>= shift; + int delta = dac - last_amp; + if ( delta ) + { + last_amp = dac; + + int filter = 0; + if ( soundInterpolation ) + { + // base filtering on how long since last sample was output + int period = time - last_time; + + int idx = (unsigned) period / 512; + if ( idx >= 3 ) + idx = 3; + + static int const filters [4] = { 0, 0, 1, 2 }; + filter = filters [idx]; + } + + pcm_synth [filter].offset( time, delta, output ); + } + last_time = time; + } } -inline int interp_pop(int ch) +void Gba_Pcm_Fifo::timer_overflowed( int which_timer ) { - return interp[ch]->pop(); + if ( which_timer == timer && enabled ) + { + if ( count <= 16 ) + { + // Need to fill FIFO + CPUCheckDMA( 3, which ? 4 : 2 ); + if ( count <= 16 ) + { + // Not filled by DMA, so fill with silence + int reg = which ? FIFOB_L : FIFOA_L; + for ( int n = 4; n--; ) + { + soundEvent(reg , (u16)0); + soundEvent(reg+2, (u16)0); + } + } + } + + // Read next sample from FIFO + count--; + dac = (s8) fifo [readIndex]; + readIndex = (readIndex + 1) & 31; + pcm.update( dac ); + } } -// *** End snd_interp code +void Gba_Pcm_Fifo::write_control( int data ) +{ + enabled = (data & 0x0300) ? true : false; + timer = (data & 0x0400) ? 1 : 0; + + if ( data & 0x0800 ) + { + // Reset + writeIndex = 0; + readIndex = 0; + count = 0; + dac = 0; + memset( fifo, 0, sizeof fifo ); + } + + pcm.apply_control( which ); + pcm.update( dac ); +} +void Gba_Pcm_Fifo::write_fifo( int data ) +{ + fifo [writeIndex ] = data & 0xFF; + fifo [writeIndex+1] = data >> 8; + count += 2; + writeIndex = (writeIndex + 2) & 31; +} +static void apply_control() +{ + pcm [0].pcm.apply_control( 0 ); + pcm [1].pcm.apply_control( 1 ); +} +static int gba_to_gb_sound( int addr ) +{ + static const int table [0x40] = + { + 0xFF10, 0,0xFF11,0xFF12,0xFF13,0xFF14, 0, 0, + 0xFF16,0xFF17, 0, 0,0xFF18,0xFF19, 0, 0, + 0xFF1A, 0,0xFF1B,0xFF1C,0xFF1D,0xFF1E, 0, 0, + 0xFF20,0xFF21, 0, 0,0xFF22,0xFF23, 0, 0, + 0xFF24,0xFF25, 0, 0,0xFF26, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37, + 0xFF38,0xFF39,0xFF3A,0xFF3B,0xFF3C,0xFF3D,0xFF3E,0xFF3F, + }; + if ( addr >= 0x60 && addr < 0xA0 ) + return table [addr - 0x60]; + return 0; +} void soundEvent(u32 address, u8 data) { - int freq = 0; + int gb_addr = gba_to_gb_sound( address ); + if ( gb_addr ) + { + ioMem[address] = data; + gb_apu->write_register( blip_time(), gb_addr, data ); + + if ( address == NR52 ) + apply_control(); + } + + // TODO: what about byte writes to SGCNT0_H etc.? +} - switch(address) { - case NR10: - data &= 0x7f; - sound1SweepATL = sound1SweepATLReload = 344 * ((data >> 4) & 7); - sound1SweepSteps = data & 7; - sound1SweepUpDown = data & 0x08; - sound1SweepStep = 0; - ioMem[address] = data; - break; - case NR11: - sound1Wave = soundWavePattern[data >> 6]; - sound1ATL = 172 * (64 - (data & 0x3f)); - ioMem[address] = data; - break; - case NR12: - sound1EnvelopeUpDown = data & 0x08; - sound1EnvelopeATLReload = 689 * (data & 7); - if((data & 0xF8) == 0) - sound1EnvelopeVolume = 0; - ioMem[address] = data; - break; - case NR13: - freq = (((int)(ioMem[NR14] & 7)) << 8) | data; - sound1ATL = 172 * (64 - (ioMem[NR11] & 0x3f)); - freq = 2048 - freq; - if(freq) { - sound1Skip = SOUND_MAGIC / freq; - } else - sound1Skip = 0; - ioMem[address] = data; - break; - case NR14: - data &= 0xC7; - freq = (((int)(data&7) << 8) | ioMem[NR13]); - freq = 2048 - freq; - sound1ATL = 172 * (64 - (ioMem[NR11] & 0x3f)); - sound1Continue = data & 0x40; - if(freq) { - sound1Skip = SOUND_MAGIC / freq; - } else - sound1Skip = 0; - if(data & 0x80) { - ioMem[NR52] |= 1; - sound1EnvelopeVolume = ioMem[NR12] >> 4; - sound1EnvelopeUpDown = ioMem[NR12] & 0x08; - sound1ATL = 172 * (64 - (ioMem[NR11] & 0x3f)); - sound1EnvelopeATLReload = sound1EnvelopeATL = 689 * (ioMem[NR12] & 7); - sound1SweepATL = sound1SweepATLReload = 344 * ((ioMem[NR10] >> 4) & 7); - sound1SweepSteps = ioMem[NR10] & 7; - sound1SweepUpDown = ioMem[NR10] & 0x08; - sound1SweepStep = 0; - - sound1Index = 0; - sound1On = 1; - } - ioMem[address] = data; - break; - case NR21: - sound2Wave = soundWavePattern[data >> 6]; - sound2ATL = 172 * (64 - (data & 0x3f)); - ioMem[address] = data; - break; - case NR22: - sound2EnvelopeUpDown = data & 0x08; - sound2EnvelopeATLReload = 689 * (data & 7); - if((data & 0xF8) == 0) - sound2EnvelopeVolume = 0; - ioMem[address] = data; - break; - case NR23: - freq = (((int)(ioMem[NR24] & 7)) << 8) | data; - sound2ATL = 172 * (64 - (ioMem[NR21] & 0x3f)); - freq = 2048 - freq; - if(freq) { - sound2Skip = SOUND_MAGIC / freq; - } else - sound2Skip = 0; - ioMem[address] = data; - break; - case NR24: - data &= 0xC7; - freq = (((int)(data&7) << 8) | ioMem[NR23]); - freq = 2048 - freq; - sound2ATL = 172 * (64 - (ioMem[NR21] & 0x3f)); - sound2Continue = data & 0x40; - if(freq) { - sound2Skip = SOUND_MAGIC / freq; - } else - sound2Skip = 0; - if(data & 0x80) { - ioMem[NR52] |= 2; - sound2EnvelopeVolume = ioMem[NR22] >> 4; - sound2EnvelopeUpDown = ioMem[NR22] & 0x08; - sound2ATL = 172 * (64 - (ioMem[NR21] & 0x3f)); - sound2EnvelopeATLReload = sound2EnvelopeATL = 689 * (ioMem[NR22] & 7); - - sound2Index = 0; - sound2On = 1; - } - ioMem[address] = data; - break; - case NR30: - data &= 0xe0; - if(!(data & 0x80)) { - ioMem[NR52] &= 0xfb; - sound3On = 0; - } - if(((data >> 6) & 1) != sound3Bank) - memcpy(&ioMem[0x90], &sound3WaveRam[(((data >> 6) & 1) * 0x10)^0x10], - 0x10); - sound3Bank = (data >> 6) & 1; - sound3DataSize = (data >> 5) & 1; - ioMem[address] = data; - break; - case NR31: - sound3ATL = 172 * (256-data); - ioMem[address] = data; - break; - case NR32: - data &= 0xe0; - sound3OutputLevel = (data >> 5) & 3; - sound3ForcedOutput = (data >> 7) & 1; - ioMem[address] = data; - break; - case NR33: - freq = 2048 - (((int)(ioMem[NR34]&7) << 8) | data); - if(freq) { - sound3Skip = SOUND_MAGIC_2 / freq; - } else - sound3Skip = 0; - ioMem[address] = data; - break; - case NR34: - data &= 0xc7; - freq = 2048 - (((data &7) << 8) | (int)ioMem[NR33]); - if(freq) { - sound3Skip = SOUND_MAGIC_2 / freq; - } else { - sound3Skip = 0; - } - sound3Continue = data & 0x40; - if((data & 0x80) && (ioMem[NR30] & 0x80)) { - ioMem[NR52] |= 4; - sound3ATL = 172 * (256 - ioMem[NR31]); - sound3Index = 0; - sound3On = 1; - } - ioMem[address] = data; - break; - case NR41: - data &= 0x3f; - sound4ATL = 172 * (64 - (data & 0x3f)); - ioMem[address] = data; - break; - case NR42: - sound4EnvelopeUpDown = data & 0x08; - sound4EnvelopeATLReload = 689 * (data & 7); - if((data & 0xF8) == 0) - sound4EnvelopeVolume = 0; - ioMem[address] = data; - break; - case NR43: - freq = soundFreqRatio[data & 7]; - sound4NSteps = data & 0x08; - - sound4Skip = (freq << 8) / NOISE_MAGIC; - - sound4Clock = data >> 4; - - freq = freq / soundShiftClock[sound4Clock]; - - sound4ShiftSkip = (freq << 8) / NOISE_MAGIC; - ioMem[address] = data; - break; - case NR44: - data &= 0xc0; - sound4Continue = data & 0x40; - if(data & 0x80) { - ioMem[NR52] |= 8; - sound4EnvelopeVolume = ioMem[NR42] >> 4; - sound4EnvelopeUpDown = ioMem[NR42] & 0x08; - sound4ATL = 172 * (64 - (ioMem[NR41] & 0x3f)); - sound4EnvelopeATLReload = sound4EnvelopeATL = 689 * (ioMem[NR42] & 7); - - sound4On = 1; - - sound4Index = 0; - sound4ShiftIndex = 0; - - freq = soundFreqRatio[ioMem[NR43] & 7]; - - sound4Skip = (freq << 8) / NOISE_MAGIC; - - sound4NSteps = ioMem[NR43] & 0x08; - - freq = freq / soundShiftClock[ioMem[NR43] >> 4]; - - sound4ShiftSkip = (freq << 8) / NOISE_MAGIC; - if(sound4NSteps) - sound4ShiftRight = 0x7fff; - else - sound4ShiftRight = 0x7f; - } - ioMem[address] = data; - break; - case NR50: - data &= 0x77; - soundLevel1 = data & 7; - soundLevel2 = (data >> 4) & 7; - ioMem[address] = data; - break; - case NR51: - soundBalance = (data & soundEnableFlag); - ioMem[address] = data; - break; - case NR52: - data &= 0x80; - data |= ioMem[NR52] & 15; - soundMasterOn = data & 0x80; - if(!(data & 0x80)) { - sound1On = 0; - sound2On = 0; - sound3On = 0; - sound4On = 0; - } - ioMem[address] = data; - break; - case 0x90: - case 0x91: - case 0x92: - case 0x93: - case 0x94: - case 0x95: - case 0x96: - case 0x97: - case 0x98: - case 0x99: - case 0x9a: - case 0x9b: - case 0x9c: - case 0x9d: - case 0x9e: - case 0x9f: - sound3WaveRam[(sound3Bank*0x10)^0x10+(address&15)] = data; - break; - } +static void apply_volume( bool apu_only = false ) +{ + if ( !apu_only ) + soundVolume_ = soundVolume; + + // Emulator volume + static float const vols [6] = { 1, 2, 3, 4, 0.25, 0.5 }; + double const volume = vols [soundVolume_]; + + if ( gb_apu ) + { + static float const apu_vols [4] = { 0.25, 0.5, 1, 0.25 }; + gb_apu->volume( volume * apu_vols [ioMem [SGCNT0_H] & 3] ); + } + + if ( !apu_only ) + { + for ( int i = 0; i < 3; i++ ) + pcm_synth [i].volume( 0.66 / 256 * volume ); + } } void soundEvent(u32 address, u16 data) { - switch(address) { - case SGCNT0_H: - data &= 0xFF0F; - soundControl = data & 0x770F; - if(data & 0x0800) { - soundDSFifoAWriteIndex = 0; - soundDSFifoAIndex = 0; - soundDSFifoACount = 0; - soundDSAValue = 0; - memset(soundDSFifoA, 0, 32); - } - soundDSAEnabled = (data & 0x0300) ? true : false; - soundDSATimer = (data & 0x0400) ? 1 : 0; - if(data & 0x8000) { - soundDSFifoBWriteIndex = 0; - soundDSFifoBIndex = 0; - soundDSFifoBCount = 0; - soundDSBValue = 0; - memset(soundDSFifoB, 0, 32); - } - soundDSBEnabled = (data & 0x3000) ? true : false; - soundDSBTimer = (data & 0x4000) ? 1 : 0; - *((u16 *)&ioMem[address]) = soundControl; - break; - case FIFOA_L: - case FIFOA_H: - soundDSFifoA[soundDSFifoAWriteIndex++] = data & 0xFF; - soundDSFifoA[soundDSFifoAWriteIndex++] = data >> 8; - soundDSFifoACount += 2; - soundDSFifoAWriteIndex &= 31; - *((u16 *)&ioMem[address]) = data; - break; - case FIFOB_L: - case FIFOB_H: - soundDSFifoB[soundDSFifoBWriteIndex++] = data & 0xFF; - soundDSFifoB[soundDSFifoBWriteIndex++] = data >> 8; - soundDSFifoBCount += 2; - soundDSFifoBWriteIndex &= 31; - *((u16 *)&ioMem[address]) = data; - break; - case 0x88: - data &= 0xC3FF; - *((u16 *)&ioMem[address]) = data; - break; - case 0x90: - case 0x92: - case 0x94: - case 0x96: - case 0x98: - case 0x9a: - case 0x9c: - case 0x9e: - *((u16 *)&sound3WaveRam[(sound3Bank*0x10)^0x10+(address&14)]) = data; - *((u16 *)&ioMem[address]) = data; - break; - } -} - -void soundChannel1() -{ - int vol = sound1EnvelopeVolume; - - int freq = 0; - int value = 0; - - if(sound1On && (sound1ATL || !sound1Continue)) { - sound1Index += soundQuality*sound1Skip; - sound1Index &= 0x1fffffff; - - value = ((s8)sound1Wave[sound1Index>>24]) * vol; - } - - soundBuffer[0][soundIndex] = value; - - - if(sound1On) { - if(sound1ATL) { - sound1ATL-=soundQuality; - - if(sound1ATL <=0 && sound1Continue) { - ioMem[NR52] &= 0xfe; - sound1On = 0; - } - } - - if(sound1EnvelopeATL) { - sound1EnvelopeATL-=soundQuality; - - if(sound1EnvelopeATL<=0) { - if(sound1EnvelopeUpDown) { - if(sound1EnvelopeVolume < 15) - sound1EnvelopeVolume++; - } else { - if(sound1EnvelopeVolume) - sound1EnvelopeVolume--; - } - - sound1EnvelopeATL += sound1EnvelopeATLReload; - } - } - - if(sound1SweepATL) { - sound1SweepATL-=soundQuality; - - if(sound1SweepATL<=0) { - freq = (((int)(ioMem[NR14]&7) << 8) | ioMem[NR13]); - - int updown = 1; - - if(sound1SweepUpDown) - updown = -1; - - int newfreq = 0; - if(sound1SweepSteps) { - newfreq = freq + updown * freq / (1 << sound1SweepSteps); - if(newfreq == freq) - newfreq = 0; - } else - newfreq = freq; - - if(newfreq < 0) { - sound1SweepATL += sound1SweepATLReload; - } else if(newfreq > 2047) { - sound1SweepATL = 0; - sound1On = 0; - ioMem[NR52] &= 0xfe; - } else { - sound1SweepATL += sound1SweepATLReload; - sound1Skip = SOUND_MAGIC/(2048 - newfreq); - - ioMem[NR13] = newfreq & 0xff; - ioMem[NR14] = (ioMem[NR14] & 0xf8) |((newfreq >> 8) & 7); - } - } - } - } -} - -void soundChannel2() -{ - // int freq = 0; - int vol = sound2EnvelopeVolume; - - int value = 0; - - if(sound2On && (sound2ATL || !sound2Continue)) { - sound2Index += soundQuality*sound2Skip; - sound2Index &= 0x1fffffff; - - value = ((s8)sound2Wave[sound2Index>>24]) * vol; - } - - soundBuffer[1][soundIndex] = value; - - if(sound2On) { - if(sound2ATL) { - sound2ATL-=soundQuality; - - if(sound2ATL <= 0 && sound2Continue) { - ioMem[NR52] &= 0xfd; - sound2On = 0; - } - } - - if(sound2EnvelopeATL) { - sound2EnvelopeATL-=soundQuality; - - if(sound2EnvelopeATL <= 0) { - if(sound2EnvelopeUpDown) { - if(sound2EnvelopeVolume < 15) - sound2EnvelopeVolume++; - } else { - if(sound2EnvelopeVolume) - sound2EnvelopeVolume--; - } - sound2EnvelopeATL += sound2EnvelopeATLReload; - } - } - } -} - -void soundChannel3() -{ - int value = sound3Last; - - if(sound3On && (sound3ATL || !sound3Continue)) { - sound3Index += soundQuality*sound3Skip; - if(sound3DataSize) { - sound3Index &= 0x3fffffff; - value = sound3WaveRam[sound3Index>>25]; - } else { - sound3Index &= 0x1fffffff; - value = sound3WaveRam[sound3Bank*0x10 + (sound3Index>>25)]; - } - - if( (sound3Index & 0x01000000)) { - value &= 0x0f; - } else { - value >>= 4; - } - - value -= 8; - value *= 2; - - if(sound3ForcedOutput) { - value = ((value >> 1) + value) >> 1; - } else { - switch(sound3OutputLevel) { - case 0: - value = 0; - break; - case 1: - break; - case 2: - value = (value >> 1); - break; - case 3: - value = (value >> 2); - break; - } - } - sound3Last = value; - } - - soundBuffer[2][soundIndex] = value; - - if(sound3On) { - if(sound3ATL) { - sound3ATL-=soundQuality; - - if(sound3ATL <= 0 && sound3Continue) { - ioMem[NR52] &= 0xfb; - sound3On = 0; - } - } - } -} - -void soundChannel4() -{ - int vol = sound4EnvelopeVolume; - - int value = 0; - - if(sound4Clock <= 0x0c) { - if(sound4On && (sound4ATL || !sound4Continue)) { - sound4Index += soundQuality*sound4Skip; - sound4ShiftIndex += soundQuality*sound4ShiftSkip; - - if(sound4NSteps) { - while(sound4ShiftIndex > 0x1fffff) { - sound4ShiftRight = (((sound4ShiftRight << 6) ^ - (sound4ShiftRight << 5)) & 0x40) | - (sound4ShiftRight >> 1); - sound4ShiftIndex -= 0x200000; - } - } else { - while(sound4ShiftIndex > 0x1fffff) { - sound4ShiftRight = (((sound4ShiftRight << 14) ^ - (sound4ShiftRight << 13)) & 0x4000) | - (sound4ShiftRight >> 1); - - sound4ShiftIndex -= 0x200000; - } - } - - sound4Index &= 0x1fffff; - sound4ShiftIndex &= 0x1fffff; - - value = ((sound4ShiftRight & 1)*2-1) * vol; - } else { - value = 0; - } - } - - soundBuffer[3][soundIndex] = value; - - if(sound4On) { - if(sound4ATL) { - sound4ATL-=soundQuality; - - if(sound4ATL <= 0 && sound4Continue) { - ioMem[NR52] &= 0xfd; - sound4On = 0; - } - } - - if(sound4EnvelopeATL) { - sound4EnvelopeATL-=soundQuality; - - if(sound4EnvelopeATL <= 0) { - if(sound4EnvelopeUpDown) { - if(sound4EnvelopeVolume < 15) - sound4EnvelopeVolume++; - } else { - if(sound4EnvelopeVolume) - sound4EnvelopeVolume--; - } - sound4EnvelopeATL += sound4EnvelopeATLReload; - } - } - } -} - - - - -void soundDirectSoundATimer() -{ - if(soundDSAEnabled) { - if(soundDSFifoACount <= 16) { - CPUCheckDMA(3, 2); - if(soundDSFifoACount <= 16) { - soundEvent(FIFOA_L, (u16)0); - soundEvent(FIFOA_H, (u16)0); - soundEvent(FIFOA_L, (u16)0); - soundEvent(FIFOA_H, (u16)0); - soundEvent(FIFOA_L, (u16)0); - soundEvent(FIFOA_H, (u16)0); - soundEvent(FIFOA_L, (u16)0); - soundEvent(FIFOA_H, (u16)0); - } - } - soundDSAValue = (soundDSFifoA[soundDSFifoAIndex]); - interp_push(0, (s8)soundDSAValue << 8); - soundDSFifoAIndex = (++soundDSFifoAIndex) & 31; - soundDSFifoACount--; - } else - soundDSAValue = 0; -} - -void soundDirectSoundA() -{ - directBuffer[0][soundIndex] = interp_pop(0); //soundDSAValue; -} - -void soundDirectSoundB() -{ - directBuffer[1][soundIndex] = interp_pop(1); //soundDSBValue; -} - -void soundDirectSoundBTimer() -{ - if(soundDSBEnabled) { - if(soundDSFifoBCount <= 16) { - CPUCheckDMA(3, 4); - if(soundDSFifoBCount <= 16) { - soundEvent(FIFOB_L, (u16)0); - soundEvent(FIFOB_H, (u16)0); - soundEvent(FIFOB_L, (u16)0); - soundEvent(FIFOB_H, (u16)0); - soundEvent(FIFOB_L, (u16)0); - soundEvent(FIFOB_H, (u16)0); - soundEvent(FIFOB_L, (u16)0); - soundEvent(FIFOB_H, (u16)0); - } - } - - soundDSBValue = (soundDSFifoB[soundDSFifoBIndex]); - interp_push(1, (s8)soundDSBValue << 8); - soundDSFifoBIndex = (++soundDSFifoBIndex) & 31; - soundDSFifoBCount--; - } else { - soundDSBValue = 0; - } + switch ( address ) + { + case SGCNT0_H: + WRITE16LE( &ioMem[address], data ); + pcm [0].write_control( data ); + pcm [1].write_control( data >> 4 ); + apply_volume( true ); + break; + + case FIFOA_L: + case FIFOA_H: + pcm [0].write_fifo( data ); + WRITE16LE( &ioMem[address], data ); + break; + + case FIFOB_L: + case FIFOB_H: + pcm [1].write_fifo( data ); + WRITE16LE( &ioMem[address], data ); + break; + + case 0x88: + data &= 0xC3FF; + WRITE16LE( &ioMem[address], data ); + break; + + default: + soundEvent( address & ~1, (u8) (data ) ); // even + soundEvent( address | 1, (u8) (data >> 8) ); // odd + break; + } } void soundTimerOverflow(int timer) { - if(soundDSAEnabled && (soundDSATimer == timer)) { - soundDirectSoundATimer(); - } - if(soundDSBEnabled && (soundDSBTimer == timer)) { - soundDirectSoundBTimer(); - } + pcm [0].timer_overflowed( timer ); + pcm [1].timer_overflowed( timer ); } -#ifndef max -#define max(a,b) (a)<(b)?(b):(a) -#endif - -void soundMix() +static void end_frame( blip_time_t time ) { - int res = 0; - int cgbRes = 0; - int ratio = ioMem[0x82] & 3; - int dsaRatio = ioMem[0x82] & 4; - int dsbRatio = ioMem[0x82] & 8; - - if(soundBalance & 16) { - cgbRes = ((s8)soundBuffer[0][soundIndex]); - } - if(soundBalance & 32) { - cgbRes += ((s8)soundBuffer[1][soundIndex]); - } - if(soundBalance & 64) { - cgbRes += ((s8)soundBuffer[2][soundIndex]); - } - if(soundBalance & 128) { - cgbRes += ((s8)soundBuffer[3][soundIndex]); - } - - if((soundControl & 0x0200) && (soundEnableFlag & 0x100)){ - if(!dsaRatio) - res = ((s16)directBuffer[0][soundIndex])>>1; - else - res = ((s16)directBuffer[0][soundIndex]); - } - - if((soundControl & 0x2000) && (soundEnableFlag & 0x200)){ - if(!dsbRatio) - res += ((s16)directBuffer[1][soundIndex])>>1; - else - res += ((s16)directBuffer[1][soundIndex]); - } - - res = (res * 170) >> 8; - cgbRes = (cgbRes * 52 * soundLevel1); - - switch(ratio) { - case 0: - case 3: // prohibited, but 25% - cgbRes >>= 2; - break; - case 1: - cgbRes >>= 1; - break; - case 2: - break; - } - - res += cgbRes; - - if(soundEcho) { - res *= 2; - res += soundFilter[soundEchoIndex]; - res /= 2; - soundFilter[soundEchoIndex++] = res; - } - - if(soundLowPass) { - soundLeft[4] = soundLeft[3]; - soundLeft[3] = soundLeft[2]; - soundLeft[2] = soundLeft[1]; - soundLeft[1] = soundLeft[0]; - soundLeft[0] = res; - res = (soundLeft[4] + 2*soundLeft[3] + 8*soundLeft[2] + 2*soundLeft[1] + - soundLeft[0])/14; - } - - switch(soundVolume) { - case 0: - case 1: - case 2: - case 3: - res *= (soundVolume+1); - break; - case 4: - res >>= 2; - break; - case 5: - res >>= 1; - break; - } - - if(res > 32767) - res = 32767; - if(res < -32768) - res = -32768; - - if(soundReverse) - soundFinalWave[++soundBufferIndex] = res; - else - soundFinalWave[soundBufferIndex++] = res; - - res = 0; - cgbRes = 0; - - if(soundBalance & 1) { - cgbRes = ((s8)soundBuffer[0][soundIndex]); - } - if(soundBalance & 2) { - cgbRes += ((s8)soundBuffer[1][soundIndex]); - } - if(soundBalance & 4) { - cgbRes += ((s8)soundBuffer[2][soundIndex]); - } - if(soundBalance & 8) { - cgbRes += ((s8)soundBuffer[3][soundIndex]); - } - - if((soundControl & 0x0100) && (soundEnableFlag & 0x100)){ - if(!dsaRatio) - res = ((s16)directBuffer[0][soundIndex])>>1; - else - res = ((s16)directBuffer[0][soundIndex]); - } - - if((soundControl & 0x1000) && (soundEnableFlag & 0x200)){ - if(!dsbRatio) - res += ((s16)directBuffer[1][soundIndex])>>1; - else - res += ((s16)directBuffer[1][soundIndex]); - } - - res = (res * 170) >> 8; - cgbRes = (cgbRes * 52 * soundLevel1); - - switch(ratio) { - case 0: - case 3: // prohibited, but 25% - cgbRes >>= 2; - break; - case 1: - cgbRes >>= 1; - break; - case 2: - break; - } - - res += cgbRes; - - if(soundEcho) { - res *= 2; - res += soundFilter[soundEchoIndex]; - res /= 2; - soundFilter[soundEchoIndex++] = res; - - if(soundEchoIndex >= 4000) - soundEchoIndex = 0; - } - - if(soundLowPass) { - soundRight[4] = soundRight[3]; - soundRight[3] = soundRight[2]; - soundRight[2] = soundRight[1]; - soundRight[1] = soundRight[0]; - soundRight[0] = res; - res = (soundRight[4] + 2*soundRight[3] + 8*soundRight[2] + 2*soundRight[1] + - soundRight[0])/14; - } - - switch(soundVolume) { - case 0: - case 1: - case 2: - case 3: - res *= (soundVolume+1); - break; - case 4: - res >>= 2; - break; - case 5: - res >>= 1; - break; - } - - if(res > 32767) - res = 32767; - if(res < -32768) - res = -32768; - - if(soundReverse) - soundFinalWave[-1+soundBufferIndex++] = res; - else - soundFinalWave[soundBufferIndex++] = res; + pcm [0].pcm.end_frame( time ); + pcm [1].pcm.end_frame( time ); + + gb_apu ->end_frame( time ); + stereo_buffer->end_frame( time ); } -// soundTick gets called a lot -// if we are operating normally -// call normalSoundTick to avoid -// all the comparison checks. - -void normalsoundTick() +static void flush_samples() { - soundChannel1(); - soundChannel2(); - soundChannel3(); - soundChannel4(); - soundDirectSoundA(); - soundDirectSoundB(); - soundMix(); - soundIndex++; - - if(2*soundBufferIndex >= soundBufferLen) + // number of samples in output buffer + int const out_buf_size = soundBufferLen / sizeof *soundFinalWave; + + // Keep filling and writing soundFinalWave until it can't be fully filled + while ( stereo_buffer->samples_avail() >= out_buf_size ) { - systemWriteDataToSoundBuffer(); - soundIndex = 0; - soundBufferIndex = 0; - } + stereo_buffer->read_samples( (blip_sample_t*) soundFinalWave, out_buf_size ); + if(systemSoundOn) + { + if(soundPaused) + soundResume(); + + systemWriteDataToSoundBuffer(); + } + } } - -void soundTick() +static void soundTick() { - if(systemSoundOn) { - if(soundMasterOn && !stopState) { - soundChannel1(); - soundChannel2(); - soundChannel3(); - soundChannel4(); - soundDirectSoundA(); - soundDirectSoundB(); - soundMix(); - } else { - soundFinalWave[soundBufferIndex++] = 0; - soundFinalWave[soundBufferIndex++] = 0; - } - - soundIndex++; - - if(2*soundBufferIndex >= soundBufferLen) { - if(systemSoundOn) { - if(soundPaused) { - soundResume(); - setSoundFn(); - } - - systemWriteDataToSoundBuffer(); - } - soundIndex = 0; - soundBufferIndex = 0; - } - } + if ( systemSoundOn && gb_apu && stereo_buffer ) + { + // Run sound hardware to present + end_frame( SOUND_CLOCK_TICKS ); + + flush_samples(); + + if ( soundVolume_ != soundVolume ) + apply_volume(); + } } -void (*psoundTickfn)()=soundTick; +void (*psoundTickfn)() = soundTick; - -void setSoundFn() +static void apply_muting() { - if (systemSoundOn && !soundPaused && soundMasterOn) - psoundTickfn = normalsoundTick; - else - psoundTickfn = soundTick; - if (soundInterpolation != interpolation) - interp_switch(soundInterpolation); +if ( !stereo_buffer || !ioMem ) + return; + // PCM + apply_control(); + + if ( gb_apu ) + { + // APU + for ( int i = 0; i < 4; i++ ) + { + if ( soundEnableFlag >> i & 1 ) + gb_apu->set_output( stereo_buffer->center(), + stereo_buffer->left(), stereo_buffer->right(), i ); + else + gb_apu->set_output( 0, 0, 0, i ); + } + } +} +static void remake_stereo_buffer() +{ + // Clears pointers kept to old stereo_buffer + pcm [0].pcm.init(); + pcm [1].pcm.init(); + + // Stereo_Buffer + delete stereo_buffer; + stereo_buffer = 0; + + stereo_buffer = new Stereo_Buffer; // TODO: handle out of memory + long const sample_rate = 44100 / soundQuality; + stereo_buffer->set_sample_rate( sample_rate ); // TODO: handle out of memory + stereo_buffer->clock_rate( gb_apu->clock_rate ); + + // PCM + pcm [0].which = 0; + pcm [1].which = 1; + for ( int i = 0; i < 3; i++ ) + { + int freq = 32768 >> i; + pcm_synth [i].treble_eq( blip_eq_t( 0, 0, sample_rate, freq / 2 ) ); + } + + // APU + if ( !gb_apu ) + gb_apu = new Gb_Apu; // TODO: handle out of memory + + gb_apu->reset( gb_apu->mode_agb, true ); + + apply_muting(); + apply_volume(); } void setsystemSoundOn(bool value) { systemSoundOn = value; - setSoundFn(); } void setsoundPaused(bool value) { soundPaused = value; - setSoundFn(); } -void setsoundMasterOn(bool value) -{ - soundMasterOn = value; - setSoundFn(); -} - - - void soundShutdown() { - systemSoundShutdown(); + systemSoundShutdown(); } void soundPause() { - systemSoundPause(); - setsoundPaused(true); + systemSoundPause(); + setsoundPaused(true); } void soundResume() { - systemSoundResume(); - setsoundPaused(false); + systemSoundResume(); + setsoundPaused(false); } void soundEnable(int channels) { - int c = channels & 0x0f; - - soundEnableFlag |= ((channels & 0x30f) |c | (c << 4)); - if(ioMem) - soundBalance = (ioMem[NR51] & soundEnableFlag); + soundEnableFlag = channels; + apply_muting(); } void soundDisable(int channels) { - int c = channels & 0x0f; - - soundEnableFlag &= (~((channels & 0x30f)|c|(c<<4))); - if(ioMem) - soundBalance = (ioMem[NR51] & soundEnableFlag); + soundEnableFlag &= ~channels; + apply_muting(); } int soundGetEnable() { - return (soundEnableFlag & 0x30f); + return (soundEnableFlag & 0x30f); } void soundReset() { - systemSoundReset(); - - setsoundPaused(true); - soundPlay = 0; - SOUND_CLOCK_TICKS = soundQuality * USE_TICKS_AS; - soundTicks = SOUND_CLOCK_TICKS; - soundNextPosition = 0; - setsoundMasterOn(true); - soundIndex = 0; - soundBufferIndex = 0; - soundLevel1 = 7; - soundLevel2 = 7; - - sound1On = 0; - sound1ATL = 0; - sound1Skip = 0; - sound1Index = 0; - sound1Continue = 0; - sound1EnvelopeVolume = 0; - sound1EnvelopeATL = 0; - sound1EnvelopeUpDown = 0; - sound1EnvelopeATLReload = 0; - sound1SweepATL = 0; - sound1SweepATLReload = 0; - sound1SweepSteps = 0; - sound1SweepUpDown = 0; - sound1SweepStep = 0; - sound1Wave = soundWavePattern[2]; - - sound2On = 0; - sound2ATL = 0; - sound2Skip = 0; - sound2Index = 0; - sound2Continue = 0; - sound2EnvelopeVolume = 0; - sound2EnvelopeATL = 0; - sound2EnvelopeUpDown = 0; - sound2EnvelopeATLReload = 0; - sound2Wave = soundWavePattern[2]; - - sound3On = 0; - sound3ATL = 0; - sound3Skip = 0; - sound3Index = 0; - sound3Continue = 0; - sound3OutputLevel = 0; - sound3Last = 0; - sound3Bank = 0; - sound3DataSize = 0; - sound3ForcedOutput = 0; - - sound4On = 0; - sound4Clock = 0; - sound4ATL = 0; - sound4Skip = 0; - sound4Index = 0; - sound4ShiftRight = 0x7f; - sound4NSteps = 0; - sound4CountDown = 0; - sound4Continue = 0; - sound4EnvelopeVolume = 0; - sound4EnvelopeATL = 0; - sound4EnvelopeUpDown = 0; - sound4EnvelopeATLReload = 0; - - sound1On = 0; - sound2On = 0; - sound3On = 0; - sound4On = 0; - - int addr = 0x90; - - while(addr < 0xA0) { - ioMem[addr++] = 0x00; - ioMem[addr++] = 0xff; - } - - addr = 0; - while(addr < 0x20) { - sound3WaveRam[addr++] = 0x00; - sound3WaveRam[addr++] = 0xff; - } - - memset(soundFinalWave, 0, soundBufferLen); - - memset(soundFilter, 0, sizeof(soundFilter)); - soundEchoIndex = 0; + systemSoundReset(); + + remake_stereo_buffer(); + + setsoundPaused(true); + SOUND_CLOCK_TICKS = 167772; + soundTicks = SOUND_CLOCK_TICKS; + + soundNextPosition = 0; + + soundEvent( NR52, (u8) 0x80 ); } bool soundInit() { - if(systemSoundInit()) { - memset(soundBuffer[0], 0, 735*2); - memset(soundBuffer[1], 0, 735*2); - memset(soundBuffer[2], 0, 735*2); - memset(soundBuffer[3], 0, 735*2); - - memset(soundFinalWave, 0, soundBufferLen); - - soundPaused = true; - return true; - } - return false; + if ( !systemSoundInit() ) + return false; + + soundPaused = true; + return true; } void soundSetQuality(int quality) { - if(soundQuality != quality && systemCanChangeSoundQuality()) { - if(!soundOffFlag) - soundShutdown(); - soundQuality = quality; - soundNextPosition = 0; - if(!soundOffFlag) - soundInit(); - SOUND_CLOCK_TICKS = USE_TICKS_AS * soundQuality; - soundIndex = 0; - soundBufferIndex = 0; - } else if(soundQuality != quality) { - soundNextPosition = 0; - SOUND_CLOCK_TICKS = USE_TICKS_AS * soundQuality; - soundIndex = 0; - soundBufferIndex = 0; - } + if ( soundQuality != quality ) + { + if ( systemCanChangeSoundQuality() ) + { + if ( !soundOffFlag ) + soundShutdown(); + + soundQuality = quality; + soundNextPosition = 0; + + if ( !soundOffFlag ) + soundInit(); + } + else + { + soundQuality = quality; + soundNextPosition = 0; + } + + remake_stereo_buffer(); + } } void soundSaveGame(gzFile gzFile) { - utilWriteData(gzFile, soundSaveStruct); - utilWriteData(gzFile, soundSaveStructV2); - - utilGzWrite(gzFile, &soundQuality, sizeof(int)); + // TODO: implement } void soundReadGame(gzFile gzFile, int version) { - utilReadData(gzFile, soundSaveStruct); - if(version >= SAVE_GAME_VERSION_3) { - utilReadData(gzFile, soundSaveStructV2); - } else { - sound3Bank = (ioMem[NR30] >> 6) & 1; - sound3DataSize = (ioMem[NR30] >> 5) & 1; - sound3ForcedOutput = (ioMem[NR32] >> 7) & 1; - // nothing better to do here... - memcpy(&sound3WaveRam[0x00], &ioMem[0x90], 0x10); - memcpy(&sound3WaveRam[0x10], &ioMem[0x90], 0x10); - } - soundBufferIndex = soundIndex * 2; - - int quality = 1; - utilGzRead(gzFile, &quality, sizeof(int)); - soundSetQuality(quality); - - sound1Wave = soundWavePattern[ioMem[NR11] >> 6]; - sound2Wave = soundWavePattern[ioMem[NR21] >> 6]; + // TODO: implement } diff --git a/src/Sound.h b/src/Sound.h index a6799b97..620e61c3 100644 --- a/src/Sound.h +++ b/src/Sound.h @@ -50,7 +50,6 @@ #define FIFOB_L 0xa4 #define FIFOB_H 0xa6 -extern void setSoundFn(); extern void (*psoundTickfn)(); extern void soundShutdown(); extern bool soundInit(); @@ -68,7 +67,6 @@ extern void soundTimerOverflow(int); extern void soundSetQuality(int); extern void setsystemSoundOn(bool value); extern void setsoundPaused(bool value); -extern void setsoundMasterOn(bool value); extern void interp_rate(); extern int SOUND_CLOCK_TICKS; diff --git a/src/gb/gbSound.cpp b/src/gb/gbSound.cpp index 4719de71..7e290294 100644 --- a/src/gb/gbSound.cpp +++ b/src/gb/gbSound.cpp @@ -29,125 +29,32 @@ static Gb_Apu* gb_apu; static Simple_Effects_Buffer* stereo_buffer; -extern u8 soundBuffer[6][735]; extern u16 soundFinalWave[1470]; extern int soundVolume; -#define SOUND_MAGIC 0x60000000 -#define SOUND_MAGIC_2 0x30000000 -#define NOISE_MAGIC 5 - -extern int speed; extern int gbHardware; extern void soundResume(); -extern u8 soundWavePattern[4][32]; - extern int soundBufferLen; -extern int soundBufferTotalLen; extern int soundQuality; extern bool soundPaused; -extern int soundPlay; extern int soundTicks; extern int SOUND_CLOCK_TICKS; extern u32 soundNextPosition; -extern int soundLevel1; -extern int soundLevel2; -extern int soundBalance; -extern int soundMasterOn; -extern int soundIndex; -extern int soundBufferIndex; -int soundVIN = 0; extern int soundDebug; -extern int sound1On; -extern int sound1ATL; -int sound1ATLreload; -int freq1low; -int freq1high; -extern int sound1Skip; -extern int sound1Index; -extern int sound1Continue; -extern int sound1EnvelopeVolume; -extern int sound1EnvelopeATL; -extern int sound1EnvelopeUpDown; -extern int sound1EnvelopeATLReload; -extern int sound1SweepATL; -extern int sound1SweepATLReload; -extern int sound1SweepSteps; -extern int sound1SweepUpDown; -extern int sound1SweepStep; -extern u8 *sound1Wave; - -extern int sound2On; -extern int sound2ATL; -int sound2ATLreload; -int freq2low; -int freq2high; -extern int sound2Skip; -extern int sound2Index; -extern int sound2Continue; -extern int sound2EnvelopeVolume; -extern int sound2EnvelopeATL; -extern int sound2EnvelopeUpDown; -extern int sound2EnvelopeATLReload; -extern u8 *sound2Wave; - -extern int sound3On; -extern int sound3ATL; -int sound3ATLreload; -int freq3low; -int freq3high; -extern int sound3Skip; -extern int sound3Index; -extern int sound3Continue; -extern int sound3OutputLevel; -extern int sound3Last; - -extern int sound4On; -extern int sound4Clock; -extern int sound4ATL; -int sound4ATLreload; -int freq4; -extern int sound4Skip; -extern int sound4Index; -extern int sound4ShiftRight; -extern int sound4ShiftSkip; -extern int sound4ShiftIndex; -extern int sound4NSteps; -extern int sound4CountDown; -extern int sound4Continue; -extern int sound4EnvelopeVolume; -extern int sound4EnvelopeATL; -extern int sound4EnvelopeUpDown; -extern int sound4EnvelopeATLReload; - -extern int soundEnableFlag; - -extern int soundFreqRatio[8]; -extern int soundShiftClock[16]; - -extern s16 soundFilter[4000]; -extern s16 soundLeft[5]; -extern s16 soundRight[5]; -extern int soundEchoIndex; extern bool soundEcho; extern bool soundLowPass; extern bool soundReverse; extern bool soundOffFlag; -bool gbDigitalSound = false; - -static inline blip_time_t ticks_to_time( int ticks ) -{ - return ticks * 2; -} +int const ticks_to_time = 2 * GB_APU_OVERCLOCK; static inline blip_time_t blip_time() { - return ticks_to_time( SOUND_CLOCK_TICKS - soundTicks ); + return (SOUND_CLOCK_TICKS - soundTicks) * ticks_to_time; } u8 gbSoundRead( u16 address ) @@ -160,47 +67,33 @@ u8 gbSoundRead( u16 address ) void gbSoundEvent(register u16 address, register int data) { - int freq = 0; + int freq = 0; - gbMemory[address] = data; + gbMemory[address] = data; #ifndef FINAL_VERSION - if(soundDebug) { - // don't translate. debug only - log("Sound event: %08lx %02x\n", address, data); - } + if(soundDebug) { + // don't translate. debug only + log("Sound event: %08lx %02x\n", address, data); + } #endif if ( gb_apu && address >= NR10 && address <= 0xFF3F ) gb_apu->write_register( blip_time(), address, data ); } -void gbSoundChannel1() -{ -} - -void gbSoundChannel2() -{ -} - -void gbSoundChannel3() -{ -} - -void gbSoundChannel4() -{ -} - static void end_frame( blip_time_t time ) { - gb_apu->end_frame( time ); + gb_apu ->end_frame( time ); stereo_buffer->end_frame( time ); } static void flush_samples() { + // number of samples in output buffer int const out_buf_size = soundBufferLen / sizeof *soundFinalWave; + // Keep filling and writing soundFinalWave until it can't be fully filled while ( stereo_buffer->samples_avail() >= out_buf_size ) { stereo_buffer->read_samples( (blip_sample_t*) soundFinalWave, out_buf_size ); @@ -211,8 +104,6 @@ static void flush_samples() systemWriteDataToSoundBuffer(); } - soundIndex = 0; - soundBufferIndex = 0; } } @@ -240,9 +131,11 @@ static void apply_effects() void gbSoundTick() { - if(systemSoundOn && gb_apu && stereo_buffer) + if ( systemSoundOn && gb_apu && stereo_buffer ) { - end_frame( ticks_to_time( SOUND_CLOCK_TICKS ) ); + // Run sound hardware to present + end_frame( SOUND_CLOCK_TICKS * ticks_to_time ); + flush_samples(); // Update effects config if it was changed @@ -254,145 +147,82 @@ void gbSoundTick() static void remake_stereo_buffer() { - if ( !gb_apu ) - gb_apu = new Gb_Apu; - + // Stereo_Buffer delete stereo_buffer; stereo_buffer = 0; - stereo_buffer = new Simple_Effects_Buffer; - stereo_buffer->set_sample_rate( 44100 / soundQuality ); + stereo_buffer = new Simple_Effects_Buffer; // TODO: handle out of memory + stereo_buffer->set_sample_rate( 44100 / soundQuality ); // TODO: handle out of memory stereo_buffer->clock_rate( gb_apu->clock_rate ); + // APU static int const chan_types [chan_count] = { - Multi_Buffer::wave_type+1, Multi_Buffer::wave_type+2, - Multi_Buffer::wave_type+3, Multi_Buffer::mixed_type+1 + Multi_Buffer::wave_type+1, Multi_Buffer::wave_type+2, + Multi_Buffer::wave_type+3, Multi_Buffer::mixed_type+1 }; stereo_buffer->set_channel_count( chan_count, chan_types ); + if ( !gb_apu ) + gb_apu = new Gb_Apu; + apply_effects(); - gb_apu->reset( gb_apu->mode_cgb ); + // Use DMG or CGB sound differences based on type of game + gb_apu->reset( gbHardware & 1 ? gb_apu->mode_dmg : gb_apu->mode_cgb ); } void gbSoundReset() -{ - remake_stereo_buffer(); +{ + remake_stereo_buffer(); - soundPaused = 1; - soundPlay = 0; - SOUND_CLOCK_TICKS = 20000; - soundTicks = SOUND_CLOCK_TICKS; - soundNextPosition = 0; - soundMasterOn = 1; - soundIndex = 0; - soundBufferIndex = 0; - soundLevel1 = 7; - soundLevel2 = 7; - soundVIN = 0; + soundPaused = 1; + SOUND_CLOCK_TICKS = 20000; + soundTicks = SOUND_CLOCK_TICKS; + soundNextPosition = 0; - sound1On = 0; - sound1ATL = 0; - sound1Skip = 0; - sound1Index = 0; - sound1Continue = 0; - sound1EnvelopeVolume = 0; - sound1EnvelopeATL = 0; - sound1EnvelopeUpDown = 0; - sound1EnvelopeATLReload = 0; - sound1SweepATL = 0; - sound1SweepATLReload = 0; - sound1SweepSteps = 0; - sound1SweepUpDown = 0; - sound1SweepStep = 0; - sound1Wave = soundWavePattern[2]; - - sound2On = 0; - sound2ATL = 0; - sound2Skip = 0; - sound2Index = 0; - sound2Continue = 0; - sound2EnvelopeVolume = 0; - sound2EnvelopeATL = 0; - sound2EnvelopeUpDown = 0; - sound2EnvelopeATLReload = 0; - sound2Wave = soundWavePattern[2]; - - sound3On = 0; - sound3ATL = 0; - sound3Skip = 0; - sound3Index = 0; - sound3Continue = 0; - sound3OutputLevel = 0; - - sound4On = 0; - sound4Clock = 0; - sound4ATL = 0; - sound4Skip = 0; - sound4Index = 0; - sound4ShiftRight = 0x7f; - sound4NSteps = 0; - sound4CountDown = 0; - sound4Continue = 0; - sound4EnvelopeVolume = 0; - sound4EnvelopeATL = 0; - sound4EnvelopeUpDown = 0; - sound4EnvelopeATLReload = 0; - - // don't translate + // don't translate #ifndef FINAL_VERSION - if(soundDebug) { - log("*** Sound Init ***\n"); - } + if(soundDebug) { + log("*** Sound Init ***\n"); + } #endif - gbSoundEvent(0xff10, 0x80); - gbSoundEvent(0xff11, 0xbf); - gbSoundEvent(0xff12, 0xf3); - gbSoundEvent(0xff14, 0xbf); - gbSoundEvent(0xff16, 0x3f); - gbSoundEvent(0xff17, 0x00); - gbSoundEvent(0xff19, 0xbf); + gbSoundEvent(0xff10, 0x80); + gbSoundEvent(0xff11, 0xbf); + gbSoundEvent(0xff12, 0xf3); + gbSoundEvent(0xff14, 0xbf); + gbSoundEvent(0xff16, 0x3f); + gbSoundEvent(0xff17, 0x00); + gbSoundEvent(0xff19, 0xbf); - gbSoundEvent(0xff1a, 0x7f); - gbSoundEvent(0xff1b, 0xff); - gbSoundEvent(0xff1c, 0xbf); - gbSoundEvent(0xff1e, 0xbf); + gbSoundEvent(0xff1a, 0x7f); + gbSoundEvent(0xff1b, 0xff); + gbSoundEvent(0xff1c, 0xbf); + gbSoundEvent(0xff1e, 0xbf); - gbSoundEvent(0xff20, 0xff); - gbSoundEvent(0xff21, 0x00); - gbSoundEvent(0xff22, 0x00); - gbSoundEvent(0xff23, 0xbf); - gbSoundEvent(0xff24, 0x77); - gbSoundEvent(0xff25, 0xf3); + gbSoundEvent(0xff20, 0xff); + gbSoundEvent(0xff21, 0x00); + gbSoundEvent(0xff22, 0x00); + gbSoundEvent(0xff23, 0xbf); + gbSoundEvent(0xff24, 0x77); + gbSoundEvent(0xff25, 0xf3); - if (gbHardware & 0x4) - gbSoundEvent(0xff26, 0xf0); - else - gbSoundEvent(0xff26, 0xf1); + if (gbHardware & 0x4) + gbSoundEvent(0xff26, 0xf0); + else + gbSoundEvent(0xff26, 0xf1); - // don't translate + // don't translate #ifndef FINAL_VERSION - if(soundDebug) { - log("*** Sound Init Complete ***\n"); - } + if(soundDebug) { + log("*** Sound Init Complete ***\n"); + } #endif - sound1On = 0; - sound2On = 0; - sound3On = 0; - sound4On = 0; + int addr = 0xff30; - int addr = 0xff30; - - while(addr < 0xff40) { - gbMemory[addr++] = 0x00; - gbMemory[addr++] = 0xff; - } - - memset(soundFinalWave, 0x00, soundBufferLen); - - - memset(soundFilter, 0, sizeof(soundFilter)); - soundEchoIndex = 0; + while(addr < 0xff40) { + gbMemory[addr++] = 0x00; + gbMemory[addr++] = 0xff; + } } extern bool soundInit(); @@ -400,29 +230,35 @@ extern void soundShutdown(); void gbSoundSetQuality(int quality) { - if(soundQuality != quality && systemCanChangeSoundQuality()) { - if(!soundOffFlag) - soundShutdown(); - soundQuality = quality; - soundNextPosition = 0; - if(!soundOffFlag) - soundInit(); - //SOUND_CLOCK_TICKS = (gbSpeed ? 2 : 1) * 24 * soundQuality; - soundIndex = 0; - soundBufferIndex = 0; - } else { - soundNextPosition = 0; - //SOUND_CLOCK_TICKS = (gbSpeed ? 2 : 1) * 24 * soundQuality; - soundIndex = 0; - soundBufferIndex = 0; - } - remake_stereo_buffer(); + if ( soundQuality != quality ) + { + if ( systemCanChangeSoundQuality() ) + { + if ( !soundOffFlag ) + soundShutdown(); + + soundQuality = quality; + soundNextPosition = 0; + + if ( !soundOffFlag ) + soundInit(); + } + else + { + soundQuality = quality; + soundNextPosition = 0; + } + + remake_stereo_buffer(); + } } void gbSoundSaveGame(gzFile gzFile) { + // TODO: implement } void gbSoundReadGame(int version,gzFile gzFile) { + // TODO: implement } diff --git a/src/gb/gbSound.h b/src/gb/gbSound.h index 1bd715f5..3548466a 100644 --- a/src/gb/gbSound.h +++ b/src/gb/gbSound.h @@ -63,10 +63,12 @@ extern int SOUND_CLOCK_TICKS; struct gb_effects_config_t { bool enabled; // false = disable all effects + float echo; // 0.0 = none, 1.0 = lots float stereo; // 0.0 = channels in center, 1.0 = channels on left/right bool surround; // true = put some channels in back }; + // Can be changed at any time, probably from another thread too. // Sound will notice changes during next 1/100 second. extern gb_effects_config_t gb_effects_config; diff --git a/src/gb/gb_apu/blargg_config.h b/src/gb/gb_apu/blargg_config.h index d92c7520..bcdde837 100644 --- a/src/gb/gb_apu/blargg_config.h +++ b/src/gb/gb_apu/blargg_config.h @@ -1,11 +1,11 @@ -// Gb_Snd_Emu 0.2.0 user configuration file. Don't replace when updating library. +// $package user configuration file. Don't replace when updating library. #ifndef BLARGG_CONFIG_H #define BLARGG_CONFIG_H // Uncomment to have Gb_Apu run at 4x normal clock rate (16777216 Hz), useful in // a Game Boy Advance emulator. -//#define GB_APU_OVERCLOCK 4 +#define GB_APU_OVERCLOCK 4 // Uncomment to enable platform-specific (and possibly non-portable) optimizations. //#define BLARGG_NONPORTABLE 1