首页 > 解决方案 > Pulseaudio 的 pa_simple_read() 输出 0-5 或 248-255

问题描述

我正在尝试学习使用 pulseaudio 的 Simple API for C。当我意识到我收到的值没有意义时,我开始有所收获。起初他们在 ~64-66 和 0 之间交换。即像这样的“65 0 64 0 65 0 66 0 64 0”。然后,过了一会儿(也许我做了一些事情来促使这种变化?)我开始得到非常低或非常高的值,没有介于两者之间。更奇怪的是,这些值是 4 的系列。即 n 个小数后面跟着 m 个大数,其中 n 和 m 是 4 的倍数。

偶尔会有一些数字不是那么极端,但非常罕见。

这是代码:

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

int main() {
    pa_simple *s;
    pa_sample_spec ss;

    ss.format = PA_SAMPLE_S16NE;
    ss.channels = 2;
    ss.rate = 44100;

    int error;

    s = pa_simple_new(
        NULL,
        "PulseAudioTest",
        PA_STREAM_RECORD,
        NULL,
        "My test",
        &ss,
        NULL,
        NULL,
        &error
    );

    if(!s)
        printf("error: %s", pa_strerror(error));

    uint8_t buf[1024];

    while(1) {
        pa_simple_read(s, buf, sizeof(buf), NULL);

        for(int i = 0; i < sizeof(buf); i++)
            printf("%d,", buf[i]);
        printf("\n-----------\n"); // just here for me separating the data, im not playing this back
    }

    if(s)
        pa_simple_free(s);
    else
        printf("error\n");

    return 0;
}

和一些示例输出:https ://pastebin.com/FgFekmf0 。

我也尝试过http://0pointer.de/lennart/projects/pulseaudio/doxygen/parec-simple_8c-example.html中的 parec.c 和 pacat.c ,但是当我播放录音时,它只是静态的。我还尝试切换默认接收器并通过默认和非默认接收器播放音乐。

此外,如果有人对在哪里学习使用它(以及音频的一般工作方式)有任何建议,我会喜欢建议(即使只是我对谷歌的一个术语 - 我花了一段时间才知道输出是“pcm”,甚至是什么)。

标签: clinuxaudiopulseaudio

解决方案


为什么代码打印值以零分隔,即“65 0 64 0 65 0 66 0 64 0 ...”?

通常,默认的录制音频规范是编写在您的代码中的规范:

/* Each sample is Signed 16-bit integer recorded in little-endian (the Native
 * Endian in intel x86 and x86-64).
 */
ss.format = PA_SAMPLE_S16NE;
/* There are 2 channels. */
ss.channels = 2;
/* Sample rate is 44100 Hz. */
ss.rate = 44100;
/* This is how audio samples are arranged. Given a stream of samples
 *
 * ADDR      0x0    0x2    0x4    0x6    0x8    0xA
 * SAMPLE    12345, 13579,  -185,    -3, -9876, -9999, ...
 *          |-----||-----||-----||-----||-----||-----|
 *            CH1    CH2    CH1    CH2    CH1    CH2
 *          |------------||------------||------------|
 *              FRAME         FRAME         FRAME
 */

给定一个值为 65 的样本,其十六进制为 0x0041,使用little-endian,它将存储41 00在内存中。buf请注意,您的代码逐字节打印,这就是您得到65 0. 因此,对于样本流 65、64、65,...,您将打印65 0 64 0 65 0 ....

试过parecand pacat,但只有静态噪音。如何让它发挥作用?

确保音频输入(麦克风)未静音且音量合适。运行pavucontrol,进入“输入设备”选项卡,取消麦克风静音,并调整音量。发出一些噪音,您应该会看到信号条(在音量条下方)跳跃。

PulseAudio 附带了几个录音和播放实用程序(libpulsearch 中的软件包pulseaudio-utils,debian 中的软件包):parec, parecord, pacat, paplay. 试试看。他们应该工作。

播放parec用录制的声音pacat

