手把手教你实现基于RT-Thread的百度语音识别(五)

前言

在前面的4篇连载中我们已经讲解了百度语音识别的流程,如何使用webclient软件包进行语音识别,如何使用CJson软件包进行数据解析,如何在LCD上显示识别结果,如何通过语音识别控制外设。这一切的一切的首要前提,就是语音,那我们前面使用的都是事先录制好的音频,而本次连载,我们终于要来实现录音功能了,有了录音,你想怎么识别就可以怎么识别,是不是很棒。

我将采用RT-Thread的Audio设备框架,下面我会简单介绍该框架。但在那之前,我建议你先去看看正点原子教程的 “音乐播放器” 及 “录音机” 两个例程,确保你对WAVE文件,音频编解码芯片,SAI等知识点有一定的了解。

Audio设备框架

Audio(音频)设备是嵌入式系统中非常重要的一个组成部分,负责音频数据的采样和输出。如下图所示:

undefined

RT-Thread的Audio设备驱动框架为我们提供了标准 device 接口(open/close/read/control),只要我们对接好设备框架,就可以在我们的应用代码里直接使用这些标准接口,对设备进行操作。(RT-Thread其他设备框架实现原理都是如此)

详细介绍见RT-Thread文档中心

本篇我们不具体讲解Audio设备框架的对接,因为我使用的潘多拉开发板是官方支持的板子,所以底层驱动,框架对接这部分已经有相应的支持了。这里只简单提一下设备框架的对接方法,RT-Thread所有的设备框架的对接,基本上都是两大步骤:

  1. 准备好相应的设备驱动,实现对应框架的ops函数
  2. 进行设备注册

想要弄懂这两步,RT-Thread的文档中心必须熟看,一定要去看框架源码。

应用代码

#include <rtthread.h>
#include <rtdevice.h>
#include <dfs_posix.h>

#define RECORD_TIME_MS      5000
#define RECORD_SAMPLERATE   8000
#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 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;
}

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);

注意这里:

#define RECORD_SAMPLERATE   8000
#define RECORD_CHANNEL      2

音频采样率设置为8000,通道数设置为2,因为百度语音要求的是16000(8000*2)的采样率。

同样的,我们这里导出了wavrecord_sample这个命令,使用方式:
在finsh控制台输入:

wavrecord_sample bd.wav

录音功能便开启了,程序里设置的录音时间是5s,音频将存放于bd.wav中。

再用之前实现的bd命令进行语音识别,发现效果还是非常不错的。

总结

至此,我们的录音功能就算实现了。说实话,Audio设备还是挺复杂的,需要反复学习,所以我可能讲的不好,需要大家多去看看框架的源码。

不知大家有没有发现,我前面的所有实现都是基于finsh控制台的命令来的,那实际做一个项目,总不能敲一个命令,实现一个功能吧。所以在下一篇(也是连载的最后一篇),我将把各个部分的功能拆成多个线程,通过线程间通信及同步的方式,来完成整个项目的串接,这才算是用上了实时操作系统,尽情期待~

RT-Thread作品秀】手语识别翻译发生装置作者:岁月触礁如梦 概述(说明应用产生的背景、实现功能)手语是聋人使用的语言,是由手形动作辅之以表情姿势由符号构成的比较稳定的表达系统,是一种靠动作/视觉交际的语言.手语识别的研究目标是让机器弄懂聋人的语言.因此我们选择基于 STM32 为主控,对手语识别进行识别和处理,再利用显示系统或者语音模块,从而实现利用 MCU 对手语翻译从而帮助发音障碍人士之间的交流。 开发环境(所采用的软、硬件方案)硬件:ART-PI+MPU6050 RT-Thread版本: 开发工具及版本:RT-Thread Studio RT-Thread使用情况概述(简要总结下应用中RT-Thread使用情况:内核部分、组件部分、软件包部分、内核、其他)使用rt-thread的i2c驱动部分驱动多个MPU6050,然后通过uart驱动发送出去 ART-PI 硬件框架(概述应用所采用的硬件方案框图,并对核心部分做介绍)MPU6050x6 电脑 STM32H750 采集来自 MPU6050 的数据,打包后通过uart模块发送到电 脑上进行数据处理 软件框架说明(介绍应用所采用的软件方案框图、流程图等,并加以解说)数据 声音单元 Sotfmax 全连接层 软件模块说明(介绍应用软件关键部分的逻辑、采用的实现方式等)使用 tensorflow 搭建模型判断手势,使识别成功率大大提高。模型包含一个输入层,两层全连接层,和一个输出的softmax层,最后比较输出结果,如果结果大于0.8则发出对应手势的声音 演示效果(演示效果请采用3张高清图片,并录制一段不少于1min视频解说应用所实现的效果,视频上传至B站或者腾讯视频或其他视频平台,给出链接即可)比赛感悟(可以围绕这次比赛学到了什么,克服了哪些困难,有哪些收获,不低于200字)这次比赛让我深入了解了rt-thread,对RT-Thread 软件包的使用有了丰富的经验。它是运行于 RT-Thread 物联网操作系统平台上,面向不同应用领域的通用软件组件,由描述信息、源代码或库文件组成。RT-Thread 提供了开放的软件包平台,这里存放了官方提供或开发者提供的软件包,该平台为开发者提供了众多可重用软件包的选择,这也是 RT-Thread 生态的重要组成部分。软件包生态对于一个操作系统的选择至关重要,因为这些软件包具有很强的可重用性,模块化程度很高,极大的方便应用开发者在最短时间内,打造出自己想要的系统。RT-Thread 已经支持的软件包数量已经达到60+,如下举例:。通过此次参赛,学习到了许多机器学习相关的知识,现在还处在入门阶段,对于过多数据的处理方法没有选对,使得手套动作的识别准确率不高,但后期一定会做更多的研究,做出一个完整的作品来
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值