void ChannelMergerNode::process(size_t framesToProcess) { AudioNodeOutput* output = this->output(0); ASSERT(output); ASSERT_UNUSED(framesToProcess, framesToProcess == output->bus()->length()); // Output bus not updated yet, so just output silence. if (m_desiredNumberOfOutputChannels != output->numberOfChannels()) { output->bus()->zero(); return; } // Merge all the channels from all the inputs into one output. unsigned outputChannelIndex = 0; for (unsigned i = 0; i < numberOfInputs(); ++i) { AudioNodeInput* input = this->input(i); if (input->isConnected()) { unsigned numberOfInputChannels = input->bus()->numberOfChannels(); // Merge channels from this particular input. for (unsigned j = 0; j < numberOfInputChannels; ++j) { AudioChannel* inputChannel = input->bus()->channel(j); AudioChannel* outputChannel = output->bus()->channel(outputChannelIndex); outputChannel->copyFrom(inputChannel); ++outputChannelIndex; } } } ASSERT(outputChannelIndex == output->numberOfChannels()); }
void AudioNode::connect(AudioNode* destination, unsigned outputIndex, unsigned inputIndex, ExceptionCode& ec) { ASSERT(isMainThread()); AudioContext::AutoLocker locker(context()); if (!destination) { ec = SYNTAX_ERR; return; } // Sanity check input and output indices. if (outputIndex >= numberOfOutputs()) { ec = INDEX_SIZE_ERR; return; } if (destination && inputIndex >= destination->numberOfInputs()) { ec = INDEX_SIZE_ERR; return; } if (context() != destination->context()) { ec = SYNTAX_ERR; return; } AudioNodeInput* input = destination->input(inputIndex); AudioNodeOutput* output = this->output(outputIndex); input->connect(output); // Let context know that a connection has been made. context()->incrementConnectionCount(); }
// Any time a connection or disconnection happens on any of our inputs, we potentially need to change the // number of channels of our output. void ChannelMergerNode::checkNumberOfChannelsForInput(AudioNodeInput* input) { ASSERT(context()->isAudioThread() && context()->isGraphOwner()); // Count how many channels we have all together from all of the inputs. unsigned numberOfOutputChannels = 0; for (unsigned i = 0; i < numberOfInputs(); ++i) { AudioNodeInput* input = this->input(i); if (input->isConnected()) numberOfOutputChannels += input->numberOfChannels(); } // If the actual number of channels exceeds the max allowed, just drop the excess. numberOfOutputChannels = std::min(numberOfOutputChannels, AudioContext::maxNumberOfChannels()); // Set the correct number of channels on the output AudioNodeOutput* output = this->output(0); ASSERT(output); output->setNumberOfChannels(numberOfOutputChannels); // There can in rare cases be a slight delay before the output bus is updated to the new number of // channels because of tryLocks() in the context's updating system. So record the new number of // output channels here. m_desiredNumberOfOutputChannels = numberOfOutputChannels; AudioNode::checkNumberOfChannelsForInput(input); }
bool AudioNode::connect(AudioNode* destination, unsigned outputIndex, unsigned inputIndex) { ASSERT(isMainThread()); AudioContext::AutoLocker locker(context()); // Sanity check input and output indices. if (outputIndex >= numberOfOutputs()) return false; if (destination && inputIndex >= destination->numberOfInputs()) return false; AudioNodeOutput* output = this->output(outputIndex); if (!destination) { // Disconnect output from any inputs it may be currently connected to. output->disconnectAllInputs(); return true; } if (context() != destination->context()) return false; AudioNodeInput* input = destination->input(inputIndex); input->connect(output); // Let context know that a connection has been made. context()->incrementConnectionCount(); return true; }
void PannerNode::notifyAudioSourcesConnectedToNode(AudioNode* node, HashSet<AudioNode*>& visitedNodes) { ASSERT(node); if (!node) return; // First check if this node is an AudioBufferSourceNode. If so, let it know about us so that doppler shift pitch can be taken into account. if (node->nodeType() == NodeTypeAudioBufferSource) { AudioBufferSourceNode* bufferSourceNode = reinterpret_cast<AudioBufferSourceNode*>(node); bufferSourceNode->setPannerNode(this); } else { // Go through all inputs to this node. for (unsigned i = 0; i < node->numberOfInputs(); ++i) { AudioNodeInput* input = node->input(i); // For each input, go through all of its connections, looking for AudioBufferSourceNodes. for (unsigned j = 0; j < input->numberOfRenderingConnections(); ++j) { AudioNodeOutput* connectedOutput = input->renderingOutput(j); AudioNode* connectedNode = connectedOutput->node(); if (visitedNodes.contains(connectedNode)) continue; visitedNodes.add(connectedNode); notifyAudioSourcesConnectedToNode(connectedNode, visitedNodes); } } } }
void PannerNode::notifyAudioSourcesConnectedToNode(AudioNode* node, HashMap<AudioNode*, bool>& visitedNodes) { ASSERT(node); if (!node) return; // First check if this node is an AudioBufferSourceNode. If so, let it know about us so that doppler shift pitch can be taken into account. if (node->nodeType() == NodeTypeAudioBufferSource) { AudioBufferSourceNode* bufferSourceNode = static_cast<AudioBufferSourceNode*>(node); bufferSourceNode->setPannerNode(this); } else { // Go through all inputs to this node. for (unsigned i = 0; i < node->numberOfInputs(); ++i) { AudioNodeInput* input = node->input(i); // For each input, go through all of its connections, looking for AudioBufferSourceNodes. for (unsigned j = 0; j < input->numberOfRenderingConnections(); ++j) { AudioNodeOutput* connectedOutput = input->renderingOutput(j); AudioNode* connectedNode = connectedOutput->node(); HashMap<AudioNode*, bool>::iterator iterator = visitedNodes.find(connectedNode); // If we've seen this node already, we don't need to process it again. Otherwise, // mark it as visited and recurse through the node looking for sources. if (iterator == visitedNodes.end()) { visitedNodes.set(connectedNode, true); notifyAudioSourcesConnectedToNode(connectedNode, visitedNodes); // recurse } } } } }
void AudioNodeOutput::disconnectAllInputs() { ASSERT(context()->isGraphOwner()); // AudioNodeInput::disconnect() changes m_inputs by calling removeInput(). while (!m_inputs.isEmpty()) { AudioNodeInput* input = *m_inputs.begin(); input->disconnect(this); } }
void AudioNodeOutput::enable() { ASSERT(context()->isGraphOwner()); if (!m_isEnabled) { for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) { AudioNodeInput* input = *i; input->enable(this); } m_isEnabled = true; } }
void AudioNodeOutput::propagateChannelCount() { ASSERT(context()->isAudioThread() && context()->isGraphOwner()); if (isChannelCountKnown()) { // Announce to any nodes we're connected to that we changed our channel count for its input. for (InputsIterator i = m_inputs.begin(); i != m_inputs.end(); ++i) { AudioNodeInput* input = *i; AudioNode* connectionNode = input->node(); connectionNode->checkNumberOfChannelsForInput(input); } } }
// Any time a connection or disconnection happens on any of our inputs, we potentially need to change the // number of channels of our output. void AudioChannelMerger::checkNumberOfChannelsForInput(AudioNodeInput*) { ASSERT(context()->isAudioThread() && context()->isGraphOwner()); // Count how many channels we have all together from all of the inputs. unsigned numberOfOutputChannels = 0; for (unsigned i = 0; i < numberOfInputs(); ++i) { AudioNodeInput* input = this->input(i); if (input->isConnected()) numberOfOutputChannels += input->bus()->numberOfChannels(); } // Set the correct number of channels on the output AudioNodeOutput* output = this->output(0); ASSERT(output); output->setNumberOfChannels(numberOfOutputChannels); }
void AudioNode::connect(AudioNode* destination, unsigned outputIndex, unsigned inputIndex, ExceptionState& exceptionState) { ASSERT(isMainThread()); AudioContext::AutoLocker locker(context()); if (!destination) { exceptionState.throwDOMException( SyntaxError, "invalid destination node."); return; } // Sanity check input and output indices. if (outputIndex >= numberOfOutputs()) { exceptionState.throwDOMException( IndexSizeError, "output index (" + String::number(outputIndex) + ") exceeds number of outputs (" + String::number(numberOfOutputs()) + ")."); return; } if (destination && inputIndex >= destination->numberOfInputs()) { exceptionState.throwDOMException( IndexSizeError, "input index (" + String::number(inputIndex) + ") exceeds number of inputs (" + String::number(destination->numberOfInputs()) + ")."); return; } if (context() != destination->context()) { exceptionState.throwDOMException( SyntaxError, "cannot connect to a destination belonging to a different audio context."); return; } AudioNodeInput* input = destination->input(inputIndex); AudioNodeOutput* output = this->output(outputIndex); input->connect(output); // Let context know that a connection has been made. context()->incrementConnectionCount(); }
void ChannelMergerNode::process(size_t framesToProcess) { AudioNodeOutput* output = this->output(0); ASSERT(output); ASSERT_UNUSED(framesToProcess, framesToProcess == output->bus()->length()); // Output bus not updated yet, so just output silence. if (m_desiredNumberOfOutputChannels != output->numberOfChannels()) { output->bus()->zero(); return; } // Merge all the channels from all the inputs into one output. unsigned outputChannelIndex = 0; unsigned maxAllowedOutputChannels = output->numberOfChannels(); for (unsigned i = 0; i < numberOfInputs(); ++i) { AudioNodeInput* input = this->input(i); if (input->isConnected()) { unsigned numberOfInputChannels = input->bus()->numberOfChannels(); // Merge channels from this particular input, but be careful not to exceed the number of // output channels. (This can happen if there are many inputs with each input // containing many channels.) for (unsigned j = 0; j < numberOfInputChannels; ++j) { if (outputChannelIndex < maxAllowedOutputChannels) { AudioChannel* inputChannel = input->bus()->channel(j); AudioChannel* outputChannel = output->bus()->channel(outputChannelIndex); outputChannel->copyFrom(inputChannel); ++outputChannelIndex; } } } if (outputChannelIndex >= maxAllowedOutputChannels) break; } ASSERT(outputChannelIndex == output->numberOfChannels()); }
void AudioNodeOutput::disconnectInput(AudioNodeInput& input) { ASSERT(deferredTaskHandler().isGraphOwner()); DCHECK(isConnectedToInput(input)); input.disconnect(*this); }
void AudioNodeOutput::removeInput(AudioNodeInput& input) { ASSERT(deferredTaskHandler().isGraphOwner()); input.handler().breakConnection(); m_inputs.remove(&input); }
void AudioNodeOutput::addInput(AudioNodeInput& input) { ASSERT(deferredTaskHandler().isGraphOwner()); m_inputs.add(&input); input.handler().makeConnection(); }