2 * Copyright (C) 2010, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "AudioNodeInput.h"
33 #include "AudioNodeOutput.h"
34 #include "AudioUtilities.h"
35 #include <wtf/IsoMallocInlines.h>
39 WTF_MAKE_ISO_ALLOCATED_IMPL(GainNode);
41 ExceptionOr<Ref<GainNode>> GainNode::create(BaseAudioContext& context, const GainOptions& options)
43 if (context.isStopped())
44 return Exception { InvalidStateError };
46 context.lazyInitialize();
48 auto gainNode = adoptRef(*new GainNode(context));
50 auto result = gainNode->handleAudioNodeOptions(options, { 2, ChannelCountMode::Max, ChannelInterpretation::Speakers });
51 if (result.hasException())
52 return result.releaseException();
54 gainNode->gain().setValue(options.gain);
59 GainNode::GainNode(BaseAudioContext& context)
61 , m_sampleAccurateGainValues(AudioUtilities::renderQuantumSize) // FIXME: can probably share temp buffer in context
62 , m_gain(AudioParam::create(context, "gain"_s, 1.0, -FLT_MAX, FLT_MAX, AutomationRate::ARate))
64 setNodeType(NodeTypeGain);
72 void GainNode::process(size_t framesToProcess)
74 // FIXME: for some cases there is a nice optimization to avoid processing here, and let the gain change
75 // happen in the summing junction input of the AudioNode we're connected to.
76 // Then we can avoid all of the following:
78 AudioBus* outputBus = output(0)->bus();
81 if (!isInitialized() || !input(0)->isConnected())
84 AudioBus* inputBus = input(0)->bus();
86 if (gain().hasSampleAccurateValues() && gain().automationRate() == AutomationRate::ARate) {
87 // Apply sample-accurate gain scaling for precise envelopes, grain windows, etc.
88 ASSERT(framesToProcess <= m_sampleAccurateGainValues.size());
89 if (framesToProcess <= m_sampleAccurateGainValues.size()) {
90 float* gainValues = m_sampleAccurateGainValues.data();
91 gain().calculateSampleAccurateValues(gainValues, framesToProcess);
92 outputBus->copyWithSampleAccurateGainValuesFrom(*inputBus, gainValues, framesToProcess);
95 // Apply the gain with de-zippering into the output bus.
96 float gain = this->gain().hasSampleAccurateValues() ? this->gain().finalValue() : this->gain().value();
98 // If the gain is 0 just zero the bus.
101 outputBus->copyWithGainFrom(*inputBus, gain);
106 void GainNode::processOnlyAudioParams(size_t framesToProcess)
108 float values[AudioUtilities::renderQuantumSize];
109 ASSERT(framesToProcess <= AudioUtilities::renderQuantumSize);
111 m_gain->calculateSampleAccurateValues(values, framesToProcess);
114 // FIXME: this can go away when we do mixing with gain directly in summing junction of AudioNodeInput
116 // As soon as we know the channel count of our input, we can lazily initialize.
117 // Sometimes this may be called more than once with different channel counts, in which case we must safely
118 // uninitialize and then re-initialize with the new channel count.
119 void GainNode::checkNumberOfChannelsForInput(AudioNodeInput* input)
121 ASSERT(context().isAudioThread() && context().isGraphOwner());
123 ASSERT(input && input == this->input(0));
124 if (input != this->input(0))
127 unsigned numberOfChannels = input->numberOfChannels();
129 if (isInitialized() && numberOfChannels != output(0)->numberOfChannels()) {
130 // We're already initialized but the channel count has changed.
134 if (!isInitialized()) {
135 // This will propagate the channel count to any nodes connected further downstream in the graph.
136 output(0)->setNumberOfChannels(numberOfChannels);
140 AudioNode::checkNumberOfChannelsForInput(input);
143 } // namespace WebCore
145 #endif // ENABLE(WEB_AUDIO)