Recording Issue

I i am trying to record this way

void AudioRecorder::startRecording (AudioBuffer<float>* bufferToRecord, const File& file)
{
    stop();
    
    //    file.deleteFile();
    
    //get numChannel based on the track need (Mono or Stereo)
    auto numChannels = bufferToRecord->getNumChannels();
    
    
    //create a new file based on the passed argument
    
    writer.reset (format.createWriterFor (new FileOutputStream (file),
                                          44100.0,
                                          numChannels,
                                          24,
                                          {},
                                          0));
}

//=============================================================================
//Actual Recording

void AudioRecorder::Record(AudioBuffer<float>* bufferToRecord)
{
    const auto numSamples = bufferToRecord->getNumSamples();
    if (writer != 0)
    {
        writer->writeFromAudioSampleBuffer(*bufferToRecord, 0, numSamples);
    }
}

and it actually create and write on a .wav, except for the fact that the .wav output is slowed down and distorted
Anyone encountered this problem ?

Fun fact : I am flying to Berlin and I am on the same plane with Jean-Baptiste Thiebaut

I am not using any background Thread… all of this is called on the getNextAudioBlock() of one of my audioSources and i am using

std::unique_ptr writer, not

std::atomic<;AudioFormatWriter::ThreadedWriter*> activeWriter ;

You set the writer to write 44100 Hz into the header, but you don’t check, if your audio device is actually running at that speed. It seems rather to run on 48000 Hz judging by your description.

N.B. say hi to JB :slight_smile: (well 3 hrs, you are probably there by now…)

I can confirm my device is working at 44100.

I mess a bit around and I noticed two things :

1. if i divide the num of samples by two
writer->writeFromAudioSampleBuffer(*bufferToRecord, 0, numSamples/2);
the beat does not slow down…

2. If i change the buffer size of the DeviceManager from the AudioDeviceSelector from 512 to 16 samples the distortion almost goes away

would you know what could be the problems that brought to this two behaviours ?

Can you show the code, where your AudioRecorder::Record method is called? I would be interested, how you fill the buffer.
If it is slowed down, that sounds like you are sending more samples, than you are actually having available, hence the written signal becomes longer. And since the additional samples from each block are not part of the signal, that might be the distortion you are hearing.

void Track::prepareToPlay (int samplesPerBlockExpected, double sampleRate) {

    bufferToRecordL.setSize(1, samplesPerBlockExpected);
    bufferToRecordR.setSize(1, samplesPerBlockExpected);
    bufferToRecordStereo.setSize(2, samplesPerBlockExpected);

        
}
void Track::releaseResources() {}

void Track::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) {
    
    if (inputL){
    
        if(!isStereo){
            
        //copy the desired content(channel) from bufferToFill and pass it to the recorder
    const auto numSamples = bufferToFill.buffer->getNumSamples();
          
            bufferToRecordL.copyFrom(0, 0, *bufferToFill.buffer, (inputL - 1), 0, numSamples);
        
           
            bufferToFill.clearActiveBufferRegion();
            
            bufferToRecordL.applyGain(0, 0, numSamples, sliderValue);
            
            if (recordState == RecordState::Recording)
                **recorder.Record(&bufferToRecordL);**
          
            bufferToFill.buffer->addFrom(0, 0, bufferToRecordL, 0, 0, numSamples);
            bufferToFill.buffer->addFrom(1, 0, bufferToRecordL, 0, 0, numSamples);
    
        
            
    }
        if(isStereo){
       
            //copy the desired content(channel) from bufferToFill and pass it to the recorder
            const auto numSamples = bufferToFill.buffer->getNumSamples();
            bufferToRecordL.copyFrom(0, 0, *bufferToFill.buffer, (inputL - 1), 0, numSamples);
            bufferToRecordR.copyFrom(0, 0, *bufferToFill.buffer, (inputR - 1), 0, numSamples);

            bufferToFill.clearActiveBufferRegion();

            bufferToRecordStereo.copyFrom(0, 0, bufferToRecordL, 0, 0, numSamples);
            bufferToRecordStereo.copyFrom(1, 0, bufferToRecordR, 0, 0, numSamples);
            
            bufferToRecordStereo.applyGain(0, 0, numSamples, sliderValue);
            bufferToRecordStereo.applyGain(1, 0, numSamples, sliderValue);
  
            if (recordState == RecordState::Recording)
                recorder.Record(&bufferToRecordStereo);
            
            bufferToFill.buffer->addFrom(0, 0, bufferToRecordStereo, 0, 0, numSamples);
            bufferToFill.buffer->addFrom(1, 0, bufferToRecordStereo, 1, 0, numSamples);
            
            
        };
    }
    
     else {
         
         bufferToFill.clearActiveBufferRegion();}
}

