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
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
23 * DAMAGE.
24 */
25
26 #include "third_party/blink/renderer/modules/webaudio/realtime_audio_destination_node.h"
27
28 #include "third_party/blink/renderer/modules/webaudio/audio_context.h"
29 #include "third_party/blink/renderer/modules/webaudio/audio_node_input.h"
30 #include "third_party/blink/renderer/modules/webaudio/audio_node_output.h"
31 #include "third_party/blink/renderer/modules/webaudio/audio_worklet.h"
32 #include "third_party/blink/renderer/modules/webaudio/audio_worklet_messaging_proxy.h"
33 #include "third_party/blink/renderer/platform/audio/audio_utilities.h"
34 #include "third_party/blink/renderer/platform/audio/denormal_disabler.h"
35 #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
36 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
37 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
38
39 namespace blink {
40
41 scoped_refptr<RealtimeAudioDestinationHandler>
Create(AudioNode & node,const WebAudioLatencyHint & latency_hint,base::Optional<float> sample_rate)42 RealtimeAudioDestinationHandler::Create(AudioNode& node,
43 const WebAudioLatencyHint& latency_hint,
44 base::Optional<float> sample_rate) {
45 return base::AdoptRef(
46 new RealtimeAudioDestinationHandler(node, latency_hint, sample_rate));
47 }
48
RealtimeAudioDestinationHandler(AudioNode & node,const WebAudioLatencyHint & latency_hint,base::Optional<float> sample_rate)49 RealtimeAudioDestinationHandler::RealtimeAudioDestinationHandler(
50 AudioNode& node,
51 const WebAudioLatencyHint& latency_hint,
52 base::Optional<float> sample_rate)
53 : AudioDestinationHandler(node),
54 latency_hint_(latency_hint),
55 sample_rate_(sample_rate),
56 allow_pulling_audio_graph_(false),
57 task_runner_(Context()->GetExecutionContext()->GetTaskRunner(
58 TaskType::kInternalMediaRealTime)) {
59 // Node-specific default channel count and mixing rules.
60 channel_count_ = 2;
61 SetInternalChannelCountMode(kExplicit);
62 SetInternalChannelInterpretation(AudioBus::kSpeakers);
63 }
64
~RealtimeAudioDestinationHandler()65 RealtimeAudioDestinationHandler::~RealtimeAudioDestinationHandler() {
66 DCHECK(!IsInitialized());
67 }
68
Dispose()69 void RealtimeAudioDestinationHandler::Dispose() {
70 Uninitialize();
71 AudioDestinationHandler::Dispose();
72 }
73
Initialize()74 void RealtimeAudioDestinationHandler::Initialize() {
75 DCHECK(IsMainThread());
76
77 CreatePlatformDestination();
78 AudioHandler::Initialize();
79 }
80
Uninitialize()81 void RealtimeAudioDestinationHandler::Uninitialize() {
82 DCHECK(IsMainThread());
83
84 // It is possible that the handler is already uninitialized.
85 if (!IsInitialized()) {
86 return;
87 }
88
89 StopPlatformDestination();
90 AudioHandler::Uninitialize();
91 }
92
SetChannelCount(unsigned channel_count,ExceptionState & exception_state)93 void RealtimeAudioDestinationHandler::SetChannelCount(
94 unsigned channel_count,
95 ExceptionState& exception_state) {
96 DCHECK(IsMainThread());
97
98 // The channelCount for the input to this node controls the actual number of
99 // channels we send to the audio hardware. It can only be set if the number
100 // is less than the number of hardware channels.
101 if (channel_count > MaxChannelCount()) {
102 exception_state.ThrowDOMException(
103 DOMExceptionCode::kIndexSizeError,
104 ExceptionMessages::IndexOutsideRange<unsigned>(
105 "channel count", channel_count, 1,
106 ExceptionMessages::kInclusiveBound, MaxChannelCount(),
107 ExceptionMessages::kInclusiveBound));
108 return;
109 }
110
111 uint32_t old_channel_count = this->ChannelCount();
112 AudioHandler::SetChannelCount(channel_count, exception_state);
113
114 // Stop, re-create and start the destination to apply the new channel count.
115 if (this->ChannelCount() != old_channel_count &&
116 !exception_state.HadException()) {
117 StopPlatformDestination();
118 CreatePlatformDestination();
119 StartPlatformDestination();
120 }
121 }
122
StartRendering()123 void RealtimeAudioDestinationHandler::StartRendering() {
124 DCHECK(IsMainThread());
125
126 StartPlatformDestination();
127 }
128
StopRendering()129 void RealtimeAudioDestinationHandler::StopRendering() {
130 DCHECK(IsMainThread());
131
132 StopPlatformDestination();
133 }
134
Pause()135 void RealtimeAudioDestinationHandler::Pause() {
136 DCHECK(IsMainThread());
137 if (platform_destination_) {
138 platform_destination_->Pause();
139 }
140 }
141
Resume()142 void RealtimeAudioDestinationHandler::Resume() {
143 DCHECK(IsMainThread());
144 if (platform_destination_) {
145 platform_destination_->Resume();
146 }
147 }
148
RestartRendering()149 void RealtimeAudioDestinationHandler::RestartRendering() {
150 DCHECK(IsMainThread());
151
152 StopRendering();
153 StartRendering();
154 }
155
MaxChannelCount() const156 uint32_t RealtimeAudioDestinationHandler::MaxChannelCount() const {
157 return AudioDestination::MaxChannelCount();
158 }
159
SampleRate() const160 double RealtimeAudioDestinationHandler::SampleRate() const {
161 // This can be accessed from both threads (main and audio), so it is
162 // possible that |platform_destination_| is not fully functional when it
163 // is accssed by the audio thread.
164 return platform_destination_ ? platform_destination_->SampleRate() : 0;
165 }
166
Render(AudioBus * destination_bus,uint32_t number_of_frames,const AudioIOPosition & output_position,const AudioCallbackMetric & metric)167 void RealtimeAudioDestinationHandler::Render(
168 AudioBus* destination_bus,
169 uint32_t number_of_frames,
170 const AudioIOPosition& output_position,
171 const AudioCallbackMetric& metric) {
172 TRACE_EVENT0("webaudio", "RealtimeAudioDestinationHandler::Render");
173
174 // Denormals can seriously hurt performance of audio processing. This will
175 // take care of all AudioNode processes within this scope.
176 DenormalDisabler denormal_disabler;
177
178 AudioContext* context = static_cast<AudioContext*>(Context());
179
180 // A sanity check for the associated context, but this does not guarantee the
181 // safe execution of the subsequence operations because the hanlder holds
182 // the context as |UntracedMember| and it can go away anytime.
183 DCHECK(context);
184 if (!context) {
185 return;
186 }
187
188 context->GetDeferredTaskHandler().SetAudioThreadToCurrentThread();
189
190 // If this node is not initialized yet, pass silence to the platform audio
191 // destination. It is for the case where this node is in the middle of
192 // tear-down process.
193 if (!IsInitialized()) {
194 destination_bus->Zero();
195 return;
196 }
197
198 context->HandlePreRenderTasks(&output_position, &metric);
199
200 // Only pull on the audio graph if we have not stopped the destination. It
201 // takes time for the destination to stop, but we want to stop pulling before
202 // the destination has actually stopped.
203 if (IsPullingAudioGraphAllowed()) {
204 // Renders the graph by pulling all the inputs to this node. This will in
205 // turn pull on their inputs, all the way backwards through the graph.
206 scoped_refptr<AudioBus> rendered_bus =
207 Input(0).Pull(destination_bus, number_of_frames);
208
209 DCHECK(rendered_bus);
210 if (!rendered_bus) {
211 // AudioNodeInput might be in the middle of destruction. Then the internal
212 // summing bus will return as nullptr. Then zero out the output.
213 destination_bus->Zero();
214 } else if (rendered_bus != destination_bus) {
215 // In-place processing was not possible. Copy the rendered result to the
216 // given |destination_bus| buffer.
217 destination_bus->CopyFrom(*rendered_bus);
218 }
219 } else {
220 destination_bus->Zero();
221 }
222
223 // Processes "automatic" nodes that are not connected to anything. This can
224 // be done after copying because it does not affect the rendered result.
225 context->GetDeferredTaskHandler().ProcessAutomaticPullNodes(number_of_frames);
226
227 context->HandlePostRenderTasks();
228
229 context->HandleAudibility(destination_bus);
230
231 // Advances the current sample-frame.
232 AdvanceCurrentSampleFrame(number_of_frames);
233
234 context->UpdateWorkletGlobalScopeOnRenderingThread();
235
236 SetDetectSilenceIfNecessary(
237 context->GetDeferredTaskHandler().HasAutomaticPullNodes());
238 }
239
SetDetectSilenceIfNecessary(bool has_automatic_pull_nodes)240 void RealtimeAudioDestinationHandler::SetDetectSilenceIfNecessary(
241 bool has_automatic_pull_nodes) {
242 // When there is no automatic pull nodes, or the destination has an active
243 // input connection, the silence detection should be turned on.
244 bool needs_silence_detection =
245 !has_automatic_pull_nodes || Input(0).IsConnected();
246
247 // Post a cross-thread task only when the detecting condition has changed.
248 if (is_detecting_silence_ != needs_silence_detection) {
249 PostCrossThreadTask(*task_runner_, FROM_HERE,
250 CrossThreadBindOnce(&RealtimeAudioDestinationHandler::SetDetectSilence,
251 AsWeakPtr(), needs_silence_detection));
252 is_detecting_silence_ = needs_silence_detection;
253 }
254 }
255
SetDetectSilence(bool detect_silence)256 void RealtimeAudioDestinationHandler::SetDetectSilence(bool detect_silence) {
257 DCHECK(IsMainThread());
258
259 platform_destination_->SetDetectSilence(detect_silence);
260 }
261
GetCallbackBufferSize() const262 uint32_t RealtimeAudioDestinationHandler::GetCallbackBufferSize() const {
263 DCHECK(IsMainThread());
264 DCHECK(IsInitialized());
265
266 return platform_destination_->CallbackBufferSize();
267 }
268
GetFramesPerBuffer() const269 int RealtimeAudioDestinationHandler::GetFramesPerBuffer() const {
270 DCHECK(IsMainThread());
271 DCHECK(IsInitialized());
272
273 return platform_destination_ ? platform_destination_->FramesPerBuffer() : 0;
274 }
275
CreatePlatformDestination()276 void RealtimeAudioDestinationHandler::CreatePlatformDestination() {
277 platform_destination_ = AudioDestination::Create(*this, ChannelCount(),
278 latency_hint_, sample_rate_);
279 }
280
StartPlatformDestination()281 void RealtimeAudioDestinationHandler::StartPlatformDestination() {
282 DCHECK(IsMainThread());
283
284 if (platform_destination_->IsPlaying()) {
285 return;
286 }
287
288 AudioWorklet* audio_worklet = Context()->audioWorklet();
289 if (audio_worklet && audio_worklet->IsReady()) {
290 // This task runner is only used to fire the audio render callback, so it
291 // MUST not be throttled to avoid potential audio glitch.
292 platform_destination_->StartWithWorkletTaskRunner(
293 audio_worklet->GetMessagingProxy()
294 ->GetBackingWorkerThread()
295 ->GetTaskRunner(TaskType::kInternalMediaRealTime));
296 } else {
297 platform_destination_->Start();
298 }
299
300 // Allow the graph to be pulled once the destination actually starts
301 // requesting data.
302 EnablePullingAudioGraph();
303 }
304
StopPlatformDestination()305 void RealtimeAudioDestinationHandler::StopPlatformDestination() {
306 DCHECK(IsMainThread());
307
308 // Stop pulling on the graph, even if the destination is still requesting data
309 // for a while. (It may take a bit of time for the destination to stop.)
310 DisablePullingAudioGraph();
311
312 if (platform_destination_->IsPlaying()) {
313 platform_destination_->Stop();
314 }
315 }
316
317 // -----------------------------------------------------------------------------
318
RealtimeAudioDestinationNode(AudioContext & context,const WebAudioLatencyHint & latency_hint,base::Optional<float> sample_rate)319 RealtimeAudioDestinationNode::RealtimeAudioDestinationNode(
320 AudioContext& context,
321 const WebAudioLatencyHint& latency_hint,
322 base::Optional<float> sample_rate)
323 : AudioDestinationNode(context) {
324 SetHandler(RealtimeAudioDestinationHandler::Create(*this, latency_hint,
325 sample_rate));
326 }
327
Create(AudioContext * context,const WebAudioLatencyHint & latency_hint,base::Optional<float> sample_rate)328 RealtimeAudioDestinationNode* RealtimeAudioDestinationNode::Create(
329 AudioContext* context,
330 const WebAudioLatencyHint& latency_hint,
331 base::Optional<float> sample_rate) {
332 return MakeGarbageCollected<RealtimeAudioDestinationNode>(
333 *context, latency_hint, sample_rate);
334 }
335
336 } // namespace blink
337