面对奇怪的PulseAudio监视器设备(即播放声音发送到扬声器的audioinput设备)的行为。 我已经减less了从我的真实项目代码到基于PulseAudio代码的简单示例https://freedesktop.org/software/pulseaudio/doxygen/parec-simple_8c-example.html ,我只添加了时间限制和读取字节数数。 它工作例如30秒,并打印读取字节数。 问题是如果在程序运行期间播放某些内容,那么字节计数将大不相同。 我已经执行这个程序,并行执行bash for
循环包含tada.wav
与短tada.wav
文件。 差异是9%。 为了进一步testing,我尝试了与PulseAudio示例并行运行4个这样的循环,差异甚至更大–34%。 但是,如果不是用短wav播放几个aplay
,而是用长mp3文件运行mplayer
– 没有这样的区别,字节数与没有播放声音的情况类似。
这样的行为会导致声音处理代码在我的真实项目中失败,所以如果有人可以build议如何解决它 – 我将非常感激。
在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; }
它可以用以下命令build立:
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
这里是testing结果:
testing1.扬声器没有声音
$ time ./main 30 seconds elapsed, terminating 5323776 bytes read real 0m30.028s user 0m0.168s sys 0m0.388s
testing2. bash for
循环“for i seq 1 22;做aplay tada.wav;完成”与短的wav文件在后台。 字节数增加为5798912 / 5323776 = 1.089
倍。
$ time ./main 30 seconds elapsed, terminating 5798912 bytes read real 0m30.023s user 0m0.120s sys 0m0.184s
testing3. 4在后台使用短wav文件的Bash循环。 字节数增加7129088 / 5323776 = 1.339
倍。
$ time ./main 30 seconds elapsed, terminating 7129088 bytes read real 0m30.019s user 0m0.164s sys 0m0.196s
testing4.在后台长mp3的mplayer
。 5288960 / 5323776 = 0.993
,即无重要字节计数差异。
$ time ./main 30 seconds elapsed, terminating 5288960 bytes read real 0m30.024s user 0m0.096s sys 0m0.204s
试图执行一组每个testing,平均字节数 – 类似的差异。
PS:我的系统configuration:
这里是来自PulseAudio邮件列表的Tanu Kaskinen的回复https://lists.freedesktop.org/archives/pulseaudio-discuss/2017-January/027412.html 。 不是一个完整的答案,但有一点解释和解决方法:
你可能会遇到一个我们已经知道的在监视源(“已知”意味着在这种情况下,症状是已知的,但不是确切的原因)处理的错误。 当一个数据流开始播放到受监控的信宿时,信宿将重写其播放缓冲区的内容(这称为“倒带”),这会在监听源中产生一个毛刺(一些额外的音频显然会被推到记录流中,从你的实验)。 我希望有一天能够解决这个问题,但是不久的将来我就不会有时间了。 如果你有时间和动力来调查和修复这个bug,那就太棒了。
作为解决方法,您可以通过减少播放缓冲区大小来减少错误的大小。 要做到这一点,请在/etc/pulse/default.pa中将tsched_buffer_size = X传递给module-udev-detect(用缓冲区大小以字节替换X)。