From bb392c4d9d8983f6aa8dc4cb581669e1754472e3 Mon Sep 17 00:00:00 2001 From: Bliblank Date: Thu, 15 Jan 2026 23:51:51 -0600 Subject: [PATCH] fix scope triggering --- src/synth/AudioEngine.h | 2 +- src/synth/Synth.cpp | 32 +++++++++++++++++++++----------- src/synth/Voice.cpp | 1 + src/synth/Voice.h | 1 + 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/synth/AudioEngine.h b/src/synth/AudioEngine.h index a66c51f..b1c6b7d 100644 --- a/src/synth/AudioEngine.h +++ b/src/synth/AudioEngine.h @@ -47,7 +47,7 @@ private: RtAudio audio_{AUDIO_API}; // audio device // TODO: id like a yml config file or something for these uint32_t sampleRate_ = 44100; - uint32_t bufferFrames_ = 256; // time per buffer = BF/SR (256/44100 = 5.8ms) + uint32_t bufferFrames_ = 512; // time per buffer = BF/SR (256/44100 = 5.8ms) uint32_t channels_ = 2; // stereo }; diff --git a/src/synth/Synth.cpp b/src/synth/Synth.cpp index 52790d0..077fd6a 100644 --- a/src/synth/Synth.cpp +++ b/src/synth/Synth.cpp @@ -76,6 +76,7 @@ void Synth::process(float* out, uint32_t nFrames, uint32_t sampleRate) { float sampleOut = 0.0f; bool triggered = false; + bool once = false; for (uint32_t i = 0; i < nFrames; i++) { @@ -88,10 +89,24 @@ void Synth::process(float* out, uint32_t nFrames, uint32_t sampleRate) { params[i] = params_[i].current; } // maybe take this outside the loop if performance is an issue + // find the lowest voice for scope triggering + int lowestVoice = 0; + float lowestFreq = 100000.0f; + for(int i = 0; i < voices_.size(); i++) { + if(!voices_[i].isActive()) continue; + float currentFreq = voices_[i].frequency(); + if(currentFreq < lowestFreq) { + lowestVoice = i; + lowestFreq = currentFreq; + } + } + // foreach voice, process... float mix = 0.0f; - for(Voice& v : voices_) { - mix += v.process(params, triggered); + for(int i = 0; i < voices_.size(); i++) { + bool temp = false; + mix += voices_[i].process(params, temp); + if(i == lowestVoice) triggered = temp; } mix /= 4.0f; // for number of voices to prevent clipping mix = tanh(mix); // really prevents clipping @@ -109,15 +124,10 @@ void Synth::process(float* out, uint32_t nFrames, uint32_t sampleRate) { } // triggering business - // TODO: get trigger info from voice (lowest frequency voice) - float phase_ = 0.0f; - if (phase_ > 2.0f * M_PI) { - phase_ -= 2.0f * M_PI; - if(!triggered) { - scope_->setTrigger(i); // this is where we consider the start of a waveform - triggered = true; - // TODO: investigate triggering accross buffers when a single wave period transcends a single audio buffer - } + if(triggered && (!once)) { + scope_->setTrigger(i); // this is where we consider the start of a waveform + once = true; + // TODO: investigate triggering accross buffers when a single wave period transcends a single audio buffer } } diff --git a/src/synth/Voice.cpp b/src/synth/Voice.cpp index 69c326a..a3a15b2 100644 --- a/src/synth/Voice.cpp +++ b/src/synth/Voice.cpp @@ -125,6 +125,7 @@ float Voice::process(float* params, bool& scopeTrigger) { // state tracking, may keep this here even if oscillators store their own phase because it might help with scope triggering phase_ += phaseInc; if (phase_ > 2.0f * M_PI) { + scopeTrigger = true; phase_ -= 2.0f * M_PI; } diff --git a/src/synth/Voice.h b/src/synth/Voice.h index 650b612..79c6233 100644 --- a/src/synth/Voice.h +++ b/src/synth/Voice.h @@ -40,6 +40,7 @@ public: float process(float* params, bool& scopeTrigger); uint8_t note() { return note_; } + float frequency() { return frequency_; } private: