-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #67 from b-ma/feature/audio-buffer-source-node
compliant AudioBufferSourceNode and AudioBuffer
- Loading branch information
Showing
17 changed files
with
1,379 additions
and
352 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 not shown.
Oops, something went wrong.