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