1 /*
2 * Copyright (C) 2011, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
23 * DAMAGE.
24 */
25
26 #include "third_party/blink/renderer/modules/webaudio/biquad_filter_node.h"
27
28 #include <memory>
29
30 #include "base/metrics/histogram_functions.h"
31 #include "third_party/blink/renderer/bindings/modules/v8/v8_biquad_filter_options.h"
32 #include "third_party/blink/renderer/core/inspector/console_message.h"
33 #include "third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.h"
34 #include "third_party/blink/renderer/modules/webaudio/audio_node_output.h"
35 #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
36 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
37 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
38 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
39 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
40
41 namespace blink {
42
BiquadFilterHandler(AudioNode & node,float sample_rate,AudioParamHandler & frequency,AudioParamHandler & q,AudioParamHandler & gain,AudioParamHandler & detune)43 BiquadFilterHandler::BiquadFilterHandler(AudioNode& node,
44 float sample_rate,
45 AudioParamHandler& frequency,
46 AudioParamHandler& q,
47 AudioParamHandler& gain,
48 AudioParamHandler& detune)
49 : AudioBasicProcessorHandler(kNodeTypeBiquadFilter,
50 node,
51 sample_rate,
52 std::make_unique<BiquadProcessor>(sample_rate,
53 1,
54 frequency,
55 q,
56 gain,
57 detune)) {
58 DCHECK(Context());
59 DCHECK(Context()->GetExecutionContext());
60
61 task_runner_ = Context()->GetExecutionContext()->GetTaskRunner(
62 TaskType::kMediaElementEvent);
63
64 // Initialize the handler so that AudioParams can be processed.
65 Initialize();
66 }
67
Create(AudioNode & node,float sample_rate,AudioParamHandler & frequency,AudioParamHandler & q,AudioParamHandler & gain,AudioParamHandler & detune)68 scoped_refptr<BiquadFilterHandler> BiquadFilterHandler::Create(
69 AudioNode& node,
70 float sample_rate,
71 AudioParamHandler& frequency,
72 AudioParamHandler& q,
73 AudioParamHandler& gain,
74 AudioParamHandler& detune) {
75 return base::AdoptRef(
76 new BiquadFilterHandler(node, sample_rate, frequency, q, gain, detune));
77 }
78
Process(uint32_t frames_to_process)79 void BiquadFilterHandler::Process(uint32_t frames_to_process) {
80 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("webaudio.audionode"),
81 "BiquadFilterHandler::Process");
82
83 AudioBasicProcessorHandler::Process(frames_to_process);
84
85 if (!did_warn_bad_filter_state_) {
86 // Inform the user once if the output has a non-finite value. This is a
87 // proxy for the filter state containing non-finite values since the output
88 // is also saved as part of the state of the filter.
89 if (HasNonFiniteOutput()) {
90 did_warn_bad_filter_state_ = true;
91
92 PostCrossThreadTask(
93 *task_runner_, FROM_HERE,
94 CrossThreadBindOnce(&BiquadFilterHandler::NotifyBadState,
95 AsWeakPtr()));
96 }
97 }
98 }
99
NotifyBadState() const100 void BiquadFilterHandler::NotifyBadState() const {
101 DCHECK(IsMainThread());
102 if (!Context() || !Context()->GetExecutionContext())
103 return;
104
105 Context()->GetExecutionContext()->AddConsoleMessage(
106 MakeGarbageCollected<ConsoleMessage>(
107 mojom::ConsoleMessageSource::kJavaScript,
108 mojom::ConsoleMessageLevel::kWarning,
109 NodeTypeName() +
110 ": state is bad, probably due to unstable filter caused "
111 "by fast parameter automation."));
112 }
113
BiquadFilterNode(BaseAudioContext & context)114 BiquadFilterNode::BiquadFilterNode(BaseAudioContext& context)
115 : AudioNode(context),
116 frequency_(
117 AudioParam::Create(context,
118 Uuid(),
119 AudioParamHandler::kParamTypeBiquadFilterFrequency,
120 350.0,
121 AudioParamHandler::AutomationRate::kAudio,
122 AudioParamHandler::AutomationRateMode::kVariable,
123 0,
124 context.sampleRate() / 2)),
125 q_(AudioParam::Create(context,
126 Uuid(),
127 AudioParamHandler::kParamTypeBiquadFilterQ,
128 1.0,
129 AudioParamHandler::AutomationRate::kAudio,
130 AudioParamHandler::AutomationRateMode::kVariable)),
131 gain_(AudioParam::Create(context,
132 Uuid(),
133 AudioParamHandler::kParamTypeBiquadFilterGain,
134 0.0,
135 AudioParamHandler::AutomationRate::kAudio,
136 AudioParamHandler::AutomationRateMode::kVariable,
137 std::numeric_limits<float>::lowest(),
138 40 * log10f(std::numeric_limits<float>::max()))),
139 detune_(
140 AudioParam::Create(context,
141 Uuid(),
142 AudioParamHandler::kParamTypeBiquadFilterDetune,
143 0.0,
144 AudioParamHandler::AutomationRate::kAudio,
145 AudioParamHandler::AutomationRateMode::kVariable,
146 -1200 * log2f(std::numeric_limits<float>::max()),
147 1200 * log2f(std::numeric_limits<float>::max()))) {
148 SetHandler(BiquadFilterHandler::Create(*this, context.sampleRate(),
149 frequency_->Handler(), q_->Handler(),
150 gain_->Handler(), detune_->Handler()));
151
152 setType("lowpass");
153 }
154
Create(BaseAudioContext & context,ExceptionState & exception_state)155 BiquadFilterNode* BiquadFilterNode::Create(BaseAudioContext& context,
156 ExceptionState& exception_state) {
157 DCHECK(IsMainThread());
158
159 // TODO(crbug.com/1055983): Remove this when the execution context validity
160 // check is not required in the AudioNode factory methods.
161 if (!context.CheckExecutionContextAndThrowIfNecessary(exception_state))
162 return nullptr;
163
164 return MakeGarbageCollected<BiquadFilterNode>(context);
165 }
166
Create(BaseAudioContext * context,const BiquadFilterOptions * options,ExceptionState & exception_state)167 BiquadFilterNode* BiquadFilterNode::Create(BaseAudioContext* context,
168 const BiquadFilterOptions* options,
169 ExceptionState& exception_state) {
170 BiquadFilterNode* node = Create(*context, exception_state);
171
172 if (!node)
173 return nullptr;
174
175 node->HandleChannelOptions(options, exception_state);
176
177 node->setType(options->type());
178 node->q()->setValue(options->q());
179 node->detune()->setValue(options->detune());
180 node->frequency()->setValue(options->frequency());
181 node->gain()->setValue(options->gain());
182
183 return node;
184 }
185
Trace(Visitor * visitor) const186 void BiquadFilterNode::Trace(Visitor* visitor) const {
187 visitor->Trace(frequency_);
188 visitor->Trace(q_);
189 visitor->Trace(gain_);
190 visitor->Trace(detune_);
191 AudioNode::Trace(visitor);
192 }
193
GetBiquadProcessor() const194 BiquadProcessor* BiquadFilterNode::GetBiquadProcessor() const {
195 return static_cast<BiquadProcessor*>(
196 static_cast<BiquadFilterHandler&>(Handler()).Processor());
197 }
198
type() const199 String BiquadFilterNode::type() const {
200 switch (
201 const_cast<BiquadFilterNode*>(this)->GetBiquadProcessor()->GetType()) {
202 case BiquadProcessor::FilterType::kLowPass:
203 return "lowpass";
204 case BiquadProcessor::FilterType::kHighPass:
205 return "highpass";
206 case BiquadProcessor::FilterType::kBandPass:
207 return "bandpass";
208 case BiquadProcessor::FilterType::kLowShelf:
209 return "lowshelf";
210 case BiquadProcessor::FilterType::kHighShelf:
211 return "highshelf";
212 case BiquadProcessor::FilterType::kPeaking:
213 return "peaking";
214 case BiquadProcessor::FilterType::kNotch:
215 return "notch";
216 case BiquadProcessor::FilterType::kAllpass:
217 return "allpass";
218 }
219 NOTREACHED();
220 return "lowpass";
221 }
222
setType(const String & type)223 void BiquadFilterNode::setType(const String& type) {
224 if (type == "lowpass") {
225 SetType(BiquadProcessor::FilterType::kLowPass);
226 } else if (type == "highpass") {
227 SetType(BiquadProcessor::FilterType::kHighPass);
228 } else if (type == "bandpass") {
229 SetType(BiquadProcessor::FilterType::kBandPass);
230 } else if (type == "lowshelf") {
231 SetType(BiquadProcessor::FilterType::kLowShelf);
232 } else if (type == "highshelf") {
233 SetType(BiquadProcessor::FilterType::kHighShelf);
234 } else if (type == "peaking") {
235 SetType(BiquadProcessor::FilterType::kPeaking);
236 } else if (type == "notch") {
237 SetType(BiquadProcessor::FilterType::kNotch);
238 } else if (type == "allpass") {
239 SetType(BiquadProcessor::FilterType::kAllpass);
240 }
241 }
242
SetType(BiquadProcessor::FilterType type)243 bool BiquadFilterNode::SetType(BiquadProcessor::FilterType type) {
244 if (type > BiquadProcessor::FilterType::kAllpass)
245 return false;
246
247 base::UmaHistogramEnumeration("WebAudio.BiquadFilter.Type", type);
248
249 GetBiquadProcessor()->SetType(type);
250 return true;
251 }
252
getFrequencyResponse(NotShared<const DOMFloat32Array> frequency_hz,NotShared<DOMFloat32Array> mag_response,NotShared<DOMFloat32Array> phase_response,ExceptionState & exception_state)253 void BiquadFilterNode::getFrequencyResponse(
254 NotShared<const DOMFloat32Array> frequency_hz,
255 NotShared<DOMFloat32Array> mag_response,
256 NotShared<DOMFloat32Array> phase_response,
257 ExceptionState& exception_state) {
258 size_t frequency_hz_length = frequency_hz.View()->length();
259
260 if (mag_response.View()->length() != frequency_hz_length) {
261 exception_state.ThrowDOMException(
262 DOMExceptionCode::kInvalidAccessError,
263 ExceptionMessages::IndexOutsideRange(
264 "magResponse length", mag_response.View()->length(),
265 frequency_hz_length, ExceptionMessages::kInclusiveBound,
266 frequency_hz_length, ExceptionMessages::kInclusiveBound));
267 return;
268 }
269
270 if (phase_response.View()->length() != frequency_hz_length) {
271 exception_state.ThrowDOMException(
272 DOMExceptionCode::kInvalidAccessError,
273 ExceptionMessages::IndexOutsideRange(
274 "phaseResponse length", phase_response.View()->length(),
275 frequency_hz_length, ExceptionMessages::kInclusiveBound,
276 frequency_hz_length, ExceptionMessages::kInclusiveBound));
277 return;
278 }
279
280 int frequency_hz_length_as_int;
281 if (!base::CheckedNumeric<int>(frequency_hz_length)
282 .AssignIfValid(&frequency_hz_length_as_int)) {
283 exception_state.ThrowRangeError(
284 "frequencyHz length exceeds the maximum supported length");
285 return;
286 }
287
288 // If the length is 0, there's nothing to do.
289 if (frequency_hz_length_as_int > 0) {
290 GetBiquadProcessor()->GetFrequencyResponse(
291 frequency_hz_length_as_int, frequency_hz.View()->Data(),
292 mag_response.View()->Data(), phase_response.View()->Data());
293 }
294 }
295
ReportDidCreate()296 void BiquadFilterNode::ReportDidCreate() {
297 GraphTracer().DidCreateAudioNode(this);
298 GraphTracer().DidCreateAudioParam(detune_);
299 GraphTracer().DidCreateAudioParam(frequency_);
300 GraphTracer().DidCreateAudioParam(gain_);
301 GraphTracer().DidCreateAudioParam(q_);
302 }
303
ReportWillBeDestroyed()304 void BiquadFilterNode::ReportWillBeDestroyed() {
305 GraphTracer().WillDestroyAudioParam(detune_);
306 GraphTracer().WillDestroyAudioParam(frequency_);
307 GraphTracer().WillDestroyAudioParam(gain_);
308 GraphTracer().WillDestroyAudioParam(q_);
309 GraphTracer().WillDestroyAudioNode(this);
310 }
311
312 } // namespace blink
313