简体   繁体   English

使用 Portaudio C++ 进行实时音频处理

[英]Real time audio processing with Portaudio C++

I'm trying to make a C++ application to transmit audio via a VoIP protocol between 2 clients (using UDP).我正在尝试制作一个 C++ 应用程序,通过 VoIP 协议在 2 个客户端(使用 UDP)之间传输音频。

I'm working with Portaudio C library and I have issues to encapsulate this lib.我正在使用 Portaudio C 库,但在封装这个库时遇到了问题。 In order to send the recorded audio to another client, I'd like to get sound samples as it is recorded (real time).为了将录制的音频发送到另一个客户端,我想在录制时(实时)获取声音样本。

For the moment I can only record sound, and then, play what I recorded.目前我只能录制声音,然后播放我录制的内容。 I'm not comfortable at all with this library and any help would be appreciated.我对这个图书馆一点都不舒服,任何帮助将不胜感激。

Here's what I've done so far.这是我到目前为止所做的。

Audio.cpp -> Callback methods : Audio.cpp -> 回调方法

static int PaRecordCallback(const void *input, void *output, unsigned long frameCount, \
    const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
    Audio *audio = reinterpret_cast<Audio *>(userData);

    return audio->RecordCallback(input, output, frameCount, timeInfo, statusFlags);
}

static int PaPlayCallback(const void *input, void *output, unsigned long frameCount, \
    const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
    Audio *audio = reinterpret_cast<Audio *>(userData);

    return audio->PlayCallback(input, output, frameCount, timeInfo, statusFlags);
}

int Audio::RecordCallback(const void *input, void *output, unsigned long &frameCount, \
    const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags &statusFlags)
{
    std::cout << "Frame index:\t\t" << _recordedFrameIndex << std::endl << "Max frame index:\t" << _maxFrameIndex << std::endl << "--------------" << std::endl;
    const SAMPLE *rptr = static_cast<const SAMPLE *>(input);
    SAMPLE *wptr = &_recordedSamples[_recordedFrameIndex * NUM_CHANNELS];
    unsigned long framesLeft = _maxFrameIndex - _recordedFrameIndex;
    unsigned long framesToCalc;
    int finished;

    if (framesLeft < frameCount) {
        framesToCalc = framesLeft;
        finished = paComplete;
    } else {
        framesToCalc = frameCount;
        finished = paContinue;
    }
    for (unsigned long i = 0; i < framesToCalc; i++) {
        *wptr++ = *rptr++;
        if (NUM_CHANNELS == 2)
            *wptr++ = *rptr++;
    }
    _recordedFrameIndex += framesToCalc;
    return finished;
}

int Audio::PlayCallback(const void *input, void *output, unsigned long &frameCount, \
    const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags &statusFlags)
{
    SAMPLE *rptr = &_recordedSamples[_playedFrameIndex * NUM_CHANNELS];
    SAMPLE *wptr = static_cast<SAMPLE *>(output);
    unsigned long framesLeft = _maxFrameIndex - _playedFrameIndex;
    unsigned int i;
    int finished;

    if (framesLeft < frameCount) {
        for (i = 0; i < framesLeft; i++) {
            *wptr++ = *rptr++;
            if (NUM_CHANNELS == 2)
                *wptr++ = *rptr++;
        }
        for (; i < frameCount; i++) {
            *wptr++ = 0;
            if (NUM_CHANNELS == 2)
                *wptr++ = 0;
        }
        _playedFrameIndex += framesLeft;
        finished = paComplete;
    } else {
        for (i = 0; i < frameCount; i++) {
            *wptr++ = *rptr++;
            if (NUM_CHANNELS == 2)
                *wptr++ = *rptr++;
        }
        _playedFrameIndex += frameCount;
        finished = paContinue;
    }
    return finished;
}

Audio.cpp -> Record and Play methods : Audio.cpp -> 录制和播放方法

void Audio::Record()
{
    if (!_recordStream) {
        OpenRecordStream();
        _recordedFrameIndex = 0;
        _err = Pa_StartStream(_recordStream);
        if (_err != paNoError)
            AudioError("Audio::Record -> Pa_StartStream()");
        std::cout << "Audio record stream started." << std::endl;
        std::cout << "Recording ..." << std::endl;
        _recording = true;
        fflush(stdout);
    } else if (_recording)
        Pa_Sleep(1000);
}

void Audio::Play()
{
    if (!_playStream) {
        OpenPlayStream();
        _playedFrameIndex = 0;
        _err = Pa_StartStream(_playStream);
        if (_err != paNoError)
            AudioError("Audio::Play -> Pa_StartStream()");
        std::cout << "Audio play stream started." << std::endl;
        std::cout << "Playing ..." << std::endl;
        _playing = true;
        fflush(stdout);
    } else if (_playing)
        Pa_Sleep(500);
}

Audio.hpp -> Class audio : Audio.hpp -> 类音频

#include <portaudio.h>

typedef int16_t SAMPLE;

#define PA_SAMPLE_TYPE      paInt16
#define PRINTF_S_FORMAT     "%.8f"
#define SAMPLE_RATE         44100
#define SAMPLE_SILENCE      0.0f
#define FRAMES_PER_BUFFER   1
#define NUM_SECONDS         5
#define NUM_CHANNELS        2
#define DITHER_FLAG         0
#define WRITE_TO_FILE       0
#define SAMPLE_SIZE         NUM_SECONDS * SAMPLE_RATE * NUM_CHANNELS

class Audio
{
    public:
        Audio();
        ~Audio();

        void Record();
        void Play();

        void OpenRecordStream();
        void OpenPlayStream();

        void CloseRecordStream();
        void ClosePlayStream();

        const bool &isRecording() const;
        const bool &isPlaying() const;

        const PaStream *GetRecordStream() const;
        const PaStream *GetPlayStream() const;

        void GetSamples(SAMPLE *);
        void SetSamples(SAMPLE *);

        int RecordCallback(const void *, void *, unsigned long &, \
            const PaStreamCallbackTimeInfo *, PaStreamCallbackFlags &);
        int PlayCallback(const void *, void *, unsigned long &, \
            const PaStreamCallbackTimeInfo *, PaStreamCallbackFlags &);
        bool _recording;
        bool _playing;

    protected:
    private:
        // Functions:
        void AudioError(const std::string &);

        // Variables:
        PaError _err;
        PaStream *_playStream;
        PaStream *_recordStream;
        SAMPLE *_samplesToPlay;
        SAMPLE *_recordedSamples;
        unsigned long _recordedFrameIndex;
        unsigned long _playedFrameIndex;
        unsigned long _maxFrameIndex;
        PaStreamParameters _inputParameters;
        PaStreamParameters _outputParameters;
};

I apologize if it's very long, but I want you to have all the necessary informations to understand my problem.如果它很长,我深表歉意,但我希望您拥有所有必要的信息来理解我的问题。 I don't ask questions often, so I really need some help here.我不经常问问题,所以我真的需要一些帮助。

Thank you.谢谢你。

You need to use a full duplex callback to record and play in real time so you catch the input buffer in chunks for your recordings (when you want to record) and send the recordings (or even the incoming sound) to the output buffer, also in chunks.您需要使用全双工回调来实时录制和播放,以便您以块的形式捕获录音的输入缓冲区(当您想要录制时)并将录音(甚至传入的声音)发送到输出缓冲区,也在大块。 The chunksize normally would be from 64 to 4096 frames, and each frame normally would contain 2 samples (channel L and channel R)块大小通常为 64 到 4096 帧,每帧通常包含 2 个样本(通道 L 和通道 R)

The full duplex callback is a kind of circular buffer that brings you x frames when the input buffer is filled by the ADC and where you fill the output buffer with x frames for being ready for when the DAC asks for it.全双工回调是一种循环缓冲区,当输入缓冲区被 ADC 填充时,它会为您带来 x 帧,并在其中用 x 帧填充输出缓冲区,以便在 DAC 请求时准备就绪。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM