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.
This commit is contained in:
2026-08-01 00:21:39 +08:00
parent 43962aceb0
commit 841f127005
2 changed files with 33 additions and 22 deletions
+1 -1
View File
@@ -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({
+32 -21
View File
@@ -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")