6.3.2 语音识别集成实现

1.语音识别核心实现

功能包ros2_xf_bridge的src目录下,新建C++文件v2t.cpp,并编辑文件,输入如下内容:

/*
 * 语音听写(iFly Auto Transform)技术能够实时地将语音转换成对应的文字。
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <termio.h>

#include "ros2_xf_bridge/qisr.h"
#include "ros2_xf_bridge/msp_cmn.h"
#include "ros2_xf_bridge/msp_errors.h"
#include "ros2_xf_bridge/speech_recognizer.h"

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

int wakeupFlag = 0;
int resultFlag = 0;

#define FRAME_LEN 640
#define BUFFER_SIZE 4096

/* Upload User words */
static int upload_userwords()
{
  char *userwords = NULL;
  size_t len = 0;
  size_t read_len = 0;
  FILE *fp = NULL;
  int ret = -1;

  fp = fopen("userwords.txt", "rb");
  if (NULL == fp)
  {
    printf("\nopen [userwords.txt] failed! \n");
    goto upload_exit;
  }

  fseek(fp, 0, SEEK_END);
  len = ftell(fp);
  fseek(fp, 0, SEEK_SET);

  userwords = (char *)malloc(len + 1);
  if (NULL == userwords)
  {
    printf("\nout of memory! \n");
    goto upload_exit;
  }

  read_len = fread((void *)userwords, 1, len, fp);
  if (read_len != len)
  {
    printf("\nread [userwords.txt] failed!\n");
    goto upload_exit;
  }
  userwords[len] = '\0';

  MSPUploadData("userwords", userwords, len, "sub = uup, dtt = userword", &ret); // ÉÏ´«Óû§´Ê±í
  if (MSP_SUCCESS != ret)
  {
    printf("\nMSPUploadData failed ! errorCode: %d \n", ret);
    goto upload_exit;
  }

upload_exit:
  if (NULL != fp)
  {
    fclose(fp);
    fp = NULL;
  }
  if (NULL != userwords)
  {
    free(userwords);
    userwords = NULL;
  }

  return ret;
}

static void show_result(char *string, char is_over)
{
  resultFlag = 1;

  printf("\rResult: [ %s ]", string);
  if (is_over)
    putchar('\n');
}

static char *g_result = NULL;
static unsigned int g_buffersize = BUFFER_SIZE;

void on_result(const char *result, char is_last)
{
  if (result)
  {
    size_t left = g_buffersize - 1 - strlen(g_result);
    size_t size = strlen(result);
    if (left < size)
    {
      g_result = (char *)realloc(g_result, g_buffersize + BUFFER_SIZE);
      if (g_result)
        g_buffersize += BUFFER_SIZE;
      else
      {
        printf("mem alloc failed\n");
        return;
      }
    }
    strncat(g_result, result, size);
    show_result(g_result, is_last);
  }
}
void on_speech_begin()
{
  if (g_result)
  {
    free(g_result);
  }
  g_result = (char *)malloc(BUFFER_SIZE);
  g_buffersize = BUFFER_SIZE;
  memset(g_result, 0, g_buffersize);

  printf("Start Listening...\n");
}
void on_speech_end(int reason)
{
  if (reason == END_REASON_VAD_DETECT)
    printf("\nSpeaking done \n");
  else
    printf("\nRecognizer error %d\n", reason);
}

/* demo send audio data from a file */
static void demo_file(const char *audio_file, const char *session_begin_params)
{
  int errcode = 0;
  FILE *f_pcm = NULL;
  char *p_pcm = NULL;
  unsigned long pcm_count = 0;
  unsigned long pcm_size = 0;
  unsigned long read_size = 0;
  struct speech_rec iat;
  struct speech_rec_notifier recnotifier = {
      on_result,
      on_speech_begin,
      on_speech_end};

  if (NULL == audio_file)
    goto iat_exit;

  f_pcm = fopen(audio_file, "rb");
  if (NULL == f_pcm)
  {
    printf("\nopen [%s] failed! \n", audio_file);
    goto iat_exit;
  }

  fseek(f_pcm, 0, SEEK_END);
  pcm_size = ftell(f_pcm);
  fseek(f_pcm, 0, SEEK_SET);

  p_pcm = (char *)malloc(pcm_size);
  if (NULL == p_pcm)
  {
    printf("\nout of memory! \n");
    goto iat_exit;
  }

  read_size = fread((void *)p_pcm, 1, pcm_size, f_pcm);
  if (read_size != pcm_size)
  {
    printf("\nread [%s] error!\n", audio_file);
    goto iat_exit;
  }

  errcode = sr_init(&iat, session_begin_params, SR_USER, &recnotifier);
  if (errcode)
  {
    printf("speech recognizer init failed : %d\n", errcode);
    goto iat_exit;
  }

  errcode = sr_start_listening(&iat);
  if (errcode)
  {
    printf("\nsr_start_listening failed! error code:%d\n", errcode);
    goto iat_exit;
  }

  while (1)
  {
    unsigned int len = 10 * FRAME_LEN; /* 200ms audio */
    int ret = 0;

    if (pcm_size < 2 * len)
      len = pcm_size;
    if (len <= 0)
      break;

    ret = sr_write_audio_data(&iat, &p_pcm[pcm_count], len);

    if (0 != ret)
    {
      printf("\nwrite audio data failed! error code:%d\n", ret);
      goto iat_exit;
    }

    pcm_count += (long)len;
    pcm_size -= (long)len;
  }

  errcode = sr_stop_listening(&iat);
  if (errcode)
  {
    printf("\nsr_stop_listening failed! error code:%d \n", errcode);
    goto iat_exit;
  }

iat_exit:
  if (NULL != f_pcm)
  {
    fclose(f_pcm);
    f_pcm = NULL;
  }
  if (NULL != p_pcm)
  {
    free(p_pcm);
    p_pcm = NULL;
  }

  sr_stop_listening(&iat);
  sr_uninit(&iat);
}

