Skip to content

Commit

Permalink
Update.
Browse files Browse the repository at this point in the history
  • Loading branch information
SamiPerttu committed Sep 5, 2024
1 parent 5f0c7ad commit 3ae8d08
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 38 deletions.
6 changes: 4 additions & 2 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- Added choice of interpolation algorithm to `AtomicSynth`.
- New opcode `sine_phase`.
- Opcodes `delay` and `tap_linear` now support zero sample delays.
- New method `Net::crossfade` for replacing a unit with a crossfade.
- New method `Net::crossfade` for replacing a unit with a smooth crossfade.
- Clarified latency: it only applies to involuntary causal latencies.
- `AdaptiveTanh` is now generic `Adaptive` distortion with an inner shape.
To migrate, try `Adaptive::new(timescale, Tanh(hardness))`.
Expand All @@ -14,7 +14,9 @@
- Implemented denormal prevention for `x86` inside feedback loops.
- The resonator now accepts a Q parameter instead of bandwidth in Hz.
To migrate, Q = center / bandwidth.
- Feedback biquads and dirty biquads.
- Feedback biquads and dirty biquads by Jatin Chowdhury.
- Sine oscillator has now generic inner state. To migrate, use
`Sine<f32>` if speed is important or `Sine<f64>` if steady maintenance of phase is important.

### Version 0.18.2

Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,8 @@ and folding constants. Linear networks are constructed from linear filters, dela

Signal latencies are similarly analyzed from input to output in detail,
facilitating automatic removal of pre-delay from effects chains.
The definition of latency is one of involuntary causal kind, so
voluntarily placed delay elements do not count as latency.

