【UNI-APP】阿里NLS一句话听写typescript模块

阿里提供的demo代码都是javascript,自己捏个轮子。参考着自己写了一个阿里巴巴一句话听写Nls的typescript模块。VUE3的组合式API形式

startClient:开始听写,注意下一步要尽快开启识别和传数据,否则6秒后会关闭

startRecognition:开始识别事务,传入识别回调,可以打印字符或显示到屏幕

sendSound:发送二进制PCM数据(格式16MHz16bit)

stopRecognition:结束识别事务

/**
 * 阿里语音,一句话识别模块for ccframe
 *
 * 无心跳设计,非长连接推送,因此在需要使用的时候才进行连接
 *
 * @Jim 2024/07/08
 */
import * as utils from '@/utils/index'
import { nextTick } from 'vue'
// import Global from '@/utils/constants'

const NLS_SERVER_URL = 'wss://nls-gateway.aliyuncs.com/ws/v1'
const NLS_MODE = 'SpeechRecognizer' // 一句话识别
const WEBSOCKET_MAX_RETRY = 3
const RECONNECT_INTERVAL = 3000

interface INlsConfig {
  url?: string
  appkey: string // 应用的key
  token: string // 从服务器获得,要缓存
}

let client: (UniNamespace.SocketTask & { readyState?: WsState }) | undefined
const clientId = utils.uuid(utils.UUIDFormat.StandardCompact)
let taskId: string = ''
let config: INlsConfig
let reconnectAttempts = 0
let taskStarted = false

enum WsState {
  CONNECTING,
  OPEN,
  CLOSING,
  CLOSED
}

/**
 *
 * @param action
 * @returns 请求json
 */
const buildMsg: (action: string, payload: Record<string, any>) => string = (
  action,
  payload = {}
) => {
  if (taskId.length === 0) {
    taskId = utils.uuid(utils.UUIDFormat.StandardCompact)
  }
  const msg = {
    header: {
      message_id: utils.uuid(utils.UUIDFormat.StandardCompact),
      task_id: taskId,
      namespace: NLS_MODE,
      name: action,
      appkey: config.appkey
    },
    payload,
    context: {
      sdk: {
        name: 'nls-wx-sdk',
        version: '0.0.1',
        language: 'wxjs'
      }
    }
  }
  return JSON.stringify(msg, null, 0)
}

/**
 * 开启连接,开启后立即要传,否则会被关闭.
 * @param config
 * @param callback
 */
export const startClient = (
  conf?: INlsConfig,
  startCallback?: () => void,
  recognizedCallback?: (text: string) => void
) => {
  if (client && client.readyState !== WsState.CLOSED) {
    // 关闭原连接
    client.close({})
  }

  client = uni.connectSocket({
    url: conf.url ?? NLS_SERVER_URL,
    tcpNoDelay: true,
    header: {
      'X-NLS-Token': conf?.token ?? config.token
    },
    success: (res) => {
      if (!config) config = conf
      console.log(`connected to ${NLS_SERVER_URL} success`)
    },
    fail: (res) => {
      console.log(`connect to ${NLS_SERVER_URL} failed:${res.errMsg}`)
    }
  })
  client.readyState = WsState.CONNECTING

  client.onMessage((res) => {
    if (typeof res.data === 'string') {
      const msgObj = JSON.parse(res.data)
      switch (msgObj?.header?.name) {
        case 'RecognitionStarted': {
          console.log('started')
          break
        }
        case 'RecognitionResultChanged': {
          if (recognizedCallback) {
            const text = msgObj?.payload?.result
            if (text) {
              recognizedCallback(text)
            }
          }
          console.log('changed')
          break
        }
        case 'RecognitionCompleted': {
          const text = msgObj?.payload?.result
          if (text) {
            recognizedCallback(text)
          }
          taskStarted = false // 结束识别
          break
        }
        case 'TaskFailed': {
          taskStarted = false // 结束识别
          break
        }
      }
    }
    console.log('recv:' + res.data)
  })

  client.onOpen(() => {
    reconnectAttempts = 0
    client.readyState = WsState.OPEN
    if (startCallback) nextTick(startCallback)
  })

  client.onError((error) => {
    console.error('WebSocket error:', error)
    if (reconnectAttempts < WEBSOCKET_MAX_RETRY) {
      setTimeout(() => startClient(), RECONNECT_INTERVAL)
    } else {
      console.error('Max reconnect attempts reached')
    }
  })

  client.onClose(() => {
    client.readyState = WsState.CLOSED
    console.log('connection closed')
  })
}

export const startRecognition = () => {
  if (client && client.readyState === WsState.OPEN)
    client.send({
      data: buildMsg('StartRecognition', {
        format: 'opus',
        sample_rate: 16000,
        enable_intermediate_result: true,
        enable_punctuation_prediction: true,
        enable_inverse_text_normalization: true
      }),
      success: (res) => {
        taskStarted = true
      }
    })
}

export const stopRecognition = () => {
  if (client && client.readyState === WsState.OPEN)
    client.send({
      data: buildMsg('StopRecognition', {
        format: 'opus',
        sample_rate: 16000,
        enable_intermediate_result: true,
        enable_punctuation_prediction: true,
        enable_inverse_text_normalization: true
      }),
      complete: () => {
        taskStarted = false // 不管是否成功,都不发送音频了
      }
    })
}

export const sendSound = (msgBytes: ArrayBuffer) => {
  if (client && client.readyState === WsState.OPEN && taskStarted)
    client.send({
      data: msgBytes,
      success: (res) => {
        console.log('send ' + msgBytes.byteLength + ' success')
      }
    })
}

util的uuid工具见我前一篇文章https://mp.csdn.net/mp_blog/creation/editor/140267684icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/140267684

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值