Repository navigation
Conversation
Add bounded render-reference processing for desktop dictation, preserve playback without muting, expose a local-only best-effort setting, and document platform limitations. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Update the standalone recorder module harness to match the production cpal API and expose the shared render-reference buffer used by the recorder. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4 tasks
HKLHaoBin
marked this pull request as draft
October 19, 2026 06:30
Add per-capture structured probes for reference availability, native CPAL errors, first and post-error callbacks, queue backpressure and watchdog faults. Keep microphone error behavior unchanged until the diagnostic build is reproduced on affected hardware. Cover diagnostic counters and error-kind redaction with recorder tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Separate CPAL Xrun notifications from fatal microphone errors after instrumented Windows logs showed data callbacks continuing immediately after each xrun. Keep capture liveness unchanged so the existing watchdog still detects stalled streams, preserve device and invalidated-stream failures, and retain diagnostic probes for hardware verification. Add regression coverage for xrun handling, repeated notifications, real faults and stopped capture. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Keep push and pull request defaults unchanged while allowing manual CI dispatches to select macos-15-intel when hosted ARM runners cannot acquire jobs. Apply the same explicit choice to macOS stable and MSRV checks, preserve all gates, and cover the routing contract in the frontend test suite. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Integrate voice activation and Linux native-format fallback with AEC. Preserve microphone Xrun recovery, diagnostics and regression coverage; combine both sets of local preferences and current dependency versions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
HKLHaoBin
marked this pull request as ready for review
October 21, 2026 13:39
Use the current sample-rate alias and pass StreamConfig by value. Include the native cue module in the recorder harness so its CPAL calls are compiled by the standalone remote checks. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
appergb
reviewed
Oct 21, 2026
appergb
left a comment
Collaborator
There was a problem hiding this comment.
自动只读审查结论:建议暂缓合并(FAIL)
- PR:#1228
feat(audio): add optional playback echo cancellation - 审查基线:base
beta@43df23be3259…/ head07e3bc6a5284…(merge-base = base;当前 PR head 未变) - 范围:全部 36 个变更文件(+1599/−179)+ 必要调用方;只读审查,未修改/提交代码、未合并、未包含密钥或完整 patch
- 结论:FAIL —— 7 个 P2、12 个 P3,无 P0/P1。契约、默认关闭、serde 兼容、云同步排除、参考不可用回退与既有测试基线是健康的;问题集中在 AEC 主路径的正确性、降级可观测性和平台能力判断。
已执行并通过的验证(本机只读执行):
cargo test --locked -p openless-core --lib audio→ exit 0,76 passed(含 5 个新增 AEC 测试)cargo test --locked -p openless-core --lib→ exit 0,1355 passed / 3 ignoredpython3 scripts/test-recorder-module.py(编译生产recorder.rs+native_audio_cue.rs+ coreaudio.rs)→ exit 0,63 passed(含 7 个新增 recorder 测试)node --test scripts/ci-macos-runner-contract.test.mjs→ 1 passed;前端tsc --noEmit→ exit 0;mock-data 测试 1 passed- UI(Orca 内置浏览器,本地 dev/mock):桌面 zh-CN「设置 → 录音与输入」显示「播放回声抑制」,默认关闭、点击后开启;未证明真实音频/原生链路
CI 观测(评论时):大部分 job SUCCESS,
macOS checks仍在进行,native-capsule (macos-latest)为 CANCELLED,mergeStateStatus=BLOCKED、reviewDecision=REVIEW_REQUIRED;CI 未全部完成,未作为通过依据。
P2(建议合并前处理)
P2-A|macOS 双工输出端点可能把“麦克风”当播放参考
- 位置:
openless-all/app/src-tauri/src/recorder.rsstart_reference_stream(约 2535-2561);cpal 0.18.2 CoreAudio 设备路径 - 触发:macOS 默认输出设备同时
supports_input()(USB 耳麦、音频接口、虚拟声卡等)且开启 AEC - 预期 vs 实际:预期参考流=系统播放 loopback;实际 cpal 对该设备按普通 Input 建流,参考信号=近端麦克风,且
reference_available=true仍会误报可用 - 影响:AEC 可能抵消用户自己的声音;macOS 主推路径在常见双工设备上错误
- 证据级别:static(cpal 0.18.2 源码行号可核);introduced
- 最小修法:只接受无输入通道的 render endpoint 或显式 tap;否则返回 capability=unavailable,而不是静默把麦克风当参考
P2-B|macOS 缺少系统音频捕获权限与系统版本门禁
- 位置:
openless-all/app/src-tauri/Info.plist、tauri.conf.json、src-tauri/src/permissions.rs、recorder.rsstart_reference_stream - 预期 vs 实际:应具备
NSAudioCaptureUsageDescription、系统音频捕获权限探测与版本能力判断;实际三者均缺失,最低系统声明 14.0 而 cpal loopback 需要 macOS >14.6。失败时仅log::warn回退,UI 偏好仍显示开启 - 影响:macOS 主路径可能长期静默无 AEC;14.0–14.5 首次调用的实际行为未验证
- 证据级别:static(拒绝形态为 hypothesis);introduced
- 最小修法:补 purpose string、权限探测、系统版本门禁;旧系统/未授权时 capability=unavailable 并给出原因
P2-C|Linux PulseAudio monitor 依赖本地化 description 匹配
- 位置:
openless-all/app/linux-egui/src/audio.rsselect_playback_monitor_device(约 570-606);cpal PulseAudio 后端返回服务器本地化info.description - 触发:非英文 locale 的 PulseAudio,或 monitor source 描述不含英文
monitor - 预期 vs 实际:预期按 source 名称(
*.monitor)/设备类型选源;实际用device.description()子串匹配"monitor",zh/ja/ko 环境匹配失败 → Unsupported → 静默无 AEC(PipeWiredefault_sink英文路径不受影响) - 影响:主要非英文用户群 AEC 静默失效且无提示
- 证据级别:static;introduced
- 最小修法:优先匹配
info.name后缀/设备 id,description 仅 fallback;失败写 capability/诊断
P2-D|参考样本按 ceil 多取,非整数时长组合会周期性/长期欠载并复位
- 位置:
openless-all/app/crates/openless-core/src/audio.rsReferenceAudioBuffer::take_for_input(74-88)与process_with_reference的回退复位(约 447-472) - 实际:
needed = ceil(input_frames*ref_rate/input_rate)每次会多取小数样本;队列一旦len < needed立即清空整队,随后 canceller reset 并整段走旧路径。部分采样率/回调帧组合(如监听 48k + 麦克风 44.1k/512 帧)可反复触发 - 影响:开关打开但 AEC 间歇或长期不工作(录音本身不丢)
- 证据级别:static(算术确定,具体设备组合未真机复现);introduced
- 最小修法:累积小数亏欠或四舍五入;欠载时仅 bypass、不清空整队;补采样率不匹配单测
P2-E|参考 FIFO 无时间戳/延迟对齐,滤波器跨度不足,溢出后滞后永久化
- 位置:
openless-all/app/crates/openless-core/src/audio.rs(参考缓冲,约 9、32-98、295)与src-tauri/src/recorder.rs(参考流启动,约 1926-1961) - 实际:参考 FIFO 无时间戳、无延迟估计、无漂移补偿;滤波器仅 1024 taps=64ms,覆盖不了常见/蓝牙链路延迟;2s 缓冲溢出时静默丢最旧且滞后不会自愈。PR 自身的可行性文档也要求 timestamps/delay/bounded drift
- 影响:蓝牙/高延迟/异速率场景 AEC 基本无效,且无 under/overflow 诊断
- 证据级别:static(设备延迟 hypothesis);introduced
- 最小修法:采集两路时间戳/延迟并按其对齐;启动时修剪 backlog;溢出时重置并追最新;补 under/overflow 计数
P2-F|单个非有限 mic 样本可污染 NLMS 系数,导致整段录音静默
- 位置:
openless-all/app/crates/openless-core/src/audio.rs(EchoCanceller::process约 331-375、process_with_reference约 452-457) - 路径:mic 重采样结果未清洗 non-finite(参考侧有清洗)→ 初始收敛期
estimate.abs()<0.01允许自适应 →error=NaN写入coefficients(f32::clamp保留 NaN)→ 此后每样本error.is_finite()失败输出 0.0 → 整段 PCM 全 0,而 UI 仍显示录音 - 影响:正是验收禁止的“静默静音”;只有 reference 缺口触发 reset 才能恢复
- 证据级别:代码路径 verified(算术与 NaN clamp 行为已核),触发条件 hypothesis(取决于设备是否输出非有限样本);introduced
- 最小修法:清洗 mic 重采样结果的 non-finite;
error非有限时复位系数;补 NaN 注入测试
P2-G|AEC 能力/可用性没有产品信号,UI 用“桌面壳”当能力,失败静默降级
- 位置:
openless-all/app/src/pages/settings/RecordingInputSection.tsx:81,558;src/lib/types.ts的PlatformCapabilities无 AEC 能力字段;src-tauri/src/recorder.rs失败仅 warn 回退 - 预期 vs 实际:UI 应依据 host capability 显示/禁用并说明原因;实际仅判断
platformCaps?.platform === 'desktop',macOS/Linux-ALSA/蓝牙等不可用路径仍显示“已开启”,免责声明只在 tooltip 且未提 macOS - 影响:用户无法区分真实生效与静默 fallback;与 P2-A/B/C/E 共同构成“开了但没消回声”的产品风险
- 证据级别:static + UI 症状 verified(mock 页);introduced
- 最小修法:core/native 增加
echoCancellationSupport { backend, available, reason },前端据此显示/禁用并给原因
P3(建议修复,不单独阻塞)
- core:AEC 开启时 enhancement 的输入域改变且无测试;mid-session 回退只复位 canceller/重采样器、未复位 enhancer/normalizer;渲染回调
try_lock失败静默丢整块且无计数;双讲检测用瞬时样本能量,过零附近易误判;AEC 主路径缺少集成/异常路径测试(仅直接测EchoCanceller)。 - native:音频回调内做
format!/日志/Vec分配(实时安全风险);参考/麦克风时间戳未利用(见 P2-E);[DEBUG-ISSUE1223]诊断探针留在生产路径(含 PID/session_id 日志)。 - frontend:session-frozen 生效时机未在文案中告知;AEC 与「录音时静音」并存没有引导(语义待产品决策,非 bug);i18n 中文「回声抑制/回声消除」混用、法文把 best effort 译成 « expérimentale »;仓库无组件测试基座,本 PR 未补任何前端回归;首帧平台能力未知时该行后插入造成轻微布局跳动。
未测 / 需要真机或环境补齐(不视为通过)
- 真机 AEC:Windows WASAPI loopback、macOS CoreAudio tap/TCC 授权、PipeWire/PulseAudio monitor、ALSA 回退、蓝牙/路由切换、双讲保真、长时漂移、CPU/功耗
src-tauri完整cargo test/打包;linux-egui在 Linux 上的编译/运行;移动端 Android/iOS/Harmony;Swift/CoreML- 真实 IPC 持久化与英文/移动/窄屏 UI、刷新后的持久化(本轮 UI 仅 mock/dev、zh-CN 桌面)
建议
合并前至少处理 P2-A、P2-B、P2-C、P2-D、P2-F(或明确将 AEC 标为实验性并把不可用/降级状态显式暴露),P2-E 转为真机验收缺口并补诊断,P2-G 补 capability 通路与可见文案。修复后可再跑同一套只读审查(代码与测试基线这次是干净的)。
说明:以上来自本机只读自动 Review(分模块静态审查 + 指定测试 + mock UI),不含密钥/完整 patch,也未修改或合并代码。若某条与你的设计意图冲突(例如“best effort 允许静默回退”),欢迎指出取舍点,我可以按最新要求复核等级。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Xrunnotifications as recoverable rather than immediately raising Fatal and cancelling usable capture. Other stream errors remain fatal, and the existing no-callback watchdog thresholds/liveness rules are unchanged.beta(43df23be) into the feature branch without rewriting its history, preserving upstream voice activation, Linux microphone format fallback and current CI gates. Both new voice-activation processing configurations now retain the user's AEC preference.Fixes #1223
Why the diagnostic probes are intentionally retained
The original recording failure is intermittent and depends on hardware, drivers and playback conditions. A single tester's short run cannot reliably reproduce it. We are retaining the existing
[DEBUG-ISSUE1223]P1–P5 structured diagnostics while expanding real-device testing and review, rather than removing the evidence needed to distinguish microphone failures from playback-reference glitches:xrun_recoverable.These probes record controlled states/error-kind enums, run-local correlation labels, counters, frame counts and timestamps—not audio samples, transcription text, credentials or provider configuration. Callback diagnostics are emitted only on the first callback or the callback following an error, not for every packet. Existing ordinary application logs are separate and should still be privacy-reviewed before sharing; this statement does not mean the entire application log is safe to publish.
Evidence and limits: The reporter's earlier instrumented log captured five microphone P2 Xruns followed by non-empty microphone callbacks and then cancellation under the old error handling. The targeted fix changes only the Xrun classification. A subsequent local hardware smoke test completed six AEC-enabled recordings through
outcome=Inserted; it observed reference-side P3 Xruns followed by reference callbacks, but no microphone P2 Xrun. This is positive smoke-test evidence, not confirmation that microphone Xrun recovery, all device/driver combinations, long-term stability, or echo-cancellation quality have been validated. Lack of a new issue comment is not treated as proof of resolution.Removal criteria: Retain the probes until broader real-device feedback includes the original failure conditions and the chain microphone P2
xrun_recoverable→ same-capture microphone P5 data → normal dictation completion, with confirmation that the original symptom is resolved. Diagnostic cleanup will be a separate follow-up; user logs will not be automatically deleted or cleared.Validation
07e3bc6a5284124622f7c6131caf4aa41a3d9254(merge commit:2b274ea1).No bugs found.sample_rate.0and a borrowed outputStreamConfig). The complete failed job log was inspected;07e3bc6aadapts those two calls to CPAL 0.18.2 and includes the actual native cue module in the standalone recorder harness. Bugbot review of this patch:No bugs found. No diagnostic probes were removed.hdiutilresource-busy error did not recur on the bounded rerun. No packaging code or safety checks were changed.The job was not acquired by Runner of type hosted even after multiple attempts, with ARM capacity notices). Full available logs and annotations were retained. The bounded rerun is finished; no further automatic rerun is queued, no runner architecture or gate was silently replaced, and an Apple Silicon installer for this commit is not yet available.07e3bc6a5284124622f7c6131caf4aa41a3d9254. Passing PR CI and available installers do not imply that the all-platform artifact workflow is green or that the rare real-hardware microphone P2 recovery has been confirmed.2b274ea1do not verify the newer fix.Testing notes / not covered
The feature is best effort. Device, driver, Bluetooth, protected-content, Linux ALSA, and output-route limitations may cause microphone-only fallback. The render route is bound at session start; changing it requires starting a new dictation session. Please test with real playback and microphone hardware, especially the original reproduction scenario in #1223. Successful insertion events do not by themselves establish transcription accuracy or acoustic echo suppression quality.
When sharing evidence, retain the original log locally and submit only privacy-reviewed diagnostic excerpts. Do not post audio, transcription content, keys or other unrelated personal data.
🤖 Generated with Claude Code