/****** H5录音 ******/ let recorderManager = null; // 录音管理器实例(用于非H5平台) let innerAudioContext = null; // 音频播放管理器实例 let recordedFilePath = ""; // 录音文件路径 let mediaRecorder = null; // H5平台的录音器实例 let recordedChunks = []; // 存储H5录音片段 let audioBlob = null; // H5录音文件的 Blob 对象 // 需要引入 MP3 转换库,例如 `lamejs` import Lame from 'lamejs'; // 在实际应用中需要引入 Lame.js 库 /** * 初始化录音管理器 */ function initRecorderManager() { if (process.env.UNI_PLATFORM === 'h5') { if (window.location.origin.indexOf("https") === -1) { console.error("H5录音功能:请在 https 环境中使用插件recorder.js。"); return; } if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { console.error("当前浏览器不支持 getUserMedia API"); return; } if (typeof MediaRecorder === "undefined") { console.error("当前浏览器不支持 MediaRecorder API"); return; } if (!mediaRecorder) { navigator.mediaDevices.getUserMedia({ audio: true }) .then(stream => { try { mediaRecorder = new MediaRecorder(stream); mediaRecorder.ondataavailable = (event) => { if (event.data.size > 0) { recordedChunks.push(event.data); } }; console.log("H5 录音管理器已初始化"); } catch (e) { console.error("MediaRecorder 初始化错误:", e.message); } }) .catch(err => { console.error("H5 录音初始化错误:", err.message); }); } } else { if (!recorderManager) { recorderManager = uni.getRecorderManager(); console.log("非 H5 录音管理器已初始化"); } } } /** * 初始化音频管理器 */ function initAudioContext() { if (!innerAudioContext) { innerAudioContext = uni.createInnerAudioContext(); } } /** * 开始录音 * @param {object} options - 录音参数选项,包含录音时长、采样率等参数 */ function startRecording(options = {}) { // 确保录音管理器已初始化 initRecorderManager(); if (process.env.UNI_PLATFORM === 'h5') { if (mediaRecorder) { mediaRecorder.start(); console.log("H5 开始录音..."); } else { console.error("无法开始录音:mediaRecorder 不存在或浏览器不支持录音"); } } else { const defaultOptions = { duration: 60000, // 录音时长(毫秒) sampleRate: 44100, // 采样率 numberOfChannels: 1, // 声道数 encodeBitRate: 96000, // 编码比特率 format: "mp3", // 录音格式 }; recorderManager.start({ ...defaultOptions, ...options, }); console.log("开始录音..."); } } /** * 暂停录音 */ function pauseRecording() { if (process.env.UNI_PLATFORM === 'h5') { if (mediaRecorder && mediaRecorder.state === 'recording') { mediaRecorder.pause(); console.log("H5 暂停录音"); } } else { if (recorderManager) { recorderManager.pause(); console.log("暂停录音"); } } } /** * 继续录音 */ function resumeRecording() { if (process.env.UNI_PLATFORM === 'h5') { if (mediaRecorder && mediaRecorder.state === 'paused') { mediaRecorder.resume(); console.log("H5 继续录音"); } } else { if (recorderManager) { recorderManager.resume(); console.log("继续录音"); } } } /** * 停止录音 * @param {function} callback - 停止录音后返回录音文件路径的回调函数 */ function stopRecording(callback) { if (process.env.UNI_PLATFORM === 'h5') { if (mediaRecorder) { mediaRecorder.stop(); console.log("H5 停止录音"); // 将录音文件路径抛出 if (typeof callback === 'function') { mediaRecorder.onstop = async () => { // 先生成 Blob 对象 audioBlob = new Blob(recordedChunks, { type: 'audio/webm' }); recordedChunks = []; // 转换为 MP3 格式 const mp3Blob = await convertToMp3(audioBlob); recordedFilePath = URL.createObjectURL(mp3Blob); console.log("H5 录音停止,文件保存路径:", recordedFilePath); callback(recordedFilePath); }; } } else { console.error("无法停止录音:mediaRecorder 不存在或浏览器不支持录音"); } } else { if (recorderManager) { recorderManager.stop(); console.log("停止录音"); // 非 H5 平台录音停止后将文件路径抛出 if (typeof callback === 'function') { recorderManager.onStop((res) => { recordedFilePath = res.tempFilePath; console.log("录音停止,文件保存路径:", recordedFilePath); callback(recordedFilePath); }); } } } } /** * 转换 Blob 对象为 MP3 格式 * @param {Blob} audioBlob - 原始音频 Blob 对象 * @returns {Promise} - MP3 格式的 Blob 对象 */ async function convertToMp3(audioBlob) { // 使用 Lame.js 库将音频 Blob 转换为 MP3 格式 // 需要在实际应用中引入 Lame.js 库 return new Promise((resolve, reject) => { // 检查 Lame.js 是否存在 if (typeof Lame === "undefined") { reject(new Error("Lame.js 库未加载")); return; } const reader = new FileReader(); reader.onload = function(event) { const audioData = event.target.result; const mp3Encoder = new Lame.Mp3Encoder(1, 44100, 128); const mp3Data = []; // 使用 Lame.js 库将音频数据编码为 MP3 const samples = new Int16Array(audioData); mp3Data.push(mp3Encoder.encodeBuffer(samples)); mp3Data.push(mp3Encoder.flush()); // 生成 MP3 Blob 对象 const mp3Blob = new Blob(mp3Data, { type: 'audio/mp3' }); resolve(mp3Blob); }; reader.onerror = function(error) { reject(error); }; reader.readAsArrayBuffer(audioBlob); }); } /** * 播放音频 * @param {string} audioUrl - 音频文件 URL * @param {function} PlayCallback - 开始播放 * @param {function} progressCallback - 播放进度的回调函数 (currentTime, duration) * @param {function} errorCallback - 播放错误的回调函数 (errorMsg) * @param {function} endCallback - 播放完毕的回调函数 () * * playAudio( * audioUrl, * ()=>{ * console.log("开始播放"); * }, * (currentTime, duration) => { * console.log(`当前时间: ${currentTime}s, 总时长: ${duration}s`); * // 更新UI进度条 * }, * (errorMsg) => { * console.error("播放错误:", errorMsg); * // 显示错误信息 * }, * () => { * console.log("音频播放结束"); * // 重置 UI 状态 * } *); */ const playAudio = (audioUrl, PlayCallback, progressCallback, errorCallback, endCallback) => { if (audioUrl) { initAudioContext(); innerAudioContext.src = audioUrl; innerAudioContext.play(); console.log("播放外部音频:", audioUrl); innerAudioContext.onPlay(() => { console.log('开始播放'); PlayCallback() }); innerAudioContext.onTimeUpdate(() => { progressCallback(innerAudioContext.currentTime, innerAudioContext.duration); }); innerAudioContext.onError((error) => { const errorMsg = error.errMsg || "播放错误"; console.error("播放错误:", errorMsg); errorCallback(errorMsg); }); innerAudioContext.onEnded(() => { endCallback(); }); } else { const errorMsg = "音频 URL 不能为空"; console.error(errorMsg); errorCallback(errorMsg); } }; /** * 暂停音频播放 */ function pausePlaying() { if (innerAudioContext && innerAudioContext.paused === false) { innerAudioContext.pause(); console.log("暂停播放音频"); } } /** * 停止音频播放 */ function stopPlaying() { if (innerAudioContext) { innerAudioContext.stop(); console.log("停止播放音频"); } } // #ifdef H5 // 初始化录音管理器 initRecorderManager(); // #endif // 导出所有录音相关的工具函数 export { startRecording, pauseRecording, resumeRecording, stopRecording, playAudio, pausePlaying, stopPlaying, };