ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RT-Thread Audio 设备驱动框架实战:播放与录音 API 使用详解

RT-Thread Audio 设备驱动框架实战:播放与录音 API 使用详解 操作系统嵌入式物联网嵌入式OSRTOS【免费下载链接】rt-threadRT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/项目地址https://gitcode.com/gh_mirrors/rt/rt-thread点击查看免费下载RT-Thread 的 Audio 设备框架为嵌入式系统提供了一套统一的音频数据采样与输出接口应用层通过标准的open/close/read/control设备接口即可驱动 Codec 完成播放与录音无需关心底层硬件差异。本文以仓库文档 audio.md 为主线结合驱动框架源码 dev_audio.h 与 dev_audio.c系统讲解 Audio 设备的查找、打开、参数配置、音量控制、读写与关闭全流程并给出可直接运行在 MSH 命令行上的 WAV 播放与录音完整示例帮助读者快速在 RT-Thread 上落地音频应用。Audio 设备简介Audio 设备是嵌入式系统中负责音频数据采样和输出的关键组件。一个典型的 Audio 设备由以下几部分组成数据总线接口Data Bus负责把音频 PCM 数据在应用与 Codec 之间搬运通常基于 I2S / PCM / TDM 等串行音频总线控制总线接口Control Bus负责配置 Codec 内部的寄存器如采样率、声道、音量等通常基于 I2C / SPI音频编解码器Codec负责数字音频信号与模拟信号之间的转换DAC/ADC扬声器Speaker与麦克风Microphone音频输出与输入的物理终端。从软件分层看RT-Thread 的 Audio 设备驱动框架位于 Audio 体系如 wavplayer 等上层播放组件的底层负责原始音频数据的采集/输出、音频流控制、设备管理、音量调节以及硬件/Codec 抽象。Audio 设备特性框架具备以下核心特性见 dev_audio.h 中的group_drivers_audio定义标准设备接口遵循 RT-Thread 标准设备模型提供open/close/read/control等统一接口同步访问模式read/write均为同步阻塞调用数据不足或缓冲满时自动等待支持播放与录音输出设备Playback与输入设备Record独立管理可同时注册为sound0、mic0等名称音频参数管理通过rt_audio_caps配置采样率、声道数、采样位数音量控制通过 Mixer 类型控制项调节主音量与麦克风增益。启用 Audio 设备驱动Audio 驱动框架默认不编译需要在 menuconfig 中开启。相关配置位于 components/drivers/audio/KconfigRT_USING_AUDIO总开关Using Audio device drivers默认关闭。启用后SConscript 会编译dev_audio.c与dev_audio_pipe.cRT_AUDIO_REPLAY_MP_BLOCK_SIZE播放内存池块大小默认4096字节。即播放数据按块切割、按块排队搬运的单位RT_AUDIO_REPLAY_MP_BLOCK_COUNT播放内存池块数量默认2决定了播放队列最多可暂存的音频块数RT_AUDIO_RECORD_PIPE_SIZE录音环形管道Pipe大小默认2048字节决定录音缓冲深度。上层组件如 WAV 播放器 wavplayer通常依赖该框架同时需要挂载文件系统RT_USING_DFS以便读写音频文件。查找 Audio 设备应用通过设备名获取设备句柄查找函数为rt_device_t rt_device_find(const char* name);参数描述nameAudio 设备名称返回——设备句柄找到时返回句柄RT_NULL未找到设备示例#define SOUND_DEVICE_NAME sound0 /* Audio 设备名称 */ static rt_device_t snd_dev; /* Audio 设备句柄 */ /* 根据名称查找 Audio 设备并获取句柄 */ snd_dev rt_device_find(SOUND_DEVICE_NAME);设备名由底层驱动注册时指定播放设备通常为sound0录音设备通常为mic0在驱动测试用例 tc_audio_common.h 中同样以SOUND_PLAYER_DEVICE_NAME sound0、SOUND_MIC_DEVICE_NAME mic0约定。打开 Audio 设备应用使用设备句柄打开/关闭设备rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);参数描述dev设备句柄oflags设备模式标志返回——RT_EOK设备打开成功-RT_EBUSY以RT_DEVICE_FLAG_STANDALONE注册的设备不能重复打开-RT_EINVAL不支持的打开标志其他错误设备打开失败支持的oflags值#define RT_DEVICE_OFLAG_WRONLY 0x002 /* 只写模式用于播放设备 */ #define RT_DEVICE_FLAG_RDONLY 0x001 /* 只读模式用于录音设备 */Audio 设备分为播放输出和录音输入两类播放设备使用只写标志打开录音设备使用只读标志打开。打开播放设备rt_device_open(snd_dev, RT_DEVICE_OFLAG_WRONLY)打开录音设备rt_device_open(mic_dev, RT_DEVICE_FLAG_RDONLY)从实现看dev_audio.c 的_audio_dev_open会校验打开标志与设备注册标志是否匹配只读设备用只写标志打开会返回-RT_EIO并在打开时完成播放/录音内部结构的初始化例如播放路径会清零write_index/read_index/pos录音路径会直接调用_audio_record_start启动采样硬件。控制 Audio 设备应用通过控制命令配置 Audio 设备rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);参数描述dev设备句柄cmd控制命令见下arg控制参数见下返回——RT_EOK操作成功-RT_ENOSYS操作失败设备句柄为空其他错误操作失败支持的控制命令/* AUDIO commands */ #define _AUDIO_CTL(a) (RT_DEVICE_CTRL_BASE(Sound) a) #define AUDIO_CTL_GETCAPS _AUDIO_CTL(1) /* 获取设备能力 */ #define AUDIO_CTL_CONFIGURE _AUDIO_CTL(2) /* 配置设备 */此外 dev_audio.h 还定义了AUDIO_CTL_START启动音频流、AUDIO_CTL_STOP停止音频流与AUDIO_CTL_GETBUFFERINFO获取缓冲信息等命令。设备能力结构体struct rt_audio_caps { int main_type; /* 主命令类型 */ int sub_type; /* 子命令类型 */ union { rt_uint32_t mask; int value; /* 参数值 */ struct rt_audio_configure config; /* 音频配置 */ } udata; };main_type取值group_audio_typeAUDIO_TYPE_QUERY0x00查询类型、AUDIO_TYPE_INPUT0x01输入/录音、AUDIO_TYPE_OUTPUT0x02输出/播放、AUDIO_TYPE_MIXER0x04混音器/音量控制。rt_audio_configure结构体承载实际的音频参数struct rt_audio_configure { rt_uint32_t samplerate; /* 采样率 */ rt_uint16_t channels; /* 声道数 */ rt_uint16_t samplebits; /* 采样位数 */ };设置播放参数配置播放采样率、声道数与采样位数struct rt_audio_caps caps; caps.main_type AUDIO_TYPE_OUTPUT; /* 播放设备 */ caps.sub_type AUDIO_DSP_PARAM; /* 设置全部音频参数 */ caps.udata.config.samplerate 44100; /* 采样率 */ caps.udata.config.channels 2; /* 声道数 */ caps.udata.config.samplebits 16; /* 采样位数 */ rt_device_control(device, AUDIO_CTL_CONFIGURE, caps);sub_type为 DSP 控制项group_audio_dsp除AUDIO_DSP_PARAM0一次设置全部参数外还可单独设置AUDIO_DSP_SAMPLERATE采样率、AUDIO_DSP_CHANNELS声道、AUDIO_DSP_SAMPLEBITS采样位宽。底层_audio_dev_control收到AUDIO_CTL_CONFIGURE后会回调驱动实现的ops-configure将配置下发到 Codec。设置播放音量调节主播放音量0-100struct rt_audio_caps caps; caps.main_type AUDIO_TYPE_MIXER; /* 音量控制类型 */ caps.sub_type AUDIO_MIXER_VOLUME; /* 设置主音量 */ caps.udata.value volume; /* 取值范围0-100 */ rt_device_control(snd_dev, AUDIO_CTL_CONFIGURE, caps);音量上下限由 dev_audio.h 中AUDIO_VOLUME_MAX (100)/AUDIO_VOLUME_MIN (0)定义。Mixer 控制项group_audio_mixer还支持AUDIO_MIXER_MUTE静音、AUDIO_MIXER_BASS低频、AUDIO_MIXER_MID中频、AUDIO_MIXER_TREBLE高频、AUDIO_MIXER_EQUALIZER均衡器、AUDIO_MIXER_MIC麦克风等。设置录音参数配置录音采样率、声道数与采样位数struct rt_audio_caps caps; caps.main_type AUDIO_TYPE_INPUT; /* 录音设备 */ caps.sub_type AUDIO_DSP_PARAM; /* 设置全部音频参数 */ caps.udata.config.samplerate 44100; /* 采样率 */ caps.udata.config.channels 2; /* 声道数 */ caps.udata.config.samplebits 16; /* 采样位数 */ rt_device_control(device, AUDIO_CTL_CONFIGURE, caps);设置录音音量调节麦克风增益0-100struct rt_audio_caps caps; caps.main_type AUDIO_TYPE_MIXER; /* 音量控制类型 */ caps.sub_type AUDIO_MIXER_MIC; /* 设置麦克风增益 */ caps.udata.value volume; /* 取值范围0-100 */ rt_device_control(player-device, AUDIO_CTL_CONFIGURE, caps);写入音频数据向播放设备写入数据rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);参数描述dev设备句柄pos未使用保留偏移参数buffer待写入的数据缓冲size待写入的数据大小返回——写入字节数实际写入的字节数同步操作该同步函数把数据写入设备内部缓冲缓冲满时调用会阻塞等待。从源码看dev_audio.c 的_audio_dev_write写入路径实际是把应用数据以RT_AUDIO_REPLAY_MP_BLOCK_SIZE默认 4096 字节为块大小切分先从播放内存池rt_mp_alloc申请块、rt_memcpy填入数据整块写满后通过rt_data_queue_push压入播放数据队列队列容量CFG_AUDIO_REPLAY_QUEUE_COUNT即 4最后若播放尚未激活则调用_aduio_replay_start启动硬件。整个写操作由互斥锁replay-lock保护。读取音频数据从录音设备读取数据rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);参数描述dev设备句柄pos未使用保留偏移参数buffer存放读取数据的缓冲size要读取的数据大小返回——读取字节数实际读取的字节数同步操作0读取失败检查线程 errno 获取错误码该同步函数从设备内部管道Pipe读取数据数据不足时调用会阻塞等待。从源码看_audio_dev_read实际是转发到录音管道设备rt_device_read(RT_DEVICE(audio-record-pipe), ...)完成的而录音数据的来源是硬件中断回调rt_audio_rx_done——驱动每收到一帧采样数据就写入录音管道Pipe内部为环形缓冲同时触发rx_indicate回调通知上层有数据可读。底层 Codec 采样率的支持范围见group_audio_samp_rates中的AUDIO_SAMP_RATE_8K ~ AUDIO_SAMP_RATE_192K等宏定义。关闭 Audio 设备操作结束后关闭设备rt_err_t rt_device_close(rt_device_t dev);参数描述dev设备句柄返回——RT_EOK设备关闭成功-RT_ERROR设备已关闭其他错误关闭失败底层_audio_dev_close会根据open_flag依次停止播放流_aduio_replay_stop冲刷剩余帧、等待播放队列数据传完、再停止硬件和录音流_audio_record_stop停止硬件并关闭录音管道。播放 WAV 文件完整示例Audio 设备用于播放和录音往往伴随音频文件的编解码。下面给出播放 WAV 文件的完整示例完整代码亦可参考 RT-Thread 官方的 WAV Player 软件包 wavplayer。主要步骤如下查找 Audio 设备并获取设备句柄以只写模式打开 Audio 设备设置音频参数采样率、声道数等解码音频文件数据写入音频文件数据播放完成后关闭设备。#include rtthread.h #include rtdevice.h #include dfs_posix.h #define BUFSZ 1024 #define SOUND_DEVICE_NAME sound0 /* Audio 设备名称 */ static rt_device_t snd_dev; /* Audio 设备句柄 */ struct RIFF_HEADER_DEF { char riff_id[4]; // R,I,F,F uint32_t riff_size; char riff_format[4]; // W,A,V,E }; struct WAVE_FORMAT_DEF { uint16_t FormatTag; uint16_t Channels; uint32_t SamplesPerSec; uint32_t AvgBytesPerSec; uint16_t BlockAlign; uint16_t BitsPerSample; }; struct FMT_BLOCK_DEF { char fmt_id[4]; // f,m,t, uint32_t fmt_size; struct WAVE_FORMAT_DEF wav_format; }; struct DATA_BLOCK_DEF { char data_id[4]; // R,I,F,F uint32_t data_size; }; struct wav_info { struct RIFF_HEADER_DEF header; struct FMT_BLOCK_DEF fmt_block; struct DATA_BLOCK_DEF data_block; }; int wavplay_sample(int argc, char **argv) { int fd -1; uint8_t *buffer NULL; struct wav_info *info NULL; struct rt_audio_caps caps {0}; if (argc ! 2) { rt_kprintf(Usage:\n); rt_kprintf(wavplay_sample song.wav\n); return 0; } fd open(argv[1], O_RDONLY); if (fd 0) { rt_kprintf(open file failed!\n); goto __exit; } buffer rt_malloc(BUFSZ); if (buffer RT_NULL) goto __exit; info (struct wav_info *) rt_malloc(sizeof * info); if (info RT_NULL) goto __exit; if (read(fd, (info-header), sizeof(struct RIFF_HEADER_DEF)) 0) goto __exit; if (read(fd, (info-fmt_block), sizeof(struct FMT_BLOCK_DEF)) 0) goto __exit; if (read(fd, (info-data_block), sizeof(struct DATA_BLOCK_DEF)) 0) goto __exit; rt_kprintf(wav information:\n); rt_kprintf(samplerate %d\n, info-fmt_block.wav_format.SamplesPerSec); rt_kprintf(channel %d\n, info-fmt_block.wav_format.Channels); /* 根据名称查找 Audio 设备并获取设备句柄 */ snd_dev rt_device_find(SOUND_DEVICE_NAME); /* 以只写模式打开 Audio 播放设备 */ rt_device_open(snd_dev, RT_DEVICE_OFLAG_WRONLY); /* 设置采样率、声道数、采样位数等音频参数 */ caps.main_type AUDIO_TYPE_OUTPUT; /* 输出类型播放设备 */ caps.sub_type AUDIO_DSP_PARAM; /* 设置全部音频参数 */ caps.udata.config.samplerate info-fmt_block.wav_format.SamplesPerSec; /* 采样率 */ caps.udata.config.channels info-fmt_block.wav_format.Channels; /* 声道数 */ caps.udata.config.samplebits 16; /* 采样位数 */ rt_device_control(snd_dev, AUDIO_CTL_CONFIGURE, caps); while (1) { int length; /* 从文件系统读取音频数据 */ length read(fd, buffer, BUFSZ); if (length 0) break; /* 将音频数据写入 Audio 设备 */ rt_device_write(snd_dev, 0, buffer, length); } /* 关闭 Audio 设备 */ rt_device_close(snd_dev); __exit: if (fd 0) close(fd); if (buffer) rt_free(buffer); if (info) rt_free(info); return 0; } MSH_CMD_EXPORT(wavplay_sample, play wav file);将上述代码编译进固件并挂载文件系统后可在 MSH 控制台执行wavplay_sample song.wav播放指定 WAV 文件。播放参数直接取自 WAV 文件头中的SamplesPerSec与Channels采样位数固定按 16 位处理。录音 WAV 文件完整示例录音示例的主要步骤如下查找 Audio 设备并获取设备句柄以只读模式打开 Audio 设备设置音频参数采样率、声道数等从 Audio 设备读取数据处理录音数据写入文件录音完成后关闭设备。#include rtthread.h #include rtdevice.h #include dfs_posix.h #define RECORD_TIME_MS 5000 #define RECORD_SAMPLERATE 16000 #define RECORD_CHANNEL 2 #define RECORD_CHUNK_SZ ((RECORD_SAMPLERATE * RECORD_CHANNEL * 2) * 20 / 1000) #define SOUND_DEVICE_NAME mic0 /* Audio 设备名称 */ static rt_device_t mic_dev; /* Audio 设备句柄 */ struct wav_header { char riff_id[4]; /* RIFF */ int riff_datasize; /* RIFF chunk 数据大小不含 riff_id[4] 和 riff_datasize即 total - 8 */ char riff_type[4]; /* WAVE */ char fmt_id[4]; /* fmt */ int fmt_datasize; /* fmt chunk 数据大小PCM 为 16 */ short fmt_compression_code; /* PCM 为 1 */ short fmt_channels; /* 1单声道或 2双声道 */ int fmt_sample_rate; /* 每秒采样数 */ int fmt_avg_bytes_per_sec; /* sample_rate * channels * bit_per_sample / 8 */ short fmt_block_align; /* 每采样点字节数bit_per_sample * channels / 8 */ short fmt_bit_per_sample; /* 每个采样的位数8、16、32 */ char data_id[4]; /* data */ int data_datasize; /* data chunk 大小pcm_size - 44 */ }; static void wavheader_init(struct wav_header *header, int sample_rate, int channels, int datasize) { memcpy(header-riff_id, RIFF, 4); header-riff_datasize datasize 44 - 8; memcpy(header-riff_type, WAVE, 4); memcpy(header-fmt_id, fmt , 4); header-fmt_datasize 16; header-fmt_compression_code 1; header-fmt_channels channels; header-fmt_sample_rate sample_rate; header-fmt_bit_per_sample 16; header-fmt_avg_bytes_per_sec header-fmt_sample_rate * header-fmt_channels * header-fmt_bit_per_sample / 8; header-fmt_block_align header-fmt_bit_per_sample * header-fmt_channels / 8; memcpy(header-data_id, data, 4); header-data_datasize datasize; } int wavrecord_sample(int argc, char **argv) { int fd -1; uint8_t *buffer NULL; struct wav_header header; struct rt_audio_caps caps {0}; int length, total_length 0; if (argc ! 2) { rt_kprintf(Usage:\n); rt_kprintf(wavrecord_sample file.wav\n); return -1; } fd open(argv[1], O_WRONLY | O_CREAT); if (fd 0) { rt_kprintf(open file for recording failed!\n); return -1; } write(fd, header, sizeof(struct wav_header)); buffer rt_malloc(RECORD_CHUNK_SZ); if (buffer RT_NULL) goto __exit; /* 根据名称查找 Audio 设备并获取设备句柄 */ mic_dev rt_device_find(SOUND_DEVICE_NAME); if (mic_dev RT_NULL) goto __exit; /* 以只读模式打开 Audio 录音设备 */ rt_device_open(mic_dev, RT_DEVICE_OFLAG_RDONLY); /* 设置采样率、声道数、采样位数等音频参数 */ caps.main_type AUDIO_TYPE_INPUT; /* 输入类型录音设备 */ caps.sub_type AUDIO_DSP_PARAM; /* 设置全部音频参数 */ caps.udata.config.samplerate RECORD_SAMPLERATE; /* 采样率 */ caps.udata.config.channels RECORD_CHANNEL; /* 声道数 */ caps.udata.config.samplebits 16; /* 采样位数 */ rt_device_control(mic_dev, AUDIO_CTL_CONFIGURE, caps); while (1) { /* 从 Audio 设备读取 20ms 音频数据 */ length rt_device_read(mic_dev, 0, buffer, RECORD_CHUNK_SZ); if (length) { /* 将音频数据写入文件系统 */ write(fd, buffer, length); total_length length; } if ((total_length / RECORD_CHUNK_SZ) (RECORD_TIME_MS / 20)) break; } /* 重写 WAV 文件头 */ wavheader_init(header, RECORD_SAMPLERATE, RECORD_CHANNEL, total_length); lseek(fd, 0, SEEK_SET); write(fd, header, sizeof(struct wav_header)); close(fd); /* 关闭 Audio 设备 */ rt_device_close(mic_dev); __exit: if (fd 0) close(fd); if (buffer) rt_free(buffer); return 0; } MSH_CMD_EXPORT(wavrecord_sample, record voice to a wav file);在 MSH 控制台执行wavrecord_sample file.wav即可录制约 5 秒RECORD_TIME_MS可通过宏调整的双声道 16kHz 16 位 WAV 文件。录音流程的特点先写入占位的 WAV 文件头44 字节录音结束后用wavheader_init根据实际采集字节数total_length回填fmt_*与data_datasize字段并lseek到文件头重新写入每次读取 20ms 的音频块RECORD_CHUNK_SZ samplerate * channels * 2 * 20 / 1000其中2为 16 位采样字节数按块写入文件并累加长度直到达到设定的录制时长。数据流机制与测试用例佐证理解 Audio 框架的内部数据流有助于排查播放/录音异常。从 dev_audio.c 可以看出框架采用“应用 ↔ 队列/管道 ↔ 硬件 FIFO”三级缓冲架构播放TX路径应用rt_device_write→ 按块拷贝进播放内存池replay-mp→ 整块压入数据队列replay-queue→_audio_send_replay_frame从队列取块拷贝到硬件缓冲buf_info-buffer并通过ops-transmit送硬件硬件每传完一帧调用rt_audio_tx_complete触发下一帧发送。队列为空时发送静音零帧避免硬件欠载录音RX路径硬件中断回调rt_audio_rx_done把采样数据写入录音管道record-pipe环形缓冲并触发rx_indicate应用rt_device_read从管道同步读取。仓库在 components/drivers/audio/utest 下提供了官方测试用例tc_audio_main.c测试名components.drivers.audio.tc_audio_main通过rt_device_find查找sound0/mic0以RT_DEVICE_OFLAG_WRONLY/RT_DEVICE_OFLAG_RDONLY打开设备写入0xAA、0x55特征字节并回读校验验证了设备注册、TX/RX 数据流正确性与缓冲边界。该用例还展示了rt_container_of从标准设备句柄回溯struct rt_audio_device、进而访问replay-buf_info等框架内部结构的典型用法。运行该测试需在 menuconfig 中同时开启RT_USING_UTESTCASES、RT_USING_AUDIO与RT_UTEST_USING_AUDIO_DRIVER见 utest/Kconfig可在 QEMU 或任何支持 RT-Thread 的硬件平台上执行。小结本文围绕 RT-Thread Audio 设备框架完整覆盖了设备查找、打开、控制参数配置与音量调节、读写、关闭的标准 API 流程并给出了可直接在 MSH 上运行的 WAV 播放与录音示例。实际开发中建议优先通过AUDIO_CTL_GETCAPS查询硬件能力再配置参数注意RT_AUDIO_REPLAY_MP_BLOCK_SIZE、RT_AUDIO_RECORD_PIPE_SIZE等配置与内存开销的关系并在rt_device_write/read返回异常时结合errno与 dev_audio.c 中的数据流实现定位问题。赞分享操作系统嵌入式物联网嵌入式OSRTOS【免费下载链接】rt-threadRT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/项目地址https://gitcode.com/gh_mirrors/rt/rt-thread点击查看免费下载相关推荐RT-Thread RTC 设备驱动框架详解时间设置、date 命令与 Soft RTC 使用指南RT Thread RTC 设备驱动框架详解时间设置、date 命令与 Soft RTC 使用指南 RT Thread 的 RTC实时时钟设备为操作系统的操作系统嵌入式物联网嵌入式OSRTOSRT-Thread STM32 系列驱动体系详解I/O 设备管理框架、外设驱动分类与使用指南RT Thread STM32 系列驱动体系详解I/O 设备管理框架、外设驱动分类与使用指南 本文基于 bsp/stm32/docs/STM32系列驱动介绍.操作系统嵌入式物联网嵌入式OSRTOSRT-Thread RA 系列外设驱动详解基于 I/O 设备管理框架的驱动分类与使用指南RT Thread RA 系列外设驱动详解基于 I/O 设备管理框架的驱动分类与使用指南 RT Thread 通过统一的 I/O 设备管理框架为上层应用提供标操作系统嵌入式物联网嵌入式OSRTOS上一篇5分钟连上BilldDesk Pro免费开源远程桌面完整指南下一篇B站缓存转MP4完整教程m4s-converter无损合并m4s缓存11.7GB只要38秒创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表