查看: 827|回复: 0

[评测分享] 【米尔-STM32MP135入门级开发板测评】 5.音频播放测试

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-19 21:11
  • 签到天数: 66 天

    连续签到: 1 天

    [LV.6]常住居民II

    发表于 2023-10-3 22:56:44 | 显示全部楼层 |阅读模式
    分享到:
    1.  ALSA 概述
    ALSA(Advanced Linux Sound Architecture)是Linux操作系统的音频子系统,负责处理声音和音频。作为Linux内核的一部分,它提供了全面的音频支持,允许应用程序与音频硬件进行通信,实现音频录制、播放和处理。ALSA的关键特点包括多硬件支持,可适用于内置声卡、USB音频设备等各种硬件。它专注于提供低延迟音频处理,因此适用于实时音频应用,如音乐制作和游戏。此外,ALSA具有模块化设计,可轻松添加或替换音频驱动程序和组件,以满足不同需求。它还提供用户空间工具和库,用于配置和操作音频设备。尽管最初设计为Linux,但一些ALSA组件已移植到其他操作系统,实现跨**音频支持。总之,ALSA是Linux上强大的音频架构,为各种音频应用提供了可靠的音频处理功能。
    1.png



    音频示意图
    2.  lsa-lib库
    alsa-lib 是一套 Linux 应用层的 C 语言函数库,为音频应用程序开发提供了一套统一、标准
    的接口,应用程序只需调用这一套 API 即可完成对底层声卡设备的操控,譬如播放与录音。
    用户空间的 alsa-lib对应用程序提供了统一的API 接口,这样可以隐藏驱动层的实现细节,简化了应用
    程序的实现难度、无需应用程序开发人员直接去读写音频设备节点。
    在 Linux 内核设备驱动层、基于 ALSA 音频驱动框架注册的 sound 设备会在/dev/snd 目录下生成相应的
    设备节点文件,譬如MP135 开发板出厂系统/dev/snd目录下有如下文件:
    2.png

    3.硬件连接
    如下所示为板子的外设接口示意图,引出了音频接口,通过耳机接口引出,需要将耳机线连接到Audio接口。

    3.png


    电路原理图:
    4.png


    实物连接如下所示:
    5.jpg

    4.应用编程测试
    #include<stdio.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>
    #include<alsa/asoundlib.h>
    #definePCM_PLAYBACK_DEV "hw:0,0"
    static snd_pcm_t*pcm = NULL; //pcm 句柄
    static unsignedint buf_bytes; //应用程序缓冲区的大小(字节为单位)
    static void *buf= NULL; //指向应用程序缓冲区的指针
    static int fd =-1; //指向 WAV 音频文件的文件描述符
    staticsnd_pcm_uframes_t period_size = 1024; //周期大小(单位: 帧)
    static unsignedint periods = 4; //周期数(设备驱动层 buffer 的大小)
    static int snd_pcm_init(void)
    {
    snd_pcm_hw_params_t *hwparams = NULL;
    int ret;
    /* 打开 PCM 设备 */
    ret = snd_pcm_open(&pcm, PCM_PLAYBACK_DEV,SND_PCM_STREAM_PLAYBACK, 0);
    if (0 > ret) {
    fprintf(stderr, "snd_pcm_open error: %s:%s\n",
    PCM_PLAYBACK_DEV, snd_strerror(ret));
    return -1;
    }
    /* 实例化 hwparams 对象 */
    snd_pcm_hw_params_malloc(&hwparams);
    /* 获取 PCM 设备当前硬件配置,对 hwparams 进行初始化 */
    ret = snd_pcm_hw_params_any(pcm, hwparams);
    if (0 > ret) {
    fprintf(stderr, "snd_pcm_hw_params_any error:%s\n", snd_strerror(ret));
    goto err2;
    }
    /**************
    设置参数
    ***************/
    /* 设置访问类型: 交错模式 */
    ret = snd_pcm_hw_params_set_access(pcm,hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
    if (0 > ret) {
    fprintf(stderr,"snd_pcm_hw_params_set_access error: %s\n", snd_strerror(ret));
    goto err2;
    }
    /* 设置数据格式: 有符号 16 位、小端模式 */
    ret = snd_pcm_hw_params_set_format(pcm,hwparams, SND_PCM_FORMAT_S16_LE);
    if (0 > ret) {
    fprintf(stderr,"snd_pcm_hw_params_set_format error: %s\n", snd_strerror(ret));
    goto err2;
    }
    /* 设置采样率 */
    ret = snd_pcm_hw_params_set_rate(pcm,hwparams, wav_fmt.SampleRate, 0);
    if (0 > ret) {
    fprintf(stderr,"snd_pcm_hw_params_set_rate error: %s\n", snd_strerror(ret));
    goto err2;
    }
    /* 设置声道数: 双声道 */
    ret = snd_pcm_hw_params_set_channels(pcm,hwparams, wav_fmt.NumChannels);
    if (0 > ret) {
    fprintf(stderr,"snd_pcm_hw_params_set_channels error: %s\n", snd_strerror(ret));
    goto err2;
    }
    /* 设置周期大小: period_size */
    ret = snd_pcm_hw_params_set_period_size(pcm,hwparams, period_size, 0);
    if (0 > ret) {
    fprintf(stderr,"snd_pcm_hw_params_set_period_size error: %s\n", snd_strerror(ret));
    goto err2;
    }
    /* 设置周期数(驱动层 buffer 的大小): periods */
    ret = snd_pcm_hw_params_set_periods(pcm,hwparams, periods, 0);
    if (0 > ret) {
    fprintf(stderr,"snd_pcm_hw_params_set_periods error: %s\n", snd_strerror(ret));
    goto err2;
    }
    /* 使配置生效 */
    ret = snd_pcm_hw_params(pcm, hwparams);
    snd_pcm_hw_params_free(hwparams); //释放 hwparams 对象占用的内存
    if (0 > ret) {
    fprintf(stderr, "snd_pcm_hw_params error:%s\n", snd_strerror(ret));
    goto err1;
    }
    buf_bytes = period_size * wav_fmt.BlockAlign;//变量赋值,一个周期的字节大小
    return 0;
    err2:
    snd_pcm_hw_params_free(hwparams);
    err1:
    snd_pcm_close(pcm); //关闭 pcm 设备
    return -1;
    }
    static intopen_wav_file(const char *file)
    {
    RIFF_t wav_riff;
    DATA_t wav_data;
    int ret;
    fd = open(file, O_RDONLY);
    if (0 > fd) {
    fprintf(stderr, "open error: %s:%s\n", file, strerror(errno));
    return -1;
    }
    ret = read(fd, &wav_riff, sizeof(RIFF_t));
    if (sizeof(RIFF_t) != ret) {
    if (0 > ret)
    perror("read error");
    else
    fprintf(stderr, "check error: %s\n",file);
    close(fd);
    return -1;
    }
    if (strncmp("RIFF",wav_riff.ChunkID, 4) ||//校验
    strncmp("WAVE", wav_riff.Format, 4)){
    fprintf(stderr, "check error: %s\n",file);
    close(fd);
    return -1;
    }
    ret = read(fd, &wav_fmt, sizeof(FMT_t));
    if (sizeof(FMT_t) != ret) {
    if (0 > ret)
    perror("read error");
    else
    fprintf(stderr, "check error: %s\n",file);
    close(fd);
    return -1;
    }
    if (strncmp("fmt ",wav_fmt.Subchunk1ID, 4)) {//校验
    fprintf(stderr, "check error: %s\n",file);
    close(fd);
    return -1;
    }
    printf("<<<<音频文件格式信息>>>>\n\n");
    printf(" file name: %s\n", file);
    printf(" Subchunk1Size: %u\n",wav_fmt.Subchunk1Size);
    printf(" AudioFormat: %u\n",wav_fmt.AudioFormat);
    printf(" NumChannels: %u\n",wav_fmt.NumChannels);
    printf(" SampleRate: %u\n",wav_fmt.SampleRate);
    printf(" ByteRate: %u\n",wav_fmt.ByteRate);
    printf(" BlockAlign: %u\n",wav_fmt.BlockAlign);
    printf(" BitsPerSample: %u\n\n",wav_fmt.BitsPerSample);
    if (0 >lseek(fd, sizeof(RIFF_t) + 8 + wav_fmt.Subchunk1Size,
    SEEK_SET)) {
    perror("lseek error");
    close(fd);
    return -1;
    }
    while(sizeof(DATA_t) == read(fd,&wav_data, sizeof(DATA_t))) {
    if(!strncmp("data", wav_data.Subchunk2ID, 4))
    return 0;
    if (0 > lseek(fd, wav_data.Subchunk2Size,SEEK_CUR)) {
    perror("lseek error");
    close(fd);
    return -1;
    }
    }
    fprintf(stderr, "check error: %s\n",file);
    return -1;
    }
    int main(intargc, char *argv[])
    {
    int ret;
    if (2 != argc) {
    fprintf(stderr, "Usage: %s<audio_file>\n", argv[0]);
    exit(EXIT_FAILURE);
    }
    if (open_wav_file(argv[1]))
    exit(EXIT_FAILURE);
    if (snd_pcm_init())
    goto err1;
    buf = malloc(buf_bytes);
    if (NULL == buf) {
    perror("malloc error");
    goto err2;
    }
    While(1) {
    memset(buf, 0x00, buf_bytes);
    ret = read(fd, buf, buf_bytes);
    if (0 >= ret)
    goto err3;
    ret = snd_pcm_writei(pcm, buf, period_size);
    if (0 > ret) {
    fprintf(stderr, "snd_pcm_writei error:%s\n", snd_strerror(ret));
    goto err3;
    }
    else if (ret < period_size) {
    if (0 >lseek(fd, (ret-period_size) * wav_fmt.BlockAlign, SEEK_CUR)) {
    perror("lseek error");
    goto err3;
    }
    }
    }
    err3:
    free(buf); //释放内存
    err2:
    snd_pcm_close(pcm); //关闭 pcm 设备
    err1:
    close(fd); //关闭打开的音频文件
    exit(EXIT_FAILURE);
    }
    将编译好的可执行文件添加到开发板,并添加可执行文件,将需要播放的音频同样放在当前路径下,我这里是将下载的音乐通过格式化工厂进行格式转换,重命名为test.wav。然后输入./pcm_playback  ./test.wav进行音乐播放,并打印出音频格式信息,运行程序,开始播放音乐。
    6.png


    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-6-7 07:20 , Processed in 0.104429 second(s), 16 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.