From 9095a6952ade035fae79375bb4db9d16a225c0ac Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 3 Aug 2025 00:35:55 +0200 Subject: [PATCH] refined voice allocation procedure --- synth/synth.h | 100 +++++++++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 38 deletions(-) diff --git a/synth/synth.h b/synth/synth.h index 195b760..e9871dc 100644 --- a/synth/synth.h +++ b/synth/synth.h @@ -1,7 +1,7 @@ #pragma once +#include #include -#include #include using namespace std; @@ -23,67 +23,91 @@ struct m_event { double data; }; +constexpr size_t MAX_VOICES = 8; +constexpr size_t MAX_EVENTS_PER_VOICE = 32; + +template +void voice_process_block(voice& v, sample** audio, int num_frames, const array& events, + size_t num_events); + template struct synth { - vector> voice_ptrs; - int active_voice_count; + array voices; + array, MAX_VOICES> voice_events; + array counts; + int active_voice_count = 1; + size_t index_to_steal = 0; }; template -void synth_init(const synth& s, size_t num_voices, double samplerate) +void synth_init(synth& s, double samplerate) { - s.active_voice_count = 0; - s.voice_ptrs.reserve(num_voices); - for (size_t i = 0; i < num_voices; ++i) { s.voice_ptrs.emplace_back(make_shared()); } + for (size_t i = 0; i < MAX_VOICES; ++i) { + s.voices[i] = voice(); + s.voices[i].voice_init(samplerate); + s.counts[i] = 0; + } } template -void synth_process_block(const synth& s, sample** audio, const vector& midi_events, int frames) +void synth_process_block(synth& s, sample** audio, const vector& midi_events, int num_frames) { - int current_frame = 0; - int event_index = 0; + // reset voice events and counts + for (int i = 0; i < MAX_VOICES; i++) { + s.voice_events[i].fill(m_event {}); + s.counts[i] = 0; + } for (const auto& ev : midi_events) { - - int event_frame = ev.offset; - int voice_index = -1; - switch (ev.type) { - case note_on: + case note_on: { + bool found = false; + // attempt to find a free voice for (size_t i = 0; i < s.active_voice_count; ++i) { - // attempt to find a free voice - if (!s.voice_ptrs[i]->is_busy) { voice_index = i; } - } - // if no free voice is found, steal one - if (voice_index < 0) { - // Try to find a voice that is not gated (not playing a note) - for (size_t i = 0; i < s.active_voice_count; ++i) { - if (!s.voice_ptrs[i]->gate) { voice_index = i; } + if (!s.voices[i].is_busy) { + s.voice_events[i][s.counts[i]++] = ev; + found = true; + break; } - // If all voices are gated, steal one round-robin - voice_index = s.index_to_steal; - s.index_to_steal = (s.index_to_steal + 1) % s.active_voice_count; } - // If a voice is found, remember index - // if (voice_index >= 0) { v->note_on(ev.midi_note, ev.velocity); } + if (found) break; + + // try to find a voice that is not gated + for (size_t i = 0; i < s.active_voice_count; ++i) { + if (!s.voices[i].gate) { + s.voice_events[i][s.counts[i]++] = ev; + found = true; + break; + } + } + + if (found) break; + + // if all voices are gated, steal one round-robin + s.voice_events[s.index_to_steal][s.counts[s.index_to_steal]++] = ev; + s.index_to_steal++; + if (s.index_to_steal >= s.active_voice_count) s.index_to_steal = 0; break; - case note_off: + } + case note_off: { + for (size_t i = 0; i < s.active_voice_count; ++i) { + if (s.voices[i].midi_note == ev.midi_note) s.voice_events[i][s.counts[i]++] = ev; + } break; + } case pitch_wheel: - break; - case mod_wheel: + case mod_wheel: { + for (size_t i = 0; i < s.active_voice_count; ++i) { s.voice_events[i][s.counts[i]++] = ev; } break; } - - // process all voices from current frame to event frame - for (int i = 0; i < s.active_voice_count; i++) { - auto& v = s.voice_ptrs[i]; - if (i == voice_index) { v.midi_event = ev; } - if (current_frame < event_frame) { v->process_block(audio, current_frame, event_frame); } } + } - current_frame = event_frame; + for (size_t i = 0; i < s.active_voice_count; ++i) { + auto& v = s.voices[i]; + auto& events = s.voice_events[i]; + voice_process_block(v, audio, num_frames, events, s.counts[i]); } } } // namespace trnr