From 12ae07d11532391922a3634bba449eb450f697f9 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 6 Nov 2025 11:05:54 +0100 Subject: [PATCH] add license headers --- clip/clip.h | 65 +++++++++++++--- clip/fold.h | 23 ++++++ clip/tube.h | 59 +++++++++++--- companding/alaw.h | 28 ++++++- dynamics/oneknob.h | 32 +++++++- dynamics/pump.h | 27 ++++++- dynamics/rms_detector.h | 24 ++++++ filter/chebyshev.h | 26 ++++++- filter/spliteq.h | 55 ++++++++++--- filter/ysvf.h | 44 ++++++++++- gfx/dice.h | 31 +++++++- oversampling/oversampler.h | 27 ++++++- sequencer/combine_seq.h | 23 ++++++ sequencer/simple_seq.h | 23 ++++++ synth/triplex.h | 156 ++++++++++++++++++++++++++----------- synth/voice_allocator.h | 40 ++++++++-- util/audio_buffer.h | 27 ++++++- util/audio_math.h | 36 ++++++++- util/demo_noise.h | 23 ++++++ util/format.h | 27 ++++++- util/retro_buf.h | 23 ++++++ util/smoother.h | 28 ++++++- 22 files changed, 739 insertions(+), 108 deletions(-) diff --git a/clip/clip.h b/clip/clip.h index c6383e3..c61f69d 100644 --- a/clip/clip.h +++ b/clip/clip.h @@ -1,9 +1,40 @@ +/* + * clip.h + * Copyright (c) 2016 Chris Johnson + * Copyright (c) 2025 Christopher Herb + * Based on ClipOnly2 by Chris Johnson, 2016 + * This file is a derivative of the above module. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Changes: + * - 2025-11-06 Christopher Herb: + * - Templated audio buffer i/o + * - Converted to procedural programming style + */ + #pragma once + #include #include namespace trnr { -// clipper based on ClipOnly2 by Chris Johnson (MIT License) struct clip { double samplerate; @@ -36,7 +67,8 @@ inline void clip_init(clip& c, double _samplerate) } template -inline void clip_process_block(clip& c, t_sample** inputs, t_sample** outputs, long sample_frames) +inline void clip_process_block(clip& c, t_sample** inputs, t_sample** outputs, + long sample_frames) { t_sample* in1 = inputs[0]; t_sample* in2 = inputs[1]; @@ -47,7 +79,8 @@ inline void clip_process_block(clip& c, t_sample** inputs, t_sample** outputs, l overallscale /= 44100.0; overallscale *= c.samplerate; - int spacing = floor(overallscale); // should give us working basic scaling, usually 2 or 4 + int spacing = + floor(overallscale); // should give us working basic scaling, usually 2 or 4 if (spacing < 1) spacing = 1; if (spacing > 16) spacing = 16; @@ -55,11 +88,13 @@ inline void clip_process_block(clip& c, t_sample** inputs, t_sample** outputs, l double input_l = *in1; double input_r = *in2; - // begin ClipOnly2 stereo as a little, compressed chunk that can be dropped into code + // begin ClipOnly2 stereo as a little, compressed chunk that can be dropped into + // code if (input_l > 4.0) input_l = 4.0; if (input_l < -4.0) input_l = -4.0; if (c.was_pos_clip_l == true) { // current will be over - if (input_l < c.last_sample_l) c.last_sample_l = 0.7058208 + (input_l * 0.2609148); + if (input_l < c.last_sample_l) + c.last_sample_l = 0.7058208 + (input_l * 0.2609148); else c.last_sample_l = 0.2491717 + (c.last_sample_l * 0.7390851); } c.was_pos_clip_l = false; @@ -68,7 +103,8 @@ inline void clip_process_block(clip& c, t_sample** inputs, t_sample** outputs, l input_l = 0.7058208 + (c.last_sample_l * 0.2609148); } if (c.was_neg_clip_l == true) { // current will be -over - if (input_l > c.last_sample_l) c.last_sample_l = -0.7058208 + (input_l * 0.2609148); + if (input_l > c.last_sample_l) + c.last_sample_l = -0.7058208 + (input_l * 0.2609148); else c.last_sample_l = -0.2491717 + (c.last_sample_l * 0.7390851); } c.was_neg_clip_l = false; @@ -77,14 +113,16 @@ inline void clip_process_block(clip& c, t_sample** inputs, t_sample** outputs, l input_l = -0.7058208 + (c.last_sample_l * 0.2609148); } c.intermediate_l[spacing] = input_l; - input_l = c.last_sample_l; // Latency is however many samples equals one 44.1k sample + input_l = + c.last_sample_l; // Latency is however many samples equals one 44.1k sample for (int x = spacing; x > 0; x--) c.intermediate_l[x - 1] = c.intermediate_l[x]; c.last_sample_l = c.intermediate_l[0]; // run a little buffer to handle this if (input_r > 4.0) input_r = 4.0; if (input_r < -4.0) input_r = -4.0; if (c.was_pos_clip_r == true) { // current will be over - if (input_r < c.last_sample_r) c.last_sample_r = 0.7058208 + (input_r * 0.2609148); + if (input_r < c.last_sample_r) + c.last_sample_r = 0.7058208 + (input_r * 0.2609148); else c.last_sample_r = 0.2491717 + (c.last_sample_r * 0.7390851); } c.was_pos_clip_r = false; @@ -93,7 +131,8 @@ inline void clip_process_block(clip& c, t_sample** inputs, t_sample** outputs, l input_r = 0.7058208 + (c.last_sample_r * 0.2609148); } if (c.was_neg_clip_r == true) { // current will be -over - if (input_r > c.last_sample_r) c.last_sample_r = -0.7058208 + (input_r * 0.2609148); + if (input_r > c.last_sample_r) + c.last_sample_r = -0.7058208 + (input_r * 0.2609148); else c.last_sample_r = -0.2491717 + (c.last_sample_r * 0.7390851); } c.was_neg_clip_r = false; @@ -102,10 +141,12 @@ inline void clip_process_block(clip& c, t_sample** inputs, t_sample** outputs, l input_r = -0.7058208 + (c.last_sample_r * 0.2609148); } c.intermediate_r[spacing] = input_r; - input_r = c.last_sample_r; // Latency is however many samples equals one 44.1k sample + input_r = + c.last_sample_r; // Latency is however many samples equals one 44.1k sample for (int x = spacing; x > 0; x--) c.intermediate_r[x - 1] = c.intermediate_r[x]; c.last_sample_r = c.intermediate_r[0]; // run a little buffer to handle this - // end ClipOnly2 stereo as a little, compressed chunk that can be dropped into code + // end ClipOnly2 stereo as a little, compressed chunk that can be dropped into + // code *out1 = input_l; *out2 = input_r; @@ -116,4 +157,4 @@ inline void clip_process_block(clip& c, t_sample** inputs, t_sample** outputs, l out2++; } } -} // namespace trnr \ No newline at end of file +} // namespace trnr diff --git a/clip/fold.h b/clip/fold.h index 3b7c46b..2e0ebd9 100644 --- a/clip/fold.h +++ b/clip/fold.h @@ -1,3 +1,26 @@ +/* + * fold.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once namespace trnr { diff --git a/clip/tube.h b/clip/tube.h index 6ad67ae..506a49d 100644 --- a/clip/tube.h +++ b/clip/tube.h @@ -1,11 +1,45 @@ +/* + * tube.h + * Copyright (c) 2016 Chris Johnson + * Copyright (c) 2025 Christopher Herb + * Based on Tube2 by Chris Johnson, 2016 + * This file is a derivative/major refactor of the above module. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Changes: + * - 2025-11-06 Christopher Herb: + * - Templated audio buffer i/o + * - Converted to procedural programming style + */ + #pragma once + #include #include #include #include +using namespace std; + namespace trnr { -// modeled tube preamp based on tube2 by Chris Johnson (MIT License) + struct tube { double samplerate; @@ -22,9 +56,9 @@ struct tube { float input_vol; float tube_amt; - void set_input(double value) { input_vol = std::clamp(value, 0.0, 1.0); } + void set_input(double value) { input_vol = clamp(value, 0.0, 1.0); } - void set_tube(double value) { tube_amt = std::clamp(value, 0.0, 1.0); } + void set_tube(double value) { tube_amt = clamp(value, 0.0, 1.0); } }; inline void tube_init(tube& t, double samplerate) @@ -46,7 +80,8 @@ inline void tube_init(tube& t, double samplerate) } template -inline void tube_process_block(tube& t, t_sample** inputs, t_sample** outputs, long sampleframes) +inline void tube_process_block(tube& t, t_sample** inputs, t_sample** outputs, + long sampleframes) { t_sample* in1 = inputs[0]; t_sample* in2 = inputs[1]; @@ -114,13 +149,15 @@ inline void tube_process_block(tube& t, t_sample** inputs, t_sample** outputs, l // original Tube algorithm: powerfactor widens the more linear region of the wave double factor = input_l; // Left channel for (int x = 0; x < powerfactor; x++) factor *= input_l; - if ((powerfactor % 2 == 1) && (input_l != 0.0)) factor = (factor / input_l) * fabs(input_l); + if ((powerfactor % 2 == 1) && (input_l != 0.0)) + factor = (factor / input_l) * fabs(input_l); factor *= gainscaling; input_l -= factor; input_l *= outputscaling; factor = input_r; // Right channel for (int x = 0; x < powerfactor; x++) factor *= input_r; - if ((powerfactor % 2 == 1) && (input_r != 0.0)) factor = (factor / input_r) * fabs(input_r); + if ((powerfactor % 2 == 1) && (input_r != 0.0)) + factor = (factor / input_r) * fabs(input_r); factor *= gainscaling; input_r -= factor; input_r *= outputscaling; @@ -174,13 +211,13 @@ inline void tube_process_block(tube& t, t_sample** inputs, t_sample** outputs, l t.fdp_l ^= t.fdp_l << 13; t.fdp_l ^= t.fdp_l >> 17; t.fdp_l ^= t.fdp_l << 5; - // inputSampleL += ((double(fpdL)-uint32_t(0x7fffffff)) * 1.1e-44l * pow(2,expon+62)); - // frexp((double)inputSampleR, &expon); + // inputSampleL += ((double(fpdL)-uint32_t(0x7fffffff)) * 1.1e-44l * + // pow(2,expon+62)); frexp((double)inputSampleR, &expon); t.fdp_r ^= t.fdp_r << 13; t.fdp_r ^= t.fdp_r >> 17; t.fdp_r ^= t.fdp_r << 5; - // inputSampleR += ((double(fpdR)-uint32_t(0x7fffffff)) * 1.1e-44l * pow(2,expon+62)); - // end 64 bit stereo floating point dither + // inputSampleR += ((double(fpdR)-uint32_t(0x7fffffff)) * 1.1e-44l * + // pow(2,expon+62)); end 64 bit stereo floating point dither *out1 = input_l; *out2 = input_r; @@ -191,4 +228,4 @@ inline void tube_process_block(tube& t, t_sample** inputs, t_sample** outputs, l out2++; } } -} // namespace trnr \ No newline at end of file +} // namespace trnr diff --git a/companding/alaw.h b/companding/alaw.h index d1cd9fa..0243b9f 100644 --- a/companding/alaw.h +++ b/companding/alaw.h @@ -1,3 +1,26 @@ +/* + * fold.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once #include @@ -15,7 +38,8 @@ inline float alaw_encode(float input) if (abs_sample < (1.0f / A_LAW_A)) { output = sign * (A_LAW_A * abs_sample) / (1.0f + std::log(A_LAW_A)); } else { - output = sign * (1.0f + std::log(A_LAW_A * abs_sample)) / (1.0f + std::log(A_LAW_A)); + output = + sign * (1.0f + std::log(A_LAW_A * abs_sample)) / (1.0f + std::log(A_LAW_A)); } return output; @@ -35,4 +59,4 @@ inline float alaw_decode(float input) return sample; } -} // namespace trnr \ No newline at end of file +} // namespace trnr diff --git a/dynamics/oneknob.h b/dynamics/oneknob.h index b16c169..ea8c376 100644 --- a/dynamics/oneknob.h +++ b/dynamics/oneknob.h @@ -1,5 +1,29 @@ +/* + * oneknob.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once -#include "audio_math.h" + +#include "../util/audio_math.h" #include "rms_detector.h" #include #include @@ -80,11 +104,13 @@ inline void oneknob_process_block(oneknob_comp& c, sample** audio, int frames) // attack if (envelope_in > c.envelope_level) { - c.envelope_level = envelope_in + c.attack_coef * (c.envelope_level - envelope_in); + c.envelope_level = + envelope_in + c.attack_coef * (c.envelope_level - envelope_in); } // release else { - c.envelope_level = envelope_in + c.release_coef * (c.envelope_level - envelope_in); + c.envelope_level = + envelope_in + c.release_coef * (c.envelope_level - envelope_in); } float x = c.envelope_level; diff --git a/dynamics/pump.h b/dynamics/pump.h index b44b435..3b06159 100644 --- a/dynamics/pump.h +++ b/dynamics/pump.h @@ -1,6 +1,29 @@ +/* + * pump.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once -#include "audio_math.h" +#include "../util/audio_math.h" #include namespace trnr { @@ -149,4 +172,4 @@ inline void pump_process_block(pump& p, sample** audio, sample** sidechain, int audio[1][i] = input_r * gain_reduction_lin * makeup_lin; } } -} // namespace trnr \ No newline at end of file +} // namespace trnr diff --git a/dynamics/rms_detector.h b/dynamics/rms_detector.h index 3b4023a..7cb57b7 100644 --- a/dynamics/rms_detector.h +++ b/dynamics/rms_detector.h @@ -1,4 +1,28 @@ +/* + * rms_detector.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once + #include namespace trnr { diff --git a/filter/chebyshev.h b/filter/chebyshev.h index 4986ebb..d0514cf 100644 --- a/filter/chebyshev.h +++ b/filter/chebyshev.h @@ -1,4 +1,28 @@ +/* + * chebyshev.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once + #define _USE_MATH_DEFINES #include #include @@ -104,4 +128,4 @@ private: double state3 = 0; double passband_ripple = 1; }; -} // namespace trnr \ No newline at end of file +} // namespace trnr diff --git a/filter/spliteq.h b/filter/spliteq.h index 5ef0ac0..2b2792e 100644 --- a/filter/spliteq.h +++ b/filter/spliteq.h @@ -1,6 +1,30 @@ +/* + * spliteq.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once -#include "audio_math.h" -#include "smoother.h" + +#include "../util/audio_math.h" +#include "../util/smoother.h" #include #include @@ -21,7 +45,8 @@ struct cascade_filter { std::vector state; // State per stage }; -inline void cascade_filter_setup(cascade_filter& f, filter_type _type, int _stages, double _cutoff, double _samplerate) +inline void cascade_filter_setup(cascade_filter& f, filter_type _type, int _stages, + double _cutoff, double _samplerate) { f.type = _type; f.stages = _stages; @@ -119,7 +144,8 @@ struct aw_filter { double samplerate; }; -inline void aw_filter_init(aw_filter& f, filter_type type, float amount, double samplerate) +inline void aw_filter_init(aw_filter& f, filter_type type, float amount, + double samplerate) { f.type = type; f.amount = amount; @@ -269,7 +295,8 @@ struct spliteq { smoother transition_smoother; }; -inline void spliteq_init(spliteq& eq, double samplerate, double low_mid_crossover, double mid_high_crossover) +inline void spliteq_init(spliteq& eq, double samplerate, double low_mid_crossover, + double mid_high_crossover) { low_mid_crossover /= 2.0; mid_high_crossover /= 2.0; @@ -495,8 +522,9 @@ inline void spliteq_process_block(spliteq& eq, float** audio, int frames) aw_filter_process_block(eq.lp_r, audio[1], frames); } -inline void spliteq_update(spliteq& eq, double hp_freq, double lp_freq, double low_mid_crossover, - double mid_high_crossover, double bass_gain, double mid_gain, double treble_gain) +inline void spliteq_update(spliteq& eq, double hp_freq, double lp_freq, + double low_mid_crossover, double mid_high_crossover, + double bass_gain, double mid_gain, double treble_gain) { low_mid_crossover /= 2.0; mid_high_crossover /= 2.0; @@ -534,8 +562,10 @@ inline void spliteq_update(spliteq& eq, double hp_freq, double lp_freq, double l cascade_filter_setup(eq.bass_l, LOWPASS, 2, eq.low_mid_crossover_adj, eq.samplerate); cascade_filter_setup(eq.bass_r, LOWPASS, 2, eq.low_mid_crossover_adj, eq.samplerate); - cascade_filter_setup(eq.treble_l, HIGHPASS, 2, eq.mid_high_crossover_adj, eq.samplerate); - cascade_filter_setup(eq.treble_r, HIGHPASS, 2, eq.mid_high_crossover_adj, eq.samplerate); + cascade_filter_setup(eq.treble_l, HIGHPASS, 2, eq.mid_high_crossover_adj, + eq.samplerate); + cascade_filter_setup(eq.treble_r, HIGHPASS, 2, eq.mid_high_crossover_adj, + eq.samplerate); eq.bass1_l.cutoff = low_mid_crossover; butterworth_biquad_coeffs(eq.bass1_l, eq.samplerate); @@ -574,9 +604,10 @@ inline void spliteq_update(spliteq& eq, double hp_freq, double lp_freq, double l butterworth_biquad_coeffs(eq.treble2_r, eq.samplerate); } -inline void spliteq_update(spliteq& eq, double bass_gain, double mid_gain, double treble_gain) +inline void spliteq_update(spliteq& eq, double bass_gain, double mid_gain, + double treble_gain) { - trnr::spliteq_update(eq, eq.hp_l.amount, eq.lp_l.amount, eq.low_mid_crossover * 2.0, eq.mid_high_crossover * 2.0, - bass_gain, mid_gain, treble_gain); + trnr::spliteq_update(eq, eq.hp_l.amount, eq.lp_l.amount, eq.low_mid_crossover * 2.0, + eq.mid_high_crossover * 2.0, bass_gain, mid_gain, treble_gain); } } // namespace trnr diff --git a/filter/ysvf.h b/filter/ysvf.h index 71327c2..7168378 100644 --- a/filter/ysvf.h +++ b/filter/ysvf.h @@ -1,3 +1,39 @@ +/* + * ysfv.h + * Copyright (c) 2016 Chris Johnson + * Copyright (c) 2025 Christopher Herb + * Based on work: + * - YLowpass by Chris Johnson, 2016 + * - YHighpass by Chris Johnson, 2016 + * - YBandpass by Chris Johnson, 2016 + * - YNotch by Chris Johnson, 2016 + * This file is a derivative/major refactor consolidating the above modules. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Changes: + * - 2025-11-06 Christopher Herb: + * - Consolidated YLowpass, YHighpass, YBandpass, YNotch into one (state-variable) filter + * - Templated audio buffer i/o + * - Converted to procedural programming style + */ + #pragma once #define _USE_MATH_DEFINES @@ -36,7 +72,7 @@ enum { Y_BIQ_S_R1, Y_BIQ_S_R2, Y_BIQ_TOTAL -}; // coefnncient interpolating biquad filter, stereo +}; enum { Y_FIX_FREQ, @@ -51,7 +87,7 @@ enum { Y_FIX_S_R1, Y_FIX_S_R2, Y_FIX_TOTAL -}; // fixed frequency biquad filter for ultrasonics, stereo +}; ///////////// // LOWPASS // @@ -1104,8 +1140,8 @@ inline void ysvf_set_param(ysvf& y, ysvf_parameters param, float value) } template -inline void y_process_samples(ysvf& y, t_sample** inputs, t_sample** outputs, - int block_size) +inline void ysvf_process_samples(ysvf& y, t_sample** inputs, t_sample** outputs, + int block_size) { switch (y.filter_type) { case Y_LOWPASS: diff --git a/gfx/dice.h b/gfx/dice.h index f649866..1799bf0 100644 --- a/gfx/dice.h +++ b/gfx/dice.h @@ -1,4 +1,28 @@ +/* + * dice.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once + #include using namespace std; @@ -28,6 +52,7 @@ struct dice { array segments_left; }; +// caclulates coordinates for an isometric dice control with bar segments inline void dice_init(dice& d, float width, float height) { const float shortening = 0.866f; @@ -66,7 +91,8 @@ inline void dice_init(dice& d, float width, float height) // calculate segments of the left face for (int i = 0; i < d.segments_left.size(); i++) { - const point base_p1 = point {mid_x - face_width + pad_x, face_half_height + pad_y}; + const point base_p1 = + point {mid_x - face_width + pad_x, face_half_height + pad_y}; const point base_p2 = point {mid_x - pad_x, face_height}; float seg_y = i * (segment_height + gap_height); point p1 = {base_p1.x, base_p1.y + seg_y}; @@ -80,7 +106,8 @@ inline void dice_init(dice& d, float width, float height) // correct center of the diamond face // the diamond spans from b_p1 (top) to the middle of the left face float diamond_center_x = mid_x; - float diamond_center_y = face_half_height / 2.0f + face_half_height / 2.0f; // move down to actual center + float diamond_center_y = + face_half_height / 2.0f + face_half_height / 2.0f; // move down to actual center // for 30-degree isometric, the grid directions are: float cos30 = 1.f; // cos(30°) = √3/2 ≈ 0.866 diff --git a/oversampling/oversampler.h b/oversampling/oversampler.h index dcc3b90..b938477 100644 --- a/oversampling/oversampler.h +++ b/oversampling/oversampler.h @@ -1,3 +1,26 @@ +/* + * oversampler.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once #include @@ -7,7 +30,7 @@ namespace trnr { -template +template class oversampler { public: oversampler() @@ -105,4 +128,4 @@ private: std::array, 2> buffer; sample** ptrs = new sample*[2]; }; -} // namespace trnr \ No newline at end of file +} // namespace trnr diff --git a/sequencer/combine_seq.h b/sequencer/combine_seq.h index 339ec81..1399244 100644 --- a/sequencer/combine_seq.h +++ b/sequencer/combine_seq.h @@ -1,3 +1,26 @@ +/* + * combine_seq.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once #include "simple_seq.h" diff --git a/sequencer/simple_seq.h b/sequencer/simple_seq.h index 2b51a01..2ae304e 100644 --- a/sequencer/simple_seq.h +++ b/sequencer/simple_seq.h @@ -1,3 +1,26 @@ +/* + * simple_seq.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once #include diff --git a/synth/triplex.h b/synth/triplex.h index c673e10..7caef96 100644 --- a/synth/triplex.h +++ b/synth/triplex.h @@ -1,6 +1,30 @@ +/* + * triplex.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once -#include "audio_buffer.h" -#include "audio_math.h" + +#include "../util/audio_buffer.h" +#include "../util/audio_math.h" #include "voice_allocator.h" #include #include @@ -45,7 +69,8 @@ inline float tx_wrap(float& phase) return phase; } -inline float tx_sineosc_process_sample(tx_sineosc& s, bool trigger, float frequency, float phase_modulation = 0.f) +inline float tx_sineosc_process_sample(tx_sineosc& s, bool trigger, float frequency, + float phase_modulation = 0.f) { if (trigger) { if (s.phase_reset) s.phase = 0.f; @@ -133,15 +158,18 @@ inline void tx_envelope_init(tx_envelope& e, double samplerate, bool retrigger = e.retrigger = retrigger; } -inline size_t tx_mtos(float ms, double samplerate) { return static_cast(ms * samplerate / 1000.f); } +inline size_t tx_mtos(float ms, double samplerate) +{ + return static_cast(ms * samplerate / 1000.f); +} inline float tx_lerp(float x1, float y1, float x2, float y2, float x) { return y1 + (((x - x1) * (y2 - y1)) / (x2 - x1)); } -inline float tx_envelope_process_sample(tx_envelope& e, bool gate, bool trigger, float _attack_mod = 0, - float _decay_mod = 0) +inline float tx_envelope_process_sample(tx_envelope& e, bool gate, bool trigger, + float _attack_mod = 0, float _decay_mod = 0) { size_t attack_mid_x1 = tx_mtos(e.attack1_rate + (float)_attack_mod, e.samplerate); size_t attack_mid_x2 = tx_mtos(e.attack2_rate + (float)_attack_mod, e.samplerate); @@ -162,7 +190,8 @@ inline float tx_envelope_process_sample(tx_envelope& e, bool gate, bool trigger, if (e.state == attack1) { // while in attack phase if (e.phase < attack_mid_x1) { - e.level = tx_lerp(0, e.start_level, (float)attack_mid_x1, e.attack1_level, (float)e.phase); + e.level = tx_lerp(0, e.start_level, (float)attack_mid_x1, e.attack1_level, + (float)e.phase); e.phase += 1; } // reset phase if parameter was changed @@ -177,7 +206,8 @@ inline float tx_envelope_process_sample(tx_envelope& e, bool gate, bool trigger, if (e.state == attack2) { // while in attack phase if (e.phase < attack_mid_x2) { - e.level = tx_lerp(0, e.attack1_level, (float)attack_mid_x2, 1, (float)e.phase); + e.level = + tx_lerp(0, e.attack1_level, (float)attack_mid_x2, 1, (float)e.phase); e.phase += 1; } // reset phase if parameter was changed @@ -219,7 +249,8 @@ inline float tx_envelope_process_sample(tx_envelope& e, bool gate, bool trigger, if (e.state == decay2) { // while in decay phase if (e.phase < decay_mid_x2) { - e.level = tx_lerp(0, e.decay1_level, (float)decay_mid_x2, e.sustain_level, (float)e.phase); + e.level = tx_lerp(0, e.decay1_level, (float)decay_mid_x2, e.sustain_level, + (float)e.phase); e.phase += 1; } // reset phase if parameter was changed @@ -240,7 +271,8 @@ inline float tx_envelope_process_sample(tx_envelope& e, bool gate, bool trigger, if (e.state == release1) { // while in release phase if (e.phase < release_mid_x1) { - e.level = tx_lerp(0, e.sustain_level, (float)release_mid_x1, e.release1_level, (float)e.phase); + e.level = tx_lerp(0, e.sustain_level, (float)release_mid_x1, e.release1_level, + (float)e.phase); e.phase += 1; } // reset phase if parameter was changed @@ -255,7 +287,8 @@ inline float tx_envelope_process_sample(tx_envelope& e, bool gate, bool trigger, if (e.state == release2) { // while in release phase if (e.phase < release_mid_x2) { - e.level = tx_lerp(0, e.release1_level, (float)release_mid_x2, 0, (float)e.phase); + e.level = + tx_lerp(0, e.release1_level, (float)release_mid_x2, 0, (float)e.phase); e.phase += 1; } // reset phase if parameter was changed @@ -293,8 +326,8 @@ inline void tx_operator_init(tx_operator& op, double samplerate) tx_sineosc_init(op.oscillator, samplerate); } -inline float tx_operator_process_sample(tx_operator& op, bool gate, bool trigger, float frequency, float velocity, - float pm = 0.f) +inline float tx_operator_process_sample(tx_operator& op, bool gate, bool trigger, + float frequency, float velocity, float pm = 0.f) { float env = tx_envelope_process_sample(op.envelope, gate, trigger); @@ -336,16 +369,19 @@ inline void tx_voice_init(tx_state& s, double samplerate) tx_operator_init(s.op3, samplerate); } -inline void tx_voice_process_block(tx_state& t, voice_state& s, float** audio, size_t num_frames, +inline void tx_voice_process_block(tx_state& t, voice_state& s, float** audio, + size_t num_frames, const vector>& mods = {}) { - float frequency = midi_to_frequency(s.midi_note + t.pitch_mod + t.additional_pitch_mod); + float frequency = + midi_to_frequency(s.midi_note + t.pitch_mod + t.additional_pitch_mod); for (int i = 0; i < num_frames; i++) { voice_process_event_for_frame(s, i); - float pitch_env_signal = tx_envelope_process_sample(t.pitch_env, s.gate, s.trigger) * t.pitch_env_amt; + float pitch_env_signal = + tx_envelope_process_sample(t.pitch_env, s.gate, s.trigger) * t.pitch_env_amt; float pitched_freq = frequency + pitch_env_signal; float output = 0.f; @@ -354,77 +390,102 @@ inline void tx_voice_process_block(tx_state& t, voice_state& s, float** audio, s if (t.algorithm == 0) { float fb_freq = frequency * t.op3.ratio; float fb_mod_index = (t.feedback_amt * MOD_INDEX_COEFF); - float fb_signal = tx_sineosc_process_sample(t.feedback_osc, s.trigger, fb_freq) * fb_mod_index; + float fb_signal = + tx_sineosc_process_sample(t.feedback_osc, s.trigger, fb_freq) * + fb_mod_index; float op3_Freq = frequency * t.op3.ratio; float op3_mod_index = (t.op3.amplitude * MOD_INDEX_COEFF); float op3_signal = - tx_operator_process_sample(t.op3, s.gate, s.trigger, op3_Freq, s.velocity, fb_signal) * op3_mod_index; + tx_operator_process_sample(t.op3, s.gate, s.trigger, op3_Freq, s.velocity, + fb_signal) * + op3_mod_index; float op2_freq = frequency * t.op2.ratio; float op2_mod_index = (t.op2.amplitude * MOD_INDEX_COEFF); float op2_signal = - tx_operator_process_sample(t.op2, s.gate, s.trigger, op2_freq, s.velocity, op3_signal) * op2_mod_index; + tx_operator_process_sample(t.op2, s.gate, s.trigger, op2_freq, s.velocity, + op3_signal) * + op2_mod_index; float op1_freq = frequency * t.op1.ratio; - output = tx_operator_process_sample(t.op1, s.gate, s.trigger, op1_freq, s.velocity, op2_signal) * + output = tx_operator_process_sample(t.op1, s.gate, s.trigger, op1_freq, + s.velocity, op2_signal) * t.op1.amplitude; } else if (t.algorithm == 1) { float fb_freq = frequency * t.op3.ratio; float fb_mod_index = (t.feedback_amt * MOD_INDEX_COEFF); - float fb_signal = tx_sineosc_process_sample(t.feedback_osc, s.trigger, fb_freq) * fb_mod_index; + float fb_signal = + tx_sineosc_process_sample(t.feedback_osc, s.trigger, fb_freq) * + fb_mod_index; float op3_freq = frequency * t.op3.ratio; float op3_signal = - tx_operator_process_sample(t.op3, s.gate, s.trigger, op3_freq, s.velocity, fb_signal) * t.op3.amplitude; + tx_operator_process_sample(t.op3, s.gate, s.trigger, op3_freq, s.velocity, + fb_signal) * + t.op3.amplitude; float op2_freq = frequency * t.op2.ratio; float op2_mod_index = (t.op2.amplitude * MOD_INDEX_COEFF); - float op2_signal = - tx_operator_process_sample(t.op2, s.gate, s.trigger, op2_freq, s.velocity) * op2_mod_index; + float op2_signal = tx_operator_process_sample(t.op2, s.gate, s.trigger, + op2_freq, s.velocity) * + op2_mod_index; float op1_freq = frequency * t.op1.ratio; - float op1_signal = tx_operator_process_sample(t.op1, s.gate, s.trigger, op1_freq, s.velocity, op2_signal) * - t.op1.amplitude; + float op1_signal = + tx_operator_process_sample(t.op1, s.gate, s.trigger, op1_freq, s.velocity, + op2_signal) * + t.op1.amplitude; output = op1_signal + op3_signal; } else if (t.algorithm == 2) { float fb_freq = frequency * t.op3.ratio; float fb_mod_index = (t.feedback_amt * MOD_INDEX_COEFF); - float fb_signal = tx_sineosc_process_sample(t.feedback_osc, s.trigger, fb_freq) * fb_mod_index; + float fb_signal = + tx_sineosc_process_sample(t.feedback_osc, s.trigger, fb_freq) * + fb_mod_index; float op3_freq = frequency * t.op3.ratio; float op3_signal = - tx_operator_process_sample(t.op3, s.gate, s.trigger, op3_freq, s.velocity, fb_signal) * t.op3.amplitude; + tx_operator_process_sample(t.op3, s.gate, s.trigger, op3_freq, s.velocity, + fb_signal) * + t.op3.amplitude; float op2_freq = frequency * t.op2.ratio; - float op2_signal = - tx_operator_process_sample(t.op2, s.gate, s.trigger, op2_freq, s.velocity) * t.op2.amplitude; + float op2_signal = tx_operator_process_sample(t.op2, s.gate, s.trigger, + op2_freq, s.velocity) * + t.op2.amplitude; float op1_freq = frequency * t.op1.ratio; - float op1_signal = - tx_operator_process_sample(t.op1, s.gate, s.trigger, op1_freq, s.velocity) * t.op1.amplitude; + float op1_signal = tx_operator_process_sample(t.op1, s.gate, s.trigger, + op1_freq, s.velocity) * + t.op1.amplitude; output = op1_signal + op2_signal + op3_signal; } else if (t.algorithm == 3) { float fb_freq = frequency * t.op3.ratio; float fb_mod_index = (t.feedback_amt * MOD_INDEX_COEFF); - float fb_signal = tx_sineosc_process_sample(t.feedback_osc, s.trigger, fb_freq) * fb_mod_index; + float fb_signal = + tx_sineosc_process_sample(t.feedback_osc, s.trigger, fb_freq) * + fb_mod_index; float op3_freq = frequency * t.op3.ratio; float op3_mod_index = (t.op3.amplitude * MOD_INDEX_COEFF); float op3_signal = - tx_operator_process_sample(t.op3, s.gate, s.trigger, op3_freq, s.velocity, fb_signal) * op3_mod_index; + tx_operator_process_sample(t.op3, s.gate, s.trigger, op3_freq, s.velocity, + fb_signal) * + op3_mod_index; float op2_freq = frequency * t.op2.ratio; float op2_mod_index = (t.op2.amplitude * MOD_INDEX_COEFF); - float op2_signal = - tx_operator_process_sample(t.op2, s.gate, s.trigger, op2_freq, s.velocity) * op2_mod_index; + float op2_signal = tx_operator_process_sample(t.op2, s.gate, s.trigger, + op2_freq, s.velocity) * + op2_mod_index; float op1_freq = frequency * t.op1.ratio; - output = - tx_operator_process_sample(t.op1, s.gate, s.trigger, op1_freq, s.velocity, op2_signal + op3_signal) * - t.op1.amplitude; + output = tx_operator_process_sample(t.op1, s.gate, s.trigger, op1_freq, + s.velocity, op2_signal + op3_signal) * + t.op1.amplitude; } // reset trigger @@ -532,19 +593,24 @@ inline void tx_synth_init(tx_synth& s, double samplerate) for (int i = 0; i < MAX_VOICES; i++) { tx_voice_init(s.voices[i], samplerate); } } -inline void tx_synth_process_block(tx_synth& s, float** audio, size_t num_frames, const vector& midi_events, +inline void tx_synth_process_block(tx_synth& s, float** audio, size_t num_frames, + const vector& midi_events, const vector>& mods = {}) { - for (int i = 0; i < num_frames; i++) { audio[0][i] = audio[1][i] = 0.f; } // clear audio buffers + for (int i = 0; i < num_frames; i++) { + audio[0][i] = audio[1][i] = 0.f; + } // clear audio buffers voice_allocator_process_block(s.allocator, midi_events); for (int i = 0; i < s.allocator.active_voice_count; i++) { - tx_voice_process_block(s.voices[i], s.allocator.voices[i], audio, num_frames, mods); + tx_voice_process_block(s.voices[i], s.allocator.voices[i], audio, num_frames, + mods); } } -inline void tx_apply_parameter_mapping(array& v, tx_parameter_mapping& m, float value) +inline void tx_apply_parameter_mapping(array& v, + tx_parameter_mapping& m, float value) { if (m.range_min != m.range_max || m.exponent != 1.f) value = powf(value, m.exponent) * (m.range_max - m.range_min) + m.range_min; @@ -743,8 +809,8 @@ inline void tx_apply_parameter_mapping(array& v, tx_parame } } -inline void tx_apply_parameter_mappings(array& v, std::vector& m, - float value) +inline void tx_apply_parameter_mappings(array& v, + std::vector& m, float value) { for (int i = 0; i < m.size(); i++) { tx_apply_parameter_mapping(v, m[i], value); } } diff --git a/synth/voice_allocator.h b/synth/voice_allocator.h index a4512d4..6cc32de 100644 --- a/synth/voice_allocator.h +++ b/synth/voice_allocator.h @@ -1,3 +1,26 @@ +/* + * voice_allocator.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once #include @@ -31,7 +54,8 @@ inline void make_note_on(midi_event& ev, int _midi_note, float _velocity, int _o ev.offset = _offset; } -inline void make_note_off(midi_event& ev, int _midi_note, float _velocity, int _offset = 0) +inline void make_note_off(midi_event& ev, int _midi_note, float _velocity, + int _offset = 0) { ev.type = midi_event_type::NOTE_OFF; ev.midi_note = _midi_note; @@ -80,11 +104,14 @@ inline void voice_allocator_init(voice_allocator& va) for (size_t i = 0; i < MAX_VOICES; ++i) { va.voices[i].event_count = 0; } } -inline void voice_allocator_process_block(voice_allocator& va, const vector& midi_events) +inline void voice_allocator_process_block(voice_allocator& va, + const vector& midi_events) { // reset voice events and counts for (int i = 0; i < MAX_VOICES; i++) { - for (int j = 0; j < MAX_EVENTS_PER_VOICE; j++) { va.voices[i].events[j] = midi_event {}; } + for (int j = 0; j < MAX_EVENTS_PER_VOICE; j++) { + va.voices[i].events[j] = midi_event {}; + } va.voices[i].event_count = 0; } @@ -119,13 +146,16 @@ inline void voice_allocator_process_block(voice_allocator& va, const vector @@ -27,6 +50,8 @@ void audio_buffer_init(audio_buffer& a, size_t channels, size_t frames template void audio_buffer_update_ptrs(audio_buffer& a) { - for (int ch = 0; ch < a.channels; ++ch) { a.channel_ptrs[ch] = a.flat_data.data() + ch * a.frames; } + for (int ch = 0; ch < a.channels; ++ch) { + a.channel_ptrs[ch] = a.flat_data.data() + ch * a.frames; + } } } // namespace trnr diff --git a/util/audio_math.h b/util/audio_math.h index d99bd85..f059996 100644 --- a/util/audio_math.h +++ b/util/audio_math.h @@ -1,8 +1,32 @@ +/* + * audio_math.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once #include namespace trnr { + inline double lin_2_db(double lin) { if (lin <= 1e-20) lin = 1e-20; // avoid log(0) @@ -11,7 +35,13 @@ inline double lin_2_db(double lin) inline double db_2_lin(double db) { return pow(10.0, db / 20.0); } -inline float midi_to_frequency(float midi_note) { return 440.0 * powf(2.0, ((float)midi_note - 69.0) / 12.0); } +inline float midi_to_frequency(float midi_note) +{ + return 440.0 * powf(2.0, ((float)midi_note - 69.0) / 12.0); +} -inline float ms_to_samples(float ms, double sample_rate) { return (ms * 0.001f) * (float)sample_rate; } -} // namespace trnr \ No newline at end of file +inline float ms_to_samples(float ms, double sample_rate) +{ + return (ms * 0.001f) * (float)sample_rate; +} +} // namespace trnr diff --git a/util/demo_noise.h b/util/demo_noise.h index 8612fc6..e69893c 100644 --- a/util/demo_noise.h +++ b/util/demo_noise.h @@ -1,3 +1,26 @@ +/* + * demo_noise.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once #include diff --git a/util/format.h b/util/format.h index 89831e7..7248d4f 100644 --- a/util/format.h +++ b/util/format.h @@ -1,4 +1,28 @@ +/* + * format.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once + #include #include #include @@ -46,7 +70,8 @@ inline string float_to_string_trimmed(float value) inline string to_upper(string& str) { - std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::toupper(c); }); + std::transform(str.begin(), str.end(), str.begin(), + [](unsigned char c) { return std::toupper(c); }); return str; } } // namespace trnr diff --git a/util/retro_buf.h b/util/retro_buf.h index 7d80cc7..12da36a 100644 --- a/util/retro_buf.h +++ b/util/retro_buf.h @@ -1,3 +1,26 @@ +/* + * retro_buf.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once #include "../companding/alaw.h" diff --git a/util/smoother.h b/util/smoother.h index b0fcedd..baf851a 100644 --- a/util/smoother.h +++ b/util/smoother.h @@ -1,3 +1,26 @@ +/* + * smoother.h + * Copyright (c) 2025 Christopher Herb + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #pragma once #include "audio_math.h" @@ -14,7 +37,8 @@ struct smoother { int32_t remaining; }; -inline void smoother_init(smoother& s, double samplerate, float time_ms, float initial_value = 0.0f) +inline void smoother_init(smoother& s, double samplerate, float time_ms, + float initial_value = 0.0f) { s.samplerate = fmax(0.0, samplerate); s.time_samples = ms_to_samples(time_ms, s.samplerate); @@ -57,4 +81,4 @@ inline float smoother_process_sample(smoother& s) return s.current; } -} // namespace trnr \ No newline at end of file +} // namespace trnr