Hello, I want to use MemoryMappedAudioFormatReader
with AudioTransportSource
for a multi-track audio playback application. (In my experiments, using AudioFormatManager::createReaderFor(file)
on multiple tracks may randomly lag and is unacceptable.)
The following code is adapted from the Projucer demo AudioPlaybackDemo.h
, for playing one audio file. Here I stripped out the UI and replaced the reader with a MemoryMappedAudioFormatReader
, which will map the entire file.
class AudioPlayerComponent : public juce::AudioAppComponent
{
public:
AudioPlayerComponent()
{
formatManager.registerBasicFormats();
setAudioChannels(0, 2);
}
~AudioPlayerComponent() override
{
shutdownAudio();
}
void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override
{
transportSource.prepareToPlay(samplesPerBlockExpected, sampleRate);
}
void getNextAudioBlock(const juce::AudioSourceChannelInfo& bufferToFill) override
{
if (readerSource.get() == nullptr)
{
bufferToFill.clearActiveBufferRegion();
return;
}
transportSource.getNextAudioBlock(bufferToFill);
}
void releaseResources() override
{
transportSource.releaseResources();
}
void resized() override
{
}
void play()
{
transportSource.start();
}
void stop()
{
transportSource.stop();
}
void setPosition(double position)
{
transportSource.setPosition(position);
}
void openFile(juce::String& path)
{
juce::File file(path);
if (file != juce::File{})
{
// auto* reader = formatManager.createReaderFor(file); // Don't use this AudioFormatReader
juce::AudioFormat* audioFormat = formatManager.findFormatForFileExtension(file.getFileExtension());
if (audioFormat == nullptr) return;
auto* reader = audioFormat->createMemoryMappedReader(file);
if (!reader->mapEntireFile())
{
throw "mapEntireFile() failed";
}
if (reader != nullptr)
{
auto newSource = std::make_unique<juce::AudioFormatReaderSource>(reader, true);
transportSource.setSource(newSource.get(), 0, nullptr, reader->sampleRate);
readerSource.reset(newSource.release());
}
}
}
private:
juce::AudioFormatManager formatManager;
std::unique_ptr<juce::AudioFormatReaderSource> readerSource;
juce::AudioTransportSource transportSource;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AudioPlayerComponent)
};
I ran it as a console app to play a WAV file. I noticed that when the playhead reaches the end of the file, I would get the following assertion failure:
JUCE Assertion failure in juce_WavAudioFormat.cpp:1868
if (map == nullptr || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples)))
{
jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read.
I added print statements and checked at that point, the requested mappedSection
has range( 2930654, 2930173), an invalid range where start > end, whereas the reader
created above has the valid range(0, 2930173).
What am I doing wrong? Please advise. Thank you for your time and consideration.