#include "stdafx.h" #include "GbApu.h" #include "Console.h" #include "Gameboy.h" #include "SoundMixer.h" #include "EmuSettings.h" #include "../Utilities/Serializer.h" GbApu::GbApu() { _soundBuffer = new int16_t[GbApu::MaxSamples * 2]; memset(_soundBuffer, 0, GbApu::MaxSamples * 2 * sizeof(int16_t)); _leftChannel = blip_new(GbApu::MaxSamples); _rightChannel = blip_new(GbApu::MaxSamples); } void GbApu::Init(Console* console, Gameboy* gameboy) { _square1.reset(new GbSquareChannel(this)); _square2.reset(new GbSquareChannel(this)); _wave.reset(new GbWaveChannel(this)); _noise.reset(new GbNoiseChannel(this)); _prevLeftOutput = 0; _prevRightOutput = 0; _clockCounter = 0; _prevClockCount = 0; _console = console; _soundMixer = console->GetSoundMixer().get(); _gameboy = gameboy; _state = {}; blip_clear(_leftChannel); blip_clear(_rightChannel); if(_gameboy->IsSgb()) { blip_set_rates(_leftChannel, _console->GetMasterClockRate() / 5, GbApu::SampleRate); blip_set_rates(_rightChannel, _console->GetMasterClockRate() / 5, GbApu::SampleRate); } else { blip_set_rates(_leftChannel, GbApu::ApuFrequency, GbApu::SampleRate); blip_set_rates(_rightChannel, GbApu::ApuFrequency, GbApu::SampleRate); } } GbApu::~GbApu() { blip_delete(_leftChannel); blip_delete(_rightChannel); delete[] _soundBuffer; } GbApuDebugState GbApu::GetState() { GbApuDebugState state; state.Common = _state; state.Square1 = _square1->GetState(); state.Square2 = _square2->GetState(); state.Wave = _wave->GetState(); state.Noise = _noise->GetState(); return state; } void GbApu::Run() { uint64_t clockCount = _gameboy->GetApuCycleCount(); uint32_t clocksToRun = (uint32_t)(clockCount - _prevClockCount); _prevClockCount = clockCount; if(!_state.ApuEnabled) { _clockCounter += clocksToRun; } else { while(clocksToRun > 0) { uint32_t minTimer = std::min({ clocksToRun, _square1->GetState().Timer, _square2->GetState().Timer, _wave->GetState().Timer, _noise->GetState().Timer }); clocksToRun -= minTimer; _square1->Exec(minTimer); _square2->Exec(minTimer); _wave->Exec(minTimer); _noise->Exec(minTimer); int16_t leftOutput = ( (_square1->GetOutput() & _state.EnableLeftSq1) + (_square2->GetOutput() & _state.EnableLeftSq2) + (_wave->GetOutput() & _state.EnableLeftWave) + (_noise->GetOutput() & _state.EnableLeftNoise) ) * (_state.LeftVolume + 1) * 40; if(_prevLeftOutput != leftOutput) { blip_add_delta(_leftChannel, _clockCounter, leftOutput - _prevLeftOutput); _prevLeftOutput = leftOutput; } int16_t rightOutput = ( (_square1->GetOutput() & _state.EnableRightSq1) + (_square2->GetOutput() & _state.EnableRightSq2) + (_wave->GetOutput() & _state.EnableRightWave) + (_noise->GetOutput() & _state.EnableRightNoise) ) * (_state.RightVolume + 1) * 40; if(_prevRightOutput != rightOutput) { blip_add_delta(_rightChannel, _clockCounter, rightOutput - _prevRightOutput); _prevRightOutput = rightOutput; } _clockCounter += minTimer; } } if(!_gameboy->IsSgb() && _clockCounter >= 20000) { blip_end_frame(_leftChannel, _clockCounter); blip_end_frame(_rightChannel, _clockCounter); uint32_t sampleCount = (uint32_t)blip_read_samples(_leftChannel, _soundBuffer, GbApu::MaxSamples, 1); blip_read_samples(_rightChannel, _soundBuffer + 1, GbApu::MaxSamples, 1); _soundMixer->PlayAudioBuffer(_soundBuffer, sampleCount, GbApu::SampleRate); _clockCounter = 0; } } void GbApu::GetSoundSamples(int16_t* &samples, uint32_t& sampleCount) { Run(); blip_end_frame(_leftChannel, _clockCounter); blip_end_frame(_rightChannel, _clockCounter); sampleCount = (uint32_t)blip_read_samples(_leftChannel, _soundBuffer, GbApu::MaxSamples, 1); blip_read_samples(_rightChannel, _soundBuffer + 1, GbApu::MaxSamples, 1); samples = _soundBuffer; _clockCounter = 0; } void GbApu::ClockFrameSequencer() { Run(); if(!_state.ApuEnabled) { return; } if((_state.FrameSequenceStep & 0x01) == 0) { _square1->ClockLengthCounter(); _square2->ClockLengthCounter(); _wave->ClockLengthCounter(); _noise->ClockLengthCounter(); if((_state.FrameSequenceStep & 0x03) == 2) { _square1->ClockSweepUnit(); } } else if(_state.FrameSequenceStep == 7) { _square1->ClockEnvelope(); _square2->ClockEnvelope(); _noise->ClockEnvelope(); } _state.FrameSequenceStep = (_state.FrameSequenceStep + 1) & 0x07; } uint8_t GbApu::Read(uint16_t addr) { Run(); switch(addr) { case 0xFF10: case 0xFF11: case 0xFF12: case 0xFF13: case 0xFF14: return _square1->Read(addr - 0xFF10); case 0xFF16: case 0xFF17: case 0xFF18: case 0xFF19: return _square2->Read(addr - 0xFF15); case 0xFF1A: case 0xFF1B: case 0xFF1C: case 0xFF1D: case 0xFF1E: return _wave->Read(addr - 0xFF1A); case 0xFF20: case 0xFF21: case 0xFF22: case 0xFF23: return _noise->Read(addr - 0xFF1F); case 0xFF24: return ( (_state.ExtAudioLeftEnabled ? 0x80 : 0) | (_state.LeftVolume << 4) | (_state.ExtAudioRightEnabled ? 0x08 : 0) | _state.RightVolume ); case 0xFF25: return ( (_state.EnableLeftNoise ? 0x80 : 0) | (_state.EnableLeftWave ? 0x40 : 0) | (_state.EnableLeftSq2 ? 0x20 : 0) | (_state.EnableLeftSq1 ? 0x10 : 0) | (_state.EnableRightNoise ? 0x08 : 0) | (_state.EnableRightWave ? 0x04 : 0) | (_state.EnableRightSq2 ? 0x02 : 0) | (_state.EnableRightSq1 ? 0x01 : 0) ); case 0xFF26: return ( (_state.ApuEnabled ? 0x80 : 0) | 0x70 | //open bus ((_state.ApuEnabled && _noise->Enabled()) ? 0x08 : 0) | ((_state.ApuEnabled && _wave->Enabled()) ? 0x04 : 0) | ((_state.ApuEnabled && _square2->Enabled()) ? 0x02 : 0) | ((_state.ApuEnabled && _square1->Enabled()) ? 0x01 : 0) ); case 0xFF30: case 0xFF31: case 0xFF32: case 0xFF33: case 0xFF34: case 0xFF35: case 0xFF36: case 0xFF37: case 0xFF38: case 0xFF39: case 0xFF3A: case 0xFF3B: case 0xFF3C: case 0xFF3D: case 0xFF3E: case 0xFF3F: return _wave->ReadRam(addr); } //Open bus return 0xFF; } void GbApu::Write(uint16_t addr, uint8_t value) { Run(); if(!_state.ApuEnabled) { if(addr == 0xFF11 || addr == 0xFF16 || addr == 0xFF20) { //Allow writes to length counter, but not the upper 2 bits (square duty) value &= 0x3F; } else if(addr < 0xFF26 && addr != 0xFF1B) { //Ignore all writes to these registers when APU is disabled return; } } switch(addr) { case 0xFF10: case 0xFF11: case 0xFF12: case 0xFF13: case 0xFF14: _square1->Write(addr - 0xFF10, value); break; case 0xFF16: case 0xFF17: case 0xFF18: case 0xFF19: _square2->Write(addr - 0xFF15, value); //Same as square1, but without a sweep unit break; case 0xFF1A: case 0xFF1B: case 0xFF1C: case 0xFF1D: case 0xFF1E: _wave->Write(addr - 0xFF1A, value); break; case 0xFF20: case 0xFF21: case 0xFF22: case 0xFF23: _noise->Write(addr - 0xFF1F, value); break; case 0xFF24: _state.ExtAudioLeftEnabled = (value & 0x80) != 0; _state.LeftVolume = (value & 0x70) >> 4; _state.ExtAudioRightEnabled = (value & 0x08) != 0; _state.RightVolume = (value & 0x07); break; case 0xFF25: _state.EnableLeftNoise = (value & 0x80) ? 0xFF : 0; _state.EnableLeftWave = (value & 0x40) ? 0xFF : 0; _state.EnableLeftSq2 = (value & 0x20) ? 0xFF : 0; _state.EnableLeftSq1 = (value & 0x10) ? 0xFF : 0; _state.EnableRightNoise = (value & 0x08) ? 0xFF : 0; _state.EnableRightWave = (value & 0x04) ? 0xFF : 0; _state.EnableRightSq2 = (value & 0x02) ? 0xFF : 0; _state.EnableRightSq1 = (value & 0x01) ? 0xFF : 0; break; case 0xFF26: { bool apuEnabled = (value & 0x80) != 0; if(_state.ApuEnabled != apuEnabled) { if(!apuEnabled) { _square1->Disable(); _square2->Disable(); _wave->Disable(); _noise->Disable(); Write(0xFF24, 0); Write(0xFF25, 0); } else { //When powered on, the frame sequencer is reset so that the next step will be 0, //the square duty units are reset to the first step of the waveform, and the wave channel's sample buffer is reset to 0. _state.FrameSequenceStep = 0; } _state.ApuEnabled = apuEnabled; } break; } case 0xFF30: case 0xFF31: case 0xFF32: case 0xFF33: case 0xFF34: case 0xFF35: case 0xFF36: case 0xFF37: case 0xFF38: case 0xFF39: case 0xFF3A: case 0xFF3B: case 0xFF3C: case 0xFF3D: case 0xFF3E: case 0xFF3F: _wave->WriteRam(addr, value); break; } } template void GbApu::ProcessLengthEnableFlag(uint8_t value, T &length, bool &lengthEnabled, bool &enabled) { bool newLengthEnabled = (value & 0x40) != 0; if(newLengthEnabled && !lengthEnabled && (_state.FrameSequenceStep & 0x01) == 1) { //"Extra length clocking occurs when writing to NRx4 when the frame sequencer's next step is one that doesn't clock //the length counter. In this case, if the length counter was PREVIOUSLY disabled and now enabled and the length counter //is not zero, it is decremented. If this decrement makes it zero and trigger is clear, the channel is disabled." if(length > 0) { length--; if(length == 0) { if(value & 0x80) { length = sizeof(T) == 1 ? 0x3F : 0xFF; } else { enabled = false; } } } } lengthEnabled = newLengthEnabled; } void GbApu::Serialize(Serializer& s) { s.Stream( _state.ApuEnabled, _state.FrameSequenceStep, _state.EnableLeftSq1, _state.EnableLeftSq2, _state.EnableLeftWave, _state.EnableLeftNoise, _state.EnableRightSq1, _state.EnableRightSq2, _state.EnableRightWave, _state.EnableRightNoise, _state.LeftVolume, _state.RightVolume, _state.ExtAudioLeftEnabled, _state.ExtAudioRightEnabled, _prevLeftOutput, _prevRightOutput, _clockCounter, _prevClockCount ); s.Stream(_square1.get()); s.Stream(_square2.get()); s.Stream(_wave.get()); s.Stream(_noise.get()); } template void GbApu::ProcessLengthEnableFlag(uint8_t value, uint8_t& length, bool& lengthEnabled, bool& enabled); template void GbApu::ProcessLengthEnableFlag(uint8_t value, uint16_t& length, bool& lengthEnabled, bool& enabled);