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:
+61
-33
@@ -1,6 +1,6 @@
|
||||
|
||||
-- 1. Configuration & Constants
|
||||
local APP_VERSION_TEXT = "v1.0.49"
|
||||
local APP_VERSION_TEXT = "v1.0.50"
|
||||
|
||||
local CONFIG = {
|
||||
CLOUD_URL = "ws://stabadmin.fse-media.group",
|
||||
@@ -32,6 +32,8 @@ local COLORS = {
|
||||
SUCCESS = colors.green
|
||||
}
|
||||
|
||||
local NATIVE_AUDIO_FRAME_SAMPLES = 128
|
||||
|
||||
|
||||
-- 2. State Management
|
||||
|
||||
@@ -1170,46 +1172,70 @@ 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 = {}
|
||||
local totalSamples = #samples
|
||||
if totalSamples == 0 then return true end
|
||||
|
||||
local speakerMap = {}
|
||||
local nativeSpeakerCount = 0
|
||||
for _, sp in ipairs(Peripherals.speakers) do
|
||||
local name = peripheral.getName(sp)
|
||||
if name then pending[name] = sp end
|
||||
end
|
||||
|
||||
local usedNativePath = false
|
||||
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
|
||||
if name then
|
||||
speakerMap[name] = sp
|
||||
if type(sp.playAudio) == "function" then
|
||||
nativeSpeakerCount = nativeSpeakerCount + 1
|
||||
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
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
end
|
||||
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")
|
||||
if frameStart == 1 then
|
||||
Utils.log("WARN", "Falling back to controller.playAudioAll for decoded samples")
|
||||
end
|
||||
while true do
|
||||
if shouldStop(playSeq) then return false end
|
||||
local ok = false
|
||||
pcall(function()
|
||||
ok = controller.playAudioAll(samples, volume)
|
||||
ok = controller.playAudioAll(frame, volume)
|
||||
end)
|
||||
if ok then break end
|
||||
local ev = { os.pullEvent() }
|
||||
@@ -1217,11 +1243,13 @@ local function playChunkOnSpeakers(samples, volume, playSeq, waitForDrain)
|
||||
return false
|
||||
end
|
||||
end
|
||||
else
|
||||
return false
|
||||
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
|
||||
return waitForNativeSpeakersEmpty(Peripherals.speakers, playSeq)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user