Strange PulseAudio监控设备行为

时间:2017-01-25 08:48:31

标签: c++ linux audio-recording alsa pulseaudio

面向奇怪的PulseAudio监听设备(即播放发送给扬声器的声音的音频输入设备)行为。我将实际项目中的代码减少为基于PulseAudio docs https://freedesktop.org/software/pulseaudio/doxygen/parec-simple_8c-example.html代码的简单示例,我只添加了时间限制和读取字节数。它工作例如30秒并打印读取字节数。问题是如果在程序运行期间播放某些内容,字节数会大不相同。我已执行此程序并同时执行bash for循环,其中aplay包含短tada.wav个文件。差异是9%。为了更多地测试它,我尝试与PulseAudio示例并行运行4个这样的循环,差异甚至更多--34%。但是,如果使用短wav而不是几个aplay我使用长mp3文件运行mplayer - 没有这样的差异,字节数与没有播放声音的情况类似。

这种行为导致我的真实项目中的声音处理代码失败,所以如果有人可以建议如何解决它 - 我将非常感激。

类似的代码在Windows,基于Qt和使用立体声混音器设备作为PulseAudio监视器的模拟工作没有这样的问题。

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

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

可以使用以下命令构建:

g++ -o main main.cpp -lpulse -lpulse-simple -std=c++11

示例wav文件来自http://d0.waper.ru/f/462151/23/HgDwimvX37CwxWqW38eywg%2C1485353628/7d74/9/462151.wav/tada.wav

以下是测试结果:

测试1.扬声器没有声音

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

real    0m30.028s
user    0m0.168s
sys     0m0.388s

测试2. Bash for循环&#34; for seq 1 22;做aplay tada.wav;完成&#34;在背景中使用短wav文件。字节数增加5798912 / 5323776 = 1.089次。

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

real    0m30.023s
user    0m0.120s
sys     0m0.184s

测试3.在后台使用短wav文件进行4次Bash for循环。字节数增加7129088 / 5323776 = 1.339次。

$ 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

尝试执行一组每个测试,平均字节数 - 相似的差异。

P.S。:我系统的配置:

  • OS Ubuntu 16.04.1 amd64
  • pulseaudio 1:8.0-0ubuntu3.2
  • alsa-base 1.0.25 + dfsg-0ubuntu5

1 个答案:

答案 0 :(得分:1)

以下是来自PulseAudio邮件列表COPY files的Tanu Kaskinen的回复。不完整的答案,但一点点解释和解决方法:

  

你可能正在使用倒带处理中遇到的已知错误   监测来源(“已知”在这种情况下意味着症状   已知,但不是确切的原因)。当一个流开始播放时   监视接收器,接收器将重写其回放缓冲区内容   (这称为“倒带”),这会导致显示器出现故障   来源(一些额外的音频显然被推到录音   流,从你的实验判断)。我希望有一天能解决这个问题,   但看起来我不会在不久的将来有时间。如果   你有时间和动力来调查和修复这个bug   会很棒的。

     

作为一种解决方法,您可以通过减少播放缓冲区大小来减少错误的大小。要做到这一点,通过   tsched_buffer_size = X到/etc/pulse/default.pa中的module-udev-detect   (用缓冲区大小(以字节为单位)替换X。“