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:
@@ -324,7 +324,7 @@ async function sendLiveWavChunksToStation(stationName, fallbackWs, task) {
|
||||
const sampleRate = 48000;
|
||||
const channels = 1;
|
||||
const bitsPerSample = 16;
|
||||
const chunkDurationSec = 0.5;
|
||||
const chunkDurationSec = 0.25;
|
||||
const chunkBytes = Math.max(4096, Math.floor(sampleRate * channels * (bitsPerSample / 8) * chunkDurationSec));
|
||||
|
||||
targetWs.send(JSON.stringify({
|
||||
|
||||
+45
-2
@@ -2,7 +2,7 @@
|
||||
-- ============================================================================
|
||||
-- 1. Configuration & Constants
|
||||
-- ============================================================================
|
||||
local APP_VERSION_TEXT = "v1.0.4"
|
||||
local APP_VERSION_TEXT = "v1.0.5"
|
||||
|
||||
local CONFIG = {
|
||||
CLOUD_URL = "ws://stabadmin.fse-media.group",
|
||||
@@ -612,6 +612,43 @@ local function waitForNativeSpeakersEmpty(speakers, playSeq)
|
||||
return true
|
||||
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)
|
||||
if not Peripherals.speakers or #Peripherals.speakers == 0 then return false 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.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")
|
||||
return
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user