用于在 Raspberry Pi 上读取和播放 WAV 文件的 ALSA 应用程序

Posted

技术标签:

【中文标题】用于在 Raspberry Pi 上读取和播放 WAV 文件的 ALSA 应用程序【英文标题】:ALSA application to read and play a WAV file on Raspberry Pi 【发布时间】:2015-03-03 17:00:47 【问题描述】:

尝试学习 ALSA 音频层,最终为 Raspberry Pi 平台编写 ALSA 设备驱动程序。从简单的开始,我将来自 ALSA 项目站点和其他在线资源的各种样本粘合在一起,以做最简单的事情:读取 WAV 文件并在默认声音设备上播放它。我无法让这个简单的 C 示例工作。

我正在使用 libsndfile 进行所有 WAV 文件读取/标题解码。我验证了我读入缓冲区的样本是正确的(针对将样本值转储到文本文件的 sndfile-to-text 应用程序验证了程序读取的前 400K 样本)。所以我知道我的缓冲区包含正确的数据,问题一定出在我将它传递给 ALSA API 的方式上。

运行时,它仅在正确的通道中产生声音,并且失真/浑浊 - 几乎无法辨认。顺便说一句,“aplay”应用程序完美地播放相同的 WAV 文件,并报告该文件是 16 位签名 LE、44100Hz、立体声,这也与我的应用程序报告的内容相匹配。在 Raspberry Pi 上运行它。

为了节省空间,我将 C 程序精简到最少,但我验证了所有 API 调用的正确返回码。为什么这个简单的 ALSA 应用程序不能产生正确的声音?

#include <alsa/asoundlib.h>
#include <stdio.h>
#include <sndfile.h>

#define PCM_DEVICE "default"

int main(int argc, char **argv) 

    snd_pcm_t *pcm_handle;
    snd_pcm_hw_params_t *params;
    snd_pcm_uframes_t frames;
    int dir, pcmrc;

    char *infilename = "/home/pi/shortsample.wav";
    int* buf = NULL;
    int readcount;

    SF_INFO sfinfo;
    SNDFILE *infile = NULL;

    infile = sf_open(infilename, SFM_READ, &sfinfo);
    fprintf(stderr,"Channels: %d\n", sfinfo.channels);
    fprintf(stderr,"Sample rate: %d\n", sfinfo.samplerate);
    fprintf(stderr,"Sections: %d\n", sfinfo.sections);
    fprintf(stderr,"Format: %d\n", sfinfo.format);

    /* Open the PCM device in playback mode */
    snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0);

    /* Allocate parameters object and fill it with default values*/
    snd_pcm_hw_params_alloca(&params);
    snd_pcm_hw_params_any(pcm_handle, params);
    /* Set parameters */
    snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_S16_LE);
    snd_pcm_hw_params_set_channels(pcm_handle, params, sfinfo.channels);
    snd_pcm_hw_params_set_rate(pcm_handle, params, sfinfo.samplerate, 0);

    /* Write parameters */
    snd_pcm_hw_params(pcm_handle, params);

    /* Allocate buffer to hold single period */
    snd_pcm_hw_params_get_period_size(params, &frames, &dir);
    fprintf(stderr,"# frames in a period: %d\n", frames);

    fprintf(stderr,"Starting read/write loop\n");
    buf = malloc(frames * sfinfo.channels * sizeof(int));
    while ((readcount = sf_readf_int(infile, buf, frames))>0) 

        pcmrc = snd_pcm_writei(pcm_handle, buf, readcount);
        if (pcmrc == -EPIPE) 
            fprintf(stderr, "Underrun!\n");
            snd_pcm_prepare(pcm_handle);
        
        else if (pcmrc < 0) 
            fprintf(stderr, "Error writing to PCM device: %s\n", snd_strerror(pcmrc));
        
        else if (pcmrc != readcount) 
            fprintf(stderr,"PCM write difffers from PCM read.\n");
        

    
    fprintf(stderr,"End read/write loop\n");

    snd_pcm_drain(pcm_handle);
    snd_pcm_close(pcm_handle);
    free(buf);

    return 0;

【问题讨论】:

【参考方案1】:

必须检查所有可能失败的snd_ 函数的返回值。

S16_LE 格式每个样本有两个字节,但int 有四个。 请改用shortsf_readf_short

【讨论】:

我的真实代码确实检查了所有返回值,我只是为了发布而缩短了它。从 int 更改为 short 修复了它,WAV 现在可以正确播放...谢谢!

以上是关于用于在 Raspberry Pi 上读取和播放 WAV 文件的 ALSA 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 应用程序中播放 Raspberry Pi h264 流

OpenCV VideoCapture 在 Raspberry Pi 2 上总是失败

用于Raspberry Pi的Tkinter / Canvas-based kiosk-like程序

如何使用 Python 在 Raspberry Pi 上杀死 omxplayer 播放器

MediaPlayer无法在Android Things Raspberry Pi 3上运行

PyAudio 在 Raspberry Pi 上引发警告