The problem seems to me, that you are using the buffer from the AudioSourceChannelInfo.buffer.
Instead you should only copy info.numSamples starting at info.startSample.

I would suggest to change the signature of your Record method to:

void AudioRecorder::Record (const AudioBuffer<float>& bufferToRecord)

and call it like:

void Track::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill)
{
    if (recordState == RecordState::Recording)
    {
        AudioBuffer<float> proxy (bufferToFill.buffer->getArrayOfWritePointers(),
                                  bufferToFill.buffer->getNumChannels(),
                                  bufferToFill.startSample,
                                  bufferToFill.numSamples);
        recorder.Record (proxy);
    }
    // ...
}

Aslo moving forward, remember you are writing to a potentially slow disk in the audio thread, so have a look at ThreadedWriter

Hope that helps

Thanks a lot for your time
I am going to work at it later and let you know…

Just one thing, so your suggestion is to pass the entire bufferToFil(proxy) to the recorder and then do the copyFrom and stuff into the recorder ?

Since your Recorder code is used to write the whole buffer, but the AudioSourceChannelInfo is designed to denote a subset of that buffer, I suggest to create this proxy buffer.
It references the audio data coming in from AudioSourceChannelInfo, but looks like it is starting at startSample and is numSamples long.

There is no need to copy stuff around, it will wait until it is written anyway (which is the problem, that you can avoid with ThreadedWriter. That will copy the needed data and return immediately)

1m

Maybe I am using the wrong approach, but i need the copyFrom because i am using it to send to Record only the channels I desire.

How would you select only a pair or a single channel to send ?

You can supply your own array of write pointers:

void Track::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill)
{
    if (recordState == RecordState::Recording)
    {
        float* pointers [2] = { bufferToFill.buffer->getWritePointer (1), 
                                bufferToFill.buffer->getWritePointer (3) };
        AudioBuffer<float> proxy (pointers, 2, bufferToFill.startSample, bufferToFill.numSamples);
        recorder.Record (proxy);
    }
    // ...
}

I think you can even assign the same pointer twice to turn a mono into stereo…

In order to implement the ThreadedWriter I have looked at the Demo code and I can see it uses a AudioIODeviceCallback therefore it has const float** inputChannelData to ->write,
Instead I am doing my recording in the getNextAudioBlock() from my AudioSources…
Is there a way to convert BufferToFill into const float** inputChannelData ?
cause the ThreadedWriter doesn’t seem to have accept the function writeFromSampleBuffer …

The method to get a float** from a AudioBuffer<float> is buffer.getArrayOfWritePointers().
But in your case that makes things even easier, you take the startSample into account when you create the pointer array (float* pointers[2] is the same like float** pointers, simplified speaking).
So you can scrap the proxy buffer and call:

float* pointers [2] = { bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample), 
                        bufferToFill.buffer->getWritePointer (3, bufferToFill.startSample) };
threadedWriter.write (pointers, bufferToFill.numSamples);

It is probably the easiest way to change your record method to look like:

void AudioRecorder::Record (const AudioSourceChannelInfo& bufferToFill)

that way you can keep the needed information together, and the recorder can then decide, which channel to record etc.

Daniel… I can’t believe I finally got it… the problem was that i was setting the track as as source for two AudioSourcePlayer
this was residue of my old implementation :

// audioSourcePlayer.setSource(fTracks.getLast()); in the mainComponent.cpp

and this was the actual implementation :

Track* newTrack = new Track("Track",
                             &sliderTrack1,
                             &buttonTrack1,
                             "2",
                             &midiCollector,
                             newTree,
                             undoManager);
    
    fTracks.add (newTrack);
    tracks.items.add(FlexItem(200, 200, *fTracks.getLast()));
    
    AudioSourcePlayer* newAudioSourcePlayer = new AudioSourcePlayer;
    audioSourcePlayers.add(newAudioSourcePlayer);
    audioSourcePlayers.getLast()->setSource(fTracks.getLast());
    deviceManager.addAudioCallback(audioSourcePlayers.getLast());

The two were colliding… remove the first and everything was fine :rofl:

Thanks so much for the suggestion about the new implementation with pointers and the getArrayOfWritePointers function was really illuminating as a newbie in JUCE…

I feel stupid and sorry for making you waste your time but in the end now I have ThreadedWriter record function that is very cool !!!

Lovely, glad it all works out :slight_smile: