@bdky/aaas-pilot-kit 1.0.9 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +255 -23
- package/dist/index.cjs.js.LICENSE.txt +9 -0
- package/dist/index.esm.js +254 -22
- package/dist/index.esm.js.LICENSE.txt +9 -0
- package/dist/index.umd.js +254 -22
- package/dist/index.umd.js.LICENSE.txt +9 -0
- package/dist/ky-aaas-pilot-kit.umd.js +254 -22
- package/dist/ky-aaas-pilot-kit.umd.js.LICENSE.txt +9 -0
- package/dist/libs/aaas-pilot-kit/src/index.d.ts +2 -0
- package/dist/libs/aaas-pilot-kit/src/lib/DI/types.d.ts +1 -0
- package/dist/libs/aaas-pilot-kit/src/lib/constants/azure.d.ts +423 -0
- package/dist/libs/aaas-pilot-kit/src/lib/service/rtc-asr/asr/azureAsrService.d.ts +108 -0
- package/dist/libs/aaas-pilot-kit/src/lib/service/rtc-asr/asr/baseAsrService.d.ts +28 -3
- package/dist/libs/aaas-pilot-kit/src/lib/service/rtc-asr/signal/noop.d.ts +54 -0
- package/dist/libs/aaas-pilot-kit/src/lib/utils/EnergyBasedGate.d.ts +240 -0
- package/dist/libs/aaas-pilot-kit/src/lib/utils/MediaStreamManager.d.ts +209 -0
- package/dist/libs/aaas-pilot-kit/src/lib/utils/audio-processing/AudioContextLifecycle.d.ts +110 -0
- package/dist/libs/aaas-pilot-kit/src/lib/utils/audio-processing/AudioWorkletCapability.d.ts +95 -0
- package/dist/libs/aaas-pilot-kit/src/lib/utils/audio-processing/FloatAPICompat.d.ts +103 -0
- package/dist/libs/aaas-pilot-kit/src/types/config.d.ts +290 -3
- package/package.json +2 -1
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file 基于能量检测的智能门控 (Energy-based Gate)
|
|
3
|
+
* @description 通过实时监测音频能量差异,智能过滤外部音频干扰(如 TTS 回声),减少移动端 ASR 误识别
|
|
4
|
+
*
|
|
5
|
+
* 原理:
|
|
6
|
+
* 1. 实时计算麦克风音频的能量(RMS)
|
|
7
|
+
* 2. 在降噪启用期间,提高 ASR 激活阈值
|
|
8
|
+
* 3. 只有当麦克风能量显著高于基准能量时,才允许 ASR 识别
|
|
9
|
+
* 4. 使用自适应阈值算法,适应不同环境噪音
|
|
10
|
+
*
|
|
11
|
+
* 重构版本 (v2):
|
|
12
|
+
* - ✅ 修复历史窗口计算错误 (100帧 ≠ 1秒)
|
|
13
|
+
* - ✅ 直接从 inputBuffer 计算 RMS (绕过 analyser 分支不更新问题)
|
|
14
|
+
* - ✅ Float API 兼容层 (防止 iOS 崩溃)
|
|
15
|
+
* - ✅ 渐进增强: AudioWorklet → ScriptProcessorNode
|
|
16
|
+
* - ✅ AudioContext 生命周期管理
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* 能量门控配置选项
|
|
20
|
+
*/
|
|
21
|
+
export interface IEnergyGateOptions {
|
|
22
|
+
/**
|
|
23
|
+
* 外部播放抑制启用时的能量倍数阈值
|
|
24
|
+
* 麦克风能量必须显著高于基准能量才能通过
|
|
25
|
+
* @default 3.0
|
|
26
|
+
*/
|
|
27
|
+
suppressionEnergyMultiplier?: number;
|
|
28
|
+
/**
|
|
29
|
+
* 空闲期间的基准能量阈值(dBFS)
|
|
30
|
+
* 低于此值的信号被视为静音或微弱回声
|
|
31
|
+
* @default -45
|
|
32
|
+
*/
|
|
33
|
+
idleThreshold?: number;
|
|
34
|
+
/**
|
|
35
|
+
* 能量平滑系数(0-1)
|
|
36
|
+
* 值越大,能量变化越平滑(降低噪声干扰)
|
|
37
|
+
* @default 0.7
|
|
38
|
+
*/
|
|
39
|
+
smoothingFactor?: number;
|
|
40
|
+
/**
|
|
41
|
+
* 外部播放抑制启用延迟(毫秒)
|
|
42
|
+
* 启用抑制后,延迟多久才启用高阈值
|
|
43
|
+
* @default 100
|
|
44
|
+
*/
|
|
45
|
+
suppressionActivationDelay?: number;
|
|
46
|
+
/**
|
|
47
|
+
* 外部播放抑制关闭恢复延迟(毫秒)
|
|
48
|
+
* 停止抑制后,延迟多久恢复正常阈值
|
|
49
|
+
* @default 500
|
|
50
|
+
*/
|
|
51
|
+
suppressionRecoveryDelay?: number;
|
|
52
|
+
/**
|
|
53
|
+
* 是否启用调试日志
|
|
54
|
+
* @default false
|
|
55
|
+
*/
|
|
56
|
+
debug?: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 能量统计数据
|
|
60
|
+
*/
|
|
61
|
+
export interface IEnergyStats {
|
|
62
|
+
/**
|
|
63
|
+
* 当前麦克风能量(dBFS)
|
|
64
|
+
*/
|
|
65
|
+
microphoneEnergy: number;
|
|
66
|
+
/**
|
|
67
|
+
* 外部播放抑制是否启用(考虑延迟后的状态)
|
|
68
|
+
*/
|
|
69
|
+
isSuppressionEnabled: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* 当前激活阈值(dBFS)
|
|
72
|
+
*/
|
|
73
|
+
currentThreshold: number;
|
|
74
|
+
/**
|
|
75
|
+
* 信号是否允许通过
|
|
76
|
+
*/
|
|
77
|
+
isGateOpen: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* 最近 1 秒内通过的帧数占比
|
|
80
|
+
*/
|
|
81
|
+
passRate: number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 基于能量检测的智能门控
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const gate = new EnergyBasedGate(audioContext, {
|
|
89
|
+
* suppressionEnergyMultiplier: 3.0,
|
|
90
|
+
* idleThreshold: -45
|
|
91
|
+
* });
|
|
92
|
+
*
|
|
93
|
+
* // 连接麦克风
|
|
94
|
+
* microphoneNode.connect(gate.input);
|
|
95
|
+
*
|
|
96
|
+
* // 获取过滤后的输出流
|
|
97
|
+
* const cleanStream = gate.getOutputStream();
|
|
98
|
+
*
|
|
99
|
+
* // 外部音频播放时启用回声消除(如 TTS 播报)
|
|
100
|
+
* tts.on('start', () => gate.setSuppressionEnabled(true));
|
|
101
|
+
* tts.on('end', () => gate.setSuppressionEnabled(false));
|
|
102
|
+
*
|
|
103
|
+
* // 传给 Azure ASR
|
|
104
|
+
* const audioConfig = SpeechSDK.AudioConfig.fromStreamInput(cleanStream);
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export declare class EnergyBasedGate {
|
|
108
|
+
/**
|
|
109
|
+
* 音频输入节点
|
|
110
|
+
*/
|
|
111
|
+
readonly input: GainNode;
|
|
112
|
+
private readonly context;
|
|
113
|
+
private readonly options;
|
|
114
|
+
/**
|
|
115
|
+
* 输出目标节点
|
|
116
|
+
*/
|
|
117
|
+
private readonly destination;
|
|
118
|
+
/**
|
|
119
|
+
* 门控增益节点(用于静音)
|
|
120
|
+
*/
|
|
121
|
+
private readonly gate;
|
|
122
|
+
/**
|
|
123
|
+
* 音频处理器 (AudioWorkletNode 或 ScriptProcessorNode)
|
|
124
|
+
*/
|
|
125
|
+
private processor;
|
|
126
|
+
/**
|
|
127
|
+
* 处理器类型
|
|
128
|
+
*/
|
|
129
|
+
private processorType;
|
|
130
|
+
/**
|
|
131
|
+
* 门控状态历史(用于计算通过率)
|
|
132
|
+
*/
|
|
133
|
+
private readonly gateHistory;
|
|
134
|
+
/**
|
|
135
|
+
* ScriptProcessor 门控历史最大长度(动态计算,约 1 秒)
|
|
136
|
+
*/
|
|
137
|
+
private readonly scriptProcessorMaxHistoryLength;
|
|
138
|
+
/**
|
|
139
|
+
* AudioWorklet 门控历史最大长度(动态计算,约 1 秒)
|
|
140
|
+
*
|
|
141
|
+
* @remarks
|
|
142
|
+
* AudioWorklet 的 render quantum 固定为 128 samples/callback,
|
|
143
|
+
* 与 ScriptProcessor(这里使用 2048) 不同,因此需要分别计算。
|
|
144
|
+
*/
|
|
145
|
+
private readonly audioWorkletMaxHistoryLength;
|
|
146
|
+
/**
|
|
147
|
+
* 回声消除是否启用
|
|
148
|
+
*/
|
|
149
|
+
private isSuppressionEnabled;
|
|
150
|
+
/**
|
|
151
|
+
* 回声消除状态切换定时器
|
|
152
|
+
*/
|
|
153
|
+
private suppressionStateTimer;
|
|
154
|
+
/**
|
|
155
|
+
* 实际回声消除状态(考虑延迟后的状态)
|
|
156
|
+
*/
|
|
157
|
+
private effectiveSuppressionState;
|
|
158
|
+
/**
|
|
159
|
+
* 当前麦克风能量(RMS,dBFS)
|
|
160
|
+
*/
|
|
161
|
+
private currentEnergy;
|
|
162
|
+
/**
|
|
163
|
+
* 平滑后的能量值
|
|
164
|
+
*/
|
|
165
|
+
private smoothedEnergy;
|
|
166
|
+
/**
|
|
167
|
+
* AudioWorklet 上报的最近一次统计(用于 getStats)
|
|
168
|
+
*/
|
|
169
|
+
private workletPassRate;
|
|
170
|
+
private workletIsGateOpen;
|
|
171
|
+
/**
|
|
172
|
+
* 是否已启动
|
|
173
|
+
*/
|
|
174
|
+
private started;
|
|
175
|
+
/**
|
|
176
|
+
* 创建配置的promise
|
|
177
|
+
*/
|
|
178
|
+
private readonly setupAudioGraphPromise;
|
|
179
|
+
constructor(context: AudioContext, options?: IEnergyGateOptions);
|
|
180
|
+
/**
|
|
181
|
+
* 设置外部播放抑制启用状态(例如 TTS 播放期间抬阈)
|
|
182
|
+
*
|
|
183
|
+
* @param enabled - 是否启用抑制
|
|
184
|
+
*/
|
|
185
|
+
setSuppressionEnabled: (enabled: boolean) => void;
|
|
186
|
+
/**
|
|
187
|
+
* 启动门控
|
|
188
|
+
*/
|
|
189
|
+
start: () => void;
|
|
190
|
+
/**
|
|
191
|
+
* 停止门控
|
|
192
|
+
*/
|
|
193
|
+
stop: () => void;
|
|
194
|
+
/**
|
|
195
|
+
* 获取处理后的音频流
|
|
196
|
+
*/
|
|
197
|
+
getOutputStream: () => MediaStream;
|
|
198
|
+
/**
|
|
199
|
+
* 获取当前能量统计
|
|
200
|
+
*/
|
|
201
|
+
getStats: () => IEnergyStats;
|
|
202
|
+
/**
|
|
203
|
+
* 动态调整阈值倍数
|
|
204
|
+
*/
|
|
205
|
+
setEnergyMultiplier: (multiplier: number) => void;
|
|
206
|
+
/**
|
|
207
|
+
* 清理资源
|
|
208
|
+
*/
|
|
209
|
+
dispose: () => void;
|
|
210
|
+
/**
|
|
211
|
+
* 搭建音频处理图(异步,支持 AudioWorklet 检测)
|
|
212
|
+
*/
|
|
213
|
+
private setupAudioGraph;
|
|
214
|
+
/**
|
|
215
|
+
* 使用 AudioWorklet 设置音频图
|
|
216
|
+
*/
|
|
217
|
+
private setupWithAudioWorklet;
|
|
218
|
+
/**
|
|
219
|
+
* 使用 ScriptProcessorNode 设置音频图(降级方案)
|
|
220
|
+
*/
|
|
221
|
+
private setupWithScriptProcessor;
|
|
222
|
+
/**
|
|
223
|
+
* ScriptProcessor 音频处理回调
|
|
224
|
+
* 修复: 直接从 inputBuffer 计算 RMS,绕过 analyser 问题
|
|
225
|
+
*/
|
|
226
|
+
private readonly processAudioScriptProcessor;
|
|
227
|
+
/**
|
|
228
|
+
* 判断门控是否应该打开
|
|
229
|
+
*/
|
|
230
|
+
private readonly shouldGateOpen;
|
|
231
|
+
/**
|
|
232
|
+
* 获取当前激活阈值
|
|
233
|
+
*/
|
|
234
|
+
private readonly getCurrentThreshold;
|
|
235
|
+
/**
|
|
236
|
+
* 获取 AudioWorklet 处理器代码
|
|
237
|
+
* (内联版本,生产环境可改为加载外部文件)
|
|
238
|
+
*/
|
|
239
|
+
private getEnergyGateWorkletCode;
|
|
240
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file MediaStream 管理器
|
|
3
|
+
* @description 负责获取和管理 MediaStream,支持浏览器特性检测、动态约束构建、降级策略
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 音频处理特性配置
|
|
7
|
+
*
|
|
8
|
+
* 直接使用 Web 标准的 MediaTrackConstraints 类型,支持所有标准音频约束参数
|
|
9
|
+
*
|
|
10
|
+
* 常用特性:
|
|
11
|
+
* - echoCancellation: 回声消除
|
|
12
|
+
* - noiseSuppression: 降噪处理
|
|
13
|
+
* - autoGainControl: 自动增益控制
|
|
14
|
+
* - sampleRate: 采样率(Hz)
|
|
15
|
+
* - sampleSize: 采样大小(位)
|
|
16
|
+
* - channelCount: 通道数(1=单声道, 2=立体声)
|
|
17
|
+
* - deviceId: 设备 ID
|
|
18
|
+
*
|
|
19
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints
|
|
20
|
+
*/
|
|
21
|
+
export type IAudioFeatureConfig = MediaTrackConstraints;
|
|
22
|
+
/**
|
|
23
|
+
* 浏览器特性支持检测结果
|
|
24
|
+
*/
|
|
25
|
+
export interface IFeatureSupportResult {
|
|
26
|
+
/**
|
|
27
|
+
* 支持的特性列表
|
|
28
|
+
*/
|
|
29
|
+
supported: string[];
|
|
30
|
+
/**
|
|
31
|
+
* 不支持的特性列表
|
|
32
|
+
*/
|
|
33
|
+
unsupported: string[];
|
|
34
|
+
/**
|
|
35
|
+
* 是否有不支持的特性
|
|
36
|
+
*/
|
|
37
|
+
hasUnsupportedFeatures: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* MediaStream 获取结果
|
|
41
|
+
*/
|
|
42
|
+
export interface IMediaStreamResult {
|
|
43
|
+
/**
|
|
44
|
+
* 获取到的 MediaStream 实例
|
|
45
|
+
*/
|
|
46
|
+
stream: MediaStream;
|
|
47
|
+
/**
|
|
48
|
+
* 实际应用的约束配置
|
|
49
|
+
*/
|
|
50
|
+
appliedConstraints: MediaTrackConstraints;
|
|
51
|
+
/**
|
|
52
|
+
* 启用的特性列表(用于调试)
|
|
53
|
+
*/
|
|
54
|
+
enabledFeatures: string[];
|
|
55
|
+
/**
|
|
56
|
+
* 浏览器不支持的特性列表
|
|
57
|
+
*/
|
|
58
|
+
unsupportedFeatures: string[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* MediaStreamManager 配置选项
|
|
62
|
+
*/
|
|
63
|
+
export interface IMediaStreamManagerOptions {
|
|
64
|
+
/**
|
|
65
|
+
* 音频特性配置
|
|
66
|
+
* @default { echoCancellation: true, noiseSuppression: true, autoGainControl: true }
|
|
67
|
+
*/
|
|
68
|
+
audioFeatures?: IAudioFeatureConfig;
|
|
69
|
+
/**
|
|
70
|
+
* 是否在检测到不支持的特性时输出警告
|
|
71
|
+
* @default true
|
|
72
|
+
*/
|
|
73
|
+
warnOnUnsupportedFeatures?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* 是否在成功获取流后输出调试信息
|
|
76
|
+
* @default true
|
|
77
|
+
*/
|
|
78
|
+
logEnabledFeatures?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* 是否在完全不支持任何特性时回退到基础约束 {audio: true}
|
|
81
|
+
* @default true
|
|
82
|
+
*/
|
|
83
|
+
enableFallback?: boolean;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* MediaStream 管理器
|
|
87
|
+
*
|
|
88
|
+
* 负责获取和管理 MediaStream,支持:
|
|
89
|
+
* - 浏览器特性检测(echoCancellation、noiseSuppression、autoGainControl)
|
|
90
|
+
* - 动态约束构建(仅使用支持的特性)
|
|
91
|
+
* - 降级策略(不支持时使用基础约束)
|
|
92
|
+
* - 资源清理
|
|
93
|
+
*
|
|
94
|
+
* @example 默认配置
|
|
95
|
+
* ```typescript
|
|
96
|
+
* const manager = new MediaStreamManager();
|
|
97
|
+
* const result = await manager.getUserMedia();
|
|
98
|
+
* console.log('启用的特性:', result.enabledFeatures);
|
|
99
|
+
*
|
|
100
|
+
* // 使用完毕后清理
|
|
101
|
+
* manager.cleanup();
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* @example 自定义配置
|
|
105
|
+
* ```typescript
|
|
106
|
+
* const manager = new MediaStreamManager({
|
|
107
|
+
* audioFeatures: {
|
|
108
|
+
* echoCancellation: true,
|
|
109
|
+
* noiseSuppression: true,
|
|
110
|
+
* autoGainControl: false, // 禁用自动增益
|
|
111
|
+
* sampleRate: 48000, // 高采样率
|
|
112
|
+
* channelCount: 1 // 单声道
|
|
113
|
+
* },
|
|
114
|
+
* warnOnUnsupportedFeatures: true
|
|
115
|
+
* });
|
|
116
|
+
*
|
|
117
|
+
* const result = await manager.getUserMedia();
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export declare class MediaStreamManager {
|
|
121
|
+
/**
|
|
122
|
+
* 默认音频特性配置(三大核心特性)
|
|
123
|
+
*/
|
|
124
|
+
private static readonly DEFAULT_AUDIO_FEATURES;
|
|
125
|
+
/**
|
|
126
|
+
* 当前持有的 MediaStream 实例
|
|
127
|
+
*/
|
|
128
|
+
private mediaStream;
|
|
129
|
+
/**
|
|
130
|
+
* 管理器配置选项
|
|
131
|
+
*/
|
|
132
|
+
private readonly options;
|
|
133
|
+
/**
|
|
134
|
+
* 构造函数
|
|
135
|
+
*
|
|
136
|
+
* @param options - 配置选项
|
|
137
|
+
*/
|
|
138
|
+
constructor(options?: IMediaStreamManagerOptions);
|
|
139
|
+
/**
|
|
140
|
+
* 检测浏览器支持的音频约束特性
|
|
141
|
+
*
|
|
142
|
+
* @returns 特性支持检测结果
|
|
143
|
+
*/
|
|
144
|
+
checkFeatureSupport: () => IFeatureSupportResult;
|
|
145
|
+
/**
|
|
146
|
+
* 获取 MediaStream
|
|
147
|
+
*
|
|
148
|
+
* 核心方法,执行以下流程:
|
|
149
|
+
* 1. 检测浏览器特性支持
|
|
150
|
+
* 2. 构建动态约束对象
|
|
151
|
+
* 3. 降级处理(如需要)
|
|
152
|
+
* 4. 调用 getUserMedia 获取流
|
|
153
|
+
* 5. 记录日志
|
|
154
|
+
*
|
|
155
|
+
* @returns MediaStream 获取结果
|
|
156
|
+
* @throws Error - 获取失败时抛出原始错误(包含权限、设备等)
|
|
157
|
+
*/
|
|
158
|
+
getUserMedia: () => Promise<IMediaStreamResult>;
|
|
159
|
+
/**
|
|
160
|
+
* 获取当前持有的 MediaStream
|
|
161
|
+
*
|
|
162
|
+
* @returns 当前的 MediaStream 实例,如果未获取则返回 null
|
|
163
|
+
*/
|
|
164
|
+
getStream: () => MediaStream | null;
|
|
165
|
+
/**
|
|
166
|
+
* 控制音频轨道静音
|
|
167
|
+
*
|
|
168
|
+
* 通过设置 MediaStreamTrack.enabled 实现软静音
|
|
169
|
+
* enabled = false 时,track 生成空帧
|
|
170
|
+
*
|
|
171
|
+
* @param muted - 是否静音
|
|
172
|
+
* @returns this(支持链式调用)
|
|
173
|
+
*/
|
|
174
|
+
mute: (muted: boolean) => MediaStreamManager;
|
|
175
|
+
/**
|
|
176
|
+
* 清理资源
|
|
177
|
+
*
|
|
178
|
+
* 停止所有音频轨道并释放 MediaStream
|
|
179
|
+
* 调用后可以再次调用 getUserMedia 获取新流
|
|
180
|
+
*/
|
|
181
|
+
cleanup: () => void;
|
|
182
|
+
/**
|
|
183
|
+
* 检查当前是否持有有效的 MediaStream
|
|
184
|
+
*
|
|
185
|
+
* @returns 是否持有有效流
|
|
186
|
+
*/
|
|
187
|
+
hasActiveStream: () => boolean;
|
|
188
|
+
/**
|
|
189
|
+
* 构建动态音频约束对象
|
|
190
|
+
*
|
|
191
|
+
* 仅包含浏览器支持的特性
|
|
192
|
+
*
|
|
193
|
+
* @param supportResult - 特性支持检测结果
|
|
194
|
+
* @returns 动态构建的约束对象
|
|
195
|
+
*/
|
|
196
|
+
private readonly buildAudioConstraints;
|
|
197
|
+
/**
|
|
198
|
+
* 记录不支持的特性警告
|
|
199
|
+
*
|
|
200
|
+
* @param unsupportedFeatures - 不支持的特性列表
|
|
201
|
+
*/
|
|
202
|
+
private readonly logUnsupportedFeaturesWarning;
|
|
203
|
+
/**
|
|
204
|
+
* 记录启用的特性
|
|
205
|
+
*
|
|
206
|
+
* @param enabledFeatures - 启用的特性列表
|
|
207
|
+
*/
|
|
208
|
+
private readonly logEnabledFeatures;
|
|
209
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file AudioContext 生命周期管理
|
|
3
|
+
* @description 管理 AudioContext 在移动端的生命周期,处理页面后台/前台切换
|
|
4
|
+
*
|
|
5
|
+
* 移动端问题:
|
|
6
|
+
* - iOS: 页面后台运行或锁屏后,AudioContext 自动 suspend
|
|
7
|
+
* - Android: 部分设备在后台会暂停音频处理
|
|
8
|
+
* - 用户需要手动恢复(需要用户手势)
|
|
9
|
+
*
|
|
10
|
+
* 解决方案:
|
|
11
|
+
* - 监听 visibilitychange 事件
|
|
12
|
+
* - 监听 AudioContext.statechange 事件
|
|
13
|
+
* - 页面前台时自动尝试 resume
|
|
14
|
+
* - 提供回调通知上层应用
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* AudioContext 生命周期选项
|
|
18
|
+
*/
|
|
19
|
+
export interface IAudioContextLifecycleOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Context 被挂起时的回调
|
|
22
|
+
*/
|
|
23
|
+
onSuspended?: () => void;
|
|
24
|
+
/**
|
|
25
|
+
* Context 恢复运行时的回调
|
|
26
|
+
*/
|
|
27
|
+
onResumed?: () => void;
|
|
28
|
+
/**
|
|
29
|
+
* Context 被中断时的回调 (如来电)
|
|
30
|
+
*/
|
|
31
|
+
onInterrupted?: () => void;
|
|
32
|
+
/**
|
|
33
|
+
* 是否自动恢复 (页面前台时)
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
36
|
+
autoResume?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* AudioContext 生命周期管理器
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const context = new AudioContext();
|
|
44
|
+
* const lifecycle = new AudioContextLifecycle(
|
|
45
|
+
* context,
|
|
46
|
+
* {
|
|
47
|
+
* onSuspended: () => console.log('Audio suspended'),
|
|
48
|
+
* onResumed: () => console.log('Audio resumed'),
|
|
49
|
+
* autoResume: true
|
|
50
|
+
* }
|
|
51
|
+
* );
|
|
52
|
+
*
|
|
53
|
+
* lifecycle.startMonitoring();
|
|
54
|
+
*
|
|
55
|
+
* // 手动确保运行 (需要用户手势上下文)
|
|
56
|
+
* await lifecycle.ensureRunning();
|
|
57
|
+
*
|
|
58
|
+
* // 停止监控
|
|
59
|
+
* lifecycle.stopMonitoring();
|
|
60
|
+
* lifecycle.dispose();
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare class AudioContextLifecycle {
|
|
64
|
+
private readonly context;
|
|
65
|
+
private readonly options;
|
|
66
|
+
private isMonitoring;
|
|
67
|
+
private readonly boundHandleVisibilityChange;
|
|
68
|
+
private readonly boundHandleStateChange;
|
|
69
|
+
constructor(context: AudioContext, options?: IAudioContextLifecycleOptions);
|
|
70
|
+
/**
|
|
71
|
+
* 开始监控 AudioContext 生命周期
|
|
72
|
+
*
|
|
73
|
+
* @remarks
|
|
74
|
+
* 监听:
|
|
75
|
+
* - document.visibilitychange (页面前后台切换)
|
|
76
|
+
* - AudioContext.statechange (Context 状态变化)
|
|
77
|
+
* - window.pagehide/pageshow (iOS 特定)
|
|
78
|
+
*/
|
|
79
|
+
startMonitoring: () => void;
|
|
80
|
+
/**
|
|
81
|
+
* 停止监控
|
|
82
|
+
*/
|
|
83
|
+
stopMonitoring: () => void;
|
|
84
|
+
/**
|
|
85
|
+
* 确保 AudioContext 处于运行状态
|
|
86
|
+
*
|
|
87
|
+
* @returns 是否成功恢复到运行状态
|
|
88
|
+
*
|
|
89
|
+
* @remarks
|
|
90
|
+
* - 必须在用户手势上下文中调用 (如 click/touch 事件)
|
|
91
|
+
* - 如果 Context 已关闭,无法恢复,返回 false
|
|
92
|
+
*/
|
|
93
|
+
ensureRunning: () => Promise<boolean>;
|
|
94
|
+
/**
|
|
95
|
+
* 获取当前 AudioContext 状态
|
|
96
|
+
*/
|
|
97
|
+
getState: () => AudioContextState;
|
|
98
|
+
/**
|
|
99
|
+
* 清理资源
|
|
100
|
+
*/
|
|
101
|
+
dispose: () => void;
|
|
102
|
+
/**
|
|
103
|
+
* 处理页面可见性变化
|
|
104
|
+
*/
|
|
105
|
+
private readonly handleVisibilityChange;
|
|
106
|
+
/**
|
|
107
|
+
* 处理 AudioContext 状态变化
|
|
108
|
+
*/
|
|
109
|
+
private readonly handleStateChange;
|
|
110
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file AudioWorklet 能力检测
|
|
3
|
+
* @description 检测浏览器是否支持 AudioWorklet API
|
|
4
|
+
*
|
|
5
|
+
* 浏览器支持情况:
|
|
6
|
+
* - Chrome 66+ ✅
|
|
7
|
+
* - Edge 79+ ✅
|
|
8
|
+
* - Safari 14.1+ ✅
|
|
9
|
+
* - Firefox 76+ ✅
|
|
10
|
+
* - iOS Safari 14.5+ ✅
|
|
11
|
+
* - Android Chrome 66+ ✅
|
|
12
|
+
*
|
|
13
|
+
* 降级场景:
|
|
14
|
+
* - Safari < 14.1 → ScriptProcessorNode
|
|
15
|
+
* - iOS Safari < 14.5 → ScriptProcessorNode
|
|
16
|
+
* - Android WebView (部分) → ScriptProcessorNode
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* AudioWorklet 能力检测结果
|
|
20
|
+
*/
|
|
21
|
+
export interface IAudioWorkletCapability {
|
|
22
|
+
/**
|
|
23
|
+
* 是否支持 AudioWorklet
|
|
24
|
+
*/
|
|
25
|
+
supported: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* 不支持的原因 (仅当 supported=false 时)
|
|
28
|
+
*/
|
|
29
|
+
reason?: 'no-audio-worklet' | 'no-add-module' | 'no-constructor' | 'context-error' | 'test-load-failed';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* AudioWorklet 能力检测工具
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const context = new AudioContext();
|
|
37
|
+
* const capability = await AudioWorkletCapability.check(context);
|
|
38
|
+
*
|
|
39
|
+
* if (capability.supported) {
|
|
40
|
+
* // 使用 AudioWorklet
|
|
41
|
+
* await context.audioWorklet.addModule('processor.js');
|
|
42
|
+
* const node = new AudioWorkletNode(context, 'my-processor');
|
|
43
|
+
* }
|
|
44
|
+
* else {
|
|
45
|
+
* // 降级到 ScriptProcessorNode
|
|
46
|
+
* console.warn('AudioWorklet not supported:', capability.reason);
|
|
47
|
+
* const node = context.createScriptProcessor(2048, 1, 1);
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare class AudioWorkletCapability {
|
|
52
|
+
/**
|
|
53
|
+
* 缓存的检测结果 (避免重复检测)
|
|
54
|
+
*/
|
|
55
|
+
private static cachedResult;
|
|
56
|
+
/**
|
|
57
|
+
* 检测 AudioWorklet 支持情况
|
|
58
|
+
*
|
|
59
|
+
* @param context - AudioContext 实例
|
|
60
|
+
* @returns 检测结果
|
|
61
|
+
*
|
|
62
|
+
* @remarks
|
|
63
|
+
* - 首次调用会执行实际检测并缓存结果
|
|
64
|
+
* - 后续调用直接返回缓存结果
|
|
65
|
+
* - 调用 reset() 可清除缓存
|
|
66
|
+
*/
|
|
67
|
+
static check: (context: AudioContext) => Promise<IAudioWorkletCapability>;
|
|
68
|
+
/**
|
|
69
|
+
* 重置缓存的检测结果
|
|
70
|
+
*
|
|
71
|
+
* @remarks
|
|
72
|
+
* 调用此方法后,下次 check() 会重新执行检测
|
|
73
|
+
* 一般用于测试场景
|
|
74
|
+
*/
|
|
75
|
+
static reset: () => void;
|
|
76
|
+
/**
|
|
77
|
+
* 同步检测 (仅基础属性检查,不执行加载测试)
|
|
78
|
+
*
|
|
79
|
+
* @param context - AudioContext 实例
|
|
80
|
+
* @returns 是否可能支持 AudioWorklet
|
|
81
|
+
*
|
|
82
|
+
* @remarks
|
|
83
|
+
* - 此方法仅检查 API 存在性,不测试实际加载
|
|
84
|
+
* - 返回 true 不保证 100% 可用,建议使用异步的 check()
|
|
85
|
+
* - 用于快速预判,避免不必要的异步等待
|
|
86
|
+
*/
|
|
87
|
+
static checkSync: (context: AudioContext) => boolean;
|
|
88
|
+
/**
|
|
89
|
+
* 执行实际的能力检测
|
|
90
|
+
*
|
|
91
|
+
* @param context - AudioContext 实例
|
|
92
|
+
* @returns 检测结果
|
|
93
|
+
*/
|
|
94
|
+
private static readonly performCheck;
|
|
95
|
+
}
|