Skip to content

AEC in Unity Audio - #381

Draft
MaxHeimbrock wants to merge 3 commits into
mainfrom
max/meet-unity-aec
Draft

AEC in Unity Audio#381
MaxHeimbrock wants to merge 3 commits into
mainfrom
max/meet-unity-aec

Conversation

@MaxHeimbrock

@MaxHeimbrock MaxHeimbrock commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Background

Unity's Microphone capture bypasses the platform audio device module, so MicrophoneSource had no echo cancellation: everything the game played came back through the mic. Only PlatformAudioSource had AEC.

Changes

  • MicrophoneSource (and any RtcAudioSource subclass) accepts AudioProcessingOptions. The capture then runs through libwebrtc's AudioProcessingModule (AEC3, NS, AGC, HPF) before CaptureAudioFrame. If the module cannot be created, the source warns and publishes unprocessed.
  • New PlayoutReference component: attached by the SDK to the active AudioListener, taps the final mix in OnAudioFilterRead and feeds it to the APM reverse stream. Re-attaches after scene loads and AudioSettings.OnAudioConfigurationChanged.
  • Internal AudioProcessor re-chunks capture and reference into 10 ms int16 frames on the Unity audio thread (the Rust side asserts on any other size). Output rates that are not a multiple of 100 Hz bypass processing with a warning.
  • Stream delay hint seeded from DSP block size plus device latency, refreshed every 2 s. iOS reads AVAudioSession latencies via three new exported functions in LiveKitAudioSession.mm.
  • FFI routing for NewApm, ApmProcessStream, ApmProcessReverseStream, ApmSetStreamDelay (protos already present).
  • New files live in Runtime/Scripts/Audio/Processing/.
  • Meet sample: AEC/NS/AGC toggles now apply in Unity-audio mode; new remoteAudioGain (0.7) on remote AudioSources; periodic AudioProcessingStats log. Custom editor updated.
  • Tests: EditMode for PcmRingBuffer, PcmConvert, rate/frame helpers, delay hint; PlayMode for 10 ms chunking end to end and for cancelling a delayed echo of the playout.
  • README: "Unity Audio Processing" section.

API surface

Type Member Change
AudioProcessingOptions bool HighPassFilter Added. Default sets true. Unity-audio sources only; PlatformAudioSource ignores it.
AudioProcessingOptions bool PreferHardware Unchanged. Documented as PlatformAudioSource only.
MicrophoneSource MicrophoneSource(string, GameObject, AudioProcessingOptions) Added constructor.
RtcAudioSource protected RtcAudioSource(RtcAudioSourceType, AudioProcessingOptions) Added constructor.
RtcAudioSource protected RtcAudioSource(RtcAudioSourceType, uint, uint, AudioProcessingOptions?) Added constructor.
RtcAudioSource bool AudioProcessingEnabled { get; } Added.
RtcAudioSource AudioProcessingStats AudioProcessingStats { get; } Added.
RtcAudioSource protected void ResetAudioProcessing() Added.
AudioProcessingModule class New. FFI APM wrapper: ctor (4 bools), ChunkSizeMs, Handle, IsNativeSampleRate, IsSupportedApiRate, FrameSizeFor, ProcessStream, ProcessReverseStream, SetStreamDelayMs, Dispose.
AudioProcessingStats readonly struct New. Counter snapshot from RtcAudioSource.AudioProcessingStats.
PlayoutReference MonoBehaviour New component (LiveKit/Playout Reference). No public members; hand-placing it on the AudioListener is supported.

Nothing removed or changed in signature. AudioProcessingOptions is a struct gaining a field: default(AudioProcessingOptions) has HighPassFilter == false, AudioProcessingOptions.Default has it true.

@MaxHeimbrock MaxHeimbrock changed the title The first version is already working in Meet on MacOS AEC in Unity Audio Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant