script/dom/audio/
convolvernode.rs1use std::cell::Cell;
5
6use dom_struct::dom_struct;
7use js::context::JSContext;
8use js::rust::HandleObject;
9use script_bindings::codegen::GenericBindings::AudioBufferBinding::AudioBufferMethods;
10use script_bindings::codegen::GenericBindings::AudioNodeBinding::{
11 AudioNodeMethods, ChannelCountMode, ChannelInterpretation,
12};
13use script_bindings::codegen::GenericBindings::BaseAudioContextBinding::BaseAudioContextMethods;
14use script_bindings::codegen::GenericBindings::ConvolverNodeBinding::ConvolverNodeMethods;
15use script_bindings::reflector::reflect_dom_object_with_proto;
16use servo_media::audio::audio_node::{AudioNodeInit, AudioNodeMessage};
17use servo_media::audio::convolver_node::{ConvolverNodeMessage, ConvolverNodeOptions};
18use servo_media::audio::graph::NodeId;
19
20use crate::conversions::ConvertWithCx;
21use crate::dom::audio::audionode::AudioNodeOptionsHelper;
22use crate::dom::audio::baseaudiocontext::BaseAudioContext;
23use crate::dom::audionode::UnwrappedAudioNodeOptions;
24use crate::dom::bindings::codegen::Bindings::ConvolverNodeBinding::ConvolverOptions;
25use crate::dom::bindings::error::{Error, Fallible};
26use crate::dom::bindings::root::{DomRoot, MutNullableDom};
27use crate::dom::types::{AudioBuffer, AudioNode};
28use crate::dom::window::Window;
29
30#[dom_struct]
31pub(crate) struct ConvolverNode {
32 source_node: AudioNode,
33 buffer: MutNullableDom<AudioBuffer>,
35 normalize: Cell<bool>,
37}
38
39impl ConvolverNode {
40 fn new_inherited(
41 node_id: Option<NodeId>,
42 context: &BaseAudioContext,
43 node_options: UnwrappedAudioNodeOptions,
44 normalize: bool,
45 ) -> Self {
46 ConvolverNode {
47 source_node: AudioNode::new_inherited(node_id, context, node_options, 1, 1),
48 buffer: Default::default(),
49 normalize: Cell::new(normalize),
50 }
51 }
52}
53
54impl ConvolverNodeMethods<crate::DomTypeHolder> for ConvolverNode {
55 fn Constructor(
57 cx: &mut JSContext,
58 window: &Window,
59 proto: Option<HandleObject>,
60 context: &BaseAudioContext,
61 options: &ConvolverOptions,
62 ) -> Fallible<DomRoot<ConvolverNode>> {
63 let node_options = options.parent.unwrap_or(
64 2,
65 ChannelCountMode::Clamped_max,
66 ChannelInterpretation::Speakers,
67 );
68 if AudioNode::channel_count_check(node_options.count) {
69 return Err(Error::NotSupported(Some(String::from(
70 "Channel count not supported",
71 ))));
72 }
73 if node_options.count > 2 {
77 return Err(Error::NotSupported(Some(String::from(
78 "Channel count greater than 2 is not supported",
79 ))));
80 }
81 if let ChannelCountMode::Max = node_options.mode {
85 return Err(Error::NotSupported(Some(String::from(
86 "Channel count mode Max is not supported",
87 ))));
88 }
89 let convolver_options = AudioNodeInit::ConvolverNode(options.convert(cx));
90 let node_id =
91 AudioNode::generate_audio_node_id(cx, &node_options, convolver_options, context);
92
93 let node = reflect_dom_object_with_proto(
95 cx,
96 Box::new(ConvolverNode::new_inherited(
97 node_id,
98 context,
99 node_options,
100 !options.disableNormalization,
101 )),
102 window,
103 proto,
104 );
105 if let Some(Some(ref buffer)) = options.buffer {
107 node.SetBuffer(cx, Some(buffer))?;
108 }
109 Ok(node)
110 }
111
112 fn GetBuffer(&self) -> Fallible<Option<DomRoot<AudioBuffer>>> {
114 Ok(self.buffer.get())
115 }
116
117 fn SetBuffer(&self, cx: &mut JSContext, new_buffer: Option<&AudioBuffer>) -> Fallible<()> {
119 let Some(new_buffer) = new_buffer else {
120 debug!("Setting buffer to null");
121 self.buffer.set(new_buffer);
122 return Ok(());
123 };
124 if ![1u32, 2, 4].contains(&new_buffer.NumberOfChannels()) {
128 return Err(Error::NotSupported(Some(String::from(
129 "Buffer number of channels is not 1, 2 or 4",
130 ))));
131 }
132 if new_buffer.SampleRate() != self.source_node.Context().SampleRate() {
133 return Err(Error::NotSupported(Some(String::from(
134 "Buffer sample rate not equal to sample rate of convolver node's associated base audio context",
135 ))));
136 }
137 self.buffer.set(Some(new_buffer));
138
139 if let Some(buffer) = self.buffer.get() {
141 let buffer = buffer.get_channels(cx);
142 if buffer.is_some() {
143 self.source_node.message(AudioNodeMessage::ConvolverNode(
144 ConvolverNodeMessage::SetBuffer(buffer),
145 ));
146 }
147 }
148
149 Ok(())
150 }
151
152 fn Normalize(&self) -> bool {
154 self.normalize.get()
155 }
156
157 fn SetNormalize(&self, value: bool) {
159 self.normalize.set(value);
160 self.source_node.message(AudioNodeMessage::ConvolverNode(
161 ConvolverNodeMessage::SetNormalize(value),
162 ));
163 }
164}
165
166impl ConvertWithCx<ConvolverNodeOptions> for ConvolverOptions {
167 fn convert(&self, cx: &mut JSContext) -> ConvolverNodeOptions {
168 ConvolverNodeOptions {
169 buffer: self
170 .buffer
171 .as_ref()
172 .and_then(|b| b.as_ref()?.get_channels(cx).map(|buffer| (*buffer).clone())),
173 normalize: !self.disableNormalization,
174 }
175 }
176}