更新说明
Changelog
本文件记录各发布版本的关键变更。格式参考 Keep a Changelog, 版本号遵循语义化版本(SemVer)。
[1.9.8p] - 未发布
2026-09-25 稳定补丁收尾
- 正式候选关闭实验解耦同步栈,保留原有 KabuSync 功能;同房间立体声精度、音质与长播目标未签核,本版不再扩大同步研发。
- 共享输出保护覆盖 raw 静音及预填充;组长本代输出失败停止继续消费,操作 token 回绕跳过无效值。
- ALAC 24-bit 输入有界转换为 S16,补齐 PCM 容量与格式检查;不宣称 24-bit 端到端无损。
- 共享 ASRC 的输出积压按输出率换算;DLNA HQ 切换采用先停止、再播报、再重播。HQ 本身不是本版新增功能。
- 新增默认关闭的 AirPlay 单设备快响应,组播仍须使用标准补偿;实际响应速度不以理论等待减少量代替实测。
- 本轮复核发现网页内嵌的历史文档与源码不符,现补齐六页及 CSS 随应用发布;旧版本说明不能证明此前已实现。
- 打包在写文件前核验来源,拒绝实验同步、故障注入或缺失应用配置的候选,并绑定配置散列。
- 补齐 HTTP 截断响应的 EOF 判定与 decoder 结束时错误传播;DLNA 服务增加既有 watchdog 所请求的轻量 ping 路由。
- 自动化回归(含浏览器)1286通过、8项明确跳过;固件工具链下载阻塞,尚未完成构建或产生本轮固件包。未刷机、未做本候选实机 A/B、未提交或公开发布,详见
docs/validation_v1.9.8p.md。
新增与兼容
- 主页新增本机四模式选择、播放中确认和设备实际状态同步;实体按键、MQTT、网页共用模式状态机。
- 模式请求采用串行限频轮询与退避,断连不重发命令;跨 KabuSync 重启后自动重新读取状态。
- 六个 Web 页面与 CSS 内置于应用,使 v1.9.6Plus(客户称 v1.9.6p)一次 OTA 即获得新版网页。
保持旧分区和 NVS 布局,页面随应用槽共同升级/回退。
- OTA 与模式命令使用原子互斥门禁;播放检测覆盖 KabuSync 主机及成员;失联上传有界退出并释放门禁。
- 发布流水线支持小写 p 后缀,区分 OTA/工厂镜像,并生成 SHA-256 与版本清单。
验证说明
- 测试修正 v1.9.8 已存在的 RTSP 故障注入源码断言漂移;已移除的实验 Android 项目明确跳过。
- 升级方法见
docs/upgrade_v1.9.8p.md;实际验证结果见docs/validation_v1.9.8p.md。 - 继承 v1.9.8 已知问题,HomePod 绝对对齐不在本补丁中宣称已验证。
[1.9.8] - 2026-09-15
本版本主题:AirPlay RTSP 生命周期加固(阶段1–5)+ HomePod 混组早响根因修复, 并以真机三机台架(COM59/60/65)完成 Apple Music 与多房间投送验证。候选版: 正式对外发布仍待 HomePod 真机声场对齐证据(见"已知问题")。
修复(客户可感知)
- HomePod/Apple TV 混组早响 ~250ms 根因修复(latencyMin 补偿被启动流程清零):
v1.9.4 移植的 latencyMin(realtime type 96 延后 11025 samples=250ms 对齐 iPhone 原生多房间)在 handle_setup 中"先写后清"——解析 streams[] 时写入 playout_latency_samples,随后启动接收器触发 audio_timing_reset() 又清零,且 全仓库仅此一处写入,补偿整场会话恒为 0,本机相对 HomePod 早响。修复:latencyMin 改到 start_audio_receiver_or_fail(所有 reset)之后再写入;新增 receiver 会话态 session_playout_latency_samples,seek_flush(切歌/seek)、reset_timing (SETPEERS/PTP 主变更)在 reset 后重 apply,stop_until(结束/换手)清除——同流 暂停/恢复/seek 全程保留补偿。作用域不变:buffered(type 103)/AirPlay 1 恒 0, KabuSync 零触碰。真机实证 SETUP: applied playout_latency=11025 after start (单机 20/20 投送全生效)。守卫:tests/test_airplay_latencymin.py 21 项 (新增 handle_setup 顺序守卫 + 会话保留 5 项)。
- 三机 buffered 多房间播放时 web/日志被饿死修复(buff_audio 未绑核):buffered
接收任务 buff_audio(TCP 收包 + ChaCha20-Poly1305 解密 + AAC 解码,CPU 重)原用 xTaskCreate 未绑核,多房间(type 103)播放时被调度到 core 0 与 httpd 抢核、并饿死 优先级 3 的 log_ws 广播 → 播放期间 /api 超时、/ws/logs 冻结(音频在 core 1 不受影响,故表现为"音乐照放、网页/接口失灵")。修复:buff_audio 主建与 PSRAM fallback 两路均 pin 到 AUDIO_TASK_CORE(=core 1),与 realtime worker 同核,把解码 CPU 移出 core 0。真机三机 A/B:修复前播放中三台 /api 连 20s 全超时;修复后 .103=102ms、.105=208ms 播放中秒回,音频零回归(decode_err=0、underrun=0、buf~900、 ptp 锁定)。守卫:test_audio_task_alloc_resilience.py::test_buffered_task_pinned_to_audio_core。
加固(AirPlay RTSP 生命周期,阶段1–5)
- 单一常驻 RTSP worker(静态 TCB + 永久 INTERNAL 栈、删动态 slot-1、串行换手);
socket 生命周期 mutex + 会话绑定取消 + 分阶段清理确认(停止未确认不发布管线释放); 加密/普通 I/O 分段取消、1s 发送超时、部分帧毒化即终止连接;airplay_ready 严格 合取发布(worker+监听+输出+广播全就绪);普通域常驻栈启动准备提前、四态幂等。
- 可观测性:
/api/system/info新增 AirPlay 生命周期 14 字段(worker_create_count、
session_id、stream_generation、handoff/cleanup_timeout/worker_defer 等)+ 结构化 失败日志(单一可计数记录不重复统计);CONFIG_KABU_FAULT_INJECTION 门控注入点。
验证(真机三机台架,三台 build 一致)
- Apple Music 定位收口:客户 1.9.6P"连上无声"根因 = 内部 RAM 碎片化致 RTSP
控制任务 8192B 栈申请失败(Failed to create client task),已由 v1.9.7 M3-b 常驻 worker 根治、本版延续。真机实证:单台 Apple Music(realtime ALAC type 96) 与三机多房间(buffered type 103 / AAC-LC ct=4)均正常出声、decode_err=0、 underrun=0;三台 worker_create_count=1、boot_count=1、cleanup_timeout=0, 且 internal_largest_free_block 低至 6656–7680(< 8192,即 1.9.6P 致命门槛)仍 稳定起播——常驻 worker 结构性消除该失败模式。
- 单机 Apple Music 20 次投送/暂停恢复/切歌循环:
applied playout_latency20/20、
失败签名(task 创建失败/ballast/decode_err/underrun/crash)全 0、零重启。
已知问题
- 个别弱信号台播放中同步 HTTP 仍可能超时(三机 A/B 中 .104 观测):一台历史
rssi 弱至 -45dBm 的设备,多房间播放中 /api 请求-响应 >8s 超时、httpd 反复 ECONNRESET,但其音频完全健康(decode_err=0、underrun=0、buf~900、rssi 回升 -24、/ws/logs 单向推送正常)。同固件的另两台播放中 /api 秒回(102/208ms), 故非系统性 core 饿死(已被 buff_audio 绑核修复消除);疑为该台 WiFi 链路在三路 buffered 拥塞下的往返抖动——深缓冲音频与单向日志推送耐丢包,同步 HTTP 往返不耐。 待单独复确认(更好摆位/单机隔离/更长超时),不阻塞本修复,也不影响播放。
- HomePod 混组同步:真机实测确认(用户 v1.9.8 实机,2026-09):用户在真 HomePod
混组下实测 v1.9.8 已与 HomePod 同步,印证 latencyMin 修复(realtime/type 96 早响) 在真机 HomePod 场景生效。仍保留的边界(证据优先,不越级宣称):多房间常走 buffered(type 103,latencyMin 恒 0),buffered 混组下 KABU 与 HomePod 的绝对 对齐、以及稳定性报告 §9.5 的脉冲同录(扣声传播 2.9ms/m、固定中位偏差 ≤5ms)声学 定量测量尚未完成;故记为"真机听感确认同步",非"≤5ms 声学验收通过"。
[1.9.7p] - 2026-09-14
本版本主题:v1.9.7 硬件复测两个发布阻塞项的根因修复(复测报告 output/hardware_audit_20260914_053411,修复验证 output/hardware_audit_20260914_063024/repair_validation.md 与 output/source_matrix_*),并包含 v1.9.7 后的 EQ 低音修复。 全部结论有实机数据支撑,验收门槛未放宽、失败原样保留;三机矩阵 (COM59/60/65)复验通过。
修复(客户可感知,EQ)
- EQ 低音增益失效修复(v1.9.6p 用户反馈“低音不好”):自动前级把最坏情况
预测峰(全频段同时满幅的估计)全额扣回,实测旧 温暖 预设净低频 -1.0 dB、中频 -3.0 dB(滑条总提升 +9.5 dB 被抵消殆尽,拉满 15 段 更是 -9.8 dB 响度暴跌)。现自动前级下限钳位在 -3 dB(用户手动前级 不受影响),超出部分由输出端 float 域二阶软限幅(knee -0.5 dBFS,与 AQ-4 int16 限幅器同形状)平滑吸收,杜绝硬削波平顶。软限幅仅在前级 地板实际生效(存在未补偿增益,即合成峰 > 2.5 dB)时激活:EQ 关闭、纯衰减、 合成峰≤2.5 dB 的轻度提升等场景与改动前位精确一致,旁路透明性不变。
- 预设重设计(数据驱动,参考 AutoEq/Harman 低频曲线思路):
温暖预设
低频重心从 20/31.5 Hz(小喇叭物理上放不出)移至 50–125 Hz,净低频 -1.0 → +1.25 dB;新增 重低音(净低频 +2.87 dB)与 小音量响度(等响曲线思路:低频 +1.4 dB、高频近似持平)预设; 前端响应曲线同步 preamp floor 逻辑,显示与固件一致。
可观测性(EQ)
audio_eq_status_t新增soft_limited计数(进入软限幅 knee 的样本数),
/api/eq runtime 同步暴露;台架核验判据:播放热素材时计数应少量增长且 saturations 保持 0。
验证(EQ)
- 新增
tests/test_eq_bass_response.py数值回归(复刻固件 biquad/Q/峰扫描:
旧 warm 净低频 < 0 锚点、新预设净低频阈值、软限幅 C1/单调/零直通形状), 架构测试补 preamp floor + 软限幅落点断言。
修复(发布阻塞项)
- 历史成员模式失效拖慢起播(P1-1):曾入组后切走模式(如 AirPlay)的设备
仍持有 initial_expected_players 席位,起播要等 4 s dead-peer retire 再拖到 8 s deadline(实测 claim-to-ready 8-9.9 s、startup_miss=1、历史成员对照 3/3 FAIL)。现在 fast-start probe 收到完整 v2 非 Sendspin 响应即撤销 slot 资格并清 peer cache(revoke_non_sendspin_peer);mDNS 残留记录复活 slot 不再盲目重确认(仅 fresh slot 获得资格),真实 WS 连接(ws_on_connected) 才恢复资格——设备切回 Sendspin 即重新入组。三主机矩阵历史成员对照 3/3 PASS,claim-to-ready 9876→3345/3362/3500 ms,串口证据链完整 (ignoring cached non-Sendspin peer mode=1 → slot revoked)。
- 320 kbps 供帧不足(P1-2):MP3 增量解码(512 B feed)下 input 缓冲每循环
被读向 64 KB 而只消耗 512 B,memmove 每次搬移 ~63.5 KB 尾部——320 kbps 每秒 ~112 次循环(128 kbps 仅 ~48 次),memmove 独占 stream_task 56.3% 时间,供帧被压到 97.4-98.4%(包率 24.09-24.73 < 24.75 门槛,三机 9/9 FAIL)。 input 稳态收窄到 decode_feed×4(2 KB)后 memmove→1.3%、source_rate→99.96%、 包率→24.99 PASS、source_waits→0;128/320 × 双/三机四格矩阵 8/8 PASS, 320 kbps 的 encoder/socket profile 回到与 128 kbps 一致的健康形态。 FLAC 路径不受影响(decode_feed 即整缓冲);未放宽门槛、未动看门狗让出、 未加大缓冲——消除的是无效拷贝本身。
- 角色转换竞态第一梯队(P1-3):become_member 先挂起 retained receiver
再拆 leader overlay;sendspin_player_start 非 idle 时先完成 stop/idle 握手, 仍非 idle 才计数放弃——不再带着未排空 ring 与重置的全局量继续运行。
- presence/probe 模式过滤与资格门:非 Sendspin 端点不再进入 peer cache、
standby 仲裁或起播 barrier(confirmed_sendspin 位);截断 v2 响应不再降级 当 v1 接受(leader 两路 probe + kabu_presence 计数前丢弃);peer cache 加 自旋锁(discovery/HTTPD/persist 三任务并发,NVS 读写保持在临界区外)。
- 冷启动校时卡死(6h 压测实机确认后修复):基线轮第 18 轮 KabuSync
成员 sigma 卡 584-600 µs(贴 CONV_SIGMA_US 600 门槛),冷启动 claim 连续 3 次(31/79/127 s)ready=1/miss=1 直到重启才恢复。根因为 v1.9.6 既有 代码:innovation gate 拒样时 p00 冻结、gate 永不自我放宽,纯冷启动路径 无逃生门;且 gate 地板 500 µs 与 11.5 ms RTT 链路的诚实样本误差界 (rtt/2,ms 级)不匹配。修复为双逃生门(同步质量门槛不动): gate = max(3σ, 500 µs, rtt_accepted_ema/2)(封顶 25 ms)+ 全路径连续 30 次拒样 cold_restart。冷启动 A/B 三轮 3/3 PASS(claim-to-ready 2949/2949/6549 ms vs 修复前 6449-8148 ms + 127 s 卡死),稳态 sigma 259-334 µs(余量翻倍)。
可观测性
- 源侧四级计时(decode/src_write/emit/resample)+ 压缩 ring 饿证据
(starve/wait/low-water)+ memmove 计时,经 /api/sendspin/leader 顶层暴露; 计数器仅 stream writer 任务写、HTTPD 读,全 __atomic 无锁,音频路径零临界区。
scripts/v197_hardware_audit.py:三主机 × 3 cycle + 历史成员对照矩阵,
串口采集器活过设备重启(S3 原生 USB 重枚举后静默 3 s 自动重连续写)。
scripts/v197_source_matrix.py:128/320 kbps × 双/三机对照矩阵(leader 固定,
第三方设备 airplay 旁观),阶段计数差分分析。
已知问题
- **AirPlay 域内部碎片病征残余(v1.9.6Plus 头号已知问题的治理残余,
非本版引入)**:6h 压测中 Failed to create client task 触发 20 次, 其中 13 次被 M3-b idle 重试自愈(用户无感),7 次成为场景失败(3 次 投送 did-not-decode + 4 次 A5 改名强应力),签名与 v1.9.6Plus Known Issue 一字不差(internal_largest 7680 < 8192 压舱石、packets_received=0、 ptp 未锁、不崩溃、下轮自愈)。同口径 6h 频率:v1.9.6Plus 24 次 → v1.9.7 2 次 → 本版纯投送 3 次(本轮台架 .103 碎片基线偏紧,7680 B 反复出现);碎片最低 2816 B(v1.9.6Plus 记录 1984 B);M3 不变量保持: ballast unavailable 0(时代 372)、PSRAM fallback 0(时代 102)。 彻底归零需 2 常驻槽 +8192 B(超内部预算门槛,属 M2/M4 后续专项)。 用户视角:长时间多模式混用后 AirPlay 单投小概率“连上无声”,重启 该台即恢复。
- v1 混装边界:v1.9.5 及更早固件的 presence 响应无 mode 字段,若其在
AirPlay 模式也应答 probe,混装场景历史成员问题不可修(v2 自 v1.9.6 KabuSync Stage 0/1 已存在,实际风险面极小)。
- 资源门禁冷态口径(复测 P2,未动):首次播放建立工作集导致的内部
空闲块下降非持续泄漏(暖态稳定),发布判据以暖态趋势为准。
验证
tests/test_v197_release_repairs.py12 项源码守卫(资格门、负证据撤销、
revive 语义、peer cache 锁、截断丢弃、角色转换、源侧 profile、ring 饿证据、 校时双逃生门);test_sendspin_fast_convergence 同步质量门槛守卫不变。
pio run -e airplay2零警告;全量 pytest 765 passed(6 项 android branding
失败为工作区该目录处于删除状态的既有问题,与本版本无关)。
- 实机矩阵:三主机 × 3 cycle + 历史成员对照(063024 轮,P1-1 证实)、
128/320 × 双/三机四格矩阵 ×2 cycle 全 PASS(075219 轮,P1-2 证实)、 冷启动 A/B ×3(timefilter 证实)。
- 最终 6h 发布压测(01dc8dc4b,31 轮/192 场景,
output/v197_fullstack_20260914_143025/report.md):KabuSync 30/30 (underruns=0)、DLNA 30/30、模式轮转 30/30、长稳循环 31/31;零崩溃、 boot_count=1 全程、结构异常(重启/分脑/黑洞/幽灵组长)=无; decode/decrypt/malformed=0,68.9 万包 98.9% 解码,buffer_underruns 7 (0.001%,pyatv RAOP 压力路径量级,与 v1.9.6Plus 5 次/v1.9.7 1 次同 数量级);7 次场景失败全为上述 AirPlay 碎片病征。基线轮(修复前 08b457109,19 轮)对照:KabuSync 校时卡死 1 次 → 修复后 31 轮零失败。
- 发布总结:
output/v197p_validation_summary.md。
[1.9.7] - 2026-09-10
本版本主题:内部 RAM / PSRAM 碎片治理——长期可靠性。根治 v1.9.6Plus 头号 已知问题“多小时混用后 AirPlay 偶发起播失败、需重启才恢复”。方案分 M0–M3 里程碑落地,全部经台架实机 + 6h 长测验证(详见 docs/kabusync_memory_governance.md)。
修复(客户可感知)
- AirPlay 起播失败根治(M3-b):组长内部 RAM 碎片化使 RTSP 控制任务栈
(8192B)申请失败(Failed to create client task)→ 会话起不来、需重启。 改为 RTSP 主槽常驻 worker(client_worker0:开机一次性分配内部栈、连接间 park/unpark、永不按连接重申请,创建失败时 idle tick 重试)。6h 确认长测 起播失败 24→2(-92%)、Client ballast unavailable 372→0;残余 2 次 为罕见换手瞬间(客户端自动重连)。
- 音频接收零 PSRAM 回退(M3-a):AirPlay 接收/控制任务改常驻内部栈 worker
(audio_stream_realtime.c),消除碎片化下 102 次/6h 的 PSRAM 栈回退 (回退拖慢接收、增加高负载丢帧风险);6h 长测回退 102→0、退出超时 0。
- KabuSync 组长内存池化(M1):256 密文缓冲→单块 16B 对齐连续 PSRAM slab、
sendq/freeq/txq 静态队列(存储移 PSRAM),组长侧内部 RAM 占用下降;6h 组播 underruns=0。
- P0 生命周期修复(M0):playback resample_buf 泄漏、init 失败回滚、
WithCaps 任务停车+回收(杜绝 TCB/栈泄漏)。
新增(可观测性,M0)
/api/system/memory内存快照端点 + 后台mem_monitor(内部/PSRAM 的
free/largest/水位 + 分配失败回调环),现场可诊断碎片化。
CONFIG_KABU_FAULT_INJECTION门控的故障注入(诊断固件airplay2-fi专用、
生产零 footprint),确定性验证退化路径。
预算与红线
- 全模式预算中性:常驻栈精确替换原 ballast(同尺寸、同模式门控),sendspin/
radio 模式零额外占用;构建静态 RAM 33.2%(与治理前持平)。
- 音频核独占 core 1、RTSP/接收栈强制内部(断连写 NVS,绝不 PSRAM)等红线守住。
验证
- 自动化准入:pytest 704 passed、
pio run -e airplay2零警告、buildfs OK。 - 实机长测:M3-a 6h(0 回退 vs 基线 102)+ M3-b 6h(起播失败 24→2)+ 多次
ip_play_2dev_soak --quick 全过;两台零崩溃、6.7h 无重启、KabuSync 组播 underruns=0。证据:docs/kabusync_memory_governance.md §11、 output/ip_play_2dev_20260910_063602/。
发版验证矩阵(最终发布构建;COM31=.76 组长 / COM32=.75 成员 / COM45=.101)
- factory_flash:
merge --build生成 16MB 工厂镜像kabu_factory_v1.9.7.bin,
COM31/COM32 试烧均 RESULT:PASS。
- §2 双机冒烟(
ip_play_2dev_soak --quick,1.9.7 factory):AirPlay 投送/控制、
DLNA、KabuSync 组播(underruns=0)、三模式轮转+语音、长稳循环全过;稳态计数 malformed/decrypt/decode/underrun=0;M3 不变量全 0。
- §2 Web OTA(.101):1.9.7→1.9.6Plus→1.9.7 双向 OTA(用本次发布 firmware.bin),
双槽 ota_0↔ota_1、播放门控、回滚保护(新镜像正常启动未回滚)、版本号变化核验。
- §2 旧版语音自愈(.101):
/api/fs/delete删 13 项素材(hifi/std+11 digits,
复刻 v1.9.2 缺失态)→ 重启 → Prompt self-heal: 13 restored, 0 failed → /api/fs/list 18/18 复原。
- §3 asrc A/B(DLNA 源双机组,8.1min/48 采样):成员 .75 11/11 门限全过
(centered p95=max=380us«1000、ppm 84.5≤150、underrun/overrun/hard_resync/ discontinuity 全 0);组长 .76 为 DLNA 源=时间线参考(无从动 cursor,该门限 N/A、 其余全过)。
- 6h 最终发版压测(29 轮 / 172 场景,.76 组长 + .75 成员):KabuSync 组播
29/29 underruns=0、模式轮转 28/28、长稳循环 29/29;两台零崩溃、结构异常 (重启/分脑/黑洞/幽灵组长)=无;61.86 万包接收 / 99.04% 解码, decode_errors=decrypt_errors=malformed_packets=0、buffer_underruns 全程仅 1; M3 不变量保持:PSRAM 回退 0、ballast 0、Failed to create client task 24→1(换手 slot1 遇碎片 largest=2176B、184ms 自愈)、worker0 deferred 2→created on retry 2(補强 A 生效)。4 个失败场景全为环境/压测产物:3 次组长 /api/system/info 轮询超时(AirPlay 打满 WiFi 的争用;该轮播放实际 0% 丢包、无重启) + 1 次 WiFi 抖动突发(late=resync=32、underrun=0)。证据: output/ip_play_2dev_20260910_231626/report.md。
已知限制
- 罕见换手路径(主槽忙 + 内部碎片同时)次槽 dynamic 栈仍可能起播失败(6h 2 次、
客户端自动重连);彻底归零需 2 常驻槽 +8192B,超当前 .67 内部预算门槛,暂缓。
- 内部堆碎片化本身(largest floor ~2304B,源于其它分配器)属后续 M2/M4 范畴。
[1.9.6Plus] - 2026-09-08
本版本主题:同步修复验证发布版。内容 = v1.9.6 + 三项修复(KabuSync P1 校时、AirPlay 音量曲线回退、ASRC 停靠偏移自适应回拉),全部经台架实机 验证后随工厂镜像 output/kabu_factory_v1.9.6Plus.bin 发布。
发版验证矩阵(COM26/27/28 三台,build f6e370c8a)
- 夜间 soak(COM29 修复版,pyatv RAOP 压力路径):60 会话 / 280 min,
自适应滑移 61 次启动(59 次满速 100 µs/s),音量曲线 8/8 PASS。
- 晨间 iPhone/AirPlay2 双机 A/B(修复版 vs 原版同组同环境):rebase 后
滑移恢复 5.1 s vs 原版 ~120 s;修复版基线 -10.6 ms→135 s 归零、再遇 rebase(-7.6 ms)114 s 归零零过冲;原版停靠 -29.6 ms 冻结 2 min 后 20 µs/s 龟爬;机间偏差 100% 来自原版机单方漂移。证据: output/night_sync_debug/morning_ab_report_15min.md。
- quick 冒烟(ip_play_2dev_soak):6 场景全过;COM26 实测 -33.2 ms
停靠被 100 µs/s 满速回拉,KabuSync 成员路径零扰动。
- 6 h 正式发版压测(29 轮 / 173 场景,COM26 组长 + COM27 成员 +
COM28 在网三机组播):KabuSync 成组/组播/拆组 29/29 全过 (underruns=0)、DLNA 29/29、模式轮转 28/28、长稳循环 29/29;两台 零崩溃零意外重启;64.5 万包接收 / 99.05% 解码,buffer_underruns 全程 仅 5 次(1 帧级);ASRC 滑移 9 次全满速,最大停靠偏移 63.9 ms 全部 拉回。AirPlay 投送 13 次失败中 12 次为 pyatv/AirPlay1 压力路径抖动 特性(late/resync 27~46,underrun≈0;iPhone/AirPlay2 真实路径已验证 late=0),1 次为既有碎片化问题(见下)。证据: output/ip_play_2dev_20260907_193152/report.md。
P1 校时修复验证状态更新
- v1.9.6 段落中“待用户测试”的 P1 校时修复已随本版完成实机验证:
6 h 压测 KabuSync 场景 29/29 全过、成员 COM27 串口 glide/rebase/ unlocked 全零、/api/sendspin/player clock 诊断对象工作正常。
老版本 OTA 升级语音素材自愈(2026-09-08)
- 根因:Web OTA 只写 app 分区,SPIFFS 保留旧内容——v1.9.2/v1.9.6
设备 OTA 到本版后缺少新固件新增的语音素材(v1.9.2 缺 hifi/std + 11 段数字 WAV,v1.9.6 缺数字 WAV),表现为 IP 播报无声、部分 模式播报缺失(串口 Prompt asset missing)。
- 机制:18 个语音素材(7 mp3 + 11 WAV,约 530 KB)嵌入固件镜像
(main/CMakeLists.txt PROMPT_ASSETS 清单);启动时 voice_prompt_ensure_assets() 在 SPIFFS 挂载后、网络/音频/web server 启动前逐个比对大小,缺失或不符即从内嵌副本重写(每 4 KB chunk yield 一次,避开 idle 看门狗告警;O_TRUNC 覆写语义已核 实)。失败仅告警不阻塞启动(heal 处于 OTA 回滚窗口内,无 panic 路径)。
- 实测(COM28 = KABU-C567C040,build 95146c5d1):
- 小规模(写坏 3 个素材:2 数字 WAV + hifi.mp3):1.5 s 内全部
恢复,Prompt self-heal: 3 restored, 0 failed,零看门狗/panic。
- v1.9.2 规模(经
/api/fs/delete删除 13 个素材 = 11 数字 WAV
+ hifi + std,共 449 KB,与老设备升级后的真实缺口一致): Prompt self-heal: 13 restored, 0 failed,heal 窗口 1535→7254 ms 即 5.7 s(写吞吐 ~86 KB/s,SPIFFS GC 随分区填充介入),零看门狗 /panic,启动继续正常(web server 13.7 s、main stack free 12576 B)。 两轮均 18/18 素材经 HTTP 下载后 SHA-256 与 data/ 逐字节一致 (含新建的 13 个文件,证明缺失新建与截断重写两条路径)。
- 缓存时序:hifi.mp3 删除后 HTTP 仍返回旧快照(prompt_cache),
重启后返回恢复内容 → 证明 heal 早于 web_server_start()。
- 幂等:素材齐全时二次重启零
Restoring日志;稳态开销为 18 次
大小探测约 0.5 s/每次开机(由 116 ms 完成前 4 次探测推得 ~29 ms/次)。若后续要求零稳态开销,可加 NVS 素材集版本号门控。
- SPIFFS 余量:补齐后 781 KB / 3.97 MB used。
- 工程约束:IDF 原生
EMBED_FILES在本项目的 PlatformIO espidf
桥接下不可用(桥接按 codemodel 用 SCons 编译,不执行 CMake custom command,生成的 .S 变成悬空相对路径源)。改用 configure 期 execute_process 预生成 .S + 绝对路径 target_sources, 勿回退到 EMBED_FILES 写法。
- 边界:同尺寸内容损坏不检测(与现状一致);发行提示——OTA 后
首次开机请耐心等待约 1 分钟,勿断电(回滚窗口内断电会自动回滚 旧版,可重试 OTA,不 brick)。守卫: tests/test_voice_prompt_embed_assets.py(清单三向一致、basename 唯一、符号声明、启动顺序、非致命 + yield、总量预算 ≤1.5 MB)。
Known Issues(本版已知限制,v1.9.7 头号专项)
- 内部堆碎片化 → 长时压测后新 AirPlay 会话偶发起播失败:6 h 混合模式
压测中 COM26 internal_largest_free_block 最低跌至 1984 B;第 26 轮 新会话起播失败时快照 7680 B < 8192 B(cc945fa rtsp_client 压舱石 阈值),RTSP 握手未完成(packets_received=0 / ptp 未锁定),设备不崩溃 (boot_count=1),下一次模式轮转重启后自愈(27~29 轮正常)。与 2026-09-07 夜间 soak“新会话偶发不起播”同款签名;KabuSync 成员路径 不受影响(COM27 最低 12288 B)。压舱石未覆盖“重启后快速再碎片化” 路径,治理方案见 v1.9.7 专项。
[1.9.6] - 2026-09-01
本版本主题:家用 Mesh 组网支持。目标:纯 Mesh 路由环境(同 SSID 多节点、 带外网)下除 KabuSync 外所有功能可用。KabuSync 在无线回程下的时序保证另立版本。
KabuSync P1 校时修复(2026-09-05,待用户测试)
- 拒样预测改为局部候选值,接受测量后才提交 offset/协方差/时间基准,
消除连续 innovation 拒样造成的漂移重复累计。
- 冷/热初始化前统一校验时序、重复请求与 RTT;热 seed 不再重复更新,
至少两个独立有效测量并满足既有收敛门槛后才允许起播。确认保持 50 ms burst,5 次拒样或从首次回复起 2 s 未确认回落冷启动。
/api/sendspin/player增加clock质量、样本年龄、预测不确定度、
分原因拒样及热启动诊断;旧 time_synced 保持会话已收敛语义。
/api/sendspin/leader的每成员新增clock_tx本地发送排队分桶;
不改变 WS/Noise 顺序、编码、音频缓冲和 ASRC 参数。
- 仅完成代码审查与 diff 检查;未运行测试、编译或刷机,验证由用户执行。
字段含义与验证要点见 docs/KabuSync_v1.9.6_P1_handoff.md。
AirPlay 音量曲线回退(2026-09-07 夜间台架验证)
- 回退 AQ-5(9e0c88f):AirPlay 音量曲线由"准确 10^(db/20) 线性还原"
- 恢复为 v1.9.5 平方感知 taper(-30..0 dB 窗口,-30 dB 以下静音)。真机 iOS
- 滑杆下新曲线两大体验问题:底部不再完全静音(iOS 最低档仍可闻)、线性
- dB 响应手感平钝。工具与测试同步回退(
scripts/roon_raop_acceptance.py percent_to_q15、tests/test_audio_quality_chain.py)。- 验证:COM29 刷机后 pyatv RAOP 逐点比对 8/8 PASS(100%→32768、
- 50%→8192、30%→2949、15%→737、10%→327、5%→81、0%→0)。
AirPlay 多房间同步修复:ASRC 停靠偏移自适应回拉(2026-09-07 夜间)
- 根因(台架实证,COM28/COM29 + 真机 iPhone 会话):ASRC 控制器的
- "2 ms 持续 1 s 即 rebase 停靠"设计假设步进是跨设备共模;AirPlay 多房间每机
- 独立 RTSP/链路,单机步进非共模,停靠后基线随机游走无回复力。实测 COM28
- 一夜三次步进(-4.4/-8.7/-31.6 ms),每次步进重置 120 s 滑移延迟把唯一回复力
- (基线滑移 20 µs/s)饿死,步进到 -35.6 ms 后需近 30 分钟才能爬回,
- 机间偏差长期维持在可闻回声级(当晚实测最高 ~92 ms)。
- 修复(
audio_asrc.c,Stage 4.1):① rebase 后滑移延迟 120 s→5 s - (rebase 保持 ≥1 s 已证明新平衡真实,晶体估计中途不重置);② 滑移速率自适应
- |baseline|×5%/s,钳位 [20, 100] µs/s 且不超出目标钳位余量(前馈永不
- 被裁剪);③ 漂移估计器扣除滑移指令量(快恢复不被误读为晶振漂移);
- ④ rebase/滑移启动新增串口日志(夜间取证链)。健康稳态(|baseline|<400 µs)
- 仍为恒 20 µs/s,KabuSync/AirPlay 已验证稳态零改动;仅加快大偏移恢复
- (62.8 ms 由 52 min→约 11 min,且不再被 rebase 饿死)。
- 验证:pytest 705 全绿;airplay2 零警告;COM29 实机日志确认自适应
- 滑移以 100 µs/s 生效、rebase 后 5 s 恢复滑移;pyatv RAOP 高噪声路径
- (rebase 122 次 + R6 自恢复 33 次)下 underrun/late/decode_err 恒 0。
- iPhone/AirPlay2 真实双机 A/B 待晨间重投后补充(COM29 修复版 vs
- COM28 原版对照)。
Added 新增
GET /api/wifi/status诊断接口:当前连接节点(BSSID/RSSI/信道)、
AP 的 802.11k/v 能力、漫游开关与运行时状态、漫游事件历史(rssi_low / btm_query / steered / self_roam,内存环形 8 条)、AirPlay/DLNA 广播状态与 SSDP LOCATION,Mesh 环境排障无需串口日志。
GET /api/wifi/scan多 AP 检测:每个网络新增 BSSID 字段,并输出
same_ssid_bssids / multi_ap(同 SSID 多 BSSID = Mesh/多 AP 组网)。
- Web 设置页 Mesh 提示:扫描发现 Mesh 组网且漫游未开启时,漫游开关下显示
中英双语建议;data/www/index.html。
- 漫游事件历史:
wifi_roaming.c/h新增wifi_roaming_get_history(),
关闭漫游编译时为零开销 stub;tests/test_mesh_network_support.py 静态锁定。
- 用户手册 14.6 节“Mesh 组网环境”:推荐设置、组播转发限制说明、
IP 变化行为与诊断入口。
- 无缝漫游出厂默认开启(v1.9.6 起):NVS 已有偏好的设备不受影响;
单路由器环境无副作用(无第二节点可漫游)。
- COM16 克隆模组正式退役:研发车队不再使用该自组模组(历史对照见
docs/archive/module_comparison_wroom_vs_clone.md),Known Issues 中 “WiFi OTA 不可靠(wontfix)”条目随之移除;验收设备 = COM15/COM20/COM13 (原厂 WROOM)。
Fixed 修复
- DLNA SSDP LOCATION 随 IP 变化刷新(Mesh 漫游核心缺陷):此前仅启动时构建一次,
漫游/DHCP 换 IP 后控制点失联。新增 IP_EVENT_STA/ETH_GOT_IP 监听:旧地址发 byebye → 重建 LOCATION → 新地址发 alive,失效事件订阅同步清理;同 IP 重获取时 补发 alive 刷新控制点缓存。新增 dlna_renderer_get_location() 供诊断。
- RTSP 控制连接有界 TCP keepalive:播放期间控制连接长时间静默,漫游或 DHCP 换
IP 静默杀死 L3 邻接后原会话永不回收(无 FIN 时 recv 只会 EAGAIN 循环)。新增 SO_KEEPALIVE(idle=10s/intvl=3s/cnt=3,约 19s 检测);新连接替换机制不变。
- AirPlay mDNS 重播验证:确认 esp-mdns 组件在 GOT_IP 时自动重新广播,无需改动。
- 内部堆碎片化导致 AirPlay 永久 SETUP 500(8h 压测抓获的 P0 可用性缺陷):
长时间会话更替后最大连续内部块可跌至 9216B(< 接收任务 12KB 栈), 此后每次 SETUP 均 500("Failed to create receiver task")直到重启,实测卡死 25+ 分钟(总 free 36KB、无任务泄漏,纯碎片化)。修复:双压舱石(与两个 任务栈同尺寸的 12288B/4096B 内部块,会话间隙持有,建任务前释放形成 精确窗口,任务退出后原位回收)+ PSRAM 栈兜底(接收任务不碰 flash/NVS, 缓存失效协议下安全),覆盖 realtime 与 buffered 两条路径;两层防护均经 真机日志实证。新增 tests/test_audio_task_alloc_resilience.py 5 项锁定。
- RTSP 拥塞崩溃防护(第二轮 8h 压测抓获的 P1):弱信号机在高频会话重试下
RTSP :7000 可进入小时级无响应(HTTP :80 同期正常)。根因:排队中超时已死 的连接仍消耗完整槽位替换周期(最长 6s+3s),服务速率低于重试速率后自持 崩溃。修复:accept 后非破坏性探活(select 零超时 + MSG_PEEK)微秒级甄别 并丢弃死连接;拒绝计数 rtsp_stale_refused/rtsp_slot_refused 经 /api/system/info 可观测(5s 限速日志)。核实:本 IDF 无 LWIP_TCP_LISTEN_BACKLOG 符号,accept 队列由 ACCEPTMBOX(6) 承担且队满 RST 快速失败,保持不变。新增 tests/test_rtsp_congestion_guard.py 5 项。 验证(0.5h 强化档,会话间隔 0.5-2s、abrupt 25%,路由器+网桥强信号 RSSI -26~-33):865 动作仅 1 次瞬时断连(秒级自愈,非黑洞型),黑洞型 失败窗口归零,DLNA SOAP 100%,模式切换 5/5,零意外重启,堆稳定。 弱信号环境下的防护路径触发证据留待下次 mesh 环境补测。
Verified 验证
- pytest 全量回归 639 passed / 2 skipped(含新增 8 项 Mesh 不变量 + 5 项碎片化守卫测试)。
- PlatformIO airplay2 环境与 buildfs 编译通过,改动文件零警告。
- **两轮 8h 真机压测(三机 COM26/27/28,AirPlay+DLNA 全控制动作编排,
江南/梁静茹循环,含杀进程断流/计划重启/模式热切换)**:
- 第一轮(修复前,2026-09-01 夜):10143 动作;抓获 P0——内部堆碎片化致
AirPlay 永久 SETUP 500(.68 卡死 25+ 分钟,262 次连续 500); 46 次突然断流全部正确恢复(RTSP keepalive 修复实证);9 次重启全部 ~9.3s 恢复;31 次模式热切换全成功;DLNA SOAP p99=625ms。
- 第二轮(修复后,2026-09-02 昼):8060 动作;碎片化 P0 确认治愈:
持续性 SETUP-500 归零(仅剩 3 次孤立且秒级自愈);ballast 循环全程 在位(int_largest 稳定 12288);堆零泄漏;新发现 P1(未修复,已归档 Known Issues):弱信号机(.70,RSSI -61~-72)在高频会话更替下 RTSP :7000 可进入小时级无响应(HTTP :80 同期正常,重启后彻底恢复)。
- 真机 Mesh 漫游实证:.70 触发 rssi_low(-70)→btm_query,AP 确认支持
802.11k/v,漫游默认开启生效。
增补(同版本分支后续提交,version.txt 未升,归入 1.9.6)
主题:上游 v0.2.0 对照移植 A1/A2/A4/A5、中文设备名 AirPlay 兼容修复。 归因:A1/A2/A4/A5 对照上游 rbouteiller/airplay-esp32 v0.2.0(PR #96/#115/#127) 的协议行为与工程参数自写实现(上游 Non-Commercial,策略见 docs/upstream_tracking_v0.3.md 第五节)。
Added 新增
- 上游对照移植 A1/A2/A4:
- A1 (lwip):
CONFIG_LWIP_UDP_RECVMBOX_SIZE16→64(sdkconfig.defaults),
实时 ALAC 突发收包邮箱扩容(每活动 UDP PCB +192B)。
- A2 (rtsp):控制 socket accept 后加
TCP_NODELAY,免除 SETUP/SETVOLUME/
SETRATEANCHORTIME 控制命令的 Nagle + 延迟 ACK 等待。
- A4 (rtsp):stream SETUP 末尾补发
RTSP_EVENT_PLAYING(AirPlay 2 无 RECORD,
SETUP 起播即广播);修复按键/睡眠本地静音后经 SETUP 恢复仍无声 (playback_control 仅在 PLAYING 清本地静音)。
- 三机全功能混合压测编排
scripts/v197_fullstack_soak.py:AirPlay(pyatv
投送)/DLNA/KabuSync 成组/模式轮转/A5 中文名专项/长稳循环轮转,全程串口 + 健康 + 堆监控,per-session 计数器采集,report.md/json 汇总;素材用江南/ 梁静茹两首原始曲循环重投(不拼接,避免拼接点污染音频计数器)。
Fixed 修复
- A5 mDNS hostname 保持 ASCII(mdns_airplay.c):hostname 固定
KABU-<MAC>,instance 名保留 Unicode。此前直接把用户设备名传入 mdns_hostname_set,中文名致 <名>.local 不可解析(AirPlay 列表可见但 连不上)。对照上游 PR #127。
- bplist
/info中文名编码(bplist_builder.c):device_name含非 ASCII
时改用 0x60 UTF-16BE Unicode-string(新增 bplist_write_string / bplist_utf8_to_utf16be / bplist_is_ascii)。原用 0x50 ASCII-string 直接 memcpy 中文 UTF-8 字节,违反 bplist 规范,致严格解析器(pyatv/plistlib, 可能含 iOS)UnicodeDecodeError(0xe5) 崩溃、AirPlay /info 握手失败。此为 A5「中文名可连」目标中 hostname 之外的另一半(7h 压测 A5 专项挖出)。
- 缩组起播被残留 peer 卡死(
sendspin_leader.c,2.1 台架 09-05 实证):
成员离开组后(如切 AirPlay,8928 关闭)仍会应答 fast-start presence probe(UDP 8930 任意模式常驻),导致每次起播都被 roll-call 重新准入,虚增 initial_expected_players;而播放期 30s stale 阈值在 8s 起播窗口内永不触发,join window 还被 !available 成员 +3s 扩展,ready set 实测 ~9.2s 才冻结,两机组 超时失败。修复:起播窗口内(仅 LEADER_START_PREPARING)对 “claim 快照在册且本次 start 从未建过 TCP”的 slot 一次性快速退役 (LEADER_DEAD_PEER_STARTUP_MS=4s,present_at_start/ ever_connected_this_start 双豁免 + one-shot),mDNS/late-join 回组兑底不变。三重安全边界:晚于 claim 才被 mDNS 发现的成员豁免、 曾建连(含 claim 时 warm 会话)的成员豁免、已连接 slot 由既有 guard 先行 continue;播放期 30s/standby/hot-start/join window churn fix/FINAL_ROLLCALL(PARKED) 全部零改动。新增 tests/test_leader_dead_peer_startup.py 9 项静态守卫;验证:三机→ COM37 切 AirPlay(不清 peer cache)→两机 FAIL→修复后 PASS, ready frozen ≤8s(详见 output/channel_21/)。
- 按键新功能:KabuSync 单击暂停全组+逐位播报本机 IP / 长按幂等重开 AP:
- 长按(任意模式):由 toggle 改为幂等
wifi_request_ap_enable()
(新 WIFI_CMD_ENSURE_AP)——任何状态下长按都确保配网 AP 开启; 已开启时重置 10 分钟无客户端超时窗口,AP 不再被长按误关; 关闭路径仅剩既有超时机制。AP+STA 共存不断 STA 连接。
- 单击(KabuSync 模式,任意设备生效):组长(DLNA 源所在)本地
dlna_renderer_pause() 全组暂停;成员经新增 Sendspin 上行控制信令 client/command/pause(加密会话,同 client/time 传输;leader 端经 回调钩子 sendspin_leader_set_member_pause_cb() 转发到 DLNA 层, 不引入反向依赖)请求全组暂停。被按键设备随后逐位语音播报本机 IPv4(新 voice_prompt_play_ip() + 11 段中文 TTS 数字 WAV, scripts/gen_digit_prompts.ps1 生成,44.1k/16bit/mono,尾部静音裁剪 后每段 ~0.5s,整次播报 ~6s)。播报会终结暂停会话(与 DLNA 模式 toggle_hq 先例同模式),恢复交给手机 App 的 DLNA 播放键(产品决策); 重复单击幂等地再次播报。新增 tests/test_button_ip_announce.py 10 项静态守卫。
- 配套修复:web_server
max_uri_handlers80→96——11 个 digits URI 使实测
注册数达 ~82,80 槽位时尾部 6 个注册失败(含 /ws/logs WebSocket 与 speedtest 路由,日志 "no slots"/HANDLERS_FULL);守卫测试 test_uri_handler_budget_covers_the_digit_routes 锁定预算 ≥96。
Verified 验证
- 三机 7h 全功能混合压测(COM26/27/28 = .67/.68/.70):截至 30 轮/6.1h,
scenario 186 通过 / 2 偶发(98.9%),无结构异常、无 fatal、无堆耗尽。
- A1:AirPlay 丢包率稳定 0.89-1.15%(每轮 ~22870 包,unexplained_drops=0);
KabuSync 组播放全程 2/2 成员流播、underruns=0。
- A2/A4:AirPlay 投送 + 音量/停止控制全通过。
- A5+bplist:中文名「客厅音响」下 AirPlay 发现+投送 7/8 通过(1 次为重启
冷启动无预热的 gates 抖动,error=null 连接本身成功)。
- 2 次偶发(长稳 DLNA 起播 STOPPED、A5 冷启动 gates)均为压测脚本时序/判定
层,非固件缺陷。
[1.9.5] - 2026-08-31
Added 新增(2026-09-01 发布配套)
- HA / MA 投送链路真机验证与图文指南:Music Assistant→Sendspin、
Home Assistant→DLNA 两条链路在 v1.9.5 固件完成全流程真机实测 (注册/播放/暂停/恢复/音量/状态同步),沉淀离线图文手册 docs/ma_ha_user_guide.html + docs/ma_ha_guide_images/(22 图,含 故障排查表与验证清单);当日 pytest 全量回归 626 passed / 2 skipped。
- 发布配套脚本入库:
scripts/one_click_flash.py(操作员一键烧录,
复用 factory_flash 流水线与出厂自检)、scripts/release_coldboot_probe.py (冷启动 OOM 压测循环)、scripts/dlna_release_acceptance.py + tests/test_dlna_release_acceptance.py(DLNA 验收脚本与静态守卫)。
- 营销素材工具:
tools/render_shopify_asset_kit.py、
tools/render_youtube_banner_journal.py(产物 assets/ 体积较大且可 重新渲染,已加入 .gitignore 不入库)。
- docs 归档整理:25 份一次性/已被取代的历史报告移入
docs/archive/,
CHANGELOG/CLAUDE/脚本等 11 处引用同步更新,docs 根目录 47→23 份。
Fixed 修复(8h 压测成员回组缺陷,见 docs/stress8h_analysis_20260831.md)
- 成员回组重连按应用会话健康状态退避(
sendspin_wsclient.c/h、
sendspin_leader.c):成员重启后组长以固定 250ms 节奏重拨(30s retire 窗口内 ~120 次)形成连接风暴,把回组拖到分钟级(8h 压测两次回组 >120s 失败)。失败定义收紧为"应用会话达到 SESSION_ACTIVE 之前的失败",覆盖 TCP 连接失败、HTTP Upgrade 失败、101 后立即断开三类,统一按 250→500→ 1000→2000→4000→8000ms 饱和退避(共享 backoff_wait_and_advance(), 250ms 分片并响应 stop)。
- 新增幂等接口
ss_wsclient_mark_healthy():只有组长收到有效
client/hello、会话切到 SESSION_ACTIVE 后才能复位退避到 250ms——TCP/HTTP 层成功不等于成员可用(重启中的接收机会应答 TCP 而应用未就绪);健康会话 断开仍固定 250ms 快速重连,下一次失败才重新进入指数退避。
- 审计否决的第一版方案:TCP 成功、握手前就复位退避,连续握手失败始终 250ms
重试,无法消除"101 后立即 read error"的固定节奏循环。
- 验证:十轮确定性回组专项(固定组长、交替硬重启两名成员)10/10 成功、
均值 18.6s(门槛 <20s)、单次最大 27.3s(门槛 <30s)、退避序列日志解析 零违规、零结构异常。
- 压测脚本:新增
--member-rejoin-count确定性专项模式(自动执行回组与
退避序列门槛);EXPECTED_VERSION 默认升至 1.9.5。
[1.9.4] - 2026-08-31
Added 新增
- AirPlay2 latencyMin 支持(选择性移植公版 v0.2.0 PR #115):
AirPlay2 实时流(type 96)按发送端 SETUP 协商的 latencyMin (默认 11025 samples = 250ms@44.1k)延后播放。此前基线忽略该值, 在 iPhone 原生 AirPlay2 多房间(本设备 + HomePod/Apple TV 混组) 时早响 250ms——人耳可闻声场错位;修复后与群组对齐。
audio_timing_t新增playout_latency_samples字段,
compute_early_us 的 PTP/NTP/本地三路径目标时间统一加项; audio_receiver_set_playout_latency_samples() 转发 API; handle_setup 解析 streams[] 的 latencyMin(合理性校验: >0 且 <10s 采样数,异常回落默认值)。
- 作用域保护:AirPlay 1 与 buffered(type 103)路径显式设 0;
KabuSync 链路零触碰(架构断言守护)。
- 不影响既有指标:250ms 是内容绝对延迟(所有设备统一),
与同步偏差(KabuSync <1ms / AirPlay2 混播 ≤5ms)正交。
- 测试:
tests/test_airplay_latencymin.py15 项(字段/公式/
解析/作用域/公式数值);既有同步测试无绝对延迟断言,不受影响。
- 幽灵组长遥测:
/api/sendspin/leader新增local_source_active
(与看门狗同谓词),压测 harness 据此断言无源组长 ≤8s 自愈。
Fixed 修复(KabuSync 脑裂 R25/R32 级联,见 docs/v1.9.4_kabusync_split_brain_fix_plan.md)
- 统一活跃谓词:claim/WS admission/自选举门禁用 `stream_active ||
playback_activity 替代 session_alive`。连接态不再阻塞热备成员的本地 claim(R25 直接根因),同时保留换曲瞬空窗口对活跃组的保护;协议零改动。
- 预算反转:claim 调用者 6.5s > worker 4.5s > harness 8s SOAP 上限;
释放 grace 从 10s 收到 4s,按绝对 deadline 轮询。消除“先 TIMEOUT 后约 3s 才 reject”的倒挂;调用者永远拿到确定结果(接受或明确拒绝)。
- claim 代次围栏 + 回滚:超时即失效代次;worker 在每个阻塞点后检查,
become_dlna_leader() 成功后发现失效立即退回待机,不再留下已广播的无源组长。
- 无源组长看门狗:LEADER 无本地声源 >5s 自动释放组,任何未知路径留下的
幽灵组长 ≤6s 自愈,级联在结构上不可持续。
- claim 路径有界化:
add_player()的 session lock 从portMAX_DELAY收为 1s,
失败则保留 retired 状态待重试;释放路径 pause-ack 用 4s 预算;每次 enable 最多 revive 1 个 retired slot;选举/稳定等待/拨号循环对排队 claim 让路。
- standby 选举加固:两扫稳定性改双向集合相等并回传确认快照,胜者判定与首次
拨号复用同一快照(删除第三/四次独立探测);先加冕 + 发 beacon 再拨号;加冕前对 leader/standby beacon 复检。
- (fix2,5h 压测串口定位)standby-leader 快路径补上 busy 守卫:
暂停释放转待机窗口内节点是 STANDBY_LEADER 且会话仍带上一世代音频, claim 快路径(跳 mDNS、无成员 grace)会在成员仍在渲染旧流时提升新组长, 导致“transport stayed STOPPED”与无源组长 churn;守卫从仅 MEMBER 扩展为 MEMBER || STANDBY_LEADER,同享 4s 有界等待。同轮压测中无源组长看门狗两次 实战自愈(≤6s,未级联),验证了 fix1 防线。
- 测试:
tests/test_claim_arbitration.py6 项 + 既有守卫更新;三机压测新增“Stop 后
立即换组长”竞态变体与幽灵组长专项断言;新增 scripts/ma_sendspin_acceptance.py (MA WebSocket API 自动化验收)与整晚/白天无人值守编排脚本。
[1.9.3] - 2026-08-27(暂定,待实测验收)
多台设备在 QQ 音乐等 DLNA 投屏列表里全部显示同名 "KABU",无法区分 哪台是哪台;且非 DLNA 模式设备(KabuSync 按设计暴露 DLNA 入口、SSDP 缓存残留)与 DLNA 模式设备混在一起,造成误认。
Added 新增
- 统一对外设备名规则(
settings.c/h):新增
settings_get_public_name()——用户自定义名优先;从未改名的设备 自动使用 KABU- + Wi-Fi MAC 后 4 字节(如 KABU-A3F21B7C),多台 出厂机在投屏列表中不再重名。
- 公共名出口切换:DLNA
friendlyName、AirPlay/RAOP mDNS 实例名、
KabuSync 组内昵称、Web 门户与设置页全部改走公共名;kabu_mqtt.c (Home Assistant 实体)与 mode_manager 内部组长标识保持不变。
- 回归守卫
tests/test_device_naming.py:出口切换与默认名规则。 - 说明文档
docs/device_naming_and_dlna_visibility.md:各协议
名字来源与改名生效时机对照,“为何能看到非 DLNA 模式设备”成因。
- DLNA 高品质模式(单击切换):DLNA 模式下物理单击不再暂停/继续,
改为切换标准/高品质输出;高品质模式按源采样率直通输出 (44.1/48/88.2/96 kHz,消除 44.1→48 k 重采样损耗,16bit),切换时 插播英文语音提示(“High quality mode” / “Standard quality mode”), 播完自动重放当前曲目。HQ 状态 NVS 持久化,重启保持;暂停/继续改由 推流端 App、网页控制台与 MQTT 控制(mode_manager.c、 dlna_renderer_native.c、audio_output.c 运行时 I2S 时钟重配、 http_stream_player.c 会话挂钩、voice_prompt 新增提示音、 settings.c dlna_hq 键)。KabuSync 组内不启用直通(共享组时钟)。 回归守卫:tests/test_mode_manager_architecture.py 新增 3 项。
- FLAC 内嵌封面支持(实测修复):真机验证发现乐鑫预编译解码器的
ES parser 无法跳过 FLAC 大体积 metadata 块——内嵌专辑封面 (PICTURE,实测 242 KB)会导致 "Not found frame in max frame size" 随后 no-PCM 超时拒播;且 no-PCM 看门狗按原始网络字节计数,封面 本身就会耗尽 64 KB 配额。新增 FLAC metadata 剥离器 (http_stream_player.c:reader 侧仅剥 PICTURE 块,其余 metadata 原样透传;FLAC 喂入从 512 B/次改为整缓冲,大帧不再被拆碎)。 《杀死那个石家庄人》(16bit/44.1kHz/带 242KB 封面)实机压测通过。
- DLNA 压测脚本通用化(
dlna_lifecycle_stress_v192.py):媒体
支持自动扫描 tmp/dlna_media/(mp3/flac/wav/m4a/ogg,MIME 按后缀 映射)或命令行直接传任意文件路径;修复非 ASCII 文件名(中文+空格) 未做 URL 编码导致拉流失败的隐性 bug。
- DLNA 进度条同步(Seek):实现 AVTransport
Seek(REL_TIME/
ABS_TIME):字节目标由会话自身进度(已收字节/首帧以来时间)推导, 不依赖 Content-Length 或恒定码率;前向 seek 发 HTTP Range 请求, 后向 seek 从头重拉并丢弃前置字节;FLAC 重开时自动前置缓存的 "fLaC"+STREAMINFO 头(否则无头流无法解析)。SCPD 声明 Seek action 与状态变量,GetTransportInfo 的 Actions 上报 Seek;拖动后 GetPositionInfo 的 RelTime 从新基点继续,暂停冻结。压测脚本每 周期验证 seek 位置误差与暂停位置冻结(dlna_renderer_native.c、 http_stream_player.c)。
- DLNA HQ 测试入口:新增
GET/POST /api/dlna/hq(设置 NVS 持久
的 HQ 标志,下一会话生效;物理单击仍是播报+重放的即时切换)。
- 双模式 1h 实机压测:16bit/44.1kHz/242KB 封面 FLAC,标准模式与
HQ 直通模式各 1 小时(DLNA_STRESS_HQ=0/1),各 101 周期(切歌、 seek 90s、音量、暂停位置冻结、声道切换、恢复、停止)全部通过, 堆漂移 -120B / 0B,零重启。
- HiFi 升级:24bit/192k 直通 + 大缓冲预读(实验先行,报告
docs/archive/hires_24bit_decoder_experiment.md):真机实验确认预编译解码器 对 24bit FLAC 如实报告 bps=24 且输出 3 字节 packed(容器判别 扫描法);打通端到端 24bit 管道——解包 float → float EQ(新增 audio_eq_process_float_in)→ float 音量 → 通道模式 → audio_eq_float_to_s32 → I2S 32bit slot @ 源采样率;直通白名单扩 至 176.4/192 kHz(24/192 实测 retune: 192000 Hz, 32-bit slot、 underrun=0);非 HQ、白名单外、SUB 模式自动降 16bit 走原链。 顺带修复:24bit 大帧(max 27KB)使 Seek 重开在最坏情况下超出 32KB 解码输入缓冲导致静默死锁——HSP_INPUT_BUFFER_SIZE 32KB→64KB; DLNA 拉流 ring 128KB→2MB(PSRAM,约 19s 抗抖动,512KB/128KB 逐级回退);web URI 槽 72→80(新增端点后 /ws/logs 注册被挤掉)。 实测:24/96 与 24/192 FLAC seek/播放全通过,16bit 标准+HQ 回归 通过;DLNA_STRESS_SEEK 环境变量支持短曲目 seek 目标。
- Seek 字节映射修复(三机压测发现):2MB 预读 ring 使“已收字节/
时长”码率估计严重偏大,seek offset overshoot 至 EOF 附近导致重开 会话立即结束——改用精确几何 Content-Length × target/duration (reader 记录首包 Content-Length;DLNA 传入 metadata 时长),bitrate 作回退,并留 3s 边距。向后 seek(从头重拉+丢弃)路径的 strip 会吃掉 流自带头、skip 又将其丢弃导致解码器无头 stall——该路径同样前置缓存 的 fLaC+STREAMINFO 头。压测脚本升级为类真控制点:解析本地资产时长、 推送含 duration 的 DIDL 元数据、seek 目标随时长随机、随机上一首/ 下一首与暂停时长。
- KabuSync 组崩溃修复(2h 三机压测发现,watchdog 回溯定位):
成员 churn/换组长窗口内 local_sink_task 在 OVERRUN/DISCONTINUITY 路径零延迟 continue 形成 busy-spin,饿死本核 IDLE → task watchdog 连发 → HTTP 看门狗重启设备 → 组塌方与 “Another Sendspin source is active” 连锁拒播(2h 矩阵 9 轮失败)。三处 continue 补 1 tick 让步(audio_group_timeline.c),churn 聚焦回归验证。
Known Issues 已知问题(不在本次修复范围,需专项迭代)
- KabuSync churn 边界稳定性:
--churn-focus高频成组/拆组矩阵下,
成员时钟收敛可能晚于 leader 的 ready-set 冻结窗口(实测迟到 ~5s), 导致“组起播后成员未全部流播”;个别成员在长时 churn 下软件重启 (reset reason 3)后恢复缓慢。该缺陷在 v1.9.2 固件的 8h/2h 矩阵中 即存在(同类黑洞/卡死),本次 spin 修复消除了 watchdog 重启放大 链路,但收敛时序与重启恢复需专项迭代(ready-set 窗口自适应、 成员重连退避、leader 释放死锁超时)。常规场景(暂停/恢复/切歌/ 换组长/soak/外来抢占)2h 矩阵 20/29 轮通过,失败全部集中在 churn 边界。
- churn 三修复(本轮,churn 聚焦矩阵从“第 1 轮起持续塌方”
改善为“零星深层失败”):① leader ready-set 冻结自适应——窗口到期 时若存在已连接但未收敛成员,一次性延展 3s(sendspin_leader.c, 实测冻结全部变为 all_ready miss=0);② leader 释放死锁超时—— release_pending 超 8s 且 DLNA 会话残留时强制释放,消除 “Another Sendspin source is active” 持续拒播(sendspin_role.c); ③ 成员 warm 时钟二次验证——首次 snap-gate 失败不再立即冷重置, 下一采样再验一次,churn 后重收敛从 ~5s 降到亚秒(sendspin_client.c)。 残留深层失败(leader 重启自愈 rebase 卡死、外来抢占拒绝判定) 在修复前基线同样失败,属控制面多轮迭代缺陷,需专项。
- 抢占拒绝判定 + preroll 卡死自愈(churn 残留两项专项修复):
① 外来抢占拒绝扩展——member 守卫从“正在流播”扩为“连着组会话” (新增 sendspin_client_session_alive()),覆盖 stream 重启瞬空窗口, 播放中的外来 DLNA 抢占实测被正确拒绝、组保持不动;② SOURCE_PREROLL 卡死自愈——write_frame==0 超 10s 先重放 DLNA 源 (有界一次),再卡则回退重 claim;配套把 enable 时的 group_start+barrier 初始化抽为 begin_preparing(),并在其中对共享 编码器做新代际 reposition,消除快速 Stop→Play 后旧代际缓存残留 (修复前日志 write=0/encoded>0/exact=0 永久卡死的直接原因); preroll diag 增加 dlna_active/dlna_playing 字段。基线对比: preroll 卡死事件 0 次。遗留:冷启动成组“未全员流播”与外部 SetAVTransportURI 改写 transport 状态后的级联残留(下轮专项)。
- 发行专项 P0/P1(v1.9.3 正式版阻塞项):① transport 污染防护
结论修正——SetAVTransportURI 不加组会话守卫:它是 DLNA 标准切歌 动作(播放中直接覆盖曲目),对 armed leader 拒绝会破坏切歌功能 (实测 track_switch 全挂);组完整性由 Play claim 守卫单独承担 (成员在组内被 Play 劫持会被拒),外部 set_uri 预设 URI 无害。 ② 冷启动成组——join window 自适应扩展条件从“已连接未收敛”放宽 为“已知槽位未 ready”,消除首启后 window 冻结 miss 与 late-join rebase 链。③ release 顺序保持“先成员 stream/end 后停时间轴” (fast path 变体会破坏 stream/end 送达,已回退)。验收:常规 0.35h 5/5 PASS(成员回组 2.2s,此前 19.6s);churn 0.7h 15/18 零级联(失败不传播,后续轮次正常,对比修复前基线持续性塌方); 单台 DLNA 1h 80 周期 79 过、1 次瞬态切歌 error 下一周期自恢复、 零重启、堆漂移阈值内。pytest 596 通过。
Changed 变更
- Web 设置页改名提示不再声称“重启后生效”:DLNA 列表重新扫描即
生效;AirPlay 需重新进入 AirPlay 模式(中英文)。
[1.9.2] - 2026-08-26
三台 KABU 组成 2.1 声场:KabuSync 模式下每台设备在网页自选声道角色 (立体声/左/右/低音炮),DLNA 推给任意一台即三机同步播放各自声道。 声道分离位于每台设备本地渲染末端,组时间轴与协议零改动;AirPlay 与 蓝牙音源强制立体声,稳定性与音质不受影响。
Fixed 修复
- **(严重,阻塞级)HTTP 看门狗探针误判导致 DLNA/Sendspin 模式 48 秒
重启循环**(web_server.c):v1.9.1 的探针有界化提交有两处缺陷: ① 非阻塞 connect 走 select 成功时返回 1 而非 0,if (rc == 0) 永不 进请求阶段;② 为 connect 设置的 O_NONBLOCK 未清除,recv 在 httpd 尚未应答前就立即 EAGAIN 返回(实测 ~160µs)。tcpip 线程一旦忙到 loopback connect 异步化(DLNA/Sendspin 模式必然如此)探针即 100% 失败,健康设备每 48 秒被看门狗重启一次,三机组播与 DLNA 压测全部 不可用。现修复:select 成功路径归一化 rc=0;请求阶段恢复阻塞 I/O (由 SO_*TIMEO 限界);探针另改 SO_LINGER=0 关闭(RST 直接销毁 PCB,避免 TIME_WAIT 冲刷 TCP PCB 池)。实测三机 DLNA 静置与压测 不再重启。
- (严重,阻塞级)僵尸组长互踢活锁导致组砖化无法自愈
(sendspin_leader.c):高频会话翻动下,一台设备角色层已降为成员后 其组长 WebSocket 会话任务仍持续重拨成员,与真组长互相置换成员唯一 的服务器槽位,任何一侧都无法建立稳定会话,后续全部场景连环失败。 根因:降权拆除只走异步 TX_PAUSE(命令队列卡住时永不执行);既有 "32 秒无连接即退役"守卫被每秒重连刷新时间戳绕过。现修复:新增 stop_all_leader_transports() 同步停止全部成员 wsclient; standby_stop() 追加同步拆除;discovery_task 禁用分支增加僵尸 回收器(带 claim→enable 过渡守卫)。配套静态守卫 tests/test_leader_zombie_guard.py;修复后同类高频翻动 1h 压测 23/23 场景全过。详见 [docs/archive/v192_stereo_kabusync_heap_acceptance.md] (docs/archive/v192_stereo_kabusync_heap_acceptance.md)
Added 新增
- 2.1 声道角色(
audio_output.c/h):新增AUDIO_CHANNEL_SUB
低音炮角色——(L+R)/2 经 120 Hz 二阶 Butterworth 低通(Q39 定点 + 8 bit 分数反馈,规避近 DC 极点的极限环增益损失);LEFT/RIGHT 沿用 既有声道提取;audio_output_set_channel_mode() 直设接口。
- NVS 持久化(
settings.c/h):新键chan_mode,启动预载
(PSRAM 任务不碰 NVS),两种 mode profile 初始化后统一恢复 (mode_manager.c)。
- Web API:
/api/audio/channelGET/POST(stereo|left|right|sub,
写后读回校验,no-store)。
- 设置页:新增"声场模式"卡片(中英双语),注明 KabuSync 组播放
生效、AirPlay 始终立体声。
- 工装:
scripts/channel_21_acceptance.py(COM26/27/28 三机台架:
刷写→串口取 IP→声道配置持久化闭环→DLNA 三机组播→AirPlay 门控→ 模式切换回归);tests/test_channel_mode_21.py 静态架构守卫 + 定点滤波器参考仿真(DC 增益、极限环增益损失两项门禁)。
AirPlay 稳定性底线
- AirPlay/蓝牙音源下声道门控强制 STEREO,STEREO 分支逐字节保持现状;
- 不新增任务/堆分配,不改 I2S/DMA/时间轴/Sendspin 协议;SUB 低通仅
在 SUB 角色且非 AirPlay 音源时运行。
[1.9.1] - 2026-08-25
Music Assistant 实测兼容修复:Sendspin legacy 明文方言自动协商。 MA 2.9.13 捆绑的 aiosendspin 6.0.5 是明文旧版协议(无 client/init、无 Noise),与 v1.9 新规范握手首条消息即不兼容;本版本在固件侧增加 per-IP 探测与明文方言回退,实测 MA 投送出声、暂停、音量全通过; HA DLNA 链路同步复核通过。附完整验收报告与连接操作手册。
Added 新增
- Sendspin legacy 明文方言自动协商(
sendspin_client.c):同一外部
IP 连续两次在等待 server/init 阶段断开后,后续连接自动以明文 client/hello 应答并全程明文收发(二进制音频帧直入既有 9 字节头 管线);KabuSync 内部拨号(X-Kabu-Internal)永不走 legacy,组协议 路径零改动。
- 工装:
scripts/dlna_lifecycle_stress.py(1 小时 DLNA 生命周期循环
压测 + heap/任务数/重启门禁);home_assistant_dlna_acceptance.py 版本门禁参数化(--fw-version,默认 1.9.1)。
- 文档:
docs/connection_manual.md(MA/HA 连接操作手册)、
docs/ma_ha_cast_environment.md(E 盘环境部署指南)、 docs/ma_ha_interop_report.md(互操作验证报告)、 docs/v191_release_acceptance_report.md(发布验收报告)。
验收门禁
- pytest 主机侧 554 + 53 passed,1 skipped(与 v1.9.0 等价);
- 三台设备模式切换验收、DLNA 单机生命周期、MA/HA 投送实测全部 PASS;
- 1h DLNA 压测:112 循环 0 失败,heap 漂移 +32 B,无重启;
- 静态资源:RAM 93520 B(+56 B)/ Flash 2392847 B(+2492 B)vs v1.9.0;
- 已知边界:KabuSync 三机分组在当前受测机群不稳定,v1.9.0 基线同等
表现(非本版本回归),登记为独立定位任务。
[1.9.0] - 2026-08-24
三普通模式无重启热切换 + 三击进入 KabuSync;Music Assistant / Home Assistant / Roon 兼容补强;待机热会话 Noise nonce 顺序问题根治。 AirPlay 2 核心路径与 v1.8 KabuSync 同步行为零劣化。
Added 新增
- 普通模式无重启热切换:AirPlay 2、电台、DLNA 之间双击原地切换
(stop→播报→start,不再 esp_restart),秒级完成;提交改用 settings_confirm_boot_mode(不残留 boot_pending,断电不误计启动失败)。
- 三击手势:任意普通模式三击进入 KabuSync(一次受控重启);KabuSync
内双击返回最近使用的普通模式(last_normal NVS 持久化,无效安全回退 AirPlay);普通模式双击循环不再包含 KabuSync。重启后首次进入 ACTIVE 播报"当前模式名 + 成功提示音"。
- 第三方兼容:Music Assistant 的 Sendspin 播放与会话仲裁;Home
Assistant 的 DLNA 渲染器兼容完善;Roon AirPlay(RAOP)播放路径补强 (mDNS / bplist / RTSP)。
- 验收工具:新增 mode_switch_acceptance / roon_raop_acceptance /
home_assistant_dlna_acceptance / kabusync_jiangnan_stress(江南单曲循环 与三机主机轮换压力门禁)四套真机门禁脚本。
Changed 变更
- KabuSync 组播入组准备窗 6s→8s(
CONFIG_SENDSPIN_JOIN_WINDOW_MS):
三机实测健康待机主机接管 6.049s、旧窗口贴边;与 8s 硬件验收门禁对齐, 稳定全集仍提前冻结(不是固定等待 8s)。
- 双击判定延迟 +350ms(区分第二/三击的必要代价)。
Fixed 修复
- 待机热会话 Noise nonce 失步根治:原 TX_STANDBY 以
send_epoch++
丢弃在队密文,违反"nonce 一旦推进,密文必须完整按序送达"不变量, 会让保温成员在下次认领时解密失败。改为先排空已加密音频、再发送 stream/end 并等待写入确认,失败才退回干净重连;standby 状态先于传输 处理发布,warm 会话不再被误拆。
- KabuSync 待机握手卡死:WS 已连接但 2s 未达 SESSION_ACTIVE 即触发
重连看门狗(健康成员被误判晚入组的根因),ACTIVE 会话不受影响。
- 模式切换失败立即回滚:跨域准备失败立即重新持久化并启动原模式;
不可用启动模式在计数与资源构造前清除并安全回退 AirPlay。
- 编译警告清零(含 v1.8 遗留 4 条):ASRC shadow 诊断变量、LED 后备
动画、mute GPIO 负移位。
发布门禁(全部通过)
- pytest 558 绿 + 1 skip;clean build 零 warning(RAM 28.5% /
Flash 38.8%);buildfs PASS。
- 三机真机验收(COM26/27/28,同一 clean binary):KabuSync 冷启动与
三主机轮换全 PASS(公共首帧 0、group time 一致、startup miss 0, 丢包/跳帧/时间线不连续/sendq drop/writer failure/late chunk/underrun/ flap 全零);每台 5 轮普通模式循环 + KabuSync 往返 27/27 PASS (普通域 uptime 单调零重启,跨域恰有一次可观测重启边沿)。
- 30min 三机轮换压测(3×10min 正式窗、江南单曲循环 9 轮、工厂镜像
重刷后全量重验):同步/音频/协议门禁全绿——零 underrun/零 desync/ 零 skip/零 sendq drop/零 flap/零 startup miss,公共起播首帧 0 且 group time 完全一致,覆盖率 ≥99.36%,包速 25/s 锁定,时钟全程收敛; 待机释放(nonce 有序排空路径)执行 6 次零异常。脚本判定的 FAIL 项 均为短窗伪象(脚本门禁按默认 30min/轮设计,10min 窗无法产生 “≥7 曲循环”与校准后资源样本),非固件缺陷。
- 4 小时 KabuSync 长稳压测(3×78min 三主机轮换、54 曲循环、三路串口
全程采集):零 underrun/零解密错误/零 panic/零看门狗,累计 uptime 7.2h;堆低水位漂移 ≤1KB(门禁 8KB)、最大块与任务数全程稳定, 无泄漏;热认领 claim→ready 325–527ms(v1.8 基线 312–590ms, 持平)、首次冷组网 3.2–6.4s(v1.8 实测 6.049s,持平);待机释放 nonce 排空路径执行 57 次零异常;压测后单机三模式热切换验收 PASS (heap 跨轮无损失、普通域零重启)。
已知问题(沿用 v1.8)
- AirPlay2 多房间后加入设备音量可能不一致(见
docs/archive/airplay_join_volume_unification_issue.md,本版不修)。
本版新增已知边界(如实登记)
- 认领后首次突发 TCP 短写:4.5h 累计测试中 3 次(均在 EOF 再认领
或轮首认领后 0.4–6s 内、双成员同时),固件按设计毒化连接并干净 重握手,<1s 自愈,零 underrun/零失步、无可闻影响;后续带串口抓 首次突发时序定位。
- EOF 再认领 floor margin 瞬低:9 轮中 1 轮在 EOF 后重新认领的
起始段 reader/floor margin 瞬降至 120ms(门禁 160ms),斜率 +2406 fps 快速回补,零 underrun;登记观察,不修。
- RF 极限案例(非缺陷,韧性实证):4h 压测轮 1 中 .67→.68 链路
连续 6 个循环(约 26min)出现近 100% 射频丢失,全部经重传 100% 救回(迟到副本按序丢弃),零 underrun、音频全程连续、同步不失; 后续两轮同成员组合零复现,判定环境 RF 事件。
[1.8.0] - 2026-08-22
五机 KabuSync 支持:群组上限 3→5(CONFIG_SENDSPIN_GROUP_CAP=5, Kconfig 默认仍为 3)。
通宵五机深度压测(约 4.7h 矩阵 + 复验,全程五路串口)
- 快循环 12/12 PASS(SOAP ack 172–250ms、claim→ready 312–590ms);
冷启动 8 轮完全确定性:claim→冻结 312ms、claim→出声 4312ms(与三机一致)。
- 成员逐台重启自愈 ×8 次:5–15s 恢复 warm=4;五机模式 churn×3:
待机组重建 5/40/5s。
- 长稳 90min×2 + 28min:四成员同步块数差 ≤6、组长零写失败/零不连续;
仅弱信号成员出现 RF 迟到块(0.015–0.35%,零 underrun,各轮设备不同, 与 RSSI 相关,环境问题)。
- 全夜串口零 panic/零看门狗/零 desync;流播守卫拦截流播中选举 526 次。
已知容量边界(如实登记)
- cap=5 组长内部堆历史最低 ~4.8KB(cap=3 时 ~31KB):两个额外常驻会话的
lwIP/WiFi 开销,全夜功能零异常;TCP_SND_BUF 实测非瓶颈(保持调优值 64KB), WiFi TX 缓冲 128 为同步稳定性调优值不动。后续若需加大群组,优先内部 RAM 专项审计。
- 拥塞 2.4GHz 下个别弱信号成员长稳会出现迟到块(零 underrun);建议
信道 6/11 或 5GHz。
- AirPlay2 多房间后加入设备音量可能与组内不一致(通常偏大):设备加入即用
上次存储音量,且音源对各设备下发音量可能有 ~1dB 差异。已记录不在本版修复, 见 docs/archive/airplay_join_volume_unification_issue.md。
发布门禁(全部通过)
- pytest 479 绿 + 1 skip、构建零 warning、buildfs PASS。
- 工厂镜像 kabu_factory_v1.8.0.bin 真机试烧 RESULT: PASS;三模式切换
冒烟 PASS;OTA 实测 1.7.2→1.8.0 版本号变化;AirPlay 三机 10min: 126pps 满速、零 underrun/零硬重同步、PTP 全锁、丢包 100% 重传救回。
- AirPlay A/B 第 3 轮(五机 90min 长稳压测):五台均 124.6pps 满速、PTP
锁定率 100%、堆漂移斜率 ≈0(无泄漏);拥塞信道下丢包 578–1953 个/台 中 4 台 100% 重传救回、零 underrun;仅最弱信号机(RSSI -68)出现一次 射频突发(64 underrun/47 重同步,11 个间隙未能重传),其后 50+ 分钟 无复发、自愈。属拥塞 2.4GHz 环境对最弱信号机的影响,非固件缺陷。
[1.7.2] - 2026-08-21
KabuSync 对标 Sonos 快速起播发布:L0 常驻存在层 + L1 待机热会话, 组网从“每次起播重新发现/连接/握手/收敛”变为“舰队常驻热态、认领 直接起播”。快循环成员崩溃根因一并根治。AirPlay2 路径零改动、实测 零劣化。
Added 新增
- L0 常驻存在层(
CONFIG_KABU_PRESENCE,默认开):所有模式下常驻的
UDP :8930 被动应答服务(probe 协议 v2:RESP 携带 mode/boot_id/flags, REQ 保持 v1 兼容旧固件)。AirPlay 模式下纯被动、零周期发包(源码钉住); /api/lan/devices 增加 presence 快路径与 boot_id 重启检测;点名结果 自动保鲜组长 NVS peer 缓存。
- L1 待机组(
CONFIG_SENDSPIN_STANDBY_GROUP,默认开):KabuSync 模式
下设备自动选举待机组长(上次组长优先、NVS 持久化、MAC 平局,无协商 协议),预建 WS+Noise 会话并以成员 1Hz 时钟交换保温时间滤波器;Stop/EOF 不再拆连接(滞留待机);认领走热路径:跳过发现/连接/Noise/收敛,300ms 稳定窗即冻结起播。选举含两次点名稳定校验 + 30s 复核让位,防启动竞态 双组长分脑;见活动组长 beacon 即拆会话退位。
- 遥测:
/api/sendspin/leader新增standby/standby_since_us/
sessions_warm;角色状态新增 standby_leader/standby_member。
- 工具:
scripts/sendspin_latency_profile.py组网延迟剖析(复用 soak
组网路径,分阶段 p50/p95,warm/cold 分桶);soak 门禁适配待机角色。
Fixed 修复
- 快循环成员崩溃根治:快间隔 Stop→Play 时成员在会话激活瞬间 abort——
handle_server_time 的时钟热重启日志位于自旋锁临界区内,newlib stdio 锁在高 INTLEVEL 下被 FreeRTOS 视为 ISR 上下文,锁竞争即 abort(v1.7.1 “成员 10–20s 补入”已知限制的真实根因,串口+coredump 实锤)。两处日志 移出临界区(置标志位后打印)并源码钉住。
- 重连风暴:retire 槽位不停 wsclient,重连循环与复活客户端永久互抢
成员单槽位(500ms 周期风暴);retire 现在同步停连。
- 待机组分脑根治(通宵 2h 压测实锤):拥塞 AP 下 presence 点名视图不完整,
各设备独立选举同时加冕多个待机组长,影子拨号互相翻搅会话,导致 60min 长稳中成员黑洞 47min。修复三件套:① 成员流播中会话拒绝新连接抢占; ② 选举宽限 30s + 待机组长广播 _kabu-group role=standby beacon,选举遇 在任 beacon 不加冕、同时加冕按 id 确定性让位、退位一律撤 beacon; ③ 维护拨号前 beacon 复核。验证:重启组长 80s 监控零分脑、待机 16s 重建、 快循环回归通过。
- 成员黑洞根治(8h 压测矩阵串口实锤,两轮递进):流播成员保持
STANDBY_MEMBER 角色且重选 tick 持续运行,拥塞 AP 上 mDNS beacon 连丢 >30s 宽限失效后自选待机组长,拨号风暴活跃组、自毁会话成永久黑洞 (三次复现均 .65,t+4.8/11.4/15.0min,连锁看门狗复位)。双守卫: ① 流播成员拒绝任何认领劫持(改源必须先停组);② 流播节点永不参选待机 组长(sendspin_player_stream_active 守卫,实测拦截 45 次/台)。复验: 同 churn 条件旧固件 15min 即黑洞,新固件 60min 全门禁 PASS。
- SOAP Play ack 临界根治:claim 在 role 任务排队,待机组长环每秒背靠背
两次 mDNS 查询 + claim 自身再查,最坏 ~900ms 顶破 1s 门禁(近 4 次运行 3 现)。修复:待机组长认领免重复 mDNS 复核(每秒监视已覆盖)+ 双 beacon 监视按 tick 奇偶交替(每 tick 至多一次查询)。实测 ack 1109ms→188ms。
- 启动期 INT WDT 崩溃根治:启动期待机组建期 NVS 持久化触发 flash 扇区
擦除,关缓存窗口把 discovery 任务短临界区拉长 >300ms 触发 Interrupt WDT panic(串口回溯 addr2line 实锤)。启用 CONFIG_SPI_FLASH_AUTO_SUSPEND (SoC 原生支持,擦写期间 CPU 取指不再被整段阻塞)。
Performance 性能(三机实测,素材:江南单曲循环)
- Play→全组出声:1.2–2.0s(v1.7.1:6–9s,快循环退化 10–20s)。
- claim→ready 冻结:323–330ms(v1.7.1 冷路径 6000–6400ms,提速 ≈19×)。
- 稳态冷启动(组长重启后,10 轮)claim→出声:p50=4324ms,9/10 轮 ≤4.9s
(目标 ≤4.5s 达成 9 成);p95=6339ms 含拓扑变更后 ≤30s 过渡窗内的冷 路径轮次(不劣于旧基线 6–9s,窗口内自愈)。
- 快循环 10 轮 × 52s 全绿(manifest 12 项门禁);堆水位轮间 ±56B 零泄漏;
待机会话常驻开销 ≈8KB 内部 RAM(一次性分配)。
验证(发布门禁)
- pytest 479 全绿(新增 31 项源码钉住:存在层 9 + 待机组/黑洞守卫 18 +
临界区日志 1 + 看门狗栈 1 + 配置钉住 2);pio run -e airplay2 零新增 warning。
- 8h 多场景压力矩阵(v1.7.2 固件,全程串口):快循环/冷启动 8 轮/
成员重启自愈/90min 长稳/模式 churn×4/90min 长稳;发现的 3 个问题均串口 实锤定位并修复,修复后同条件复验全绿(churn 后 60min 长稳全门禁 PASS、 快循环 5/5 PASS、SOAP ack 218–796ms)。
- AirPlay2 红线门禁(存在层常驻,32.1min × 3 台,真实 AirPlay 源):
各收 24.1 万包 125.3pps 满速;丢包间隙 227/228/159 全部重传救回(100%); 零 underrun/late/hard_resync、零 PTP 重锁、零 ASRC 异常;堆 ±1KB 稳定; 零重启。计划要求 30min×3 轮,本轮为第 1 轮,其余轮次留待后续长听时补测。
- 五机测试(cap=5 测试固件,两台新片入网):5 机待机组自组建;15min
长稳全门禁 PASS;快循环 4/4 轮 PASS(热路径 claim_to_ready=329ms);测后 刷回 cap=3 发布固件冒烟 PASS。
- 工件:
output/stage3_final.manifest.json、output/stage4_profile_*.jsonl、
output/airplay_30min_monitor.jsonl。
已知限制
- 拓扑变更(设备重启/模式切换)后 ≤30s 过渡窗内的首次认领可能走冷路径
(6–7.5s),窗口内自愈;稳态后恢复秒级。
- 群组上限仍为 3 台;待机组选举无跨子网能力(同子网广播)。
- 全舰队同时重启/模式切换后,待机组重建偶发 45–60s(点名稳定校验 +
重选周期),单台重启不受影响(~15s)。
- 2.4GHz 拥塞环境下偶发 >10s 的 AP/源端停顿会终止 DLNA 源流(组长侧
source_waits=0、发送满速,非固件缺陷);建议信道 6/11 或 5GHz。
- 本版本调试期使用的 coredump-to-UART 配置已随发布移除。
[1.7.1] - 2026-08-19
KabuSync 体验改善包 + 快速循环挂起专项修复。
修复(可靠性)
- 快速循环挂起根因修复:组长 ws 客户端 socket 无 SO_SNDTIMEO,向半死连接
发送会阻塞会话任务整个 TCP 重传窗口(~15s),stop 超时后 destroy 拒释活任务, 每轮泄漏一个 fd;反复 stop/play 耗尽 lwIP TCP PCB 池,HTTP 与全部成员连接 同时瘫痪(此前需断电恢复)。组长 socket 现与成员侧一致:SO_SNDTIMEO 2s + keepalive 放宽(idle=5/intvl=2/cnt=3)。回归实测:原挂死场景第 5 轮自恢复, 全程无重启、HTTP 始终存活。
- HTTP 回环看门狗:每 5s 自探一次,连续 8 次无响应(~40s)自动重启,
作为残余 lwIP 死锁类故障的自愈兜底。
新增(用户体验)
- Web 全屋一键切换:新页面 /lan(设置页入口"全屋模式")——局域网设备/
模式总览 + 一键全部切换 KabuSync/AirPlay。发现走 mDNS 双服务扫描 (_sendspin + _raop,两轮合并去重,任一模式下的设备都能被发现和切回), 成员信息经 esp_http_client 有界轮询,本机最后切换以保证响应送达。 新端点:GET /api/lan/devices、POST /api/lan/mode。
- 组群中 LED 反馈:DLNA 认领到起播之间,组长显示 connected/paused 图案
(不再静默待机),群组解散回待机。
- DLNA 音量全组联动:群组激活时 DLNA SetVolume 经组长广播全组音量,
不再只调组长本机。
- 改名引导:设置页设备名输入下新增双语提示(建议每台设备起不同名字)。
- 手册更正:群组满员行为描述改为实际行为(不加入本次播放、无提示)。
验证
- pytest 448 passed(新增 tests/test_kabusync_ux.py 7 项架构钉住 + 挂起修复钉住)。
- 五台实机:全屋切换往返 5/5(sendspin→airplay→sendspin,含混合模式舰队);
快速循环回归无硬挂起;看门狗无误重启(uptime 持续 >300s)。
已知限制(延续 1.7.0)
- 快速连续 Stop→Play(间隔 <30s)第二轮起仍退化为组长先播、成员 10–20s 补入
(成员侧会话拆除后的重新接受延迟,根因待成员侧串口定位)。
[1.7.0] - 2026-08-19
R3 起播提速发布:多机同步起播从"双峰 7–48s"收敛为"常态全员共同起播 ~6–9s", 并新增组长有界加入窗口兜底。底层同步算法与数据面零改动。
Added 新增
- 组长有界加入窗口:Kconfig
SENDSPIN_JOIN_WINDOW_MS(1000–8000,默认
6000ms)。到期按当前已就绪集合冻结(可为 0,组长独播),未到成员走迟到 加入路径数秒内流式补入;冻结日志标注触发原因(all_ready/window/deadline)。
- 成员时钟收敛提速四件套:收敛前旁路创新门限与步长限幅(收敛前未播放,
大步长无听感风险,根治"错首样本→样本饥饿"的 48s 尾部);收敛前突发采样 100→50ms;RTT 接收地板 25→50ms;会话边界热重启再对齐(同组长 120s 内重连 且首样本与 drift 外推差 <25ms 时再对齐 offset、保留 drift,亚秒再收敛, 带连续拒收冷复位兜底)。收敛判据(10 样本 + 600us)不变,同步质量不降级。
- 快路径点名真正生效:probe UDP 端口 8929 与 httpd 控制口
(SENDSPIN_CTRL_PORT)撞车导致 responder 每次开机 bind 失败、此前从未工作, 移至 8930 并加防回归钉住;probe 随 CONFIG_SENDSPIN_PEER_PROBE 启用。
- 组长 enable 时重启被 TX_PAUSE 停掉的成员 WS;成员 socket 加 SO_SNDTIMEO 2s、
会话拆除互斥等待改有界 2.5s、keepalive 放宽至 5s+3×2s(丢包 AP 误杀健康会话 的修复);wsclient 连接超时 5s→2s、重试间隔 1s→250ms。
验证(拥塞 AP,COM23/24/25 三机)
- 起播分段遥测(connect/noise/available)上线,慢起播可直接定位到段。
- 首轮/冷启动:双成员共同起播(all_ready 冻结),起播 6–9s,available 1.3–2.3s。
- pytest 437+ 全过(含新增收敛/窗口/端口/keepalive 钉住测试)。
- 三机数据面长测零错误基线保持(underrun/disc/rebase 全零)。
已知限制
- 快速连续 Stop→Play 循环(间隔 <30s)时,成员侧会话拆除后存在 ~10s 的重新
接受延迟,导致此类场景第二轮起退化为"组长先播、成员约 10–15s 流式补入"; 长时间间隔的常规使用与长播稳态不受影响。
- 深入压测发现(v1.7.0 三机/五机反复加入测试):上述快速循环场景偶发
组长硬挂起(无 panic 转储,需断电/串口复位恢复;三机 10 轮后与五机第 2 轮 各复现一次)。连续播放长稳(三机 30min/10min、五机 15min)均零错误,常规 使用路径不受影响。成员侧 accept 阻塞与组长挂起同属会话生命周期问题,列为 下一调查项(需成员/组长两侧串口捕获定位)。
- 验证补充:五机首轮共同起播 5.27s(4 成员 initial=4,available 2.2–3.2s);
五机 15min 长稳四成员零 underrun/零 disc。
[1.6.1] - 2026-08-19
R3 起播问题修复与诊断化发布。核心成果:起播 0 失败(原 5/6 失败)、 两次崩溃/挂起隐患根因修复、起播分段遥测上线,慢起播残留尾端被精确定位 到成员时钟收敛(待干净 AP 复测分离环境贡献)。
Fixed 修复
- PSRAM 栈 NVS 访问崩溃:discovery 任务栈在 PSRAM,而冻结时的成员缓存
NVS 读写会关闭 flash cache,触发 cache_utils.c:127 断言 panic(堆栈回溯 实锤)。修复:缓存加载提前到 sendspin_leader_init(内部 RAM 上下文), 冻结持久化移交专职内部 RAM worker。
- 循环起停挂起:每次冻结派生一次性任务做 NVS 持久化,反复 stop/play
压测下组长会硬挂起(纯 v1.6.0 12 轮对照无此问题)。改为 init 期一次性 创建常驻 worker,冻结仅发任务通知,12 轮压测验证无挂起。
Added 新增
- 起播分段遥测:
/api/sendspin/leader每 peer 新增 connect_ms/noise_ms/
available_ms(相对认领时刻),慢起播可直接拆分为发现/连接/收敛三段。
- 快路径代码(搜置):NVS 缓存 UDP 单播点名 + 广播兜底(probe)与冻结前
二次点名(final roll-call)均已实现并有架构测试钉住,但分别由 CONFIG_SENDSPIN_PEER_PROBE / CONFIG_SENDSPIN_FINAL_ROLLCALL(均默认关) 门控:前者待挂起风险排查后启用,后者待 barrier-tick 交互根因定位。
- 工具:release_startup 支持成员参数与分段遥测输出;流判据改 >= 适配
多成员组。
R3 验证数据(拥塞 AP,三机,10 轮)
- 起播失败 0/10(v1.6.0 前基线 5/6 失败)。
- 快轮 6.7–7.2 s;慢轮尾部由成员 available(时钟收敛)偶发 10–48 s 导致,
发现+连接+Noise 三段均 <1 s(遥测实锤)——瓶颈不在发现环节。
- 干净 AP 复测(Q2)为后续项,以分离环境与固件贡献。
[1.6.0] - 2026-08-18
用户层品牌改名发布。多房间同步模式对外名称由 Sendspin 改为 KabuSync, 仅调整用户可直接感知的载体;底层协议实现、代码命名与全部对外接口保持不变。
Changed 变更
- 用户层改名:同步模式对外品牌名 Sendspin → KabuSync,同步更新四处用户
可见载体:模式切换语音提示(改播 "KabuSync mode")、Android 发送端 App 名称与界面文案(apps/sendspin-android)、用户手册 docs/user_manual.md、本发布说明。
- 模式切换语音恢复 + 成功提示:修复 3c61a1a 重构时丢失的切换播报接线
(stop→播报目标模式→持久化→重启);新增一次性 "Switch complete" 提示音 (success.mp3),仅在用户切换触发的启动首次 ACTIVE 时播报,失败回退启动不播。
- 用户手册群组上限描述同步修正为 1.5.0 定稿的 3 台(1 组长 + 2 成员)。
- 新增测试工具:三机单曲循环长测
tools/g1_stage0/g1_stage0e_three_dev_loop.py、
AirPlay 低频监控 tools/g1_stage0/airplay_30min_monitor.py。
Unchanged 保持不变(兼容性承诺)
- 底层 C 代码、Kconfig、协议实现零改动;
_sendspin._tcpmDNS 服务、
WebSocket 路径、MQTT/API 标识、SENDSPIN_GROUP_CAP 等代码标识符均不变。
- 与 Music Assistant 等 Sendspin 生态服务端的互操作性不变。
- 固件功能与 1.5.0 一致(仅恢复丢失的切换语音 + 新增成功提示);语音提示
资产(SPIFFS)有变化,USB 全量烧录或重刷 SPIFFS 生效。
Validated 已验证
- pytest 424 全通过;pio 构建 SUCCESS。
- KabuSync 三机 30 min 单曲循环(6 次 EOF 重启起播):零欠载/零丢包/零 ASRC
跳变,成员堆零泄漏,leader CPU core0 62–64%。
- AirPlay 三台 33 min:408 次丢包间隙全部重传救回,零欠载、零硬重同步、
PTP 零重锁,堆零泄漏。
[1.5.0] - 2026-08-17
三机 Sendspin 同步发布。首次实现无需服务器、无需 Apple 源的多台 ESP32 自包含 同步播放,同步精度从行业常见的 <10 ms 提升到 p95 <1 ms。
Added 新增
- 三机 Sendspin 同步组:leader + 2 成员,共享 Opus 编码分发;组上限
SENDSPIN_GROUP_CAP 定稿为 3(去实验前缀,range 2-8,>3 需扩展容量验证)。
- 成员 ASRC 失锁自愈(R6):disc/rebase 事件在 4 次/20s 内判定失锁,自动
全重置控制器并让渲染端 re-anchor,无需重启整组即可恢复同步;self_recoveries 计数经 /api/system/info 暴露。
- 发布测试工具:
tools/g1_stage0/新增长稳、启动分布、掉线重连、ASRC 注入、
单机循环听感等探针脚本。
- 本 CHANGELOG 与
docs/release_test_checklist.md(发布完成定义)。
Changed 变更
- 全局 48 kHz 输出时间线:
CONFIG_OUTPUT_SAMPLE_RATE_HZ=48000,所有播放源
(DLNA / 电台 / AirPlay / Sendspin)统一汇聚到 48k 输出。
- fast_resample_441_480:新增整数多相重采样器(147:160、Q15、零浮点),
取代通用浮点重采样用于 44.1→48k;源路径 core1 占用 96.7%→55.9%, source_waits 归零。Kconfig AUDIO_FAST_RESAMPLE_441_480 默认 y。
- 时间滤波器门限(B1):成员时钟滤波器加入创新门限 max(3σ,500µs) 与单步
限幅 ±200µs,抑制 WiFi 抖动坏样本导致的时钟瞬移。
- gap 吸收阈值(B2):
SP_GAP_EPSILON_US2ms→10ms,小时间戳偏移交由闭环
ASRC 吸收而非填静音。
Performance / Quality 性能与质量
- 多设备同步稳态偏差门禁 p95 <1 ms、max <2 ms,实测 p95 ≈ 0.64–0.76 ms
(对比原 AirPlay 基线 <10 ms,提升约 10 倍)。
- Opus 96 kbps 分发,较 PCM 节省约 14× 带宽;CPU 余量估算支持约 8 台成员。
Validated 已验证(发布门禁)
- R1 三机 120 min 长稳:25.00 pkt/s、全错误计数为 0。
- R2 内存泄漏:leader/成员 heap 零泄漏。
- R4 掉线重连:源断恢复、成员复位恢复。
- R5 cap 定稿;R6 失锁自愈确定性注入回归。
- Q1 音质听感(用户确认);E1 pytest 418 通过。
- 详见
docs/release_test_checklist.md与docs/archive/sendspin_g1_capacity_baseline.md。
Known Limitations 已知限制
- R3 起播时长在拥塞 AP 下呈双峰(mDNS 发现延迟致成员偶发晚接入);不影响播放中
的亚毫秒同步与音质。干净 AP 复测与可选的静态成员白名单为后续项。
- AirPlay2 在 48k 输出下的多房回归为后续验证项。
[1.0.0] - 早期基线
AirPlay 渲染基线(本仓库改造前上游版本,对应 1c68f12 v0.1.29 之后)。