From d57d9d5a0d1bea5e712e58b11221cd862d38b8fb Mon Sep 17 00:00:00 2001 From: Michiel Roos Date: Mon, 11 Nov 2024 12:26:00 +0100 Subject: [PATCH] Update examples (#29) * Add more examples that are short and to the point --- knyst/Cargo.toml | 51 ++++++-- knyst/examples/README.md | 123 ++++++++++++++++-- .../adjust_frequency.rs} | 51 +++++--- knyst/examples/basic/modulation.rs | 57 ++++++++ knyst/examples/basic/scheduling.rs | 59 +++++++++ .../{ => basic}/sound_file_playback.rs | 37 ++++-- knyst/examples/basic/tone.rs | 51 ++++++++ knyst/examples/basic/tones.rs | 52 ++++++++ knyst/examples/beat_callbacks.rs | 6 + knyst/examples/compressor_test.rs | 6 +- .../examples/envelopes/frequency_envelope.rs | 71 ++++++++++ knyst/examples/envelopes/volume_envelope.rs | 70 ++++++++++ knyst/examples/filter_test.rs | 37 ++++-- knyst/examples/more_advanced_example.rs | 6 +- 14 files changed, 616 insertions(+), 61 deletions(-) rename knyst/examples/{simple_example.rs => basic/adjust_frequency.rs} (59%) create mode 100644 knyst/examples/basic/modulation.rs create mode 100644 knyst/examples/basic/scheduling.rs rename knyst/examples/{ => basic}/sound_file_playback.rs (63%) create mode 100644 knyst/examples/basic/tone.rs create mode 100644 knyst/examples/basic/tones.rs create mode 100644 knyst/examples/envelopes/frequency_envelope.rs create mode 100644 knyst/examples/envelopes/volume_envelope.rs diff --git a/knyst/Cargo.toml b/knyst/Cargo.toml index 6c44c5e..ccfa3a0 100644 --- a/knyst/Cargo.toml +++ b/knyst/Cargo.toml @@ -81,29 +81,62 @@ path = "macro_tests/macro_tests_main.rs" name = "large_sine_graph" harness = false +# Basic examples [[example]] -name = "beat_callbacks" -path = "examples/beat_callbacks.rs" +name = "tone" +path = "examples/basic/tone.rs" required-features = ["cpal"] [[example]] -name = "simple_example" -path = "examples/simple_example.rs" +name = "tones" +path = "examples/basic/tones.rs" required-features = ["cpal"] [[example]] -name = "more_advanced_example" -path = "examples/more_advanced_example.rs" +name = "modulation" +path = "examples/basic/modulation.rs" required-features = ["cpal"] [[example]] -name = "interactive" -path = "examples/interactive.rs" +name = "adjust_frequency" +path = "examples/basic/adjust_frequency.rs" required-features = ["cpal"] [[example]] name = "sound_file_playback" -path = "examples/sound_file_playback.rs" +path = "examples/basic/sound_file_playback.rs" +required-features = ["cpal"] + +[[example]] +name = "scheduling" +path = "examples/basic/scheduling.rs" +required-features = ["cpal"] + +# Envelope examples +[[example]] +name = "volume_envelope" +path = "examples/envelopes/volume_envelope.rs" +required-features = ["cpal"] + +[[example]] +name = "frequency_envelope" +path = "examples/envelopes/frequency_envelope.rs" +required-features = ["cpal"] + +# Advanced examples +[[example]] +name = "beat_callbacks" +path = "examples/beat_callbacks.rs" +required-features = ["cpal"] + +[[example]] +name = "more_advanced_example" +path = "examples/more_advanced_example.rs" +required-features = ["cpal"] + +[[example]] +name = "interactive" +path = "examples/interactive.rs" required-features = ["cpal"] [[example]] diff --git a/knyst/examples/README.md b/knyst/examples/README.md index de8457f..080a6ae 100644 --- a/knyst/examples/README.md +++ b/knyst/examples/README.md @@ -1,16 +1,123 @@ # Knyst examples -All the examples currently use either the JACK or the CPAL backend. That means that to run them you need to enable that feature, e.g. +List all available examples using: -```sh -cargo run --release --example interactive --features cpal +```shell +cargo run --example +``` + +## Basic examples + +### Tone + +This example plays a single tone + +```shell +cargo run --example tone +``` + +### Tones + +This example plays two tones. One in the left channel and one in the right channel. + +```shell +cargo run --example tones +``` + +### Modulation + +This example plays a tone modulated by a oscillator. + +```shell +cargo run --example modulation ``` -## Interactive +### Adjust frequency + +This example plays a different tone in each channel, both with different modulators. You can change the frequency of the tone in the left channel by entering a numerical value. + +```shell +cargo run --example adjust_frequency +``` + +### Sound file playback + +Plays back 10 seconds of an audio file chosen by the user. + +```shell +cargo run --example sound_file_playback +``` + +### Schedule a tone + +Plays back a tone after 3 seconds of silence. + +```shell +cargo run --example scheduling +``` + +## Envelopes + +### Volume envelope + +This example plays a tone with a volume envelope. + +```shell +cargo run --example volume_envelope +``` + +### Frequency envelope + +This example plays a tone with a frequency envelope. + +```shell +cargo run --example frequency_envelope +``` + +## Advanced + +### Beat Callbacks + +The main function initializes and starts the audio processing system with the default settings. It sets up a graph with wavetables, modulators, and amplitude modulators, and schedules beat-accurate parameter changes. The function reads user input to allow interaction with the callback and offers options to stop the callback or quit the program. + +```shell +cargo run --example beat_callbacks +``` + +### Filter + +```shell +cargo run --example filter_test +``` + +### Interactive This example aims to provide an overview of different ways that Knyst can be used. The example currently demonstrates: -- async using tokio -- interactivity using the keyboard to play pitches monophonically -- sound file playback -- replacing a wavetable in Resources while everything is running +- starting an audio backend +- pushing nodes +- making connections +- inner graphs +- async and multi threaded usage of KnystCommands +- scheduling changes +- interactivity +- wrapping other dsp libraries (fundsp in this case) +- writing a custom error handler + +```shell +cargo run --example interactive +``` + +### More advanced example + +```shell +cargo run --example more_advanced_example +``` + +## Using JACK + +All the examples currently use either the JACK or the CPAL backend. If you want to use JACK, add that as a feature flag. Also uncomment the JACK backend line in the example and comment out the CPAL backend line. + +```sh +cargo run --example filter_test --features jack +``` diff --git a/knyst/examples/simple_example.rs b/knyst/examples/basic/adjust_frequency.rs similarity index 59% rename from knyst/examples/simple_example.rs rename to knyst/examples/basic/adjust_frequency.rs index e5cbdf5..cd3f83b 100644 --- a/knyst/examples/simple_example.rs +++ b/knyst/examples/basic/adjust_frequency.rs @@ -6,39 +6,30 @@ use knyst::{ prelude::*, }; +/// Sets up both an owned and shared wavetable oscillator, and connects them to audio output channels. +/// +/// Listens for user input to dynamically adjust the frequency of the first oscillator. +/// fn main() -> Result<()> { - let mut backend = CpalBackend::new(CpalBackendOptions::default())?; - // Uncomment the line below and comment the line above to use the JACK backend instead - // let mut backend = JackBackend::new("Knyst<3JACK")?; - - let sample_rate = backend.sample_rate() as Sample; - let block_size = backend.block_size().unwrap_or(64); - println!("sr: {sample_rate}, block: {block_size}"); + let _backend = setup(); - // Start with an automatic helper thread for scheduling changes and managing resources. - // If you want to manage the `Controller` yourself, use `start_return_controller`. - let _sphere = KnystSphere::start( - &mut backend, - SphereSettings { - num_inputs: 1, - num_outputs: 2, - ..Default::default() - }, - print_error_handler, - ); // Owned wavetable oscillator at 440 Hz let node0 = wavetable_oscillator_owned(Wavetable::sine()).freq(440.); - // We can also use a shared wavetable oscillator which reads its wavetable from `Resources`. A cosine wavetable + // We can also use a shared wavetable oscillator which reads it's wavetable from `Resources`. A cosine wavetable // is created by default. let modulator = oscillator(WavetableId::cos()).freq(5.); // Output to the zeroth (left) channel graph_output(0, node0 * (modulator * 0.25 + 0.5)); + let node1 = wavetable_oscillator_owned(Wavetable::sine()).freq(220.); let modulator = oscillator(WavetableId::cos()).freq(3.); // Output a different sound to the first (right) channel graph_output(1, node1 * (modulator * 0.25 + 0.5)); // Respond to user input. This kind of interaction can be put in a different thread and/or in an async runtime. + println!("Playing a sine wave with 440 Hz and 220 Hz"); + println!("Enter a new frequency for the left channel followed by [ENTER]"); + println!("Press [q] to quit"); let mut input = String::new(); loop { match std::io::stdin().read_line(&mut input) { @@ -57,3 +48,25 @@ fn main() -> Result<()> { } Ok(()) } + +/// Initializes the audio backend and starts a `KnystSphere` for audio processing. +/// Start with an automatic helper thread for scheduling changes and managing resources. +/// If you want to manage the `Controller` yourself, use `start_return_controller`. +/// +/// The backend is returned here because it would otherwise be dropped at the end of setup() +fn setup() -> impl AudioBackend { + let mut backend = + CpalBackend::new(CpalBackendOptions::default()).expect("Unable to connect to CPAL backend"); + // Uncomment the line below and comment the line above to use the JACK backend instead + // let mut backend = JackBackend::new("Knyst<3JACK").expect("Unable to start JACK backend"); + + let _sphere_id = KnystSphere::start( + &mut backend, + SphereSettings { + ..Default::default() + }, + print_error_handler, + ) + .ok(); + backend +} diff --git a/knyst/examples/basic/modulation.rs b/knyst/examples/basic/modulation.rs new file mode 100644 index 0000000..5aa830b --- /dev/null +++ b/knyst/examples/basic/modulation.rs @@ -0,0 +1,57 @@ +use anyhow::Result; +#[allow(unused)] +use knyst::{ + audio_backend::{CpalBackend, CpalBackendOptions, JackBackend}, + controller::print_error_handler, + prelude::*, +}; + +/// Configures a wavetable oscillator to output modulated audio. +/// +/// The function performs the following steps: +/// 1. Creates an owned wavetable oscillator set at 110 Hz. +/// 2. Outputs the oscillator to the left and right channels at 30% volume each, modulating it with a frequency of 2 Hz. +/// 3. Waits for user to press ENTER. +/// +/// The modulation signal is a sine which has a range of values between -1 and 1. To prevent +/// zero-crossings, the signal is shifted up by dividing by 2 (so the range becomes -0.5 to 0.5) and +/// then adding 0.5 (so the range becomes 0 to 1). +/// +fn main() -> Result<()> { + let _backend = setup(); + + let node0 = wavetable_oscillator_owned(Wavetable::sine()).freq(110.); + + let modulator = oscillator(WavetableId::cos()).freq(2.); + + graph_output(0, node0 * 0.3 * (modulator * 0.5 + 0.5)); + graph_output(1, node0 * 0.3 * (modulator * 0.5 + 0.5)); + + println!("Playing a modulated sine wave at 110 Hz at an amplitude of 0.3"); + println!("Press [ENTER] to exit"); + let mut input = String::new(); + std::io::stdin().read_line(&mut input)?; + Ok(()) +} + +/// Initializes the audio backend and starts a `KnystSphere` for audio processing. +/// Start with an automatic helper thread for scheduling changes and managing resources. +/// If you want to manage the `Controller` yourself, use `start_return_controller`. +/// +/// The backend is returned here because it would otherwise be dropped at the end of setup() +fn setup() -> impl AudioBackend { + let mut backend = + CpalBackend::new(CpalBackendOptions::default()).expect("Unable to connect to CPAL backend"); + // Uncomment the line below and comment the line above to use the JACK backend instead + // let mut backend = JackBackend::new("Knyst<3JACK").expect("Unable to start JACK backend"); + + let _sphere_id = KnystSphere::start( + &mut backend, + SphereSettings { + ..Default::default() + }, + print_error_handler, + ) + .ok(); + backend +} diff --git a/knyst/examples/basic/scheduling.rs b/knyst/examples/basic/scheduling.rs new file mode 100644 index 0000000..567eb1c --- /dev/null +++ b/knyst/examples/basic/scheduling.rs @@ -0,0 +1,59 @@ +use anyhow::Result; +use knyst::controller::schedule_bundle; +use knyst::graph; +#[allow(unused)] +use knyst::{ + audio_backend::{CpalBackend, CpalBackendOptions, JackBackend}, + controller::print_error_handler, + prelude::*, +}; + +/// Configures a wavetable oscillator to output audio after a number of seconds. +/// +/// The function performs the following steps: +/// 1. Creates an owned wavetable oscillator set at 110 Hz. +/// 2. Wait 3 seconds +/// 3. Outputs the oscillator to the left and right channels at 30% volume each. +/// 4. Waits for user to press ENTER. +/// +/// Scheduling is currently limited to changes of constant values and spawning new nodes, not new +/// connections. This means you need to place all node and envelope creation inside the +/// schedule_bundle cAll. +/// +fn main() -> Result<()> { + let _backend = setup(); + + schedule_bundle(graph::Time::Seconds(Seconds::new(3, 0)), || { + let node0 = wavetable_oscillator_owned(Wavetable::sine()).freq(110.); + graph_output(0, node0 * 0.3); + graph_output(1, node0 * 0.3); + }); + + println!("Playing a sine wave at 110 Hz at an amplitude of 0.3, after 3 seconds"); + println!("Press [ENTER] to exit"); + let mut input = String::new(); + std::io::stdin().read_line(&mut input)?; + Ok(()) +} + +/// Initializes the audio backend and starts a `KnystSphere` for audio processing. +/// Start with an automatic helper thread for scheduling changes and managing resources. +/// If you want to manage the `Controller` yourself, use `start_return_controller`. +/// +/// The backend is returned here because it would otherwise be dropped at the end of setup() +fn setup() -> impl AudioBackend { + let mut backend = + CpalBackend::new(CpalBackendOptions::default()).expect("Unable to connect to CPAL backend"); + // Uncomment the line below and comment the line above to use the JACK backend instead + // let mut backend = JackBackend::new("Knyst<3JACK").expect("Unable to start JACK backend"); + + let _sphere_id = KnystSphere::start( + &mut backend, + SphereSettings { + ..Default::default() + }, + print_error_handler, + ) + .ok(); + backend +} diff --git a/knyst/examples/sound_file_playback.rs b/knyst/examples/basic/sound_file_playback.rs similarity index 63% rename from knyst/examples/sound_file_playback.rs rename to knyst/examples/basic/sound_file_playback.rs index 3129dcb..944852a 100644 --- a/knyst/examples/sound_file_playback.rs +++ b/knyst/examples/basic/sound_file_playback.rs @@ -8,18 +8,9 @@ use knyst::{ prelude::*, }; +/// Plays back 10 seconds of an audio file chosen by the user. fn main() -> anyhow::Result<()> { - let mut backend = CpalBackend::new(CpalBackendOptions::default())?; - // let mut backend = JackBackend::new("Knyst<3JACK")?; - let _sphere = KnystSphere::start( - &mut backend, - SphereSettings { - num_inputs: 0, - num_outputs: 2, - ..Default::default() - }, - print_error_handler, - ); + let _backend = setup(); // Get file path to a sound file from the user let file_path = dialog::FileSelection::new("Please select an audio file") @@ -27,6 +18,7 @@ fn main() -> anyhow::Result<()> { .show() .expect("Could not display dialog box") .unwrap(); + // Insert the buffer before sending Resources to the audio thread let sound_buffer = Buffer::from_sound_file(file_path)?; let channels = sound_buffer.num_channels(); @@ -39,6 +31,7 @@ fn main() -> anyhow::Result<()> { .channels(channels) .looping(true) .upload(); + // Connect the buffer to the outputs, connecting 2 channels (a mono buffer will be played in both the left and right channels) graph_output(0, buf_playback_node.channels(2)); @@ -46,3 +39,25 @@ fn main() -> anyhow::Result<()> { std::thread::sleep(Duration::from_millis(10000)); Ok(()) } + +/// Initializes the audio backend and starts a `KnystSphere` for audio processing. +/// Start with an automatic helper thread for scheduling changes and managing resources. +/// If you want to manage the `Controller` yourself, use `start_return_controller`. +/// +/// The backend is returned here because it would otherwise be dropped at the end of setup() +fn setup() -> impl AudioBackend { + let mut backend = + CpalBackend::new(CpalBackendOptions::default()).expect("Unable to connect to CPAL backend"); + // Uncomment the line below and comment the line above to use the JACK backend instead + // let mut backend = JackBackend::new("Knyst<3JACK").expect("Unable to start JACK backend"); + + let _sphere_id = KnystSphere::start( + &mut backend, + SphereSettings { + ..Default::default() + }, + print_error_handler, + ) + .ok(); + backend +} diff --git a/knyst/examples/basic/tone.rs b/knyst/examples/basic/tone.rs new file mode 100644 index 0000000..c1b82d0 --- /dev/null +++ b/knyst/examples/basic/tone.rs @@ -0,0 +1,51 @@ +use anyhow::Result; +#[allow(unused)] +use knyst::{ + audio_backend::{CpalBackend, CpalBackendOptions, JackBackend}, + controller::print_error_handler, + prelude::*, +}; + +/// Configures a wavetable oscillator to output audio. +/// +/// The function performs the following steps: +/// 1. Creates an owned wavetable oscillator set at 110 Hz. +/// 2. Outputs the oscillator to the left and right channels at 30% volume each. +/// 3. Waits for user to press ENTER. +/// +fn main() -> Result<()> { + let _backend = setup(); + + let node0 = wavetable_oscillator_owned(Wavetable::sine()).freq(110.); + + graph_output(0, node0 * 0.3); + graph_output(1, node0 * 0.3); + + println!("Playing a sine wave at 110 Hz at an amplitude of 0.3"); + println!("Press [ENTER] to exit"); + let mut input = String::new(); + std::io::stdin().read_line(&mut input)?; + Ok(()) +} + +/// Initializes the audio backend and starts a `KnystSphere` for audio processing. +/// Start with an automatic helper thread for scheduling changes and managing resources. +/// If you want to manage the `Controller` yourself, use `start_return_controller`. +/// +/// The backend is returned here because it would otherwise be dropped at the end of setup() +fn setup() -> impl AudioBackend { + let mut backend = + CpalBackend::new(CpalBackendOptions::default()).expect("Unable to connect to CPAL backend"); + // Uncomment the line below and comment the line above to use the JACK backend instead + // let mut backend = JackBackend::new("Knyst<3JACK").expect("Unable to start JACK backend"); + + let _sphere_id = KnystSphere::start( + &mut backend, + SphereSettings { + ..Default::default() + }, + print_error_handler, + ) + .ok(); + backend +} diff --git a/knyst/examples/basic/tones.rs b/knyst/examples/basic/tones.rs new file mode 100644 index 0000000..3e575d3 --- /dev/null +++ b/knyst/examples/basic/tones.rs @@ -0,0 +1,52 @@ +use anyhow::Result; +#[allow(unused)] +use knyst::{ + audio_backend::{CpalBackend, CpalBackendOptions, JackBackend}, + controller::print_error_handler, + prelude::*, +}; + +/// Configures a wavetable oscillator to output audio. +/// +/// The function performs the following steps: +/// 1. Creates an owned wavetable oscillator set at 110 Hz. +/// 2. Outputs the oscillators to the left and right channels at 30% volume each. +/// 3. Waits for user to press ENTER. +/// +fn main() -> Result<()> { + let _backend = setup(); + + let node0 = wavetable_oscillator_owned(Wavetable::sine()).freq(110.); + let node1 = wavetable_oscillator_owned(Wavetable::sine()).freq(220.); + + graph_output(0, node0 * 0.3); + graph_output(1, node1 * 0.3); + + println!("Playing sine waves at 110 and 220 Hz at an amplitude of 0.3"); + println!("Press [ENTER] to exit"); + let mut input = String::new(); + std::io::stdin().read_line(&mut input)?; + Ok(()) +} + +/// Initializes the audio backend and starts a `KnystSphere` for audio processing. +/// Start with an automatic helper thread for scheduling changes and managing resources. +/// If you want to manage the `Controller` yourself, use `start_return_controller`. +/// +/// The backend is returned here because it would otherwise be dropped at the end of setup() +fn setup() -> impl AudioBackend { + let mut backend = + CpalBackend::new(CpalBackendOptions::default()).expect("Unable to connect to CPAL backend"); + // Uncomment the line below and comment the line above to use the JACK backend instead + // let mut backend = JackBackend::new("Knyst<3JACK").expect("Unable to start JACK backend"); + + let _sphere_id = KnystSphere::start( + &mut backend, + SphereSettings { + ..Default::default() + }, + print_error_handler, + ) + .ok(); + backend +} diff --git a/knyst/examples/beat_callbacks.rs b/knyst/examples/beat_callbacks.rs index ad5a148..5f14e75 100644 --- a/knyst/examples/beat_callbacks.rs +++ b/knyst/examples/beat_callbacks.rs @@ -9,6 +9,11 @@ use knyst::{ use rand::{thread_rng, Rng}; use std::time::Duration; +/// The main function initializes and starts the audio processing system with the default +/// settings. It sets up a graph with wavetables, modulators, and amplitude modulators, +/// and schedules beat-accurate parameter changes. The function reads user input to allow +/// interaction with the callback and offers options to stop the callback or quit the program. +/// fn main() -> Result<()> { let mut backend = CpalBackend::new(CpalBackendOptions::default())?; @@ -86,6 +91,7 @@ fn main() -> Result<()> { }, StartBeat::Multiple(Beats::from_beats(4)), // Start after a multiple of 4 beats )); + println!("Press [q] to quit"); let mut input = String::new(); loop { match std::io::stdin().read_line(&mut input) { diff --git a/knyst/examples/compressor_test.rs b/knyst/examples/compressor_test.rs index 9b981be..63a9ea8 100644 --- a/knyst/examples/compressor_test.rs +++ b/knyst/examples/compressor_test.rs @@ -1,11 +1,15 @@ use anyhow::Result; +use knyst::audio_backend::{CpalBackend, CpalBackendOptions}; use knyst::{ audio_backend::JackBackend, controller::print_error_handler, gen::dynamics::randja_compressor::RandjaCompressor, prelude::*, }; fn main() -> Result<()> { - let mut backend = JackBackend::new("Knyst<3JACK")?; + let mut backend = + CpalBackend::new(CpalBackendOptions::default()).expect("Unable to connect to CPAL backend"); + // Uncomment the line below and comment the line above to use the JACK backend instead + // let mut backend = JackBackend::new("Knyst<3JACK").expect("Unable to start JACK backend"); let sample_rate = backend.sample_rate() as Sample; let block_size = backend.block_size().unwrap_or(64); diff --git a/knyst/examples/envelopes/frequency_envelope.rs b/knyst/examples/envelopes/frequency_envelope.rs new file mode 100644 index 0000000..ab047c3 --- /dev/null +++ b/knyst/examples/envelopes/frequency_envelope.rs @@ -0,0 +1,71 @@ +use anyhow::Result; +use knyst::envelope::envelope_gen; +#[allow(unused)] +use knyst::{ + audio_backend::{CpalBackend, CpalBackendOptions, JackBackend}, + controller::print_error_handler, + prelude::*, +}; + +/// Configures a wavetable oscillator to output audio. The frequency is provided in an envelope. +/// +/// The function performs the following steps: +/// 1. Creates an owned wavetable oscillator set at 110 Hz. +/// 2. Outputs the oscillator to the left and right channels at 30% volume each. +/// 3. Frequency of the oscillator is changed by a frequency envelope. +/// 4. Waits for user to press ENTER. +/// +/// The envelope has a starting value of 0.0. The envelope consists of points that are in the +/// format (level, duration). The duration is given in seconds, and indicates the: time to reach the +/// point from the start value or from the preceding point. +/// +fn main() -> Result<()> { + let _backend = setup(); + + let frequency_envelope = envelope_gen( + 110.0, + vec![ + (220.0, 2.0), + (440.5, 2.), + (220.25, 4.0), + (77.5, 2.0), + (220.1, 2.0), + (330.0, 6.0), + ], + knyst::envelope::SustainMode::NoSustain, + StopAction::Continue, + ); + + let node0 = wavetable_oscillator_owned(Wavetable::sine()).freq(frequency_envelope); + + graph_output(0, node0 * 0.3); + graph_output(1, node0 * 0.3); + + println!("Playing a sine wave with an envelope at 110 Hz at an amplitude of 0.3"); + println!("Press [ENTER] to exit"); + let mut input = String::new(); + std::io::stdin().read_line(&mut input)?; + Ok(()) +} + +/// Initializes the audio backend and starts a `KnystSphere` for audio processing. +/// Start with an automatic helper thread for scheduling changes and managing resources. +/// If you want to manage the `Controller` yourself, use `start_return_controller`. +/// +/// The backend is returned here because it would otherwise be dropped at the end of setup() +fn setup() -> impl AudioBackend { + let mut backend = + CpalBackend::new(CpalBackendOptions::default()).expect("Unable to connect to CPAL backend"); + // Uncomment the line below and comment the line above to use the JACK backend instead + // let mut backend = JackBackend::new("Knyst<3JACK").expect("Unable to start JACK backend"); + + let _sphere_id = KnystSphere::start( + &mut backend, + SphereSettings { + ..Default::default() + }, + print_error_handler, + ) + .ok(); + backend +} diff --git a/knyst/examples/envelopes/volume_envelope.rs b/knyst/examples/envelopes/volume_envelope.rs new file mode 100644 index 0000000..d62646d --- /dev/null +++ b/knyst/examples/envelopes/volume_envelope.rs @@ -0,0 +1,70 @@ +use anyhow::Result; +use knyst::envelope::envelope_gen; +#[allow(unused)] +use knyst::{ + audio_backend::{CpalBackend, CpalBackendOptions, JackBackend}, + controller::print_error_handler, + prelude::*, +}; + +/// Configures a wavetable oscillator to output audio. The volume is provided in an envelope +/// +/// The function performs the following steps: +/// 1. Creates an owned wavetable oscillator set at 110 Hz. +/// 2. Outputs the oscillator to the left and right channels at 30% volume each + volume envelope. +/// 3. Waits for user to press ENTER. +/// +/// The envelope has a starting value of 0.0. The envelope consists of points that are in the +/// format (level, duration). The duration is given in seconds, and indicates the: time to reach the +/// point from the start value or from the preceding point. +/// +fn main() -> Result<()> { + let _backend = setup(); + + let node0 = wavetable_oscillator_owned(Wavetable::sine()).freq(110.); + + let volume_envelope = envelope_gen( + 0.0, + vec![ + (1.0, 2.0), + (0.5, 2.), + (0.25, 4.0), + (0.5, 2.0), + (0.1, 2.0), + (1.0, 6.0), + ], + knyst::envelope::SustainMode::NoSustain, + StopAction::Continue, + ); + + graph_output(0, node0 * 0.3 * volume_envelope); + graph_output(1, node0 * 0.3 * volume_envelope); + + println!("Playing a sine wave with an envelope at 110 Hz at an amplitude of 0.3"); + println!("Press [ENTER] to exit"); + let mut input = String::new(); + std::io::stdin().read_line(&mut input)?; + Ok(()) +} + +/// Initializes the audio backend and starts a `KnystSphere` for audio processing. +/// Start with an automatic helper thread for scheduling changes and managing resources. +/// If you want to manage the `Controller` yourself, use `start_return_controller`. +/// +/// The backend is returned here because it would otherwise be dropped at the end of setup() +fn setup() -> impl AudioBackend { + let mut backend = + CpalBackend::new(CpalBackendOptions::default()).expect("Unable to connect to CPAL backend"); + // Uncomment the line below and comment the line above to use the JACK backend instead + // let mut backend = JackBackend::new("Knyst<3JACK").expect("Unable to start JACK backend"); + + let _sphere_id = KnystSphere::start( + &mut backend, + SphereSettings { + ..Default::default() + }, + print_error_handler, + ) + .ok(); + backend +} diff --git a/knyst/examples/filter_test.rs b/knyst/examples/filter_test.rs index 9c96e38..ee58bec 100644 --- a/knyst/examples/filter_test.rs +++ b/knyst/examples/filter_test.rs @@ -10,18 +10,9 @@ use knyst::{ prelude::{delay::static_sample_delay, *}, sphere::{KnystSphere, SphereSettings}, }; + fn main() -> Result<()> { - let mut backend = CpalBackend::new(CpalBackendOptions::default())?; - // let mut backend = JackBackend::new("Knyst<3JACK")?; - let _sphere = KnystSphere::start( - &mut backend, - SphereSettings { - num_inputs: 1, - num_outputs: 1, - ..Default::default() - }, - print_error_handler, - ); + let _backend = setup(); let filter = svf_filter(SvfFilterType::Band, 1000., 10000.0, -20.) .input(graph_input(0, 1) + pink_noise()); @@ -64,3 +55,27 @@ fn main() -> Result<()> { std::io::stdin().read_line(&mut input)?; Ok(()) } + +/// Initializes the audio backend and starts a `KnystSphere` for audio processing. +/// Start with an automatic helper thread for scheduling changes and managing resources. +/// If you want to manage the `Controller` yourself, use `start_return_controller`. +/// +/// The backend is returned here because it would otherwise be dropped at the end of setup() +fn setup() -> impl AudioBackend { + let mut backend = + CpalBackend::new(CpalBackendOptions::default()).expect("Unable to connect to CPAL backend"); + // Uncomment the line below and comment the line above to use the JACK backend instead + // let mut backend = JackBackend::new("Knyst<3JACK").expect("Unable to start JACK backend"); + + let _sphere_id = KnystSphere::start( + &mut backend, + SphereSettings { + num_inputs: 1, + num_outputs: 1, + ..Default::default() + }, + print_error_handler, + ) + .ok(); + backend +} diff --git a/knyst/examples/more_advanced_example.rs b/knyst/examples/more_advanced_example.rs index c049bab..2efe84f 100644 --- a/knyst/examples/more_advanced_example.rs +++ b/knyst/examples/more_advanced_example.rs @@ -12,8 +12,10 @@ use knyst::{ }; use rand::{thread_rng, Rng}; fn main() -> Result<()> { - // let mut backend = CpalBackend::new(CpalBackendOptions::default())?; - let mut backend = JackBackend::new("Knyst<3JACK")?; + let mut backend = + CpalBackend::new(CpalBackendOptions::default()).expect("Unable to connect to CPAL backend"); + // Uncomment the line below and comment the line above to use the JACK backend instead + // let mut backend = JackBackend::new("Knyst<3JACK").expect("Unable to start JACK backend"); let _sphere = KnystSphere::start( &mut backend, SphereSettings {