c++ - 加入 Portaudio 和 Opus

标签 c++ portaudio opus

您好,我正在尝试从打开的 PortAudio 流中获取声音,使用 opus 对其进行编码、解码并再次使用 portaudio 进行再现。

我将此作为原型(prototype)只是为了尝试理解该系统的机制,因此对遵循此具体流程没有真正的兴趣。

问题是,portaudio 提供了 OPUS 需要帧的缓冲区。 Mi 认为在 portaudio 方面引导我到这个:

err = (Pa_ReadStream(stream, readBuffer, FRAMES_PER_BUFFER));
        if (err = paNoError){
            qDebug()<<"Fail read";
            qDebug()<<Pa_GetErrorText(err);
          // blockingRecord = false;
        }
       while (pos<FRAMES_PER_BUFFER){
            memcpy(frameBuffer,readBuffer+(pos*FRAME_SIZE*NUM_CHANNELS),FRAME_SIZE*CHANNELS);
            compressedSound = om.encodeOpus(frameBuffer);
            unCompressedSound = om.decodeOpus(compressedSound);
            memcpy(readBuffer+(pos*FRAME_SIZE*NUM_CHANNELS),unCompressedSound,FRAME_SIZE*CHANNELS);
            pos++;
        }
        pos = 0;
        err = (Pa_WriteStream(stream, readBuffer, FRAMES_PER_BUFFER));
        if (err != paNoError)
        {
            qDebug() << "FAIL WRITE";
            qDebug()<<Pa_GetErrorText(err);
            //blockingRecord = false;
        }

在 OPUS 方面:

unsigned char * OpusManager::encodeOpus(unsigned char *frame){
    memcpy(encoded, frame, FRAME_SIZE*CHANNELS);
    int ret = opus_encode(enc, encoded, FRAME_SIZE, compressed_buffer, encoded_data_size);
    if (ret<0){
        qDebug()<<"Failure while compressing sound";
        return NULL;
    }
    return (compressed_buffer);
}

unsigned char * OpusManager::decodeOpus(unsigned char *frame){
    int ret= opus_decode(dec, frame, encoded_data_size, decoded, FRAME_SIZE, 0);
    if (ret<0){
        qDebug()<<"Failure while decompressing sound";
        return NULL;
    }
    memcpy(uncompressed_buffer, decoded, FRAME_SIZE*CHANNELS);
    return (uncompressed_buffer);
}

没有错误就没有诱惑和完美的声音。使用编码,我在 PA_Writestream 调用之前没有得到任何错误,在那里我得到一个“输出下溢”PaError。我想采用我实现的框架的方式一定是错误的,但找不到可以帮助我解决这个问题的信息。

最佳答案

您对 Opus 的 frame_size 参数对 opus_encode 和 opus_decode 的解释似乎不正确。如果我正确理解您的代码,您正在记录一个大小为 FRAMES_PER_BUFFER 帧的数据包,然后尝试将其转换为 N 个大小为 FRAME_SIZE 的数据包。相反,在我看来,Opus 想要将您的 FRAMES_PER_BUFFER 数据包转换为另一个帧数相等的数据包,并且在这样做时,仅使用它的 FRAME_SIZE 参数作为编码过程的某种质量控制参数。您将在下面找到一个完整的示例,我相信它可以满足您的需求。在 encode()/decode() 中使用“480”魔数(Magic Number),听听音频质量的变化。

int opusErr;
PaError paErr;
std::string s;

int const channels = 2;
int const bufferSize = 480;
int const sampleRate = 48000;
int const durationSeconds = 5;

opus_int32 enc_bytes;
opus_int32 dec_bytes;
int framesProcessed = 0;

std::vector<unsigned short> captured(bufferSize * channels);
std::vector<unsigned short> decoded(bufferSize * channels);
// * 2: byte count, 16 bit samples
std::vector<unsigned char> encoded(bufferSize * channels * 2);

// initialize opus
OpusEncoder* enc = opus_encoder_create(
    sampleRate, channels, OPUS_APPLICATION_AUDIO, &opusErr);
if (opusErr != OPUS_OK)
{
    std::cout << "opus_encoder_create failed: " << opusErr << "\n";
    std::getline(std::cin, s);
    return 1;
}

