Files
aistream-test/测试流式传输uniapp/unpackage/dist/dev/app-plus/app-renderjs.js
T
2025-12-04 17:42:00 +08:00

544 lines
19 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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());
});
};
// <stdin>
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;
}
};
// <stdin>
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());
});
};
// <stdin>
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);
})();