/src/mozilla-central/dom/media/webaudio/DynamicsCompressorNode.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | 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(DynamicsCompressorNode) |
29 | 0 | NS_INTERFACE_MAP_END_INHERITING(AudioNode) |
30 | | |
31 | | NS_IMPL_ADDREF_INHERITED(DynamicsCompressorNode, AudioNode) |
32 | | NS_IMPL_RELEASE_INHERITED(DynamicsCompressorNode, AudioNode) |
33 | | |
34 | | class DynamicsCompressorNodeEngine final : public AudioNodeEngine |
35 | | { |
36 | | public: |
37 | | explicit DynamicsCompressorNodeEngine(AudioNode* aNode, |
38 | | AudioDestinationNode* aDestination) |
39 | | : AudioNodeEngine(aNode) |
40 | | , 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 | | , mCompressor(new DynamicsCompressor(mDestination->SampleRate(), 2)) |
49 | 0 | { |
50 | 0 | } |
51 | | |
52 | | enum Parameters { |
53 | | THRESHOLD, |
54 | | KNEE, |
55 | | RATIO, |
56 | | ATTACK, |
57 | | RELEASE |
58 | | }; |
59 | | void RecvTimelineEvent(uint32_t aIndex, |
60 | | AudioTimelineEvent& aEvent) override |
61 | 0 | { |
62 | 0 | MOZ_ASSERT(mDestination); |
63 | 0 |
|
64 | 0 | WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent, |
65 | 0 | mDestination); |
66 | 0 |
|
67 | 0 | switch (aIndex) { |
68 | 0 | case THRESHOLD: |
69 | 0 | mThreshold.InsertEvent<int64_t>(aEvent); |
70 | 0 | break; |
71 | 0 | case KNEE: |
72 | 0 | mKnee.InsertEvent<int64_t>(aEvent); |
73 | 0 | break; |
74 | 0 | case RATIO: |
75 | 0 | mRatio.InsertEvent<int64_t>(aEvent); |
76 | 0 | break; |
77 | 0 | case ATTACK: |
78 | 0 | mAttack.InsertEvent<int64_t>(aEvent); |
79 | 0 | break; |
80 | 0 | case RELEASE: |
81 | 0 | mRelease.InsertEvent<int64_t>(aEvent); |
82 | 0 | break; |
83 | 0 | default: |
84 | 0 | NS_ERROR("Bad DynamicsCompresssorNodeEngine TimelineParameter"); |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | | void ProcessBlock(AudioNodeStream* aStream, |
89 | | GraphTime aFrom, |
90 | | const AudioBlock& aInput, |
91 | | AudioBlock* aOutput, |
92 | | bool* aFinished) override |
93 | 0 | { |
94 | 0 | if (aInput.IsNull()) { |
95 | 0 | // Just output silence |
96 | 0 | *aOutput = aInput; |
97 | 0 | return; |
98 | 0 | } |
99 | 0 | |
100 | 0 | const uint32_t channelCount = aInput.ChannelCount(); |
101 | 0 | if (mCompressor->numberOfChannels() != channelCount) { |
102 | 0 | // Create a new compressor object with a new channel count |
103 | 0 | mCompressor = new WebCore::DynamicsCompressor(aStream->SampleRate(), |
104 | 0 | aInput.ChannelCount()); |
105 | 0 | } |
106 | 0 |
|
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 | 0 |
|
119 | 0 | aOutput->AllocateChannels(channelCount); |
120 | 0 | mCompressor->process(&aInput, aOutput, aInput.GetDuration()); |
121 | 0 |
|
122 | 0 | SendReductionParamToMainThread(aStream, |
123 | 0 | mCompressor->parameterValue(DynamicsCompressor::ParamReduction)); |
124 | 0 | } |
125 | | |
126 | | size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override |
127 | 0 | { |
128 | 0 | // Not owned: |
129 | 0 | // - mDestination (probably) |
130 | 0 | // - Don't count the AudioParamTimelines, their inner refs are owned by the |
131 | 0 | // AudioNode. |
132 | 0 | size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); |
133 | 0 | amount += mCompressor->sizeOfIncludingThis(aMallocSizeOf); |
134 | 0 | return amount; |
135 | 0 | } |
136 | | |
137 | | size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override |
138 | 0 | { |
139 | 0 | return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
140 | 0 | } |
141 | | |
142 | | private: |
143 | | void SendReductionParamToMainThread(AudioNodeStream* aStream, float aReduction) |
144 | 0 | { |
145 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
146 | 0 |
|
147 | 0 | class Command final : public Runnable |
148 | 0 | { |
149 | 0 | public: |
150 | 0 | Command(AudioNodeStream* aStream, float aReduction) |
151 | 0 | : mozilla::Runnable("Command") |
152 | 0 | , mStream(aStream) |
153 | 0 | , mReduction(aReduction) |
154 | 0 | { |
155 | 0 | } |
156 | 0 |
|
157 | 0 | NS_IMETHOD Run() override |
158 | 0 | { |
159 | 0 | RefPtr<DynamicsCompressorNode> node = |
160 | 0 | static_cast<DynamicsCompressorNode*> |
161 | 0 | (mStream->Engine()->NodeMainThread()); |
162 | 0 | if (node) { |
163 | 0 | node->SetReduction(mReduction); |
164 | 0 | } |
165 | 0 | return NS_OK; |
166 | 0 | } |
167 | 0 |
|
168 | 0 | private: |
169 | 0 | RefPtr<AudioNodeStream> mStream; |
170 | 0 | float mReduction; |
171 | 0 | }; |
172 | 0 |
|
173 | 0 | mAbstractMainThread->Dispatch(do_AddRef(new Command(aStream, aReduction))); |
174 | 0 | } |
175 | | |
176 | | private: |
177 | | RefPtr<AudioNodeStream> mDestination; |
178 | | AudioParamTimeline mThreshold; |
179 | | AudioParamTimeline mKnee; |
180 | | AudioParamTimeline mRatio; |
181 | | AudioParamTimeline mAttack; |
182 | | AudioParamTimeline mRelease; |
183 | | nsAutoPtr<DynamicsCompressor> mCompressor; |
184 | | }; |
185 | | |
186 | | DynamicsCompressorNode::DynamicsCompressorNode(AudioContext* aContext) |
187 | | : AudioNode(aContext, |
188 | | 2, |
189 | | ChannelCountMode::Explicit, |
190 | | ChannelInterpretation::Speakers) |
191 | | , mThreshold(new AudioParam(this, DynamicsCompressorNodeEngine::THRESHOLD, |
192 | | "threshold", -24.f, -100.f, 0.f)) |
193 | | , mKnee(new AudioParam(this, DynamicsCompressorNodeEngine::KNEE, |
194 | | "knee", 30.f, 0.f, 40.f)) |
195 | | , mRatio(new AudioParam(this, DynamicsCompressorNodeEngine::RATIO, |
196 | | "ratio", 12.f, 1.f, 20.f)) |
197 | | , mReduction(0) |
198 | | , mAttack(new AudioParam(this, DynamicsCompressorNodeEngine::ATTACK, |
199 | | "attack", 0.003f, 0.f, 1.f)) |
200 | | , mRelease(new AudioParam(this, DynamicsCompressorNodeEngine::RELEASE, |
201 | | "release", 0.25f, 0.f, 1.f)) |
202 | 0 | { |
203 | 0 | DynamicsCompressorNodeEngine* engine = new DynamicsCompressorNodeEngine(this, aContext->Destination()); |
204 | 0 | mStream = AudioNodeStream::Create(aContext, engine, |
205 | 0 | AudioNodeStream::NO_STREAM_FLAGS, |
206 | 0 | aContext->Graph()); |
207 | 0 | } |
208 | | |
209 | | /* static */ already_AddRefed<DynamicsCompressorNode> |
210 | | DynamicsCompressorNode::Create(AudioContext& aAudioContext, |
211 | | const DynamicsCompressorOptions& aOptions, |
212 | | ErrorResult& aRv) |
213 | 0 | { |
214 | 0 | if (aAudioContext.CheckClosed(aRv)) { |
215 | 0 | return nullptr; |
216 | 0 | } |
217 | 0 | |
218 | 0 | RefPtr<DynamicsCompressorNode> audioNode = |
219 | 0 | new DynamicsCompressorNode(&aAudioContext); |
220 | 0 |
|
221 | 0 | audioNode->Initialize(aOptions, aRv); |
222 | 0 | if (NS_WARN_IF(aRv.Failed())) { |
223 | 0 | return nullptr; |
224 | 0 | } |
225 | 0 | |
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 | 0 |
|
232 | 0 | return audioNode.forget(); |
233 | 0 | } |
234 | | |
235 | | size_t |
236 | | DynamicsCompressorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
237 | 0 | { |
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 | 0 | } |
246 | | |
247 | | size_t |
248 | | DynamicsCompressorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
249 | 0 | { |
250 | 0 | return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
251 | 0 | } |
252 | | |
253 | | JSObject* |
254 | | DynamicsCompressorNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) |
255 | 0 | { |
256 | 0 | return DynamicsCompressorNode_Binding::Wrap(aCx, this, aGivenProto); |
257 | 0 | } |
258 | | |
259 | | } // namespace dom |
260 | | } // namespace mozilla |