RK3568笔记二十六:音频应用

若该文为原创文章,转载请注明原文出处。

一、介绍

音频是我们最常用到的功能,音频也是 linux 和安卓的重点应用场合。

测试使用的是ATK-DLR3568板子,板载外挂RK809 CODEC芯片,RK官方驱动是写好的,不用在自己重新写。测试应用层录音和放音使用的是ALSA方式。

二、硬件原理图

I2S 接口一共用到了 5 根线,这个 5 根线用于 RK3568 与 RK809 之间的音频数据收
发。

三、音频驱动使能

RK 官方已经写好了 RK809 CODEC 驱动,直接配置内核使能 RK809 CODEC 驱动即可。

打开kernel/arch/arm64/configs/rockchip_linux_defconfig

修改RK817为y, 重新编译kenel.RK官方把RK809 与 RK817 CODEC 部分驱动代码做了兼容。

驱动源码路径:kernel/sound/soc/codecs/rk817_codec.c

四、设备树配置

RK809 CODEC芯片的I2S连接到RK3568的i2c0, 内核使能后,需要通过I2C配置一系列参数。

1、I2C设备树配置

设备树路径: arch/arm64/boot/dts/rockchip/rk3568-evb.dtsi

2、声卡设备树配置

配置声卡主要是为了和RK809驱动关联。

声卡驱动文件路径为 sound/soc/generic/simple-card.c

五、声卡设置与测试

alsa-utils 自带了 amixer 这个声卡设置工具,使用amixer测试录音和放音。

1、使用 amixer 设置声卡

amixer cset name='Capture MIC Path' 'Main Mic'

2、使用 arecord 录制音频

arecord -r 44100 -f S16_LE -d 10 record.wav
录制一段 10 秒音频, 采样率 44.1K,S16_LE 格式进行采样,

3、播放音频

aplay record.wav

六、开机自动配置声卡

声卡设置的保存通过 alsactl 工具来完成,此工具也是 alsa-utils 编译出来的。alsactl 默认将
声卡配置文件保存在/var/lib/alsa 目录下。
使用 amixer 设置声卡,然后输入如下命令保存声卡设置
alsactl -f /var/lib/alsa/asound.state store //保存声卡设置
如果要使用 asound.state 中的配置信息来配置声卡,执行如下命令即可:
alsactl -f /var/lib/alsa/asound.state restore

创建自启动文件

/etc/init.d/S98alactl
PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
start_restore()
{if [ -f "/var/lib/alsa/asound.state" ]; then/usr/sbin/alsactl -f /var/lib/alsa/asound.state restore &fi
}
stop_restore()
{echo -n ""
}
case "$1" instart)echo -n "ALSA: Restoring mixer setting......"start_restoreecho "done.";;stop)stop_restoreecho "done.";;restart|reload)echo -n "stoping restore... "stop_restore && sleep .3echo "done."echo -n "starting restore... "start_restoreecho "done.";;*)echo "Usage: $0 {start|stop|restart}"exit 1esacexit 0
chmod 777 /etc/init.d/S98alactl   # 赋予文件可执行权限

修改/etc/init.d/rcS,添加执行文件命令,上电后会自动配置声卡

七、使用ALSA录制音频和放音

ALSA程序网上很多,不多做介绍,直接上代码

录制音频alsa_pcm_rec.c


