Why do changes to buffer in thread not be reflected in buffer on mainThread?

324 Views Asked by At

I'm trying to receive audio from the soundcard via RtAudio Api. It has a callback function that gets called once the audio has enough bytes received and the user can then copy the data to a custom object. This custom object can be sent to the callback via pointer. My class that encapsulates RtAudio looks like this:

class Audio {
private:
    AudioConfiguration config;
    AudioData data;
    RtAudio* rt;

    // the void* d is the casted AudioData-object
    static int input( void*, void* inputBuffer, unsigned int bufferSize, double, RtAudioStreamStatus status, void* d );
    void openStream( RtAudio::StreamParameters& params, RtAudio::StreamOptions& options, AudioConfiguration& config );
    bool isDeviceOk();

public:
    // ctor & dtor
    Audio( AudioConfiguration& c );
    ~Audio();

    // copy ctor & assignment
    Audio( const Audio& other );
    Audio& operator=( const Audio& a );

    // move ctor & assignment
    Audio( Audio&& other );
    Audio& operator=( Audio&& a);

    AudioConfiguration& getConfiguration();
    AudioData& getData();

    void start();
    void stop();
};

This is the implementation of the static function that gets called from inside the audio thread

int Audio::input( void*, void* inputBuffer, unsigned int bufferSize, double, RtAudioStreamStatus status, void* d ){
    if( status == RTAUDIO_INPUT_OVERFLOW )
        std::cout << "Audio Thread: Input overflow detected." << std::endl;

    //std::cout << "Audio Thread: Received input from soundcard" << std::endl;

    float* in = static_cast<float*>( inputBuffer );
    AudioData* data = static_cast<AudioData*>( d );

    boost::lock_guard<boost::mutex> lock{data->getMutex()};

    unsigned int i = 0;
    while( i < bufferSize ){
        data->getBuffer().push_back( *in );
        in++;
        i++;
    }

    return 0;
}

The custom object that I share between the threads is of the class AudioData, which looks like this:

class AudioData {
private:
    boost::circular_buffer<float> buffer;
    boost::mutex mutex;

public:
    AudioData();
    ~AudioData();

    boost::circular_buffer<float>& getBuffer();
    boost::mutex& getMutex();
};

The audio-object gets embedded in a Recorder-Object, which then reads the buffer in the AudioData member variable of Audio.

typedef boost::container::vector<boost::circular_buffer<float>> Buffers;
class Recorder {
    private:
        Audio audio;
        Buffers buffers;

        /*
         *  Reads n samples per channel from audio buffers to NUM_CHANNELS distinct buffers
         *  When done, it returns the number of samples read into each channel
         *  Why? We want to keep audio buffer to be locked as minimal time as possible
         */
        unsigned int read( unsigned int samples );
        /*
         *  Deletes n samples from channel buffer
         *  When done, it returns the number of samples deleted
         */
        unsigned int deleteBegin( unsigned int ch, unsigned int samples );
        /*
         *  Detects the trigger on TRIGGER_CHANNEL
         *  Returns true, if trigger was found and its position
         */
        bool detectTrigger( unsigned int* pos );

    public:
        Recorder( AudioConfiguration& c );
        Recorder( Audio&& a );

        boost::container::vector<float> record( RecorderConfiguration& config );
    };

The function record(..) looks like this:

boost::container::vector<float> Recorder::record( RecorderConfiguration& config ){

    AudioConfiguration audioConfig = audio.getConfiguration();
    unsigned int length = ( audioConfig.fs * config.length ) / 1000;

    boost::container::vector<float> recording;
    recording.resize( length );

    // Tell Audio to start recording
    audio.start();

    // State
    unsigned int times = 0;             // Count averages
    unsigned int left = length;         // Samples left on current average
    bool triggered = false;             // Trigger has been read

    while( true ){

        // Read into local buffer
        unsigned int samplesToRead = length / 10;
        unsigned int samplesRead = read( samplesToRead );

        // if not enough samples, wait for more
        if( samplesRead < 100 )
            continue;

        // Actual logic
        unsigned int triggerPos = 0;
        if( !triggered && detectTrigger( &triggerPos ) ){
            std::cout << "Recorder: Trigger detected." << std::endl;
            triggered = true;

            // delete everything that comes before trigger on both channels
            for( unsigned int i = 0 ; i < NUM_CHANNELS ; i++ ){
                deleteBegin( i, triggerPos - 1);
            }
        }

        // Copy from buffer if trigger was found beforehand
        if( triggered ){
            boost::circular_buffer<float>& buffer = buffers[ EEG_CHANNEL ];
            unsigned int samplesToCopy = buffer.size();

            if( samplesToCopy > left )
                samplesToCopy = left;

            for( unsigned int i = 0 ; i < samplesToCopy ; i++ ){
                recording[ length - left ] = recording[ left - left ] + buffer.front();
                buffer.pop_front();
                left--;
            }
        }

        // current average done
        if( left <= 0 ){
            // increment times
            times++;
            // check if recording is done
            if( times >= config.times )
                break;
            // if not
            else {
                triggered = false;
                left = length;
            }
        }
    }

    // Stop receiving input from audio
    audio.stop();

    return recording;
}

I read that the heap is the place to hold data that is shared between threads, but in the example by rtaudio they use a global variable that gets allocated on the stack for pushing the data to Link. So I am a little bit confused. Help would be gladly accepted!

Edit: When i debug my app. I can see that the input function of the audio-thread gets called and it writes to the buffer. Also the record function works as expected. Only the buffer (of AudioData) does not seem to have any data in it...

Edit2: Here is the code where I register the callback in the rtaudio api.

void Audio::openStream( RtAudio::StreamParameters& params, RtAudio::StreamOptions& options, AudioConfiguration& config ){
    try {
        rt->openStream( nullptr, &params, RTAUDIO_FLOAT32, config.fs, &config.bufferSize, &this->input, &data, &options, nullptr );
    } catch( RtAudioError& e ){
        std::cout << "Audio::openStream(): Cannot open stream." << std::endl;
        throw e;
    }
}
0

There are 0 best solutions below