Skip to content

Commit 66cd723

Browse files
committed
Simplify voice receive event args
1 parent 23dbe8c commit 66cd723

4 files changed

Lines changed: 16 additions & 161 deletions

File tree

Documentation/guides/basic-concepts/Voice/VoiceModule.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,6 @@ public async Task<string> EchoAsync()
141141

142142
voiceClient.VoiceReceive += args =>
143143
{
144-
// If the timestamp is null, the packet was lost.
145-
// We skip it, which mirrors the packet loss to the echo recipients.
146-
if (args.IsLost)
147-
return default;
148-
149144
// Pass current user voice directly to SendAsync to create echo
150145
if (voiceClient.Cache.SsrcUsers.TryGetValue(args.Ssrc, out var voiceUserId) && voiceUserId == userId)
151146
voiceClient.SendVoice(args.SequenceNumber, args.Timestamp, args.Frame);
Lines changed: 4 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,22 @@
1-
using System.Diagnostics;
2-
using System.Diagnostics.CodeAnalysis;
3-
using System.Runtime.CompilerServices;
41
using System.Runtime.InteropServices;
52

63
namespace NetCord.Gateway.Voice;
74

85
[StructLayout(LayoutKind.Auto)]
96
public readonly ref struct VoiceReceiveEventArgs
107
{
11-
internal const uint FecFlag = 1u << 31;
12-
13-
private VoiceReceiveEventArgs(ReadOnlySpan<byte> frame, uint fecAndSamples, uint ssrc, uint timestamp, ushort sequenceNumber)
8+
public VoiceReceiveEventArgs(ReadOnlySpan<byte> frame, uint ssrc, uint timestamp, ushort sequenceNumber)
149
{
15-
_frame = frame;
16-
_fecAndSamples = fecAndSamples;
10+
Frame = frame;
1711
Ssrc = ssrc;
1812
Timestamp = timestamp;
1913
SequenceNumber = sequenceNumber;
2014
}
2115

22-
public static VoiceReceiveEventArgs Delivered(ReadOnlySpan<byte> frame, uint ssrc, uint timestamp, ushort sequenceNumber)
23-
{
24-
return new(frame, 0, ssrc, timestamp, sequenceNumber);
25-
}
26-
27-
public static VoiceReceiveEventArgs Lost(uint ssrc, uint timestamp, ushort sequenceNumber, int samplesPerChannel, ReadOnlySpan<byte> fecData = default)
28-
{
29-
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(samplesPerChannel);
30-
31-
var fecAndSamples = (uint)samplesPerChannel | (fecData.IsEmpty ? 0 : FecFlag);
32-
33-
return new(fecData, fecAndSamples, ssrc, timestamp, sequenceNumber);
34-
}
35-
36-
private readonly ReadOnlySpan<byte> _frame;
37-
38-
private readonly uint _fecAndSamples;
39-
4016
/// <summary>
41-
/// The voice frame data. Empty if the frame was lost.
17+
/// The voice frame data.
4218
/// </summary>
43-
public readonly ReadOnlySpan<byte> Frame
44-
{
45-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
46-
get => IsLost ? null : _frame;
47-
}
19+
public readonly ReadOnlySpan<byte> Frame { get; }
4820

4921
/// <summary>
5022
/// The synchronization source (SSRC) of the sender of the voice frame.
@@ -60,88 +32,4 @@ public readonly ReadOnlySpan<byte> Frame
6032
/// The sequence number of the voice frame.
6133
/// </summary>
6234
public readonly ushort SequenceNumber { get; }
63-
64-
/// <summary>
65-
/// Whether the voice frame was lost. If true, the <see cref="Frame"/> property will be empty
66-
/// and the <see cref="AsLost"/> method can be used to retrieve the lost-specific data.
67-
/// </summary>
68-
public readonly bool IsLost
69-
{
70-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
71-
get => _fecAndSamples is not 0;
72-
}
73-
74-
/// <summary>
75-
/// Converts this <see cref="VoiceReceiveEventArgs"/> to a <see cref="LostVoiceReceiveEventArgs"/>.
76-
/// </summary>
77-
/// <exception cref="InvalidOperationException">Thrown when the voice frame was successfully delivered and is not considered lost.</exception>
78-
/// <returns><see cref="LostVoiceReceiveEventArgs"/> containing the lost-specific data of this event args.</returns>
79-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
80-
public LostVoiceReceiveEventArgs AsLost()
81-
{
82-
if (!IsLost)
83-
ThrowNotLost();
84-
85-
return new(Ssrc, Timestamp, SequenceNumber, _frame, _fecAndSamples);
86-
}
87-
88-
[DoesNotReturn]
89-
[StackTraceHidden]
90-
private static void ThrowNotLost()
91-
{
92-
throw new InvalidOperationException("This event args is not lost.");
93-
}
94-
}
95-
96-
[StructLayout(LayoutKind.Auto)]
97-
public readonly ref struct LostVoiceReceiveEventArgs
98-
{
99-
internal LostVoiceReceiveEventArgs(uint ssrc, uint timestamp, ushort sequenceNumber, ReadOnlySpan<byte> fecData, uint fecAndSamples)
100-
{
101-
FecData = fecData;
102-
Ssrc = ssrc;
103-
Timestamp = timestamp;
104-
SequenceNumber = sequenceNumber;
105-
_fecAndSamples = fecAndSamples;
106-
}
107-
108-
/// <summary>
109-
/// The FEC data of the lost voice frame, if any. This will be empty if no FEC data is available for the lost frame.
110-
/// </summary>
111-
public readonly ReadOnlySpan<byte> FecData { get; }
112-
113-
/// <summary>
114-
/// The synchronization source (SSRC) of the sender of the voice frame.
115-
/// </summary>
116-
public readonly uint Ssrc { get; }
117-
118-
/// <summary>
119-
/// The timestamp of the voice frame.
120-
/// </summary>
121-
public readonly uint Timestamp { get; }
122-
123-
/// <summary>
124-
/// The sequence number of the voice frame.
125-
/// </summary>
126-
public readonly ushort SequenceNumber { get; }
127-
128-
/// <summary>
129-
/// The number of samples per channel in the lost voice frame.
130-
/// </summary>
131-
public readonly int SamplesPerChannel
132-
{
133-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
134-
get => (int)(_fecAndSamples & ~VoiceReceiveEventArgs.FecFlag);
135-
}
136-
137-
/// <summary>
138-
/// Whether the frame should be decoded using FEC.
139-
/// </summary>
140-
public readonly bool DecodeFec
141-
{
142-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
143-
get => (_fecAndSamples & VoiceReceiveEventArgs.FecFlag) is not 0;
144-
}
145-
146-
private readonly uint _fecAndSamples;
14735
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace NetCord.Gateway.Voice;
1+
namespace NetCord.Gateway.Voice;
22

33
public sealed class RawVoiceReceiveHandler : VoiceReceiveHandler
44
{
@@ -7,6 +7,6 @@ public sealed class RawVoiceReceiveHandler : VoiceReceiveHandler
77
public override void Handle(VoiceReceiveContext context)
88
{
99
var packet = context.Packet;
10-
InvokeVoiceReceive(VoiceReceiveEventArgs.Delivered(context.Frame, packet.Ssrc, packet.Timestamp, packet.SequenceNumber));
10+
InvokeVoiceReceive(new(context.Frame, packet.Ssrc, packet.Timestamp, packet.SequenceNumber));
1111
}
1212
}

Tests/NetCord.Test/ApplicationCommands/VoiceCommands.cs

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,7 @@ public async Task EchoAsync(IVoiceGuildChannel? channel = null, VoiceEncryption?
142142

143143
voiceClient.VoiceReceive += args =>
144144
{
145-
if (!args.IsLost)
146-
voiceClient.SendVoice(args.SequenceNumber, args.Timestamp, args.Frame);
147-
else
148-
Console.WriteLine($"Frame {args.SequenceNumber} got lost");
145+
voiceClient.SendVoice(args.SequenceNumber, args.Timestamp, args.Frame);
149146

150147
return default;
151148

@@ -174,13 +171,6 @@ public async Task RecordAsync(IVoiceGuildChannel? channel = null,
174171
RedirectStandardInput = true,
175172
})!;
176173

177-
using var ffmpeg2 = Process.Start(new ProcessStartInfo
178-
{
179-
FileName = "ffmpeg",
180-
Arguments = $"-f {(pcmFormat is PcmFormat.Short ? "s16le" : "f32le")} -ar 48000 -ac {(byte)voiceChannels} -i pipe:0 recording-{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}-2.wav",
181-
RedirectStandardInput = true,
182-
})!;
183-
184174
var voiceClient = await JoinAsync(channel, encryption, args =>
185175
{
186176
if (!args.Reconnect)
@@ -191,47 +181,29 @@ public async Task RecordAsync(IVoiceGuildChannel? channel = null,
191181
await RespondAsync(InteractionCallback.Message("Recording!"));
192182

193183
using OpusDecoder decoder = new(voiceChannels);
194-
using OpusDecoder decoder2 = new(voiceChannels);
195-
var bufferSize = Opus.GetFrameBufferSize(Opus.GetSamplesPerChannel(Opus.MaxFrameDuration), pcmFormat, voiceChannels);
184+
185+
var frameSize = Opus.GetSamplesPerChannel(Opus.MaxFrameDuration);
186+
var bufferSize = Opus.GetFrameBufferSize(frameSize, pcmFormat, voiceChannels);
196187
var buffer = new byte[bufferSize];
197188

198189
Func<ReadOnlySpan<byte>, Span<byte>, int, bool, int> decode = pcmFormat is PcmFormat.Short ? decoder.Decode : decoder.DecodeFloat;
199190

200-
Func<ReadOnlySpan<byte>, Span<byte>, int, bool, int> decode2 = pcmFormat is PcmFormat.Short ? decoder2.Decode : decoder2.DecodeFloat;
201-
202191
voiceClient.VoiceReceive += args =>
203192
{
204-
if (args.IsLost)
205-
Console.WriteLine($"Lost frame {args.SequenceNumber} with {args.AsLost().SamplesPerChannel} samples");
206-
else
207-
voiceClient.SendVoice(args.SequenceNumber, args.Timestamp, args.Frame);
208-
209-
var frameSize = args.IsLost ? args.AsLost().SamplesPerChannel : Opus.GetSamplesPerChannel(Opus.MaxFrameDuration);
193+
voiceClient.SendVoice(args.SequenceNumber, args.Timestamp, args.Frame);
210194

211-
// No FEC
212-
{
213-
var samples = decode(args.Frame,
214-
buffer.AsSpan(0, Opus.GetFrameBufferSize(frameSize, pcmFormat, voiceChannels)),
215-
frameSize,
216-
false);
217-
ffmpeg.StandardInput.BaseStream.Write(buffer, 0, Opus.GetFrameBufferSize(samples, pcmFormat, voiceChannels));
218-
}
195+
var samples = decode(args.Frame,
196+
buffer.AsSpan(0, bufferSize),
197+
frameSize,
198+
false);
219199

220-
// With FEC if available
221-
{
222-
var samples = decode2(args.IsLost ? args.AsLost().FecData : args.Frame,
223-
buffer.AsSpan(0, Opus.GetFrameBufferSize(frameSize, pcmFormat, voiceChannels)),
224-
frameSize,
225-
args.IsLost && args.AsLost().DecodeFec);
226-
ffmpeg2.StandardInput.BaseStream.Write(buffer, 0, Opus.GetFrameBufferSize(samples, pcmFormat, voiceChannels));
227-
}
200+
ffmpeg.StandardInput.BaseStream.Write(buffer.AsSpan(0, Opus.GetFrameBufferSize(samples, pcmFormat, voiceChannels)));
228201

229202
return default;
230203
};
231204

232205
await taskCompletionSource.Task;
233206
ffmpeg.Kill();
234-
ffmpeg2.Kill();
235207
}
236208

237209
public enum VoiceEncryption : byte

0 commit comments

Comments
 (0)