From b433838e9fdcb54f17596cbf5a2f04df1fa1b104 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Thu, 3 Feb 2011 22:17:35 +1100 Subject: [PATCH] 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. --- bsnes/Makefile | 2 +- bsnes/gameboy/apu/apu.cpp | 18 ++++---- bsnes/gameboy/apu/apu.hpp | 4 +- bsnes/gameboy/apu/master/master.cpp | 66 ++++++++++++++--------------- bsnes/gameboy/apu/serialization.cpp | 4 +- bsnes/gameboy/apu/wave/wave.cpp | 14 +++--- bsnes/gameboy/cpu/cpu.cpp | 2 +- bsnes/gameboy/cpu/timing/timing.cpp | 10 ++--- bsnes/gameboy/gameboy.hpp | 34 +++++++++++++-- bsnes/gameboy/lcd/lcd.cpp | 2 +- bsnes/snes/audio/audio.cpp | 12 +++--- bsnes/snes/audio/audio.hpp | 3 +- bsnes/snes/snes.hpp | 2 +- bsnes/ui-gameboy/main.cpp | 2 +- 14 files changed, 97 insertions(+), 78 deletions(-) diff --git a/bsnes/Makefile b/bsnes/Makefile index 88a4caaf..3b20cc03 100755 --- a/bsnes/Makefile +++ b/bsnes/Makefile @@ -2,7 +2,7 @@ include nall/Makefile snes := snes gameboy := gameboy profile := compatibility -ui := ui-gameboy +ui := ui # compiler c := $(compiler) -std=gnu99 diff --git a/bsnes/gameboy/apu/apu.cpp b/bsnes/gameboy/apu/apu.cpp index 2b5ce1dd..87c90d95 100755 --- a/bsnes/gameboy/apu/apu.cpp +++ b/bsnes/gameboy/apu/apu.cpp @@ -21,23 +21,24 @@ void APU::main() { scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); } - if((counter & 8191) == 0) { //512hz - if(sequencer == 0 || sequencer == 2 || sequencer == 4 || sequencer == 6) { //256hz + if(sequencer_base == 0) { //512hz + if(sequencer_step == 0 || sequencer_step == 2 || sequencer_step == 4 || sequencer_step == 6) { //256hz square1.clock_length(); square2.clock_length(); wave.clock_length(); noise.clock_length(); } - if(sequencer == 2 || sequencer == 6) { //128hz + if(sequencer_step == 2 || sequencer_step == 6) { //128hz square1.clock_sweep(); } - if(sequencer == 7) { //64hz + if(sequencer_step == 7) { //64hz square1.clock_envelope(); square2.clock_envelope(); noise.clock_envelope(); } - sequencer = (sequencer + 1) & 7; + sequencer_step++; } + sequencer_base++; square1.run(); square2.run(); @@ -46,9 +47,6 @@ void APU::main() { master.run(); 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); } } @@ -58,8 +56,8 @@ void APU::power() { for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this; foreach(n, mmio_data) n = 0x00; - counter = 0; - sequencer = 0; + sequencer_base = 0; + sequencer_step = 0; square1.power(); square2.power(); diff --git a/bsnes/gameboy/apu/apu.hpp b/bsnes/gameboy/apu/apu.hpp index 935d92fe..7218415c 100755 --- a/bsnes/gameboy/apu/apu.hpp +++ b/bsnes/gameboy/apu/apu.hpp @@ -6,8 +6,8 @@ struct APU : Processor, MMIO { #include "master/master.hpp" uint8 mmio_data[48]; - unsigned counter; - unsigned sequencer; + uint13 sequencer_base; + uint3 sequencer_step; Square1 square1; Square2 square2; diff --git a/bsnes/gameboy/apu/master/master.cpp b/bsnes/gameboy/apu/master/master.cpp index 591d52d6..c2df5ccd 100755 --- a/bsnes/gameboy/apu/master/master.cpp +++ b/bsnes/gameboy/apu/master/master.cpp @@ -1,15 +1,26 @@ #ifdef APU_CPP 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.square2.output; sample += apu.wave.output; sample += apu.noise.output; - sample /= channels; - if(sample < -32768) sample = -32768; - if(sample > +32767) sample = +32767; - center = sample; + sample >>= 2; + center = sclamp<16>(sample); + + if(left_enable == false && right_enable == false) { + left = center; + right = center; + return; + } sample = 0; channels = 0; @@ -18,17 +29,16 @@ void APU::Master::run() { if(channel3_left_enable) { sample += apu.wave.output; channels++; } if(channel4_left_enable) { sample += apu.noise.output; channels++; } if(channels) sample /= channels; - left = sample; + left = sclamp<16>(sample); switch(left_volume) { - case 0: left *= 0.125; break; - case 1: left *= 0.250; break; - case 2: left *= 0.375; break; - case 3: left *= 0.500; break; - case 4: left *= 0.625; break; - case 5: left *= 0.750; break; - case 6: left *= 0.875; break; - case 7: left *= 1.000; break; + case 0: left >>= 3; break; // 12.5% + case 1: left >>= 2; break; // 25.0% + case 2: left = (left >> 2) + (left >> 3); break; // 37.5% + case 3: left >>= 1; break; // 50.0% + case 4: left = (left >> 1) + (left >> 3); break; // 62.5% + case 5: left -= (left >> 2); break; // 75.0% + case 6: left -= (left >> 3); break; // 87.5% } if(left_enable == false) left = 0; @@ -39,30 +49,18 @@ void APU::Master::run() { if(channel3_right_enable) { sample += apu.wave.output; channels++; } if(channel4_right_enable) { sample += apu.noise.output; channels++; } if(channels) sample /= channels; - right = sample; + right = sclamp<16>(sample); switch(right_volume) { - case 0: right *= 0.125; break; - case 1: right *= 0.250; break; - case 2: right *= 0.375; break; - case 3: right *= 0.500; break; - case 4: right *= 0.625; break; - case 5: right *= 0.750; break; - case 6: right *= 0.875; break; - case 7: right *= 1.000; break; + case 0: right >>= 3; break; // 12.5% + case 1: right >>= 2; break; // 25.0% + case 2: right = (right >> 2) + (right >> 3); break; // 37.5% + case 3: right >>= 1; break; // 50.0% + case 4: right = (right >> 1) + (right >> 3); break; // 62.5% + case 5: right -= (right >> 2); break; // 75.0% + case 6: right -= (right >> 3); break; // 87.5% } 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) { diff --git a/bsnes/gameboy/apu/serialization.cpp b/bsnes/gameboy/apu/serialization.cpp index e781d5a4..c04dc533 100755 --- a/bsnes/gameboy/apu/serialization.cpp +++ b/bsnes/gameboy/apu/serialization.cpp @@ -2,8 +2,8 @@ void APU::serialize(serializer &s) { s.array(mmio_data); - s.integer(counter); - s.integer(sequencer); + s.integer(sequencer_base); + s.integer(sequencer_step); square1.serialize(s); square2.serialize(s); diff --git a/bsnes/gameboy/apu/wave/wave.cpp b/bsnes/gameboy/apu/wave/wave.cpp index 449c99df..bce6f444 100755 --- a/bsnes/gameboy/apu/wave/wave.cpp +++ b/bsnes/gameboy/apu/wave/wave.cpp @@ -12,12 +12,7 @@ void APU::Wave::run() { if(enable == false) sample = 0; output = (sample * 4369) - 32768; - switch(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; - } + output >>= volume; } void APU::Wave::clock_length() { @@ -38,7 +33,12 @@ void APU::Wave::write(unsigned r, uint8 data) { } 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) { diff --git a/bsnes/gameboy/cpu/cpu.cpp b/bsnes/gameboy/cpu/cpu.cpp index 0b30ba1f..b78aac8b 100755 --- a/bsnes/gameboy/cpu/cpu.cpp +++ b/bsnes/gameboy/cpu/cpu.cpp @@ -94,7 +94,7 @@ void CPU::interrupt_exec(uint16 pc) { } 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 = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror) diff --git a/bsnes/gameboy/cpu/timing/timing.cpp b/bsnes/gameboy/cpu/timing/timing.cpp index 215e8977..f771466c 100755 --- a/bsnes/gameboy/cpu/timing/timing.cpp +++ b/bsnes/gameboy/cpu/timing/timing.cpp @@ -1,13 +1,9 @@ //4194304hz (4 * 1024 * 1024) + //70224 clocks/frame // 456 clocks/scanline // 154 scanlines/frame -//4194304 / 4096 = 1024 -//4194304 / 262144 = 16 -//4194304 / 65536 = 64 -//4394304 / 16384 = 256 - #ifdef CPU_CPP #include "opcode.cpp" @@ -17,8 +13,8 @@ void CPU::add_clocks(unsigned clocks) { scheduler.exit(Scheduler::ExitReason::StepEvent); status.clock += clocks; - if(status.clock >= 4 * 1024 * 1024) { - status.clock -= 4 * 1024 * 1024; + if(status.clock >= 4194304) { + status.clock -= 4194304; cartridge.mbc3.second(); } diff --git a/bsnes/gameboy/gameboy.hpp b/bsnes/gameboy/gameboy.hpp index 9a47a5ba..eac830c2 100755 --- a/bsnes/gameboy/gameboy.hpp +++ b/bsnes/gameboy/gameboy.hpp @@ -5,7 +5,7 @@ namespace GameBoy { namespace Info { static const char Name[] = "bgameboy"; - static const char Version[] = "000.16"; + static const char Version[] = "000.17"; static unsigned SerializerVersion = 1; } } @@ -33,12 +33,38 @@ namespace GameBoy { typedef uint32_t uint32; typedef uint64_t uint64; - typedef int_t< 4> int4; - typedef int_t<15> int15; - + typedef uint_t< 1> uint1; + typedef uint_t< 2> uint2; + typedef uint_t< 3> uint3; 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<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 alwaysinline bool within(uint16 addr) { static const uint16 mask = ~(hi ^ lo); diff --git a/bsnes/gameboy/lcd/lcd.cpp b/bsnes/gameboy/lcd/lcd.cpp index 78e85ea2..7d12d321 100755 --- a/bsnes/gameboy/lcd/lcd.cpp +++ b/bsnes/gameboy/lcd/lcd.cpp @@ -190,7 +190,7 @@ void LCD::render_obj() { } 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 = 0xff40; n <= 0xff4b; n++) bus.mmio[n] = this; //MMIO diff --git a/bsnes/snes/audio/audio.cpp b/bsnes/snes/audio/audio.cpp index 4b2f6861..eaea1c63 100755 --- a/bsnes/snes/audio/audio.cpp +++ b/bsnes/snes/audio/audio.cpp @@ -24,8 +24,8 @@ void Audio::sample(int16 left, int16 right) { system.interface->audio_sample(left, right); } else { dsp_buffer[dsp_wroffset] = ((uint16)left << 0) + ((uint16)right << 16); - dsp_wroffset = (dsp_wroffset + 1) & 32767; - dsp_length = (dsp_length + 1) & 32767; + dsp_wroffset = (dsp_wroffset + 1) & buffer_mask; + dsp_length = (dsp_length + 1) & buffer_mask; flush(); } } @@ -50,8 +50,8 @@ void Audio::coprocessor_sample(int16 left, int16 right) { r_frac = r_step - first; cop_buffer[cop_wroffset] = (output_left << 0) + (output_right << 16); - cop_wroffset = (cop_wroffset + 1) & 32767; - cop_length = (cop_length + 1) & 32767; + cop_wroffset = (cop_wroffset + 1) & buffer_mask; + cop_length = (cop_length + 1) & buffer_mask; flush(); } @@ -63,8 +63,8 @@ void Audio::flush() { uint32 dsp_sample = dsp_buffer[dsp_rdoffset]; uint32 cop_sample = cop_buffer[cop_rdoffset]; - dsp_rdoffset = (dsp_rdoffset + 1) & 32767; - cop_rdoffset = (cop_rdoffset + 1) & 32767; + dsp_rdoffset = (dsp_rdoffset + 1) & buffer_mask; + cop_rdoffset = (cop_rdoffset + 1) & buffer_mask; dsp_length--; cop_length--; diff --git a/bsnes/snes/audio/audio.hpp b/bsnes/snes/audio/audio.hpp index 3cd522fa..29d6dbae 100755 --- a/bsnes/snes/audio/audio.hpp +++ b/bsnes/snes/audio/audio.hpp @@ -8,7 +8,8 @@ public: private: 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_wroffset, cop_wroffset; unsigned dsp_length, cop_length; diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index f3498ee9..cc1ed8dd 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -1,7 +1,7 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "075.05"; + static const char Version[] = "075.06"; static const unsigned SerializerVersion = 18; } } diff --git a/bsnes/ui-gameboy/main.cpp b/bsnes/ui-gameboy/main.cpp index e614b8ac..5dfca13c 100755 --- a/bsnes/ui-gameboy/main.cpp +++ b/bsnes/ui-gameboy/main.cpp @@ -38,7 +38,7 @@ void Application::main(int argc, char **argv) { audio.driver("ALSA"); #endif 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::Latency, 80U); audio.set(Audio::Frequency, 44100U);