perf(audio): 优化音频播放帧处理逻辑,升级版本至v1.0.50

- 为test.lua添加标准文件头部注释
- 将station_broadcast.lua版本从v1.0.49更新至v1.0.50
- 新增NATIVE_AUDIO_FRAME_SAMPLES常量,将音频处理拆分为128样本每帧
- 修复空音频样本输入的边界异常问题
- 优化原生扬声器播放的循环逻辑,提升兼容性与执行效率
This commit is contained in:
2026-08-03 09:49:01 +08:00
parent 394f81b9fa
commit bf1895ebc5
+61 -33
View File
@@ -1,6 +1,6 @@
-- 1. Configuration & Constants -- 1. Configuration & Constants
local APP_VERSION_TEXT = "v1.0.49" local APP_VERSION_TEXT = "v1.0.50"
local CONFIG = { local CONFIG = {
CLOUD_URL = "ws://stabadmin.fse-media.group", CLOUD_URL = "ws://stabadmin.fse-media.group",
@@ -32,6 +32,8 @@ local COLORS = {
SUCCESS = colors.green SUCCESS = colors.green
} }
local NATIVE_AUDIO_FRAME_SAMPLES = 128
-- 2. State Management -- 2. State Management
@@ -1170,46 +1172,70 @@ 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
local pending = {} local totalSamples = #samples
if totalSamples == 0 then return true end
local speakerMap = {}
local nativeSpeakerCount = 0
for _, sp in ipairs(Peripherals.speakers) do for _, sp in ipairs(Peripherals.speakers) do
local name = peripheral.getName(sp) local name = peripheral.getName(sp)
if name then pending[name] = sp end if name then
end speakerMap[name] = sp
if type(sp.playAudio) == "function" then
local usedNativePath = false nativeSpeakerCount = nativeSpeakerCount + 1
while next(pending) do
if shouldStop(playSeq) then return false end
for name, sp in pairs(pending) do
local ok = false
pcall(function()
if type(sp.playAudio) == "function" then
ok = sp.playAudio(samples, volume)
end
end)
if ok then
usedNativePath = true
pending[name] = nil
end end
end end
if next(pending) then end
local ev = { os.pullEvent() }
if ev[1] == "audio_interrupt" and tonumber(ev[2]) and tonumber(ev[2]) >= playSeq then local controller = Peripherals.speakers[1]
local usedNativePath = false
for frameStart = 1, totalSamples, NATIVE_AUDIO_FRAME_SAMPLES do
local frameEnd = math.min(frameStart + NATIVE_AUDIO_FRAME_SAMPLES - 1, totalSamples)
local frame = {}
for i = frameStart, frameEnd do
frame[#frame + 1] = samples[i]
end
if nativeSpeakerCount > 0 then
local pending = {}
for name, sp in pairs(speakerMap) do
if 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(frame, volume)
end)
if ok then
usedNativePath = true
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
else
if not controller or type(controller.playAudioAll) ~= "function" then
return false return false
end end
end if frameStart == 1 then
end Utils.log("WARN", "Falling back to controller.playAudioAll for decoded samples")
end
if usedNativePath then
Utils.log("INFO", string.format("Audio samples queued via native speaker.playAudio: samples=%d", #samples))
else
local controller = Peripherals.speakers[1]
if controller and type(controller.playAudioAll) == "function" then
Utils.log("WARN", "Falling back to controller.playAudioAll for decoded samples")
while true do while true do
if shouldStop(playSeq) then return false end if shouldStop(playSeq) then return false end
local ok = false local ok = false
pcall(function() pcall(function()
ok = controller.playAudioAll(samples, volume) ok = controller.playAudioAll(frame, volume)
end) end)
if ok then break end if ok then break end
local ev = { os.pullEvent() } local ev = { os.pullEvent() }
@@ -1217,11 +1243,13 @@ local function playChunkOnSpeakers(samples, volume, playSeq, waitForDrain)
return false return false
end end
end end
else
return false
end end
end end
if usedNativePath then
Utils.log("INFO", string.format("Audio samples queued via native speaker.playAudio: samples=%d frame=%d", totalSamples, NATIVE_AUDIO_FRAME_SAMPLES))
end
if not waitForDrain then return true end if not waitForDrain then return true end
return waitForNativeSpeakersEmpty(Peripherals.speakers, playSeq) return waitForNativeSpeakersEmpty(Peripherals.speakers, playSeq)
end end