Is this a .wav file write bug?

I wrote a simple function to write some audio samples to a wav file.
It works. But when I choose an existing file to overwrite, the resulting file seems wrong. I can not read that file in Audacity for instance).

void MyComponent::onSaveClick()
{
    if (!mSampleBuffer) return;

    mChooser = std::make_unique<juce::FileChooser> ("Save as wave file ...",
                                                    juce::File{},
                                                    "*.wav");
    
    auto chooserFlags = juce::FileBrowserComponent::saveMode
                        | juce::FileBrowserComponent::warnAboutOverwriting;

    mChooser->launchAsync(chooserFlags, [this] (const juce::FileChooser& fc)
    {
        auto file = fc.getResult();

        if (file != juce::File{})
        {
            if (auto fileStream = std::unique_ptr<FileOutputStream> (file.createOutputStream())) {
                // Now create a WAV writer object that writes to our output stream...
                juce::WavAudioFormat wavFormat;
                auto* writer = wavFormat.createWriterFor(fileStream.get(), mSampleRate, 1, 16, {}, 0);
                if (writer != nullptr) {
                    fileStream.release(); // passes responsibility for deleting the stream to the writer object that is now using it
                    // read samples from mSampleBuffer
                    int numSamples = mSampleBuffer->getSize();
                    float* rptr = mSampleBuffer->getSamplesPtr(0);
                    // use juce AudioBuffer to write the samples
                    juce::AudioBuffer<float> audioBuffer;
                    audioBuffer.setSize(1, numSamples);
                    float* wptr = audioBuffer.getWritePointer(0);
                    for (int i=0 ; i<numSamples ; i++) *wptr++ = *rptr++;
                    // write the audio buffer to file
                    writer->writeFromAudioSampleBuffer(audioBuffer, 0, numSamples);
                    //
                    delete writer;
                }
            }
        }
    });
}

The docs for the constructor of FileOutputStream explain what’s going on. When the output file already exists, the FileOutputStream will append to the existing file, rather than overwriting it.

A quick fix would be to call stream.setPosition (0); stream.truncate() after verifying that the stream opened successfully. A more robust approach would be to write the output to a temporary file, and to replace the old file with the temporary file once the temporary file has been completely written.

That worked! Thx.
Though I find it a bit strange because I set the warnAboutOverwriting flag and it asks if it is ok to overwrite the file.

Altering and overwriting is the same for the file system. But I agree, it might be misleading.

FWIW I am used now to call setPosition(0), but it would be nice to have an optional parameter to select replace instead of appending. (I would even want the default to be replacing, but that would mess things up for existing code)

2 Likes