diff --git a/synth/ivoice.h b/synth/ivoice.h index df45bf9..d4f2f6c 100644 --- a/synth/ivoice.h +++ b/synth/ivoice.h @@ -1,9 +1,11 @@ #pragma once namespace trnr { +template struct ivoice { virtual ~ivoice() = default; - virtual float process_sample() = 0; + //virtual float process_sample() = 0; + virtual void process_samples(t_sample** _outputs, int _start_index, int _block_size) = 0; virtual bool is_busy() = 0; virtual void set_samplerate(double samplerate) = 0; virtual void note_on(int _note, float _velocity) = 0; @@ -12,7 +14,7 @@ struct ivoice { }; // check if a template derives from ivoice -template +template struct is_convertible { template static char test(T*); @@ -20,6 +22,6 @@ struct is_convertible { template static double test(...); - static const bool value = sizeof(test(static_cast(0))) == 1; + static const bool value = sizeof(test>(static_cast(0))) == 1; }; } // namespace trnr \ No newline at end of file diff --git a/synth/midi_synth.h b/synth/midi_synth.h index 6745f80..11d6e96 100644 --- a/synth/midi_synth.h +++ b/synth/midi_synth.h @@ -16,7 +16,7 @@ public: : m_voices_active {false} { // checks whether template derives from ivoice - typedef t_voice assert_at_compile_time[is_convertible::value ? 1 : -1]; + typedef t_voice assert_at_compile_time[is_convertible::value ? 1 : -1]; } void set_samplerate_blocksize(double _samplerate, int _block_size) @@ -27,6 +27,9 @@ public: void process_block(t_sample** _outputs, int _n_frames) { + // clear outputs + for (auto i = 0; i < 2; i++) { memset(_outputs[i], 0, _n_frames * sizeof(t_sample)); } + // sample accurate event handling based on the iPlug2 synth by Oli Larkin if (m_voices_active || !m_event_queue.empty()) { int block_size = m_block_size; diff --git a/synth/tx_voice.h b/synth/tx_voice.h index 0f991f9..ef49185 100644 --- a/synth/tx_voice.h +++ b/synth/tx_voice.h @@ -7,7 +7,8 @@ namespace trnr { -class tx_voice : public ivoice { +template +class tx_voice : public ivoice { public: tx_voice() : algorithm {0} @@ -46,36 +47,42 @@ public: // modulates the pitch in semitones void modulate_pitch(float _pitch) override { this->pitch_mod = _pitch; } - float process_sample() override + void process_samples(t_sample** _outputs, int _start_index, int _block_size) override { float pitch_env_signal = pitch_env.process_sample(gate, trigger) * pitch_env_amt; float pitched_freq = midi_to_frequency(midi_note + pitch_mod + additional_pitch_mod) + pitch_env_signal; - float output = 0.f; + for (int s = _start_index; s < _start_index + _block_size; s++) { - // mix operator signals according to selected algorithm - switch (algorithm) { - case 0: - output = calc_algo1(pitched_freq); - break; - case 1: - output = calc_algo2(pitched_freq); - break; - case 2: - output = calc_algo3(pitched_freq); - break; - case 3: - output = calc_algo4(pitched_freq); - break; - default: - output = calc_algo1(pitched_freq); - break; + float output = 0.f; + + // mix operator signals according to selected algorithm + switch (algorithm) { + case 0: + output = calc_algo1(pitched_freq); + break; + case 1: + output = calc_algo2(pitched_freq); + break; + case 2: + output = calc_algo3(pitched_freq); + break; + case 3: + output = calc_algo4(pitched_freq); + break; + default: + output = calc_algo1(pitched_freq); + break; + } + + // reset trigger + trigger = false; + + redux(output, bit_resolution); + + _outputs[0][s] += output / 3.; + _outputs[1][s] = _outputs[0][s]; } - - // reset trigger - trigger = false; - - return redux(output, bit_resolution); } bool is_busy() override diff --git a/synth/voice_allocator.h b/synth/voice_allocator.h index 7b65abd..1b834ba 100644 --- a/synth/voice_allocator.h +++ b/synth/voice_allocator.h @@ -14,7 +14,7 @@ public: : voices(4, t_voice()) { // checks whether template derives from ivoice - typedef t_voice assert_at_compile_time[is_convertible::value ? 1 : -1]; + typedef t_voice assert_at_compile_time[is_convertible::value ? 1 : -1]; } void set_voice_count(const int& voice_count) { voices.resize(voice_count, voices.at(0)); } @@ -39,17 +39,16 @@ public: void process_samples(t_sample** _outputs, int _start_index, int _block_size) { - for (int s = _start_index; s < _start_index + _block_size; s++) { + for (int b = _start_index; b < _start_index + _block_size; b += internal_block_size) { - process_events(s); + const int block_size = internal_block_size; - float voices_signal = 0.; + // process all events in the block (introduces potential inaccuracy of up to 16 samples) + process_events(b, block_size); - std::for_each(voices.begin(), voices.end(), - [&voices_signal](t_voice& voice) { voices_signal += (voice.process_sample() / 3.); }); - - _outputs[0][s] = voices_signal; - _outputs[1][s] = voices_signal; + std::for_each(voices.begin(), voices.end(), [&_outputs, &b, &block_size](t_voice& voice) { + voice.process_samples(_outputs, b, block_size); + }); } } @@ -75,6 +74,7 @@ public: private: std::vector input_queue; int index_to_steal = 0; + const int internal_block_size = 16; t_voice* get_free_voice(float frequency) { @@ -111,31 +111,33 @@ private: return free_voice; } - void process_events(int _start_index) + void process_events(int _start_index, int _block_size) { - auto iterator = input_queue.begin(); - while (iterator != input_queue.end()) { + for (int s = _start_index; s < _start_index + _block_size; s++) { + auto iterator = input_queue.begin(); + while (iterator != input_queue.end()) { - midi_event& event = *iterator; - if (event.offset == _start_index) { + midi_event& event = *iterator; + if (event.offset == _start_index) { - switch (event.type) { - case midi_event_type::note_on: - note_on(event); - break; - case midi_event_type::note_off: - note_off(event); - break; - case midi_event_type::pitch_wheel: - access([&event](t_voice& voice) { voice.modulate_pitch(event.data); }); - break; - default: - break; + switch (event.type) { + case midi_event_type::note_on: + note_on(event); + break; + case midi_event_type::note_off: + note_off(event); + break; + case midi_event_type::pitch_wheel: + access([&event](t_voice& voice) { voice.modulate_pitch(event.data); }); + break; + default: + break; + } + + iterator = input_queue.erase(iterator); + } else { + iterator++; } - - iterator = input_queue.erase(iterator); - } else { - iterator++; } } }