1/*
2 * Copyright (C) 2012, 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 "modules/webaudio/MediaStreamAudioSourceNode.h"
30
31#include "platform/Logging.h"
32#include "modules/webaudio/AudioContext.h"
33#include "modules/webaudio/AudioNodeOutput.h"
34#include "wtf/Locker.h"
35
36namespace blink {
37
38MediaStreamAudioSourceNode* MediaStreamAudioSourceNode::create(AudioContext* context, MediaStream* mediaStream, MediaStreamTrack* audioTrack, PassOwnPtr<AudioSourceProvider> audioSourceProvider)
39{
40    return adoptRefCountedGarbageCollectedWillBeNoop(new MediaStreamAudioSourceNode(context, mediaStream, audioTrack, audioSourceProvider));
41}
42
43MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(AudioContext* context, MediaStream* mediaStream, MediaStreamTrack* audioTrack, PassOwnPtr<AudioSourceProvider> audioSourceProvider)
44    : AudioSourceNode(context, context->sampleRate())
45    , m_mediaStream(mediaStream)
46    , m_audioTrack(audioTrack)
47    , m_audioSourceProvider(audioSourceProvider)
48    , m_sourceNumberOfChannels(0)
49{
50    // Default to stereo. This could change depending on the format of the
51    // MediaStream's audio track.
52    addOutput(AudioNodeOutput::create(this, 2));
53
54    setNodeType(NodeTypeMediaStreamAudioSource);
55
56    initialize();
57}
58
59MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode()
60{
61    ASSERT(!isInitialized());
62}
63
64void MediaStreamAudioSourceNode::dispose()
65{
66    uninitialize();
67    AudioSourceNode::dispose();
68}
69
70void MediaStreamAudioSourceNode::setFormat(size_t numberOfChannels, float sourceSampleRate)
71{
72    if (numberOfChannels != m_sourceNumberOfChannels || sourceSampleRate != sampleRate()) {
73        // The sample-rate must be equal to the context's sample-rate.
74        if (!numberOfChannels || numberOfChannels > AudioContext::maxNumberOfChannels() || sourceSampleRate != sampleRate()) {
75            // process() will generate silence for these uninitialized values.
76            WTF_LOG(Media, "MediaStreamAudioSourceNode::setFormat(%u, %f) - unhandled format change", static_cast<unsigned>(numberOfChannels), sourceSampleRate);
77            m_sourceNumberOfChannels = 0;
78            return;
79        }
80
81        // Synchronize with process().
82        MutexLocker locker(m_processLock);
83
84        m_sourceNumberOfChannels = numberOfChannels;
85
86        {
87            // The context must be locked when changing the number of output channels.
88            AudioContext::AutoLocker contextLocker(context());
89
90            // Do any necesssary re-configuration to the output's number of channels.
91            output(0)->setNumberOfChannels(numberOfChannels);
92        }
93    }
94}
95
96void MediaStreamAudioSourceNode::process(size_t numberOfFrames)
97{
98    AudioBus* outputBus = output(0)->bus();
99
100    if (!audioSourceProvider()) {
101        outputBus->zero();
102        return;
103    }
104
105    if (!mediaStream() || m_sourceNumberOfChannels != outputBus->numberOfChannels()) {
106        outputBus->zero();
107        return;
108    }
109
110    // Use a tryLock() to avoid contention in the real-time audio thread.
111    // If we fail to acquire the lock then the MediaStream must be in the middle of
112    // a format change, so we output silence in this case.
113    MutexTryLocker tryLocker(m_processLock);
114    if (tryLocker.locked())
115        audioSourceProvider()->provideInput(outputBus, numberOfFrames);
116    else {
117        // We failed to acquire the lock.
118        outputBus->zero();
119    }
120}
121
122void MediaStreamAudioSourceNode::trace(Visitor* visitor)
123{
124    visitor->trace(m_mediaStream);
125    visitor->trace(m_audioTrack);
126    AudioSourceNode::trace(visitor);
127    AudioSourceProviderClient::trace(visitor);
128}
129
130} // namespace blink
131
132#endif // ENABLE(WEB_AUDIO)
133