// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" namespace Audio { /** TDeinterleaveView * * TDeinterleaveView provides iterators to access deinterleaved arrays from an interleaved array. */ template class TDeinterleaveView { friend class TChannelIterator; public: /** TChannel * * TChannel provides a contiguous copy of an single channel from an interleaved array. */ template class TChannel { public: TArray& Values; const int32 ChannelIndex; TChannel(TArray& InValues, const int32 InChannelIndex) : Values(InValues) , ChannelIndex(InChannelIndex) {} }; /** TChannelIterator * * TChannelIterator iterates over channels in an interleaved array and providing * contiguous arrays of a single channel. */ template class TChannelIterator { TDeinterleaveView DeinterleaveView; TArray& ArrayToFill; int32 ChannelIndex; public: /** Denotes the end of a channel iterator */ static const int32 ChannelIndexEnd = INDEX_NONE; /** TChannelIterator * * TChannelIterator allows iteration over interleaved arrays and provides contiguous arrays of single channels. * * InDeinterleaveView is the parent view provides the source interleaved array for this iterator. * InArrayToFill is the array which will be populated with contiguous elements for a channel. * InChannelIndex is the channel index which this iterator will point to. */ TChannelIterator(TDeinterleaveView InDeinterleaveView, TArray& InArrayToFill, int32 InChannelIndex) : DeinterleaveView(InDeinterleaveView) , ArrayToFill(InArrayToFill) , ChannelIndex(InChannelIndex) { if (ChannelIndex >= DeinterleaveView.NumChannels) { // Channel index is more than number of total channels ChannelIndex = ChannelIndexEnd; } } /** Increment the iterator forward by one channel */ TChannelIterator& operator++() { if (ChannelIndex != ChannelIndexEnd) { ChannelIndex++; if (ChannelIndex >= DeinterleaveView.NumChannels) { ChannelIndex = ChannelIndexEnd; } } return *this; } /** Check equality between iterators */ bool operator!=(const TChannelIterator& Other) const { return Other.ChannelIndex != ChannelIndex; } /** Get the current channel index */ int32 GetChannelIndex() const { return ChannelIndex; } /** Dereference the iterator, returns a TChannel object */ TChannel operator*() { if (ChannelIndex == ChannelIndexEnd) { ArrayToFill.Reset(DeinterleaveView.NumElementsPerChannel); } else { // prepare the array ArrayToFill.Reset(DeinterleaveView.NumElementsPerChannel); if (DeinterleaveView.NumElementsPerChannel > 0) { ArrayToFill.AddUninitialized(DeinterleaveView.NumElementsPerChannel); } // Fill array with deinterleave data T* ArrayToFillData = ArrayToFill.GetData(); const T* InterleavedData = DeinterleaveView.InterleavedArray.GetData(); int32 InterleavedPos = ChannelIndex; for (int32 OutPos = 0; OutPos < DeinterleaveView.NumElementsPerChannel; OutPos++, InterleavedPos += DeinterleaveView.NumChannels) { ArrayToFillData[OutPos] = InterleavedData[InterleavedPos]; } } // Create and return channel return TChannel(ArrayToFill, ChannelIndex); } }; /** TDeinterleave constructor. * * InInterleavedArray is the interleaved array to be deinterleaved. * InNumChannels is the number of channels in the deinterleaved array. */ TDeinterleaveView(TArrayView InInterleavedArray, int32 InNumChannels) : InterleavedArray(InInterleavedArray) , NumChannels(InNumChannels) , NumElementsPerChannel(0) { check(InterleavedArray.Num() % NumChannels == 0); NumElementsPerChannel = InterleavedArray.Num() / NumChannels; } /** Return an STL iterator to the first channel. It fills the given array with channel elements */ template TChannelIterator begin(TArray& InArrayToFill) const { return TChannelIterator(*this, InArrayToFill, 0); } /** Return an STL iterator to the end. */ template TChannelIterator end(TArray& InArrayToFill) const { return TChannelIterator(*this, InArrayToFill, TChannelIterator::ChannelIndexEnd); } private: // view to interleaved data TArrayView InterleavedArray; int32 NumChannels; int32 NumElementsPerChannel; }; /** TAutoDeinterleaveView * * TAutoDeinterlaveView provides a STL like iterators which exposes contiguous channel arrays from interleaved arrays. As opposed to TDeinterleaveView, this class can be used in range based for loops, but only one iterator is valid at a time since they all share the same InArrayToFill. * * Example: * * TArray ArrayToFill; * for (auto Channel : TAudoDeineterleaveView(InterleavedArray, ArrayToFill, 2)) * { * DoSomethingWithAudio(Channel.Values, Channel.ChannelIndex); * } */ template class TAutoDeinterleaveView : public TDeinterleaveView { TArray& ArrayToFill; typedef typename TDeinterleaveView::template TChannelIterator TAutoChannelIterator; public: /** TAutoDeinterleaveView Constructor. * * InInterleavedArray is the interleaved array to be deinterleaved. * InArrayToFill is the array which will be populated with contiguous elements for a channel. * InNumChannels is the number of channels in the deinterleaved array. */ TAutoDeinterleaveView(TArrayView InInterleavedArray, TArray& InArrayToFill, int32 InNumChannels) : TDeinterleaveView(InInterleavedArray, InNumChannels) , ArrayToFill(InArrayToFill) {} /** Return an STL iterator to the first channel. */ TAutoChannelIterator begin() { return TDeinterleaveView::template begin(ArrayToFill); } /** Return an STL iterator to the end. */ TAutoChannelIterator end() { return TDeinterleaveView::template end(ArrayToFill); } }; }