fix(music): synchronize multi-speaker wav streaming

Bump the station build to v1.0.5, reduce live WAV chunks to 0.25 seconds, and queue decoded samples in a synchronized multi-speaker mode so multiple speakers stay aligned during streaming playback.
This commit is contained in:
2026-08-01 00:25:26 +08:00
parent 841f127005
commit 6610b168d7
2 changed files with 46 additions and 3 deletions
+1 -1
View File
@@ -324,7 +324,7 @@ async function sendLiveWavChunksToStation(stationName, fallbackWs, task) {
const sampleRate = 48000; const sampleRate = 48000;
const channels = 1; const channels = 1;
const bitsPerSample = 16; const bitsPerSample = 16;
const chunkDurationSec = 0.5; const chunkDurationSec = 0.25;
const chunkBytes = Math.max(4096, Math.floor(sampleRate * channels * (bitsPerSample / 8) * chunkDurationSec)); const chunkBytes = Math.max(4096, Math.floor(sampleRate * channels * (bitsPerSample / 8) * chunkDurationSec));
targetWs.send(JSON.stringify({ targetWs.send(JSON.stringify({
+45 -2
View File
@@ -2,7 +2,7 @@
-- ============================================================================ -- ============================================================================
-- 1. Configuration & Constants -- 1. Configuration & Constants
-- ============================================================================ -- ============================================================================
local APP_VERSION_TEXT = "v1.0.4" local APP_VERSION_TEXT = "v1.0.5"
local CONFIG = { local CONFIG = {
CLOUD_URL = "ws://stabadmin.fse-media.group", CLOUD_URL = "ws://stabadmin.fse-media.group",
@@ -612,6 +612,43 @@ local function waitForNativeSpeakersEmpty(speakers, playSeq)
return true return true
end end
local function queueSamplesSynchronized(samples, volume, playSeq)
if not Peripherals.speakers or #Peripherals.speakers == 0 then return false end
if not waitForNativeSpeakersEmpty(Peripherals.speakers, playSeq) then
return false
end
local pending = {}
for _, sp in ipairs(Peripherals.speakers) do
local name = peripheral.getName(sp)
if name and type(sp.playAudio) == "function" then
pending[name] = sp
end
end
while next(pending) do
if shouldStop(playSeq) then return false end
for name, sp in pairs(pending) do
local ok = false
pcall(function()
ok = sp.playAudio(samples, volume)
end)
if ok then
pending[name] = nil
end
end
if next(pending) then
local ev = { os.pullEvent() }
if ev[1] == "audio_interrupt" and tonumber(ev[2]) and tonumber(ev[2]) >= playSeq then
return false
end
end
end
Utils.log("INFO", string.format("Audio samples queued in synchronized multi-speaker mode: samples=%d speakers=%d", #samples, #Peripherals.speakers))
return true
end
local function playChunkOnSpeakers(samples, volume, playSeq, waitForDrain) local function playChunkOnSpeakers(samples, volume, playSeq, waitForDrain)
if not Peripherals.speakers or #Peripherals.speakers == 0 then return false end if not Peripherals.speakers or #Peripherals.speakers == 0 then return false end
if waitForDrain == nil then waitForDrain = true end if waitForDrain == nil then waitForDrain = true end
@@ -1111,7 +1148,13 @@ function Audio.playWavStreamItem(item)
tonumber(meta and meta.channels) or 0, tonumber(meta and meta.channels) or 0,
tonumber(meta and meta.bitsPerSample) or 0 tonumber(meta and meta.bitsPerSample) or 0
)) ))
if not playChunkOnSpeakers(samples, CONFIG.AUDIO_VOLUME, playSeq, false) then local playOk
if #Peripherals.speakers > 1 then
playOk = queueSamplesSynchronized(samples, CONFIG.AUDIO_VOLUME, playSeq)
else
playOk = playChunkOnSpeakers(samples, CONFIG.AUDIO_VOLUME, playSeq, false)
end
if not playOk then
Audio.stop("current") Audio.stop("current")
return return
end end