Skip to content

Android: platform AEC doesn't cancel app's own playback unless the OUTPUT stream uses Usage::VoiceCommunication #1254

Description

@arekrgw

Feature Request

When building a full-duplex voice assistant (mic capture + TTS playback at the same
time), enabling androidInputPreset: 'voiceCommunication' on the AudioRecorder alone
was not enough to get working acoustic echo cancellation on Android. Our STT kept
transcribing the assistant's own TTS playback.

The missing piece: the output stream (AudioPlayer.cpp) is opened without any
Usage, which defaults to USAGE_MEDIA. On many devices, the platform AEC chain
engaged by the VOICE_COMMUNICATION input preset only references the
voice-communication output path and media-stream playback leaks into the microphone
uncancelled.

Adding two lines to the output stream builder fixed it completely:

// AudioPlayer.cpp — openAudioStream()
builder.setSharingMode(SharingMode::Exclusive)
    ->setFormat(AudioFormat::Float)
    // ...existing config...
    ->setUsage(Usage::VoiceCommunication)   // ← added
    ->setContentType(ContentType::Speech)  // ← added
    // ...

Environment

  • react-native-audio-api 1.0.0-nightly-f8b57cd-20260825
  • React Native 0.86.2 / Expo SDK 57, physical Android device (loudspeaker, no headphones)
  • Capture: AudioRecorder with { androidInputPreset: 'voiceCommunication', iosVoiceProcessing: true }
  • Playback: AudioContext playing TTS PCM buffers
  • Use case: streaming STT (websocket ASR) while speaking TTS replies (barge-in style interaction)

Symptom before the patch

  • Speech-to-text transcribed the assistant's own spoken replies as if they were user input
  • Token-overlap heuristics and LLM-based intent filtering reduced but never eliminated
    the self-echo turns
  • Verified the input preset reaches Oboe correctly (setInputPreset(VoiceCommunication)
    present in openAudioStream()), so the gap was on the output/reference side

After the patch

  • Self-echo eliminated at DSP level (zero phantom transcripts during playback)
  • Real barge-in (user talking over the reply) works reliably
  • No audio-routing regressions on our test device: playback still comes out of the
    loudspeaker as before

Suggestions

  1. Library behavior: consider applying Usage::VoiceCommunication (+ ContentType::Speech)
    to the output stream automatically when an active recorder uses
    androidInputPreset: 'voiceCommunication' or expose an option for it.
  2. Docs (next → AudioRecorder): the full-duplex section recommends
    androidInputPreset: 'voiceCommunication' + iosVoiceProcessing: true, but doesn't
    mention that the playback side must also sit on the voice-communication path for
    Android AEC to reference it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    androidNative Android implementation, C++/Java/Kotlin bindings, or Android-specific issuesbugSomething isn't working as expected or produces unexpected errors

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions