Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "DynamicsCompressorNode.h"
8 : #include "mozilla/dom/DynamicsCompressorNodeBinding.h"
9 : #include "nsAutoPtr.h"
10 : #include "AudioNodeEngine.h"
11 : #include "AudioNodeStream.h"
12 : #include "AudioDestinationNode.h"
13 : #include "WebAudioUtils.h"
14 : #include "blink/DynamicsCompressor.h"
15 :
16 : using WebCore::DynamicsCompressor;
17 :
18 : namespace mozilla {
19 : namespace dom {
20 :
21 0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(DynamicsCompressorNode, AudioNode,
22 : mThreshold,
23 : mKnee,
24 : mRatio,
25 : mAttack,
26 : mRelease)
27 :
28 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DynamicsCompressorNode)
29 0 : NS_INTERFACE_MAP_END_INHERITING(AudioNode)
30 :
31 0 : NS_IMPL_ADDREF_INHERITED(DynamicsCompressorNode, AudioNode)
32 0 : NS_IMPL_RELEASE_INHERITED(DynamicsCompressorNode, AudioNode)
33 :
34 0 : class DynamicsCompressorNodeEngine final : public AudioNodeEngine
35 : {
36 : public:
37 0 : explicit DynamicsCompressorNodeEngine(AudioNode* aNode,
38 : AudioDestinationNode* aDestination)
39 0 : : AudioNodeEngine(aNode)
40 0 : , mDestination(aDestination->Stream())
41 : // Keep the default value in sync with the default value in
42 : // DynamicsCompressorNode::DynamicsCompressorNode.
43 : , mThreshold(-24.f)
44 : , mKnee(30.f)
45 : , mRatio(12.f)
46 : , mAttack(0.003f)
47 : , mRelease(0.25f)
48 0 : , mCompressor(new DynamicsCompressor(mDestination->SampleRate(), 2))
49 : {
50 0 : }
51 :
52 : enum Parameters {
53 : THRESHOLD,
54 : KNEE,
55 : RATIO,
56 : ATTACK,
57 : RELEASE
58 : };
59 0 : void RecvTimelineEvent(uint32_t aIndex,
60 : AudioTimelineEvent& aEvent) override
61 : {
62 0 : MOZ_ASSERT(mDestination);
63 :
64 0 : WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent,
65 0 : mDestination);
66 :
67 0 : switch (aIndex) {
68 : case THRESHOLD:
69 0 : mThreshold.InsertEvent<int64_t>(aEvent);
70 0 : break;
71 : case KNEE:
72 0 : mKnee.InsertEvent<int64_t>(aEvent);
73 0 : break;
74 : case RATIO:
75 0 : mRatio.InsertEvent<int64_t>(aEvent);
76 0 : break;
77 : case ATTACK:
78 0 : mAttack.InsertEvent<int64_t>(aEvent);
79 0 : break;
80 : case RELEASE:
81 0 : mRelease.InsertEvent<int64_t>(aEvent);
82 0 : break;
83 : default:
84 0 : NS_ERROR("Bad DynamicsCompresssorNodeEngine TimelineParameter");
85 : }
86 0 : }
87 :
88 0 : void ProcessBlock(AudioNodeStream* aStream,
89 : GraphTime aFrom,
90 : const AudioBlock& aInput,
91 : AudioBlock* aOutput,
92 : bool* aFinished) override
93 : {
94 0 : if (aInput.IsNull()) {
95 : // Just output silence
96 0 : *aOutput = aInput;
97 0 : return;
98 : }
99 :
100 0 : const uint32_t channelCount = aInput.ChannelCount();
101 0 : if (mCompressor->numberOfChannels() != channelCount) {
102 : // Create a new compressor object with a new channel count
103 0 : mCompressor = new WebCore::DynamicsCompressor(aStream->SampleRate(),
104 0 : aInput.ChannelCount());
105 : }
106 :
107 0 : StreamTime pos = mDestination->GraphTimeToStreamTime(aFrom);
108 0 : mCompressor->setParameterValue(DynamicsCompressor::ParamThreshold,
109 0 : mThreshold.GetValueAtTime(pos));
110 0 : mCompressor->setParameterValue(DynamicsCompressor::ParamKnee,
111 0 : mKnee.GetValueAtTime(pos));
112 0 : mCompressor->setParameterValue(DynamicsCompressor::ParamRatio,
113 0 : mRatio.GetValueAtTime(pos));
114 0 : mCompressor->setParameterValue(DynamicsCompressor::ParamAttack,
115 0 : mAttack.GetValueAtTime(pos));
116 0 : mCompressor->setParameterValue(DynamicsCompressor::ParamRelease,
117 0 : mRelease.GetValueAtTime(pos));
118 :
119 0 : aOutput->AllocateChannels(channelCount);
120 0 : mCompressor->process(&aInput, aOutput, aInput.GetDuration());
121 :
122 0 : SendReductionParamToMainThread(aStream,
123 0 : mCompressor->parameterValue(DynamicsCompressor::ParamReduction));
124 : }
125 :
126 0 : size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
127 : {
128 : // Not owned:
129 : // - mDestination (probably)
130 : // - Don't count the AudioParamTimelines, their inner refs are owned by the
131 : // AudioNode.
132 0 : size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
133 0 : amount += mCompressor->sizeOfIncludingThis(aMallocSizeOf);
134 0 : return amount;
135 : }
136 :
137 0 : size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
138 : {
139 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
140 : }
141 :
142 : private:
143 0 : void SendReductionParamToMainThread(AudioNodeStream* aStream, float aReduction)
144 : {
145 0 : MOZ_ASSERT(!NS_IsMainThread());
146 :
147 0 : class Command final : public Runnable
148 : {
149 : public:
150 0 : Command(AudioNodeStream* aStream, float aReduction)
151 0 : : mozilla::Runnable("Command")
152 : , mStream(aStream)
153 0 : , mReduction(aReduction)
154 : {
155 0 : }
156 :
157 0 : NS_IMETHOD Run() override
158 : {
159 : RefPtr<DynamicsCompressorNode> node =
160 : static_cast<DynamicsCompressorNode*>
161 0 : (mStream->Engine()->NodeMainThread());
162 0 : if (node) {
163 0 : node->SetReduction(mReduction);
164 : }
165 0 : return NS_OK;
166 : }
167 :
168 : private:
169 : RefPtr<AudioNodeStream> mStream;
170 : float mReduction;
171 : };
172 :
173 0 : mAbstractMainThread->Dispatch(do_AddRef(new Command(aStream, aReduction)));
174 0 : }
175 :
176 : private:
177 : AudioNodeStream* mDestination;
178 : AudioParamTimeline mThreshold;
179 : AudioParamTimeline mKnee;
180 : AudioParamTimeline mRatio;
181 : AudioParamTimeline mAttack;
182 : AudioParamTimeline mRelease;
183 : nsAutoPtr<DynamicsCompressor> mCompressor;
184 : };
185 :
186 0 : DynamicsCompressorNode::DynamicsCompressorNode(AudioContext* aContext)
187 : : AudioNode(aContext,
188 : 2,
189 : ChannelCountMode::Explicit,
190 : ChannelInterpretation::Speakers)
191 : , mThreshold(new AudioParam(this, DynamicsCompressorNodeEngine::THRESHOLD,
192 0 : "threshold", -24.f, -100.f, 0.f))
193 : , mKnee(new AudioParam(this, DynamicsCompressorNodeEngine::KNEE,
194 0 : "knee", 30.f, 0.f, 40.f))
195 : , mRatio(new AudioParam(this, DynamicsCompressorNodeEngine::RATIO,
196 0 : "ratio", 12.f, 1.f, 20.f))
197 : , mReduction(0)
198 : , mAttack(new AudioParam(this, DynamicsCompressorNodeEngine::ATTACK,
199 0 : "attack", 0.003f, 0.f, 1.f))
200 : , mRelease(new AudioParam(this, DynamicsCompressorNodeEngine::RELEASE,
201 0 : "release", 0.25f, 0.f, 1.f))
202 : {
203 0 : DynamicsCompressorNodeEngine* engine = new DynamicsCompressorNodeEngine(this, aContext->Destination());
204 0 : mStream = AudioNodeStream::Create(aContext, engine,
205 : AudioNodeStream::NO_STREAM_FLAGS,
206 0 : aContext->Graph());
207 0 : }
208 :
209 : /* static */ already_AddRefed<DynamicsCompressorNode>
210 0 : DynamicsCompressorNode::Create(AudioContext& aAudioContext,
211 : const DynamicsCompressorOptions& aOptions,
212 : ErrorResult& aRv)
213 : {
214 0 : if (aAudioContext.CheckClosed(aRv)) {
215 0 : return nullptr;
216 : }
217 :
218 : RefPtr<DynamicsCompressorNode> audioNode =
219 0 : new DynamicsCompressorNode(&aAudioContext);
220 :
221 0 : audioNode->Initialize(aOptions, aRv);
222 0 : if (NS_WARN_IF(aRv.Failed())) {
223 0 : return nullptr;
224 : }
225 :
226 0 : audioNode->Attack()->SetValue(aOptions.mAttack);
227 0 : audioNode->Knee()->SetValue(aOptions.mKnee);
228 0 : audioNode->Ratio()->SetValue(aOptions.mRatio);
229 0 : audioNode->GetRelease()->SetValue(aOptions.mRelease);
230 0 : audioNode->Threshold()->SetValue(aOptions.mThreshold);
231 :
232 0 : return audioNode.forget();
233 : }
234 :
235 : size_t
236 0 : DynamicsCompressorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
237 : {
238 0 : size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
239 0 : amount += mThreshold->SizeOfIncludingThis(aMallocSizeOf);
240 0 : amount += mKnee->SizeOfIncludingThis(aMallocSizeOf);
241 0 : amount += mRatio->SizeOfIncludingThis(aMallocSizeOf);
242 0 : amount += mAttack->SizeOfIncludingThis(aMallocSizeOf);
243 0 : amount += mRelease->SizeOfIncludingThis(aMallocSizeOf);
244 0 : return amount;
245 : }
246 :
247 : size_t
248 0 : DynamicsCompressorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
249 : {
250 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
251 : }
252 :
253 : JSObject*
254 0 : DynamicsCompressorNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
255 : {
256 0 : return DynamicsCompressorNodeBinding::Wrap(aCx, this, aGivenProto);
257 : }
258 :
259 : } // namespace dom
260 : } // namespace mozilla
|