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