Help writing buffer to Wav file

Hello people!

I am really struggling writing audio to wav file. Sorry if this are basic questions, i am just starting, so please be patient :slight_smile:

I am trying to build a bare minimum code to test it, but keep getting different errors. Searched the forum and tried for two days now but cant get it working.

What am i doing wrong?

From what i understood from the documentation, i should use the function:
writeFromAudioSampleBuffer()
from the class:
AudioFormatWriter

The class description says:

After creating one of these with the AudioFormat::createWriterFor() method you can call its write() method to store the samples, and then delete it.

So since the goal is to write wav files, i choose the WavAudioFormat subclass and go to createWriterFor() , which has a constructor like this:

OutputStream           *streamToWriteTo*,
double                 *sampleRateToUse*,
unsigned int           *numberOfChannels*,
int                    *bitsPerSample*,
const StringPairArray& *metadataValues*,
int                    *qualityOptionIndex*
  • qualityOptionIndex “…just pass 0 in this parameter, as it’ll be ignored”
  • metadataValues can also be ignored (from the AudioRecordingDemo.h) “{}”
  • bitsPerSample is self explanatory (values 8,16,24,32) (saw this somewhere in the source code but cant find it anymore)
  • numberOfChannels and sampleRateToUse, also self explanatory
  • streamToWriteTo: we need to create an OutputStream object:

The documentation for the functions also says:

The writer object that is returned can be used to write to the stream, and should then be deleted by the caller.

but OutputStream has a more promissing subclass to use: FileOutputStream:

Which has a constructor like this:

FileOutputStream::FileOutputStream ( const File& *fileToWriteTo*,
                                     size_t *bufferSizeToUse* = 16384 )

write next to it the documentation gives us an example as:

FileOutputStream stream (file);

question 1)
What is this bufferSizeToUse for? what implications does it have?
is this not set later by the writeFromAudioSampleBuffer?

writeFromAudioSampleBuffer ( 	const AudioBuffer< float > & 	source, 
                                int 	startSample,
 	                            int 	numSamples )

but going foward, we now need a file object:

File::File 	( const String & absolutePath ) 	

So our code in compact form should be something like:

juce::File file("path/output.wav");
juce::FileOutputStream stream (file);
// test with stream.openedOk()

// now we need the WavAudioFormat to return the pointer for the writer:
juce::WavAudioFormat wavFormat;

// from other forum posts i am assuming that the returned writer object is in the form of a unique pointer 
std::unique_ptr<juce::AudioFormatWriter> writer;
 writer.reset(wavFormat.createWriterFor(
        &stream,
        44100,
        2,
        16,
        {},
        0));

//still dont understand when to use . or ->
writer->writeFromAudioSampleBuffer(buffer, 0, buffer.getNumSamples())

Question 2)
How should this be applied to a plugin, if i want to write the incoming buffers to a wav file?
just calling writeFromAudioSampleBuffer inside processBlock should be avoided so we dont block the audiothread.
But i am confused on how to use the ThreadedWriter

Question 3)
from AudioRecordingDemo.h:

fileStream.release(); // (passes responsibility for deleting the stream to the writer object that is now using it)

Why we need this?
i am really confused how this all works. We have a file, a file stream to this file, and a writer that “uses” this stream to write to the file.
I need to study pointers more.

Question 4)
What happens if the Sample rate and BitDepth from the buffer passed to writeFromAudioSampleBuffer() are different from the ones declared inside wavFormat.createWriterFor()?
.
.
.

My attempt, to just get it working in the simplest way, even if breaking the audiothread, was to do it like this:

// Declare stuff in ProcessPlugin.h
private:
    std::unique_ptr<juce::AudioFormatWriter> writer;
// PluginProcessor.cpp

// inside  prepareToPlay()  (not really sure if here or at AudioProcessor() constructor)
// open/create file, initialize stream and writer
    juce::File file("path/output.wav");

    juce::FileOutputStream stream(file);

    juce::WavAudioFormat wavFormat;

    writer.reset(wavFormat.createWriterFor(
        &stream,
        44100,
        2,
        16,
        {},
        0));

// Inside ProcessBlock() write the buffer
    if (writer != nullptr)
        writer->writeFromAudioSampleBuffer(buffer, 0, buffer.getNumSamples());

Runing this i receive the Error:

//at juce_WavAudioFormat.cpp 1652:
// bool write(
// ...
// if (! output->write (tempBlock.getData(), bytes)) {...}

Exception thrown: read access violation.
this->output-> was 0x1F68.

Any thoughts or insights on how to proper use the writeFromAudioSampleBuffer and write incoming audio to Wav?

Running on Windows 11, Visual Studio 2022, AudioPluginHost.
Tried to make this the most detailed possible, sorry for the long post.

You declare the File and FileOutputStream objects as local variables in prepareToPlay, so they would have been destroyed when the code gets to running the processBlock code. (The AudioFormatWriter does not copy those objects, you need to keep them alive yourself by having them as member variables of the AudioProcessor.)

1 Like

Thank you so much!

As almost always, it is something so simple.

making the file and stream member variables of the AudioProcessor alone did not work, returning the exception at memory:

Exception thrown at 0x00007FFA432A19F8 (NewProject.vst3) in AudioPluginHost.exe: 0xC0000005: Access violation reading location 0xFFFFFFFFFFFFFFFF.

but from this post (latter saw it is also writen in the documentation) i figured i had to release the stream pointer after creating the writer, and it worked!

so the code ended like this:

// @PluginProcessor.h
class AudioProcessor
{ // ... existing code...
private:
    juce::File file;
    std::unique_ptr<juce::FileOutputStream> stream;
    std::unique_ptr<juce::AudioFormatWriter> writer;
// @PluginProcessor.cpp
perpareToPlay()
{
file.operator=("path/output.wav");

stream = std::make_unique<juce::FileOutputStream>(file);
// safety checks

juce::WavAudioFormat wavFormat;
writer.reset(wavFormat.createWriterFor(
    stream.get(),
    44100,
    2,
    16,
    {},
    0));

stream.release();
}

processBlock()
{
if (writer != nullptr)
   writer->writeFromAudioSampleBuffer(buffer, 0, buffer.getNumSamples());
}

Should i also handle the memory release with something like this?

// @PluginProcessor.cpp
releaseResources()
{ 
   if (writer != nullptr)
    {
        writer.reset();
    }

    if (stream != nullptr)
    {
        stream->flush();
        stream.reset();
    }
}

Regarding question 2, does anyone has any tips, examples or resources about the ThreadedWriter?

Question 2)
How should this be applied to a plugin, if i want to write the incoming buffers to a wav file?
just calling writeFromAudioSampleBuffer inside processBlock should be avoided so we dont block the audiothread.
But i am confused on how to use the ThreadedWriter

Any thoughts on Question 4 also?

Question 4)
What happens if the Sample rate and BitDepth from the buffer passed to writeFromAudioSampleBuffer() are different from the ones declared inside wavFormat.createWriterFor()?

For q2 there’s an example in the AudioRecordingDemo.h that shows how the ThreadedWriter can be used.

For q4 the buffer passed to writeFromAudioSampleBuffer doesn’t have any notion of its own sample rate. If you have a buffer of data that you created/recorded at one sample rate, and you write it at a different sample rate, then the resulting file will contain the original data and the wrong sample rate, so it will play back at the wrong speed/pitch.

Thanks for the Help!
I will take a look at the ThreadedWriter inside the AudioRecordingDemo.h