feat(audio): A.5 — surface mobile voice-preset + effects toggles in AudioEngineConfig

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.
This commit is contained in:
EdisonJwa
2026-05-15 01:28:23 +08:00
parent 43a3c9ba76
commit fd181c014c
+51
View File
@@ -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));