Skip to content

Commit

Permalink
Update, fixes.
Browse files Browse the repository at this point in the history
  • Loading branch information
SamiPerttu committed Sep 14, 2024
1 parent 2804400 commit 0677f4e
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 81 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1079,7 +1079,7 @@ The type parameters in the table refer to the hacker preludes.
| `branchi::<U, _, _>(f)`| `f` | `U * f` | Branch into `U` nodes from indexed generator `f`. |
| `branchf::<U, _, _>(f)`| `f` | `U * f` | Branch into `U` nodes from fractional generator `f`, e.g., `\| x \| resonator_hz(xerp(20.0, 20_000.0, x), xerp(5.0, 5_000.0, x))`. |
| `bus(x, y)` | `x = y` | `x = y` | Bus `x` and `y`. Identical with `x & y`. |
| `busi::<U, _, _>(f)` | `f` | `f` | Bus together `U` nodes from indexed generator `f`, e.g., `\| i \| mul(i as f64 + 1.0) >> sine()`. |
| `busi::<U, _, _>(f)` | `f` | `f` | Bus together `U` nodes from indexed generator `f`, e.g., `\| i \| mul(i as f32 + 1.0) >> sine()`. |
| `busf::<U, _, _>(f)` | `f` | `f` | Bus together `U` nodes from fractional generator `f`. |
| `butterpass()` | 2 (audio, frequency) | 1 | Butterworth lowpass filter (2nd order). |
| `butterpass_hz(f)` | 1 | 1 | Butterworth lowpass filter (2nd order) with cutoff frequency `f` Hz. |
Expand Down Expand Up @@ -1108,7 +1108,7 @@ The type parameters in the table refer to the hacker preludes.
| `envelope(f)` | - | `f` | Time-varying control `f` with scalar or tuple output, e.g., `\|t\| exp(-t)`. Synonymous with `lfo`. |
| `envelope2(f)` | 1 (x) | `f` | Time-varying, input dependent control `f` with scalar or tuple output, e.g., `\|t, x\| exp(-t * x)`. Synonymous with `lfo2`. |
| `envelope3(f)` | 2 (x, y) | `f` | Time-varying, input dependent control `f` with scalar or tuple output, e.g., `\|t, x, y\| y * exp(-t * x)`. Synonymous with `lfo3`. |
| `envelope_in(f)` | `f` | `f` | Time-varying, input dependent control `f` with scalar or tuple output, e.g., `\|t, i: &Frame<f64, U1>\| exp(-t * i[0])`. Synonymous with `lfo_in`. |
| `envelope_in(f)` | `f` | `f` | Time-varying, input dependent control `f` with scalar or tuple output, e.g., `\|t, i: &Frame<f32, U1>\| exp(-t * i[0])`. Synonymous with `lfo_in`. |
| `fbell(shape)` | 4 (audio, frequency, Q, gain) | 1 | Feedback biquad bell equalizer (2nd order) with feedback `shape`, for example, `Tanh(1.0)`. |
| `fbell_hz(shape, f, q, gain)` | 1 | 1 | Feedback biquad bell equalizer (2nd order) with feedback `shape`, center `f` Hz, Q value `q` and amplitude gain `gain`. |
| `fdn(x)` | `x` | `x` | Feedback Delay Network: enclose feedback circuit `x` (with equal number of inputs and outputs) using diffusive [Hadamard](https://en.wikipedia.org/wiki/Hadamard_matrix) feedback. |
Expand Down Expand Up @@ -1142,7 +1142,7 @@ The type parameters in the table refer to the hacker preludes.
| `lfo(f)` | - | `f` | Time-varying control `f` with scalar or tuple output, e.g., `\|t\| exp(-t)`. Synonymous with `envelope`. |
| `lfo2(f)` | 1 (x) | `f` | Time-varying, input dependent control `f` with scalar or tuple output, e.g., `\|t, x\| exp(-t * x)`. Synonymous with `envelope2`. |
| `lfo3(f)` | 2 (x, y) | `f` | Time-varying, input dependent control `f` with scalar or tuple output, e.g., `\|t, x, y\| y * exp(-t * x)`. Synonymous with `envelope3`. |
| `lfo_in(f)` | `f` | `f` | Time-varying, input dependent control `f` with scalar or tuple output, e.g., `\|t, i: &Frame<f64, U1>\| exp(-t * i[0])`. Synonymous with `envelope_in`. |
| `lfo_in(f)` | `f` | `f` | Time-varying, input dependent control `f` with scalar or tuple output, e.g., `\|t, i: &Frame<f32, U1>\| exp(-t * i[0])`. Synonymous with `envelope_in`. |
| `limiter(a, r)` | 1 | 1 | Look-ahead limiter with attack time (and latency) `a` seconds and release time `r` seconds. |
| `limiter_stereo(a, r)` | 2 | 2 | Stereo look-ahead limiter with attack time (and latency) `a` seconds and release time `r` seconds. |
| `lorenz()` | 1 (frequency) | 1 | [Lorenz dynamical system](https://en.wikipedia.org/wiki/Lorenz_system) oscillator. |
Expand All @@ -1157,7 +1157,7 @@ The type parameters in the table refer to the hacker preludes.
| `lowshelf()` | 4 (audio, frequency, Q, gain) | 1 | Low shelf filter (2nd order) with adjustable amplitude gain. |
| `lowshelf_hz(f, q, gain)`| 1 | 1 | Low shelf filter (2nd order) centered at `f` Hz with Q `q` and amplitude gain `gain`. |
| `lowshelf_q(q, gain)` | 2 (audio, frequency) | 1 | Low shelf filter (2nd order) with Q `q` and amplitude gain `gain`. |
| `map(f)` | `f` | `f` | Map channels freely, e.g., `map(\|i: &Frame<f64, U2>\| max(i[0], i[1]))`. |
| `map(f)` | `f` | `f` | Map channels freely, e.g., `map(\|i: &Frame<f32, U2>\| max(i[0], i[1]))`. |
| `meter(mode)` | 1 | 1 (meter) | Analyze input and output a summary according to the metering mode. |
| `mls()` | - | 1 | White [MLS noise](https://en.wikipedia.org/wiki/Maximum_length_sequence) source. |
| `mls_bits(n)` | - | 1 | White MLS noise source from `n`-bit MLS sequence (1 <= `n` <= 31). |
Expand Down
67 changes: 0 additions & 67 deletions src/audionode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,6 @@ impl<T, A: ArrayLength + Sync + Send + Clone> Size<T> for A {}
/// between `AudioNode` instances.
pub type Frame<T, Size> = NumericArray<T, Size>;

unsafe fn uninitialized_f32_array() -> [f32; 8] {
#[allow(clippy::uninit_assumed_init)]
#[allow(invalid_value)]
core::mem::MaybeUninit::uninit().assume_init()
}

unsafe fn uninitialized_frame<T, Size: ArrayLength>() -> Frame<T, Size> {
#[allow(clippy::uninit_assumed_init)]
core::mem::MaybeUninit::uninit().assume_init()
}

/*
Order of type arguments in nodes:
1. Basic input and output arities excepting filter input selector arities.
Expand Down Expand Up @@ -95,7 +84,6 @@ pub trait AudioNode: Clone + Sync + Send {
/// ```
fn tick(&mut self, input: &Frame<f32, Self::Inputs>) -> Frame<f32, Self::Outputs>;

/*
/// Process up to 64 (`MAX_BUFFER_SIZE`) samples.
/// If `size` is zero then this is a no-op, which is permitted.
fn process(&mut self, size: usize, input: &BufferRef, output: &mut BufferMut) {
Expand All @@ -119,61 +107,6 @@ pub trait AudioNode: Clone + Sync + Send {
}
}
}
*/

/// Process up to 64 (`MAX_BUFFER_SIZE`) samples.
/// If `size` is zero then this is a no-op, which is permitted.
fn process(&mut self, size: usize, input: &BufferRef, output: &mut BufferMut) {
// The default implementation is a fallback that calls into `tick`.
debug_assert!(size <= MAX_BUFFER_SIZE);
debug_assert!(input.channels() == self.inputs());
debug_assert!(output.channels() == self.outputs());

// Actually unrolling this seems to improve performance.
let mut input_frame0: Frame<f32, Self::Inputs> = unsafe { uninitialized_frame() };
let mut input_frame1: Frame<f32, Self::Inputs> = unsafe { uninitialized_frame() };
let mut input_frame2: Frame<f32, Self::Inputs> = unsafe { uninitialized_frame() };
let mut input_frame3: Frame<f32, Self::Inputs> = unsafe { uninitialized_frame() };
let mut input_frame4: Frame<f32, Self::Inputs> = unsafe { uninitialized_frame() };
let mut input_frame5: Frame<f32, Self::Inputs> = unsafe { uninitialized_frame() };
let mut input_frame6: Frame<f32, Self::Inputs> = unsafe { uninitialized_frame() };
let mut input_frame7: Frame<f32, Self::Inputs> = unsafe { uninitialized_frame() };
for i in 0..full_simd_items(size) {
for channel in 0..self.inputs() {
let at = input.at(channel, i).to_array();
input_frame0[channel] = at[0];
input_frame1[channel] = at[1];
input_frame2[channel] = at[2];
input_frame3[channel] = at[3];
input_frame4[channel] = at[4];
input_frame5[channel] = at[5];
input_frame6[channel] = at[6];
input_frame7[channel] = at[7];
}
let output_frame0 = self.tick(&input_frame0);
let output_frame1 = self.tick(&input_frame1);
let output_frame2 = self.tick(&input_frame2);
let output_frame3 = self.tick(&input_frame3);
let output_frame4 = self.tick(&input_frame4);
let output_frame5 = self.tick(&input_frame5);
let output_frame6 = self.tick(&input_frame6);
let output_frame7 = self.tick(&input_frame7);
for channel in 0..self.outputs() {
let mut set: [f32; 8] = unsafe { uninitialized_f32_array() };
set[0] = output_frame0[channel];
set[1] = output_frame1[channel];
set[2] = output_frame2[channel];
set[3] = output_frame3[channel];
set[4] = output_frame4[channel];
set[5] = output_frame5[channel];
set[6] = output_frame6[channel];
set[7] = output_frame7[channel];
output.set(channel, i, F32x::from(set));
}
}

self.process_remainder(size, input, output);
}

/// Process samples left over using `tick` after processing all full SIMD items.
/// This is a convenience method for implementers.
Expand Down
29 changes: 19 additions & 10 deletions src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,9 +588,11 @@ impl Net {
self.vertex.len()
}

/// Assuming this network is a chain of processing units ordered by insertion order,
/// add a new unit to the chain. Global outputs will be assigned to the outputs of the unit
/// if possible. The number of inputs to the unit must match the number of outputs of the
/// Assuming this network is a chain of processing units,
/// add a new unit to the chain. 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.
/// The number of inputs to the unit must match the number of outputs of the
/// previous unit, or the number of network inputs if there is no previous unit.
/// Returns the ID of the new unit.
///
Expand All @@ -607,16 +609,21 @@ impl Net {
let unit_outputs = unit.outputs();
let id = self.push(unit);
let index = self.node_index[&id];
if self.outputs() == unit_outputs {
self.pipe_output(id);
}
if unit_inputs > 0 {
if self.size() > 1 {
self.pipe(self.vertex[index - 1].id, id);
} else {

if self.size() == 1 {
if self.inputs() > 0 {
self.pipe_input(id);
}
} else {
for i in 0..unit_inputs {
self.vertex[index].source[i].source = self.output_edge[i % self.outputs()].source;
}
}

for i in 0..self.outputs() {
self.output_edge[i].source = Port::Local(index, i % unit_outputs);
}

self.invalidate_order();
id
}
Expand Down Expand Up @@ -963,6 +970,7 @@ impl Net {
}

/// Process one sample using the supplied `sender` to deallocate units.
#[inline]
pub(crate) fn tick_2(
&mut self,
input: &[f32],
Expand Down Expand Up @@ -999,6 +1007,7 @@ impl Net {
}

/// Process a block of samples using the supplied `sender` to deallocate units.
#[inline]
pub(crate) fn process_2(
&mut self,
size: usize,
Expand Down
7 changes: 7 additions & 0 deletions tests/test_basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,13 @@ fn test_basic() {
net.check();
check_wave(net);

let mut net = Net::wrap(Box::new(sine_hz(42.)));
net = net.clone() | net;
let verb = Net::wrap(Box::new(reverb_stereo(10., 5., 0.5)));
net.chain(Box::new(verb));
net.check();
check_wave(net);

check_wave((noise() | envelope(|t| spline_noise(1, t * 10.0))) >> panner());
check_wave(impulse::<U2>());

Expand Down

0 comments on commit 0677f4e

Please sign in to comment.