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