OpusDecoder* dec = opus_decoder_create(
    sampleRate, channels, &opusErr);
if (opusErr != OPUS_OK)
{
    std::cout << "opus_decoder_create failed: " << opusErr << "\n";
    std::getline(std::cin, s);
    return 1;
}

// initialize portaudio
if ((paErr = Pa_Initialize()) != paNoError)
{
    std::cout << "Pa_Initialize failed: " << Pa_GetErrorText(paErr) << "\n";
    std::getline(std::cin, s);
    return 1;
}

PaStream* stream = nullptr;
if ((paErr = Pa_OpenDefaultStream(&stream,
      channels, channels, paInt16, sampleRate,
      bufferSize, nullptr, nullptr)) != paNoError)
{
    std::cout << "Pa_OpenDefaultStream failed: " << Pa_GetErrorText(paErr) << "\n";
    std::getline(std::cin, s);
    return 1;
}

// start stream
if ((paErr = Pa_StartStream(stream)) != paNoError) 
{
    std::cout << "Pa_StartStream failed: " << Pa_GetErrorText(paErr) << "\n";
    std::getline(std::cin, s);
    return 1;
}

// capture, encode, decode & render durationSeconds of audio
while (framesProcessed < sampleRate * durationSeconds)
{
    if ((paErr = Pa_ReadStream(stream, 
        captured.data(), bufferSize)) != paNoError)
    {
        std::cout << "Pa_ReadStream failed: " << Pa_GetErrorText(paErr) << "\n";
        std::getline(std::cin, s);
        return 1;
    }

    if ((enc_bytes = opus_encode(enc, reinterpret_cast<opus_int16 const*>(
        captured.data()), 480, encoded.data(), encoded.size())) < 0)
    {
        std::cout << "opus_encode failed: " << enc_bytes << "\n";
        std::getline(std::cin, s);
        return 1;
    }

    if ((dec_bytes = opus_decode(dec, encoded.data(), enc_bytes,
        reinterpret_cast<opus_int16*>(decoded.data()), 480, 0)) < 0)
    {
        std::cout << "opus_decode failed: " << dec_bytes << "\n";
        std::getline(std::cin, s);
        return 1;
    }

    if ((paErr = Pa_WriteStream(stream, decoded.data(), bufferSize)) != paNoError)
    {
        std::cout << "Pa_WriteStream failed: " << Pa_GetErrorText(paErr) << "\n";
        std::getline(std::cin, s);
        return 1;
    }

    framesProcessed += bufferSize;
}

// stop stream
if ((paErr = Pa_StopStream(stream)) != paNoError)
{
    std::cout << "Pa_StopStream failed: " << Pa_GetErrorText(paErr) << "\n";
    std::getline(std::cin, s);
    return 1;
}

// cleanup portaudio
if ((paErr = Pa_CloseStream(stream)) != paNoError) 
{
    std::cout << "Pa_CloseStream failed: " << Pa_GetErrorText(paErr) << "\n";
    std::getline(std::cin, s);
    return 1;
}

if ((paErr = Pa_Terminate()) != paNoError) 
{
    std::cout << "Pa_Terminate failed: " << Pa_GetErrorText(paErr) << "\n";
    std::getline(std::cin, s);
    return 1;
}

// cleanup opus
opus_decoder_destroy(dec);
opus_encoder_destroy(enc);

关于c++ - 加入 Portaudio 和 Opus,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25475160/

相关文章:

node.js - 通过 opusscript 播放音频时出现 RangeError

android - 将cpp类添加到android项目中

c++ - 与 Boost 和 ncurses 的静态链接

c++ - 编写一个 makefile 以在调试版本中使用 -D_GLIBCXX_DEBUG

c++ - 随机数函数未正确随机化 (C++)

portaudio - 回调调用之间的时间间隔?

java - SHA-1 需要多长时间来创建哈希?

c - 检测特定频率和持续时间的蜂鸣声

python - 使用支持 ASIO 的 portaudio 构建 pyaudio

c++ - 使用 libwebm (VP8/Opus) 的无声视频 -- 同步音频 --