11//! VAD 阈值共享槽(REQ-115 PRE-O4 / v0.7.0 M2)。
22//!
3- //! @ai-context: 健康判定口径统一的可观测性通道——会话线程内 AdaptiveVad 的
4- //! 当前阈值(f32)经共享 AtomicU32(位模式)暴露给诊断面板:
5- //! 降级提示与切段判定口径可对照核查("诊断可查阈值"验收点)。
6- //! @ai-context: 共享槽语义 = 最近一次会话的最后阈值(无活动会话时保留旧值——
7- //! 诊断面板可看上次会话的阈值走向;初始化 0.0 = 无数据)。
8- //! @ai-context: 审查 MEDIUM-8 修复:槽记录发布来源会话 id——诊断面板可区分
9- //! "实时值"(发布会话=活动会话)与"残留值"(上次会话遗留),
10- //! 避免把旧会话阈值当成当前口径误读。
3+ //! @ai-context: 健康判定口径统一的可观测性通道(**当前仅剩写端**):会话线程内
4+ //! AdaptiveVad 的当前阈值(f32,AtomicU32 位模式)与发布来源会话 id
5+ //! 持续写入本共享槽;诊断读端已随「不留半成品」的删除处置移除——
6+ //! 未来若需要,届时按诊断需求重新引入(写端数值不受影响)。
7+ //! @ai-context: 共享槽语义 = 最近一次会话的最后阈值(无活动会话时保留旧值);
8+ //! 初始化 0.0 = 无数据。审查 MEDIUM-8 留下的来源会话 id 仍在写入
9+ //! (供未来读端区分"实时值"与"残留值"),当前无读取方。
1110
1211use std:: sync:: atomic:: { AtomicI64 , AtomicU32 , Ordering } ;
1312
@@ -25,68 +24,4 @@ impl VadThresholdSlot {
2524 self . bits . store ( threshold. to_bits ( ) , Ordering :: Relaxed ) ;
2625 self . source_session . store ( session_id, Ordering :: Relaxed ) ;
2726 }
28-
29- /// 读取当前阈值(无数据时 0.0)。
30- pub fn read ( & self ) -> f32 {
31- f32:: from_bits ( self . bits . load ( Ordering :: Relaxed ) )
32- }
33-
34- /// 发布来源会话 id(0 = 无数据;诊断新鲜度判定用)。
35- pub fn source_session_id ( & self ) -> i64 {
36- self . source_session . load ( Ordering :: Relaxed )
37- }
38- }
39-
40- /// 暴露接口(诊断面板):当前阈值 + 基础阈值对照 + 新鲜度。
41- #[ derive( Debug , Clone , serde:: Serialize ) ]
42- #[ serde( rename_all = "camelCase" ) ]
43- pub struct VadThresholdView {
44- /// 当前自适应阈值(0.0 = 无数据)
45- pub current : f32 ,
46- /// 固定基础阈值(SILENCE_RMS_THRESHOLD——对照口径)
47- pub base : f32 ,
48- /// 发布来源会话 id(0 = 无数据)
49- pub source_session : i64 ,
50- /// 是否为活动会话的实时值(诊断面板区分实时/残留)
51- pub is_live : bool ,
52- }
53-
54- #[ cfg( test) ]
55- mod tests {
56- use super :: * ;
57-
58- #[ test]
59- fn slot_roundtrip_preserves_bits ( ) {
60- // f32 → 位模式 → f32 往返一致(含特殊值)
61- let slot = VadThresholdSlot :: default ( ) ;
62- slot. publish ( 42 , 0.0042 ) ;
63- assert ! ( ( slot. read( ) - 0.0042 ) . abs( ) < 1e-7 ) ;
64- assert_eq ! ( slot. source_session_id( ) , 42 ) ;
65- }
66-
67- #[ test]
68- fn slot_defaults_to_zero ( ) {
69- // 无数据 → 0.0 + 来源 0(诊断面板"无数据"判定)
70- let slot = VadThresholdSlot :: default ( ) ;
71- assert_eq ! ( slot. read( ) , 0.0 ) ;
72- assert_eq ! ( slot. source_session_id( ) , 0 ) ;
73- }
74-
75- #[ test]
76- fn slot_handles_nan_safely ( ) {
77- // NaN 位模式往返不崩溃(防御:会话线程异常值)
78- let slot = VadThresholdSlot :: default ( ) ;
79- slot. publish ( 42 , f32:: NAN ) ;
80- assert ! ( slot. read( ) . is_nan( ) ) ;
81- }
82-
83- #[ test]
84- fn slot_overwrites_latest ( ) {
85- // 后写覆盖先写(最近阈值 + 最近来源语义)
86- let slot = VadThresholdSlot :: default ( ) ;
87- slot. publish ( 1 , 0.01 ) ;
88- slot. publish ( 2 , 0.02 ) ;
89- assert ! ( ( slot. read( ) - 0.02 ) . abs( ) < 1e-7 ) ;
90- assert_eq ! ( slot. source_session_id( ) , 2 ) ;
91- }
9227}
0 commit comments