Saving a .wav file in JUCE

Looked through all previous examples of this question but can’t seem to figure it out. I’ve attached my MainComponent.cpp and MainComponent.h. I’m using maximilian to create an oscillator at 200 Hz. I’d like to save 3 seconds of that to a .wav file in the current working directory. Right now no errors occur but also no file is saved. Any help would be great.

#include "MainComponent.h"

//==============================================================================
MainComponent::MainComponent()
{
    // Make sure you set the size of the component after
    // you add any child components.
    setSize (800, 600);

    // Some platforms require permissions to open input channels so request that here
    if (juce::RuntimePermissions::isRequired (juce::RuntimePermissions::recordAudio)
        && ! juce::RuntimePermissions::isGranted (juce::RuntimePermissions::recordAudio))
    {
        juce::RuntimePermissions::request (juce::RuntimePermissions::recordAudio,
                                           [&] (bool granted) { setAudioChannels (granted ? 2 : 0, 2); });
    }
    else
    {
        // Specify the number of input and output channels that we want to open
        setAudioChannels (2, 2);
    }
}

MainComponent::~MainComponent()
{
    // This shuts down the audio device and clears the audio source.
    shutdownAudio();
}

//==============================================================================
void MainComponent::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
{
    buffer.setSize(2, 132300);
    buffer.clear();
    manager.registerBasicFormats();
    //file.deleteFile();
    outStream = file.createOutputStream();
    writer.reset(format.createWriterFor(outStream.get(), 44100, 2, 16, {}, 0));
    //specifies where the audio file will be saved, and deletes a file if it is currenntly there
    file.getCurrentWorkingDirectory().getChildFile ("test.wav");
    file.create();
    
}

void MainComponent::getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill)
{
    float* leftSpeaker1 = buffer.getWritePointer(0);
    float* rightSpeaker1 = buffer.getWritePointer(1);
    
    for(int sample = 0; sample < bufferToFill.buffer->getNumSamples(); ++sample){
        
        leftSpeaker1[sample] = testOsc.sinewave(200);
        rightSpeaker1[sample] = leftSpeaker1[sample];
    }
    
    if (writer != nullptr){
        writer->writeFromAudioSampleBuffer(buffer, 1, buffer.getNumSamples());
        writer->flush();
        //delete writer;
    }
    
}

void MainComponent::releaseResources()
{
    // This will be called when the audio device stops, or when it is being
    // restarted due to a setting change.

    // For more details, see the help for AudioProcessor::releaseResources()
}

//==============================================================================
void MainComponent::paint (juce::Graphics& g)
{
    // (Our component is opaque, so we must completely fill the background with a solid colour)
    g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));

    // You can add your drawing code here!
}

void MainComponent::resized()
{
    // This is called when the MainContentComponent is resized.
    // If you add any child components, this is where you should
    // update their positions.
}

#pragma once

#include <JuceHeader.h>
#include "maximilian.h"

//==============================================================================
/*
    This component lives inside our window, and this is where you should put all
    your controls and content.
*/
class MainComponent  : public juce::AudioAppComponent
{
public:
    //==============================================================================
    MainComponent();
    ~MainComponent() override;

    //==============================================================================
    void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
    void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override;
    void releaseResources() override;

    //==============================================================================
    void paint (juce::Graphics& g) override;
    void resized() override;

private:
    //==============================================================================
    maxiOsc testOsc;
    //member variables for saving file
    juce::AudioBuffer<float> buffer;
    juce::File file;
    juce::AudioFormatManager manager;
    juce::String outFileExt = file.getFileExtension();
    juce::WavAudioFormat format;
    std::unique_ptr<juce::AudioFormatWriter> writer;
    std::unique_ptr<juce::FileOutputStream> outStream;
    
    

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};

I didn’t check your code otherwise, but attempting to use the current working directory doesn’t necessarily give the results you are thinking of. You could try some different location for the output file.

Thanks for the response! I looked into it and it seems that the writer object is actually a null value when debugging. On line 56 of MainComponent.cpp I have an if statement that checks if the writer object is null or not. I think this is the source of my problem, but I’m unsure how to approach it since the writer object is an abstract class.

I noticed some problems in the prepareToPlay method, might need changes like this (not tested here though) :

