using System; using System.Runtime.InteropServices; public static class WebRTCAPMWrapper { #if UNITY_IOS private const string LibraryName = "__Internal"; #elif UNITY_ANDROID && !UNITY_EDITOR private const string LibraryName = "libwebrtc_apm"; #else private const string LibraryName = "libwebrtc_apm"; #endif [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr WebRTC_APM_Create(); [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] public static extern void WebRTC_APM_Destroy(IntPtr handle); [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr WebRTC_APM_CreateStreamConfig(int sampleRate, int numChannels); [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr WebRTC_APM_DestroyStreamConfig(IntPtr streamConfig); [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] public static extern int WebRTC_APM_ApplyConfig(IntPtr handle, ref Config config); [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] public static extern int WebRTC_APM_ProcessReverseStream(IntPtr handle, ref short src, IntPtr srcConfig, IntPtr destConfig, ref short dest); [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] public static extern int WebRTC_APM_ProcessStream(IntPtr handle, ref short src, IntPtr srcConfig, IntPtr destConfig, ref short dest); [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] public static extern void WebRTC_APM_SetStreamDelayMs(IntPtr handle, int delayMs); [StructLayout(LayoutKind.Sequential)] public struct Config { /// /// Sets the properties of the audio processing pipeline. /// [StructLayout(LayoutKind.Sequential)] public struct Pipeline { /// /// Ways to downmix a multi-channel track to mono. /// public enum DownmixMethod { /// Average across channels. AverageChannels, /// Use the first channel. UseFirstChannel } /// /// Maximum allowed processing rate used internally. May only be set to /// 32000 or 48000 and any differing values will be treated as 48000. /// public int MaximumInternalProcessingRate; /// Allow multi-channel processing of render audio. [MarshalAs(UnmanagedType.I1)] public bool MultiChannelRender; /// /// Allow multi-channel processing of capture audio when AEC3 is active /// or a custom AEC is injected. /// [MarshalAs(UnmanagedType.I1)] public bool MultiChannelCapture; /// /// Indicates how to downmix multi-channel capture audio to mono (when needed). /// public DownmixMethod CaptureDownmixMethod; } /// /// Enabled the pre-amplifier. It amplifies the capture signal /// before any other processing is done. /// [StructLayout(LayoutKind.Sequential)] public struct PreAmplifier { [MarshalAs(UnmanagedType.I1)] public bool Enabled; public float FixedGainFactor; } /// /// Functionality for general level adjustment in the capture pipeline. /// [StructLayout(LayoutKind.Sequential)] public struct CaptureLevelAdjustment { [MarshalAs(UnmanagedType.I1)] public bool Enabled; /// The pre_gain_factor scales the signal before any processing is done. public float PreGainFactor; /// The post_gain_factor scales the signal after all processing is done. public float PostGainFactor; [StructLayout(LayoutKind.Sequential)] public struct AnalogMicGainEmulation { [MarshalAs(UnmanagedType.I1)] public bool Enabled; /// /// Initial analog gain level to use for the emulated analog gain. /// Must be in the range [0...255]. /// public int InitialLevel; } public AnalogMicGainEmulation MicGainEmulation; } [StructLayout(LayoutKind.Sequential)] public struct HighPassFilter { [MarshalAs(UnmanagedType.I1)] public bool Enabled; [MarshalAs(UnmanagedType.I1)] public bool ApplyInFullBand; } [StructLayout(LayoutKind.Sequential)] public struct EchoCanceller { [MarshalAs(UnmanagedType.I1)] public bool Enabled; [MarshalAs(UnmanagedType.I1)] public bool MobileMode; [MarshalAs(UnmanagedType.I1)] public bool ExportLinearAecOutput; /// /// Enforce the highpass filter to be on (has no effect for the mobile mode). /// [MarshalAs(UnmanagedType.I1)] public bool EnforceHighPassFiltering; } /// Enables background noise suppression. [StructLayout(LayoutKind.Sequential)] public struct NoiseSuppression { [MarshalAs(UnmanagedType.I1)] public bool Enabled; public enum Level { Low, Moderate, High, VeryHigh } public Level NoiseLevel; [MarshalAs(UnmanagedType.I1)] public bool AnalyzeLinearAecOutputWhenAvailable; } /// Enables transient suppression. [StructLayout(LayoutKind.Sequential)] public struct TransientSuppression { [MarshalAs(UnmanagedType.I1)] public bool Enabled; } /// /// Enables automatic gain control (AGC) functionality. /// The automatic gain control (AGC) component brings the signal to an /// appropriate range. This is done by applying a digital gain directly and, /// in the analog mode, prescribing an analog gain to be applied at the audio HAL. /// [StructLayout(LayoutKind.Sequential)] public struct GainController1 { [MarshalAs(UnmanagedType.I1)] public bool Enabled; public enum Mode { /// /// Adaptive mode intended for use if an analog volume control is available /// on the capture device. /// AdaptiveAnalog, /// /// Adaptive mode intended for situations in which an analog volume control /// is unavailable. /// AdaptiveDigital, /// /// Fixed mode which enables only the digital compression stage. /// FixedDigital } public Mode ControllerMode; /// /// Sets the target peak level (or envelope) of the AGC in dBFs (decibels /// from digital full-scale). Limited to [0, 31]. /// public int TargetLevelDbfs; /// /// Sets the maximum gain the digital compression stage may apply, in dB. /// Limited to [0, 90]. /// public int CompressionGainDb; /// /// When enabled, the compression stage will hard limit the signal to the /// target level. /// [MarshalAs(UnmanagedType.I1)] public bool EnableLimiter; [StructLayout(LayoutKind.Sequential)] public struct AnalogGainController { [MarshalAs(UnmanagedType.I1)] public bool Enabled; public int StartupMinVolume; /// /// Lowest analog microphone level that will be applied in response to clipping. /// public int ClippedLevelMin; /// If true, an adaptive digital gain is applied. [MarshalAs(UnmanagedType.I1)] public bool EnableDigitalAdaptive; /// /// Amount the microphone level is lowered with every clipping event. /// Limited to (0, 255]. /// public int ClippedLevelStep; /// /// Proportion of clipped samples required to declare a clipping event. /// Limited to (0.f, 1.f). /// public float ClippedRatioThreshold; /// /// Time in frames to wait after a clipping event before checking again. /// Limited to values higher than 0. /// public int ClippedWaitFrames; [StructLayout(LayoutKind.Sequential)] public struct ClippingPredictor { [MarshalAs(UnmanagedType.I1)] public bool Enabled; public enum Mode { /// Clipping event prediction mode with fixed step estimation. ClippingEventPrediction, /// Clipped peak estimation mode with adaptive step estimation. AdaptiveStepClippingPeakPrediction, /// Clipped peak estimation mode with fixed step estimation. FixedStepClippingPeakPrediction } public Mode PredictorMode; /// Number of frames in the sliding analysis window. public int WindowLength; /// Number of frames in the sliding reference window. public int ReferenceWindowLength; /// Reference window delay (unit: number of frames). public int ReferenceWindowDelay; /// Clipping prediction threshold (dBFS). public float ClippingThreshold; /// Crest factor drop threshold (dB). public float CrestFactorMargin; /// /// If true, the recommended clipped level step is used to modify the analog gain. /// Otherwise, the predictor runs without affecting the analog gain. /// [MarshalAs(UnmanagedType.I1)] public bool UsePredictedStep; } public ClippingPredictor Predictor; } public AnalogGainController AnalogController; } /// /// Parameters for AGC2, which brings the captured audio signal to the desired level by /// combining three different controllers and a limiter. /// [StructLayout(LayoutKind.Sequential)] public struct GainController2 { [MarshalAs(UnmanagedType.I1)] public bool Enabled; /// /// Parameters for the input volume controller, which adjusts the input volume /// applied when the audio is captured. /// [StructLayout(LayoutKind.Sequential)] public struct InputVolumeController { [MarshalAs(UnmanagedType.I1)] public bool Enabled; } /// /// Parameters for the adaptive digital controller, which adjusts and applies /// a digital gain after echo cancellation and noise suppression. /// [StructLayout(LayoutKind.Sequential)] public struct AdaptiveDigital { [MarshalAs(UnmanagedType.I1)] public bool Enabled; public float HeadroomDb; public float MaxGainDb; public float InitialGainDb; public float MaxGainChangeDbPerSecond; public float MaxOutputNoiseLevelDbfs; } /// /// Parameters for the fixed digital controller, which applies a fixed digital /// gain after the adaptive digital controller and before the limiter. /// [StructLayout(LayoutKind.Sequential)] public struct FixedDigital { /// /// By setting gain_db to a value greater than zero, the limiter can be /// turned into a compressor that first applies a fixed gain. /// public float GainDb; } public InputVolumeController VolumeController; public AdaptiveDigital AdaptiveController; public FixedDigital FixedController; } public Pipeline PipelineConfig; public PreAmplifier PreAmp; public CaptureLevelAdjustment LevelAdjustment; public HighPassFilter HighPass; public EchoCanceller Echo; public NoiseSuppression NoiseSuppress; public TransientSuppression TransientSuppress; public GainController1 GainControl1; public GainController2 GainControl2; /// /// Creates a new Config instance with default settings. /// /// A Config instance initialized with default values. public static Config Build() { return new Config { PipelineConfig = new Pipeline { MaximumInternalProcessingRate = 48000, MultiChannelRender = false, MultiChannelCapture = false, CaptureDownmixMethod = Pipeline.DownmixMethod.AverageChannels }, PreAmp = new PreAmplifier { Enabled = false, FixedGainFactor = 1.0f }, LevelAdjustment = new CaptureLevelAdjustment { Enabled = false, PreGainFactor = 1.0f, PostGainFactor = 1.0f, MicGainEmulation = new CaptureLevelAdjustment.AnalogMicGainEmulation { Enabled = false, InitialLevel = 255 } }, HighPass = new HighPassFilter { Enabled = false, ApplyInFullBand = true }, Echo = new EchoCanceller { Enabled = false, MobileMode = false, ExportLinearAecOutput = false, EnforceHighPassFiltering = true }, NoiseSuppress = new NoiseSuppression { Enabled = false, NoiseLevel = NoiseSuppression.Level.Moderate, AnalyzeLinearAecOutputWhenAvailable = false }, TransientSuppress = new TransientSuppression { Enabled = false }, GainControl1 = new GainController1 { Enabled = false, ControllerMode = GainController1.Mode.AdaptiveAnalog, TargetLevelDbfs = 3, CompressionGainDb = 9, EnableLimiter = true, AnalogController = new GainController1.AnalogGainController { Enabled = true, StartupMinVolume = 0, ClippedLevelMin = 70, EnableDigitalAdaptive = true, ClippedLevelStep = 15, ClippedRatioThreshold = 0.1f, ClippedWaitFrames = 300, Predictor = new GainController1.AnalogGainController.ClippingPredictor { Enabled = false, PredictorMode = GainController1.AnalogGainController.ClippingPredictor.Mode .ClippingEventPrediction, WindowLength = 5, ReferenceWindowLength = 5, ReferenceWindowDelay = 5, ClippingThreshold = -1.0f, CrestFactorMargin = 3.0f, UsePredictedStep = true } } }, GainControl2 = new GainController2 { Enabled = false, VolumeController = new GainController2.InputVolumeController { Enabled = false }, AdaptiveController = new GainController2.AdaptiveDigital { Enabled = false, HeadroomDb = 5.0f, MaxGainDb = 50.0f, InitialGainDb = 15.0f, MaxGainChangeDbPerSecond = 6.0f, MaxOutputNoiseLevelDbfs = -50.0f }, FixedController = new GainController2.FixedDigital { GainDb = 0.0f } } }; } } }