6103768228ee25181e57a85fe4b3907cc6d8e83d
[WebKit-https.git] / Source / WebCore / Modules / webaudio / GainNode.cpp
1 /*
2  * Copyright (C) 2010, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
23  */
24
25 #include "config.h"
26
27 #if ENABLE(WEB_AUDIO)
28
29 #include "GainNode.h"
30
31 #include "AudioBus.h"
32 #include "AudioNodeInput.h"
33 #include "AudioNodeOutput.h"
34 #include <wtf/IsoMallocInlines.h>
35
36 namespace WebCore {
37
38 WTF_MAKE_ISO_ALLOCATED_IMPL(GainNode);
39
40 ExceptionOr<Ref<GainNode>> GainNode::create(BaseAudioContext& context, const GainOptions& options)
41 {
42     if (context.isStopped())
43         return Exception { InvalidStateError };
44
45     context.lazyInitialize();
46
47     auto gainNode = adoptRef(*new GainNode(context));
48
49     auto result = gainNode->handleAudioNodeOptions(options, { 2, ChannelCountMode::Max, ChannelInterpretation::Speakers });
50     if (result.hasException())
51         return result.releaseException();
52
53     gainNode->gain().setValue(options.gain);
54
55     return gainNode;
56 }
57
58 GainNode::GainNode(BaseAudioContext& context)
59     : AudioNode(context)
60     , m_sampleAccurateGainValues(AudioUtilities::renderQuantumSize) // FIXME: can probably share temp buffer in context
61     , m_gain(AudioParam::create(context, "gain"_s, 1.0, -FLT_MAX, FLT_MAX, AutomationRate::ARate))
62 {
63     setNodeType(NodeTypeGain);
64
65     addInput();
66     addOutput(1);
67
68     initialize();
69 }
70
71 void GainNode::process(size_t framesToProcess)
72 {
73     // FIXME: for some cases there is a nice optimization to avoid processing here, and let the gain change
74     // happen in the summing junction input of the AudioNode we're connected to.
75     // Then we can avoid all of the following:
76
77     AudioBus* outputBus = output(0)->bus();
78     ASSERT(outputBus);
79
80     if (!isInitialized() || !input(0)->isConnected())
81         outputBus->zero();
82     else {
83         AudioBus* inputBus = input(0)->bus();
84
85         if (gain().hasSampleAccurateValues() && gain().automationRate() == AutomationRate::ARate) {
86             // Apply sample-accurate gain scaling for precise envelopes, grain windows, etc.
87             ASSERT(framesToProcess <= m_sampleAccurateGainValues.size());
88             if (framesToProcess <= m_sampleAccurateGainValues.size()) {
89                 float* gainValues = m_sampleAccurateGainValues.data();
90                 gain().calculateSampleAccurateValues(gainValues, framesToProcess);
91                 outputBus->copyWithSampleAccurateGainValuesFrom(*inputBus, gainValues, framesToProcess);
92             }
93         } else {
94             // Apply the gain with de-zippering into the output bus.
95             float gain = this->gain().hasSampleAccurateValues() ? this->gain().finalValue() : this->gain().value();
96             if (!gain) {
97                 // If the gain is 0 just zero the bus.
98                 outputBus->zero();
99             } else
100                 outputBus->copyWithGainFrom(*inputBus, gain);
101         }
102     }
103 }
104
105 void GainNode::processOnlyAudioParams(size_t framesToProcess)
106 {
107     float values[AudioUtilities::renderQuantumSize];
108     ASSERT(framesToProcess <= AudioUtilities::renderQuantumSize);
109
110     m_gain->calculateSampleAccurateValues(values, framesToProcess);
111 }
112
113 // FIXME: this can go away when we do mixing with gain directly in summing junction of AudioNodeInput
114 //
115 // As soon as we know the channel count of our input, we can lazily initialize.
116 // Sometimes this may be called more than once with different channel counts, in which case we must safely
117 // uninitialize and then re-initialize with the new channel count.
118 void GainNode::checkNumberOfChannelsForInput(AudioNodeInput* input)
119 {
120     ASSERT(context().isAudioThread() && context().isGraphOwner());
121
122     ASSERT(input && input == this->input(0));
123     if (input != this->input(0))
124         return;
125         
126     unsigned numberOfChannels = input->numberOfChannels();    
127
128     if (isInitialized() && numberOfChannels != output(0)->numberOfChannels()) {
129         // We're already initialized but the channel count has changed.
130         uninitialize();
131     }
132
133     if (!isInitialized()) {
134         // This will propagate the channel count to any nodes connected further downstream in the graph.
135         output(0)->setNumberOfChannels(numberOfChannels);
136         initialize();
137     }
138
139     AudioNode::checkNumberOfChannelsForInput(input);
140 }
141
142 } // namespace WebCore
143
144 #endif // ENABLE(WEB_AUDIO)