зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1248861: P5. Add AudioConverter class and relatives. r=cpearce
To be used in combination with AudioDataBuffer class that will be able to perform format conversion. Can currently only perform channel re-ordering. Future use will add downmixing, upmixing and resampling capabilities. MozReview-Commit-ID: 2FBu9aRVtgj
This commit is contained in:
Родитель
dd9f244fa3
Коммит
b5ca86120b
|
@ -0,0 +1,131 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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>
|
||||
|
||||
/*
|
||||
* Parts derived from MythTV AudioConvert Class
|
||||
* Created by Jean-Yves Avenard.
|
||||
*
|
||||
* Copyright (C) Bubblestuff Pty Ltd 2013
|
||||
* Copyright (C) foobum@gmail.com 2010
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
AudioConverter::AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut)
|
||||
: mIn(aIn)
|
||||
, mOut(aOut)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aIn.Channels() == aOut.Channels() &&
|
||||
aIn.Rate() == aOut.Rate() &&
|
||||
aIn.Format() == aOut.Format() &&
|
||||
aIn.Interleaved() == aOut.Interleaved(),
|
||||
"Only channel reordering is supported at this stage");
|
||||
MOZ_DIAGNOSTIC_ASSERT(aOut.Interleaved(), "planar audio format not supported");
|
||||
InitChannelMap();
|
||||
}
|
||||
|
||||
bool
|
||||
AudioConverter::InitChannelMap()
|
||||
{
|
||||
if (!CanReorderAudio()) {
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < mIn.Layout().Count(); i++) {
|
||||
for (uint32_t j = 0; j < mIn.Layout().Count(); j++) {
|
||||
if (mOut.Layout()[j] == mIn.Layout()[i]) {
|
||||
mChannelOrderMap[j] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AudioConverter::CanWorkInPlace() const
|
||||
{
|
||||
return mIn.Channels() * mIn.Rate() * AudioConfig::SampleSize(mIn.Format()) >=
|
||||
mOut.Channels() * mOut.Rate() * AudioConfig::SampleSize(mOut.Format());
|
||||
}
|
||||
|
||||
size_t
|
||||
AudioConverter::Process(void* aOut, const void* aIn, size_t aBytes)
|
||||
{
|
||||
if (!CanWorkInPlace()) {
|
||||
return 0;
|
||||
}
|
||||
if (mIn.Layout() != mOut.Layout() &&
|
||||
CanReorderAudio()) {
|
||||
ReOrderInterleavedChannels(aOut, aIn, aBytes);
|
||||
}
|
||||
return aBytes;
|
||||
}
|
||||
|
||||
// Reorder interleaved channels.
|
||||
// Can work in place (e.g aOut == aIn).
|
||||
template <class AudioDataType>
|
||||
void
|
||||
_ReOrderInterleavedChannels(AudioDataType* aOut, const AudioDataType* aIn,
|
||||
uint32_t aFrames, uint32_t aChannels,
|
||||
const uint32_t* aChannelOrderMap)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aChannels <= MAX_AUDIO_CHANNELS);
|
||||
AudioDataType val[MAX_AUDIO_CHANNELS];
|
||||
for (uint32_t i = 0; i < aFrames; i++) {
|
||||
for (uint32_t j = 0; j < aChannels; j++) {
|
||||
val[j] = aIn[aChannelOrderMap[j]];
|
||||
}
|
||||
for (uint32_t j = 0; j < aChannels; j++) {
|
||||
aOut[j] = val[j];
|
||||
}
|
||||
aOut += aChannels;
|
||||
aIn += aChannels;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioConverter::ReOrderInterleavedChannels(void* aOut, const void* aIn,
|
||||
size_t aDataSize) const
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(mIn.Channels() == mOut.Channels());
|
||||
|
||||
if (mOut.Layout() == mIn.Layout()) {
|
||||
return;
|
||||
}
|
||||
if (mOut.Channels() == 1) {
|
||||
// If channel count is 1, planar and non-planar formats are the same and
|
||||
// there's nothing to reorder.
|
||||
if (aOut != aIn) {
|
||||
memmove(aOut, aIn, aDataSize);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t bits = AudioConfig::FormatToBits(mOut.Format());
|
||||
switch (bits) {
|
||||
case 8:
|
||||
_ReOrderInterleavedChannels((uint8_t*)aOut, (const uint8_t*)aIn,
|
||||
aDataSize/sizeof(uint8_t)/mIn.Channels(),
|
||||
mIn.Channels(), mChannelOrderMap);
|
||||
break;
|
||||
case 16:
|
||||
_ReOrderInterleavedChannels((int16_t*)aOut,(const int16_t*)aIn,
|
||||
aDataSize/sizeof(int16_t)/mIn.Channels(),
|
||||
mIn.Channels(), mChannelOrderMap);
|
||||
break;
|
||||
default:
|
||||
MOZ_DIAGNOSTIC_ASSERT(AudioConfig::SampleSize(mOut.Format()) == 4);
|
||||
_ReOrderInterleavedChannels((int32_t*)aOut,(const int32_t*)aIn,
|
||||
aDataSize/sizeof(int32_t)/mIn.Channels(),
|
||||
mIn.Channels(), mChannelOrderMap);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,146 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#if !defined(AudioConverter_h)
|
||||
#define AudioConverter_h
|
||||
|
||||
#include "MediaInfo.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
template <AudioConfig::SampleFormat T> struct AudioDataBufferTypeChooser;
|
||||
template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_U8>
|
||||
{ typedef uint8_t Type; };
|
||||
template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S16>
|
||||
{ typedef int16_t Type; };
|
||||
template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24LSB>
|
||||
{ typedef int32_t Type; };
|
||||
template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24>
|
||||
{ typedef int32_t Type; };
|
||||
template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S32>
|
||||
{ typedef int32_t Type; };
|
||||
template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_FLT>
|
||||
{ typedef float Type; };
|
||||
|
||||
// 'Value' is the type used externally to deal with stored value.
|
||||
// AudioDataBuffer can perform conversion between different SampleFormat content.
|
||||
template <AudioConfig::SampleFormat Format, typename Value = typename AudioDataBufferTypeChooser<Format>::Type>
|
||||
class AudioDataBuffer
|
||||
{
|
||||
public:
|
||||
AudioDataBuffer() {}
|
||||
AudioDataBuffer(Value* aBuffer, size_t aLength)
|
||||
: mBuffer(aBuffer, aLength)
|
||||
{}
|
||||
explicit AudioDataBuffer(const AudioDataBuffer& aOther)
|
||||
: mBuffer(aOther.mBuffer)
|
||||
{}
|
||||
AudioDataBuffer(AudioDataBuffer&& aOther)
|
||||
: mBuffer(Move(aOther.mBuffer))
|
||||
{}
|
||||
template <AudioConfig::SampleFormat OtherFormat, typename OtherValue>
|
||||
explicit AudioDataBuffer(const AudioDataBuffer<OtherFormat, OtherValue>& other)
|
||||
{
|
||||
// TODO: Convert from different type, may use asm routines.
|
||||
MOZ_CRASH("Conversion not implemented yet");
|
||||
}
|
||||
|
||||
// A u8, s16 and float aligned buffer can only be treated as
|
||||
// FORMAT_U8, FORMAT_S16 and FORMAT_FLT respectively.
|
||||
// So allow them as copy and move constructors.
|
||||
explicit AudioDataBuffer(const AlignedByteBuffer& aBuffer)
|
||||
: mBuffer(aBuffer)
|
||||
{
|
||||
static_assert(Format == AudioConfig::FORMAT_U8,
|
||||
"Conversion not implemented yet");
|
||||
}
|
||||
explicit AudioDataBuffer(const AlignedShortBuffer& aBuffer)
|
||||
: mBuffer(aBuffer)
|
||||
{
|
||||
static_assert(Format == AudioConfig::FORMAT_S16,
|
||||
"Conversion not implemented yet");
|
||||
}
|
||||
explicit AudioDataBuffer(const AlignedFloatBuffer& aBuffer)
|
||||
: mBuffer(aBuffer)
|
||||
{
|
||||
static_assert(Format == AudioConfig::FORMAT_FLT,
|
||||
"Conversion not implemented yet");
|
||||
}
|
||||
explicit AudioDataBuffer(AlignedByteBuffer&& aBuffer)
|
||||
: mBuffer(Move(aBuffer))
|
||||
{
|
||||
static_assert(Format == AudioConfig::FORMAT_U8,
|
||||
"Conversion not implemented yet");
|
||||
}
|
||||
explicit AudioDataBuffer(AlignedShortBuffer&& aBuffer)
|
||||
: mBuffer(Move(aBuffer))
|
||||
{
|
||||
static_assert(Format == AudioConfig::FORMAT_S16,
|
||||
"Conversion not implemented yet");
|
||||
}
|
||||
explicit AudioDataBuffer(const AlignedFloatBuffer&& aBuffer)
|
||||
: mBuffer(Move(aBuffer))
|
||||
{
|
||||
static_assert(Format == AudioConfig::FORMAT_FLT,
|
||||
"Conversion not implemented yet");
|
||||
}
|
||||
|
||||
Value* Data() const { return mBuffer.Data(); }
|
||||
size_t Length() const { return mBuffer.Length(); }
|
||||
size_t Size() const { return mBuffer.Size(); }
|
||||
AlignedBuffer<Value> Forget()
|
||||
{
|
||||
// Correct type -> Just give values as-is.
|
||||
return Move(mBuffer);
|
||||
}
|
||||
private:
|
||||
AlignedBuffer<Value> mBuffer;
|
||||
};
|
||||
|
||||
typedef AudioDataBuffer<AudioConfig::FORMAT_DEFAULT> AudioSampleBuffer;
|
||||
|
||||
class AudioConverter {
|
||||
public:
|
||||
AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut);
|
||||
|
||||
// Attempt to convert the AudioDataBuffer in place.
|
||||
// Will return 0 if the conversion wasn't possible.
|
||||
// Process may allocate memory internally should intermediary steps be
|
||||
// required.
|
||||
template <AudioConfig::SampleFormat Type, typename Value>
|
||||
size_t Process(AudioDataBuffer<Type, Value>& aBuffer)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format() && mIn.Format() == Type);
|
||||
return Process(aBuffer.Data(), aBuffer.Data(), aBuffer.Size());
|
||||
}
|
||||
bool CanWorkInPlace() const;
|
||||
bool CanReorderAudio() const
|
||||
{
|
||||
return mIn.Layout().IsValid() && mOut.Layout().IsValid() &&
|
||||
mIn.Layout().Map() == mOut.Layout().Map();
|
||||
}
|
||||
|
||||
private:
|
||||
const AudioConfig mIn;
|
||||
const AudioConfig mOut;
|
||||
uint32_t mChannelOrderMap[MAX_AUDIO_CHANNELS];
|
||||
bool InitChannelMap();
|
||||
/**
|
||||
* Process
|
||||
* Parameters:
|
||||
* aOut : destination buffer where converted samples will be copied
|
||||
* aIn : source buffer
|
||||
* aBytes: size in bytes of source buffer
|
||||
*
|
||||
* Return Value: size in bytes of samples converted or 0 if error
|
||||
*/
|
||||
size_t Process(void* aOut, const void* aIn, size_t aBytes);
|
||||
void ReOrderInterleavedChannels(void* aOut, const void* aIn, size_t aDataSize) const;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* AudioConverter_h */
|
|
@ -89,6 +89,7 @@ EXPORTS += [
|
|||
'AudioBufferUtils.h',
|
||||
'AudioChannelFormat.h',
|
||||
'AudioCompactor.h',
|
||||
'AudioConverter.h',
|
||||
'AudioMixer.h',
|
||||
'AudioPacketizer.h',
|
||||
'AudioSampleFormat.h',
|
||||
|
@ -196,6 +197,7 @@ UNIFIED_SOURCES += [
|
|||
'AudioCaptureStream.cpp',
|
||||
'AudioChannelFormat.cpp',
|
||||
'AudioCompactor.cpp',
|
||||
'AudioConverter.cpp',
|
||||
'AudioSegment.cpp',
|
||||
'AudioStream.cpp',
|
||||
'AudioStreamTrack.cpp',
|
||||
|
|
Загрузка…
Ссылка в новой задаче