Coverage Report

Created: 2018-09-25 14:53

/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