Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

compliant AudioBufferSourceNode and AudioBuffer #67

Merged
merged 38 commits into from
Dec 22, 2021
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b3d35f5
spec compliant AudioBuffer
b-ma Dec 11, 2021
a75c43b
Merge branch 'main' into feature/audio-buffer-source-node
b-ma Dec 11, 2021
8c7bb90
`decodeAudioData` first working draft
b-ma Dec 11, 2021
95412f6
clean
b-ma Dec 11, 2021
d36004d
cleaner architecture
b-ma Dec 11, 2021
8aed8da
renamed param tests for what they actually do
b-ma Dec 11, 2021
680f01c
comments
b-ma Dec 11, 2021
89eb34e
added fade-out to remove artifact at the end the file
b-ma Dec 12, 2021
e8d07e6
renamed `AudioBufferSourceNodeOptions` to `AudioBufferSourceOptions` …
b-ma Dec 12, 2021
1b9d5d2
AudioBufferSourcenode mvp
b-ma Dec 12, 2021
45d7421
pipe in gain
b-ma Dec 12, 2021
2dea81a
different style of examples
b-ma Dec 12, 2021
1f96d3c
descent AudioBufferSourceNode
b-ma Dec 13, 2021
2eaecd0
implemented all AudioBufferSourceNode interface + cleaned examples
b-ma Dec 14, 2021
e84868b
removed unused added code + fmt
b-ma Dec 14, 2021
95868ee
cleaned comments
b-ma Dec 14, 2021
5f468f4
cleaned comments + fixed clippy
b-ma Dec 14, 2021
7d758ef
fmt
b-ma Dec 14, 2021
f3e3610
fixed clippy (updated)
b-ma Dec 14, 2021
a60ce47
Implement forward/backward scrubbing example with original implementa…
orottier Dec 16, 2021
17a087d
cleaned doc
b-ma Dec 17, 2021
3ad5a5e
Merge branch 'feature/audio-buffer-source-node' of github.com:b-ma/we…
b-ma Dec 17, 2021
8ccabea
moved `decode_audio_data` in `BaseAudioContext` + merged `audio_buffe…
b-ma Dec 17, 2021
531aac5
test send buffer
b-ma Dec 17, 2021
2752872
cleaned and fixed doc examples
b-ma Dec 17, 2021
3f5803c
removed note
b-ma Dec 18, 2021
a61c064
merged orottier:main + fixed conflicts
b-ma Dec 18, 2021
4d8a252
added "rust" tag to doc example
b-ma Dec 18, 2021
fffa831
`no_run` tag instead of `rust`
b-ma Dec 18, 2021
3767de5
added AudioContext::create_buffer
b-ma Dec 18, 2021
1ef13c6
doc for `AudioContext::create_buffer`
b-ma Dec 18, 2021
b0d4f03
use array instead of Vec in `copy_from_channel`
b-ma Dec 18, 2021
836f06c
Merge remote-tracking branch 'upstream/main' into feature/audio-buffe…
b-ma Dec 19, 2021
a95422d
passed `ChannelData` and `AudioBuffer::from_channels` to `pub(crate)`…
b-ma Dec 19, 2021
e8eaeba
ChannelData is no longer public, add new constructor for AudioBuffer
orottier Dec 20, 2021
875f52f
cleaned doc and comments
b-ma Dec 20, 2021
3328b74
doc links
b-ma Dec 21, 2021
d7139ca
Review changes:
b-ma Dec 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,5 @@ fn main() {

let mut context = context;
let output = context.start_rendering();
assert_eq!(output.sample_len(), len);
assert_eq!(output.length(), len);
}
79 changes: 79 additions & 0 deletions examples/granular.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use rand::rngs::ThreadRng;
use rand::Rng;
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,
rng: &mut ThreadRng,
) {
// don't know the precision of sleep millis, but we add a small random
// offset to avoid audible pitch due to period (even if with such period it
// should be ok)
let jitter = rng.gen_range(0..1000) as f64 * 3e-6;
let start_time = audio_context.current_time() + jitter;

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 mut rng = rand::thread_rng();

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,
&mut rng,
);

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));
}
}
174 changes: 174 additions & 0 deletions examples/trigger_soundfile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
use std::fs::File;
use web_audio_api::context::{AsBaseAudioContext, AudioContext};
use web_audio_api::node::AudioNode;

// experimental API
// use web_audio_api::audio_buffer::decode_audio_data;

fn main() {
let context = AudioContext::new(None);
// @note - `resume` does not seem to be needed

// 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.