SkylarkOS 语音唤醒使用指南

优质
小牛编辑
128浏览
2023-12-01

SkylarkOS 获取音频数据不是走linux的portaudio的接口方式,我们自己定义了一个设备驱动/dev/gxvsp,通过对它的读来进行数据的获取。

通过我们编译出vsp.ko的操作。该驱动放在文件系统的/lib/modules/4.2.25

输出音频只支持:16K,16bit,单声道,PCM音频数据

操作接口参考:vsp_ioctl.h

/* Voice Signal Preprocess
 * Copyright (C) 2001-2019 NationalChip Co., Ltd
 * ALL RIGHTS RESERVED!
 *
 * vsp_ioctl.h: VSP I/O Control command between user space application and device driver
 *
 */

#ifndef __VSP_IOCTL_H__
#define __VSP_IOCTL_H__

#define VSP_DEVICE_NAME     "vsp"
#define VSP_IRQ_NAME        "vsp"
#define VSP_CLASS_NAME      "gxvsp"

#define VSP_IOC_VERSION     0x20190819

//=================================================================================================
// For VSP_IOC_SWITCH_MODE

typedef enum {
    VSP_IOC_MODE_IDLE,
    VSP_IOC_MODE_BOOT,
    VSP_IOC_MODE_STANDBY,
    VSP_IOC_MODE_ACTIVE,
    VSP_IOC_MODE_MODEM,
    VSP_IOC_MODE_BYPASS,
    VSP_IOC_MODE_TEST,
    VSP_IOC_MODE_UAC,           // For Test
    VSP_IOC_MODE_FACTORY,
    VSP_IOC_MODE_PLC,
} VSP_IOC_MODE_TYPE;

//-------------------------------------------------------------------------------------------------
// For VSP_IOC_LOAD_DSP and VSP_IOC_LOAD_MCU

typedef struct {
    void        *buffer;
    unsigned int size;
} VSP_IOC_FIRMWARE;

//-------------------------------------------------------------------------------------------------
// For VSP_IOC_GET_INFO

typedef struct {
    /* Version related */
    unsigned int    msg_version;
    unsigned int    ioc_version;
    /* Audio related */
    unsigned int    sample_rate;
    unsigned int    mic_num;
    unsigned int    ref_num;
    unsigned int    out_num;
    unsigned int    out_interlaced;
    unsigned int    frame_length;
    unsigned int    frame_num;              /* per context */
    /* Result related */
    unsigned int    features_dim;           /* per frame */
    unsigned int    ext_buffer_size;
    /* Led related */
    unsigned int    led_num;
    /* Commannd related */
    unsigned int    cmd_num;
    unsigned int    cmd_size;
    unsigned int    cmd_data_size;
} VSP_IOC_INFO;

//-------------------------------------------------------------------------------------------------
// For VSP_IOC_GET_CONTEXT and VSP_IOC_PUT_CONTEXT

typedef struct {
    void           *addr;
    unsigned int    size;
} VSP_IOC_BUFFER;

typedef struct {
    unsigned        mic_mask:16;            /* output */
    unsigned        ref_mask:16;            /* output */
    unsigned int    frame_index;            /* output */
    unsigned int    ctx_index;              /* output */
    unsigned        vad;                    /* output */
    unsigned int    kws;                    /* output */
    unsigned int    mic_gain;               /* input */
    unsigned int    ref_gain;               /* input */
    unsigned int    direction;              /* 0 - 360 degree */
    VSP_IOC_BUFFER  features;
    VSP_IOC_BUFFER  out_buffer;             /* only 1 channel */
    VSP_IOC_BUFFER  mic_buffer[8];          /* max 8 channel */
    VSP_IOC_BUFFER  ref_buffer[2];          /* max 2 channel */
    VSP_IOC_BUFFER  ext_buffer;
} VSP_IOC_CONTEXT;

//-------------------------------------------------------------------------------------------------
// For VSP_IOC_PUT_LED_FRAME

typedef union {
    unsigned int value;
    struct {
        unsigned r:8;
        unsigned g:8;
        unsigned b:8;
        unsigned a:8;
    }bits;
} VSP_IOC_PIXEL;

typedef struct {
    unsigned short  transition;             /* transition time, in ms, should <= duration */
    unsigned short  duration;               /* duration time, in ms */
    VSP_IOC_BUFFER  pixels;                 /* list of pixels */
} VSP_IOC_LED_FRAME;

