blob: ce42fc0cea9f567cf204ac83a62b3518cbaf2da0 [file] [log] [blame]
/*
* Copyright (C) 2011, 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/default_audio_destination_node.h"
#include "third_party/blink/renderer/modules/webaudio/audio_node_input.h"
#include "third_party/blink/renderer/modules/webaudio/audio_node_output.h"
#include "third_party/blink/renderer/modules/webaudio/audio_worklet.h"
#include "third_party/blink/renderer/modules/webaudio/audio_worklet_messaging_proxy.h"
#include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
#include "third_party/blink/renderer/platform/audio/denormal_disabler.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
namespace blink {
scoped_refptr<DefaultAudioDestinationHandler>
DefaultAudioDestinationHandler::Create(
AudioNode& node,
const WebAudioLatencyHint& latency_hint) {
return base::AdoptRef(new DefaultAudioDestinationHandler(node, latency_hint));
}
DefaultAudioDestinationHandler::DefaultAudioDestinationHandler(
AudioNode& node,
const WebAudioLatencyHint& latency_hint)
: AudioDestinationHandler(node),
latency_hint_(latency_hint) {
// Node-specific default channel count and mixing rules.
channel_count_ = 2;
SetInternalChannelCountMode(kExplicit);
SetInternalChannelInterpretation(AudioBus::kSpeakers);
}
DefaultAudioDestinationHandler::~DefaultAudioDestinationHandler() {
DCHECK(!IsInitialized());
}
void DefaultAudioDestinationHandler::Dispose() {
Uninitialize();
AudioDestinationHandler::Dispose();
}
void DefaultAudioDestinationHandler::Initialize() {
DCHECK(IsMainThread());
CreatePlatformDestination();
AudioHandler::Initialize();
}
void DefaultAudioDestinationHandler::Uninitialize() {
DCHECK(IsMainThread());
// It is possible that the handler is already uninitialized.
if (!IsInitialized()) {
return;
}
StopPlatformDestination();
AudioHandler::Uninitialize();
}
void DefaultAudioDestinationHandler::SetChannelCount(
unsigned channel_count,
ExceptionState& exception_state) {
DCHECK(IsMainThread());
// The channelCount for the input to this node controls the actual number of
// channels we send to the audio hardware. It can only be set if the number
// is less than the number of hardware channels.
if (channel_count > MaxChannelCount()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kIndexSizeError,
ExceptionMessages::IndexOutsideRange<unsigned>(
"channel count", channel_count, 1,
ExceptionMessages::kInclusiveBound, MaxChannelCount(),
ExceptionMessages::kInclusiveBound));
return;
}
unsigned long old_channel_count = this->ChannelCount();
AudioHandler::SetChannelCount(channel_count, exception_state);
// Stop, re-create and start the destination to apply the new channel count.
if (this->ChannelCount() != old_channel_count &&
!exception_state.HadException()) {
StopPlatformDestination();
CreatePlatformDestination();
StartPlatformDestination();
}
}
void DefaultAudioDestinationHandler::StartRendering() {
DCHECK(IsMainThread());
StartPlatformDestination();
}
void DefaultAudioDestinationHandler::StopRendering() {
DCHECK(IsMainThread());
StopPlatformDestination();
}
void DefaultAudioDestinationHandler::Pause() {
DCHECK(IsMainThread());
if (platform_destination_) {
platform_destination_->Pause();
}
}
void DefaultAudioDestinationHandler::Resume() {
DCHECK(IsMainThread());
if (platform_destination_) {
platform_destination_->Resume();
}
}
void DefaultAudioDestinationHandler::RestartRendering() {
DCHECK(IsMainThread());
StopRendering();
StartRendering();
}
uint32_t DefaultAudioDestinationHandler::MaxChannelCount() const {
return AudioDestination::MaxChannelCount();
}
double DefaultAudioDestinationHandler::SampleRate() const {
// This can be accessed from both threads (main and audio), so it is
// possible that |platform_destination_| is not fully functional when it
// is accssed by the audio thread.
return platform_destination_ ? platform_destination_->SampleRate() : 0;
}
void DefaultAudioDestinationHandler::Render(
AudioBus* destination_bus,
uint32_t number_of_frames,
const AudioIOPosition& output_position,
const AudioIOCallbackMetric& metric) {
TRACE_EVENT0("webaudio", "DefaultAudioDestinationHandler::Render");
// Denormals can seriously hurt performance of audio processing. This will
// take care of all AudioNode processes within this scope.
DenormalDisabler denormal_disabler;
// A sanity check for the associated context, but this does not guarantee the
// safe execution of the subsequence operations because the hanlder holds
// the context as |UntracedMember| and it can go away anytime.
DCHECK(Context());
if (!Context()) {
return;
}
Context()->GetDeferredTaskHandler().SetAudioThreadToCurrentThread();
// If this node is not initialized yet, pass silence to the platform audio
// destination. It is for the case where this node is in the middle of
// tear-down process.
if (!IsInitialized()) {
destination_bus->Zero();
return;
}
Context()->HandlePreRenderTasks(output_position, metric);
// Renders the graph by pulling all the input(s) to this node. This will in
// turn pull on their input(s), all the way backwards through the graph.
AudioBus* rendered_bus = Input(0).Pull(destination_bus, number_of_frames);
DCHECK(rendered_bus);
if (!rendered_bus) {
// AudioNodeInput might be in the middle of destruction. Then the internal
// summing bus will return as nullptr. Then zero out the output.
destination_bus->Zero();
} else if (rendered_bus != destination_bus) {
// In-place processing was not possible. Copy the rendererd result to the
// given |destination_bus| buffer.
destination_bus->CopyFrom(*rendered_bus);
}
// Processes "automatic" nodes that are not connected to anything. This can
// be done after copying because it does not affect the rendered result.
Context()->GetDeferredTaskHandler().ProcessAutomaticPullNodes(
number_of_frames);
Context()->HandlePostRenderTasks(destination_bus);
// Advances the current sample-frame.
AdvanceCurrentSampleFrame(number_of_frames);
Context()->UpdateWorkletGlobalScopeOnRenderingThread();
}
uint32_t DefaultAudioDestinationHandler::GetCallbackBufferSize() const {
DCHECK(IsMainThread());
DCHECK(IsInitialized());
return platform_destination_->CallbackBufferSize();
}
int DefaultAudioDestinationHandler::GetFramesPerBuffer() const {
DCHECK(IsMainThread());
DCHECK(IsInitialized());
return platform_destination_ ? platform_destination_->FramesPerBuffer() : 0;
}
void DefaultAudioDestinationHandler::CreatePlatformDestination() {
platform_destination_ =
AudioDestination::Create(*this, ChannelCount(), latency_hint_);
}
void DefaultAudioDestinationHandler::StartPlatformDestination() {
if (platform_destination_->IsPlaying()) {
return;
}
AudioWorklet* audio_worklet = Context()->audioWorklet();
if (audio_worklet && audio_worklet->IsReady()) {
// This task runner is only used to fire the audio render callback, so it
// MUST not be throttled to avoid potential audio glitch.
platform_destination_->StartWithWorkletTaskRunner(
audio_worklet->GetMessagingProxy()
->GetBackingWorkerThread()
->GetTaskRunner(TaskType::kInternalMedia));
} else {
platform_destination_->Start();
}
}
void DefaultAudioDestinationHandler::StopPlatformDestination() {
if (platform_destination_->IsPlaying()) {
platform_destination_->Stop();
}
}
// -----------------------------------------------------------------------------
DefaultAudioDestinationNode::DefaultAudioDestinationNode(
BaseAudioContext& context,
const WebAudioLatencyHint& latency_hint)
: AudioDestinationNode(context) {
SetHandler(DefaultAudioDestinationHandler::Create(*this, latency_hint));
}
DefaultAudioDestinationNode* DefaultAudioDestinationNode::Create(
BaseAudioContext* context,
const WebAudioLatencyHint& latency_hint) {
return MakeGarbageCollected<DefaultAudioDestinationNode>(*context,
latency_hint);
}
} // namespace blink