add license headers

This commit is contained in:
2025-11-06 11:05:54 +01:00
parent a362ab6c91
commit 12ae07d115
22 changed files with 739 additions and 108 deletions

View File

@@ -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 <cmath>
#include <cstdlib>
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 <typename t_sample>
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;

View File

@@ -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 {

View File

@@ -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 <algorithm>
#include <cmath>
#include <cstdlib>
#include <stdint.h>
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 <typename t_sample>
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;

View File

@@ -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 <cmath>
@@ -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;

View File

@@ -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 <algorithm>
#include <cmath>
@@ -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;

View File

@@ -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 <cmath>
namespace trnr {

View File

@@ -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 <cmath>
namespace trnr {

View File

@@ -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 <array>
#include <math.h>

View File

@@ -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 <cmath>
#include <vector>
@@ -21,7 +45,8 @@ struct cascade_filter {
std::vector<double> 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

View File

@@ -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,7 +1140,7 @@ inline void ysvf_set_param(ysvf& y, ysvf_parameters param, float value)
}
template <typename t_sample>
inline void y_process_samples(ysvf& y, t_sample** inputs, t_sample** outputs,
inline void ysvf_process_samples(ysvf& y, t_sample** inputs, t_sample** outputs,
int block_size)
{
switch (y.filter_type) {

View File

@@ -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 <array>
using namespace std;
@@ -28,6 +52,7 @@ struct dice {
array<quad, 6> 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

View File

@@ -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 <array>
@@ -7,7 +30,7 @@
namespace trnr {
template<typename sample>
template <typename sample>
class oversampler {
public:
oversampler()

View File

@@ -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"

View File

@@ -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 <cstddef>

View File

@@ -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 <cmath>
#include <random>
@@ -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<size_t>(ms * samplerate / 1000.f); }
inline size_t tx_mtos(float ms, double samplerate)
{
return static_cast<size_t>(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<audio_buffer<float>>& 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,76 +390,101 @@ 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) *
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) *
output = tx_operator_process_sample(t.op1, s.gate, s.trigger, op1_freq,
s.velocity, op2_signal + op3_signal) *
t.op1.amplitude;
}
@@ -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_event>& midi_events,
inline void tx_synth_process_block(tx_synth& s, float** audio, size_t num_frames,
const vector<midi_event>& midi_events,
const vector<audio_buffer<float>>& 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<tx_state, MAX_VOICES>& v, tx_parameter_mapping& m, float value)
inline void tx_apply_parameter_mapping(array<tx_state, MAX_VOICES>& 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<tx_state, MAX_VOICES>& v, tx_parame
}
}
inline void tx_apply_parameter_mappings(array<tx_state, MAX_VOICES>& v, std::vector<tx_parameter_mapping>& m,
float value)
inline void tx_apply_parameter_mappings(array<tx_state, MAX_VOICES>& v,
std::vector<tx_parameter_mapping>& m, float value)
{
for (int i = 0; i < m.size(); i++) { tx_apply_parameter_mapping(v, m[i], value); }
}

View File

@@ -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 <array>
@@ -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_event>& midi_events)
inline void voice_allocator_process_block(voice_allocator& va,
const vector<midi_event>& 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<midi
}
case NOTE_OFF: {
for (size_t i = 0; i < va.active_voice_count; ++i) {
if (va.voices[i].midi_note == ev.midi_note) va.voices[i].events[va.voices[i].event_count++] = ev;
if (va.voices[i].midi_note == ev.midi_note)
va.voices[i].events[va.voices[i].event_count++] = ev;
}
break;
}
case PITCH_WHEEL:
case MOD_WHEEL: {
for (size_t i = 0; i < va.active_voice_count; ++i) { va.voices[i].events[va.voices[i].event_count++] = ev; }
for (size_t i = 0; i < va.active_voice_count; ++i) {
va.voices[i].events[va.voices[i].event_count++] = ev;
}
break;
}
}

View File

@@ -1,3 +1,26 @@
/*
* audio_buffer.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 <vector>
@@ -27,6 +50,8 @@ void audio_buffer_init(audio_buffer<t_sample>& a, size_t channels, size_t frames
template <typename t_sample>
void audio_buffer_update_ptrs(audio_buffer<t_sample>& 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

View File

@@ -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 <math.h>
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; }
inline float ms_to_samples(float ms, double sample_rate)
{
return (ms * 0.001f) * (float)sample_rate;
}
} // namespace trnr

View File

@@ -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 <math.h>

View File

@@ -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 <algorithm>
#include <cstdarg>
#include <iomanip>
@@ -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

View File

@@ -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"

View File

@@ -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);