The audio pipeline works pulling samples from the end, which is the AudioIODevice. Here is defined, how many channels are produced. If you use then the AudioSourcePlayer, which is an AudioIODeviceCallback, it will call getNextAudioBlock() with an AudioBuffer having space for the requested amount of channels.
All the AudioSources, that are chained into each other, are agnostic to the number of channels, i.e. if the channels don’t match, each getNextAudioBlock() tries to do something to remedy that, sometimes mixing the channels that are too many, or just skipping.
Once you run your AudioSourcePlayer with n channels, it will try to get n channels from the previous source, which means, one AudioTransportSource is enough to control all channels, no array needed. The MixerAudioSource is also counterproductive, since it will try to mix channels of the sources, not multiplex them. ChannelRemappingAudioSource comes closest, but it works with one input source only.
It is quite simple, to create a MultiChannelAudioSource, like @Xenakios suggested. The magic happens in the getNextAudioBlock().
I’ll write down a version I believe should work, untested:
class MultiChannelAudioSource : public PositionableAudioSource
{
public:
MultiChannelAudioSource() = default;
void loadAudioAssets(std::vector<File> &audioFileSet)
{
for (auto audioFileToLoad : audioFileSet)
{
if (auto* reader = formatManager.createReaderFor (audioFileToLoad))
{
inputReaders.add (new AudioFormatReaderSource (reader, true));
}
else
{
jassertfalse;
}
}
}
int64 getNextReadPosition() override
{
if (inputReaders.isEmpty()) return 0;
return inputReaders.getUnchecked (0)->getNextReadPosition();
}
void setNextReadPosition (int64 newPosition) override
{
for (auto* reader : inputReaders)
reader->setNextReadPosition (newPosition);
}
void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override
{
for (auto* reader : inputReaders)
reader->prepareToPlay (samplesPerBlockExpected, sampleRate);
}
void releaseResources() override
{
for (auto* reader : inputReaders)
reader->releaseResources();
}
void setLooping (bool shouldLoop) override
{
for (auto* reader : inputReaders)
reader->setLooping (shouldLoop);
}
bool isLooping() const override
{
if (inputReaders.isEmpty)
inputReaders.getUnchecked (0)->isLooping();
return false;
}
void getNextAudioBlock (AudioSourceChannelInfo& bufferToFill) override
{
jassert (inputReaders.size() >= bufferToFill.buffer->getNumChannels());
for (int i=0; i < bufferToFill.buffer->getNumChannels(); ++i)
{
AudioBuffer proxyBuffer (&bufferToFill.buffer->getWritePointer (i), 1, bufferToFill.buffer->getNumSamples());
AudioSourceChannelInfo proxyInfo (&proxyBuffer, bufferToFill.startSample, bufferToFill.numSamples);
inputReaders.getUnchecked (i)->getNextAudioBlock (proxyInfo);
}
}
private:
OwnedArray<AudioFormatReaderSource> inputReaders;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiChannelAudioSource)
};
Good luck