Search code examples
c++audio-streamingaudio-recordingportaudio

Get PortAudio recorded data with a single method


I am working on a guitar tuner application in c++. I am using PortAudio to record data from the microphone. The recording code works well if it is all placed in the main class. However, I would rather have a single method in the main class to retrieve recorded data. I created a separate class (recordaudio.cpp) with all the PortAudio code, but when I call the readStream method of this class from the main class, I get the message:

Error number: -10000
Error message: PortAudio not initialized

PortAudio is already instantiated in the constructor of the "recordaudio.cpp" class, so I'm not sure why it's saying that.

Here is the main class calling the getAudioStream(buffer) method:

#include "autocorrelation.h"
#include "peakpicking.h"
#include "recordaudio.h"
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <alsa/asoundlib.h>
#include <unistd.h>

#define SAMPLE_RATE 44100
#define WINDOW_SIZE 1024
#define WIN_HAMMING 1
#define WIN_HANN 2
#define WIN_SINE 3

using namespace std;

int main(){
    double max, average, val;
    double frequency=0;
    double period; 

    // Data buffers
    double* buffer = (double*) malloc(sizeof(double)*WINDOW_SIZE);
    double* buffer_snac = (double*) malloc(sizeof(double)*WINDOW_SIZE);

    // Instantiate  
    recordaudio audio = recordaudio();
    autocorrelation acf = autocorrelation(WINDOW_SIZE);
    peakpicking pick = peakpicking(buffer_snac, WINDOW_SIZE);

    while (true){
        audioStatus = audio.getAudioStream(buffer);

        // Autocorrelation function
        acf.autocorrelation_snac(buffer, buffer_snac);

        // Peak Picking
        period = pick.getPeriod();

        // Calculate frequency
        frequency = SAMPLE_RATE/period;
        cout  << "\r" << "frequency: " <<frequency << "  "  << flush;
    }
    audio.closeStream(); 

}

The recordaudio.h file

#ifndef RECORD_AUDIO_H
#define RECORD_AUDIO_H
#include "portaudio.h"

class recordaudio {

private:
    PaStreamParameters inputParameters;
    PaError err;
    PaStream *stream;

public:
    recordaudio();
    bool getAudioStream(double *buffer);
    void closeStream();
};

#endif

Here is the recordaudio.cpp class. The idea is to continuously call the getAudioStream(*buffer) method that lies in this class.

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <alsa/asoundlib.h>
#include <unistd.h>
#include <algorithm>
#include "recordaudio.h"

#define SAMPLE_RATE 44100
#define WINDOW_SIZE 1024
#define WIN_HAMMING 1
#define WIN_HANN 2
#define WIN_SINE 3

#define NUM_CHANNELS    (1)
/* #define DITHER_FLAG     (paDitherOff)  */
#define DITHER_FLAG     (0) /**/
/* Select sample format. */
#define PA_SAMPLE_TYPE  paFloat32
#define SAMPLE_SILENCE  (0.0f)
#define PRINTF_S_FORMAT "%.8f"


using namespace std;

recordaudio::recordaudio( ){
    err = Pa_Initialize();
    if( err != paNoError ) goto error;

    inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
    if (inputParameters.device == paNoDevice) {
      fprintf(stderr,"Error: No default input device.\n");
      goto error;
    }

    inputParameters.channelCount = NUM_CHANNELS;
    inputParameters.sampleFormat = PA_SAMPLE_TYPE;
    inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
    inputParameters.hostApiSpecificStreamInfo = NULL;

    err = Pa_OpenStream(
              &stream,
              &inputParameters,
              NULL,     /* &outputParameters, */
              SAMPLE_RATE,
              WINDOW_SIZE,
              paClipOff,/* we won't output out of range samples so don't bother clipping them */
              NULL,     /* no callback, use blocking API */
              NULL );   /* no callback, so no callback userData */


    if( err != paNoError ) goto error;
    err = Pa_StartStream( stream );
    if( err != paNoError ) goto error;
    cout << "Now recording!!" << endl;

error:
    Pa_Terminate();
    fprintf( stderr, "An error occured while using the portaudio stream\n" );
    fprintf( stderr, "Error number: %d\n", err );
    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
}

bool recordaudio::getAudioStream(double *buffer){
    err = Pa_ReadStream(stream, buffer, WINDOW_SIZE);
    if( err != paNoError ) goto error;
    return true;

error:
    Pa_Terminate();
    fprintf( stderr, "An error occured while using the portaudio stream\n" );
    fprintf( stderr, "Error number: %d\n", err );
    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
    return false;


}

void recordaudio::closeStream(){
    err = Pa_CloseStream( stream );
    if( err != paNoError ) goto error;

error:
    Pa_Terminate();
    fprintf( stderr, "An error occured while using the portaudio stream\n" );
    fprintf( stderr, "Error number: %d\n", err );
    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
}

Solution

  •     if( err != paNoError ) goto error;
        err = Pa_StartStream( stream );
        if( err != paNoError ) goto error;
        cout << "Now recording!!" << endl;
    
    error:
        Pa_Terminate();
    

    So just after "now recording" - you call PA_Terminate() probably not what you wanted. GOTO's are evil and the label in this case confused you into thinking that it would only go there if you called goto error. It doesn't. The label is just a marker and execution will happily continue through it.