diff --git a/synth/synth.h b/synth/synth.h new file mode 100644 index 0000000..195b760 --- /dev/null +++ b/synth/synth.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include + +using namespace std; + +namespace trnr { + +enum m_event_type { + note_on = 0, + note_off, + pitch_wheel, + mod_wheel +}; + +struct m_event { + m_event_type type; + int offset; + int midi_note; + float velocity; + double data; +}; + +template +struct synth { + vector> voice_ptrs; + int active_voice_count; +}; + +template +void synth_init(const synth& s, size_t num_voices, 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()); } +} + +template +void synth_process_block(const synth& s, sample** audio, const vector& midi_events, int frames) +{ + int current_frame = 0; + int event_index = 0; + + for (const auto& ev : midi_events) { + + int event_frame = ev.offset; + int voice_index = -1; + + switch (ev.type) { + case note_on: + 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 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); } + break; + case note_off: + break; + case pitch_wheel: + break; + case mod_wheel: + 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; + } +} +} // namespace trnr