• Audio
  • Streaming very large audiofiles

Hey! I am trying to stream very large (2.7GB) Audiofiles from the SD card. I used the "sample-streamer" example, but I get an error: "Couldn't open file
20241012_000000.WAV
: System error : Value too large for defined data type."
But AudioFileUtilities::load should only load the buffer length, no? I changed the gNumFramesInFile to an unsigned long and also put in the number of frames directly so I dont have to use AudioFileUtilities::getNumFrames but the error still is thrown.

I read somewhere to use the AudioFileReader class, but are there any examples on how to use it?
Thanks for your help!

5 days later

Hello! Thank you very much for the link. I tried it with this example and playing the file back works. Now I want to speed up the file by playing back only every nth sample. I tried it with the following code and it works for 2x, 3x, and 4x faster, but from 5x and higher it sounds like it doesn't read in enough samples and then starts playing back some of the already played samples ... it sound like a sort of echo. I read the code multiple times but I can't wrap my head around why ... do you maybe have an idea what it could be? Thank you so much for your help already!

#include <Bela.h>
#include <libraries/AudioFile/AudioFile.h>
#include <vector>
#include <string>

std::vector<AudioFileReader> gReaders(2);
std::vector<std::string> gFilenames = {
	"20241008_192123.WAV",
	"20241012_000000.WAV",
};
size_t gLoadingFile;
size_t gCurrReader;

std::vector<float> gSamples;
size_t gFrameCount = 0;
AuxiliaryTask gStartLoadingFileTask;

int gSpeed = 10;

void loadNextFile(void*)
{
	// start preloading the next file
	gLoadingFile = (gLoadingFile + 1) % gFilenames.size();
	size_t nextReader = (gCurrReader + 1) % gReaders.size();
	gReaders[nextReader].setup(gFilenames[gLoadingFile], 16384);
	rt_printf("Opening file [%zu] %s in reader %zu with a lentgh of %zu\n", gLoadingFile, gFilenames[gLoadingFile].c_str(), 
		nextReader, gReaders[nextReader].getLength());
}

bool setup(BelaContext *context, void *userData)
{
	// create a task to load files
	gStartLoadingFileTask = Bela_createAuxiliaryTask(loadNextFile, 1, "loadNextFile");
	if(!gStartLoadingFileTask) {
		fprintf(stderr, "Error creating file loading task\n");
		return false;
	}
	gLoadingFile = -1;
	gCurrReader = -1;
	// open the first file
	loadNextFile(NULL);
	gCurrReader = 0;
	// open the second file
	loadNextFile(NULL);
	gSamples.reserve(context->audioFrames * gReaders[gCurrReader].getChannels() * gSpeed);
	return true;
}

void render(BelaContext *context, void *userData) {
	AudioFileReader& reader = gReaders[gCurrReader];
	// this call may allocate memory if getChannels() is larger than for
	// all previous files
	gSamples.resize(context->audioFrames * reader.getChannels() * gSpeed);
	reader.getSamples(gSamples);
	for(unsigned int n = 0; n < context->audioFrames; ++n)
	{
		float out = 0;
		for(unsigned int c = 0; c < context->audioOutChannels; ++c)
		{
			// write each channel from the audio file to the
			// output channels. If there are more output channels
			// than channels in the file, copy the file's last
			// channel to all remaining outputs
			if(c < reader.getChannels())
				out = gSamples[n * gSpeed * reader.getChannels() + c];
			audioWrite(context, n, c, out);
		}
		// count samples played back for the existing file
		gFrameCount += gSpeed;
		if(gFrameCount >= reader.getLength())
		{
			// reached end of file
			gFrameCount = 0;
			// start playing the file we preloaded
			gCurrReader = (gCurrReader + 1) % gReaders.size();
			reader.getSamples(gSamples);
			rt_printf("Playing from reader: %zu\n", gCurrReader);
			// start loading next file in a real-time safe way
			Bela_scheduleAuxiliaryTask(gStartLoadingFileTask);
		}
	}
}

void cleanup(BelaContext *context, void *userData)
{
}

You are now trying to read 5x the amount of data that you were reading previously. This may well put significant stress on the bandwidth and ultimately cause the underruns you are experiencing. If it's just a buffering issue, increase the AudioReader buffer size at this line:

	gReaders[nextReader].setup(gFilenames[gLoadingFile], 16384);

increase 16384 to something larger. If this is not enough, I don't think there is an efficient workaround to this beyond downsampling the file on disk: reading one frame every 5 cannot be done efficiently.