diff --git a/audio/src/block.rs b/audio/src/block.rs index ffa1f803..02e074f4 100644 --- a/audio/src/block.rs +++ b/audio/src/block.rs @@ -1,10 +1,10 @@ -use std::f32::consts::SQRT_2; -use node::ChannelInterpretation; -use graph::PortIndex; use byte_slice_cast::*; +use graph::PortIndex; +use node::ChannelInterpretation; use smallvec::SmallVec; -use std::ops::*; +use std::f32::consts::SQRT_2; use std::mem; +use std::ops::*; // defined by spec // https://webaudio.github.io/web-audio-api/#render-quantum @@ -48,7 +48,7 @@ impl Chunk { pub struct Block { /// The number of channels in this block channels: u8, - /// This is an optimization which means that the buffer is representing multiple channels with the + /// This is an optimization which means that the buffer is representing multiple channels with the /// same content at once. Happens when audio is upmixed or when a source like /// an oscillator node has multiple channel outputs repeat: bool, @@ -60,7 +60,6 @@ pub struct Block { buffer: Vec, } - impl Default for Block { fn default() -> Self { Block { @@ -72,7 +71,6 @@ impl Default for Block { } impl Block { - /// Empty block with no channels, for pushing /// new channels to. /// @@ -133,7 +131,8 @@ impl Block { self.buffer = new; self.repeat = false; } else if self.is_silence() { - self.buffer.resize(FRAMES_PER_BLOCK_USIZE * self.channels as usize, 0.); + self.buffer + .resize(FRAMES_PER_BLOCK_USIZE * self.channels as usize, 0.); } } @@ -175,8 +174,8 @@ impl Block { if self.is_silence() { 0. } else { - self.data_chan(chan)[frame] - } + self.data_chan(chan)[frame] + } } pub fn push_chan(&mut self, data: &[f32]) { @@ -194,13 +193,13 @@ impl Block { // If we're not changing the number of channels, we // don't actually need to mix if self.channels == channels { - return + return; } // Silent buffers stay silent if self.is_silence() { self.channels = channels; - return + return; } if interpretation == ChannelInterpretation::Discrete { @@ -269,11 +268,11 @@ impl Block { let mut v = Vec::with_capacity(6 * FRAMES_PER_BLOCK_USIZE); // output.{L, R} = input.{L, R} - v.extend(&self.buffer[0 .. 2 * FRAMES_PER_BLOCK_USIZE]); + v.extend(&self.buffer[0..2 * FRAMES_PER_BLOCK_USIZE]); // output.{C, LFE} = 0 v.resize(4 * FRAMES_PER_BLOCK_USIZE, 0.); // output.{SL, R} = input.{SL, SR} - v.extend(&self.buffer[2 * FRAMES_PER_BLOCK_USIZE ..]); + v.extend(&self.buffer[2 * FRAMES_PER_BLOCK_USIZE..]); self.buffer = v; self.channels = channels; } @@ -286,7 +285,8 @@ 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); - let o = 0.5 * (self.data_chan_frame(frame, 0) + self.data_chan_frame(frame, 1)); + let o = + 0.5 * (self.data_chan_frame(frame, 0) + self.data_chan_frame(frame, 1)); v.push(o); } self.buffer = v; @@ -297,10 +297,10 @@ 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; @@ -350,13 +350,13 @@ impl Block { v.resize(2 * FRAMES_PER_BLOCK_USIZE, 0.); for frame in 0..FRAMES_PER_BLOCK_USIZE { // output.L = L + sqrt(0.5) * (input.C + input.SL) - v[frame] = - self.data_chan_frame(frame, 0) + - SQRT_2 * (self.data_chan_frame(frame, 2) + self.data_chan_frame(frame, 4)); + v[frame] = self.data_chan_frame(frame, 0) + + SQRT_2 + * (self.data_chan_frame(frame, 2) + self.data_chan_frame(frame, 4)); // output.R = R + sqrt(0.5) * (input.C + input.SR) - v[frame + FRAMES_PER_BLOCK_USIZE] = - self.data_chan_frame(frame, 1) + - SQRT_2 * (self.data_chan_frame(frame, 2) + self.data_chan_frame(frame, 5)); + v[frame + FRAMES_PER_BLOCK_USIZE] = self.data_chan_frame(frame, 1) + + SQRT_2 + * (self.data_chan_frame(frame, 2) + self.data_chan_frame(frame, 5)); } self.buffer = v; self.channels = 2; @@ -369,19 +369,15 @@ impl Block { v.resize(6 * FRAMES_PER_BLOCK_USIZE, 0.); for frame in 0..FRAMES_PER_BLOCK_USIZE { // output.L = L + sqrt(0.5) * input.C - v[frame] = - self.data_chan_frame(frame, 0) + - SQRT_2 * self.data_chan_frame(frame, 2); + v[frame] = self.data_chan_frame(frame, 0) + + SQRT_2 * self.data_chan_frame(frame, 2); // output.R = R + sqrt(0.5) * input.C - v[frame + FRAMES_PER_BLOCK_USIZE] = - self.data_chan_frame(frame, 1) + - SQRT_2 * self.data_chan_frame(frame, 2); + v[frame + FRAMES_PER_BLOCK_USIZE] = self.data_chan_frame(frame, 1) + + SQRT_2 * self.data_chan_frame(frame, 2); // output.SL = input.SL - v[frame + 2 * FRAMES_PER_BLOCK_USIZE] = - self.data_chan_frame(frame, 4); + v[frame + 2 * FRAMES_PER_BLOCK_USIZE] = self.data_chan_frame(frame, 4); // output.SR = input.SR - v[frame + 3 * FRAMES_PER_BLOCK_USIZE] = - self.data_chan_frame(frame, 5); + v[frame + 3 * FRAMES_PER_BLOCK_USIZE] = self.data_chan_frame(frame, 5); } self.buffer = v; self.channels = 4; @@ -401,7 +397,8 @@ impl Block { /// Resize to add or remove channels, fill extra channels with silence fn resize_silence(&mut self, channels: u8) { self.explicit_repeat(); - self.buffer.resize(FRAMES_PER_BLOCK_USIZE * channels as usize, 0.); + self.buffer + .resize(FRAMES_PER_BLOCK_USIZE * channels as usize, 0.); self.channels = channels; } @@ -433,7 +430,7 @@ impl Block { /// An iterator over frames in a block pub struct FrameIterator<'a> { frame: Tick, - block: &'a mut Block + block: &'a mut Block, } impl<'a> FrameIterator<'a> { @@ -441,7 +438,7 @@ impl<'a> FrameIterator<'a> { pub fn new(block: &'a mut Block) -> Self { FrameIterator { frame: Tick(0), - block + block, } } @@ -455,18 +452,20 @@ impl<'a> FrameIterator<'a> { let curr = self.frame; if curr < FRAMES_PER_BLOCK { self.frame.advance(); - Some(FrameRef { frame: curr, block: &mut self.block }) + Some(FrameRef { + frame: curr, + block: &mut self.block, + }) } else { None } } } - /// A reference to a frame pub struct FrameRef<'a> { frame: Tick, - block: &'a mut Block + block: &'a mut Block, } impl<'a> FrameRef<'a> { @@ -482,20 +481,26 @@ impl<'a> FrameRef<'a> { /// /// Block must not be silence #[inline] - pub fn mutate_with(&mut self, f: F) where F: Fn(&mut f32) { - debug_assert!(!self.block.is_silence(), "mutate_frame_with should not be called with a silenced block, \ - call .explicit_silence() if you wish to use this"); + pub fn mutate_with(&mut self, f: F) + where + F: Fn(&mut f32), + { + debug_assert!( + !self.block.is_silence(), + "mutate_frame_with should not be called with a silenced block, \ + call .explicit_silence() if you wish to use this" + ); if self.block.repeat { f(&mut self.block.buffer[self.frame.0 as usize]) } else { for chan in 0..self.block.channels { - f(&mut self.block.buffer[chan as usize * FRAMES_PER_BLOCK_USIZE + self.frame.0 as usize]) + f(&mut self.block.buffer + [chan as usize * FRAMES_PER_BLOCK_USIZE + self.frame.0 as usize]) } } } } - // operator impls impl IndexMut> for Chunk { diff --git a/audio/src/buffer_source_node.rs b/audio/src/buffer_source_node.rs index d8fd2fcf..309b5a9b 100644 --- a/audio/src/buffer_source_node.rs +++ b/audio/src/buffer_source_node.rs @@ -1,6 +1,6 @@ -use node::{AudioNodeType, ChannelInfo}; use block::{Block, Chunk, Tick, FRAMES_PER_BLOCK}; -use node::{AudioNodeEngine, AudioScheduledSourceNodeMessage, BlockInfo}; +use node::{AudioNodeEngine, AudioScheduledSourceNodeMessage, BlockInfo, OnEndedCallback}; +use node::{AudioNodeType, ChannelInfo}; use param::{Param, ParamType}; /// Control messages directed to AudioBufferSourceNodes. @@ -69,6 +69,8 @@ pub(crate) struct AudioBufferSourceNode { start_at: Option, /// Time at which the source should stop playing. stop_at: Option, + /// The ended event callback. + pub onended_callback: Option, } impl AudioBufferSourceNode { @@ -84,6 +86,7 @@ impl AudioBufferSourceNode { playback_rate: Param::new(options.playback_rate), start_at: None, stop_at: None, + onended_callback: None, } } @@ -91,24 +94,15 @@ impl AudioBufferSourceNode { match message { AudioBufferSourceNodeMessage::SetBuffer(buffer) => { self.buffer = buffer; - }, - } - } - - pub fn handle_source_node_message(&mut self, message: AudioScheduledSourceNodeMessage, sample_rate: f32) { - match message { - AudioScheduledSourceNodeMessage::Start(when) => { - self.start(Tick::from_time(when, sample_rate)); - } - AudioScheduledSourceNodeMessage::Stop(when) => { - self.stop(Tick::from_time(when, sample_rate)); } } } } impl AudioNodeEngine for AudioBufferSourceNode { - fn node_type(&self) -> AudioNodeType { AudioNodeType::AudioBufferSourceNode } + fn node_type(&self) -> AudioNodeType { + AudioNodeType::AudioBufferSourceNode + } fn input_count(&self) -> u32 { 0 @@ -117,18 +111,20 @@ impl AudioNodeEngine for AudioBufferSourceNode { fn process(&mut self, mut inputs: Chunk, info: &BlockInfo) -> Chunk { debug_assert!(inputs.len() == 0); - let buffer = if let Some(buffer) = self.buffer.as_ref() { - buffer - } else { + if self.buffer.is_none() { inputs.blocks.push(Default::default()); return inputs; - }; + } - if self.playback_offset >= buffer.len() || - self.should_play_at(info.frame) == (false, true) { - inputs.blocks.push(Default::default()); - return inputs; - } + let len = { self.buffer.as_ref().unwrap().len() as usize }; + + if self.playback_offset >= len || self.should_play_at(info.frame) == (false, true) { + self.maybe_trigger_onended_callback(); + inputs.blocks.push(Default::default()); + return inputs; + } + + let buffer = self.buffer.as_ref().unwrap(); let samples_to_copy = match self.stop_at { Some(stop_at) => { @@ -144,7 +140,6 @@ impl AudioNodeEngine for AudioBufferSourceNode { let next_offset = self.playback_offset + samples_to_copy; - if samples_to_copy == FRAMES_PER_BLOCK.0 as usize { // copy entire chan let mut block = Block::empty(); @@ -159,7 +154,9 @@ impl AudioNodeEngine for AudioBufferSourceNode { block.explicit_repeat(); for chan in 0..buffer.chans() { let data = block.data_chan_mut(chan); - data.copy_from_slice(&buffer.buffers[chan as usize][self.playback_offset..next_offset]); + data.copy_from_slice( + &buffer.buffers[chan as usize][self.playback_offset..next_offset], + ); } inputs.blocks.push(block) } @@ -172,18 +169,20 @@ impl AudioNodeEngine for AudioBufferSourceNode { match id { ParamType::PlaybackRate => &mut self.playback_rate, ParamType::Detune => &mut self.detune, - _ => panic!("Unknown param {:?} for AudioBufferSourceNode", id) + _ => panic!("Unknown param {:?} for AudioBufferSourceNode", id), } } - make_message_handler!(AudioBufferSourceNode: handle_message, - AudioScheduledSourceNode: handle_source_node_message); + make_message_handler!( + AudioBufferSourceNode: handle_message, + AudioScheduledSourceNode: handle_source_node_message + ); } #[derive(Debug, Clone)] pub struct AudioBuffer { /// Invariant: all buffers must be of the same length - pub buffers: Vec> + pub buffers: Vec>, } impl AudioBuffer { @@ -200,9 +199,7 @@ impl AudioBuffer { assert!(buf.len() == buffers[0].len()) } - Self { - buffers - } + Self { buffers } } pub fn len(&self) -> usize { @@ -216,9 +213,7 @@ impl AudioBuffer { impl From> for AudioBuffer { fn from(vec: Vec) -> Self { - Self { - buffers: vec![vec] - } + Self { buffers: vec![vec] } } } diff --git a/audio/src/channel_node.rs b/audio/src/channel_node.rs index 5ad7548a..e77ad00e 100644 --- a/audio/src/channel_node.rs +++ b/audio/src/channel_node.rs @@ -1,8 +1,8 @@ use block::FRAMES_PER_BLOCK_USIZE; -use node::AudioNodeType; -use node::{AudioNodeEngine, ChannelCountMode, ChannelInfo, ChannelInterpretation}; use block::{Block, Chunk}; +use node::AudioNodeType; use node::BlockInfo; +use node::{AudioNodeEngine, ChannelCountMode, ChannelInfo, ChannelInterpretation}; #[derive(Copy, Clone, Debug)] pub struct ChannelNodeOptions { @@ -12,7 +12,7 @@ pub struct ChannelNodeOptions { #[derive(AudioNodeCommon)] pub(crate) struct ChannelMergerNode { channel_info: ChannelInfo, - channels: u8 + channels: u8, } impl ChannelMergerNode { @@ -23,13 +23,15 @@ impl ChannelMergerNode { mode: ChannelCountMode::Explicit, ..Default::default() }, - channels: params.channels + channels: params.channels, } } } impl AudioNodeEngine for ChannelMergerNode { - fn node_type(&self) -> AudioNodeType { AudioNodeType::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); @@ -38,7 +40,11 @@ impl AudioNodeEngine for ChannelMergerNode { block.repeat(self.channels); block.explicit_repeat(); - for (i, channel) in block.data_mut().chunks_mut(FRAMES_PER_BLOCK_USIZE).enumerate() { + for (i, channel) in block + .data_mut() + .chunks_mut(FRAMES_PER_BLOCK_USIZE) + .enumerate() + { channel.copy_from_slice(inputs.blocks[i].data_mut()) } @@ -47,7 +53,6 @@ impl AudioNodeEngine for ChannelMergerNode { inputs } - fn input_count(&self) -> u32 { self.channels as u32 } @@ -79,7 +84,9 @@ impl ChannelSplitterNode { } impl AudioNodeEngine for ChannelSplitterNode { - fn node_type(&self) -> AudioNodeType { AudioNodeType::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/audio/src/context.rs b/audio/src/context.rs index c9f61e14..c517b175 100644 --- a/audio/src/context.rs +++ b/audio/src/context.rs @@ -1,13 +1,13 @@ -use AudioBackend; -use std::marker::PhantomData; use decoder::{AudioDecoder, AudioDecoderCallbacks, AudioDecoderOptions}; use graph::{AudioGraph, InputPort, NodeId, OutputPort, PortId}; -use node::{AudioNodeMessage, AudioNodeInit}; +use node::{AudioNodeInit, AudioNodeMessage}; use render_thread::AudioRenderThread; use render_thread::AudioRenderThreadMsg; use std::cell::Cell; +use std::marker::PhantomData; use std::sync::mpsc::{self, Sender}; use std::thread::Builder; +use AudioBackend; /// Describes the state of the audio context on the control thread. #[derive(Clone, Copy, Debug, PartialEq)] @@ -130,7 +130,7 @@ impl AudioContext { AudioRenderThread::::start(receiver, sender_, options.sample_rate, graph) .expect("Could not start AudioRenderThread"); }) - .unwrap(); + .unwrap(); Self { sender, state: Cell::new(ProcessingState::Suspended), @@ -150,14 +150,14 @@ impl AudioContext { pub fn current_time(&self) -> f64 { let (tx, rx) = mpsc::channel(); - let _ = self.sender - .send(AudioRenderThreadMsg::GetCurrentTime(tx)); + let _ = self.sender.send(AudioRenderThreadMsg::GetCurrentTime(tx)); rx.recv().unwrap() } pub fn create_node(&self, node_type: AudioNodeInit) -> NodeId { let (tx, rx) = mpsc::channel(); - let _ = self.sender + let _ = self + .sender .send(AudioRenderThreadMsg::CreateNode(node_type, tx)); rx.recv().unwrap() } @@ -176,12 +176,14 @@ impl AudioContext { } pub fn connect_ports(&self, from: PortId, to: PortId) { - let _ = self.sender + let _ = self + .sender .send(AudioRenderThreadMsg::ConnectPorts(from, to)); } pub fn disconnect_all_from(&self, node: NodeId) { - let _ = self.sender + let _ = self + .sender .send(AudioRenderThreadMsg::DisconnectAllFrom(node)); } @@ -189,7 +191,8 @@ impl AudioContext { // /// // /// https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-output pub fn disconnect_output(&self, out: PortId) { - let _ = self.sender + let _ = self + .sender .send(AudioRenderThreadMsg::DisconnectOutput(out)); } @@ -197,7 +200,8 @@ impl AudioContext { /// /// https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode pub fn disconnect_between(&self, from: NodeId, to: NodeId) { - let _ = self.sender + let _ = self + .sender .send(AudioRenderThreadMsg::DisconnectBetween(from, to)); } @@ -205,7 +209,8 @@ impl AudioContext { /// /// https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode-output pub fn disconnect_output_between(&self, out: PortId, to: NodeId) { - let _ = self.sender + let _ = self + .sender .send(AudioRenderThreadMsg::DisconnectOutputBetween(out, to)); } @@ -213,7 +218,8 @@ impl AudioContext { // /// // /// https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode-output-input pub fn disconnect_output_between_to(&self, out: PortId, inp: PortId) { - let _ = self.sender + let _ = self + .sender .send(AudioRenderThreadMsg::DisconnectOutputBetweenTo(out, inp)); } @@ -229,7 +235,7 @@ impl AudioContext { audio_decoder.decode(data, callbacks, Some(options)); }) - .unwrap(); + .unwrap(); } } diff --git a/audio/src/decoder.rs b/audio/src/decoder.rs index 74e1f91b..7330588a 100644 --- a/audio/src/decoder.rs +++ b/audio/src/decoder.rs @@ -1,5 +1,6 @@ use std::boxed::FnBox; use std::sync::Mutex; + pub struct AudioDecoderCallbacks { pub eos: Mutex>>, pub error: Mutex>>, @@ -94,7 +95,12 @@ impl Default for AudioDecoderOptions { } pub trait AudioDecoder { - fn decode(&self, data: Vec, callbacks: AudioDecoderCallbacks, options: Option); + fn decode( + &self, + data: Vec, + callbacks: AudioDecoderCallbacks, + options: Option, + ); } pub struct DummyAudioDecoder; diff --git a/audio/src/destination_node.rs b/audio/src/destination_node.rs index a4ac02cc..6feb0c78 100644 --- a/audio/src/destination_node.rs +++ b/audio/src/destination_node.rs @@ -1,19 +1,19 @@ -use node::{AudioNodeType, ChannelCountMode, ChannelInfo}; -use node::{AudioNodeEngine, BlockInfo}; use block::Chunk; +use node::{AudioNodeEngine, BlockInfo}; +use node::{AudioNodeType, ChannelCountMode, ChannelInfo}; #[derive(AudioNodeCommon)] pub(crate) struct DestinationNode { channel_info: ChannelInfo, - chunk: Option + chunk: Option, } impl DestinationNode { pub fn new() -> Self { DestinationNode { channel_info: ChannelInfo { - mode: ChannelCountMode::Explicit, - ..Default::default() + mode: ChannelCountMode::Explicit, + ..Default::default() }, chunk: None, } @@ -21,7 +21,9 @@ impl DestinationNode { } impl AudioNodeEngine for DestinationNode { - fn node_type(&self) -> AudioNodeType { AudioNodeType::DestinationNode } + fn node_type(&self) -> AudioNodeType { + AudioNodeType::DestinationNode + } fn process(&mut self, inputs: Chunk, _: &BlockInfo) -> Chunk { self.chunk = Some(inputs); @@ -30,7 +32,7 @@ impl AudioNodeEngine for DestinationNode { fn destination_data(&mut self) -> Option { self.chunk.take() - } + } fn output_count(&self) -> u32 { 0 diff --git a/audio/src/gain_node.rs b/audio/src/gain_node.rs index b06554ca..d33f8f6a 100644 --- a/audio/src/gain_node.rs +++ b/audio/src/gain_node.rs @@ -1,8 +1,8 @@ -use node::{AudioNodeType, ChannelInfo}; use block::Chunk; use block::Tick; use node::AudioNodeEngine; use node::BlockInfo; +use node::{AudioNodeType, ChannelInfo}; use param::{Param, ParamType}; #[derive(Copy, Clone, Debug)] @@ -36,14 +36,15 @@ impl GainNode { } impl AudioNodeEngine for GainNode { - - fn node_type(&self) -> AudioNodeType { AudioNodeType::GainNode } + fn node_type(&self) -> AudioNodeType { + AudioNodeType::GainNode + } fn process(&mut self, mut inputs: Chunk, info: &BlockInfo) -> Chunk { debug_assert!(inputs.len() == 1); if inputs.blocks[0].is_silence() { - return inputs + return inputs; } { @@ -60,11 +61,10 @@ impl AudioNodeEngine for GainNode { inputs } - fn get_param(&mut self, id: ParamType) -> &mut Param { match id { ParamType::Gain => &mut self.gain, - _ => panic!("Unknown param {:?} for GainNode", id) + _ => panic!("Unknown param {:?} for GainNode", id), } } } diff --git a/audio/src/graph.rs b/audio/src/graph.rs index dd56d87b..d9e75c68 100644 --- a/audio/src/graph.rs +++ b/audio/src/graph.rs @@ -1,12 +1,12 @@ -use smallvec::SmallVec; use block::{Block, Chunk}; use destination_node::DestinationNode; use node::{AudioNodeEngine, BlockInfo, ChannelCountMode}; -use petgraph::Direction; use petgraph::graph::DefaultIx; use petgraph::stable_graph::NodeIndex; use petgraph::stable_graph::StableGraph; use petgraph::visit::{DfsPostOrder, EdgeRef, Reversed}; +use petgraph::Direction; +use smallvec::SmallVec; use std::cell::{RefCell, RefMut}; use std::cmp; @@ -72,15 +72,18 @@ pub(crate) struct Node { /// however it does not allow for duplicate connections between pairs /// of ports pub(crate) struct Edge { - connections: SmallVec<[Connection; 1]> + connections: SmallVec<[Connection; 1]>, } impl Edge { /// Find if there are connections between two given ports, return the index - fn has_between(&self, - output_idx: PortIndex, - input_idx: PortIndex) -> bool { - self.connections.iter() + fn has_between( + &self, + output_idx: PortIndex, + input_idx: PortIndex, + ) -> bool { + self.connections + .iter() .find(|e| e.input_idx == input_idx && e.output_idx == output_idx) .is_some() } @@ -89,9 +92,11 @@ impl Edge { self.connections.retain(|i| i.output_idx != output_idx) } - fn remove_by_pair(&mut self, - output_idx: PortIndex, - input_idx: PortIndex) { + fn remove_by_pair( + &mut self, + output_idx: PortIndex, + input_idx: PortIndex, + ) { self.connections .retain(|i| i.output_idx != output_idx || i.input_idx != input_idx) } @@ -126,19 +131,25 @@ impl AudioGraph { /// /// The edge goes *from* the output port *to* the input port, connecting two nodes pub fn add_edge(&mut self, out: PortId, inp: PortId) { - let edge = self.graph.edges(out.node().0) - .find(|e| e.target() == inp.node().0) - .map(|e| e.id()); + let edge = self + .graph + .edges(out.node().0) + .find(|e| e.target() == inp.node().0) + .map(|e| e.id()); if let Some(e) = edge { // .find(|e| e.weight().has_between(out.1, inp.1)); - let w = self.graph.edge_weight_mut(e).expect("This edge is known to exist"); + let w = self + .graph + .edge_weight_mut(e) + .expect("This edge is known to exist"); if w.has_between(out.1, inp.1) { return; } w.connections.push(Connection::new(inp.1, out.1)) } else { // add a new edge - self.graph.add_edge(out.node().0, inp.node().0, Edge::new(inp.1, out.1)); + self.graph + .add_edge(out.node().0, inp.node().0, Edge::new(inp.1, out.1)); } } @@ -146,10 +157,7 @@ impl AudioGraph { /// /// https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect pub fn disconnect_all_from(&mut self, node: NodeId) { - let edges = self.graph - .edges(node.0) - .map(|e| e.id()) - .collect::>(); + let edges = self.graph.edges(node.0).map(|e| e.id()).collect::>(); for edge in edges { self.graph.remove_edge(edge); } @@ -159,10 +167,16 @@ impl AudioGraph { // /// // /// https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-output pub fn disconnect_output(&mut self, out: PortId) { - let candidates: Vec<_> = self.graph.edges(out.node().0) - .map(|e| (e.id(), e.target())).collect(); + let candidates: Vec<_> = self + .graph + .edges(out.node().0) + .map(|e| (e.id(), e.target())) + .collect(); for (edge, to) in candidates { - let mut e = self.graph.remove_edge(edge).expect("Edge index is known to exist"); + let mut e = self + .graph + .remove_edge(edge) + .expect("Edge index is known to exist"); e.remove_by_output(out.1); if !e.connections.is_empty() { self.graph.add_edge(out.node().0, to, e); @@ -174,10 +188,11 @@ impl AudioGraph { /// /// https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode pub fn disconnect_between(&mut self, from: NodeId, to: NodeId) { - let edge = self.graph - .edges(from.0) - .find(|e| e.target() == to.0) - .map(|e| e.id()); + let edge = self + .graph + .edges(from.0) + .find(|e| e.target() == to.0) + .map(|e| e.id()); if let Some(i) = edge { self.graph.remove_edge(i); } @@ -193,7 +208,10 @@ impl AudioGraph { .find(|e| e.target() == to.0) .map(|e| e.id()); if let Some(edge) = edge { - let mut e = self.graph.remove_edge(edge).expect("Edge index is known to exist"); + let mut e = self + .graph + .remove_edge(edge) + .expect("Edge index is known to exist"); e.remove_by_output(out.1); if !e.connections.is_empty() { self.graph.add_edge(out.node().0, to.0, e); @@ -204,14 +222,21 @@ impl AudioGraph { // /// Disconnect all outgoing connections from a node's output to another node's input // /// // /// https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode-output-input - pub fn disconnect_output_between_to(&mut self, out: PortId, inp: PortId) { + pub fn disconnect_output_between_to( + &mut self, + out: PortId, + inp: PortId, + ) { let edge = self .graph .edges(out.node().0) .find(|e| e.target() == inp.node().0) .map(|e| e.id()); if let Some(edge) = edge { - let mut e = self.graph.remove_edge(edge).expect("Edge index is known to exist"); + let mut e = self + .graph + .remove_edge(edge) + .expect("Edge index is known to exist"); e.remove_by_pair(out.1, inp.1); if !e.connections.is_empty() { self.graph.add_edge(out.node().0, inp.node().0, e); @@ -268,7 +293,6 @@ impl AudioGraph { .expect("Cache should have been filled from traversal"); blocks[connection.input_idx.0 as usize].push(block); } - } for (i, mut blocks) in blocks.drain().enumerate() { @@ -289,7 +313,7 @@ impl AudioGraph { } } // It's one channel, it maxes itself - ChannelCountMode::Max => () + ChannelCountMode::Max => (), } } else { let mix_count = match mode { @@ -314,7 +338,6 @@ impl AudioGraph { } } - // actually run the node engine let mut out = curr.process(chunk, info); @@ -355,11 +378,13 @@ impl AudioGraph { } // The destination node stores its output on itself, extract it. - self.graph[self.dest_id.0].node.borrow_mut() - .destination_data().expect("Destination node should have data cached") + self.graph[self.dest_id.0] + .node + .borrow_mut() + .destination_data() + .expect("Destination node should have data cached") } - /// Obtain a mutable reference to a node pub(crate) fn node_mut(&self, ix: NodeId) -> RefMut> { self.graph[ix.0].node.borrow_mut() @@ -377,7 +402,7 @@ impl Node { impl Edge { pub fn new(input_idx: PortIndex, output_idx: PortIndex) -> Self { Edge { - connections: SmallVec::from_buf([Connection::new(input_idx, output_idx)]) + connections: SmallVec::from_buf([Connection::new(input_idx, output_idx)]), } } } diff --git a/audio/src/lib.rs b/audio/src/lib.rs index 22315543..ed8303a6 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -24,7 +24,6 @@ pub mod param; pub mod render_thread; pub mod sink; - pub trait AudioBackend { type Decoder: decoder::AudioDecoder; type Sink: sink::AudioSink; @@ -37,7 +36,7 @@ pub struct DummyBackend {} impl AudioBackend for DummyBackend { type Decoder = decoder::DummyAudioDecoder; - type Sink = sink::DummyAudioSink; + type Sink = sink::DummyAudioSink; fn make_decoder() -> Self::Decoder { decoder::DummyAudioDecoder } @@ -46,4 +45,4 @@ impl AudioBackend for DummyBackend { Ok(sink::DummyAudioSink) } fn init() {} -} \ No newline at end of file +} diff --git a/audio/src/node.rs b/audio/src/node.rs index 70422a13..e20d959f 100644 --- a/audio/src/node.rs +++ b/audio/src/node.rs @@ -1,10 +1,11 @@ -use std::sync::mpsc::Sender; -use param::{Param, ParamRate, ParamType, UserAutomationEvent}; -use channel_node::ChannelNodeOptions; use block::{Chunk, Tick}; use buffer_source_node::{AudioBufferSourceNodeMessage, AudioBufferSourceNodeOptions}; +use channel_node::ChannelNodeOptions; use gain_node::GainNodeOptions; use oscillator_node::OscillatorNodeOptions; +use param::{Param, ParamRate, ParamType, UserAutomationEvent}; +use std::boxed::FnBox; +use std::sync::mpsc::Sender; /// Information required to construct an audio node #[derive(Debug, Clone)] @@ -54,19 +55,17 @@ pub enum AudioNodeType { WaveShaperNode, } - #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum ChannelCountMode { Max, ClampedMax, - Explicit + Explicit, } - #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum ChannelInterpretation { Discrete, - Speakers + Speakers, } #[derive(Copy, Clone)] @@ -84,7 +83,6 @@ impl BlockInfo { } } - pub struct ChannelInfo { pub count: u8, pub mode: ChannelCountMode, @@ -101,7 +99,6 @@ impl Default for ChannelInfo { } } - pub(crate) trait AudioNodeCommon { fn channel_info(&self) -> &ChannelInfo; @@ -125,9 +122,7 @@ pub(crate) trait AudioNodeEngine: Send + AudioNodeCommon { AudioNodeMessage::SetParam(id, event) => { self.get_param(id).insert_event(event.to_event(sample_rate)) } - AudioNodeMessage::SetParamRate(id, rate) => { - self.get_param(id).set_rate(rate) - } + AudioNodeMessage::SetParamRate(id, rate) => self.get_param(id).set_rate(rate), _ => self.message_specific(msg, sample_rate), } } @@ -175,7 +170,6 @@ pub(crate) trait AudioNodeEngine: Send + AudioNodeCommon { } } -#[derive(Clone, Debug)] pub enum AudioNodeMessage { AudioBufferSourceNode(AudioBufferSourceNodeMessage), AudioScheduledSourceNode(AudioScheduledSourceNodeMessage), @@ -187,23 +181,20 @@ pub enum AudioNodeMessage { SetParamRate(ParamType, ParamRate), } -/// This trait represents the common features of the source nodes such as -/// AudioBufferSourceNode, ConstantSourceNode and OscillatorNode. -/// https://webaudio.github.io/web-audio-api/#AudioScheduledSourceNode -pub trait AudioScheduledSourceNode { - /// Schedules a sound to playback at an exact time. - /// Returns true if the scheduling request is processed succesfully. - fn start(&mut self, tick: Tick) -> bool; - /// Schedules a sound to stop playback at an exact time. - /// Returns true if the scheduling request is processed successfully. - fn stop(&mut self, tick: Tick) -> bool; +pub struct OnEndedCallback(pub Box); + +impl OnEndedCallback { + pub fn new(callback: F) -> Self { + OnEndedCallback(Box::new(callback)) + } } /// Type of message directed to AudioScheduledSourceNodes. -#[derive(Debug, Clone)] pub enum AudioScheduledSourceNodeMessage { /// Schedules a sound to playback at an exact time. Start(f64), /// Schedules a sound to stop playback at an exact time. Stop(f64), + /// Register onended event callback. + RegisterOnEndedCallback(OnEndedCallback), } diff --git a/audio/src/oscillator_node.rs b/audio/src/oscillator_node.rs index 789bfcf7..553a95b7 100644 --- a/audio/src/oscillator_node.rs +++ b/audio/src/oscillator_node.rs @@ -1,8 +1,8 @@ -use node::{AudioNodeType, ChannelInfo}; use block::{Chunk, Tick}; -use node::{AudioNodeEngine, AudioScheduledSourceNodeMessage, BlockInfo}; -use param::{Param, ParamType}; +use node::{AudioNodeEngine, AudioScheduledSourceNodeMessage, BlockInfo, OnEndedCallback}; +use node::{AudioNodeType, ChannelInfo}; use num_traits::cast::NumCast; +use param::{Param, ParamType}; #[derive(Copy, Clone, Debug)] pub struct PeriodicWaveOptions { @@ -47,9 +47,10 @@ pub(crate) struct OscillatorNode { start_at: Option, /// Time at which the source should stop playing. stop_at: Option, + /// The ended event callback. + onended_callback: Option, } - impl OscillatorNode { pub fn new(options: OscillatorNodeOptions) -> Self { Self { @@ -59,28 +60,19 @@ impl OscillatorNode { phase: 0., start_at: None, stop_at: None, + onended_callback: None, } } pub fn update_parameters(&mut self, info: &BlockInfo, tick: Tick) -> bool { self.frequency.update(info, tick) } - - pub fn handle_source_node_message(&mut self, message: AudioScheduledSourceNodeMessage, sample_rate: f32) { - match message { - AudioScheduledSourceNodeMessage::Start(when) => { - self.start(Tick::from_time(when, sample_rate)); - } - AudioScheduledSourceNodeMessage::Stop(when) => { - self.stop(Tick::from_time(when, sample_rate)); - } - } - } } impl AudioNodeEngine for OscillatorNode { - - fn node_type(&self) -> AudioNodeType { AudioNodeType::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 @@ -93,11 +85,11 @@ impl AudioNodeEngine for OscillatorNode { inputs.blocks.push(Default::default()); if self.should_play_at(info.frame) == (false, true) { + self.maybe_trigger_onended_callback(); return inputs; } { - inputs.blocks[0].explicit_silence(); let mut iter = inputs.blocks[0].iter(); @@ -118,6 +110,7 @@ impl AudioNodeEngine for OscillatorNode { let (should_play_at, should_break) = self.should_play_at(info.frame + tick); if !should_play_at { if should_break { + self.maybe_trigger_onended_callback(); break; } continue; @@ -145,7 +138,7 @@ impl AudioNodeEngine for OscillatorNode { match id { ParamType::Frequency => &mut self.frequency, ParamType::Detune => &mut self.detune, - _ => panic!("Unknown param {:?} for OscillatorNode", id) + _ => panic!("Unknown param {:?} for OscillatorNode", id), } } diff --git a/audio/src/param.rs b/audio/src/param.rs index 1b3f26b1..70d60385 100644 --- a/audio/src/param.rs +++ b/audio/src/param.rs @@ -6,10 +6,10 @@ pub enum ParamType { Frequency, Detune, Gain, - PlaybackRate + PlaybackRate, } -/// An AudioParam. +/// An AudioParam. /// /// https://webaudio.github.io/web-audio-api/#AudioParam #[derive(Debug)] @@ -30,9 +30,8 @@ pub enum ParamRate { ARate, } - impl Param { - pub fn new(val: f32) -> Self{ + pub fn new(val: f32) -> Self { Param { val, kind: ParamRate::ARate, @@ -51,7 +50,7 @@ impl Param { return false; } - if self.events.len() <= self.current_event { + if self.events.len() <= self.current_event { return false; } @@ -106,10 +105,12 @@ impl Param { break; } - - current_event.run(&mut self.val, current_tick, - self.event_start_time, - self.event_start_value) + current_event.run( + &mut self.val, + current_tick, + self.event_start_time, + self.event_start_value, + ) } pub fn value(&self) -> f32 { @@ -159,13 +160,13 @@ impl Param { #[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum RampKind { - Linear, Exponential + Linear, + Exponential, } #[derive(Clone, Copy, PartialEq, Debug)] /// https://webaudio.github.io/web-audio-api/#dfn-automation-event pub(crate) enum AutomationEvent { - SetValue(f32), SetValueAtTime(f32, Tick), RampToValueAtTime(RampKind, f32, Tick), @@ -174,11 +175,9 @@ pub(crate) enum AutomationEvent { CancelScheduledValues(Tick), } - #[derive(Clone, Copy, PartialEq, Debug)] /// An AutomationEvent that uses times in s instead of Ticks pub enum UserAutomationEvent { - SetValue(f32), SetValueAtTime(f32, /* time */ f64), RampToValueAtTime(RampKind, f32, /* time */ f64), @@ -192,17 +191,25 @@ 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) => - AutomationEvent::RampToValueAtTime(kind, val, Tick::from_time(time, rate)), - UserAutomationEvent::SetTargetAtTime(val, start, tau) => - AutomationEvent::SetTargetAtTime(val, Tick::from_time(start, rate), - tau * rate as f64), - UserAutomationEvent::CancelScheduledValues(t) => - AutomationEvent::CancelScheduledValues(Tick::from_time(t, rate)), - UserAutomationEvent::CancelAndHoldAtTime(t) => + UserAutomationEvent::SetValueAtTime(val, time) => { + AutomationEvent::SetValueAtTime(val, Tick::from_time(time, rate)) + } + UserAutomationEvent::RampToValueAtTime(kind, val, time) => { + AutomationEvent::RampToValueAtTime(kind, val, Tick::from_time(time, rate)) + } + UserAutomationEvent::SetTargetAtTime(val, start, tau) => { + AutomationEvent::SetTargetAtTime( + val, + Tick::from_time(start, rate), + tau * rate as f64, + ) + } + UserAutomationEvent::CancelScheduledValues(t) => { + AutomationEvent::CancelScheduledValues(Tick::from_time(t, rate)) + } + UserAutomationEvent::CancelAndHoldAtTime(t) => { AutomationEvent::CancelAndHoldAtTime(Tick::from_time(t, rate)) + } } } } @@ -215,8 +222,9 @@ impl AutomationEvent { AutomationEvent::RampToValueAtTime(_, _, tick) => tick, AutomationEvent::SetTargetAtTime(_, start, _) => start, AutomationEvent::CancelAndHoldAtTime(t) => t, - AutomationEvent::CancelScheduledValues(..) | AutomationEvent::SetValue(..) => - unreachable!("CancelScheduledValues/SetValue should never appear in the timeline"), + AutomationEvent::CancelScheduledValues(..) | AutomationEvent::SetValue(..) => { + unreachable!("CancelScheduledValues/SetValue should never appear in the timeline") + } } } @@ -226,8 +234,9 @@ impl AutomationEvent { AutomationEvent::RampToValueAtTime(_, _, tick) => Some(tick), AutomationEvent::SetTargetAtTime(..) => None, AutomationEvent::CancelAndHoldAtTime(t) => Some(t), - AutomationEvent::CancelScheduledValues(..) | AutomationEvent::SetValue(..) => - unreachable!("CancelScheduledValues/SetValue should never appear in the timeline"), + AutomationEvent::CancelScheduledValues(..) | AutomationEvent::SetValue(..) => { + unreachable!("CancelScheduledValues/SetValue should never appear in the timeline") + } } } @@ -237,8 +246,9 @@ impl AutomationEvent { AutomationEvent::RampToValueAtTime(..) => None, AutomationEvent::SetTargetAtTime(_, start, _) => Some(start), AutomationEvent::CancelAndHoldAtTime(t) => Some(t), - AutomationEvent::CancelScheduledValues(..) | AutomationEvent::SetValue(..) => - unreachable!("CancelScheduledValues/SetValue should never appear in the timeline"), + AutomationEvent::CancelScheduledValues(..) | AutomationEvent::SetValue(..) => { + unreachable!("CancelScheduledValues/SetValue should never appear in the timeline") + } } } @@ -248,17 +258,20 @@ impl AutomationEvent { match *self { AutomationEvent::CancelAndHoldAtTime(..) => Some(true), AutomationEvent::CancelScheduledValues(..) => Some(false), - _ => None + _ => None, } } /// Update a parameter based on this event /// /// Returns true if something changed - pub fn run(&self, value: &mut f32, - current_tick: Tick, - event_start_time: Tick, - event_start_value: f32) -> bool { + pub fn run( + &self, + value: &mut f32, + current_tick: Tick, + event_start_time: Tick, + event_start_value: f32, + ) -> bool { if let Some(start_time) = self.start_time() { if start_time > current_tick { // The previous event finished and we advanced to this @@ -277,8 +290,8 @@ impl AutomationEvent { } } AutomationEvent::RampToValueAtTime(kind, val, time) => { - let progress = (current_tick - event_start_time).0 as f32 / - (time - event_start_time).0 as f32; + let progress = + (current_tick - event_start_time).0 as f32 / (time - event_start_time).0 as f32; match kind { RampKind::Linear => { *value = event_start_value + (val - event_start_value) * progress; @@ -290,15 +303,14 @@ impl AutomationEvent { true } AutomationEvent::SetTargetAtTime(val, start, tau) => { - let exp = - ((current_tick - start) / tau); + let exp = -((current_tick - start) / tau); *value = val + (event_start_value - val) * exp.exp() as f32; true } - AutomationEvent::CancelAndHoldAtTime(..) => { - false + AutomationEvent::CancelAndHoldAtTime(..) => false, + AutomationEvent::CancelScheduledValues(..) | AutomationEvent::SetValue(..) => { + unreachable!("CancelScheduledValues/SetValue should never appear in the timeline") } - AutomationEvent::CancelScheduledValues(..) | AutomationEvent::SetValue(..) => - unreachable!("CancelScheduledValues/SetValue should never appear in the timeline"), } } } diff --git a/audio/src/render_thread.rs b/audio/src/render_thread.rs index 5994a285..e4ebd749 100644 --- a/audio/src/render_thread.rs +++ b/audio/src/render_thread.rs @@ -1,4 +1,3 @@ -use AudioBackend; use block::{Chunk, Tick, FRAMES_PER_BLOCK}; use buffer_source_node::AudioBufferSourceNode; use channel_node::{ChannelMergerNode, ChannelSplitterNode}; @@ -11,8 +10,8 @@ use node::{AudioNodeEngine, AudioNodeInit, AudioNodeMessage}; use oscillator_node::OscillatorNode; use sink::AudioSink; use std::sync::mpsc::{Receiver, Sender}; +use AudioBackend; -#[derive(Debug)] pub enum AudioRenderThreadMsg { CreateNode(AudioNodeInit, Sender), ConnectPorts(PortId, PortId), @@ -47,7 +46,6 @@ impl AudioRenderThread { sample_rate: f32, graph: AudioGraph, ) -> Result<(), ()> { - let sink = B::make_sink()?; let mut graph = Self { diff --git a/backends/gstreamer/src/audio_decoder.rs b/backends/gstreamer/src/audio_decoder.rs index 44cc7a16..a1a26472 100644 --- a/backends/gstreamer/src/audio_decoder.rs +++ b/backends/gstreamer/src/audio_decoder.rs @@ -1,10 +1,10 @@ use super::gst_app::{AppSink, AppSinkCallbacks, AppSrc}; use super::gst_audio; -use servo_media_audio::decoder::{AudioDecoder, AudioDecoderCallbacks, AudioDecoderOptions}; use byte_slice_cast::*; use gst; use gst::buffer::{MappedBuffer, Readable}; use gst::prelude::*; +use servo_media_audio::decoder::{AudioDecoder, AudioDecoderCallbacks, AudioDecoderOptions}; use std::io::Cursor; use std::io::Read; use std::sync::Arc; @@ -108,7 +108,8 @@ impl AudioDecoder for GStreamerAudioDecoder { gst_audio::AUDIO_FORMAT_F32, options.sample_rate as u32, options.channels, - ).build().ok_or(())?; + ).build() + .ok_or(())?; appsink.set_caps(&audio_info.to_caps().unwrap()); let pipeline_ = pipeline.clone(); diff --git a/backends/gstreamer/src/audio_sink.rs b/backends/gstreamer/src/audio_sink.rs index ea8718a1..1265322e 100644 --- a/backends/gstreamer/src/audio_sink.rs +++ b/backends/gstreamer/src/audio_sink.rs @@ -1,11 +1,11 @@ +use byte_slice_cast::*; +use gst; +use gst::prelude::*; use gst_app::{AppSrc, AppSrcCallbacks}; use gst_audio; use servo_media_audio::block::{Chunk, FRAMES_PER_BLOCK}; use servo_media_audio::render_thread::AudioRenderThreadMsg; use servo_media_audio::sink::AudioSink; -use byte_slice_cast::*; -use gst; -use gst::prelude::*; use std::cell::{Cell, RefCell}; use std::sync::mpsc::Sender; use std::sync::Arc; @@ -44,9 +44,11 @@ impl GStreamerAudioSink { impl GStreamerAudioSink { fn set_audio_info(&self, sample_rate: f32, channels: u8) -> Result<(), ()> { - let audio_info = - gst_audio::AudioInfo::new(gst_audio::AUDIO_FORMAT_F32, sample_rate as u32, channels.into()) - .build() + let audio_info = gst_audio::AudioInfo::new( + gst_audio::AUDIO_FORMAT_F32, + sample_rate as u32, + channels.into(), + ).build() .ok_or(())?; self.appsrc.set_caps(&audio_info.to_caps().unwrap()); *self.audio_info.borrow_mut() = Some(audio_info); @@ -57,7 +59,7 @@ impl GStreamerAudioSink { let curr_channels = if let Some(ch) = self.audio_info.borrow().as_ref() { ch.channels() } else { - return Ok(()) + return Ok(()); }; if channels != curr_channels as u8 { self.set_audio_info(self.sample_rate.get(), channels)?; @@ -71,7 +73,7 @@ impl AudioSink for GStreamerAudioSink { &self, sample_rate: f32, graph_thread_channel: Sender, - ) -> Result<(), ()> { + ) -> Result<(), ()> { self.sample_rate.set(sample_rate); self.set_audio_info(sample_rate, 2)?; self.appsrc.set_property_format(gst::Format::Time); @@ -90,7 +92,7 @@ impl AudioSink for GStreamerAudioSink { }; appsrc.set_callbacks(AppSrcCallbacks::new().need_data(need_data).build()); }) - .unwrap(); + .unwrap(); let appsrc = self.appsrc.as_ref().clone().upcast(); let resample = gst::ElementFactory::make("audioresample", None).ok_or(())?; @@ -105,11 +107,19 @@ impl AudioSink for GStreamerAudioSink { } fn play(&self) -> Result<(), ()> { - self.pipeline.set_state(gst::State::Playing).into_result().map(|_| ()).map_err(|_| ()) + self.pipeline + .set_state(gst::State::Playing) + .into_result() + .map(|_| ()) + .map_err(|_| ()) } fn stop(&self) -> Result<(), ()> { - self.pipeline.set_state(gst::State::Paused).into_result().map(|_| ()).map_err(|_| ()) + self.pipeline + .set_state(gst::State::Paused) + .into_result() + .map(|_| ()) + .map_err(|_| ()) } fn has_enough_data(&self) -> bool { diff --git a/backends/gstreamer/src/lib.rs b/backends/gstreamer/src/lib.rs index 67008672..f2e7d947 100644 --- a/backends/gstreamer/src/lib.rs +++ b/backends/gstreamer/src/lib.rs @@ -1,7 +1,6 @@ - +extern crate gstreamer as gst; extern crate gstreamer_app as gst_app; extern crate gstreamer_audio as gst_audio; -extern crate gstreamer as gst; extern crate servo_media_audio; diff --git a/examples/android/lib/src/lib.rs b/examples/android/lib/src/lib.rs index fd5ada04..7dd17de3 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, AudioNodeInit}; +use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage}; use servo_media::audio::oscillator_node::OscillatorNodeMessage; use servo_media::ServoMedia; diff --git a/examples/audio_decoder.rs b/examples/audio_decoder.rs index f9b62a35..cf2417a5 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, AudioNodeInit, AudioScheduledSourceNodeMessage}; +use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioScheduledSourceNodeMessage}; use servo_media::ServoMedia; use std::env; use std::fs::File; @@ -43,7 +43,8 @@ fn run_example(servo_media: Arc) { println!("Decoding audio"); receiver.recv().unwrap(); println!("Audio decoded"); - let buffer_source = context.create_node(AudioNodeInit::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( @@ -52,9 +53,9 @@ fn run_example(servo_media: Arc) { ); context.message_node( buffer_source, - AudioNodeMessage::AudioBufferSourceNode(AudioBufferSourceNodeMessage::SetBuffer( - Some(decoded_audio.lock().unwrap().to_vec().into()), - )), + AudioNodeMessage::AudioBufferSourceNode(AudioBufferSourceNodeMessage::SetBuffer(Some( + decoded_audio.lock().unwrap().to_vec().into(), + ))), ); let _ = context.resume(); thread::sleep(time::Duration::from_millis(5000)); diff --git a/examples/channels.rs b/examples/channels.rs index 149bd9d1..b74560fa 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, AudioNodeInit, AudioScheduledSourceNodeMessage}; +use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioScheduledSourceNodeMessage}; use servo_media::ServoMedia; use std::sync::Arc; use std::{thread, time}; @@ -27,18 +27,17 @@ fn run_example(servo_media: Arc) { context.message_node( osc, AudioNodeMessage::AudioScheduledSourceNode(AudioScheduledSourceNodeMessage::Start(0.)), - ); + ); context.message_node( osc2, AudioNodeMessage::AudioScheduledSourceNode(AudioScheduledSourceNodeMessage::Start(0.)), - ); + ); let _ = context.resume(); thread::sleep(time::Duration::from_millis(2000)); context.message_node(dest, AudioNodeMessage::SetChannelCount(1)); thread::sleep(time::Duration::from_millis(2000)); let _ = context.close(); - } fn main() { diff --git a/examples/channelsum.rs b/examples/channelsum.rs index 89769bb4..48e0e523 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, AudioNodeInit, AudioScheduledSourceNodeMessage}; +use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioScheduledSourceNodeMessage}; use servo_media::ServoMedia; use std::sync::Arc; use std::{thread, time}; @@ -31,22 +31,21 @@ fn run_example(servo_media: Arc) { context.message_node( osc, AudioNodeMessage::AudioScheduledSourceNode(AudioScheduledSourceNodeMessage::Start(0.)), - ); + ); context.message_node( osc2, AudioNodeMessage::AudioScheduledSourceNode(AudioScheduledSourceNodeMessage::Start(0.)), - ); + ); context.message_node( osc3, AudioNodeMessage::AudioScheduledSourceNode(AudioScheduledSourceNodeMessage::Start(0.)), - ); + ); let _ = context.resume(); thread::sleep(time::Duration::from_millis(2000)); context.message_node(dest, AudioNodeMessage::SetChannelCount(1)); thread::sleep(time::Duration::from_millis(2000)); let _ = context.close(); - } fn main() { diff --git a/examples/params.rs b/examples/params.rs index 769edfa5..879deb17 100644 --- a/examples/params.rs +++ b/examples/params.rs @@ -1,7 +1,7 @@ extern crate servo_media; use servo_media::audio::gain_node::GainNodeOptions; -use servo_media::audio::node::{AudioNodeMessage, AudioNodeInit, AudioScheduledSourceNodeMessage}; +use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioScheduledSourceNodeMessage}; use servo_media::audio::param::{ParamType, RampKind, UserAutomationEvent}; use servo_media::ServoMedia; use std::sync::Arc; diff --git a/examples/params_settarget.rs b/examples/params_settarget.rs index 448f6c56..a8ea8e11 100644 --- a/examples/params_settarget.rs +++ b/examples/params_settarget.rs @@ -1,6 +1,6 @@ extern crate servo_media; -use servo_media::audio::node::{AudioNodeMessage, AudioNodeInit, AudioScheduledSourceNodeMessage}; +use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioScheduledSourceNodeMessage}; use servo_media::audio::param::{ParamType, RampKind, UserAutomationEvent}; use servo_media::ServoMedia; use std::sync::Arc; diff --git a/examples/play.rs b/examples/play.rs index 2785c5d2..70504c29 100644 --- a/examples/play.rs +++ b/examples/play.rs @@ -1,7 +1,9 @@ extern crate servo_media; -use servo_media::audio::gain_node::{GainNodeOptions}; -use servo_media::audio::node::{AudioNodeMessage, AudioNodeInit, AudioScheduledSourceNodeMessage}; +use servo_media::audio::gain_node::GainNodeOptions; +use servo_media::audio::node::{ + AudioNodeInit, AudioNodeMessage, AudioScheduledSourceNodeMessage, OnEndedCallback, +}; use servo_media::audio::param::{ParamType, UserAutomationEvent}; use servo_media::ServoMedia; use std::sync::Arc; @@ -24,6 +26,15 @@ fn run_example(servo_media: Arc) { osc, AudioNodeMessage::AudioScheduledSourceNode(AudioScheduledSourceNodeMessage::Stop(3.)), ); + let callback = OnEndedCallback::new(|| { + println!("Playback ended"); + }); + context.message_node( + osc, + AudioNodeMessage::AudioScheduledSourceNode( + AudioScheduledSourceNodeMessage::RegisterOnEndedCallback(callback), + ), + ); assert_eq!(context.current_time(), 0.); let _ = context.resume(); // 0.5s: Set frequency to 110Hz diff --git a/examples/play_noise.rs b/examples/play_noise.rs index f2ee4aef..2c34f26f 100644 --- a/examples/play_noise.rs +++ b/examples/play_noise.rs @@ -2,14 +2,16 @@ extern crate rand; extern crate servo_media; use servo_media::audio::buffer_source_node::AudioBufferSourceNodeMessage; -use servo_media::audio::node::{AudioNodeMessage, AudioNodeInit, AudioScheduledSourceNodeMessage}; +use servo_media::audio::node::OnEndedCallback; +use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, 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(AudioNodeInit::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)]; @@ -23,7 +25,18 @@ fn run_example(servo_media: Arc) { ); context.message_node( buffer_source, - AudioNodeMessage::AudioBufferSourceNode(AudioBufferSourceNodeMessage::SetBuffer(Some(buffers.into()))), + AudioNodeMessage::AudioBufferSourceNode(AudioBufferSourceNodeMessage::SetBuffer(Some( + buffers.into(), + ))), + ); + let callback = OnEndedCallback::new(|| { + println!("Playback ended"); + }); + context.message_node( + buffer_source, + AudioNodeMessage::AudioScheduledSourceNode( + AudioScheduledSourceNodeMessage::RegisterOnEndedCallback(callback), + ), ); let _ = context.resume(); thread::sleep(time::Duration::from_millis(5000)); diff --git a/servo-media-derive/src/lib.rs b/servo-media-derive/src/lib.rs index d42914a4..c54a5a25 100644 --- a/servo-media-derive/src/lib.rs +++ b/servo-media-derive/src/lib.rs @@ -1,3 +1,5 @@ +#![recursion_limit = "128"] + extern crate proc_macro; extern crate syn; #[macro_use] @@ -15,10 +17,8 @@ pub fn audio_scheduled_source_node(input: TokenStream) -> TokenStream { fn impl_audio_scheduled_source_node(ast: &syn::DeriveInput) -> quote::Tokens { let name = &ast.ident; quote! { - use node::AudioScheduledSourceNode; - impl #name { - pub fn should_play_at(&self, tick: Tick) -> (bool, bool) { + fn should_play_at(&self, tick: Tick) -> (bool, bool) { if self.start_at.is_none() { return (false, true); } @@ -34,9 +34,7 @@ fn impl_audio_scheduled_source_node(ast: &syn::DeriveInput) -> quote::Tokens { (true, false) } } - } - impl AudioScheduledSourceNode for #name { fn start(&mut self, tick: Tick) -> bool { // We can only allow a single call to `start` and always before // any `stop` calls. @@ -57,13 +55,34 @@ fn impl_audio_scheduled_source_node(ast: &syn::DeriveInput) -> quote::Tokens { self.stop_at = Some(tick); true } + + fn maybe_trigger_onended_callback(&mut self) { + // We cannot have an end without a start. + if self.start_at.is_none() || self.onended_callback.is_none() { + return; + } + self.onended_callback.take().unwrap().0(); + } + + fn handle_source_node_message(&mut self, message: AudioScheduledSourceNodeMessage, sample_rate: f32) { + match message { + AudioScheduledSourceNodeMessage::Start(when) => { + self.start(Tick::from_time(when, sample_rate)); + } + AudioScheduledSourceNodeMessage::Stop(when) => { + self.stop(Tick::from_time(when, sample_rate)); + } + AudioScheduledSourceNodeMessage::RegisterOnEndedCallback(callback) => { + self.onended_callback = Some(callback); + } + } + } } } } #[proc_macro_derive(AudioNodeCommon)] pub fn channel_info(input: TokenStream) -> TokenStream { - let ast: syn::DeriveInput = syn::parse(input).unwrap(); let name = &ast.ident; let gen = quote! { diff --git a/servo-media/src/lib.rs b/servo-media/src/lib.rs index 59049144..bc682cd0 100644 --- a/servo-media/src/lib.rs +++ b/servo-media/src/lib.rs @@ -4,8 +4,8 @@ extern crate servo_media_gstreamer; use std::sync::{self, Once}; use std::sync::{Arc, Mutex}; -use audio::AudioBackend; use audio::context::{AudioContext, AudioContextOptions}; +use audio::AudioBackend; pub struct ServoMedia;