Recording AudioSource vs. AudioIODevice

I am trying to combine the AudioRecordingDemo and the AudioSynthesiserDemo so that I can record the output of the SynthAudioSource.

I am new to audio programming and I am very confused. I think it boils down to the difference between an AudioIODevice and an AudioSource.

The AudioRecordingDemo records the output of the microphone. The microphone is an AudioIODevice. The currently used/recorded AudioIODevice is chosen by the AudioDeviceManager. But I want to record an AudioSource, the SynthAudioSource. I don’t see how to choose the SynthAudioSource with the AudioDeviceManager. So, how do I modify the code of the AudioRecordingDemo to record the SynthAudioSource instead of the microphone output?

(Please note I am NOT trying to record midi notes. Just synth audio output.)

Thanks!

You don’t chose your synth as a device because, well it is no device :wink: The audio device manager helps you interacting with physical audio hardware connected to your computer, such as the built in microphone, the built in speaker, the headphone jack or any professional external audio interface connected to your computer.

Your SynthAudioSource fills a buffer of samples in the getNextAudioBlock function. You can simply pick up the samples produced there and write it to a file if you want to record the audio output of your synth. Does this help you to get a rough idea at least?

1 Like

Thanks! I will look into it. Just wasn’t sure which way to go:

  1. Somehow pipe AudioSource to an AudioIODevice and record that, or
  2. Record the AudioDevice directly.

The AudioRecorder class in the AudioRecordingDemo seems pretty slick and extends from AudioIODeviceCallback, so I didn’t want to hack together a new AudioRecorder if the existing one was the industry best practice.

I’ve revised the SynthAudioSource's getNextAudioBlock as follows:

void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
    {
        bufferToFill.clearActiveBufferRegion();
        MidiBuffer incomingMidi;
        midiCollector.removeNextBlockOfMessages (incomingMidi, bufferToFill.numSamples);
        keyboardState.processNextMidiBuffer (incomingMidi, 0, bufferToFill.numSamples, true);
        synth.renderNextBlock (*bufferToFill.buffer, incomingMidi, 0, bufferToFill.numSamples);
        
        //!!!!!!!!!!!!!!!!!!!!!!!!!NEW STUFF START
        const ScopedLock sl (writerLock);
        if (activeWriter.load() != nullptr)
        {
            if (bufferToFill.buffer->getNumChannels() > 0)
            {
                auto* channelData = bufferToFill.buffer->getReadPointer (0, bufferToFill.startSample);
                const ScopedLock sl (writerLock);
                activeWriter.load()->write ((const float**)channelData, bufferToFill.numSamples);
            }
        }
        //!!!!!!!!!!!!!!!!!!!!!!!!!NEW STUFF END
    }

I am getting the following crash error when I press record:
JUCE Assertion failure in juce_AudioSampleBuffer.h:929

It is saying that the source in AudioSampleBuffer copyFrom is null:

void copyFrom (int destChannel,
                   int destStartSample,
                   const Type* source,
                   int numSamples) noexcept

Have I given enough info for anyone to see what I am doing wrong?

1 Like

I got it working. I had to change to:

activeWriter.load()->write (( const float** )bufferToFill.buffer->getArrayOfWritePointers(), bufferToFill.numSamples);

Just a side note: wrapping the pointer to activeWriter into std::atomic won’t protect it from being used while it is destroyed. You need the ScopedLock for that purpose anyway, so you can drop the std::atomic here…

Thanks! I will make that change.

@shawnm I wonder if you could post more of the context of your solution? It’s not clear to me where activeWriter comes from, as that is part of the Demo’s AudioIODeviceCallback class that PluginPenguin said is precisely the thing not to use.

I’m sorry, but I can’t remember. I haven’t worked on this project, or in JUCE, for many months.

OK @shawnm thanks for the reply