Bug 1264199: P4. Add mono to stereo upmix to AudioConverter. r=rillian
MozReview-Commit-ID: 4sCvNWKEMZS
--- a/dom/media/AudioConverter.cpp
+++ b/dom/media/AudioConverter.cpp
@@ -2,16 +2,17 @@
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "AudioConverter.h"
#include <string.h>
#include <speex/speex_resampler.h>
+#include <cmath>
/*
* Parts derived from MythTV AudioConvert Class
* Created by Jean-Yves Avenard.
*
* Copyright (C) Bubblestuff Pty Ltd 2013
* Copyright (C) foobum@gmail.com 2010
*/
@@ -21,19 +22,19 @@ namespace mozilla {
AudioConverter::AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut)
: mIn(aIn)
, mOut(aOut)
, mResampler(nullptr)
{
MOZ_DIAGNOSTIC_ASSERT(aIn.Format() == aOut.Format() &&
aIn.Interleaved() == aOut.Interleaved(),
"No format or rate conversion is supported at this stage");
- MOZ_DIAGNOSTIC_ASSERT((aIn.Channels() > aOut.Channels() && aOut.Channels() <= 2) ||
+ MOZ_DIAGNOSTIC_ASSERT(aOut.Channels() <= 2 ||
aIn.Channels() == aOut.Channels(),
- "Only downmixing to mono or stereo is supported at this stage");
+ "Only down/upmixing to mono or stereo is supported at this stage");
MOZ_DIAGNOSTIC_ASSERT(aOut.Interleaved(), "planar audio format not supported");
mIn.Layout().MappingTable(mOut.Layout(), mChannelOrderMap);
if (aIn.Rate() != aOut.Rate()) {
int error;
mResampler = speex_resampler_init(aOut.Channels(),
aIn.Rate(),
aOut.Rate(),
SPEEX_RESAMPLER_QUALITY_DEFAULT,
@@ -55,35 +56,37 @@ AudioConverter::~AudioConverter()
mResampler = nullptr;
}
}
bool
AudioConverter::CanWorkInPlace() const
{
bool needDownmix = mIn.Channels() > mOut.Channels();
+ bool needUpmix = mIn.Channels() < mOut.Channels();
bool canDownmixInPlace =
mIn.Channels() * AudioConfig::SampleSize(mIn.Format()) >=
mOut.Channels() * AudioConfig::SampleSize(mOut.Format());
bool needResample = mIn.Rate() != mOut.Rate();
bool canResampleInPlace = mIn.Rate() >= mOut.Rate();
// We should be able to work in place if 1s of audio input takes less space
// than 1s of audio output. However, as we downmix before resampling we can't
// perform any upsampling in place (e.g. if incoming rate >= outgoing rate)
- return (!needDownmix || canDownmixInPlace) &&
+ return !needUpmix && (!needDownmix || canDownmixInPlace) &&
(!needResample || canResampleInPlace);
}
size_t
AudioConverter::ProcessInternal(void* aOut, const void* aIn, size_t aFrames)
{
if (mIn.Channels() > mOut.Channels()) {
return DownmixAudio(aOut, aIn, aFrames);
- } else if (mIn.Layout() != mOut.Layout() &&
- CanReorderAudio()) {
+ } else if (mIn.Channels() < mOut.Channels()) {
+ return UpmixAudio(aOut, aIn, aFrames);
+ } else if (mIn.Layout() != mOut.Layout() && CanReorderAudio()) {
ReOrderInterleavedChannels(aOut, aIn, aFrames);
} else if (aIn != aOut) {
memmove(aOut, aIn, FramesOutToBytes(aFrames));
}
return aFrames;
}
// Reorder interleaved channels.
@@ -226,26 +229,26 @@ AudioConverter::DownmixAudio(void* aOut,
aIn = aOut;
channels = 2;
}
if (mOut.Channels() == 1) {
if (mIn.Format() == AudioConfig::FORMAT_FLT) {
const float* in = static_cast<const float*>(aIn);
float* out = static_cast<float*>(aOut);
- for (uint32_t fIdx = 0; fIdx < aFrames; ++fIdx) {
+ for (size_t fIdx = 0; fIdx < aFrames; ++fIdx) {
float sample = 0.0;
// The sample of the buffer would be interleaved.
sample = (in[fIdx*channels] + in[fIdx*channels + 1]) * 0.5;
*out++ = sample;
}
} else if (mIn.Format() == AudioConfig::FORMAT_S16) {
const int16_t* in = static_cast<const int16_t*>(aIn);
int16_t* out = static_cast<int16_t*>(aOut);
- for (uint32_t fIdx = 0; fIdx < aFrames; ++fIdx) {
+ for (size_t fIdx = 0; fIdx < aFrames; ++fIdx) {
int32_t sample = 0.0;
// The sample of the buffer would be interleaved.
sample = (in[fIdx*channels] + in[fIdx*channels + 1]) * 0.5;
*out++ = sample;
}
} else {
MOZ_DIAGNOSTIC_ASSERT(false, "Unsupported data type");
}
@@ -275,16 +278,58 @@ AudioConverter::ResampleAudio(void* aOut
} else {
MOZ_DIAGNOSTIC_ASSERT(false, "Unsupported data type");
}
MOZ_ASSERT(inframes == aFrames, "Some frames will be dropped");
return outframes;
}
size_t
+AudioConverter::UpmixAudio(void* aOut, const void* aIn, size_t aFrames) const
+{
+ MOZ_ASSERT(mIn.Format() == AudioConfig::FORMAT_S16 ||
+ mIn.Format() == AudioConfig::FORMAT_FLT);
+ MOZ_ASSERT(mIn.Channels() < mOut.Channels());
+ MOZ_ASSERT(mIn.Channels() == 1, "Can only upmix mono for now");
+ MOZ_ASSERT(mOut.Channels() == 2, "Can only upmix to stereo for now");
+
+ if (mOut.Channels() != 2) {
+ return 0;
+ }
+
+ // Upmix mono to stereo.
+ // This is a very dumb mono to stereo upmixing, power levels are preserved
+ // following the calculation: left = right = -3dB*mono.
+ if (mIn.Format() == AudioConfig::FORMAT_FLT) {
+ const float m3db = std::sqrt(0.5); // -3dB = sqrt(1/2)
+ const float* in = static_cast<const float*>(aIn);
+ float* out = static_cast<float*>(aOut);
+ for (size_t fIdx = 0; fIdx < aFrames; ++fIdx) {
+ float sample = in[fIdx] * m3db;
+ // The samples of the buffer would be interleaved.
+ *out++ = sample;
+ *out++ = sample;
+ }
+ } else if (mIn.Format() == AudioConfig::FORMAT_S16) {
+ const int16_t* in = static_cast<const int16_t*>(aIn);
+ int16_t* out = static_cast<int16_t*>(aOut);
+ for (size_t fIdx = 0; fIdx < aFrames; ++fIdx) {
+ int16_t sample = ((int32_t)in[fIdx] * 11585) >> 14; // close enough to i*sqrt(0.5)
+ // The samples of the buffer would be interleaved.
+ *out++ = sample;
+ *out++ = sample;
+ }
+ } else {
+ MOZ_DIAGNOSTIC_ASSERT(false, "Unsupported data type");
+ }
+
+ return aFrames;
+}
+
+size_t
AudioConverter::ResampleRecipientFrames(size_t aFrames) const
{
return (uint64_t)aFrames * mOut.Rate() / mIn.Rate() + 1;
}
size_t
AudioConverter::FramesOutToSamples(size_t aFrames) const
{
--- a/dom/media/AudioConverter.h
+++ b/dom/media/AudioConverter.h
@@ -208,16 +208,17 @@ private:
* aIn : source buffer
* aSamples: number of frames in source buffer
*
* Return Value: number of frames converted or 0 if error
*/
size_t ProcessInternal(void* aOut, const void* aIn, size_t aFrames);
void ReOrderInterleavedChannels(void* aOut, const void* aIn, size_t aFrames) const;
size_t DownmixAudio(void* aOut, const void* aIn, size_t aFrames) const;
+ size_t UpmixAudio(void* aOut, const void* aIn, size_t aFrames) const;
size_t FramesOutToSamples(size_t aFrames) const;
size_t SamplesInToFrames(size_t aSamples) const;
size_t FramesOutToBytes(size_t aFrames) const;
// Resampler context.
SpeexResamplerState* mResampler;
size_t ResampleAudio(void* aOut, const void* aIn, size_t aFrames);