Update to v075r06 release.

byuu says:

Removed the floating-point volume adjustments from the wave channel and
the left/right speaker mixers. Also, against my better judgment I'm
backing out of left/right computation when they are both turned off.
This basically makes non-stereo games run faster, but will make stereo
games appear to run slower. I don't like it when end-users experience
mystery slowdowns.

Anyway, it appears that the audio calculation is really fucking
demanding. Knocks FPS from 800 down to 300. I thought it might be libco,
so I took it out and it only went up to 305fps o.O

There is also some sort of problem with bsnes/Super Game Boy audio. The
latency is really great when you first start, but it seems to drift
apart over time until it is well over 500ms, and then it either pops or
fades back to very low, sub-50ms latency again. The way I handle mixing
is that the coprocessor audio samples go into a resampler to the native
SNES rate, and fed to an output buffer. SNES audio samples go there
untouched. When there is a sample in each, I add them together and
average the result (I still don't understand why we divide by two since
these are signed integers), and output it immediately. It's just-in-time
sampling, so as long as DSP v Coprocessor do not drift very far, it
should have very low latency. And I make the CPU sync DSP and
Coprocessor once per scanline, which is something like 15 samples or so.
This commit is contained in:
Tim Allen 2011-02-03 22:17:35 +11:00
parent a3abe8ebaa
commit b433838e9f
14 changed files with 97 additions and 78 deletions

View File

@ -2,7 +2,7 @@ include nall/Makefile
snes := snes snes := snes
gameboy := gameboy gameboy := gameboy
profile := compatibility profile := compatibility
ui := ui-gameboy ui := ui
# compiler # compiler
c := $(compiler) -std=gnu99 c := $(compiler) -std=gnu99

View File

