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