From 9c275dd20de708bcb7a28f60d5ea8f1956a51946 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Sat, 16 Feb 2019 18:36:07 -0500 Subject: [PATCH] Add Module::process and Module::ProcessContext. --- include/dsp/digital.hpp | 13 +++++++++---- include/engine/Module.hpp | 7 +++++++ src/Core/AudioInterface.cpp | 7 +++---- src/Core/CV_CC.cpp | 4 ++-- src/Core/CV_Gate.cpp | 2 +- src/Core/CV_MIDI.cpp | 4 ++-- src/Core/MIDI_CC.cpp | 6 ++---- src/Core/MIDI_CV.cpp | 23 +++++++++++------------ src/Core/MIDI_Gate.cpp | 5 ++--- src/Core/MIDI_Map.cpp | 6 ++---- src/engine/Engine.cpp | 15 +++++++++++---- 11 files changed, 52 insertions(+), 40 deletions(-) diff --git a/include/dsp/digital.hpp b/include/dsp/digital.hpp index 524a13ff..79cb66e9 100644 --- a/include/dsp/digital.hpp +++ b/include/dsp/digital.hpp @@ -130,9 +130,13 @@ struct Timer { }; +/** Counts the number of `process()` calls. +If `period > 0`, `count` is reset to 0 when that number is reached. +Useful for clock dividing and waiting to fill a fixed buffer. +*/ struct Counter { int count; - int period; + int period = 0; Counter() { reset(); @@ -140,16 +144,17 @@ struct Counter { void reset() { count = 0; - period = 1; } void setPeriod(int period) { this->period = period; + reset(); } - /** Returns true if the counter reaches `period` and resets. */ + /** Returns true when the counter reaches `period` and resets. */ bool process() { - if (++count >= period) { + count++; + if (count == period) { count = 0; return true; } diff --git a/include/engine/Module.hpp b/include/engine/Module.hpp index 16e4db7f..762048bb 100644 --- a/include/engine/Module.hpp +++ b/include/engine/Module.hpp @@ -38,9 +38,16 @@ struct Module { void reset(); void randomize(); + struct ProcessContext { + float sampleRate; + float sampleTime; + }; + /** Advances the module by one audio sample. Override this method to read Inputs and Params and to write Outputs and Lights. */ + virtual void process(const ProcessContext &ctx) {} + /** Deprecated. Override process() instead. */ virtual void step() {} /** Called when the engine sample rate is changed. */ diff --git a/src/Core/AudioInterface.cpp b/src/Core/AudioInterface.cpp index 3f877de5..28ab9f44 100644 --- a/src/Core/AudioInterface.cpp +++ b/src/Core/AudioInterface.cpp @@ -122,11 +122,10 @@ struct AudioInterface : Module { onSampleRateChange(); } - void step() override { + void process(const ProcessContext &ctx) override { // Update SRC states - int sampleRate = (int) APP->engine->getSampleRate(); - inputSrc.setRates(port.sampleRate, sampleRate); - outputSrc.setRates(sampleRate, port.sampleRate); + inputSrc.setRates(port.sampleRate, ctx.sampleRate); + outputSrc.setRates(ctx.sampleRate, port.sampleRate); inputSrc.setChannels(port.numInputs); outputSrc.setChannels(port.numOutputs); diff --git a/src/Core/CV_CC.cpp b/src/Core/CV_CC.cpp index 18d9d0b4..389a854c 100644 --- a/src/Core/CV_CC.cpp +++ b/src/Core/CV_CC.cpp @@ -62,9 +62,9 @@ struct CV_CC : Module { midiOutput.midi::Output::reset(); } - void step() override { + void process(const ProcessContext &ctx) override { const float rateLimiterPeriod = 0.010f; - rateLimiterPhase += APP->engine->getSampleTime() / rateLimiterPeriod; + rateLimiterPhase += ctx.sampleTime / rateLimiterPeriod; if (rateLimiterPhase >= 1.f) { rateLimiterPhase -= 1.f; } diff --git a/src/Core/CV_Gate.cpp b/src/Core/CV_Gate.cpp index 148002c3..baa00076 100644 --- a/src/Core/CV_Gate.cpp +++ b/src/Core/CV_Gate.cpp @@ -89,7 +89,7 @@ struct CV_Gate : Module { midiOutput.midi::Output::reset(); } - void step() override { + void process(const ProcessContext &ctx) override { for (int i = 0; i < 16; i++) { int note = learnedNotes[i]; if (velocityMode) { diff --git a/src/Core/CV_MIDI.cpp b/src/Core/CV_MIDI.cpp index 15887c5c..eef4c58b 100644 --- a/src/Core/CV_MIDI.cpp +++ b/src/Core/CV_MIDI.cpp @@ -241,9 +241,9 @@ struct CV_MIDI : Module { midiOutput.midi::Output::reset(); } - void step() override { + void process(const ProcessContext &ctx) override { const float rateLimiterPeriod = 0.005f; - rateLimiterPhase += APP->engine->getSampleTime() / rateLimiterPeriod; + rateLimiterPhase += ctx.sampleTime / rateLimiterPeriod; if (rateLimiterPhase >= 1.f) { rateLimiterPhase -= 1.f; } diff --git a/src/Core/MIDI_CC.cpp b/src/Core/MIDI_CC.cpp index b1a8a021..d41a2082 100644 --- a/src/Core/MIDI_CC.cpp +++ b/src/Core/MIDI_CC.cpp @@ -42,14 +42,12 @@ struct MIDI_CC : Module { midiInput.reset(); } - void step() override { + void process(const ProcessContext &ctx) override { midi::Message msg; while (midiInput.shift(&msg)) { processMessage(msg); } - float deltaTime = APP->engine->getSampleTime(); - for (int i = 0; i < 16; i++) { if (!outputs[CC_OUTPUT + i].isConnected()) continue; @@ -65,7 +63,7 @@ struct MIDI_CC : Module { } else { // Smooth value with filter - valueFilters[i].process(deltaTime, value); + valueFilters[i].process(ctx.sampleTime, value); } lastValues[i] = values[cc]; outputs[CC_OUTPUT + i].setVoltage(valueFilters[i].out); diff --git a/src/Core/MIDI_CV.cpp b/src/Core/MIDI_CV.cpp index 232702fe..b93e942a 100644 --- a/src/Core/MIDI_CV.cpp +++ b/src/Core/MIDI_CV.cpp @@ -103,12 +103,11 @@ struct MIDI_CV : Module { heldNotes.clear(); } - void step() override { + void process(const ProcessContext &ctx) override { midi::Message msg; while (midiInput.shift(&msg)) { processMessage(msg); } - float deltaTime = APP->engine->getSampleTime(); outputs[CV_OUTPUT].setChannels(channels); outputs[GATE_OUTPUT].setChannels(channels); @@ -120,29 +119,29 @@ struct MIDI_CV : Module { outputs[GATE_OUTPUT].setVoltage(gates[c] ? 10.f : 0.f, c); outputs[VELOCITY_OUTPUT].setVoltage(rescale(velocities[c], 0, 127, 0.f, 10.f), c); outputs[AFTERTOUCH_OUTPUT].setVoltage(rescale(aftertouches[c], 0, 127, 0.f, 10.f), c); - outputs[RETRIGGER_OUTPUT].setVoltage(retriggerPulses[c].process(deltaTime) ? 10.f : 0.f, c); + outputs[RETRIGGER_OUTPUT].setVoltage(retriggerPulses[c].process(ctx.sampleTime) ? 10.f : 0.f, c); } if (polyMode == MPE_MODE) { for (int c = 0; c < channels; c++) { outputs[PITCH_OUTPUT].setChannels(channels); outputs[MOD_OUTPUT].setChannels(channels); - outputs[PITCH_OUTPUT].setVoltage(pitchFilters[c].process(deltaTime, rescale(pitches[c], 0, 1<<14, -5.f, 5.f)), c); - outputs[MOD_OUTPUT].setVoltage(modFilters[c].process(deltaTime, rescale(mods[c], 0, 127, 0.f, 10.f)), c); + outputs[PITCH_OUTPUT].setVoltage(pitchFilters[c].process(ctx.sampleTime, rescale(pitches[c], 0, 1<<14, -5.f, 5.f)), c); + outputs[MOD_OUTPUT].setVoltage(modFilters[c].process(ctx.sampleTime, rescale(mods[c], 0, 127, 0.f, 10.f)), c); } } else { outputs[PITCH_OUTPUT].setChannels(1); outputs[MOD_OUTPUT].setChannels(1); - outputs[PITCH_OUTPUT].setVoltage(pitchFilters[0].process(deltaTime, rescale(pitches[0], 0, 1<<14, -5.f, 5.f))); - outputs[MOD_OUTPUT].setVoltage(modFilters[0].process(deltaTime, rescale(mods[0], 0, 127, 0.f, 10.f))); + outputs[PITCH_OUTPUT].setVoltage(pitchFilters[0].process(ctx.sampleTime, rescale(pitches[0], 0, 1<<14, -5.f, 5.f))); + outputs[MOD_OUTPUT].setVoltage(modFilters[0].process(ctx.sampleTime, rescale(mods[0], 0, 127, 0.f, 10.f))); } - outputs[CLOCK_OUTPUT].setVoltage(clockPulse.process(deltaTime) ? 10.f : 0.f); - outputs[CLOCK_DIV_OUTPUT].setVoltage(clockDividerPulse.process(deltaTime) ? 10.f : 0.f); - outputs[START_OUTPUT].setVoltage(startPulse.process(deltaTime) ? 10.f : 0.f); - outputs[STOP_OUTPUT].setVoltage(stopPulse.process(deltaTime) ? 10.f : 0.f); - outputs[CONTINUE_OUTPUT].setVoltage(continuePulse.process(deltaTime) ? 10.f : 0.f); + outputs[CLOCK_OUTPUT].setVoltage(clockPulse.process(ctx.sampleTime) ? 10.f : 0.f); + outputs[CLOCK_DIV_OUTPUT].setVoltage(clockDividerPulse.process(ctx.sampleTime) ? 10.f : 0.f); + outputs[START_OUTPUT].setVoltage(startPulse.process(ctx.sampleTime) ? 10.f : 0.f); + outputs[STOP_OUTPUT].setVoltage(stopPulse.process(ctx.sampleTime) ? 10.f : 0.f); + outputs[CONTINUE_OUTPUT].setVoltage(continuePulse.process(ctx.sampleTime) ? 10.f : 0.f); } void processMessage(midi::Message msg) { diff --git a/src/Core/MIDI_Gate.cpp b/src/Core/MIDI_Gate.cpp index 91f21be7..8921a493 100644 --- a/src/Core/MIDI_Gate.cpp +++ b/src/Core/MIDI_Gate.cpp @@ -46,12 +46,11 @@ struct MIDI_Gate : Module { } } - void step() override { + void process(const ProcessContext &ctx) override { midi::Message msg; while (midiInput.shift(&msg)) { processMessage(msg); } - float deltaTime = APP->engine->getSampleTime(); for (int i = 0; i < 16; i++) { if (gateTimes[i] > 0.f) { @@ -59,7 +58,7 @@ struct MIDI_Gate : Module { // If the gate is off, wait 1 ms before turning the pulse off. // This avoids drum controllers sending a pulse with 0 ms duration. if (!gates[i]) { - gateTimes[i] -= deltaTime; + gateTimes[i] -= ctx.sampleTime; } } else { diff --git a/src/Core/MIDI_Map.cpp b/src/Core/MIDI_Map.cpp index 3580ecf3..3892df4d 100644 --- a/src/Core/MIDI_Map.cpp +++ b/src/Core/MIDI_Map.cpp @@ -65,14 +65,12 @@ struct MIDI_Map : Module { midiInput.reset(); } - void step() override { + void process(const ProcessContext &ctx) override { midi::Message msg; while (midiInput.shift(&msg)) { processMessage(msg); } - float deltaTime = APP->engine->getSampleTime(); - // Step channels for (int id = 0; id < mapLen; id++) { int cc = ccs[id]; @@ -92,7 +90,7 @@ struct MIDI_Map : Module { continue; // Set param float v = rescale(values[cc], 0, 127, 0.f, 1.f); - v = valueFilters[id].process(deltaTime, v); + v = valueFilters[id].process(ctx.sampleTime, v); v = rescale(v, 0.f, 1.f, param->minValue, param->maxValue); APP->engine->setParam(module, paramId, v); } diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index aefb84cc..7b61d8e2 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -190,7 +190,11 @@ static void Engine_stepModules(Engine *engine, int threadId) { // int threadCount = internal->threadCount; int modulesLen = internal->modules.size(); - float deltaTime = internal->sampleTime; + float sampleTime = internal->sampleTime; + + Module::ProcessContext processCtx; + processCtx.sampleRate = internal->sampleRate; + processCtx.sampleTime = internal->sampleTime; // Step each module // for (int i = threadId; i < modulesLen; i += threadCount) { @@ -206,25 +210,28 @@ static void Engine_stepModules(Engine *engine, int threadId) { if (settings.cpuMeter) { auto startTime = std::chrono::high_resolution_clock::now(); + module->process(processCtx); module->step(); auto stopTime = std::chrono::high_resolution_clock::now(); float cpuTime = std::chrono::duration(stopTime - startTime).count(); // Smooth CPU time const float cpuTau = 2.f /* seconds */; - module->cpuTime += (cpuTime - module->cpuTime) * deltaTime / cpuTau; + module->cpuTime += (cpuTime - module->cpuTime) * sampleTime / cpuTau; } else { + module->process(processCtx); + // Call deprecated method module->step(); } } // Iterate ports to step plug lights for (Input &input : module->inputs) { - input.process(deltaTime); + input.process(sampleTime); } for (Output &output : module->outputs) { - output.process(deltaTime); + output.process(sampleTime); } } }