@ -21,23 +21,24 @@ void APU::main() {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
} }
if((counter & 8191) == 0) { //512hz if(sequencer_base == 0) { //512hz
if(sequencer == 0 || sequencer == 2 || sequencer == 4 || sequencer == 6) { //256hz if(sequencer_step == 0 || sequencer_step == 2 || sequencer_step == 4 || sequencer_step == 6) { //256hz
square1.clock_length(); square1.clock_length();
square2.clock_length(); square2.clock_length();
wave.clock_length(); wave.clock_length();
noise.clock_length(); noise.clock_length();
} }
if(sequencer == 2 || sequencer == 6) { //128hz if(sequencer_step == 2 || sequencer_step == 6) { //128hz
square1.clock_sweep(); square1.clock_sweep();
} }
if(sequencer == 7) { //64hz if(sequencer_step == 7) { //64hz
square1.clock_envelope(); square1.clock_envelope();
square2.clock_envelope(); square2.clock_envelope();
noise.clock_envelope(); noise.clock_envelope();
} }
sequencer = (sequencer + 1) & 7; sequencer_step++;
} }
sequencer_base++;
square1.run(); square1.run();
square2.run(); square2.run();
@ -46,9 +47,6 @@ void APU::main() {
master.run(); master.run();
system.interface->audio_sample(master.center, master.left, master.right); system.interface->audio_sample(master.center, master.left, master.right);
if(++counter == 4194304) counter = 0;
if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread); if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
} }
} }
@ -58,8 +56,8 @@ void APU::power() {
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this; for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
foreach(n, mmio_data) n = 0x00; foreach(n, mmio_data) n = 0x00;
counter = 0; sequencer_base = 0;
sequencer = 0; sequencer_step = 0;
square1.power(); square1.power();
square2.power(); square2.power();

View File

@ -6,8 +6,8 @@ struct APU : Processor, MMIO {
#include "master/master.hpp" #include "master/master.hpp"
uint8 mmio_data[48]; uint8 mmio_data[48];
unsigned counter; uint13 sequencer_base;
unsigned sequencer; uint3 sequencer_step;
Square1 square1; Square1 square1;
Square2 square2; Square2 square2;

View File

@ -1,15 +1,26 @@
#ifdef APU_CPP #ifdef APU_CPP
void APU::Master::run() { void APU::Master::run() {
signed sample = 0, channels = 4; if(enable == false) {
center = 0;
left = 0;
right = 0;
return;
}
signed sample = 0, channels;
sample += apu.square1.output; sample += apu.square1.output;
sample += apu.square2.output; sample += apu.square2.output;
sample += apu.wave.output; sample += apu.wave.output;
sample += apu.noise.output; sample += apu.noise.output;
sample /= channels; sample >>= 2;
if(sample < -32768) sample = -32768; center = sclamp<16>(sample);
if(sample > +32767) sample = +32767;
center = sample; if(left_enable == false && right_enable == false) {
left = center;
right = center;
return;
}
sample = 0; sample = 0;
channels = 0; channels = 0;
@ -18,17 +29,16 @@ void APU::Master::run() {
if(channel3_left_enable) { sample += apu.wave.output; channels++; } if(channel3_left_enable) { sample += apu.wave.output; channels++; }
if(channel4_left_enable) { sample += apu.noise.output; channels++; } if(channel4_left_enable) { sample += apu.noise.output; channels++; }
if(channels) sample /= channels; if(channels) sample /= channels;
left = sample; left = sclamp<16>(sample);
switch(left_volume) { switch(left_volume) {
case 0: left *= 0.125; break; case 0: left >>= 3; break; // 12.5%
case 1: left *= 0.250; break; case 1: left >>= 2; break; // 25.0%
case 2: left *= 0.375; break; case 2: left = (left >> 2) + (left >> 3); break; // 37.5%
case 3: left *= 0.500; break; case 3: left >>= 1; break; // 50.0%
case 4: left *= 0.625; break; case 4: left = (left >> 1) + (left >> 3); break; // 62.5%
case 5: left *= 0.750; break; case 5: left -= (left >> 2); break; // 75.0%
case 6: left *= 0.875; break; case 6: left -= (left >> 3); break; // 87.5%
case 7: left *= 1.000; break;
} }
if(left_enable == false) left = 0; if(left_enable == false) left = 0;
@ -39,30 +49,18 @@ void APU::Master::run() {
if(channel3_right_enable) { sample += apu.wave.output; channels++; } if(channel3_right_enable) { sample += apu.wave.output; channels++; }
if(channel4_right_enable) { sample += apu.noise.output; channels++; } if(channel4_right_enable) { sample += apu.noise.output; channels++; }
if(channels) sample /= channels; if(channels) sample /= channels;
right = sample; right = sclamp<16>(sample);
switch(right_volume) { switch(right_volume) {
case 0: right *= 0.125; break; case 0: right >>= 3; break; // 12.5%
case 1: right *= 0.250; break; case 1: right >>= 2; break; // 25.0%
case 2: right *= 0.375; break; case 2: right = (right >> 2) + (right >> 3); break; // 37.5%
case 3: right *= 0.500; break; case 3: right >>= 1; break; // 50.0%
case 4: right *= 0.625; break; case 4: right = (right >> 1) + (right >> 3); break; // 62.5%
case 5: right *= 0.750; break; case 5: right -= (right >> 2); break; // 75.0%
case 6: right *= 0.875; break; case 6: right -= (right >> 3); break; // 87.5%
case 7: right *= 1.000; break;
} }
if(right_enable == false) right = 0; if(right_enable == false) right = 0;
if(left_enable == false && right_enable == false) {
left = center;
right = center;
}
if(enable == false) {
center = 0;
left = 0;
right = 0;
}
} }
void APU::Master::write(unsigned r, uint8 data) { void APU::Master::write(unsigned r, uint8 data) {

View File

@ -2,8 +2,8 @@
void APU::serialize(serializer &s) { void APU::serialize(serializer &s) {
s.array(mmio_data); s.array(mmio_data);
s.integer(counter); s.integer(sequencer_base);
s.integer(sequencer); s.integer(sequencer_step);
square1.serialize(s); square1.serialize(s);
square2.serialize(s); square2.serialize(s);

View File

@ -12,12 +12,7 @@ void APU::Wave::run() {
if(enable == false) sample = 0; if(enable == false) sample = 0;
output = (sample * 4369) - 32768; output = (sample * 4369) - 32768;
switch(volume) { output >>= volume;
case 0: output *= 0.00; break;
case 1: output *= 1.00; break;
case 2: output *= 0.50; break;
case 3: output *= 0.25; break;
}
} }
void APU::Wave::clock_length() { void APU::Wave::clock_length() {
@ -38,7 +33,12 @@ void APU::Wave::write(unsigned r, uint8 data) {
} }
if(r == 2) { if(r == 2) {
volume = (data >> 5) & 3; switch((data >> 5) & 3) {
case 0: volume = 16; break; // 0%
case 1: volume = 0; break; //100%
case 2: volume = 1; break; // 50%
case 3: volume = 2; break; // 25%
}
} }
if(r == 3) { if(r == 3) {

View File

@ -94,7 +94,7 @@ void CPU::interrupt_exec(uint16 pc) {
} }
void CPU::power() { void CPU::power() {
create(Main, 4 * 1024 * 1024); create(Main, 4194304);
for(unsigned n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM for(unsigned n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM
for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror) for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror)

View File

@ -1,13 +1,9 @@
//4194304hz (4 * 1024 * 1024) //4194304hz (4 * 1024 * 1024)
//70224 clocks/frame //70224 clocks/frame
// 456 clocks/scanline // 456 clocks/scanline
// 154 scanlines/frame // 154 scanlines/frame
//4194304 / 4096 = 1024
//4194304 / 262144 = 16
//4194304 / 65536 = 64
//4394304 / 16384 = 256
#ifdef CPU_CPP #ifdef CPU_CPP
#include "opcode.cpp" #include "opcode.cpp"
@ -17,8 +13,8 @@ void CPU::add_clocks(unsigned clocks) {
scheduler.exit(Scheduler::ExitReason::StepEvent); scheduler.exit(Scheduler::ExitReason::StepEvent);
status.clock += clocks; status.clock += clocks;
if(status.clock >= 4 * 1024 * 1024) { if(status.clock >= 4194304) {
status.clock -= 4 * 1024 * 1024; status.clock -= 4194304;
cartridge.mbc3.second(); cartridge.mbc3.second();
} }

View File

@ -5,7 +5,7 @@
namespace GameBoy { namespace GameBoy {
namespace Info { namespace Info {
static const char Name[] = "bgameboy"; static const char Name[] = "bgameboy";
static const char Version[] = "000.16"; static const char Version[] = "000.17";
static unsigned SerializerVersion = 1; static unsigned SerializerVersion = 1;
} }
} }
@ -33,12 +33,38 @@ namespace GameBoy {
typedef uint32_t uint32; typedef uint32_t uint32;
typedef uint64_t uint64; typedef uint64_t uint64;
typedef int_t< 4> int4; typedef uint_t< 1> uint1;
typedef int_t<15> int15; typedef uint_t< 2> uint2;
typedef uint_t< 3> uint3;
typedef uint_t< 4> uint4; typedef uint_t< 4> uint4;
typedef uint_t< 5> uint5;
typedef uint_t< 6> uint6;
typedef uint_t< 7> uint7;
typedef uint_t< 9> uint9;
typedef uint_t<10> uint10;
typedef uint_t<11> uint11;
typedef uint_t<12> uint12;
typedef uint_t<13> uint13;
typedef uint_t<14> uint14;
typedef uint_t<15> uint15; typedef uint_t<15> uint15;
typedef uint_t<17> uint17;
typedef uint_t<18> uint18;
typedef uint_t<19> uint19;
typedef uint_t<20> uint20;
typedef uint_t<21> uint21;
typedef uint_t<22> uint22;
typedef uint_t<23> uint23;
typedef uint_t<24> uint24;
typedef uint_t<25> uint25;
typedef uint_t<26> uint26;
typedef uint_t<27> uint27;
typedef uint_t<28> uint28;
typedef uint_t<29> uint29;
typedef uint_t<30> uint30;
typedef uint_t<31> uint31;
template<uint16 lo, uint16 hi> template<uint16 lo, uint16 hi>
alwaysinline bool within(uint16 addr) { alwaysinline bool within(uint16 addr) {
static const uint16 mask = ~(hi ^ lo); static const uint16 mask = ~(hi ^ lo);

View File

@ -190,7 +190,7 @@ void LCD::render_obj() {
} }
void LCD::power() { void LCD::power() {
create(Main, 4 * 1024 * 1024); create(Main, 4194304);
for(unsigned n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM for(unsigned n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
for(unsigned n = 0xff40; n <= 0xff4b; n++) bus.mmio[n] = this; //MMIO for(unsigned n = 0xff40; n <= 0xff4b; n++) bus.mmio[n] = this; //MMIO

View File

@ -24,8 +24,8 @@ void Audio::sample(int16 left, int16 right) {
system.interface->audio_sample(left, right); system.interface->audio_sample(left, right);
} else { } else {
dsp_buffer[dsp_wroffset] = ((uint16)left << 0) + ((uint16)right << 16); dsp_buffer[dsp_wroffset] = ((uint16)left << 0) + ((uint16)right << 16);
dsp_wroffset = (dsp_wroffset + 1) & 32767; dsp_wroffset = (dsp_wroffset + 1) & buffer_mask;
dsp_length = (dsp_length + 1) & 32767; dsp_length = (dsp_length + 1) & buffer_mask;
flush(); flush();
} }
} }
@ -50,8 +50,8 @@ void Audio::coprocessor_sample(int16 left, int16 right) {
r_frac = r_step - first; r_frac = r_step - first;
cop_buffer[cop_wroffset] = (output_left << 0) + (output_right << 16); cop_buffer[cop_wroffset] = (output_left << 0) + (output_right << 16);
cop_wroffset = (cop_wroffset + 1) & 32767; cop_wroffset = (cop_wroffset + 1) & buffer_mask;
cop_length = (cop_length + 1) & 32767; cop_length = (cop_length + 1) & buffer_mask;
flush(); flush();
} }
@ -63,8 +63,8 @@ void Audio::flush() {
uint32 dsp_sample = dsp_buffer[dsp_rdoffset]; uint32 dsp_sample = dsp_buffer[dsp_rdoffset];
uint32 cop_sample = cop_buffer[cop_rdoffset]; uint32 cop_sample = cop_buffer[cop_rdoffset];
dsp_rdoffset = (dsp_rdoffset + 1) & 32767; dsp_rdoffset = (dsp_rdoffset + 1) & buffer_mask;
cop_rdoffset = (cop_rdoffset + 1) & 32767; cop_rdoffset = (cop_rdoffset + 1) & buffer_mask;
dsp_length--; dsp_length--;
cop_length--; cop_length--;

View File

@ -8,7 +8,8 @@ public:
private: private:
bool coprocessor; bool coprocessor;
uint32 dsp_buffer[32768], cop_buffer[32768]; enum : unsigned { buffer_size = 32768, buffer_mask = buffer_size - 1 };
uint32 dsp_buffer[buffer_size], cop_buffer[buffer_size];
unsigned dsp_rdoffset, cop_rdoffset; unsigned dsp_rdoffset, cop_rdoffset;
unsigned dsp_wroffset, cop_wroffset; unsigned dsp_wroffset, cop_wroffset;
unsigned dsp_length, cop_length; unsigned dsp_length, cop_length;

View File

@ -1,7 +1,7 @@
namespace SNES { namespace SNES {
namespace Info { namespace Info {
static const char Name[] = "bsnes"; static const char Name[] = "bsnes";
static const char Version[] = "075.05"; static const char Version[] = "075.06";
static const unsigned SerializerVersion = 18; static const unsigned SerializerVersion = 18;
} }
} }

View File

@ -38,7 +38,7 @@ void Application::main(int argc, char **argv) {
audio.driver("ALSA"); audio.driver("ALSA");
#endif #endif
audio.set(Audio::Handle, (uintptr_t)mainWindow.viewport.handle()); audio.set(Audio::Handle, (uintptr_t)mainWindow.viewport.handle());
audio.set(Audio::Synchronize, true); audio.set(Audio::Synchronize, false);
audio.set(Audio::Volume, 100U); audio.set(Audio::Volume, 100U);
audio.set(Audio::Latency, 80U); audio.set(Audio::Latency, 80U);
audio.set(Audio::Frequency, 44100U); audio.set(Audio::Frequency, 44100U);