/* 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/. */
#ifndef MOZILLA_AUDIOCHANNELFORMAT_H_
#define MOZILLA_AUDIOCHANNELFORMAT_H_
#include <stdint.h>
#include "AudioSampleFormat.h"
#include "nsTArray.h"
#include "nsTArrayForwardDeclare.h"
namespace mozilla {
/*
* This file provides utilities for upmixing and downmixing channels .
*
* The channel layouts , upmixing and downmixing are consistent with the
* Web Audio spec .
*
* Channel layouts for up to 6 channels :
* mono { M }
* stereo { L , R }
* { L , R , C }
* quad { L , R , SL , SR }
* { L , R , C , SL , SR }
* 5 . 1 { L , R , C , LFE , SL , SR }
*
* Only 1 , 2 , 4 and 6 are currently defined in Web Audio .
*/
enum {
SURROUND_L,
SURROUND_R,
SURROUND_C,
SURROUND_LFE,
SURROUND_SL,
SURROUND_SR
};
const uint32_t CUSTOM_CHANNEL_LAYOUTS =
6 ;
// This is defined by some Windows SDK header.
#undef IGNORE
const int IGNORE = CUSTOM_CHANNEL_LAYOUTS;
const float IGNORE_F =
0 .
0 f;
const int gMixingMatrixIndexByChannels[CUSTOM_CHANNEL_LAYOUTS -
1 ] = {
0 ,
5 ,
9 ,
12 ,
14 };
/**
* Return a channel count whose channel layout includes all the channels from
* aChannels1 and aChannels2 .
*/
uint32_t GetAudioChannelsSuperset(uint32_t aChannels1, uint32_t aChannels2);
/**
* DownMixMatrix represents a conversion matrix efficiently by exploiting the
* fact that each input channel contributes to at most one output channel ,
* except possibly for the C input channel in layouts that have one . Also ,
* every input channel is multiplied by the same coefficient for every output
* channel it contributes to .
*/
const float SQRT_ONE_HALF =
0 .
7071067811865476 f;
struct DownMixMatrix {
// Every input channel c is copied to output channel mInputDestination[c]
// after multiplying by mInputCoefficient[c].
uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS];
// If not IGNORE, then the C channel is copied to this output channel after
// multiplying by its coefficient.
uint8_t mCExtraDestination;
float mInputCoefficient[CUSTOM_CHANNEL_LAYOUTS];
};
static const DownMixMatrix gDownMixMatrices[CUSTOM_CHANNEL_LAYOUTS *
(CUSTOM_CHANNEL_LAYOUTS -
1 ) /
2 ] = {
// Downmixes to mono
{{
0 ,
0 }, IGNORE, {
0 .
5 f,
0 .
5 f}},
{{
0 , IGNORE, IGNORE}, IGNORE, {
1 .
0 f, IGNORE_F, IGNORE_F}},
{{
0 ,
0 ,
0 ,
0 }, IGNORE, {
0 .
25 f,
0 .
25 f,
0 .
25 f,
0 .
25 f}},
{{
0 , IGNORE, IGNORE, IGNORE, IGNORE},
IGNORE,
{
1 .
0 f, IGNORE_F, IGNORE_F, IGNORE_F, IGNORE_F}},
{{
0 ,
0 ,
0 , IGNORE,
0 ,
0 },
IGNORE,
{SQRT_ONE_HALF, SQRT_ONE_HALF,
1 .
0 f, IGNORE_F,
0 .
5 f,
0 .
5 f}},
// Downmixes to stereo
{{
0 ,
1 , IGNORE}, IGNORE, {
1 .
0 f,
1 .
0 f, IGNORE_F}},
{{
0 ,
1 ,
0 ,
1 }, IGNORE, {
0 .
5 f,
0 .
5 f,
0 .
5 f,
0 .
5 f}},
{{
0 ,
1 , IGNORE, IGNORE, IGNORE},
IGNORE,
{
1 .
0 f,
1 .
0 f, IGNORE_F, IGNORE_F, IGNORE_F}},
{{
0 ,
1 ,
0 , IGNORE,
0 ,
1 },
1 ,
{
1 .
0 f,
1 .
0 f, SQRT_ONE_HALF, IGNORE_F, SQRT_ONE_HALF, SQRT_ONE_HALF}},
// Downmixes to 3-channel
{{
0 ,
1 ,
2 , IGNORE}, IGNORE, {
1 .
0 f,
1 .
0 f,
1 .
0 f, IGNORE_F}},
{{
0 ,
1 ,
2 , IGNORE, IGNORE}, IGNORE, {
1 .
0 f,
1 .
0 f,
1 .
0 f, IGNORE_F, IGNORE_F}},
{{
0 ,
1 ,
2 , IGNORE, IGNORE, IGNORE},
IGNORE,
{
1 .
0 f,
1 .
0 f,
1 .
0 f, IGNORE_F, IGNORE_F, IGNORE_F}},
// Downmixes to quad
{{
0 ,
1 ,
2 ,
3 , IGNORE}, IGNORE, {
1 .
0 f,
1 .
0 f,
1 .
0 f,
1 .
0 f, IGNORE_F}},
{{
0 ,
1 ,
0 , IGNORE,
2 ,
3 },
1 ,
{
1 .
0 f,
1 .
0 f, SQRT_ONE_HALF, IGNORE_F,
1 .
0 f,
1 .
0 f}},
// Downmixes to 5-channel
{{
0 ,
1 ,
2 ,
3 ,
4 , IGNORE},
IGNORE,
{
1 .
0 f,
1 .
0 f,
1 .
0 f,
1 .
0 f,
1 .
0 f, IGNORE_F}}};
/**
* Given an array of input channels , downmix to aOutputChannelCount , and copy
* the results to the channel buffers in aOutputChannels . Don ' t call this with
* input count < = output count .
* Each of aOutputChannels must be non - null .
* aInputChannels elements may be null .
*/
template <typename SrcT, typename DstT>
void AudioChannelsDownMix(Span<
const SrcT*
const > aInputChannels,
Span<DstT*
const > aOutputChannels,
uint32_t aDuration) {
uint32_t inputChannelCount = aInputChannels.Length();
uint32_t outputChannelCount = aOutputChannels.Length();
NS_ASSERTION(inputChannelCount > outputChannelCount,
"Nothing to do" );
if (inputChannelCount >
6 ) {
// Just drop the unknown channels.
for (uint32_t o =
0 ; o < outputChannelCount; ++o) {
if (aInputChannels[o]) {
ConvertAudioSamples(aInputChannels[o], aOutputChannels[o], aDuration);
}
else {
std::fill_n(aOutputChannels[o], aDuration, static_cast<DstT>(
0 ));
}
}
return ;
}
// Ignore unknown channels, they're just dropped.
inputChannelCount = std::min<uint32_t>(
6 , inputChannelCount);
const DownMixMatrix& m =
gDownMixMatrices[gMixingMatrixIndexByChannels[outputChannelCount -
1 ] +
inputChannelCount - outputChannelCount -
1 ];
// This is slow, but general. We can define custom code for special
// cases later.
for (DstT* outChannel : aOutputChannels) {
std::fill_n(outChannel, aDuration, static_cast<DstT>(
0 ));
}
for (uint32_t c =
0 ; c < inputChannelCount; ++c) {
uint32_t dstIndex = m.mInputDestination[c];
if (dstIndex == IGNORE || !aInputChannels[c]) {
continue ;
}
AddAudioSamplesWithScale(aInputChannels[c], aOutputChannels[dstIndex],
aDuration, m.mInputCoefficient[c]);
}
// Utilize the fact that in every layout, C is the only channel that may
// contribute to more than one output channel.
uint32_t dstIndex = m.mCExtraDestination;
if (dstIndex != IGNORE && aInputChannels[SURROUND_C]) {
AddAudioSamplesWithScale(aInputChannels[SURROUND_C],
aOutputChannels[dstIndex], aDuration,
m.mInputCoefficient[SURROUND_C]);
}
}
/**
* UpMixMatrix represents a conversion matrix by exploiting the fact that
* each output channel comes from at most one input channel .
*/
struct UpMixMatrix {
uint8_t mInputDestination[CUSTOM_CHANNEL_LAYOUTS];
};
static const UpMixMatrix gUpMixMatrices[CUSTOM_CHANNEL_LAYOUTS *
(CUSTOM_CHANNEL_LAYOUTS -
1 ) /
2 ] = {
// Upmixes from mono
{{
0 ,
0 }},
{{
0 , IGNORE, IGNORE}},
{{
0 ,
0 , IGNORE, IGNORE}},
{{
0 , IGNORE, IGNORE, IGNORE, IGNORE}},
{{IGNORE, IGNORE,
0 , IGNORE, IGNORE, IGNORE}},
// Upmixes from stereo
{{
0 ,
1 , IGNORE}},
{{
0 ,
1 , IGNORE, IGNORE}},
{{
0 ,
1 , IGNORE, IGNORE, IGNORE}},
{{
0 ,
1 , IGNORE, IGNORE, IGNORE, IGNORE}},
// Upmixes from 3-channel
{{
0 ,
1 ,
2 , IGNORE}},
{{
0 ,
1 ,
2 , IGNORE, IGNORE}},
{{
0 ,
1 ,
2 , IGNORE, IGNORE, IGNORE}},
// Upmixes from quad
{{
0 ,
1 ,
2 ,
3 , IGNORE}},
{{
0 ,
1 , IGNORE, IGNORE,
2 ,
3 }},
// Upmixes from 5-channel
{{
0 ,
1 ,
2 ,
3 ,
4 , IGNORE}}};
/**
* Given an array of input channel data , and an output channel count ,
* replaces the array with an array of upmixed channels .
* This shuffles the array and may set some channel buffers to aZeroChannel .
* Don ' t call this with input count > = output count .
* This may return * more * channels than requested . In that case , downmixing
* is required to to get to aOutputChannelCount . ( This is how we handle
* odd cases like 3 - > 4 upmixing . )
* If aChannelArray . Length ( ) was the input to one of a series of
* GetAudioChannelsSuperset calls resulting in aOutputChannelCount ,
* no downmixing will be required .
*/
template <typename T>
void AudioChannelsUpMix(nsTArray<
const T*>* aChannelArray,
uint32_t aOutputChannelCount,
const T* aZeroChannel) {
uint32_t inputChannelCount = aChannelArray->Length();
uint32_t outputChannelCount =
GetAudioChannelsSuperset(aOutputChannelCount, inputChannelCount);
NS_ASSERTION(outputChannelCount > inputChannelCount,
"No up-mix needed" );
MOZ_ASSERT(inputChannelCount >
0 ,
"Bad number of channels" );
MOZ_ASSERT(outputChannelCount >
0 ,
"Bad number of channels" );
aChannelArray->SetLength(outputChannelCount);
if (inputChannelCount < CUSTOM_CHANNEL_LAYOUTS &&
outputChannelCount <= CUSTOM_CHANNEL_LAYOUTS) {
const UpMixMatrix& m =
gUpMixMatrices[gMixingMatrixIndexByChannels[inputChannelCount -
1 ] +
outputChannelCount - inputChannelCount -
1 ];
const T* outputChannels[CUSTOM_CHANNEL_LAYOUTS];
for (uint32_t i =
0 ; i < outputChannelCount; ++i) {
uint8_t channelIndex = m.mInputDestination[i];
if (channelIndex == IGNORE) {
outputChannels[i] = aZeroChannel;
}
else {
outputChannels[i] = aChannelArray->ElementAt(channelIndex);
}
}
for (uint32_t i =
0 ; i < outputChannelCount; ++i) {
aChannelArray->ElementAt(i) = outputChannels[i];
}
return ;
}
for (uint32_t i = inputChannelCount; i < outputChannelCount; ++i) {
aChannelArray->ElementAt(i) = aZeroChannel;
}
}
}
// namespace mozilla
#endif /* MOZILLA_AUDIOCHANNELFORMAT_H_ */
Messung V0.5 in Prozent C=89 H=84 G=86
¤ Dauer der Verarbeitung: 0.6 Sekunden
¤
*© Formatika GbR, Deutschland