Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New example: Polysynth #19

Open
wants to merge 68 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
5f45d8c
wip: working polysynth example
SolarLiner Sep 15, 2024
4f2ba98
fix: add missing files
SolarLiner Sep 15, 2024
809dd3d
refactor: move RMS type to valib-core
SolarLiner Sep 15, 2024
1aab7fa
feat(examples): polysynth prototype gui
SolarLiner Sep 15, 2024
3e4fe38
fix(oscillators): square polyblep correct implementation
SolarLiner Sep 15, 2024
b5dec36
feat(examples): polysynth: output level and filter keyboard tracking
SolarLiner Sep 15, 2024
dc8bca6
feat(examples): polysynth: phase retriggering option
SolarLiner Sep 16, 2024
5c8affb
feat(examples): polysynth: use oversample const everywhere and increa…
SolarLiner Sep 16, 2024
70a7176
chore(core): provide type alias for sine interpolation
SolarLiner Sep 16, 2024
70e20bc
fix(oscillators): make phasor phase reset consistent
SolarLiner Sep 16, 2024
e0b611b
feat(examples): polysynth: drift parameter
SolarLiner Sep 16, 2024
dc90390
fix(voice): crash on polyphonic voice manager
SolarLiner Sep 16, 2024
e124466
feat(examples): polyphonic: VCA/VCF envelopes
SolarLiner Sep 16, 2024
2cdadaf
fix(test): add snapshot for sine interpolation
SolarLiner Sep 17, 2024
15b9a5c
feat(examples): polyphonic: tweak ADSR decay curves
SolarLiner Sep 18, 2024
7a0a69d
feat(examples): polyphonic: integrate DC blocker after the voices
SolarLiner Sep 18, 2024
a42afee
fix(core): BlockAdapter doesn't call set_samplerate on inner processor
SolarLiner Sep 18, 2024
076fc7d
fix(oscillators): Phasor does not update its step after a samplerate …
SolarLiner Sep 18, 2024
45d76bb
feat(examples): polysynth: switch to transistor ladder topology
SolarLiner Sep 18, 2024
57a741a
fix(examples): polysynth: fix formatting of s/ms
SolarLiner Sep 18, 2024
255680f
fix(examples): polysynth: fix samplerate not being updated in effects
SolarLiner Sep 18, 2024
b4b9a89
feat(examples): polysynth: add noise + ring mod mixer output
SolarLiner Sep 18, 2024
9932fa6
fix(filters): make ladder filter take a samplerate of type T
SolarLiner Sep 18, 2024
1f13f16
feat(examples): polysynth: new filter processor with multiple options…
SolarLiner Sep 18, 2024
689a7e8
feat(examples): polysynth: make ladder filters compensated + digital …
SolarLiner Sep 19, 2024
3843777
chore(examples): polysynth: tweak filter internal drive levels
SolarLiner Sep 19, 2024
0cb6801
fix(examples): polysynth: NaNs with biquad and svf filters
SolarLiner Sep 19, 2024
3403473
feat(core): fast math module
SolarLiner Sep 19, 2024
8dacae2
perf(saturators): use fast tanh
SolarLiner Sep 19, 2024
7b0db47
feat(examples): polysynth: filter fm
SolarLiner Sep 21, 2024
a567026
wip(voice): dynamic mono/poly voice manager
SolarLiner Sep 21, 2024
ed6f5bc
wip: working polysynth example
SolarLiner Sep 15, 2024
86c36ef
fix: add missing files
SolarLiner Sep 15, 2024
2b747e7
refactor: move RMS type to valib-core
SolarLiner Sep 15, 2024
f086e9e
feat(examples): polysynth prototype gui
SolarLiner Sep 15, 2024
08ef217
fix(oscillators): square polyblep correct implementation
SolarLiner Sep 15, 2024
e9d4f87
feat(examples): polysynth: output level and filter keyboard tracking
SolarLiner Sep 15, 2024
0ca9780
feat(examples): polysynth: phase retriggering option
SolarLiner Sep 16, 2024
a027b3c
feat(examples): polysynth: use oversample const everywhere and increa…
SolarLiner Sep 16, 2024
fc2b548
chore(core): provide type alias for sine interpolation
SolarLiner Sep 16, 2024
cc3c05e
fix(oscillators): make phasor phase reset consistent
SolarLiner Sep 16, 2024
7e9ab82
feat(examples): polysynth: drift parameter
SolarLiner Sep 16, 2024
d6e9d4f
fix(voice): crash on polyphonic voice manager
SolarLiner Sep 16, 2024
e85eb9b
feat(examples): polyphonic: VCA/VCF envelopes
SolarLiner Sep 16, 2024
898154e
fix(test): add snapshot for sine interpolation
SolarLiner Sep 17, 2024
2c65131
feat(examples): polyphonic: tweak ADSR decay curves
SolarLiner Sep 18, 2024
3b12dab
feat(examples): polyphonic: integrate DC blocker after the voices
SolarLiner Sep 18, 2024
1b43779
fix(core): BlockAdapter doesn't call set_samplerate on inner processor
SolarLiner Sep 18, 2024
df859ab
fix(oscillators): Phasor does not update its step after a samplerate …
SolarLiner Sep 18, 2024
92755a3
feat(examples): polysynth: switch to transistor ladder topology
SolarLiner Sep 18, 2024
3e061dc
fix(examples): polysynth: fix formatting of s/ms
SolarLiner Sep 18, 2024
702dadc
fix(examples): polysynth: fix samplerate not being updated in effects
SolarLiner Sep 18, 2024
54c642b
feat(examples): polysynth: add noise + ring mod mixer output
SolarLiner Sep 18, 2024
a2fd7d6
fix(filters): make ladder filter take a samplerate of type T
SolarLiner Sep 18, 2024
8e34b56
feat(examples): polysynth: new filter processor with multiple options…
SolarLiner Sep 18, 2024
4faa2d5
feat(examples): polysynth: make ladder filters compensated + digital …
SolarLiner Sep 19, 2024
f884db2
chore(examples): polysynth: tweak filter internal drive levels
SolarLiner Sep 19, 2024
0a38b5c
fix(examples): polysynth: NaNs with biquad and svf filters
SolarLiner Sep 19, 2024
4c25cc5
feat(core): fast math module
SolarLiner Sep 19, 2024
ab9ef4f
perf(saturators): use fast tanh
SolarLiner Sep 19, 2024
63e6727
feat(examples): polysynth: filter fm
SolarLiner Sep 21, 2024
14cc206
wip(voice): dynamic mono/poly voice manager
SolarLiner Sep 21, 2024
ba562c3
fix(polysynth): compile errors post rebase
SolarLiner Sep 30, 2024
6cbac83
fix(polysynth): change mapping of resonance into biquad
SolarLiner Sep 30, 2024
a45b0d7
chore(polysynth): make logging less verbose
SolarLiner Sep 30, 2024
bf28832
fix(filters,examples): compile errors
SolarLiner Sep 30, 2024
2f9fbae
chore(filters): update snapshots
SolarLiner Sep 30, 2024
a9e8d17
Merge remote-tracking branch 'origin/example/polysynth' into example/…
SolarLiner Oct 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .idea/valib.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ filters = ["saturators", "dep:valib-filters"]
oscillators = ["dep:valib-oscillators"]
oversample = ["filters", "dep:valib-oversample"]
voice = ["dep:valib-voice"]
voice-upsampled = ["voice", "valib-voice/resampled"]
wdf = ["filters", "dep:valib-wdf"]
fundsp = ["dep:valib-fundsp"]
nih-plug = ["dep:valib-nih-plug"]
Expand Down
6 changes: 5 additions & 1 deletion Makefile.plugins.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ dependencies = ["xtask-build"]
command = "cargo"
args = ["xtask", "bundle", "${CARGO_MAKE_CRATE_NAME}", "${@}"]

[tasks.install-target-x86_64-darwin]
command = "rustup"
args = ["target", "add", "x86_64-apple-darwin"]

[tasks.bundle-universal]
dependencies = ["xtask-build"]
dependencies = ["xtask-build", "install-target-x86_64-darwin"]
command = "cargo"
args = ["xtask", "bundle-universal", "${CARGO_MAKE_CRATE_NAME}", "${@}"]

Expand Down
15 changes: 14 additions & 1 deletion crates/valib-core/src/dsp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@ impl<P: HasParameters> HasParameters for BlockAdapter<P> {

impl<P: DSPMeta> DSPMeta for BlockAdapter<P> {
type Sample = P::Sample;

fn set_samplerate(&mut self, samplerate: f32) {
self.0.set_samplerate(samplerate);
}

fn latency(&self) -> usize {
self.0.latency()
}

fn reset(&mut self) {
self.0.reset();
}
}

impl<P: DSPProcess<I, O>, const I: usize, const O: usize> DSPProcess<I, O> for BlockAdapter<P> {
Expand Down Expand Up @@ -123,13 +135,14 @@ pub struct SampleAdapter<P, const I: usize, const O: usize>
where
P: DSPProcessBlock<I, O>,
{
/// Inner block processor
pub inner: P,
/// Size of the buffers passed into the inner block processor.
pub buffer_size: usize,
input_buffer: AudioBufferBox<P::Sample, I>,
input_filled: usize,
output_buffer: AudioBufferBox<P::Sample, O>,
output_filled: usize,
inner: P,
}

impl<P, const I: usize, const O: usize> std::ops::Deref for SampleAdapter<P, I, O>
Expand Down
99 changes: 99 additions & 0 deletions crates/valib-core/src/math/fast.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use crate::Scalar;
use numeric_literals::replace_float_literals;
use simba::simd::SimdBool;

/// Rational approximation of tanh(x) which is valid in the range -3..3
///
/// This approximation only includes the rational approximation part, and will diverge outside the
/// bounds. In order to apply the tanh function over a bigger interval, consider clamping either the
/// input or the output.
///
/// You should consider using [`tanh`] for a general-purpose faster tanh function, which uses
/// branching.
///
/// Source: <https://www.musicdsp.org/en/latest/Other/238-rational-tanh-approximation.html>
///
/// # Arguments
///
/// * `x`: Input value (low-error range: -3..3)
///
/// returns: T
#[replace_float_literals(T::from_f64(literal))]
pub fn rational_tanh<T: Scalar>(x: T) -> T {
x * (27. + x * x) / (27. + 9. * x * x)
}

/// Fast approximation of tanh(x).
///
/// This approximation uses branching to clamp the output to -1..1 in order to be useful as a
/// general-purpose approximation of tanh.
///
/// Source: <https://www.musicdsp.org/en/latest/Other/238-rational-tanh-approximation.html>
///
/// # Arguments
///
/// * `x`: Input value
///
/// returns: T
pub fn tanh<T: Scalar>(x: T) -> T {
rational_tanh(x).simd_clamp(-T::one(), T::one())
}

/// Fast approximation of exp, with maximum error in -1..1 of 0.59%, and in -3.14..3.14 of 9.8%.
///
/// You should consider using [`exp`] for a better approximation which uses this function, but
/// allows a greater range at the cost of branching.
///
/// Source: <https://www.musicdsp.org/en/latest/Other/222-fast-exp-approximations.html>
///
/// # Arguments
///
/// * `x`: Input value
///
/// returns: T
#[replace_float_literals(T::from_f64(literal))]
pub fn fast_exp5<T: Scalar>(x: T) -> T {
(120. + x * (120. + x * (60. + x * (20. + x * (5. + x))))) * 0.0083333333
}

/// Fast approximation of exp, using [`fast_exp5`]. Uses branching to get a bigger range.
///
/// Maximum error in the 0..10.58 range is 0.45%.
///
/// Source: <https://www.musicdsp.org/en/latest/Other/222-fast-exp-approximations.html>
///
/// # Arguments
///
/// * `x`:
///
/// returns: T
#[replace_float_literals(T::from_f64(literal))]
pub fn exp<T: Scalar>(x: T) -> T {
x.simd_lt(2.5).if_else2(
|| T::simd_e() * fast_exp5(x - 1.),
(|| x.simd_lt(5.), || 33.115452 * fast_exp5(x - 3.5)),
|| 403.42879 * fast_exp5(x - 6.),
)
}

/// Fast 2^x approximation, using [`exp`].
///
/// Maximum error in the 0..15.26 range is 0.45%.
///
/// Source: <https://www.musicdsp.org/en/latest/Other/222-fast-exp-approximations.html>
///
/// # Arguments
///
/// * `x`:
///
/// returns: T
///
/// # Examples
///
/// ```
///
/// ```
pub fn pow2<T: Scalar>(x: T) -> T {
let log_two = T::simd_ln_2();
exp(log_two * x)
}
7 changes: 5 additions & 2 deletions crates/valib-core/src/math/interpolation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,12 @@ impl<T: Scalar> Interpolate<T, 2> for Linear {
#[derive(Debug, Copy, Clone)]
pub struct MappedLinear<F>(pub F);

pub type Sine<T> = MappedLinear<fn(T) -> T>;

/// Returns an interpolator that performs sine interpolation.
pub fn sine_interpolation<T: Scalar>() -> MappedLinear<impl Fn(T) -> T> {
MappedLinear(|t| T::simd_cos(t * T::simd_pi()))
#[replace_float_literals(T::from_f64(literal))]
pub fn sine_interpolation<T: Scalar>() -> Sine<T> {
MappedLinear(|t| 0.5 - 0.5 * T::simd_cos(t * T::simd_pi()))
}

impl<T, F: Fn(T) -> T> Interpolate<T, 2> for MappedLinear<F>
Expand Down
2 changes: 2 additions & 0 deletions crates/valib-core/src/math/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@

use crate::Scalar;

pub mod fast;

Check warning on line 11 in crates/valib-core/src/math/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a module

warning: missing documentation for a module --> crates/valib-core/src/math/mod.rs:11:1 | 11 | pub mod fast; | ^^^^^^^^^^^^ | note: the lint level is defined here --> crates/valib-core/src/lib.rs:5:9 | 5 | #![warn(missing_docs)] | ^^^^^^^^^^^^
pub mod interpolation;
pub mod lut;
pub mod nr;
#[cfg(feature = "math-polynom")]

Check warning on line 15 in crates/valib-core/src/math/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

unexpected `cfg` condition value: `math-polynom`

warning: unexpected `cfg` condition value: `math-polynom` --> crates/valib-core/src/math/mod.rs:15:7 | 15 | #[cfg(feature = "math-polynom")] | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `feature` are: `test-utils` = help: consider adding `math-polynom` as a feature in `Cargo.toml` = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default

Check warning on line 15 in crates/valib-core/src/math/mod.rs

View workflow job for this annotation

GitHub Actions / Package plugin binaries (ubuntu-22.04, ubuntu-latest)

unexpected `cfg` condition value: `math-polynom`

Check warning on line 15 in crates/valib-core/src/math/mod.rs

View workflow job for this annotation

GitHub Actions / Package plugin binaries (windows, windows-latest)

unexpected `cfg` condition value: `math-polynom`

Check warning on line 15 in crates/valib-core/src/math/mod.rs

View workflow job for this annotation

GitHub Actions / Package plugin binaries (macos-universal, macos-13, aarch64-apple-darwin)

unexpected `cfg` condition value: `math-polynom`
pub mod polynom;

/// Return the complex number in the z-plane corresponding to the frequency `f` at sample rate
Expand Down Expand Up @@ -86,6 +87,7 @@
#[inline]
pub fn smooth_min<T: Scalar>(t: T, a: T, b: T) -> T {
let r = (-a / t).simd_exp2() + (-b / t).simd_exp2();
// let r = fast::pow2(-a / t) + fast::pow2(-b / t);
-t * r.simd_log2()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
source: crates/valib-core/src/math/interpolation.rs
expression: "&actual as &[_]"
---
0.0
0.146447
0.5
0.853553
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
53 changes: 52 additions & 1 deletion crates/valib-core/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
};
use num_traits::{AsPrimitive, Float, Zero};
use numeric_literals::replace_float_literals;
use simba::simd::SimdValue;
use simba::simd::{SimdComplexField, SimdValue};

Check warning on line 10 in crates/valib-core/src/util.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `SimdComplexField`

warning: unused import: `SimdComplexField` --> crates/valib-core/src/util.rs:10:19 | 10 | use simba::simd::{SimdComplexField, SimdValue}; | ^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default

Check warning on line 10 in crates/valib-core/src/util.rs

View workflow job for this annotation

GitHub Actions / Package plugin binaries (ubuntu-22.04, ubuntu-latest)

unused import: `SimdComplexField`

Check warning on line 10 in crates/valib-core/src/util.rs

View workflow job for this annotation

GitHub Actions / Package plugin binaries (windows, windows-latest)

unused import: `SimdComplexField`

Check warning on line 10 in crates/valib-core/src/util.rs

View workflow job for this annotation

GitHub Actions / Package plugin binaries (macos-universal, macos-13, aarch64-apple-darwin)

unused import: `SimdComplexField`
use std::collections::VecDeque;

/// Transmutes a slice into a slice of static arrays, putting the remainder of the slice not fitting
/// as a separate slice.
Expand Down Expand Up @@ -202,6 +203,27 @@
2.0.simd_powf(semi / 12.0)
}

/// Compute the semitone equivalent change in pitch that would have resulted by multiplying the
/// input ratio to a frequency value.
///
/// # Arguments
///
/// * `ratio`: Frequency ratio (unitless)
///
/// returns: T
///
/// # Examples
///
/// ```
/// use valib_core::util::ratio_to_semitone;
/// assert_eq!(0., ratio_to_semitone(1.));
/// assert_eq!(12., ratio_to_semitone(2.));
/// assert_eq!(-12., ratio_to_semitone(0.5));
/// ```
pub fn ratio_to_semitone<T: Scalar>(ratio: T) -> T {
T::from_f64(12.) * ratio.simd_log2()
}

/// Create a new matrix referencing this one as storage. The resulting matrix will have the same
/// shape and same strides as the input one.
///
Expand Down Expand Up @@ -264,3 +286,32 @@

#[cfg(feature = "test-utils")]
pub mod tests;

#[derive(Debug, Clone)]
pub struct Rms<T> {

Check warning on line 291 in crates/valib-core/src/util.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a struct

warning: missing documentation for a struct --> crates/valib-core/src/util.rs:291:1 | 291 | pub struct Rms<T> { | ^^^^^^^^^^^^^^^^^
data: VecDeque<T>,
summed_squared: T,
}

impl<T: Zero> Rms<T> {
pub fn new(size: usize) -> Self {

Check warning on line 297 in crates/valib-core/src/util.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for an associated function

warning: missing documentation for an associated function --> crates/valib-core/src/util.rs:297:5 | 297 | pub fn new(size: usize) -> Self { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Self {
data: (0..size).map(|_| T::zero()).collect(),
summed_squared: T::zero(),
}
}
}

impl<T: Scalar> Rms<T> {
pub fn add_element(&mut self, value: T) -> T {

Check warning on line 306 in crates/valib-core/src/util.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a method

warning: missing documentation for a method --> crates/valib-core/src/util.rs:306:5 | 306 | pub fn add_element(&mut self, value: T) -> T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
let v2 = value.simd_powi(2);
self.summed_squared -= self.data.pop_front().unwrap();
self.summed_squared += v2;
self.data.push_back(v2);
self.get_rms()
}

pub fn get_rms(&self) -> T {

Check warning on line 314 in crates/valib-core/src/util.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for a method

warning: missing documentation for a method --> crates/valib-core/src/util.rs:314:5 | 314 | pub fn get_rms(&self) -> T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^
self.summed_squared.simd_sqrt()
}
}
5 changes: 2 additions & 3 deletions crates/valib-filters/src/ladder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,7 @@ impl<T: Scalar, Topo: LadderTopology<T>> Ladder<T, Topo> {
/// let transistor_ladder = Ladder::<_, Transistor<DiodeClipperModel<_>>>::new(48000.0, 440.0, 1.0);
/// ```
#[replace_float_literals(T::from_f64(literal))]
pub fn new(samplerate: impl Into<f64>, cutoff: T, resonance: T) -> Self {
let samplerate = T::from_f64(samplerate.into());
pub fn new(samplerate: T, cutoff: T, resonance: T) -> Self {
let mut this = Self {
inv_2fs: T::simd_recip(2.0 * samplerate),
samplerate,
Expand Down Expand Up @@ -369,7 +368,7 @@ mod tests {
bode: true,
series: &[Series {
label: "Frequency response",
samplerate,
samplerate: samplerate as f32,
series: &responsef32,
color: &BLUE,
}],
Expand Down
Loading
Loading