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 } } } } }
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; }
AudioBus* AudioNodeInput::pull(AudioBus* inPlaceBus, size_t framesToProcess) { ASSERT(context()->isAudioThread()); // Handle single connection case. if (numberOfRenderingConnections() == 1 && node()->internalChannelCountMode() == AudioNode::Max) { // The output will optimize processing using inPlaceBus if it's able. AudioNodeOutput* output = this->renderingOutput(0); return output->pull(inPlaceBus, framesToProcess); } AudioBus* internalSummingBus = this->internalSummingBus(); if (!numberOfRenderingConnections()) { // At least, generate silence if we're not connected to anything. // FIXME: if we wanted to get fancy, we could propagate a 'silent hint' here to optimize the downstream graph processing. internalSummingBus->zero(); return internalSummingBus; } // Handle multiple connections case. sumAllConnections(internalSummingBus, framesToProcess); return internalSummingBus; }
// 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); }
void AudioNodeInput::sumAllConnections(AudioBus* summingBus, size_t framesToProcess) { ASSERT(context()->isAudioThread()); // We shouldn't be calling this method if there's only one connection, since it's less efficient. ASSERT(numberOfRenderingConnections() > 1 || node()->internalChannelCountMode() != AudioNode::Max); ASSERT(summingBus); if (!summingBus) return; summingBus->zero(); AudioBus::ChannelInterpretation interpretation = node()->internalChannelInterpretation(); for (unsigned i = 0; i < numberOfRenderingConnections(); ++i) { AudioNodeOutput* output = renderingOutput(i); ASSERT(output); // Render audio from this output. AudioBus* connectionBus = output->pull(0, framesToProcess); // Sum, with unity-gain. summingBus->sumFrom(*connectionBus, interpretation); } }
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 AudioChannelMerger::process(size_t framesToProcess) { AudioNodeOutput* output = this->output(0); ASSERT(output); ASSERT_UNUSED(framesToProcess, framesToProcess == output->bus()->length()); // 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()); }
unsigned AudioNodeInput::numberOfChannels() const { // Find the number of channels of the connection with the largest number of channels. unsigned maxChannels = 1; // one channel is the minimum allowed for (HashSet<AudioNodeOutput*>::iterator i = m_outputs.begin(); i != m_outputs.end(); ++i) { AudioNodeOutput* output = *i; maxChannels = max(maxChannels, output->bus()->numberOfChannels()); } return maxChannels; }
void AudioNode::disconnect(unsigned outputIndex, ExceptionCode& ec) { ASSERT(isMainThread()); AudioContext::AutoLocker locker(context()); // Sanity check input and output indices. if (outputIndex >= numberOfOutputs()) { ec = INDEX_SIZE_ERR; return; } AudioNodeOutput* output = this->output(outputIndex); output->disconnectAllInputs(); }
void AudioNode::disconnect(unsigned outputIndex, ExceptionState& exceptionState) { ASSERT(isMainThread()); AudioContext::AutoLocker locker(context()); // 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; } AudioNodeOutput* output = this->output(outputIndex); output->disconnectAll(); }
// 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 AudioSummingJunction::updateRenderingState() { ASSERT(context()->isAudioThread() && context()->isGraphOwner()); if (m_renderingStateNeedUpdating) { // Copy from m_outputs to m_renderingOutputs. m_renderingOutputs.resize(m_outputs.size()); unsigned j = 0; for (HashSet<AudioNodeOutput*>::iterator i = m_outputs.begin(); i != m_outputs.end(); ++i, ++j) { AudioNodeOutput* output = *i; m_renderingOutputs[j] = output; output->updateRenderingState(); } didUpdate(); m_renderingStateNeedUpdating = false; } }
void AudioParamHandler::disconnect(AudioNodeOutput& output) { ASSERT(deferredTaskHandler().isGraphOwner()); if (m_outputs.contains(&output)) { m_outputs.remove(&output); changedOutputs(); output.removeParam(*this); } }
void AudioNodeInput::updateRenderingState() { ASSERT(context()->isAudioThread() && context()->isGraphOwner()); if (m_renderingStateNeedUpdating && !node()->isMarkedForDeletion()) { // Copy from m_outputs to m_renderingOutputs. m_renderingOutputs.resize(m_outputs.size()); unsigned j = 0; for (HashSet<AudioNodeOutput*>::iterator i = m_outputs.begin(); i != m_outputs.end(); ++i, ++j) { AudioNodeOutput* output = *i; m_renderingOutputs[j] = output; output->updateRenderingState(); } node()->checkNumberOfChannelsForInput(this); m_renderingStateNeedUpdating = false; } }
void AudioNodeInput::connect(AudioNodeOutput& output) { ASSERT(deferredTaskHandler().isGraphOwner()); // Check if we're already connected to this output. if (m_outputs.contains(&output)) return; output.addInput(*this); m_outputs.add(&output); changedOutputs(); }
void AudioParamHandler::connect(AudioNodeOutput& output) { ASSERT(deferredTaskHandler().isGraphOwner()); if (m_outputs.contains(&output)) return; output.addParam(*this); m_outputs.add(&output); changedOutputs(); }
unsigned AudioNodeInput::numberOfChannels() const { AudioNode::ChannelCountMode mode = node()->internalChannelCountMode(); if (mode == AudioNode::Explicit) return node()->channelCount(); // Find the number of channels of the connection with the largest number of channels. unsigned maxChannels = 1; // one channel is the minimum allowed for (HashSet<AudioNodeOutput*>::iterator i = m_outputs.begin(); i != m_outputs.end(); ++i) { AudioNodeOutput* output = *i; // Use output()->numberOfChannels() instead of output->bus()->numberOfChannels(), // because the calling of AudioNodeOutput::bus() is not safe here. maxChannels = max(maxChannels, output->numberOfChannels()); } if (mode == AudioNode::ClampedMax) maxChannels = min(maxChannels, static_cast<unsigned>(node()->channelCount())); return maxChannels; }
void AudioParam::calculateFinalValues(float* values, unsigned numberOfValues, bool sampleAccurate) { bool isGood = context() && context()->isAudioThread() && values && numberOfValues; ASSERT(isGood); if (!isGood) return; // The calculated result will be the "intrinsic" value summed with all audio-rate connections. if (sampleAccurate) { // Calculate sample-accurate (a-rate) intrinsic values. calculateTimelineValues(values, numberOfValues); } else { // Calculate control-rate (k-rate) intrinsic value. bool hasValue; float timelineValue = m_timeline.valueForContextTime(context(), narrowPrecisionToFloat(m_value), hasValue); if (hasValue) m_value = timelineValue; values[0] = narrowPrecisionToFloat(m_value); } // Now sum all of the audio-rate connections together (unity-gain summing junction). // Note that connections would normally be mono, but we mix down to mono if necessary. RefPtr<AudioBus> summingBus = AudioBus::create(1, numberOfValues, false); summingBus->setChannelMemory(0, values, numberOfValues); for (unsigned i = 0; i < numberOfRenderingConnections(); ++i) { AudioNodeOutput* output = renderingOutput(i); ASSERT(output); // Render audio from this output. AudioBus* connectionBus = output->pull(0, AudioNode::ProcessingSizeInFrames); // Sum, with unity-gain. summingBus->sumFrom(*connectionBus); } }
void AudioNodeInput::disconnect(AudioNodeOutput& output) { ASSERT(deferredTaskHandler().isGraphOwner()); // First try to disconnect from "active" connections. if (m_outputs.contains(&output)) { m_outputs.remove(&output); changedOutputs(); output.removeInput(*this); // Note: it's important to return immediately after removeInput() calls // since the node may be deleted. return; } // Otherwise, try to disconnect from disabled connections. if (m_disabledOutputs.contains(&output)) { m_disabledOutputs.remove(&output); output.removeInput(*this); // Note: it's important to return immediately after all removeInput() calls // since the node may be deleted. return; } ASSERT_NOT_REACHED(); }
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()); }