RVB2601开发板试用5——远程音频采集系统

简介: 远程音频采集系统

本文作者:我爱下载


1、概述

远程音频采集系统利用RVB2601的ES7210麦克风数字化采样,W800的WiFi通讯和OLED液晶显示相关信息,进行音频收集和存储。开发专用的上位软件,通过以太网通讯启动下位机完成一定数据量的音频收集后,传输到上位机,并存储为RAW文件。


系统中使用了麦克风音频采集ADC,W800无线以太网通讯,OLED液晶显示,指示灯等几个外设设备。


2、测试程序

通过概述的描述,利用之前的外设测试和学习经验,本次开发包括下位机软件,和上位机软件。


2.1 下位机软件

下位机软件主要包含wifi接口处理,音频采集、缓存管理和OLED三个主要部分。


1) 初始化

据以太网通讯的研究,《以太网通讯测试》连接 http://bbs.eeworld.com.cn/thread-1174636-1-1.html  建立wifi连接,获取ip地址,这里为了后面的处理方便,路由器中设置了固定的ip地址和mac的绑定关系,保证ip地址的获取唯一。


根据音频测试的研究,《麦克风录音测试》 连接 http://bbs.eeworld.com.cn/thread-1175631-1-1.html  完成音频采样音频初始化,并进入等待状态。


为了更好的管理采样数据,建立一个RingBuffer缓冲队列。开辟一个48K的缓冲区,用来存储音频采样数据。

ringbuffer_t mic_ring_buffer;
#define MIC_RECORDER_BUF_SIZE 49152
uint8_t repeater_data_addr[MIC_RECORDER_BUF_SIZE];
//设置录音缓冲区环形队列
mic_ring_buffer.buffer = repeater_data_addr;
mic_ring_buffer.size = MIC_RECORDER_BUF_SIZE;
ringbuffer_reset(&mic_ring_buffer);


创建一个画面,画面中包换本次试用程序的标题“THEAD RVB2601 RECORDER”和2个显示标签。显示标签中实时显示当前的运行状态。

static void gui_label_create(void)
{
  lv_obj_t* scr = lv_scr_act();//获取当前活跃的屏幕对象
    lv_obj_t *p = lv_label_create(scr, NULL);
    lv_label_set_long_mode(p, LV_LABEL_LONG_BREAK);
    lv_label_set_align(p, LV_LABEL_ALIGN_CENTER);
    lv_obj_set_pos(p, 0, 4);
    lv_obj_set_size(p, 128, 60);
    lv_label_set_text(p, "THEAD RVB2601\nRECORDER");
    p = lv_label_create(scr, NULL);
    lv_label_set_align(p, LV_LABEL_ALIGN_LEFT);
    lv_obj_set_pos(p, 0, 40);
    lv_obj_set_size(p, 60, 20);
    lv_label_set_text(p, "STATUS:");
    pLabel = lv_label_create(scr, NULL);
    lv_label_set_align(pLabel, LV_LABEL_ALIGN_CENTER);
    lv_obj_set_pos(pLabel, 65, 40);
    lv_obj_set_size(pLabel, 50, 20);
    lv_label_set_text(pLabel, "idel");
}

建立了以太网接收函数,音频采集任务,音频数据发送任务,图形显示这4个主要任务,并利用信号量完成任务间的协调工作。


  • 音频采集任务
aos_task_new("mic_recorder", main_mic_task, NULL, 2 * 1024);


  • 音频数据发送任务
aos_task_new("mic_recorder_send", wifi_send_task, NULL, 4 * 1024);


  • 图形显示任务
aos_task_new("gui", gui_lvgl_task, NULL, 10 * 1024);


  • 以太网接收函数通过串口终端启动。
int mic_tcpclient(void);


2) 以太网数据接收

根据前面的研究,RVB2601只能作为以太网的Client端,所以,这里必须以以太网的客户端方式建立连接,完成数据收发。

int mic_tcpclient(void)
{
    struct sockaddr_in  sAddr;
    int                 iAddrSize;
    int                 iStatus;
    char            *cBsdBuf = NULL;
//    int time_ms = aos_now_ms();
//    int time_ms_step = aos_now_ms();
  running = 1;
    cBsdBuf = lan_buf;
    //filling the TCP server socket address
    FD_ZERO(&sAddr);
    sAddr.sin_family = AF_INET;
    sAddr.sin_port = htons(26666);
    sAddr.sin_addr.s_addr = inet_addr("192.168.1.3");
    iAddrSize = sizeof(struct sockaddr_in);
    // creating a TCP socket
    iSockFD = socket(AF_INET, SOCK_STREAM, 0);
    if (iSockFD < 0) {
        LOGE(TAG, "TCP ERROR: create tcp client socket fd error!");
        goto Exit1;
    }
    LOGD(TAG, "ServerIP=%s port=%d.", "192.168.1.3", 26666);
    LOGD(TAG, "Create socket %d.", iSockFD);
    // connecting to TCP server
    iStatus = connect(iSockFD, (struct sockaddr *)&sAddr, iAddrSize);
    if (iStatus < 0) {
        LOGE(TAG, "TCP ERROR: tcp client connect server error! ");
        goto Exit;
    }
    LOGD(TAG, "TCP: Connect server successfully.");
  record_sta = NET_CONNECT;
    // sending multiple packets to the TCP server
    while (running) {
    if ( (iStatus = read(iSockFD, cBsdBuf, 1024)) < 0) 
        {
            break;
        }
    //解析数据
    if(cBsdBuf[0] == 1) //起动录音
    {
      LOGD(TAG, "get record start command!");
      mic_recorder_start();
      wifi_send_run_flag = 1;
    }
//    else if(cBsdBuf[0] == 2)  //起动传输
//    {
//      LOGD(TAG, "get stop command!");
//      mic_recorder_stop();
//      wifi_send_task_run_status = 0;
//    }
    else if(cBsdBuf[0] == 3)  
    {
      wifi_send_run_flag = 0;
      LOGD(TAG, "quit command!");
      break;
    }
    }
    LOGD(TAG, "RECORDER: Quit!");
  record_sta = NET_DISCON;
Exit:
    //closing the socket after sending 1000 packets
    close(iSockFD);
Exit1:
    return 0;
}


3)以太网数据发送

每帧发送1024字节数据,连续发送,直到所有数据发送完成为止。

void wifi_send_task(void *arg)
{
    int                 iStatus;
  int len;
  LOGD(TAG, "MIC Recorder send thread begin!");
  aos_sem_new(&mic_data_send_sem, 0); 
    while (1)
    {
        aos_sem_wait(&mic_data_send_sem, AOS_WAIT_FOREVER);
    while(wifi_send_run_flag)
    {
      len = mic_recorder_getdata_len();
      if(len > 0)
      {
        len = mic_recorder_getdata((uint8_t *)lan_buf,1024);
        if(len > 0)
        {
          record_sta = REC_SEND;
          // sending packet
          iStatus = send(iSockFD, lan_buf, len, 0);
          if (iStatus <= 0) {
            printf("TCP ERROR: tcp client send data error!  iStatus:%d", iStatus);
            record_sta = NET_ERROR;
          }
          aos_msleep(100);
        }
      }
      else
      {
        wifi_send_run_flag = 0;
      }
    }
    record_sta = IDEL;
    }
}

image.gifimage.gif

4)音频采样

音频启动中负责复位缓冲队列,然后启动音频采样。

void mic_recorder_start(void)
{
    ringbuffer_reset(&mic_ring_buffer);
    csi_codec_input_start(&codec_input_ch);
}


录取到一定数量音频后,产生触发事件,事件中利用信号量环形音频采样任务,将数据读出并存入缓冲队列。

static void codec_input_event_cb_fun(csi_codec_input_t *i2s, csi_codec_event_t event, void *arg)
{
   if (event == CODEC_EVENT_PERIOD_READ_COMPLETE) {
     aos_sem_signal(&mic_input_sem);
   } 
}


通过音频采集任务,接收事件信号触发,完成数据读取

static void main_mic_task(void *arg)
{
    LOGD(TAG, "MIC Recorder thread begin!");
    aos_sem_new(&mic_input_sem, 0); //创建音频采样信号
    mic_recorder_init(); //初始化音频采样
    while (1)
    {
       aos_sem_wait(&mic_input_sem, AOS_WAIT_FOREVER); //等待一组音频数据采集完成
       mic_recorder_to_buff(); //将采集到的音频数据压入缓冲队列中
    }
}


