| /* |
| * 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_output.h" |
| |
| #include <memory> |
| |
| #include "base/memory/ptr_util.h" |
| #include "third_party/blink/renderer/modules/webaudio/audio_node_input.h" |
| #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h" |
| #include "third_party/blink/renderer/platform/wtf/threading.h" |
| |
| namespace blink { |
| |
| inline AudioNodeOutput::AudioNodeOutput(AudioHandler* handler, |
| unsigned number_of_channels) |
| : handler_(*handler), |
| number_of_channels_(number_of_channels), |
| desired_number_of_channels_(number_of_channels), |
| is_in_place_(false), |
| is_enabled_(true), |
| did_call_dispose_(false), |
| rendering_fan_out_count_(0), |
| rendering_param_fan_out_count_(0) { |
| DCHECK_LE(number_of_channels, BaseAudioContext::MaxNumberOfChannels()); |
| |
| internal_bus_ = AudioBus::Create(number_of_channels, |
| audio_utilities::kRenderQuantumFrames); |
| } |
| |
| std::unique_ptr<AudioNodeOutput> AudioNodeOutput::Create( |
| AudioHandler* handler, |
| unsigned number_of_channels) { |
| return base::WrapUnique(new AudioNodeOutput(handler, number_of_channels)); |
| } |
| |
| void AudioNodeOutput::Dispose() { |
| did_call_dispose_ = true; |
| |
| GetDeferredTaskHandler().RemoveMarkedAudioNodeOutput(this); |
| DisconnectAll(); |
| DCHECK(inputs_.IsEmpty()); |
| DCHECK(params_.IsEmpty()); |
| } |
| |
| void AudioNodeOutput::SetNumberOfChannels(unsigned number_of_channels) { |
| DCHECK_LE(number_of_channels, BaseAudioContext::MaxNumberOfChannels()); |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| |
| desired_number_of_channels_ = number_of_channels; |
| |
| if (GetDeferredTaskHandler().IsAudioThread()) { |
| // If we're in the audio thread then we can take care of it right away (we |
| // should be at the very start or end of a rendering quantum). |
| UpdateNumberOfChannels(); |
| } else { |
| DCHECK(!did_call_dispose_); |
| // Let the context take care of it in the audio thread in the pre and post |
| // render tasks. |
| GetDeferredTaskHandler().MarkAudioNodeOutputDirty(this); |
| } |
| } |
| |
| void AudioNodeOutput::UpdateInternalBus() { |
| if (NumberOfChannels() == internal_bus_->NumberOfChannels()) |
| return; |
| |
| internal_bus_ = AudioBus::Create(NumberOfChannels(), |
| audio_utilities::kRenderQuantumFrames); |
| } |
| |
| void AudioNodeOutput::UpdateRenderingState() { |
| UpdateNumberOfChannels(); |
| rendering_fan_out_count_ = FanOutCount(); |
| rendering_param_fan_out_count_ = ParamFanOutCount(); |
| } |
| |
| void AudioNodeOutput::UpdateNumberOfChannels() { |
| DCHECK(GetDeferredTaskHandler().IsAudioThread()); |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| |
| if (number_of_channels_ != desired_number_of_channels_) { |
| number_of_channels_ = desired_number_of_channels_; |
| UpdateInternalBus(); |
| PropagateChannelCount(); |
| } |
| } |
| |
| void AudioNodeOutput::PropagateChannelCount() { |
| DCHECK(GetDeferredTaskHandler().IsAudioThread()); |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| |
| if (IsChannelCountKnown()) { |
| // Announce to any nodes we're connected to that we changed our channel |
| // count for its input. |
| for (AudioNodeInput* i : inputs_) |
| i->Handler().CheckNumberOfChannelsForInput(i); |
| } |
| } |
| |
| AudioBus* AudioNodeOutput::Pull(AudioBus* in_place_bus, |
| uint32_t frames_to_process) { |
| DCHECK(GetDeferredTaskHandler().IsAudioThread()); |
| DCHECK(rendering_fan_out_count_ > 0 || rendering_param_fan_out_count_ > 0); |
| |
| // Causes our AudioNode to process if it hasn't already for this render |
| // quantum. We try to do in-place processing (using inPlaceBus) if at all |
| // possible, but we can't process in-place if we're connected to more than one |
| // input (fan-out > 1). In this case pull() is called multiple times per |
| // rendering quantum, and the processIfNecessary() call below will cause our |
| // node to process() only the first time, caching the output in |
| // m_internalOutputBus for subsequent calls. |
| |
| is_in_place_ = |
| in_place_bus && in_place_bus->NumberOfChannels() == NumberOfChannels() && |
| (rendering_fan_out_count_ + rendering_param_fan_out_count_) == 1; |
| |
| in_place_bus_ = is_in_place_ ? in_place_bus : nullptr; |
| |
| Handler().ProcessIfNecessary(frames_to_process); |
| return Bus(); |
| } |
| |
| AudioBus* AudioNodeOutput::Bus() const { |
| DCHECK(GetDeferredTaskHandler().IsAudioThread()); |
| return is_in_place_ ? in_place_bus_.get() : internal_bus_.get(); |
| } |
| |
| unsigned AudioNodeOutput::FanOutCount() { |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| return inputs_.size(); |
| } |
| |
| unsigned AudioNodeOutput::ParamFanOutCount() { |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| return params_.size(); |
| } |
| |
| unsigned AudioNodeOutput::RenderingFanOutCount() const { |
| return rendering_fan_out_count_; |
| } |
| |
| void AudioNodeOutput::AddInput(AudioNodeInput& input) { |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| inputs_.insert(&input); |
| input.Handler().MakeConnection(); |
| } |
| |
| void AudioNodeOutput::RemoveInput(AudioNodeInput& input) { |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| input.Handler().BreakConnectionWithLock(); |
| inputs_.erase(&input); |
| } |
| |
| void AudioNodeOutput::DisconnectAllInputs() { |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| |
| // AudioNodeInput::disconnect() changes m_inputs by calling removeInput(). |
| while (!inputs_.IsEmpty()) |
| (*inputs_.begin())->Disconnect(*this); |
| } |
| |
| void AudioNodeOutput::DisconnectInput(AudioNodeInput& input) { |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| DCHECK(IsConnectedToInput(input)); |
| input.Disconnect(*this); |
| } |
| |
| void AudioNodeOutput::DisconnectAudioParam(AudioParamHandler& param) { |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| DCHECK(IsConnectedToAudioParam(param)); |
| param.Disconnect(*this); |
| } |
| |
| void AudioNodeOutput::AddParam(AudioParamHandler& param) { |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| params_.insert(¶m); |
| } |
| |
| void AudioNodeOutput::RemoveParam(AudioParamHandler& param) { |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| params_.erase(¶m); |
| } |
| |
| void AudioNodeOutput::DisconnectAllParams() { |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| |
| // AudioParam::disconnect() changes m_params by calling removeParam(). |
| while (!params_.IsEmpty()) |
| (*params_.begin())->Disconnect(*this); |
| } |
| |
| void AudioNodeOutput::DisconnectAll() { |
| DisconnectAllInputs(); |
| DisconnectAllParams(); |
| } |
| |
| bool AudioNodeOutput::IsConnectedToInput(AudioNodeInput& input) { |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| return inputs_.Contains(&input); |
| } |
| |
| bool AudioNodeOutput::IsConnectedToAudioParam(AudioParamHandler& param) { |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| return params_.Contains(¶m); |
| } |
| |
| void AudioNodeOutput::Disable() { |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| |
| if (is_enabled_) { |
| is_enabled_ = false; |
| for (AudioNodeInput* i : inputs_) |
| i->Disable(*this); |
| } |
| } |
| |
| void AudioNodeOutput::Enable() { |
| GetDeferredTaskHandler().AssertGraphOwner(); |
| |
| if (!is_enabled_) { |
| is_enabled_ = true; |
| for (AudioNodeInput* i : inputs_) |
| i->Enable(*this); |
| } |
| } |
| |
| } // namespace blink |