-
Notifications
You must be signed in to change notification settings - Fork 57
Expand file tree
/
Copy pathAudioProcessingModule.cs
More file actions
135 lines (125 loc) · 5.95 KB
/
AudioProcessingModule.cs
File metadata and controls
135 lines (125 loc) · 5.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
using LiveKit.Proto;
using LiveKit.Internal.FFIClients.Requests;
using LiveKit.Internal;
using System;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Tests")]
namespace LiveKit
{
/// <summary>
/// Provides WebRTC audio processing capabilities including echo cancellation, noise suppression,
/// high-pass filtering, and gain control.
/// </summary>
public sealed class AudioProcessingModule
{
internal readonly FfiHandle Handle;
/// <summary>
/// Initializes an <see cref="AudioProcessingModule" /> instance with the specified audio processing features.
/// </summary>
/// <param name="echoCancellationEnabled">Whether to enable echo cancellation.</param>
/// <param name="noiseSuppressionEnabled">Whether to enable noise suppression.</param>
/// <param name="highPassFilterEnabled">Whether to enable high-pass filtering.</param>
/// <param name="gainControllerEnabled">Whether to enable gain control.</param>
public AudioProcessingModule(
bool echoCancellationEnabled,
bool noiseSuppressionEnabled,
bool highPassFilterEnabled,
bool gainControllerEnabled)
{
using var request = FFIBridge.Instance.NewRequest<NewApmRequest>();
var newApm = request.request;
newApm.EchoCancellerEnabled = echoCancellationEnabled;
newApm.NoiseSuppressionEnabled = noiseSuppressionEnabled;
newApm.HighPassFilterEnabled = highPassFilterEnabled;
newApm.GainControllerEnabled = gainControllerEnabled;
using var response = request.Send();
FfiResponse res = response;
Handle = FfiHandle.FromOwnedHandle(res.NewApm.Apm.Handle);
}
/// <summary>
/// Process the provided audio frame using the configured audio processing features.
/// </summary>
/// <param name="data">The audio frame to process.</param>
/// <remarks>
/// Important: Audio frames must be exactly 10 ms in duration.
///
/// The input audio frame is modified in-place (if applicable) by the underlying audio
/// processing module (e.g., echo cancellation, noise suppression, etc.).
/// </remarks>
public void ProcessStream(AudioFrame data)
{
using var request = FFIBridge.Instance.NewRequest<ApmProcessStreamRequest>();
var processStream = request.request;
processStream.ApmHandle = (ulong)Handle.DangerousGetHandle();
processStream.DataPtr = (ulong)data.Data;
processStream.Size = (uint)data.Length;
processStream.SampleRate = data.SampleRate;
processStream.NumChannels = data.NumChannels;
using var response = request.Send();
FfiResponse res = response;
if (res.ApmProcessStream.HasError)
{
throw new Exception(res.ApmProcessStream.Error);
}
}
/// <summary>
/// Process the reverse audio frame (typically used for echo cancellation in a full-duplex setup).
/// </summary>
/// <param name="data">The audio frame to process.</param>
/// <remarks>
/// Important: Audio frames must be exactly 10 ms in duration.
///
/// In an echo cancellation scenario, this method is used to process the "far-end" audio
/// prior to mixing or feeding it into the echo canceller. Like <see cref="ProcessStream"/>, the
/// input audio frame is modified in-place by the underlying processing module.
/// </remarks>
public void ProcessReverseStream(AudioFrame data)
{
using var request = FFIBridge.Instance.NewRequest<ApmProcessReverseStreamRequest>();
var processReverseStream = request.request;
processReverseStream.ApmHandle = (ulong)Handle.DangerousGetHandle();
processReverseStream.DataPtr = (ulong)data.Data;
processReverseStream.Size = (uint)data.Length;
processReverseStream.SampleRate = data.SampleRate;
processReverseStream.NumChannels = data.NumChannels;
using var response = request.Send();
FfiResponse res = response;
if (res.ApmProcessReverseStream.HasError)
{
throw new Exception(res.ApmProcessReverseStream.Error);
}
}
/// <summary>
/// This must be called if and only if echo processing is enabled.
/// </summary>
/// <remarks>
/// Sets the `delay` in milliseconds between receiving a far-end frame in <see cref="ProcessReverseStream"/>
/// and receiving the corresponding echo in a near-end frame in <see cref="ProcessStream"/>.
///
/// The delay can be calculated as: delay = (t_render - t_analyze) + (t_process - t_capture)
///
/// Where:
/// - t_analyze: Time when frame is passed to <see cref="ProcessReverseStream"/>
/// - t_render: Time when first sample of frame is rendered by audio hardware
/// - t_capture: Time when first sample of frame is captured by audio hardware
/// - t_process: Time when frame is passed to <see cref="ProcessStream"/>
/// </remarks>
public void SetStreamDelayMs(int delayMs)
{
using var request = FFIBridge.Instance.NewRequest<ApmSetStreamDelayRequest>();
var setStreamDelay = request.request;
setStreamDelay.ApmHandle = (ulong)Handle.DangerousGetHandle();
setStreamDelay.DelayMs = delayMs;
using var response = request.Send();
FfiResponse res = response;
if (res.ApmSetStreamDelay.HasError)
{
throw new Exception(res.ApmSetStreamDelay.Error);
}
}
/// <summary>
/// The required duration for audio frames being processed.
/// </summary>
public const uint FRAME_DURATION_MS = 10;
}
}