Skip to content

Commit 1defb2e

Browse files
authored
Merge pull request #475 from orottier/feature/script-processor
ScriptProcessorNode
2 parents 0d8a837 + e8b4630 commit 1defb2e

File tree

8 files changed

+440
-3
lines changed

8 files changed

+440
-3
lines changed

examples/script_processor.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use rand::Rng;
2+
3+
use web_audio_api::context::{
4+
AudioContext, AudioContextLatencyCategory, AudioContextOptions, BaseAudioContext,
5+
};
6+
use web_audio_api::node::{AudioNode, AudioScheduledSourceNode};
7+
8+
// ScriptProcessorNode example
9+
//
10+
// `cargo run --release --example script_processor`
11+
//
12+
// If you are on Linux and use ALSA as audio backend backend, you might want to run
13+
// the example with the `WEB_AUDIO_LATENCY=playback ` env variable which will
14+
// increase the buffer size to 1024
15+
//
16+
// `WEB_AUDIO_LATENCY=playback cargo run --release --example script_processor`
17+
fn main() {
18+
env_logger::init();
19+
20+
let latency_hint = match std::env::var("WEB_AUDIO_LATENCY").as_deref() {
21+
Ok("playback") => AudioContextLatencyCategory::Playback,
22+
_ => AudioContextLatencyCategory::default(),
23+
};
24+
25+
let context = AudioContext::new(AudioContextOptions {
26+
latency_hint,
27+
..AudioContextOptions::default()
28+
});
29+
30+
let node = context.create_script_processor(512, 1, 1);
31+
node.set_onaudioprocess(|e| {
32+
let mut rng = rand::thread_rng();
33+
e.output_buffer
34+
.get_channel_data_mut(0)
35+
.iter_mut()
36+
.zip(e.input_buffer.get_channel_data(0))
37+
.for_each(|(o, i)| *o = *i + rng.gen_range(-0.3..0.3));
38+
});
39+
40+
let mut src = context.create_oscillator();
41+
src.frequency().set_value(400.);
42+
src.start();
43+
src.connect(&node);
44+
node.connect(&context.destination());
45+
46+
std::thread::sleep(std::time::Duration::from_millis(5000));
47+
}

src/context/base.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,30 @@ pub trait BaseAudioContext {
214214
PeriodicWave::new(self.base(), options)
215215
}
216216

217+
/// Creates an `ScriptProcessorNode` for custom audio processing (deprecated);
218+
///
219+
/// # Panics
220+
///
221+
/// This function panics if:
222+
/// - `buffer_size` is not 256, 512, 1024, 2048, 4096, 8192, or 16384
223+
/// - the number of input and output channels are both zero
224+
/// - either of the channel counts exceed [`crate::MAX_CHANNELS`]
225+
#[must_use]
226+
fn create_script_processor(
227+
&self,
228+
buffer_size: usize,
229+
number_of_input_channels: usize,
230+
number_of_output_channels: usize,
231+
) -> node::ScriptProcessorNode {
232+
let options = node::ScriptProcessorOptions {
233+
buffer_size,
234+
number_of_input_channels,
235+
number_of_output_channels,
236+
};
237+
238+
node::ScriptProcessorNode::new(self.base(), options)
239+
}
240+
217241
/// Creates an `StereoPannerNode` to pan a stereo output
218242
#[must_use]
219243
fn create_stereo_panner(&self) -> node::StereoPannerNode {

src/context/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ impl From<u8> for AudioContextState {
9090
/// This object allows for communication with the render thread and dynamic lifetime management.
9191
//
9292
// The only way to construct this object is by calling [`BaseAudioContext::register`]
93+
#[derive(Clone)]
9394
pub struct AudioContextRegistration {
9495
/// the audio context in which nodes and connections lives
9596
context: ConcreteBaseAudioContext,

src/events.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub(crate) enum EventType {
2626
Diagnostics,
2727
Message(AudioNodeId),
2828
Complete,
29+
AudioProcessing(AudioNodeId),
2930
}
3031

3132
/// The Error Event interface
@@ -40,6 +41,19 @@ pub struct ErrorEvent {
4041
pub event: Event,
4142
}
4243

44+
/// The AudioProcessingEvent interface
45+
#[non_exhaustive]
46+
#[derive(Debug)]
47+
pub struct AudioProcessingEvent {
48+
/// The input buffer
49+
pub input_buffer: AudioBuffer,
50+
/// The output buffer
51+
pub output_buffer: AudioBuffer,
52+
/// The time when the audio will be played in the same time coordinate system as the
53+
/// AudioContext's currentTime.
54+
pub playback_time: f64,
55+
}
56+
4357
/// The OfflineAudioCompletionEvent Event interface
4458
#[non_exhaustive]
4559
#[derive(Debug)]
@@ -59,6 +73,7 @@ pub(crate) enum EventPayload {
5973
Message(Box<dyn Any + Send + 'static>),
6074
AudioContextState(AudioContextState),
6175
Complete(AudioBuffer),
76+
AudioProcessing(AudioProcessingEvent),
6277
}
6378

6479
#[derive(Debug)]
@@ -123,6 +138,13 @@ impl EventDispatch {
123138
payload: EventPayload::Complete(buffer),
124139
}
125140
}
141+
142+
pub fn audio_processing(id: AudioNodeId, value: AudioProcessingEvent) -> Self {
143+
EventDispatch {
144+
type_: EventType::AudioProcessing(id),
145+
payload: EventPayload::AudioProcessing(value),
146+
}
147+
}
126148
}
127149

128150
pub(crate) enum EventHandler {

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub mod media_streams;
3131
pub mod node;
3232

3333
mod events;
34-
pub use events::{ErrorEvent, Event, OfflineAudioCompletionEvent};
34+
pub use events::*;
3535

3636
mod message_port;
3737
pub use message_port::MessagePort;

src/node/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ mod oscillator;
4747
pub use oscillator::*;
4848
mod panner;
4949
pub use panner::*;
50+
mod script_processor;
51+
pub use script_processor::*;
5052
mod stereo_panner;
5153
pub use stereo_panner::*;
5254
mod waveshaper;

0 commit comments

Comments
 (0)