From b82b1fed9f173443a92e0e42b5563ac11e0183dd Mon Sep 17 00:00:00 2001 From: Steve Russell Date: Sat, 14 Jun 2025 01:58:16 +0100 Subject: [PATCH 01/20] Audio: Optimize output summing --- src/Audio.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Audio.cpp b/src/Audio.cpp index baaa1cc..1d14b66 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -59,7 +59,7 @@ void Audio::ChannelProcess1(rainbow::IO &io, rack::engine::Input &input, rack::e nInputBuffer[i].startIncr(inLen); for (int j = 0; j < NUM_SAMPLES; j++) { - int32_t v = (int32_t)clamp(nInputFrames[i][j].samples[0] * MAX_12BIT, MIN_12BIT, MAX_12BIT); + int32_t v = std::clamp(nInputFrames[i][j].samples[0] * MAX_12BIT, MIN_12BIT, MAX_12BIT); switch(inChannels) { case 1: @@ -89,14 +89,9 @@ void Audio::ChannelProcess1(rainbow::IO &io, rack::engine::Input &input, rack::e filterbank.process_audio_block(); // Convert output buffer - for (int chan = 0; chan < NUM_CHANNELS; chan++) { - for (int i = 0; i < NUM_SAMPLES; i++) { - outputFrames1[i].samples[0] = 0; - } - } - - for (int chan = 0; chan < NUM_CHANNELS; chan++) { - for (int i = 0; i < NUM_SAMPLES; i++) { + for (int i = 0; i < NUM_SAMPLES; i++) { + outputFrames1[i].samples[0] = 0; + for (int chan = 0; chan < NUM_CHANNELS; chan++) { outputFrames1[i].samples[0] += io.out[chan][i] / MAX_12BIT; } } From 56aa0e9a40963e33f357eb976d27a5bc243c7548 Mon Sep 17 00:00:00 2001 From: Steve Russell Date: Sat, 14 Jun 2025 01:59:46 +0100 Subject: [PATCH 02/20] Filter: Use C++ pointer casts --- src/Filter.cpp | 58 ++++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/src/Filter.cpp b/src/Filter.cpp index 84b3a5f..daa8527 100644 --- a/src/Filter.cpp +++ b/src/Filter.cpp @@ -80,7 +80,7 @@ void Filter::filter_twopass(FilterBank *fb, float **filter_out) { // limit q knob range on second filter if (qc[channel_num] < 3900.0f) { qval_b[channel_num] = 1000.0f; - } else if (qc[channel_num] >= 3900.0f) { + } else { qval_b[channel_num] = 1000.0f + (qc[channel_num] - 3900.0f) * 15.0f; } // 1000 to 3925 @@ -150,7 +150,6 @@ void Filter::filter_twopass(FilterBank *fb, float **filter_out) { filter_out_b[j][i] = buf[channel_num][scale_num][filter_num][1]; filter_out[j][i] = (ratio_a * filter_out_a[j][i]) - filter_out_b[j][i]; // output of filter two needs to be inverted to avoid phase cancellation - } // Set VOCT output @@ -442,42 +441,40 @@ void Filter::filter_bpre(FilterBank *fb, float **filter_out) { void Filter::reset_buffer(int i, bool twopass) { - float *ff = (float *)buf[i]; + float *ff = reinterpret_cast(buf[i]); for (int j = 0; j < (NUM_SCALES * NUM_FILTS); j++) { - *(ff+j) = 0.0f; - *(ff+j+1) = 0.0f; - *(ff+j+2) = 0.0f; + *(ff+j) = 0.0f; + *(ff+j+1) = 0.0f; + *(ff+j+2) = 0.0f; } if (twopass) { - float *ffa = (float *)buf_a[i]; + float *ffa = reinterpret_cast(buf_a[i]); for (int j = 0; j < (NUM_SCALES * NUM_FILTS); j++) { - *(ffa+j) = 0.0f; - *(ffa+j+1) = 0.0f; - *(ffa+j+2) = 0.0f; + *(ffa+j) = 0.0f; + *(ffa+j+1) = 0.0f; + *(ffa+j+2) = 0.0f; } } - } void MaxQFilter::reset(FilterBank *fb) { - float *ff = (float *)buf; + float *ff = reinterpret_cast(buf); for (int j = 0; j < (NUM_SCALES * NUM_FILTS); j++) { - *(ff+j) = 0.0f; - *(ff+j+1) = 0.0f; - *(ff+j+2) = 0.0f; + *(ff+j) = 0.0f; + *(ff+j+1) = 0.0f; + *(ff+j+2) = 0.0f; } if (fb->filter_mode == TWOPASS) { - float *ffa = (float *)buf_a; + float *ffa = reinterpret_cast(buf_a); for (int j = 0; j < (NUM_SCALES * NUM_FILTS); j++) { - *(ffa+j) = 0.0f; - *(ffa+j+1) = 0.0f; - *(ffa+j+2) = 0.0f; + *(ffa+j) = 0.0f; + *(ffa+j+1) = 0.0f; + *(ffa+j+2) = 0.0f; } } - } void MaxQFilter::filter(FilterBank *fb, int channel_num, float **filter_out) { @@ -487,7 +484,6 @@ void MaxQFilter::filter(FilterBank *fb, int channel_num, float **filter_out) { } else { onepass(fb, channel_num, filter_out); } - } void MaxQFilter::onepass(FilterBank *fb, int channel_num, float **filter_out) { @@ -576,7 +572,6 @@ void MaxQFilter::onepass(FilterBank *fb, int channel_num, float **filter_out) { } } } - } void MaxQFilter::twopass(FilterBank *fb, int channel_num, float **filter_out) { @@ -616,13 +611,13 @@ void MaxQFilter::twopass(FilterBank *fb, int channel_num, float **filter_out) { // limit q knob range on second filter if (qc < 3900.0f) { qval_b = 1000.0f; - } else if (qc >= 3900.0f) { + } else { qval_b = 1000.0f + (qc - 3900.0f) * 15.0f; } // 1000 to 3925 // Q/RESONANCE: c0 = 1 - 2/(decay * samplerate), where decay is around 0.01 to 4.0 uint32_t qval_b_idx = (qval_b / 1.4f) + 200; - qval_b_idx = clamp(qval_b_idx, 200, 3125); + qval_b_idx = std::clamp(qval_b_idx, 200, 3125); if (fb->io->HICPUMODE) { c0_a = 1.0f - exp_4096[(uint32_t)(qval_a / 1.4f) + 200] / 10.0f; //exp[200...3125] @@ -663,8 +658,8 @@ void MaxQFilter::twopass(FilterBank *fb, int channel_num, float **filter_out) { // FIXME: 43801543.68f gain could be directly printed into calibration vector // AMPLITUDE: Boost high freqs and boost low resonance - c2_a = (0.003f * c1) - (0.1f * c0_a) + 0.102f; - c2 = (0.003f * c1) - (0.1f * c0) + 0.102f; + c2_a = (0.003f * c1) - (0.1f * c0_a) + 0.102f; + c2 = (0.003f * c1) - (0.1f * c0) + 0.102f; c2 *= ratio_b; ptmp_i32 = fb->io->in[channel_num]; @@ -691,7 +686,6 @@ void MaxQFilter::twopass(FilterBank *fb, int channel_num, float **filter_out) { filter_out_b[filter_index][sample_index] = buf[scale_num][filter_num][1]; filter_out[filter_index][sample_index] = (ratio_a * filter_out_a[filter_index][sample_index]) - filter_out_b[filter_index][sample_index]; // output of filter two needs to be inverted to avoid phase cancellation - } // Set VOCT output @@ -758,13 +752,12 @@ void MaxQFilter::twopass(FilterBank *fb, int channel_num, float **filter_out) { void BpreFilter::reset(FilterBank *fb) { - float *ff = (float *)buf; + float *ff = reinterpret_cast(buf); for (int j = 0; j < (NUM_SCALES * NUM_FILTS); j++) { - *(ff+j) = 0.0f; - *(ff+j+1) = 0.0f; - *(ff+j+2) = 0.0f; + *(ff+j) = 0.0f; + *(ff+j+1) = 0.0f; + *(ff+j+2) = 0.0f; } - } void BpreFilter::filter(FilterBank *fb, int channel_num, float **filter_out) { @@ -872,7 +865,6 @@ void BpreFilter::filter(FilterBank *fb, int channel_num, float **filter_out) { buf[scale_num][filter_num][1] = iir; filter_out[filter_index][sample_index] = fir; - } // VOCT output with glissando From 7dc48f36dcdbbd023784312bfeca353efe4db8a5 Mon Sep 17 00:00:00 2001 From: Steve Russell Date: Sat, 14 Jun 2025 02:06:32 +0100 Subject: [PATCH 03/20] Q: Use std::clamp instead --- src/Q.cpp | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/src/Q.cpp b/src/Q.cpp index 45df884..22db7fd 100644 --- a/src/Q.cpp +++ b/src/Q.cpp @@ -1,4 +1,3 @@ - /* * params.c - Parameters * @@ -28,13 +27,12 @@ */ #include - #include "Rainbow.hpp" using namespace rainbow; void Q::configure(IO *_io) { - io = _io; + io = _io; } void Q::update(void) { @@ -44,26 +42,14 @@ void Q::update(void) { float lpf = io->HICPUMODE ? Q_LPF_96 : Q_LPF_48; - //Check jack + LPF - int32_t qg = io->GLOBAL_Q_LEVEL + io->GLOBAL_Q_CONTROL; - if (qg < 0) { - qg = 0; - } - if (qg > 4095) { - qg = 4095; - } + //Check jack + LPF + int32_t qg = std::clamp(io->GLOBAL_Q_LEVEL + io->GLOBAL_Q_CONTROL, 0, 4095); global_lpf *= lpf; global_lpf += (1.0f - lpf) * qg; for (int i = 0; i < NUM_CHANNELS; i++){ - int32_t qc = io->CHANNEL_Q_LEVEL[i] + io->CHANNEL_Q_CONTROL[i]; - if (qc < 0) { - qc = 0; - } - if (qc > 4095) { - qc = 4095; - } + int32_t qc = std::clamp(io->CHANNEL_Q_LEVEL[i] + io->CHANNEL_Q_CONTROL[i], 0, 4095); qlockpot_lpf[i] *= lpf; qlockpot_lpf[i] += (1.0f - lpf) * qc; @@ -81,5 +67,4 @@ void Q::update(void) { for (int i = 0; i < NUM_CHANNELS; i++) { qval[i] = (uint32_t)(prev_qval[i] + (q_update_ctr * (qval_goal[i] - prev_qval[i]) / 51.0f)); // Q_UPDATE_RATE + 1 } - -} \ No newline at end of file +} From ffece14b257df8aff681eafe16fe2acfad44231d Mon Sep 17 00:00:00 2001 From: Steve Russell Date: Sat, 14 Jun 2025 02:07:39 +0100 Subject: [PATCH 04/20] Rainbow: Small fixes for Spread and Morph --- src/Rotation.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Rotation.cpp b/src/Rotation.cpp index 9fca8e1..3f082fe 100644 --- a/src/Rotation.cpp +++ b/src/Rotation.cpp @@ -109,7 +109,7 @@ void Rotation::rotate_up(void) { } } -bool Rotation::is_spreading(void) { +bool Rotation::is_spreading(void) { // UNUSED; TODO? for (int i = 0; i < NUM_CHANNELS; i++) { if (motion_spread_dir[i] != 0) { return true; @@ -120,7 +120,7 @@ bool Rotation::is_spreading(void) { bool Rotation::is_morphing(void) { for (int i = 0; i < NUM_CHANNELS; i++) { - if (motion_morphpos[0] != 0.0) { // TODO Shouldn't this be [i]? + if (motion_morphpos[i] != 0.0) { return true; } } @@ -128,7 +128,7 @@ bool Rotation::is_morphing(void) { } void Rotation::update_spread(int8_t t_spread) { - int32_t test_motion[NUM_CHANNELS] {}; + int32_t test_motion[NUM_CHANNELS] = {99, 99, 99, 99, 99, 99}; float spread_out; spread = t_spread; @@ -139,10 +139,6 @@ void Rotation::update_spread(int8_t t_spread) { } old_spread = spread; - for (int ii = 0 ; ii < NUM_CHANNELS; ii++) { - test_motion[ii] = 99; - } - int32_t base_note = motion_fadeto_note[2]; for (int32_t i = 0; i < NUM_CHANNELS; i++) { @@ -160,7 +156,8 @@ void Rotation::update_spread(int8_t t_spread) { //Find an open filter channel: //Our starting point is based on the location of channel 2, and spread outward from there - int32_t offset = (((int32_t)i) - 2) * spread; // TODO is cast correct? re: spread is int8_t + // int32_t offset = (((int32_t)i) - 2) * spread; // TODO is cast correct? re: spread is int8_t + int32_t offset = (i - 2) * (int32_t)spread; int32_t test_spot = base_note + offset; //Set up test_spot, since the first thing we do is in the loop is test_spot += spread_dir[i] @@ -182,9 +179,9 @@ void Rotation::update_spread(int8_t t_spread) { is_distinct = true; // Check to see if test_spot is a distinct value: - for (int j = 0; j < NUM_CHANNELS; j++) { + for (uint8_t j = 0; j < NUM_CHANNELS; j++) { //...if the test spot is already assigned to a locked or stationary channel (2), or a channel we already spread'ed, or a blocked frequency then try again - if ((i != j && ((test_spot == test_motion[j] && j < i) || (test_spot == motion_fadeto_note[j] && (io->LOCK_ON[j] || j == 2)))) || io->FREQ_BLOCK[test_spot]) { + if ((i != j && j < i && ((test_spot == test_motion[j]) || (test_spot == motion_fadeto_note[j] && (io->LOCK_ON[j] || j == 2)))) || io->FREQ_BLOCK[test_spot]) { is_distinct = false; } } From 4d10ad022e027deac30ef10adad29b4f41fad452 Mon Sep 17 00:00:00 2001 From: Steve Russell Date: Sat, 14 Jun 2025 02:12:05 +0100 Subject: [PATCH 05/20] Tuning: Stylistic edits --- src/Tuning.cpp | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/Tuning.cpp b/src/Tuning.cpp index c389749..20798eb 100644 --- a/src/Tuning.cpp +++ b/src/Tuning.cpp @@ -27,7 +27,6 @@ */ #include - #include "Rainbow.hpp" using namespace rainbow; @@ -57,8 +56,8 @@ void Tuning::update(void) { if (filterbank->filter_type == MAXQ) { // Read buffer knob and normalize input: 0-1 - t_fo = (float)(io->FREQNUDGE1_ADC); - t_fe = (float)(io->FREQNUDGE6_ADC); + float t_fo = io->FREQNUDGE1_ADC; + float t_fe = io->FREQNUDGE6_ADC; if (io->FREQCV1_CHAN > 1) { f_shift_all[0] = pow(2.0f, io->FREQCV1_CV[0]); @@ -145,13 +144,10 @@ void Tuning::update(void) { if (!io->LOCK_ON[2]) { freq_nudge[2] = f_nudge_odds * coarse_adj[2]; } - if (!io->LOCK_ON[4]) { freq_nudge[4] = f_nudge_odds * coarse_adj[4]; } - } - // disable freq nudge and shift on channel 3 and 5 when in "1 mode" - else { + } else { // disable freq nudge and shift on channel 3 and 5 when in "1 mode" if (!io->LOCK_ON[2]) { freq_nudge[2] = coarse_adj[2]; } @@ -160,7 +156,7 @@ void Tuning::update(void) { } } - //EVENS + //EVENS if (!io->LOCK_ON[5]) { freq_nudge[5] = f_nudge_evens * coarse_adj[5]; } @@ -172,9 +168,7 @@ void Tuning::update(void) { if (!io->LOCK_ON[3]) { freq_nudge[3] = f_nudge_evens * coarse_adj[3]; } - } - // disable freq nudge and shift on channel 2 and 4 when in "6 mode" - else { + } else { // disable freq nudge and shift on channel 2 and 4 when in "6 mode" if (!io->LOCK_ON[3]) { freq_nudge[3] = coarse_adj[3]; } @@ -182,10 +176,8 @@ void Tuning::update(void) { freq_nudge[1] = coarse_adj[1]; } } - } else { // BPRE Filter - - t_fo = (float)(io->FREQNUDGE1_ADC + io->FREQCV1_CV[0]) / 4096.0f; + float t_fo = (float)(io->FREQNUDGE1_ADC + io->FREQCV1_CV[0]) / 4096.0f; if (t_fo > 1.0f) { t_fo = 1.0f; } @@ -193,7 +185,7 @@ void Tuning::update(void) { t_fo = -1.0f; } - t_fe = (float)(io->FREQNUDGE6_ADC + io->FREQCV6_CV[0]) / 4096.0f; + float t_fe = (float)(io->FREQNUDGE6_ADC + io->FREQCV6_CV[0]) / 4096.0f; if (t_fe > 1.0f) { t_fe = 1.0f; } @@ -204,11 +196,11 @@ void Tuning::update(void) { float f_shift_odds = 1.0f; float f_shift_evens = 1.0f; - f_nudge_odds *= FREQNUDGE_LPF; - f_nudge_odds += (1.0f - FREQNUDGE_LPF) * t_fo; + f_nudge_odds *= FREQNUDGE_LPF; + f_nudge_odds += (1.0f - FREQNUDGE_LPF) * t_fo; - f_nudge_evens *= FREQNUDGE_LPF; - f_nudge_evens += (1.0f - FREQNUDGE_LPF) * t_fe; + f_nudge_evens *= FREQNUDGE_LPF; + f_nudge_evens += (1.0f - FREQNUDGE_LPF) * t_fe; if (!io->LOCK_ON[0]) { freq_nudge[0] = f_nudge_odds; From dfac37cda6e72e812f069d852b7abc64bd6deffc Mon Sep 17 00:00:00 2001 From: Steve Russell Date: Sat, 14 Jun 2025 02:12:52 +0100 Subject: [PATCH 06/20] Noise: Add faster brown noise gen --- src/dsp/noise.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/dsp/noise.hpp b/src/dsp/noise.hpp index ff4e7f5..4a5d835 100644 --- a/src/dsp/noise.hpp +++ b/src/dsp/noise.hpp @@ -110,5 +110,18 @@ namespace bogaudio { } }; + struct FastBrownNoiseGenerator : WhiteNoiseGenerator { + constexpr static float c0 = 0.0688f; + constexpr static float c1 = (1. - c0) * 0.984f; + + float _state = 0; + + float _next() override { + auto r = _uniform(_generator); + _state = _state * c1 + r * c0; + return _state; + } + }; + } // namespace dsp } // namespace bogaudio From 5ccdd98346c57b92c0d5779b8eb0231502ff6cf0 Mon Sep 17 00:00:00 2001 From: Steve Russell Date: Sat, 14 Jun 2025 08:21:35 +0100 Subject: [PATCH 07/20] Rainbow: Numerous changes More var init's; Populate state when needed; Use std::clamp; Some type changes --- src/Common.hpp | 2 +- src/Rainbow.cpp | 182 +++++++++++++++++------------------ src/Rainbow.hpp | 247 ++++++++++++++++++++++++------------------------ 3 files changed, 213 insertions(+), 218 deletions(-) diff --git a/src/Common.hpp b/src/Common.hpp index d40a834..06503d2 100644 --- a/src/Common.hpp +++ b/src/Common.hpp @@ -8,7 +8,7 @@ namespace prism::core { -const double PI = 3.14159265358979323846264338327950288; +constexpr static double PI = 3.14159265358979323846264338327950288; struct PrismModule : rack::Module { PrismModule(int numParams, int numInputs, int numOutputs, int numLights = 0) { diff --git a/src/Rainbow.cpp b/src/Rainbow.cpp index feeab73..86a72f3 100644 --- a/src/Rainbow.cpp +++ b/src/Rainbow.cpp @@ -18,18 +18,17 @@ struct LED : LightWidget { Rainbow *module {}; int id; - const float ledRadius = 5.0f; - const float ledStrokeWidth = 1.0f; + constexpr static float ledRadius = 5.0f; + constexpr static float ledStrokeWidth = 1.0f; float xCenter; float yCenter; - LED(int i, float xPos, float yPos) { + LED(int i, float xPos, float yPos) : color(nvgRGB(255.f, 255.f, 255.f)) { id = i; box.pos.x = xPos; box.pos.y = yPos; box.size.x = ledRadius * 2.0f + ledStrokeWidth * 2.0f; box.size.y = ledRadius * 2.0f + ledStrokeWidth * 2.0f; - color = nvgRGB(255, 255, 255); Vec ctr = box.getCenter(); xCenter = ctr.x / SVG_DPI; yCenter = ctr.y / SVG_DPI; @@ -134,22 +133,16 @@ struct Rainbow : core::PrismModule { NUM_LIGHTS }; - // LED *ringLEDs[NUM_FILTS] = {}; std::array ringLEDs {}; - // LED *scaleLEDs[NUM_SCALES] = {}; std::array scaleLEDs {}; - // LED *envelopeLEDs[NUM_CHANNELS] = {}; std::array envelopeLEDs {}; - // LED *qLEDs[NUM_CHANNELS] = {}; std::array qLEDs {}; - // LED *tuningLEDs[NUM_CHANNELS] = {}; std::array tuningLEDs {}; - // dsp::VuMeter2 vuMeters[6]; std::array vuMeters; dsp::ClockDivider lightDivider; - // uint32_t channelClipCnt[6]; - std::array channelClipCnt; + std::array channelClipCnt {}; + const float clipLimit = -5.2895f; // Clip at 10V; int frameRate = 735; // 44100Hz / 60fps @@ -271,6 +264,8 @@ struct Rainbow : core::PrismModule { json_t* scale_array = json_array(); json_t* scale_bank_array = json_array(); + populate_state(); + for (int i = 0; i < NUM_CHANNELS; i++) { json_t* noteJ = json_integer(state.note[i]); json_t* scaleJ = json_integer(state.scale[i]); @@ -439,6 +434,11 @@ struct Rainbow : core::PrismModule { configParam(FREQNUDGE6_PARAM, -4095, 4095, 0, "Freq Nudge evens"); configSwitch(MOD135_PARAM, 0, 1, 0, "Mod", {"1", "135"}); configSwitch(MOD246_PARAM, 0, 1, 0, "Mod", {"6", "246"}); + configParam(LOCK135_PARAM, 0, 1, 0, "Inv. Lock 1-135"); + configParam(LOCK246_PARAM, 0, 1, 0, "Inv. Lock 6-246"); + // TODO: New Prism::Button needed for these + // configSwitch(LOCK135_PARAM, 0, 1, 0, "Inv. Lock 1-135", {"Unlocked", "Locked"}); + // configSwitch(LOCK246_PARAM, 0, 1, 0, "Inv. Lock 6-246", {"Unlocked", "Locked"}); configParam(BANK_PARAM, 0, 19, 0, "Bank"); configParam(SWITCHBANK_PARAM, 0, 1, 0, "Switch bank"); @@ -473,21 +473,18 @@ struct Rainbow : core::PrismModule { configInput(MONO_Q_INPUT + i, string::f("Mono Q %i", i + 1)); configOutput(MONO_VOCT_OUTPUT + i, string::f("Mono V/Oct %i", i + 1)); configOutput(MONO_ENV_OUTPUT + i, string::f("Mono envelope %i", i + 1)); - } - for (int n = 0; n < NUM_CHANNELS; n++) { - configParam(CHANNEL_LEVEL_PARAM + n, 0, 4095, 4095, "Channel Level"); - configParam(LEVEL_OUT_PARAM + n, 0, 2, 1, "Channel Level"); + configParam(CHANNEL_LEVEL_PARAM + i, 0, 4095, 4095, "Channel Level"); + configParam(LEVEL_OUT_PARAM + i, 0, 2, 1, "Channel Level"); - configParam(CHANNEL_Q_PARAM + n, 0, 4095, 2048, "Channel Q"); - configParam(CHANNEL_Q_ON_PARAM + n, 0, 1, 0, "Channel Q activate"); + configParam(CHANNEL_Q_PARAM + i, 0, 4095, 2048, "Channel Q"); + configButton(CHANNEL_Q_ON_PARAM + i, "Channel Q activate"); - configParam(LOCKON_PARAM + n, 0, 1, 0, "Lock channel"); + configButton(LOCKON_PARAM + i, "Lock channel"); - configParam(TRANS_PARAM + n, -12, 12, 0, "Semitone transpose"); + configParam(TRANS_PARAM + i, -12, 12, 0, "Semitone transpose"); - vuMeters[n].mode = dsp::VuMeter2::RMS; - channelClipCnt[n] = 0; + vuMeters[i].mode = dsp::VuMeter2::RMS; } configBypass(POLY_IN_INPUT, POLY_OUT_OUTPUT); @@ -621,6 +618,7 @@ void Rainbow::process(const ProcessArgs &args) { if (lock246Trigger.process(inputs[LOCK246_INPUT].getVoltage()) || lock246ButtonTrigger.process(params[LOCK246_PARAM].getValue())) { + io.LOCK_ON[5] = !io.LOCK_ON[5]; if (io.MOD246_SWITCH == Mod_246) { @@ -677,11 +675,11 @@ void Rainbow::process(const ProcessArgs &args) { int noiseSelected = params[NOISE_PARAM].getValue(); - io.MORPH_ADC = (uint16_t)clamp(params[MORPH_PARAM].getValue() + inputs[MORPH_INPUT].getVoltage() * 409.5f, 0.0f, 4095.0f); - io.SPREAD_ADC = (uint16_t)clamp(params[SPREAD_PARAM].getValue() + inputs[SPREAD_INPUT].getVoltage() * 409.5f, 0.0f, 4095.0f); + io.MORPH_ADC = std::clamp(params[MORPH_PARAM].getValue() + inputs[MORPH_INPUT].getVoltage() * 409.5f, 0.0f, 4095.0f); + io.SPREAD_ADC = std::clamp(params[SPREAD_PARAM].getValue() + inputs[SPREAD_INPUT].getVoltage() * 409.5f, 0.0f, 4095.0f); - io.GLOBAL_Q_LEVEL = (int16_t)clamp(inputs[GLOBAL_Q_INPUT].getVoltage() * 409.5f, -4095.0f, 4095.0f); - io.GLOBAL_Q_CONTROL = (int16_t)params[GLOBAL_Q_PARAM].getValue(); + io.GLOBAL_Q_LEVEL = std::clamp(inputs[GLOBAL_Q_INPUT].getVoltage() * 409.5f, -4095.0f, 4095.0f); + io.GLOBAL_Q_CONTROL = (int32_t)params[GLOBAL_Q_PARAM].getValue(); io.GLOBAL_LEVEL_ADC = params[GLOBAL_LEVEL_PARAM].getValue() / 4095.0f; io.GLOBAL_LEVEL_CV = inputs[GLOBAL_LEVEL_INPUT].getVoltage() / 5.0f; @@ -690,29 +688,29 @@ void Rainbow::process(const ProcessArgs &args) { if (!inputs[MONO_LEVEL_INPUT + n].isConnected() && !inputs[POLY_LEVEL_INPUT].isConnected()) { io.LEVEL_CV[n] = 1.0f; } else { - io.LEVEL_CV[n] = clamp((inputs[MONO_LEVEL_INPUT + n].getVoltage() + inputs[POLY_LEVEL_INPUT].getVoltage(n) + 5.0f) / 10.0f, 0.0f, 1.0f); + io.LEVEL_CV[n] = std::clamp((inputs[MONO_LEVEL_INPUT + n].getVoltage() + inputs[POLY_LEVEL_INPUT].getVoltage(n) + 5.0f) / 10.0f, 0.0f, 1.0f); } - io.LEVEL_ADC[n] = clamp(params[CHANNEL_LEVEL_PARAM + n].getValue() / 4095.0f, 0.0f, 1.0f); - io.CHANNEL_Q_LEVEL[n] = (int16_t)clamp((inputs[MONO_Q_INPUT + n].getVoltage() + inputs[POLY_Q_INPUT].getVoltage(n)) * 409.5f, -4095.0f, 4095.0f); - io.CHANNEL_Q_CONTROL[n] = (int16_t)params[CHANNEL_Q_PARAM + n].getValue(); + io.LEVEL_ADC[n] = std::clamp(params[CHANNEL_LEVEL_PARAM + n].getValue() / 4095.0f, 0.0f, 1.0f); + io.CHANNEL_Q_LEVEL[n] = std::clamp((inputs[MONO_Q_INPUT + n].getVoltage() + inputs[POLY_Q_INPUT].getVoltage(n)) * 409.5f, -4095.0f, 4095.0f); + io.CHANNEL_Q_CONTROL[n] = (int32_t)params[CHANNEL_Q_PARAM + n].getValue(); io.TRANS_DIAL[n] = params[TRANS_PARAM + n].getValue(); } - io.FREQNUDGE1_ADC = (int16_t)params[FREQNUDGE1_PARAM].getValue(); - io.FREQNUDGE6_ADC = (int16_t)params[FREQNUDGE6_PARAM].getValue(); + io.FREQNUDGE1_ADC = params[FREQNUDGE1_PARAM].getValue(); + io.FREQNUDGE6_ADC = params[FREQNUDGE6_PARAM].getValue(); - io.SCALE_ADC = (uint16_t)clamp(inputs[SCALE_INPUT].getVoltage() * 409.5f, 0.0f, 4095.0f); - io.ROTCV_ADC = (uint16_t)clamp(inputs[ROTATECV_INPUT].getVoltage() * 409.5f, 0.0f, 4095.0f); + io.SCALE_ADC = std::clamp(inputs[SCALE_INPUT].getVoltage() * 409.5f, 0.0f, 4095.0f); + io.ROTCV_ADC = std::clamp(inputs[ROTATECV_INPUT].getVoltage() * 409.5f, 0.0f, 4095.0f); io.FREQCV1_CHAN = inputs[FREQCV1_INPUT].getChannels(); io.FREQCV6_CHAN = inputs[FREQCV6_INPUT].getChannels(); for (int i = 0; i < 3; i++) { - io.FREQCV1_CV[i] = clamp(inputs[FREQCV1_INPUT].getVoltage(i) * 0.5f, -5.0f, 5.0f); - io.FREQCV6_CV[i] = clamp(inputs[FREQCV6_INPUT].getVoltage(i) * 0.5f, -5.0f, 5.0f); + io.FREQCV1_CV[i] = std::clamp(inputs[FREQCV1_INPUT].getVoltage(i) * 0.5f, -5.0f, 5.0f); + io.FREQCV6_CV[i] = std::clamp(inputs[FREQCV6_INPUT].getVoltage(i) * 0.5f, -5.0f, 5.0f); } - io.SLEW_ADC = (uint16_t)params[SLEW_PARAM].getValue(); + io.SLEW_ADC = (uint32_t)params[SLEW_PARAM].getValue(); io.ENV_SWITCH = (EnvelopeMode)params[ENV_PARAM].getValue(); if (glissTrigger.process(params[VOCTGLIDE_PARAM].getValue())) { @@ -754,10 +752,10 @@ void Rainbow::process(const ProcessArgs &args) { outputs[POLY_VOCT_OUTPUT].setChannels(6); outputs[POLY_ENV_OUTPUT].setChannels(12); for (int n = 0; n < NUM_CHANNELS; n++) { - outputs[POLY_ENV_OUTPUT].setVoltage(clamp(io.env_out[n] * 100.0f, 0.0f, 10.0f), n); + outputs[POLY_ENV_OUTPUT].setVoltage(std::clamp(io.env_out[n] * 100.0f, 0.0f, 10.0f), n); outputs[POLY_ENV_OUTPUT].setVoltage(io.OUTLEVEL[n] * 10.0f, n + 6); outputs[POLY_VOCT_OUTPUT].setVoltage(io.voct_out[n], n); - outputs[MONO_ENV_OUTPUT + n].setVoltage(clamp(io.env_out[n] * 100.0f, 0.0f, 10.0f)); + outputs[MONO_ENV_OUTPUT + n].setVoltage(std::clamp(io.env_out[n] * 100.0f, 0.0f, 10.0f)); outputs[MONO_VOCT_OUTPUT + n].setVoltage(io.voct_out[n]); params[Rainbow::LEVEL_OUT_PARAM + n].setValue(io.OUTLEVEL[n]); @@ -822,8 +820,8 @@ void Rainbow::process(const ProcessArgs &args) { for (int i = 0; i < NUM_FILTS; i++) { if (io.FREQ_BLOCK[i] && ringLEDs[i]) { - ringLEDs[i]->color = nvgRGBf(0.0f, 0.0f, 0.0f); - ringLEDs[i]->colorBorder = blockedBorder; + ringLEDs[i]->color = nvgRGBf(0.0f, 0.0f, 0.0f); + ringLEDs[i]->colorBorder = blockedBorder; } else if (ringLEDs[i]) { ringLEDs[i]->color = nvgRGBf( io.ring[i][0], @@ -851,7 +849,7 @@ void Rainbow::process(const ProcessArgs &args) { } if ((channelClipCnt[i] & 32) && envelopeLEDs[i]) { - envelopeLEDs[i]->color = nvgRGBf(0.0f, 0.0f, 0.0f); + envelopeLEDs[i]->color = nvgRGBf(0.0f, 0.0f, 0.0f); envelopeLEDs[i]->colorBorder = defaultBorder; } else if (envelopeLEDs[i]) { envelopeLEDs[i]->color = nvgRGBf( @@ -921,7 +919,6 @@ void Rainbow::prepare(void) { input.process_rotateCV(); input.process_scaleCV(); levels.update(); - populate_state(); } void Rainbow::set_default_param_values(void) { @@ -940,8 +937,8 @@ void Rainbow::set_default_param_values(void) { rotation.motion_scalecv_overage[i] = 0; } - rotation.motion_notejump = 0; - rotation.motion_rotate = 0; + rotation.motion_notejump = 0; + rotation.motion_rotate = 0; filterbank.filter_type = MAXQ; filterbank.filter_mode = TWOPASS; @@ -972,8 +969,8 @@ void Rainbow::load_from_state(void) { filterbank.userscale_bank48[i] = state.userscale48[i]; } - rotation.motion_notejump = 0; - rotation.motion_rotate = 0; + rotation.motion_notejump = 0; + rotation.motion_rotate = 0; state.initialised = true; } @@ -1011,34 +1008,33 @@ struct BankWidget : Widget { NVGcolor colors[NUM_SCALEBANKS] = { // Shades of Blue - nvgRGBf( 255.0f/255.0f, 070.0f/255.0f, 255.0f/255.0f ), - nvgRGBf( 250.0f/255.0f, 080.0f/255.0f, 250.0f/255.0f ), - nvgRGBf( 245.0f/255.0f, 090.0f/255.0f, 245.0f/255.0f ), - nvgRGBf( 240.0f/255.0f, 100.0f/255.0f, 240.0f/255.0f ), - nvgRGBf( 235.0f/255.0f, 110.0f/255.0f, 235.0f/255.0f ), - nvgRGBf( 230.0f/255.0f, 120.0f/255.0f, 230.0f/255.0f ), + nvgRGBf( 255.0f/255.0f, 070.0f/255.0f, 255.0f/255.0f ), + nvgRGBf( 250.0f/255.0f, 080.0f/255.0f, 250.0f/255.0f ), + nvgRGBf( 245.0f/255.0f, 090.0f/255.0f, 245.0f/255.0f ), + nvgRGBf( 240.0f/255.0f, 100.0f/255.0f, 240.0f/255.0f ), + nvgRGBf( 235.0f/255.0f, 110.0f/255.0f, 235.0f/255.0f ), + nvgRGBf( 230.0f/255.0f, 120.0f/255.0f, 230.0f/255.0f ), // Shades of Cyan - nvgRGBf( 150.0f/255.0f, 255.0f/255.0f, 255.0f/255.0f ), - nvgRGBf( 130.0f/255.0f, 245.0f/255.0f, 245.0f/255.0f ), - nvgRGBf( 120.0f/255.0f, 235.0f/255.0f, 235.0f/255.0f ), + nvgRGBf( 150.0f/255.0f, 255.0f/255.0f, 255.0f/255.0f ), + nvgRGBf( 130.0f/255.0f, 245.0f/255.0f, 245.0f/255.0f ), + nvgRGBf( 120.0f/255.0f, 235.0f/255.0f, 235.0f/255.0f ), // Shades of Yellow - nvgRGBf( 255.0f/255.0f, 255.0f/255.0f, 150.0f/255.0f ), - nvgRGBf( 255.0f/255.0f, 245.0f/255.0f, 130.0f/255.0f ), - nvgRGBf( 255.0f/255.0f, 235.0f/255.0f, 120.0f/255.0f ), - nvgRGBf( 255.0f/255.0f, 225.0f/255.0f, 110.0f/255.0f ), + nvgRGBf( 255.0f/255.0f, 255.0f/255.0f, 150.0f/255.0f ), + nvgRGBf( 255.0f/255.0f, 245.0f/255.0f, 130.0f/255.0f ), + nvgRGBf( 255.0f/255.0f, 235.0f/255.0f, 120.0f/255.0f ), + nvgRGBf( 255.0f/255.0f, 225.0f/255.0f, 110.0f/255.0f ), // Shades of Green - nvgRGBf( 588.0f/1023.0f, 954.0f/1023.0f, 199.0f/1023.0f ), - nvgRGBf( 274.0f/1023.0f, 944.0f/1023.0f, 67.0f/1023.0f ), - nvgRGBf( 83.0f/1023.0f, 934.0f/1023.0f, 1.0f/1023.0f ), - nvgRGBf( 1.0f/1023.0f, 924.0f/1023.0f, 1.0f/1023.0f ), - nvgRGBf( 100.0f/1023.0f, 824.0f/1023.0f, 9.0f/1023.0f ), - nvgRGBf( 100.0f/1023.0f, 724.0f/1023.0f, 4.0f/1023.0f ), - - nvgRGBf( 900.0f/1023.0f, 900.0f/1023.0f, 900.0f/1023.0f ) - + nvgRGBf( 588.0f/1023.0f, 954.0f/1023.0f, 199.0f/1023.0f ), + nvgRGBf( 274.0f/1023.0f, 944.0f/1023.0f, 67.0f/1023.0f ), + nvgRGBf( 83.0f/1023.0f, 934.0f/1023.0f, 1.0f/1023.0f ), + nvgRGBf( 1.0f/1023.0f, 924.0f/1023.0f, 1.0f/1023.0f ), + nvgRGBf( 100.0f/1023.0f, 824.0f/1023.0f, 9.0f/1023.0f ), + nvgRGBf( 100.0f/1023.0f, 724.0f/1023.0f, 4.0f/1023.0f ), + + nvgRGBf( 900.0f/1023.0f, 900.0f/1023.0f, 900.0f/1023.0f ) }; BankWidget() { @@ -1217,69 +1213,69 @@ struct RainbowWidget : ModuleWidget { if(module) { BankWidget *bankW = new BankWidget(); bankW->module = module; - bankW->box.pos = Vec(474.962f, 380.0 - 320.162 - 17.708); - bankW->box.size = Vec(80.0, 20.0f); + bankW->box.pos = Vec(474.962f, 42.13f); + bankW->box.size = Vec(80.f, 20.0f); addChild(bankW); - float XStartL = 106.5; - float XStartR = 256.5 + 2.0; - float xDelta = 40.0; - float yVoct = 380.0 - 339.500 - 4.5; - float yEnv = 380.0 - 261.500 - 4.5; - float yQ = 380.0 - 77.500 - 4.5; + constexpr static float XStartL = 106.5f; + constexpr static float XStartR = 258.5f; + constexpr static float xDelta = 40.f; + constexpr static float yVoct = 36.f; + constexpr static float yEnv = 114.f; + constexpr static float yQ = 298.f; for (int i = 0; i < 3; i++) { module->qLEDs[i] = new LED(i, XStartL + i * xDelta, yQ); - module->qLEDs[i]->module = NULL; + module->qLEDs[i]->module = nullptr; addChild(module->qLEDs[i]); module->envelopeLEDs[i] = new LED(i, XStartL + i * xDelta, yEnv); - module->envelopeLEDs[i]->module = NULL; + module->envelopeLEDs[i]->module = nullptr; addChild(module->envelopeLEDs[i]); module->tuningLEDs[i] = new LED(i, XStartL + i * xDelta, yVoct); - module->tuningLEDs[i]->module = NULL; + module->tuningLEDs[i]->module = nullptr; addChild(module->tuningLEDs[i]); } for (int i = 3; i < 6; i++) { module->qLEDs[i] = new LED(i, XStartR + (i - 3) * xDelta, yQ); - module->qLEDs[i]->module = NULL; + module->qLEDs[i]->module = nullptr; addChild(module->qLEDs[i]); module->envelopeLEDs[i] = new LED(i, XStartR + (i - 3) * xDelta, yEnv); - module->envelopeLEDs[i]->module = NULL; + module->envelopeLEDs[i]->module = nullptr; addChild(module->envelopeLEDs[i]); module->tuningLEDs[i] = new LED(i, XStartR + (i - 3) * xDelta, yVoct); - module->tuningLEDs[i]->module = NULL; + module->tuningLEDs[i]->module = nullptr; addChild(module->tuningLEDs[i]); } } if (module) { - Vec ringBox(Vec(429.258, 137.198 - 2.9)); - float ringDiv = (core::PI * 2.0f) / NUM_FILTS; + Vec ringBox(Vec(429.258f, 134.298f)); + constexpr static float ringDiv = (core::PI * 2.f) / NUM_FILTS; for (int i = 0; i < NUM_FILTS; i++) { - float xPos = sin(core::PI - ringDiv * i) * 50.0f; - float yPos = cos(core::PI - ringDiv * i) * 50.0f; + float xPos = sin(core::PI - ringDiv * i) * 50.f; + float yPos = cos(core::PI - ringDiv * i) * 50.f; - module->ringLEDs[i] = new LED(i, ringBox.x + 50 + xPos, ringBox.y + 50.0f + yPos); + module->ringLEDs[i] = new LED(i, ringBox.x + 50.f + xPos, ringBox.y + 50.f + yPos); module->ringLEDs[i]->module = module; addChild(module->ringLEDs[i]); } - float scaleDiv = (core::PI * 2.0f) / NUM_SCALES; + constexpr static float scaleDiv = (core::PI * 2.0f) / NUM_SCALES; for (int i = 0; i < NUM_SCALES; i++) { - float xPos = sin(core::PI - scaleDiv * i) * 30.0f; - float yPos = cos(core::PI - scaleDiv * i) * 30.0f; + float xPos = sin(core::PI - scaleDiv * i) * 30.f; + float yPos = cos(core::PI - scaleDiv * i) * 30.f; - module->scaleLEDs[i] = new LED(i, ringBox.x + 50.0f + xPos, ringBox.y + 50.0f + yPos); - module->scaleLEDs[i]->module = NULL; + module->scaleLEDs[i] = new LED(i, ringBox.x + 50.f + xPos, ringBox.y + 50.f + yPos); + module->scaleLEDs[i]->module = nullptr; addChild(module->scaleLEDs[i]); } } diff --git a/src/Rainbow.hpp b/src/Rainbow.hpp index 0c2627e..5ff324f 100644 --- a/src/Rainbow.hpp +++ b/src/Rainbow.hpp @@ -1,6 +1,7 @@ // cppcheck-suppress-file unusedStructMember #pragma once +#include #include #include #include @@ -36,8 +37,8 @@ #define CENTER_PLATEAU 80 struct RainbowScaleExpanderMessage { - std::array maxq48; - std::array maxq96; + std::array maxq48 {}; + std::array maxq96 {}; bool updated; }; @@ -123,7 +124,7 @@ struct Audio { float outputScale = 2.0f; bogaudio::dsp::PinkNoiseGenerator pink; - bogaudio::dsp::RedNoiseGenerator brown; + bogaudio::dsp::FastBrownNoiseGenerator brown; bogaudio::dsp::WhiteNoiseGenerator white; dsp::SampleRateConverter<1> nInputSrc[NUM_CHANNELS] = {}; @@ -161,27 +162,27 @@ struct Envelope { Levels* levels; IO* io; - const float MIN_VOCT = -10.0f/3.0f; - const float MAX_VOCT = 4.75f; - const float VOCT_RANGE = -MIN_VOCT + MAX_VOCT; - const float ENV_SCALE = 4.0e+7; + constexpr static float MIN_VOCT = -10.0f / 3.0f; + constexpr static float MAX_VOCT = 4.75f; + constexpr static float VOCT_RANGE = -MIN_VOCT + MAX_VOCT; + constexpr static float ENV_SCALE = 4.0e+7; - float envout_preload[NUM_CHANNELS]; - float envout_preload_voct[NUM_CHANNELS]; + float envout_preload[NUM_CHANNELS] = {}; + float envout_preload_voct[NUM_CHANNELS] = {}; // Private float stored_trigger_level[NUM_CHANNELS] = {}; - float envelope[NUM_CHANNELS]; - uint32_t env_trigout[NUM_CHANNELS]; - uint32_t env_low_ctr[NUM_CHANNELS]; + float envelope[NUM_CHANNELS] = {}; + uint32_t env_trigout[NUM_CHANNELS] = {}; + uint32_t env_low_ctr[NUM_CHANNELS] = {}; uint32_t env_update_ctr = UINT32_MAX; uint32_t ENV_UPDATE_RATE = 50; bool env_prepost_mode; // false = pre - EnvOutModes env_track_mode; - float envspeed_attack; - float envspeed_decay; + EnvOutModes env_track_mode {}; + float envspeed_attack {}; + float envspeed_decay {}; void configure(IO *_io, Levels *_levels); void initialise(void); @@ -241,7 +242,7 @@ struct Filter { float buf_a[NUM_CHANNELS][NUM_SCALES][NUM_FILTS][3] = {}; // Filter parameters - float qval_b[NUM_CHANNELS] = {}; + float qval_b[NUM_CHANNELS] = {}; float qval_a[NUM_CHANNELS] = {}; float qc[NUM_CHANNELS] = {}; @@ -266,9 +267,9 @@ struct FilterBank { IO* io; Levels* levels; - // Filter filter; - std::array maxq; - std::array bpre; + // Filter filter; + std::array maxq = {}; + std::array bpre = {}; FilterTypes filter_type = MAXQ; FilterModes filter_mode = TWOPASS; @@ -294,12 +295,12 @@ struct FilterBank { // std::array userscale_bank48; // std::array userscale_bank96; - float *c_hiq[NUM_CHANNELS] {}; - float *c_loq[NUM_CHANNELS] {}; - float *bpretuning[NUM_CHANNELS] {}; + float *c_hiq[NUM_CHANNELS] = {}; + float *c_loq[NUM_CHANNELS] = {}; + float *bpretuning[NUM_CHANNELS] = {}; - float userscale_bank96[NUM_BANKNOTES]; - float userscale_bank48[NUM_BANKNOTES]; + float userscale_bank96[NUM_BANKNOTES] = {}; + float userscale_bank48[NUM_BANKNOTES] = {}; float **filter_out; @@ -321,66 +322,66 @@ struct FilterBank { }; struct IO { - bool UI_UPDATE; - bool HICPUMODE; + bool UI_UPDATE {}; + bool HICPUMODE {}; bool READCOEFFS = true; - uint16_t MORPH_ADC; + uint32_t MORPH_ADC {}; - int16_t GLOBAL_Q_LEVEL; - int16_t GLOBAL_Q_CONTROL; - int16_t CHANNEL_Q_LEVEL[NUM_CHANNELS]; - int16_t CHANNEL_Q_CONTROL[NUM_CHANNELS]; + int32_t GLOBAL_Q_LEVEL {}; + int32_t GLOBAL_Q_CONTROL {}; + int32_t CHANNEL_Q_LEVEL[NUM_CHANNELS] {}; + int32_t CHANNEL_Q_CONTROL[NUM_CHANNELS] {}; - float GLOBAL_LEVEL_ADC; - float GLOBAL_LEVEL_CV; - float LEVEL_ADC[NUM_CHANNELS]; - float LEVEL_CV[NUM_CHANNELS]; + float GLOBAL_LEVEL_ADC {}; + float GLOBAL_LEVEL_CV {}; + float LEVEL_ADC[NUM_CHANNELS] {}; + float LEVEL_CV[NUM_CHANNELS] {}; - int16_t FREQNUDGE1_ADC; - int16_t FREQNUDGE6_ADC; + float FREQNUDGE1_ADC {}; + float FREQNUDGE6_ADC {}; - uint16_t SLEW_ADC; - uint16_t SCALE_ADC; - uint16_t SPREAD_ADC; - uint16_t ROTCV_ADC; + uint32_t SLEW_ADC {}; + uint32_t SCALE_ADC {}; + uint32_t SPREAD_ADC {}; + uint32_t ROTCV_ADC {}; - float FREQCV1_CV[3]; - int FREQCV1_CHAN; + float FREQCV1_CV[3] {}; + int FREQCV1_CHAN {}; - float FREQCV6_CV[3]; - int FREQCV6_CHAN; + float FREQCV6_CV[3] {}; + int FREQCV6_CHAN {}; - FilterSetting FILTER_SWITCH; - Mod135Setting MOD135_SWITCH; - Mod246Setting MOD246_SWITCH; - bool SCALEROT_SWITCH; - bool PREPOST_SWITCH; - bool GLIDE_SWITCH; - EnvelopeMode ENV_SWITCH; + FilterSetting FILTER_SWITCH {}; + Mod135Setting MOD135_SWITCH {}; + Mod246Setting MOD246_SWITCH {}; + bool SCALEROT_SWITCH {}; + bool PREPOST_SWITCH {}; + bool GLIDE_SWITCH {}; + EnvelopeMode ENV_SWITCH {}; - bool CHANNEL_Q_ON[NUM_CHANNELS] = { false }; - bool LOCK_ON[NUM_CHANNELS] = { false }; - int8_t TRANS_DIAL[NUM_CHANNELS]; + bool CHANNEL_Q_ON[NUM_CHANNELS] = {}; + bool LOCK_ON[NUM_CHANNELS] = {}; + int8_t TRANS_DIAL[NUM_CHANNELS] = {}; // CV Rotate - bool ROTUP_TRIGGER; - bool ROTDOWN_TRIGGER; + bool ROTUP_TRIGGER {}; + bool ROTDOWN_TRIGGER {}; // Button Rotate - bool ROTUP_BUTTON; - bool ROTDOWN_BUTTON; + bool ROTUP_BUTTON {}; + bool ROTDOWN_BUTTON {}; // Button scale bool SCALEUP_BUTTON; bool SCALEDOWN_BUTTON; // Bank select - bool CHANGED_BANK; - uint8_t NEW_BANK; - float USERSCALE96[NUM_BANKNOTES]; - float USERSCALE48[NUM_BANKNOTES]; - bool USERSCALE_CHANGED = false; + bool CHANGED_BANK {}; + uint8_t NEW_BANK {}; + float USERSCALE96[NUM_BANKNOTES] {}; + float USERSCALE48[NUM_BANKNOTES] {}; + bool USERSCALE_CHANGED {}; //FREQ BLOCKS std::bitset<20> FREQ_BLOCK; @@ -390,25 +391,25 @@ struct IO { int32_t out[NUM_CHANNELS][NUM_SAMPLES] = {}; // OUTPUTS - float env_out[NUM_CHANNELS]; - float voct_out[NUM_CHANNELS]; - float OUTLEVEL[NUM_SCALES]; + float env_out[NUM_CHANNELS] {}; + float voct_out[NUM_CHANNELS] {}; + float OUTLEVEL[NUM_SCALES] {}; // LEDS - bool INPUT_CLIP; + bool INPUT_CLIP {}; - float ring[NUM_FILTS][3]; - float scale[NUM_SCALES][3]; + float ring[NUM_FILTS][3] {}; + float scale[NUM_SCALES][3] {}; - float envelope_leds[NUM_CHANNELS][3]; - float q_leds[NUM_CHANNELS][3]; - float tuning_out_leds[NUM_CHANNELS][3]; + float envelope_leds[NUM_CHANNELS][3] {}; + float q_leds[NUM_CHANNELS][3] {}; + float tuning_out_leds[NUM_CHANNELS][3] {}; - float channelLevel[NUM_CHANNELS]; // 0.0 - 1+, 1 = Clipping + float channelLevel[NUM_CHANNELS] {}; // 0.0 - 1+, 1 = Clipping bool FORCE_RING_UPDATE = true; - // float DEBUG[16]; // Not used + // float DEBUG[16]; // Not used }; struct LEDRing { @@ -418,16 +419,16 @@ struct LEDRing { FilterBank* filterbank; Q* q; - const float sqrt2over2 = sqrt(2.0f) / 2.0f; - const float sqrt2 = sqrt(2.0f); - const float maxNudge = 1.0f + 4095.0f / 55000.0f; - const float hslRange = 2.0f/3.0f; + constexpr static float sqrt2over2 = sqrt(2.0f) / 2.0f; + constexpr static float sqrt2 = sqrt(2.0f); + constexpr static float maxNudge = 1.0f + 4095.0f / 55000.0f; + constexpr static float hslRange = 2.0f / 3.0f; // Counters - uint8_t filter_flash_ctr = 0; - uint32_t led_ring_update_ctr = UINT32_MAX; // Initialise to always fire on first pass - uint8_t flash_ctr = 0; - uint8_t elacs_ctr[NUM_SCALES] = {}; + uint8_t filter_flash_ctr = 0; + uint32_t led_ring_update_ctr = UINT32_MAX; // Initialise to always fire on first pass + uint8_t flash_ctr = 0; + uint8_t elacs_ctr[NUM_SCALES] = {}; float channel_led_colors[NUM_CHANNELS][3] = { // New palette by Pyer @@ -465,7 +466,7 @@ struct Inputs { int32_t t_scalecv = 0; int32_t t_old_scalecv = 0; - float lpf_buf; + float lpf_buf = 0.f; FilterSetting oldFilter; @@ -480,19 +481,19 @@ struct Inputs { struct LPF { //Value outputs: - float raw_val; - float lpf_val; - float bracketed_val; + float raw_val = 0.f; + float lpf_val = 0.f; + float bracketed_val = 0.f; //Settings (input) - uint16_t iir_lpf_size; //size of iir average. 0 = disabled. if fir_lpf_size > 0, then iir average is disabled. - uint16_t fir_lpf_size; //size of moving average (number of samples to average). 0 = disabled. - float bracket_size; //size of bracket (ignore changes when old_val-bracket_size < new_val < old_val+bracket_size) - AnalogPolarity polarity; //AP_UNIPOLAR or AP_BIPOLAR + uint16_t iir_lpf_size = 0; //size of iir average. 0 = disabled. if fir_lpf_size > 0, then iir average is disabled. + uint16_t fir_lpf_size = 0; //size of moving average (number of samples to average). 0 = disabled. + float bracket_size = 0.f; //size of bracket (ignore changes when old_val-bracket_size < new_val < old_val+bracket_size) + AnalogPolarity polarity {}; //AP_UNIPOLAR or AP_BIPOLAR //Filter window buffer and index - float fir_lpf[MAX_FIR_LPF_SIZE] = {}; - uint32_t fir_lpf_i = 0; + float fir_lpf[MAX_FIR_LPF_SIZE] = {}; + uint32_t fir_lpf_i = 0; void setup_fir_filter(); void apply_fir_lpf(); @@ -503,22 +504,22 @@ struct Rotation { FilterBank* filterbank; IO* io; - uint16_t rotate_to_next_scale; + uint16_t rotate_to_next_scale = 0; - int8_t motion_fadeto_note[NUM_CHANNELS]; - int8_t motion_fadeto_scale[NUM_CHANNELS]; + int8_t motion_fadeto_note[NUM_CHANNELS] = {}; + int8_t motion_fadeto_scale[NUM_CHANNELS] = {}; - int32_t motion_rotate; - int8_t motion_spread_dest[NUM_CHANNELS]; - int8_t motion_spread_dir[NUM_CHANNELS]; + int32_t motion_rotate = 0; + int8_t motion_spread_dest[NUM_CHANNELS] = {}; + int8_t motion_spread_dir[NUM_CHANNELS] = {}; - int8_t motion_notejump; - int8_t motion_scale_dest[NUM_CHANNELS]; + int8_t motion_notejump = 0; + int8_t motion_scale_dest[NUM_CHANNELS] = {}; int8_t motion_scalecv_overage[NUM_CHANNELS] = {}; float motion_morphpos[NUM_CHANNELS] = {}; - float f_morph = 0.0; + float f_morph = 0.f; int8_t spread = 0; int8_t old_spread = 1; @@ -542,7 +543,7 @@ struct Rotation { void jump_scale_with_cv(int8_t shift_amt); bool is_morphing(void); - bool is_spreading(void); + bool is_spreading(void); // UNUSED; See Rotation L112 void jump_rotate_with_cv(int8_t shift_amt); }; @@ -551,7 +552,7 @@ struct Q { IO* io; //Q POT AND CV - uint32_t qval[NUM_CHANNELS]; + uint32_t qval[NUM_CHANNELS] = {}; float qval_goal[NUM_CHANNELS] = {}; float prev_qval[NUM_CHANNELS] = {}; @@ -574,27 +575,25 @@ struct Tuning { IO* io; //FREQ NUDGE/LOCK JACKS - float freq_nudge[NUM_CHANNELS] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; - float coarse_adj_led[NUM_CHANNELS]; - float coarse_adj[NUM_CHANNELS] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; - float freq_shift[NUM_CHANNELS]; + float freq_nudge[NUM_CHANNELS] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + float coarse_adj_led[NUM_CHANNELS] = {}; + float coarse_adj[NUM_CHANNELS] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + float freq_shift[NUM_CHANNELS] = {}; - float twelveroottwo[25]; + float twelveroottwo[25] = {}; uint32_t tuning_update_ctr = UINT32_MAX; uint32_t TUNING_UPDATE_RATE = 50; float FREQNUDGE_LPF = 0.995f; - uint16_t mod_mode_135; - uint16_t mod_mode_246; + uint16_t mod_mode_135 = 0; + uint16_t mod_mode_246 = 0; - float t_fo; - float t_fe; // buffers for freq nudge knob readouts - float f_nudge_odds = 1; - float f_nudge_evens = 1; + float f_nudge_odds = 1.f; + float f_nudge_evens = 1.f; - LPF freq_jack_conditioning[2]; //LPF and bracketing for freq jacks + LPF freq_jack_conditioning[2] = {}; //LPF and bracketing for freq jacks void configure(IO *_io, FilterBank *_filter); @@ -611,8 +610,8 @@ struct Levels { float CHANNEL_LEVEL_MIN_LPF = 0.75f; float channel_level_lpf = CHANNEL_LEVEL_MIN_LPF; - float global_cv_lpf; - float level_cv_lpf[6]; + float global_cv_lpf = 0.f; + float level_cv_lpf[6] = {}; // Private uint32_t level_update_ctr = UINT32_MAX; // Initialise to always fire on first pass @@ -632,14 +631,14 @@ struct Levels { struct State { bool initialised = false; - uint8_t note[NUM_CHANNELS] = {}; - uint8_t scale[NUM_CHANNELS] = {}; + uint8_t note[NUM_CHANNELS] = {}; + uint8_t scale[NUM_CHANNELS] = {}; uint8_t scale_bank[NUM_CHANNELS] = {}; - float userscale96[NUM_BANKNOTES]; - float userscale48[NUM_BANKNOTES]; + float userscale96[NUM_BANKNOTES] = {}; + float userscale48[NUM_BANKNOTES] = {}; - FilterTypes filter_type; - FilterModes filter_mode; + FilterTypes filter_type = {}; + FilterModes filter_mode = {}; }; } // rainbow From 0236c08e04ad597d7a78bf21fbdfb1d5524b85f4 Mon Sep 17 00:00:00 2001 From: Steve Russell <41975381+SteveRussell33@users.noreply.github.com> Date: Sat, 14 Jun 2025 09:07:49 +0100 Subject: [PATCH 08/20] Update Rainbow.hpp for CI breakage std::clamp is C++17 --- src/Rainbow.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Rainbow.hpp b/src/Rainbow.hpp index 5ff324f..2c01f6b 100644 --- a/src/Rainbow.hpp +++ b/src/Rainbow.hpp @@ -1,7 +1,17 @@ // cppcheck-suppress-file unusedStructMember #pragma once +// Rack CI SDK is C++11 +#if __cplusplus >= 201703L #include +using std::clamp; +#else +template +T clamp(T v, T x, T y) { + return std::max(std::min(v, y), x); +} +#endif + #include #include #include From 85dcffa5e590ab524dd47ed16b335331ad81e363 Mon Sep 17 00:00:00 2001 From: Steve Russell <41975381+SteveRussell33@users.noreply.github.com> Date: Sat, 14 Jun 2025 09:23:25 +0100 Subject: [PATCH 09/20] Update Rainbow.hpp for CI breakage II --- src/Rainbow.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Rainbow.hpp b/src/Rainbow.hpp index 2c01f6b..5df02aa 100644 --- a/src/Rainbow.hpp +++ b/src/Rainbow.hpp @@ -7,8 +7,8 @@ using std::clamp; #else template -T clamp(T v, T x, T y) { - return std::max(std::min(v, y), x); +constexpr const T& clamp(const T& v, const T& x, const T& y) { + return (v < x) ? x : (y < v) ? y : v; } #endif From 8437b9fb55e9baebc6d0fc6024192b07da464b36 Mon Sep 17 00:00:00 2001 From: Steve Russell <41975381+SteveRussell33@users.noreply.github.com> Date: Sat, 14 Jun 2025 09:28:36 +0100 Subject: [PATCH 10/20] Update Rainbow.hpp - add namespace --- src/Rainbow.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Rainbow.hpp b/src/Rainbow.hpp index 5df02aa..92e8b56 100644 --- a/src/Rainbow.hpp +++ b/src/Rainbow.hpp @@ -6,10 +6,11 @@ #include using std::clamp; #else +namespace std { template constexpr const T& clamp(const T& v, const T& x, const T& y) { return (v < x) ? x : (y < v) ? y : v; -} +}} #endif #include From 06c307e795219d33a412bc6356438c1d65c5fb3d Mon Sep 17 00:00:00 2001 From: Steve Russell <41975381+SteveRussell33@users.noreply.github.com> Date: Sat, 14 Jun 2025 09:48:35 +0100 Subject: [PATCH 11/20] Update Rainbow.hpp - `sqrt` not `constexpr` on Mac's --- src/Rainbow.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Rainbow.hpp b/src/Rainbow.hpp index 92e8b56..eb5d41e 100644 --- a/src/Rainbow.hpp +++ b/src/Rainbow.hpp @@ -430,8 +430,13 @@ struct LEDRing { FilterBank* filterbank; Q* q; +#ifdef ARCH_MAC + const static float sqrt2over2 = sqrt(2.0f) / 2.0f; + const static float sqrt = sqrt(2.0f); +#else constexpr static float sqrt2over2 = sqrt(2.0f) / 2.0f; constexpr static float sqrt2 = sqrt(2.0f); +#endif constexpr static float maxNudge = 1.0f + 4095.0f / 55000.0f; constexpr static float hslRange = 2.0f / 3.0f; From 3424dbdb04ba1b5205aa257084c1150f1645d3f0 Mon Sep 17 00:00:00 2001 From: Steve Russell <41975381+SteveRussell33@users.noreply.github.com> Date: Sat, 14 Jun 2025 09:53:30 +0100 Subject: [PATCH 12/20] Update Rainbow.hpp - `sqrt` not `constexpr` on Mac's (typo) --- src/Rainbow.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Rainbow.hpp b/src/Rainbow.hpp index eb5d41e..c513cfb 100644 --- a/src/Rainbow.hpp +++ b/src/Rainbow.hpp @@ -431,8 +431,8 @@ struct LEDRing { Q* q; #ifdef ARCH_MAC - const static float sqrt2over2 = sqrt(2.0f) / 2.0f; - const static float sqrt = sqrt(2.0f); + const float sqrt2over2 = sqrt(2.0f) / 2.0f; + const float sqrt = sqrt(2.0f); #else constexpr static float sqrt2over2 = sqrt(2.0f) / 2.0f; constexpr static float sqrt2 = sqrt(2.0f); From 62db7a86b77023fa0a2be1033b1be8230d03aaa7 Mon Sep 17 00:00:00 2001 From: Steve Russell <41975381+SteveRussell33@users.noreply.github.com> Date: Sat, 14 Jun 2025 10:00:57 +0100 Subject: [PATCH 13/20] Update Rainbow.hpp for CI breakage III --- src/Rainbow.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Rainbow.hpp b/src/Rainbow.hpp index c513cfb..407737e 100644 --- a/src/Rainbow.hpp +++ b/src/Rainbow.hpp @@ -431,8 +431,8 @@ struct LEDRing { Q* q; #ifdef ARCH_MAC - const float sqrt2over2 = sqrt(2.0f) / 2.0f; - const float sqrt = sqrt(2.0f); + float sqrt2over2 = sqrt(2.0f) / 2.0f; + float sqrt = sqrt(2.0f); #else constexpr static float sqrt2over2 = sqrt(2.0f) / 2.0f; constexpr static float sqrt2 = sqrt(2.0f); From 8bada3402146776960284d6bb7b5050099a103cf Mon Sep 17 00:00:00 2001 From: Steve Russell <41975381+SteveRussell33@users.noreply.github.com> Date: Sat, 14 Jun 2025 10:07:20 +0100 Subject: [PATCH 14/20] Update Rainbow.hpp - `sqrt` not `constexpr` on Mac's (typo - again!) --- src/Rainbow.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Rainbow.hpp b/src/Rainbow.hpp index 407737e..840dbd3 100644 --- a/src/Rainbow.hpp +++ b/src/Rainbow.hpp @@ -431,8 +431,8 @@ struct LEDRing { Q* q; #ifdef ARCH_MAC - float sqrt2over2 = sqrt(2.0f) / 2.0f; - float sqrt = sqrt(2.0f); + const float sqrt2over2 = sqrt(2.0f) / 2.0f; + const float sqrt2 = sqrt(2.0f); #else constexpr static float sqrt2over2 = sqrt(2.0f) / 2.0f; constexpr static float sqrt2 = sqrt(2.0f); From 23dfe1f33e07862d447dce5e36d5f266c3a5e3c3 Mon Sep 17 00:00:00 2001 From: Steve Russell Date: Sat, 14 Jun 2025 10:34:23 +0100 Subject: [PATCH 15/20] Rainbow: no need for 2 x sqr.roots --- src/Rainbow.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Rainbow.hpp b/src/Rainbow.hpp index 840dbd3..85d3cbd 100644 --- a/src/Rainbow.hpp +++ b/src/Rainbow.hpp @@ -431,11 +431,11 @@ struct LEDRing { Q* q; #ifdef ARCH_MAC - const float sqrt2over2 = sqrt(2.0f) / 2.0f; - const float sqrt2 = sqrt(2.0f); + const float sqrt2 = sqrt(2.0f); + const float sqrt2over2 = sqrt2 / 2.0f; #else - constexpr static float sqrt2over2 = sqrt(2.0f) / 2.0f; - constexpr static float sqrt2 = sqrt(2.0f); + constexpr static float sqrt2 = sqrt(2.0f); + constexpr static float sqrt2over2 = sqrt2 / 2.0f; #endif constexpr static float maxNudge = 1.0f + 4095.0f / 55000.0f; constexpr static float hslRange = 2.0f / 3.0f; From 3ef769e8ce99db0a2a9a61752d11b8e6ee5c05bb Mon Sep 17 00:00:00 2001 From: Steve Russell <41975381+SteveRussell33@users.noreply.github.com> Date: Sat, 14 Jun 2025 11:03:04 +0100 Subject: [PATCH 16/20] Update Rainbow.hpp - always use `ag` to check what's actually used! --- src/Rainbow.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Rainbow.hpp b/src/Rainbow.hpp index 85d3cbd..fad6ddf 100644 --- a/src/Rainbow.hpp +++ b/src/Rainbow.hpp @@ -430,6 +430,7 @@ struct LEDRing { FilterBank* filterbank; Q* q; +/*** NOT USED #ifdef ARCH_MAC const float sqrt2 = sqrt(2.0f); const float sqrt2over2 = sqrt2 / 2.0f; @@ -438,6 +439,7 @@ struct LEDRing { constexpr static float sqrt2over2 = sqrt2 / 2.0f; #endif constexpr static float maxNudge = 1.0f + 4095.0f / 55000.0f; +***/ constexpr static float hslRange = 2.0f / 3.0f; // Counters From ed554d80c3e790b372bd351f4da7ed930b3da41d Mon Sep 17 00:00:00 2001 From: Steve Russell <41975381+SteveRussell33@users.noreply.github.com> Date: Tue, 17 Jun 2025 08:17:30 +0100 Subject: [PATCH 17/20] Update CHANGELOG.md v2.4.0 --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9140217..e8da9b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,20 @@ # Prism change log +2.4.0 + +Rainbow: +~33% to ~50% Rack CPU load improvement - thanks [@danngreen](https://github.com/danngreen) +* Populate state only when needed +* Added faster brown noise gen +* Use std::clamp +* Some type changes + +Audio: Optimized output summing + +Small fixes for Spread and Morph + +CI builds: Added ``#include `` conditionally to Rainbow.hpp to fix CI builds (C++11) +* Local dev builds will use std::clamp (C++17) + 2.3.4 * Rainbow: Fix intermittent wayward output levels in audio processor (due to Audio module init bug) * Rainbow: Fix CPU mode menu From e2fb7db3dcb44b92f3122c45d096f2702536e1fa Mon Sep 17 00:00:00 2001 From: Steve Russell <41975381+SteveRussell33@users.noreply.github.com> Date: Tue, 17 Jun 2025 08:20:40 +0100 Subject: [PATCH 18/20] Update Makefile Update to C++17 --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6c6f4cc..5bf270e 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # FLAGS will be passed to both the C and C++ compiler FLAGS += -flto CFLAGS += -CXXFLAGS += +CXXFLAGS += -std=c++17 # Careful about linking to shared libraries, since you can't assume much about the user's environment and library search path. # Static libraries are fine. @@ -19,3 +19,5 @@ RACK_DIR ?= ../.. # Include the VCV Rack plugin Makefile framework include $(RACK_DIR)/plugin.mk + +CXXFLAGS := $(filter-out -std=c++11,$(CXXFLAGS)) From 498cfb915dd2b632865b52691a1e2d9b5d871350 Mon Sep 17 00:00:00 2001 From: Steve Russell <41975381+SteveRussell33@users.noreply.github.com> Date: Tue, 17 Jun 2025 08:45:53 +0100 Subject: [PATCH 19/20] Update Rainbow.hpp - test C++17 CI --- src/Rainbow.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Rainbow.hpp b/src/Rainbow.hpp index fad6ddf..56e016a 100644 --- a/src/Rainbow.hpp +++ b/src/Rainbow.hpp @@ -2,7 +2,7 @@ #pragma once // Rack CI SDK is C++11 -#if __cplusplus >= 201703L +/*#if __cplusplus >= 201703L #include using std::clamp; #else @@ -11,7 +11,7 @@ template constexpr const T& clamp(const T& v, const T& x, const T& y) { return (v < x) ? x : (y < v) ? y : v; }} -#endif +#endif*/ #include #include From 7ff1c0b4380770da83d949264171733f07b6be54 Mon Sep 17 00:00:00 2001 From: Steve Russell <41975381+SteveRussell33@users.noreply.github.com> Date: Tue, 17 Jun 2025 08:50:39 +0100 Subject: [PATCH 20/20] Update CHANGELOG.md --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8da9b0..ef4cfd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,7 @@ Audio: Optimized output summing Small fixes for Spread and Morph -CI builds: Added ``#include `` conditionally to Rainbow.hpp to fix CI builds (C++11) -* Local dev builds will use std::clamp (C++17) +CI builds: use C++17 for ``std::clamp`` 2.3.4 * Rainbow: Fix intermittent wayward output levels in audio processor (due to Audio module init bug)