Skip to content

Commit

Permalink
Merge pull request #67 from b-ma/feature/audio-buffer-source-node
Browse files Browse the repository at this point in the history
compliant AudioBufferSourceNode and AudioBuffer
  • Loading branch information
orottier committed Dec 22, 2021
2 parents 6413407 + d7139ca commit 8107493
Show file tree
Hide file tree
Showing 17 changed files with 1,379 additions and 352 deletions.
80 changes: 0 additions & 80 deletions examples/bench.rs

This file was deleted.

64 changes: 64 additions & 0 deletions examples/granular.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use std::fs::File;
use std::{thread, time};
use web_audio_api::buffer::AudioBuffer;
use web_audio_api::context::{AsBaseAudioContext, AudioContext};
use web_audio_api::node::AudioNode;

// run in release mode
// cargo run --release --example granular

fn trigger_grain(
audio_context: &AudioContext,
audio_buffer: &AudioBuffer,
position: f64,
duration: f64,
) {
let start_time = audio_context.current_time();

let env = audio_context.create_gain();
env.gain().set_value(0.);
env.connect(&audio_context.destination());

let mut src = audio_context.create_buffer_source();
src.set_buffer(audio_buffer);
src.connect(&env);

// ramp
env.gain().set_value_at_time(0., start_time);
env.gain()
.linear_ramp_to_value_at_time(1., start_time + duration / 2.);
env.gain()
.linear_ramp_to_value_at_time(0., start_time + duration);

src.start_at_with_offset(start_time, position);
src.stop_at(start_time + duration);
}

fn main() {
let audio_context = AudioContext::new(None);

println!("++ scrub into file forward and backward at 0.5 speed");

// grab audio buffer
let file = File::open("sample.wav").unwrap();
let audio_buffer = audio_context.decode_audio_data(file);

let period = 0.05;
let grain_duration = 0.2;
let mut position = 0.;
let mut incr_position = period / 2.;

loop {
trigger_grain(&audio_context, &audio_buffer, position, grain_duration);

if position + incr_position > audio_buffer.duration() - (grain_duration * 2.)
|| position + incr_position < 0.
{
incr_position *= -1.;
}

position += incr_position;

thread::sleep(time::Duration::from_millis((period * 1000.) as u64));
}
}
171 changes: 171 additions & 0 deletions examples/trigger_soundfile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
use std::fs::File;
use web_audio_api::context::{AsBaseAudioContext, AudioContext};
use web_audio_api::node::AudioNode;

fn main() {
let context = AudioContext::new(None);
// @note - `context.resume` is not needed for now

// load and decode buffer
let file = File::open("sample.wav").unwrap();
let audio_buffer = context.decode_audio_data(file);

// @fixme - if only one node in the graph it is never removed even when returning
// false, se we put this dummy node in the graph so that other ones are properly
// removed
let mut src = context.create_buffer_source();
src.set_buffer(&audio_buffer);
src.connect(&context.destination());

{
println!("++ play until end");
let mut src = context.create_buffer_source();
src.set_buffer(&audio_buffer);
src.connect(&context.destination());
src.start_at(context.current_time());
}

std::thread::sleep(std::time::Duration::from_millis(3500));

{
println!("++ play / stop 1sec");
let mut src = context.create_buffer_source();
src.set_buffer(&audio_buffer);
src.connect(&context.destination());
src.start_at(context.current_time());
src.stop_at(context.current_time() + 1.);
}

std::thread::sleep(std::time::Duration::from_millis(1500));

{
println!("++ play / stop 1sec with offset");
let mut src = context.create_buffer_source();
src.set_buffer(&audio_buffer);
src.connect(&context.destination());
src.start_at_with_offset(context.current_time(), 1.);
src.stop_at(context.current_time() + 1.);
}

std::thread::sleep(std::time::Duration::from_millis(1500));

{
println!("++ play 1sec with offset and duration");
let mut src = context.create_buffer_source();
src.set_buffer(&audio_buffer);
src.connect(&context.destination());
src.start_at_with_offset_and_duration(context.current_time(), 1., 1.);
}

std::thread::sleep(std::time::Duration::from_millis(1500));

{
println!("++ play backward from offset 1.");
let mut src = context.create_buffer_source();
src.set_buffer(&audio_buffer);
src.connect(&context.destination());
src.playback_rate().set_value(-1.);
src.start_at_with_offset(context.current_time(), 1.);
}

std::thread::sleep(std::time::Duration::from_millis(1500));

{
println!("++ play backward full buffer");
let mut src = context.create_buffer_source();
src.set_buffer(&audio_buffer);
src.connect(&context.destination());
src.playback_rate().set_value(-1.);
src.start_at_with_offset(context.current_time(), audio_buffer.duration());
}

std::thread::sleep(std::time::Duration::from_millis(3500));

{
println!("++ simple loop (x2)");
let mut src = context.create_buffer_source();
src.set_buffer(&audio_buffer);
src.connect(&context.destination());
src.set_loop(true);
src.start_at(context.current_time());
src.stop_at(context.current_time() + audio_buffer.duration() * 2.);
}

std::thread::sleep(std::time::Duration::from_millis(7000));

{
println!("++ loop between 1 and 2 starting from 0");
let mut src = context.create_buffer_source();
src.set_buffer(&audio_buffer);
src.connect(&context.destination());
src.set_loop(true);
src.set_loop_start(1.);
src.set_loop_end(2.);
src.start_at(context.current_time());

std::thread::sleep(std::time::Duration::from_millis(4500));
src.set_loop(false);
}

std::thread::sleep(std::time::Duration::from_millis(2500));

{
println!("++ loop backward between 1 and 2 starting from end");
let mut src = context.create_buffer_source();
src.set_buffer(&audio_buffer);
src.connect(&context.destination());
src.playback_rate().set_value(-1.);
src.set_loop(true);
src.set_loop_start(1.);
src.set_loop_end(2.);
src.start_at_with_offset(context.current_time(), audio_buffer.duration());

std::thread::sleep(std::time::Duration::from_millis(4500));
src.set_loop(false);
}

std::thread::sleep(std::time::Duration::from_millis(2500));

println!("++ end of examples");

for i in 0..9 {
let offset = i as f64 / 2.;

let gain = if i % 4 == 0 { 1. } else { 0.2 };
let env = context.create_gain();
env.gain().set_value(gain);
env.connect(&context.destination());

let mut src = context.create_buffer_source();
src.set_buffer(&audio_buffer);
src.connect(&env);
src.start_at(context.current_time() + offset);
}

std::thread::sleep(std::time::Duration::from_millis(8000));

// some stress test
// let num_sources_by_sec = 100; // one source per 10ms
// // 100 is ok
// // 200 starts to click
// // 1000 is really not ok
// // ...let's agree 10ms is ok for descent granular synthesis

// for i in 0..num_sources_by_sec {
// // let offset = i as f64 / 1000.; // 10ms - look ok in --release
// let offset = i as f64 / num_sources_by_sec as f64;

// // this starts to look like home :)
// let gain = if i % 4 == 0 { 1. } else { 0.3 };
// let env = context.create_gain();
// env.gain().set_value(gain);
// env.connect(&context.destination());

// let mut src = context.create_buffer_source();
// src.set_buffer(&audio_buffer);
// src.connect(&env);
// src.start_at(context.current_time() + offset);
// }

// std::thread::sleep(std::time::Duration::from_secs(8));
}
Binary file modified sample.wav
Binary file not shown.

0 comments on commit 8107493

Please sign in to comment.