diff --git a/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp b/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp index 42c872f911..bd4a2d7441 100644 --- a/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp +++ b/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp @@ -434,32 +434,14 @@ void AudioDataConverters::convertFormatToFloat (DataFormat sourceFormat, const v //============================================================================== void AudioDataConverters::interleaveSamples (const float** source, float* dest, int numSamples, int numChannels) { - for (int chan = 0; chan < numChannels; ++chan) - { - auto i = chan; - auto src = source [chan]; - - for (int j = 0; j < numSamples; ++j) - { - dest [i] = src [j]; - i += numChannels; - } - } + AudioData::interleaveSamples (source, numChannels, dest, numChannels, numSamples); } void AudioDataConverters::deinterleaveSamples (const float* source, float** dest, int numSamples, int numChannels) { - for (int chan = 0; chan < numChannels; ++chan) - { - auto i = chan; - auto dst = dest [chan]; - - for (int j = 0; j < numSamples; ++j) - { - dst [j] = source [i]; - i += numChannels; - } - } + AudioData::deinterleaveSamples (source, numChannels, dest, numChannels, numSamples); } @@ -591,6 +573,50 @@ public: Test1 ::test (*this, r); beginTest ("Round-trip conversion: Float32"); Test1 ::test (*this, r); + + beginTest ("Interleaving"); + { + constexpr auto numChannels = 4; + constexpr auto numSamples = 512; + + AudioBuffer sourceBuffer { numChannels, numSamples }, + destBuffer { 1, numChannels * numSamples }; + + for (int ch = 0; ch < numChannels; ++ch) + for (int i = 0; i < numSamples; ++i) + sourceBuffer.setSample (ch, i, r.nextFloat()); + + AudioData::interleaveSamples (sourceBuffer.getArrayOfReadPointers(), numChannels, + destBuffer.getWritePointer (0), numChannels, + numSamples); + + for (int ch = 0; ch < numChannels; ++ch) + for (int i = 0; i < numSamples; ++i) + expect (destBuffer.getSample (0, ch + (i * numChannels)) == sourceBuffer.getSample (ch, i)); + } + + beginTest ("Deinterleaving"); + { + constexpr auto numChannels = 4; + constexpr auto numSamples = 512; + + AudioBuffer sourceBuffer { 1, numChannels * numSamples }, + destBuffer { numChannels, numSamples }; + + for (int ch = 0; ch < numChannels; ++ch) + for (int i = 0; i < numSamples; ++i) + sourceBuffer.setSample (0, ch + (i * numChannels), r.nextFloat()); + + AudioData::deinterleaveSamples (sourceBuffer.getReadPointer (0), numChannels, + destBuffer.getArrayOfWritePointers(), numChannels, + numSamples); + + for (int ch = 0; ch < numChannels; ++ch) + for (int i = 0; i < numSamples; ++i) + expect (sourceBuffer.getSample (0, ch + (i * numChannels)) == destBuffer.getSample (ch, i)); + } } }; diff --git a/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h b/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h index 43fd6a1bf4..15a16094a2 100644 --- a/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h +++ b/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h @@ -639,10 +639,69 @@ public: const int sourceChannels, destChannels; }; + + //============================================================================== + template + using SourceDataPointerType = const typename std::remove_pointer::type*; + + /** A helper function for converting a sequence of samples from a non-interleaved source + to an interleaved destination. + */ + template + static void interleaveSamples (SourceDataPointerType* sourceData, int numSourceChannels, + decltype (DestSampleFormat::data) destData, int numDestChannels, + int numSamples) + { + using SourceType = Pointer ; + using DestType = Pointer ; + + for (int i = 0; i < numDestChannels; ++i) + { + const DestType dest (addBytesToPointer (destData, i * DestType::getBytesPerSample()), numDestChannels); + + if (i < numSourceChannels) + { + if (*sourceData != nullptr) + { + dest.convertSamples (SourceType (*sourceData), numSamples); + ++sourceData; + } + } + else + { + dest.clearSamples (numSamples); + } + } + } + + /** A helper function for converting a sequence of samples from an interleaved source + to a non-interleaved destination. + */ + template + static void deinterleaveSamples (SourceDataPointerType sourceData, int numSourceChannels, + decltype (DestSampleFormat::data)* destData, int numDestChannels, + int numSamples) + { + using SourceType = Pointer ; + using DestType = Pointer ; + + for (int i = 0; i < numDestChannels; ++i) + { + if (auto* targetChan = destData[i]) + { + const DestType dest (targetChan); + + if (i < numSourceChannels) + dest.convertSamples (SourceType (addBytesToPointer (sourceData, i * SourceType::getBytesPerSample()), numSourceChannels), numSamples); + else + dest.clearSamples (numSamples); + } + } + } }; - - //============================================================================== #ifndef DOXYGEN