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