2018-01-23 23:06:07 +03:00
|
|
|
/* -*- 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 "AudioConfig.h"
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
|
|
|
typedef AudioConfig::ChannelLayout ChannelLayout;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* AudioConfig::ChannelLayout
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
SMPTE channel layout (also known as wave order)
|
|
|
|
DUAL-MONO L R
|
|
|
|
DUAL-MONO-LFE L R LFE
|
|
|
|
MONO M
|
|
|
|
MONO-LFE M LFE
|
|
|
|
STEREO L R
|
|
|
|
STEREO-LFE L R LFE
|
|
|
|
3F L R C
|
|
|
|
3F-LFE L R C LFE
|
|
|
|
2F1 L R S
|
|
|
|
2F1-LFE L R LFE S
|
|
|
|
3F1 L R C S
|
|
|
|
3F1-LFE L R C LFE S
|
|
|
|
2F2 L R LS RS
|
|
|
|
2F2-LFE L R LFE LS RS
|
|
|
|
3F2 L R C LS RS
|
|
|
|
3F2-LFE L R C LFE LS RS
|
|
|
|
3F3R-LFE L R C LFE BC LS RS
|
|
|
|
3F4-LFE L R C LFE Rls Rrs LS RS
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
AudioConfig::ChannelLayout::UpdateChannelMap()
|
|
|
|
{
|
2018-03-16 18:37:27 +03:00
|
|
|
mValid = mChannels.Length() <= MAX_CHANNELS;
|
|
|
|
mChannelMap = UNKNOWN_MAP;
|
2018-01-23 23:06:07 +03:00
|
|
|
if (mValid) {
|
|
|
|
mChannelMap = Map();
|
|
|
|
mValid = mChannelMap > 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto
|
|
|
|
AudioConfig::ChannelLayout::Map() const -> ChannelMap
|
|
|
|
{
|
2018-03-16 18:37:27 +03:00
|
|
|
if (mChannelMap != UNKNOWN_MAP) {
|
2018-01-23 23:06:07 +03:00
|
|
|
return mChannelMap;
|
|
|
|
}
|
2018-03-16 18:37:27 +03:00
|
|
|
if (mChannels.Length() > MAX_CHANNELS) {
|
|
|
|
return UNKNOWN_MAP;
|
|
|
|
}
|
2018-01-23 23:06:07 +03:00
|
|
|
ChannelMap map = UNKNOWN_MAP;
|
2018-03-16 18:37:27 +03:00
|
|
|
for (size_t i = 0; i < mChannels.Length(); i++) {
|
|
|
|
if (uint32_t(mChannels[i]) > sizeof(ChannelMap) * 8) {
|
|
|
|
return UNKNOWN_MAP;
|
|
|
|
}
|
|
|
|
ChannelMap mask = 1 << mChannels[i];
|
2018-01-23 23:06:07 +03:00
|
|
|
if (mChannels[i] == CHANNEL_INVALID || (mChannelMap & mask)) {
|
|
|
|
// Invalid configuration.
|
2018-03-16 18:37:27 +03:00
|
|
|
return UNKNOWN_MAP;
|
2018-01-23 23:06:07 +03:00
|
|
|
}
|
|
|
|
map |= mask;
|
|
|
|
}
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
|
|
|
const AudioConfig::Channel*
|
|
|
|
AudioConfig::ChannelLayout::DefaultLayoutForChannels(uint32_t aChannels) const
|
|
|
|
{
|
|
|
|
switch (aChannels) {
|
|
|
|
case 1: // MONO
|
|
|
|
{
|
|
|
|
static const Channel config[] = { CHANNEL_FRONT_CENTER };
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
case 2: // STEREO
|
|
|
|
{
|
|
|
|
static const Channel config[] = { CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT };
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
case 3: // 3F
|
|
|
|
{
|
|
|
|
static const Channel config[] = { CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT, CHANNEL_FRONT_CENTER };
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
case 4: // QUAD
|
|
|
|
{
|
|
|
|
static const Channel config[] = { CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT, CHANNEL_BACK_LEFT, CHANNEL_BACK_RIGHT };
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
case 5: // 3F2
|
|
|
|
{
|
|
|
|
static const Channel config[] = { CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT, CHANNEL_FRONT_CENTER, CHANNEL_SIDE_LEFT, CHANNEL_SIDE_RIGHT };
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
case 6: // 3F2-LFE
|
|
|
|
{
|
|
|
|
static const Channel config[] = { CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT, CHANNEL_FRONT_CENTER, CHANNEL_LFE, CHANNEL_SIDE_LEFT, CHANNEL_SIDE_RIGHT };
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
case 7: // 3F3R-LFE
|
|
|
|
{
|
|
|
|
static const Channel config[] = { CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT, CHANNEL_FRONT_CENTER, CHANNEL_LFE, CHANNEL_BACK_CENTER, CHANNEL_SIDE_LEFT, CHANNEL_SIDE_RIGHT };
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
case 8: // 3F4-LFE
|
|
|
|
{
|
|
|
|
static const Channel config[] = { CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT, CHANNEL_FRONT_CENTER, CHANNEL_LFE, CHANNEL_BACK_LEFT, CHANNEL_BACK_RIGHT, CHANNEL_SIDE_LEFT, CHANNEL_SIDE_RIGHT };
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ AudioConfig::ChannelLayout
|
|
|
|
AudioConfig::ChannelLayout::SMPTEDefault(
|
|
|
|
const ChannelLayout& aChannelLayout)
|
|
|
|
{
|
|
|
|
if (!aChannelLayout.IsValid()) {
|
|
|
|
return aChannelLayout;
|
|
|
|
}
|
|
|
|
return SMPTEDefault(aChannelLayout.Map());
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ ChannelLayout
|
|
|
|
AudioConfig::ChannelLayout::SMPTEDefault(ChannelMap aMap)
|
|
|
|
{
|
|
|
|
// First handle the most common cases.
|
|
|
|
switch (aMap) {
|
2018-03-23 20:41:11 +03:00
|
|
|
case LMONO_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_CENTER };
|
|
|
|
case LSTEREO_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT };
|
|
|
|
case L3F_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT,
|
|
|
|
CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_FRONT_CENTER };
|
|
|
|
case L3F_LFE_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT,
|
|
|
|
CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_FRONT_CENTER,
|
|
|
|
CHANNEL_LFE };
|
|
|
|
case L2F1_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT,
|
|
|
|
CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_BACK_CENTER };
|
|
|
|
case L2F1_LFE_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT,
|
|
|
|
CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_LFE,
|
|
|
|
CHANNEL_BACK_CENTER };
|
|
|
|
case L3F1_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT,
|
|
|
|
CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_FRONT_CENTER,
|
|
|
|
CHANNEL_BACK_CENTER };
|
|
|
|
case L3F1_LFE_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT,
|
|
|
|
CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_FRONT_CENTER,
|
|
|
|
CHANNEL_LFE,
|
|
|
|
CHANNEL_BACK_CENTER };
|
|
|
|
case L2F2_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT,
|
|
|
|
CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_SIDE_LEFT,
|
|
|
|
CHANNEL_SIDE_RIGHT };
|
|
|
|
case L2F2_LFE_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT,
|
|
|
|
CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_LFE,
|
|
|
|
CHANNEL_SIDE_LEFT,
|
|
|
|
CHANNEL_SIDE_RIGHT };
|
|
|
|
case LQUAD_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT,
|
|
|
|
CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_BACK_LEFT,
|
|
|
|
CHANNEL_BACK_RIGHT };
|
|
|
|
case LQUAD_LFE_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT,
|
|
|
|
CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_LFE,
|
|
|
|
CHANNEL_BACK_LEFT,
|
|
|
|
CHANNEL_BACK_RIGHT };
|
|
|
|
case L3F2_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT,
|
|
|
|
CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_FRONT_CENTER,
|
|
|
|
CHANNEL_SIDE_LEFT,
|
|
|
|
CHANNEL_SIDE_RIGHT };
|
|
|
|
case L3F2_LFE_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_FRONT_CENTER, CHANNEL_LFE,
|
|
|
|
CHANNEL_SIDE_LEFT, CHANNEL_SIDE_RIGHT };
|
|
|
|
case L3F2_BACK_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT,
|
|
|
|
CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_FRONT_CENTER,
|
|
|
|
CHANNEL_BACK_LEFT,
|
|
|
|
CHANNEL_BACK_RIGHT };
|
|
|
|
case L3F2_BACK_LFE_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_FRONT_CENTER, CHANNEL_LFE,
|
|
|
|
CHANNEL_BACK_LEFT, CHANNEL_BACK_RIGHT };
|
|
|
|
case L3F3R_LFE_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_FRONT_CENTER, CHANNEL_LFE,
|
|
|
|
CHANNEL_BACK_CENTER, CHANNEL_SIDE_LEFT,
|
|
|
|
CHANNEL_SIDE_RIGHT };
|
|
|
|
case L3F4_LFE_MAP:
|
|
|
|
return ChannelLayout{ CHANNEL_FRONT_LEFT, CHANNEL_FRONT_RIGHT,
|
|
|
|
CHANNEL_FRONT_CENTER, CHANNEL_LFE,
|
|
|
|
CHANNEL_BACK_LEFT, CHANNEL_BACK_RIGHT,
|
|
|
|
CHANNEL_SIDE_LEFT, CHANNEL_SIDE_RIGHT };
|
2018-01-23 23:06:07 +03:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2018-03-16 18:37:27 +03:00
|
|
|
|
|
|
|
static_assert(MAX_CHANNELS <= sizeof(ChannelMap) * 8,
|
|
|
|
"Must be able to fit channels on bit mask");
|
|
|
|
AutoTArray<Channel, MAX_CHANNELS> layout;
|
2018-01-23 23:06:07 +03:00
|
|
|
uint32_t channels = 0;
|
|
|
|
|
2018-03-09 15:44:11 +03:00
|
|
|
uint32_t i = 0;
|
2018-01-23 23:06:07 +03:00
|
|
|
while (aMap) {
|
|
|
|
if (aMap & 1) {
|
|
|
|
channels++;
|
2018-03-16 18:37:27 +03:00
|
|
|
if (channels > MAX_CHANNELS) {
|
2018-01-23 23:06:07 +03:00
|
|
|
return ChannelLayout();
|
|
|
|
}
|
2018-03-16 18:37:27 +03:00
|
|
|
layout.AppendElement(static_cast<Channel>(i));
|
2018-01-23 23:06:07 +03:00
|
|
|
}
|
|
|
|
aMap >>= 1;
|
2018-03-09 15:44:11 +03:00
|
|
|
i++;
|
2018-01-23 23:06:07 +03:00
|
|
|
}
|
|
|
|
return ChannelLayout(channels, layout.Elements());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
AudioConfig::ChannelLayout::MappingTable(const ChannelLayout& aOther,
|
2018-03-16 18:45:13 +03:00
|
|
|
nsTArray<uint8_t>* aMap) const
|
2018-01-23 23:06:07 +03:00
|
|
|
{
|
2018-03-16 18:45:13 +03:00
|
|
|
if (!IsValid() || !aOther.IsValid() || Map() != aOther.Map()) {
|
|
|
|
if (aMap) {
|
|
|
|
aMap->SetLength(0);
|
|
|
|
}
|
2018-01-23 23:06:07 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!aMap) {
|
|
|
|
return true;
|
|
|
|
}
|
2018-03-16 18:45:13 +03:00
|
|
|
aMap->SetLength(Count());
|
2018-01-23 23:06:07 +03:00
|
|
|
for (uint32_t i = 0; i < Count(); i++) {
|
|
|
|
for (uint32_t j = 0; j < Count(); j++) {
|
|
|
|
if (aOther[j] == mChannels[i]) {
|
2018-03-16 18:45:13 +03:00
|
|
|
(*aMap)[j] = i;
|
2018-01-23 23:06:07 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* AudioConfig::ChannelConfig
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* static */ const char*
|
|
|
|
AudioConfig::FormatToString(AudioConfig::SampleFormat aFormat)
|
|
|
|
{
|
|
|
|
switch (aFormat) {
|
|
|
|
case FORMAT_U8: return "unsigned 8 bit";
|
|
|
|
case FORMAT_S16: return "signed 16 bit";
|
|
|
|
case FORMAT_S24: return "signed 24 bit MSB";
|
|
|
|
case FORMAT_S24LSB: return "signed 24 bit LSB";
|
|
|
|
case FORMAT_S32: return "signed 32 bit";
|
|
|
|
case FORMAT_FLT: return "32 bit floating point";
|
|
|
|
case FORMAT_NONE: return "none";
|
|
|
|
default: return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* static */ uint32_t
|
|
|
|
AudioConfig::SampleSize(AudioConfig::SampleFormat aFormat)
|
|
|
|
{
|
|
|
|
switch (aFormat) {
|
|
|
|
case FORMAT_U8: return 1;
|
|
|
|
case FORMAT_S16: return 2;
|
|
|
|
case FORMAT_S24: MOZ_FALLTHROUGH;
|
|
|
|
case FORMAT_S24LSB: MOZ_FALLTHROUGH;
|
|
|
|
case FORMAT_S32: MOZ_FALLTHROUGH;
|
|
|
|
case FORMAT_FLT: return 4;
|
|
|
|
case FORMAT_NONE:
|
|
|
|
default: return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ uint32_t
|
|
|
|
AudioConfig::FormatToBits(AudioConfig::SampleFormat aFormat)
|
|
|
|
{
|
|
|
|
switch (aFormat) {
|
|
|
|
case FORMAT_U8: return 8;
|
|
|
|
case FORMAT_S16: return 16;
|
|
|
|
case FORMAT_S24LSB: MOZ_FALLTHROUGH;
|
|
|
|
case FORMAT_S24: return 24;
|
|
|
|
case FORMAT_S32: MOZ_FALLTHROUGH;
|
|
|
|
case FORMAT_FLT: return 32;
|
|
|
|
case FORMAT_NONE: MOZ_FALLTHROUGH;
|
|
|
|
default: return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioConfig::AudioConfig(const ChannelLayout& aChannelLayout, uint32_t aRate,
|
|
|
|
AudioConfig::SampleFormat aFormat, bool aInterleaved)
|
|
|
|
: mChannelLayout(aChannelLayout)
|
|
|
|
, mChannels(aChannelLayout.Count())
|
|
|
|
, mRate(aRate)
|
|
|
|
, mFormat(aFormat)
|
|
|
|
, mInterleaved(aInterleaved)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-03-16 18:49:09 +03:00
|
|
|
AudioConfig::AudioConfig(const ChannelLayout& aChannelLayout,
|
|
|
|
uint32_t aChannels,
|
|
|
|
uint32_t aRate,
|
|
|
|
AudioConfig::SampleFormat aFormat,
|
|
|
|
bool aInterleaved)
|
|
|
|
: mChannelLayout(aChannelLayout)
|
|
|
|
, mChannels(aChannels)
|
|
|
|
, mRate(aRate)
|
|
|
|
, mFormat(aFormat)
|
|
|
|
, mInterleaved(aInterleaved)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-01-23 23:06:07 +03:00
|
|
|
AudioConfig::AudioConfig(uint32_t aChannels,
|
|
|
|
uint32_t aRate,
|
|
|
|
AudioConfig::SampleFormat aFormat,
|
|
|
|
bool aInterleaved)
|
|
|
|
: mChannelLayout(aChannels)
|
|
|
|
, mChannels(aChannels)
|
|
|
|
, mRate(aRate)
|
|
|
|
, mFormat(aFormat)
|
|
|
|
, mInterleaved(aInterleaved)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace mozilla
|