Skip to content

Commit

Permalink
Update and fix.
Browse files Browse the repository at this point in the history
  • Loading branch information
SamiPerttu committed Oct 20, 2024
1 parent 3017484 commit 05b3efd
Show file tree
Hide file tree
Showing 11 changed files with 71 additions and 16 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
- New builder notation for setting noise generator seed, for example, `noise().seed(1)`.
- New opcode `biquad_bank()`.
- `BiquadBank` parameters for channel `i` are now set with the syntax `Setting::biquad(...).index(i)`.
- New `Wave` methods `mix` and `mix_channel`.
- New builder notation for setting envelope sampling interval, for example, `lfo(|t| exp(-t)).interval(0.01)`.

### Version 0.20

Expand Down
1 change: 1 addition & 0 deletions FUTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ This is a list of feature ideas for the future.
- Resampler with sinc interpolation.
- Support feedback loops in `Net`.
- Looping in `Sequencer`.
- Delay component that crossfades between taps.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1316,6 +1316,9 @@ The return type of the function - scalar or tuple - determines the number of out
The samples are spaced at an average of 2 ms apart, jittered by noise derived from pseudorandom phase.
The values in between are linearly interpolated.

To set a different sampling interval, use the `interval` builder method.
For example, `envelope(|t| clamp01(sqr_hz(10.0, t))).interval(0.01)` samples the envelope at 10 ms intervals.

`lfo` (Low Frequency Oscillator) is another name for `envelope`.

#### Indexed And Fractional Generator Functions
Expand Down
13 changes: 11 additions & 2 deletions src/combinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ impl<X: AudioNode> An<X> {
}

/// This builder method sets oscillator initial phase in 0...1,
/// overriding pseudorandom phase.
/// overriding pseudorandom phase. The setting takes effect immediately.
///
/// ### Example (Square Wave At 110 Hz With Initial Phase 0.5)
/// ```
Expand All @@ -267,13 +267,22 @@ impl<X: AudioNode> An<X> {
}

/// This builder method sets noise generator seed,
/// overriding pseudorandom phase.
/// overriding pseudorandom phase. The setting takes effect immediately.
/// Works with opcodes `mls`, `noise`, `white`, `pink` and `brown`.
pub fn seed(mut self, seed: u64) -> Self {
self.set(Setting::seed(seed).left());
self.reset();
self
}

/// This builder method sets the average interval (in seconds)
/// between samples in envelopes. The setting takes effect immediately.
/// Works with opcodes `envelope`, `envelope2`, `envelope3`, `envelope_in`,
/// `lfo`, `lfo2`, `lfo3` and `lfo_in`.
pub fn interval(mut self, time: f32) -> Self {
self.set(Setting::interval(time));
self
}
}

impl<X> Neg for An<X>
Expand Down
13 changes: 13 additions & 0 deletions src/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use super::audionode::*;
use super::buffer::*;
use super::combinator::*;
use super::math::*;
use super::setting::*;
use super::signal::*;
use super::*;
use core::marker::PhantomData;
Expand Down Expand Up @@ -161,6 +162,12 @@ where
}
}

fn set(&mut self, setting: Setting) {
if let Parameter::Interval(time) = setting.parameter() {
self.interval = F::from_f32(*time);
}
}

fn set_hash(&mut self, hash: u64) {
self.hash = hash;
self.t_hash = hash;
Expand Down Expand Up @@ -334,6 +341,12 @@ where
}
}

fn set(&mut self, setting: Setting) {
if let Parameter::Interval(time) = setting.parameter() {
self.interval = F::from_f32(*time);
}
}