/*进行音频采集,采集pcm数据并直接保存pcm数据音频参数:声道数:		1采样位数:	16bitLE格式t、LE格式采样频率:	44100Hz运行示例:$ gcc linux_pcm_save.c -lasound$ ./a.out
*/#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <signal.h>/** 指定音频的格式,其他常用格式:* SND_PCM_FORMAT_U24_LE、* SND_PCM_FORMAT_U32_LE*/
#define AudioFormat SND_PCM_FORMAT_S16_LE
/* 1单声道   2立体声*/
#define AUDIO_CHANNEL_SET 1
/** 音频采样率,常用的采样频率:* 44100Hz 、16000HZ、8000HZ、48000HZ、22050HZ*/
#define AUDIO_RATE_SET 44100#define PCM_FILE "test.pcm"FILE *pcm_data_file = NULL;
int run_flag = 0;void exit_sighandler(int sig)
{run_flag = 1;
}int main(int argc, char *argv[])
{int  i;int  err;char *buffer;int  buffer_frames = 1024;unsigned int rate = AUDIO_RATE_SET;snd_pcm_t *capture_handle;       // 一个指向PCM设备的	句柄snd_pcm_hw_params_t *hw_params;  //此结构包含有关硬件的信息,可用于指定PCM流的配置/* 注册信号捕获退出接口 */signal(2, exit_sighandler);/* PCM的采样格式在pcm.h文件里有定义 */snd_pcm_format_t format = AudioFormat;  // 采样位数:16bit、LE格式/*打开音频采集卡硬件,并判断硬件是否打开成功,若打开失败则打印出错误提示* SND_PCM_STREAM_PLAYBACK 输出流* SND_PCM_STREAM_CAPTURE  输入流*/if((err = snd_pcm_open(&capture_handle, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0){printf("无法打开音频设备.\n");exit(1);}printf("音频接口打开成功.\n");/* 创建一个保存PCM数据的文件 */if((pcm_data_file = fopen(PCM_FILE, "wb")) == NULL){printf("无法创建音频文件.\n");exit(1);}printf(" 用于录制的音频文件已打开.\n");/* 分配硬件参数结构对象,并判断是否分配成功 */if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0){printf("无法分配硬件参数结构.\n");exit(1);}printf("硬件参数结构分配成功.\n");/* 按照默认设置对硬件对象进行设置,并判断是否成功 */if((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0){printf("无法初始化硬件参数结构.\n");exit(1);}printf("硬件参数结构初始化成功.\n");/*设置数据为交叉模式,并判断是否设置成功interleaved/non interleaved:交叉/非交叉模式。表示在多声道数据传输的过程中是采样交叉的模式还是非交叉的模式。对多声道数据,如果采样交叉模式,使用一块buffer即可,其中各声道的数据交叉传输;如果使用非交叉模式,需要为各声道分别分配一个buffer,各声道数据分别传输。*/if((err = snd_pcm_hw_params_set_access (capture_handle,hw_params,SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){printf("无法设置访问类型(%s)\n",snd_strerror(err));exit(1);}printf("访问类型设置成功.\n");/* 设置数据编码格式,并判断是否设置成功 */if((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, format)) < 0){printf("无法设置格式.\n");exit(1);}printf("PCM数据格式设置成功.\n");/* 设置采样频率,并判断是否设置成功 */if((err = snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &rate, 0)) < 0){printf("无法设置采样率.\n");exit(1);}printf("采样率设置成功.\n");/* 设置声道,并判断是否设置成功 */if((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, AUDIO_CHANNEL_SET)) < 0){printf("无法设置声道参数.\n");exit(1);}printf("声道参数设置成功.\n");/* 将配置写入驱动程序中,并判断是否配置成功 */if((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0){printf("无法向驱动程序设置参数.\n");exit(1);}printf("参数设置成功.\n");/* 使采集卡处于空闲状态 */snd_pcm_hw_params_free(hw_params);/* 准备音频接口,并判断是否准备好 */if((err = snd_pcm_prepare(capture_handle)) < 0){printf("无法使用音频接口.\n");exit(1);}printf("音频接口准备好.\n");/* 配置一个数据缓冲区用来缓冲数据 *//* snd_pcm_format_width(format) 获取样本格式对应的大小(单位是:bit) */int frame_byte = snd_pcm_format_width(format)/8;buffer = malloc(buffer_frames*frame_byte*AUDIO_CHANNEL_SET);printf("缓冲区分配成功.\n");/* 开始采集音频PCM数据 */printf("开始采集数据...\n");while(1){/* 从声卡设备读取一帧音频数据 2048字节 */if((err=snd_pcm_readi(capture_handle, buffer, buffer_frames)) != buffer_frames){printf("从音频接口读取失败.\n");exit(1);}/* 写数据到文件: 音频的每帧数据样本大小是16位=2个字节 */fwrite(buffer, (buffer_frames*AUDIO_CHANNEL_SET), frame_byte, pcm_data_file);if(run_flag){printf("停止采集.\n");break;}}/* 释放数据缓冲区 */free(buffer);/* 关闭音频采集卡硬件 */snd_pcm_close(capture_handle);/* 关闭文件流 */fclose(pcm_data_file);return 0;
}

播放音频alsa_pcm_play.c


