diff --git a/higan/audio/stream.cpp b/higan/audio/stream.cpp index dc39be22..b2ad126a 100644 --- a/higan/audio/stream.cpp +++ b/higan/audio/stream.cpp @@ -7,7 +7,7 @@ auto Stream::reset(uint channels_, double inputFrequency, double outputFrequency channel.iir.resize(order / 2); for(auto phase : range(order / 2)) { double q = DSP::IIR::Biquad::butterworth(order, phase); - channel.iir[phase].reset(DSP::IIR::Biquad::Type::LowPass, 20000.0 / inputFrequency, q); + channel.iir[phase].reset(DSP::IIR::Biquad::Type::LowPass, 20000.0, inputFrequency, q); } } diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index ce9b5cd5..1867ed80 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 = "102.14"; + static const string Version = "102.15"; 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 2c7ef4eb..4f94c2ae 100644 --- a/higan/fc/apu/apu.cpp +++ b/higan/fc/apu/apu.cpp @@ -49,15 +49,11 @@ auto APU::main() -> void { clockFrameCounterDivider(); - int output = pulseDAC[pulse_output] + dmcTriangleNoiseDAC[dmc_output][triangle_output][noise_output]; - - output = filter.runHipassStrong(output); + int output = 0; + output += pulseDAC[pulse_output]; + output += dmcTriangleNoiseDAC[dmc_output][triangle_output][noise_output]; output += cartridgeSample; - output = filter.runHipassWeak(output); -//output = filter.runLopass(output); - output = sclamp<16>(output); - - stream->sample(output / 32768.0); + stream->sample(sclamp<16>(output) / 32768.0); tick(); } @@ -79,10 +75,6 @@ auto APU::power() -> void { create(APU::Enter, system.colorburst() * 6.0); stream = Emulator::audio.createStream(1, system.colorburst() / 2.0); - filter.hipassStrong = 0; - filter.hipassWeak = 0; - filter.lopass = 0; - pulse[0].power(); pulse[1].power(); triangle.power(); @@ -265,21 +257,6 @@ auto APU::writeIO(uint16 addr, uint8 data) -> void { } } -auto APU::Filter::runHipassStrong(int sample) -> int { - hipassStrong += ((((int64)sample << 16) - (hipassStrong >> 16)) * HiPassStrong) >> 16; - return sample - (hipassStrong >> 32); -} - -auto APU::Filter::runHipassWeak(int sample) -> int { - hipassWeak += ((((int64)sample << 16) - (hipassWeak >> 16)) * HiPassWeak) >> 16; - return sample - (hipassWeak >> 32); -} - -auto APU::Filter::runLopass(int sample) -> int { - lopass += ((((int64)sample << 16) - (lopass >> 16)) * LoPass) >> 16; - return (lopass >> 32); -} - auto APU::clockFrameCounter() -> void { frame.counter++; diff --git a/higan/fc/apu/apu.hpp b/higan/fc/apu/apu.hpp index 9e041476..17deae97 100644 --- a/higan/fc/apu/apu.hpp +++ b/higan/fc/apu/apu.hpp @@ -16,20 +16,6 @@ struct APU : Thread { auto serialize(serializer&) -> void; - struct Filter { - auto runHipassStrong(int sample) -> int; - auto runHipassWeak(int sample) -> int; - auto runLopass(int sample) -> int; - - auto serialize(serializer&) -> void; - - enum : int { HiPassStrong = 225574, HiPassWeak = 57593, LoPass = 86322413 }; - - int64 hipassStrong; - int64 hipassWeak; - int64 lopass; - }; - struct Envelope { auto volume() const -> uint; auto clock() -> void; @@ -174,7 +160,6 @@ struct APU : Thread { auto clockFrameCounter() -> void; auto clockFrameCounterDivider() -> void; - Filter filter; FrameCounter frame; uint8 enabledChannels; diff --git a/higan/fc/apu/serialization.cpp b/higan/fc/apu/serialization.cpp index 8e20f979..48b80b58 100644 --- a/higan/fc/apu/serialization.cpp +++ b/higan/fc/apu/serialization.cpp @@ -1,8 +1,6 @@ auto APU::serialize(serializer& s) -> void { Thread::serialize(s); - filter.serialize(s); - pulse[0].serialize(s); pulse[1].serialize(s); triangle.serialize(s); @@ -13,12 +11,6 @@ auto APU::serialize(serializer& s) -> void { s.integer(cartridgeSample); } -auto APU::Filter::serialize(serializer& s) -> void { - s.integer(hipassStrong); - s.integer(hipassWeak); - s.integer(lopass); -} - auto APU::Envelope::serialize(serializer& s) -> void { s.integer(speed); s.integer(useSpeedAsVolume); diff --git a/higan/md/psg/psg.cpp b/higan/md/psg/psg.cpp index bba58032..4014600f 100644 --- a/higan/md/psg/psg.cpp +++ b/higan/md/psg/psg.cpp @@ -18,16 +18,12 @@ auto PSG::main() -> void { noise.run(); int output = 0; - if(tone0.output) output += levels[tone0.volume]; - if(tone1.output) output += levels[tone1.volume]; - if(tone2.output) output += levels[tone2.volume]; - if(noise.output) output += levels[noise.volume]; + output += levels[tone0.volume] * tone0.output; + output += levels[tone1.volume] * tone1.output; + output += levels[tone2.volume] * tone2.output; + output += levels[noise.volume] * noise.output; - lowpass += (output - lowpass) * 20 / 256; - output = output * 2 / 6 + lowpass * 3 / 4; - output = sclamp<16>(output - 32768); - - stream->sample(output / 32768.0); + stream->sample(sclamp<16>(output) / 32768.0); step(1); } @@ -42,9 +38,8 @@ auto PSG::power() -> void { stream = Emulator::audio.createStream(1, frequency()); select = 0; - lowpass = 0; for(auto n : range(15)) { - levels[n] = 0x1000 * pow(2, n * -2.0 / 6.0) + 0.5; + levels[n] = 0x2000 * pow(2, n * -2.0 / 6.0) + 0.5; } levels[15] = 0; diff --git a/higan/md/psg/psg.hpp b/higan/md/psg/psg.hpp index 5ee805c9..12fbbf69 100644 --- a/higan/md/psg/psg.hpp +++ b/higan/md/psg/psg.hpp @@ -39,9 +39,8 @@ private: uint1 output; } noise; - uint3 select; - int lowpass; - uint16 levels[16]; + uint3 select; + int16 levels[16]; }; extern PSG psg; diff --git a/higan/md/ym2612/channel.cpp b/higan/md/ym2612/channel.cpp index 70010774..1f35ce13 100644 --- a/higan/md/ym2612/channel.cpp +++ b/higan/md/ym2612/channel.cpp @@ -135,8 +135,8 @@ auto YM2612::Channel::Operator::updateLevel() -> void { } auto YM2612::Channel::power() -> void { - leftEnable = true; - rightEnable = true; + leftEnable = 1; + rightEnable = 1; algorithm = 0; feedback = 0; @@ -146,8 +146,8 @@ auto YM2612::Channel::power() -> void { mode = 0; for(auto& op : operators) { - op.keyOn = false; - op.lfoEnable = false; + op.keyOn = 0; + op.lfoEnable = 0; op.detune = 0; op.multiple = 0; op.totalLevel = 0; @@ -180,11 +180,11 @@ auto YM2612::Channel::power() -> void { op.envelope.sustainLevel = 0; op.envelope.releaseRate = 1; - op.ssg.enable = false; - op.ssg.attack = false; - op.ssg.alternate = false; - op.ssg.hold = false; - op.ssg.invert = false; + op.ssg.enable = 0; + op.ssg.attack = 0; + op.ssg.alternate = 0; + op.ssg.hold = 0; + op.ssg.invert = 0; op.updatePitch(); op.updateLevel(); diff --git a/higan/md/ym2612/timer.cpp b/higan/md/ym2612/timer.cpp index d8441f16..a25f0b13 100644 --- a/higan/md/ym2612/timer.cpp +++ b/higan/md/ym2612/timer.cpp @@ -6,14 +6,6 @@ auto YM2612::TimerA::run() -> void { line |= irq; } -auto YM2612::TimerA::power() -> void { - enable = 0; - irq = 0; - line = 0; - period = 0; - counter = 0; -} - auto YM2612::TimerB::run() -> void { if(!enable) return; if(++divider) return; @@ -22,12 +14,3 @@ auto YM2612::TimerB::run() -> void { counter = period; line |= irq; } - -auto YM2612::TimerB::power() -> void { - enable = 0; - irq = 0; - line = 0; - period = 0; - counter = 0; - divider = 0; -} diff --git a/higan/md/ym2612/ym2612.cpp b/higan/md/ym2612/ym2612.cpp index cbfca0a4..2c8c75f5 100644 --- a/higan/md/ym2612/ym2612.cpp +++ b/higan/md/ym2612/ym2612.cpp @@ -137,22 +137,14 @@ auto YM2612::sample() -> void { accumulator += out(0) + out(1) + out(2) + out(3); } - int voiceData = outMask & min(max(accumulator, -0x1ffff), +0x1ffff); + int voiceData = sclamp<14>(accumulator) & outMask; if(dac.enable && (&channel == &channels[5])) voiceData = dac.sample << 6; if(channel.leftEnable ) left += voiceData; if(channel.rightEnable) right += voiceData; } - int cutoff = 20; - - lpfLeft = (left - lpfLeft) * cutoff / 256; - lpfRight = (right - lpfRight) * cutoff / 256; - - left = left * 2 / 6 + lpfLeft * 3 / 4; - right = right * 2 / 6 + lpfRight * 3 / 4; - - stream->sample(left / 32768.0, right / 32768.0); + stream->sample(sclamp<16>(left) / 32768.0, sclamp<16>(right) / 32768.0); } auto YM2612::step(uint clocks) -> void { @@ -165,13 +157,12 @@ auto YM2612::power() -> void { create(YM2612::Enter, system.colorburst() * 15.0 / 7.0 / 144.0); stream = Emulator::audio.createStream(2, frequency()); - memory::fill(&io, sizeof(IO)); - memory::fill(&lfo, sizeof(LFO)); - memory::fill(&dac, sizeof(DAC)); - memory::fill(&envelope, sizeof(Envelope)); - - timerA.power(); - timerB.power(); + io = {}; + lfo = {}; + dac = {}; + envelope = {}; + timerA = {}; + timerB = {}; for(auto& channel : channels) channel.power(); const uint positive = 0; diff --git a/higan/md/ym2612/ym2612.hpp b/higan/md/ym2612/ym2612.hpp index 1a257203..0722c390 100644 --- a/higan/md/ym2612/ym2612.hpp +++ b/higan/md/ym2612/ym2612.hpp @@ -17,49 +17,47 @@ struct YM2612 : Thread { private: struct IO { - uint9 address; + uint9 address = 0; } io; struct LFO { - uint1 enable; - uint3 rate; - uint32 clock; - uint32 divider; + uint1 enable = 0; + uint3 rate = 0; + uint32 clock = 0; + uint32 divider = 0; } lfo; struct DAC { - uint1 enable; - uint8 sample; + uint1 enable = 0; + uint8 sample = 0; } dac; struct Envelope { - uint32 clock; - uint32 divider; + uint32 clock = 0; + uint32 divider = 0; } envelope; struct TimerA { //timer.cpp auto run() -> void; - auto power() -> void; - uint1 enable; - uint1 irq; - uint1 line; - uint10 period; - uint10 counter; + uint1 enable = 0; + uint1 irq = 0; + uint1 line = 0; + uint10 period = 0; + uint10 counter = 0; } timerA; struct TimerB { //timer.cpp auto run() -> void; - auto power() -> void; - uint1 enable; - uint1 irq; - uint1 line; - uint8 period; - uint8 counter; - uint4 divider; + uint1 enable = 0; + uint1 irq = 0; + uint1 line = 0; + uint8 period = 0; + uint8 counter = 0; + uint4 divider = 0; } timerB; enum : uint { Attack, Decay, Sustain, Release }; @@ -68,15 +66,15 @@ private: //channel.cpp auto power() -> void; - bool leftEnable; - bool rightEnable; + uint1 leftEnable = 1; + uint1 rightEnable = 1; - uint3 algorithm; - uint3 feedback; - uint3 vibrato; - uint2 tremolo; + uint3 algorithm = 0; + uint3 feedback = 0; + uint3 vibrato = 0; + uint2 tremolo = 0; - uint2 mode; + uint2 mode = 0; struct Operator { Channel& channel; @@ -93,54 +91,54 @@ private: auto updatePhase() -> void; auto updateLevel() -> void; - bool keyOn; - bool lfoEnable; - uint3 detune; - uint4 multiple; - uint7 totalLevel; + uint1 keyOn = 0; + uint1 lfoEnable = 0; + uint3 detune = 0; + uint4 multiple = 0; + uint7 totalLevel = 0; - uint16 outputLevel; - int16 output; - int16 prior; + uint16 outputLevel = 0x1fff; + int16 output = 0; + int16 prior = 0; struct Pitch { - uint11 value; - uint11 reload; - uint11 latch; + uint11 value = 0; + uint11 reload = 0; + uint11 latch = 0; } pitch; struct Octave { - uint3 value; - uint3 reload; - uint3 latch; + uint3 value = 0; + uint3 reload = 0; + uint3 latch = 0; } octave; struct Phase { - uint20 value; - uint20 delta; + uint20 value = 0; + uint20 delta = 0; } phase; struct Envelope { - uint state; - int rate; - int divider; - uint32 steps; - uint10 value; + uint state = Release; + int rate = 0; + int divider = 11; + uint32 steps = 0; + uint10 value = 0x3ff; - uint2 keyScale; - uint5 attackRate; - uint5 decayRate; - uint5 sustainRate; - uint4 sustainLevel; - uint5 releaseRate; + uint2 keyScale = 0; + uint5 attackRate = 0; + uint5 decayRate = 0; + uint5 sustainRate = 0; + uint4 sustainLevel = 0; + uint5 releaseRate = 1; } envelope; struct SSG { - bool enable; - bool attack; - bool alternate; - bool hold; - bool invert; + uint1 enable = 0; + uint1 attack = 0; + uint1 alternate = 0; + uint1 hold = 0; + uint1 invert = 0; } ssg; } operators[4]{*this, *this, *this, *this}; @@ -150,9 +148,6 @@ private: uint16 sine[0x400]; int16 pow2[0x200]; - int lpfLeft; - int lpfRight; - //constants.cpp struct EnvelopeRate { uint32_t divider; diff --git a/higan/ms/psg/psg.cpp b/higan/ms/psg/psg.cpp index 6407b4a9..fa4d7526 100644 --- a/higan/ms/psg/psg.cpp +++ b/higan/ms/psg/psg.cpp @@ -19,26 +19,18 @@ auto PSG::main() -> void { noise.run(); int left = 0; - if(tone0.output && tone0.left) left += levels[tone0.volume]; - if(tone1.output && tone1.left) left += levels[tone1.volume]; - if(tone2.output && tone2.left) left += levels[tone2.volume]; - if(noise.output && noise.left) left += levels[noise.volume]; - - lowpassLeft += (left - lowpassLeft) * 20 / 256; - left = left * 2 / 6 + lowpassLeft * 3 / 4; - left = sclamp<16>(left - 32768); + left += levels[tone0.volume] * tone0.output * tone0.left; + left += levels[tone1.volume] * tone1.output * tone1.left; + left += levels[tone2.volume] * tone2.output * tone2.left; + left += levels[noise.volume] * noise.output * noise.left; int right = 0; - if(tone0.output && tone0.right) right += levels[tone0.volume]; - if(tone1.output && tone1.right) right += levels[tone1.volume]; - if(tone2.output && tone2.right) right += levels[tone2.volume]; - if(noise.output && noise.right) right += levels[noise.volume]; + right += levels[tone0.volume] * tone0.output * tone0.right; + right += levels[tone1.volume] * tone1.output * tone1.right; + right += levels[tone2.volume] * tone2.output * tone2.right; + right += levels[noise.volume] * noise.output * noise.right; - lowpassRight += (right - lowpassRight) * 20 / 256; - right = right * 2 / 6 + lowpassRight * 3 / 4; - right = sclamp<16>(right - 32768); - - stream->sample(left / 32768.0, right / 32768.0); + stream->sample(sclamp<16>(left) / 32768.0, sclamp<16>(right) / 32768.0); step(1); } @@ -54,8 +46,6 @@ auto PSG::power() -> void { stream = Emulator::audio.createStream(2, frequency()); select = 0; - lowpassLeft = 0; - lowpassRight = 0; for(auto n : range(15)) { levels[n] = 0x2000 * pow(2, n * -2.0 / 6.0) + 0.5; } diff --git a/higan/ms/psg/psg.hpp b/higan/ms/psg/psg.hpp index 91fc3305..4f7fde56 100644 --- a/higan/ms/psg/psg.hpp +++ b/higan/ms/psg/psg.hpp @@ -55,10 +55,8 @@ private: uint1 right; } noise; - uint3 select; - int lowpassLeft; - int lowpassRight; - uint16 levels[16]; + uint3 select; + int16 levels[16]; }; extern PSG psg; diff --git a/higan/ms/psg/serialization.cpp b/higan/ms/psg/serialization.cpp index 9c2adf28..1fa33c8a 100644 --- a/higan/ms/psg/serialization.cpp +++ b/higan/ms/psg/serialization.cpp @@ -7,8 +7,6 @@ auto PSG::serialize(serializer& s) -> void { noise.serialize(s); s.integer(select); - s.integer(lowpassLeft); - s.integer(lowpassRight); s.array(levels); } diff --git a/higan/pce/psg/psg.cpp b/higan/pce/psg/psg.cpp index e4e5b74a..9af1d441 100644 --- a/higan/pce/psg/psg.cpp +++ b/higan/pce/psg/psg.cpp @@ -40,8 +40,7 @@ auto PSG::main() -> void { } } - //normalize 0.0 to 65536.0 => -1.0 to +1.0 - stream->sample(outputLeft / 32768.0 - 1.0, outputRight / 32768.0 - 1.0); + stream->sample(sclamp<16>(outputLeft) / 32768.0, sclamp<16>(outputRight) / 32768.0); step(1); } @@ -57,7 +56,7 @@ auto PSG::power() -> void { memory::fill(&io, sizeof(IO)); for(auto C : range(6)) channel[C].power(C); - double level = 65536.0 / 6.0 / 32.0; //max volume / channels / steps + double level = 32767.0 / 6.0 / 32.0; //max volume / channels / steps double step = 48.0 / 32.0; //48dB volume range spread over 32 steps for(uint n : range(31)) { volumeScalar[n] = level; diff --git a/nall/dsp/iir/biquad.hpp b/nall/dsp/iir/biquad.hpp index 7a3ce0ec..81ea432f 100644 --- a/nall/dsp/iir/biquad.hpp +++ b/nall/dsp/iir/biquad.hpp @@ -15,23 +15,25 @@ struct Biquad { HighShelf, }; - inline auto reset(Type type, double cutoff, double quality, double gain = 0.0) -> void; + inline auto reset(Type type, double cutoffFrequency, double samplingFrequency, double quality, double gain = 0.0) -> void; inline auto process(double in) -> double; //normalized sample (-1.0 to +1.0) inline static auto butterworth(uint order, uint phase) -> double; private: - Type type; //filter type - double cutoff; //frequency cutoff + Type type; + double cutoffFrequency; + double samplingFrequency; double quality; //frequency response quality double gain; //peak gain double a0, a1, a2, b1, b2; //coefficients double z1, z2; //second-order IIR }; -auto Biquad::reset(Type type, double cutoff, double quality, double gain) -> void { +auto Biquad::reset(Type type, double cutoffFrequency, double samplingFrequency, double quality, double gain) -> void { this->type = type; - this->cutoff = cutoff; + this->cutoffFrequency = cutoffFrequency; + this->samplingFrequency = samplingFrequency; this->quality = quality; this->gain = gain; @@ -39,7 +41,7 @@ auto Biquad::reset(Type type, double cutoff, double quality, double gain) -> voi z2 = 0.0; double v = pow(10, fabs(gain) / 20.0); - double k = tan(Math::Pi * cutoff); + double k = tan(Math::Pi * cutoffFrequency / samplingFrequency); double q = quality; double n = 0.0; diff --git a/nall/dsp/iir/one-pole.hpp b/nall/dsp/iir/one-pole.hpp new file mode 100644 index 00000000..c644b9aa --- /dev/null +++ b/nall/dsp/iir/one-pole.hpp @@ -0,0 +1,45 @@ +#pragma once + +//one-pole first-order IIR filter + +namespace nall { namespace DSP { namespace IIR { + +struct OnePole { + enum class Type : uint { + LowPass, + HighPass, + }; + + inline auto reset(Type type, double cutoffFrequency, double samplingFrequency) -> void; + inline auto process(double in) -> double; //normalized sample (-1.0 to +1.0) + +private: + Type type; + double cutoffFrequency; + double samplingFrequency; + double a0, b1; //coefficients + double z1; //first-order IIR +}; + +auto OnePole::reset(Type type, double cutoffFrequency, double samplingFrequency) -> void { + this->type = type; + 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; + } +} + +}}}