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/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.
     38 const unsigned minSampleRate = 8000;
     39 const unsigned maxSampleRate = 192000;
     40 
     41 namespace WebCore {
     42 
     43 PassRefPtr<MediaElementAudioSourceNode> MediaElementAudioSourceNode::create(AudioContext* context, HTMLMediaElement* mediaElement)
     44 {
     45     return adoptRef(new MediaElementAudioSourceNode(context, mediaElement));
     46 }
     47 
     48 MediaElementAudioSourceNode::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 
     63 MediaElementAudioSourceNode::~MediaElementAudioSourceNode()
     64 {
     65     m_mediaElement->setAudioSourceNode(0);
     66     uninitialize();
     67 }
     68 
     69 void 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 
    104 void 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 
    138 void MediaElementAudioSourceNode::reset()
    139 {
    140 }
    141 
    142 void MediaElementAudioSourceNode::lock()
    143 {
    144     ref();
    145     m_processLock.lock();
    146 }
    147 
    148 void MediaElementAudioSourceNode::unlock()
    149 {
    150     m_processLock.unlock();
    151     deref();
    152 }
    153 
    154 } // namespace WebCore
    155 
    156 #endif // ENABLE(WEB_AUDIO)
    157