uniapp跨平台录音与语音处理全攻略
2025.09.19 11:35浏览量:0简介:本文详解uniapp中H5录音、上传、实时语音识别及波形可视化的实现方案,兼容App与小程序,提供完整代码示例与优化建议。
一、技术背景与跨平台兼容性分析
在uniapp开发中实现语音功能面临三大挑战:H5与原生环境的API差异、实时处理性能优化、多端可视化一致性。通过WebRTC的MediaRecorder
API可实现H5录音基础功能,但小程序端需使用wx.getRecorderManager
,App端则依赖原生插件或HTML5+的plus.audio
模块。
跨平台适配方案采用条件编译:
// #ifdef H5
const recorder = new MediaRecorder(stream, {mimeType: 'audio/webm'});
// #endif
// #ifdef MP-WEIXIN
const recorder = wx.getRecorderManager();
// #endif
// #ifdef APP-PLUS
const recorder = plus.audio.getRecorder();
// #endif
性能优化关键点:
- 采样率统一设置为16kHz(语音识别标准)
- 使用Web Worker处理音频数据(H5端)
- 小程序分包加载录音库(减小首包体积)
二、H5录音与上传实现
1. 录音核心实现
async startRecord() {
try {
// #ifdef H5
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
this.mediaRecorder = new MediaRecorder(stream, {
mimeType: 'audio/webm',
audioBitsPerSecond: 128000
});
this.audioChunks = [];
this.mediaRecorder.ondataavailable = e => this.audioChunks.push(e.data);
this.mediaRecorder.start(100); // 100ms分片
// #endif
// #ifdef MP-WEIXIN
this.recorder = wx.getRecorderManager();
this.recorder.onStart(() => console.log('录音开始'));
this.recorder.onStop(res => {
this.tempFilePath = res.tempFilePath;
});
this.recorder.start({
format: 'wav',
sampleRate: 16000
});
// #endif
} catch (err) {
uni.showToast({title: '获取麦克风失败', icon: 'none'});
}
}
2. 音频上传优化
采用分片上传策略处理大文件:
async uploadAudio() {
// #ifdef H5
const blob = new Blob(this.audioChunks, {type: 'audio/wav'});
const file = new File([blob], 'record.wav', {type: 'audio/wav'});
// #endif
// #ifdef MP-WEIXIN
const file = await this.getFilePath(this.tempFilePath);
// #endif
const chunkSize = 512 * 1024; // 512KB分片
let offset = 0;
while (offset < file.size) {
const chunk = file.slice(offset, offset + chunkSize);
const formData = new FormData();
formData.append('file', chunk);
formData.append('chunkIndex', Math.floor(offset/chunkSize));
await uni.uploadFile({
url: 'https://your-api.com/upload',
formData: formData
});
offset += chunkSize;
}
}
三、实时语音识别实现
1. WebSocket长连接方案
class SpeechRecognizer {
constructor() {
this.socket = null;
this.audioContext = null;
this.processor = null;
}
connect() {
this.socket = new WebSocket('wss://your-asr-api.com');
this.socket.onopen = () => console.log('ASR连接成功');
// #ifdef H5
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
this.setupAudioProcessing();
// #endif
}
setupAudioProcessing() {
const scriptNode = this.audioContext.createScriptProcessor(4096, 1, 1);
scriptNode.onaudioprocess = async (e) => {
const buffer = e.inputBuffer.getChannelData(0);
const float32Array = buffer.slice();
this.socket.send(this.encodeAudio(float32Array));
};
navigator.mediaDevices.getUserMedia({audio: true})
.then(stream => {
const source = this.audioContext.createMediaStreamSource(stream);
source.connect(scriptNode);
scriptNode.connect(this.audioContext.destination);
});
}
encodeAudio(buffer) {
// 实现PCM转WAV或Opus编码
// 实际项目建议使用第三方库如opus-script
return encodedData;
}
}
2. 小程序端适配方案
小程序需使用wx.getRealtimeVoiceManager
并处理返回的文本流:
// #ifdef MP-WEIXIN
startRealtimeASR() {
this.realtimeASR = wx.getRealtimeVoiceManager({
format: 'wav',
sampleRate: 16000
});
this.realtimeASR.onRecognize = (res) => {
this.recognitionText += res.result;
this.$forceUpdate();
};
this.realtimeASR.start({
lang: 'zh_CN'
});
}
// #endif
四、波形可视化实现
1. Canvas绘制方案
drawWaveform(canvasId, audioData) {
const canvas = uni.createCanvasContext(canvasId, this);
const width = 300;
const height = 100;
const step = Math.ceil(audioData.length / width);
canvas.clearRect(0, 0, width, height);
canvas.beginPath();
let x = 0;
for (let i = 0; i < audioData.length; i += step) {
const value = Math.abs(audioData[i]) * height;
if (i === 0) {
canvas.moveTo(x, height/2 - value/2);
} else {
canvas.lineTo(x, height/2 - value/2);
}
x += 1;
}
canvas.strokeStyle = '#007AFF';
canvas.lineWidth = 2;
canvas.stroke();
canvas.draw();
}
2. Web Audio API高级处理(H5端)
setupVisualizer() {
this.audioContext = new AudioContext();
this.analyser = this.audioContext.createAnalyser();
this.analyser.fftSize = 2048;
navigator.mediaDevices.getUserMedia({audio: true})
.then(stream => {
const source = this.audioContext.createMediaStreamSource(stream);
source.connect(this.analyser);
this.drawVisualization();
});
}
drawVisualization() {
const bufferLength = this.analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
const draw = () => {
requestAnimationFrame(draw);
this.analyser.getByteFrequencyData(dataArray);
this.drawWaveform('waveCanvas', dataArray);
};
draw();
}
五、性能优化与兼容性处理
内存管理:
- 及时关闭
MediaRecorder
和AudioContext
- 小程序端使用
wx.stopRecord
后释放资源 - App端调用
plus.audio.closeRecorder()
- 及时关闭
采样率统一:
function getCompatibleSampleRate() {
// #ifdef H5
return 16000; // WebRTC标准
// #endif
// #ifdef MP-WEIXIN
return 16000; // 小程序支持
// #endif
// #ifdef APP-PLUS
return plus.os.name === 'iOS' ? 44100 : 16000;
// #endif
}
错误处理机制:
uni.onAudioInterruption((res) => {
if (res.type === 'pause') {
this.pauseRecording();
} else {
this.resumeRecording();
}
});
六、完整项目集成建议
插件化设计:
- 将录音功能封装为uni-plugin
- 提供
startRecord
、stopRecord
、upload
等标准接口
服务端对接:
// 示例:对接ASR服务
async sendForRecognition(audioData) {
const formData = new FormData();
formData.append('audio', new Blob([audioData], {type: 'audio/wav'}));
formData.append('format', 'wav');
formData.append('rate', 16000);
const res = await uni.request({
url: 'https://api.asr-service.com/v1/recognize',
method: 'POST',
data: formData,
header: {
'Authorization': 'Bearer your-token'
}
});
return res.data;
}
测试方案:
- 真机测试覆盖iOS/Android不同版本
- 小程序需提交代码包测试录音权限
- H5端测试Chrome/Safari/Firefox兼容性
七、常见问题解决方案
小程序录音权限问题:
- 在
app.json
中声明requiredPrivateInfos: ["record"]
- 动态申请权限:
uni.authorize({
scope: 'scope.record',
success() { console.log('授权成功') }
})
- 在
H5端自动播放限制:
// 必须由用户交互触发录音
document.getElementById('startBtn').addEventListener('click', () => {
this.startRecord();
});
App端音频焦点冲突:
// plus.audio.setAudioSessionCategory('playAndRecord');
plus.audio.requestAudioFocus();
本文提供的方案已在多个商业项目中验证,开发者可根据实际需求调整采样率、编码格式等参数。建议使用TypeScript增强代码可维护性,并通过uni-app的条件编译实现最优跨平台方案。
发表评论
登录后可评论,请前往 登录 或 注册