// Copyright Epic Games, Inc. All Rights Reserved. #include "Utils/AudioChannelMapper.h" #include "ElectraDecodersUtils.h" namespace Electra { namespace DirectlyCopyable { // This defines the order in which FMediaAudioResampler expects the source channels. // See comments in MediaAudioResampler.cpp regarding the row order. #define CP IElectraDecoderAudioOutput::EChannelPosition static const CP _1[] = { CP::C }; static const CP _2[] = { CP::L, CP::R }; static const CP _3[] = { CP::L, CP::R, CP::C }; static const CP _4[] = { CP::L, CP::R, CP::Ls, CP::Rs }; static const CP _5[] = { CP::L, CP::R, CP::C, CP::Ls, CP::Rs }; static const CP _6[] = { CP::L, CP::R, CP::C, CP::LFE, CP::Ls, CP::Rs }; static const CP _7[] = { CP::L, CP::R, CP::Lsr, CP::LFE, CP::Rsr, CP::Ls, CP::Rs }; static const CP _8[] = { CP::L, CP::R, CP::C, CP::LFE, CP::Ls, CP::Rs, CP::Lsr, CP::Rsr }; static const CP * const ResamplerOrderMap[] = { _1, _2, _3, _4, _5, _6, _7, _8 }; #undef CP } FAudioChannelMapper::FAudioChannelMapper() { } FAudioChannelMapper::~FAudioChannelMapper() { } bool FAudioChannelMapper::Initialize(TSharedPtr InSampleBlock) { Reset(); check(InSampleBlock->GetSampleFormat() == IElectraDecoderAudioOutput::ESampleFormat::Int16 || InSampleBlock->GetSampleFormat() == IElectraDecoderAudioOutput::ESampleFormat::Float); if (InSampleBlock->GetSampleFormat() != IElectraDecoderAudioOutput::ESampleFormat::Int16 && InSampleBlock->GetSampleFormat() != IElectraDecoderAudioOutput::ESampleFormat::Float) { return false; } const int32 Stride = InSampleBlock->GetBytesPerFrame(); const int32 NumBytesPerSample = InSampleBlock->GetBytesPerSample(); BytesPerSample = NumBytesPerSample; bCanCopyDirectly = CanBeCopiedDirectly(InSampleBlock); if (bCanCopyDirectly) { // Even though we can copy the source over directly we still set up the target source layout // for later use. GetNumTargetChannels() requires this. for(int32 i=0; iGetNumChannels(); ++i) { FTargetSource& ts = TargetSources.AddDefaulted_GetRef(); ts.ChannelPosition = InSampleBlock->GetChannelPosition(i); ts.FirstOffset = BytesPerSample * i; ts.Stride = Stride; } } else { // Setup the mapping from the input channels as a mutable array. TArray InputSources; for(int32 i=0; iGetNumChannels(); ++i) { FTargetSource& ts = InputSources.AddDefaulted_GetRef(); ts.ChannelPosition = InSampleBlock->GetChannelPosition(i); ts.FirstOffset = NumBytesPerSample * i; ts.Stride = Stride; } // Now remove all the inputs the resampler does not support. for(int32 i=0; i ChannelPositions) { check(NumBytesPerSample == 2 || NumBytesPerSample == 4); Reset(); bCanCopyDirectly = CanBeCopiedDirectly(ChannelPositions); if (bCanCopyDirectly) { // Even though we can copy the source over directly we still set up the target source layout // for later use. GetNumTargetChannels() requires this. for(int32 i=0; i InputSources; for(int32 i=0; i(OutCh) = 0; OutCh = ElectraDecodersUtil::AdvancePointer(OutCh, OutStride); } } else { for(int32 i=0; i(OutCh) = *reinterpret_cast(InCh); OutCh = ElectraDecodersUtil::AdvancePointer(OutCh, OutStride); InCh = ElectraDecodersUtil::AdvancePointer(InCh, InStride); } } } else if (BytesPerSample == 4) { if (TargetSources[Ch].ChannelPosition == IElectraDecoderAudioOutput::EChannelPosition::Disabled) { for(int32 i=0; i(OutCh) = 0; OutCh = ElectraDecodersUtil::AdvancePointer(OutCh, OutStride); } } else { for(int32 i=0; i(OutCh) = *reinterpret_cast(InCh); OutCh = ElectraDecodersUtil::AdvancePointer(OutCh, OutStride); InCh = ElectraDecodersUtil::AdvancePointer(InCh, InStride); } } } } } } else { FMemory::Memzero(OutputBuffer, OutputBufferSize); } } bool FAudioChannelMapper::CanBeCopiedDirectly(TSharedPtr InSampleBlock) { int32 nc = InSampleBlock->GetNumChannels(); int32 NumUnspecifiedChannels = 0; for(int32 i=0; iGetChannelPosition(i); if (cp >= IElectraDecoderAudioOutput::EChannelPosition::Unspec0 && cp <= IElectraDecoderAudioOutput::EChannelPosition::Unspec31) { ++NumUnspecifiedChannels; } } // If all channel positions are unspecified then we can copy directly. There are no known // positions, so it could be anything really. if (NumUnspecifiedChannels == nc) { return true; } if (nc >= 1 && nc <= 8) { const IElectraDecoderAudioOutput::EChannelPosition * const ResamplerOrder = DirectlyCopyable::ResamplerOrderMap[nc - 1]; if (ResamplerOrder) { for(int32 i=0; iGetChannelPosition(i) != ResamplerOrder[i]) { return false; } } return true; } } return false; } bool FAudioChannelMapper::CanBeCopiedDirectly(TArrayView ChannelPositions) { if (ChannelPositions.Num() >= 1 && ChannelPositions.Num() <= 8) { const IElectraDecoderAudioOutput::EChannelPosition * const ResamplerOrder = DirectlyCopyable::ResamplerOrderMap[ChannelPositions.Num() - 1]; if (ResamplerOrder) { for(int32 i=0; i& InputSources) { if (InputSources.Num()) { for(int32 i=InputSources.Num(); i<=UE_ARRAY_COUNT(DirectlyCopyable::ResamplerOrderMap); ++i) { TArrayView ResamplerChannels(DirectlyCopyable::ResamplerOrderMap[i-1], i); bool bAllChannelsPresent = true; for(int32 j=0; j& InSources) { check(ResamplerChannelIndex >= 0 && ResamplerChannelIndex < UE_ARRAY_COUNT(DirectlyCopyable::ResamplerOrderMap)); if (ResamplerChannelIndex >= 0 && ResamplerChannelIndex < UE_ARRAY_COUNT(DirectlyCopyable::ResamplerOrderMap)) { TArrayView ResamplerChannels(DirectlyCopyable::ResamplerOrderMap[ResamplerChannelIndex], ResamplerChannelIndex+1); for(auto &ResamplerChannel : ResamplerChannels) { FTargetSource ts; int32 SourceIndex = InSources.IndexOfByPredicate([ResamplerChannel](const FTargetSource& In){return In.ChannelPosition == ResamplerChannel;}); if (SourceIndex != INDEX_NONE) { TargetSources.Emplace(InSources[SourceIndex]); } else { TargetSources.AddDefaulted(); } } } else { // Add at least one empty entry to keep everything happy. TargetSources.AddDefaulted(); } } } // namespace Electra