09fd0c08aeaf5142d8a101eec17c33b087d82634
[WebKit-https.git] / Source / WebCore / Modules / webaudio / DefaultAudioDestinationNode.cpp
1 /*
2  * Copyright (C) 2011, 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 "DefaultAudioDestinationNode.h"
30
31 #include "AudioContext.h"
32 #include "AudioDestination.h"
33 #include "AudioWorklet.h"
34 #include "AudioWorkletMessagingProxy.h"
35 #include "Logging.h"
36 #include "MediaStrategy.h"
37 #include "PlatformStrategies.h"
38 #include "ScriptExecutionContext.h"
39 #include "WorkerRunLoop.h"
40 #include <wtf/IsoMallocInlines.h>
41 #include <wtf/MainThread.h>
42
43 constexpr unsigned EnabledInputChannels = 2;
44
45 namespace WebCore {
46
47 WTF_MAKE_ISO_ALLOCATED_IMPL(DefaultAudioDestinationNode);
48
49 DefaultAudioDestinationNode::DefaultAudioDestinationNode(AudioContext& context, Optional<float> sampleRate)
50     : AudioDestinationNode(context, sampleRate.valueOr(AudioDestination::hardwareSampleRate()))
51 {
52     ASSERT(BaseAudioContext::isSupportedSampleRate(AudioDestination::hardwareSampleRate()));
53     initializeDefaultNodeOptions(2, ChannelCountMode::Explicit, ChannelInterpretation::Speakers);
54 }
55
56 DefaultAudioDestinationNode::~DefaultAudioDestinationNode()
57 {
58     uninitialize();
59 }
60
61 AudioContext& DefaultAudioDestinationNode::context()
62 {
63     return downcast<AudioContext>(AudioDestinationNode::context());
64 }
65
66 const AudioContext& DefaultAudioDestinationNode::context() const
67 {
68     return downcast<AudioContext>(AudioDestinationNode::context());
69 }
70
71 void DefaultAudioDestinationNode::initialize()
72 {
73     ASSERT(isMainThread()); 
74     if (isInitialized())
75         return;
76     ALWAYS_LOG(LOGIDENTIFIER);
77
78     createDestination();
79     AudioNode::initialize();
80 }
81
82 void DefaultAudioDestinationNode::uninitialize()
83 {
84     ASSERT(isMainThread()); 
85     if (!isInitialized())
86         return;
87
88     ALWAYS_LOG(LOGIDENTIFIER);
89     clearDestination();
90     m_numberOfInputChannels = 0;
91
92     AudioNode::uninitialize();
93 }
94
95 void DefaultAudioDestinationNode::clearDestination()
96 {
97     ASSERT(m_destination);
98     if (m_wasDestinationStarted) {
99         m_destination->stop();
100         m_wasDestinationStarted = false;
101     }
102     m_destination->clearCallback();
103     m_destination = nullptr;
104 }
105
106 void DefaultAudioDestinationNode::createDestination()
107 {
108     ALWAYS_LOG(LOGIDENTIFIER, "contextSampleRate = ", m_sampleRate, ", hardwareSampleRate = ", AudioDestination::hardwareSampleRate());
109     ASSERT(!m_destination);
110     m_destination = platformStrategies()->mediaStrategy().createAudioDestination(*this, m_inputDeviceId, m_numberOfInputChannels, channelCount(), m_sampleRate);
111 }
112
113 void DefaultAudioDestinationNode::recreateDestination()
114 {
115     bool wasDestinationStarted = m_wasDestinationStarted;
116     clearDestination();
117     createDestination();
118     if (wasDestinationStarted) {
119         m_wasDestinationStarted = true;
120         m_destination->start(dispatchToRenderThreadFunction());
121     }
122 }
123
124 void DefaultAudioDestinationNode::enableInput(const String& inputDeviceId)
125 {
126     ALWAYS_LOG(LOGIDENTIFIER);
127
128     ASSERT(isMainThread());
129     if (m_numberOfInputChannels != EnabledInputChannels) {
130         m_numberOfInputChannels = EnabledInputChannels;
131         m_inputDeviceId = inputDeviceId;
132
133         if (isInitialized())
134             recreateDestination();
135     }
136 }
137
138 Function<void(Function<void()>&&)> DefaultAudioDestinationNode::dispatchToRenderThreadFunction()
139 {
140     if (auto* workletProxy = context().audioWorklet().proxy()) {
141         return [workletProxy = makeRef(*workletProxy)](Function<void()>&& function) {
142             workletProxy->postTaskForModeToWorkletGlobalScope([function = WTFMove(function)](ScriptExecutionContext&) mutable {
143                 function();
144             }, WorkerRunLoop::defaultMode());
145         };
146     }
147     return nullptr;
148 }
149
150 void DefaultAudioDestinationNode::startRendering(CompletionHandler<void(Optional<Exception>&&)>&& completionHandler)
151 {
152     ASSERT(isInitialized());
153     if (!isInitialized())
154         return completionHandler(Exception { InvalidStateError, "AudioDestinationNode is not initialized"_s });
155
156     auto innerCompletionHandler = [completionHandler = WTFMove(completionHandler)](bool success) mutable {
157         completionHandler(success ? WTF::nullopt : makeOptional(Exception { InvalidStateError, "Failed to start the audio device"_s }));
158     };
159
160     m_wasDestinationStarted = true;
161     m_destination->start(dispatchToRenderThreadFunction(), WTFMove(innerCompletionHandler));
162 }
163
164 void DefaultAudioDestinationNode::resume(CompletionHandler<void(Optional<Exception>&&)>&& completionHandler)
165 {
166     ASSERT(isInitialized());
167     if (!isInitialized()) {
168         context().postTask([completionHandler = WTFMove(completionHandler)]() mutable {
169             completionHandler(Exception { InvalidStateError, "AudioDestinationNode is not initialized"_s });
170         });
171         return;
172     }
173     m_wasDestinationStarted = true;
174     m_destination->start(dispatchToRenderThreadFunction(), [completionHandler = WTFMove(completionHandler)](bool success) mutable {
175         completionHandler(success ? WTF::nullopt : makeOptional(Exception { InvalidStateError, "Failed to start the audio device"_s }));
176     });
177 }
178
179 void DefaultAudioDestinationNode::suspend(CompletionHandler<void(Optional<Exception>&&)>&& completionHandler)
180 {
181     ASSERT(isInitialized());
182     if (!isInitialized()) {
183         context().postTask([completionHandler = WTFMove(completionHandler)]() mutable {
184             completionHandler(Exception { InvalidStateError, "AudioDestinationNode is not initialized"_s });
185         });
186         return;
187     }
188
189     m_wasDestinationStarted = false;
190     m_destination->stop([completionHandler = WTFMove(completionHandler)](bool success) mutable {
191         completionHandler(success ? WTF::nullopt : makeOptional(Exception { InvalidStateError, "Failed to stop the audio device"_s }));
192     });
193 }
194
195 void DefaultAudioDestinationNode::restartRendering()
196 {
197     if (!m_wasDestinationStarted)
198         return;
199
200     m_destination->stop();
201     m_destination->start(dispatchToRenderThreadFunction());
202 }
203
204 void DefaultAudioDestinationNode::close(CompletionHandler<void()>&& completionHandler)
205 {
206     ASSERT(isInitialized());
207     uninitialize();
208     context().postTask(WTFMove(completionHandler));
209 }
210
211 unsigned DefaultAudioDestinationNode::maxChannelCount() const
212 {
213     return AudioDestination::maxChannelCount();
214 }
215
216 ExceptionOr<void> DefaultAudioDestinationNode::setChannelCount(unsigned channelCount)
217 {
218     // The channelCount for the input to this node controls the actual number of channels we
219     // send to the audio hardware. It can only be set depending on the maximum number of
220     // channels supported by the hardware.
221
222     ASSERT(isMainThread());
223     ALWAYS_LOG(LOGIDENTIFIER, channelCount);
224
225     if (channelCount > maxChannelCount())
226         return Exception { IndexSizeError, "Channel count exceeds maximum limit"_s };
227
228     auto oldChannelCount = this->channelCount();
229     auto result = AudioNode::setChannelCount(channelCount);
230     if (result.hasException())
231         return result;
232
233     if (this->channelCount() != oldChannelCount && isInitialized())
234         recreateDestination();
235
236     return { };
237 }
238
239 bool DefaultAudioDestinationNode::isPlaying()
240 {
241     return m_destination && m_destination->isPlaying();
242 }
243
244 unsigned DefaultAudioDestinationNode::framesPerBuffer() const
245 {
246     return m_destination ? m_destination->framesPerBuffer() : 0.;
247 }
248
249 } // namespace WebCore
250
251 #endif // ENABLE(WEB_AUDIO)