diff options
author | yvt <i@yvt.jp> | 2021-07-10 17:24:27 +0900 |
---|---|---|
committer | yvt <i@yvt.jp> | 2021-07-10 17:55:42 +0900 |
commit | 01a7de50ab1843d85295f9dccad7f4c099e7208c (patch) | |
tree | ee53fb6e8889deb7b880ee969e6c662e6128d210 /components/script/dom/audionode.rs | |
parent | ff8d2cdbbfc7a9dc7f38b7dd47cb350fde39388f (diff) | |
parent | 94b613fbdaa2b98f2179fc0bbda13c64e6fa0d38 (diff) | |
download | servo-01a7de50ab1843d85295f9dccad7f4c099e7208c.tar.gz servo-01a7de50ab1843d85295f9dccad7f4c099e7208c.zip |
Merge remote-tracking branch 'upstream/master' into feat-cow-infra
`tests/wpt/web-platform-tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html`
was reverted to the upstream version.
Diffstat (limited to 'components/script/dom/audionode.rs')
-rw-r--r-- | components/script/dom/audionode.rs | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/components/script/dom/audionode.rs b/components/script/dom/audionode.rs new file mode 100644 index 00000000000..841b99d2d8f --- /dev/null +++ b/components/script/dom/audionode.rs @@ -0,0 +1,419 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::audioparam::AudioParam; +use crate::dom::baseaudiocontext::BaseAudioContext; +use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{ + AudioNodeMethods, AudioNodeOptions, +}; +use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{ + ChannelCountMode, ChannelInterpretation, +}; +use crate::dom::bindings::codegen::InheritTypes::{ + AudioNodeTypeId, AudioScheduledSourceNodeTypeId, EventTargetTypeId, +}; +use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::eventtarget::EventTarget; +use dom_struct::dom_struct; +use servo_media::audio::graph::NodeId; +use servo_media::audio::node::ChannelCountMode as ServoMediaChannelCountMode; +use servo_media::audio::node::ChannelInterpretation as ServoMediaChannelInterpretation; +use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, ChannelInfo}; +use std::cell::Cell; + +// 32 is the minimum required by the spec for createBuffer() and the deprecated +// createScriptProcessor() and matches what is used by Blink and Gecko. +// The limit protects against large memory allocations. +pub const MAX_CHANNEL_COUNT: u32 = 32; + +#[dom_struct] +pub struct AudioNode { + eventtarget: EventTarget, + #[ignore_malloc_size_of = "servo_media"] + node_id: NodeId, + context: Dom<BaseAudioContext>, + number_of_inputs: u32, + number_of_outputs: u32, + channel_count: Cell<u32>, + channel_count_mode: Cell<ChannelCountMode>, + channel_interpretation: Cell<ChannelInterpretation>, +} + +impl AudioNode { + pub fn new_inherited( + node_type: AudioNodeInit, + context: &BaseAudioContext, + options: UnwrappedAudioNodeOptions, + number_of_inputs: u32, + number_of_outputs: u32, + ) -> Fallible<AudioNode> { + if options.count == 0 || options.count > MAX_CHANNEL_COUNT { + return Err(Error::NotSupported); + } + let ch = ChannelInfo { + count: options.count as u8, + mode: options.mode.into(), + interpretation: options.interpretation.into(), + }; + let node_id = context + .audio_context_impl() + .lock() + .unwrap() + .create_node(node_type, ch); + Ok(AudioNode::new_inherited_for_id( + node_id, + context, + options, + number_of_inputs, + number_of_outputs, + )) + } + + pub fn new_inherited_for_id( + node_id: NodeId, + context: &BaseAudioContext, + options: UnwrappedAudioNodeOptions, + number_of_inputs: u32, + number_of_outputs: u32, + ) -> AudioNode { + AudioNode { + eventtarget: EventTarget::new_inherited(), + node_id, + context: Dom::from_ref(context), + number_of_inputs, + number_of_outputs, + channel_count: Cell::new(options.count), + channel_count_mode: Cell::new(options.mode), + channel_interpretation: Cell::new(options.interpretation), + } + } + + pub fn message(&self, message: AudioNodeMessage) { + self.context + .audio_context_impl() + .lock() + .unwrap() + .message_node(self.node_id, message); + } + + pub fn node_id(&self) -> NodeId { + self.node_id + } +} + +impl AudioNodeMethods for AudioNode { + // https://webaudio.github.io/web-audio-api/#dom-audionode-connect + fn Connect( + &self, + destination: &AudioNode, + output: u32, + input: u32, + ) -> Fallible<DomRoot<AudioNode>> { + if self.context != destination.context { + return Err(Error::InvalidAccess); + } + + if output >= self.NumberOfOutputs() || input >= destination.NumberOfInputs() { + return Err(Error::IndexSize); + } + + // servo-media takes care of ignoring duplicated connections. + + self.context + .audio_context_impl() + .lock() + .unwrap() + .connect_ports( + self.node_id().output(output), + destination.node_id().input(input), + ); + + Ok(DomRoot::from_ref(destination)) + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-connect-destinationparam-output + fn Connect_(&self, dest: &AudioParam, output: u32) -> Fallible<()> { + if self.context != dest.context() { + return Err(Error::InvalidAccess); + } + + if output >= self.NumberOfOutputs() { + return Err(Error::IndexSize); + } + + // servo-media takes care of ignoring duplicated connections. + + self.context + .audio_context_impl() + .lock() + .unwrap() + .connect_ports( + self.node_id().output(output), + dest.node_id().param(dest.param_type()), + ); + + Ok(()) + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect + fn Disconnect(&self) -> ErrorResult { + self.context + .audio_context_impl() + .lock() + .unwrap() + .disconnect_all_from(self.node_id()); + Ok(()) + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-output + fn Disconnect_(&self, out: u32) -> ErrorResult { + self.context + .audio_context_impl() + .lock() + .unwrap() + .disconnect_output(self.node_id().output(out)); + Ok(()) + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode + fn Disconnect__(&self, to: &AudioNode) -> ErrorResult { + self.context + .audio_context_impl() + .lock() + .unwrap() + .disconnect_between(self.node_id(), to.node_id()); + Ok(()) + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode-output + fn Disconnect___(&self, to: &AudioNode, out: u32) -> ErrorResult { + self.context + .audio_context_impl() + .lock() + .unwrap() + .disconnect_output_between(self.node_id().output(out), to.node_id()); + Ok(()) + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode-output-input + fn Disconnect____(&self, to: &AudioNode, out: u32, inp: u32) -> ErrorResult { + self.context + .audio_context_impl() + .lock() + .unwrap() + .disconnect_output_between_to(self.node_id().output(out), to.node_id().input(inp)); + Ok(()) + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect + fn Disconnect_____(&self, param: &AudioParam) -> ErrorResult { + self.context + .audio_context_impl() + .lock() + .unwrap() + .disconnect_to(self.node_id(), param.node_id().param(param.param_type())); + Ok(()) + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect + fn Disconnect______(&self, param: &AudioParam, out: u32) -> ErrorResult { + self.context + .audio_context_impl() + .lock() + .unwrap() + .disconnect_output_between_to( + self.node_id().output(out), + param.node_id().param(param.param_type()), + ); + Ok(()) + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-context + fn Context(&self) -> DomRoot<BaseAudioContext> { + DomRoot::from_ref(&self.context) + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-numberofinputs + fn NumberOfInputs(&self) -> u32 { + self.number_of_inputs + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-numberofoutputs + fn NumberOfOutputs(&self) -> u32 { + self.number_of_outputs + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-channelcount + fn ChannelCount(&self) -> u32 { + self.channel_count.get() + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-channelcount + fn SetChannelCount(&self, value: u32) -> ErrorResult { + match self.upcast::<EventTarget>().type_id() { + EventTargetTypeId::AudioNode(AudioNodeTypeId::AudioDestinationNode) => { + if self.context.is_offline() { + return Err(Error::InvalidState); + } else if value < 1 || value > MAX_CHANNEL_COUNT { + return Err(Error::IndexSize); + } + }, + EventTargetTypeId::AudioNode(AudioNodeTypeId::PannerNode) => { + if value > 2 { + return Err(Error::NotSupported); + } + }, + EventTargetTypeId::AudioNode(AudioNodeTypeId::AudioScheduledSourceNode( + AudioScheduledSourceNodeTypeId::StereoPannerNode, + )) => { + if value > 2 { + return Err(Error::NotSupported); + } + }, + EventTargetTypeId::AudioNode(AudioNodeTypeId::ChannelMergerNode) => { + return Err(Error::InvalidState); + }, + EventTargetTypeId::AudioNode(AudioNodeTypeId::ChannelSplitterNode) => { + return Err(Error::InvalidState); + }, + // XXX We do not support any of the other AudioNodes with + // constraints yet. Add more cases here as we add support + // for new AudioNodes. + _ => (), + }; + + if value == 0 || value > MAX_CHANNEL_COUNT { + return Err(Error::NotSupported); + } + + self.channel_count.set(value); + self.message(AudioNodeMessage::SetChannelCount(value as u8)); + Ok(()) + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-channelcountmode + fn ChannelCountMode(&self) -> ChannelCountMode { + self.channel_count_mode.get() + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-channelcountmode + fn SetChannelCountMode(&self, value: ChannelCountMode) -> ErrorResult { + // Channel count mode has no effect for nodes with no inputs. + if self.number_of_inputs == 0 { + return Ok(()); + } + + match self.upcast::<EventTarget>().type_id() { + EventTargetTypeId::AudioNode(AudioNodeTypeId::AudioDestinationNode) => { + if self.context.is_offline() { + return Err(Error::InvalidState); + } + }, + EventTargetTypeId::AudioNode(AudioNodeTypeId::PannerNode) => { + if value == ChannelCountMode::Max { + return Err(Error::NotSupported); + } + }, + EventTargetTypeId::AudioNode(AudioNodeTypeId::AudioScheduledSourceNode( + AudioScheduledSourceNodeTypeId::StereoPannerNode, + )) => { + if value == ChannelCountMode::Max { + return Err(Error::NotSupported); + } + }, + EventTargetTypeId::AudioNode(AudioNodeTypeId::ChannelMergerNode) => { + return Err(Error::InvalidState); + }, + EventTargetTypeId::AudioNode(AudioNodeTypeId::ChannelSplitterNode) => { + return Err(Error::InvalidState); + }, + // XXX We do not support any of the other AudioNodes with + // constraints yet. Add more cases here as we add support + // for new AudioNodes. + _ => (), + }; + + self.channel_count_mode.set(value); + self.message(AudioNodeMessage::SetChannelMode(value.into())); + Ok(()) + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-channelinterpretation + fn ChannelInterpretation(&self) -> ChannelInterpretation { + self.channel_interpretation.get() + } + + // https://webaudio.github.io/web-audio-api/#dom-audionode-channelinterpretation + fn SetChannelInterpretation(&self, value: ChannelInterpretation) -> ErrorResult { + // Channel interpretation mode has no effect for nodes with no inputs. + if self.number_of_inputs == 0 { + return Ok(()); + } + + match self.upcast::<EventTarget>().type_id() { + EventTargetTypeId::AudioNode(AudioNodeTypeId::ChannelSplitterNode) => { + return Err(Error::InvalidState); + }, + _ => (), + }; + + self.channel_interpretation.set(value); + self.message(AudioNodeMessage::SetChannelInterpretation(value.into())); + Ok(()) + } +} + +impl From<ChannelCountMode> for ServoMediaChannelCountMode { + fn from(mode: ChannelCountMode) -> Self { + match mode { + ChannelCountMode::Max => ServoMediaChannelCountMode::Max, + ChannelCountMode::Clamped_max => ServoMediaChannelCountMode::ClampedMax, + ChannelCountMode::Explicit => ServoMediaChannelCountMode::Explicit, + } + } +} + +impl From<ChannelInterpretation> for ServoMediaChannelInterpretation { + fn from(interpretation: ChannelInterpretation) -> Self { + match interpretation { + ChannelInterpretation::Discrete => ServoMediaChannelInterpretation::Discrete, + ChannelInterpretation::Speakers => ServoMediaChannelInterpretation::Speakers, + } + } +} + +impl AudioNodeOptions { + pub fn unwrap_or( + &self, + count: u32, + mode: ChannelCountMode, + interpretation: ChannelInterpretation, + ) -> UnwrappedAudioNodeOptions { + UnwrappedAudioNodeOptions { + count: self.channelCount.unwrap_or(count), + mode: self.channelCountMode.unwrap_or(mode), + interpretation: self.channelInterpretation.unwrap_or(interpretation), + } + } +} + +/// Each node has a set of defaults, so this lets us work with them +/// easily without having to deal with the Options +pub struct UnwrappedAudioNodeOptions { + pub count: u32, + pub mode: ChannelCountMode, + pub interpretation: ChannelInterpretation, +} + +impl Default for UnwrappedAudioNodeOptions { + fn default() -> Self { + UnwrappedAudioNodeOptions { + count: 2, + mode: ChannelCountMode::Max, + interpretation: ChannelInterpretation::Speakers, + } + } +} |