/** 进行音频采集,读取存放pcm数据文件通过声卡进行播放'* 音频参数:*     声道数:      1*     采样位叔:   16bit、LE格式*     采样频率:    44100Hz* 运行救命:* $ gcc alsa_pcm_play.c -lasound* $ ./a.out test.pcm*/#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <signal.h>/* 指定音频的格式,其他常用格式:** SND_PCM_FORMAT_U24_LE、* SND_PCM_FORMAT_U32_LE*/
#define AudioFormat  SND_PCM_FORMAT_S16_LE
/* 1单声道   2立体声 */
#define AUDIO_CHANNEL_SET 1
/* 音频采样率,常用的采样频率:* 44100Hz 、16000HZ、8000HZ、48000HZ、22050HZ*/
#define AUDIO_RATE_SET     44100FILE *pcm_data_file = NULL;int run_flag = 0;void exit_sighandler(int sig)
{run_flag = 1;
}int main(int argc, char *argv[])
{int  i;int  err;char *buffer;int  buffer_frames = 1024;unsigned int rate = AUDIO_RATE_SET;snd_pcm_t *capture_handle;   // 一个指向PCM设备的句柄snd_pcm_hw_params_t *hw_params; //此结构包含有关硬件的信息,可用于指定PCM流的配置/* 注册信号捕获退出接口 */signal(2, exit_sighandler);/* PCM的采样格式在pcm.h文件里有定义 */snd_pcm_format_t format = AudioFormat;/*打开音频采集卡硬件,并判断硬件是否打开成功,若打开失败则打印出错误提示* SND_PCM_STREAM_PLAYBACK 输出流* SND_PCM_STREAM_CAPTURE  输入流*/if((err = snd_pcm_open(&capture_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0){printf("无法打开音频设备.\n");exit(1);}printf("音频接口打开.\n");/* 打开存放PCM数据的文件 */if((pcm_data_file = fopen(argv[1], "rb")) == NULL){printf("无法打开音频文件.\n");exit(1);}printf("用于播放的音频文件已打开.\n");/* 分配硬件参数结构对象, 并判断是否分配成功 */if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0){printf("无法分配参数结构.\n");exit(1);}printf("硬件参数结构已分配成功.\n");/* 按照默认设置对硬件对象进行设置,并判断是否设置成功 */if((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0){printf("无法初始化硬件参数结构.\n");exit(1);}printf("硬件参数结构初始化成功.\n");/*设置数据为交叉模式,并判断是否设置成功interleaved/non interleaved:交叉/非交叉模式。表示在多声道数据传输的过程中是采样交叉的模式还是非交叉的模式。对多声道数据,如果采样交叉模式,使用一块buffer即可,其中各声道的数据交叉传输;如果使用非交叉模式,需要为各声道分别分配一个buffer,各声道数据分别传输。*/if((err = snd_pcm_hw_params_set_access (capture_handle,hw_params,SND_PCM_ACCESS_RW_INTERLEAVED)) < 0){printf("无法设置访问类型.\n");exit(1);}printf("访问类型访问.\n");/* 设置数据编码格式,并判断是否设置成功 */if((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, format)) < 0){printf("无法设置格式.\n");exit(1);}printf("PCM数据格式设置成功.\n");/* 设置采样频率,并判断是否设置成功 */if((err = snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &rate, 0)) < 0){printf("无法设置采样率.\n");exit(1);}printf("采样率设置成功.\n");/* 设置声道,并判断是否设置成功 */if((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, AUDIO_CHANNEL_SET)) < 0){printf("无法设置声道数.\n");exit(1);}printf("声道数设置成功.\n");/* 将配置写入驱动程序中,并判断是否配置成功 */if((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0){printf("无法向驱动程序设置参数.\n");exit(1);}printf("参数设置成功.\n");/* 使采集卡处理空闲状态 */snd_pcm_hw_params_free(hw_params);/* 准备音频接口, 并判断是否准备好  */if((err = snd_pcm_prepare(capture_handle)) < 0){printf("无法使用音频接口.\n");exit(1);}printf("音频接口准备好.\n");/* 配置一个数据缓冲区用来缓冲数据 *//* snd_pcm_format_width(format) 获取样本格式对应的大小(单位是:bit) */int frame_byte = snd_pcm_format_width(format)/8;buffer = malloc(buffer_frames*frame_byte*AUDIO_CHANNEL_SET);printf("缓冲区分配成功.\n");/* 开始播放音频数据...*/printf("开始播放音频数据...\n");int read_cnt;while(1){read_cnt = fread(buffer, 1, frame_byte*(buffer_frames*AUDIO_CHANNEL_SET), pcm_data_file);if(read_cnt <= 0)break;/* 向声卡设备写一帧音频数据: 2048字节 */if((err = snd_pcm_writei(capture_handle, buffer, buffer_frames)) != buffer_frames){printf("向音频接口写数据失败.\n");exit(1);}if(run_flag){printf("停止播放.\n");break;}}printf("播放完成.\n");/* 释放数据缓冲区 */free(buffer);/* 关闭音频采集卡硬件 */snd_pcm_close(capture_handle);/* 关闭文件流 */fclose(pcm_data_file);return 0;
}

