summaryrefslogtreecommitdiffstats
path: root/dom/media/webaudio/GainNode.cpp
blob: c85849735336da0faab1cacad767cb7ccaac7a6a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "GainNode.h"
#include "mozilla/dom/GainNodeBinding.h"
#include "AlignmentUtils.h"
#include "AudioNodeEngine.h"
#include "AudioNodeTrack.h"
#include "AudioDestinationNode.h"
#include "WebAudioUtils.h"
#include "Tracing.h"

namespace mozilla::dom {

NS_IMPL_CYCLE_COLLECTION_INHERITED(GainNode, AudioNode, mGain)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GainNode)
NS_INTERFACE_MAP_END_INHERITING(AudioNode)

NS_IMPL_ADDREF_INHERITED(GainNode, AudioNode)
NS_IMPL_RELEASE_INHERITED(GainNode, AudioNode)

class GainNodeEngine final : public AudioNodeEngine {
 public:
  GainNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
      : AudioNodeEngine(aNode),
        mDestination(aDestination->Track())
        // Keep the default value in sync with the default value in
        // GainNode::GainNode.
        ,
        mGain(1.f) {}

  enum Parameters { GAIN };
  void RecvTimelineEvent(uint32_t aIndex, AudioTimelineEvent& aEvent) override {
    MOZ_ASSERT(mDestination);
    WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent, mDestination);

    switch (aIndex) {
      case GAIN:
        mGain.InsertEvent<int64_t>(aEvent);
        break;
      default:
        NS_ERROR("Bad GainNodeEngine TimelineParameter");
    }
  }

  void ProcessBlock(AudioNodeTrack* aTrack, GraphTime aFrom,
                    const AudioBlock& aInput, AudioBlock* aOutput,
                    bool* aFinished) override {
    TRACE("GainNodeEngine::ProcessBlock");
    if (aInput.IsNull()) {
      // If input is silent, so is the output
      aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
    } else if (mGain.HasSimpleValue()) {
      // Optimize the case where we only have a single value set as the volume
      float gain = mGain.GetValue();
      if (gain == 0.0f) {
        aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
      } else {
        *aOutput = aInput;
        aOutput->mVolume *= gain;
      }
    } else {
      // First, compute a vector of gains for each track tick based on the
      // timeline at hand, and then for each channel, multiply the values
      // in the buffer with the gain vector.
      aOutput->AllocateChannels(aInput.ChannelCount());

      // Compute the gain values for the duration of the input AudioChunk
      TrackTime tick = mDestination->GraphTimeToTrackTime(aFrom);
      float computedGain[WEBAUDIO_BLOCK_SIZE + 4];
      float* alignedComputedGain = ALIGNED16(computedGain);
      ASSERT_ALIGNED16(alignedComputedGain);
      mGain.GetValuesAtTime(tick, alignedComputedGain, WEBAUDIO_BLOCK_SIZE);

      for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
        alignedComputedGain[counter] *= aInput.mVolume;
      }

      // Apply the gain to the output buffer
      for (size_t channel = 0; channel < aOutput->ChannelCount(); ++channel) {
        const float* inputBuffer =
            static_cast<const float*>(aInput.mChannelData[channel]);
        float* buffer = aOutput->ChannelFloatsForWrite(channel);
        AudioBlockCopyChannelWithScale(inputBuffer, alignedComputedGain,
                                       buffer);
      }
    }
  }

  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override {
    // Not owned:
    // - mDestination - MediaTrackGraphImpl::CollectSizesForMemoryReport()
    // accounts for mDestination.
    // - mGain - Internal ref owned by AudioNode
    return AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
  }

  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
  }

  RefPtr<AudioNodeTrack> mDestination;
  AudioParamTimeline mGain;
};

GainNode::GainNode(AudioContext* aContext)
    : AudioNode(aContext, 2, ChannelCountMode::Max,
                ChannelInterpretation::Speakers) {
  mGain = CreateAudioParam(GainNodeEngine::GAIN, u"gain"_ns, 1.0f);
  GainNodeEngine* engine = new GainNodeEngine(this, aContext->Destination());
  mTrack = AudioNodeTrack::Create(
      aContext, engine, AudioNodeTrack::NO_TRACK_FLAGS, aContext->Graph());
}

/* static */
already_AddRefed<GainNode> GainNode::Create(AudioContext& aAudioContext,
                                            const GainOptions& aOptions,
                                            ErrorResult& aRv) {
  RefPtr<GainNode> audioNode = new GainNode(&aAudioContext);

  audioNode->Initialize(aOptions, aRv);
  if (NS_WARN_IF(aRv.Failed())) {
    return nullptr;
  }

  audioNode->Gain()->SetInitialValue(aOptions.mGain);
  return audioNode.forget();
}

size_t GainNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
  amount += mGain->SizeOfIncludingThis(aMallocSizeOf);
  return amount;
}

size_t GainNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}

JSObject* GainNode::WrapObject(JSContext* aCx,
                               JS::Handle<JSObject*> aGivenProto) {
  return GainNode_Binding::Wrap(aCx, this, aGivenProto);
}

}  // namespace mozilla::dom