//-------------------------------------------------------------------------------------------------
// For VSP_IOC_GET_MCU_COMMAND
typedef struct {
    unsigned int    cmd_id;
    unsigned int    cmd_index;
    VSP_IOC_BUFFER  cmd_data;
} VSP_IOC_COMMAND;

//-------------------------------------------------------------------------------------------------
// For VSP_IOC_GET_WAKEUP_REASON
// The Enum is clone of VSP_CPU_WAKE_UP_REASON

typedef enum {
    VSP_IOC_WAKEUP_REASON_COLD = 0,
    VSP_IOC_WAKEUP_REASON_BUTTON,           /* User press button */
    VSP_IOC_WAKEUP_REASON_KEYWORD,          /* User Speak a keyword */
    VSP_IOC_WAKEUP_REASON_WIFI,             /* WiFi Module */
    VSP_IOC_WAKEUP_REASON_RTC,              /* RTC */
    VSP_IOC_WAKEUP_REASON_CHARGER,          /* Wall plug */
} VSP_IOC_WAKE_UP_REASON;

//-------------------------------------------------------------------------------------------------
// For VSP_IOC_SET_PARAM

typedef struct {
    void        *addr;
    unsigned int size;
} VSP_IOC_PARAM;

//=================================================================================================
// For VSP
#define VSP_IOC_SWITCH_MODE         (0x0101)    /* VSP_IOC_MODE_TYPE */
#define VSP_IOC_LOAD_DSP            (0x0102)    /* VSP_IOC_FIRMWARE */
#define VSP_IOC_LOAD_MCU            (0x0103)    /* VSP_IOC_FIRMWARE */
#define VSP_IOC_START_STREAM        (0x0104)
#define VSP_IOC_STOP_STREAM         (0x0105)
#define VSP_IOC_GET_INFO            (0x0106)    /* VSP_IOC_INFO */
#define VSP_IOC_SET_TIMEOUT         (0x0107)    /* integer, millisecond */
#define VSP_IOC_GET_CONTEXT         (0x0108)    /* VSP_IOC_CONTEXT */
#define VSP_IOC_PUT_CONTEXT         (0x0109)    /* VSP_IOC_CONTEXT */
#define VSP_IOC_SET_PARAM           (0x010A)    /* VSP_IOC_PARAM */
#define VSP_IOC_EXIT_VSP            (0x010B)    /* VSP_IOC_EXIT_VSP */
#define VSP_IOC_ENTER_UPGRADE       (0x010C)    /* VSP_IOC_ENTER_UPGRADE */
#define VSP_IOC_GET_MCU_COMMAND     (0x010D)    /* VSP_IOC_GET_MCU_COMMAND */

// For LED
#define VSP_IOC_PUT_LED_FRAME       (0x0201)    /* VSP_IOC_LED_FRAME */
#define VSP_IOC_FLUSH_LED_QUEUE     (0x0202)

// For Standby
#define VSP_IOC_GET_WAKEUP_REASON   (0x0301)    /* VSP_IOC_WAKE_UP_REASON */
#define VSP_IOC_SET_WAKEUP_MASK     (0x0302)    /* unsigned int */

// For PMU
#define VSP_IOC_GET_BATTERY_VOLTAGE (0x0403)    /* unsigned, 0 - 1024 */

#endif  /* __VSP_IOCTL_H__ */

SkylarkApp 中使用的例子

见:/skylark/senseflow/compoents/vsp/sf_vsp_input.cc

senseflow是我们的一个专门做信号处理的系统,包括音频的获取,离线识别的处理等。在另外的章节专门讲解。

单独使用的参考例子

下面给出一个用C语音实现的获取算法处理后的音频的例子

vsp_read.h

#ifndef __VSP_READ_H__
#define __VSP_READ_H__

int vsp_open(void);
void vsp_close(void);
int vsp_read(int fd, int *vad, bool readOn);

#endif

vsp_read.c

#include <vector>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "vsp_ioctl.h"

#include "udp/UdpClient.h"
#include "udp/UdpServer.h"

#define VSP_DEV_PATH "/dev/" VSP_DEVICE_NAME

static int _vspFd;
static VSP_IOC_INFO _info;
static VSP_IOC_CONTEXT _contextTemplate;
static short * context_buffer = NULL;

int vsp_open(void)
{
    // Open Device
    _vspFd = open(VSP_DEV_PATH, O_RDWR);
    if (_vspFd < 0) {
        printf("Failed to open device\n");
        return -1;
    }

    // Get Context size
    int result = ioctl(_vspFd, VSP_IOC_GET_INFO, &_info);
    if (result) {
        printf("Failed to get system info!\n");
        return -1;
    }

    printf("==========================================================\n");
    printf(" Message Version:               %x\n", _info.msg_version);
    printf(" Kernel IOC Version:            %x\n", _info.ioc_version);
    printf(" SenseFlow IOC Version:         %x\n", VSP_IOC_VERSION);
    printf(" Sample Rate:                   %d (Hz)\n", _info.sample_rate);
    printf(" MIC Channel Number:            %d (channels)\n", _info.mic_num);
    printf(" REF Channel Number:            %d (channels)\n", _info.ref_num);
    printf(" Output Channel Number:         %d (channels)\n", _info.out_num);
    printf(" Audio Frame Length:            %d (ms)\n", _info.frame_length);
    printf(" Audio Frame Num in Context:    %d (frames)\n", _info.frame_num);
    printf(" Features Dimension:            %d (dims)\n", _info.features_dim);
    printf("==========================================================\n");

    // Reserve input/output buffers
    unsigned int channel_size = _info.frame_length * _info.frame_num * _info.sample_rate / 1000;
    unsigned int channel_num = _info.mic_num + _info.ref_num + _info.out_num;
    unsigned int buffer_size = channel_size * channel_num * sizeof(short);
    context_buffer = (short *)malloc(buffer_size);
    if (!context_buffer) {
        printf("Failed to allocate %d bytes for context buffer!\n", buffer_size);
        return -1;
    }

    // Fill Context Template
    memset(&_contextTemplate, 0, sizeof(_contextTemplate));
    for (unsigned int i = 0; i < _info.mic_num; i++) {
        _contextTemplate.mic_buffer[i].addr = context_buffer + i * channel_size;
        _contextTemplate.mic_buffer[i].size = channel_size * sizeof(short);
    }
    for (unsigned int i = 0; i < _info.ref_num; i++) {
        _contextTemplate.ref_buffer[i].addr = context_buffer + (_info.mic_num + i) * channel_size;
        _contextTemplate.ref_buffer[i].size = channel_size * sizeof(short);
    }
    _contextTemplate.out_buffer.addr = context_buffer + (_info.mic_num + _info.ref_num) * channel_size;
    _contextTemplate.out_buffer.size = _info.out_num * channel_size * sizeof(short);

    result = ioctl(_vspFd, VSP_IOC_SWITCH_MODE, VSP_IOC_MODE_ACTIVE);
    if(result)
    {
        printf("Failed to switch to ACTIVE mode!\n");
        return -1;
    }

    return 0;
}

void vsp_close(void)
{
    if (_vspFd >= 0)
        close(_vspFd);

    if (context_buffer) {
        free(context_buffer);
        context_buffer = NULL;
    }
}

int vsp_read(int fd, int *vad, bool readOn)
{
    *vad = 0;

    if (_vspFd < 0)
        return -1;

    VSP_IOC_CONTEXT context = _contextTemplate;
    int result = ioctl(_vspFd, VSP_IOC_GET_CONTEXT, &context);

    if (get_azero_mute_status())
        return 0;

    if (!result) {
        // copy out to output
        if (readOn) {
            if (context.out_buffer.size == _contextTemplate.out_buffer.size)
            {
                int channel_size = _info.frame_length * _info.frame_num * _info.sample_rate / 1000;
                if (context.out_buffer.size >= channel_size * sizeof(short) * 2)
                    write(fd, context.out_buffer.addr + channel_size * sizeof(short), channel_size * sizeof(short));
                else
                    write(fd, context.out_buffer.addr, channel_size * sizeof(short));
            }
        }

        if (context.kws) {
            std::string sendInfo = "kws::" + std::to_string(context.direction);
            UdpClient::SendInfo(sendInfo.c_str(), sendInfo.length());
        }

        *vad = context.vad;
        return context.kws;
    }
    else {
        printf("Failed to get context!");
    }

    return 0;
}