void AudioDestinationHandler::render(AudioBus* sourceBus, AudioBus* destinationBus, size_t numberOfFrames) { // We don't want denormals slowing down any of the audio processing // since they can very seriously hurt performance. // This will take care of all AudioNodes because they all process within this scope. DenormalDisabler denormalDisabler; // Need to check if the context actually alive. Otherwise the subsequent // steps will fail. If the context is not alive somehow, return immediately // and do nothing. // // TODO(hongchan): because the context can go away while rendering, so this // check cannot guarantee the safe execution of the following steps. ASSERT(context()); if (!context()) return; context()->deferredTaskHandler().setAudioThreadToCurrentThread(); // If the destination node is not initialized, pass the silence to the final // audio destination (one step before the FIFO). This check is for the case // where the destination is in the middle of tearing down process. if (!isInitialized()) { destinationBus->zero(); return; } // Let the context take care of any business at the start of each render quantum. context()->handlePreRenderTasks(); // Prepare the local audio input provider for this render quantum. if (sourceBus) m_localAudioInputProvider.set(sourceBus); ASSERT(numberOfInputs() >= 1); if (numberOfInputs() < 1) { destinationBus->zero(); return; } // This will cause the node(s) connected to us to process, which in turn will pull on their input(s), // all the way backwards through the rendering graph. AudioBus* renderedBus = input(0).pull(destinationBus, numberOfFrames); if (!renderedBus) { destinationBus->zero(); } else if (renderedBus != destinationBus) { // in-place processing was not possible - so copy destinationBus->copyFrom(*renderedBus); } // Process nodes which need a little extra help because they are not connected to anything, but still need to process. context()->deferredTaskHandler().processAutomaticPullNodes(numberOfFrames); // Let the context take care of any business at the end of each render quantum. context()->handlePostRenderTasks(); // Advance current sample-frame. size_t newSampleFrame = m_currentSampleFrame + numberOfFrames; releaseStore(&m_currentSampleFrame, newSampleFrame); }
bool OfflineAudioDestinationHandler::renderIfNotSuspended(AudioBus* sourceBus, AudioBus* destinationBus, size_t numberOfFrames) { // We don't want denormals slowing down any of the audio processing // since they can very seriously hurt performance. // This will take care of all AudioNodes because they all process within this scope. DenormalDisabler denormalDisabler; context()->deferredTaskHandler().setAudioThread(currentThread()); if (!context()->isDestinationInitialized()) { destinationBus->zero(); return false; } // Take care pre-render tasks at the beginning of each render quantum. Then // it will stop the rendering loop if the context needs to be suspended // at the beginning of the next render quantum. if (context()->handlePreOfflineRenderTasks()) { suspendOfflineRendering(); return true; } // Prepare the local audio input provider for this render quantum. if (sourceBus) m_localAudioInputProvider.set(sourceBus); ASSERT(numberOfInputs() >= 1); if (numberOfInputs() < 1) { destinationBus->zero(); return false; } // This will cause the node(s) connected to us to process, which in turn will pull on their input(s), // all the way backwards through the rendering graph. AudioBus* renderedBus = input(0).pull(destinationBus, numberOfFrames); if (!renderedBus) { destinationBus->zero(); } else if (renderedBus != destinationBus) { // in-place processing was not possible - so copy destinationBus->copyFrom(*renderedBus); } // Process nodes which need a little extra help because they are not connected to anything, but still need to process. context()->deferredTaskHandler().processAutomaticPullNodes(numberOfFrames); // Let the context take care of any business at the end of each render quantum. context()->handlePostOfflineRenderTasks(); // Advance current sample-frame. size_t newSampleFrame = m_currentSampleFrame + numberOfFrames; releaseStore(&m_currentSampleFrame, newSampleFrame); return false; }
void ChannelMergerHandler::process(size_t framesToProcess) { AudioNodeOutput& output = this->output(0); DCHECK_EQ(framesToProcess, output.bus()->length()); unsigned numberOfOutputChannels = output.numberOfChannels(); DCHECK_EQ(numberOfInputs(), numberOfOutputChannels); // Merge multiple inputs into one output. for (unsigned i = 0; i < numberOfOutputChannels; ++i) { AudioNodeInput& input = this->input(i); DCHECK_EQ(input.numberOfChannels(), 1u); AudioChannel* outputChannel = output.bus()->channel(i); if (input.isConnected()) { // The mixing rules will be applied so multiple channels are down- // mixed to mono (when the mixing rule is defined). Note that only // the first channel will be taken for the undefined input channel // layout. // // See: // http://webaudio.github.io/web-audio-api/#channel-up-mixing-and-down-mixing AudioChannel* inputChannel = input.bus()->channel(0); outputChannel->copyFrom(inputChannel); } else { // If input is unconnected, fill zeros in the channel. outputChannel->zero(); } } }
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()); }
// 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); }
// 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(ContextRenderLock& r, AudioNodeInput* input) { // Count how many channels we have all together from all of the inputs. uint32_t numberOfOutputChannels = 0; for (uint32_t i = 0; i < numberOfInputs(); ++i) { auto input = this->input(i); if (input->isConnected()) { numberOfOutputChannels += input->bus(r)->numberOfChannels(); } } // Set the correct number of channels on the output auto output = this->output(0); output->setNumberOfChannels(r, numberOfOutputChannels); // Note * 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(r, 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 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 ChannelMergerNode::process(ContextRenderLock& r, size_t framesToProcess) { auto output = this->output(0); ASSERT_UNUSED(framesToProcess, framesToProcess == output->bus(r)->length()); // Output bus not updated yet, so just output silence. See Note * in checkNumberOfChannelsForInput if (m_desiredNumberOfOutputChannels != output->numberOfChannels()) { output->bus(r)->zero(); return; } // Merge all the channels from all the inputs into one output. uint32_t outputChannelIndex = 0; for (uint32_t i = 0; i < numberOfInputs(); ++i) { auto input = this->input(i); if (input->isConnected()) { uint32_t numberOfInputChannels = input->bus(r)->numberOfChannels(); // Merge channels from this particular input. for (uint32_t j = 0; j < numberOfInputChannels; ++j) { AudioChannel* inputChannel = input->bus(r)->channel(j); AudioChannel* outputChannel = output->bus(r)->channel(outputChannelIndex); outputChannel->copyFrom(inputChannel); ++outputChannelIndex; } } } ASSERT(outputChannelIndex == output->numberOfChannels()); }