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/dynamics_compressor_node.h"
27
28 #include "third_party/blink/renderer/bindings/modules/v8/v8_dynamics_compressor_options.h"
29 #include "third_party/blink/renderer/modules/webaudio/audio_node_input.h"
30 #include "third_party/blink/renderer/modules/webaudio/audio_node_output.h"
31 #include "third_party/blink/renderer/platform/audio/audio_utilities.h"
32 #include "third_party/blink/renderer/platform/audio/dynamics_compressor.h"
33 #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
34 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
35
36 // Set output to stereo by default.
37 static const unsigned defaultNumberOfOutputChannels = 2;
38
39 namespace blink {
40
DynamicsCompressorHandler(AudioNode & node,float sample_rate,AudioParamHandler & threshold,AudioParamHandler & knee,AudioParamHandler & ratio,AudioParamHandler & attack,AudioParamHandler & release)41 DynamicsCompressorHandler::DynamicsCompressorHandler(
42 AudioNode& node,
43 float sample_rate,
44 AudioParamHandler& threshold,
45 AudioParamHandler& knee,
46 AudioParamHandler& ratio,
47 AudioParamHandler& attack,
48 AudioParamHandler& release)
49 : AudioHandler(kNodeTypeDynamicsCompressor, node, sample_rate),
50 threshold_(&threshold),
51 knee_(&knee),
52 ratio_(&ratio),
53 reduction_(0),
54 attack_(&attack),
55 release_(&release) {
56 AddInput();
57 AddOutput(defaultNumberOfOutputChannels);
58
59 SetInternalChannelCountMode(kClampedMax);
60
61 Initialize();
62 }
63
Create(AudioNode & node,float sample_rate,AudioParamHandler & threshold,AudioParamHandler & knee,AudioParamHandler & ratio,AudioParamHandler & attack,AudioParamHandler & release)64 scoped_refptr<DynamicsCompressorHandler> DynamicsCompressorHandler::Create(
65 AudioNode& node,
66 float sample_rate,
67 AudioParamHandler& threshold,
68 AudioParamHandler& knee,
69 AudioParamHandler& ratio,
70 AudioParamHandler& attack,
71 AudioParamHandler& release) {
72 return base::AdoptRef(new DynamicsCompressorHandler(
73 node, sample_rate, threshold, knee, ratio, attack, release));
74 }
75
~DynamicsCompressorHandler()76 DynamicsCompressorHandler::~DynamicsCompressorHandler() {
77 Uninitialize();
78 }
79
Process(uint32_t frames_to_process)80 void DynamicsCompressorHandler::Process(uint32_t frames_to_process) {
81 AudioBus* output_bus = Output(0).Bus();
82 DCHECK(output_bus);
83
84 float threshold = threshold_->Value();
85 float knee = knee_->Value();
86 float ratio = ratio_->Value();
87 float attack = attack_->Value();
88 float release = release_->Value();
89
90 dynamics_compressor_->SetParameterValue(DynamicsCompressor::kParamThreshold,
91 threshold);
92 dynamics_compressor_->SetParameterValue(DynamicsCompressor::kParamKnee, knee);
93 dynamics_compressor_->SetParameterValue(DynamicsCompressor::kParamRatio,
94 ratio);
95 dynamics_compressor_->SetParameterValue(DynamicsCompressor::kParamAttack,
96 attack);
97 dynamics_compressor_->SetParameterValue(DynamicsCompressor::kParamRelease,
98 release);
99
100 scoped_refptr<AudioBus> input_bus = Input(0).Bus();
101 dynamics_compressor_->Process(input_bus.get(), output_bus, frames_to_process);
102
103 float reduction =
104 dynamics_compressor_->ParameterValue(DynamicsCompressor::kParamReduction);
105 reduction_.store(reduction, std::memory_order_relaxed);
106 }
107
ProcessOnlyAudioParams(uint32_t frames_to_process)108 void DynamicsCompressorHandler::ProcessOnlyAudioParams(
109 uint32_t frames_to_process) {
110 DCHECK(Context()->IsAudioThread());
111 DCHECK_LE(frames_to_process, audio_utilities::kRenderQuantumFrames);
112
113 float values[audio_utilities::kRenderQuantumFrames];
114
115 threshold_->CalculateSampleAccurateValues(values, frames_to_process);
116 knee_->CalculateSampleAccurateValues(values, frames_to_process);
117 ratio_->CalculateSampleAccurateValues(values, frames_to_process);
118 attack_->CalculateSampleAccurateValues(values, frames_to_process);
119 release_->CalculateSampleAccurateValues(values, frames_to_process);
120 }
121
Initialize()122 void DynamicsCompressorHandler::Initialize() {
123 if (IsInitialized())
124 return;
125
126 AudioHandler::Initialize();
127 dynamics_compressor_ = std::make_unique<DynamicsCompressor>(
128 Context()->sampleRate(), defaultNumberOfOutputChannels);
129 }
130
RequiresTailProcessing() const131 bool DynamicsCompressorHandler::RequiresTailProcessing() const {
132 // Always return true even if the tail time and latency might both be zero.
133 return true;
134 }
135
TailTime() const136 double DynamicsCompressorHandler::TailTime() const {
137 return dynamics_compressor_->TailTime();
138 }
139
LatencyTime() const140 double DynamicsCompressorHandler::LatencyTime() const {
141 return dynamics_compressor_->LatencyTime();
142 }
143
SetChannelCount(unsigned channel_count,ExceptionState & exception_state)144 void DynamicsCompressorHandler::SetChannelCount(
145 unsigned channel_count,
146 ExceptionState& exception_state) {
147 DCHECK(IsMainThread());
148 BaseAudioContext::GraphAutoLocker locker(Context());
149
150 // A DynamicsCompressorNode only supports 1 or 2 channels
151 if (channel_count > 0 && channel_count <= 2) {
152 if (channel_count_ != channel_count) {
153 channel_count_ = channel_count;
154 if (InternalChannelCountMode() != kMax)
155 UpdateChannelsForInputs();
156 }
157 } else {
158 exception_state.ThrowDOMException(
159 DOMExceptionCode::kNotSupportedError,
160 ExceptionMessages::IndexOutsideRange<uint32_t>(
161 "channelCount", channel_count, 1,
162 ExceptionMessages::kInclusiveBound, 2,
163 ExceptionMessages::kInclusiveBound));
164 }
165 }
166
SetChannelCountMode(const String & mode,ExceptionState & exception_state)167 void DynamicsCompressorHandler::SetChannelCountMode(
168 const String& mode,
169 ExceptionState& exception_state) {
170 DCHECK(IsMainThread());
171 BaseAudioContext::GraphAutoLocker locker(Context());
172
173 ChannelCountMode old_mode = InternalChannelCountMode();
174
175 if (mode == "clamped-max") {
176 new_channel_count_mode_ = kClampedMax;
177 } else if (mode == "explicit") {
178 new_channel_count_mode_ = kExplicit;
179 } else if (mode == "max") {
180 // This is not supported for a DynamicsCompressorNode, which can
181 // only handle 1 or 2 channels.
182 exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
183 "The provided value 'max' is not an "
184 "allowed value for ChannelCountMode");
185 new_channel_count_mode_ = old_mode;
186 } else {
187 // Do nothing for other invalid values.
188 new_channel_count_mode_ = old_mode;
189 }
190
191 if (new_channel_count_mode_ != old_mode)
192 Context()->GetDeferredTaskHandler().AddChangedChannelCountMode(this);
193 }
194 // ----------------------------------------------------------------
195
DynamicsCompressorNode(BaseAudioContext & context)196 DynamicsCompressorNode::DynamicsCompressorNode(BaseAudioContext& context)
197 : AudioNode(context),
198 threshold_(AudioParam::Create(
199 context,
200 Uuid(),
201 AudioParamHandler::kParamTypeDynamicsCompressorThreshold,
202 -24,
203 AudioParamHandler::AutomationRate::kControl,
204 AudioParamHandler::AutomationRateMode::kFixed,
205 -100,
206 0)),
207 knee_(AudioParam::Create(
208 context,
209 Uuid(),
210 AudioParamHandler::kParamTypeDynamicsCompressorKnee,
211 30,
212 AudioParamHandler::AutomationRate::kControl,
213 AudioParamHandler::AutomationRateMode::kFixed,
214 0,
215 40)),
216 ratio_(AudioParam::Create(
217 context,
218 Uuid(),
219 AudioParamHandler::kParamTypeDynamicsCompressorRatio,
220 12,
221 AudioParamHandler::AutomationRate::kControl,
222 AudioParamHandler::AutomationRateMode::kFixed,
223 1,
224 20)),
225 attack_(AudioParam::Create(
226 context,
227 Uuid(),
228 AudioParamHandler::kParamTypeDynamicsCompressorAttack,
229 0.003,
230 AudioParamHandler::AutomationRate::kControl,
231 AudioParamHandler::AutomationRateMode::kFixed,
232 0,
233 1)),
234 release_(AudioParam::Create(
235 context,
236 Uuid(),
237 AudioParamHandler::kParamTypeDynamicsCompressorRelease,
238 0.250,
239 AudioParamHandler::AutomationRate::kControl,
240 AudioParamHandler::AutomationRateMode::kFixed,
241 0,
242 1)) {
243 SetHandler(DynamicsCompressorHandler::Create(
244 *this, context.sampleRate(), threshold_->Handler(), knee_->Handler(),
245 ratio_->Handler(), attack_->Handler(), release_->Handler()));
246 }
247
Create(BaseAudioContext & context,ExceptionState & exception_state)248 DynamicsCompressorNode* DynamicsCompressorNode::Create(
249 BaseAudioContext& context,
250 ExceptionState& exception_state) {
251 DCHECK(IsMainThread());
252
253 return MakeGarbageCollected<DynamicsCompressorNode>(context);
254 }
255
Create(BaseAudioContext * context,const DynamicsCompressorOptions * options,ExceptionState & exception_state)256 DynamicsCompressorNode* DynamicsCompressorNode::Create(
257 BaseAudioContext* context,
258 const DynamicsCompressorOptions* options,
259 ExceptionState& exception_state) {
260 DynamicsCompressorNode* node = Create(*context, exception_state);
261
262 if (!node)
263 return nullptr;
264
265 node->HandleChannelOptions(options, exception_state);
266
267 node->attack()->setValue(options->attack());
268 node->knee()->setValue(options->knee());
269 node->ratio()->setValue(options->ratio());
270 node->release()->setValue(options->release());
271 node->threshold()->setValue(options->threshold());
272
273 return node;
274 }
275
Trace(Visitor * visitor)276 void DynamicsCompressorNode::Trace(Visitor* visitor) {
277 visitor->Trace(threshold_);
278 visitor->Trace(knee_);
279 visitor->Trace(ratio_);
280 visitor->Trace(attack_);
281 visitor->Trace(release_);
282 AudioNode::Trace(visitor);
283 }
284
285 DynamicsCompressorHandler&
GetDynamicsCompressorHandler() const286 DynamicsCompressorNode::GetDynamicsCompressorHandler() const {
287 return static_cast<DynamicsCompressorHandler&>(Handler());
288 }
289
threshold() const290 AudioParam* DynamicsCompressorNode::threshold() const {
291 return threshold_;
292 }
293
knee() const294 AudioParam* DynamicsCompressorNode::knee() const {
295 return knee_;
296 }
297
ratio() const298 AudioParam* DynamicsCompressorNode::ratio() const {
299 return ratio_;
300 }
301
reduction() const302 float DynamicsCompressorNode::reduction() const {
303 return GetDynamicsCompressorHandler().ReductionValue();
304 }
305
attack() const306 AudioParam* DynamicsCompressorNode::attack() const {
307 return attack_;
308 }
309
release() const310 AudioParam* DynamicsCompressorNode::release() const {
311 return release_;
312 }
313
ReportDidCreate()314 void DynamicsCompressorNode::ReportDidCreate() {
315 GraphTracer().DidCreateAudioNode(this);
316 GraphTracer().DidCreateAudioParam(attack_);
317 GraphTracer().DidCreateAudioParam(knee_);
318 GraphTracer().DidCreateAudioParam(ratio_);
319 GraphTracer().DidCreateAudioParam(release_);
320 GraphTracer().DidCreateAudioParam(threshold_);
321 }
322
ReportWillBeDestroyed()323 void DynamicsCompressorNode::ReportWillBeDestroyed() {
324 GraphTracer().WillDestroyAudioParam(attack_);
325 GraphTracer().WillDestroyAudioParam(knee_);
326 GraphTracer().WillDestroyAudioParam(ratio_);
327 GraphTracer().WillDestroyAudioParam(release_);
328 GraphTracer().WillDestroyAudioParam(threshold_);
329 GraphTracer().WillDestroyAudioNode(this);
330 }
331
332 } // namespace blink
333