Home | History | Annotate | Download | only in webaudio
      1 /*
      2  * Copyright (C) 2010, 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 "AudioNodeInput.h"
     30 
     31 #include "AudioContext.h"
     32 #include "AudioNode.h"
     33 #include "AudioNodeOutput.h"
     34 #include <algorithm>
     35 
     36 using namespace std;
     37 
     38 namespace WebCore {
     39 
     40 AudioNodeInput::AudioNodeInput(AudioNode* node)
     41     : m_node(node)
     42     , m_renderingStateNeedUpdating(false)
     43 {
     44     m_monoSummingBus = adoptPtr(new AudioBus(1, AudioNode::ProcessingSizeInFrames));
     45     m_stereoSummingBus = adoptPtr(new AudioBus(2, AudioNode::ProcessingSizeInFrames));
     46 }
     47 
     48 void AudioNodeInput::connect(AudioNodeOutput* output)
     49 {
     50     ASSERT(context()->isGraphOwner());
     51 
     52     ASSERT(output && node());
     53     if (!output || !node())
     54         return;
     55 
     56     // Check if we're already connected to this output.
     57     if (m_outputs.contains(output))
     58         return;
     59 
     60     output->addInput(this);
     61     m_outputs.add(output);
     62     changedOutputs();
     63 
     64     // Sombody has just connected to us, so count it as a reference.
     65     node()->ref(AudioNode::RefTypeConnection);
     66 }
     67 
     68 void AudioNodeInput::disconnect(AudioNodeOutput* output)
     69 {
     70     ASSERT(context()->isGraphOwner());
     71 
     72     ASSERT(output && node());
     73     if (!output || !node())
     74         return;
     75 
     76     // First try to disconnect from "active" connections.
     77     if (m_outputs.contains(output)) {
     78         m_outputs.remove(output);
     79         changedOutputs();
     80         output->removeInput(this);
     81         node()->deref(AudioNode::RefTypeConnection); // Note: it's important to return immediately after all deref() calls since the node may be deleted.
     82         return;
     83     }
     84 
     85     // Otherwise, try to disconnect from disabled connections.
     86     if (m_disabledOutputs.contains(output)) {
     87         m_disabledOutputs.remove(output);
     88         output->removeInput(this);
     89         node()->deref(AudioNode::RefTypeDisabled); // Note: it's important to return immediately after all deref() calls since the node may be deleted.
     90         return;
     91     }
     92 
     93     ASSERT_NOT_REACHED();
     94 }
     95 
     96 void AudioNodeInput::disable(AudioNodeOutput* output)
     97 {
     98     ASSERT(context()->isGraphOwner());
     99 
    100     ASSERT(output && node());
    101     if (!output || !node())
    102         return;
    103 
    104     ASSERT(m_outputs.contains(output));
    105 
    106     m_disabledOutputs.add(output);
    107     m_outputs.remove(output);
    108     changedOutputs();
    109 
    110     node()->ref(AudioNode::RefTypeDisabled);
    111     node()->deref(AudioNode::RefTypeConnection); // Note: it's important to return immediately after all deref() calls since the node may be deleted.
    112 }
    113 
    114 void AudioNodeInput::enable(AudioNodeOutput* output)
    115 {
    116     ASSERT(context()->isGraphOwner());
    117 
    118     ASSERT(output && node());
    119     if (!output || !node())
    120         return;
    121 
    122     ASSERT(m_disabledOutputs.contains(output));
    123 
    124     // Move output from disabled list to active list.
    125     m_outputs.add(output);
    126     m_disabledOutputs.remove(output);
    127     changedOutputs();
    128 
    129     node()->ref(AudioNode::RefTypeConnection);
    130     node()->deref(AudioNode::RefTypeDisabled); // Note: it's important to return immediately after all deref() calls since the node may be deleted.
    131 }
    132 
    133 void AudioNodeInput::changedOutputs()
    134 {
    135     ASSERT(context()->isGraphOwner());
    136     if (!m_renderingStateNeedUpdating && !node()->isMarkedForDeletion()) {
    137         context()->markAudioNodeInputDirty(this);
    138         m_renderingStateNeedUpdating = true;
    139     }
    140 }
    141 
    142 void AudioNodeInput::updateRenderingState()
    143 {
    144     ASSERT(context()->isAudioThread() && context()->isGraphOwner());
    145 
    146     if (m_renderingStateNeedUpdating && !node()->isMarkedForDeletion()) {
    147         // Copy from m_outputs to m_renderingOutputs.
    148         m_renderingOutputs.resize(m_outputs.size());
    149         unsigned j = 0;
    150         for (HashSet<AudioNodeOutput*>::iterator i = m_outputs.begin(); i != m_outputs.end(); ++i, ++j) {
    151             AudioNodeOutput* output = *i;
    152             m_renderingOutputs[j] = output;
    153             output->updateRenderingState();
    154         }
    155 
    156         node()->checkNumberOfChannelsForInput(this);
    157 
    158         m_renderingStateNeedUpdating = false;
    159     }
    160 }
    161 
    162 unsigned AudioNodeInput::numberOfChannels() const
    163 {
    164     // Find the number of channels of the connection with the largest number of channels.
    165     unsigned maxChannels = 1; // one channel is the minimum allowed
    166 
    167     for (HashSet<AudioNodeOutput*>::iterator i = m_outputs.begin(); i != m_outputs.end(); ++i) {
    168         AudioNodeOutput* output = *i;
    169         maxChannels = max(maxChannels, output->bus()->numberOfChannels());
    170     }
    171 
    172     return maxChannels;
    173 }
    174 
    175 unsigned AudioNodeInput::numberOfRenderingChannels()
    176 {
    177     ASSERT(context()->isAudioThread());
    178 
    179     // Find the number of channels of the rendering connection with the largest number of channels.
    180     unsigned maxChannels = 1; // one channel is the minimum allowed
    181 
    182     for (unsigned i = 0; i < numberOfRenderingConnections(); ++i)
    183         maxChannels = max(maxChannels, renderingOutput(i)->bus()->numberOfChannels());
    184 
    185     return maxChannels;
    186 }
    187 
    188 AudioBus* AudioNodeInput::bus()
    189 {
    190     ASSERT(context()->isAudioThread());
    191 
    192     // Handle single connection specially to allow for in-place processing.
    193     if (numberOfRenderingConnections() == 1)
    194         return renderingOutput(0)->bus();
    195 
    196     // Multiple connections case (or no connections).
    197     return internalSummingBus();
    198 }
    199 
    200 AudioBus* AudioNodeInput::internalSummingBus()
    201 {
    202     ASSERT(context()->isAudioThread());
    203 
    204     // We must pick a summing bus which is the right size to handle the largest connection.
    205     switch (numberOfRenderingChannels()) {
    206     case 1:
    207         return m_monoSummingBus.get();
    208     case 2:
    209         return m_stereoSummingBus.get();
    210     // FIXME: could implement more than just mono and stereo mixing in the future
    211     }
    212 
    213     ASSERT_NOT_REACHED();
    214     return 0;
    215 }
    216 
    217 void AudioNodeInput::sumAllConnections(AudioBus* summingBus, size_t framesToProcess)
    218 {
    219     ASSERT(context()->isAudioThread());
    220 
    221     // We shouldn't be calling this method if there's only one connection, since it's less efficient.
    222     ASSERT(numberOfRenderingConnections() > 1);
    223 
    224     ASSERT(summingBus);
    225     if (!summingBus)
    226         return;
    227 
    228     summingBus->zero();
    229 
    230     for (unsigned i = 0; i < numberOfRenderingConnections(); ++i) {
    231         AudioNodeOutput* output = renderingOutput(i);
    232         ASSERT(output);
    233 
    234         // Render audio from this output.
    235         AudioBus* connectionBus = output->pull(0, framesToProcess);
    236 
    237         // Sum, with unity-gain.
    238         summingBus->sumFrom(*connectionBus);
    239     }
    240 }
    241 
    242 AudioBus* AudioNodeInput::pull(AudioBus* inPlaceBus, size_t framesToProcess)
    243 {
    244     ASSERT(context()->isAudioThread());
    245 
    246     // Handle single connection case.
    247     if (numberOfRenderingConnections() == 1) {
    248         // The output will optimize processing using inPlaceBus if it's able.
    249         AudioNodeOutput* output = this->renderingOutput(0);
    250         return output->pull(inPlaceBus, framesToProcess);
    251     }
    252 
    253     AudioBus* internalSummingBus = this->internalSummingBus();
    254 
    255     if (!numberOfRenderingConnections()) {
    256         // At least, generate silence if we're not connected to anything.
    257         // FIXME: if we wanted to get fancy, we could propagate a 'silent hint' here to optimize the downstream graph processing.
    258         internalSummingBus->zero();
    259         return internalSummingBus;
    260     }
    261 
    262     // Handle multiple connections case.
    263     sumAllConnections(internalSummingBus, framesToProcess);
    264 
    265     return internalSummingBus;
    266 }
    267 
    268 } // namespace WebCore
    269 
    270 #endif // ENABLE(WEB_AUDIO)
    271