Skip to content

Commit

Permalink
Update.
Browse files Browse the repository at this point in the history
  • Loading branch information
SamiPerttu committed Dec 23, 2023
1 parent 22037ef commit 7099e69
Show file tree
Hide file tree
Showing 16 changed files with 128 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- `AudioNode` now requires `Send` and `Sync`.
- Feedback units `Feedback64` and `Feedback32`.
- `Shape::Atan` was contributed by Serdnad.

### Version 0.15

Expand Down
13 changes: 7 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ edition = "2021"
[dependencies]
generic-array = "0.14.7"
numeric-array = "0.5.2"
num-complex = "0.4.3"
num-complex = "0.4.4"
rustfft = "6.1.0"
realfft = "3.3.0"
lazy_static = "1.4.0"
tinyvec = { version = "1.6.0", features = ["alloc"] }
rsor = "0.1.3"
duplicate = "1.0.0"
dyn-clone = "1.0.11"
dyn-clone = "1.0.16"
symphonia = { version = "0.5.3", optional = true, features = ["all"] }
thingbuf = "0.1.4"
funutd = "0.12.1"
Expand All @@ -30,14 +31,14 @@ files = ["dep:symphonia"]

[dev-dependencies]
cpal = "0.15.2"
anyhow = "1.0.71"
plotters = "0.3.4"
anyhow = "1.0.75"
plotters = "0.3.5"
criterion = "0.5.1"
midi-msg = "0.4.0"
midi-msg = "0.5.0"
midir = "0.9.1"
read_input = "0.8.6"
assert_no_alloc = "1.1.2"
eframe = "0.22.0"
eframe = "0.24.1"

[[bench]]
name = "benchmark"
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ Some nodes may also use the heap for audio buffers and the like.

The `allocate` method preallocates all needed memory. It should be called last before
sending something into a real-time context. This is done automatically in
the `Net32` and `Net64` frontend.
the `Net` and `Sequencer` frontends.

The purpose of the `AudioUnit` system is to grant more flexibility in dynamic situations:
decisions about input and output arities and contents can be deferred to runtime.
Expand Down Expand Up @@ -561,7 +561,7 @@ Due to nonlinearity, we do not attempt to calculate frequency responses for thes

### More On Multithreading And Real-Time Control

Besides `Net32` and `Net64` frontends, there are two ways to introduce real-time
Besides `Net` and `Sequencer` frontends, there are two ways to introduce real-time
control to graph expressions: shared atomic variables and setting listeners.

#### Atomic Variables
Expand Down Expand Up @@ -736,7 +736,7 @@ to examine frequency responses interactively:
```rust
C:\rust>evcxr
Welcome to evcxr. For help, type :help
>> :dep fundsp = "0.13.0"
>> :dep fundsp
>> use fundsp::hacker::*;
>> print!("{}", bell_hz(1000.0, 1.0, db_amp(50.0)).display())
60 dB ------------------------------------------------ 60 dB
Expand Down Expand Up @@ -961,9 +961,9 @@ The values in between are linearly interpolated.
`branch`, `bus`, `pipe`, `sum` and `stack` are opcodes that combine multiple nodes,
according to their first generic argument.
They accept a generator function that is issued `i64` integers starting from 0.
The nodes are stack allocated.
The nodes are stack allocated, and each node is assigned its own pseudorandom phase.

For example, to create 20 pseudorandom noise bands in 1 kHz...2 kHz:
For example, to create 20 noise bands in 1 kHz...2 kHz:

```rust
use fundsp::hacker::*;
Expand Down
9 changes: 5 additions & 4 deletions examples/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,16 @@ where
)?;
stream.play()?;

let viewport = ViewportBuilder::default().with_min_inner_size(vec2(360.0, 420.0));

let options = eframe::NativeOptions {
min_window_size: Some(vec2(360.0, 420.0)),
viewport,
..eframe::NativeOptions::default()
};

