C++ 奇怪的脉冲音频监视器设备行为

C++ 奇怪的脉冲音频监视器设备行为,c++,linux,audio-recording,alsa,pulseaudio,C++,Linux,Audio Recording,Alsa,Pulseaudio,面对奇怪的脉冲音频监控设备(即播放发送至扬声器的声音的音频输入设备)行为。我将真实项目中的代码简化为基于PulseAudio文档的简单示例,只增加了时间限制和读取字节计数。例如,它工作30秒并打印读取字节数。问题是,若在程序运行期间播放某些内容,则字节数会有很大差异。我已经执行了这个程序,并并行执行了bashfor循环,该循环由aplay和shorttada.wav文件组成。差别是9%。为了进一步测试它,我尝试与PulseAudio示例并行运行4个这样的循环,差异甚至更大-34%。但是,如果我用

面对奇怪的脉冲音频监控设备(即播放发送至扬声器的声音的音频输入设备)行为。我将真实项目中的代码简化为基于PulseAudio文档的简单示例,只增加了时间限制和读取字节计数。例如,它工作30秒并打印读取字节数。问题是,若在程序运行期间播放某些内容,则字节数会有很大差异。我已经执行了这个程序,并并行执行了bash
for
循环,该循环由
aplay
和short
tada.wav
文件组成。差别是9%。为了进一步测试它,我尝试与PulseAudio示例并行运行4个这样的循环,差异甚至更大-34%。但是,如果我用长mp3文件运行
mplayer
,而不是用短wav播放多个
aplay
,则没有这种区别,字节数与不播放声音的情况类似

这种行为会导致我实际项目中的声音处理代码失败,所以如果有人能建议如何解决这个问题,我将非常感激

Windows上的类似代码,基于Qt,并使用立体声混音器设备作为PulseAudio监视器的模拟,工作时不会出现此类问题

以下是基于PulseAudio文档示例的代码:

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <iostream>
#include <chrono>

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include <pulse/simple.h>
#include <pulse/error.h>

#define BUFSIZE 1024

using namespace std;
using namespace std::chrono;

int main(int argc, char *argv[])
{
    int duration = 30000;
    int64_t readBytesCounter = 0;
    milliseconds msStart = duration_cast< milliseconds >(system_clock::now().time_since_epoch());

    /* The sample type to use */
    static const pa_sample_spec ss = {
        .format = PA_SAMPLE_S16LE,
        .rate = 44100,
        .channels = 2
    };
    pa_simple *s = NULL;
    int ret = 1;
    int error;
    /* Create the recording stream */
    if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, "alsa_output.pci-0000_00_1b.0.analog-stereo.monitor", "record", &ss, NULL, NULL, &error))) {
        fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
        goto finish;
    }
    for (;;) {
        uint8_t buf[BUFSIZE];
        /* Record some data ... */
        if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) {
            fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
            goto finish;
        }
        readBytesCounter += BUFSIZE;

        milliseconds msCurrent = duration_cast< milliseconds >(system_clock::now().time_since_epoch());
        int elapsed = msCurrent.count() - msStart.count();
        if (elapsed > duration)
        {
            cerr << int(elapsed / 1000) << " seconds elapsed, terminating" << endl;
            cerr << readBytesCounter << " bytes read" << endl;
            goto finish;
        }
    }
    ret = 0;
finish:
    if (s)
        pa_simple_free(s);
    return ret;
}
示例wav文件,我从

以下是测试结果:

测试1。扬声器中没有声音

$ time ./main
30 seconds elapsed, terminating
5323776 bytes read

real    0m30.028s
user    0m0.168s
sys     0m0.388s
测试2。Bash
for
在后台使用短wav文件循环“for i in seq 1 22;do aplay tada.wav;done”。字节数增加为5798912/5323776=1.089次

$ time ./main 
30 seconds elapsed, terminating
5798912 bytes read

real    0m30.023s
user    0m0.120s
sys     0m0.184s
$ time ./main 
30 seconds elapsed, terminating
7129088 bytes read

real    0m30.019s
user    0m0.164s
sys     0m0.196s
测试3。4 Bash
用于在后台使用短wav文件进行循环。字节数的增加是7129088/5323776=1.339倍

$ time ./main 
30 seconds elapsed, terminating
5798912 bytes read

real    0m30.023s
user    0m0.120s
sys     0m0.184s
$ time ./main 
30 seconds elapsed, terminating
7129088 bytes read

real    0m30.019s
user    0m0.164s
sys     0m0.196s
测试4<代码>mplayer
背景为长mp3<代码>5288960/5323776=0.993,即没有显著的字节计数差异

$ time ./main 
30 seconds elapsed, terminating
5288960 bytes read

real    0m30.024s
user    0m0.096s
sys     0m0.204s
尝试执行一组每个测试,平均字节数-类似的差异

$ time ./main 
30 seconds elapsed, terminating
5288960 bytes read

real    0m30.024s
user    0m0.096s
sys     0m0.204s
备注:我的系统配置:

  • 操作系统Ubuntu 16.04.1 amd64
  • pulseaudio 1:8.0-0 uBuntu3.2
  • alsa基础1.0.25+dfsg-0ubuntu5

    • 这是PulseAudio邮件列表中的塔努·卡斯基宁的回复。不是完整的答案,而是一点解释和解决方法:

      您可能遇到了我们在中使用倒带处理的已知错误 监测来源(“已知”在本例中是指症状 已知,但不是确切原因)。当一条流开始播放到 受监视的接收器,接收器将重写其播放缓冲区内容 (这称为“倒带”),这会导致监视器出现故障 来源(一些额外的音频显然被推到录音中 从你的实验判断,这是一条小溪)。我希望有一天能解决这个问题, 但在不久的将来,我似乎没有时间做这件事。如果 你有时间和动机去调查和修复这个bug 那太棒了

      作为一种解决方法,您可能可以通过减小播放缓冲区大小来减小错误的大小。要做到这一点,请通过 tsched_buffer_size=X到模块udev detect in/etc/pulse/default.pa (将X替换为以字节为单位的缓冲区大小)。”