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 "AudioDestinationNode.h"
30
31#include "AudioContext.h"
32#include "AudioNodeInput.h"
33#include "AudioNodeOutput.h"
34#include "AudioUtilities.h"
35#include "DenormalDisabler.h"
36#include <wtf/IsoMallocInlines.h>
37
38namespace WebCore {
39
40WTF_MAKE_ISO_ALLOCATED_IMPL(AudioDestinationNode);
41
42AudioDestinationNode::AudioDestinationNode(AudioContext& context, float sampleRate)
43 : AudioNode(context, sampleRate)
44 , m_currentSampleFrame(0)
45 , m_isSilent(true)
46 , m_isEffectivelyPlayingAudio(false)
47 , m_muted(false)
48{
49 setNodeType(NodeTypeDestination);
50 addInput(std::make_unique<AudioNodeInput>(this));
51}
52
53AudioDestinationNode::~AudioDestinationNode()
54{
55 uninitialize();
56}
57
58void AudioDestinationNode::render(AudioBus*, AudioBus* destinationBus, size_t numberOfFrames)
59{
60 // We don't want denormals slowing down any of the audio processing
61 // since they can very seriously hurt performance.
62 // This will take care of all AudioNodes because they all process within this scope.
63 DenormalDisabler denormalDisabler;
64
65 context().setAudioThread(Thread::current());
66
67 if (!context().isInitialized()) {
68 destinationBus->zero();
69 setIsSilent(true);
70 return;
71 }
72
73 ASSERT(numberOfFrames);
74 if (!numberOfFrames) {
75 destinationBus->zero();
76 setIsSilent(true);
77 return;
78 }
79
80 // Let the context take care of any business at the start of each render quantum.
81 context().handlePreRenderTasks();
82
83 // This will cause the node(s) connected to us to process, which in turn will pull on their input(s),
84 // all the way backwards through the rendering graph.
85 AudioBus* renderedBus = input(0)->pull(destinationBus, numberOfFrames);
86
87 if (!renderedBus)
88 destinationBus->zero();
89 else if (renderedBus != destinationBus) {
90 // in-place processing was not possible - so copy
91 destinationBus->copyFrom(*renderedBus);
92 }
93
94 // Process nodes which need a little extra help because they are not connected to anything, but still need to process.
95 context().processAutomaticPullNodes(numberOfFrames);
96
97 // Let the context take care of any business at the end of each render quantum.
98 context().handlePostRenderTasks();
99
100 // Advance current sample-frame.
101 m_currentSampleFrame += numberOfFrames;
102
103 setIsSilent(destinationBus->isSilent());
104
105 // The reason we are handling mute after the call to setIsSilent() is because the muted state does
106 // not affect the audio destination node's effective playing state.
107 if (m_muted)
108 destinationBus->zero();
109}
110
111void AudioDestinationNode::isPlayingDidChange()
112{
113 updateIsEffectivelyPlayingAudio();
114}
115
116void AudioDestinationNode::setIsSilent(bool isSilent)
117{
118 if (m_isSilent == isSilent)
119 return;
120
121 m_isSilent = isSilent;
122 updateIsEffectivelyPlayingAudio();
123}
124
125void AudioDestinationNode::updateIsEffectivelyPlayingAudio()
126{
127 bool isEffectivelyPlayingAudio = isPlaying() && !m_isSilent;
128 if (m_isEffectivelyPlayingAudio == isEffectivelyPlayingAudio)
129 return;
130
131 m_isEffectivelyPlayingAudio = isEffectivelyPlayingAudio;
132 context().isPlayingAudioDidChange();
133}
134
135} // namespace WebCore
136
137#endif // ENABLE(WEB_AUDIO)
138