let mut state: State = State {
let state: State = State {
rnd: Rnd::from_time(),
id: Vec::new(),
id: vec![None; KEYS.len()],
sequencer,
net,
waveform: Waveform::Saw,
Expand All @@ -180,7 +182,6 @@ where
snoop0,
snoop1,
};
state.id.resize(KEYS.len(), None);

eframe::run_native(
"Virtual Keyboard Example",
Expand Down
12 changes: 11 additions & 1 deletion src/audionode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ pub trait AudioNode: Clone + Sync + Send {

/// Set node pseudorandom phase hash.
/// This is called from `ping` (only). It should not be called by users.
/// The node is allowed to reset itself here.
#[allow(unused_variables)]
fn set_hash(&mut self, hash: u64) {
// Override this to use the hash.
Expand All @@ -152,8 +153,16 @@ pub trait AudioNode: Clone + Sync + Send {
/// Leaf nodes should not need to override this.
/// If `probe` is true, then this is a probe for computing the network hash
/// and `set_hash` should not be called yet.
/// To set a custom hash for a graph, call this method with `ping`
/// To set a custom hash for a graph, call this method with `probe`
/// set to false and `hash` initialized with the custom hash.
/// The node is allowed to reset itself here.
///
/// ### Example (Setting a Custom Hash)
/// ```
/// use fundsp::hacker32::*;
/// let mut node = square_hz(440.0);
/// node.ping(false, AttoHash::new(1));
/// ```
fn ping(&mut self, probe: bool, hash: AttoHash) -> AttoHash {
if !probe {
self.set_hash(hash.state());
Expand All @@ -163,6 +172,7 @@ pub trait AudioNode: Clone + Sync + Send {

/// Route constants, latencies and frequency responses at `frequency` Hz
/// from inputs to outputs. Return output signal.
/// If there are no frequency responses in `input`, then `frequency` is ignored.
#[allow(unused_variables)]
fn route(&mut self, input: &SignalFrame, frequency: f64) -> SignalFrame {
// Default implementation marks all outputs unknown.
Expand Down
2 changes: 2 additions & 0 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,15 @@ impl<T: Float> Buffer<T> {
}

/// Get reference to a slice of slices with the given number of `channels`.
/// The buffer is resized if necessary.
#[inline]
pub fn get_ref(&mut self, channels: usize) -> &[&[T]] {
self.resize(channels);
self.slice.from_refs(&self.buffer)
}

/// Get reference to a mutable slice of slices with the given number of `channels`.
/// The buffer is resized if necessary.
#[inline]
pub fn get_mut(&mut self, channels: usize) -> &mut [&mut [T]] {
self.resize(channels);
Expand Down
1 change: 1 addition & 0 deletions src/hacker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub use super::pan::*;
pub use super::realnet::*;
pub use super::realseq::*;
pub use super::resample::*;
pub use super::resynth::*;
pub use super::rez::*;
pub use super::sequencer::*;
pub use super::setting::*;
Expand Down
1 change: 1 addition & 0 deletions src/hacker32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub use super::pan::*;
pub use super::realnet::*;
pub use super::realseq::*;
pub use super::resample::*;
pub use super::resynth::*;
pub use super::rez::*;
pub use super::sequencer::*;
pub use super::setting::*;
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
//!
//! See `README.md` in crate root folder for an overview.
//! For a list of changes, see `CHANGES.md` in the same folder.
//!
//! The central abstractions are located in the `audionode` and `audiounit` modules.
//! The `combinator` module defines the graph operators.
#![allow(
clippy::precedence,
Expand Down Expand Up @@ -276,6 +279,7 @@ pub mod prelude;
pub mod realnet;
pub mod realseq;
pub mod resample;
pub mod resynth;
pub mod rez;
pub mod sequencer;
pub mod setting;
Expand Down
1 change: 1 addition & 0 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub use super::pan::*;
pub use super::realnet::*;
pub use super::realseq::*;
pub use super::resample::*;
pub use super::resynth::*;
pub use super::rez::*;
pub use super::sequencer::*;
pub use super::setting::*;
Expand Down
1 change: 1 addition & 0 deletions src/realseq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub struct SequencerBackend48 {
pub sender: Sender<Option<Event48>>,
/// For receiving new events from the frontend.
receiver: Receiver<Message48>,
/// The backend sequencer.
sequencer: Sequencer48,
}

Expand Down
75 changes: 75 additions & 0 deletions src/resynth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//! Frequency domain resynthesis. WIP.
use super::audionode::*;
use super::*;
use num_complex::Complex32;

pub struct FftWindow<T, I, O>
where
T: Float,
I: Size<T>,
O: Size<T>,
{
/// Window length. Equals the length of each input and output channel vector.
length: usize,
/// Input samples for each input channel.
input: Vec<Vec<f32>>,
/// Input samples for each input channel in frequency domain.
input_fft: Vec<Vec<Complex32>>,
/// Output samples for each output channel in frequency domain.
output_fft: Vec<Vec<Complex32>>,
/// Output samples for each output channel.
output: Vec<Vec<f32>>,
/// Sample rate for convenience.
sample_rate: f32,
_marker: std::marker::PhantomData<(T, I, O)>,
}

impl<T, I, O> FftWindow<T, I, O>
where
T: Float,
I: Size<T>,
O: Size<T>,
{
/// Number of input channels.
#[inline]
pub fn inputs(&self) -> usize {
self.input.len()
}

/// Number of output channels.
#[inline]
pub fn outputs(&self) -> usize {
self.output.len()
}

/// FFT window length. Must be divisible by four.
#[inline]
pub fn length(&self) -> usize {
self.length
}

/// Number of FFT bins. Equals the length of each frequency domain vector.
#[inline]
pub fn bins(&self) -> usize {
(self.length() >> 1) + 1
}

/// Get input value at bin `i` of `channel`.
#[inline]
pub fn at(&self, channel: usize, i: usize) -> Complex32 {
self.input_fft[channel][i]
}

/// Set output value for bin `i` of `channel`.
#[inline]
pub fn set(&mut self, channel: usize, i: usize, value: Complex32) {
self.output_fft[channel][i] = value;
}

/// Return frequency associated with bin `i`.
#[inline]
pub fn frequency(&self, i: usize) -> f32 {
self.sample_rate / self.length() as f32 * i as f32
}
}
11 changes: 8 additions & 3 deletions src/sequencer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,21 @@ pub struct Sequencer48 {
past: Vec<Event48>,
/// Map of edits to be made to events in the ready queue.
edit_map: HashMap<EventId, Edit48>,
/// Number of output channels.
outputs: usize,
/// Current time. Does not apply to frontends.
time: f48,
/// Current sample rate.
sample_rate: f48,
/// Current sample duration.
sample_duration: f48,
/// Intermediate output buffer.
buffer: Buffer<f48>,
/// Intermediate output frame.
tick_buffer: Vec<f48>,
/// Optional frontend.
front: Option<(Sender<Message48>, Receiver<Option<Event48>>)>,
/// Whether we replay existing events after a call to `reset`.
replay_events: bool,
}

Expand Down Expand Up @@ -749,9 +756,7 @@ impl AudioUnit48 for Sequencer48 {
while let Some(_past) = self.past.pop() {}
}
for channel in 0..self.outputs {
for i in 0..size {
output[channel][i] = 0.0;
}
output[channel][..size].fill(0.0);
}
let end_time = self.time + self.sample_duration * size as f48;
self.ready_to_active(end_time);
Expand Down
2 changes: 2 additions & 0 deletions tests/basic.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Basic component tests.
#![allow(
dead_code,
clippy::precedence,
Expand Down
2 changes: 2 additions & 0 deletions tests/dynamics.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Dynamics components tests.
#![allow(clippy::manual_range_contains)]
#![allow(dead_code)]

Expand Down
3 changes: 2 additions & 1 deletion tests/filter.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Frequency response system tests.
#![allow(
dead_code,
clippy::precedence,
Expand Down Expand Up @@ -114,7 +116,6 @@ where
}

let fft = Radix4::new(length, FftDirection::Forward);
// Note. Output from process() appears normalized, contrary to documentation.
fft.process(&mut buffer);

let mut f = 10.0;
Expand Down

0 comments on commit 7099e69

Please sign in to comment.