#include "Synth.hpp" Synth::Synth(ConfigService* config, LoggerService* logger, ScopeBuffer* scope, NoteQueue* queue) : config_(config), logger_(logger), scope_(scope), noteQueue_(queue) { voices_.fill(Voice(config_, logger_)); filter_.setSampleRate(44100.0f); filter_.setParams(Filter::Type::BiquadLowpass, 4000.0f, 0.707f); } void Synth::handleNoteEvent(const NoteEvent& event) { // Voice* v = findVoiceByNote(event.note); // if(v != nullptr) v->noteOff(); // stop all voices that are currently playing this note for(Voice& v : voices_) { if(v.isActive() && v.note() == event.note) { v.noteOff(); } } if(event.type == NoteEventType::NoteOn) { Voice* v = findFreeVoice(); if(v != nullptr) v->noteOn(event.note, event.velocity); } } void Synth::process(float* out, size_t nFrames) { NoteEvent noteEvent; while(noteQueue_->pop(noteEvent)) { handleNoteEvent(noteEvent); } size_t lowestVoice = 0; float lowestFreq = 100000.0f; for(size_t i = 0; i < voices_.size(); i++) { if(!voices_[i].isActive()) continue; float currentFreq = voices_[i].frequency(); if(currentFreq < lowestFreq) { lowestVoice = i; lowestFreq = currentFreq; } } scope_->spinlock(true); float sampleOut = 0.0f; bool triggered = false; bool once = false; for(size_t i = 0; i < nFrames; i++) { float mix = 0.0f; for(size_t j = 0; j < voices_.size(); j++) { bool temp = false; if(!voices_[j].isActive()) continue; mix += voices_[j].process(temp); if(j == lowestVoice) triggered = temp; } mix = tanh(mix); // prevent clipping sampleOut = filter_.biquadProcess(mix); out[2*i] = sampleOut; out[2*i+1] = sampleOut; if(scope_) scope_->push(sampleOut); if(triggered && !once) { scope_->setTrigger(i); once = true; } } scope_->spinlock(false); } Voice* Synth::findFreeVoice() { for(Voice& v : voices_) { if(!v.isActive()) { return &v; } } return nullptr; } Voice* Synth::findVoiceByNote(uint8_t note) { for(Voice& v : voices_) { if(v.isActive() && v.note() == note) { return &v; } } return nullptr; }