diff --git a/examples/android/lib/src/lib.rs b/examples/android/lib/src/lib.rs index 169f1993..fd5ada04 100644 --- a/examples/android/lib/src/lib.rs +++ b/examples/android/lib/src/lib.rs @@ -2,7 +2,7 @@ extern crate servo_media; use servo_media::audio::gain_node::GainNodeOptions; use servo_media::audio::graph::AudioGraph; -use servo_media::audio::node::{AudioNodeMessage, AudioNodeType}; +use servo_media::audio::node::{AudioNodeMessage, AudioNodeInit}; use servo_media::audio::oscillator_node::OscillatorNodeMessage; use servo_media::ServoMedia; @@ -13,14 +13,14 @@ struct AudioStream { impl AudioStream { pub fn new() -> Self { let graph = ServoMedia::get().unwrap().create_audio_graph(); - graph.create_node(AudioNodeType::OscillatorNode(Default::default())); + graph.create_node(AudioNodeInit::OscillatorNode(Default::default())); graph.message_node( 0, AudioNodeMessage::OscillatorNode(OscillatorNodeMessage::Start(0.)), ); let mut options = GainNodeOptions::default(); options.gain = 0.5; - graph.create_node(AudioNodeType::GainNode(options)); + graph.create_node(AudioNodeInit::GainNode(options)); Self { graph } } diff --git a/examples/audio_decoder.rs b/examples/audio_decoder.rs index c8d0f986..f9b62a35 100644 --- a/examples/audio_decoder.rs +++ b/examples/audio_decoder.rs @@ -2,7 +2,7 @@ extern crate servo_media; use servo_media::audio::buffer_source_node::AudioBufferSourceNodeMessage; use servo_media::audio::decoder::AudioDecoderCallbacks; -use servo_media::audio::node::{AudioNodeMessage, AudioNodeType, AudioScheduledSourceNodeMessage}; +use servo_media::audio::node::{AudioNodeMessage, AudioNodeInit, AudioScheduledSourceNodeMessage}; use servo_media::ServoMedia; use std::env; use std::fs::File; @@ -43,7 +43,7 @@ fn run_example(servo_media: Arc) { println!("Decoding audio"); receiver.recv().unwrap(); println!("Audio decoded"); - let buffer_source = context.create_node(AudioNodeType::AudioBufferSourceNode(Default::default())); + let buffer_source = context.create_node(AudioNodeInit::AudioBufferSourceNode(Default::default())); let dest = context.dest_node(); context.connect_ports(buffer_source.output(0), dest.input(0)); context.message_node( diff --git a/examples/channels.rs b/examples/channels.rs index 2a051e2a..149bd9d1 100644 --- a/examples/channels.rs +++ b/examples/channels.rs @@ -2,7 +2,7 @@ extern crate servo_media; use servo_media::audio::channel_node::ChannelNodeOptions; use servo_media::audio::gain_node::GainNodeOptions; -use servo_media::audio::node::{AudioNodeMessage, AudioNodeType, AudioScheduledSourceNodeMessage}; +use servo_media::audio::node::{AudioNodeMessage, AudioNodeInit, AudioScheduledSourceNodeMessage}; use servo_media::ServoMedia; use std::sync::Arc; use std::{thread, time}; @@ -10,14 +10,14 @@ 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(AudioNodeType::OscillatorNode(options)); + let osc = context.create_node(AudioNodeInit::OscillatorNode(options)); options.freq = 213.; - let osc2 = context.create_node(AudioNodeType::OscillatorNode(options)); + let osc2 = context.create_node(AudioNodeInit::OscillatorNode(options)); let mut options = GainNodeOptions::default(); options.gain = 0.7; - let gain = context.create_node(AudioNodeType::GainNode(options)); + let gain = context.create_node(AudioNodeInit::GainNode(options)); let options = ChannelNodeOptions { channels: 2 }; - let merger = context.create_node(AudioNodeType::ChannelMergerNode(options)); + let merger = context.create_node(AudioNodeInit::ChannelMergerNode(options)); let dest = context.dest_node(); context.connect_ports(osc.output(0), gain.input(0)); diff --git a/examples/channelsum.rs b/examples/channelsum.rs index 48eb6861..89769bb4 100644 --- a/examples/channelsum.rs +++ b/examples/channelsum.rs @@ -2,7 +2,7 @@ extern crate servo_media; use servo_media::audio::channel_node::ChannelNodeOptions; use servo_media::audio::gain_node::GainNodeOptions; -use servo_media::audio::node::{AudioNodeMessage, AudioNodeType, AudioScheduledSourceNodeMessage}; +use servo_media::audio::node::{AudioNodeMessage, AudioNodeInit, AudioScheduledSourceNodeMessage}; use servo_media::ServoMedia; use std::sync::Arc; use std::{thread, time}; @@ -10,17 +10,17 @@ 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(AudioNodeType::OscillatorNode(options)); + let osc = context.create_node(AudioNodeInit::OscillatorNode(options)); options.freq = 213.; - let osc2 = context.create_node(AudioNodeType::OscillatorNode(options)); + let osc2 = context.create_node(AudioNodeInit::OscillatorNode(options)); options.freq = 100.; - let osc3 = context.create_node(AudioNodeType::OscillatorNode(options)); + let osc3 = context.create_node(AudioNodeInit::OscillatorNode(options)); let mut options = GainNodeOptions::default(); options.gain = 0.7; - let gain = context.create_node(AudioNodeType::GainNode(options)); + let gain = context.create_node(AudioNodeInit::GainNode(options)); let options = ChannelNodeOptions { channels: 2 }; - let merger = context.create_node(AudioNodeType::ChannelMergerNode(options)); + let merger = context.create_node(AudioNodeInit::ChannelMergerNode(options)); let dest = context.dest_node(); context.connect_ports(osc.output(0), merger.input(0)); diff --git a/examples/params.rs b/examples/params.rs index 5b86cbf5..769edfa5 100644 --- a/examples/params.rs +++ b/examples/params.rs @@ -1,9 +1,8 @@ extern crate servo_media; -use servo_media::audio::gain_node::{GainNodeMessage, GainNodeOptions}; -use servo_media::audio::node::{AudioNodeMessage, AudioNodeType, AudioScheduledSourceNodeMessage}; -use servo_media::audio::oscillator_node::OscillatorNodeMessage; -use servo_media::audio::param::{RampKind, UserAutomationEvent}; +use servo_media::audio::gain_node::GainNodeOptions; +use servo_media::audio::node::{AudioNodeMessage, AudioNodeInit, AudioScheduledSourceNodeMessage}; +use servo_media::audio::param::{ParamType, RampKind, UserAutomationEvent}; use servo_media::ServoMedia; use std::sync::Arc; use std::{thread, time}; @@ -11,10 +10,10 @@ 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 osc = context.create_node(AudioNodeType::OscillatorNode(Default::default())); + let osc = context.create_node(AudioNodeInit::OscillatorNode(Default::default())); let mut options = GainNodeOptions::default(); options.gain = 0.5; - let gain = context.create_node(AudioNodeType::GainNode(options)); + let gain = context.create_node(AudioNodeInit::GainNode(options)); context.connect_ports(osc.output(0), gain.input(0)); context.connect_ports(gain.output(0), dest.input(0)); let _ = context.resume(); @@ -25,59 +24,67 @@ fn run_example(servo_media: Arc) { // 0.5s: Set frequency to 110Hz context.message_node( osc, - AudioNodeMessage::OscillatorNode(OscillatorNodeMessage::SetFrequency( + AudioNodeMessage::SetParam( + ParamType::Frequency, UserAutomationEvent::SetValueAtTime(110., 0.5), - )), + ), ); // 1s: Set frequency to 220Hz context.message_node( osc, - AudioNodeMessage::OscillatorNode(OscillatorNodeMessage::SetFrequency( + AudioNodeMessage::SetParam( + ParamType::Frequency, UserAutomationEvent::SetValueAtTime(220., 1.), - )), + ), ); // 0.75s: Set gain to 0.25 context.message_node( gain, - AudioNodeMessage::GainNode(GainNodeMessage::SetGain( + AudioNodeMessage::SetParam( + ParamType::Gain, UserAutomationEvent::SetValueAtTime(0.25, 0.75), - )), + ), ); // 0.75s - 1.5s: Exponentially ramp gain to 1 context.message_node( gain, - AudioNodeMessage::GainNode(GainNodeMessage::SetGain( + AudioNodeMessage::SetParam( + ParamType::Gain, UserAutomationEvent::RampToValueAtTime(RampKind::Exponential, 1., 1.5), - )), + ), ); // 0.75s - 1.75s: Linearly ramp frequency to 880Hz context.message_node( osc, - AudioNodeMessage::OscillatorNode(OscillatorNodeMessage::SetFrequency( + AudioNodeMessage::SetParam( + ParamType::Frequency, UserAutomationEvent::RampToValueAtTime(RampKind::Linear, 880., 1.75), - )), + ), ); // 1.75s - 2.5s: Exponentially ramp frequency to 110Hz context.message_node( osc, - AudioNodeMessage::OscillatorNode(OscillatorNodeMessage::SetFrequency( + AudioNodeMessage::SetParam( + ParamType::Frequency, UserAutomationEvent::RampToValueAtTime(RampKind::Exponential, 110., 2.5), - )), + ), ); // 2.75s: Exponentially approach 110Hz context.message_node( osc, - AudioNodeMessage::OscillatorNode(OscillatorNodeMessage::SetFrequency( + AudioNodeMessage::SetParam( + ParamType::Frequency, UserAutomationEvent::SetTargetAtTime(1100., 2.75, 1.1), - )), + ), ); // 3.3s: But actually stop at 3.3Hz and hold context.message_node( osc, - AudioNodeMessage::OscillatorNode(OscillatorNodeMessage::SetFrequency( + AudioNodeMessage::SetParam( + ParamType::Frequency, UserAutomationEvent::CancelAndHoldAtTime(3.3), - )), + ), ); thread::sleep(time::Duration::from_millis(5000)); } diff --git a/examples/params_settarget.rs b/examples/params_settarget.rs index 98e57624..448f6c56 100644 --- a/examples/params_settarget.rs +++ b/examples/params_settarget.rs @@ -1,8 +1,7 @@ extern crate servo_media; -use servo_media::audio::node::{AudioNodeMessage, AudioNodeType, AudioScheduledSourceNodeMessage}; -use servo_media::audio::oscillator_node::OscillatorNodeMessage; -use servo_media::audio::param::{RampKind, UserAutomationEvent}; +use servo_media::audio::node::{AudioNodeMessage, AudioNodeInit, AudioScheduledSourceNodeMessage}; +use servo_media::audio::param::{ParamType, RampKind, UserAutomationEvent}; use servo_media::ServoMedia; use std::sync::Arc; use std::{thread, time}; @@ -10,7 +9,7 @@ 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 osc = context.create_node(AudioNodeType::OscillatorNode(Default::default())); + let osc = context.create_node(AudioNodeInit::OscillatorNode(Default::default())); context.connect_ports(osc.output(0), dest.input(0)); let _ = context.resume(); context.message_node( @@ -20,32 +19,36 @@ fn run_example(servo_media: Arc) { // 0.1s: Set frequency to 110Hz context.message_node( osc, - AudioNodeMessage::OscillatorNode(OscillatorNodeMessage::SetFrequency( + AudioNodeMessage::SetParam( + ParamType::Frequency, UserAutomationEvent::SetValueAtTime(110., 0.1), - )), + ), ); // 0.3s: Start increasing frequency to 440Hz exponentially with a time constant of 1 context.message_node( osc, - AudioNodeMessage::OscillatorNode(OscillatorNodeMessage::SetFrequency( + AudioNodeMessage::SetParam( + ParamType::Frequency, UserAutomationEvent::SetTargetAtTime(440., 0.3, 1.), - )), + ), ); // 1.5s: Start increasing frequency to 1760Hz exponentially // this event effectively doesn't happen, but instead sets a starting point // for the next ramp event context.message_node( osc, - AudioNodeMessage::OscillatorNode(OscillatorNodeMessage::SetFrequency( + AudioNodeMessage::SetParam( + ParamType::Frequency, UserAutomationEvent::SetTargetAtTime(1760., 1.5, 0.1), - )), + ), ); // 1.5s - 3s Linearly ramp down from the previous event (1.5s) to 110Hz context.message_node( osc, - AudioNodeMessage::OscillatorNode(OscillatorNodeMessage::SetFrequency( + AudioNodeMessage::SetParam( + ParamType::Frequency, UserAutomationEvent::RampToValueAtTime(RampKind::Linear, 110., 3.0), - )), + ), ); thread::sleep(time::Duration::from_millis(5000)); } diff --git a/examples/play.rs b/examples/play.rs index 09ead88c..2785c5d2 100644 --- a/examples/play.rs +++ b/examples/play.rs @@ -1,19 +1,18 @@ extern crate servo_media; -use servo_media::audio::gain_node::{GainNodeMessage, GainNodeOptions}; -use servo_media::audio::node::{AudioNodeMessage, AudioNodeType, AudioScheduledSourceNodeMessage}; -use servo_media::audio::oscillator_node::OscillatorNodeMessage; -use servo_media::audio::param::UserAutomationEvent; +use servo_media::audio::gain_node::{GainNodeOptions}; +use servo_media::audio::node::{AudioNodeMessage, AudioNodeInit, AudioScheduledSourceNodeMessage}; +use servo_media::audio::param::{ParamType, 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 osc = context.create_node(AudioNodeType::OscillatorNode(Default::default())); + let osc = context.create_node(AudioNodeInit::OscillatorNode(Default::default())); let mut options = GainNodeOptions::default(); options.gain = 0.5; - let gain = context.create_node(AudioNodeType::GainNode(options)); + let gain = context.create_node(AudioNodeInit::GainNode(options)); let dest = context.dest_node(); context.connect_ports(osc.output(0), gain.input(0)); context.connect_ports(gain.output(0), dest.input(0)); @@ -30,23 +29,26 @@ fn run_example(servo_media: Arc) { // 0.5s: Set frequency to 110Hz context.message_node( osc, - AudioNodeMessage::OscillatorNode(OscillatorNodeMessage::SetFrequency( + AudioNodeMessage::SetParam( + ParamType::Frequency, UserAutomationEvent::SetValueAtTime(110., 0.5), - )), + ), ); // 1s: Set frequency to 220Hz context.message_node( osc, - AudioNodeMessage::OscillatorNode(OscillatorNodeMessage::SetFrequency( + AudioNodeMessage::SetParam( + ParamType::Frequency, UserAutomationEvent::SetValueAtTime(220., 1.), - )), + ), ); // 0.75s: Set gain to 0.25 context.message_node( gain, - AudioNodeMessage::GainNode(GainNodeMessage::SetGain( + AudioNodeMessage::SetParam( + ParamType::Gain, UserAutomationEvent::SetValueAtTime(0.25, 0.75), - )), + ), ); thread::sleep(time::Duration::from_millis(1200)); // 1.2s: Suspend processing diff --git a/examples/play_noise.rs b/examples/play_noise.rs index 66e0998e..f2ee4aef 100644 --- a/examples/play_noise.rs +++ b/examples/play_noise.rs @@ -2,14 +2,14 @@ extern crate rand; extern crate servo_media; use servo_media::audio::buffer_source_node::AudioBufferSourceNodeMessage; -use servo_media::audio::node::{AudioNodeMessage, AudioNodeType, AudioScheduledSourceNodeMessage}; +use servo_media::audio::node::{AudioNodeMessage, AudioNodeInit, AudioScheduledSourceNodeMessage}; 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 buffer_source = context.create_node(AudioNodeType::AudioBufferSourceNode(Default::default())); + let buffer_source = context.create_node(AudioNodeInit::AudioBufferSourceNode(Default::default())); let dest = context.dest_node(); context.connect_ports(buffer_source.output(0), dest.input(0)); let mut buffers = vec![Vec::with_capacity(4096), Vec::with_capacity(4096)]; diff --git a/servo-media/src/audio/buffer_source_node.rs b/servo-media/src/audio/buffer_source_node.rs index 68455a54..3b7a2dcb 100644 --- a/servo-media/src/audio/buffer_source_node.rs +++ b/servo-media/src/audio/buffer_source_node.rs @@ -1,15 +1,13 @@ -use audio::node::ChannelInfo; +use audio::node::{AudioNodeType, ChannelInfo}; use audio::block::{Block, Chunk, Tick, FRAMES_PER_BLOCK}; use audio::node::{AudioNodeEngine, AudioScheduledSourceNodeMessage, BlockInfo}; -use audio::param::{Param, UserAutomationEvent}; +use audio::param::{Param, ParamType}; /// Control messages directed to AudioBufferSourceNodes. #[derive(Debug, Clone)] pub enum AudioBufferSourceNodeMessage { /// Set the data block holding the audio sample data to be played. SetBuffer(Option), - SetPlaybackRate(UserAutomationEvent), - SetDetune(UserAutomationEvent), } /// This specifies options for constructing an AudioBufferSourceNode. @@ -48,7 +46,7 @@ impl Default for AudioBufferSourceNodeOptions { /// XXX Implement playbackRate and related bits #[derive(AudioScheduledSourceNode, AudioNodeCommon)] #[allow(dead_code)] -pub struct AudioBufferSourceNode { +pub(crate) struct AudioBufferSourceNode { channel_info: ChannelInfo, /// A data block holding the audio sample data to be played. buffer: Option, @@ -89,17 +87,11 @@ impl AudioBufferSourceNode { } } - pub fn handle_message(&mut self, message: AudioBufferSourceNodeMessage, sample_rate: f32) { + pub fn handle_message(&mut self, message: AudioBufferSourceNodeMessage, _: f32) { match message { AudioBufferSourceNodeMessage::SetBuffer(buffer) => { self.buffer = buffer; }, - AudioBufferSourceNodeMessage::SetPlaybackRate(event) => { - self.playback_rate.insert_event(event.to_event(sample_rate)); - }, - AudioBufferSourceNodeMessage::SetDetune(event) => { - self.detune.insert_event(event.to_event(sample_rate)); - } } } @@ -116,6 +108,8 @@ impl AudioBufferSourceNode { } impl AudioNodeEngine for AudioBufferSourceNode { + fn node_type(&self) -> AudioNodeType { AudioNodeType::AudioBufferSourceNode } + fn input_count(&self) -> u32 { 0 } @@ -174,6 +168,14 @@ impl AudioNodeEngine for AudioBufferSourceNode { inputs } + fn get_param(&mut self, id: ParamType) -> &mut Param { + match id { + ParamType::PlaybackRate => &mut self.playback_rate, + ParamType::Detune => &mut self.detune, + _ => panic!("Unknown param {:?} for AudioBufferSourceNode", id) + } + } + make_message_handler!(AudioBufferSourceNode: handle_message, AudioScheduledSourceNode: handle_source_node_message); } diff --git a/servo-media/src/audio/channel_node.rs b/servo-media/src/audio/channel_node.rs index 53a75eda..4d3b126b 100644 --- a/servo-media/src/audio/channel_node.rs +++ b/servo-media/src/audio/channel_node.rs @@ -1,4 +1,5 @@ use audio::block::FRAMES_PER_BLOCK_USIZE; +use audio::node::AudioNodeType; use audio::node::{AudioNodeEngine, ChannelCountMode, ChannelInfo, ChannelInterpretation}; use audio::block::{Block, Chunk}; use audio::node::BlockInfo; @@ -9,7 +10,7 @@ pub struct ChannelNodeOptions { } #[derive(AudioNodeCommon)] -pub struct ChannelMergerNode { +pub(crate) struct ChannelMergerNode { channel_info: ChannelInfo, channels: u8 } @@ -28,6 +29,8 @@ impl ChannelMergerNode { } impl AudioNodeEngine for ChannelMergerNode { + fn node_type(&self) -> AudioNodeType { AudioNodeType::ChannelMergerNode } + fn process(&mut self, mut inputs: Chunk, _: &BlockInfo) -> Chunk { debug_assert!(inputs.len() == self.channels as usize); @@ -59,7 +62,7 @@ impl AudioNodeEngine for ChannelMergerNode { } #[derive(AudioNodeCommon)] -pub struct ChannelSplitterNode { +pub(crate) struct ChannelSplitterNode { channel_info: ChannelInfo, } @@ -76,6 +79,8 @@ impl ChannelSplitterNode { } impl AudioNodeEngine for ChannelSplitterNode { + fn node_type(&self) -> AudioNodeType { AudioNodeType::ChannelSplitterNode } + fn process(&mut self, mut inputs: Chunk, _: &BlockInfo) -> Chunk { debug_assert!(inputs.len() == 1); diff --git a/servo-media/src/audio/context.rs b/servo-media/src/audio/context.rs index 254ce785..64d8a42b 100644 --- a/servo-media/src/audio/context.rs +++ b/servo-media/src/audio/context.rs @@ -1,6 +1,6 @@ use audio::decoder::{AudioDecoder, AudioDecoderCallbacks, AudioDecoderOptions}; use audio::graph::{AudioGraph, InputPort, NodeId, OutputPort, PortId}; -use audio::node::{AudioNodeMessage, AudioNodeType}; +use audio::node::{AudioNodeMessage, AudioNodeInit}; use audio::render_thread::AudioRenderThread; use audio::render_thread::AudioRenderThreadMsg; use std::cell::Cell; @@ -154,7 +154,7 @@ impl AudioContext { rx.recv().unwrap() } - pub fn create_node(&self, node_type: AudioNodeType) -> NodeId { + pub fn create_node(&self, node_type: AudioNodeInit) -> NodeId { let (tx, rx) = mpsc::channel(); let _ = self.sender .send(AudioRenderThreadMsg::CreateNode(node_type, tx)); diff --git a/servo-media/src/audio/destination_node.rs b/servo-media/src/audio/destination_node.rs index 91bb0695..7fe3757e 100644 --- a/servo-media/src/audio/destination_node.rs +++ b/servo-media/src/audio/destination_node.rs @@ -1,10 +1,9 @@ -use audio::node::ChannelInfo; -use audio::node::ChannelCountMode; +use audio::node::{AudioNodeType, ChannelCountMode, ChannelInfo}; use audio::node::{AudioNodeEngine, BlockInfo}; use audio::block::Chunk; #[derive(AudioNodeCommon)] -pub struct DestinationNode { +pub(crate) struct DestinationNode { channel_info: ChannelInfo, chunk: Option } @@ -22,6 +21,8 @@ impl DestinationNode { } impl AudioNodeEngine for DestinationNode { + fn node_type(&self) -> AudioNodeType { AudioNodeType::DestinationNode } + fn process(&mut self, inputs: Chunk, _: &BlockInfo) -> Chunk { self.chunk = Some(inputs); Chunk::default() diff --git a/servo-media/src/audio/gain_node.rs b/servo-media/src/audio/gain_node.rs index 595933ef..498fd3db 100644 --- a/servo-media/src/audio/gain_node.rs +++ b/servo-media/src/audio/gain_node.rs @@ -1,14 +1,9 @@ -use audio::node::ChannelInfo; +use audio::node::{AudioNodeType, ChannelInfo}; use audio::block::Chunk; use audio::block::Tick; use audio::node::AudioNodeEngine; use audio::node::BlockInfo; -use audio::param::{Param, UserAutomationEvent}; - -#[derive(Debug, Clone)] -pub enum GainNodeMessage { - SetGain(UserAutomationEvent), -} +use audio::param::{Param, ParamType}; #[derive(Copy, Clone, Debug)] pub struct GainNodeOptions { @@ -22,7 +17,7 @@ impl Default for GainNodeOptions { } #[derive(AudioNodeCommon)] -pub struct GainNode { +pub(crate) struct GainNode { channel_info: ChannelInfo, gain: Param, } @@ -38,15 +33,12 @@ impl GainNode { pub fn update_parameters(&mut self, info: &BlockInfo, tick: Tick) -> bool { self.gain.update(info, tick) } - - pub fn handle_message(&mut self, message: GainNodeMessage, sample_rate: f32) { - match message { - GainNodeMessage::SetGain(event) => self.gain.insert_event(event.to_event(sample_rate)), - } - } } impl AudioNodeEngine for GainNode { + + fn node_type(&self) -> AudioNodeType { AudioNodeType::GainNode } + fn process(&mut self, mut inputs: Chunk, info: &BlockInfo) -> Chunk { debug_assert!(inputs.len() == 1); @@ -68,5 +60,11 @@ impl AudioNodeEngine for GainNode { inputs } - make_message_handler!(GainNode: handle_message); + + fn get_param(&mut self, id: ParamType) -> &mut Param { + match id { + ParamType::Gain => &mut self.gain, + _ => panic!("Unknown param {:?} for GainNode", id) + } + } } diff --git a/servo-media/src/audio/graph.rs b/servo-media/src/audio/graph.rs index 266d38d1..efdb08ee 100644 --- a/servo-media/src/audio/graph.rs +++ b/servo-media/src/audio/graph.rs @@ -7,7 +7,7 @@ use petgraph::graph::DefaultIx; use petgraph::stable_graph::NodeIndex; use petgraph::stable_graph::StableGraph; use petgraph::visit::{DfsPostOrder, EdgeRef, Reversed}; -use std::cell::{Ref, RefCell, RefMut}; +use std::cell::{RefCell, RefMut}; use std::cmp; #[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, Debug)] @@ -60,7 +60,7 @@ pub struct AudioGraph { dest_id: NodeId, } -pub struct Node { +pub(crate) struct Node { node: RefCell>, } @@ -71,7 +71,7 @@ pub struct Node { /// WebAudio allows for multiple connections to/from the same port /// however it does not allow for duplicate connections between pairs /// of ports -pub struct Edge { +pub(crate) struct Edge { connections: SmallVec<[Connection; 1]> } @@ -118,7 +118,7 @@ impl AudioGraph { } /// Create a node, obtain its id - pub fn add_node(&mut self, node: Box) -> NodeId { + pub(crate) fn add_node(&mut self, node: Box) -> NodeId { NodeId(self.graph.add_node(Node::new(node))) } @@ -361,14 +361,9 @@ impl AudioGraph { /// Obtain a mutable reference to a node - pub fn node_mut(&self, ix: NodeId) -> RefMut> { + pub(crate) fn node_mut(&self, ix: NodeId) -> RefMut> { self.graph[ix.0].node.borrow_mut() } - - /// Obtain an immutable reference to a node - pub fn node(&self, ix: NodeId) -> Ref> { - self.graph[ix.0].node.borrow() - } } impl Node { diff --git a/servo-media/src/audio/node.rs b/servo-media/src/audio/node.rs index dadddc97..1dfdde48 100644 --- a/servo-media/src/audio/node.rs +++ b/servo-media/src/audio/node.rs @@ -1,12 +1,13 @@ +use audio::param::{Param, ParamType, UserAutomationEvent}; use audio::channel_node::ChannelNodeOptions; use audio::block::{Chunk, Tick}; use audio::buffer_source_node::{AudioBufferSourceNodeMessage, AudioBufferSourceNodeOptions}; -use audio::gain_node::{GainNodeMessage, GainNodeOptions}; -use audio::oscillator_node::{OscillatorNodeMessage, OscillatorNodeOptions}; +use audio::gain_node::GainNodeOptions; +use audio::oscillator_node::OscillatorNodeOptions; -/// Type of AudioNodeEngine. +/// Information required to construct an audio node #[derive(Debug, Clone)] -pub enum AudioNodeType { +pub enum AudioNodeInit { AnalyserNode, BiquadFilterNode, AudioBuffer, @@ -28,6 +29,31 @@ pub enum AudioNodeType { WaveShaperNode, } +/// Type of AudioNodeEngine. +#[derive(Debug, Clone, Copy)] +pub enum AudioNodeType { + AnalyserNode, + BiquadFilterNode, + AudioBuffer, + AudioBufferSourceNode, + ChannelMergerNode, + ChannelSplitterNode, + ConstantSourceNode, + ConvolverNode, + DelayNode, + DestinationNode, + DynamicsCompressionNode, + GainNode, + IIRFilterNode, + OscillatorNode, + PannerNode, + PeriodicWave, + ScriptProcessorNode, + StereoPannerNode, + WaveShaperNode, +} + + #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum ChannelCountMode { Max, @@ -75,14 +101,16 @@ impl Default for ChannelInfo { } -pub trait AudioNodeCommon { +pub(crate) trait AudioNodeCommon { fn channel_info(&self) -> &ChannelInfo; fn channel_info_mut(&mut self) -> &mut ChannelInfo; } /// This trait represents the common features of all audio nodes. -pub trait AudioNodeEngine: Send + AudioNodeCommon { +pub(crate) trait AudioNodeEngine: Send + AudioNodeCommon { + fn node_type(&self) -> AudioNodeType; + fn process(&mut self, inputs: Chunk, info: &BlockInfo) -> Chunk; fn message(&mut self, msg: AudioNodeMessage, sample_rate: f32) { @@ -90,6 +118,9 @@ pub trait AudioNodeEngine: Send + AudioNodeCommon { AudioNodeMessage::SetChannelCount(c) => self.set_channel_count(c), AudioNodeMessage::SetChannelMode(c) => self.set_channel_count_mode(c), AudioNodeMessage::SetChannelInterpretation(c) => self.set_channel_interpretation(c), + AudioNodeMessage::SetParam(id, event) => { + self.get_param(id).insert_event(event.to_event(sample_rate)) + } _ => self.message_specific(msg, sample_rate), } } @@ -131,17 +162,20 @@ pub trait AudioNodeEngine: Send + AudioNodeCommon { fn destination_data(&mut self) -> Option { None } + + fn get_param(&mut self, _: ParamType) -> &mut Param { + panic!("No params on node {:?}", self.node_type()) + } } #[derive(Clone, Debug)] pub enum AudioNodeMessage { AudioBufferSourceNode(AudioBufferSourceNodeMessage), AudioScheduledSourceNode(AudioScheduledSourceNodeMessage), - GainNode(GainNodeMessage), - OscillatorNode(OscillatorNodeMessage), SetChannelCount(u8), SetChannelMode(ChannelCountMode), SetChannelInterpretation(ChannelInterpretation), + SetParam(ParamType, UserAutomationEvent) } /// This trait represents the common features of the source nodes such as diff --git a/servo-media/src/audio/oscillator_node.rs b/servo-media/src/audio/oscillator_node.rs index 535ecae5..2b7b7515 100644 --- a/servo-media/src/audio/oscillator_node.rs +++ b/servo-media/src/audio/oscillator_node.rs @@ -1,15 +1,9 @@ -use audio::node::ChannelInfo; +use audio::node::{AudioNodeType, ChannelInfo}; use audio::block::{Chunk, Tick}; use audio::node::{AudioNodeEngine, AudioScheduledSourceNodeMessage, BlockInfo}; -use audio::param::{Param, UserAutomationEvent}; +use audio::param::{Param, ParamType}; use num_traits::cast::NumCast; -#[derive(Copy, Clone, Debug)] -pub enum OscillatorNodeMessage { - SetDetune(UserAutomationEvent), - SetFrequency(UserAutomationEvent), -} - #[derive(Copy, Clone, Debug)] pub struct PeriodicWaveOptions { // XXX https://webaudio.github.io/web-audio-api/#dictdef-periodicwaveoptions @@ -44,9 +38,10 @@ impl Default for OscillatorNodeOptions { } #[derive(AudioScheduledSourceNode, AudioNodeCommon)] -pub struct OscillatorNode { +pub(crate) struct OscillatorNode { channel_info: ChannelInfo, frequency: Param, + detune: Param, phase: f64, /// Time at which the source should start playing. start_at: Option, @@ -60,6 +55,7 @@ impl OscillatorNode { Self { channel_info: Default::default(), frequency: Param::new(options.freq.into()), + detune: Param::new(options.detune.into()), phase: 0., start_at: None, stop_at: None, @@ -70,15 +66,6 @@ impl OscillatorNode { self.frequency.update(info, tick) } - pub fn handle_message(&mut self, message: OscillatorNodeMessage, sample_rate: f32) { - match message { - OscillatorNodeMessage::SetFrequency(event) => { - self.frequency.insert_event(event.to_event(sample_rate)) - } - _ => () - } - } - pub fn handle_source_node_message(&mut self, message: AudioScheduledSourceNodeMessage, sample_rate: f32) { match message { AudioScheduledSourceNodeMessage::Start(when) => { @@ -92,6 +79,9 @@ impl OscillatorNode { } impl AudioNodeEngine for OscillatorNode { + + fn node_type(&self) -> AudioNodeType { AudioNodeType::OscillatorNode } + fn process(&mut self, mut inputs: Chunk, info: &BlockInfo) -> Chunk { // XXX Implement this properly and according to self.options // as defined in https://webaudio.github.io/web-audio-api/#oscillatornode @@ -151,6 +141,13 @@ impl AudioNodeEngine for OscillatorNode { 0 } - make_message_handler!(AudioScheduledSourceNode: handle_source_node_message, - OscillatorNode: handle_message); + fn get_param(&mut self, id: ParamType) -> &mut Param { + match id { + ParamType::Frequency => &mut self.frequency, + ParamType::Detune => &mut self.detune, + _ => panic!("Unknown param {:?} for OscillatorNode", id) + } + } + + make_message_handler!(AudioScheduledSourceNode: handle_source_node_message); } diff --git a/servo-media/src/audio/param.rs b/servo-media/src/audio/param.rs index d0fc2ca4..ea3782a2 100644 --- a/servo-media/src/audio/param.rs +++ b/servo-media/src/audio/param.rs @@ -1,6 +1,14 @@ use audio::block::Tick; use audio::node::BlockInfo; +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub enum ParamType { + Frequency, + Detune, + Gain, + PlaybackRate +} + /// An AudioParam. /// /// https://webaudio.github.io/web-audio-api/#AudioParam @@ -109,6 +117,12 @@ impl Param { } pub(crate) fn insert_event(&mut self, event: AutomationEvent) { + if let AutomationEvent::SetValue(val) = event { + self.val = val; + self.event_start_value = val; + return; + } + let time = event.time(); let result = self.events.binary_search_by(|e| e.time().cmp(&time)); @@ -148,6 +162,7 @@ pub enum RampKind { /// https://webaudio.github.io/web-audio-api/#dfn-automation-event pub(crate) enum AutomationEvent { + SetValue(f32), SetValueAtTime(f32, Tick), RampToValueAtTime(RampKind, f32, Tick), SetTargetAtTime(f32, Tick, /* time constant, units of Tick */ f64), @@ -160,6 +175,7 @@ pub(crate) enum AutomationEvent { /// An AutomationEvent that uses times in s instead of Ticks pub enum UserAutomationEvent { + SetValue(f32), SetValueAtTime(f32, /* time */ f64), RampToValueAtTime(RampKind, f32, /* time */ f64), SetTargetAtTime(f32, f64, /* time constant, units of s */ f64), @@ -171,6 +187,7 @@ pub enum UserAutomationEvent { impl UserAutomationEvent { pub(crate) fn to_event(self, rate: f32) -> AutomationEvent { match self { + UserAutomationEvent::SetValue(val) => AutomationEvent::SetValue(val), UserAutomationEvent::SetValueAtTime(val, time) => AutomationEvent::SetValueAtTime(val, Tick::from_time(time, rate)), UserAutomationEvent::RampToValueAtTime(kind, val, time) => @@ -194,8 +211,8 @@ impl AutomationEvent { AutomationEvent::RampToValueAtTime(_, _, tick) => tick, AutomationEvent::SetTargetAtTime(_, start, _) => start, AutomationEvent::CancelAndHoldAtTime(t) => t, - AutomationEvent::CancelScheduledValues(..) => - unreachable!("CancelScheduledValues should never appear in the timeline"), + AutomationEvent::CancelScheduledValues(..) | AutomationEvent::SetValue(..) => + unreachable!("CancelScheduledValues/SetValue should never appear in the timeline"), } } @@ -205,8 +222,8 @@ impl AutomationEvent { AutomationEvent::RampToValueAtTime(_, _, tick) => Some(tick), AutomationEvent::SetTargetAtTime(..) => None, AutomationEvent::CancelAndHoldAtTime(t) => Some(t), - AutomationEvent::CancelScheduledValues(..) => - unreachable!("CancelScheduledValues should never appear in the timeline"), + AutomationEvent::CancelScheduledValues(..) | AutomationEvent::SetValue(..) => + unreachable!("CancelScheduledValues/SetValue should never appear in the timeline"), } } @@ -216,8 +233,8 @@ impl AutomationEvent { AutomationEvent::RampToValueAtTime(..) => None, AutomationEvent::SetTargetAtTime(_, start, _) => Some(start), AutomationEvent::CancelAndHoldAtTime(t) => Some(t), - AutomationEvent::CancelScheduledValues(..) => - unreachable!("CancelScheduledValues should never appear in the timeline"), + AutomationEvent::CancelScheduledValues(..) | AutomationEvent::SetValue(..) => + unreachable!("CancelScheduledValues/SetValue should never appear in the timeline"), } } @@ -276,8 +293,8 @@ impl AutomationEvent { AutomationEvent::CancelAndHoldAtTime(..) => { false } - AutomationEvent::CancelScheduledValues(..) => - unreachable!("CancelScheduledValues should never appear in the timeline"), + AutomationEvent::CancelScheduledValues(..) | AutomationEvent::SetValue(..) => + unreachable!("CancelScheduledValues/SetValue should never appear in the timeline"), } } } diff --git a/servo-media/src/audio/render_thread.rs b/servo-media/src/audio/render_thread.rs index 2f5c6689..45a9fda5 100644 --- a/servo-media/src/audio/render_thread.rs +++ b/servo-media/src/audio/render_thread.rs @@ -6,7 +6,7 @@ use audio::destination_node::DestinationNode; use audio::gain_node::GainNode; use audio::graph::{AudioGraph, NodeId, PortId, InputPort, OutputPort}; use audio::node::BlockInfo; -use audio::node::{AudioNodeEngine, AudioNodeMessage, AudioNodeType}; +use audio::node::{AudioNodeEngine, AudioNodeMessage, AudioNodeInit}; use audio::oscillator_node::OscillatorNode; use audio::sink::AudioSink; use std::sync::mpsc::{Receiver, Sender}; @@ -16,7 +16,7 @@ use backends::gstreamer::audio_sink::GStreamerAudioSink; #[derive(Debug)] pub enum AudioRenderThreadMsg { - CreateNode(AudioNodeType, Sender), + CreateNode(AudioNodeInit, Sender), ConnectPorts(PortId, PortId), MessageNode(NodeId, AudioNodeMessage), Resume(Sender), @@ -71,14 +71,14 @@ impl AudioRenderThread { make_render_thread_state_change!(suspend, Suspended, stop); - fn create_node(&mut self, node_type: AudioNodeType) -> NodeId { + fn create_node(&mut self, node_type: AudioNodeInit) -> NodeId { let node: Box = match node_type { - AudioNodeType::AudioBufferSourceNode(options) => Box::new(AudioBufferSourceNode::new(options)), - AudioNodeType::DestinationNode => Box::new(DestinationNode::new()), - AudioNodeType::GainNode(options) => Box::new(GainNode::new(options)), - AudioNodeType::OscillatorNode(options) => Box::new(OscillatorNode::new(options)), - AudioNodeType::ChannelMergerNode(options) => Box::new(ChannelMergerNode::new(options)), - AudioNodeType::ChannelSplitterNode(options) => Box::new(ChannelSplitterNode::new(options)), + AudioNodeInit::AudioBufferSourceNode(options) => Box::new(AudioBufferSourceNode::new(options)), + AudioNodeInit::DestinationNode => Box::new(DestinationNode::new()), + AudioNodeInit::GainNode(options) => Box::new(GainNode::new(options)), + AudioNodeInit::OscillatorNode(options) => Box::new(OscillatorNode::new(options)), + AudioNodeInit::ChannelMergerNode(options) => Box::new(ChannelMergerNode::new(options)), + AudioNodeInit::ChannelSplitterNode(options) => Box::new(ChannelSplitterNode::new(options)), _ => unimplemented!(), }; self.graph.add_node(node)