AUDIO设备用户开发指南
简介
本开发指南给用户提供了AUDIO的基本配置,工程构建编译、操作API介绍以及使用示例展示。
目录
配置指南
以下配置均以万耦一代开发板(STML475)为例。
STM32CubeMX配置
打开oneos\projects\xxxxx(project文件夹)\board\CubeMX_Config下的STM32CubeMX工程文件,按照具体的需求进行配置。
1. SAI接口设置
如下图所示,SAI A作为播放通道,SAI B作为录音通道,且STM32L475需要输出时钟给ESP8388,因此,SAI A设置为主模式且输出时钟,SAI B设置为同步,从模式。
2. 配置DMA
因为音频处理的特性,两个数据通道均需要使用DMA,且设置为循环模式。
OnsOS-Cube配置
用户使用音频功能时,需要在Drivers→ Audio→ Using audio device drivers路径下进行配置。
1. 使用音频相关的驱动
音频相关的驱动可大致分为音频设备层数据缓存配置,数据通道驱动层配置和编解码芯片配置三个层级。
2. 配置音频设备数据缓存
如下,可将音频播放数据缓存配置为2块,每块大小为2048Byte。音频播放设置为2块,每块大小为3528。其中音频播放是采用的内存池方式,而接受采用的是fifo方式。
3. 配置数据通道驱动
因为万耦STM32L475开发板采用的是SAI方式,所以此处使能SAI驱动。如是I2S接口,则选择I2S驱动。
4. 配置编解码芯片
1)配置编解码芯片的控制总线
由硬件原理图可知,其控制总线是i2c3。
2)配置编解码芯片数据通道
退出并保存配置。
生成MDK工程
完成OnsOS-Cube配置后,使用scons指令:scons --ide=mdk5编译生成keil工程。
运行
1、进入工程目录,双击project.uvprojx打开工程 ;
2、添加tf内存卡与音频测试文件;
3、编译下载,运行程序;
4、输入tf卡测试程序,挂载文件系统;
5、输入音频测试指令。
如下,先使用录音机录制了一段名为r513.wav的音频文件,然后使用播放器播放。
sh />wav_recorder
Usage:
wavplay_sample song.wav
sh />
sh />wav_player
Usage:
wavplay_sample song.wav
wav_recorder
sh />wav_recorder r513.wav
sh />
sh />end of audio recording!
wav_player
sh />wav_player r513.wav
wav information:
samplerate 44100
channel 2
sh />end of audio playing!
注意事项
1、使用音频功能,因为需要读取或者存储大量数据,首先需要将TF卡与文件系统相关的驱动使能。
2、当前音频功能只支持.wav格式的音频文件处理。
AUDIO-PLAYER设备API介绍
API 列表
函数 | 说明 |
---|---|
os_device_find() | 根据AUDIO设备名称寻找设备并获取设备指针 |
os_device_open() | 打开设备 |
os_device_write_block() | 以阻塞方式往设备写数据 |
os_device_write_nonblock() | 以非阻塞方式往设备写数据 |
os_device_close() | 关闭设备 |
os_device_control() | 控制设备音量与参数 |
API说明
os_device_find
该函数根据设备名查找对应的AUDIO设备,函数原型如下:
os_device_t *os_device_find(const char *name);
参数 | 说明 |
---|---|
name | AUDIO设备名称 |
返回 | 说明 |
设备指针 | 查找到对应设备将返回相应的设备指针 |
OS_NULL | 没有找到设备 |
os_device_open
该函数用于开启ADC设备,函数原型如下:
os_err_t os_device_open(struct os_device *dev)
参数 | 说明 |
---|---|
dev | AUDIO设备指针 |
返回 | 说明 |
OS_EOK | 成功 |
其他错误码 | 失败 |
os_device_write_block
该函数用于以阻塞方式向audio设备写入数据,函数原型如下:
os_size_t os_device_write_block(os_device_t *dev, os_off_t pos, const void *buffer, os_size_t size);
参数 | 说明 |
---|---|
dev | audio设备指针 |
pos | 未使用,给0 |
buffer | 写入数据缓存指针 |
size | 写入数据大小 |
返回 | 说明 |
0 | 输出成功 |
其他值 | 输出失败 |
os_device_write_nonblock
该函数用于以阻塞方式向audio设备写入数据,函数原型如下:
os_size_t os_device_write_nonblock(os_device_t *dev, os_off_t pos, const void *buffer, os_size_t size);
参数 | 说明 |
---|---|
dev | audio设备指针 |
pos | 未使用,给0 |
buffer | 写入数据缓存指针 |
size | 写入数据大小 |
返回 | 说明 |
0 | 输出成功 |
其他值 | 输出失败 |
os_device_close
该函数用于关闭AUDIO设备,函数原型如下:
os_err_t os_device_close(struct os_device *dev)
参数 | 说明 |
---|---|
dev | AUDIO设备指针 |
返回 | 说明 |
OS_EOK | 成功 |
其他错误码 | 失败 |
os_device_control
该函数用于控制AUDIO设备的参数,当音频设备处于播放模式,进行数据输出时才能使用,函数原型如下:
os_err_t os_device_control(os_device_t *dev, int cmd, void *arg);
参数 | 说明 |
---|---|
dev | 设备指针 |
cmd | 命令控制字 |
arg | 控制的参数 |
返回 | 说明 |
OS_EOK | 函数执行成功 |
其他错误码 | 失败 |
调用该函数前,将控制AUDIO设备的参数封装到struct os_audio_caps结构中,并作为arg参数。该结构体成员如下:
成员 | 说明 |
---|---|
config_type | 配置类型,如AUDIO_PARAM_CMD或AUDIO_VOLUME_CMD |
value | 配置类型为AUDIO_VOLUME_CMD时,保存音量值 |
config | 配置类型为AUDIO_PARAM_CMD时,保存相应的配置参数。 |
config成员变量是struct os_audio_configure类型变量,该结构体成员如下:
成员 | 说明 |
---|---|
samplerate | 采样率 |
channels | 通道 |
samplebits | 采样位数 |
AUDIO-RECORDER设备API介绍
API 列表
函数 | 说明 |
---|---|
os_device_find() | 根据AUDIO设备名称寻找设备并获取设备指针 |
os_device_open() | 打开设备 |
os_device_read_block() | 以阻塞方式从设备读数据 |
os_device_read_nonblock() | 以非阻塞方式从设备读数据 |
os_device_close() | 关闭设备 |
API说明
os_device_find
该函数根据设备名查找对应的AUDIO设备,函数原型如下:
os_device_t *os_device_find(const char *name);
参数 | 说明 |
---|---|
name | AUDIO设备名称 |
返回 | 说明 |
设备指针 | 查找到对应设备将返回相应的设备指针 |
OS_NULL | 没有找到设备 |
os_device_open
该函数用于开启ADC设备,函数原型如下:
os_err_t os_device_open(struct os_device *dev)
参数 | 说明 |
---|---|
dev | AUDIO设备指针 |
返回 | 说明 |
OS_EOK | 成功 |
其他错误码 | 失败 |
os_device_read_block
该函数用于以阻塞方式从audio设备读数据,函数原型如下:
os_size_t os_device_read_block(os_device_t *dev, os_off_t pos, void *buffer, os_size_t size);
参数 | 说明 |
---|---|
dev | audio设备指针 |
pos | 未使用,给0 |
buffer | 数据缓存指针 |
size | 数据大小,以字节为单位 |
返回 | 说明 |
读到数据的实际大小 | 返回数据大小,以字节为单位 |
<= 0 | 输出失败 |
os_device_read_nonblock
该函数用于以非阻塞方式从audio设备读数据,函数原型如下:
os_size_t os_device_read_nonblock(os_device_t *dev, os_off_t pos, void *buffer, os_size_t size);
参数 | 说明 |
---|---|
dev | audio设备指针 |
pos | 未使用,给0 |
buffer | 数据缓存指针 |
size | 数据大小,以字节为单位 |
返回 | 说明 |
读到数据的实际大小 | 返回数据大小,以字节为单位 |
<= 0 | 输出失败 |
os_device_close
该函数用于关闭AUDIO设备,函数原型如下:
os_err_t os_device_close(struct os_device *dev)
参数 | 说明 |
---|---|
dev | AUDIO设备指针 |
返回 | 说明 |
OS_EOK | 成功 |
其他错误码 | 失败 |
使用示例
AUDIO-player设备使用示例
使用音频设备需要先准备一个音频文件,可以通过sd卡挂载文件系统的方式动态加载,参考sd卡的使用教程,挂载好音频源后,调用本例即可开启一个专门的播放音乐的线程,播放格式为.wav的音乐。
#include <os_task.h>
#include <os_device.h>
#include <vfs_posix.h>
#include <audio/audio.h>
#include <shell.h>
#define BUFSZ OS_AUDIO_REPLAY_MP_BLOCK_SIZE
#define SOUND_DEVICE_NAME "audio0" /* Audio device name */
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;
};
char media_name[24];
void wavplay_sample_task(void *parameter)
{
int fd = -1;
int length;
uint8_t *buffer = NULL;
struct wav_info *info = NULL;
struct os_audio_caps caps = {0};
os_device_t *snd_dev;
fd = open(media_name, O_RDONLY);
if (fd < 0)
{
os_kprintf("open file failed!\n");
goto __exit;
}
buffer = os_malloc(BUFSZ);
if (buffer == OS_NULL)
goto __exit;
info = (struct wav_info *) os_malloc(sizeof * info);
if (info == OS_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;
os_kprintf("wav information:\n");
os_kprintf("samplerate %d\n", info->fmt_block.wav_format.SamplesPerSec);
os_kprintf("channel %d\n", info->fmt_block.wav_format.Channels);
snd_dev = os_device_find(SOUND_DEVICE_NAME);
OS_ASSERT(snd_dev != OS_NULL);
os_device_open(snd_dev, OS_DEVICE_OFLAG_WRONLY);
/* parameter settings */
caps.config_type = AUDIO_PARAM_CMD;
caps.udata.config.samplerate = info->fmt_block.wav_format.SamplesPerSec;
caps.udata.config.channels = info->fmt_block.wav_format.Channels;
os_device_control(snd_dev, AUDIO_CTL_CONFIGURE, &caps);
caps.config_type = AUDIO_VOLUME_CMD;
caps.udata.value = 35;
os_device_control(snd_dev, AUDIO_CTL_CONFIGURE, &caps);
while (1)
{
length = read(fd, buffer, BUFSZ);
if (length <= 0)
break;
os_device_write(snd_dev, 0, buffer, length);
}
os_device_close(snd_dev);
__exit:
if (fd >= 0)
close(fd);
if (buffer)
os_free(buffer);
if (info)
os_free(info);
}
int wavplay_sample(int argc, char **argv)
{
os_task_t *task;
if (argc != 2)
{
os_kprintf("Usage:\n");
os_kprintf("wavplay_sample song.wav\n");
return 0;
}
snprintf(media_name, sizeof(media_name) - 1, "%s", argv[1]);
task = os_task_create("media_player", wavplay_sample_task, NULL, 4096, 18, 5);
OS_ASSERT(task);
os_task_startup(task);
return 0;
}
SH_CMD_EXPORT(wav_player, wavplay_sample, "play wav file in task");
运行结果如下:
sdmmc_test
sh />sdmmc_test
[255] I/VFS_FS tshell: Mount fat to / [vfs_mount][402]
Filesystem initialized!
sh />
sh />
sh />ls
Directory /:
System Volume Information<DIR>
zhou.wav 50883344
lin.wav 52616064
3.wav 43159596
zhang.wav 38382556
liu.wav 37643974
fei.wav 14018886
MUSIC <DIR>
sh />
sh />
sh />wav_player
Usage:
wavplay_sample song.wav
sh />
sh />
sh />wav_player zhang.wav
wav information:
samplerate 44100
channel 2
注意事项:
因为需要使用到TF CARD,因此需要添加相关的测试用例sdmmc_test.c。
AUDIO-RECORDER设备使用示例
#include <os_task.h>
#include <device.h>
#include <vfs_posix.h>
#include <audio/audio.h>
#include <shell.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFSZ OS_AUDIO_RECORD_MP_BLOCK_SIZE
#define SOUND_DEVICE_NAME "audio1" /* Audio device name */
#define RECORD_TIME_MS 5000
#define RECORD_SAMPLERATE 44100
#define RECORD_CHANNEL 2
#define RECORD_CHUNK_SZ ((RECORD_SAMPLERATE * RECORD_CHANNEL * 2) * 20 / 1000)
struct wav_header
{
char riff_id[4]; /* "RIFF" */
int riff_datasize; /* RIFF chunk data size,exclude riff_id[4] and riff_datasize,total - 8 */
char riff_type[4]; /* "WAVE" */
char fmt_id[4]; /* "fmt " */
int fmt_datasize; /* fmt chunk data size,16 for pcm */
short fmt_compression_code; /* 1 for PCM */
short fmt_channels; /* 1(mono) or 2(stereo) */
int fmt_sample_rate; /* samples per second */
int fmt_avg_bytes_per_sec; /* sample_rate * channels * bit_per_sample / 8 */
short fmt_block_align; /* number bytes per sample, bit_per_sample * channels / 8 */
short fmt_bit_per_sample; /* bits of each sample(8,16,32). */
char data_id[4]; /* "data" */
int data_datasize; /* data chunk size,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;
}
char recorder_name[24];
void wavrecord_sample_task(void *parameter)
{
int fd = -1;
int length, total_length = 0;
uint8_t *buffer = NULL;
struct wav_header header;
os_device_t *snd_dev;
fd = open(recorder_name, O_WRONLY | O_CREAT);
if (fd < 0)
{
os_kprintf("open file failed!\r\n");
goto __exit;
}
buffer = os_calloc(1, RECORD_CHUNK_SZ);
if (buffer == OS_NULL)
goto __exit;
write(fd, &header, sizeof(struct wav_header));
snd_dev = os_device_find(SOUND_DEVICE_NAME);
OS_ASSERT(snd_dev != OS_NULL);
os_device_open(snd_dev);
/* parameter settings */
while (1)
{
length = os_device_read_nonblock(snd_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;
}
wavheader_init(&header, RECORD_SAMPLERATE, RECORD_CHANNEL, total_length);
lseek(fd, 0, SEEK_SET);
write(fd, &header, sizeof(struct wav_header));
close(fd);
os_device_close(snd_dev);
os_kprintf("end of audio recording!\r\n");
__exit:
if (fd >= 0)
close(fd);
if (buffer)
os_free(buffer);
}
int wavrecord_sample(int argc, char **argv)
{
os_task_t *task;
if (argc != 2)
{
os_kprintf("Usage:\r\n");
os_kprintf("wavplay_sample song.wav\r\n");
return 0;
}
snprintf(recorder_name, sizeof(recorder_name) - 1, "%s", argv[1]);
task = os_task_create("media_player", wavrecord_sample_task, NULL, 4096, 18);
OS_ASSERT(task);
os_task_startup(task);
return 0;
}
SH_CMD_EXPORT(wav_recorder, wavrecord_sample, "record wav file in task");
运行结果如下:
wav_recorder
sh />wav_recorder r513.wav
sh />
sh />end of audio recording!