For example,
[FIR](https://en.wikipedia.org/wiki/Finite_impulse_response) filters
Expand Down Expand Up @@ -734,11 +736,11 @@ Due to nonlinearity, we do not attempt to calculate frequency responses for thes
| Opcode | Type | Parameters | Family | Notes |
| ------------ | ---------------------- | ------------ | ------------ | --------- |
| `bandrez` | bandpass (2nd order) | frequency, Q | nested 1st order | |
| `dbell` | peaking (2nd order) | frequency, Q, gain | dirty biquad | Biquad with nonlinear state shaping and adjustable amplitude gain. |
| `dbell` | peaking (2nd order) | frequency, Q, gain | [dirty biquad](https://jatinchowdhury18.medium.com/complex-nonlinearities-episode-4-nonlinear-biquad-filters-ae6b3f23cb0e) | Biquad with nonlinear state shaping and adjustable amplitude gain. |
| `dhighpass` | highpass (2nd order) | frequency, Q | dirty biquad | |
| `dlowpass` | lowpass (2nd order) | frequency, Q | dirty biquad | |
| `dresonator` | bandpass (2nd order) | frequency, Q | dirty biquad | |
| `fbell` | peaking (2nd order) | frequency, Q, gain | feedback biquad | Biquad with nonlinear feedback and adjustable amplitude gain. |
| `fbell` | peaking (2nd order) | frequency, Q, gain | [feedback biquad](https://jatinchowdhury18.medium.com/complex-nonlinearities-episode-5-nonlinear-feedback-filters-115e65fc0402) | Biquad with nonlinear feedback and adjustable amplitude gain. |
| `fhighpass` | highpass (2nd order) | frequency, Q | feedback biquad | |
| `flowpass` | lowpass (2nd order) | frequency, Q | feedback biquad | |
| `fresonator` | bandpass (2nd order) | frequency, Q | feedback biquad | |
Expand Down
4 changes: 3 additions & 1 deletion examples/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,9 @@ impl eframe::App for State {
>> peak(),
)),
Filter::DirtyBiquad => Net::wrap(Box::new(
(pass() | lfo(move |t| (max(400.0, 20000.0 * exp(-t * 8.0)), 2.0)))
(pass() | lfo(move |t| (max(800.0, 20000.0 * exp(-t * 6.0)), 3.0)))
>> !dlowpass(Tanh(1.02))
>> mul((1.0, 0.666, 1.0))
>> dlowpass(Tanh(1.0)),
)),
Filter::FeedbackBiquad => Net::wrap(Box::new(
Expand Down
7 changes: 6 additions & 1 deletion src/biquad.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
//! Biquad filters with optional nonlinearities.
//! Biquad filters with optional nonlinearities by Jatin Chowdhury.
// For more information, see:
// https://github.com/jatinchowdhury18/ComplexNonlinearities entries 4 and 5.
// For some of the biquad formulae, see the Audio EQ Cookbook:
// https://www.w3.org/TR/audio-eq-cookbook/

use super::audionode::*;
use super::math::*;
Expand Down
8 changes: 4 additions & 4 deletions src/hacker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ pub fn reverse<N: Size<f32>>() -> An<Reverse<N>> {
/// use fundsp::hacker::*;
/// lfo(|t| 110.0 + lerp11(-2.0, 2.0, sin_hz(t, 5.0))) >> sine();
/// ```
pub fn sine() -> An<Sine> {
pub fn sine() -> An<Sine<f64>> {
An(Sine::new())
}

Expand All @@ -342,14 +342,14 @@ pub fn sine() -> An<Sine> {
/// use fundsp::hacker::*;
/// sine_hz(440.0);
/// ```
pub fn sine_hz(f: f32) -> An<Pipe<Constant<U1>, Sine>> {
pub fn sine_hz(f: f32) -> An<Pipe<Constant<U1>, Sine<f64>>> {
constant(f) >> sine()
}

/// Sine oscillator with initial `phase` in 0...1.
/// - Input 0: frequency (Hz)
/// - Output 0: sine wave
pub fn sine_phase(phase: f32) -> An<Sine> {
pub fn sine_phase(phase: f32) -> An<Sine<f64>> {
An(Sine::with_phase(phase))
}

Expand Down Expand Up @@ -2464,7 +2464,7 @@ pub fn fbell<S: Shape>(shape: S) -> An<FbBiquad<f64, BellBiquad<f64>, S>> {
/// (The usual waveshapes are nonexpansive up to hardness 1.0).
/// - Input 0: audio
/// - Output 0: filtered audio
pub fn fbell_hz<F: Real, S: Shape>(
pub fn fbell_hz<S: Shape>(
shape: S,
center: f32,
q: f32,
Expand Down
8 changes: 4 additions & 4 deletions src/hacker32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ pub fn reverse<N: Size<f32>>() -> An<Reverse<N>> {
/// use fundsp::hacker32::*;
/// lfo(|t| 110.0 + lerp11(-2.0, 2.0, sin_hz(t, 5.0))) >> sine();
/// ```
pub fn sine() -> An<Sine> {
pub fn sine() -> An<Sine<f32>> {
An(Sine::new())
}

Expand All @@ -342,14 +342,14 @@ pub fn sine() -> An<Sine> {
/// use fundsp::hacker32::*;
/// sine_hz(440.0);
/// ```
pub fn sine_hz(f: f32) -> An<Pipe<Constant<U1>, Sine>> {
pub fn sine_hz(f: f32) -> An<Pipe<Constant<U1>, Sine<f32>>> {
constant(f) >> sine()
}

/// Sine oscillator with initial `phase` in 0...1.
/// - Input 0: frequency (Hz)
/// - Output 0: sine wave
pub fn sine_phase(phase: f32) -> An<Sine> {
pub fn sine_phase(phase: f32) -> An<Sine<f32>> {
An(Sine::with_phase(phase))
}

Expand Down Expand Up @@ -2464,7 +2464,7 @@ pub fn fbell<S: Shape>(shape: S) -> An<FbBiquad<f32, BellBiquad<f32>, S>> {
/// (The usual waveshapes are nonexpansive up to hardness 1.0).
/// - Input 0: audio
/// - Output 0: filtered audio
pub fn fbell_hz<F: Real, S: Shape>(
pub fn fbell_hz<S: Shape>(
shape: S,
center: f32,
q: f32,
Expand Down
28 changes: 14 additions & 14 deletions src/oscillator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ use alloc::vec::Vec;
/// - Input 0: frequency in Hz.
/// - Output 0: sine wave.
#[derive(Default, Clone)]
pub struct Sine {
phase: f32,
sample_duration: f32,
pub struct Sine<F: Real> {
phase: F,
sample_duration: F,
hash: u64,
initial_phase: Option<f32>,
initial_phase: Option<F>,
}

impl Sine {
impl<F: Real> Sine<F> {
/// Create sine oscillator.
pub fn new() -> Self {
let mut sine = Sine::default();
Expand All @@ -36,26 +36,26 @@ impl Sine {
/// Create sine oscillator with initial phase in 0...1.
pub fn with_phase(initial_phase: f32) -> Self {
let mut sine = Self {
phase: 0.0,
sample_duration: 0.0,
phase: F::zero(),
sample_duration: F::zero(),
hash: 0,
initial_phase: Some(initial_phase),
initial_phase: Some(F::from_f32(initial_phase)),
};
sine.reset();
sine.set_sample_rate(DEFAULT_SR);
sine
}
}

impl AudioNode for Sine {
impl<F: Real> AudioNode for Sine<F> {
const ID: u64 = 21;
type Inputs = typenum::U1;
type Outputs = typenum::U1;

fn reset(&mut self) {
self.phase = match self.initial_phase {
Some(phase) => phase,
None => rnd1(self.hash) as f32,
None => convert(rnd1(self.hash)),
};
}

Expand All @@ -66,17 +66,17 @@ impl AudioNode for Sine {
#[inline]
fn tick(&mut self, input: &Frame<f32, Self::Inputs>) -> Frame<f32, Self::Outputs> {
let phase = self.phase;
self.phase += input[0] * self.sample_duration;
self.phase += F::from_f32(input[0]) * self.sample_duration;
self.phase -= self.phase.floor();
[sin(phase * f32::TAU)].into()
[sin(phase.to_f32() * f32::TAU)].into()
}

fn process(&mut self, size: usize, input: &BufferRef, output: &mut BufferMut) {
let mut phase = self.phase;
for i in 0..full_simd_items(size) {
let element: [f32; SIMD_N] = core::array::from_fn(|j| {
let tmp = phase;
phase += input.at_f32(0, (i << SIMD_S) + j) * self.sample_duration;
let tmp = phase.to_f32();
phase += F::from_f32(input.at_f32(0, (i << SIMD_S) + j)) * self.sample_duration;
tmp
});
output.set(0, i, (F32x::new(element) * f32::TAU).sin());
Expand Down
20 changes: 10 additions & 10 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ pub type U128 = numeric_array::typenum::U128;
/// ### Example: Sine Oscillator
/// ```
/// use fundsp::prelude::*;
/// constant(440.0) >> sine();
/// constant(440.0) >> sine::<f64>();
/// ```
pub fn constant<X: ConstantFrame<Sample = f32>>(x: X) -> An<Constant<X::Size>>
where
Expand All @@ -197,7 +197,7 @@ where
/// ### Example: Dual Sine Oscillator
/// ```
/// use fundsp::prelude::*;
/// dc((220.0, 440.0)) >> (sine() + sine());
/// dc((220.0, 440.0)) >> (sine::<f32>() + sine::<f32>());
/// ```
pub fn dc<X: ConstantFrame<Sample = f32>>(x: X) -> An<Constant<X::Size>>
where
Expand Down Expand Up @@ -328,9 +328,9 @@ pub fn reverse<N: Size<f32>>() -> An<Reverse<N>> {
/// ### Example: Vibrato
/// ```
/// use fundsp::prelude::*;
/// lfo(|t| 110.0 + lerp11(-2.0, 2.0, sin_hz(t, 5.0))) >> sine();
/// lfo(|t| 110.0 + lerp11(-2.0, 2.0, sin_hz(t, 5.0))) >> sine::<f64>();
/// ```
pub fn sine() -> An<Sine> {
pub fn sine<F: Real>() -> An<Sine<F>> {
An(Sine::new())
}

Expand All @@ -340,16 +340,16 @@ pub fn sine() -> An<Sine> {
/// ### Example
/// ```
/// use fundsp::prelude::*;
/// sine_hz(440.0);
/// sine_hz::<f32>(440.0);
/// ```
pub fn sine_hz(f: f32) -> An<Pipe<Constant<U1>, Sine>> {
pub fn sine_hz<F: Real>(f: f32) -> An<Pipe<Constant<U1>, Sine<F>>> {
constant(f) >> sine()
}

/// Sine oscillator with initial `phase` in 0...1.
/// - Input 0: frequency (Hz)
/// - Output 0: sine wave
pub fn sine_phase(phase: f32) -> An<Sine> {
pub fn sine_phase<F: Real>(phase: f32) -> An<Sine<F>> {
An(Sine::with_phase(phase))
}

Expand Down Expand Up @@ -981,7 +981,7 @@ where
/// use fundsp::prelude::*;
/// let f: f32 = 440.0;
/// let m: f32 = 1.0;
/// oversample(sine_hz(f) * f * m + f >> sine());
/// oversample(sine_hz::<f32>(f) * f * m + f >> sine::<f32>());
/// ```
pub fn oversample<X>(node: An<X>) -> An<Oversampler<X>>
where
Expand Down Expand Up @@ -1204,7 +1204,7 @@ pub fn clip_to(minimum: f32, maximum: f32) -> An<Shaper<ClipTo>> {
/// ### Example: Panning Noise
/// ```
/// use fundsp::prelude::*;
/// (noise() | sine_hz(0.5)) >> panner();
/// (noise() | sine_hz::<f64>(0.5)) >> panner();
/// ```
pub fn panner() -> An<Panner<U2>> {
An(Panner::new(0.0))
Expand Down Expand Up @@ -1355,7 +1355,7 @@ where
/// ### Example (Sine Bundle)
/// ```
/// use fundsp::prelude::*;
/// busi::<U20, _, _>(|i| sine_hz(110.0 * exp(lerp(-0.2, 0.2, rnd2(i) as f32))));
/// busi::<U20, _, _>(|i| sine_hz::<f32>(110.0 * exp(lerp(-0.2, 0.2, rnd2(i) as f32))));
/// ```
pub fn busi<N, X, F>(f: F) -> An<MultiBus<N, X>>
where
Expand Down
17 changes: 17 additions & 0 deletions tests/test_basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,23 @@ fn test_basic() {
dc((440.0, 880.0)) >> multisplit::<U2, U3>() >> multijoin::<U2, U3>() >> (sine() | sine()),
);
check_wave((noise() >> split::<U16>() >> join()) | (noise() >> split::<U11>() >> join()));
check_wave(
noise() >> dbell_hz(Tanh(1.0), 1000.0, 10.0, 2.0)
| noise() >> dhighpass_hz(Softsign(1.0), 2000.0, 2.0),
);
check_wave(
noise() >> dresonator_hz(Tanh(0.5), 1000.0, 10.0)
| noise() >> dlowpass_hz(Softsign(0.5), 2000.0, 2.0),
);
check_wave(
noise() >> fbell_hz(Atan(1.0), 500.0, 50.0, 0.5)
| noise() >> flowpass_hz(Clip(1.0), 2000.0, 2.0),
);
check_wave(
noise() >> fresonator_hz(Atan(0.5), 500.0, 50.0)
| noise() >> fhighpass_hz(Softsign(0.2), 2000.0, 2.0),
);

check_wave_big(Box::new(dc((110.0, 0.5)) >> pulse() * 0.2 >> delay(0.1)));
check_wave_big(Box::new(envelope(|t| exp(-t * 10.0))));

Expand Down

0 comments on commit 3ae8d08

Please sign in to comment.