2022-01-25 10:40:58 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "DSP/ConvertDeinterleave.h"
# include "Containers/Array.h"
# include "Containers/ArrayView.h"
# include "DSP/ChannelMap.h"
# include "DSP/MultichannelBuffer.h"
2022-06-15 01:43:38 -04:00
# include "DSP/FloatArrayMath.h"
2022-01-25 10:40:58 -05:00
namespace Audio
{
namespace ConvertDeinterleavePrivate
{
// Template class for generic deinterleave/convert operation. It uses
// mixing gains provided by `Audio::Create2DChannelMap` which are AC3
// downmixing values.
//
// Using a template class with the channel counts as template arguments
// allows the compiler to use hard coded channel counts when compiling
// the sample loop and/or for optimizations to be hand introduced using
// class template specialization.
template < int32 NumInputChannels , int32 NumOutputChannels >
struct TConvertDeinterleave : IConvertDeinterleave
{
static_assert ( NumInputChannels > 0 ) ;
static_assert ( NumOutputChannels > 0 ) ;
2022-02-08 14:13:09 -05:00
TConvertDeinterleave ( const FConvertDeinterleaveParams & InParams )
2022-01-25 10:40:58 -05:00
{
using namespace Audio ;
2022-02-08 14:13:09 -05:00
check ( InParams . NumInputChannels = = NumInputChannels ) ;
check ( InParams . NumOutputChannels = = NumOutputChannels ) ;
2022-01-25 10:40:58 -05:00
2022-02-08 14:13:09 -05:00
FChannelMapParams ChannelMapParams ;
ChannelMapParams . NumInputChannels = NumInputChannels ;
ChannelMapParams . NumOutputChannels = NumOutputChannels ;
ChannelMapParams . Order = EChannelMapOrder : : InputMajorOrder ;
ChannelMapParams . MonoUpmixMethod = InParams . MonoUpmixMethod ;
ChannelMapParams . bIsCenterChannelOnly = false ;
bool bSuccess = Create2DChannelMap ( ChannelMapParams , ChannelGains ) ;
2022-01-25 10:40:58 -05:00
check ( bSuccess ) ;
checkf ( ChannelGains . Num ( ) = = ( NumInputChannels * NumOutputChannels ) , TEXT ( " Expected channel array of %d elements. Actual num elements: %d " ) , ( NumInputChannels * NumOutputChannels ) , ChannelGains . Num ( ) ) ;
}
virtual ~ TConvertDeinterleave ( ) = default ;
const float * GetChannelGains ( int32 InOutputChannelIndex ) const
{
return & ChannelGains . GetData ( ) [ InOutputChannelIndex * NumInputChannels ] ;
}
virtual void ProcessAudio ( TArrayView < const float > InSamples , FMultichannelBuffer & OutSamples ) const override
{
checkf ( ( InSamples . Num ( ) % NumInputChannels ) = = 0 , TEXT ( " Input sample buffer contains partial audio frame. " ) ) ;
checkf ( ( InSamples . Num ( ) / NumInputChannels ) = = GetMultichannelBufferNumFrames ( OutSamples ) , TEXT ( " Audio buffer frame count mismatch. " ) ) ;
checkf ( NumOutputChannels = = OutSamples . Num ( ) , TEXT ( " Output audio buffer not initialized to expected channel count. " ) ) ;
const int32 NumFrames = GetMultichannelBufferNumFrames ( OutSamples ) ;
const float * InSampleData = InSamples . GetData ( ) ;
float * OutputChannelDataPtrs [ NumOutputChannels ] ;
for ( int32 OutChannelIndex = 0 ; OutChannelIndex < NumOutputChannels ; OutChannelIndex + + )
{
OutputChannelDataPtrs [ OutChannelIndex ] = OutSamples [ OutChannelIndex ] . GetData ( ) ;
}
for ( int32 Frame = 0 ; Frame < NumFrames ; Frame + + )
{
const float * InSampleFrameStart = & InSampleData [ Frame * NumInputChannels ] ;
for ( int32 OutChannelIndex = 0 ; OutChannelIndex < NumOutputChannels ; OutChannelIndex + + )
{
float Value = 0.f ;
const float * Gains = GetChannelGains ( OutChannelIndex ) ;
// Apply gains to interleaved input samples for a single output
// sample.
for ( int32 InChannelIndex = 0 ; InChannelIndex < NumInputChannels ; InChannelIndex + + )
{
Value + = InSampleFrameStart [ InChannelIndex ] * Gains [ InChannelIndex ] ;
}
* OutputChannelDataPtrs [ OutChannelIndex ] = Value ;
OutputChannelDataPtrs [ OutChannelIndex ] + + ;
}
}
}
private :
TArray < float > ChannelGains ;
} ;
// Specialization of TConvertDeinterleave for mono sources. This avoids
// the need for deinterleaving.
template < int32 NumOutputChannels >
struct TConvertDeinterleave < 1 , NumOutputChannels > : IConvertDeinterleave
{
static_assert ( NumOutputChannels > 0 ) ;
2022-02-08 14:13:09 -05:00
TConvertDeinterleave ( const FConvertDeinterleaveParams & InParams )
2022-01-25 10:40:58 -05:00
{
using namespace Audio ;
2022-02-08 14:13:09 -05:00
check ( InParams . NumInputChannels = = 1 ) ;
check ( InParams . NumOutputChannels = = NumOutputChannels ) ;
2022-01-25 10:40:58 -05:00
2022-02-08 14:13:09 -05:00
FChannelMapParams ChannelMapParams ;
ChannelMapParams . NumInputChannels = 1 ;
ChannelMapParams . NumOutputChannels = NumOutputChannels ;
ChannelMapParams . Order = EChannelMapOrder : : InputMajorOrder ;
ChannelMapParams . MonoUpmixMethod = InParams . MonoUpmixMethod ;
ChannelMapParams . bIsCenterChannelOnly = false ;
bool bSuccess = Create2DChannelMap ( ChannelMapParams , ChannelGains ) ;
2022-01-25 10:40:58 -05:00
check ( bSuccess ) ;
checkf ( ChannelGains . Num ( ) = = NumOutputChannels , TEXT ( " Expected channel array of %d elements. Actual num elements: %d " ) , NumOutputChannels , ChannelGains . Num ( ) ) ;
}
virtual ~ TConvertDeinterleave ( ) = default ;
virtual void ProcessAudio ( TArrayView < const float > InSamples , FMultichannelBuffer & OutSamples ) const override
{
checkf ( InSamples . Num ( ) = = GetMultichannelBufferNumFrames ( OutSamples ) , TEXT ( " Audio buffer frame count mismatch. " ) ) ;
checkf ( NumOutputChannels = = OutSamples . Num ( ) , TEXT ( " Output audio buffer not initialized to expected channel count. " ) ) ;
const int32 NumFrames = GetMultichannelBufferNumFrames ( OutSamples ) ;
for ( int32 OutChannelIndex = 0 ; OutChannelIndex < NumOutputChannels ; OutChannelIndex + + )
{
const float Gain = ChannelGains [ OutChannelIndex ] ;
if ( Gain ! = 0.f )
{
2022-04-04 13:09:43 -04:00
TArrayView < float > OutSampleView ( OutSamples [ OutChannelIndex ] . GetData ( ) , NumFrames ) ;
2022-01-25 10:40:58 -05:00
// Only need to multiply if gain is non-zero
2022-04-04 13:09:43 -04:00
Audio : : ArrayMultiplyByConstant ( InSamples , ChannelGains [ OutChannelIndex ] , OutSampleView ) ;
2022-01-25 10:40:58 -05:00
}
else
{
// If gain is zero, we can set the output channel to zero.
FMemory : : Memset ( OutSamples [ OutChannelIndex ] . GetData ( ) , 0 , NumFrames * sizeof ( float ) ) ;
}
}
}
private :
TArray < float > ChannelGains ;
} ;
// Template specialization for converting mono to mono. No need to adjust
// gains or deinterleave.
template < >
struct TConvertDeinterleave < 1 , 1 > : IConvertDeinterleave
{
2022-02-08 14:13:09 -05:00
TConvertDeinterleave ( const FConvertDeinterleaveParams & InParams )
{
check ( InParams . NumInputChannels = = 1 ) ;
check ( InParams . NumOutputChannels = = 1 ) ;
}
2022-01-25 10:40:58 -05:00
virtual ~ TConvertDeinterleave ( ) = default ;
virtual void ProcessAudio ( TArrayView < const float > InAudio , FMultichannelBuffer & OutAudio ) const override
{
checkf ( InAudio . Num ( ) = = GetMultichannelBufferNumFrames ( OutAudio ) , TEXT ( " Audio buffer frame count mismatch. " ) ) ;
checkf ( 1 = = OutAudio . Num ( ) , TEXT ( " Output audio buffer not initialized to expected channel count. " ) ) ;
const int32 NumFrames = GetMultichannelBufferNumFrames ( OutAudio ) ;
const float * InSampleData = InAudio . GetData ( ) ;
FMemory : : Memcpy ( OutAudio [ 0 ] . GetData ( ) , InSampleData , NumFrames * sizeof ( float ) ) ;
}
} ;
template < int32 NumInputChannels >
2022-02-08 14:13:09 -05:00
TUniquePtr < IConvertDeinterleave > CreateConvertDeinterleave ( const FConvertDeinterleaveParams & InParams )
2022-01-25 10:40:58 -05:00
{
// IConvertDeinterleave defines conversion operations channel counts
// between 1 and 8. This range mirrors the supported channel map channel
// counts. If the supported set of channel maps is altered, the supported
// set of defined TConvertDeinterleave classes should also be updated.
static_assert ( ChannelMapMaxNumChannels = = 8 ) ;
// Find the appropriate instantiation given the number of input and output channels.
2022-02-08 14:13:09 -05:00
switch ( InParams . NumOutputChannels )
2022-01-25 10:40:58 -05:00
{
case 1 :
2022-02-08 14:13:09 -05:00
return MakeUnique < TConvertDeinterleave < NumInputChannels , 1 > > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
case 2 :
2022-02-08 14:13:09 -05:00
return MakeUnique < TConvertDeinterleave < NumInputChannels , 2 > > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
case 3 :
2022-02-08 14:13:09 -05:00
return MakeUnique < TConvertDeinterleave < NumInputChannels , 3 > > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
case 4 :
2022-02-08 14:13:09 -05:00
return MakeUnique < TConvertDeinterleave < NumInputChannels , 4 > > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
case 5 :
2022-02-08 14:13:09 -05:00
return MakeUnique < TConvertDeinterleave < NumInputChannels , 5 > > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
case 6 :
2022-02-08 14:13:09 -05:00
return MakeUnique < TConvertDeinterleave < NumInputChannels , 6 > > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
case 7 :
2022-02-08 14:13:09 -05:00
return MakeUnique < TConvertDeinterleave < NumInputChannels , 7 > > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
case 8 :
2022-02-08 14:13:09 -05:00
return MakeUnique < TConvertDeinterleave < NumInputChannels , 8 > > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
default :
{
2022-02-08 14:13:09 -05:00
checkf ( false , TEXT ( " Unsupported output channel count: %d " ) , InParams . NumOutputChannels ) ;
2022-01-25 10:40:58 -05:00
return TUniquePtr < IConvertDeinterleave > ( nullptr ) ;
}
}
}
}
2022-02-08 14:13:09 -05:00
TUniquePtr < IConvertDeinterleave > IConvertDeinterleave : : Create ( const FConvertDeinterleaveParams & InParams )
2022-01-25 10:40:58 -05:00
{
using namespace ConvertDeinterleavePrivate ;
// IConvertDeinterleave defines conversion operations channel counts
// between 1 and 8. This range mirrors the supported channel map channel
// counts. If the supported set of channel maps is altered, the supported
// set of defined TConvertDeinterleave classes should also be updated.
static_assert ( ChannelMapMaxNumChannels = = 8 ) ;
// Find the appropriate instantiation given the number of input and output channels.
2022-02-08 14:13:09 -05:00
switch ( InParams . NumInputChannels )
2022-01-25 10:40:58 -05:00
{
case 1 :
2022-02-08 14:13:09 -05:00
return CreateConvertDeinterleave < 1 > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
case 2 :
2022-02-08 14:13:09 -05:00
return CreateConvertDeinterleave < 2 > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
case 3 :
2022-02-08 14:13:09 -05:00
return CreateConvertDeinterleave < 3 > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
case 4 :
2022-02-08 14:13:09 -05:00
return CreateConvertDeinterleave < 4 > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
case 5 :
2022-02-08 14:13:09 -05:00
return CreateConvertDeinterleave < 5 > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
case 6 :
2022-02-08 14:13:09 -05:00
return CreateConvertDeinterleave < 6 > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
case 7 :
2022-02-08 14:13:09 -05:00
return CreateConvertDeinterleave < 7 > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
case 8 :
2022-02-08 14:13:09 -05:00
return CreateConvertDeinterleave < 8 > ( InParams ) ;
2022-01-25 10:40:58 -05:00
break ;
default :
{
2022-02-08 14:13:09 -05:00
checkf ( false , TEXT ( " Unsupported input channel count: %d " ) , InParams . NumInputChannels ) ;
2022-01-25 10:40:58 -05:00
return TUniquePtr < IConvertDeinterleave > ( nullptr ) ;
}
}
}
}