UniApp集成百度语音识别:Vue2框架下的移动端实现指南

一、技术选型与前期准备

1.1 百度语音识别服务开通

开发者需先在百度智能云平台完成语音识别服务的开通。进入”语音技术”产品页,创建应用并获取API Key和Secret Key。注意区分”短语音识别”和”实时语音识别”两种模式,移动端通常采用短语音识别(REST API)以降低流量消耗。

1.2 UniApp项目配置

在Vue2项目中,需确保manifest.json配置了录音权限:

  1. {
  2. "permission": {
  3. "scope.record": {
  4. "desc": "需要录音权限以实现语音识别"
  5. }
  6. }
  7. }

同时安装axios用于HTTP请求:

  1. npm install axios --save

二、核心实现步骤

2.1 录音功能封装

创建utils/recorder.js实现跨平台录音:

  1. export default {
  2. startRecord(success, fail) {
  3. #ifdef H5
  4. // H5端使用WebRTC API
  5. navigator.mediaDevices.getUserMedia({ audio: true })
  6. .then(stream => {
  7. const mediaRecorder = new MediaRecorder(stream);
  8. mediaRecorder.ondataavailable = e => success(e.data);
  9. mediaRecorder.start();
  10. })
  11. .catch(fail);
  12. #endif
  13. #ifdef APP-PLUS
  14. // App端使用原生录音插件
  15. const recorder = plus.audio.getRecorder();
  16. recorder.record({ filename: '_doc/audio/' },
  17. data => success(data),
  18. fail
  19. );
  20. #endif
  21. },
  22. stopRecord() {
  23. #ifdef APP-PLUS
  24. plus.audio.getRecorder().stop();
  25. #endif
  26. }
  27. }

2.2 百度API认证与请求

创建api/baiduVoice.js处理认证和请求:

  1. import axios from 'axios';
  2. import CryptoJS from 'crypto-js'; // 需安装crypto-js
  3. const API_KEY = '你的API_KEY';
  4. const SECRET_KEY = '你的SECRET_KEY';
  5. // 获取access_token
  6. async function getToken() {
  7. const timestamp = Date.now();
  8. const sign = CryptoJS.HmacSHA256(
  9. `/oauth/2.0/token?grant_type=client_credentials&client_id=${API_KEY}&client_secret=${SECRET_KEY}`,
  10. SECRET_KEY
  11. ).toString();
  12. const res = await axios.get(`https://aip.baidubce.com/oauth/2.0/token`, {
  13. params: {
  14. grant_type: 'client_credentials',
  15. client_id: API_KEY,
  16. client_secret: SECRET_KEY
  17. }
  18. });
  19. return res.data.access_token;
  20. }
  21. // 语音识别
  22. export async function recognizeSpeech(audioData) {
  23. const token = await getToken();
  24. const formData = new FormData();
  25. #ifdef APP-PLUS
  26. // App端需将音频转为base64
  27. const base64 = plus.io.convertLocalFileSystemURL(audioData).then(url => {
  28. return new Promise((resolve) => {
  29. plus.io.resolveLocalFileSystemURL(url, entry => {
  30. entry.file(file => {
  31. const reader = new FileReader();
  32. reader.onload = e => resolve(e.target.result.split(',')[1]);
  33. reader.readAsDataURL(file);
  34. });
  35. });
  36. });
  37. });
  38. formData.append('speech', await base64);
  39. #endif
  40. #ifdef H5
  41. // H5端直接传递Blob
  42. formData.append('speech', audioData, 'audio.wav');
  43. #endif
  44. formData.append('format', 'wav');
  45. formData.append('rate', 16000);
  46. formData.append('channel', 1);
  47. formData.append('token', token);
  48. const res = await axios.post(
  49. 'https://vop.baidu.com/server_api',
  50. formData,
  51. {
  52. headers: { 'Content-Type': 'multipart/form-data' }
  53. }
  54. );
  55. return res.data;
  56. }

2.3 组件集成实现

创建components/VoiceInput.vue组件:

  1. <template>
  2. <view>
  3. <button @click="startRecording" :disabled="isRecording">
  4. {{ isRecording ? '录制中...' : '开始录音' }}
  5. </button>
  6. <button @click="stopRecording" :disabled="!isRecording">
  7. 停止录音
  8. </button>
  9. <view v-if="result">识别结果:{{ result }}</view>
  10. </view>
  11. </template>
  12. <script>
  13. import recorder from '@/utils/recorder';
  14. import { recognizeSpeech } from '@/api/baiduVoice';
  15. export default {
  16. data() {
  17. return {
  18. isRecording: false,
  19. audioData: null,
  20. result: ''
  21. };
  22. },
  23. methods: {
  24. async startRecording() {
  25. this.isRecording = true;
  26. recorder.startRecord(
  27. data => this.audioData = data,
  28. err => console.error('录音失败:', err)
  29. );
  30. },
  31. async stopRecording() {
  32. recorder.stopRecord();
  33. this.isRecording = false;
  34. try {
  35. const res = await recognizeSpeech(this.audioData);
  36. if (res.result) {
  37. this.result = res.result[0];
  38. }
  39. } catch (err) {
  40. console.error('识别失败:', err);
  41. uni.showToast({ title: '识别失败', icon: 'none' });
  42. }
  43. }
  44. }
  45. };
  46. </script>

