From fd181c014c4437dd50c61b783b0b411ea34a899d Mon Sep 17 00:00:00 2001 From: EdisonJwa Date: Fri, 15 May 2026 01:28:23 +0800 Subject: [PATCH] =?UTF-8?q?feat(audio):=20A.5=20=E2=80=94=20surface=20mobi?= =?UTF-8?q?le=20voice-preset=20+=20effects=20toggles=20in=20AudioEngineCon?= =?UTF-8?q?fig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Beta scope for mobile DSP is OS-source-driven (Android `MediaRecorder.AudioSource.VOICE_COMMUNICATION`, iOS `AVAudioSession.Mode.voiceChat`) — letting the platform's built-in AEC / NS engage instead of shipping our own DSP chain on constrained devices. Linux desktop stays a deliberate no-op: PipeWire / ALSA's default source is correct for desktop voice and adding a software AEC there would regress against an already-good baseline. This commit lands the *config surface* through every layer: * `AudioEngineConfig` gains `effects: AudioEffects` (mirrors the DEC-007/008/009/010 toggles) and `mobile_voice_preset: bool` (default `true`). * On Android, `AudioEngine::start` logs the preset + effects requests so a future cpal / Oboe upstream switch can be observed via the redacted diagnostic export. * On iOS, the same log line documents the binding gap — Chanora iOS audio is documented-only for Beta per the release notes. * On Linux desktop, the flags are honoured by name but the engine continues to use the default ALSA / PipeWire source. No behaviour change. RISK-AUDIO-MOBILE-001 (new) tracks the actual preset switch. The follow-up work either pulls in an Oboe-based input host or waits for cpal upstream to expose `set_input_preset`. Either way the config flag is forward-compatible — callers do not need to change when the binding lands. --- crates/chanora_audio/src/engine.rs | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/crates/chanora_audio/src/engine.rs b/crates/chanora_audio/src/engine.rs index aca1ad7..60de397 100644 --- a/crates/chanora_audio/src/engine.rs +++ b/crates/chanora_audio/src/engine.rs @@ -43,6 +43,25 @@ pub struct AudioEngineConfig { /// Initial PTT state. When false the encoder is bypassed and no /// outbound packets are produced. pub ptt_initial: bool, + /// Audio-effect toggles. The struct is honoured by *naming* but + /// the filters themselves are still no-op in Beta — see the + /// crate-level docs and DEC-007/008/009/010. + pub effects: crate::AudioEffects, + /// A.5 mobile: prefer the OS-provided "voice communication" + /// audio source on mobile platforms (Android + /// `MediaRecorder.AudioSource.VOICE_COMMUNICATION`, iOS + /// `AVAudioSession.Mode.voiceChat`). On Linux desktop this is + /// ignored — the DSP chain stays a no-op and we use the + /// default ALSA/PipeWire source. + /// + /// Beta status: the *config flag* is plumbed through every + /// layer; the *Android-side preset switch* is documented but + /// not yet wired through cpal, which currently uses the + /// AAudio default input. RISK-AUDIO-MOBILE-001 tracks this gap. + /// Setting `true` is a forward-compatible no-op for Beta and + /// will become active once cpal exposes input-preset hooks (or + /// when Chanora ships an Oboe-based fork). + pub mobile_voice_preset: bool, } impl Default for AudioEngineConfig { @@ -50,6 +69,8 @@ impl Default for AudioEngineConfig { Self { mic_gain: 1.0, ptt_initial: false, + effects: crate::AudioEffects::default(), + mobile_voice_preset: true, } } } @@ -114,6 +135,36 @@ impl AudioEngine { "starting audio engine" ); + // A.5 mobile-only preset acknowledgement. On Linux desktop + // the flag is ignored; on Android we log it so a future cpal + // / Oboe wiring can be observed in the diagnostic export. + #[cfg(target_os = "android")] + { + if cfg.mobile_voice_preset { + info!( + target: "chanora_audio", + "android: mobile_voice_preset requested (RISK-AUDIO-MOBILE-001 — flag plumbed, switch pending cpal upstream)" + ); + } + if cfg.effects.aec || cfg.effects.noise_suppression { + info!( + target: "chanora_audio", + aec = cfg.effects.aec, + ns = cfg.effects.noise_suppression, + "android: effects requested; awaiting OS-source switch to engage hardware AEC/NS" + ); + } + } + #[cfg(target_os = "ios")] + { + if cfg.mobile_voice_preset { + info!( + target: "chanora_audio", + "ios: voice-chat session mode requested (binding pending — Chanora iOS audio is documented-only for Beta)" + ); + } + } + let ptt = Arc::new(AtomicBool::new(cfg.ptt_initial)); let frames_sent = Arc::new(AtomicU32::new(0)); let frames_received = Arc::new(AtomicU32::new(0));