mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-09 09:18:42 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
783
dom/media/webaudio/AudioNodeStream.cpp
Normal file
783
dom/media/webaudio/AudioNodeStream.cpp
Normal file
|
|
@ -0,0 +1,783 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AudioNodeStream.h"
|
||||
|
||||
#include "MediaStreamGraphImpl.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "AudioNodeEngine.h"
|
||||
#include "ThreeDPoint.h"
|
||||
#include "AudioChannelFormat.h"
|
||||
#include "AudioParamTimeline.h"
|
||||
#include "AudioContext.h"
|
||||
#include "nsMathUtils.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* An AudioNodeStream produces a single audio track with ID
|
||||
* AUDIO_TRACK. This track has rate AudioContext::sIdealAudioRate
|
||||
* for regular audio contexts, and the rate requested by the web content
|
||||
* for offline audio contexts.
|
||||
* Each chunk in the track is a single block of WEBAUDIO_BLOCK_SIZE samples.
|
||||
* Note: This must be a different value than MEDIA_STREAM_DEST_TRACK_ID
|
||||
*/
|
||||
|
||||
AudioNodeStream::AudioNodeStream(AudioNodeEngine* aEngine,
|
||||
Flags aFlags,
|
||||
TrackRate aSampleRate)
|
||||
: ProcessedMediaStream(),
|
||||
mEngine(aEngine),
|
||||
mSampleRate(aSampleRate),
|
||||
mFlags(aFlags),
|
||||
mNumberOfInputChannels(2),
|
||||
mIsActive(aEngine->IsActive()),
|
||||
mMarkAsFinishedAfterThisBlock(false),
|
||||
mAudioParamStream(false),
|
||||
mPassThrough(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mSuspendedCount = !(mIsActive || mFlags & EXTERNAL_OUTPUT);
|
||||
mChannelCountMode = ChannelCountMode::Max;
|
||||
mChannelInterpretation = ChannelInterpretation::Speakers;
|
||||
// AudioNodes are always producing data
|
||||
mHasCurrentData = true;
|
||||
mLastChunks.SetLength(std::max(uint16_t(1), mEngine->OutputCount()));
|
||||
MOZ_COUNT_CTOR(AudioNodeStream);
|
||||
}
|
||||
|
||||
AudioNodeStream::~AudioNodeStream()
|
||||
{
|
||||
MOZ_ASSERT(mActiveInputCount == 0);
|
||||
MOZ_COUNT_DTOR(AudioNodeStream);
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::DestroyImpl()
|
||||
{
|
||||
// These are graph thread objects, so clean up on graph thread.
|
||||
mInputChunks.Clear();
|
||||
mLastChunks.Clear();
|
||||
|
||||
ProcessedMediaStream::DestroyImpl();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<AudioNodeStream>
|
||||
AudioNodeStream::Create(AudioContext* aCtx, AudioNodeEngine* aEngine,
|
||||
Flags aFlags, MediaStreamGraph* aGraph)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_RELEASE_ASSERT(aGraph);
|
||||
|
||||
// MediaRecorders use an AudioNodeStream, but no AudioNode
|
||||
AudioNode* node = aEngine->NodeMainThread();
|
||||
|
||||
RefPtr<AudioNodeStream> stream =
|
||||
new AudioNodeStream(aEngine, aFlags, aGraph->GraphRate());
|
||||
stream->mSuspendedCount += aCtx->ShouldSuspendNewStream();
|
||||
if (node) {
|
||||
stream->SetChannelMixingParametersImpl(node->ChannelCount(),
|
||||
node->ChannelCountModeValue(),
|
||||
node->ChannelInterpretationValue());
|
||||
}
|
||||
aGraph->AddStream(stream);
|
||||
return stream.forget();
|
||||
}
|
||||
|
||||
size_t
|
||||
AudioNodeStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
size_t amount = 0;
|
||||
|
||||
// Not reported:
|
||||
// - mEngine
|
||||
|
||||
amount += ProcessedMediaStream::SizeOfExcludingThis(aMallocSizeOf);
|
||||
amount += mLastChunks.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
for (size_t i = 0; i < mLastChunks.Length(); i++) {
|
||||
// NB: This is currently unshared only as there are instances of
|
||||
// double reporting in DMD otherwise.
|
||||
amount += mLastChunks[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
size_t
|
||||
AudioNodeStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SizeOfAudioNodesIncludingThis(MallocSizeOf aMallocSizeOf,
|
||||
AudioNodeSizes& aUsage) const
|
||||
{
|
||||
// Explicitly separate out the stream memory.
|
||||
aUsage.mStream = SizeOfIncludingThis(aMallocSizeOf);
|
||||
|
||||
if (mEngine) {
|
||||
// This will fill out the rest of |aUsage|.
|
||||
mEngine->SizeOfIncludingThis(aMallocSizeOf, aUsage);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SetStreamTimeParameter(uint32_t aIndex, AudioContext* aContext,
|
||||
double aStreamTime)
|
||||
{
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream, uint32_t aIndex, MediaStream* aRelativeToStream,
|
||||
double aStreamTime)
|
||||
: ControlMessage(aStream), mStreamTime(aStreamTime),
|
||||
mRelativeToStream(aRelativeToStream), mIndex(aIndex)
|
||||
{}
|
||||
void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->
|
||||
SetStreamTimeParameterImpl(mIndex, mRelativeToStream, mStreamTime);
|
||||
}
|
||||
double mStreamTime;
|
||||
MediaStream* mRelativeToStream;
|
||||
uint32_t mIndex;
|
||||
};
|
||||
|
||||
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex,
|
||||
aContext->DestinationStream(),
|
||||
aStreamTime));
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelativeToStream,
|
||||
double aStreamTime)
|
||||
{
|
||||
StreamTime ticks = aRelativeToStream->SecondsToNearestStreamTime(aStreamTime);
|
||||
mEngine->SetStreamTimeParameter(aIndex, ticks);
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SetDoubleParameter(uint32_t aIndex, double aValue)
|
||||
{
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream, uint32_t aIndex, double aValue)
|
||||
: ControlMessage(aStream), mValue(aValue), mIndex(aIndex)
|
||||
{}
|
||||
void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->Engine()->
|
||||
SetDoubleParameter(mIndex, mValue);
|
||||
}
|
||||
double mValue;
|
||||
uint32_t mIndex;
|
||||
};
|
||||
|
||||
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex, aValue));
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SetInt32Parameter(uint32_t aIndex, int32_t aValue)
|
||||
{
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream, uint32_t aIndex, int32_t aValue)
|
||||
: ControlMessage(aStream), mValue(aValue), mIndex(aIndex)
|
||||
{}
|
||||
void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->Engine()->
|
||||
SetInt32Parameter(mIndex, mValue);
|
||||
}
|
||||
int32_t mValue;
|
||||
uint32_t mIndex;
|
||||
};
|
||||
|
||||
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex, aValue));
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SendTimelineEvent(uint32_t aIndex,
|
||||
const AudioTimelineEvent& aEvent)
|
||||
{
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream, uint32_t aIndex,
|
||||
const AudioTimelineEvent& aEvent)
|
||||
: ControlMessage(aStream),
|
||||
mEvent(aEvent),
|
||||
mSampleRate(aStream->SampleRate()),
|
||||
mIndex(aIndex)
|
||||
{}
|
||||
void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->Engine()->
|
||||
RecvTimelineEvent(mIndex, mEvent);
|
||||
}
|
||||
AudioTimelineEvent mEvent;
|
||||
TrackRate mSampleRate;
|
||||
uint32_t mIndex;
|
||||
};
|
||||
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex, aEvent));
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SetThreeDPointParameter(uint32_t aIndex, const ThreeDPoint& aValue)
|
||||
{
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream, uint32_t aIndex, const ThreeDPoint& aValue)
|
||||
: ControlMessage(aStream), mValue(aValue), mIndex(aIndex)
|
||||
{}
|
||||
void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->Engine()->
|
||||
SetThreeDPointParameter(mIndex, mValue);
|
||||
}
|
||||
ThreeDPoint mValue;
|
||||
uint32_t mIndex;
|
||||
};
|
||||
|
||||
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex, aValue));
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList>&& aBuffer)
|
||||
{
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream,
|
||||
already_AddRefed<ThreadSharedFloatArrayBufferList>& aBuffer)
|
||||
: ControlMessage(aStream), mBuffer(aBuffer)
|
||||
{}
|
||||
void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->Engine()->
|
||||
SetBuffer(mBuffer.forget());
|
||||
}
|
||||
RefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
|
||||
};
|
||||
|
||||
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aBuffer));
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SetRawArrayData(nsTArray<float>& aData)
|
||||
{
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream,
|
||||
nsTArray<float>& aData)
|
||||
: ControlMessage(aStream)
|
||||
{
|
||||
mData.SwapElements(aData);
|
||||
}
|
||||
void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->Engine()->SetRawArrayData(mData);
|
||||
}
|
||||
nsTArray<float> mData;
|
||||
};
|
||||
|
||||
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aData));
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SetChannelMixingParameters(uint32_t aNumberOfChannels,
|
||||
ChannelCountMode aChannelCountMode,
|
||||
ChannelInterpretation aChannelInterpretation)
|
||||
{
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream,
|
||||
uint32_t aNumberOfChannels,
|
||||
ChannelCountMode aChannelCountMode,
|
||||
ChannelInterpretation aChannelInterpretation)
|
||||
: ControlMessage(aStream),
|
||||
mNumberOfChannels(aNumberOfChannels),
|
||||
mChannelCountMode(aChannelCountMode),
|
||||
mChannelInterpretation(aChannelInterpretation)
|
||||
{}
|
||||
void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->
|
||||
SetChannelMixingParametersImpl(mNumberOfChannels, mChannelCountMode,
|
||||
mChannelInterpretation);
|
||||
}
|
||||
uint32_t mNumberOfChannels;
|
||||
ChannelCountMode mChannelCountMode;
|
||||
ChannelInterpretation mChannelInterpretation;
|
||||
};
|
||||
|
||||
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aNumberOfChannels,
|
||||
aChannelCountMode,
|
||||
aChannelInterpretation));
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SetPassThrough(bool aPassThrough)
|
||||
{
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream, bool aPassThrough)
|
||||
: ControlMessage(aStream), mPassThrough(aPassThrough)
|
||||
{}
|
||||
void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->mPassThrough = mPassThrough;
|
||||
}
|
||||
bool mPassThrough;
|
||||
};
|
||||
|
||||
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aPassThrough));
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SetChannelMixingParametersImpl(uint32_t aNumberOfChannels,
|
||||
ChannelCountMode aChannelCountMode,
|
||||
ChannelInterpretation aChannelInterpretation)
|
||||
{
|
||||
// Make sure that we're not clobbering any significant bits by fitting these
|
||||
// values in 16 bits.
|
||||
MOZ_ASSERT(int(aChannelCountMode) < INT16_MAX);
|
||||
MOZ_ASSERT(int(aChannelInterpretation) < INT16_MAX);
|
||||
|
||||
mNumberOfInputChannels = aNumberOfChannels;
|
||||
mChannelCountMode = aChannelCountMode;
|
||||
mChannelInterpretation = aChannelInterpretation;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
AudioNodeStream::ComputedNumberOfChannels(uint32_t aInputChannelCount)
|
||||
{
|
||||
switch (mChannelCountMode) {
|
||||
case ChannelCountMode::Explicit:
|
||||
// Disregard the channel count we've calculated from inputs, and just use
|
||||
// mNumberOfInputChannels.
|
||||
return mNumberOfInputChannels;
|
||||
case ChannelCountMode::Clamped_max:
|
||||
// Clamp the computed output channel count to mNumberOfInputChannels.
|
||||
return std::min(aInputChannelCount, mNumberOfInputChannels);
|
||||
default:
|
||||
case ChannelCountMode::Max:
|
||||
// Nothing to do here, just shut up the compiler warning.
|
||||
return aInputChannelCount;
|
||||
}
|
||||
}
|
||||
|
||||
class AudioNodeStream::AdvanceAndResumeMessage final : public ControlMessage {
|
||||
public:
|
||||
AdvanceAndResumeMessage(AudioNodeStream* aStream, StreamTime aAdvance) :
|
||||
ControlMessage(aStream), mAdvance(aAdvance) {}
|
||||
void Run() override
|
||||
{
|
||||
auto ns = static_cast<AudioNodeStream*>(mStream);
|
||||
ns->mTracksStartTime -= mAdvance;
|
||||
|
||||
StreamTracks::Track* track = ns->EnsureTrack(AUDIO_TRACK);
|
||||
track->Get<AudioSegment>()->AppendNullData(mAdvance);
|
||||
|
||||
ns->GraphImpl()->DecrementSuspendCount(mStream);
|
||||
}
|
||||
private:
|
||||
StreamTime mAdvance;
|
||||
};
|
||||
|
||||
void
|
||||
AudioNodeStream::AdvanceAndResume(StreamTime aAdvance)
|
||||
{
|
||||
mMainThreadCurrentTime += aAdvance;
|
||||
GraphImpl()->AppendMessage(MakeUnique<AdvanceAndResumeMessage>(this, aAdvance));
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::ObtainInputBlock(AudioBlock& aTmpChunk,
|
||||
uint32_t aPortIndex)
|
||||
{
|
||||
uint32_t inputCount = mInputs.Length();
|
||||
uint32_t outputChannelCount = 1;
|
||||
AutoTArray<const AudioBlock*,250> inputChunks;
|
||||
for (uint32_t i = 0; i < inputCount; ++i) {
|
||||
if (aPortIndex != mInputs[i]->InputNumber()) {
|
||||
// This input is connected to a different port
|
||||
continue;
|
||||
}
|
||||
MediaStream* s = mInputs[i]->GetSource();
|
||||
AudioNodeStream* a = static_cast<AudioNodeStream*>(s);
|
||||
MOZ_ASSERT(a == s->AsAudioNodeStream());
|
||||
if (a->IsAudioParamStream()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const AudioBlock* chunk = &a->mLastChunks[mInputs[i]->OutputNumber()];
|
||||
MOZ_ASSERT(chunk);
|
||||
if (chunk->IsNull() || chunk->mChannelData.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
inputChunks.AppendElement(chunk);
|
||||
outputChannelCount =
|
||||
GetAudioChannelsSuperset(outputChannelCount, chunk->ChannelCount());
|
||||
}
|
||||
|
||||
outputChannelCount = ComputedNumberOfChannels(outputChannelCount);
|
||||
|
||||
uint32_t inputChunkCount = inputChunks.Length();
|
||||
if (inputChunkCount == 0 ||
|
||||
(inputChunkCount == 1 && inputChunks[0]->ChannelCount() == 0)) {
|
||||
aTmpChunk.SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputChunkCount == 1 &&
|
||||
inputChunks[0]->ChannelCount() == outputChannelCount) {
|
||||
aTmpChunk = *inputChunks[0];
|
||||
return;
|
||||
}
|
||||
|
||||
if (outputChannelCount == 0) {
|
||||
aTmpChunk.SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
aTmpChunk.AllocateChannels(outputChannelCount);
|
||||
DownmixBufferType downmixBuffer;
|
||||
ASSERT_ALIGNED16(downmixBuffer.Elements());
|
||||
|
||||
for (uint32_t i = 0; i < inputChunkCount; ++i) {
|
||||
AccumulateInputChunk(i, *inputChunks[i], &aTmpChunk, &downmixBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::AccumulateInputChunk(uint32_t aInputIndex,
|
||||
const AudioBlock& aChunk,
|
||||
AudioBlock* aBlock,
|
||||
DownmixBufferType* aDownmixBuffer)
|
||||
{
|
||||
AutoTArray<const float*,GUESS_AUDIO_CHANNELS> channels;
|
||||
UpMixDownMixChunk(&aChunk, aBlock->ChannelCount(), channels, *aDownmixBuffer);
|
||||
|
||||
for (uint32_t c = 0; c < channels.Length(); ++c) {
|
||||
const float* inputData = static_cast<const float*>(channels[c]);
|
||||
float* outputData = aBlock->ChannelFloatsForWrite(c);
|
||||
if (inputData) {
|
||||
if (aInputIndex == 0) {
|
||||
AudioBlockCopyChannelWithScale(inputData, aChunk.mVolume, outputData);
|
||||
} else {
|
||||
AudioBlockAddChannelWithScale(inputData, aChunk.mVolume, outputData);
|
||||
}
|
||||
} else {
|
||||
if (aInputIndex == 0) {
|
||||
PodZero(outputData, WEBAUDIO_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::UpMixDownMixChunk(const AudioBlock* aChunk,
|
||||
uint32_t aOutputChannelCount,
|
||||
nsTArray<const float*>& aOutputChannels,
|
||||
DownmixBufferType& aDownmixBuffer)
|
||||
{
|
||||
for (uint32_t i = 0; i < aChunk->ChannelCount(); i++) {
|
||||
aOutputChannels.AppendElement(static_cast<const float*>(aChunk->mChannelData[i]));
|
||||
}
|
||||
if (aOutputChannels.Length() < aOutputChannelCount) {
|
||||
if (mChannelInterpretation == ChannelInterpretation::Speakers) {
|
||||
AudioChannelsUpMix<float>(&aOutputChannels, aOutputChannelCount, nullptr);
|
||||
NS_ASSERTION(aOutputChannelCount == aOutputChannels.Length(),
|
||||
"We called GetAudioChannelsSuperset to avoid this");
|
||||
} else {
|
||||
// Fill up the remaining aOutputChannels by zeros
|
||||
for (uint32_t j = aOutputChannels.Length(); j < aOutputChannelCount; ++j) {
|
||||
aOutputChannels.AppendElement(nullptr);
|
||||
}
|
||||
}
|
||||
} else if (aOutputChannels.Length() > aOutputChannelCount) {
|
||||
if (mChannelInterpretation == ChannelInterpretation::Speakers) {
|
||||
AutoTArray<float*,GUESS_AUDIO_CHANNELS> outputChannels;
|
||||
outputChannels.SetLength(aOutputChannelCount);
|
||||
aDownmixBuffer.SetLength(aOutputChannelCount * WEBAUDIO_BLOCK_SIZE);
|
||||
for (uint32_t j = 0; j < aOutputChannelCount; ++j) {
|
||||
outputChannels[j] = &aDownmixBuffer[j * WEBAUDIO_BLOCK_SIZE];
|
||||
}
|
||||
|
||||
AudioChannelsDownMix(aOutputChannels, outputChannels.Elements(),
|
||||
aOutputChannelCount, WEBAUDIO_BLOCK_SIZE);
|
||||
|
||||
aOutputChannels.SetLength(aOutputChannelCount);
|
||||
for (uint32_t j = 0; j < aOutputChannels.Length(); ++j) {
|
||||
aOutputChannels[j] = outputChannels[j];
|
||||
}
|
||||
} else {
|
||||
// Drop the remaining aOutputChannels
|
||||
aOutputChannels.RemoveElementsAt(aOutputChannelCount,
|
||||
aOutputChannels.Length() - aOutputChannelCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The MediaStreamGraph guarantees that this is actually one block, for
|
||||
// AudioNodeStreams.
|
||||
void
|
||||
AudioNodeStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
|
||||
{
|
||||
uint16_t outputCount = mLastChunks.Length();
|
||||
MOZ_ASSERT(outputCount == std::max(uint16_t(1), mEngine->OutputCount()));
|
||||
|
||||
if (!mIsActive) {
|
||||
// mLastChunks are already null.
|
||||
#ifdef DEBUG
|
||||
for (const auto& chunk : mLastChunks) {
|
||||
MOZ_ASSERT(chunk.IsNull());
|
||||
}
|
||||
#endif
|
||||
} else if (InMutedCycle()) {
|
||||
mInputChunks.Clear();
|
||||
for (uint16_t i = 0; i < outputCount; ++i) {
|
||||
mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
}
|
||||
} else {
|
||||
// We need to generate at least one input
|
||||
uint16_t maxInputs = std::max(uint16_t(1), mEngine->InputCount());
|
||||
mInputChunks.SetLength(maxInputs);
|
||||
for (uint16_t i = 0; i < maxInputs; ++i) {
|
||||
ObtainInputBlock(mInputChunks[i], i);
|
||||
}
|
||||
bool finished = false;
|
||||
if (mPassThrough) {
|
||||
MOZ_ASSERT(outputCount == 1, "For now, we only support nodes that have one output port");
|
||||
mLastChunks[0] = mInputChunks[0];
|
||||
} else {
|
||||
if (maxInputs <= 1 && outputCount <= 1) {
|
||||
mEngine->ProcessBlock(this, aFrom,
|
||||
mInputChunks[0], &mLastChunks[0], &finished);
|
||||
} else {
|
||||
mEngine->ProcessBlocksOnPorts(this, mInputChunks, mLastChunks, &finished);
|
||||
}
|
||||
}
|
||||
for (uint16_t i = 0; i < outputCount; ++i) {
|
||||
NS_ASSERTION(mLastChunks[i].GetDuration() == WEBAUDIO_BLOCK_SIZE,
|
||||
"Invalid WebAudio chunk size");
|
||||
}
|
||||
if (finished) {
|
||||
mMarkAsFinishedAfterThisBlock = true;
|
||||
if (mIsActive) {
|
||||
ScheduleCheckForInactive();
|
||||
}
|
||||
}
|
||||
|
||||
if (GetDisabledTrackMode(static_cast<TrackID>(AUDIO_TRACK)) != DisabledTrackMode::ENABLED) {
|
||||
for (uint32_t i = 0; i < outputCount; ++i) {
|
||||
mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mFinished) {
|
||||
// Don't output anything while finished
|
||||
if (mFlags & EXTERNAL_OUTPUT) {
|
||||
AdvanceOutputSegment();
|
||||
}
|
||||
if (mMarkAsFinishedAfterThisBlock && (aFlags & ALLOW_FINISH)) {
|
||||
// This stream was finished the last time that we looked at it, and all
|
||||
// of the depending streams have finished their output as well, so now
|
||||
// it's time to mark this stream as finished.
|
||||
if (mFlags & EXTERNAL_OUTPUT) {
|
||||
FinishOutput();
|
||||
}
|
||||
FinishOnGraphThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::ProduceOutputBeforeInput(GraphTime aFrom)
|
||||
{
|
||||
MOZ_ASSERT(mEngine->AsDelayNodeEngine());
|
||||
MOZ_ASSERT(mEngine->OutputCount() == 1,
|
||||
"DelayNodeEngine output count should be 1");
|
||||
MOZ_ASSERT(!InMutedCycle(), "DelayNodes should break cycles");
|
||||
MOZ_ASSERT(mLastChunks.Length() == 1);
|
||||
|
||||
if (!mIsActive) {
|
||||
mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
} else {
|
||||
mEngine->ProduceBlockBeforeInput(this, aFrom, &mLastChunks[0]);
|
||||
NS_ASSERTION(mLastChunks[0].GetDuration() == WEBAUDIO_BLOCK_SIZE,
|
||||
"Invalid WebAudio chunk size");
|
||||
if (GetDisabledTrackMode(static_cast<TrackID>(AUDIO_TRACK)) != DisabledTrackMode::ENABLED) {
|
||||
mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::AdvanceOutputSegment()
|
||||
{
|
||||
StreamTracks::Track* track = EnsureTrack(AUDIO_TRACK);
|
||||
// No more tracks will be coming
|
||||
mTracks.AdvanceKnownTracksTime(STREAM_TIME_MAX);
|
||||
|
||||
AudioSegment* segment = track->Get<AudioSegment>();
|
||||
|
||||
if (!mLastChunks[0].IsNull()) {
|
||||
segment->AppendAndConsumeChunk(mLastChunks[0].AsMutableChunk());
|
||||
} else {
|
||||
segment->AppendNullData(mLastChunks[0].GetDuration());
|
||||
}
|
||||
|
||||
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
|
||||
MediaStreamListener* l = mListeners[j];
|
||||
AudioChunk copyChunk = mLastChunks[0].AsAudioChunk();
|
||||
AudioSegment tmpSegment;
|
||||
tmpSegment.AppendAndConsumeChunk(©Chunk);
|
||||
l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
|
||||
segment->GetDuration(), TrackEventCommand::TRACK_EVENT_NONE, tmpSegment);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::FinishOutput()
|
||||
{
|
||||
StreamTracks::Track* track = EnsureTrack(AUDIO_TRACK);
|
||||
track->SetEnded();
|
||||
|
||||
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
|
||||
MediaStreamListener* l = mListeners[j];
|
||||
AudioSegment emptySegment;
|
||||
l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
|
||||
track->GetSegment()->GetDuration(),
|
||||
TrackEventCommand::TRACK_EVENT_ENDED, emptySegment);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::AddInput(MediaInputPort* aPort)
|
||||
{
|
||||
ProcessedMediaStream::AddInput(aPort);
|
||||
AudioNodeStream* ns = aPort->GetSource()->AsAudioNodeStream();
|
||||
// Streams that are not AudioNodeStreams are considered active.
|
||||
if (!ns || (ns->mIsActive && !ns->IsAudioParamStream())) {
|
||||
IncrementActiveInputCount();
|
||||
}
|
||||
}
|
||||
void
|
||||
AudioNodeStream::RemoveInput(MediaInputPort* aPort)
|
||||
{
|
||||
ProcessedMediaStream::RemoveInput(aPort);
|
||||
AudioNodeStream* ns = aPort->GetSource()->AsAudioNodeStream();
|
||||
// Streams that are not AudioNodeStreams are considered active.
|
||||
if (!ns || (ns->mIsActive && !ns->IsAudioParamStream())) {
|
||||
DecrementActiveInputCount();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SetActive()
|
||||
{
|
||||
if (mIsActive || mMarkAsFinishedAfterThisBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
mIsActive = true;
|
||||
if (!(mFlags & EXTERNAL_OUTPUT)) {
|
||||
GraphImpl()->DecrementSuspendCount(this);
|
||||
}
|
||||
if (IsAudioParamStream()) {
|
||||
// Consumers merely influence stream order.
|
||||
// They do not read from the stream.
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& consumer : mConsumers) {
|
||||
AudioNodeStream* ns = consumer->GetDestination()->AsAudioNodeStream();
|
||||
if (ns) {
|
||||
ns->IncrementActiveInputCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AudioNodeStream::CheckForInactiveMessage final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
explicit CheckForInactiveMessage(AudioNodeStream* aStream) :
|
||||
ControlMessage(aStream) {}
|
||||
void Run() override
|
||||
{
|
||||
auto ns = static_cast<AudioNodeStream*>(mStream);
|
||||
ns->CheckForInactive();
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
AudioNodeStream::ScheduleCheckForInactive()
|
||||
{
|
||||
if (mActiveInputCount > 0 && !mMarkAsFinishedAfterThisBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto message = MakeUnique<CheckForInactiveMessage>(this);
|
||||
GraphImpl()->RunMessageAfterProcessing(Move(message));
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::CheckForInactive()
|
||||
{
|
||||
if (((mActiveInputCount > 0 || mEngine->IsActive()) &&
|
||||
!mMarkAsFinishedAfterThisBlock) ||
|
||||
!mIsActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
mIsActive = false;
|
||||
mInputChunks.Clear(); // not required for foreseeable future
|
||||
for (auto& chunk : mLastChunks) {
|
||||
chunk.SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
}
|
||||
if (!(mFlags & EXTERNAL_OUTPUT)) {
|
||||
GraphImpl()->IncrementSuspendCount(this);
|
||||
}
|
||||
if (IsAudioParamStream()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& consumer : mConsumers) {
|
||||
AudioNodeStream* ns = consumer->GetDestination()->AsAudioNodeStream();
|
||||
if (ns) {
|
||||
ns->DecrementActiveInputCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::IncrementActiveInputCount()
|
||||
{
|
||||
++mActiveInputCount;
|
||||
SetActive();
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::DecrementActiveInputCount()
|
||||
{
|
||||
MOZ_ASSERT(mActiveInputCount > 0);
|
||||
--mActiveInputCount;
|
||||
CheckForInactive();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
Loading…
Add table
Add a link
Reference in a new issue