0

我正在尝试通过 RtAudio Api 从声卡接收音频。它有一个回调函数,一旦音频接收到足够的字节,就会调用该函数,然后用户可以将数据复制到自定义对象。这个自定义对象可以通过指针发送到回调。我封装 RtAudio 的类如下所示:

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();
};

这是从音频线程内部调用的静态函数的实现

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;
}

我在线程之间共享的自定义对象属于 AudioData 类,如下所示:

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

public:
    AudioData();
    ~AudioData();

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

音频对象嵌入到 Recorder-Object 中,然后读取 Audio 的 AudioData 成员变量中的缓冲区。

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 );
    };

函数 record(..) 如下所示:

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;
}

我读到堆是保存线程之间共享的数据的地方,但在 rtaudio 的示例中,他们使用在堆栈上分配的全局变量将数据推送到Link。所以我有点困惑。我们很乐意接受帮助!

编辑:当我调试我的应用程序时。我可以看到音频线程的输入函数被调用并写入缓冲区。记录功能也按预期工作。只有缓冲区(AudioData)似乎没有任何数据......

Edit2:这是我在 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;
    }
}
4

0 回答 0