读取音频数据压入缓冲队列,判断采样结束,并触发音频数据发送

void mic_recorder_to_buff(void)
{
   csi_codec_input_read_async(&codec_input_ch, mic_input_buf, 1024);
   ringbuffer_in(&mic_ring_buffer, mic_input_buf, 1024);
   uint32_t len = ringbuffer_len(&mic_ring_buffer);
   if(len >= MIC_RECORDER_BUF_SIZE)
   {
       csi_codec_input_stop(&codec_input_ch);
       aos_sem_signal(&mic_data_send_sem);
   }
}


5)运行状态显示

首先创建了一个枚举类型,包换所有待显示的状态定义。

typedef enum _RECORDER_STA
{
   IDEL = 0, //空闲状态
   NET_CONNECT, //网络连接
   NET_DISCON, //网络断开
   NET_ERROR, //网络数据发送错误
   REC_INIT, //录音初始化
   REC_START, //录音启动
   REC_RUNNING, //录音采样中
   REC_SEND, //发送录音数据
}RECORDER_STA;


根据当前系统运行的状态,显示在OLED的画面上。

static void gui_lvgl_task(void *arg)
{
    lv_init();
    /*Initialize for LittlevGL*/
    oled_init();
    /*Select display 1*/
    // demo_create();
    gui_label_create();
    while (1) {
    switch(record_sta)
    {
      case  IDEL: //空闲状态
        lv_label_set_text(pLabel, "idel");
      break;
      case NET_CONNECT: //网络连接
        lv_label_set_text(pLabel, "connect");
      break;
      case NET_DISCON:    //网络断开
        lv_label_set_text(pLabel, "discon");
      break;
      case NET_ERROR:   //网络数据发送错误
        lv_label_set_text(pLabel, "neterr");
      break;
      case REC_INIT:    //录音初始化
        lv_label_set_text(pLabel, "init");
      break;
      case REC_START:   //录音启动
        lv_label_set_text(pLabel, "start");
      break;
      case REC_RUNNING: //录音采样中
        lv_label_set_text(pLabel, "rec...");
      break;
      case REC_SEND:    //发送录音数据
        lv_label_set_text(pLabel, "send");
      break;
      default:
      break;
    };
        /* Periodically call the lv_task handler.
         * It could be done in a timer interrupt or an OS task too.*/
        lv_task_handler();
        aos_msleep(5);
        lv_tick_inc(1);
    }
}


2.2 上位机软件

软件是本次测试工程的上位机软件,软件功能包括网络通讯,声音采样控制,波形展示,录音数据存储,状态显示等几个部分。利用TCPServer在端口为26666建立一个监听,接收来自下位机的连接申请。通过上位机的采样启动按钮发送启动命令,下位机接收到启动命令后启动录音采样并存储,采样结束后,触发数据发送,上位机接收到数据后存入缓存中,同时刷新波形显示。点击文件保存按钮,在弹出的对话框中输入名称和选择存储路径,完成接收到的数据保存到文件的工作。


1)界面布局

如图所示,界面中包括标题栏“RVB2601声音采集系统”,按钮控制区,包括录音启动控制和录音文件保存。状态显示区,在录音过程中,显示包括网络通讯在内的信息显示。波形展示区,将接收到的波形实时显示出来,支持波形的局部放大查看等基本功能。

16295337836361.jpg


2)波形数据分析

利用软件Audition,打开保存的录音数据,可以播放和对数据进行分析。

2.jpg


3、实测效果演示

3.1 连接建立

3.jpg


上位机在和下位机完成以太网连接后,小灯由绿色变为红色,如果连接断开,小灯由红色变为绿色。

4.jpg


通过串口中断,如果连接成功建立,屏幕打印连接服务器成功提示。

5.jpg


液晶显示屏上显示“connect"字样。


3.2 启动录音和数据传输

6.jpg


接到上位机启动录音采样命令后,终端打印录音开始。当录音采样结束后,自动进入发送程序,完成录音数据发送。液晶显示屏上也有相应的显示,由于切换速度较快,具体细节参见后面的视频部分。


3.3 波形展示

7.jpg


通过软件打开形成的录音文件和上位软件采集的数据比对,是相同的。

8.jpg


上位软件实时显示的录音数据波形。


3.4 视频

1) RVB2601录音过程中,液晶显示屏上状态变化显示


