Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/webaudio/StereoPannerNode.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 "StereoPannerNode.h"
8
#include "mozilla/dom/StereoPannerNodeBinding.h"
9
#include "AudioNodeEngine.h"
10
#include "AudioNodeStream.h"
11
#include "AudioDestinationNode.h"
12
#include "AlignmentUtils.h"
13
#include "WebAudioUtils.h"
14
#include "PanningUtils.h"
15
#include "AudioParamTimeline.h"
16
#include "AudioParam.h"
17
18
namespace mozilla {
19
namespace dom {
20
21
using namespace std;
22
23
NS_IMPL_CYCLE_COLLECTION_INHERITED(StereoPannerNode, AudioNode, mPan)
24
25
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StereoPannerNode)
26
0
NS_INTERFACE_MAP_END_INHERITING(AudioNode)
27
28
NS_IMPL_ADDREF_INHERITED(StereoPannerNode, AudioNode)
29
NS_IMPL_RELEASE_INHERITED(StereoPannerNode, AudioNode)
30
31
class StereoPannerNodeEngine final : public AudioNodeEngine
32
{
33
public:
34
  StereoPannerNodeEngine(AudioNode* aNode,
35
                         AudioDestinationNode* aDestination)
36
    : AudioNodeEngine(aNode)
37
    , mDestination(aDestination->Stream())
38
    // Keep the default value in sync with the default value in
39
    // StereoPannerNode::StereoPannerNode.
40
    , mPan(0.f)
41
0
  {
42
0
  }
43
44
  enum Parameters {
45
    PAN
46
  };
47
  void RecvTimelineEvent(uint32_t aIndex,
48
                         AudioTimelineEvent& aEvent) override
49
0
  {
50
0
    MOZ_ASSERT(mDestination);
51
0
    WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent,
52
0
                                                    mDestination);
53
0
54
0
    switch (aIndex) {
55
0
    case PAN:
56
0
      mPan.InsertEvent<int64_t>(aEvent);
57
0
      break;
58
0
    default:
59
0
      NS_ERROR("Bad StereoPannerNode TimelineParameter");
60
0
    }
61
0
  }
62
63
  void GetGainValuesForPanning(float aPanning,
64
                               bool aMonoToStereo,
65
                               float& aLeftGain,
66
                               float& aRightGain)
67
0
  {
68
0
    // Clamp and normalize the panning in [0; 1]
69
0
    aPanning = std::min(std::max(aPanning, -1.f), 1.f);
70
0
71
0
    if (aMonoToStereo) {
72
0
      aPanning += 1;
73
0
      aPanning /= 2;
74
0
    } else if (aPanning <= 0) {
75
0
      aPanning += 1;
76
0
    }
77
0
78
0
    aLeftGain  = cos(0.5 * M_PI * aPanning);
79
0
    aRightGain = sin(0.5 * M_PI * aPanning);
80
0
  }
81
82
  void SetToSilentStereoBlock(AudioBlock* aChunk)
83
0
  {
84
0
    for (uint32_t channel = 0; channel < 2; channel++) {
85
0
      float* samples = aChunk->ChannelFloatsForWrite(channel);
86
0
      for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; i++) {
87
0
        samples[i] = 0.f;
88
0
      }
89
0
    }
90
0
  }
91
92
  void UpmixToStereoIfNeeded(const AudioBlock& aInput, AudioBlock* aOutput)
93
0
  {
94
0
    if (aInput.ChannelCount() == 2) {
95
0
      const float* inputL = static_cast<const float*>(aInput.mChannelData[0]);
96
0
      const float* inputR = static_cast<const float*>(aInput.mChannelData[1]);
97
0
      float* outputL = aOutput->ChannelFloatsForWrite(0);
98
0
      float* outputR = aOutput->ChannelFloatsForWrite(1);
99
0
100
0
      AudioBlockCopyChannelWithScale(inputL, aInput.mVolume, outputL);
101
0
      AudioBlockCopyChannelWithScale(inputR, aInput.mVolume, outputR);
102
0
    } else {
103
0
      MOZ_ASSERT(aInput.ChannelCount() == 1);
104
0
      GainMonoToStereo(aInput, aOutput, aInput.mVolume, aInput.mVolume);
105
0
    }
106
0
  }
107
108
  virtual void ProcessBlock(AudioNodeStream* aStream,
109
                            GraphTime aFrom,
110
                            const AudioBlock& aInput,
111
                            AudioBlock* aOutput,
112
                            bool *aFinished) override
