简介:本文详细解析uniapp中实现H5录音、上传、实时语音识别及波形可视化的技术方案,提供跨平台兼容性解决方案与代码示例,助力开发者快速构建语音交互功能。
在智能客服、语音笔记、在线教育等场景中,录音、语音识别与波形可视化已成为核心功能需求。uniapp作为跨平台开发框架,需同时兼容H5、App(iOS/Android)及小程序环境,开发者面临三大挑战:
// H5录音实现(需用户授权)async startH5Recording() {try {const stream = await navigator.mediaDevices.getUserMedia({ audio: true });const audioContext = new (window.AudioContext || window.webkitAudioContext)();const source = audioContext.createMediaStreamSource(stream);const processor = audioContext.createScriptProcessor(1024, 1, 1);processor.onaudioprocess = (e) => {const buffer = e.inputBuffer.getChannelData(0);this.processAudioData(buffer); // 实时处理音频数据};source.connect(processor);processor.connect(audioContext.destination);this.recorder = { stream, processor };} catch (err) {console.error('录音失败:', err);}}
plus.audio.getRecorder()(5+ API)wx.getRecorderManager()
const getRecorder = () => {if (uni.getSystemInfoSync().platform === 'h5') {return { start: startH5Recording };} else if (process.env.VUE_APP_PLATFORM === 'mp-weixin') {return uni.getRecorderManager();} else {return plus.audio.getRecorder();}};
分片上传:处理大文件(示例使用uni.uploadFile)
async uploadAudio(filePath) {const chunkSize = 1024 * 512; // 512KB分片const fileSize = (await uni.getFileInfo({ filePath })).size;const chunks = Math.ceil(fileSize / chunkSize);for (let i = 0; i < chunks; i++) {const start = i * chunkSize;const end = Math.min(start + chunkSize, fileSize);const chunkPath = await this.sliceAudio(filePath, start, end);await uni.uploadFile({url: 'https://your-api.com/upload',filePath: chunkPath,formData: { chunk: i, total: chunks }});}}
// 建立WebSocket连接const startSpeechRecognition = () => {const socket = new WebSocket('wss://your-asr-api.com');const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' });mediaRecorder.ondataavailable = (e) => {if (e.data.size > 0) {socket.send(e.data);}};socket.onmessage = (e) => {const result = JSON.parse(e.data);this.updateTranscript(result.text); // 更新识别结果};mediaRecorder.start(100); // 100ms间隔发送};
wx.onBackgroundAudioPlay监听音频流
// 初始化CanvasinitWaveform() {const canvas = document.getElementById('waveform');this.ctx = canvas.getContext('2d');this.canvasWidth = canvas.width;this.canvasHeight = canvas.height;}// 更新波形updateWaveform(audioData) {this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);this.ctx.beginPath();const step = Math.ceil(audioData.length / this.canvasWidth);const amp = this.canvasHeight / 2;for (let i = 0; i < this.canvasWidth; i++) {const min = 1.0;const max = -1.0;for (let j = 0; j < step; j++) {const datum = audioData[(i * step) + j];if (datum < min) min = datum;if (datum > max) max = datum;}const x = i;const top = min * amp;const height = (max - min) * amp;if (i === 0) {this.ctx.moveTo(x, top + (height / 2));} else {this.ctx.lineTo(x, top + (height / 2));}}this.ctx.stroke();}
降采样处理:对原始音频数据进行抽样
function downsample(buffer, sampleRate, targetRate) {const newLength = Math.round(buffer.length * targetRate / sampleRate);const result = new Float32Array(newLength);const step = buffer.length / newLength;for (let i = 0; i < newLength; i++) {const pos = i * step;const left = Math.floor(pos);const right = Math.ceil(pos);const t = pos - left;if (right < buffer.length) {result[i] = buffer[left] * (1 - t) + buffer[right] * t;} else {result[i] = buffer[left];}}return result;}
插件市场方案:
uni-recorder插件ifly-voice-recognitionwavesurfer.js库权限管理清单:
microphone权限请求manifest.json中的录音权限app.json中声明record权限调试技巧:
wx.getSetting检查权限状态plus.io.resolveLocalFileSystemURL验证文件路径iOS录音失败:
<uses-permission android:name="android.permission.RECORD_AUDIO" />到原生配置语音识别延迟:
波形卡顿:
requestAnimationFrame)通过以上技术方案,开发者可在uniapp中实现全平台兼容的语音处理功能。实际开发中建议先完成H5端的完整实现,再通过条件编译逐步适配App和小程序环境。对于商业项目,推荐采用成熟的语音服务SDK以降低开发成本。