三、关键问题解决方案

3.1 音频格式兼容性

百度语音识别要求:

  • 采样率:16000Hz(必须)
  • 格式:wav/pcm/amr/mp3
  • 码率:建议16kbps以上

在App端录音时需显式设置参数:

  1. // APP-PLUS录音配置
  2. plus.audio.getRecorder().record({
  3. format: 'wav',
  4. samplerate: 16000,
  5. bitrate: 256000
  6. }, success, fail);

3.2 网络请求优化

  1. Token缓存:access_token有效期24小时,可缓存避免重复获取
    ```javascript
    let cachedToken = null;
    let tokenExpire = 0;

export async function getToken() {
if (cachedToken && Date.now() < tokenExpire) {
return cachedToken;
}

const res = await axios.get(‘…’); // 同前
cachedToken = res.data.access_token;
tokenExpire = Date.now() + res.data.expires_in * 1000 - 300000; // 提前5分钟刷新
return cachedToken;
}

  1. 2. **请求超时处理**:
  2. ```javascript
  3. axios.post(url, data, {
  4. timeout: 10000, // 10秒超时
  5. headers: { 'Content-Type': 'multipart/form-data' }
  6. }).catch(err => {
  7. if (err.code === 'ECONNABORTED') {
  8. uni.showToast({ title: '请求超时', icon: 'none' });
  9. }
  10. });

3.3 错误处理机制

建立完整的错误处理体系:

  1. async function handleRecognition(audioData) {
  2. try {
  3. const res = await recognizeSpeech(audioData);
  4. if (res.error_code) {
  5. throw new Error(res.error_msg || '识别服务错误');
  6. }
  7. return res.result[0];
  8. } catch (err) {
  9. const errorMap = {
  10. 40001: '参数错误',
  11. 40002: '请求超时',
  12. 40003: '音频过长',
  13. 40004: '音频格式错误'
  14. };
  15. const msg = errorMap[err.response?.data?.error_code] || err.message;
  16. uni.showToast({ title: `识别失败: ${msg}`, icon: 'none' });
  17. throw err;
  18. }
  19. }

四、性能优化建议

  1. 音频压缩:使用opus编码减小体积

    1. // 使用opus-encoder插件(需原生插件支持)
    2. function compressAudio(blob) {
    3. return new Promise((resolve) => {
    4. const worker = new Worker('/static/opus-worker.js');
    5. worker.postMessage(blob);
    6. worker.onmessage = e => resolve(e.data);
    7. });
    8. }
  2. 分片上传:对于长音频(>60s)采用WebSocket分片传输

    1. async function streamRecognize(audioChunks) {
    2. const token = await getToken();
    3. const ws = new WebSocket(`wss://vop.baidu.com/websocket_api?token=${token}`);
    4. ws.onopen = () => {
    5. audioChunks.forEach(chunk => {
    6. ws.send(JSON.stringify({
    7. type: 'audio',
    8. data: arrayBufferToBase64(chunk)
    9. }));
    10. });
    11. ws.send(JSON.stringify({ type: 'finish' }));
    12. };
    13. let result = '';
    14. ws.onmessage = e => {
    15. const data = JSON.parse(e.data);
    16. if (data.type === 'final_result') {
    17. result = data.result;
    18. }
    19. };
    20. return new Promise(resolve => {
    21. ws.onclose = () => resolve(result);
    22. });
    23. }

五、完整项目结构建议

  1. /src
  2. /api
  3. baiduVoice.js # 百度API封装
  4. /components
  5. VoiceInput.vue # 语音输入组件
  6. /utils
  7. recorder.js # 录音封装
  8. audioProcessor.js # 音频处理工具
  9. /static
  10. opus-worker.js # WebWorker脚本(可选)
  11. App.vue # 主入口
  12. manifest.json # 权限配置

六、测试与调试要点

  1. 真机测试:H5模拟器无法测试录音权限
  2. 日志收集
    1. uni.setDebug({
    2. enableDebug: true,
    3. debugOptions: {
    4. logging: true,
    5. info: true
    6. }
    7. });
  3. 抓包分析:使用Charles或Fiddler检查API请求

七、扩展功能建议

  1. 多语言支持:通过dev_pid参数指定语言模型

    1. // 中文普通话
    2. formData.append('dev_pid', 1537);
    3. // 英文
    4. formData.append('dev_pid', 1737);
  2. 语音唤醒:结合WebAudio API实现关键词检测

  3. 离线识别:考虑集成百度离线语音SDK(需商业授权)

通过以上实现方案,开发者可以在UniApp(Vue2)项目中快速构建稳定的语音识别功能。实际开发中需注意百度API的调用频率限制(免费版QPS=5),对于高并发场景建议申请企业版服务。