Files
UnrealEngine/Engine/Source/Runtime/SignalProcessing/Private/ChannelMap.cpp
2025-05-18 13:04:45 +08:00

468 lines
19 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DSP/ChannelMap.h"
#include "AudioDefines.h"
namespace Audio
{
namespace ChannelMapPrivate
{
// Tables based on Ac-3 down-mixing
// Rows: output speaker configuration
// Cols: input channels
static constexpr float ToMonoMatrix[ChannelMapMaxNumChannels * 1] =
{
// FrontLeft FrontRight Center LowFrequency SideLeft SideRight BackLeft BackRight
0.707f, 0.707f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.5f, // FrontLeft
};
static constexpr float VorbisToMonoMatrix[ChannelMapVorbisNumChannels * 1] =
{
// FrontLeft Center FrontRight SideLeft SideRight LowFrequency
0.707f, 1.0f, 0.707f, 0.5f, 0.5f, 0.0f, // FrontLeft
};
static constexpr float ToStereoMatrix[ChannelMapMaxNumChannels * 2] =
{
// FrontLeft FrontRight Center LowFrequency SideLeft SideRight BackLeft BackRight
1.0f, 0.0f, 0.707f, 0.0f, 0.707f, 0.0f, 0.707f, 0.0f, // FrontLeft
0.0f, 1.0f, 0.707f, 0.0f, 0.0f, 0.707f, 0.0f, 0.707f, // FrontRight
};
static constexpr float VorbisToStereoMatrix[ChannelMapVorbisNumChannels * 2] =
{
// FrontLeft Center FrontRight SideLeft SideRight LowFrequency
1.0f, 0.707f, 0.0f, 0.707f, 0.0f, 0.0f, // FrontLeft
0.0f, 0.707f, 1.0f, 0.0f, 0.707f, 0.0f, // FrontRight
};
static constexpr float ToTriMatrix[ChannelMapMaxNumChannels * 3] =
{
// FrontLeft FrontRight Center LowFrequency SideLeft SideRight BackLeft BackRight
1.0f, 0.0f, 0.0f, 0.0f, 0.707f, 0.0f, 0.707f, 0.0f, // FrontLeft
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.707f, 0.0f, 0.707f, // FrontRight
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // Center
};
static constexpr float VorbisToTriMatrix[ChannelMapVorbisNumChannels * 3] =
{
// FrontLeft Center FrontRight SideLeft SideRight LowFrequency
1.0f, 0.0f, 0.0f, 0.707f, 0.0f, 0.0f, // FrontLeft
0.0f, 0.0f, 1.0f, 0.0f, 0.707f, 0.0f, // FrontRight
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // Center
};
static constexpr float ToQuadMatrix[ChannelMapMaxNumChannels * 4] =
{
// FrontLeft FrontRight Center LowFrequency SideLeft SideRight BackLeft BackRight
1.0f, 0.0f, 0.707f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontLeft
0.0f, 1.0f, 0.707f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontRight
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // SideLeft
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // SideRight
};
static constexpr float VorbisToQuadMatrix[ChannelMapVorbisNumChannels * 4] =
{
// FrontLeft Center FrontRight SideLeft SideRight LowFrequency
1.0f, 0.707f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontLeft
0.0f, 0.707f, 1.0f, 0.0f, 0.0f, 0.0f, // FrontRight
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // SideLeft
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // SideRight
};
static constexpr float To5Matrix[ChannelMapMaxNumChannels * 5] =
{
// FrontLeft FrontRight Center LowFrequency SideLeft SideRight BackLeft BackRight
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontLeft
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontRight
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // Center
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // SideLeft
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // SideRight
};
static constexpr float VorbisTo5Matrix[ChannelMapVorbisNumChannels * 5] =
{
// FrontLeft Center FrontRight SideLeft SideRight LowFrequency
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontLeft
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // FrontRight
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // Center
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // SideLeft
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // SideRight
};
static constexpr float To5Point1Matrix[ChannelMapMaxNumChannels * 6] =
{
// FrontLeft FrontRight Center LowFrequency SideLeft SideRight BackLeft BackRight
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontLeft
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontRight
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // Center
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LowFrequency
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // SideLeft
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // SideRight
};
static constexpr float VorbisTo5Point1Matrix[ChannelMapVorbisNumChannels * 6] =
{
// FrontLeft Center FrontRight SideLeft SideRight LowFrequency
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontLeft
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // FrontRight
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // Center
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, // LowFrequency
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // SideLeft
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // SideRight
};
static constexpr float ToHexMatrix[ChannelMapMaxNumChannels * 7] =
{
// FrontLeft FrontRight Center LowFrequency SideLeft SideRight BackLeft BackRight
1.0f, 0.0f, 0.707f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontLeft
0.0f, 1.0f, 0.707f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontRight
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // BackLeft
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LFE
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, // BackRight
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // SideLeft
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // SideRight
};
static constexpr float VorbisToHexMatrix[ChannelMapVorbisNumChannels * 7] =
{
// FrontLeft Center FrontRight SideLeft SideRight LowFrequency
1.0f, 0.707f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontLeft
0.0f, 0.707f, 1.0f, 0.0f, 0.0f, 0.0f, // FrontRight
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // BackLeft
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, // LFE
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // BackRight
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // SideLeft
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // SideRight
};
#if PLATFORM_MICROSOFT
static constexpr float To7Point1Matrix[ChannelMapMaxNumChannels * 8] =
{
// FrontLeft FrontRight Center LowFrequency SideLeft SideRight BackLeft BackRight
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontLeft
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontRight
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontCenter
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LowFrequency
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // SideLeft
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // SideRight
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // BackLeft
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, // BackRight
};
#else //PLATFORM_MICROSOFT
// NOTE: the BackLeft/BackRight and SideLeft/SideRight are reversed than they should be since our 7.1 importer code has it backward
static constexpr float To7Point1Matrix[ChannelMapMaxNumChannels * 8] =
{
// FrontLeft FrontRight Center LowFrequency SideLeft SideRight BackLeft BackRight
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontLeft
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontRight
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontCenter
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // LowFrequency
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // BackLeft
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, // BackRight
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // SideLeft
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // SideRight
};
#endif//PLATFORM_MICROSOFT
static constexpr float VorbisTo7Point1Matrix[ChannelMapVorbisNumChannels * 8] =
{
// FrontLeft Center FrontRight SideLeft SideRight LowFrequency
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontLeft
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // FrontRight
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // FrontCenter
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, // LowFrequency
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // SideLeft
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // SideRight
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // BackLeft
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // BackRight
};
static float const * const OutputChannelMaps[ChannelMapMaxNumChannels] =
{
ToMonoMatrix,
ToStereoMatrix,
ToTriMatrix, // Experimental
ToQuadMatrix,
To5Matrix, // Experimental
To5Point1Matrix,
ToHexMatrix, // Experimental
To7Point1Matrix
};
// 5.1 Vorbis files have a different channel order than normal
static float const * const VorbisChannelMaps[ChannelMapMaxNumChannels] =
{
VorbisToMonoMatrix,
VorbisToStereoMatrix,
VorbisToTriMatrix, // Experimental
VorbisToQuadMatrix,
VorbisTo5Matrix, // Experimental
VorbisTo5Point1Matrix,
VorbisToHexMatrix, // Experimental
VorbisTo7Point1Matrix
};
// Create a channel map for a generic multichannel input to any supported
// multichannel output.
//
// @param InNumInputChannels - The number of channels in the input content.
// @param InNumOutputChannels - The number of channels in the output audio.
// @param OutChannelMap - An array of channel gains in OutputMajorOrder (see EChannelMapOrder for details on format).
//
// @return true on success, false on failure.
bool Create2DGenericInputChannelMap(int32 InNumInputChannels, int32 InNumOutputChannels, TArray<float>& OutChannelMap)
{
const int32 OutputChannelMapIndex = InNumOutputChannels - 1;
check(OutputChannelMapIndex >= 0);
check(OutputChannelMapIndex < ChannelMapMaxNumChannels);
const float* RESTRICT Matrix = OutputChannelMaps[OutputChannelMapIndex];
check(Matrix != nullptr);
for (int32 InputChannel = 0; InputChannel < InNumInputChannels; ++InputChannel)
{
for (int32 OutputChannel = 0; OutputChannel < InNumOutputChannels; ++OutputChannel)
{
const int32 Index = OutputChannel * ChannelMapMaxNumChannels + InputChannel;
OutChannelMap.Add(Matrix[Index]);
}
}
return true;
}
// Create a channel map for a mono input to any supported multichannel output.
//
// @param InNumOutputChannels - The number of channels in the output audio.
// @param InMonoChannelUpmixMethod - Method for upmixing mono to front-left and front-right (not used if bInIsCenterChannelOnly is true and output format has center channel, or if output is Mono or Quad).
// @param bInIsCenterChannelOnly - If true, and if the output channel count corresponds to format containing a center channel, the mono input will be mapped directly to the center channel.
// @param OutChannelMap - An array of channel gains in OutputMajorOrder (see EChannelMapOrder for details on format).
//
// @return true on success, false on failure.
bool Create2DMonoInputChannelMap(int32 InNumOutputChannels, EChannelMapMonoUpmixMethod InMonoChannelUpmixMethod, bool bInIsCenterChannelOnly, TArray<float>& OutChannelMap)
{
const int32 OutputChannelMapIndex = InNumOutputChannels - 1;
check(OutputChannelMapIndex >= 0);
check(OutputChannelMapIndex < ChannelMapMaxNumChannels);
// Mono-in mono-out channel map
if (InNumOutputChannels == 1)
{
OutChannelMap.Add(1.0f);
}
else
{
// If we have more than stereo output (means we have center channel, which is always the 3rd index)
// Then we need to only apply 1.0 to the center channel, 0.0 for everything else
if ((InNumOutputChannels == 3 || InNumOutputChannels > 4) && bInIsCenterChannelOnly)
{
for (int32 OutputChannel = 0; OutputChannel < InNumOutputChannels; ++OutputChannel)
{
// Center channel is always 3rd index
if (OutputChannel == 2)
{
OutChannelMap.Add(1.0f);
}
else
{
OutChannelMap.Add(0.0f);
}
}
}
else
{
// Mapping out to more than 2 channels, mono inputs should be equally spread to left and right
switch (InMonoChannelUpmixMethod)
{
case EChannelMapMonoUpmixMethod::Linear:
OutChannelMap.Add(Audio::MonoUpmixLinear);
OutChannelMap.Add(Audio::MonoUpmixLinear);
break;
case EChannelMapMonoUpmixMethod::EqualPower:
OutChannelMap.Add(Audio::MonoUpmixEqualPower);
OutChannelMap.Add(Audio::MonoUpmixEqualPower);
break;
case EChannelMapMonoUpmixMethod::FullVolume:
OutChannelMap.Add(Audio::MonoUpmixFullVolume);
OutChannelMap.Add(Audio::MonoUpmixFullVolume);
break;
}
const float* RESTRICT Matrix = OutputChannelMaps[OutputChannelMapIndex];
check(Matrix != nullptr);
for (int32 OutputChannel = 2; OutputChannel < InNumOutputChannels; ++OutputChannel)
{
const int32 Index = OutputChannel * ChannelMapMaxNumChannels;
OutChannelMap.Add(Matrix[Index]);
}
}
}
return true;
}
// Create a channel map for a quad input to any supported multichannel output.
//
// @param InNumOutputChannels - The number of channels in the output audio.
// @param OutChannelMap - An array of channel gains in OutputMajorOrder (see EChannelMapOrder for details on format).
//
// @return true on success, false on failure.
bool Create2DQuadInputChannelMap(int32 InNumOutputChannels, TArray<float>& OutChannelMap)
{
const int32 OutputChannelMapIndex = InNumOutputChannels - 1;
check(OutputChannelMapIndex >= 0);
check(OutputChannelMapIndex < ChannelMapMaxNumChannels);
const float* RESTRICT Matrix = OutputChannelMaps[OutputChannelMapIndex];
check(Matrix != nullptr);
// Quad has a special-case to map input channels 0 1 2 3 to 0 1 4 5:
const int32 FrontLeftChannel = 0;
for (int32 OutputChannel = 0; OutputChannel < InNumOutputChannels; ++OutputChannel)
{
const int32 Index = OutputChannel * ChannelMapMaxNumChannels + FrontLeftChannel;
OutChannelMap.Add(Matrix[Index]);
}
const int32 FrontRightChannel = 1;
for (int32 OutputChannel = 0; OutputChannel < InNumOutputChannels; ++OutputChannel)
{
const int32 Index = OutputChannel * ChannelMapMaxNumChannels + FrontRightChannel;
OutChannelMap.Add(Matrix[Index]);
}
const int32 SurroundLeftChannel = 4;
for (int32 OutputChannel = 0; OutputChannel < InNumOutputChannels; ++OutputChannel)
{
const int32 Index = OutputChannel * ChannelMapMaxNumChannels + SurroundLeftChannel;
OutChannelMap.Add(Matrix[Index]);
}
const int32 SurroundRightChannel = 5;
for (int32 OutputChannel = 0; OutputChannel < InNumOutputChannels; ++OutputChannel)
{
const int32 Index = OutputChannel * ChannelMapMaxNumChannels + SurroundRightChannel;
OutChannelMap.Add(Matrix[Index]);
}
return true;
}
// Returns true if channel counts are supported by the hardcoded channel maps.
bool EnsureValidChannelCounts(int32 InNumInputChannels, int32 InNumOutputChannels)
{
const bool bIsValidNumOutputChannels = (InNumOutputChannels > 0) && (InNumOutputChannels <= ChannelMapMaxNumChannels);
if (ensureMsgf(bIsValidNumOutputChannels, TEXT("Invalid number of output channels: %d"), InNumOutputChannels))
{
const bool bIsValidNumInputChannels = (InNumInputChannels > 0) && (InNumInputChannels <= ChannelMapMaxNumChannels);
if (ensureMsgf(bIsValidNumInputChannels, TEXT("Invalid number of input channels: %d"), InNumInputChannels))
{
return true;
}
}
return false;
}
// Transpose a matrix from OutputMajorOrder to InputMajorOrder. See EChannelMapOrder
// for details on the formats.
void TransposeOutputMajorOrderToInputMajorOrder(int32 InNumInputChannels, int32 InNumOutputChannels, TArray<float>& OutChannelMap)
{
check(OutChannelMap.Num() == (InNumInputChannels * InNumOutputChannels));
// Transpose matrix to get input major order.
TArray<float> CopyOfMap = OutChannelMap;
for (int32 InputChannelIndex = 0; InputChannelIndex < InNumInputChannels; InputChannelIndex++)
{
int32 OriginalIndex = InputChannelIndex * InNumOutputChannels;
int32 NewIndex = InputChannelIndex;
for (int32 OutputChannelIndex = 0; OutputChannelIndex < InNumOutputChannels; OutputChannelIndex++)
{
OutChannelMap[NewIndex] = CopyOfMap[OriginalIndex];
OriginalIndex++;
NewIndex += InNumInputChannels;
}
}
}
}
bool Create2DChannelMap(const FChannelMapParams& InParams, TArray<float>& OutChannelMap)
{
using namespace ChannelMapPrivate;
if (!EnsureValidChannelCounts(InParams.NumInputChannels, InParams.NumOutputChannels))
{
return false;
}
bool bSuccess = false;
// Mono and quad have special considerations for upmixing.
if (InParams.NumInputChannels == 1)
{
bSuccess = Create2DMonoInputChannelMap(InParams.NumOutputChannels, InParams.MonoUpmixMethod, InParams.bIsCenterChannelOnly, OutChannelMap);
}
else if (InParams.NumInputChannels == 4)
{
bSuccess = Create2DQuadInputChannelMap(InParams.NumOutputChannels, OutChannelMap);
}
else
{
bSuccess = Create2DGenericInputChannelMap(InParams.NumInputChannels, InParams.NumOutputChannels, OutChannelMap);
}
// By default, functions create channel map in "OutputMajorOrder". Check
// if it needs to be transposed.
if (bSuccess && (InParams.Order == EChannelMapOrder::InputMajorOrder))
{
TransposeOutputMajorOrderToInputMajorOrder(InParams.NumInputChannels, InParams.NumOutputChannels, OutChannelMap);
}
return bSuccess;
}
bool CreateVorbis2DChannelMap(int32 InNumOutputChannels, EChannelMapOrder InOrder, TArray<float>& OutVorbisChannelMap)
{
using namespace ChannelMapPrivate;
if (!ensureMsgf((InNumOutputChannels > 0) && (InNumOutputChannels <= ChannelMapMaxNumChannels), TEXT("Invalid number of output channels: %d"), InNumOutputChannels))
{
return false;
}
// Get the matrix for the channel map index
const int32 OutputChannelMapIndex = InNumOutputChannels - 1;
const float* VorbisMatrix = VorbisChannelMaps[OutputChannelMapIndex];
check(nullptr != VorbisMatrix);
// Build it by looping over the vorbis ordered 5.1 input channels
for (int32 InputChannel = 0; InputChannel < ChannelMapVorbisNumChannels; ++InputChannel)
{
for (int32 OutputChannel = 0; OutputChannel < InNumOutputChannels; ++OutputChannel)
{
const int32 Index = OutputChannel * ChannelMapVorbisNumChannels + InputChannel;
OutVorbisChannelMap.Add(VorbisMatrix[Index]);
}
}
// By default, channel map is in "OutputMajorOrder". Check if it needs to be transposed.
if (InOrder == EChannelMapOrder::InputMajorOrder)
{
TransposeOutputMajorOrderToInputMajorOrder(ChannelMapVorbisNumChannels, InNumOutputChannels, OutVorbisChannelMap);
}
return true;
}
}