Skip to content

Commit 5569acc

Browse files
committed
Integrate audio buffer
1 parent 624935f commit 5569acc

File tree

3 files changed

+40
-60
lines changed

3 files changed

+40
-60
lines changed

Runtime/Scripts/Internal/FFIClient.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ internal sealed class FfiClient : IFFIClient
4343
// participant events are not allowed in the fii protocol public event ParticipantEventReceivedDelegate ParticipantEventReceived;
4444
public event VideoStreamEventReceivedDelegate? VideoStreamEventReceived;
4545
public event AudioStreamEventReceivedDelegate? AudioStreamEventReceived;
46+
public event CaptureAudioFrameReceivedDelegate? CaptureAudioFrameReceived;
4647

4748
public event PerformRpcReceivedDelegate? PerformRpcReceived;
4849

@@ -275,6 +276,7 @@ static unsafe void FFICallback(UIntPtr data, UIntPtr size)
275276
Instance.AudioStreamEventReceived?.Invoke(r.AudioStreamEvent!);
276277
break;
277278
case FfiEvent.MessageOneofCase.CaptureAudioFrame:
279+
Instance.CaptureAudioFrameReceived?.Invoke(r.CaptureAudioFrame!);
278280
break;
279281
case FfiEvent.MessageOneofCase.PerformRpc:
280282
Instance.PerformRpcReceived?.Invoke(r.PerformRpc!);

Runtime/Scripts/Internal/FFIClients/FFIEvents.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ namespace LiveKit.Internal
4444

4545
internal delegate void SendTextReceivedDelegate(StreamSendTextCallback e);
4646

47+
internal delegate void CaptureAudioFrameReceivedDelegate(CaptureAudioFrameCallback e);
48+
4749
// Events
4850
internal delegate void RoomEventReceivedDelegate(RoomEvent e);
4951

Runtime/Scripts/RtcAudioSource.cs

Lines changed: 36 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ public enum RtcAudioSourceType
1818
public abstract class RtcAudioSource : IRtcSource
1919
{
2020
public abstract event Action<float[], int, int> AudioRead;
21-
public virtual IEnumerator Prepare(float timeout = 0) { yield break; }
21+
public virtual IEnumerator Prepare(float timeout = 0) { yield break; }
2222
public abstract void Play();
2323

2424
#if UNITY_IOS
2525
// iOS microphone sample rate is 24k,
26-
// please make sure when you using
26+
// please make sure when you using
2727
// sourceType is AudioSourceMicrophone
2828
public static uint DefaultMirophoneSampleRate = 24000;
2929

@@ -43,7 +43,7 @@ public abstract class RtcAudioSource : IRtcSource
4343

4444
// Possibly used on the AudioThread
4545
private Thread _readAudioThread;
46-
private ThreadSafeQueue<AudioFrame> _frameQueue = new ThreadSafeQueue<AudioFrame>();
46+
private AudioBuffer _captureBuffer = new AudioBuffer();
4747

4848
private bool _muted = false;
4949
public override bool Muted => _muted;
@@ -85,7 +85,6 @@ public void Start()
8585
Stop();
8686
_readAudioThread = new Thread(Update);
8787
_readAudioThread.Start();
88-
8988
AudioRead += OnAudioRead;
9089
Play();
9190
}
@@ -101,78 +100,55 @@ private void Update()
101100
while (true)
102101
{
103102
Thread.Sleep(Constants.TASK_DELAY);
104-
ReadAudio();
103+
var frame = _captureBuffer.ReadDuration(10); // 10ms
104+
if (_muted || frame == null) continue;
105+
Capture(frame);
105106
}
106107
}
107108

