Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 "AudioResampler.h"
#include "TimeUnits.h"
namespace mozilla {
AudioResampler::AudioResampler(uint32_t aInRate, uint32_t aOutRate,
uint32_t aInputPreBufferFrameCount,
const PrincipalHandle& aPrincipalHandle)
: mResampler(aInRate, aOutRate, aInputPreBufferFrameCount),
mOutputChunks(aOutRate / 10, STEREO, aPrincipalHandle) {}
void AudioResampler::AppendInput(const AudioSegment& aInSegment) {
MOZ_ASSERT(aInSegment.GetDuration());
for (AudioSegment::ConstChunkIterator iter(aInSegment); !iter.IsEnded();
iter.Next()) {
const AudioChunk& chunk = *iter;
if (!mIsSampleFormatSet) {
// We don't know the format yet and all buffers are empty.
if (chunk.mBufferFormat == AUDIO_FORMAT_SILENCE) {
// Only silence has been received and the format is unkown. Igonre it,
// if Resampler() is called it will return silence too.
continue;
}
// First no silence data, set the format once for lifetime and let it
// continue the rest of the flow. We will not get in here again.
mOutputChunks.SetSampleFormat(chunk.mBufferFormat);
mResampler.SetSampleFormat(chunk.mBufferFormat);
mIsSampleFormatSet = true;
}
MOZ_ASSERT(mIsSampleFormatSet);
if (chunk.IsNull()) {
mResampler.AppendInputSilence(chunk.GetDuration());
continue;
}
// Make sure the channel is up to date. An AudioSegment can contain chunks
// with different channel count.
UpdateChannels(chunk.mChannelData.Length());
if (chunk.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
mResampler.AppendInput(chunk.ChannelData<float>(), chunk.GetDuration());
} else {
mResampler.AppendInput(chunk.ChannelData<int16_t>(), chunk.GetDuration());
}
}
}
AudioSegment AudioResampler::Resample(uint32_t aOutFrames, bool* aHasUnderrun) {
MOZ_ASSERT(aHasUnderrun);
AudioSegment segment;
// We don't know what to do yet and we only have received silence if any just
// return what they want and leave
if (!mIsSampleFormatSet) {
segment.AppendNullData(aOutFrames);
return segment;
}
media::TimeUnit outDuration(aOutFrames, mResampler.mOutRate);
mResampler.EnsurePreBuffer(outDuration);
const media::TimeUnit chunkCapacity(mOutputChunks.ChunkCapacity(),
mResampler.mOutRate);
while (!outDuration.IsZero()) {
MOZ_ASSERT(outDuration.IsPositive());
AudioChunk& chunk = mOutputChunks.GetNext();
const media::TimeUnit chunkDuration = std::min(outDuration, chunkCapacity);
outDuration -= chunkDuration;
const uint32_t outFrames = chunkDuration.ToTicksAtRate(mResampler.mOutRate);
for (uint32_t i = 0; i < chunk.ChannelCount(); ++i) {
if (chunk.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
*aHasUnderrun |= mResampler.Resample(
chunk.ChannelDataForWrite<float>(i), outFrames, i);
} else {
*aHasUnderrun |= mResampler.Resample(
chunk.ChannelDataForWrite<int16_t>(i), outFrames, i);
}
}
chunk.mDuration = outFrames;
// Create a copy in order to consume that copy and not the pre-allocated
// chunk
segment.AppendAndConsumeChunk(AudioChunk(chunk));
}
return segment;
}
void AudioResampler::Update(uint32_t aInRate, uint32_t aChannels) {
mResampler.UpdateResampler(aInRate, aChannels);
mOutputChunks.Update(aChannels);
}
uint32_t AudioResampler::InputCapacityFrames() const {
return mResampler.InFramesBufferSize();
}
uint32_t AudioResampler::InputReadableFrames() const {
return mResampler.InFramesBuffered(0);
}
} // namespace mozilla