$ parec sample1.wav
^C
$ pacat sample1.wav

播放parecord用录制的声音paplay

$ parecord sample2.wav
^C
$ paplay sample2.wav

它们之间的主要区别在于,parecpacat不写和读声音文件中的头文件,而parecordpaplay做。

成功运行这些命令后,您可以尝试从官方存储库中编译pacat-simple.cparec-simple.c,位于.src/tests/

$ gcc pacat-simple.c -o pacat-simple -lpulse-simple -lpulse
$ gcc parec-simple.c -o parec-simple -lpulse-simple -lpulse

之后,尝试运行它们。

$ ./parec-simple > sample3.wav
^C
$ ./pacat-simple < sample3.wav

它应该工作。现在您可以开始检查和玩parec-simple.cand了pacat-simple.c。请注意,parec-simplepacat-simple不要在声音文件中写入和读取标题,就像parecand一样pacat

通过对 while 循环进行一些修改,您的代码将像parec-simple. 修改版(文件my-parec.c):

while(1) {
    pa_simple_read(s, buf, sizeof(buf), NULL);

    //for(int i = 0; i < sizeof(buf); i++)
    //    printf("%d,", buf[i]);
    //printf("\n-----------\n"); // just here for me separating the data, im not playing this back

    fwrite(buf, 1, sizeof(buf), stdout);
}

编译并运行它:

$ gcc my-parec.c -o my-parec -lpulse-simple -lpulse
$ my-parec > sample4.wav
^C

有没有使用 PulseAudio C API 的例子?

此外parec-simple.cpacat-simple.c如果您已经阅读了 PulseAudio异步 API 文档,您可以查看下面的pacat.csrc/utils/作为示例。实际上parecparecordpaplay只是pacat源代码的符号链接pacat.c

声音文件的标题是什么?上面提到过。

如果声音文件仅包含数据,则播放器无法知道该文件的音频规范,例如通道数、每个样本的位数(位深度)、字节序等。在这种情况下,它只能假设默认规范是使用过,可能不是这样。因此,为了将这些属性保存到声音文件中,我们在该文件中创建了一个标题部分来存储它们。有关标头格式,请参阅WAV PCM 声音文件格式音频文件格式规范

要管理声音文件的标头部分,可以使用C 库libsndfile 。pacat.c确实使用该库来读取和写入带有标题的声音文件。libsndfile还提供了一些实用程序,例如sndfile-info帮助我们检查声音文件。这些实用程序包含libsndfile在 arch中的包和sndfile-programsdebian 中的包中。

$ sndfile-info sample2.wav
========================================
File : sample2.wav
Length : 698668
RIFF : 698660
WAVE
fmt  : 16
  Format        : 0x1 => WAVE_FORMAT_PCM
  Channels      : 2
  Sample Rate   : 44100
  Block Align   : 4
  Bit Width     : 16
  Bytes/sec     : 176400
data : 698624
End

----------------------------------------
Sample Rate : 44100
Frames      : 174656
Channels    : 2
Format      : 0x00010002
Sections    : 1
Seekable    : TRUE
Duration    : 00:00:03.960
Signal Max  : 17226 (-5.59 dB)

要将标题添加到没有标题的声音文件中,我能想到的最简单的方法是,从另一个有标题的声音文件中复制标题,将其添加到没有标题的声音文件中,然后使用十六进制编辑器更改一些值(我使用ghex)。编辑标题中的数字字段时请注意字节顺序。

# In my system, sample2.wav has the simplest header which is 44 bytes long.
head -c 44 sample2.wav > header.wav
# Prepend header to sound file without header.
cat header.wav sample3.wav > sample3-with-header.wav
# Edit the chunk size field and data chunk size field.
ghex sample3-with-header.wav

其他参考

PulseAudio 在后台很好地解释了 PulseAudio 服务器。它关于缓冲和延迟的主题有助于理解 API 的使用,例如pa_stream_begin_write()pa_stream_write().


推荐阅读