fn set_hash(&mut self, hash: u64) {
self.hash = hash;
self.t_hash = hash;
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ use numeric_array::{ArrayLength, NumericArray};
use typenum::{U1, U4, U8};

use core::cmp::PartialEq;
use core::marker::{Send, Sync};
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
use std::marker::{Send, Sync};

use wide::{f32x8, f64x4, i32x8, u32x8};

Expand Down
3 changes: 2 additions & 1 deletion src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,8 @@ impl Net {
/// Global outputs will be assigned to the outputs of the unit.
/// If there are more global outputs than there are outputs in the unit, then a modulo
/// is taken to plug all of them.
/// The previous global output sources become inputs to the unit.
/// If this is the first unit in the net, then global inputs are assigned to inputs of the unit;
/// if the net was not empty then the previous global output sources become inputs to the unit.
/// Returns the ID of the new unit.
///
/// ### Example (Lowpass And Highpass Filters In Series)
Expand Down
5 changes: 3 additions & 2 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2654,7 +2654,7 @@ pub fn chorus(
) -> An<impl AudioNode<Inputs = U1, Outputs = U1>> {
(pass()
& (pass()
| An(Envelope::new(0.01, move |t| {
| lfo(move |t| {
(
lerp11(
separation,
Expand All @@ -2677,7 +2677,8 @@ pub fn chorus(
fractal_noise(hash1(seed ^ 0xfedcba), 8, 0.45, t * (mod_frequency + 0.06)),
),
)
})))
})
.interval(0.01))
>> multitap::<U4>(separation, separation * 4.0 + variation))
* dc(0.2)
}
Expand Down
9 changes: 9 additions & 0 deletions src/setting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ pub enum Parameter {
Phase(f32),
/// Generator seed.
Seed(u64),
/// Average sampling interval in seconds for envelopes.
Interval(f32),
}

/// Address specifies location to apply setting in a graph.
Expand Down Expand Up @@ -164,6 +166,13 @@ impl Setting {
address: ArrayVec::new(),
}
}
/// Create setting for envelope sampling interval in seconds.
pub fn interval(time: f32) -> Self {
Self {
parameter: Parameter::Interval(time),
address: ArrayVec::new(),
}
}
/// Add indexed address to setting.
pub fn index(mut self, index: usize) -> Self {
self.address.push(Address::Index(index));
Expand Down
34 changes: 25 additions & 9 deletions src/wave.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ impl Wave {
}

/// The sample rate of the wave.
#[inline]
pub fn sample_rate(&self) -> f64 {
self.sample_rate
}
Expand Down Expand Up @@ -157,6 +158,15 @@ impl Wave {
self.vec.insert(channel, samples.into());
}

/// Mix from a vector of `samples` to an existing channel here starting from index `offset`.
/// The offset may be negative, in which case the first items from `samples` will be ignored.
/// Does not resize this wave - ignores samples that spill over from either end.
pub fn mix_channel(&mut self, channel: usize, offset: isize, samples: &[f32]) {
for i in max(offset, 0)..min(offset + samples.len() as isize, self.len() as isize) {
self.mix(channel, i as usize, samples[(i - offset) as usize]);
}
}

/// Remove channel `channel` from this wave. Returns the removed channel.
pub fn remove_channel(&mut self, channel: usize) -> Vec<f32> {
assert!(channel < self.channels());
Expand All @@ -169,12 +179,18 @@ impl Wave {
self.vec[channel][index]
}

/// Set sample to value.
/// Set sample to `value`.
#[inline]
pub fn set(&mut self, channel: usize, index: usize, value: f32) {
self.vec[channel][index] = value;
}

/// Add `value` to sample.
#[inline]
pub fn mix(&mut self, channel: usize, index: usize, value: f32) {
self.vec[channel][index] += value;
}

/// Insert a new frame of samples to the end of the wave.
/// Pushing a scalar frame, the value is broadcast to any number of channels.
/// Otherwise, the number of channels must match.
Expand Down Expand Up @@ -271,7 +287,7 @@ impl Wave {
/// use fundsp::hacker::*;
/// let mut wave = Wave::render(44100.0, 1.0, &mut (sine_hz(60.0)));
/// let amplitude = wave.amplitude();
/// assert!(amplitude > 1.0 - 1.0e-5 && amplitude <= 1.0);
/// assert!(amplitude >= 1.0 - 1.0e-5 && amplitude <= 1.0);
/// ```
pub fn amplitude(&self) -> f32 {
let mut peak = 0.0;
Expand Down Expand Up @@ -305,8 +321,8 @@ impl Wave {
}
}

/// Applies a fade-in envelope to the wave with a duration of `time` seconds.
/// The duration may not exceed the duration of the wave.
/// Applies a smooth fade-in envelope to the wave with a duration of `time` seconds.
/// If `time` is greater than the duration of the wave, then it will be set to the duration of the wave.
///
/// ### Example
///
Expand All @@ -316,7 +332,7 @@ impl Wave {
/// wave.fade_in(1.0);
/// ```
pub fn fade_in(&mut self, time: f64) {
assert!(time <= self.duration());
let time = min(time, self.duration());
let fade_n = round(time * self.sample_rate());
for i in 0..fade_n as usize {
let a = smooth5((i + 1) as f64 / (fade_n + 1.0)) as f32;
Expand All @@ -326,8 +342,8 @@ impl Wave {
}
}

/// Applies a fade-out envelope to the wave with a duration of `time` seconds.
/// The duration may not exceed the duration of the wave.
/// Applies a smooth fade-out envelope to the wave with a duration of `time` seconds.
/// If `time` is greater than the duration of the wave, then it will be set to the duration of the wave.
///
/// ### Example
///
Expand All @@ -337,7 +353,7 @@ impl Wave {
/// wave.fade_out(5.0);
/// ```
pub fn fade_out(&mut self, time: f64) {
assert!(time <= self.duration());
let time = min(time, self.duration());
let fade_n = round(time * self.sample_rate());
let fade_i = fade_n as usize;
for i in 0..fade_i {
Expand All @@ -350,7 +366,7 @@ impl Wave {
}

/// Applies both fade-in and fade-out to the wave with a duration of `time` seconds.
/// The duration may not exceed the duration of the wave.
/// If `time` is greater than the duration of the wave, then it will be set to the duration of the wave.
///
/// ### Example
///
Expand Down
2 changes: 1 addition & 1 deletion tests/test_basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ fn test_basic() {
check_wave_filter(&input, tap_node.clone() | tap_node.clone());

// Check cycle.
let mut cycle = Net::new(1, 1);
let mut cycle = Net::new(2, 1);
let id1 = cycle.chain(Box::new(join::<U2>()));
let id2 = cycle.chain(Box::new(pass()));
assert_eq!(cycle.error(), &None);
Expand Down

0 comments on commit 05b3efd

Please sign in to comment.