113
0
  {
114
0
    // The output of this node is always stereo, no matter what the inputs are.
115
0
    MOZ_ASSERT(aInput.ChannelCount() <= 2);
116
0
    aOutput->AllocateChannels(2);
117
0
    bool monoToStereo = aInput.ChannelCount() == 1;
118
0
119
0
    if (aInput.IsNull()) {
120
0
      // If input is silent, so is the output
121
0
      SetToSilentStereoBlock(aOutput);
122
0
    } else if (mPan.HasSimpleValue()) {
123
0
      float panning = mPan.GetValue();
124
0
      // If the panning is 0.0, we can simply copy the input to the
125
0
      // output with gain applied, up-mixing to stereo if needed.
126
0
      if (panning == 0.0f) {
127
0
        UpmixToStereoIfNeeded(aInput, aOutput);
128
0
      } else {
129
0
        // Optimize the case where the panning is constant for this processing
130
0
        // block: we can just apply a constant gain on the left and right
131
0
        // channel
132
0
        float gainL, gainR;
133
0
134
0
        GetGainValuesForPanning(panning, monoToStereo, gainL, gainR);
135
0
        ApplyStereoPanning(aInput, aOutput,
136
0
                           gainL * aInput.mVolume,
137
0
                           gainR * aInput.mVolume,
138
0
                           panning <= 0);
139
0
      }
140
0
    } else {
141
0
      float computedGain[2*WEBAUDIO_BLOCK_SIZE + 4];
142
0
      bool onLeft[WEBAUDIO_BLOCK_SIZE];
143
0
144
0
      float values[WEBAUDIO_BLOCK_SIZE];
145
0
      StreamTime tick = mDestination->GraphTimeToStreamTime(aFrom);
146
0
      mPan.GetValuesAtTime(tick, values, WEBAUDIO_BLOCK_SIZE);
147
0
148
0
      float* alignedComputedGain = ALIGNED16(computedGain);
149
0
      ASSERT_ALIGNED16(alignedComputedGain);
150
0
      for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
151
0
        float left, right;
152
0
        GetGainValuesForPanning(values[counter], monoToStereo, left, right);
153
0
154
0
        alignedComputedGain[counter] = left * aInput.mVolume;
155
0
        alignedComputedGain[WEBAUDIO_BLOCK_SIZE + counter] = right * aInput.mVolume;
156
0
        onLeft[counter] = values[counter] <= 0;
157
0
      }
158
0
159
0
      // Apply the gain to the output buffer
160
0
      ApplyStereoPanning(aInput, aOutput, alignedComputedGain, &alignedComputedGain[WEBAUDIO_BLOCK_SIZE], onLeft);
161
0
    }
162
0
  }
163
164
  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
165
0
  {
166
0
    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
167
0
  }
168
169
  RefPtr<AudioNodeStream> mDestination;
170
  AudioParamTimeline mPan;
171
};
172
173
StereoPannerNode::StereoPannerNode(AudioContext* aContext)
174
  : AudioNode(aContext,
175
              2,
176
              ChannelCountMode::Clamped_max,
177
              ChannelInterpretation::Speakers)
178
  , mPan(new AudioParam(this, StereoPannerNodeEngine::PAN, "pan", 0.f, -1.f, 1.f))
179
0
{
180
0
  StereoPannerNodeEngine* engine = new StereoPannerNodeEngine(this, aContext->Destination());
181
0
  mStream = AudioNodeStream::Create(aContext, engine,
182
0
                                    AudioNodeStream::NO_STREAM_FLAGS,
183
0
                                    aContext->Graph());
184
0
}
185
186
/* static */ already_AddRefed<StereoPannerNode>
187
StereoPannerNode::Create(AudioContext& aAudioContext,
188
                         const StereoPannerOptions& aOptions,
189
                         ErrorResult& aRv)
190
0
{
191
0
  if (aAudioContext.CheckClosed(aRv)) {
192
0
    return nullptr;
193
0
  }
194
0
195
0
  RefPtr<StereoPannerNode> audioNode = new StereoPannerNode(&aAudioContext);
196
0
197
0
  audioNode->Initialize(aOptions, aRv);
198
0
  if (NS_WARN_IF(aRv.Failed())) {
199
0
    return nullptr;
200
0
  }
201
0
202
0
  audioNode->Pan()->SetValue(aOptions.mPan);
203
0
  return audioNode.forget();
204
0
}
205
206
size_t
207
StereoPannerNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
208
0
{
209
0
  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
210
0
  amount += mPan->SizeOfIncludingThis(aMallocSizeOf);
211
0
  return amount;
212
0
}
213
214
size_t
215
StereoPannerNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
216
0
{
217
0
  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
218
0
}
219
220
JSObject*
221
StereoPannerNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
222
0
{
223
0
  return StereoPannerNode_Binding::Wrap(aCx, this, aGivenProto);
224
0
}
225
226
} // namespace dom
227
} // namespace mozilla