From 7e7003fd29b06b9497babc5a9d7abd2b1f2bf71e Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Tue, 7 Mar 2017 07:23:22 +1100 Subject: [PATCH] Update to v102r15 release. byuu says: Changelog: - nall: added DSP::IIR::OnePole (which is a first-order IIR filter) - FC/APU: removed strong highpass, weak hipass filters (and the dummied out lowpass filter) - MS,GG,MD/PSG: removed lowpass filter - MS,GG,MD/PSG: audio was not being centered properly; removed centering for now - MD/YM2612: fixed clipping of accumulator from 18 signed bits to 14 signed bits (-0x2000 to +0x1fff) [Cydrak] - MD/YM2612: removed lowpass filter - PCE/PSG: audio was not being centered properly; removed centering for now First thing is that I've removed all of the ad-hoc audio filtering. Emulator::Stream intrinsically provides a three-pass, second-order biquad IIR butterworth lowpass filter that clips frequencies above 20KHz with very good attenuation (as good as IIR gets, anyway.) It doesn't really make sense to have the various cores running additional lowpass filters. If we want to filter frequencies below 20KHz, then I can adapt Emulator::Audio::createStream() to take a cutoff frequency value, and we can do it all at once, with much better quality. Right now, I don't know what frequencies are best to cut off the various other audio cores, so they're just gone for now. As for the highpass filters for the Famicom core, well ... you don't get aliasing from resampling low frequencies. And generally speaking, too low a frequency will be inaudible anyway. All these were doing was killing possible bass (if they were too strong.) We can add them again, but only if someone can convert Ryphecha's ad-hoc magic integers into a frequency cutoff. In which case, I'll use my biquad IIR filter to do it even better. On this note, it may prove useful to do this for the MD PSG as well, to try and head off unnecessary clamping when mixing with the YM2612. Finally, there was the audio centering issue that affected the MS,GG,MD,PCE,SG cores. It was flooring the "silent" audio level, which was resulting in extremely heavy distortion if you tried listening to higan and, say, audacious at the same time. Without the botched centering, this distortion is completely gone now. However, without any centering, we've halved the potential volume range. This means the audio slider in higan's audio settings panel will start clamping twice as quickly. So ultimately, we need to figure out how to fix the centering. This isn't as simple as just subtracting less. We will probably have to center every individual audio channel before summing them to do this properly. Results: On the Mega Drive, Altered Beast sounds quite a bit better, a lot less distortion now. But it's still not perfect, especially sound effects. Further, Bare Knuckle / Streets of Rage still has really bad sound effects. It looks like I broke something in Cydrak's code when trying to adapt it to my style =( --- higan/audio/stream.cpp | 2 +- higan/emulator/emulator.hpp | 2 +- higan/fc/apu/apu.cpp | 31 ++------- higan/fc/apu/apu.hpp | 15 ---- higan/fc/apu/serialization.cpp | 8 --- higan/md/psg/psg.cpp | 17 ++--- higan/md/psg/psg.hpp | 5 +- higan/md/ym2612/channel.cpp | 18 ++--- higan/md/ym2612/timer.cpp | 17 ----- higan/md/ym2612/ym2612.cpp | 25 +++---- higan/md/ym2612/ym2612.hpp | 123 ++++++++++++++++----------------- higan/ms/psg/psg.cpp | 28 +++----- higan/ms/psg/psg.hpp | 6 +- higan/ms/psg/serialization.cpp | 2 - higan/pce/psg/psg.cpp | 5 +- nall/dsp/iir/biquad.hpp | 14 ++-- nall/dsp/iir/one-pole.hpp | 45 ++++++++++++ 17 files changed, 156 insertions(+), 207 deletions(-) create mode 100644 nall/dsp/iir/one-pole.hpp 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; + } +} + +}}}