diff --git a/.gitignore b/.gitignore index 6353f120..5ab528ea 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ **/*.rs.bk **/wget-log /gstreamer/ +.DS_Store diff --git a/audio/src/biquad_filter_node.rs b/audio/src/biquad_filter_node.rs index a54e3158..01fb33ca 100644 --- a/audio/src/biquad_filter_node.rs +++ b/audio/src/biquad_filter_node.rs @@ -5,7 +5,7 @@ use node::BlockInfo; use node::{AudioNodeMessage, AudioNodeType, ChannelInfo}; use param::{Param, ParamType}; use smallvec::SmallVec; -use std::f64::consts::{SQRT_2, PI}; +use std::f64::consts::{PI, SQRT_2}; #[derive(Copy, Clone, Debug)] pub struct BiquadFilterNodeOptions { @@ -147,7 +147,7 @@ impl BiquadFilterNode { self.b2 = 0.; self.a1 = 0.; self.a2 = 0.; - } + } /// Update the coefficients a1, a2, b0, b1, b2, given the sample_rate /// @@ -180,7 +180,7 @@ impl BiquadFilterNode { return; } else if normalized == 0. { self.constant_z_transform(0.); - return; + return; } } FilterType::HighPass => { @@ -189,7 +189,7 @@ impl BiquadFilterNode { return; } else if normalized == 0. { self.constant_z_transform(1.); - return; + return; } } FilterType::LowShelf => { @@ -198,7 +198,7 @@ impl BiquadFilterNode { return; } else if normalized == 0. { self.constant_z_transform(1.); - return; + return; } } FilterType::HighShelf => { @@ -207,7 +207,7 @@ impl BiquadFilterNode { return; } else if normalized == 0. { self.constant_z_transform(a * a); - return; + return; } } FilterType::Peaking => { diff --git a/audio/src/block.rs b/audio/src/block.rs index c8955bee..01d31c6b 100644 --- a/audio/src/block.rs +++ b/audio/src/block.rs @@ -315,10 +315,11 @@ impl Block { let mut v = Vec::with_capacity(FRAMES_PER_BLOCK_USIZE); for frame in 0..FRAMES_PER_BLOCK_USIZE { // output = 0.5 * (input.L + input.R + input.SL + input.SR); - let o = 0.25 * (self.data_chan_frame(frame, 0) - + self.data_chan_frame(frame, 1) - + self.data_chan_frame(frame, 2) - + self.data_chan_frame(frame, 3)); + let o = 0.25 + * (self.data_chan_frame(frame, 0) + + self.data_chan_frame(frame, 1) + + self.data_chan_frame(frame, 2) + + self.data_chan_frame(frame, 3)); v.push(o); } self.buffer = v; diff --git a/audio/src/buffer_source_node.rs b/audio/src/buffer_source_node.rs index 7c4fde3b..122e4d0f 100644 --- a/audio/src/buffer_source_node.rs +++ b/audio/src/buffer_source_node.rs @@ -129,7 +129,7 @@ impl AudioNodeEngine for AudioBufferSourceNode { inputs.blocks.push(Default::default()); return inputs; } - ShouldPlay::Between(start, end) => (start.0 as usize, end.0 as usize) + ShouldPlay::Between(start, end) => (start.0 as usize, end.0 as usize), }; let buffer = self.buffer.as_ref().unwrap(); diff --git a/audio/src/constant_source_node.rs b/audio/src/constant_source_node.rs new file mode 100644 index 00000000..8f567b31 --- /dev/null +++ b/audio/src/constant_source_node.rs @@ -0,0 +1,92 @@ +use block::Chunk; +use block::Tick; +use node::BlockInfo; +use node::{AudioNodeEngine, AudioScheduledSourceNodeMessage, OnEndedCallback}; +use node::{AudioNodeType, ChannelInfo, ShouldPlay}; +use param::{Param, ParamType}; + +#[derive(Copy, Clone, Debug)] +pub struct ConstantSourceNodeOptions { + pub offset: f32, +} + +impl Default for ConstantSourceNodeOptions { + fn default() -> Self { + ConstantSourceNodeOptions { offset: 1. } + } +} + +#[derive(AudioScheduledSourceNode, AudioNodeCommon)] +pub(crate) struct ConstantSourceNode { + channel_info: ChannelInfo, + offset: Param, + start_at: Option, + stop_at: Option, + onended_callback: Option, +} + +impl ConstantSourceNode { + pub fn new(options: ConstantSourceNodeOptions, channel_info: ChannelInfo) -> Self { + Self { + channel_info, + offset: Param::new(options.offset.into()), + start_at: None, + stop_at: None, + onended_callback: None, + } + } + + pub fn update_parameters(&mut self, info: &BlockInfo, tick: Tick) -> bool { + self.offset.update(info, tick) + } +} + +impl AudioNodeEngine for ConstantSourceNode { + fn node_type(&self) -> AudioNodeType { + AudioNodeType::ConstantSourceNode + } + + fn process(&mut self, mut inputs: Chunk, info: &BlockInfo) -> Chunk { + debug_assert!(inputs.len() == 0); + + inputs.blocks.push(Default::default()); + + let (start_at, stop_at) = match self.should_play_at(info.frame) { + ShouldPlay::No => { + return inputs; + } + ShouldPlay::Between(start, end) => (start, end), + }; + + { + inputs.blocks[0].explicit_silence(); + + let mut iter = inputs.blocks[0].iter(); + let mut offset = self.offset.value(); + while let Some(mut frame) = iter.next() { + let tick = frame.tick(); + if tick < start_at { + continue; + } else if tick > stop_at { + break; + } + if self.update_parameters(info, frame.tick()) { + offset = self.offset.value(); + } + frame.mutate_with(|sample, _| *sample = offset); + } + } + inputs + } + fn input_count(&self) -> u32 { + 0 + } + + fn get_param(&mut self, id: ParamType) -> &mut Param { + match id { + ParamType::Offset => &mut self.offset, + _ => panic!("Unknown param {:?} for the offset", id), + } + } + make_message_handler!(AudioScheduledSourceNode: handle_source_node_message); +} diff --git a/audio/src/context.rs b/audio/src/context.rs index f126a916..797a1e81 100644 --- a/audio/src/context.rs +++ b/audio/src/context.rs @@ -142,8 +142,7 @@ impl AudioContext { graph, options, ); - }) - .unwrap(); + }).unwrap(); Self { sender, state: Cell::new(ProcessingState::Suspended), @@ -265,8 +264,7 @@ impl AudioContext { let audio_decoder = B::make_decoder(); audio_decoder.decode(data, callbacks, Some(options)); - }) - .unwrap(); + }).unwrap(); } pub fn set_eos_callback(&self, callback: Box>) + Send + Sync + 'static>) { diff --git a/audio/src/lib.rs b/audio/src/lib.rs index dba031a8..fccfc9f0 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -20,6 +20,7 @@ pub mod biquad_filter_node; pub mod block; pub mod buffer_source_node; pub mod channel_node; +pub mod constant_source_node; pub mod context; pub mod decoder; pub mod destination_node; diff --git a/audio/src/node.rs b/audio/src/node.rs index 10ba8be0..b898c264 100644 --- a/audio/src/node.rs +++ b/audio/src/node.rs @@ -3,6 +3,7 @@ use block::{Block, Chunk, Tick}; use boxfnonce::SendBoxFnOnce; use buffer_source_node::{AudioBufferSourceNodeMessage, AudioBufferSourceNodeOptions}; use channel_node::ChannelNodeOptions; +use constant_source_node::ConstantSourceNodeOptions; use gain_node::GainNodeOptions; use oscillator_node::OscillatorNodeOptions; use panner_node::{PannerNodeMessage, PannerNodeOptions}; @@ -17,7 +18,7 @@ pub enum AudioNodeInit { AudioBufferSourceNode(AudioBufferSourceNodeOptions), ChannelMergerNode(ChannelNodeOptions), ChannelSplitterNode, - ConstantSourceNode, + ConstantSourceNode(ConstantSourceNodeOptions), ConvolverNode, DelayNode, DynamicsCompressionNode, diff --git a/audio/src/oscillator_node.rs b/audio/src/oscillator_node.rs index e27b854a..50866b85 100644 --- a/audio/src/oscillator_node.rs +++ b/audio/src/oscillator_node.rs @@ -4,12 +4,11 @@ use node::{AudioNodeType, ChannelInfo, ShouldPlay}; use num_traits::cast::NumCast; use param::{Param, ParamType}; -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] pub struct PeriodicWaveOptions { // XXX https://webaudio.github.io/web-audio-api/#dictdef-periodicwaveoptions } - -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] pub enum OscillatorType { Sine, Square, @@ -18,7 +17,7 @@ pub enum OscillatorType { Custom, } -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] pub struct OscillatorNodeOptions { pub oscillator_type: OscillatorType, pub freq: f32, diff --git a/audio/src/param.rs b/audio/src/param.rs index 2e6542ae..0dfa0364 100644 --- a/audio/src/param.rs +++ b/audio/src/param.rs @@ -14,6 +14,7 @@ pub enum ParamType { Forward(ParamDir), Up(ParamDir), Orientation(ParamDir), + Offset, } #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] diff --git a/audio/src/render_thread.rs b/audio/src/render_thread.rs index f5575959..ee3281c6 100644 --- a/audio/src/render_thread.rs +++ b/audio/src/render_thread.rs @@ -3,6 +3,7 @@ use biquad_filter_node::BiquadFilterNode; use block::{Chunk, Tick, FRAMES_PER_BLOCK}; use buffer_source_node::AudioBufferSourceNode; use channel_node::{ChannelMergerNode, ChannelSplitterNode}; +use constant_source_node::ConstantSourceNode; use context::{AudioContextOptions, ProcessingState, StateChangeResult}; use gain_node::GainNode; use graph::{AudioGraph, InputPort, NodeId, OutputPort, PortId}; @@ -169,7 +170,7 @@ impl AudioRenderThread { graph, options, ).map_err(|_| ()) - .unwrap(); + .unwrap(); thread.event_loop(event_queue) } } @@ -198,6 +199,9 @@ impl AudioRenderThread { AudioNodeInit::ChannelMergerNode(options) => { Box::new(ChannelMergerNode::new(options, ch)) } + AudioNodeInit::ConstantSourceNode(options) => { + Box::new(ConstantSourceNode::new(options, ch)) + } AudioNodeInit::ChannelSplitterNode => Box::new(ChannelSplitterNode::new(ch)), _ => unimplemented!(), }; diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 52cdd3c9..69535092 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -76,3 +76,7 @@ path = "simple_player.rs" [[bin]] name = "oscillator" path = "oscillator.rs" + +[[bin]] +name = "constant_source" +path = "constant_source.rs" diff --git a/examples/biquad.rs b/examples/biquad.rs index 147292c0..a9b3e0be 100644 --- a/examples/biquad.rs +++ b/examples/biquad.rs @@ -15,9 +15,15 @@ fn run_example(servo_media: Arc) { let dest = context.dest_node(); let mut options = OscillatorNodeOptions::default(); options.freq = 100.; - let osc1 = context.create_node(AudioNodeInit::OscillatorNode(options), Default::default()); + let osc1 = context.create_node( + AudioNodeInit::OscillatorNode(options.clone()), + Default::default(), + ); options.freq = 800.; - let osc2 = context.create_node(AudioNodeInit::OscillatorNode(options), Default::default()); + let osc2 = context.create_node( + AudioNodeInit::OscillatorNode(options.clone()), + Default::default(), + ); let mut options = BiquadFilterNodeOptions::default(); options.frequency = 50.; options.filter = FilterType::LowPass; diff --git a/examples/channels.rs b/examples/channels.rs index 816a3b1d..32c1f2cc 100644 --- a/examples/channels.rs +++ b/examples/channels.rs @@ -3,19 +3,26 @@ extern crate servo_media; use servo_media::audio::channel_node::ChannelNodeOptions; use servo_media::audio::gain_node::GainNodeOptions; use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioScheduledSourceNodeMessage}; +use servo_media::audio::oscillator_node::OscillatorNodeOptions; use servo_media::ServoMedia; use std::sync::Arc; use std::{thread, time}; fn run_example(servo_media: Arc) { let context = servo_media.create_audio_context(Default::default()); - let mut options = Default::default(); - let osc = context.create_node(AudioNodeInit::OscillatorNode(options), Default::default()); + let mut options = OscillatorNodeOptions::default(); + let osc = context.create_node( + AudioNodeInit::OscillatorNode(options.clone()), + Default::default(), + ); options.freq = 213.; - let osc2 = context.create_node(AudioNodeInit::OscillatorNode(options), Default::default()); + let osc2 = context.create_node( + AudioNodeInit::OscillatorNode(options), + Default::default(), + ); let mut options = GainNodeOptions::default(); options.gain = 0.7; - let gain = context.create_node(AudioNodeInit::GainNode(options), Default::default()); + let gain = context.create_node(AudioNodeInit::GainNode(options.clone()), Default::default()); let options = ChannelNodeOptions { channels: 2 }; let merger = context.create_node( AudioNodeInit::ChannelMergerNode(options), diff --git a/examples/channelsum.rs b/examples/channelsum.rs index e7c71d53..130f87ad 100644 --- a/examples/channelsum.rs +++ b/examples/channelsum.rs @@ -3,21 +3,31 @@ extern crate servo_media; use servo_media::audio::channel_node::ChannelNodeOptions; use servo_media::audio::gain_node::GainNodeOptions; use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioScheduledSourceNodeMessage}; +use servo_media::audio::oscillator_node::OscillatorNodeOptions; use servo_media::ServoMedia; use std::sync::Arc; use std::{thread, time}; fn run_example(servo_media: Arc) { let context = servo_media.create_audio_context(Default::default()); - let mut options = Default::default(); - let osc = context.create_node(AudioNodeInit::OscillatorNode(options), Default::default()); + let mut options = OscillatorNodeOptions::default(); + let osc = context.create_node( + AudioNodeInit::OscillatorNode(options.clone()), + Default::default(), + ); options.freq = 213.; - let osc2 = context.create_node(AudioNodeInit::OscillatorNode(options), Default::default()); + let osc2 = context.create_node( + AudioNodeInit::OscillatorNode(options.clone()), + Default::default(), + ); options.freq = 100.; - let osc3 = context.create_node(AudioNodeInit::OscillatorNode(options), Default::default()); + let osc3 = context.create_node( + AudioNodeInit::OscillatorNode(options), + Default::default(), + ); let mut options = GainNodeOptions::default(); options.gain = 0.7; - let gain = context.create_node(AudioNodeInit::GainNode(options), Default::default()); + let gain = context.create_node(AudioNodeInit::GainNode(options.clone()), Default::default()); let options = ChannelNodeOptions { channels: 2 }; let merger = context.create_node( diff --git a/examples/constant_source.rs b/examples/constant_source.rs new file mode 100644 index 00000000..82be67f7 --- /dev/null +++ b/examples/constant_source.rs @@ -0,0 +1,94 @@ +extern crate servo_media; + +use servo_media::audio::constant_source_node::ConstantSourceNodeOptions; +use servo_media::audio::gain_node::GainNodeOptions; +use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioScheduledSourceNodeMessage}; +use servo_media::audio::param::{ParamType, RampKind, UserAutomationEvent}; +use servo_media::ServoMedia; +use std::sync::Arc; +use std::{thread, time}; + +fn run_example(servo_media: Arc) { + let context = servo_media.create_audio_context(Default::default()); + let dest = context.dest_node(); + + let cs = context.create_node( + AudioNodeInit::ConstantSourceNode(ConstantSourceNodeOptions::default()), + Default::default(), + ); + + let mut gain_options = GainNodeOptions::default(); + gain_options.gain = 0.1; + let gain = context.create_node( + AudioNodeInit::GainNode(gain_options.clone()), + Default::default(), + ); + + let osc = context.create_node( + AudioNodeInit::OscillatorNode(Default::default()), + Default::default(), + ); + + context.connect_ports(osc.output(0), gain.input(0)); + context.connect_ports(cs.output(0), gain.param(ParamType::Gain)); + context.connect_ports(gain.output(0), dest.input(0)); + + let _ = context.resume(); + context.message_node( + osc, + AudioNodeMessage::AudioScheduledSourceNode(AudioScheduledSourceNodeMessage::Start(0.)), + ); + + context.message_node( + gain, + AudioNodeMessage::AudioScheduledSourceNode(AudioScheduledSourceNodeMessage::Start(0.)), + ); + + context.message_node( + cs, + AudioNodeMessage::AudioScheduledSourceNode(AudioScheduledSourceNodeMessage::Start(0.)), + ); + + context.message_node( + cs, + AudioNodeMessage::SetParam( + ParamType::Offset, + UserAutomationEvent::RampToValueAtTime(RampKind::Linear, 1., 1.5), + ), + ); + + context.message_node( + cs, + AudioNodeMessage::SetParam( + ParamType::Offset, + UserAutomationEvent::RampToValueAtTime(RampKind::Linear, 0.1, 3.0), + ), + ); + + context.message_node( + cs, + AudioNodeMessage::SetParam( + ParamType::Offset, + UserAutomationEvent::RampToValueAtTime(RampKind::Linear, 1., 4.5), + ), + ); + + context.message_node( + cs, + AudioNodeMessage::SetParam( + ParamType::Offset, + UserAutomationEvent::RampToValueAtTime(RampKind::Linear, 0.1, 6.0), + ), + ); + + thread::sleep(time::Duration::from_millis(9000)); + let _ = context.close(); +} + +fn main() { + if let Ok(servo_media) = ServoMedia::get() { + run_example(servo_media); + } else { + unreachable!(); + } +} diff --git a/examples/oscillator.rs b/examples/oscillator.rs index 7e7a0015..e5b70f05 100644 --- a/examples/oscillator.rs +++ b/examples/oscillator.rs @@ -2,11 +2,13 @@ extern crate servo_media; use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioScheduledSourceNodeMessage}; use servo_media::audio::oscillator_node::OscillatorNodeOptions; -use servo_media::audio::oscillator_node::OscillatorType::Sawtooth; -use servo_media::audio::oscillator_node::OscillatorType::Triangle; -//use servo_media::audio::oscillator_node::OscillatorType::Sine; + +//use servo_media::audio::oscillator_node::PeriodicWaveOptions; + use servo_media::audio::oscillator_node::OscillatorType::Custom; +use servo_media::audio::oscillator_node::OscillatorType::Sawtooth; use servo_media::audio::oscillator_node::OscillatorType::Square; +use servo_media::audio::oscillator_node::OscillatorType::Triangle; use servo_media::ServoMedia; use std::sync::Arc; use std::{thread, time}; @@ -15,7 +17,10 @@ fn run_example(servo_media: Arc) { let context = servo_media.create_audio_context(Default::default()); let dest = context.dest_node(); let mut options = OscillatorNodeOptions::default(); - let osc1 = context.create_node(AudioNodeInit::OscillatorNode(options), Default::default()); + let osc1 = context.create_node( + AudioNodeInit::OscillatorNode(options.clone()), + Default::default(), + ); context.connect_ports(osc1.output(0), dest.input(0)); let _ = context.resume(); context.message_node( @@ -30,7 +35,10 @@ fn run_example(servo_media: Arc) { options.oscillator_type = Square; let context = servo_media.create_audio_context(Default::default()); let dest = context.dest_node(); - let osc2 = context.create_node(AudioNodeInit::OscillatorNode(options), Default::default()); + let osc2 = context.create_node( + AudioNodeInit::OscillatorNode(options.clone()), + Default::default(), + ); context.connect_ports(osc2.output(0), dest.input(0)); let _ = context.resume(); context.message_node( @@ -45,7 +53,10 @@ fn run_example(servo_media: Arc) { options.oscillator_type = Sawtooth; let context = servo_media.create_audio_context(Default::default()); let dest = context.dest_node(); - let osc3 = context.create_node(AudioNodeInit::OscillatorNode(options), Default::default()); + let osc3 = context.create_node( + AudioNodeInit::OscillatorNode(options.clone()), + Default::default(), + ); context.connect_ports(osc3.output(0), dest.input(0)); thread::sleep(time::Duration::from_millis(3000)); @@ -61,7 +72,10 @@ fn run_example(servo_media: Arc) { options.oscillator_type = Triangle; let context = servo_media.create_audio_context(Default::default()); let dest = context.dest_node(); - let osc4 = context.create_node(AudioNodeInit::OscillatorNode(options), Default::default()); + let osc4 = context.create_node( + AudioNodeInit::OscillatorNode(options.clone()), + Default::default(), + ); context.connect_ports(osc4.output(0), dest.input(0)); thread::sleep(time::Duration::from_millis(3000)); @@ -70,24 +84,31 @@ fn run_example(servo_media: Arc) { osc4, AudioNodeMessage::AudioScheduledSourceNode(AudioScheduledSourceNodeMessage::Start(0.)), ); - thread::sleep(time::Duration::from_millis(3000)); let _ = context.close(); - thread::sleep(time::Duration::from_millis(1000)); + + thread::sleep(time::Duration::from_millis(3000)); options.oscillator_type = Custom; + let context = servo_media.create_audio_context(Default::default()); let dest = context.dest_node(); - let osc5 = context.create_node(AudioNodeInit::OscillatorNode(options), Default::default()); + let osc5 = context.create_node( + AudioNodeInit::OscillatorNode(options.clone()), + Default::default(), + ); context.connect_ports(osc5.output(0), dest.input(0)); thread::sleep(time::Duration::from_millis(3000)); let _ = context.resume(); context.message_node( - osc5, + osc4, AudioNodeMessage::AudioScheduledSourceNode(AudioScheduledSourceNodeMessage::Start(0.)), ); + thread::sleep(time::Duration::from_millis(3000)); + let _ = context.close(); + thread::sleep(time::Duration::from_millis(1000)); } fn main() {