From f384387113769839588770842ec8b482ddf786ce Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Fri, 17 Nov 2023 11:46:27 +0100 Subject: [PATCH 1/9] DEVICES: blepsynth/blepsynth.cc: implement sample accurate event processing Signed-off-by: Stefan Westerfeld --- devices/blepsynth/blepsynth.cc | 113 ++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 37 deletions(-) diff --git a/devices/blepsynth/blepsynth.cc b/devices/blepsynth/blepsynth.cc index dc65d9b7..13c9720c 100644 --- a/devices/blepsynth/blepsynth.cc +++ b/devices/blepsynth/blepsynth.cc @@ -348,6 +348,7 @@ class BlepSynth : public AudioProcessor { }; enum { FILTER_TYPE_BYPASS, FILTER_TYPE_LADDER, FILTER_TYPE_SKFILTER }; + int filter_type_ = 0; static constexpr int CUTOFF_MIN_MIDI = 15; static constexpr int CUTOFF_MAX_MIDI = 144; @@ -581,10 +582,17 @@ class BlepSynth : public AudioProcessor { { if (tag == FILTER_TYPE) { - for (Voice *voice : active_voices_) + int new_filter_type = irintf (get_param (FILTER_TYPE)); + if (new_filter_type != filter_type_) { - voice->ladder_filter_.reset(); - voice->skfilter_.reset(); + filter_type_ = new_filter_type; + for (Voice *voice : active_voices_) + { + if (filter_type_ == FILTER_TYPE_LADDER) + voice->ladder_filter_.reset(); + if (filter_type_ == FILTER_TYPE_SKFILTER) + voice->skfilter_.reset(); + } } } if (tag == ATTACK || tag == DECAY || tag == SUSTAIN || tag == RELEASE || @@ -864,13 +872,12 @@ class BlepSynth : public AudioProcessor { } }; - int filter_type = irintf (get_param (FILTER_TYPE)); - if (filter_type == FILTER_TYPE_LADDER) + if (filter_type_ == FILTER_TYPE_LADDER) { voice->ladder_filter_.set_mode (LadderVCF::Mode (irintf (get_param (LADDER_MODE)))); filter_process_block (voice->ladder_filter_); } - else if (filter_type == FILTER_TYPE_SKFILTER) + else if (filter_type_ == FILTER_TYPE_SKFILTER) { voice->skfilter_.set_mode (SKFilter::Mode (irintf (get_param (SKFILTER_MODE)))); filter_process_block (voice->skfilter_); @@ -902,29 +909,10 @@ class BlepSynth : public AudioProcessor { voice->fil_envelope_.set_release (perc_to_s (get_param (FIL_RELEASE))); } void - render (uint n_frames) override + render_audio (float *left_out, float *right_out, uint n_frames) { - MidiEventInput evinput = midi_event_input(); - for (const auto &ev : evinput) - switch (ev.message()) - { - case MidiMessage::NOTE_OFF: - note_off (ev.channel, ev.key); - break; - case MidiMessage::NOTE_ON: - note_on (ev.channel, ev.key, ev.velocity); - break; - case MidiMessage::ALL_NOTES_OFF: - for (auto voice : active_voices_) - if (voice->state_ == Voice::ON && voice->channel_ == ev.channel) - note_off (voice->channel_, voice->midi_note_); - break; - case MidiMessage::PARAM_VALUE: - apply_event (ev); - adjust_param (ev.param); - break; - default: ; - } + if (!n_frames) + return; /* TODO: replace this with true midi input */ check_note (KEY_C, old_c_, 60); @@ -933,23 +921,16 @@ class BlepSynth : public AudioProcessor { check_note (KEY_F, old_f_, 65); check_note (KEY_G, old_g_, 67); - assert_return (n_ochannels (stereout_) == 2); bool need_free = false; - float *left_out = oblock (stereout_, 0); - float *right_out = oblock (stereout_, 1); - - floatfill (left_out, 0.f, n_frames); - floatfill (right_out, 0.f, n_frames); for (Voice *voice : active_voices_) { if (voice->new_voice_) { - int filter_type = irintf (get_param (FILTER_TYPE)); int idelay = 0; - if (filter_type == FILTER_TYPE_LADDER) + if (filter_type_ == FILTER_TYPE_LADDER) idelay = voice->ladder_filter_.delay(); - if (filter_type == FILTER_TYPE_SKFILTER) + if (filter_type_ == FILTER_TYPE_SKFILTER) idelay = voice->skfilter_.delay(); if (idelay) { @@ -987,6 +968,64 @@ class BlepSynth : public AudioProcessor { need_update_volume_envelope_ = false; need_update_filter_envelope_ = false; } + void + render (uint n_frames) override + { + assert_return (n_ochannels (stereout_) == 2); + + float *left_out = oblock (stereout_, 0); + float *right_out = oblock (stereout_, 1); + + floatfill (left_out, 0.f, n_frames); + floatfill (right_out, 0.f, n_frames); + + uint offset = 0; + MidiEventInput evinput = midi_event_input(); + for (const auto &ev : evinput) + { + assert_return (ev.frame >= 0); // TODO: should be unsigned anyway, issue #26 + + // ensure that new offset from event is not larger than n_frames + uint new_offset = ev.frame; + if (new_offset > n_frames) + { + printerr ("*** BlepSynth: event offset is after end of block (new_offset = %d, n_frrames = %d)\n", new_offset, n_frames); + new_offset = n_frames; + } + // ensure that new offset is after last event + if (new_offset < offset) + { + printerr ("*** BlepSynth: events not sorted (event new_offset = %d less than offset = %d)\n", new_offset, offset); + new_offset = offset; + } + // process any audio that is before the event + render_audio (left_out + offset, right_out + offset, new_offset - offset); + offset = new_offset; + + switch (ev.message()) + { + case MidiMessage::NOTE_OFF: + note_off (ev.channel, ev.key); + break; + case MidiMessage::NOTE_ON: + note_on (ev.channel, ev.key, ev.velocity); + break; + case MidiMessage::ALL_NOTES_OFF: + for (auto voice : active_voices_) + if (voice->state_ == Voice::ON && voice->channel_ == ev.channel) + note_off (voice->channel_, voice->midi_note_); + break; + case MidiMessage::PARAM_VALUE: + apply_event (ev); + adjust_param (ev.param); + break; + default: ; + } + } + // process frames after last event + render_audio (left_out + offset, right_out + offset, n_frames - offset); + } + static double convert_cutoff (double midi_note) { From 814ae414817983e602f7302798f80eacf8300278 Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Fri, 17 Nov 2023 11:54:45 +0100 Subject: [PATCH 2/9] DEVICES: blepsynth/blepsynth.cc: use switch for parameter updates Signed-off-by: Stefan Westerfeld --- devices/blepsynth/blepsynth.cc | 65 +++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/devices/blepsynth/blepsynth.cc b/devices/blepsynth/blepsynth.cc index 13c9720c..a0ebc6e2 100644 --- a/devices/blepsynth/blepsynth.cc +++ b/devices/blepsynth/blepsynth.cc @@ -580,37 +580,52 @@ class BlepSynth : public AudioProcessor { void adjust_param (uint32_t tag) override { - if (tag == FILTER_TYPE) + switch (tag) { - int new_filter_type = irintf (get_param (FILTER_TYPE)); - if (new_filter_type != filter_type_) + case FILTER_TYPE: { - filter_type_ = new_filter_type; - for (Voice *voice : active_voices_) + int new_filter_type = irintf (get_param (FILTER_TYPE)); + if (new_filter_type != filter_type_) { - if (filter_type_ == FILTER_TYPE_LADDER) - voice->ladder_filter_.reset(); - if (filter_type_ == FILTER_TYPE_SKFILTER) - voice->skfilter_.reset(); + filter_type_ = new_filter_type; + for (Voice *voice : active_voices_) + { + if (filter_type_ == FILTER_TYPE_LADDER) + voice->ladder_filter_.reset(); + if (filter_type_ == FILTER_TYPE_SKFILTER) + voice->skfilter_.reset(); + } } } - } - if (tag == ATTACK || tag == DECAY || tag == SUSTAIN || tag == RELEASE || - tag == ATTACK_SLOPE || tag == DECAY_SLOPE || tag == RELEASE_SLOPE) - { - need_update_volume_envelope_ = true; - } - if (tag == FIL_ATTACK || tag == FIL_DECAY || tag == FIL_SUSTAIN || tag == FIL_RELEASE) - { - need_update_filter_envelope_ = true; - } - if (tag == VE_MODEL) - { - bool ve_has_slope = irintf (get_param (VE_MODEL)) > 0; // exponential envelope has no slope parameters + break; + case ATTACK: + case DECAY: + case SUSTAIN: + case RELEASE: + case ATTACK_SLOPE: + case DECAY_SLOPE: + case RELEASE_SLOPE: + { + need_update_volume_envelope_ = true; + break; + } + case FIL_ATTACK: + case FIL_DECAY: + case FIL_SUSTAIN: + case FIL_RELEASE: + { + need_update_filter_envelope_ = true; + break; + } + case VE_MODEL: + { + bool ve_has_slope = irintf (get_param (VE_MODEL)) > 0; // exponential envelope has no slope parameters - set_parameter_used (ATTACK_SLOPE, ve_has_slope); - set_parameter_used (DECAY_SLOPE, ve_has_slope); - set_parameter_used (RELEASE_SLOPE, ve_has_slope); + set_parameter_used (ATTACK_SLOPE, ve_has_slope); + set_parameter_used (DECAY_SLOPE, ve_has_slope); + set_parameter_used (RELEASE_SLOPE, ve_has_slope); + break; + } } } void From 9e1eb997e8fc6f154375069d1fd99fa6940e0304 Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Fri, 17 Nov 2023 12:04:36 +0100 Subject: [PATCH 3/9] DEVICES: blepsynth/blepsynth.cc: set more parameter used flags Signed-off-by: Stefan Westerfeld --- devices/blepsynth/blepsynth.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devices/blepsynth/blepsynth.cc b/devices/blepsynth/blepsynth.cc index a0ebc6e2..dab6f5ac 100644 --- a/devices/blepsynth/blepsynth.cc +++ b/devices/blepsynth/blepsynth.cc @@ -596,6 +596,8 @@ class BlepSynth : public AudioProcessor { voice->skfilter_.reset(); } } + set_parameter_used (LADDER_MODE, filter_type_ == FILTER_TYPE_LADDER); + set_parameter_used (SKFILTER_MODE, filter_type_ == FILTER_TYPE_SKFILTER); } break; case ATTACK: @@ -645,6 +647,9 @@ class BlepSynth : public AudioProcessor { int unison_voices = irintf (get_param (O+OSC1_UNISON_VOICES)); unison_voices = CLAMP (unison_voices, 1, 16); osc.set_unison (unison_voices, get_param (O+OSC1_UNISON_DETUNE), get_param (O+OSC1_UNISON_STEREO) * 0.01); + + set_parameter_used (O + OSC1_UNISON_DETUNE, unison_voices > 1); + set_parameter_used (O + OSC1_UNISON_STEREO, unison_voices > 1); } static double perc_to_s (double perc) From 4a245ccfb61336405bda3ffef62670f29e5e2c87 Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Fri, 17 Nov 2023 12:11:56 +0100 Subject: [PATCH 4/9] DEVICES: blepsynth/blepsynth.cc: use new filter version from dsp-research - use better tanh() approximation in filter saturation function Signed-off-by: Stefan Westerfeld --- devices/blepsynth/laddervcf.hh | 37 +++++++++++++++++++++---------- devices/blepsynth/skfilter.hh | 40 +++++++++++++++++++++++----------- 2 files changed, 53 insertions(+), 24 deletions(-) diff --git a/devices/blepsynth/laddervcf.hh b/devices/blepsynth/laddervcf.hh index daf38e10..a7f07076 100644 --- a/devices/blepsynth/laddervcf.hh +++ b/devices/blepsynth/laddervcf.hh @@ -6,6 +6,9 @@ #include "pandaresampler.hh" #include +#include +#include +#include namespace Ase { @@ -36,6 +39,7 @@ private: float freq_ = 440; float reso_ = 0; float drive_ = 0; + float global_volume_ = 1; uint over_ = 0; bool test_linear_ = false; @@ -86,6 +90,17 @@ public: fparams_valid_ = false; } void + set_global_volume (float global_volume) + { + /* every samples that is processed by the filter is + * - multiplied with global_volume before processing + * - divided by global_volume after processing + * which has an effect on the non-linear part of the filter (drive) + */ + global_volume_ = global_volume; + fparams_valid_ = false; + } + void set_test_linear (bool test_linear) { test_linear_ = test_linear; @@ -135,14 +150,6 @@ private: clamp_freq_min_ = frequency_range_min_; clamp_freq_max_ = std::min (frequency_range_max_, rate_ * over_ * 0.49f); } - float - distort (float x) - { - /* shaped somewhat similar to tanh() and others, but faster */ - x = std::clamp (x, -1.0f, 1.0f); - - return x - x * x * x * (1.0f / 3); - } void setup_reso_drive (FParams& fparams, float reso, float drive) { @@ -171,10 +178,18 @@ private: reso += drive * sqrt (reso) * reso * 0.03f; float vol = exp2f ((drive + -12 * sqrt (reso)) * db_x2_factor); - fparams.pre_scale = negative_drive_vol * vol; - fparams.post_scale = std::max (1 / vol, 1.0f); + fparams.pre_scale = negative_drive_vol * vol * global_volume_; + fparams.post_scale = std::max (1 / vol, 1.0f) / global_volume_; fparams.reso = sqrt (reso) * 4; } + static float + tanh_approx (float x) + { + // https://www.musicdsp.org/en/latest/Other/238-rational-tanh-approximation.html + x = std::clamp (x, -3.0f, 3.0f); + + return x * (27.0f + x * x) / (27.0f + 9.0f * x * x); + } /* * This ladder filter implementation is mainly based on * @@ -203,7 +218,7 @@ private: Channel& c = channels_[i]; const float x = value * fparams_.pre_scale; const float g_comp = 0.5f; // passband gain correction - const float x0 = distort (x - (c.y4 - g_comp * x) * res); + const float x0 = tanh_approx (x - (c.y4 - g_comp * x) * res); c.y1 = b0 * x0 + b1 * c.x1 - a1 * c.y1; c.x1 = x0; diff --git a/devices/blepsynth/skfilter.hh b/devices/blepsynth/skfilter.hh index 662661c3..9f798bfa 100644 --- a/devices/blepsynth/skfilter.hh +++ b/devices/blepsynth/skfilter.hh @@ -1,5 +1,7 @@ // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 +#pragma once + #define PANDA_RESAMPLER_HEADER_ONLY #include "pandaresampler.hh" #include @@ -20,6 +22,7 @@ private: float freq_ = 440; float reso_ = 0; float drive_ = 0; + float global_volume_ = 1; bool test_linear_ = false; int over_ = 1; float freq_warp_factor_ = 0; @@ -202,8 +205,8 @@ private: reso = 1 - (1-0.9f)*(1-0.9f)*(1-sqrt2/4) + (reso-0.9f)*0.1f; } - fparams.pre_scale = negative_drive_vol * vol; - fparams.post_scale = std::max (1 / vol, 1.0f); + fparams.pre_scale = negative_drive_vol * vol * global_volume_; + fparams.post_scale = std::max (1 / vol, 1.0f) / global_volume_; setup_k (fparams, reso); } void @@ -258,6 +261,17 @@ public: fparams_valid_ = false; } void + set_global_volume (float global_volume) + { + /* every samples that is processed by the filter is + * - multiplied with global_volume before processing + * - divided by global_volume after processing + * which has an effect on the non-linear part of the filter (drive) + */ + global_volume_ = global_volume; + fparams_valid_ = false; + } + void set_test_linear (bool test_linear) { test_linear_ = test_linear; @@ -319,6 +333,14 @@ private: return x * (c1 + c2 * x2) / (c3 + x2); } + static float + tanh_approx (float x) + { + // https://www.musicdsp.org/en/latest/Other/238-rational-tanh-approximation.html + x = std::clamp (x, -3.0f, 3.0f); + + return x * (27.0f + x * x) / (27.0f + 9.0f * x * x); + } template [[gnu::flatten]] void @@ -369,14 +391,6 @@ private: } }; - auto distort = [] (float x) - { - /* shaped somewhat similar to tanh() and others, but faster */ - x = std::clamp (x, -1.0f, 1.0f); - - return x - x * x * x * (1.0f / 3); - }; - float s1l, s1r, s2l, s2r; s1l = channels_[0].s1[stage]; @@ -405,8 +419,8 @@ private: if (last_stage) { - { y0l = distort (y0l); } - if (STEREO) { y0r = distort (y0r); } + { y0l = tanh_approx (y0l); } + if (STEREO) { y0r = tanh_approx (y0r); } } { y1l = lowpass (y0l, s1l); } if (STEREO) { y1r = lowpass (y0r, s1r); } @@ -564,7 +578,7 @@ private: } public: void - process_block (uint n_samples, float *left, float *right, const float *freq_in = nullptr, const float *reso_in = nullptr, const float *drive_in = nullptr) + process_block (uint n_samples, float *left, float *right = nullptr, const float *freq_in = nullptr, const float *reso_in = nullptr, const float *drive_in = nullptr) { static constexpr auto jump_table { make_jump_table (std::make_index_sequence()) }; From 5f0afefecfe3420a5b5a6aae2bc9b18f6b8e7e28 Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Fri, 17 Nov 2023 12:34:39 +0100 Subject: [PATCH 5/9] DEVICES: blepsynth/blepsynth.cc: improve updates for some parameters Signed-off-by: Stefan Westerfeld --- devices/blepsynth/blepsynth.cc | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/devices/blepsynth/blepsynth.cc b/devices/blepsynth/blepsynth.cc index dab6f5ac..ad11ef5c 100644 --- a/devices/blepsynth/blepsynth.cc +++ b/devices/blepsynth/blepsynth.cc @@ -353,10 +353,6 @@ class BlepSynth : public AudioProcessor { static constexpr int CUTOFF_MIN_MIDI = 15; static constexpr int CUTOFF_MAX_MIDI = 144; - bool need_update_volume_envelope_; - - bool need_update_filter_envelope_; - class Voice { public: @@ -608,7 +604,8 @@ class BlepSynth : public AudioProcessor { case DECAY_SLOPE: case RELEASE_SLOPE: { - need_update_volume_envelope_ = true; + for (Voice *voice : active_voices_) + update_volume_envelope (voice); break; } case FIL_ATTACK: @@ -616,7 +613,8 @@ class BlepSynth : public AudioProcessor { case FIL_SUSTAIN: case FIL_RELEASE: { - need_update_filter_envelope_ = true; + for (Voice *voice : active_voices_) + update_filter_envelope (voice); break; } case VE_MODEL: @@ -628,6 +626,11 @@ class BlepSynth : public AudioProcessor { set_parameter_used (RELEASE_SLOPE, ve_has_slope); break; } + case KEY_C: check_note (KEY_C, old_c_, 60); break; + case KEY_D: check_note (KEY_D, old_d_, 62); break; + case KEY_E: check_note (KEY_E, old_e_, 64); break; + case KEY_F: check_note (KEY_F, old_f_, 65); break; + case KEY_G: check_note (KEY_G, old_g_, 67); break; } } void @@ -852,9 +855,6 @@ class BlepSynth : public AudioProcessor { auto filter_process_block = [&] (auto& filter) { - if (need_update_filter_envelope_) - update_filter_envelope (voice); - auto gen_filter_input = [&] (float *freq_in, float *reso_in, float *drive_in, uint n_frames) { voice->fil_envelope_.process (freq_in, n_frames); @@ -934,13 +934,6 @@ class BlepSynth : public AudioProcessor { if (!n_frames) return; - /* TODO: replace this with true midi input */ - check_note (KEY_C, old_c_, 60); - check_note (KEY_D, old_d_, 62); - check_note (KEY_E, old_e_, 64); - check_note (KEY_F, old_f_, 65); - check_note (KEY_G, old_g_, 67); - bool need_free = false; for (Voice *voice : active_voices_) @@ -967,8 +960,6 @@ class BlepSynth : public AudioProcessor { // apply volume envelope float volume_env[n_frames]; - if (need_update_volume_envelope_) - update_volume_envelope (voice); voice->envelope_.process (volume_env, n_frames); float post_gain_factor = db2voltage (get_param (POST_GAIN)); for (uint i = 0; i < n_frames; i++) @@ -985,8 +976,6 @@ class BlepSynth : public AudioProcessor { } if (need_free) free_unused_voices(); - need_update_volume_envelope_ = false; - need_update_filter_envelope_ = false; } void render (uint n_frames) override From ea9bbed3f4fbdc7bd7d6dad948e31a2ae7dac784 Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Sat, 18 Nov 2023 10:30:09 +0100 Subject: [PATCH 6/9] DEVICES: blepsynth: remove event offset debug checks Signed-off-by: Stefan Westerfeld --- devices/blepsynth/blepsynth.cc | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/devices/blepsynth/blepsynth.cc b/devices/blepsynth/blepsynth.cc index ad11ef5c..eb8fed98 100644 --- a/devices/blepsynth/blepsynth.cc +++ b/devices/blepsynth/blepsynth.cc @@ -994,22 +994,9 @@ class BlepSynth : public AudioProcessor { { assert_return (ev.frame >= 0); // TODO: should be unsigned anyway, issue #26 - // ensure that new offset from event is not larger than n_frames - uint new_offset = ev.frame; - if (new_offset > n_frames) - { - printerr ("*** BlepSynth: event offset is after end of block (new_offset = %d, n_frrames = %d)\n", new_offset, n_frames); - new_offset = n_frames; - } - // ensure that new offset is after last event - if (new_offset < offset) - { - printerr ("*** BlepSynth: events not sorted (event new_offset = %d less than offset = %d)\n", new_offset, offset); - new_offset = offset; - } // process any audio that is before the event - render_audio (left_out + offset, right_out + offset, new_offset - offset); - offset = new_offset; + render_audio (left_out + offset, right_out + offset, ev.frame - offset); + offset = ev.frame; switch (ev.message()) { From 21cd3e73f4f0cc76e59dfa79203b2ea8b0325868 Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Sat, 18 Nov 2023 12:04:50 +0100 Subject: [PATCH 7/9] DEVICES: blepsynth: change defaults for 2nd oscillator / mix Signed-off-by: Stefan Westerfeld --- devices/blepsynth/blepsynth.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/devices/blepsynth/blepsynth.cc b/devices/blepsynth/blepsynth.cc index eb8fed98..8a102d2c 100644 --- a/devices/blepsynth/blepsynth.cc +++ b/devices/blepsynth/blepsynth.cc @@ -409,15 +409,19 @@ class BlepSynth : public AudioProcessor { const uint I = oscnum + 1; const uint O = oscnum * (OSC2_SHAPE - OSC1_SHAPE); const String o = string_format ("osc_%u_", I); + + const double shape_default = oscnum ? -100 : 0; + const double octave_default = oscnum; + pmap.group = _("Oscillator %u", I); - pmap[O+OSC1_SHAPE] = Param { o+"shape", _("Osc %u Shape", I), _("Shp%u", I), 0, "%", { -100, 100, }, }; + pmap[O+OSC1_SHAPE] = Param { o+"shape", _("Osc %u Shape", I), _("Shp%u", I), shape_default, "%", { -100, 100, }, }; pmap[O+OSC1_PULSE_WIDTH] = Param { o+"pulse_width", _("Osc %u Pulse Width", I), _("PW%u", I), 50, "%", { 0, 100, }, }; pmap[O+OSC1_SUB] = Param { o+"subharmonic", _("Osc %u Subharmonic", I), _("Sub%u", I), 0, "%", { 0, 100, }, }; pmap[O+OSC1_SUB_WIDTH] = Param { o+"subharmonic_width", _("Osc %u Subharmonic Width", I), _("SbW%u", I), 50, "%", { 0, 100, }, }; pmap[O+OSC1_SYNC] = Param { o+"sync_slave", _("Osc %u Sync Slave", I), _("Syn%u", I), 0, "Semitones", { 0, 60, }, }; - pmap[O+OSC1_PITCH] = Param { o+"pitch", _("Osc %u Pitch", I), _("Pit%u", I), 0, "semitones", { -7, 7, }, }; - pmap[O+OSC1_OCTAVE] = Param { o+"octave", _("Osc %u Octave", I), _("Oct%u", I), 0, "octaves", { -2, 3, }, }; + pmap[O+OSC1_PITCH] = Param { o+"pitch", _("Osc %u Pitch", I), _("Pit%u", I), 0, "semitones", { -7, 7, }, }; + pmap[O+OSC1_OCTAVE] = Param { o+"octave", _("Osc %u Octave", I), _("Oct%u", I), octave_default, "octaves", { -2, 3, }, }; /* TODO: unison_voices property should have stepping set to 1 */ pmap[O+OSC1_UNISON_VOICES] = Param { o+"unison_voices", _("Osc %u Unison Voices", I), _("Voi%u", I), 1, "Voices", { 1, 16, }, }; @@ -428,7 +432,7 @@ class BlepSynth : public AudioProcessor { oscparams (0); pmap.group = _("Mix"); - pmap[MIX] = Param { "mix", _("Mix"), _("Mix"), 0, "%", { 0, 100 }, }; + pmap[MIX] = Param { "mix", _("Mix"), _("Mix"), 30, "%", { 0, 100 }, }; pmap[VEL_TRACK] = Param { "vel_track", _("Velocity Tracking"), _("VelTr"), 50, "%", { 0, 100, }, }; // TODO: post_gain probably should default to 0dB once we have track/mixer volumes pmap[POST_GAIN] = Param { "post_gain", _("Post Gain"), _("Gain"), -12, "dB", { -24, 24, }, }; From bc5aac2ccfe0235567cd5d82de927317dd78c529 Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Sat, 18 Nov 2023 22:09:03 +0100 Subject: [PATCH 8/9] DEVICES: blepsynth: remove debugging code Signed-off-by: Stefan Westerfeld --- devices/blepsynth/blepsynth.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/devices/blepsynth/blepsynth.cc b/devices/blepsynth/blepsynth.cc index 8a102d2c..8ab06a18 100644 --- a/devices/blepsynth/blepsynth.cc +++ b/devices/blepsynth/blepsynth.cc @@ -996,8 +996,6 @@ class BlepSynth : public AudioProcessor { MidiEventInput evinput = midi_event_input(); for (const auto &ev : evinput) { - assert_return (ev.frame >= 0); // TODO: should be unsigned anyway, issue #26 - // process any audio that is before the event render_audio (left_out + offset, right_out + offset, ev.frame - offset); offset = ev.frame; From d0b3afb57bb1c84aa95ec0fb72af42c29402c0db Mon Sep 17 00:00:00 2001 From: Stefan Westerfeld Date: Sat, 18 Nov 2023 22:16:40 +0100 Subject: [PATCH 9/9] DEVICES: blepsynth: treat negative time offsets as time offset 0 events Signed-off-by: Stefan Westerfeld --- devices/blepsynth/blepsynth.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devices/blepsynth/blepsynth.cc b/devices/blepsynth/blepsynth.cc index 8ab06a18..0d504bca 100644 --- a/devices/blepsynth/blepsynth.cc +++ b/devices/blepsynth/blepsynth.cc @@ -996,9 +996,11 @@ class BlepSynth : public AudioProcessor { MidiEventInput evinput = midi_event_input(); for (const auto &ev : evinput) { + uint frame = std::max (ev.frame, 0); // TODO: should be unsigned anyway, issue #26 + // process any audio that is before the event - render_audio (left_out + offset, right_out + offset, ev.frame - offset); - offset = ev.frame; + render_audio (left_out + offset, right_out + offset, frame - offset); + offset = frame; switch (ev.message()) {