refined voice allocation procedure
This commit is contained in:
100
synth/synth.h
100
synth/synth.h
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
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 <typename voice, typename sample>
|
||||
void voice_process_block(voice& v, sample** audio, int num_frames, const array<m_event, MAX_EVENTS_PER_VOICE>& events,
|
||||
size_t num_events);
|
||||
|
||||
template <typename voice>
|
||||
struct synth {
|
||||
vector<shared_ptr<voice>> voice_ptrs;
|
||||
int active_voice_count;
|
||||
array<voice, MAX_VOICES> voices;
|
||||
array<array<m_event, MAX_EVENTS_PER_VOICE>, MAX_VOICES> voice_events;
|
||||
array<size_t, MAX_VOICES> counts;
|
||||
int active_voice_count = 1;
|
||||
size_t index_to_steal = 0;
|
||||
};
|
||||
|
||||
template <typename voice>
|
||||
void synth_init(const synth<voice>& s, size_t num_voices, double samplerate)
|
||||
void synth_init(synth<voice>& 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<voice>()); }
|
||||
for (size_t i = 0; i < MAX_VOICES; ++i) {
|
||||
s.voices[i] = voice();
|
||||
s.voices[i].voice_init(samplerate);
|
||||
s.counts[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename voice, typename sample>
|
||||
void synth_process_block(const synth<voice>& s, sample** audio, const vector<m_event>& midi_events, int frames)
|
||||
void synth_process_block(synth<voice>& s, sample** audio, const vector<m_event>& 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
|
||||
|
||||
Reference in New Issue
Block a user