AudioFormatWriter + FileStream combo won't let me overwrite files


#1

I have a wrapper class in my program that creates readers for a set of source audio files (mono wave files) then uses those readers to fill buffers that are processed by an external 3rd party API and dumped into another set of buffers which I’m using to write a new set of (processed) audio files. The wrapper is always created as a new instance, so in theory all readers and writers should be fresh and clean. The wrapper works correctly on a first pass, but if I destroy that instance and call it again for a new set of source files, my processed files do not get overwritten correctly. Instead, the file size gets increased by the size of the new material, but when I play those files back, they are identical in content and duration to the processed files from the first pass.

In the interest of brevity, I’ve posted below only the methods I’m using to prepare the AudioFormatWriters and write the processed material to new files. I don’t think there is a problem with the readers because the first pass is always successful. Or the writers for that matter. I think it’s the stream. Somehow I’m using it incorrectly for overwrite situations like this. (The output file URIs are always the same regardless of the source material.)

prepareToWrite(std::vector<File> &sourceFileSet);
{
	// outputAudioWriters is a private class member (OwnedArray<AudioFormatWriter>)
	outputAudioWriters.clear(true);

	for (auto sourceFile : sourceFileSet)
	{
		int channel = (int)outputAudioWriters.size();
		std::string filename("ProcessedAudio_ch" + std::to_string(channel));

		// folderURI is a private class member (std::string)
		File outputAudioFile(folderURI + filename);

		if (outputAudioFile.existsAsFile())
			outputAudioFile.deleteFile();

		std::unique_ptr<FileOutputStream> fileStream(outputAudioFile.createOutputStream());

		if (fileStream.get() != nullptr)
		{
			WavAudioFormat wavFormat;

			// inputReaders is a private class member (OwnedArray<AudioFormatReaderSource>)
			auto inputAudioReader = inputReaders.getUnchecked(channel)->getAudioFormatReader();

			outputAudioWriters.add(
				wavFormat.createWriterFor(
					fileStream.get(),
					inputAudioReader->sampleRate,
					inputAudioReader->numChannels,
					inputAudioReader->bitsPerSample,
					{},
					0)
			);

			if (outputAudioWriters.getUnchecked(channel) != nullptr)
			{
				fileStream.release();
			}
		}
	}
}

void process()
{
	AudioBuffer<float> multiChannelBuffer(numOfChannels, outputBufferSize);
	multiChannelBuffer.clear();

	// External processing via 3rd party API fills multi-channel array with audio data.
	// Implementation details unknown and inaccesible.

	ext.process(multiChannelBuffer.getArrayOfWritePointers(), outputBufferSize);

	// This loop correctly writes the processed data to wave files the first time, but
	// unless I re-launch the program or delete the wave files from disk via the OS,
	// subsequent instances of ExternalAPI::Processor behave oddly, increasing the
	// file size by exactly the size of the new material. This suggests to me that
	// the writer is adding wave data to the file, however when played back, the files
	// have the original content and duration.

	for (int channel = 0; channel < numOfChannels; channel++)
	{
		auto readPointer = multiChannelBuffer.getReadPointer(channel);

		// Write processed data to output file stream
		outputAudioWriters.getUnchecked(channel)->writeFromFloatArrays(&readPointer, 1, outputBufferSize);
	}
}

#2

The symptoms sound 100% like when you call outputAudioFile.createOutputStream() it is opened for appending. And it would explain the file increase, but the header still contains the old length, so the appended new header would never be read.

However, you are calling deleteFile() first, so that sounds correct.

Is there a chance, that deleteFile() fails e.g. for permissions reasons? (Write permissions on the file, but no write permissions on the folder would be such a thing).
Maybe simply call inside your block:

File outputAudioFile (folderURI + filename);
std::unique_ptr<FileOutputStream> fileStream (outputAudioFile.createOutputStream());
if (fileStream.get() != nullptr)
{
    fileStream.setPosition (0);
    fileStream.truncate();
    // ...

Good luck


#3

@daniel to the rescue, once again. That did the trick. Thank you, my friend!