From 841f1270058427cdea8f447d05a97ecf109128f7 Mon Sep 17 00:00:00 2001 From: HenryDu8133 <813367384@qq.com> Date: Sat, 1 Aug 2026 00:21:39 +0800 Subject: [PATCH] fix(music): smooth wav chunk streaming playback Bump the station build to v1.0.4, reduce live WAV chunk size to 0.5 seconds, and keep native speaker queues fed during streaming instead of draining after every chunk so playback is less jittery and stays closer to real time. --- CloudServer/server.js | 2 +- station_broadcast.lua | 53 ++++++++++++++++++++++++++----------------- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/CloudServer/server.js b/CloudServer/server.js index 652638c..4c87be1 100644 --- a/CloudServer/server.js +++ b/CloudServer/server.js @@ -324,7 +324,7 @@ async function sendLiveWavChunksToStation(stationName, fallbackWs, task) { const sampleRate = 48000; const channels = 1; const bitsPerSample = 16; - const chunkDurationSec = 1.0; + const chunkDurationSec = 0.5; const chunkBytes = Math.max(4096, Math.floor(sampleRate * channels * (bitsPerSample / 8) * chunkDurationSec)); targetWs.send(JSON.stringify({ diff --git a/station_broadcast.lua b/station_broadcast.lua index 8c7fc2f..4e74fea 100644 --- a/station_broadcast.lua +++ b/station_broadcast.lua @@ -2,7 +2,7 @@ -- ============================================================================ -- 1. Configuration & Constants -- ============================================================================ -local APP_VERSION_TEXT = "v1.0.3" +local APP_VERSION_TEXT = "v1.0.4" local CONFIG = { CLOUD_URL = "ws://stabadmin.fse-media.group", @@ -591,8 +591,30 @@ local function waitForInterruptibleDelay(playSeq, seconds) end end -local function playChunkOnSpeakers(samples, volume, playSeq) +local function waitForNativeSpeakersEmpty(speakers, playSeq) + local waiting = {} + for _, sp in ipairs(speakers or {}) do + local name = peripheral.getName(sp) + if name then waiting[name] = true end + end + + while next(waiting) do + if shouldStop(playSeq) then return false end + local ev = { os.pullEvent() } + if ev[1] == "audio_interrupt" and tonumber(ev[2]) and tonumber(ev[2]) >= playSeq then + return false + end + if ev[1] == "speaker_audio_empty" and ev[2] and waiting[ev[2]] then + waiting[ev[2]] = nil + end + end + + return true +end + +local function playChunkOnSpeakers(samples, volume, playSeq, waitForDrain) if not Peripherals.speakers or #Peripherals.speakers == 0 then return false end + if waitForDrain == nil then waitForDrain = true end local pending = {} for _, sp in ipairs(Peripherals.speakers) do local name = peripheral.getName(sp) @@ -645,24 +667,8 @@ local function playChunkOnSpeakers(samples, volume, playSeq) end end - local waiting = {} - for _, sp in ipairs(Peripherals.speakers) do - local name = peripheral.getName(sp) - if name then waiting[name] = true end - end - - while next(waiting) do - if shouldStop(playSeq) then return false end - local ev = { os.pullEvent() } - if ev[1] == "audio_interrupt" and tonumber(ev[2]) and tonumber(ev[2]) >= playSeq then - return false - end - if ev[1] == "speaker_audio_empty" and ev[2] and waiting[ev[2]] then - waiting[ev[2]] = nil - end - end - - return true + if not waitForDrain then return true end + return waitForNativeSpeakersEmpty(Peripherals.speakers, playSeq) end local function decodePcmS8Chunk(chunk) @@ -1105,7 +1111,7 @@ function Audio.playWavStreamItem(item) tonumber(meta and meta.channels) or 0, tonumber(meta and meta.bitsPerSample) or 0 )) - if not playChunkOnSpeakers(samples, CONFIG.AUDIO_VOLUME, playSeq) then + if not playChunkOnSpeakers(samples, CONFIG.AUDIO_VOLUME, playSeq, false) then Audio.stop("current") return end @@ -1119,6 +1125,11 @@ function Audio.playWavStreamItem(item) end end + if not waitForNativeSpeakersEmpty(Peripherals.speakers, playSeq) then + Audio.stop("current") + return + end + while areHqSpeakersBusy(Peripherals.speakers) do if shouldStop(playSeq) then Audio.stop("current")