/* demo recognize the audio from microphone */
static void demo_mic(const char *session_begin_params)
{
  int errcode;
  int i = 0;

  struct speech_rec iat;

  struct speech_rec_notifier recnotifier = {
      on_result,
      on_speech_begin,
      on_speech_end};

  errcode = sr_init(&iat, session_begin_params, SR_MIC, &recnotifier);
  if (errcode)
  {
    printf("speech recognizer init failed\n");
    return;
  }
  errcode = sr_start_listening(&iat);
  if (errcode)
  {
    printf("start listen failed %d\n", errcode);
  }
  /* demo 15 seconds recording */
  // while (i++ < 15)
  //     sleep(1);
  int ch;
  while (1)
  {
    ch = getchar();
    if (ch == 32)
    {
      printf("\nSpeaking done \n");
      break;
    }
  }

  errcode = sr_stop_listening(&iat);
  if (errcode)
  {
    printf("stop listening failed %d\n", errcode);
  }

  sr_uninit(&iat);
}

/* main thread: start/stop record ; query the result of recgonization.
 * record thread: record callback(data write)
 * helper thread: ui(keystroke detection)
 */

int main(int argc, char *argv[])
{

  rclcpp::init(argc, argv);

  rclcpp::Rate loop_rate(10);

  auto node = std::make_shared<rclcpp::Node>("v2t_node");

  // 注意此处参数 appid 的设置,需要根据自己应用的 appid 自行修改
  node->declare_parameter("appid","d7389bad");

  auto voiceWordsPub = node->create_publisher<std_msgs::msg::String>("voicewords", 10);

  termios tms_old, tms_new;
  tcgetattr(0, &tms_old);
  tms_new = tms_old;
  tms_new.c_lflag &= ~(ICANON | ECHO);
  tcsetattr(0, TCSANOW, &tms_new);

  RCLCPP_INFO(node->get_logger(), "Press \"Space\" key to Start,Press \"Enter\" key to Exit.");
  int count = 0;
  int ch;
  while (rclcpp::ok())
  {
    ch = getchar();
    printf("Pressed Key Value %d\n", ch);
    if (ch == 32)
    { // Space key
      wakeupFlag = 1;
    }
    if (ch == 10)
    { // Enter key
      RCLCPP_INFO(node->get_logger(), "Node Exit.");
      break;
    }
    if (wakeupFlag)
    {
      int ret = MSP_SUCCESS;
      int upload_on = 1; /* whether upload the user word */
      /* login params, please do keep the appid correct */
      std::string lp = "appid = " + node->get_parameter("appid").as_string() + ", work_dir = .";
      const char *login_params = lp.c_str();
      int aud_src = 0; /* from mic or file */

      /*
       * See "iFlytek MSC Reference Manual"
       */
      const char *session_begin_params =
          "sub = iat, domain = iat, language = zh_cn, "
          "accent = mandarin, sample_rate = 16000, "
          "result_type = plain, result_encoding = utf8";

      /* Login first. the 1st arg is username, the 2nd arg is password
       * just set them as NULL. the 3rd arg is login paramertes
       * */
      ret = MSPLogin(NULL, NULL, login_params);
      if (MSP_SUCCESS != ret)
      {
        MSPLogout();
        printf("MSPLogin failed , Error code %d.\n", ret);
        // goto exit; // login fail, exit the program
      }
      printf("Demo recognizing the speech from microphone\n");

      demo_mic(session_begin_params);

      wakeupFlag = 0;
      MSPLogout();
    }
    // 语音识别完成
    if (resultFlag)
    {
      resultFlag = 0;
      std_msgs::msg::String msg;
      msg.data = g_result;
      voiceWordsPub->publish(msg);
    }
    rclcpp::spin_some(node);
    loop_rate.sleep();
    count++;
  }

exit:
  tcsetattr(0, TCSANOW, &tms_old);
  MSPLogout(); // Logout...
  return 0;
}

在上述文件中,需要注意我们设置了一个名为appid的参数,该参数的值是你在讯飞开放平台创建应用时生成的值,该值需要动态调整。

2.编辑配置文件

CMakeLists.txt 中需要添加的配置如下:

add_executable(v2t src/v2t.cpp src/speech_recognizer.c src/linuxrec.c)

target_link_libraries(v2t
  ${catkin_LIBRARIES}
  libmsc.so -ldl -lpthread -lm -lrt -lasound
)

target_include_directories(v2t PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>)
ament_target_dependencies(
  v2t
  "rclcpp"
  "std_msgs"
  "geometry_msgs"
)

install(TARGETS v2t
  DESTINATION lib/${PROJECT_NAME})

install(
  DIRECTORY include/
    DESTINATION include
)

3.编译

终端中进入当前工作空间,编译功能包:

colcon build --packages-select ros2_xf_bridge

4.执行

当前工作空间下,启动终端,并输入如下指令:

. install/setup.bash
ros2 run ros2_xf_bridge v2t

再新建一个终端,可以打印订阅到的语音转文本相关话题数据,指令如下:

ros2 topic echo /voicewords

按下空格,可以开始录入语音,再次按下空格则结束录制。录制生成的文本会在第二个终端中被一并输出。

results matching ""

    No results matching ""