Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b349f49
Add voice overview and voice connection guides
KubaZ2 Feb 12, 2026
938879f
Add glossary to index.md
KubaZ2 Feb 12, 2026
9cf52ec
Add sending voice guide
KubaZ2 Feb 13, 2026
801b0c0
Add a Voice section project
KubaZ2 Feb 14, 2026
424c83b
Update the functional project, add /record for the receiving voice guide
KubaZ2 Feb 15, 2026
dda3f07
Add an almost finished voice receiving guide
KubaZ2 Feb 15, 2026
653696c
Update voice guides
KubaZ2 Jun 17, 2026
9f8b901
Fix toc
KubaZ2 Jun 17, 2026
72ab5eb
Improve voice overview guide
KubaZ2 Jun 17, 2026
c40f47c
Fix compilation error of VoiceSetup
KubaZ2 Jun 17, 2026
7df92b1
Cleanup
KubaZ2 Jun 17, 2026
cbbc912
Merge branch 'main' into feature/voice-guides
KubaZ2 Jun 17, 2026
4a069d0
Fix toc names and cleanup "see also"
KubaZ2 Jun 18, 2026
8316043
Merge branch 'main' into feature/voice-guides
KubaZ2 Jun 18, 2026
62cfa9b
Merge branch 'main' into feature/voice-guides
KubaZ2 Jun 18, 2026
5be4d5a
Add sample.mp3 made by Mewyk
KubaZ2 Jun 18, 2026
91f12ef
Add native libs to voice example
KubaZ2 Jun 18, 2026
5a1989a
Merge branch 'main' into feature/voice-guides
KubaZ2 Jun 18, 2026
19dab8c
Fix wording
KubaZ2 Jun 18, 2026
569ec5f
Update sample sound
KubaZ2 Jun 18, 2026
7e99d82
Merge branch 'main' into feature/voice-guides
KubaZ2 Jun 19, 2026
af71ffb
Update allowed frame durations and add new Opus applications and add …
KubaZ2 Jun 19, 2026
9324278
Normalize "Opus application"
KubaZ2 Jun 19, 2026
81e4d95
Improve record sample command to flush the input stream
KubaZ2 Jun 20, 2026
83dda11
Move troubleshooting to a separate guide
KubaZ2 Jun 20, 2026
98624f9
Merge branch 'main' into feature/voice-guides
KubaZ2 Jun 20, 2026
12d7154
Improve troubleshooting guide
KubaZ2 Jun 20, 2026
1e5b82f
Merge branch 'main' into feature/voice-guides
KubaZ2 Jun 23, 2026
628f131
Add "Maintaining Voice Connections" to the voice connection guide
KubaZ2 Jun 23, 2026
632264d
Explain external socket address discovery failure mitigation better
KubaZ2 Jun 23, 2026
e48a7da
Merge branch 'main' into feature/voice-guides
KubaZ2 Jun 23, 2026
2a78cf2
Update timeouts in the guide
KubaZ2 Jun 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Documentation/docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
],
"resource": [
{
"files": [ "images/**", "robots.txt" ]
"files": [ "images/**", "sounds/**", "robots.txt" ]
}
],
"overwrite": [
Expand Down
12 changes: 12 additions & 0 deletions Documentation/guides/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@
href: basic-concepts/sharding.md
- name: Voice
href: basic-concepts/voice.md
- name: Voice
items:
- name: Overview
href: voice/index.md
- name: Voice Connection
href: voice/voice-connection.md
- name: Sending Voice
href: voice/sending-voice.md
- name: Receiving Voice
href: voice/receiving-voice.md
- name: Troubleshooting
href: voice/troubleshooting.md
- name: Services
items:
- name: Introduction
Expand Down
73 changes: 73 additions & 0 deletions Documentation/guides/voice/ReceivingVoice/Examples.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System.Threading.Channels;

using NetCord.Gateway.Voice;

namespace MyBot;

internal static class Examples
{
public static void VoiceReceiveEvent(VoiceClient voiceClient)
{
voiceClient.VoiceReceive += args =>
{
if (voiceClient.Cache.SsrcUsers.TryGetValue(args.Ssrc, out var userId))
Console.WriteLine($"Received audio from user {userId}");
else
Console.WriteLine($"Received audio from unknown user with SSRC {args.Ssrc}");

return default;
};
}

public static void VoiceReceiveEventAsyncHandler(VoiceClient voiceClient)
{
voiceClient.VoiceReceive += args =>
{
// You may consider renting a buffer
// from 'ArrayPool<T>' in a real application
var ownedFrame = args.Frame.ToArray();

return HandleAsync(ownedFrame, args.Ssrc);

static async ValueTask HandleAsync(byte[] frame, uint ssrc)
{
await Task.Delay(100); // Simulate async processing

Console.WriteLine($"Processed audio frame from SSRC {ssrc}");
}
};
}

public static async Task BufferingReceivedAudio(VoiceClient voiceClient)
{
// Configure the channel to drop frames when the buffer is full;
// 1000 frames means from 2.5 to 120 seconds of
// audio depending on the Opus frame duration;
// Dropping frames should generally never happen, but it is a good
// idea to handle it just in case to avoid running out of memory
var channel = Channel.CreateBounded<(byte[] Frame, uint? Timestamp)>(new BoundedChannelOptions(1000)
{
FullMode = BoundedChannelFullMode.DropWrite,
});

var writer = channel.Writer;

voiceClient.VoiceReceive += args =>
{
// You may consider renting a buffer from 'ArrayPool<T>' in a real application
var frame = args.Frame.ToArray();

return writer.WriteAsync((frame, args.Timestamp));
};

await foreach (var (frame, timestamp) in channel.Reader.ReadAllAsync())
Console.WriteLine($"Received audio frame of size {frame.Length} with timestamp {timestamp}");
}

public static void CreateOpusDecodeStream(Stream stream)
{
OpusDecodeStream opusDecodeStream = new(stream,
PcmFormat.Float,
VoiceChannels.Stereo);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\..\..\..\NetCord\NetCord.csproj" />
</ItemGroup>

</Project>
74 changes: 74 additions & 0 deletions Documentation/guides/voice/SendingVoice/Examples.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System.Diagnostics;

using NetCord.Gateway.Voice;

namespace MyBot;

internal static class Examples
{
public static void CreateVoiceStream(VoiceClient voiceClient)
{
var voiceStream = voiceClient.CreateVoiceStream();
}

public static void CreateVoiceStreamWithConfiguration(VoiceClient voiceClient)
{
var voiceStream = voiceClient.CreateVoiceStream(new VoiceStreamConfiguration
{
NormalizeSpeed = false,
FrameDuration = 60,
});
}

public static void CreateOpusEncodeStream(Stream voiceStream)
{
OpusEncodeStream opusEncodeStream = new(voiceStream,
PcmFormat.Float,
VoiceChannels.Stereo,
OpusApplication.Audio);
}

public static void CreateOpusEncodeStreamWithConfiguration(Stream voiceStream)
{
OpusEncodeStream opusEncodeStream = new(voiceStream,
PcmFormat.Float,
VoiceChannels.Stereo,
OpusApplication.Audio,
new OpusEncodeStreamConfiguration
{
Segment = false,
FrameDuration = 2.5f,
});
}

public static async Task FfmpegAsync(OpusEncodeStream opusEncodeStream, string input)
{
using var process = Process.Start(new ProcessStartInfo
{
FileName = "ffmpeg",
ArgumentList =
{
// Input file
"-i", input,
// Output format 32-bit float PCM with native endianness
"-f", BitConverter.IsLittleEndian ? "f32le" : "f32be",
// Sampling rate 48kHz
"-ar", "48000",
// 2 channels (stereo)
"-ac", "2",
// Output to stdout
"pipe:1",
},
RedirectStandardOutput = true,
})!;

var ffmpegOutput = process.StandardOutput.BaseStream;

// Copy the FFmpeg output directly to the OpusEncodeStream
await ffmpegOutput.CopyToAsync(opusEncodeStream);

// Flush the OpusEncodeStream to ensure all data is sent,
// appended by the silence frames to prevent audio interpolation
await opusEncodeStream.FlushAsync();
}
}
7 changes: 7 additions & 0 deletions Documentation/guides/voice/SendingVoice/SendingVoice.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\..\..\..\NetCord\NetCord.csproj" />
</ItemGroup>

</Project>
31 changes: 31 additions & 0 deletions Documentation/guides/voice/Troubleshooting/Examples.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using NetCord.Gateway;
using NetCord.Gateway.Voice;

namespace MyBot;

internal static class Examples
{
public static async Task LongerConnectionTimeoutAsync(GatewayClient client,
ulong guildId,
ulong channelId,
VoiceClientConfiguration? configuration)
{
var voiceClient = await client.JoinVoiceChannelAsync(guildId, channelId, configuration, TimeSpan.FromSeconds(15));
}

public static async Task LongerExternalSocketAddressDiscoveryTimeoutAsync()
{
VoiceClientConfiguration configuration = new()
{
ExternalSocketAddressDiscoveryTimeout = TimeSpan.FromSeconds(15),
};
}

public static async Task KeepAliveAsync(VoiceClient voiceClient, CancellationToken cancellationToken)
{
using PeriodicTimer timer = new(TimeSpan.FromSeconds(30));

while (await timer.WaitForNextTickAsync(cancellationToken))
await voiceClient.SendDatagramAsync(ReadOnlyMemory<byte>.Empty, cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\..\..\..\NetCord\NetCord.csproj" />
</ItemGroup>

</Project>
Loading