Makes sense, updated the files. I know have the code building, and the writer object is no longer a null value. However, when I run the code now I get an error after it builds and it creates a breakpoint in juce_WavAudioFormat.cpp on line 1402. Right above that line is a commented out warning for why I might be getting the error.

It says this: “if this fails, you’ve given it an output stream that can’t seek! It needs to be able to seek back to go back and write the header after the data has been written.”

I looked back and saw that the output stream value of mine called “outStream” was also a null value so I changed that, but I’m still getting the error. I’ve attached my updated code below.

#include "MainComponent.h"

//==============================================================================
MainComponent::MainComponent()
{
    // Make sure you set the size of the component after
    // you add any child components.
    setSize (800, 600);

    // Some platforms require permissions to open input channels so request that here
    if (juce::RuntimePermissions::isRequired (juce::RuntimePermissions::recordAudio)
        && ! juce::RuntimePermissions::isGranted (juce::RuntimePermissions::recordAudio))
    {
        juce::RuntimePermissions::request (juce::RuntimePermissions::recordAudio,
                                           [&] (bool granted) { setAudioChannels (granted ? 2 : 0, 2); });
    }
    else
    {
        // Specify the number of input and output channels that we want to open
        setAudioChannels (2, 2);
    }
}

MainComponent::~MainComponent()
{
    // This shuts down the audio device and clears the audio source.
    shutdownAudio();
}

//==============================================================================
void MainComponent::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
{
    buffer.setSize(2, 132300);
    buffer.clear();
    buffer1->clear();
    manager.registerBasicFormats();
    writer.reset (format->createWriterFor (outStream, 44100.0, buffer.getNumChannels(), 24, {}, 0));
    file.deleteFile();
    file.create();
    
}

void MainComponent::getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill)
{
    float* leftSpeaker1 = buffer.getWritePointer(0);
    float* rightSpeaker1 = buffer.getWritePointer(1);
    
    
    for(int sample = 0; sample < 5000; ++sample){

        leftSpeaker1[sample] = testOsc.sinewave(200);
        rightSpeaker1[sample] = leftSpeaker1[sample];
        
    }
    
    if (writer != nullptr){
        writer->writeFromAudioSampleBuffer(buffer, 0, 5000);
        writer->flush();
        //delete writer;
    }
    
}

void MainComponent::releaseResources()
{
    // This will be called when the audio device stops, or when it is being
    // restarted due to a setting change.

    // For more details, see the help for AudioProcessor::releaseResources()
}

//==============================================================================
void MainComponent::paint (juce::Graphics& g)
{
    // (Our component is opaque, so we must completely fill the background with a solid colour)
    g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));

    // You can add your drawing code here!
}

void MainComponent::resized()
{
    // This is called when the MainContentComponent is resized.
    // If you add any child components, this is where you should
    // update their positions.
}

#pragma once

#include <JuceHeader.h>
#include "maximilian.h"

//==============================================================================
/*
    This component lives inside our window, and this is where you should put all
    your controls and content.
*/
class MainComponent  : public juce::AudioAppComponent
{
public:
    //==============================================================================
    MainComponent();
    ~MainComponent() override;

    //==============================================================================
    void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
    void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override;
    void releaseResources() override;

    //==============================================================================
    void paint (juce::Graphics& g) override;
    void resized() override;

private:
    //==============================================================================
    maxiOsc testOsc;
    //member variables for saving file
    juce::AudioBuffer<float> buffer;//AudioBuffer is different from AudioSampleBuffer
    juce::AudioSampleBuffer* buffer1 = new juce::AudioSampleBuffer(2,5000);
    //juce::File file;
    juce::File file = juce::File("/users/USERNAME/JuceTestPlugins/SaveAudioFile/Source");//absolute path
    juce::AudioFormatManager manager;
    juce::String outFileExt = file.getFileExtension();
    juce::WavAudioFormat* format = new juce::WavAudioFormat;
    std::unique_ptr<juce::AudioFormatWriter> writer;
    //std::unique_ptr<juce::FileOutputStream> outStream;
    juce::FileOutputStream* outStream = new juce::FileOutputStream(file);
    
    //creating file test
    //unique_ptr<juce::FileInputStream> input (file.createInputStream());
    

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};