bsnes/higan/gba/apu/apu.cpp

99 lines
2.6 KiB
C++

#include <gba/gba.hpp>
namespace GameBoyAdvance {
#include "io.cpp"
#include "square.cpp"
#include "square1.cpp"
#include "square2.cpp"
#include "wave.cpp"
#include "noise.cpp"
#include "sequencer.cpp"
#include "fifo.cpp"
#include "serialization.cpp"
APU apu;
auto APU::Enter() -> void {
while(true) scheduler.synchronize(), apu.main();
}
auto APU::main() -> void {
//GBA clock runs at 16777216hz
//GBA PSG channels run at 2097152hz
runsequencer();
step(8);
//audio PWM output frequency and bit-rate are dependent upon amplitude setting:
//0 = 9-bit @ 32768hz
//1 = 8-bit @ 65536hz
//2 = 7-bit @ 131072hz
//3 = 6-bit @ 262144hz
//dynamically changing the output frequency under emulation would be difficult;
//always run the output frequency at the maximum 262144hz
if(++clock & 7) return;
int lsample = regs.bias.level - 0x0200;
int rsample = regs.bias.level - 0x0200;
//amplitude: 0 = 32768hz; 1 = 65536hz; 2 = 131072hz; 3 = 262144hz
if((clock & (63 >> (3 - regs.bias.amplitude))) == 0) {
sequencer.sample();
fifo[0].sample();
fifo[1].sample();
}
lsample += sequencer.loutput;
rsample += sequencer.routput;
int fifo0 = fifo[0].output << fifo[0].volume;
int fifo1 = fifo[1].output << fifo[1].volume;
if(fifo[0].lenable) lsample += fifo0;
if(fifo[1].lenable) lsample += fifo1;
if(fifo[0].renable) rsample += fifo0;
if(fifo[1].renable) rsample += fifo1;
lsample = sclamp<11>(lsample);
rsample = sclamp<11>(rsample);
//clip 11-bit signed output to more limited output bit-rate
//note: leaving 2-bits more on output to prevent quantization noise
if(regs.bias.amplitude == 0) lsample &= ~0, rsample &= ~0; //9-bit
if(regs.bias.amplitude == 1) lsample &= ~1, rsample &= ~1; //8-bit
if(regs.bias.amplitude == 2) lsample &= ~3, rsample &= ~3; //7-bit
if(regs.bias.amplitude == 3) lsample &= ~7, rsample &= ~7; //6-bit
if(cpu.regs.mode == CPU::Registers::Mode::Stop) lsample = 0, rsample = 0;
stream->sample((lsample << 5) / 32768.0, (rsample << 5) / 32768.0);
}
auto APU::step(uint clocks) -> void {
Thread::step(clocks);
synchronize(cpu);
}
auto APU::power() -> void {
create(APU::Enter, 16'777'216);
stream = Emulator::audio.createStream(2, frequency() / 64.0);
stream->addLowPassFilter(20000.0, 3);
stream->addHighPassFilter(20.0, 3);
clock = 0;
square1.power();
square2.power();
wave.power();
noise.power();
sequencer.power();
fifo[0].power();
fifo[1].power();
regs.bias.amplitude = 0;
regs.bias.level = 0x200;
for(uint n = 0x060; n <= 0x0a7; n++) bus.io[n] = this;
}
}