Skip to content

Commit a1a35ef

Browse files
authored
Merge branch 'main' into dev/ai_module
2 parents ba5b707 + a378f4a commit a1a35ef

34 files changed

Lines changed: 3235 additions & 191 deletions

examples/graphics/source/examples/FilterDemo.h

Lines changed: 419 additions & 75 deletions
Large diffs are not rendered by default.
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
==============================================================================
3+
4+
This file is part of the YUP library.
5+
Copyright (c) 2026 - kunitoki@gmail.com
6+
7+
YUP is an open source library subject to open-source licensing.
8+
9+
The code included in this file is provided under the terms of the ISC license
10+
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
11+
to use, copy, modify, and/or distribute this software for any purpose with or
12+
without fee is hereby granted provided that the above copyright notice and
13+
this permission notice appear in all copies.
14+
15+
YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
16+
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
17+
DISCLAIMED.
18+
19+
==============================================================================
20+
*/
21+
22+
#pragma once
23+
24+
namespace yup
25+
{
26+
27+
//==============================================================================
28+
/**
29+
Coefficients for the saturator used by analog-model filters.
30+
31+
The coefficients describe a symmetric atan waveshaper. A drive of zero keeps
32+
the processor transparent.
33+
*/
34+
template <typename CoeffType = double>
35+
struct AnalogSaturatorCoefficients
36+
{
37+
CoeffType drive = static_cast<CoeffType> (0);
38+
CoeffType preScale = static_cast<CoeffType> (1);
39+
CoeffType postScale = static_cast<CoeffType> (1);
40+
};
41+
42+
//==============================================================================
43+
/**
44+
Coefficients for a trapezoidal-integrator two-pole state-variable filter.
45+
46+
The output gains select low-pass, high-pass, band-pass, peak, or notch
47+
responses without changing the integrator state update.
48+
*/
49+
template <typename CoeffType = double>
50+
struct AnalogTwoPoleCoefficients
51+
{
52+
CoeffType g = static_cast<CoeffType> (0);
53+
CoeffType h = static_cast<CoeffType> (1);
54+
CoeffType r2 = static_cast<CoeffType> (0);
55+
CoeffType gainCorrection = static_cast<CoeffType> (1);
56+
CoeffType lowOut = static_cast<CoeffType> (1);
57+
CoeffType bandOut = static_cast<CoeffType> (0);
58+
CoeffType highOut = static_cast<CoeffType> (0);
59+
};
60+
61+
//==============================================================================
62+
/**
63+
Coefficients for one trapezoidal one-pole section used inside ladder models.
64+
*/
65+
template <typename CoeffType = double>
66+
struct AnalogOnePoleCoefficients
67+
{
68+
CoeffType alpha = static_cast<CoeffType> (0);
69+
CoeffType beta = static_cast<CoeffType> (1);
70+
};
71+
72+
//==============================================================================
73+
/**
74+
Coefficients for a Korg35-inspired analog-model filter.
75+
*/
76+
template <typename CoeffType = double>
77+
struct AnalogKorg35Coefficients
78+
{
79+
std::array<AnalogOnePoleCoefficients<CoeffType>, 3> poles;
80+
CoeffType alpha0 = static_cast<CoeffType> (1);
81+
CoeffType feedback = static_cast<CoeffType> (0);
82+
CoeffType gainCorrection = static_cast<CoeffType> (1);
83+
};
84+
85+
//==============================================================================
86+
/**
87+
Moog ladder output mode.
88+
*/
89+
enum class AnalogMoogLadderMode
90+
{
91+
lowpass24,
92+
highpass24,
93+
lowpass18,
94+
highpass18,
95+
lowpass12,
96+
highpass12,
97+
lowpass6,
98+
highpass6,
99+
bandpass12,
100+
bandpass6
101+
};
102+
103+
//==============================================================================
104+
/**
105+
Coefficients for a four-pole Moog ladder-style analog-model filter.
106+
*/
107+
template <typename CoeffType = double>
108+
struct AnalogMoogLadderCoefficients
109+
{
110+
std::array<AnalogOnePoleCoefficients<CoeffType>, 4> poles;
111+
std::array<CoeffType, 5> outputs {
112+
static_cast<CoeffType> (0),
113+
static_cast<CoeffType> (0),
114+
static_cast<CoeffType> (0),
115+
static_cast<CoeffType> (0),
116+
static_cast<CoeffType> (1)
117+
};
118+
CoeffType alpha0 = static_cast<CoeffType> (1);
119+
CoeffType feedback = static_cast<CoeffType> (0);
120+
CoeffType gainCorrection = static_cast<CoeffType> (1);
121+
};
122+
123+
//==============================================================================
124+
/**
125+
Coefficients for a Roland diode-ladder-inspired low-pass filter.
126+
*/
127+
template <typename CoeffType = double>
128+
struct AnalogRolandDiodeCoefficients
129+
{
130+
CoeffType cutoff = static_cast<CoeffType> (0);
131+
CoeffType feedback = static_cast<CoeffType> (0);
132+
CoeffType gainCorrection = static_cast<CoeffType> (1);
133+
CoeffType a = static_cast<CoeffType> (0);
134+
CoeffType a2 = static_cast<CoeffType> (0);
135+
CoeffType aInv = static_cast<CoeffType> (1);
136+
CoeffType b = static_cast<CoeffType> (1);
137+
CoeffType b2 = static_cast<CoeffType> (1);
138+
CoeffType c = static_cast<CoeffType> (1);
139+
CoeffType g0 = static_cast<CoeffType> (0);
140+
CoeffType g = static_cast<CoeffType> (0);
141+
CoeffType fg = static_cast<CoeffType> (1);
142+
CoeffType highpassA = static_cast<CoeffType> (0);
143+
CoeffType highpassB = static_cast<CoeffType> (1);
144+
};
145+
146+
//==============================================================================
147+
/**
148+
Coefficients for the vowel/formant analog-model filter.
149+
*/
150+
template <typename CoeffType = double>
151+
struct AnalogVowelCoefficients
152+
{
153+
std::array<AnalogTwoPoleCoefficients<CoeffType>, 3> formants;
154+
CoeffType gainCompensation = static_cast<CoeffType> (1);
155+
};
156+
157+
} // namespace yup
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/*
2+
==============================================================================
3+
4+
This file is part of the YUP library.
5+
Copyright (c) 2026 - kunitoki@gmail.com
6+
7+
YUP is an open source library subject to open-source licensing.
8+
9+
The code included in this file is provided under the terms of the ISC license
10+
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
11+
to use, copy, modify, and/or distribute this software for any purpose with or
12+
without fee is hereby granted provided that the above copyright notice and
13+
this permission notice appear in all copies.
14+
15+
YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
16+
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
17+
DISCLAIMED.
18+
19+
==============================================================================
20+
*/
21+
22+
#pragma once
23+
24+
namespace yup
25+
{
26+
27+
//==============================================================================
28+
/** State for a one-pole analog-model filter.
29+
30+
The state is designed to be used with the coefficients defined in AnalogOnePoleCoefficients.
31+
*/
32+
template <typename CoeffType>
33+
struct AnalogOnePoleState
34+
{
35+
CoeffType z1 = static_cast<CoeffType> (0);
36+
37+
void reset() noexcept
38+
{
39+
z1 = static_cast<CoeffType> (0);
40+
}
41+
42+
CoeffType processLowPass (CoeffType input, const AnalogOnePoleCoefficients<CoeffType>& coefficients) noexcept
43+
{
44+
const auto v = (input - z1) * coefficients.alpha;
45+
const auto output = v + z1;
46+
z1 = output + v;
47+
48+
return output;
49+
}
50+
51+
CoeffType processHighPass (CoeffType input, const AnalogOnePoleCoefficients<CoeffType>& coefficients) noexcept
52+
{
53+
return input - processLowPass (input, coefficients);
54+
}
55+
56+
CoeffType getFeedbackOutput (const AnalogOnePoleCoefficients<CoeffType>& coefficients) const noexcept
57+
{
58+
return coefficients.beta * z1;
59+
}
60+
};
61+
62+
//==============================================================================
63+
/** State for a two-pole analog-model filter.
64+
65+
The state is designed to be used with the coefficients defined in AnalogTwoPoleCoefficients.
66+
*/
67+
template <typename CoeffType>
68+
struct AnalogTwoPoleState
69+
{
70+
CoeffType s1 = static_cast<CoeffType> (0);
71+
CoeffType s2 = static_cast<CoeffType> (0);
72+
73+
void reset() noexcept
74+
{
75+
s1 = static_cast<CoeffType> (0);
76+
s2 = static_cast<CoeffType> (0);
77+
}
78+
};
79+
80+
//==============================================================================
81+
/** Computes the low-pass output of a one-pole analog-model filter.
82+
83+
@param input The input sample to process
84+
@param state The current state of the filter
85+
@param coefficients The coefficients defining the filter behavior
86+
87+
@return The low-pass output sample
88+
*/
89+
template <typename CoeffType>
90+
CoeffType getAnalogOnePoleLowPassOutput (CoeffType input, CoeffType state, const AnalogOnePoleCoefficients<CoeffType>& coefficients) noexcept
91+
{
92+
return coefficients.alpha * input + (static_cast<CoeffType> (1) - coefficients.alpha) * state;
93+
}
94+
95+
/** Computes the high-pass output of a one-pole analog-model filter.
96+
97+
@param input The input sample to process
98+
@param state The current state of the filter
99+
@param coefficients The coefficients defining the filter behavior
100+
101+
@return The high-pass output sample
102+
*/
103+
template <typename CoeffType>
104+
CoeffType getAnalogOnePoleHighPassOutput (CoeffType input, CoeffType state, const AnalogOnePoleCoefficients<CoeffType>& coefficients) noexcept
105+
{
106+
return (static_cast<CoeffType> (1) - coefficients.alpha) * (input - state);
107+
}
108+
109+
/** Computes the next state of a one-pole analog-model filter.
110+
111+
@param input The input sample to process
112+
@param state The current state of the filter
113+
@param coefficients The coefficients defining the filter behavior
114+
115+
@return The next state of the filter
116+
*/
117+
template <typename CoeffType>
118+
CoeffType getAnalogOnePoleNextState (CoeffType input, CoeffType state, const AnalogOnePoleCoefficients<CoeffType>& coefficients) noexcept
119+
{
120+
return static_cast<CoeffType> (2) * coefficients.alpha * input
121+
+ (static_cast<CoeffType> (1) - static_cast<CoeffType> (2) * coefficients.alpha) * state;
122+
}
123+
124+
//==============================================================================
125+
/** Processes a single sample through a two-pole analog-model filter.
126+
127+
This function implements the processing for a trapezoidal-integrator two-pole state-variable filter.
128+
The output is a combination of low-pass, high-pass, and band-pass responses based on the coefficients provided.
129+
130+
@param input The input sample to process
131+
@param coefficients The coefficients defining the filter behavior
132+
@param state The state of the filter, which will be updated during processing
133+
134+
@return The processed output sample
135+
*/
136+
template <typename SampleType, typename CoeffType>
137+
SampleType processAnalogTwoPole (
138+
SampleType input,
139+
const AnalogTwoPoleCoefficients<CoeffType>& coefficients,
140+
AnalogTwoPoleState<CoeffType>& state) noexcept
141+
{
142+
const auto inputValue = static_cast<CoeffType> (input);
143+
const auto highpass = (inputValue - coefficients.r2 * state.s1 - coefficients.g * state.s1 - state.s2) * coefficients.h;
144+
const auto bandpass = coefficients.g * highpass + state.s1;
145+
const auto bandpassFeedback = clipAnalogResonance (bandpass);
146+
const auto lowpass = coefficients.g * bandpass + state.s2;
147+
148+
state.s1 = coefficients.g * highpass + bandpassFeedback;
149+
state.s2 = coefficients.g * bandpassFeedback + lowpass;
150+
151+
const auto output = (coefficients.lowOut * lowpass + coefficients.bandOut * bandpass + coefficients.highOut * highpass)
152+
* coefficients.gainCorrection;
153+
154+
return static_cast<SampleType> (output);
155+
}
156+
157+
//==============================================================================
158+
/** Computes the complex frequency response of a two-pole analog-model filter.
159+
160+
@param coefficients The coefficients defining the filter behavior
161+
@param frequency The frequency at which to evaluate the response
162+
@param sampleRate The sample rate of the system
163+
164+
@return The complex frequency response
165+
*/
166+
template <typename CoeffType>
167+
Complex<CoeffType> getAnalogTwoPoleComplexResponse (
168+
const AnalogTwoPoleCoefficients<CoeffType>& coefficients,
169+
CoeffType frequency,
170+
double sampleRate) noexcept
171+
{
172+
using ComplexType = Complex<CoeffType>;
173+
174+
const auto highpassState1 = -coefficients.h * (coefficients.r2 + coefficients.g);
175+
const auto highpassState2 = -coefficients.h;
176+
const auto highpassInput = coefficients.h;
177+
178+
const auto bandpassState1 = coefficients.g * highpassState1 + static_cast<CoeffType> (1);
179+
const auto bandpassState2 = coefficients.g * highpassState2;
180+
const auto bandpassInput = coefficients.g * highpassInput;
181+
182+
const auto lowpassState1 = coefficients.g * bandpassState1;
183+
const auto lowpassState2 = coefficients.g * bandpassState2 + static_cast<CoeffType> (1);
184+
const auto lowpassInput = coefficients.g * bandpassInput;
185+
186+
const auto state11 = coefficients.g * highpassState1 + bandpassState1;
187+
const auto state12 = coefficients.g * highpassState2 + bandpassState2;
188+
const auto state1Input = coefficients.g * highpassInput + bandpassInput;
189+
190+
const auto state21 = coefficients.g * bandpassState1 + lowpassState1;
191+
const auto state22 = coefficients.g * bandpassState2 + lowpassState2;
192+
const auto state2Input = coefficients.g * bandpassInput + lowpassInput;
193+
194+
const auto outputState1 = (coefficients.lowOut * lowpassState1 + coefficients.bandOut * bandpassState1 + coefficients.highOut * highpassState1)
195+
* coefficients.gainCorrection;
196+
const auto outputState2 = (coefficients.lowOut * lowpassState2 + coefficients.bandOut * bandpassState2 + coefficients.highOut * highpassState2)
197+
* coefficients.gainCorrection;
198+
const auto outputInput = (coefficients.lowOut * lowpassInput + coefficients.bandOut * bandpassInput + coefficients.highOut * highpassInput)
199+
* coefficients.gainCorrection;
200+
201+
const auto z = polar (static_cast<CoeffType> (1), frequencyToAngular (frequency, static_cast<CoeffType> (sampleRate)));
202+
const auto determinant = (z - state11) * (z - state22) - state12 * state21;
203+
const auto responseState1 = ((z - state22) * state1Input + state12 * state2Input) / determinant;
204+
const auto responseState2 = (state21 * state1Input + (z - state11) * state2Input) / determinant;
205+
206+
return ComplexType (outputInput) + outputState1 * responseState1 + outputState2 * responseState2;
207+
}
208+
209+
} // namespace yup

0 commit comments

Comments
 (0)