From e5b650d910a0b2100496b0f94932abe4c4de0d76 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 19 Jul 2018 18:28:31 -0700 Subject: [PATCH 1/3] Fix oscillator node updates --- audio/src/oscillator_node.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/audio/src/oscillator_node.rs b/audio/src/oscillator_node.rs index 553a95b7..745d355a 100644 --- a/audio/src/oscillator_node.rs +++ b/audio/src/oscillator_node.rs @@ -95,7 +95,6 @@ impl AudioNodeEngine for OscillatorNode { // Convert all our parameters to the target type for calculations let vol: f32 = 1.0; - let freq = self.frequency.value() as f64; let sample_rate = info.sample_rate as f64; let two_pi = 2.0 * PI; @@ -104,7 +103,7 @@ impl AudioNodeEngine for OscillatorNode { // converted to floating point numbers and then iterated over in 1-steps // // Also, if the frequency changes the phase should not - let mut step = two_pi * freq / sample_rate; + let mut step = two_pi * self.frequency.value() as f64 / sample_rate; while let Some(mut frame) = iter.next() { let tick = frame.tick(); let (should_play_at, should_break) = self.should_play_at(info.frame + tick); @@ -116,7 +115,7 @@ impl AudioNodeEngine for OscillatorNode { continue; } if self.update_parameters(info, tick) { - step = two_pi * freq / sample_rate; + step = two_pi * self.frequency.value() as f64 / sample_rate; } let value = vol * f32::sin(NumCast::from(self.phase).unwrap()); frame.mutate_with(|sample| *sample = value); From 00ec1b3d44d21bd96236443ccaa61fe54b955bf1 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 19 Jul 2018 18:37:14 -0700 Subject: [PATCH 2/3] Don't skip nodes without inputs since they may have params --- audio/src/graph.rs | 128 ++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 65 deletions(-) diff --git a/audio/src/graph.rs b/audio/src/graph.rs index d6b9eb1f..16492603 100644 --- a/audio/src/graph.rs +++ b/audio/src/graph.rs @@ -328,83 +328,81 @@ impl AudioGraph { chunk .blocks .resize(curr.input_count() as usize, Default::default()); + // if we have inputs, collect all the computed blocks // and construct a Chunk - if curr.input_count() > 0 { - // set up scratch space to store all the blocks - blocks.clear(); - blocks.resize(curr.input_count() as usize, Default::default()); - - let mode = curr.channel_count_mode(); - let count = curr.channel_count(); - let interpretation = curr.channel_interpretation(); - - // all edges to this node are from its dependencies - for edge in self.graph.edges_directed(ix, Direction::Incoming) { - let edge = edge.weight(); - for connection in &edge.connections { - let mut block = connection - .cache - .borrow_mut() - .take() - .expect("Cache should have been filled from traversal"); - - match connection.input_idx { - PortIndex::Port(idx) => { - blocks[idx as usize].push(block); - } - PortIndex::Param(param) => { - // param inputs are downmixed to mono - // https://webaudio.github.io/web-audio-api/#dom-audionode-connect-destinationparam-output - block.mix(1, ChannelInterpretation::Speakers); - curr.get_param(param).add_block(block) - } - } - + // set up scratch space to store all the blocks + blocks.clear(); + blocks.resize(curr.input_count() as usize, Default::default()); + + let mode = curr.channel_count_mode(); + let count = curr.channel_count(); + let interpretation = curr.channel_interpretation(); + + // all edges to this node are from its dependencies + for edge in self.graph.edges_directed(ix, Direction::Incoming) { + let edge = edge.weight(); + for connection in &edge.connections { + let mut block = connection + .cache + .borrow_mut() + .take() + .expect("Cache should have been filled from traversal"); + + match connection.input_idx { + PortIndex::Port(idx) => { + blocks[idx as usize].push(block); + } + PortIndex::Param(param) => { + // param inputs are downmixed to mono + // https://webaudio.github.io/web-audio-api/#dom-audionode-connect-destinationparam-output + block.mix(1, ChannelInterpretation::Speakers); + curr.get_param(param).add_block(block) + } } } + } - for (i, mut blocks) in blocks.drain().enumerate() { - if blocks.len() == 0 { - if mode == ChannelCountMode::Explicit { - // It's silence, but mix it anyway + for (i, mut blocks) in blocks.drain().enumerate() { + if blocks.len() == 0 { + if mode == ChannelCountMode::Explicit { + // It's silence, but mix it anyway + chunk.blocks[i].mix(count, interpretation); + } + } else if blocks.len() == 1 { + chunk.blocks[i] = blocks.pop().expect("`blocks` had length 1"); + match mode { + ChannelCountMode::Explicit => { chunk.blocks[i].mix(count, interpretation); } - } else if blocks.len() == 1 { - chunk.blocks[i] = blocks.pop().expect("`blocks` had length 1"); - match mode { - ChannelCountMode::Explicit => { + ChannelCountMode::ClampedMax => { + if chunk.blocks[i].chan_count() > count { chunk.blocks[i].mix(count, interpretation); } - ChannelCountMode::ClampedMax => { - if chunk.blocks[i].chan_count() > count { - chunk.blocks[i].mix(count, interpretation); - } - } - // It's one channel, it maxes itself - ChannelCountMode::Max => (), } - } else { - let mix_count = match mode { - ChannelCountMode::Explicit => count, - _ => { - let mut max = 0; // max channel count - for block in &blocks { - max = cmp::max(max, block.chan_count()); - } - if mode == ChannelCountMode::ClampedMax { - max = cmp::min(max, count); - } - max - } - }; - let block = blocks.into_iter().fold(Block::default(), |acc, mut block| { - block.mix(mix_count, interpretation); - acc.sum(block) - }); - chunk.blocks[i] = block; + // It's one channel, it maxes itself + ChannelCountMode::Max => (), } + } else { + let mix_count = match mode { + ChannelCountMode::Explicit => count, + _ => { + let mut max = 0; // max channel count + for block in &blocks { + max = cmp::max(max, block.chan_count()); + } + if mode == ChannelCountMode::ClampedMax { + max = cmp::min(max, count); + } + max + } + }; + let block = blocks.into_iter().fold(Block::default(), |acc, mut block| { + block.mix(mix_count, interpretation); + acc.sum(block) + }); + chunk.blocks[i] = block; } } From e1c5d68c39f41cccebcc2292896f61e39e9ba086 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 19 Jul 2018 18:37:45 -0700 Subject: [PATCH 3/3] Example for connect() to frequency --- examples/params_connect2.rs | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 examples/params_connect2.rs diff --git a/examples/params_connect2.rs b/examples/params_connect2.rs new file mode 100644 index 00000000..411a55b8 --- /dev/null +++ b/examples/params_connect2.rs @@ -0,0 +1,55 @@ +extern crate servo_media; + +use servo_media::audio::oscillator_node::OscillatorNodeOptions; +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 mut options = OscillatorNodeOptions::default(); + options.freq = 2.0; + let lfo = context.create_node(AudioNodeInit::OscillatorNode(options)); + let osc = context.create_node(AudioNodeInit::OscillatorNode(Default::default())); + let mut options = GainNodeOptions::default(); + options.gain = 100.; + let gain = context.create_node(AudioNodeInit::GainNode(options)); + let dest = context.dest_node(); + context.connect_ports(lfo.output(0), gain.input(0)); + context.connect_ports(gain.output(0), osc.param(ParamType::Frequency)); + context.connect_ports(osc.output(0), dest.input(0)); + let _ = context.resume(); + context.message_node( + osc, + AudioNodeMessage::AudioScheduledSourceNode(AudioScheduledSourceNodeMessage::Start(0.)), + ); + context.message_node( + lfo, + AudioNodeMessage::AudioScheduledSourceNode(AudioScheduledSourceNodeMessage::Start(0.)), + ); + thread::sleep(time::Duration::from_millis(3000)); + // 0.75s - 1.75s: Linearly ramp frequency to 880Hz + context.message_node( + gain, + AudioNodeMessage::SetParam( + ParamType::Gain, + UserAutomationEvent::RampToValueAtTime(RampKind::Linear, 0., 6.), + ), + ); + + thread::sleep(time::Duration::from_millis(3000)); + let _ = context.close(); +} + +fn main() { + if let Ok(servo_media) = ServoMedia::get() { + run_example(servo_media); + } else { + unreachable!(); + } +}