编译:

/opt/atk-dlrk356x-toolchain/usr/bin/aarch64-buildroot-linux-gnu-g++ alsa_pcm_rec.c -o alsa_pcm_play -lasound
/opt/atk-dlrk356x-toolchain/usr/bin/aarch64-buildroot-linux-gnu-g++ alsa_pcm_play.c -o alsa_pcm_play -lasound

生成的可执行文件,通过ADB或TFTP等方式上传到板子运行。

测试录音和放音正常。

如有侵权,或需要完整代码,请及时联系博主。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/16381.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智慧城市运维可视化:透视未来城市高效管理的新视窗

行业痛点 现代城市运维是一个复杂而庞大的系统&#xff0c;涉及到诸多方面&#xff0c;包括交通、环境、能源等等。然而&#xff0c;在城市运维中&#xff0c;存在着一些现实的痛点&#xff0c;给城市管理者带来了不小的压力和困扰&#xff1a; 1、交通拥堵 随着城市化进程的…

帝国cms自定义专题列表模板list.var中获取对应专题下的信息、信息数量及信息所属栏目名称

帝国cms自定义专题列表模板list.var中获取对应专题下的信息、信息数量及信息所属栏目名称 代码如下&#xff1a; $rr $empire->fetch1("SELECT GROUP_CONCAT(id) from phome_enewsztinfo where ztid$r[id]"); $ff $rr[0]; $ga explode(",", $ff); …

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《考虑分布式光伏高效消纳与负荷损失最小的区域配电网应急资源协同配置策略》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

一款网站源码下载开源工具 Website Downloader

一、简介 Website Downloader 是一款免费的网站源码下载器&#xff0c;下载任何网站的完整源代码&#xff0c;包括 JavaScript、样式表、图像等等&#xff0c;而且使用也非常简单&#xff0c;只需要粘贴网址&#xff0c;用户即可将网页链接内容下载到本地&#xff0c;而且自动…

堆(建堆算法,堆排序)

目录 一.什么是堆&#xff1f; 1.堆 2.堆的储存 二.堆结构的创建 1.头文件的声明&#xff1a; 2.向上调整 3.向下调整 4.源码&#xff1a; 三.建堆算法 1.向上建堆法 2.向下建堆法 四.堆排序 五.在文件中Top出最小的K个数 一.什么是堆&#xff1f; 1.堆 堆就…

Docker之xfs文件系统下安装报错解决方案

一、需求说明 centos系统下安装docker最新版的时候&#xff0c;安装成功&#xff0c;启动的时候报错。报错信息“failed to start daemon: error initializing graphdriver: overlay2: the backing xfs filesystem is formatted without d_type support, which leads to incorr…

ROS添加GDB调试

文章目录 一、问题描述二、配置步骤1. debug 模式编译2. rosrun 添加GDB指令3. launch 添加GDB指令 三、GDB基本命令1. 基本2. 显示被调试文件信息3. 查看/修改内存4. 断点5. 调试运行 一、问题描述 在享受ROS带来便利的同时&#xff0c;但因每运行出现错误&#xff0c;ROS不会…

风电机组的振动控制

文章目录 0. 背景1. 原文记录 0. 背景 混塔机组的频率大概是目前业内遇见的比较普遍的通病。最近在了解风电机组振动控制的知识&#xff0c;看到一篇科普性质的文章&#xff0c;感觉不错&#xff0c;所以记录下来。想要看原文的点击这里。感谢原作者。 1. 原文记录

一文带你了解.NET能做什么?

前言 在DotNetGuide技术社区微信交流群经常看到有小伙伴问&#xff1a;.NET除了能写桌面应用和Web应用还能做什么&#xff1f;今天大姚将通过本篇文章来简单讲讲.NET能做哪些开发&#xff0c;对.NET感兴趣的小伙伴也可以自行领取文末附带的.NET相关学习资料。 .NET简单介绍 .…

