var __renderjsModules={}; __renderjsModules["49b84592"] = (() => { var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // var stdin_exports = {}; __export(stdin_exports, { default: () => stdin_default }); // C:/Users/1/Desktop/testAudio/测试流式传输uniapp/pages/index/StreamPlayer.js var StreamPlayer = class { constructor({ inputSampleRate = 16e3, numChannels = 1, bitDepth = 16, littleEndian = true, pcmType = "int", callback = () => { } } = {}) { if (![16, 32].includes(bitDepth)) throw new Error("bitDepth \u5FC5\u987B\u662F 16 \u6216 32"); if (inputSampleRate <= 0) throw new Error("\u91C7\u6837\u7387\u5FC5\u987B\u5927\u4E8E 0"); if (!["int", "float"].includes(pcmType)) throw new Error("pcmType \u5FC5\u987B\u662F int \u6216 float"); this.audioContext = new (window.AudioContext || window.webkitAudioContext)(); this.inputSampleRate = inputSampleRate; this.numChannels = numChannels; this.bitDepth = bitDepth; this.littleEndian = littleEndian; this.pcmType = pcmType; this.audioQueue = []; this.playbackEndTime = 0; this.currentSource = null; this.lastBlockTail = null; this.callback = callback; } appendChunk(audioData) { console.log("this.audioQueue", this.audioQueue.length); this.audioQueue.push(audioData); this._processQueue(); } destroy() { this.audioQueue = []; this.playbackEndTime = 0; if (this.currentSource) { this.currentSource.stop(); this.currentSource = null; } if (this.audioContext) { this.audioContext.close(); this.audioContext = null; } } _processQueue() { return __async(this, null, function* () { if (this.audioQueue.length === 0) { this.callback("ended"); return; } if (!this.audioContext || this.currentSource) return; const buffer = this._mergeArrayBuffers(this.audioQueue); this.audioQueue = []; try { const audioBuffer = this._convertPCM(buffer); yield this._schedulePlay(audioBuffer); this._processQueue(); } catch (err) { console.error("\u97F3\u9891\u5904\u7406\u5931\u8D25:", err.name, err.message, err.stack); } }); } _convertPCM(buffer) { const bytesPerSample = this.bitDepth / 8; const requiredBytes = bytesPerSample * this.numChannels; const validByteLength = Math.floor(buffer.byteLength / requiredBytes) * requiredBytes; const validBuffer = buffer.slice(0, validByteLength); const dataView = new DataView(validBuffer); const numSamples = validByteLength / requiredBytes; const audioBuffer = this.audioContext.createBuffer( this.numChannels, numSamples, this.inputSampleRate ); for (let ch = 0; ch < this.numChannels; ch++) { const channelData = audioBuffer.getChannelData(ch); for (let i = 0; i < numSamples; i++) { const pos = i * this.numChannels * bytesPerSample + ch * bytesPerSample; let value; if (this.bitDepth === 16) { value = dataView.getInt16(pos, this.littleEndian) / 32768; } else { if (this.pcmType === "int") { value = dataView.getInt32(pos, this.littleEndian) / 2147483648; } else { value = dataView.getFloat32(pos, this.littleEndian); } } channelData[i] = value; } let sum = 0; for (let i = 0; i < channelData.length; i++) { sum += channelData[i]; } const dcOffset = sum / channelData.length; for (let i = 0; i < channelData.length; i++) { channelData[i] -= dcOffset; } const fadeLength = Math.min(100, channelData.length); for (let i = 0; i < fadeLength; i++) { channelData[i] *= i / fadeLength; } const startFadeOut = Math.max(0, channelData.length - fadeLength); for (let i = startFadeOut; i < channelData.length; i++) { const factor = 1 - (i - startFadeOut) / fadeLength; channelData[i] *= factor; } } const crossfadeLength = 50; if (this.lastBlockTail && this.lastBlockTail.length === this.numChannels) { for (let ch = 0; ch < this.numChannels; ch++) { const channelData = audioBuffer.getChannelData(ch); const prevTail = this.lastBlockTail[ch]; if (prevTail.length >= crossfadeLength && channelData.length >= crossfadeLength) { for (let i = 0; i < crossfadeLength; i++) { const prevWeight = (crossfadeLength - i) / crossfadeLength; const currWeight = i / crossfadeLength; channelData[i] = channelData[i] * currWeight + prevTail[i] * prevWeight; } } } } this.lastBlockTail = []; for (let ch = 0; ch < this.numChannels; ch++) { const channelData = audioBuffer.getChannelData(ch); const tailStart = Math.max(0, channelData.length - crossfadeLength); this.lastBlockTail[ch] = channelData.slice(tailStart); } return audioBuffer; } _schedulePlay(audioBuffer) { return new Promise((resolve) => { const source = this.audioContext.createBufferSource(); source.buffer = audioBuffer; source.connect(this.audioContext.destination); const plannedStartTime = this.playbackEndTime; const now = this.audioContext.currentTime; let startTime = Math.max(plannedStartTime, now); const timeDrift = plannedStartTime - now; if (timeDrift < -0.02) { source.playbackRate.value = Math.min(1.04, 1 - timeDrift / audioBuffer.duration); } else if (timeDrift > 0.02) { source.playbackRate.value = Math.max(0.96, 1 - timeDrift / audioBuffer.duration); } else { source.playbackRate.value = 1; } source.start(startTime); this.playbackEndTime = startTime + audioBuffer.duration / source.playbackRate.value; source.onended = () => { this.currentSource = null; resolve(); }; this.currentSource = source; }); } // 合并多个 ArrayBuffer 的辅助函数 _mergeArrayBuffers(buffers) { let totalLength = 0; for (let buffer of buffers) { totalLength += buffer.byteLength; } const result = new Uint8Array(totalLength); let offset = 0; for (let buffer of buffers) { const view = new Uint8Array(buffer); result.set(view, offset); offset += view.length; } return result.buffer; } }; // var player = null; var stdin_default = { mounted() { const _this = this; player = new StreamPlayer({ inputSampleRate: 44100, // 后端PCM采样率(如8000/24000) numChannels: 1, // 声道数(单声道/双声道) bitDepth: 16, // 位深(16/32bit) littleEndian: true, // 端序(和后端一致) pcmType: "int", // 类型(int/float) callback: _this.callback }); }, methods: { appendPCMChunk(numArray) { if (!numArray || numArray.length === 0) return; const uint8 = new Uint8Array(numArray); const arrayBuffer = uint8.buffer; player.appendChunk(arrayBuffer); }, // 停止播放(销毁播放器) stop() { if (player) { player.destroy(); player = null; const _this = this; player = new StreamPlayer({ inputSampleRate: 16e3, numChannels: 1, bitDepth: 16, littleEndian: true, pcmType: "int", callback: _this.callback }); } }, // 播放结束回调(通知主线程) callback(e) { if (e === "ended") { this.$ownerInstance.callMethod("changeStreamPlaying", { type: false }); } } } }; return __toCommonJS(stdin_exports); })(); __renderjsModules["1e54c19a"] = (() => { var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // var stdin_exports = {}; __export(stdin_exports, { default: () => stdin_default }); var mediaStream; var audioContext; var processor; var recordedChunks = []; var decibelHistory = []; var DB_CONFIG = { minDecibels: -80, // 最小可测分贝 maxDecibels: -30, // 最大可测分贝 smoothingTimeConstant: 0.8 // 平滑系数,使分贝变化更平缓 }; var stdin_default = { data() { return {}; }, methods: { startRecord(options) { return __async(this, null, function* () { if (options == null) return; if (audioContext) return; try { var a, i = options.sampleRate, s = options.frameSize; if (options.gain < 1) { options.gain = 1; } if (options.gain > 20) { options.gain = 20; } audioContext = new AudioContext(); yield audioContext.audioWorklet.addModule("static/dist/processor.worklet.js"); mediaStream = yield navigator.mediaDevices.getUserMedia({ audio: { sampleRate: options.sampleRate, channelCount: 1 } }); const source = audioContext.createMediaStreamSource(mediaStream); processor = new AudioWorkletNode(audioContext, "processor-worklet"); processor.port.postMessage({ type: "init", data: { frameSize: options.frameSize, // 样本数 (1280字节 / 2字节每样本) fromSampleRate: 48e3, // 输入采样率 toSampleRate: options.sampleRate, // 输出采样率 (1/3) arrayBufferType: "short16", gain: options.gain } }); processor.port.onmessage = (t) => { var r = t.data, o = r.frameBuffer, n = r.isLastFrame; if (o && o.byteLength > 0) { const decibels = this.calculateDecibels(o); this.onDecibelsCalculated(decibels); decibelHistory.push(decibels); if (decibelHistory.length > 100) { decibelHistory.shift(); } } if (null == o ? void 0 : o.byteLength) for (var a2 = 0; a2 < o.byteLength; ) { const frameData = { isLastFrame: n && a2 + s >= o.byteLength, frameBuffer: t.data.frameBuffer.slice(a2, a2 + s) }; this.onFrameRecorded(frameData); recordedChunks.push(frameData.frameBuffer); a2 += s; } else this.onFrameRecorded(t.data); }; source.connect(processor); processor.connect(audioContext.destination); } catch (err) { this.$ownerInstance.callMethod("toShowToast"); } }); }, onFrameRecorded({ isLastFrame, frameBuffer }) { this.$ownerInstance.callMethod("frameRecorded", { isLastFrame, frameBuffer: this.toBase64(frameBuffer) }); }, calculateDecibels(frameBuffer) { try { const samples = new Int16Array(frameBuffer); let sum = 0; for (let i = 0; i < samples.length; i++) { const value = samples[i] / 32768; sum += value * value; } const rms = Math.sqrt(sum / samples.length); if (rms < 1e-5) { return DB_CONFIG.minDecibels; } let db = 20 * Math.log10(rms); if (decibelHistory.length > 0) { const lastDb = decibelHistory[decibelHistory.length - 1]; db = lastDb * DB_CONFIG.smoothingTimeConstant + db * (1 - DB_CONFIG.smoothingTimeConstant); } return Math.max(DB_CONFIG.minDecibels, Math.min(DB_CONFIG.maxDecibels, db)); } catch (e) { console.error("\u8BA1\u7B97\u5206\u8D1D\u65F6\u51FA\u9519:", e); return DB_CONFIG.minDecibels; } }, onDecibelsCalculated(decibels) { this.$ownerInstance.callMethod("decibels", decibels.toFixed(1)); }, toBase64(buffer) { let binary = ""; const bytes = new Uint8Array(buffer); const len = bytes.byteLength; for (let i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); } return window.btoa(binary); }, onRecordedChunks(chunks) { return __async(this, null, function* () { var mergedBuffer = this.mergeAudioBuffers(chunks); const wavBlob = this.createWavBlob(mergedBuffer, 1, 16e3); const base64 = yield this.blobToBase64(wavBlob); this.$ownerInstance.callMethod("recordedChunks", base64); }); }, onStop(value) { if (value !== "stop") return; this.onFrameRecorded({ isLastFrame: true, frameBuffer: "" }); this.onRecordedChunks(recordedChunks); recordedChunks = []; if (mediaStream) { mediaStream.getTracks().forEach((track) => track.stop()); mediaStream = null; } if (processor) { processor.disconnect(); processor = null; } if (audioContext) { audioContext.close().then(() => { audioContext = null; }); } }, //合并所有buffer mergeAudioBuffers(buffers) { let totalLength = buffers.reduce((acc, buf) => acc + buf.byteLength, 0); const result = new Uint8Array(totalLength); let offset = 0; buffers.forEach((buffer) => { result.set(new Uint8Array(buffer), offset); offset += buffer.byteLength; }); if (offset !== totalLength) console.error("\u5408\u5E76\u540E\u7684\u957F\u5EA6\u4E0D\u7B26\uFF01"); return result.buffer; }, // 新增WAV封装函数(基于前序回答的createWavBlob) createWavBlob(pcmData, numChannels, sampleRate) { const bytesPerSample = 2; const blockAlign = numChannels * bytesPerSample; const byteRate = sampleRate * blockAlign; const bufferLength = pcmData.byteLength; const totalLength = 44 + bufferLength; const buffer = new ArrayBuffer(totalLength); const view = new DataView(buffer); this.writeString(view, 0, "RIFF"); view.setUint32(4, totalLength - 8, true); this.writeString(view, 8, "WAVE"); this.writeString(view, 12, "fmt "); view.setUint32(16, 16, true); view.setUint16(20, 1, true); view.setUint16(22, numChannels, true); view.setUint32(24, sampleRate, true); view.setUint32(28, byteRate, true); view.setUint16(32, blockAlign, true); view.setUint16(34, 16, true); this.writeString(view, 36, "data"); view.setUint32(40, bufferLength, true); const pcm16 = new Int16Array(pcmData); for (let i = 0; i < pcm16.length; i++) { view.setInt16(44 + i * 2, pcm16[i], true); } return new Blob([buffer], { type: "audio/wav" }); }, // 辅助函数:Blob转Base64 blobToBase64(blob) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(blob); }); }, // 辅助函数:字符串写入DataView writeString(view, offset, string) { for (let i = 0; i < string.length; i++) { view.setUint8(offset + i, string.charCodeAt(i)); } } } }; return __toCommonJS(stdin_exports); })();