From 9514e33cca88122c8d358e8c78190155bfc20520 Mon Sep 17 00:00:00 2001 From: "1054425342@qq.com" <1054425342@qq.com> Date: Tue, 9 Sep 2025 09:04:15 +0800 Subject: [PATCH] =?UTF-8?q?agent=E6=96=87=E4=BB=B6=E6=9B=BF=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/GPT/index.vue | 2 +- components/GPT/utils/audio.js | 12 +- components/GPT/utils/pcm-player.js | 131 +++---- pages.json | 6 + subPackages/user/couponList.vue | 552 +++++++++++++++++++++++++++++ 5 files changed, 641 insertions(+), 62 deletions(-) create mode 100644 subPackages/user/couponList.vue diff --git a/components/GPT/index.vue b/components/GPT/index.vue index 830a242..fe0ad51 100644 --- a/components/GPT/index.vue +++ b/components/GPT/index.vue @@ -61,7 +61,7 @@ - 点击 说话 + 按住 说话 diff --git a/components/GPT/utils/audio.js b/components/GPT/utils/audio.js index 9ff13a7..cf07b22 100644 --- a/components/GPT/utils/audio.js +++ b/components/GPT/utils/audio.js @@ -81,8 +81,8 @@ export default class Audio { } } // this.on(type, e); + this.createInter(); resolve(); - // this.createInter(); }); // 失败 socket.onError((e) => { @@ -199,6 +199,16 @@ export default class Audio { } } + createInter() { + if (this.timeoutObj) { + clearTimeout(this.timeoutObj); + } + this.timeoutObj = setTimeout(() => { + console.log(111111, this.socket) + this.socket && this.socket.send && this.socket.send({ data: 3 }); + }, HEART_BEAT_TIME); + } + // 关闭socket destroy() { if (this.socket && this.socket.readyState == 1) { diff --git a/components/GPT/utils/pcm-player.js b/components/GPT/utils/pcm-player.js index e7f0f6d..5333158 100644 --- a/components/GPT/utils/pcm-player.js +++ b/components/GPT/utils/pcm-player.js @@ -1,47 +1,76 @@ -// utils/pcm-player.js -export default class PCMPlayer { +export default class PcmPlayer { constructor(options) { - this.queue = []; - this._options = options - this.AllTexts = []; + this._options = options; this.audioCtx = null; + this.rate = this._options.rate ?? 16000; + this.ch = this._options.ch ?? 1; + this.samplesPerFrame = (16000 * 20) / 1000; + this.queue = []; + this.isPlaying = false; this.startTime = 0; - this._t = null + this._t = null; + this.AllTexts = []; } + feed(data) { if (!this.audioCtx) { this.audioCtx = wx.createWebAudioContext(); } - this.playPCM(data); + this.queue.push(new Int16Array(data)); + if (!this.isPlaying) this._play(); } - playPCM(pcmBuffer, sampleRate = 16000) { - const length = pcmBuffer.byteLength / 2; // 16-bit = 2 bytes per sample - const audioBuffer = this.audioCtx.createBuffer(1, length, sampleRate); - const channelData = audioBuffer.getChannelData(0); // Float32Array - const view = new DataView(pcmBuffer); - - // 将 16-bit PCM 转换为 Float32 [-1, 1] - for (let i = 0; i < length; i++) { - const sample = view.getInt16(i * 2, true); // little-endian - channelData[i] = sample / 0x8000; // convert to [-1, 1] + async _play() { + this.isPlaying = true; + if (!this.audioCtx) { + return } + // 开始播放 + this.startTime = this.audioCtx.currentTime ?? 0; + setTimeout(() => { + this.syncLoop(); + }); + while (this.queue.length) { + const pcm = this.queue.shift(); + const len = pcm.length / this.ch; + const buf = this.audioCtx.createBuffer(this.ch, len, this.rate); + + // 1. 归一化(防溢出) + for (let c = 0; c < this.ch; c++) { + const data = buf.getChannelData(c); + for (let i = 0; i < len; i++) { + const val = pcm[i * this.ch + c]; + data[i] = val > 0 ? val / 0x7fff : val / 0x8000; + } + } - this.queue.push(audioBuffer); - if (!this.isPlaying) { - // 开始播放 - this.startTime = this.audioCtx.currentTime ?? 0; - this.playNext(); - this.isPlaying = true; - setTimeout(() => { - this.syncLoop(); + // 2. 帧边界淡入淡出(防不连续滋滋) + const fadeSamples = Math.min(80, len); // 5ms@16kHz + const data = buf.getChannelData(0); + for (let j = 0; j < fadeSamples; j++) { + const gain = j / fadeSamples; + data[j] *= gain; // fade-in + data[len - 1 - j] *= gain; // fade-out + } + + // 3. 播放并等待结束 + await new Promise((resolve) => { + const src = this.audioCtx.createBufferSource(); + src.buffer = buf; + src.connect(this.audioCtx.destination); + src.onended = resolve; + src.start(); }); - } + this.isPlaying = false; } + // 监听音频播放进度 syncLoop() { const loop = () => { + if (!this.audioCtx) { + return + } const now = this.audioCtx.currentTime - this.startTime; // 找当前字幕 const index = this.AllTexts.findIndex( @@ -51,13 +80,13 @@ export default class PCMPlayer { const obj = this.AllTexts[index]; if (this.AllTexts.length == 1) { // 最后一条数据 - obj.is_final = true + obj.is_final = true; } const tmp = this.AllTexts.shift(); this._options.onPlay && this._options.onPlay(tmp); if (!this.AllTexts.length) { clearTimeout(this._t); - return + return; } } if (!this.AllTexts.length) { @@ -68,46 +97,28 @@ export default class PCMPlayer { } else { // 结束 clearTimeout(this._t); - console.log('语音播放结束') - this.destroy() + console.log("语音播放结束"); + this.destroy(); } } }; loop(); } - playNext() { - if (this.queue.length === 0) { - // 结束 - this.isPlaying = true; - return; - } - this.isPlaying = true; - const source = this.audioCtx.createBufferSource(); - source.buffer = this.queue.shift(); - source.connect(this.audioCtx.destination); - source.start(); - source.onended = () => { - setTimeout(() => { - this.playNext(); - }); - }; - } - // 增加文字 addText(res) { if (res.result && res.result.subtitles && res.result.subtitles.length) { - const tmpText = res.result.subtitles.map((it) => { - return { - ...it, - content: it.text, - beginTime: it.beginTime / 1000, - endTime: it.endTime / 1000, - request_id: res.requestId, - sessionId: res.sessionId - }; - }); - this.AllTexts = [...this.AllTexts, ...tmpText]; + const tmpText = res.result.subtitles.map((it) => { + return { + ...it, + content: it.text, + beginTime: it.beginTime / 1000, + endTime: it.endTime / 1000, + request_id: res.requestId, + sessionId: res.sessionId, + }; + }); + this.AllTexts = [...this.AllTexts, ...tmpText]; } } @@ -122,6 +133,6 @@ export default class PCMPlayer { this.audioCtx = null; this.AllTexts = []; this.startTime = 0; - this._options.onAudioEnd && this._options.onAudioEnd() + this._options.onAudioEnd && this._options.onAudioEnd(); } } diff --git a/pages.json b/pages.json index 453b54f..af5712a 100644 --- a/pages.json +++ b/pages.json @@ -315,6 +315,12 @@ "navigationBarTextStyle": "white", "navigationStyle": "custom" } + }, + { + "path": "user/couponList", + "style": { + "navigationBarTitleText": "优惠券" + } } ] }, diff --git a/subPackages/user/couponList.vue b/subPackages/user/couponList.vue new file mode 100644 index 0000000..2f9e0a6 --- /dev/null +++ b/subPackages/user/couponList.vue @@ -0,0 +1,552 @@ + + + + +