简介:本文详解uniapp中H5录音、上传、实时语音识别及波形可视化的跨平台实现方案,涵盖Web、App和小程序三端兼容技术,提供完整代码示例和性能优化建议。
H5录音通过navigator.mediaDevices.getUserMedia()获取音频流,结合AudioContext和ScriptProcessorNode实现音频数据处理。需注意浏览器兼容性,推荐使用mediaDevices-polyfill库提升兼容性。
// 获取音频流示例async function startRecording() {try {const stream = await navigator.mediaDevices.getUserMedia({ audio: true });const audioContext = new (window.AudioContext || window.webkitAudioContext)();const source = audioContext.createMediaStreamSource(stream);// 后续处理...} catch (err) {console.error('录音权限错误:', err);}}
App端使用原生插件plus.audio.getRecorder()实现,需在manifest.json中配置录音权限:
"app-plus": {"permissions": ["audio"]}
录音参数配置示例:
const recorder = plus.audio.getRecorder();recorder.record({filename: '_doc/audio/',format: 'mp3',samplerate: 16000});
微信小程序使用wx.getRecorderManager(),需在app.json中声明权限:
"permission": {"scope.record": {"desc": "需要录音权限"}}
录音配置示例:
const recorderManager = wx.getRecorderManager();recorderManager.start({format: 'mp3',sampleRate: 16000,numberOfChannels: 1});
对于大文件采用分片上传策略,使用FormData和XMLHttpRequest实现:
function uploadAudio(blob, filename) {const chunkSize = 1024 * 1024; // 1MB分片const chunks = Math.ceil(blob.size / chunkSize);for (let i = 0; i < chunks; i++) {const start = i * chunkSize;const end = Math.min(start + chunkSize, blob.size);const chunk = blob.slice(start, end);const formData = new FormData();formData.append('file', chunk, `${filename}_part${i}`);formData.append('index', i);formData.append('total', chunks);// 实际项目中应使用axios等库fetch('/upload', { method: 'POST', body: formData });}}
推荐使用兼容性好的存储方案:
建立WebSocket连接传输音频流:
const socket = new WebSocket('wss://asr.example.com');const audioProcessor = audioContext.createScriptProcessor(4096, 1, 1);audioProcessor.onaudioprocess = (e) => {const buffer = e.inputBuffer.getChannelData(0);socket.send(buffer);};socket.onmessage = (e) => {const result = JSON.parse(e.data);console.log('识别结果:', result.text);};
主流语音识别服务对比:
| 服务 | 准确率 | 延迟 | 费用 | 跨平台支持 |
|——————|————|————|——————|——————|
| 阿里云ASR | 98% | 500ms | 按量计费 | 优秀 |
| 腾讯云ASR | 97% | 600ms | 包年包月 | 优秀 |
| 科大讯飞 | 99% | 300ms | 较高 | 主要App端 |
对于隐私要求高的场景,可使用WebAssembly实现的本地识别:
// 加载Vosk模型示例async function loadModel() {const response = await fetch('vosk-model-small.wasm');const bytes = await response.arrayBuffer();const model = await Vosk.createModel(bytes);return model;}
使用Canvas绘制波形:
function drawWaveform(canvas, audioData) {const ctx = canvas.getContext('2d');const width = canvas.width;const height = canvas.height;const step = Math.ceil(audioData.length / width);ctx.clearRect(0, 0, width, height);ctx.beginPath();for (let i = 0; i < width; 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 + 1) * height / 2;const bottom = (max + 1) * height / 2;if (i === 0) {ctx.moveTo(x, top);} else {ctx.lineTo(x, top);}ctx.lineTo(x, bottom);}ctx.strokeStyle = '#00ff00';ctx.stroke();}
requestAnimationFrame进行动画使用uniapp的条件编译:
// #ifdef H5const recordingMethod = 'webAudio';// #endif// #ifdef APP-PLUSconst recordingMethod = 'nativeRecorder';// #endif// #ifdef MP-WEIXINconst recordingMethod = 'wxRecorder';// #endif
分层设计:
状态管理:
// 使用Vuex管理录音状态const store = new Vuex.Store({state: {isRecording: false,waveformData: [],recognitionResult: ''},mutations: {startRecording(state) {state.isRecording = true;},updateWaveform(state, data) {state.waveformData = data;}}});
性能监控:
本文提供的方案已在多个uniapp项目中验证,可根据实际需求调整参数和实现细节。建议开发者先实现核心功能,再逐步完善高级特性,确保各平台体验一致性。