blob: 390f6607a05539ff2cd0d95acc2e791d3a75d948 [file] [log] [blame]
/*
* Copyright (C) 2010, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include "third_party/blink/renderer/modules/webaudio/audio_node_input.h"
#include <algorithm>
#include <memory>
#include "base/memory/ptr_util.h"
#include "third_party/blink/renderer/modules/webaudio/audio_node_output.h"
namespace blink {
AudioNodeInput::AudioNodeInput(AudioHandler& handler)
: AudioSummingJunction(handler.Context()->GetDeferredTaskHandler()),
handler_(handler) {
// Set to mono by default.
internal_summing_bus_ =
AudioBus::Create(1, audio_utilities::kRenderQuantumFrames);
}
AudioNodeInput::~AudioNodeInput() {
GetDeferredTaskHandler().AssertGraphOwner();
for (AudioNodeOutput* output : outputs_)
output->RemoveInput(*this);
for (AudioNodeOutput* output : disabled_outputs_)
output->RemoveInput(*this);
outputs_.clear();
disabled_outputs_.clear();
ChangedOutputs();
}
std::unique_ptr<AudioNodeInput> AudioNodeInput::Create(AudioHandler& handler) {
return base::WrapUnique(new AudioNodeInput(handler));
}
void AudioNodeInput::Connect(AudioNodeOutput& output) {
GetDeferredTaskHandler().AssertGraphOwner();
// Check if we're already connected to this output.
if (outputs_.Contains(&output))
return;
output.AddInput(*this);
outputs_.insert(&output);
ChangedOutputs();
}
void AudioNodeInput::Disconnect(AudioNodeOutput& output) {
GetDeferredTaskHandler().AssertGraphOwner();
// First try to disconnect from "active" connections.
if (outputs_.Contains(&output)) {
outputs_.erase(&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 (disabled_outputs_.Contains(&output)) {
disabled_outputs_.erase(&output);
output.RemoveInput(*this);
// Note: it's important to return immediately after all removeInput() calls
// since the node may be deleted.
return;
}
NOTREACHED();
}
void AudioNodeInput::Disable(AudioNodeOutput& output) {
GetDeferredTaskHandler().AssertGraphOwner();
DCHECK(outputs_.Contains(&output));
disabled_outputs_.insert(&output);
outputs_.erase(&output);
ChangedOutputs();
// Propagate disabled state to outputs.
Handler().DisableOutputsIfNecessary();
}
void AudioNodeInput::Enable(AudioNodeOutput& output) {
GetDeferredTaskHandler().AssertGraphOwner();
// Move output from disabled list to active list.
outputs_.insert(&output);
if (disabled_outputs_.size() > 0) {
DCHECK(disabled_outputs_.Contains(&output));
disabled_outputs_.erase(&output);
}
ChangedOutputs();
// Propagate enabled state to outputs.
Handler().EnableOutputsIfNecessary();
}
void AudioNodeInput::DidUpdate() {
Handler().CheckNumberOfChannelsForInput(this);
}
void AudioNodeInput::UpdateInternalBus() {
DCHECK(GetDeferredTaskHandler().IsAudioThread());
GetDeferredTaskHandler().AssertGraphOwner();
unsigned number_of_input_channels = NumberOfChannels();
if (number_of_input_channels == internal_summing_bus_->NumberOfChannels())
return;
internal_summing_bus_ = AudioBus::Create(
number_of_input_channels, audio_utilities::kRenderQuantumFrames);
}
unsigned AudioNodeInput::NumberOfChannels() const {
AudioHandler::ChannelCountMode mode = Handler().InternalChannelCountMode();
if (mode == AudioHandler::kExplicit)
return Handler().ChannelCount();
// Find the number of channels of the connection with the largest number of
// channels.
unsigned max_channels = 1; // one channel is the minimum allowed
for (AudioNodeOutput* output : outputs_) {
// Use output()->numberOfChannels() instead of
// output->bus()->numberOfChannels(), because the calling of
// AudioNodeOutput::bus() is not safe here.
max_channels = std::max(max_channels, output->NumberOfChannels());
}
if (mode == AudioHandler::kClampedMax)
max_channels =
std::min(max_channels, static_cast<unsigned>(Handler().ChannelCount()));
return max_channels;
}
AudioBus* AudioNodeInput::Bus() {
DCHECK(GetDeferredTaskHandler().IsAudioThread());
// Handle single connection specially to allow for in-place processing.
if (NumberOfRenderingConnections() == 1 &&
Handler().InternalChannelCountMode() == AudioHandler::kMax)
return RenderingOutput(0)->Bus();
// Multiple connections case or complex ChannelCountMode (or no connections).
return InternalSummingBus();
}
AudioBus* AudioNodeInput::InternalSummingBus() {
DCHECK(GetDeferredTaskHandler().IsAudioThread());
return internal_summing_bus_.get();
}
void AudioNodeInput::SumAllConnections(AudioBus* summing_bus,
uint32_t frames_to_process) {
DCHECK(GetDeferredTaskHandler().IsAudioThread());
// We shouldn't be calling this method if there's only one connection, since
// it's less efficient.
// DCHECK(numberOfRenderingConnections() > 1 ||
// handler().internalChannelCountMode() != AudioHandler::Max);
DCHECK(summing_bus);
if (!summing_bus)
return;
summing_bus->Zero();
AudioBus::ChannelInterpretation interpretation =
Handler().InternalChannelInterpretation();
for (unsigned i = 0; i < NumberOfRenderingConnections(); ++i) {
AudioNodeOutput* output = RenderingOutput(i);
DCHECK(output);
// Render audio from this output.
AudioBus* connection_bus = output->Pull(nullptr, frames_to_process);
// Sum, with unity-gain.
summing_bus->SumFrom(*connection_bus, interpretation);
}
}
AudioBus* AudioNodeInput::Pull(AudioBus* in_place_bus,
uint32_t frames_to_process) {
DCHECK(GetDeferredTaskHandler().IsAudioThread());
// Handle single connection case.
if (NumberOfRenderingConnections() == 1 &&
Handler().InternalChannelCountMode() == AudioHandler::kMax) {
// The output will optimize processing using inPlaceBus if it's able.
AudioNodeOutput* output = this->RenderingOutput(0);
return output->Pull(in_place_bus, frames_to_process);
}
AudioBus* internal_summing_bus = 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.
internal_summing_bus->Zero();
return internal_summing_bus;
}
// Handle multiple connections case.
SumAllConnections(internal_summing_bus, frames_to_process);
return internal_summing_bus;
}
} // namespace blink