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