DefaultAudioDestinationNode.cpp [plain text]
#include "config.h"
#if ENABLE(WEB_AUDIO)
#include "DefaultAudioDestinationNode.h"
#include "AudioContext.h"
#include "AudioDestination.h"
#include "AudioWorklet.h"
#include "AudioWorkletMessagingProxy.h"
#include "Logging.h"
#include "MediaStrategy.h"
#include "PlatformStrategies.h"
#include "ScriptExecutionContext.h"
#include "WorkerRunLoop.h"
#include <wtf/IsoMallocInlines.h>
#include <wtf/MainThread.h>
constexpr unsigned EnabledInputChannels = 2;
namespace WebCore {
WTF_MAKE_ISO_ALLOCATED_IMPL(DefaultAudioDestinationNode);
DefaultAudioDestinationNode::DefaultAudioDestinationNode(BaseAudioContext& context, Optional<float> sampleRate)
: AudioDestinationNode(context, sampleRate.valueOr(AudioDestination::hardwareSampleRate()))
{
initializeDefaultNodeOptions(2, ChannelCountMode::Explicit, ChannelInterpretation::Speakers);
}
DefaultAudioDestinationNode::~DefaultAudioDestinationNode()
{
uninitialize();
}
void DefaultAudioDestinationNode::initialize()
{
ASSERT(isMainThread());
if (isInitialized())
return;
ALWAYS_LOG(LOGIDENTIFIER);
createDestination();
AudioNode::initialize();
}
void DefaultAudioDestinationNode::uninitialize()
{
ASSERT(isMainThread());
if (!isInitialized())
return;
ALWAYS_LOG(LOGIDENTIFIER);
clearDestination();
m_numberOfInputChannels = 0;
AudioNode::uninitialize();
}
void DefaultAudioDestinationNode::clearDestination()
{
ASSERT(m_destination);
if (m_wasDestinationStarted) {
m_destination->stop();
m_wasDestinationStarted = false;
}
m_destination->clearCallback();
m_destination = nullptr;
}
void DefaultAudioDestinationNode::createDestination()
{
ALWAYS_LOG(LOGIDENTIFIER, "contextSampleRate = ", m_sampleRate, ", hardwareSampleRate = ", AudioDestination::hardwareSampleRate());
ASSERT(!m_destination);
m_destination = platformStrategies()->mediaStrategy().createAudioDestination(*this, m_inputDeviceId, m_numberOfInputChannels, channelCount(), m_sampleRate);
}
void DefaultAudioDestinationNode::recreateDestination()
{
bool wasDestinationStarted = m_wasDestinationStarted;
clearDestination();
createDestination();
if (wasDestinationStarted) {
m_wasDestinationStarted = true;
m_destination->start(dispatchToRenderThreadFunction());
}
}
void DefaultAudioDestinationNode::enableInput(const String& inputDeviceId)
{
ALWAYS_LOG(LOGIDENTIFIER);
ASSERT(isMainThread());
if (m_numberOfInputChannels != EnabledInputChannels) {
m_numberOfInputChannels = EnabledInputChannels;
m_inputDeviceId = inputDeviceId;
if (isInitialized())
recreateDestination();
}
}
Function<void(Function<void()>&&)> DefaultAudioDestinationNode::dispatchToRenderThreadFunction()
{
if (auto* workletProxy = context().audioWorklet().proxy()) {
return [workletProxy = makeRef(*workletProxy)](Function<void()>&& function) {
workletProxy->postTaskForModeToWorkletGlobalScope([function = WTFMove(function)](ScriptExecutionContext&) mutable {
function();
}, WorkerRunLoop::defaultMode());
};
}
return [](Function<void()>&& function) { function(); };
}
void DefaultAudioDestinationNode::startRendering(CompletionHandler<void(Optional<Exception>&&)>&& completionHandler)
{
ASSERT(isInitialized());
if (!isInitialized())
return completionHandler(Exception { InvalidStateError, "AudioDestinationNode is not initialized"_s });
auto innerCompletionHandler = [completionHandler = WTFMove(completionHandler)](bool success) mutable {
completionHandler(success ? WTF::nullopt : makeOptional(Exception { InvalidStateError, "Failed to start the audio device"_s }));
};
m_wasDestinationStarted = true;
m_destination->start(dispatchToRenderThreadFunction(), WTFMove(innerCompletionHandler));
}
void DefaultAudioDestinationNode::resume(CompletionHandler<void(Optional<Exception>&&)>&& completionHandler)
{
ASSERT(isInitialized());
if (!isInitialized()) {
context().postTask([completionHandler = WTFMove(completionHandler)]() mutable {
completionHandler(Exception { InvalidStateError, "AudioDestinationNode is not initialized"_s });
});
return;
}
m_wasDestinationStarted = true;
m_destination->start(dispatchToRenderThreadFunction(), [completionHandler = WTFMove(completionHandler)](bool success) mutable {
completionHandler(success ? WTF::nullopt : makeOptional(Exception { InvalidStateError, "Failed to start the audio device"_s }));
});
}
void DefaultAudioDestinationNode::suspend(CompletionHandler<void(Optional<Exception>&&)>&& completionHandler)
{
ASSERT(isInitialized());
if (!isInitialized()) {
context().postTask([completionHandler = WTFMove(completionHandler)]() mutable {
completionHandler(Exception { InvalidStateError, "AudioDestinationNode is not initialized"_s });
});
return;
}
m_wasDestinationStarted = false;
m_destination->stop([completionHandler = WTFMove(completionHandler)](bool success) mutable {
completionHandler(success ? WTF::nullopt : makeOptional(Exception { InvalidStateError, "Failed to stop the audio device"_s }));
});
}
void DefaultAudioDestinationNode::restartRendering()
{
if (!m_wasDestinationStarted)
return;
m_destination->stop();
m_destination->start(dispatchToRenderThreadFunction());
}
void DefaultAudioDestinationNode::close(CompletionHandler<void()>&& completionHandler)
{
ASSERT(isInitialized());
uninitialize();
context().postTask(WTFMove(completionHandler));
}
unsigned DefaultAudioDestinationNode::maxChannelCount() const
{
return AudioDestination::maxChannelCount();
}
ExceptionOr<void> DefaultAudioDestinationNode::setChannelCount(unsigned channelCount)
{
ASSERT(isMainThread());
ALWAYS_LOG(LOGIDENTIFIER, channelCount);
if (channelCount > maxChannelCount())
return Exception { IndexSizeError, "Channel count exceeds maximum limit"_s };
auto oldChannelCount = this->channelCount();
auto result = AudioNode::setChannelCount(channelCount);
if (result.hasException())
return result;
if (this->channelCount() != oldChannelCount && isInitialized())
recreateDestination();
return { };
}
bool DefaultAudioDestinationNode::isPlaying()
{
return m_destination && m_destination->isPlaying();
}
unsigned DefaultAudioDestinationNode::framesPerBuffer() const
{
return m_destination ? m_destination->framesPerBuffer() : 0.;
}
}
#endif // ENABLE(WEB_AUDIO)