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 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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user