全部文档
概述 硬件支持 快速开发指南 内核 驱动 通用组件 专业组件 常见问题

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设置为同步,从模式。

audio_cubemx1

​ 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!

results matching ""

    No results matching ""