108109
private void OnAudioRead(float[] data, int channels, int sampleRate)
109110
{
110-
var samplesPerChannel = data.Length / channels;
111-
var frame = new AudioFrame((uint)sampleRate, (uint)channels, (uint)samplesPerChannel);
112-
113-
static short FloatToS16(float v)
114-
{
115-
v *= 32768f;
116-
v = Math.Min(v, 32767f);
117-
v = Math.Max(v, -32768f);
118-
return (short)(v + Math.Sign(v) * 0.5f);
119-
}
120-
unsafe
111+
_captureBuffer.Write(data, (uint)channels, (uint)sampleRate);
112+
if (_sourceType == RtcAudioSourceType.AudioSourceMicrophone)
121113
{
122-
var frameData = new Span<short>(frame.Data.ToPointer(), frame.Length / sizeof(short));
123-
for (int i = 0; i < data.Length; i++)
124-
{
125-
frameData[i] = FloatToS16(data[i]);
126-
}
127-
if (_sourceType == RtcAudioSourceType.AudioSourceMicrophone)
128-
{
129-
// Don't play the audio locally, to avoid echo.
130-
Array.Clear(data, 0, data.Length);
131-
}
114+
// Don't play the audio locally, to avoid echo.
115+
Array.Clear(data, 0, data.Length);
132116
}
133-
_frameQueue.Enqueue(frame);
134117
}
135118

136-
private void ReadAudio()
119+
private void Capture(AudioFrame frame)
137120
{
138-
while (_frameQueue.Count > 0)
121+
using var request = FFIBridge.Instance.NewRequest<CaptureAudioFrameRequest>();
122+
using var audioFrameBufferInfo = request.TempResource<AudioFrameBufferInfo>();
123+
124+
var pushFrame = request.request;
125+
pushFrame.SourceHandle = (ulong)Handle.DangerousGetHandle();
126+
127+
pushFrame.Buffer = audioFrameBufferInfo;
128+
pushFrame.Buffer.DataPtr = (ulong)frame.Data;
129+
pushFrame.Buffer.NumChannels = frame.NumChannels;
130+
pushFrame.Buffer.SampleRate = frame.SampleRate;
131+
pushFrame.Buffer.SamplesPerChannel = frame.SamplesPerChannel;
132+
133+
using var response = request.Send();
134+
FfiResponse res = response;
135+
136+
// Frame needs to stay alive until receiving the async callback.
137+
var asyncId = res.CaptureAudioFrame.AsyncId;
138+
void Callback(CaptureAudioFrameCallback callback)
139139
{
140-
try
141-
{
142-
AudioFrame frame = _frameQueue.Dequeue();
143-
144-
if(_muted)
145-
{
146-
continue;
147-
}
148-
unsafe
149-
{
150-
using var request = FFIBridge.Instance.NewRequest<CaptureAudioFrameRequest>();
151-
using var audioFrameBufferInfo = request.TempResource<AudioFrameBufferInfo>();
152-
153-
var pushFrame = request.request;
154-
pushFrame.SourceHandle = (ulong)Handle.DangerousGetHandle();
155-
156-
pushFrame.Buffer = audioFrameBufferInfo;
157-
pushFrame.Buffer.DataPtr = (ulong)frame.Data;
158-
pushFrame.Buffer.NumChannels = frame.NumChannels;
159-
pushFrame.Buffer.SampleRate = frame.SampleRate;
160-
pushFrame.Buffer.SamplesPerChannel = frame.SamplesPerChannel;
161-
162-
using var response = request.Send();
163-
}
164-
}
165-
catch (Exception e)
166-
{
167-
Utils.Error("Audio Framedata error: " + e.Message);
168-
}
140+
if (callback.AsyncId != asyncId) return;
141+
if (callback.HasError)
142+
Utils.Error($"Audio capture failed: {callback.Error}");
143+
frame.Dispose();
144+
FfiClient.Instance.CaptureAudioFrameReceived -= Callback;
169145
}
146+
FfiClient.Instance.CaptureAudioFrameReceived += Callback;
170147
}
171148

172149
public override void SetMute(bool muted)
173150
{
174151
_muted = muted;
175152
}
176-
177153
}
178-
}
154+
}

0 commit comments

Comments
 (0)