From ecc7e899e02872e1fc5c7d9c06d69e29e3c1db69 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 26 Jun 2017 11:41:58 +1000 Subject: [PATCH] Update to v103r01 release. byuu says: Changelog: - nall/dsp: improve one pole coefficient calculations [Fatbag] - higan/audio: reworked filters to support selection of either one pole (first-order) or biquad (second-order) filters - note: the design is not stable yet; so forks should not put too much effort into synchronizing with this change yet - fc: added first-order filters as per NESdev wiki (90hz lowpass + 440hz lowpass + 14khz highpass) - fc: created separate NTSC-J and NTSC-U regions - NESdev wiki says the Japanese Famicom uses a separate audio filtering strategy, but details are fuzzy - there's also cartridge audio output being disabled on NES units; and differences with controllers - this stuff will be supported in the future, just adding the support for it now - gba: corrected serious bugs in PSG wave channel emulation [Cydrak] - note that if there are still bugs here, it's my fault - md/psg,ym2612: added first-order low-pass 2840hz filter to match VA3-VA6 Mega Drives - md/psg: lowered volume relative to the YM2612 - using 0x1400; multiple people agreed it was the closest to the hardware recordings against a VA6 - ms,md/psg: don't serialize the volume levels array - md/vdp: Hblank bit acts the same during Vblank as outside of it (it isn't always set during Vblank) - md/vdp: return isPAL in bit 0 of control port reads - tomoko: change command-line option separator from : to | - [Editor's note: This change was present in the public v103, but it's in this changelog because it was made after the v103 WIP] - higan/all: change the 20hz high-pass filters from second-order three-pass to first-order one-pass - these filters are meant to remove DC bias, but I honestly can't hear a difference with or without them - so there's really no sense wasting CPU power with an extremely powerful filter here Things I did not do: - change icarus install rule - work on 8-bit Mega Drive SRAM - work on Famicom or Mega Drive region detection heuristics in icarus My long-term dream plan is to devise a special user-configurable filtering system where you can set relative volumes and create your own list of filters (any number of them in any order at any frequency), that way people can make the systems sound however they want. Right now, the sanest place to put this information is inside the $system.sys/manifest.bml files. But that's not very user friendly, and upgrading to new versions will lose these changes if you don't copy them over manually. Of course, cluttering the GUI with a fancy filter editor is probably supreme overkill for 99% of users, so maybe that's fine. --- higan/audio/audio.hpp | 16 ++- higan/audio/stream.cpp | 38 ++++--- higan/emulator/emulator.hpp | 2 +- higan/fc/apu/apu.cpp | 6 +- higan/fc/apu/apu.hpp | 2 +- higan/fc/apu/dmc.cpp | 4 +- higan/fc/apu/noise.cpp | 2 +- higan/fc/cartridge/cartridge.cpp | 2 +- higan/fc/cartridge/cartridge.hpp | 2 +- higan/fc/cpu/cpu.hpp | 2 +- higan/fc/fc.hpp | 3 +- higan/fc/ppu/ppu.hpp | 4 +- higan/fc/system/system.cpp | 8 +- higan/fc/system/system.hpp | 7 +- higan/gb/apu/apu.cpp | 4 +- higan/gba/apu/apu.cpp | 4 +- higan/gba/apu/apu.hpp | 161 +++++++++++++++++++++++++++- higan/gba/apu/registers.hpp | 158 --------------------------- higan/gba/apu/wave.cpp | 10 +- higan/md/psg/psg.cpp | 7 +- higan/md/psg/serialization.cpp | 1 - higan/md/vdp/io.cpp | 3 +- higan/md/vdp/vdp.cpp | 1 + higan/md/ym2612/ym2612.cpp | 3 + higan/ms/psg/psg.cpp | 4 +- higan/ms/psg/serialization.cpp | 1 - higan/pce/psg/psg.cpp | 4 +- higan/sfc/coprocessor/icd2/icd2.cpp | 4 +- higan/ws/apu/apu.cpp | 4 +- nall/dsp/iir/one-pole.hpp | 23 ++-- 30 files changed, 260 insertions(+), 230 deletions(-) delete mode 100644 higan/gba/apu/registers.hpp diff --git a/higan/audio/audio.hpp b/higan/audio/audio.hpp index d589974f..3a390491 100644 --- a/higan/audio/audio.hpp +++ b/higan/audio/audio.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -7,6 +8,7 @@ namespace Emulator { struct Interface; struct Audio; +struct Filter; struct Stream; struct Audio { @@ -37,11 +39,19 @@ private: friend class Stream; }; +struct Filter { + enum class Order : uint { First, Second }; + enum class Type : uint { LowPass, HighPass }; + + Order order; + DSP::IIR::OnePole onePole; //first-order + DSP::IIR::Biquad biquad; //second-order +}; + struct Stream { auto reset(uint channels, double inputFrequency, double outputFrequency) -> void; - auto addLowPassFilter(double cutoffFrequency, uint passes = 1) -> void; - auto addHighPassFilter(double cutoffFrequency, uint passes = 1) -> void; + auto addFilter(Filter::Order order, Filter::Type type, double cutoffFrequency, uint passes = 1) -> void; auto pending() const -> bool; auto read(double* samples) -> uint; @@ -54,7 +64,7 @@ struct Stream { private: struct Channel { - vector filters; + vector filters; DSP::Resampler::Cubic resampler; }; vector channels; diff --git a/higan/audio/stream.cpp b/higan/audio/stream.cpp index 8f16a32d..a84f23bf 100644 --- a/higan/audio/stream.cpp +++ b/higan/audio/stream.cpp @@ -11,22 +11,27 @@ auto Stream::reset(uint channels_, double inputFrequency, double outputFrequency } } -auto Stream::addLowPassFilter(double cutoffFrequency, uint passes) -> void { +auto Stream::addFilter(Filter::Order order, Filter::Type type, double cutoffFrequency, uint passes) -> void { for(auto& channel : channels) { for(auto pass : range(passes)) { - double q = DSP::IIR::Biquad::butterworth(passes * 2, pass); - channel.filters.append(DSP::IIR::Biquad{}); - channel.filters.right().reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, inputFrequency, q); - } - } -} + Filter filter{order}; -auto Stream::addHighPassFilter(double cutoffFrequency, uint passes) -> void { - for(auto& channel : channels) { - for(auto pass : range(passes)) { - double q = DSP::IIR::Biquad::butterworth(passes * 2, pass); - channel.filters.append(DSP::IIR::Biquad{}); - channel.filters.right().reset(DSP::IIR::Biquad::Type::HighPass, cutoffFrequency, inputFrequency, q); + if(order == Filter::Order::First) { + DSP::IIR::OnePole::Type _type; + if(type == Filter::Type::LowPass) _type = DSP::IIR::OnePole::Type::LowPass; + if(type == Filter::Type::HighPass) _type = DSP::IIR::OnePole::Type::HighPass; + filter.onePole.reset(_type, cutoffFrequency, inputFrequency); + } + + if(order == Filter::Order::Second) { + DSP::IIR::Biquad::Type _type; + if(type == Filter::Type::LowPass) _type = DSP::IIR::Biquad::Type::LowPass; + if(type == Filter::Type::HighPass) _type = DSP::IIR::Biquad::Type::HighPass; + double q = DSP::IIR::Biquad::butterworth(passes * 2, pass); + filter.biquad.reset(_type, cutoffFrequency, inputFrequency, q); + } + + channel.filters.append(filter); } } } @@ -43,7 +48,12 @@ auto Stream::read(double* samples) -> uint { auto Stream::write(const double* samples) -> void { for(auto c : range(channels)) { double sample = samples[c] + 1e-25; //constant offset used to suppress denormals - for(auto& filter : channels[c].filters) sample = filter.process(sample); + for(auto& filter : channels[c].filters) { + switch(filter.order) { + case Filter::Order::First: sample = filter.onePole.process(sample); break; + case Filter::Order::Second: sample = filter.biquad.process(sample); break; + } + } channels[c].resampler.write(sample); } diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index a458a549..f5ae48cd 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "103"; + static const string Version = "103.01"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/fc/apu/apu.cpp b/higan/fc/apu/apu.cpp index ec05ea37..8a99c3e1 100644 --- a/higan/fc/apu/apu.cpp +++ b/higan/fc/apu/apu.cpp @@ -74,8 +74,10 @@ auto APU::setSample(int16 sample) -> void { auto APU::power() -> void { create(APU::Enter, system.frequency()); stream = Emulator::audio.createStream(1, frequency() / rate()); - stream->addLowPassFilter(20000.0, 3); - stream->addHighPassFilter(20.0, 3); + stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 90.0); + stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 440.0); + stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::LowPass, 14000.0); + stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3); pulse[0].power(); pulse[1].power(); diff --git a/higan/fc/apu/apu.hpp b/higan/fc/apu/apu.hpp index 0aa7ac31..420dc0e4 100644 --- a/higan/fc/apu/apu.hpp +++ b/higan/fc/apu/apu.hpp @@ -1,7 +1,7 @@ struct APU : Thread { shared_pointer stream; - inline auto rate() const -> uint { return Region::NTSC() ? 12 : 16; } + inline auto rate() const -> uint { return Region::PAL() ? 16 : 12; } //apu.cpp APU(); diff --git a/higan/fc/apu/dmc.cpp b/higan/fc/apu/dmc.cpp index 8ebf3622..b007f346 100644 --- a/higan/fc/apu/dmc.cpp +++ b/higan/fc/apu/dmc.cpp @@ -57,7 +57,7 @@ auto APU::DMC::clock() -> uint8 { } } - periodCounter = Region::NTSC() ? dmcPeriodTableNTSC[period] : dmcPeriodTablePAL[period]; + periodCounter = Region::PAL() ? dmcPeriodTablePAL[period] : dmcPeriodTableNTSC[period]; } if(lengthCounter > 0 && !dmaBufferValid && dmaDelayCounter == 0) { @@ -73,7 +73,7 @@ auto APU::DMC::power() -> void { irqPending = 0; period = 0; - periodCounter = Region::NTSC() ? dmcPeriodTableNTSC[0] : dmcPeriodTablePAL[0]; + periodCounter = Region::PAL() ? dmcPeriodTablePAL[0] : dmcPeriodTableNTSC[0]; irqEnable = 0; loopMode = 0; dacLatch = 0; diff --git a/higan/fc/apu/noise.cpp b/higan/fc/apu/noise.cpp index 54e27c37..356280b2 100644 --- a/higan/fc/apu/noise.cpp +++ b/higan/fc/apu/noise.cpp @@ -19,7 +19,7 @@ auto APU::Noise::clock() -> uint8 { } lfsr = (lfsr >> 1) | (feedback << 14); - periodCounter = Region::NTSC() ? apu.noisePeriodTableNTSC[period] : apu.noisePeriodTablePAL[period]; + periodCounter = Region::PAL() ? apu.noisePeriodTablePAL[period] : apu.noisePeriodTableNTSC[period]; } return result; diff --git a/higan/fc/cartridge/cartridge.cpp b/higan/fc/cartridge/cartridge.cpp index a8e65158..663c3255 100644 --- a/higan/fc/cartridge/cartridge.cpp +++ b/higan/fc/cartridge/cartridge.cpp @@ -16,7 +16,7 @@ auto Cartridge::main() -> void { } auto Cartridge::load() -> bool { - if(auto loaded = platform->load(ID::Famicom, "Famicom", "fc", {"NTSC", "PAL"})) { + if(auto loaded = platform->load(ID::Famicom, "Famicom", "fc", {"NTSC-J", "NTSC-U", "PAL"})) { information.pathID = loaded.pathID(); information.region = loaded.option(); } else return false; diff --git a/higan/fc/cartridge/cartridge.hpp b/higan/fc/cartridge/cartridge.hpp index fbb7f6cc..50e6a8ca 100644 --- a/higan/fc/cartridge/cartridge.hpp +++ b/higan/fc/cartridge/cartridge.hpp @@ -2,7 +2,7 @@ #include "board/board.hpp" struct Cartridge : Thread { - inline auto rate() const -> uint { return Region::NTSC() ? 12 : 16; } + inline auto rate() const -> uint { return Region::PAL() ? 16 : 12; } //cartridge.cpp static auto Enter() -> void; diff --git a/higan/fc/cpu/cpu.hpp b/higan/fc/cpu/cpu.hpp index b9ef2481..978e5372 100644 --- a/higan/fc/cpu/cpu.hpp +++ b/higan/fc/cpu/cpu.hpp @@ -1,5 +1,5 @@ struct CPU : Processor::MOS6502, Thread { - inline auto rate() const -> uint { return Region::NTSC() ? 12 : 16; } + inline auto rate() const -> uint { return Region::PAL() ? 16 : 12; } //cpu.cpp static auto Enter() -> void; diff --git a/higan/fc/fc.hpp b/higan/fc/fc.hpp index 598da789..912590c4 100644 --- a/higan/fc/fc.hpp +++ b/higan/fc/fc.hpp @@ -30,7 +30,8 @@ namespace Famicom { }; struct Region { - static inline auto NTSC() -> bool; + static inline auto NTSCJ() -> bool; + static inline auto NTSCU() -> bool; static inline auto PAL() -> bool; }; diff --git a/higan/fc/ppu/ppu.hpp b/higan/fc/ppu/ppu.hpp index 856fdffe..52efb8fa 100644 --- a/higan/fc/ppu/ppu.hpp +++ b/higan/fc/ppu/ppu.hpp @@ -1,6 +1,6 @@ struct PPU : Thread { - inline auto rate() const -> uint { return Region::NTSC() ? 4 : 5; } - inline auto vlines() const -> uint { return Region::NTSC() ? 262 : 312; } + inline auto rate() const -> uint { return Region::PAL() ? 5 : 4; } + inline auto vlines() const -> uint { return Region::PAL() ? 312 : 262; } //ppu.cpp static auto Enter() -> void; diff --git a/higan/fc/system/system.cpp b/higan/fc/system/system.cpp index 0f533604..18350906 100644 --- a/higan/fc/system/system.cpp +++ b/higan/fc/system/system.cpp @@ -32,8 +32,12 @@ auto System::load(Emulator::Interface* interface) -> bool { auto document = BML::unserialize(information.manifest); if(!cartridge.load()) return false; - if(cartridge.region() == "NTSC") { - information.region = Region::NTSC; + if(cartridge.region() == "NTSC-J") { + information.region = Region::NTSCJ; + information.frequency = Emulator::Constants::Colorburst::NTSC * 6.0; + } + if(cartridge.region() == "NTSC-U") { + information.region = Region::NTSCU; information.frequency = Emulator::Constants::Colorburst::NTSC * 6.0; } if(cartridge.region() == "PAL") { diff --git a/higan/fc/system/system.hpp b/higan/fc/system/system.hpp index 7725a6fb..a72375d9 100644 --- a/higan/fc/system/system.hpp +++ b/higan/fc/system/system.hpp @@ -1,5 +1,5 @@ struct System { - enum class Region : uint { NTSC, PAL }; + enum class Region : uint { NTSCJ, NTSCU, PAL }; auto loaded() const -> bool { return information.loaded; } auto region() const -> Region { return information.region; } @@ -33,7 +33,7 @@ private: struct Information { bool loaded = false; - Region region = Region::NTSC; + Region region = Region::NTSCJ; double frequency = Emulator::Constants::Colorburst::NTSC * 6.0; string manifest; } information; @@ -53,5 +53,6 @@ struct Peripherals { extern System system; extern Peripherals peripherals; -auto Region::NTSC() -> bool { return system.region() == System::Region::NTSC; } +auto Region::NTSCJ() -> bool { return system.region() == System::Region::NTSCJ; } +auto Region::NTSCU() -> bool { return system.region() == System::Region::NTSCU; } auto Region::PAL() -> bool { return system.region() == System::Region::PAL; } diff --git a/higan/gb/apu/apu.cpp b/higan/gb/apu/apu.cpp index 240d86a3..b9f7498d 100644 --- a/higan/gb/apu/apu.cpp +++ b/higan/gb/apu/apu.cpp @@ -55,8 +55,8 @@ auto APU::power() -> void { create(Enter, 2 * 1024 * 1024); if(!Model::SuperGameBoy()) { stream = Emulator::audio.createStream(2, frequency()); - stream->addLowPassFilter(20000.0, 3); - stream->addHighPassFilter(20.0, 3); + stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0); + stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3); } for(uint n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this; diff --git a/higan/gba/apu/apu.cpp b/higan/gba/apu/apu.cpp index 33e5982f..8b944813 100644 --- a/higan/gba/apu/apu.cpp +++ b/higan/gba/apu/apu.cpp @@ -77,8 +77,8 @@ auto APU::step(uint clocks) -> void { 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); + stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0); + stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3); clock = 0; square1.power(); diff --git a/higan/gba/apu/apu.hpp b/higan/gba/apu/apu.hpp index f2d958df..ecc3b117 100644 --- a/higan/gba/apu/apu.hpp +++ b/higan/gba/apu/apu.hpp @@ -1,8 +1,6 @@ struct APU : Thread, IO { shared_pointer stream; - #include "registers.hpp" - static auto Enter() -> void; auto main() -> void; auto step(uint clocks) -> void; @@ -16,6 +14,165 @@ struct APU : Thread, IO { auto serialize(serializer&) -> void; uint clock; + + struct Registers { + struct SoundBias { + uint10 level; + uint2 amplitude; + } bias; + } regs; + + struct Sweep { + uint3 shift; + uint1 direction; + uint3 frequency; + + uint1 enable; + uint1 negate; + uint3 period; + }; + + struct Envelope { + uint3 frequency; + uint1 direction; + uint4 volume; + + uint3 period; + + auto dacEnable() const -> bool { return volume || direction; } + }; + + struct Square { + Envelope envelope; + uint1 enable; + uint6 length; + uint2 duty; + uint11 frequency; + uint1 counter; + uint1 initialize; + + int shadowfrequency; + uint1 signal; + uint4 output; + uint period; + uint3 phase; + uint4 volume; + + auto run() -> void; + auto clocklength() -> void; + auto clockenvelope() -> void; + }; + + struct Square1 : Square { + Sweep sweep; + + auto runsweep(bool update) -> void; + auto clocksweep() -> void; + auto read(uint addr) const -> uint8; + auto write(uint addr, uint8 byte) -> void; + auto power() -> void; + } square1; + + struct Square2 : Square { + auto read(uint addr) const -> uint8; + auto write(uint addr, uint8 byte) -> void; + auto power() -> void; + } square2; + + struct Wave { + uint1 mode; + uint1 bank; + uint1 dacenable; + uint8 length; + uint3 volume; + uint11 frequency; + uint1 counter; + uint1 initialize; + uint4 pattern[2 * 32]; + + uint1 enable; + uint4 output; + uint5 patternaddr; + uint1 patternbank; + uint4 patternsample; + uint period; + + auto run() -> void; + auto clocklength() -> void; + auto read(uint addr) const -> uint8; + auto write(uint addr, uint8 byte) -> void; + auto readram(uint addr) const -> uint8; + auto writeram(uint addr, uint8 byte) -> void; + auto power() -> void; + } wave; + + struct Noise { + Envelope envelope; + uint6 length; + uint3 divisor; + uint1 narrowlfsr; + uint4 frequency; + uint1 counter; + uint1 initialize; + + uint1 enable; + uint15 lfsr; + uint4 output; + uint period; + uint4 volume; + + auto divider() const -> uint; + auto run() -> void; + auto clocklength() -> void; + auto clockenvelope() -> void; + auto read(uint addr) const -> uint8; + auto write(uint addr, uint8 byte) -> void; + auto power() -> void; + } noise; + + struct Sequencer { + uint2 volume; + uint3 lvolume; + uint3 rvolume; + uint1 lenable[4]; + uint1 renable[4]; + uint1 masterenable; + + uint12 base; + uint3 step; + int16 lsample; + int16 rsample; + + uint10 loutput; + uint10 routput; + + auto sample() -> void; + + auto read(uint addr) const -> uint8; + auto write(uint addr, uint8 byte) -> void; + auto power() -> void; + } sequencer; + + struct FIFO { + int8 samples[32]; + int8 active; + int8 output; + + uint5 rdoffset; + uint5 wroffset; + uint6 size; + + uint1 volume; //0 = 50%, 1 = 100% + uint1 lenable; + uint1 renable; + uint1 timer; + + auto sample() -> void; + auto read() -> void; + auto write(int8 byte) -> void; + auto reset() -> void; + auto power() -> void; + } fifo[2]; }; extern APU apu; diff --git a/higan/gba/apu/registers.hpp b/higan/gba/apu/registers.hpp deleted file mode 100644 index 1e574369..00000000 --- a/higan/gba/apu/registers.hpp +++ /dev/null @@ -1,158 +0,0 @@ -struct Registers { - struct SoundBias { - uint10 level; - uint2 amplitude; - } bias; -} regs; - -struct Sweep { - uint3 shift; - uint1 direction; - uint3 frequency; - - uint1 enable; - uint1 negate; - uint3 period; -}; - -struct Envelope { - uint3 frequency; - uint1 direction; - uint4 volume; - - uint3 period; - - auto dacEnable() const -> bool { return volume || direction; } -}; - -struct Square { - Envelope envelope; - uint1 enable; - uint6 length; - uint2 duty; - uint11 frequency; - uint1 counter; - uint1 initialize; - - int shadowfrequency; - uint1 signal; - uint4 output; - uint period; - uint3 phase; - uint4 volume; - - auto run() -> void; - auto clocklength() -> void; - auto clockenvelope() -> void; -}; - -struct Square1 : Square { - Sweep sweep; - - auto runsweep(bool update) -> void; - auto clocksweep() -> void; - auto read(uint addr) const -> uint8; - auto write(uint addr, uint8 byte) -> void; - auto power() -> void; -} square1; - -struct Square2 : Square { - auto read(uint addr) const -> uint8; - auto write(uint addr, uint8 byte) -> void; - auto power() -> void; -} square2; - -struct Wave { - uint1 mode; - uint1 bank; - uint1 dacenable; - uint8 length; - uint3 volume; - uint11 frequency; - uint1 counter; - uint1 initialize; - uint4 pattern[32]; - - uint1 enable; - uint4 output; - uint4 patternaddr; - uint1 patternbank; - uint4 patternsample; - uint period; - - auto run() -> void; - auto clocklength() -> void; - auto read(uint addr) const -> uint8; - auto write(uint addr, uint8 byte) -> void; - auto readram(uint addr) const -> uint8; - auto writeram(uint addr, uint8 byte) -> void; - auto power() -> void; -} wave; - -struct Noise { - Envelope envelope; - uint6 length; - uint3 divisor; - uint1 narrowlfsr; - uint4 frequency; - uint1 counter; - uint1 initialize; - - uint1 enable; - uint15 lfsr; - uint4 output; - uint period; - uint4 volume; - - auto divider() const -> uint; - auto run() -> void; - auto clocklength() -> void; - auto clockenvelope() -> void; - auto read(uint addr) const -> uint8; - auto write(uint addr, uint8 byte) -> void; - auto power() -> void; -} noise; - -struct Sequencer { - uint2 volume; - uint3 lvolume; - uint3 rvolume; - uint1 lenable[4]; - uint1 renable[4]; - uint1 masterenable; - - uint12 base; - uint3 step; - int16 lsample; - int16 rsample; - - uint10 loutput; - uint10 routput; - - auto sample() -> void; - - auto read(uint addr) const -> uint8; - auto write(uint addr, uint8 byte) -> void; - auto power() -> void; -} sequencer; - -struct FIFO { - int8 samples[32]; - int8 active; - int8 output; - - uint5 rdoffset; - uint5 wroffset; - uint6 size; - - uint1 volume; //0 = 50%, 1 = 100% - uint1 lenable; - uint1 renable; - uint1 timer; - - auto sample() -> void; - auto read() -> void; - auto write(int8 byte) -> void; - auto reset() -> void; - auto power() -> void; -} fifo[2]; diff --git a/higan/gba/apu/wave.cpp b/higan/gba/apu/wave.cpp index 647fc140..ebfcb223 100644 --- a/higan/gba/apu/wave.cpp +++ b/higan/gba/apu/wave.cpp @@ -1,7 +1,7 @@ auto APU::Wave::run() -> void { if(period && --period == 0) { period = 1 * (2048 - frequency); - patternsample = pattern[patternbank * 16 + patternaddr++]; + patternsample = pattern[patternbank << 5 | patternaddr++]; if(patternaddr == 0) patternbank ^= mode; } @@ -66,14 +66,14 @@ auto APU::Wave::write(uint addr, uint8 byte) -> void { auto APU::Wave::readram(uint addr) const -> uint8 { uint8 byte = 0; - byte |= pattern[addr * 2 + 0] << 0; - byte |= pattern[addr * 2 + 1] << 4; + byte |= pattern[!bank << 5 | addr << 1 | 0] << 0; + byte |= pattern[!bank << 5 | addr << 1 | 1] << 4; return byte; } auto APU::Wave::writeram(uint addr, uint8 byte) -> void { - pattern[addr * 2 + 0] = byte >> 0; - pattern[addr * 2 + 1] = byte >> 4; + pattern[!bank << 5 | addr << 1 | 0] = byte >> 0; + pattern[!bank << 5 | addr << 1 | 1] = byte >> 4; } auto APU::Wave::power() -> void { diff --git a/higan/md/psg/psg.cpp b/higan/md/psg/psg.cpp index f32e8105..b02527fb 100644 --- a/higan/md/psg/psg.cpp +++ b/higan/md/psg/psg.cpp @@ -37,12 +37,13 @@ auto PSG::step(uint clocks) -> void { auto PSG::power() -> void { create(PSG::Enter, system.colorburst() / 16.0); stream = Emulator::audio.createStream(1, frequency()); - stream->addLowPassFilter(20000.0, 3); - stream->addHighPassFilter(20.0, 3); + stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0); + stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::LowPass, 2840.0); + stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3); select = 0; for(auto n : range(15)) { - levels[n] = 0x2000 * pow(2, n * -2.0 / 6.0) + 0.5; + levels[n] = 0x1400 * pow(2, n * -2.0 / 6.0) + 0.5; } levels[15] = 0; diff --git a/higan/md/psg/serialization.cpp b/higan/md/psg/serialization.cpp index 42a04120..121179d9 100644 --- a/higan/md/psg/serialization.cpp +++ b/higan/md/psg/serialization.cpp @@ -7,7 +7,6 @@ auto PSG::serialize(serializer& s) -> void { noise.serialize(s); s.integer(select); - s.array(levels); } auto PSG::Tone::serialize(serializer& s) -> void { diff --git a/higan/md/vdp/io.cpp b/higan/md/vdp/io.cpp index 8c723998..bcb26dab 100644 --- a/higan/md/vdp/io.cpp +++ b/higan/md/vdp/io.cpp @@ -116,8 +116,9 @@ auto VDP::readControlPort() -> uint16 { uint16 result = 0b0011'0100'0000'0000; result |= 1 << 9; //FIFO empty result |= (state.vcounter >= screenHeight()) << 3; //vertical blank - result |= (state.vcounter >= screenHeight() || state.hcounter >= 1280) << 2; //horizontal blank + result |= (state.hcounter >= 1280) << 2; //horizontal blank result |= io.command.bit(5) << 1; //DMA active + result |= Region::PAL() << 0; return result; } diff --git a/higan/md/vdp/vdp.cpp b/higan/md/vdp/vdp.cpp index 8c349263..9b0b75bd 100644 --- a/higan/md/vdp/vdp.cpp +++ b/higan/md/vdp/vdp.cpp @@ -30,6 +30,7 @@ auto VDP::main() -> void { if(io.verticalBlankInterruptEnable) { cpu.raise(CPU::Interrupt::VerticalBlank); } + //todo: should only stay high for ~2573/2 clocks apu.setINT(true); } diff --git a/higan/md/ym2612/ym2612.cpp b/higan/md/ym2612/ym2612.cpp index da9ee4de..28f9775d 100644 --- a/higan/md/ym2612/ym2612.cpp +++ b/higan/md/ym2612/ym2612.cpp @@ -157,6 +157,9 @@ auto YM2612::step(uint clocks) -> void { auto YM2612::power() -> void { create(YM2612::Enter, system.colorburst() * 15.0 / 7.0); stream = Emulator::audio.createStream(2, frequency() / 144.0); + stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0); + stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::LowPass, 2840.0); + stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3); io = {}; lfo = {}; diff --git a/higan/ms/psg/psg.cpp b/higan/ms/psg/psg.cpp index c3face17..4c7248d6 100644 --- a/higan/ms/psg/psg.cpp +++ b/higan/ms/psg/psg.cpp @@ -44,8 +44,8 @@ auto PSG::power() -> void { //use stereo mode for both; output same sample to both channels for Master System create(PSG::Enter, system.colorburst() / 16.0); stream = Emulator::audio.createStream(2, frequency()); - stream->addLowPassFilter(20000.0, 3); - stream->addHighPassFilter(20.0, 3); + stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0); + stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3); select = 0; for(auto n : range(15)) { diff --git a/higan/ms/psg/serialization.cpp b/higan/ms/psg/serialization.cpp index 1fa33c8a..cad1b1ba 100644 --- a/higan/ms/psg/serialization.cpp +++ b/higan/ms/psg/serialization.cpp @@ -7,7 +7,6 @@ auto PSG::serialize(serializer& s) -> void { noise.serialize(s); s.integer(select); - s.array(levels); } auto PSG::Tone::serialize(serializer& s) -> void { diff --git a/higan/pce/psg/psg.cpp b/higan/pce/psg/psg.cpp index bebca9a5..57c0cfec 100644 --- a/higan/pce/psg/psg.cpp +++ b/higan/pce/psg/psg.cpp @@ -52,8 +52,8 @@ auto PSG::step(uint clocks) -> void { auto PSG::power() -> void { create(PSG::Enter, system.colorburst()); stream = Emulator::audio.createStream(2, frequency()); - stream->addLowPassFilter(20000.0, 3); - stream->addHighPassFilter(20.0, 3); + stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0); + stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3); memory::fill(&io, sizeof(IO)); for(auto C : range(6)) channel[C].power(C); diff --git a/higan/sfc/coprocessor/icd2/icd2.cpp b/higan/sfc/coprocessor/icd2/icd2.cpp index b177a8bd..ca5bc401 100644 --- a/higan/sfc/coprocessor/icd2/icd2.cpp +++ b/higan/sfc/coprocessor/icd2/icd2.cpp @@ -48,8 +48,8 @@ auto ICD2::unload() -> void { auto ICD2::power() -> void { create(ICD2::Enter, system.colorburst() * 6.0 / 5.0); stream = Emulator::audio.createStream(2, frequency() / 2.0); - stream->addLowPassFilter(20000.0, 3); - stream->addHighPassFilter(20.0, 3); + stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0); + stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3); r6003 = 0x00; r6004 = 0xff; diff --git a/higan/ws/apu/apu.cpp b/higan/ws/apu/apu.cpp index ff62eddf..d7dd6e59 100644 --- a/higan/ws/apu/apu.cpp +++ b/higan/ws/apu/apu.cpp @@ -67,8 +67,8 @@ auto APU::step(uint clocks) -> void { auto APU::power() -> void { create(APU::Enter, 3'072'000); stream = Emulator::audio.createStream(2, frequency()); - stream->addLowPassFilter(20000.0, 3); - stream->addHighPassFilter(20.0, 3); + stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0); + stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3); bus.map(this, 0x004a, 0x004c); bus.map(this, 0x004e, 0x0050); diff --git a/nall/dsp/iir/one-pole.hpp b/nall/dsp/iir/one-pole.hpp index c644b9aa..e678c999 100644 --- a/nall/dsp/iir/one-pole.hpp +++ b/nall/dsp/iir/one-pole.hpp @@ -26,20 +26,19 @@ auto OnePole::reset(Type type, double cutoffFrequency, double samplingFrequency) this->cutoffFrequency = cutoffFrequency; this->samplingFrequency = samplingFrequency; - b1 = exp(-2.0 * Math::Pi * cutoffFrequency / samplingFrequency); - a0 = 1.0 - b1; - z1 = 0.0; -} - -auto OnePole::process(double in) -> double { - z1 = in * a0 + z1 * b1; - - switch(type) { - case Type::LowPass: return z1; - case Type::HighPass: return in - z1; - default: return 0.0; + double x = cos(2.0 * Math::Pi * cutoffFrequency / samplingFrequency); + if(type == Type::LowPass) { + b1 = +2.0 - x - sqrt((+2.0 - x) * (+2.0 - x) - 1); + a0 = 1.0 - b1; + } else { + b1 = -2.0 - x + sqrt((-2.0 - x) * (-2.0 - x) - 1); + a0 = 1.0 + b1; } } +auto OnePole::process(double in) -> double { + return z1 = in * a0 + z1 * b1; +} + }}}