多层派生时的构造函数和派生类的析构函数

一、多层派生时的构造函数 目录 一、多层派生时的构造函数 二、派生类的析构函数 析构函数的作用&#xff1a; 例&#xff1a;多级派生情况下派生类的构造函数 #include <iostream> #include<string> using namespace std; class Student { public:Student(int…

【SpeedAI科研小助手】2分钟解决知网维普AIGC检测

2分钟搞定AIGC率&#xff1f;还能降到0%&#xff1f; 使用方法&#xff1a; 打开SpeedAI科研小助手&#xff0c;将功能模式换成降AIGC率&#xff0c;后面可以一段一段自己改&#xff0c;也可以直接上传论文文件&#xff0c;SpeedAI直接帮你全文修改&#xff08;主打一个用户友…

HTML5 基本框架

HTML5基本的内容 文章目录 系列文章目录前言一、HTML5 基本框架二、具体框架结构三、知识补充总结 前言 HTML5的介绍&#xff1a; HTML5 是一种用于构建网页内容的标准化语言。它是 HTML&#xff08;超文本标记语言&#xff09;的第五个版本&#xff0c;引入了许多新的功能和特…

Jmeter-使用手册(_5.5版本)

JMeter是一个Java桌面应用程序&#xff0c;具有使用Swing图形API的图形界面。可以进行接口、性能等测试&#xff0c;也可以对任何数据库进行同样的测试&#xff0c;具有可移植性&#xff0c;可跨平台支持Windows&#xff0c;Linux&#xff0c;Mac上使用。 JMeter运行场景不仅可…

模仿高效网络进行目标检测——知识蒸馏

摘要 链接&#xff1a;https://openaccess.thecvf.com/content_cvpr_2017/papers/Li_Mimicking_Very_Efficient_CVPR_2017_paper.pdf 当前的基于卷积神经网络&#xff08;CNN&#xff09;的目标检测器需要从预训练的ImageNet分类模型中初始化&#xff0c;这通常非常耗时。在本…

Java整合ELK实现日志收集 之 Elasticsearch、Logstash、Kibana

简介 Logstash&#xff1a;用于收集并处理日志&#xff0c;将日志信息存储到Elasticsearch里面 Elasticsearch&#xff1a;用于存储收集到的日志信息 Kibana&#xff1a;通过Web端的可视化界面来查看日志&#xff08;数据可视化&#xff09; Logstash 是免费且开放的服务器端数…

AI办公自动化:用kimi批量将word文档部分文件名保存到Excel中

文件夹中有很多个word文档&#xff0c;现在只要英文部分的文件名&#xff0c;保存到一个Excel文件中。 可以在kimi中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个编写Python脚本的任务&#xff0c;具体步骤如下&#xff1a; 打开文件夹&#xff1a;…

数据集002:眼疾识别数据集 (含数据集下载链接)

说明 病理性近视&#xff08;Pathologic Myopia&#xff0c;PM&#xff09;的医疗类数据集&#xff0c;包含1200个受试者的眼底视网膜图片&#xff0c;训练、验证和测试数据集各400张。 说明&#xff1a; 如今近视已经成为困扰人们健康的一项全球性负担&#xff0c;在近视人…

斯坦福2024人工智能指数报告 1

《人工智能指数报告》由斯坦福大学、AI指数指导委员会及业内众多大佬Raymond Perrault、Erik Brynjolfsson 、James Manyika、Jack Clark等人员和组织合著&#xff0c;旨在追踪、整理、提炼并可视化与人工智能&#xff08;AI&#xff09;相关各类数据&#xff0c;该报告已被大多…

RedHat9 | DNS剖析-建立子域并进行区域委派

一、实验环境 1、委派DNS服务器 域名空间由多个域构成&#xff0c;DNS提供了将域名空间划分为1个或多个区域的方法&#xff0c;这样使得管理更加方便。在域的规模增大后&#xff0c;可以为域添加附加域&#xff0c;上级域为父域&#xff0c;下级域为子域&#xff0c;下列案例…

韩顺平0基础学Java——第11天

p234-249 又一个月了&#xff0c;时间过得好快啊&#xff0c;希望支棱起来 可变参数 public int sum(int ... nums){ } 这个nums是数组 细节&#xff1a; 1可变参数可以为0个&#xff0c;或任意个 2可变参数的实参可以为数组 3可变参数的本质就是数组 4可变参数可以和普通…