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