2)下位机和上位机联调全过程视频


4、写在最后总结的话

通过本次的测试,体验到了国产RSIC-V的发展,尤其是平头哥的这款处理器,和CDK开发环境的组合,给我的感觉是丝般顺滑的开发过程。而且在遇到问题的时候,钉钉上的技术支持和网站上的工单问题解决方式,都非常有效率,非常不错的一次测试。  


注:联调视频拍的不太好,主要是手机拍摄,画面范围小,而且一个人拍不太好操作。


本文源自:平头哥芯片开放社区

相关文章
|
6月前
|
网络协议 Go 网络安全
一种远程升级PLC和HMI组态屏程序的方法-做个笔记
一种远程升级PLC和HMI组态屏程序的方法-做个笔记
100 2
|
3月前
|
编解码 Linux 开发工具
Linux平台x86_64(麒麟|统信UOS)|aarch64(飞腾)如何实现摄像头|屏幕和麦克风|扬声器采集推送RTMP服务或轻量级RTSP服务
国产化操作系统的发展,减少了外部依赖,更符合国家安全标准,并可提升自主研发能力,促进产业链发展,满足定制开发能力,减少了外部技术封锁的风险,提高了国际竞争力,推动了产业升级。目前大牛直播SDK针对Linux平台x86_64架构和aarch64架构的RTMP推送模块和轻量级RTSP服务模块
|
传感器 Ubuntu Java
ESP-IDF 蓝牙开发实战 — 传感器数据上传及手机控制开发板
ESP32-C3 蓝牙部分我们学习了GATT,本文博主手把手带领大家使用 ESP32-C3的蓝牙做一个简单的小应用。
1241 0
ESP-IDF 蓝牙开发实战 — 传感器数据上传及手机控制开发板
|
Ubuntu 网络协议 数据安全/隐私保护
RK3568开发笔记(六):开发板烧写ubuntu固件(支持mipi屏镜像+支持hdmi屏镜像)
编译了uboot,kernel,buildroot后,可以单独输入固件,也可以整体打包成rootfs进行一次性输入,rootfs直接更新升级这个方式目前也是常用的。本篇刷了2个镜像,一个支持mipi屏幕得ubuntu固件,一个支持hdmi固件,但是都不支持笔者的usb触摸屏
|
传感器 定位技术 计算机视觉
树莓派开发笔记(九):基于CSI口的摄像头拍照程序(同样适用USB摄像头)
树莓派开发笔记(九):基于CSI口的摄像头拍照程序(同样适用USB摄像头)
树莓派开发笔记(九):基于CSI口的摄像头拍照程序(同样适用USB摄像头)
|
数据采集 物联网 Linux
Unity3D下实现Linux平台RTMP推流(以采集Unity窗体和声音为例)
随着物联网等行业的崛起,越来越多的传统行业如虚拟仿真、航天工业、工业仿真、城市规划等,对Linux下的生态构建,有了更大的期望,Linux平台下,可选的直播推拉流解决方案相对Windows和移动端,非常少,基于Unity的Linux推送方案,更是几无参考。本文以Unity3d环境下Linux平台推送Unity窗体和Unity采集的音频,然后编码推送到RTMP服务器为例,大概说下实现过程。
189 0
|
缓存 网络协议 API
Qt广告机服务器(上位机)
客户端列表(下位机) 广告图片广播 天气信息多选点播 消息提醒广播 日期显示模块
99 0
An工具介绍之摄像头
An工具介绍之摄像头
322 0
An工具介绍之摄像头
|
传感器 API
HarmonyOS系统中内核实现温湿度采集方法
大家好,今天主要来聊一聊,如何使用鸿蒙系统中的温湿度传感器方法。
219 0
HarmonyOS系统中内核实现温湿度采集方法
|
JavaScript 前端开发 网络协议
基于阿里云 Haas510 制作数据转发服务
通过Haas-510制作Lora 无线发射器,只有 Javascript 的开发背景,是否可以完成这项任务呢?答案是肯定的!接下来内容将说明如何使用 Haas-510 ,完成数据转发的过程;本篇内容需要简单的 终端、Nodejs 知识,代码量很少,前端程序员、nodejs 程序员很容易上手。
1486 1
基于阿里云 Haas510 制作数据转发服务