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 "modules/webaudio/MediaElementAudioSourceNode.h"
30
31#include "core/platform/Logging.h"
32#include "core/platform/graphics/MediaPlayer.h"
33#include "modules/webaudio/AudioContext.h"
34#include "modules/webaudio/AudioNodeOutput.h"
35#include "wtf/Locker.h"
36
37// These are somewhat arbitrary limits, but we need to do some kind of sanity-checking.
38const unsigned minSampleRate = 8000;
39const unsigned maxSampleRate = 192000;
40
41namespace WebCore {
42
43PassRefPtr<MediaElementAudioSourceNode> MediaElementAudioSourceNode::create(AudioContext* context, HTMLMediaElement* mediaElement)
44{
45    return adoptRef(new MediaElementAudioSourceNode(context, mediaElement));
46}
47
48MediaElementAudioSourceNode::MediaElementAudioSourceNode(AudioContext* context, HTMLMediaElement* mediaElement)
49    : AudioSourceNode(context, context->sampleRate())
50    , m_mediaElement(mediaElement)
51    , m_sourceNumberOfChannels(0)
52    , m_sourceSampleRate(0)
53{
54    ScriptWrappable::init(this);
55    // Default to stereo. This could change depending on what the media element .src is set to.
56    addOutput(adoptPtr(new AudioNodeOutput(this, 2)));
57
58    setNodeType(NodeTypeMediaElementAudioSource);
59
60    initialize();
61}
62
63MediaElementAudioSourceNode::~MediaElementAudioSourceNode()
64{
65    m_mediaElement->setAudioSourceNode(0);
66    uninitialize();
67}
68
69void MediaElementAudioSourceNode::setFormat(size_t numberOfChannels, float sourceSampleRate)
70{
71    if (numberOfChannels != m_sourceNumberOfChannels || sourceSampleRate != m_sourceSampleRate) {
72        if (!numberOfChannels || numberOfChannels > AudioContext::maxNumberOfChannels() || sourceSampleRate < minSampleRate || sourceSampleRate > maxSampleRate) {
73            // process() will generate silence for these uninitialized values.
74            LOG(Media, "MediaElementAudioSourceNode::setFormat(%u, %f) - unhandled format change", static_cast<unsigned>(numberOfChannels), sourceSampleRate);
75            m_sourceNumberOfChannels = 0;
76            m_sourceSampleRate = 0;
77            return;
78        }
79
80        m_sourceNumberOfChannels = numberOfChannels;
81        m_sourceSampleRate = sourceSampleRate;
82
83        // Synchronize with process().
84        Locker<MediaElementAudioSourceNode> locker(*this);
85
86        if (sourceSampleRate != sampleRate()) {
87            double scaleFactor = sourceSampleRate / sampleRate();
88            m_multiChannelResampler = adoptPtr(new MultiChannelResampler(scaleFactor, numberOfChannels));
89        } else {
90            // Bypass resampling.
91            m_multiChannelResampler.clear();
92        }
93
94        {
95            // The context must be locked when changing the number of output channels.
96            AudioContext::AutoLocker contextLocker(context());
97
98            // Do any necesssary re-configuration to the output's number of channels.
99            output(0)->setNumberOfChannels(numberOfChannels);
100        }
101    }
102}
103
104void MediaElementAudioSourceNode::process(size_t numberOfFrames)
105{
106    AudioBus* outputBus = output(0)->bus();
107
108    if (!mediaElement() || !m_sourceNumberOfChannels || !m_sourceSampleRate) {
109        outputBus->zero();
110        return;
111    }
112
113    // Use a tryLock() to avoid contention in the real-time audio thread.
114    // If we fail to acquire the lock then the HTMLMediaElement must be in the middle of
115    // reconfiguring its playback engine, so we output silence in this case.
116    MutexTryLocker tryLocker(m_processLock);
117    if (tryLocker.locked()) {
118        if (AudioSourceProvider* provider = mediaElement()->audioSourceProvider()) {
119            if (m_multiChannelResampler.get()) {
120                ASSERT(m_sourceSampleRate != sampleRate());
121                m_multiChannelResampler->process(provider, outputBus, numberOfFrames);
122            } else {
123                // Bypass the resampler completely if the source is at the context's sample-rate.
124                ASSERT(m_sourceSampleRate == sampleRate());
125                provider->provideInput(outputBus, numberOfFrames);
126            }
127        } else {
128            // Either this port doesn't yet support HTMLMediaElement audio stream access,
129            // or the stream is not yet available.
130            outputBus->zero();
131        }
132    } else {
133        // We failed to acquire the lock.
134        outputBus->zero();
135    }
136}
137
138void MediaElementAudioSourceNode::reset()
139{
140}
141
142void MediaElementAudioSourceNode::lock()
143{
144    ref();
145    m_processLock.lock();
146}
147
148void MediaElementAudioSourceNode::unlock()
149{
150    m_processLock.unlock();
151    deref();
152}
153
154} // namespace WebCore
155
156#endif // ENABLE(WEB_AUDIO)
157