diff --git a/DisCatSharp.Docs/api/DisCatSharp.VoiceNext/index.md b/DisCatSharp.Docs/api/DisCatSharp.VoiceNext/index.md deleted file mode 100644 index 59bfe5a77..000000000 --- a/DisCatSharp.Docs/api/DisCatSharp.VoiceNext/index.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -uid: api_discatsharp_voicenext_index -title: DisCatSharp.VoiceNext API Reference ---- - -# API Reference - -Welcome to the DisCatSharp.VoiceNext API reference. - -To begin, select a namespace, then a class, from the table of contents on the left. - -If you encounter any problems or see typos, please inform us on our [Discord server](https://discord.gg/Uk7sggRBTm). diff --git a/DisCatSharp.Docs/api/index.md b/DisCatSharp.Docs/api/index.md index edd4a0801..4330592e7 100644 --- a/DisCatSharp.Docs/api/index.md +++ b/DisCatSharp.Docs/api/index.md @@ -1,25 +1,24 @@ --- uid: api_index title: DisCatSharp Global API --- # API Reference Welcome to the DisCatSharp Global API reference. ## Main packages - [DisCatSharp](xref:api_discatsharp_index) - [DisCatSharp.ApplicationCommands](xref:api_discatsharp_applicationcommands_index) - [DisCatSharp.CommandsNext](xref:api_discatsharp_commandsnext_index) - [DisCatSharp.Interactivity](xref:api_discatsharp_interactivity_index) ## Voice packages - [DisCatSharp.Lavalink](xref:api_discatsharp_lavalink_index) -- [DisCatSharp.VoiceNext](xref:api_discatsharp_voicenext_index) ## Hosting packages - [DisCatSharp.Configuration](xref:api_discatsharp_configuration_index) - [DisCatSharp.Hosting](xref:api_discatsharp_hosting_index) - [DisCatSharp.DependencyInjection](xref:api_discatsharp_hosting_dependencyinjection_index) ## Other packages - [DisCatSharp.Common](xref:api_discatsharp_common_index) diff --git a/DisCatSharp.Docs/api/toc.yml b/DisCatSharp.Docs/api/toc.yml index 73874f696..eb65bb7ec 100644 --- a/DisCatSharp.Docs/api/toc.yml +++ b/DisCatSharp.Docs/api/toc.yml @@ -1,22 +1,20 @@ - name: Intro href: index.md - name: DisCatSharp href: DisCatSharp/ - name: DisCatSharp.ApplicationCommands href: DisCatSharp.ApplicationCommands/ - name: DisCatSharp.CommandsNext href: DisCatSharp.CommandsNext/ - name: DisCatSharp.Common href: DisCatSharp.Common/ - name: DisCatSharp.Configuration href: DisCatSharp.Configuration/ - name: DisCatSharp.Hosting href: DisCatSharp.Hosting/ - name: DisCatSharp.Hosting.DependencyInjection href: DisCatSharp.Hosting.DependencyInjection/ - name: DisCatSharp.Interactivity href: DisCatSharp.Interactivity/ - name: DisCatSharp.Lavalink href: DisCatSharp.Lavalink/ -- name: DisCatSharp.VoiceNext - href: DisCatSharp.VoiceNext/ diff --git a/DisCatSharp.Docs/articles/modules/audio/voicenext/prerequisites.md b/DisCatSharp.Docs/articles/modules/audio/voicenext/prerequisites.md deleted file mode 100644 index 483210fa6..000000000 --- a/DisCatSharp.Docs/articles/modules/audio/voicenext/prerequisites.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -uid: modules_audio_voicenext_prerequisites -title: VoiceNext Prerequisites ---- - -# VoiceNext Prerequisites - -> [!NOTE] - > We highly suggest using the [DisCatSharp.Lavalink](xref:modules_audio_lavalink_configuration) package for audio playback. It is much easier to use and has a lot of features that VoiceNext does not have. - -## Required Libraries -VoiceNext depends on the [libsodium](https://github.com/jedisct1/libsodium) and [Opus](https://opus-codec.org/) libraries to decrypt and process audio packets.
-Both *must* be available on your development and host machines otherwise VoiceNext will *not* work. - - -### Windows -When installing VoiceNext though NuGet, an additional package containing the native Windows binaries will automatically be included with **no additional steps required**. - -However, if you are using DisCatSharp from source or without a NuGet package manager, you must manually [download](xref:natives) the binaries and place them at the root of your working directory where your application is located. - -### MacOS -Native libraries for Apple's macOS can be installed using the [Homebrew](https://brew.sh) package manager: -```console -$ brew install opus libsodium -``` - -### Linux - - -#### Debian and Derivatives -Opus package naming is consistent across Debian, Ubuntu, and Linux Mint. -```bash -sudo apt-get install libopus0 libopus-dev -``` - -Package naming for *libsodium* will vary depending on your distro and version: - -Distributions|Terminal Command -:---:|:---: -Ubuntu 18.04+, Debian 10+|`sudo apt-get install libsodium23 libsodium-dev` -Linux Mint, Ubuntu 16.04, Debian 9 |`sudo apt-get install libsodium18 libsodium-dev` -Debian 8|`sudo apt-get install libsodium13 libsodium-dev` diff --git a/DisCatSharp.Docs/articles/modules/audio/voicenext/receive.md b/DisCatSharp.Docs/articles/modules/audio/voicenext/receive.md deleted file mode 100644 index b07b5e118..000000000 --- a/DisCatSharp.Docs/articles/modules/audio/voicenext/receive.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -uid: modules_audio_voicenext_receive -title: Receiving ---- - -## Receiving with VoiceNext - -### Enable Receiver -Receiving incoming audio is disabled by default to save on bandwidth, as most users will never make use of incoming data. -This can be changed by providing a configuration object to `DiscordClient#UseVoiceNext()`. -```cs -var discord = new DiscordClient(); - -discord.UseVoiceNext(new VoiceNextConfiguration() -{ - EnableIncoming = true -}); -``` - -### Establish Connection -The voice channel join process is the exact same as when transmitting. -```cs -DiscordChannel channel; -VoiceNextConnection connection = await channel.ConnectAsync(); -``` - -### Write Event Handler -We'll be able to receive incoming audio from the `VoiceReceived` event fired by `VoiceNextConnection`. -```cs -connection.VoiceReceived += ReceiveHandler; -``` - -Writing the logic for this event handler will depend on your overall goal. - -The event arguments will contain a PCM audio packet for you to make use of. -You can convert each packet to another format, concatenate them all together, feed them into an external program, or process the packets any way that'll suit your needs. - -When a user is speaking, `VoiceReceived` should fire once every twenty milliseconds and its packet will contain around twenty milliseconds worth of audio; this can vary due to differences in client settings. -To help keep track of the torrent of packets for each user, you can use user IDs in combination the synchronization value (SSRC) sent by Discord to determine the source of each packet. - -This short-and-simple example will use [ffmpeg](https://ffmpeg.org/about.html) to convert each packet to a *wav* file. -```cs -private async Task ReceiveHandler(VoiceNextConnection _, VoiceReceiveEventArgs args) -{ - var name = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - var ffmpeg = Process.Start(new ProcessStartInfo - { - FileName = "ffmpeg", - Arguments = $@"-ac 2 -f s16le -ar 48000 -i pipe:0 -ac 2 -ar 44100 {name}.wav", - RedirectStandardInput = true - }); - - await ffmpeg.StandardInput.BaseStream.WriteAsync(args.PcmData); -} -``` - -
-That's really all there is to it. Connect to a voice channel, hook an event, process the data as you see fit. - -![Wav Files](/images/voicenext_receive_01.png) - - -## Example Commands -```cs -[Command("start")] -public async Task StartCommand(CommandContext ctx, DiscordChannel channel = null) -{ - channel ??= ctx.Member.VoiceState?.Channel; - var connection = await channel.ConnectAsync(); - - Directory.CreateDirectory("Output"); - connection.VoiceReceived += VoiceReceiveHandler; -} - - -[Command("stop")] -public Task StopCommand(CommandContext ctx) -{ - var vnext = ctx.Client.GetVoiceNext(); - - var connection = vnext.GetConnection(ctx.Guild); - connection.VoiceReceived -= VoiceReceiveHandler; - connection.Dispose(); - - return Task.CompletedTask; -} - -private async Task VoiceReceiveHandler(VoiceNextConnection connection, VoiceReceiveEventArgs args) -{ - var fileName = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - var ffmpeg = Process.Start(new ProcessStartInfo - { - FileName = "ffmpeg", - Arguments = $@"-ac 2 -f s16le -ar 48000 -i pipe:0 -ac 2 -ar 44100 Output/{fileName}.wav", - RedirectStandardInput = true - }); - - await ffmpeg.StandardInput.BaseStream.WriteAsync(args.PcmData); - ffmpeg.Dispose(); -} -``` diff --git a/DisCatSharp.Docs/articles/modules/audio/voicenext/transmit.md b/DisCatSharp.Docs/articles/modules/audio/voicenext/transmit.md deleted file mode 100644 index 4216f5c73..000000000 --- a/DisCatSharp.Docs/articles/modules/audio/voicenext/transmit.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -uid: modules_audio_voicenext_transmit -title: Transmitting ---- - -## Transmitting with VoiceNext - -### Enable VoiceNext -Install the `DisCatSharp.VoiceNext` package from NuGet. - -![NuGet Package Manager](/images/voicenext_transmit_01.png) - -Then use the `UseVoiceNext` extension method on your instance of `DiscordClient`. -```cs -var discord = new DiscordClient(); -discord.UseVoiceNext(); -``` - -### Connect -Joining a voice channel is *very* easy; simply use the `ConnectAsync` extension method on `DiscordChannel`. -```cs -DiscordChannel channel; -VoiceNextConnection connection = await channel.ConnectAsync(); -``` - -### Transmit -Discord requires that we send Opus encoded stereo PCM audio data at a sample rate of 48,000 Hz. - -You'll need to convert your audio source to PCM S16LE using your preferred program for media conversion, then read -that data into a `Stream` object or an array of `byte` to be used with VoiceNext. Opus encoding of the PCM data will -be done automatically by VoiceNext before sending it to Discord. - -This example will use [ffmpeg](https://ffmpeg.org/about.html) to convert an MP3 file to a PCM stream. -```cs -var filePath = "funiculi_funicula.mp3"; -var ffmpeg = Process.Start(new ProcessStartInfo -{ - FileName = "ffmpeg", - Arguments = $@"-i ""{filePath}"" -ac 2 -f s16le -ar 48000 pipe:1", - RedirectStandardOutput = true, - UseShellExecute = false -}); - -Stream pcm = ffmpeg.StandardOutput.BaseStream; -``` - -Now that our audio is the correct format, we'll need to get a *transmit sink* for the channel we're connected to. -You can think of the transmit stream as our direct interface with a voice channel; any data written to one will be -processed by VoiceNext, queued, and sent to Discord which will then be output to the connected voice channel. -```cs -VoiceTransmitSink transmit = connection.GetTransmitSink(); -``` - -Once we have a transmit sink, we can 'play' our audio by copying our PCM data to the transmit sink buffer. -```cs -await pcm.CopyToAsync(transmit); -``` -`Stream#CopyToAsync()` will copy PCM data from the input stream to the output sink, up to the sink's configured -capacity, at which point it will wait until it can copy more. This means that the call will hold the task's execution, -until such time that the entire input stream has been consumed, and enqueued in the sink. - -This operation cannot be cancelled. If you'd like to have finer control of the playback, you should instead consider -using `Stream#ReadAsync()` and `VoiceTransmitSink#WriteAsync()` to manually copy small portions of PCM data to the -transmit sink. - -### Disconnect -Similar to joining, leaving a voice channel is rather straightforward. -```cs -var vnext = discord.GetVoiceNext(); -var connection = vnext.GetConnection(); - -connection.Disconnect(); -``` - -## Example Commands -```cs -[Command("join")] -public async Task JoinCommand(CommandContext ctx, DiscordChannel channel = null) -{ - channel ??= ctx.Member.VoiceState?.Channel; - await channel.ConnectAsync(); -} - -[Command("play")] -public async Task PlayCommand(CommandContext ctx, string path) -{ - var vnext = ctx.Client.GetVoiceNext(); - var connection = vnext.GetConnection(ctx.Guild); - - var transmit = connection.GetTransmitSink(); - - var pcm = ConvertAudioToPcm(path); - await pcm.CopyToAsync(transmit); - await pcm.DisposeAsync(); -} - -[Command("leave")] -public async Task LeaveCommand(CommandContext ctx) -{ - var vnext = ctx.Client.GetVoiceNext(); - var connection = vnext.GetConnection(ctx.Guild); - - connection.Disconnect(); -} - -private Stream ConvertAudioToPcm(string filePath) -{ - var ffmpeg = Process.Start(new ProcessStartInfo - { - FileName = "ffmpeg", - Arguments = $@"-i ""{filePath}"" -ac 2 -f s16le -ar 48000 pipe:1", - RedirectStandardOutput = true, - UseShellExecute = false - }); - - return ffmpeg.StandardOutput.BaseStream; -} -``` diff --git a/DisCatSharp.Docs/articles/toc.yml b/DisCatSharp.Docs/articles/toc.yml index 61d10cd62..e22cd7088 100644 --- a/DisCatSharp.Docs/articles/toc.yml +++ b/DisCatSharp.Docs/articles/toc.yml @@ -1,109 +1,101 @@ - name: Preamble href: preamble.md - name: Changelogs href: ../changelogs/index.md - name: The Basics items: - name: Creating a Bot Account href: basics/bot_account.md - name: Writing Your First Bot href: basics/first_bot.md - name: Bot as Hosted Service href: basics/web_app.md - name: Project Templates href: basics/templates.md - name: Beyond Basics items: - name: Events href: beyond_basics/events.md - name: Logging href: beyond_basics/logging/default.md items: - name: The Default Logger href: beyond_basics/logging/default.md - name: Third Party Loggers href: beyond_basics/logging/third_party.md - name: Dependency Injection Loggers href: beyond_basics/logging/di.md - name: Intents href: beyond_basics/intents.md - name: Sharding href: beyond_basics/sharding.md - name: Message Builder href: beyond_basics/messagebuilder.md - name: Components items: - name: Buttons href: beyond_basics/components/buttons.md - name: Select Menu href: beyond_basics/components/select_menus.md - name: Workarounds href: beyond_basics/workarounds.md - name: Modules items: - name: Application Commands items: - name: Introduction href: modules/application_commands/intro.md - name: Options href: modules/application_commands/options.md - name: Events href: modules/application_commands/events.md - name: Translations items: - name: Using Translations href: modules/application_commands/translations/using.md - name: Translation Reference href: modules/application_commands/translations/reference.md - name: Modals href: modules/application_commands/modals.md - name: Paginated Modals href: modules/application_commands/paginated_modals.md - name: CommandsNext items: - name: Introduction href: modules/commandsnext/intro.md - name: Command Attributes href: modules/commandsnext/command_attributes.md - name: Dependency Injection href: modules/commandsnext/dependency_injection.md - name: Customization items: - name: Help Formatter href: modules/commandsnext/help_formatter.md - name: Argument Converters href: modules/commandsnext/argument_converters.md - name: Command Handler href: modules/commandsnext/command_handler.md - name: Audio items: - name: Lavalink items: - name: Setup href: modules/audio/lavalink/setup.md - name: Configuration href: modules/audio/lavalink/configuration.md - name: Music Commands href: modules/audio/lavalink/music_commands.md - name: Advanced href: modules/audio/lavalink/advanced.md - - name: VoiceNext - items: - - name: Prerequisites - href: modules/audio/voicenext/prerequisites.md - - name: Transmitting - href: modules/audio/voicenext/transmit.md - - name: Receiving - href: modules/audio/voicenext/receive.md - name: Interactivity href: modules/interactivity.md - name: Miscellaneous items: - name: Nightly Builds href: misc/nightly_builds.md - name: Reporting Issues href: misc/reporting_issues.md - name: Hosting href: misc/hosting.md - name: Voice Activities href: misc/voice_activities.md diff --git a/DisCatSharp.Docs/docfx.json b/DisCatSharp.Docs/docfx.json index e8ee5c578..5ae335f09 100644 --- a/DisCatSharp.Docs/docfx.json +++ b/DisCatSharp.Docs/docfx.json @@ -1,264 +1,244 @@ { "metadata":[ { "src":[ { "src":"../DisCatSharp/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"api/DisCatSharp", "filter":"filter_config.yml", "disableDefaultFilter":false }, { "src":[ { "src":"../DisCatSharp.Interactivity/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"api/DisCatSharp.Interactivity", "filter":"filter_config.yml", "disableDefaultFilter":false }, { "src":[ { "src":"../DisCatSharp.Common/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"api/DisCatSharp.Common", "filter":"filter_config.yml", "disableDefaultFilter":false }, { "src":[ { "src":"../DisCatSharp.Hosting/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"api/DisCatSharp.Hosting", "filter":"filter_config.yml", "disableDefaultFilter":false }, { "src":[ { "src":"../DisCatSharp.Configuration/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"api/DisCatSharp.Configuration", "filter":"filter_config.yml", "disableDefaultFilter":false }, { "src":[ { "src":"../DisCatSharp.ApplicationCommands/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"api/DisCatSharp.ApplicationCommands", "filter":"filter_config.yml", "disableDefaultFilter":false }, { "src":[ { "src":"../DisCatSharp.CommandsNext/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"api/DisCatSharp.CommandsNext", "filter":"filter_config.yml", "disableDefaultFilter":false }, { "src":[ { "src":"../DisCatSharp.Hosting.DependencyInjection/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"api/DisCatSharp.Hosting.DependencyInjection", "filter":"filter_config.yml", "disableDefaultFilter":false }, { "src":[ { "src":"../DisCatSharp.Lavalink/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"api/DisCatSharp.Lavalink", "filter":"filter_config.yml", "disableDefaultFilter":false - }, - { - "src":[ - { - "src":"../DisCatSharp.VoiceNext/", - "files":[ - "**.csproj" - ], - "exclude":[ - "**/obj/**", - "**/bin/**" - ] - } - ], - "dest":"api/DisCatSharp.VoiceNext", - "filter":"filter_config.yml", - "disableDefaultFilter":false } ], "build":{ "xrefService":[ "https://xref.docs.microsoft.com/query?uid={uid}" ], "content":[ { "files":[ "api/**/**.yml", "api/**/**.md", "api/**.yml", "api/**.md" ], "exclude":[ "libdev/**" ] }, { "files":[ "**.md", "toc.yml", "faq/**.yml", "faq/**.md", "articles/**.yml", "articles/**.md", "changelogs/**.yml", - "changelogs/**.md", - "natives/**.yml", - "natives/**.md" + "changelogs/**.md" ], "exclude":[ "**/bin/**", "**/obj/**", "_site/**", "dcs/**" ] } ], "resource":[ { "files":[ "images/**", - "natives/**.zip", "./.htaccess" ], "exclude":[ "**/bin/**", "**/obj/**", "_site/**", "images/_**" ] } ], "overwrite":[], "dest":"_site", "globalMetadata":{ "_appTitle": "DisCatSharp Docs", "_appName": "DisCatSharp", "_appFooter": "Made with ♥ by AITSYS", "_copyrightFooter": "© Aiko IT Systems. All rights reserved.", "_enableSearch":true, "_disableSideFilter": false, "_enableNewTab":true, "_disableContribution": false, "_disableBreadcrumb": false, "_gitUrlPattern":"git", "_gitContribute":{ "repo":"https://github.com/Aiko-IT-Systems/DisCatSharp", "branch":"main" } }, "disableGitFeatures":false, "exportRawModel":true, "globalMetadataFiles":[], "fileMetadataFiles":[], "template":[ "dcs" ], "postProcessors":[ "ExtractSearchIndex", "CustomMemberIndexer" ], "noLangKeyword":false, "keepFileLink":false, "cleanupCacheHistory":false, "sitemap":{ "baseUrl":"https://docs.discatsharp.tech/", "changefreq":"daily", "priority":1.0 } } } diff --git a/DisCatSharp.Docs/images/voicenext_receive_01.png b/DisCatSharp.Docs/images/voicenext_receive_01.png deleted file mode 100644 index caa27de36..000000000 Binary files a/DisCatSharp.Docs/images/voicenext_receive_01.png and /dev/null differ diff --git a/DisCatSharp.Docs/images/voicenext_transmit_01.png b/DisCatSharp.Docs/images/voicenext_transmit_01.png deleted file mode 100644 index 11429803c..000000000 Binary files a/DisCatSharp.Docs/images/voicenext_transmit_01.png and /dev/null differ diff --git a/DisCatSharp.Docs/libdev/DisCatSharp.VoiceNext/index.md b/DisCatSharp.Docs/libdev/DisCatSharp.VoiceNext/index.md deleted file mode 100644 index 95fc9e082..000000000 --- a/DisCatSharp.Docs/libdev/DisCatSharp.VoiceNext/index.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -uid: libdev_discatsharp_voicenext_index -title: DisCatSharp.VoiceNext API Reference ---- - -# API Reference - -Welcome to the DisCatSharp.VoiceNext API reference. - -To begin, select a namespace, then a class, from the table of contents on the left. - -If you encounter any problems or see typos, please inform us on our [Discord server](https://discord.gg/Uk7sggRBTm). diff --git a/DisCatSharp.Docs/libdev/index.md b/DisCatSharp.Docs/libdev/index.md index 43572829b..46dbe85ef 100644 --- a/DisCatSharp.Docs/libdev/index.md +++ b/DisCatSharp.Docs/libdev/index.md @@ -1,30 +1,29 @@ --- uid: libdev_index title: DisCatSharp Library Development API --- # API Reference Welcome to the DisCatSharp Library Development API reference. This documentation is intended for developers who wish to contribute to the DisCatSharp library. You find internal and private methods and properties here. ## Main packages - [DisCatSharp](xref:libdev_discatsharp_index) - [DisCatSharp.ApplicationCommands](xref:libdev_discatsharp_applicationcommands_index) - [DisCatSharp.CommandsNext](xref:libdev_discatsharp_commandsnext_index) - [DisCatSharp.Interactivity](xref:libdev_discatsharp_interactivity_index) ## Voice packages - [DisCatSharp.Lavalink](xref:libdev_discatsharp_lavalink_index) -- [DisCatSharp.VoiceNext](xref:libdev_discatsharp_voicenext_index) ## Hosting packages - [DisCatSharp.Configuration](xref:libdev_discatsharp_configuration_index) - [DisCatSharp.Hosting](xref:libdev_discatsharp_hosting_index) - [DisCatSharp.DependencyInjection](xref:libdev_discatsharp_hosting_dependencyinjection_index) ## Other packages - [DisCatSharp.Common](xref:libdev_discatsharp_common_index) diff --git a/DisCatSharp.Docs/libdev/toc.yml b/DisCatSharp.Docs/libdev/toc.yml index 73874f696..eb65bb7ec 100644 --- a/DisCatSharp.Docs/libdev/toc.yml +++ b/DisCatSharp.Docs/libdev/toc.yml @@ -1,22 +1,20 @@ - name: Intro href: index.md - name: DisCatSharp href: DisCatSharp/ - name: DisCatSharp.ApplicationCommands href: DisCatSharp.ApplicationCommands/ - name: DisCatSharp.CommandsNext href: DisCatSharp.CommandsNext/ - name: DisCatSharp.Common href: DisCatSharp.Common/ - name: DisCatSharp.Configuration href: DisCatSharp.Configuration/ - name: DisCatSharp.Hosting href: DisCatSharp.Hosting/ - name: DisCatSharp.Hosting.DependencyInjection href: DisCatSharp.Hosting.DependencyInjection/ - name: DisCatSharp.Interactivity href: DisCatSharp.Interactivity/ - name: DisCatSharp.Lavalink href: DisCatSharp.Lavalink/ -- name: DisCatSharp.VoiceNext - href: DisCatSharp.VoiceNext/ diff --git a/DisCatSharp.Docs/natives/index.md b/DisCatSharp.Docs/natives/index.md deleted file mode 100644 index f4dd6ac3a..000000000 --- a/DisCatSharp.Docs/natives/index.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -uid: natives -title: Native Libraries ---- - -#### Downloads -Operating System|Download -| -------------- | -------------------------------------------------- | -| 64-bit Windows | [Click Here](/natives/vnext_natives_win32_x64.zip) | -| 32-bit Windows | [Click Here](/natives/vnext_natives_win32_x86.zip) | - -#### Licenses -Library|License -| --------- | -------------------------------------------------------- | -| Opus |https://opus-codec.org/license/ | -| libsodium |https://github.com/jedisct1/libsodium/blob/master/LICENSE | diff --git a/DisCatSharp.Docs/natives/vnext_natives_win32_x64.zip b/DisCatSharp.Docs/natives/vnext_natives_win32_x64.zip deleted file mode 100644 index a447803e5..000000000 Binary files a/DisCatSharp.Docs/natives/vnext_natives_win32_x64.zip and /dev/null differ diff --git a/DisCatSharp.Docs/natives/vnext_natives_win32_x86.zip b/DisCatSharp.Docs/natives/vnext_natives_win32_x86.zip deleted file mode 100644 index 35522e1ec..000000000 Binary files a/DisCatSharp.Docs/natives/vnext_natives_win32_x86.zip and /dev/null differ diff --git a/DisCatSharp.Docs/save.json b/DisCatSharp.Docs/save.json index c97c1f23b..98d734bbb 100644 --- a/DisCatSharp.Docs/save.json +++ b/DisCatSharp.Docs/save.json @@ -1,190 +1,173 @@ { "metadata": [ { "src":[ { "src":"../DisCatSharp/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"libdev/DisCatSharp", "filter":"filter_config.yml", "disableDefaultFilter":true }, { "src":[ { "src":"../DisCatSharp.Interactivity/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"libdev/DisCatSharp.Interactivity", "filter":"filter_config.yml", "disableDefaultFilter":true }, { "src":[ { "src":"../DisCatSharp.Common/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"libdev/DisCatSharp.Common", "filter":"filter_config.yml", "disableDefaultFilter":true }, { "src":[ { "src":"../DisCatSharp.Hosting/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"libdev/DisCatSharp.Hosting", "filter":"filter_config.yml", "disableDefaultFilter":true }, { "src":[ { "src":"../DisCatSharp.Configuration/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"libdev/DisCatSharp.Configuration", "filter":"filter_config.yml", "disableDefaultFilter":true }, { "src":[ { "src":"../DisCatSharp.ApplicationCommands/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"libdev/DisCatSharp.ApplicationCommands", "filter":"filter_config.yml", "disableDefaultFilter":true }, { "src":[ { "src":"../DisCatSharp.CommandsNext/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"libdev/DisCatSharp.CommandsNext", "filter":"filter_config.yml", "disableDefaultFilter":true }, { "src":[ { "src":"../DisCatSharp.Hosting.DependencyInjection/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"libdev/DisCatSharp.Hosting.DependencyInjection", "filter":"filter_config.yml", "disableDefaultFilter":true }, { "src":[ { "src":"../DisCatSharp.Lavalink/", "files":[ "**.csproj" ], "exclude":[ "**/obj/**", "**/bin/**" ] } ], "dest":"libdev/DisCatSharp.Lavalink", "filter":"filter_config.yml", "disableDefaultFilter":true - }, - { - "src":[ - { - "src":"../DisCatSharp.VoiceNext/", - "files":[ - "**.csproj" - ], - "exclude":[ - "**/obj/**", - "**/bin/**" - ] - } - ], - "dest":"libdev/DisCatSharp.VoiceNext", - "filter":"filter_config.yml", - "disableDefaultFilter":true } ], "build": { "content": [ { "files":[ "libdev/**/**.yml", "libdev/**/**.md", "libdev/**.yml", "libdev/**.md" ], "exclude":[ "api/**" ], "version":"internal" } ] } } diff --git a/DisCatSharp.Docs/toc.yml b/DisCatSharp.Docs/toc.yml index 9e912f0e3..699e39c3e 100644 --- a/DisCatSharp.Docs/toc.yml +++ b/DisCatSharp.Docs/toc.yml @@ -1,12 +1,10 @@ - name: Articles href: articles/ - name: Changelogs href: changelogs/index.md - name: FAQ href: faq.md - name: API Documentation href: api/index.md - name: Library Development Docs href: libdev/index.md -- name: Voice Natives - href: natives/index.md diff --git a/DisCatSharp.VoiceNext.Natives/DisCatSharp.VoiceNext.Natives.csproj b/DisCatSharp.VoiceNext.Natives/DisCatSharp.VoiceNext.Natives.csproj deleted file mode 100644 index cf8f68f5d..000000000 --- a/DisCatSharp.VoiceNext.Natives/DisCatSharp.VoiceNext.Natives.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - net7.0 - false - win-x86;win-x64 - true - false - symbols.nupkg - - - - DisCatSharp.VoiceNext.Natives - -Natives for DisCatSharp Voice Next Extension - -Manual Download: https://docs.discatsharp.tech/natives/index.html - - DisCatSharp,Discord API Wrapper,Discord,Bots,Discord Bots,AITSYS,Net6,Voice,Audio Player - - - - - true - runtimes - - - - - - - - - - diff --git a/DisCatSharp.VoiceNext.Natives/global.json b/DisCatSharp.VoiceNext.Natives/global.json deleted file mode 100644 index 0f01f74d0..000000000 --- a/DisCatSharp.VoiceNext.Natives/global.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "sdk": { - "version": "7.0.100", - "rollForward": "latestMinor", - "allowPrerelease": true - } - } \ No newline at end of file diff --git a/DisCatSharp.VoiceNext.Natives/runtimes/win-x64/native/libopus.dll b/DisCatSharp.VoiceNext.Natives/runtimes/win-x64/native/libopus.dll deleted file mode 100644 index 74a8e3554..000000000 Binary files a/DisCatSharp.VoiceNext.Natives/runtimes/win-x64/native/libopus.dll and /dev/null differ diff --git a/DisCatSharp.VoiceNext.Natives/runtimes/win-x64/native/libsodium.dll b/DisCatSharp.VoiceNext.Natives/runtimes/win-x64/native/libsodium.dll deleted file mode 100644 index cd122b7e3..000000000 Binary files a/DisCatSharp.VoiceNext.Natives/runtimes/win-x64/native/libsodium.dll and /dev/null differ diff --git a/DisCatSharp.VoiceNext.Natives/runtimes/win-x86/native/libopus.dll b/DisCatSharp.VoiceNext.Natives/runtimes/win-x86/native/libopus.dll deleted file mode 100644 index ee71317fa..000000000 Binary files a/DisCatSharp.VoiceNext.Natives/runtimes/win-x86/native/libopus.dll and /dev/null differ diff --git a/DisCatSharp.VoiceNext.Natives/runtimes/win-x86/native/libsodium.dll b/DisCatSharp.VoiceNext.Natives/runtimes/win-x86/native/libsodium.dll deleted file mode 100644 index c2225c58b..000000000 Binary files a/DisCatSharp.VoiceNext.Natives/runtimes/win-x86/native/libsodium.dll and /dev/null differ diff --git a/DisCatSharp.VoiceNext/AudioFormat.cs b/DisCatSharp.VoiceNext/AudioFormat.cs deleted file mode 100644 index 2b037f789..000000000 --- a/DisCatSharp.VoiceNext/AudioFormat.cs +++ /dev/null @@ -1,163 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Runtime.CompilerServices; - -namespace DisCatSharp.VoiceNext; - -/// -/// Defines the format of PCM data consumed or produced by Opus. -/// -public readonly struct AudioFormat -{ - /// - /// Gets the collection of sampling rates (in Hz) the Opus encoder can use. - /// - public static IReadOnlyCollection AllowedSampleRates { get; } = new ReadOnlyCollection(new[] { 8000, 12000, 16000, 24000, 48000 }); - - /// - /// Gets the collection of channel counts the Opus encoder can use. - /// - public static IReadOnlyCollection AllowedChannelCounts { get; } = new ReadOnlyCollection(new[] { 1, 2 }); - - /// - /// Gets the collection of sample durations (in ms) the Opus encoder can use. - /// - public static IReadOnlyCollection AllowedSampleDurations { get; } = new ReadOnlyCollection(new[] { 5, 10, 20, 40, 60 }); - - /// - /// Gets the default audio format. This is a format configured for 48kHz sampling rate, 2 channels, with music quality preset. - /// - public static AudioFormat Default { get; } = new(48000, 2, VoiceApplication.Music); - - /// - /// Gets the audio sampling rate in Hz. - /// - public int SampleRate { get; } - - /// - /// Gets the audio channel count. - /// - public int ChannelCount { get; } - - /// - /// Gets the voice application, which dictates the quality preset. - /// - public VoiceApplication VoiceApplication { get; } - - /// - /// Creates a new audio format for use with Opus encoder. - /// - /// Audio sampling rate in Hz. - /// Number of audio channels in the data. - /// Encoder preset to use. - public AudioFormat(int sampleRate = 48000, int channelCount = 2, VoiceApplication voiceApplication = VoiceApplication.Music) - { - if (!AllowedSampleRates.Contains(sampleRate)) - throw new ArgumentOutOfRangeException(nameof(sampleRate), "Invalid sample rate specified."); - - if (!AllowedChannelCounts.Contains(channelCount)) - throw new ArgumentOutOfRangeException(nameof(channelCount), "Invalid channel count specified."); - - if (voiceApplication != VoiceApplication.Music && voiceApplication != VoiceApplication.Voice && voiceApplication != VoiceApplication.LowLatency) - throw new ArgumentOutOfRangeException(nameof(voiceApplication), "Invalid voice application specified."); - - this.SampleRate = sampleRate; - this.ChannelCount = channelCount; - this.VoiceApplication = voiceApplication; - } - - /// - /// Calculates a sample size in bytes. - /// - /// Millisecond duration of a sample. - /// Calculated sample size in bytes. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int CalculateSampleSize(int sampleDuration) - { - if (!AllowedSampleDurations.Contains(sampleDuration)) - throw new ArgumentOutOfRangeException(nameof(sampleDuration), "Invalid sample duration specified."); - - // Sample size in bytes is a product of the following: - // - duration in milliseconds - // - number of channels - // - sample rate in kHz - // - size of data (in this case, sizeof(int16_t)) - // which comes down to below: - return sampleDuration * this.ChannelCount * (this.SampleRate / 1000) * 2; - } - - /// - /// Gets the maximum buffer size for decoding. This method should be called when decoding Opus data to PCM, to ensure sufficient buffer size. - /// - /// Buffer size required to decode data. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetMaximumBufferSize() - => this.CalculateMaximumFrameSize(); - - /// - /// Calculates the sample duration. - /// - /// The sample size. - /// An int. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal int CalculateSampleDuration(int sampleSize) - => sampleSize / (this.SampleRate / 1000) / this.ChannelCount / 2 /* sizeof(int16_t) */; - - /// - /// Calculates the frame size. - /// - /// The sample duration. - /// An int. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal int CalculateFrameSize(int sampleDuration) - => sampleDuration * (this.SampleRate / 1000); - - /// - /// Calculates the maximum frame size. - /// - /// An int. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal int CalculateMaximumFrameSize() - => 120 * (this.SampleRate / 1000); - - /// - /// Samples the count to sample size. - /// - /// The sample count. - /// An int. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal int SampleCountToSampleSize(int sampleCount) - => sampleCount * this.ChannelCount * 2 /* sizeof(int16_t) */; - - /// - /// Are the valid. - /// - /// A bool. - internal bool IsValid() - => AllowedSampleRates.Contains(this.SampleRate) && AllowedChannelCounts.Contains(this.ChannelCount) && - (this.VoiceApplication == VoiceApplication.Music || this.VoiceApplication == VoiceApplication.Voice || this.VoiceApplication == VoiceApplication.LowLatency); -} diff --git a/DisCatSharp.VoiceNext/Codec/Helpers.cs b/DisCatSharp.VoiceNext/Codec/Helpers.cs deleted file mode 100644 index f8e069571..000000000 --- a/DisCatSharp.VoiceNext/Codec/Helpers.cs +++ /dev/null @@ -1,53 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace DisCatSharp.VoiceNext.Codec; - -/// -/// The helpers. -/// -internal static class Helpers -{ - /// - /// Fills the buffer with 0. - /// - /// The buffer. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ZeroFill(Span buff) - { - var zero = 0; - var i = 0; - for (; i < buff.Length / 4; i++) - MemoryMarshal.Write(buff, ref zero); - - var remainder = buff.Length % 4; - if (remainder == 0) - return; - - for (; i < buff.Length; i++) - buff[i] = 0; - } -} diff --git a/DisCatSharp.VoiceNext/Codec/Interop.cs b/DisCatSharp.VoiceNext/Codec/Interop.cs deleted file mode 100644 index dcd8e6736..000000000 --- a/DisCatSharp.VoiceNext/Codec/Interop.cs +++ /dev/null @@ -1,408 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Runtime.InteropServices; - -namespace DisCatSharp.VoiceNext.Codec; - -/// -/// This is an interop class. It contains wrapper methods for Opus and Sodium. -/// -internal static partial class Interop -{ - #region Sodium wrapper - /// - /// The sodium library name. - /// - private const string SODIUM_LIBRARY_NAME = "libsodium"; - - /// - /// Gets the Sodium key size for xsalsa20_poly1305 algorithm. - /// - public static int SodiumKeySize { get; } = (int)_SodiumSecretBoxKeySize(); - - /// - /// Gets the Sodium nonce size for xsalsa20_poly1305 algorithm. - /// - public static int SodiumNonceSize { get; } = (int)_SodiumSecretBoxNonceSize(); - - /// - /// Gets the Sodium MAC size for xsalsa20_poly1305 algorithm. - /// - public static int SodiumMacSize { get; } = (int)_SodiumSecretBoxMacSize(); - - /// - /// _S the sodium secret box key size. - /// - /// An UIntPtr. - [LibraryImport(SODIUM_LIBRARY_NAME, EntryPoint = "crypto_secretbox_xsalsa20poly1305_keybytes")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - [return: MarshalAs(UnmanagedType.SysUInt)] - private static partial UIntPtr _SodiumSecretBoxKeySize(); - - /// - /// _S the sodium secret box nonce size. - /// - /// An UIntPtr. - [LibraryImport(SODIUM_LIBRARY_NAME, EntryPoint = "crypto_secretbox_xsalsa20poly1305_noncebytes")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - [return: MarshalAs(UnmanagedType.SysUInt)] - private static partial UIntPtr _SodiumSecretBoxNonceSize(); - - /// - /// _S the sodium secret box mac size. - /// - /// An UIntPtr. - [LibraryImport(SODIUM_LIBRARY_NAME, EntryPoint = "crypto_secretbox_xsalsa20poly1305_macbytes")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - [return: MarshalAs(UnmanagedType.SysUInt)] - private static partial UIntPtr _SodiumSecretBoxMacSize(); - - /// - /// _S the sodium secret box create. - /// - /// The buffer. - /// The message. - /// The message length. - /// The nonce. - /// The key. - /// An int. - [LibraryImport(SODIUM_LIBRARY_NAME, EntryPoint = "crypto_secretbox_easy")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static unsafe partial int _SodiumSecretBoxCreate(byte* buffer, byte* message, ulong messageLength, byte* nonce, byte* key); - - /// - /// _S the sodium secret box open. - /// - /// The buffer. - /// The encrypted message. - /// The encrypted length. - /// The nonce. - /// The key. - /// An int. - [LibraryImport(SODIUM_LIBRARY_NAME, EntryPoint = "crypto_secretbox_open_easy")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static unsafe partial int _SodiumSecretBoxOpen(byte* buffer, byte* encryptedMessage, ulong encryptedLength, byte* nonce, byte* key); - - /// - /// Encrypts supplied buffer using xsalsa20_poly1305 algorithm, using supplied key and nonce to perform encryption. - /// - /// Contents to encrypt. - /// Buffer to encrypt to. - /// Key to use for encryption. - /// Nonce to use for encryption. - /// Encryption status. - public static unsafe int Encrypt(ReadOnlySpan source, Span target, ReadOnlySpan key, ReadOnlySpan nonce) - { - var status = 0; - fixed (byte* sourcePtr = &source.GetPinnableReference()) - fixed (byte* targetPtr = &target.GetPinnableReference()) - fixed (byte* keyPtr = &key.GetPinnableReference()) - fixed (byte* noncePtr = &nonce.GetPinnableReference()) - status = _SodiumSecretBoxCreate(targetPtr, sourcePtr, (ulong)source.Length, noncePtr, keyPtr); - - return status; - } - - /// - /// Decrypts supplied buffer using xsalsa20_poly1305 algorithm, using supplied key and nonce to perform decryption. - /// - /// Buffer to decrypt from. - /// Decrypted message buffer. - /// Key to use for decryption. - /// Nonce to use for decryption. - /// Decryption status. - public static unsafe int Decrypt(ReadOnlySpan source, Span target, ReadOnlySpan key, ReadOnlySpan nonce) - { - var status = 0; - fixed (byte* sourcePtr = &source.GetPinnableReference()) - fixed (byte* targetPtr = &target.GetPinnableReference()) - fixed (byte* keyPtr = &key.GetPinnableReference()) - fixed (byte* noncePtr = &nonce.GetPinnableReference()) - status = _SodiumSecretBoxOpen(targetPtr, sourcePtr, (ulong)source.Length, noncePtr, keyPtr); - - return status; - } - #endregion - - #region Opus wrapper - /// - /// The opus library name. - /// - private const string OPUS_LIBRARY_NAME = "libopus"; - - /// - /// _S the opus create encoder. - /// - /// The sample rate. - /// The channels. - /// The application. - /// The error. - /// An IntPtr. - [LibraryImport(OPUS_LIBRARY_NAME, EntryPoint = "opus_encoder_create")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial IntPtr _OpusCreateEncoder(int sampleRate, int channels, int application, out OpusError error); - - /// - /// Opuses the destroy encoder. - /// - /// The encoder. - [LibraryImport(OPUS_LIBRARY_NAME, EntryPoint = "opus_encoder_destroy")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - public static partial void OpusDestroyEncoder(IntPtr encoder); - - /// - /// _S the opus encode. - /// - /// The encoder. - /// The pcm data. - /// The frame size. - /// The data. - /// The max data bytes. - /// An int. - [LibraryImport(OPUS_LIBRARY_NAME, EntryPoint = "opus_encode")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static unsafe partial int _OpusEncode(IntPtr encoder, byte* pcmData, int frameSize, byte* data, int maxDataBytes); - - /// - /// _S the opus encoder control. - /// - /// The encoder. - /// The request. - /// The value. - /// An OpusError. - [LibraryImport(OPUS_LIBRARY_NAME, EntryPoint = "opus_encoder_ctl")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial OpusError _OpusEncoderControl(IntPtr encoder, OpusControl request, int value); - - /// - /// _S the opus create decoder. - /// - /// The sample rate. - /// The channels. - /// The error. - /// An IntPtr. - [LibraryImport(OPUS_LIBRARY_NAME, EntryPoint = "opus_decoder_create")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial IntPtr _OpusCreateDecoder(int sampleRate, int channels, out OpusError error); - - /// - /// Opuses the destroy decoder. - /// - /// The decoder. - [LibraryImport(OPUS_LIBRARY_NAME, EntryPoint = "opus_decoder_destroy")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - public static partial void OpusDestroyDecoder(IntPtr decoder); - - /// - /// _S the opus decode. - /// - /// The decoder. - /// The opus data. - /// The opus data length. - /// The data. - /// The frame size. - /// The decode fec. - /// An int. - [LibraryImport(OPUS_LIBRARY_NAME, EntryPoint = "opus_decode")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static unsafe partial int _OpusDecode(IntPtr decoder, byte* opusData, int opusDataLength, byte* data, int frameSize, int decodeFec); - - /// - /// _S the opus get packet channel count. - /// - /// The opus data. - /// An int. - [LibraryImport(OPUS_LIBRARY_NAME, EntryPoint = "opus_packet_get_nb_channels")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static unsafe partial int _OpusGetPacketChannelCount(byte* opusData); - - /// - /// _S the opus get packet frame count. - /// - /// The opus data. - /// The length. - /// An int. - [LibraryImport(OPUS_LIBRARY_NAME, EntryPoint = "opus_packet_get_nb_frames")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static unsafe partial int _OpusGetPacketFrameCount(byte* opusData, int length); - - /// - /// _S the opus get packet sample per frame count. - /// - /// The opus data. - /// The sampling rate. - /// An int. - [LibraryImport(OPUS_LIBRARY_NAME, EntryPoint = "opus_packet_get_samples_per_frame")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static unsafe partial int _OpusGetPacketSamplePerFrameCount(byte* opusData, int samplingRate); - - /// - /// _S the opus decoder control. - /// - /// The decoder. - /// The request. - /// The value. - /// An int. - [LibraryImport(OPUS_LIBRARY_NAME, EntryPoint = "opus_decoder_ctl")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial int _OpusDecoderControl(IntPtr decoder, OpusControl request, out int value); - - /// - /// Opuses the create encoder. - /// - /// The audio format. - /// An IntPtr. - public static IntPtr OpusCreateEncoder(AudioFormat audioFormat) - { - var encoder = _OpusCreateEncoder(audioFormat.SampleRate, audioFormat.ChannelCount, (int)audioFormat.VoiceApplication, out var error); - return error != OpusError.Ok ? throw new Exception($"Could not instantiate Opus encoder: {error} ({(int)error}).") : encoder; - } - - /// - /// Opuses the set encoder option. - /// - /// The encoder. - /// The option. - /// The value. - public static void OpusSetEncoderOption(IntPtr encoder, OpusControl option, int value) - { - var error = OpusError.Ok; - if ((error = _OpusEncoderControl(encoder, option, value)) != OpusError.Ok) - throw new Exception($"Could not set Opus encoder option: {error} ({(int)error})."); - } - - /// - /// Opuses the encode. - /// - /// The encoder. - /// The pcm. - /// The frame size. - /// The opus. - public static unsafe void OpusEncode(IntPtr encoder, ReadOnlySpan pcm, int frameSize, ref Span opus) - { - var len = 0; - - fixed (byte* pcmPtr = &pcm.GetPinnableReference()) - fixed (byte* opusPtr = &opus.GetPinnableReference()) - len = _OpusEncode(encoder, pcmPtr, frameSize, opusPtr, opus.Length); - - if (len < 0) - { - var error = (OpusError)len; - throw new Exception($"Could not encode PCM data to Opus: {error} ({(int)error})."); - } - - opus = opus[..len]; - } - - /// - /// Opuses the create decoder. - /// - /// The audio format. - /// An IntPtr. - public static IntPtr OpusCreateDecoder(AudioFormat audioFormat) - { - var decoder = _OpusCreateDecoder(audioFormat.SampleRate, audioFormat.ChannelCount, out var error); - return error != OpusError.Ok ? throw new Exception($"Could not instantiate Opus decoder: {error} ({(int)error}).") : decoder; - } - - /// - /// Opuses the decode. - /// - /// The decoder. - /// The opus. - /// The frame size. - /// The pcm. - /// If true, use fec. - /// An int. - public static unsafe int OpusDecode(IntPtr decoder, ReadOnlySpan opus, int frameSize, Span pcm, bool useFec) - { - var len = 0; - - fixed (byte* opusPtr = &opus.GetPinnableReference()) - fixed (byte* pcmPtr = &pcm.GetPinnableReference()) - len = _OpusDecode(decoder, opusPtr, opus.Length, pcmPtr, frameSize, useFec ? 1 : 0); - - if (len < 0) - { - var error = (OpusError)len; - throw new Exception($"Could not decode PCM data from Opus: {error} ({(int)error})."); - } - - return len; - } - - /// - /// Opuses the decode. - /// - /// The decoder. - /// The frame size. - /// The pcm. - /// An int. - public static unsafe int OpusDecode(IntPtr decoder, int frameSize, Span pcm) - { - var len = 0; - - fixed (byte* pcmPtr = &pcm.GetPinnableReference()) - len = _OpusDecode(decoder, null, 0, pcmPtr, frameSize, 1); - - if (len < 0) - { - var error = (OpusError)len; - throw new Exception($"Could not decode PCM data from Opus: {error} ({(int)error})."); - } - - return len; - } - - /// - /// Opuses the get packet metrics. - /// - /// The opus. - /// The sampling rate. - /// The channels. - /// The frames. - /// The samples per frame. - /// The frame size. - public static unsafe void OpusGetPacketMetrics(ReadOnlySpan opus, int samplingRate, out int channels, out int frames, out int samplesPerFrame, out int frameSize) - { - fixed (byte* opusPtr = &opus.GetPinnableReference()) - { - frames = _OpusGetPacketFrameCount(opusPtr, opus.Length); - samplesPerFrame = _OpusGetPacketSamplePerFrameCount(opusPtr, samplingRate); - channels = _OpusGetPacketChannelCount(opusPtr); - } - - frameSize = frames * samplesPerFrame; - } - - /// - /// Opuses the get last packet duration. - /// - /// The decoder. - /// The sample count. - public static void OpusGetLastPacketDuration(IntPtr decoder, out int sampleCount) - => _OpusDecoderControl(decoder, OpusControl.GetLastPacketDuration, out sampleCount); - #endregion -} diff --git a/DisCatSharp.VoiceNext/Codec/Opus.cs b/DisCatSharp.VoiceNext/Codec/Opus.cs deleted file mode 100644 index a844520b7..000000000 --- a/DisCatSharp.VoiceNext/Codec/Opus.cs +++ /dev/null @@ -1,288 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; - -namespace DisCatSharp.VoiceNext.Codec; - -/// -/// The opus. -/// -internal sealed class Opus : IDisposable -{ - /// - /// Gets the audio format. - /// - public AudioFormat AudioFormat { get; } - - /// - /// Gets the encoder. - /// - private readonly IntPtr _encoder; - - /// - /// Gets the managed decoders. - /// - private readonly List _managedDecoders; - - /// - /// Initializes a new instance of the class. - /// - /// The audio format. - public Opus(AudioFormat audioFormat) - { - if (!audioFormat.IsValid()) - throw new ArgumentException("Invalid audio format specified.", nameof(audioFormat)); - - this.AudioFormat = audioFormat; - this._encoder = Interop.OpusCreateEncoder(this.AudioFormat); - - // Set appropriate encoder options - var sig = OpusSignal.Auto; - switch (this.AudioFormat.VoiceApplication) - { - case VoiceApplication.Music: - sig = OpusSignal.Music; - break; - - case VoiceApplication.Voice: - sig = OpusSignal.Voice; - break; - } - Interop.OpusSetEncoderOption(this._encoder, OpusControl.SetSignal, (int)sig); - Interop.OpusSetEncoderOption(this._encoder, OpusControl.SetPacketLossPercent, 15); - Interop.OpusSetEncoderOption(this._encoder, OpusControl.SetInBandFec, 1); - Interop.OpusSetEncoderOption(this._encoder, OpusControl.SetBitrate, 131072); - - this._managedDecoders = new List(); - } - - /// - /// Encodes the Opus. - /// - /// The pcm. - /// The target. - public void Encode(ReadOnlySpan pcm, ref Span target) - { - if (pcm.Length != target.Length) - throw new ArgumentException("PCM and Opus buffer lengths need to be equal.", nameof(target)); - - var duration = this.AudioFormat.CalculateSampleDuration(pcm.Length); - var frameSize = this.AudioFormat.CalculateFrameSize(duration); - var sampleSize = this.AudioFormat.CalculateSampleSize(duration); - - if (pcm.Length != sampleSize) - throw new ArgumentException("Invalid PCM sample size.", nameof(target)); - - Interop.OpusEncode(this._encoder, pcm, frameSize, ref target); - } - - /// - /// Decodes the Opus. - /// - /// The decoder. - /// The opus. - /// The target. - /// If true, use fec. - /// The output format. - public void Decode(OpusDecoder decoder, ReadOnlySpan opus, ref Span target, bool useFec, out AudioFormat outputFormat) - { - //if (target.Length != this.AudioFormat.CalculateMaximumFrameSize()) - // throw new ArgumentException("PCM target buffer size needs to be equal to maximum buffer size for specified audio format.", nameof(target)); - - Interop.OpusGetPacketMetrics(opus, this.AudioFormat.SampleRate, out var channels, out var frames, out var samplesPerFrame, out var frameSize); - outputFormat = this.AudioFormat.ChannelCount != channels ? new AudioFormat(this.AudioFormat.SampleRate, channels, this.AudioFormat.VoiceApplication) : this.AudioFormat; - - if (decoder.AudioFormat.ChannelCount != channels) - decoder.Initialize(outputFormat); - - var sampleCount = Interop.OpusDecode(decoder.Decoder, opus, frameSize, target, useFec); - - var sampleSize = outputFormat.SampleCountToSampleSize(sampleCount); - target = target[..sampleSize]; - } - - /// - /// Processes the packet loss. - /// - /// The decoder. - /// The frame size. - /// The target. - public void ProcessPacketLoss(OpusDecoder decoder, int frameSize, ref Span target) => Interop.OpusDecode(decoder.Decoder, frameSize, target); - - /// - /// Gets the last packet sample count. - /// - /// The decoder. - /// An int. - public int GetLastPacketSampleCount(OpusDecoder decoder) - { - Interop.OpusGetLastPacketDuration(decoder.Decoder, out var sampleCount); - return sampleCount; - } - - /// - /// Creates the decoder. - /// - /// An OpusDecoder. - public OpusDecoder CreateDecoder() - { - lock (this._managedDecoders) - { - var managedDecoder = new OpusDecoder(this); - this._managedDecoders.Add(managedDecoder); - return managedDecoder; - } - } - - /// - /// Destroys the decoder. - /// - /// The decoder. - public void DestroyDecoder(OpusDecoder decoder) - { - lock (this._managedDecoders) - { - if (!this._managedDecoders.Contains(decoder)) - return; - - this._managedDecoders.Remove(decoder); - decoder.Dispose(); - } - } - - /// - /// Disposes the Opus. - /// - public void Dispose() - { - Interop.OpusDestroyEncoder(this._encoder); - - lock (this._managedDecoders) - { - foreach (var decoder in this._managedDecoders) - decoder.Dispose(); - } - } -} - -/// -/// Represents an Opus decoder. -/// -public class OpusDecoder : IDisposable -{ - /// - /// Gets the audio format produced by this decoder. - /// - public AudioFormat AudioFormat { get; private set; } - - /// - /// Gets the opus. - /// - internal Opus Opus { get; } - /// - /// Gets the decoder. - /// - internal IntPtr Decoder { get; private set; } - - private volatile bool _isDisposed; - - /// - /// Initializes a new instance of the class. - /// - /// The managed opus. - internal OpusDecoder(Opus managedOpus) - { - this.Opus = managedOpus; - } - - /// - /// Used to lazily initialize the decoder to make sure we're - /// using the correct output format, this way we don't end up - /// creating more decoders than we need. - /// - /// - internal void Initialize(AudioFormat outputFormat) - { - if (this.Decoder != IntPtr.Zero) - Interop.OpusDestroyDecoder(this.Decoder); - - this.AudioFormat = outputFormat; - - this.Decoder = Interop.OpusCreateDecoder(outputFormat); - } - - /// - /// Disposes of this Opus decoder. - /// - public void Dispose() - { - if (this._isDisposed) - return; - - this._isDisposed = true; - if (this.Decoder != IntPtr.Zero) - Interop.OpusDestroyDecoder(this.Decoder); - GC.SuppressFinalize(this); - } -} - -/// -/// The opus error. -/// -[Flags] -internal enum OpusError -{ - Ok = 0, - BadArgument = -1, - BufferTooSmall = -2, - InternalError = -3, - InvalidPacket = -4, - Unimplemented = -5, - InvalidState = -6, - AllocationFailure = -7 -} - -/// -/// The opus control. -/// -internal enum OpusControl : int -{ - SetBitrate = 4002, - SetBandwidth = 4008, - SetInBandFec = 4012, - SetPacketLossPercent = 4014, - SetSignal = 4024, - ResetState = 4028, - GetLastPacketDuration = 4039 -} - -/// -/// The opus signal. -/// -internal enum OpusSignal : int -{ - Auto = -1000, - Voice = 3001, - Music = 3002, -} diff --git a/DisCatSharp.VoiceNext/Codec/Rtp.cs b/DisCatSharp.VoiceNext/Codec/Rtp.cs deleted file mode 100644 index 1a4653828..000000000 --- a/DisCatSharp.VoiceNext/Codec/Rtp.cs +++ /dev/null @@ -1,158 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Buffers.Binary; - -namespace DisCatSharp.VoiceNext.Codec; - -/// -/// The rtp. -/// -internal sealed class Rtp : IDisposable -{ - /// - /// The header size. - /// - public const int HEADER_SIZE = 12; - - /// - /// The rtp no extension. - /// - private const byte RTP_NO_EXTENSION = 0x80; - /// - /// The rtp extension. - /// - private const byte RTP_EXTENSION = 0x90; - /// - /// The rtp version. - /// - private const byte RTP_VERSION = 0x78; - - /// - /// Initializes a new instance of the class. - /// - public Rtp() - { } - - /// - /// Encodes the header. - /// - /// The sequence. - /// The timestamp. - /// The ssrc. - /// The target. - public void EncodeHeader(ushort sequence, uint timestamp, uint ssrc, Span target) - { - if (target.Length < HEADER_SIZE) - throw new ArgumentException("Header buffer is too short.", nameof(target)); - - target[0] = RTP_NO_EXTENSION; - target[1] = RTP_VERSION; - - // Write data big endian - BinaryPrimitives.WriteUInt16BigEndian(target[2..], sequence); // header + magic - BinaryPrimitives.WriteUInt32BigEndian(target[4..], timestamp); // header + magic + sizeof(sequence) - BinaryPrimitives.WriteUInt32BigEndian(target[8..], ssrc); // header + magic + sizeof(sequence) + sizeof(timestamp) - } - - /// - /// Are the rtp header. - /// - /// The source. - /// A bool. - public bool IsRtpHeader(ReadOnlySpan source) => source.Length >= HEADER_SIZE && (source[0] == RTP_NO_EXTENSION || source[0] == RTP_EXTENSION) && source[1] == RTP_VERSION; - - /// - /// Decodes the header. - /// - /// The source. - /// The sequence. - /// The timestamp. - /// The ssrc. - /// If true, has extension. - public void DecodeHeader(ReadOnlySpan source, out ushort sequence, out uint timestamp, out uint ssrc, out bool hasExtension) - { - if (source.Length < HEADER_SIZE) - throw new ArgumentException("Header buffer is too short.", nameof(source)); - - if ((source[0] != RTP_NO_EXTENSION && source[0] != RTP_EXTENSION) || source[1] != RTP_VERSION) - throw new ArgumentException("Invalid RTP header.", nameof(source)); - - hasExtension = source[0] == RTP_EXTENSION; - - // Read data big endian - sequence = BinaryPrimitives.ReadUInt16BigEndian(source[2..]); - timestamp = BinaryPrimitives.ReadUInt32BigEndian(source[4..]); - ssrc = BinaryPrimitives.ReadUInt32BigEndian(source[8..]); - } - - /// - /// Calculates the packet size. - /// - /// The encrypted length. - /// The encryption mode. - /// An int. - public int CalculatePacketSize(int encryptedLength, EncryptionMode encryptionMode) => - encryptionMode switch - { - EncryptionMode.XSalsa20Poly1305 => HEADER_SIZE + encryptedLength, - EncryptionMode.XSalsa20Poly1305Suffix => HEADER_SIZE + encryptedLength + Interop.SodiumNonceSize, - EncryptionMode.XSalsa20Poly1305Lite => HEADER_SIZE + encryptedLength + 4, - _ => throw new ArgumentException("Unsupported encryption mode.", nameof(encryptionMode)), - }; - - /// - /// Gets the data from packet. - /// - /// The packet. - /// The data. - /// The encryption mode. - public void GetDataFromPacket(ReadOnlySpan packet, out ReadOnlySpan data, EncryptionMode encryptionMode) - { - switch (encryptionMode) - { - case EncryptionMode.XSalsa20Poly1305: - data = packet[HEADER_SIZE..]; - return; - - case EncryptionMode.XSalsa20Poly1305Suffix: - data = packet.Slice(HEADER_SIZE, packet.Length - HEADER_SIZE - Interop.SodiumNonceSize); - return; - - case EncryptionMode.XSalsa20Poly1305Lite: - data = packet.Slice(HEADER_SIZE, packet.Length - HEADER_SIZE - 4); - break; - - default: - throw new ArgumentException("Unsupported encryption mode.", nameof(encryptionMode)); - } - } - - /// - /// Disposes the Rtp. - /// - public void Dispose() - { - - } -} diff --git a/DisCatSharp.VoiceNext/Codec/Sodium.cs b/DisCatSharp.VoiceNext/Codec/Sodium.cs deleted file mode 100644 index b1b5e03a0..000000000 --- a/DisCatSharp.VoiceNext/Codec/Sodium.cs +++ /dev/null @@ -1,293 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Buffers.Binary; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Security.Cryptography; - -namespace DisCatSharp.VoiceNext.Codec; - -/// -/// The sodium. -/// -internal sealed class Sodium : IDisposable -{ - /// - /// Gets the supported modes. - /// - public static IReadOnlyDictionary SupportedModes { get; } - - /// - /// Gets the nonce size. - /// - public static int NonceSize => Interop.SodiumNonceSize; - - /// - /// Gets the c s p r n g. - /// - private readonly RandomNumberGenerator _csprng; - - /// - /// Gets the buffer. - /// - private readonly byte[] _buffer; - - /// - /// Gets the key. - /// - private readonly ReadOnlyMemory _key; - - /// - /// Initializes a new instance of the class. - /// - static Sodium() - { - SupportedModes = new ReadOnlyDictionary(new Dictionary() - { - ["xsalsa20_poly1305_lite"] = EncryptionMode.XSalsa20Poly1305Lite, - ["xsalsa20_poly1305_suffix"] = EncryptionMode.XSalsa20Poly1305Suffix, - ["xsalsa20_poly1305"] = EncryptionMode.XSalsa20Poly1305 - }); - } - - /// - /// Initializes a new instance of the class. - /// - /// The key. - public Sodium(ReadOnlyMemory key) - { - if (key.Length != Interop.SodiumKeySize) - throw new ArgumentException($"Invalid Sodium key size. Key needs to have a length of {Interop.SodiumKeySize} bytes.", nameof(key)); - - this._key = key; - - this._csprng = RandomNumberGenerator.Create(); - this._buffer = new byte[Interop.SodiumNonceSize]; - } - - /// - /// Generates the nonce. - /// - /// The rtp header. - /// The target. - public void GenerateNonce(ReadOnlySpan rtpHeader, Span target) - { - if (rtpHeader.Length != Rtp.HEADER_SIZE) - throw new ArgumentException($"RTP header needs to have a length of exactly {Rtp.HEADER_SIZE} bytes.", nameof(rtpHeader)); - - if (target.Length != Interop.SodiumNonceSize) - throw new ArgumentException($"Invalid nonce buffer size. Target buffer for the nonce needs to have a capacity of {Interop.SodiumNonceSize} bytes.", nameof(target)); - - // Write the header to the beginning of the span. - rtpHeader.CopyTo(target); - - // Zero rest of the span. - Helpers.ZeroFill(target[rtpHeader.Length..]); - } - - /// - /// Generates the nonce. - /// - /// The target. - public void GenerateNonce(Span target) - { - if (target.Length != Interop.SodiumNonceSize) - throw new ArgumentException($"Invalid nonce buffer size. Target buffer for the nonce needs to have a capacity of {Interop.SodiumNonceSize} bytes.", nameof(target)); - - this._csprng.GetBytes(this._buffer); - this._buffer.AsSpan().CopyTo(target); - } - - /// - /// Generates the nonce. - /// - /// The nonce. - /// The target. - public void GenerateNonce(uint nonce, Span target) - { - if (target.Length != Interop.SodiumNonceSize) - throw new ArgumentException($"Invalid nonce buffer size. Target buffer for the nonce needs to have a capacity of {Interop.SodiumNonceSize} bytes.", nameof(target)); - - // Write the uint to memory - BinaryPrimitives.WriteUInt32BigEndian(target, nonce); - - // Zero rest of the buffer. - Helpers.ZeroFill(target[4..]); - } - - /// - /// Appends the nonce. - /// - /// The nonce. - /// The target. - /// The encryption mode. - public void AppendNonce(ReadOnlySpan nonce, Span target, EncryptionMode encryptionMode) - { - switch (encryptionMode) - { - case EncryptionMode.XSalsa20Poly1305: - return; - - case EncryptionMode.XSalsa20Poly1305Suffix: - nonce.CopyTo(target[^12..]); - return; - - case EncryptionMode.XSalsa20Poly1305Lite: - nonce[..4].CopyTo(target[^4..]); - return; - - default: - throw new ArgumentException("Unsupported encryption mode.", nameof(encryptionMode)); - } - } - - /// - /// Gets the nonce. - /// - /// The source. - /// The target. - /// The encryption mode. - public void GetNonce(ReadOnlySpan source, Span target, EncryptionMode encryptionMode) - { - if (target.Length != Interop.SodiumNonceSize) - throw new ArgumentException($"Invalid nonce buffer size. Target buffer for the nonce needs to have a capacity of {Interop.SodiumNonceSize} bytes.", nameof(target)); - - switch (encryptionMode) - { - case EncryptionMode.XSalsa20Poly1305: - source[..12].CopyTo(target); - return; - - case EncryptionMode.XSalsa20Poly1305Suffix: - source[^Interop.SodiumNonceSize..].CopyTo(target); - return; - - case EncryptionMode.XSalsa20Poly1305Lite: - source[^4..].CopyTo(target); - return; - - default: - throw new ArgumentException("Unsupported encryption mode.", nameof(encryptionMode)); - } - } - - /// - /// Encrypts the Sodium. - /// - /// The source. - /// The target. - /// The nonce. - public void Encrypt(ReadOnlySpan source, Span target, ReadOnlySpan nonce) - { - if (nonce.Length != Interop.SodiumNonceSize) - throw new ArgumentException($"Invalid nonce size. Nonce needs to have a length of {Interop.SodiumNonceSize} bytes.", nameof(nonce)); - - if (target.Length != Interop.SodiumMacSize + source.Length) - throw new ArgumentException($"Invalid target buffer size. Target buffer needs to have a length that is a sum of input buffer length and Sodium MAC size ({Interop.SodiumMacSize} bytes).", nameof(target)); - - int result; - if ((result = Interop.Encrypt(source, target, this._key.Span, nonce)) != 0) - throw new CryptographicException($"Could not encrypt the buffer. Sodium returned code {result}."); - } - - /// - /// Decrypts the Sodium. - /// - /// The source. - /// The target. - /// The nonce. - public void Decrypt(ReadOnlySpan source, Span target, ReadOnlySpan nonce) - { - if (nonce.Length != Interop.SodiumNonceSize) - throw new ArgumentException($"Invalid nonce size. Nonce needs to have a length of {Interop.SodiumNonceSize} bytes.", nameof(nonce)); - - if (target.Length != source.Length - Interop.SodiumMacSize) - throw new ArgumentException($"Invalid target buffer size. Target buffer needs to have a length that is input buffer decreased by Sodium MAC size ({Interop.SodiumMacSize} bytes).", nameof(target)); - - int result; - if ((result = Interop.Decrypt(source, target, this._key.Span, nonce)) != 0) - throw new CryptographicException($"Could not decrypt the buffer. Sodium returned code {result}."); - } - - /// - /// Disposes the Sodium. - /// - public void Dispose() => this._csprng.Dispose(); - - /// - /// Selects the mode. - /// - /// The available modes. - /// A KeyValuePair. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static KeyValuePair SelectMode(IEnumerable availableModes) - { - foreach (var kvMode in SupportedModes) - if (availableModes.Contains(kvMode.Key)) - return kvMode; - - throw new CryptographicException("Could not negotiate Sodium encryption modes, as none of the modes offered by Discord are supported. This is usually an indicator that something went very wrong."); - } - - /// - /// Calculates the target size. - /// - /// The source. - /// An int. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CalculateTargetSize(ReadOnlySpan source) - => source.Length + Interop.SodiumMacSize; - - /// - /// Calculates the source size. - /// - /// The source. - /// An int. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CalculateSourceSize(ReadOnlySpan source) - => source.Length - Interop.SodiumMacSize; -} - -/// -/// Specifies an encryption mode to use with Sodium. -/// -public enum EncryptionMode -{ - /// - /// The nonce is an incrementing uint32 value. It is encoded as big endian value at the beginning of the nonce buffer. The 4 bytes are also appended at the end of the packet. - /// - XSalsa20Poly1305Lite, - - /// - /// The nonce consists of random bytes. It is appended at the end of a packet. - /// - XSalsa20Poly1305Suffix, - - /// - /// The nonce consists of the RTP header. Nothing is appended to the packet. - /// - XSalsa20Poly1305 -} diff --git a/DisCatSharp.VoiceNext/DisCatSharp.VoiceNext.csproj b/DisCatSharp.VoiceNext/DisCatSharp.VoiceNext.csproj deleted file mode 100644 index 6570948ac..000000000 --- a/DisCatSharp.VoiceNext/DisCatSharp.VoiceNext.csproj +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - DisCatSharp.VoiceNext - DisCatSharp.VoiceNext - true - - - - DisCatSharp.VoiceNext - -DisCatSharp Voice Next Extension - -Easy made audio player for discord bots. - -Documentation: https://docs.discatsharp.tech/articles/modules/audio/voicenext/prerequisites.html - - DisCatSharp,Discord API Wrapper,Discord,Bots,Discord Bots,AITSYS,Net6,Voice,Audio Player - - - - - - - - - - - - - - - - - diff --git a/DisCatSharp.VoiceNext/DiscordClientExtensions.cs b/DisCatSharp.VoiceNext/DiscordClientExtensions.cs deleted file mode 100644 index d2a9f1aba..000000000 --- a/DisCatSharp.VoiceNext/DiscordClientExtensions.cs +++ /dev/null @@ -1,139 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Threading.Tasks; - -using DisCatSharp.Entities; -using DisCatSharp.Enums; - -namespace DisCatSharp.VoiceNext; - -/// -/// The discord client extensions. -/// -public static class DiscordClientExtensions -{ - /// - /// Creates a new VoiceNext client with default settings. - /// - /// Discord client to create VoiceNext instance for. - /// VoiceNext client instance. - public static VoiceNextExtension UseVoiceNext(this DiscordClient client) - => UseVoiceNext(client, new VoiceNextConfiguration()); - - /// - /// Creates a new VoiceNext client with specified settings. - /// - /// Discord client to create VoiceNext instance for. - /// Configuration for the VoiceNext client. - /// VoiceNext client instance. - public static VoiceNextExtension UseVoiceNext(this DiscordClient client, VoiceNextConfiguration config) - { - if (client.GetExtension() != null) - throw new InvalidOperationException("VoiceNext is already enabled for that client."); - - var vnext = new VoiceNextExtension(config); - client.AddExtension(vnext); - return vnext; - } - - /// - /// Creates new VoiceNext clients on all shards in a given sharded client. - /// - /// Discord sharded client to create VoiceNext instances for. - /// Configuration for the VoiceNext clients. - /// A dictionary of created VoiceNext clients. - public static async Task> UseVoiceNextAsync(this DiscordShardedClient client, VoiceNextConfiguration config) - { - var modules = new Dictionary(); - await client.InitializeShardsAsync().ConfigureAwait(false); - - foreach (var shard in client.ShardClients.Select(xkvp => xkvp.Value)) - { - var vnext = shard.GetExtension(); - vnext ??= shard.UseVoiceNext(config); - - modules[shard.ShardId] = vnext; - } - - return new ReadOnlyDictionary(modules); - } - - /// - /// Gets the active instance of VoiceNext client for the DiscordClient. - /// - /// Discord client to get VoiceNext instance for. - /// VoiceNext client instance. - public static VoiceNextExtension GetVoiceNext(this DiscordClient client) - => client.GetExtension(); - - /// - /// Retrieves a instance for each shard. - /// - /// The shard client to retrieve instances from. - /// A dictionary containing instances for each shard. - public static async Task> GetVoiceNextAsync(this DiscordShardedClient client) - { - await client.InitializeShardsAsync().ConfigureAwait(false); - var extensions = new Dictionary(); - - foreach (var shard in client.ShardClients.Values) - { - extensions.Add(shard.ShardId, shard.GetExtension()); - } - - return new ReadOnlyDictionary(extensions); - } - - /// - /// Connects to this voice channel using VoiceNext. - /// - /// Channel to connect to. - /// If successful, the VoiceNext connection. - public static Task ConnectAsync(this DiscordChannel channel) - { - if (channel == null) - throw new NullReferenceException(); - - if (channel.Guild == null) - throw new InvalidOperationException("VoiceNext can only be used with guild channels."); - - if (channel.Type != ChannelType.Voice && channel.Type != ChannelType.Stage) - throw new InvalidOperationException("You can only connect to voice or stage channels."); - - if (channel.Discord is not DiscordClient discord || discord == null) - throw new NullReferenceException(); - - var vnext = discord.GetVoiceNext(); - if (vnext == null) - throw new InvalidOperationException("VoiceNext is not initialized for this Discord client."); - - var vnc = vnext.GetConnection(channel.Guild); - return vnc != null - ? throw new InvalidOperationException("VoiceNext is already connected in this guild.") - : vnext.ConnectAsync(channel); - } -} diff --git a/DisCatSharp.VoiceNext/Entities/AudioSender.cs b/DisCatSharp.VoiceNext/Entities/AudioSender.cs deleted file mode 100644 index 9a817e17e..000000000 --- a/DisCatSharp.VoiceNext/Entities/AudioSender.cs +++ /dev/null @@ -1,167 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -using DisCatSharp.Entities; -using DisCatSharp.VoiceNext.Codec; - -namespace DisCatSharp.VoiceNext.Entities; - -/// -/// The audio sender. -/// -internal class AudioSender : IDisposable -{ - // starting the counter a full wrap ahead handles an edge case where the VERY first packets - // we see are right around the wraparound line. - private ulong _sequenceBase = 1 << 16; - - private SequenceWrapState _currentSequenceWrapState = SequenceWrapState.AssumeNextHighSequenceIsOutOfOrder; - - private enum SequenceWrapState - { - Normal, - AssumeNextLowSequenceIsOverflow, - AssumeNextHighSequenceIsOutOfOrder, - } - - /// - /// Gets the s s r c. - /// - public uint Ssrc { get; } - - /// - /// Gets the id. - /// - public ulong Id => this.User?.Id ?? 0; - - /// - /// Gets the decoder. - /// - public OpusDecoder Decoder { get; } - - /// - /// Gets or sets the user. - /// - public DiscordUser User { get; set; } = null; - - /// - /// Gets or sets the last sequence. - /// - public ulong? LastTrueSequence { get; set; } = null; - - /// - /// Initializes a new instance of the class. - /// - /// The ssrc. - /// The decoder. - public AudioSender(uint ssrc, OpusDecoder decoder) - { - this.Ssrc = ssrc; - this.Decoder = decoder; - } - - /// - /// Disposes . - /// - public void Dispose() => this.Decoder?.Dispose(); - - - /// - /// Accepts the 16-bit sequence number from the next RTP header in the associated stream and - /// uses heuristics to (attempt to) convert it into a 64-bit counter that takes into account - /// overflow wrapping around to zero. - /// - /// This method only works properly if it is called for every sequence number that we - /// see in the stream. - /// - /// - /// The 16-bit sequence number from the next RTP header. - /// - /// - /// Our best-effort guess of the value that would - /// have been, if the server had given us a 64-bit integer instead of a 16-bit one. - /// - public ulong GetTrueSequenceAfterWrapping(ushort originalSequence) - { - // section off a smallish zone at either end of the 16-bit integer range. whenever the - // sequence numbers creep into the higher zone, we start keeping an eye out for when - // sequence numbers suddenly start showing up in the lower zone. we expect this to mean - // that the sequence numbers overflowed and wrapped around. there's a bit of a balance - // when determining an appropriate size for the buffer zone: if it's too small, then a - // brief (but recoverable) network interruption could cause us to miss the lead-up to - // the overflow. on the other hand, if it's too large, then such a network interruption - // could cause us to misinterpret a normal sequence for one that's out-of-order. - // - // at 20 milliseconds per packet, 3,000 packets means that the buffer zone is one minute - // on either side. in other words, as long as we're getting packets delivered within a - // minute or so of when they should be, the 64-bit sequence numbers coming out of this - // method will be perfectly consistent with reality. - const ushort OVERFLOW_BUFFER_ZONE = 3_000; - const ushort LOW_THRESHOLD = OVERFLOW_BUFFER_ZONE; - const ushort HIGH_THRESHOLD = ushort.MaxValue - OVERFLOW_BUFFER_ZONE; - - ulong wrappingAdjustment = 0; - switch (this._currentSequenceWrapState) - { - case SequenceWrapState.Normal when originalSequence > HIGH_THRESHOLD: - // we were going about our business up to this point. the sequence numbers have - // gotten a bit high, so let's start looking out for any sequence numbers that - // are suddenly WAY lower than where they are right now. - this._currentSequenceWrapState = SequenceWrapState.AssumeNextLowSequenceIsOverflow; - break; - - case SequenceWrapState.AssumeNextLowSequenceIsOverflow when originalSequence < LOW_THRESHOLD: - // we had seen some sequence numbers that got a bit high, and now we see this - // sequence number that's WAY lower than before. this is a classic sign that - // the sequence numbers have wrapped around. in order to present a consistently - // increasing "true" sequence number, add another 65,536 and keep counting. if - // we see another high sequence number in the near future, assume that it's a - // packet coming in out of order. - this._sequenceBase += 1 << 16; - this._currentSequenceWrapState = SequenceWrapState.AssumeNextHighSequenceIsOutOfOrder; - break; - - case SequenceWrapState.AssumeNextHighSequenceIsOutOfOrder when originalSequence > HIGH_THRESHOLD: - // we're seeing some high sequence numbers EITHER at the beginning of the stream - // OR very close to the time when we saw some very low sequence numbers. in the - // latter case, it happened because the packets came in out of order, right when - // the sequence numbers wrapped around. in the former case, we MIGHT be in the - // same kind of situation (we can't tell yet), so we err on the side of caution - // and burn a full cycle before we start counting so that we can handle both - // cases with the exact same adjustment. - wrappingAdjustment = 1 << 16; - break; - - case SequenceWrapState.AssumeNextHighSequenceIsOutOfOrder when originalSequence > LOW_THRESHOLD: - // EITHER we're at the very beginning of the stream OR very close to the time - // when we saw some very low sequence numbers. either way, we're out of the - // zones where we should consider very low sequence numbers to come AFTER very - // high ones, so we can go back to normal now. - this._currentSequenceWrapState = SequenceWrapState.Normal; - break; - } - - return this._sequenceBase + originalSequence - wrappingAdjustment; - } -} diff --git a/DisCatSharp.VoiceNext/Entities/VoiceDispatch.cs b/DisCatSharp.VoiceNext/Entities/VoiceDispatch.cs deleted file mode 100644 index f409c139f..000000000 --- a/DisCatSharp.VoiceNext/Entities/VoiceDispatch.cs +++ /dev/null @@ -1,55 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Newtonsoft.Json; - -namespace DisCatSharp.VoiceNext.Entities; - -/// -/// The voice dispatch. -/// -internal sealed class VoiceDispatch -{ - /// - /// Gets or sets the op code. - /// - [JsonProperty("op")] - public int OpCode { get; set; } - - /// - /// Gets or sets the payload. - /// - [JsonProperty("d")] - public object Payload { get; set; } - - /// - /// Gets or sets the sequence. - /// - [JsonProperty("s", NullValueHandling = NullValueHandling.Ignore)] - public int? Sequence { get; set; } - - /// - /// Gets or sets the event name. - /// - [JsonProperty("t", NullValueHandling = NullValueHandling.Ignore)] - public string EventName { get; set; } -} diff --git a/DisCatSharp.VoiceNext/Entities/VoiceIdentifyPayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceIdentifyPayload.cs deleted file mode 100644 index f90d38c0a..000000000 --- a/DisCatSharp.VoiceNext/Entities/VoiceIdentifyPayload.cs +++ /dev/null @@ -1,55 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Newtonsoft.Json; - -namespace DisCatSharp.VoiceNext.Entities; - -/// -/// The voice identify payload. -/// -internal sealed class VoiceIdentifyPayload -{ - /// - /// Gets or sets the server id. - /// - [JsonProperty("server_id")] - public ulong ServerId { get; set; } - - /// - /// Gets or sets the user id. - /// - [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)] - public ulong? UserId { get; set; } - - /// - /// Gets or sets the session id. - /// - [JsonProperty("session_id")] - public string SessionId { get; set; } - - /// - /// Gets or sets the token. - /// - [JsonProperty("token")] - public string Token { get; set; } -} diff --git a/DisCatSharp.VoiceNext/Entities/VoicePacket.cs b/DisCatSharp.VoiceNext/Entities/VoicePacket.cs deleted file mode 100644 index ce14c2072..000000000 --- a/DisCatSharp.VoiceNext/Entities/VoicePacket.cs +++ /dev/null @@ -1,54 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace DisCatSharp.VoiceNext.Entities; - -internal struct VoicePacket -{ - /// - /// Gets the bytes. - /// - public ReadOnlyMemory Bytes { get; } - /// - /// Gets the millisecond duration. - /// - public int MillisecondDuration { get; } - /// - /// Gets or sets a value indicating whether is silence. - /// - public bool IsSilence { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The bytes. - /// The ms duration. - /// If true, is silence. - public VoicePacket(ReadOnlyMemory bytes, int msDuration, bool isSilence = false) - { - this.Bytes = bytes; - this.MillisecondDuration = msDuration; - this.IsSilence = isSilence; - } -} diff --git a/DisCatSharp.VoiceNext/Entities/VoiceReadyPayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceReadyPayload.cs deleted file mode 100644 index c4babedac..000000000 --- a/DisCatSharp.VoiceNext/Entities/VoiceReadyPayload.cs +++ /dev/null @@ -1,63 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Collections.Generic; - -using Newtonsoft.Json; - -namespace DisCatSharp.VoiceNext.Entities; - -/// -/// The voice ready payload. -/// -internal sealed class VoiceReadyPayload -{ - /// - /// Gets or sets the s s r c. - /// - [JsonProperty("ssrc")] - public uint Ssrc { get; set; } - - /// - /// Gets or sets the address. - /// - [JsonProperty("ip")] - public string Address { get; set; } - - /// - /// Gets or sets the port. - /// - [JsonProperty("port")] - public ushort Port { get; set; } - - /// - /// Gets or sets the modes. - /// - [JsonProperty("modes")] - public IReadOnlyList Modes { get; set; } - - /// - /// Gets or sets the heartbeat interval. - /// - [JsonProperty("heartbeat_interval")] - public int HeartbeatInterval { get; set; } -} diff --git a/DisCatSharp.VoiceNext/Entities/VoiceSelectProtocolPayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceSelectProtocolPayload.cs deleted file mode 100644 index ecc967498..000000000 --- a/DisCatSharp.VoiceNext/Entities/VoiceSelectProtocolPayload.cs +++ /dev/null @@ -1,43 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Newtonsoft.Json; - -namespace DisCatSharp.VoiceNext.Entities; - -/// -/// The voice select protocol payload. -/// -internal sealed class VoiceSelectProtocolPayload -{ - /// - /// Gets or sets the protocol. - /// - [JsonProperty("protocol")] - public string Protocol { get; set; } - - /// - /// Gets or sets the data. - /// - [JsonProperty("data")] - public VoiceSelectProtocolPayloadData Data { get; set; } -} diff --git a/DisCatSharp.VoiceNext/Entities/VoiceSelectProtocolPayloadData.cs b/DisCatSharp.VoiceNext/Entities/VoiceSelectProtocolPayloadData.cs deleted file mode 100644 index bf67f27d1..000000000 --- a/DisCatSharp.VoiceNext/Entities/VoiceSelectProtocolPayloadData.cs +++ /dev/null @@ -1,49 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Newtonsoft.Json; - -namespace DisCatSharp.VoiceNext.Entities; - -/// -/// The voice select protocol payload data. -/// -internal class VoiceSelectProtocolPayloadData -{ - /// - /// Gets or sets the address. - /// - [JsonProperty("address")] - public string Address { get; set; } - - /// - /// Gets or sets the port. - /// - [JsonProperty("port")] - public ushort Port { get; set; } - - /// - /// Gets or sets the mode. - /// - [JsonProperty("mode")] - public string Mode { get; set; } -} diff --git a/DisCatSharp.VoiceNext/Entities/VoiceServerUpdatePayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceServerUpdatePayload.cs deleted file mode 100644 index 572a77e2d..000000000 --- a/DisCatSharp.VoiceNext/Entities/VoiceServerUpdatePayload.cs +++ /dev/null @@ -1,49 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Newtonsoft.Json; - -namespace DisCatSharp.VoiceNext.Entities; - -/// -/// The voice server update payload. -/// -internal sealed class VoiceServerUpdatePayload -{ - /// - /// Gets or sets the token. - /// - [JsonProperty("token")] - public string Token { get; set; } - - /// - /// Gets or sets the guild id. - /// - [JsonProperty("guild_id")] - public ulong GuildId { get; set; } - - /// - /// Gets or sets the endpoint. - /// - [JsonProperty("endpoint")] - public string Endpoint { get; set; } -} diff --git a/DisCatSharp.VoiceNext/Entities/VoiceSessionDescriptionPayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceSessionDescriptionPayload.cs deleted file mode 100644 index 692e0deac..000000000 --- a/DisCatSharp.VoiceNext/Entities/VoiceSessionDescriptionPayload.cs +++ /dev/null @@ -1,43 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Newtonsoft.Json; - -namespace DisCatSharp.VoiceNext.Entities; - -/// -/// The voice session description payload. -/// -internal sealed class VoiceSessionDescriptionPayload -{ - /// - /// Gets or sets the secret key. - /// - [JsonProperty("secret_key")] - public byte[] SecretKey { get; set; } - - /// - /// Gets or sets the mode. - /// - [JsonProperty("mode")] - public string Mode { get; set; } -} diff --git a/DisCatSharp.VoiceNext/Entities/VoiceSpeakingPayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceSpeakingPayload.cs deleted file mode 100644 index 80ce120be..000000000 --- a/DisCatSharp.VoiceNext/Entities/VoiceSpeakingPayload.cs +++ /dev/null @@ -1,55 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Newtonsoft.Json; - -namespace DisCatSharp.VoiceNext.Entities; - -/// -/// The voice speaking payload. -/// -internal sealed class VoiceSpeakingPayload -{ - /// - /// Gets or sets a value indicating whether speaking. - /// - [JsonProperty("speaking")] - public bool Speaking { get; set; } - - /// - /// Gets or sets the delay. - /// - [JsonProperty("delay", NullValueHandling = NullValueHandling.Ignore)] - public int? Delay { get; set; } - - /// - /// Gets or sets the s s r c. - /// - [JsonProperty("ssrc", NullValueHandling = NullValueHandling.Ignore)] - public uint? Ssrc { get; set; } - - /// - /// Gets or sets the user id. - /// - [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)] - public ulong? UserId { get; set; } -} diff --git a/DisCatSharp.VoiceNext/Entities/VoiceStateUpdatePayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceStateUpdatePayload.cs deleted file mode 100644 index 4d6cc4ecc..000000000 --- a/DisCatSharp.VoiceNext/Entities/VoiceStateUpdatePayload.cs +++ /dev/null @@ -1,67 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Newtonsoft.Json; - -namespace DisCatSharp.VoiceNext.Entities; - -/// -/// The voice state update payload. -/// -internal sealed class VoiceStateUpdatePayload -{ - /// - /// Gets or sets the guild id. - /// - [JsonProperty("guild_id")] - public ulong GuildId { get; set; } - - /// - /// Gets or sets the channel id. - /// - [JsonProperty("channel_id")] - public ulong? ChannelId { get; set; } - - /// - /// Gets or sets the user id. - /// - [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)] - public ulong? UserId { get; set; } - - /// - /// Gets or sets the session id. - /// - [JsonProperty("session_id", NullValueHandling = NullValueHandling.Ignore)] - public string SessionId { get; set; } - - /// - /// Gets or sets a value indicating whether deafened. - /// - [JsonProperty("self_deaf")] - public bool Deafened { get; set; } - - /// - /// Gets or sets a value indicating whether muted. - /// - [JsonProperty("self_mute")] - public bool Muted { get; set; } -} diff --git a/DisCatSharp.VoiceNext/Entities/VoiceUserJoinPayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceUserJoinPayload.cs deleted file mode 100644 index 31f89d2b2..000000000 --- a/DisCatSharp.VoiceNext/Entities/VoiceUserJoinPayload.cs +++ /dev/null @@ -1,43 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Newtonsoft.Json; - -namespace DisCatSharp.VoiceNext.Entities; - -/// -/// The voice user join payload. -/// -internal sealed class VoiceUserJoinPayload -{ - /// - /// Gets the user id. - /// - [JsonProperty("user_id")] - public ulong UserId { get; private set; } - - /// - /// Gets the s s r c. - /// - [JsonProperty("audio_ssrc")] - public uint Ssrc { get; private set; } -} diff --git a/DisCatSharp.VoiceNext/Entities/VoiceUserLeavePayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceUserLeavePayload.cs deleted file mode 100644 index 308f95c97..000000000 --- a/DisCatSharp.VoiceNext/Entities/VoiceUserLeavePayload.cs +++ /dev/null @@ -1,37 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Newtonsoft.Json; - -namespace DisCatSharp.VoiceNext.Entities; - -/// -/// The voice user leave payload. -/// -internal sealed class VoiceUserLeavePayload -{ - /// - /// Gets or sets the user id. - /// - [JsonProperty("user_id")] - public ulong UserId { get; set; } -} diff --git a/DisCatSharp.VoiceNext/EventArgs/VoiceReceiveEventArgs.cs b/DisCatSharp.VoiceNext/EventArgs/VoiceReceiveEventArgs.cs deleted file mode 100644 index 662d740fe..000000000 --- a/DisCatSharp.VoiceNext/EventArgs/VoiceReceiveEventArgs.cs +++ /dev/null @@ -1,76 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -using DisCatSharp.Entities; -using DisCatSharp.EventArgs; - -namespace DisCatSharp.VoiceNext.EventArgs; - -/// -/// Represents arguments for VoiceReceived events. -/// -public class VoiceReceiveEventArgs : DiscordEventArgs -{ - /// - /// Gets the SSRC of the audio source. - /// - public uint Ssrc { get; internal set; } - -#pragma warning disable CS8632 - - /// - /// Gets the user that sent the audio data. - /// - public DiscordUser? User { get; internal set; } - -#pragma warning restore - - /// - /// Gets the received voice data, decoded to PCM format. - /// - public ReadOnlyMemory PcmData { get; internal set; } - - /// - /// Gets the received voice data, in Opus format. Note that for packets that were lost and/or compensated for, this will be empty. - /// - public ReadOnlyMemory OpusData { get; internal set; } - - /// - /// Gets the format of the received PCM data. - /// - /// Important: This isn't always the format set in , and depends on the audio data received. - /// - /// - public AudioFormat AudioFormat { get; internal set; } - - /// - /// Gets the millisecond duration of the PCM audio sample. - /// - public int AudioDuration { get; internal set; } - - /// - /// Initializes a new instance of the class. - /// - internal VoiceReceiveEventArgs(IServiceProvider provider) : base(provider) { } -} diff --git a/DisCatSharp.VoiceNext/EventArgs/VoiceUserJoinEventArgs.cs b/DisCatSharp.VoiceNext/EventArgs/VoiceUserJoinEventArgs.cs deleted file mode 100644 index d6d14699e..000000000 --- a/DisCatSharp.VoiceNext/EventArgs/VoiceUserJoinEventArgs.cs +++ /dev/null @@ -1,49 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -using DisCatSharp.Entities; -using DisCatSharp.EventArgs; - -namespace DisCatSharp.VoiceNext.EventArgs; - -/// -/// Arguments for . -/// -public sealed class VoiceUserJoinEventArgs : DiscordEventArgs -{ - /// - /// Gets the user who left. - /// - public DiscordUser User { get; internal set; } - - /// - /// Gets the SSRC of the user who joined. - /// - public uint Ssrc { get; internal set; } - - /// - /// Initializes a new instance of the class. - /// - internal VoiceUserJoinEventArgs(IServiceProvider provider) : base(provider) { } -} diff --git a/DisCatSharp.VoiceNext/EventArgs/VoiceUserLeaveEventArgs.cs b/DisCatSharp.VoiceNext/EventArgs/VoiceUserLeaveEventArgs.cs deleted file mode 100644 index a8b786610..000000000 --- a/DisCatSharp.VoiceNext/EventArgs/VoiceUserLeaveEventArgs.cs +++ /dev/null @@ -1,49 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -using DisCatSharp.Entities; -using DisCatSharp.EventArgs; - -namespace DisCatSharp.VoiceNext.EventArgs; - -/// -/// Arguments for . -/// -public sealed class VoiceUserLeaveEventArgs : DiscordEventArgs -{ - /// - /// Gets the user who left. - /// - public DiscordUser User { get; internal set; } - - /// - /// Gets the SSRC of the user who left. - /// - public uint Ssrc { get; internal set; } - - /// - /// Initializes a new instance of the class. - /// - internal VoiceUserLeaveEventArgs(IServiceProvider provider) : base(provider) { } -} diff --git a/DisCatSharp.VoiceNext/GlobalSuppressions.cs b/DisCatSharp.VoiceNext/GlobalSuppressions.cs deleted file mode 100644 index 2ee039b08..000000000 --- a/DisCatSharp.VoiceNext/GlobalSuppressions.cs +++ /dev/null @@ -1,51 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.VoiceNextConnection.Dispose")] -[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.VoiceNextConnection.VoiceWS_SocketMessage(DisCatSharp.Net.WebSocket.IWebSocketClient,DisCatSharp.EventArgs.SocketMessageEventArgs)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.VoiceNextConnection.WsSendAsync(System.String)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Performance", "CA1806:Do not ignore method results", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.Codec.Interop.OpusGetLastPacketDuration(System.IntPtr,System.Int32@)")] -[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.VoiceNextConnection.HandleDispatch(Newtonsoft.Json.Linq.JObject)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.VoiceNextConnection.ProcessKeepalive(System.Byte[])")] -[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.VoiceNextConnection.Stage1(DisCatSharp.VoiceNext.Entities.VoiceReadyPayload)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.VoiceNextConnection.Stage2(DisCatSharp.VoiceNext.Entities.VoiceSessionDescriptionPayload)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.VoiceNextConnection.VoiceSenderTask~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.VoiceNextConnection.VoiceWS_SocketClosed(DisCatSharp.Net.WebSocket.IWebSocketClient,DisCatSharp.EventArgs.SocketCloseEventArgs)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.Codec.Opus.GetLastPacketSampleCount(DisCatSharp.VoiceNext.Codec.OpusDecoder)~System.Int32")] -[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.Codec.Opus.ProcessPacketLoss(DisCatSharp.VoiceNext.Codec.OpusDecoder,System.Int32,System.Span{System.Byte}@)")] -[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.Codec.Rtp.CalculatePacketSize(System.Int32,DisCatSharp.VoiceNext.Codec.EncryptionMode)~System.Int32")] -[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.Codec.Rtp.DecodeHeader(System.ReadOnlySpan{System.Byte},System.UInt16@,System.UInt32@,System.UInt32@,System.Boolean@)")] -[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.Codec.Rtp.EncodeHeader(System.UInt16,System.UInt32,System.UInt32,System.Span{System.Byte})")] -[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.Codec.Rtp.GetDataFromPacket(System.ReadOnlySpan{System.Byte},System.ReadOnlySpan{System.Byte}@,DisCatSharp.VoiceNext.Codec.EncryptionMode)")] -[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.Codec.Rtp.IsRtpHeader(System.ReadOnlySpan{System.Byte})~System.Boolean")] -[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.Codec.Sodium.AppendNonce(System.ReadOnlySpan{System.Byte},System.Span{System.Byte},DisCatSharp.VoiceNext.Codec.EncryptionMode)")] -[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.Codec.Sodium.GenerateNonce(System.ReadOnlySpan{System.Byte},System.Span{System.Byte})")] -[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.Codec.Sodium.GenerateNonce(System.UInt32,System.Span{System.Byte})")] -[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.Codec.Sodium.GetNonce(System.ReadOnlySpan{System.Byte},System.Span{System.Byte},DisCatSharp.VoiceNext.Codec.EncryptionMode)")] -[assembly: SuppressMessage("Performance", "CA1806:Do not ignore method results", Justification = "", Scope = "member", Target = "~M:DisCatSharp.VoiceNext.Interop.Bindings.GetLastPacketDuration(System.IntPtr,System.Int32@)")] diff --git a/DisCatSharp.VoiceNext/IVoiceFilter.cs b/DisCatSharp.VoiceNext/IVoiceFilter.cs deleted file mode 100644 index 207a8e329..000000000 --- a/DisCatSharp.VoiceNext/IVoiceFilter.cs +++ /dev/null @@ -1,39 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace DisCatSharp.VoiceNext; - -/// -/// Represents a filter for PCM data. PCM data submitted through a will be sent through all installed instances of first. -/// -public interface IVoiceFilter -{ - /// - /// Transforms the supplied PCM data using this filter. - /// - /// PCM data to transform. The transformation happens in-place. - /// Format of the supplied PCM data. - /// Millisecond duration of the supplied PCM data. - void Transform(Span pcmData, AudioFormat pcmFormat, int duration); -} diff --git a/DisCatSharp.VoiceNext/Interop/Bindings.cs b/DisCatSharp.VoiceNext/Interop/Bindings.cs deleted file mode 100644 index 06fdd2dca..000000000 --- a/DisCatSharp.VoiceNext/Interop/Bindings.cs +++ /dev/null @@ -1,166 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Runtime.InteropServices; - -namespace DisCatSharp.VoiceNext.Interop -{ - internal static unsafe partial class Bindings - { - [LibraryImport("libopus")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial IntPtr opus_encoder_create(int samplingRate, int channels, int application, out OpusError error); - - [LibraryImport("libopus")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial void opus_encoder_destroy(IntPtr encoder); - - [LibraryImport("libopus")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial int opus_encode(IntPtr encoder, byte* pcm, int frameSize, byte* data, int maxDataBytes); - - [LibraryImport("libopus")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial OpusError opus_encoder_ctl(IntPtr encoder, OpusControl ctl, int value); - - [LibraryImport("libopus")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial IntPtr opus_decoder_create(int sampleRate, int channels, out OpusError error); - - [LibraryImport("libopus")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial void opus_decoder_destroy(IntPtr decoder); - - [LibraryImport("libopus")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial int opus_decode(IntPtr decoder, byte* opusData, int opusDataLength, byte* data, int frameSize, int decodeFec); - - [LibraryImport("libopus")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial int opus_packet_get_nb_channels(byte* data); - - [LibraryImport("libopus")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial int opus_packet_get_nb_frames(byte* data, int length); - - [LibraryImport("libopus")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial int opus_packet_get_samples_per_frame(byte* data, int samplingRate); - - [LibraryImport("libopus")] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - private static partial int opus_decoder_ctl(IntPtr decoder, OpusControl ctl, out int value); - - public static IntPtr CreateEncoder(int sampleRate, int channelCount, int application) - { - var encoder = opus_encoder_create(sampleRate, channelCount, application, out var error); - return error == OpusError.Ok ? encoder : throw new Exception($"Failed to instantiate Opus encoder: {error} ({(int)error})"); - } - - public static void SetEncoderOption(IntPtr encoder, OpusControl option, int value) - { - var error = OpusError.Ok; - if ((error = opus_encoder_ctl(encoder, option, value)) != OpusError.Ok) - throw new Exception($"Failed to set Opus encoder option: ${error} ({(int)error})"); - } - - public static void Encode(IntPtr encoder, ReadOnlySpan pcm, int frameSize, ref Span data) - { - var length = 0; - - fixed (byte* pcmPointer = pcm) - fixed (byte* dataPointer = data) - length = opus_encode(encoder, pcmPointer, frameSize, dataPointer, data.Length); - - if (length < 0) - { - var error = (OpusError)length; - throw new Exception($"Failed to encode PCM data: {error} ({length})"); - } - - data = data[..length]; - } - - public static IntPtr CreateDecoder(int sampleRate, int channelCount) - { - var decoder = opus_decoder_create(sampleRate, channelCount, out var error); - return error == OpusError.Ok ? decoder : throw new Exception($"Failed to instantiate Opus decoder: {error} ({(int)error})"); - } - - public static int Decode(IntPtr decoder, ReadOnlySpan data, int frameSize, Span pcm, bool useFec) - { - var length = 0; - - fixed (byte* dataPointer = data) - fixed (byte* pcmPointer = pcm) - length = opus_decode(decoder, dataPointer, data.Length, pcmPointer, frameSize, useFec ? 1 : 0); - - if (length < 0) - { - var error = (OpusError)length; - throw new Exception($"Failed to decode PCM data: {error} ({length})"); - } - - return length; - } - - public static int Decode(IntPtr decoder, int frameSize, Span pcm) - { - var length = 0; - - fixed (byte* pcmPointer = pcm) - length = opus_decode(decoder, null, 0, pcmPointer, frameSize, 1); - - if (length < 0) - { - var error = (OpusError)length; - throw new Exception($"Failed to decode PCM data: {error} ({length})"); - } - - return length; - } - - public static OpusPacketMetrics GetPacketMetrics(ReadOnlySpan data, int samplingRate) - { - int channels, frames, samplesPerFrame; - - fixed (byte* dataPointer = data) - { - frames = opus_packet_get_nb_frames(dataPointer, data.Length); - samplesPerFrame = opus_packet_get_samples_per_frame(dataPointer, samplingRate); - channels = opus_packet_get_nb_channels(dataPointer); - } - - return new() - { - ChannelCount = channels, - FrameCount = frames, - SamplesPerFrame = samplesPerFrame, - FrameSize = frames * samplesPerFrame - }; - } - - public static void GetLastPacketDuration(IntPtr decoder, out int sampleCount) - => opus_decoder_ctl(decoder, OpusControl.GetLastPacketDuration, out sampleCount); - } -} diff --git a/DisCatSharp.VoiceNext/Interop/OpusControl.cs b/DisCatSharp.VoiceNext/Interop/OpusControl.cs deleted file mode 100644 index d609b0876..000000000 --- a/DisCatSharp.VoiceNext/Interop/OpusControl.cs +++ /dev/null @@ -1,35 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace DisCatSharp.VoiceNext.Interop -{ - internal enum OpusControl : int - { - SetBitrate = 4002, - SetBandwidth = 4008, - SetInBandFec = 4012, - SetPacketLossPercent = 4014, - SetSignal = 4024, - ResetState = 4028, - GetLastPacketDuration = 4039 - } -} diff --git a/DisCatSharp.VoiceNext/Interop/OpusError.cs b/DisCatSharp.VoiceNext/Interop/OpusError.cs deleted file mode 100644 index ec7b6a4e9..000000000 --- a/DisCatSharp.VoiceNext/Interop/OpusError.cs +++ /dev/null @@ -1,36 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace DisCatSharp.VoiceNext.Interop -{ - internal enum OpusError - { - Ok = 0, - InvalidArgument = -1, - BufferTooSmall = -2, - InternalError = -3, - CorruptedStream = -4, - RequestNotImplemented = -5, - InvalidState = -6, - MemoryAllocationFailed = -7 - } -} diff --git a/DisCatSharp.VoiceNext/Interop/OpusPacketMetrics.cs b/DisCatSharp.VoiceNext/Interop/OpusPacketMetrics.cs deleted file mode 100644 index c3de5ae40..000000000 --- a/DisCatSharp.VoiceNext/Interop/OpusPacketMetrics.cs +++ /dev/null @@ -1,35 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace DisCatSharp.VoiceNext.Interop -{ - internal struct OpusPacketMetrics - { - public int ChannelCount { get; set; } - - public int FrameCount { get; set; } - - public int SamplesPerFrame { get; set; } - - public int FrameSize { get; set; } - } -} diff --git a/DisCatSharp.VoiceNext/Interop/OpusSignal.cs b/DisCatSharp.VoiceNext/Interop/OpusSignal.cs deleted file mode 100644 index ffef542c7..000000000 --- a/DisCatSharp.VoiceNext/Interop/OpusSignal.cs +++ /dev/null @@ -1,31 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace DisCatSharp.VoiceNext.Interop -{ - internal enum OpusSignal : int - { - Auto = -1000, - Voice = 3000, - Music = 3002 - } -} diff --git a/DisCatSharp.VoiceNext/Properties/AssemblyProperties.cs b/DisCatSharp.VoiceNext/Properties/AssemblyProperties.cs deleted file mode 100644 index 29c9334ce..000000000 --- a/DisCatSharp.VoiceNext/Properties/AssemblyProperties.cs +++ /dev/null @@ -1,46 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("DisCatSharp.ApplicationCommands")] -[assembly: InternalsVisibleTo("DisCatSharp.CommandsNext")] -[assembly: InternalsVisibleTo("DisCatSharp.Common")] -[assembly: InternalsVisibleTo("DisCatSharp.Configuration")] -[assembly: InternalsVisibleTo("DisCatSharp.Configuration.Tests")] -[assembly: InternalsVisibleTo("DisCatSharp.Hosting")] -[assembly: InternalsVisibleTo("DisCatSharp.Hosting.DependencyInjection")] -[assembly: InternalsVisibleTo("DisCatSharp.Hosting.Tests")] -[assembly: InternalsVisibleTo("DisCatSharp.Interactivity")] -[assembly: InternalsVisibleTo("DisCatSharp.Lavalink")] -[assembly: InternalsVisibleTo("DisCatSharp.Phabricator")] -[assembly: InternalsVisibleTo("DisCatSharp.Support")] -[assembly: InternalsVisibleTo("DisCatSharp.Test")] -[assembly: InternalsVisibleTo("DisCatSharp")] -[assembly: InternalsVisibleTo("DisCatSharp.VoiceNext.Natives")] -[assembly: InternalsVisibleTo("Nyaw")] -[assembly: InternalsVisibleTo("DisCatSharp.DevTools")] -[assembly: InternalsVisibleTo("DisCatSharp.DocsGenerator")] -[assembly: InternalsVisibleTo("DisCatSharp.StaffApps")] -[assembly: InternalsVisibleTo("Microsoft.DocAsCode")] -[assembly: InternalsVisibleTo("Microsoft.DocAsCode.Metadata.ManagedReference")] - diff --git a/DisCatSharp.VoiceNext/RawVoicePacket.cs b/DisCatSharp.VoiceNext/RawVoicePacket.cs deleted file mode 100644 index b4793fd26..000000000 --- a/DisCatSharp.VoiceNext/RawVoicePacket.cs +++ /dev/null @@ -1,61 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; - -namespace DisCatSharp.VoiceNext; - -internal readonly struct RawVoicePacket -{ - /// - /// Initializes a new instance of the class. - /// - /// The bytes. - /// The duration. - /// If true, silence. - public RawVoicePacket(Memory bytes, int duration, bool silence) - { - this.Bytes = bytes; - this.Duration = duration; - this.Silence = silence; - this.RentedBuffer = null; - } - - /// - /// Initializes a new instance of the class. - /// - /// The bytes. - /// The duration. - /// If true, silence. - /// The rented buffer. - public RawVoicePacket(Memory bytes, int duration, bool silence, byte[] rentedBuffer) - : this(bytes, duration, silence) - { - this.RentedBuffer = rentedBuffer; - } - - public readonly Memory Bytes; - public readonly int Duration; - public readonly bool Silence; - - public readonly byte[] RentedBuffer; -} diff --git a/DisCatSharp.VoiceNext/StreamExtensions.cs b/DisCatSharp.VoiceNext/StreamExtensions.cs deleted file mode 100644 index 7c0c62b11..000000000 --- a/DisCatSharp.VoiceNext/StreamExtensions.cs +++ /dev/null @@ -1,71 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Buffers; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace DisCatSharp.VoiceNext; - -/// -/// The stream extensions. -/// -public static class StreamExtensions -{ - /// - /// Asynchronously reads the bytes from the current stream and writes them to the specified . - /// - /// The source - /// The target - /// The size, in bytes, of the buffer. This value must be greater than zero. If , defaults to the packet size specified by . - /// The token to monitor for cancellation requests. - /// - public static async Task CopyToAsync(this Stream source, VoiceTransmitSink destination, int? bufferSize = null, CancellationToken cancellationToken = default) - { - // adapted from CoreFX - // https://source.dot.net/#System.Private.CoreLib/Stream.cs,8048a9680abdd13b - - if (source is null) - throw new ArgumentNullException(nameof(source)); - if (destination is null) - throw new ArgumentNullException(nameof(destination)); - if (bufferSize != null && bufferSize <= 0) - throw new ArgumentOutOfRangeException(nameof(bufferSize), bufferSize, "bufferSize cannot be less than or equal to zero"); - - var bufferLength = bufferSize ?? destination.SampleLength; - var buffer = ArrayPool.Shared.Rent(bufferLength); - try - { - int bytesRead; - while ((bytesRead = await source.ReadAsync(buffer.AsMemory(0, bufferLength), cancellationToken).ConfigureAwait(false)) != 0) - { - await destination.WriteAsync(new ReadOnlyMemory(buffer, 0, bytesRead), cancellationToken).ConfigureAwait(false); - } - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } -} diff --git a/DisCatSharp.VoiceNext/VoiceApplication.cs b/DisCatSharp.VoiceNext/VoiceApplication.cs deleted file mode 100644 index 034cec8ca..000000000 --- a/DisCatSharp.VoiceNext/VoiceApplication.cs +++ /dev/null @@ -1,44 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -namespace DisCatSharp.VoiceNext; - -/// -/// Represents encoder settings preset for Opus. -/// -public enum VoiceApplication : int -{ - /// - /// Defines that the encoder must optimize settings for voice data. - /// - Voice = 2048, - - /// - /// Defines that the encoder must optimize settings for music data. - /// - Music = 2049, - - /// - /// Defines that the encoder must optimize settings for low latency applications. - /// - LowLatency = 2051 -} diff --git a/DisCatSharp.VoiceNext/VoiceNextConfiguration.cs b/DisCatSharp.VoiceNext/VoiceNextConfiguration.cs deleted file mode 100644 index 065119d04..000000000 --- a/DisCatSharp.VoiceNext/VoiceNextConfiguration.cs +++ /dev/null @@ -1,65 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Microsoft.Extensions.DependencyInjection; - -namespace DisCatSharp.VoiceNext; - -/// -/// VoiceNext client configuration. -/// -public sealed class VoiceNextConfiguration -{ - /// - /// Sets the audio format for Opus. This will determine the quality of the audio output. - /// Defaults to . - /// - public AudioFormat AudioFormat { internal get; set; } = AudioFormat.Default; - - /// - /// Sets whether incoming voice receiver should be enabled. - /// Defaults to false. - /// - public bool EnableIncoming { internal get; set; } - - /// - /// Sets the size of the packet queue. - /// Defaults to 25 or ~500ms. - /// - public int PacketQueueSize { internal get; set; } = 25; - - /// - /// Creates a new instance of . - /// - [ActivatorUtilitiesConstructor] - public VoiceNextConfiguration() { } - - /// - /// Creates a new instance of , copying the properties of another configuration. - /// - /// Configuration the properties of which are to be copied. - public VoiceNextConfiguration(VoiceNextConfiguration other) - { - this.AudioFormat = new AudioFormat(other.AudioFormat.SampleRate, other.AudioFormat.ChannelCount, other.AudioFormat.VoiceApplication); - this.EnableIncoming = other.EnableIncoming; - } -} diff --git a/DisCatSharp.VoiceNext/VoiceNextConnection.cs b/DisCatSharp.VoiceNext/VoiceNextConnection.cs deleted file mode 100644 index 624a3340a..000000000 --- a/DisCatSharp.VoiceNext/VoiceNextConnection.cs +++ /dev/null @@ -1,1359 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Buffers; -using System.Buffers.Binary; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Channels; -using System.Threading.Tasks; - -using DisCatSharp.Common.Utilities; -using DisCatSharp.Entities; -using DisCatSharp.EventArgs; -using DisCatSharp.Net; -using DisCatSharp.Net.Udp; -using DisCatSharp.Net.WebSocket; -using DisCatSharp.VoiceNext.Codec; -using DisCatSharp.VoiceNext.Entities; -using DisCatSharp.VoiceNext.EventArgs; - -using Microsoft.Extensions.Logging; - -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace DisCatSharp.VoiceNext; - -internal delegate Task VoiceDisconnectedEventHandler(DiscordGuild guild); - -/// -/// VoiceNext connection to a voice channel. -/// -public sealed class VoiceNextConnection : IDisposable -{ - /// - /// Triggered whenever a user speaks in the connected voice channel. - /// - public event AsyncEventHandler UserSpeaking - { - add => this._userSpeaking.Register(value); - remove => this._userSpeaking.Unregister(value); - } - private readonly AsyncEvent _userSpeaking; - - /// - /// Triggered whenever a user joins voice in the connected guild. - /// - public event AsyncEventHandler UserJoined - { - add => this._userJoined.Register(value); - remove => this._userJoined.Unregister(value); - } - private readonly AsyncEvent _userJoined; - - /// - /// Triggered whenever a user leaves voice in the connected guild. - /// - public event AsyncEventHandler UserLeft - { - add => this._userLeft.Register(value); - remove => this._userLeft.Unregister(value); - } - private readonly AsyncEvent _userLeft; - - /// - /// Triggered whenever voice data is received from the connected voice channel. - /// - public event AsyncEventHandler VoiceReceived - { - add => this._voiceReceived.Register(value); - remove => this._voiceReceived.Unregister(value); - } - private readonly AsyncEvent _voiceReceived; - - /// - /// Triggered whenever voice WebSocket throws an exception. - /// - public event AsyncEventHandler VoiceSocketErrored - { - add => this._voiceSocketError.Register(value); - remove => this._voiceSocketError.Unregister(value); - } - private readonly AsyncEvent _voiceSocketError; - - internal event VoiceDisconnectedEventHandler VoiceDisconnected; - - /// - /// Gets the unix epoch. - /// - private static DateTimeOffset s_unixEpoch { get; } = new(1970, 1, 1, 0, 0, 0, TimeSpan.Zero); - - /// - /// Gets the discord. - /// - private readonly DiscordClient _discord; - - /// - /// Gets the guild. - /// - private readonly DiscordGuild _guild; - - /// - /// Gets the transmitting s s r cs. - /// - private readonly ConcurrentDictionary _transmittingSsrCs; - - /// - /// Gets the udp client. - /// - private readonly BaseUdpClient _udpClient; - - /// - /// Gets or sets the voice ws. - /// - private IWebSocketClient _voiceWs; - - /// - /// Gets or sets the heartbeat task. - /// - private Task _heartbeatTask; - - /// - /// Gets or sets the heartbeat interval. - /// - private int _heartbeatInterval; - - /// - /// Gets or sets the last heartbeat. - /// - private DateTimeOffset _lastHeartbeat; - - /// - /// Gets or sets the token source. - /// - private CancellationTokenSource _tokenSource; - /// - /// Gets the token. - /// - private CancellationToken TOKEN - => this._tokenSource.Token; - - /// - /// Gets or sets the server data. - /// - internal VoiceServerUpdatePayload ServerData { get; set; } - /// - /// Gets or sets the state data. - /// - internal VoiceStateUpdatePayload StateData { get; set; } - /// - /// Gets or sets a value indicating whether resume. - /// - internal bool Resume { get; set; } - - /// - /// Gets the configuration. - /// - private readonly VoiceNextConfiguration _configuration; - - /// - /// Gets or sets the opus. - /// - private Opus _opus; - - /// - /// Gets or sets the sodium. - /// - private Sodium _sodium; - - /// - /// Gets or sets the rtp. - /// - private Rtp _rtp; - - /// - /// Gets or sets the selected encryption mode. - /// - private EncryptionMode _selectedEncryptionMode; - /// - /// Gets or sets the nonce. - /// - private uint _nonce; - - /// - /// Gets or sets the sequence. - /// - private ushort _sequence; - - /// - /// Gets or sets the timestamp. - /// - private uint _timestamp; - - /// - /// Gets or sets the s s r c. - /// - private uint _ssrc; - - /// - /// Gets or sets the key. - /// - private byte[] _key; - - /// - /// Gets or sets the discovered endpoint. - /// - private IpEndpoint _discoveredEndpoint; - /// - /// Gets or sets the web socket endpoint. - /// - internal ConnectionEndpoint WebSocketEndpoint { get; set; } - /// - /// Gets or sets the udp endpoint. - /// - internal ConnectionEndpoint UdpEndpoint { get; set; } - - /// - /// Gets or sets the ready wait. - /// - private readonly TaskCompletionSource _readyWait; - - /// - /// Gets or sets a value indicating whether is initialized. - /// - private bool _isInitialized; - - /// - /// Gets or sets a value indicating whether is disposed. - /// - private bool _isDisposed; - - /// - /// Gets or sets the playing wait. - /// - private TaskCompletionSource _playingWait; - - /// - /// Gets the pause event. - /// - private readonly AsyncManualResetEvent _pauseEvent; - - /// - /// Gets or sets the transmit stream. - /// - private VoiceTransmitSink _transmitStream; - - /// - /// Gets the transmit channel. - /// - private readonly Channel _transmitChannel; - - /// - /// Gets the keepalive timestamps. - /// - private readonly ConcurrentDictionary _keepaliveTimestamps; - private ulong _lastKeepalive; - - /// - /// Gets or sets the sender task. - /// - private Task _senderTask; - - /// - /// Gets or sets the sender token source. - /// - private CancellationTokenSource _senderTokenSource; - /// - /// Gets the sender token. - /// - private CancellationToken SENDER_TOKEN - => this._senderTokenSource.Token; - - /// - /// Gets or sets the receiver task. - /// - private Task _receiverTask; - - /// - /// Gets or sets the receiver token source. - /// - private CancellationTokenSource _receiverTokenSource; - /// - /// Gets the receiver token. - /// - private CancellationToken RECEIVER_TOKEN - => this._receiverTokenSource.Token; - - /// - /// Gets or sets the keepalive task. - /// - private Task _keepaliveTask; - - /// - /// Gets or sets the keepalive token source. - /// - private CancellationTokenSource _keepaliveTokenSource; - /// - /// Gets the keepalive token. - /// - private CancellationToken KEEPALIVE_TOKEN - => this._keepaliveTokenSource.Token; - - private volatile bool _isSpeaking; - - /// - /// Gets the audio format used by the Opus encoder. - /// - public AudioFormat AudioFormat => this._configuration.AudioFormat; - - /// - /// Gets whether this connection is still playing audio. - /// - public bool IsPlaying - => this._playingWait != null && !this._playingWait.Task.IsCompleted; - - /// - /// Gets the websocket round-trip time in ms. - /// - public int WebSocketPing - => Volatile.Read(ref this._wsPing); - private int _wsPing; - - /// - /// Gets the UDP round-trip time in ms. - /// - public int UdpPing - => Volatile.Read(ref this._udpPing); - private int _udpPing; - - private int _queueCount; - - /// - /// Gets the channel this voice client is connected to. - /// - public DiscordChannel TargetChannel { get; internal set; } - - /// - /// Initializes a new instance of the class. - /// - /// The client. - /// The guild. - /// The channel. - /// The config. - /// The server. - /// The state. - internal VoiceNextConnection(DiscordClient client, DiscordGuild guild, DiscordChannel channel, VoiceNextConfiguration config, VoiceServerUpdatePayload server, VoiceStateUpdatePayload state) - { - this._discord = client; - this._guild = guild; - this.TargetChannel = channel; - this._transmittingSsrCs = new ConcurrentDictionary(); - - this._userSpeaking = new AsyncEvent("VNEXT_USER_SPEAKING", TimeSpan.Zero, this._discord.EventErrorHandler); - this._userJoined = new AsyncEvent("VNEXT_USER_JOINED", TimeSpan.Zero, this._discord.EventErrorHandler); - this._userLeft = new AsyncEvent("VNEXT_USER_LEFT", TimeSpan.Zero, this._discord.EventErrorHandler); - this._voiceReceived = new AsyncEvent("VNEXT_VOICE_RECEIVED", TimeSpan.Zero, this._discord.EventErrorHandler); - this._voiceSocketError = new AsyncEvent("VNEXT_WS_ERROR", TimeSpan.Zero, this._discord.EventErrorHandler); - this._tokenSource = new CancellationTokenSource(); - - this._configuration = config; - this._isInitialized = false; - this._isDisposed = false; - this._opus = new Opus(this.AudioFormat); - //this.Sodium = new Sodium(); - this._rtp = new Rtp(); - - this.ServerData = server; - this.StateData = state; - - var eps = this.ServerData.Endpoint; - var epi = eps.LastIndexOf(':'); - var eph = string.Empty; - var epp = 443; - if (epi != -1) - { - eph = eps[..epi]; - epp = int.Parse(eps[(epi + 1)..]); - } - else - { - eph = eps; - } - this.WebSocketEndpoint = new ConnectionEndpoint { Hostname = eph, Port = epp }; - - this._readyWait = new TaskCompletionSource(); - - this._playingWait = null; - this._transmitChannel = Channel.CreateBounded(new BoundedChannelOptions(this._configuration.PacketQueueSize)); - this._keepaliveTimestamps = new ConcurrentDictionary(); - this._pauseEvent = new AsyncManualResetEvent(true); - - this._udpClient = this._discord.Configuration.UdpClientFactory(); - this._voiceWs = this._discord.Configuration.WebSocketClientFactory(this._discord.Configuration.Proxy, this._discord.ServiceProvider); - this._voiceWs.Disconnected += this.VoiceWS_SocketClosed; - this._voiceWs.MessageReceived += this.VoiceWS_SocketMessage; - this._voiceWs.Connected += this.VoiceWS_SocketOpened; - this._voiceWs.ExceptionThrown += this.VoiceWs_SocketException; - } - - ~VoiceNextConnection() - { - this.Dispose(); - } - - /// - /// Connects to the specified voice channel. - /// - /// A task representing the connection operation. - internal Task ConnectAsync() - { - var gwuri = new UriBuilder - { - Scheme = "wss", - Host = this.WebSocketEndpoint.Hostname, - Query = "encoding=json&v=4" - }; - - return this._voiceWs.ConnectAsync(gwuri.Uri); - } - - /// - /// Reconnects . - /// - /// A Task. - internal Task ReconnectAsync() - => this._voiceWs.DisconnectAsync(); - - /// - /// Starts . - /// - /// A Task. - internal async Task StartAsync() - { - // Let's announce our intentions to the server - var vdp = new VoiceDispatch(); - - if (!this.Resume) - { - vdp.OpCode = 0; - vdp.Payload = new VoiceIdentifyPayload - { - ServerId = this.ServerData.GuildId, - UserId = this.StateData.UserId.Value, - SessionId = this.StateData.SessionId, - Token = this.ServerData.Token - }; - this.Resume = true; - } - else - { - vdp.OpCode = 7; - vdp.Payload = new VoiceIdentifyPayload - { - ServerId = this.ServerData.GuildId, - SessionId = this.StateData.SessionId, - Token = this.ServerData.Token - }; - } - var vdj = JsonConvert.SerializeObject(vdp, Formatting.None); - await this.WsSendAsync(vdj).ConfigureAwait(false); - } - - /// - /// Waits the for ready async. - /// - /// A Task. - internal Task WaitForReadyAsync() - => this._readyWait.Task; - - /// - /// Enqueues the packet async. - /// - /// The packet. - /// The token. - /// A Task. - internal async Task EnqueuePacketAsync(RawVoicePacket packet, CancellationToken token = default) - { - await this._transmitChannel.Writer.WriteAsync(packet, token).ConfigureAwait(false); - this._queueCount++; - } - - /// - /// Prepares the packet. - /// - /// The pcm. - /// The target. - /// The length. - /// A bool. - internal bool PreparePacket(ReadOnlySpan pcm, out byte[] target, out int length) - { - target = null; - length = 0; - - if (this._isDisposed) - return false; - - var audioFormat = this.AudioFormat; - - var packetArray = ArrayPool.Shared.Rent(this._rtp.CalculatePacketSize(audioFormat.SampleCountToSampleSize(audioFormat.CalculateMaximumFrameSize()), this._selectedEncryptionMode)); - var packet = packetArray.AsSpan(); - - this._rtp.EncodeHeader(this._sequence, this._timestamp, this._ssrc, packet); - var opus = packet.Slice(Rtp.HEADER_SIZE, pcm.Length); - this._opus.Encode(pcm, ref opus); - - this._sequence++; - this._timestamp += (uint)audioFormat.CalculateFrameSize(audioFormat.CalculateSampleDuration(pcm.Length)); - - Span nonce = stackalloc byte[Sodium.NonceSize]; - switch (this._selectedEncryptionMode) - { - case EncryptionMode.XSalsa20Poly1305: - this._sodium.GenerateNonce(packet[..Rtp.HEADER_SIZE], nonce); - break; - - case EncryptionMode.XSalsa20Poly1305Suffix: - this._sodium.GenerateNonce(nonce); - break; - - case EncryptionMode.XSalsa20Poly1305Lite: - this._sodium.GenerateNonce(this._nonce++, nonce); - break; - - default: - ArrayPool.Shared.Return(packetArray); - throw new Exception("Unsupported encryption mode."); - } - - Span encrypted = stackalloc byte[Sodium.CalculateTargetSize(opus)]; - this._sodium.Encrypt(opus, encrypted, nonce); - encrypted.CopyTo(packet[Rtp.HEADER_SIZE..]); - packet = packet[..this._rtp.CalculatePacketSize(encrypted.Length, this._selectedEncryptionMode)]; - this._sodium.AppendNonce(nonce, packet, this._selectedEncryptionMode); - - target = packetArray; - length = packet.Length; - return true; - } - - /// - /// Voices the sender task. - /// - /// A Task. - private async Task VoiceSenderTask() - { - var token = this.SENDER_TOKEN; - var client = this._udpClient; - var reader = this._transmitChannel.Reader; - - byte[] data = null; - var length = 0; - - var synchronizerTicks = (double)Stopwatch.GetTimestamp(); - var synchronizerResolution = Stopwatch.Frequency * 0.005; - var tickResolution = 10_000_000.0 / Stopwatch.Frequency; - this._discord.Logger.LogDebug(VoiceNextEvents.Misc, "Timer accuracy: {0}/{1} (high resolution? {2})", Stopwatch.Frequency, synchronizerResolution, Stopwatch.IsHighResolution); - - while (!token.IsCancellationRequested) - { - await this._pauseEvent.WaitAsync().ConfigureAwait(false); - - var hasPacket = reader.TryRead(out var rawPacket); - if (hasPacket) - { - this._queueCount--; - - if (this._playingWait == null || this._playingWait.Task.IsCompleted) - this._playingWait = new TaskCompletionSource(); - } - - // Provided by Laura#0090 (214796473689178133); this is Python, but adaptable: - // - // delay = max(0, self.delay + ((start_time + self.delay * loops) + - time.time())) - // - // self.delay - // sample size - // start_time - // time since streaming started - // loops - // number of samples sent - // time.time() - // DateTime.Now - - if (hasPacket) - { - hasPacket = this.PreparePacket(rawPacket.Bytes.Span, out data, out length); - if (rawPacket.RentedBuffer != null) - ArrayPool.Shared.Return(rawPacket.RentedBuffer); - } - - var durationModifier = hasPacket ? rawPacket.Duration / 5 : 4; - var cts = Math.Max(Stopwatch.GetTimestamp() - synchronizerTicks, 0); - if (cts < synchronizerResolution * durationModifier) - await Task.Delay(TimeSpan.FromTicks((long)(((synchronizerResolution * durationModifier) - cts) * tickResolution))).ConfigureAwait(false); - - synchronizerTicks += synchronizerResolution * durationModifier; - - if (!hasPacket) - continue; - - await this.SendSpeakingAsync(true).ConfigureAwait(false); - await client.SendAsync(data, length).ConfigureAwait(false); - ArrayPool.Shared.Return(data); - - if (!rawPacket.Silence && this._queueCount == 0) - { - var nullpcm = new byte[this.AudioFormat.CalculateSampleSize(20)]; - for (var i = 0; i < 3; i++) - { - var nullpacket = new byte[nullpcm.Length]; - var nullpacketmem = nullpacket.AsMemory(); - await this.EnqueuePacketAsync(new RawVoicePacket(nullpacketmem, 20, true)).ConfigureAwait(false); - } - } - else if (this._queueCount == 0) - { - await this.SendSpeakingAsync(false).ConfigureAwait(false); - this._playingWait?.SetResult(true); - } - } - } - - /// - /// Processes the packet. - /// - /// The data. - /// The opus. - /// The pcm. - /// The pcm packets. - /// The voice sender. - /// The output format. - /// A bool. - private bool ProcessPacket(ReadOnlySpan data, ref Memory opus, ref Memory pcm, IList> pcmPackets, out AudioSender voiceSender, out AudioFormat outputFormat) - { - voiceSender = null; - outputFormat = default; - - if (!this._rtp.IsRtpHeader(data)) - return false; - - this._rtp.DecodeHeader(data, out var shortSequence, out var timestamp, out var ssrc, out var hasExtension); - - if (!this._transmittingSsrCs.TryGetValue(ssrc, out var vtx)) - { - var decoder = this._opus.CreateDecoder(); - - vtx = new AudioSender(ssrc, decoder) - { - // user isn't present as we haven't received a speaking event yet. - User = null - }; - } - - voiceSender = vtx; - var sequence = vtx.GetTrueSequenceAfterWrapping(shortSequence); - ushort gap = 0; - if (vtx.LastTrueSequence is ulong lastTrueSequence) - { - if (sequence <= lastTrueSequence) // out-of-order packet; discard - return false; - - gap = (ushort)(sequence - 1 - lastTrueSequence); - if (gap >= 5) - this._discord.Logger.LogWarning(VoiceNextEvents.VoiceReceiveFailure, "5 or more voice packets were dropped when receiving"); - } - - Span nonce = stackalloc byte[Sodium.NonceSize]; - this._sodium.GetNonce(data, nonce, this._selectedEncryptionMode); - this._rtp.GetDataFromPacket(data, out var encryptedOpus, this._selectedEncryptionMode); - - var opusSize = Sodium.CalculateSourceSize(encryptedOpus); - opus = opus[..opusSize]; - var opusSpan = opus.Span; - try - { - this._sodium.Decrypt(encryptedOpus, opusSpan, nonce); - - // Strip extensions, if any - if (hasExtension) - { - // RFC 5285, 4.2 One-Byte header - // http://www.rfcreader.com/#rfc5285_line186 - if (opusSpan[0] == 0xBE && opusSpan[1] == 0xDE) - { - var headerLen = (opusSpan[2] << 8) | opusSpan[3]; - var i = 4; - for (; i < headerLen + 4; i++) - { - var @byte = opusSpan[i]; - - // ID is currently unused since we skip it anyway - //var id = (byte)(@byte >> 4); - var length = (byte)(@byte & 0x0F) + 1; - - i += length; - } - - // Strip extension padding too - while (opusSpan[i] == 0) - i++; - - opusSpan = opusSpan[i..]; - } - - // TODO: consider implementing RFC 5285, 4.3. Two-Byte Header - } - - if (opusSpan[0] == 0x90) - { - // I'm not 100% sure what this header is/does, however removing the data causes no - // real issues, and has the added benefit of removing a lot of noise. - opusSpan = opusSpan[2..]; - } - - if (gap == 1) - { - var lastSampleCount = this._opus.GetLastPacketSampleCount(vtx.Decoder); - var fecpcm = new byte[this.AudioFormat.SampleCountToSampleSize(lastSampleCount)]; - var fecpcmMem = fecpcm.AsSpan(); - this._opus.Decode(vtx.Decoder, opusSpan, ref fecpcmMem, true, out _); - pcmPackets.Add(fecpcm.AsMemory(0, fecpcmMem.Length)); - } - else if (gap > 1) - { - var lastSampleCount = this._opus.GetLastPacketSampleCount(vtx.Decoder); - for (var i = 0; i < gap; i++) - { - var fecpcm = new byte[this.AudioFormat.SampleCountToSampleSize(lastSampleCount)]; - var fecpcmMem = fecpcm.AsSpan(); - this._opus.ProcessPacketLoss(vtx.Decoder, lastSampleCount, ref fecpcmMem); - pcmPackets.Add(fecpcm.AsMemory(0, fecpcmMem.Length)); - } - } - - var pcmSpan = pcm.Span; - this._opus.Decode(vtx.Decoder, opusSpan, ref pcmSpan, false, out outputFormat); - pcm = pcm[..pcmSpan.Length]; - } - finally - { - vtx.LastTrueSequence = sequence; - } - - return true; - } - - /// - /// Processes the voice packet. - /// - /// The data. - /// A Task. - private async Task ProcessVoicePacket(byte[] data) - { - if (data.Length < 13) // minimum packet length - return; - - try - { - var pcm = new byte[this.AudioFormat.CalculateMaximumFrameSize()]; - var pcmMem = pcm.AsMemory(); - var opus = new byte[pcm.Length]; - var opusMem = opus.AsMemory(); - var pcmFillers = new List>(); - if (!this.ProcessPacket(data, ref opusMem, ref pcmMem, pcmFillers, out var vtx, out var audioFormat)) - return; - - foreach (var pcmFiller in pcmFillers) - await this._voiceReceived.InvokeAsync(this, new VoiceReceiveEventArgs(this._discord.ServiceProvider) - { - Ssrc = vtx.Ssrc, - User = vtx.User, - PcmData = pcmFiller, - OpusData = Array.Empty().AsMemory(), - AudioFormat = audioFormat, - AudioDuration = audioFormat.CalculateSampleDuration(pcmFiller.Length) - }).ConfigureAwait(false); - - await this._voiceReceived.InvokeAsync(this, new VoiceReceiveEventArgs(this._discord.ServiceProvider) - { - Ssrc = vtx.Ssrc, - User = vtx.User, - PcmData = pcmMem, - OpusData = opusMem, - AudioFormat = audioFormat, - AudioDuration = audioFormat.CalculateSampleDuration(pcmMem.Length) - }).ConfigureAwait(false); - } - catch (Exception ex) - { - this._discord.Logger.LogError(VoiceNextEvents.VoiceReceiveFailure, ex, "Exception occurred when decoding incoming audio data"); - } - } - - /// - /// Processes the keepalive. - /// - /// The data. - private void ProcessKeepalive(byte[] data) - { - try - { - var keepalive = BinaryPrimitives.ReadUInt64LittleEndian(data); - - if (!this._keepaliveTimestamps.TryRemove(keepalive, out var timestamp)) - return; - - var tdelta = (int)((Stopwatch.GetTimestamp() - timestamp) / (double)Stopwatch.Frequency * 1000); - this._discord.Logger.LogDebug(VoiceNextEvents.VoiceKeepalive, "Received UDP keepalive {0} (ping {1}ms)", keepalive, tdelta); - Volatile.Write(ref this._udpPing, tdelta); - } - catch (Exception ex) - { - this._discord.Logger.LogError(VoiceNextEvents.VoiceKeepalive, ex, "Exception occurred when handling keepalive"); - } - } - - /// - /// Udps the receiver task. - /// - /// A Task. - private async Task UdpReceiverTask() - { - var token = this.RECEIVER_TOKEN; - var client = this._udpClient; - - while (!token.IsCancellationRequested) - { - var data = await client.ReceiveAsync().ConfigureAwait(false); - if (data.Length == 8) - this.ProcessKeepalive(data); - else if (this._configuration.EnableIncoming) - await this.ProcessVoicePacket(data).ConfigureAwait(false); - } - } - - /// - /// Sends a speaking status to the connected voice channel. - /// - /// Whether the current user is speaking or not. - /// A task representing the sending operation. - public async Task SendSpeakingAsync(bool speaking = true) - { - if (!this._isInitialized) - throw new InvalidOperationException("The connection is not initialized"); - - if (this._isSpeaking != speaking) - { - this._isSpeaking = speaking; - var pld = new VoiceDispatch - { - OpCode = 5, - Payload = new VoiceSpeakingPayload - { - Speaking = speaking, - Delay = 0 - } - }; - - var plj = JsonConvert.SerializeObject(pld, Formatting.None); - await this.WsSendAsync(plj).ConfigureAwait(false); - } - } - - /// - /// Gets a transmit stream for this connection, optionally specifying a packet size to use with the stream. If a stream is already configured, it will return the existing one. - /// - /// Duration, in ms, to use for audio packets. - /// Transmit stream. - public VoiceTransmitSink GetTransmitSink(int sampleDuration = 20) - { - if (!AudioFormat.AllowedSampleDurations.Contains(sampleDuration)) - throw new ArgumentOutOfRangeException(nameof(sampleDuration), "Invalid PCM sample duration specified."); - - this._transmitStream ??= new VoiceTransmitSink(this, sampleDuration); - - return this._transmitStream; - } - - /// - /// Asynchronously waits for playback to be finished. Playback is finished when speaking = false is signaled. - /// - /// A task representing the waiting operation. - public async Task WaitForPlaybackFinishAsync() - { - if (this._playingWait != null) - await this._playingWait.Task.ConfigureAwait(false); - } - - /// - /// Pauses playback. - /// - public void Pause() - => this._pauseEvent.Reset(); - - /// - /// Asynchronously resumes playback. - /// - /// - public async Task ResumeAsync() - => await this._pauseEvent.SetAsync().ConfigureAwait(false); - - /// - /// Disconnects and disposes this voice connection. - /// - public void Disconnect() - => this.Dispose(); - - /// - /// Disconnects and disposes this voice connection. - /// - public void Dispose() - { - if (this._isDisposed) - return; - - try - { - this._isDisposed = true; - this._isInitialized = false; - this._tokenSource?.Cancel(); - this._senderTokenSource?.Cancel(); - this._receiverTokenSource?.Cancel(); - } - catch (Exception ex) - { - this._discord.Logger.LogError(ex, ex.Message); - } - - try - { - this._voiceWs.DisconnectAsync().ConfigureAwait(false).GetAwaiter().GetResult(); - this._udpClient.Close(); - } - catch { } - - try - { - this._keepaliveTokenSource?.Cancel(); - this._tokenSource?.Dispose(); - this._senderTokenSource?.Dispose(); - this._receiverTokenSource?.Dispose(); - this._keepaliveTokenSource?.Dispose(); - this._opus?.Dispose(); - this._opus = null; - this._sodium?.Dispose(); - this._sodium = null; - this._rtp?.Dispose(); - this._rtp = null; - } - catch (Exception ex) - { - this._discord.Logger.LogError(ex, ex.Message); - } - - this.VoiceDisconnected?.Invoke(this._guild); - GC.SuppressFinalize(this); - } - - /// - /// Heartbeats . - /// - /// A Task. - private async Task HeartbeatAsync() - { - await Task.Yield(); - - var token = this.TOKEN; - while (true) - { - try - { - token.ThrowIfCancellationRequested(); - - var dt = DateTime.Now; - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceHeartbeat, "Sent heartbeat"); - - var hbd = new VoiceDispatch - { - OpCode = 3, - Payload = UnixTimestamp(dt) - }; - var hbj = JsonConvert.SerializeObject(hbd); - await this.WsSendAsync(hbj).ConfigureAwait(false); - - this._lastHeartbeat = dt; - await Task.Delay(this._heartbeatInterval).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - return; - } - } - } - - /// - /// Keepalives . - /// - /// A Task. - private async Task KeepaliveAsync() - { - await Task.Yield(); - - var token = this.KEEPALIVE_TOKEN; - var client = this._udpClient; - - while (!token.IsCancellationRequested) - { - var timestamp = Stopwatch.GetTimestamp(); - var keepalive = Volatile.Read(ref this._lastKeepalive); - Volatile.Write(ref this._lastKeepalive, keepalive + 1); - this._keepaliveTimestamps.TryAdd(keepalive, timestamp); - - var packet = new byte[8]; - BinaryPrimitives.WriteUInt64LittleEndian(packet, keepalive); - - await client.SendAsync(packet, packet.Length).ConfigureAwait(false); - - await Task.Delay(5000, token).ConfigureAwait(false); - } - } - - /// - /// Stage1S . - /// - /// The voice ready. - /// A Task. - private async Task Stage1(VoiceReadyPayload voiceReady) - { - // IP Discovery - this._udpClient.Setup(this.UdpEndpoint); - - var pck = new byte[70]; - PreparePacket(pck); - await this._udpClient.SendAsync(pck, pck.Length).ConfigureAwait(false); - - var ipd = await this._udpClient.ReceiveAsync().ConfigureAwait(false); - ReadPacket(ipd, out var ip, out var port); - this._discoveredEndpoint = new IpEndpoint - { - Address = ip, - Port = port - }; - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Endpoint discovery finished - discovered endpoint is {0}:{1}", ip, port); - - void PreparePacket(byte[] packet) - { - var ssrc = this._ssrc; - var packetSpan = packet.AsSpan(); - MemoryMarshal.Write(packetSpan, ref ssrc); - Helpers.ZeroFill(packetSpan); - } - - void ReadPacket(byte[] packet, out System.Net.IPAddress decodedIp, out ushort decodedPort) - { - var packetSpan = packet.AsSpan(); - - var ipString = Utilities.UTF8.GetString(packet, 4, 64 /* 70 - 6 */).TrimEnd('\0'); - decodedIp = System.Net.IPAddress.Parse(ipString); - - decodedPort = BinaryPrimitives.ReadUInt16LittleEndian(packetSpan[68 /* 70 - 2 */..]); - } - - // Select voice encryption mode - var selectedEncryptionMode = Sodium.SelectMode(voiceReady.Modes); - this._selectedEncryptionMode = selectedEncryptionMode.Value; - - // Ready - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Selected encryption mode is {0}", selectedEncryptionMode.Key); - var vsp = new VoiceDispatch - { - OpCode = 1, - Payload = new VoiceSelectProtocolPayload - { - Protocol = "udp", - Data = new VoiceSelectProtocolPayloadData - { - Address = this._discoveredEndpoint.Address.ToString(), - Port = (ushort)this._discoveredEndpoint.Port, - Mode = selectedEncryptionMode.Key - } - } - }; - var vsj = JsonConvert.SerializeObject(vsp, Formatting.None); - await this.WsSendAsync(vsj).ConfigureAwait(false); - - this._senderTokenSource = new CancellationTokenSource(); - this._senderTask = Task.Run(this.VoiceSenderTask, this.SENDER_TOKEN); - - this._receiverTokenSource = new CancellationTokenSource(); - this._receiverTask = Task.Run(this.UdpReceiverTask, this.RECEIVER_TOKEN); - } - - /// - /// Stage2S . - /// - /// The voice session description. - /// A Task. - private async Task Stage2(VoiceSessionDescriptionPayload voiceSessionDescription) - { - this._selectedEncryptionMode = Sodium.SupportedModes[voiceSessionDescription.Mode.ToLowerInvariant()]; - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Discord updated encryption mode - new mode is {0}", this._selectedEncryptionMode); - - // start keepalive - this._keepaliveTokenSource = new CancellationTokenSource(); - this._keepaliveTask = this.KeepaliveAsync(); - - // send 3 packets of silence to get things going - var nullpcm = new byte[this.AudioFormat.CalculateSampleSize(20)]; - for (var i = 0; i < 3; i++) - { - var nullPcm = new byte[nullpcm.Length]; - var nullpacketmem = nullPcm.AsMemory(); - await this.EnqueuePacketAsync(new RawVoicePacket(nullpacketmem, 20, true)).ConfigureAwait(false); - } - - this._isInitialized = true; - this._readyWait.SetResult(true); - } - - /// - /// Handles the dispatch. - /// - /// The jo. - /// A Task. - private async Task HandleDispatch(JObject jo) - { - var opc = (int)jo["op"]; - var opp = jo["d"] as JObject; - - switch (opc) - { - case 2: // READY - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received READY (OP2)"); - var vrp = opp.ToObject(); - this._ssrc = vrp.Ssrc; - this.UdpEndpoint = new ConnectionEndpoint(vrp.Address, vrp.Port); - // this is not the valid interval - // oh, discord - //this.HeartbeatInterval = vrp.HeartbeatInterval; - this._heartbeatTask = Task.Run(this.HeartbeatAsync); - await this.Stage1(vrp).ConfigureAwait(false); - break; - - case 4: // SESSION_DESCRIPTION - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received SESSION_DESCRIPTION (OP4)"); - var vsd = opp.ToObject(); - this._key = vsd.SecretKey; - this._sodium = new Sodium(this._key.AsMemory()); - await this.Stage2(vsd).ConfigureAwait(false); - break; - - case 5: // SPEAKING - // Don't spam OP5 - // No longer spam, Discord supposedly doesn't send many of these - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received SPEAKING (OP5)"); - var spd = opp.ToObject(); - var foundUserInCache = this._discord.TryGetCachedUserInternal(spd.UserId.Value, out var resolvedUser); - var spk = new UserSpeakingEventArgs(this._discord.ServiceProvider) - { - Speaking = spd.Speaking, - Ssrc = spd.Ssrc.Value, - User = resolvedUser, - }; - - if (foundUserInCache && this._transmittingSsrCs.TryGetValue(spk.Ssrc, out var txssrc5) && txssrc5.Id == 0) - { - txssrc5.User = spk.User; - } - else - { - var opus = this._opus.CreateDecoder(); - var vtx = new AudioSender(spk.Ssrc, opus) - { - User = await this._discord.GetUserAsync(spd.UserId.Value).ConfigureAwait(false) - }; - - if (!this._transmittingSsrCs.TryAdd(spk.Ssrc, vtx)) - this._opus.DestroyDecoder(opus); - } - - await this._userSpeaking.InvokeAsync(this, spk).ConfigureAwait(false); - break; - - case 6: // HEARTBEAT ACK - var dt = DateTime.Now; - var ping = (int)(dt - this._lastHeartbeat).TotalMilliseconds; - Volatile.Write(ref this._wsPing, ping); - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received HEARTBEAT_ACK (OP6, {0}ms)", ping); - this._lastHeartbeat = dt; - break; - - case 8: // HELLO - // this sends a heartbeat interval that we need to use for heartbeating - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received HELLO (OP8)"); - this._heartbeatInterval = opp["heartbeat_interval"].ToObject(); - break; - - case 9: // RESUMED - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received RESUMED (OP9)"); - this._heartbeatTask = Task.Run(this.HeartbeatAsync); - break; - - case 12: // CLIENT_CONNECTED - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received CLIENT_CONNECTED (OP12)"); - var ujpd = opp.ToObject(); - var usrj = await this._discord.GetUserAsync(ujpd.UserId).ConfigureAwait(false); - { - var opus = this._opus.CreateDecoder(); - var vtx = new AudioSender(ujpd.Ssrc, opus) - { - User = usrj - }; - - if (!this._transmittingSsrCs.TryAdd(vtx.Ssrc, vtx)) - this._opus.DestroyDecoder(opus); - } - - await this._userJoined.InvokeAsync(this, new VoiceUserJoinEventArgs(this._discord.ServiceProvider) { User = usrj, Ssrc = ujpd.Ssrc }).ConfigureAwait(false); - break; - - case 13: // CLIENT_DISCONNECTED - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received CLIENT_DISCONNECTED (OP13)"); - var ulpd = opp.ToObject(); - var txssrc = this._transmittingSsrCs.FirstOrDefault(x => x.Value.Id == ulpd.UserId); - if (this._transmittingSsrCs.ContainsKey(txssrc.Key)) - { - this._transmittingSsrCs.TryRemove(txssrc.Key, out var txssrc13); - this._opus.DestroyDecoder(txssrc13.Decoder); - } - - var usrl = await this._discord.GetUserAsync(ulpd.UserId).ConfigureAwait(false); - await this._userLeft.InvokeAsync(this, new VoiceUserLeaveEventArgs(this._discord.ServiceProvider) - { - User = usrl, - Ssrc = txssrc.Key - }).ConfigureAwait(false); - break; - - default: - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received unknown voice opcode (OP{0})", opc); - break; - } - } - - /// - /// Voices the w s_ socket closed. - /// - /// The client. - /// The e. - /// A Task. - private async Task VoiceWS_SocketClosed(IWebSocketClient client, SocketCloseEventArgs e) - { - this._discord.Logger.LogDebug(VoiceNextEvents.VoiceConnectionClose, "Voice WebSocket closed ({0}, '{1}')", e.CloseCode, e.CloseMessage); - - // generally this should not be disposed on all disconnects, only on requested ones - // or something - // otherwise problems happen - //this.Dispose(); - - if (e.CloseCode == 4006 || e.CloseCode == 4009) - this.Resume = false; - - if (!this._isDisposed) - { - this._tokenSource.Cancel(); - this._tokenSource = new CancellationTokenSource(); - this._voiceWs = this._discord.Configuration.WebSocketClientFactory(this._discord.Configuration.Proxy, this._discord.ServiceProvider); - this._voiceWs.Disconnected += this.VoiceWS_SocketClosed; - this._voiceWs.MessageReceived += this.VoiceWS_SocketMessage; - this._voiceWs.Connected += this.VoiceWS_SocketOpened; - - if (this.Resume) // emzi you dipshit - await this.ConnectAsync().ConfigureAwait(false); - } - } - - /// - /// Voices the w s_ socket message. - /// - /// The client. - /// The e. - /// A Task. - private Task VoiceWS_SocketMessage(IWebSocketClient client, SocketMessageEventArgs e) - { - if (e is not SocketTextMessageEventArgs et) - { - this._discord.Logger.LogCritical(VoiceNextEvents.VoiceGatewayError, "Discord Voice Gateway sent binary data - unable to process"); - return Task.CompletedTask; - } - - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceWsRx, et.Message); - return this.HandleDispatch(JObject.Parse(et.Message)); - } - - /// - /// Voices the w s_ socket opened. - /// - /// The client. - /// The e. - /// A Task. - private Task VoiceWS_SocketOpened(IWebSocketClient client, SocketEventArgs e) - => this.StartAsync(); - - /// - /// Voices the ws_ socket exception. - /// - /// The client. - /// The e. - /// A Task. - private Task VoiceWs_SocketException(IWebSocketClient client, SocketErrorEventArgs e) - => this._voiceSocketError.InvokeAsync(this, new SocketErrorEventArgs(this._discord.ServiceProvider) { Exception = e.Exception }); - - /// - /// Ws the send async. - /// - /// The payload. - /// A Task. - private async Task WsSendAsync(string payload) - { - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceWsTx, payload); - await this._voiceWs.SendMessageAsync(payload).ConfigureAwait(false); - } - - /// - /// Gets the unix timestamp. - /// - /// The datetime. - private static uint UnixTimestamp(DateTime dt) - { - var ts = dt - s_unixEpoch; - var sd = ts.TotalSeconds; - var si = (uint)sd; - return si; - } -} diff --git a/DisCatSharp.VoiceNext/VoiceNextEvents.cs b/DisCatSharp.VoiceNext/VoiceNextEvents.cs deleted file mode 100644 index 940859a53..000000000 --- a/DisCatSharp.VoiceNext/VoiceNextEvents.cs +++ /dev/null @@ -1,81 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using Microsoft.Extensions.Logging; - -namespace DisCatSharp.VoiceNext; - -/// -/// Contains well-defined event IDs used by the VoiceNext extension. -/// -public static class VoiceNextEvents -{ - /// - /// Miscellaneous events, that do not fit in any other category. - /// - public static EventId Misc { get; } = new(300, "VoiceNext"); - - /// - /// Events pertaining to Voice Gateway connection lifespan, specifically, heartbeats. - /// - public static EventId VoiceHeartbeat { get; } = new(301, nameof(VoiceHeartbeat)); - - /// - /// Events pertaining to Voice Gateway connection early lifespan, specifically, the establishing thereof as well as negotiating various modes. - /// - public static EventId VoiceHandshake { get; } = new(302, nameof(VoiceHandshake)); - - /// - /// Events emitted when incoming voice data is corrupted, or packets are being dropped. - /// - public static EventId VoiceReceiveFailure { get; } = new(303, nameof(VoiceReceiveFailure)); - - /// - /// Events pertaining to UDP connection lifespan, specifically the keepalive (or heartbeats). - /// - public static EventId VoiceKeepalive { get; } = new(304, nameof(VoiceKeepalive)); - - /// - /// Events emitted for high-level dispatch receive events. - /// - public static EventId VoiceDispatch { get; } = new(305, nameof(VoiceDispatch)); - - /// - /// Events emitted for Voice Gateway connection closes, clean or otherwise. - /// - public static EventId VoiceConnectionClose { get; } = new(306, nameof(VoiceConnectionClose)); - - /// - /// Events emitted when decoding data received via Voice Gateway fails for any reason. - /// - public static EventId VoiceGatewayError { get; } = new(307, nameof(VoiceGatewayError)); - - /// - /// Events containing raw (but decompressed) payloads, received from Discord Voice Gateway. - /// - public static EventId VoiceWsRx { get; } = new(308, "Voice ↓"); - - /// - /// Events containing raw payloads, as they're being sent to Discord Voice Gateway. - /// - public static EventId VoiceWsTx { get; } = new(309, "Voice ↑"); -} diff --git a/DisCatSharp.VoiceNext/VoiceNextExtension.cs b/DisCatSharp.VoiceNext/VoiceNextExtension.cs deleted file mode 100644 index caac30fdb..000000000 --- a/DisCatSharp.VoiceNext/VoiceNextExtension.cs +++ /dev/null @@ -1,264 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Collections.Concurrent; -using System.Threading.Tasks; - -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.EventArgs; -using DisCatSharp.Net; -using DisCatSharp.VoiceNext.Entities; - -using Newtonsoft.Json; - -namespace DisCatSharp.VoiceNext; - -/// -/// Represents VoiceNext extension, which acts as Discord voice client. -/// -public sealed class VoiceNextExtension : BaseExtension -{ - /// - /// Gets or sets the configuration. - /// - private readonly VoiceNextConfiguration _configuration; - - /// - /// Gets or sets the active connections. - /// - private readonly ConcurrentDictionary _activeConnections; - - /// - /// Gets or sets the voice state updates. - /// - private readonly ConcurrentDictionary> _voiceStateUpdates; - - /// - /// Gets or sets the voice server updates. - /// - private readonly ConcurrentDictionary> _voiceServerUpdates; - - /// - /// Gets whether this connection has incoming voice enabled. - /// - public bool IsIncomingEnabled { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The config. - internal VoiceNextExtension(VoiceNextConfiguration config) - { - this._configuration = new VoiceNextConfiguration(config); - this.IsIncomingEnabled = config.EnableIncoming; - - this._activeConnections = new ConcurrentDictionary(); - this._voiceStateUpdates = new ConcurrentDictionary>(); - this._voiceServerUpdates = new ConcurrentDictionary>(); - } - - /// - /// DO NOT USE THIS MANUALLY. - /// - /// DO NOT USE THIS MANUALLY. - /// - protected internal override void Setup(DiscordClient client) - { - if (this.Client != null) - throw new InvalidOperationException("What did I tell you?"); - - this.Client = client; - - this.Client.VoiceStateUpdated += this.Client_VoiceStateUpdate; - this.Client.VoiceServerUpdated += this.Client_VoiceServerUpdate; - } - - /// - /// Create a VoiceNext connection for the specified channel. - /// - /// Channel to connect to. - /// VoiceNext connection for this channel. - public async Task ConnectAsync(DiscordChannel channel) - { - if (channel.Type != ChannelType.Voice && channel.Type != ChannelType.Stage) - throw new ArgumentException("Invalid channel specified; needs to be voice or stage channel", nameof(channel)); - - if (channel.Guild == null) - throw new ArgumentException("Invalid channel specified; needs to be guild channel", nameof(channel)); - - if (!channel.PermissionsFor(channel.Guild.CurrentMember).HasPermission(Permissions.AccessChannels | Permissions.UseVoice)) - throw new InvalidOperationException("You need AccessChannels and UseVoice permission to connect to this voice channel"); - - var gld = channel.Guild; - if (this._activeConnections.ContainsKey(gld.Id)) - throw new InvalidOperationException("This guild already has a voice connection"); - - var vstut = new TaskCompletionSource(); - var vsrut = new TaskCompletionSource(); - this._voiceStateUpdates[gld.Id] = vstut; - this._voiceServerUpdates[gld.Id] = vsrut; - - var vsd = new VoiceDispatch - { - OpCode = 4, - Payload = new VoiceStateUpdatePayload - { - GuildId = gld.Id, - ChannelId = channel.Id, - Deafened = false, - Muted = false - } - }; - var vsj = JsonConvert.SerializeObject(vsd, Formatting.None); - await (channel.Discord as DiscordClient).WsSendAsync(vsj).ConfigureAwait(false); - - var vstu = await vstut.Task.ConfigureAwait(false); - var vstup = new VoiceStateUpdatePayload - { - SessionId = vstu.SessionId, - UserId = vstu.User.Id - }; - var vsru = await vsrut.Task.ConfigureAwait(false); - var vsrup = new VoiceServerUpdatePayload - { - Endpoint = vsru.Endpoint, - GuildId = vsru.Guild.Id, - Token = vsru.VoiceToken - }; - - var vnc = new VoiceNextConnection(this.Client, gld, channel, this._configuration, vsrup, vstup); - vnc.VoiceDisconnected += this.Vnc_VoiceDisconnected; - await vnc.ConnectAsync().ConfigureAwait(false); - await vnc.WaitForReadyAsync().ConfigureAwait(false); - this._activeConnections[gld.Id] = vnc; - return vnc; - } - - /// - /// Gets a VoiceNext connection for specified guild. - /// - /// Guild to get VoiceNext connection for. - /// VoiceNext connection for the specified guild. - public VoiceNextConnection? GetConnection(DiscordGuild guild) => this._activeConnections.TryGetValue(guild.Id, out var connection) ? connection : null; - - /// - /// Vnc_S the voice disconnected. - /// - /// The guild. - /// A Task. - private async Task Vnc_VoiceDisconnected(DiscordGuild guild) - { - if (this._activeConnections.ContainsKey(guild.Id)) - this._activeConnections.TryRemove(guild.Id, out _); - - var vsd = new VoiceDispatch - { - OpCode = 4, - Payload = new VoiceStateUpdatePayload - { - GuildId = guild.Id, - ChannelId = null - } - }; - var vsj = JsonConvert.SerializeObject(vsd, Formatting.None); - await (guild.Discord as DiscordClient).WsSendAsync(vsj).ConfigureAwait(false); - } - - /// - /// Client_S the voice state update. - /// - /// The client. - /// The e. - /// A Task. - private Task Client_VoiceStateUpdate(DiscordClient client, VoiceStateUpdateEventArgs e) - { - var gld = e.Guild; - if (gld == null) - return Task.CompletedTask; - - if (e.User == null) - return Task.CompletedTask; - - if (e.User.Id == this.Client.CurrentUser.Id) - { - if (e.After.Channel == null && this._activeConnections.TryRemove(gld.Id, out var ac)) - ac.Disconnect(); - - if (this._activeConnections.TryGetValue(e.Guild.Id, out var vnc)) - vnc.TargetChannel = e.Channel; - - if (!string.IsNullOrWhiteSpace(e.SessionId) && e.Channel != null && this._voiceStateUpdates.TryRemove(gld.Id, out var xe)) - xe.SetResult(e); - } - - return Task.CompletedTask; - } - - /// - /// Client_S the voice server update. - /// - /// The client. - /// The e. - /// A Task. - private async Task Client_VoiceServerUpdate(DiscordClient client, VoiceServerUpdateEventArgs e) - { - var gld = e.Guild; - if (gld == null) - return; - - if (this._activeConnections.TryGetValue(e.Guild.Id, out var vnc)) - { - vnc.ServerData = new VoiceServerUpdatePayload - { - Endpoint = e.Endpoint, - GuildId = e.Guild.Id, - Token = e.VoiceToken - }; - - var eps = e.Endpoint; - var epi = eps.LastIndexOf(':'); - var eph = string.Empty; - var epp = 443; - if (epi != -1) - { - eph = eps[..epi]; - epp = int.Parse(eps[(epi + 1)..]); - } - else - { - eph = eps; - } - vnc.WebSocketEndpoint = new ConnectionEndpoint { Hostname = eph, Port = epp }; - - vnc.Resume = false; - await vnc.ReconnectAsync().ConfigureAwait(false); - } - - if (this._voiceServerUpdates.ContainsKey(gld.Id)) - { - this._voiceServerUpdates.TryRemove(gld.Id, out var xe); - xe.SetResult(e); - } - } -} diff --git a/DisCatSharp.VoiceNext/VoiceTransmitSink.cs b/DisCatSharp.VoiceNext/VoiceTransmitSink.cs deleted file mode 100644 index 867a29f5c..000000000 --- a/DisCatSharp.VoiceNext/VoiceTransmitSink.cs +++ /dev/null @@ -1,282 +0,0 @@ -// This file is part of the DisCatSharp project, based off DSharpPlus. -// -// Copyright (c) 2021-2022 AITSYS -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.Buffers; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; - -using DisCatSharp.VoiceNext.Codec; - -namespace DisCatSharp.VoiceNext; - -/// -/// Sink used to transmit audio data via . -/// -public sealed class VoiceTransmitSink : IDisposable -{ - /// - /// Gets the PCM sample duration for this sink. - /// - public int SampleDuration { get; } - - /// - /// Gets the length of the PCM buffer for this sink. - /// Written packets should adhere to this size, but the sink will adapt to fit. - /// - public int SampleLength - => this._pcmBuffer.Length; - - /// - /// Gets or sets the volume modifier for this sink. Changing this will alter the volume of the output. 1.0 is 100%. - /// - public double VolumeModifier - { - get => this._volume; - set - { - if (value < 0 || value > 2.5) - throw new ArgumentOutOfRangeException(nameof(value), "Volume needs to be between 0% and 250%."); - - this._volume = value; - } - } - private double _volume = 1.0; - - /// - /// Gets the connection. - /// - private readonly VoiceNextConnection _connection; - - /// - /// Gets the pcm buffer. - /// - private readonly byte[] _pcmBuffer; - - /// - /// Gets the pcm memory. - /// - private readonly Memory _pcmMemory; - - /// - /// Gets or sets the pcm buffer length. - /// - private int _pcmBufferLength; - - /// - /// Gets the write semaphore. - /// - private readonly SemaphoreSlim _writeSemaphore; - - /// - /// Gets the filters. - /// - private readonly List _filters; - - /// - /// Initializes a new instance of the class. - /// - /// The vnc. - /// The pcm buffer duration. - internal VoiceTransmitSink(VoiceNextConnection vnc, int pcmBufferDuration) - { - this._connection = vnc; - this.SampleDuration = pcmBufferDuration; - this._pcmBuffer = new byte[vnc.AudioFormat.CalculateSampleSize(pcmBufferDuration)]; - this._pcmMemory = this._pcmBuffer.AsMemory(); - this._pcmBufferLength = 0; - this._writeSemaphore = new SemaphoreSlim(1, 1); - this._filters = new List(); - } - - /// - /// Writes PCM data to the sink. The data is prepared for transmission, and enqueued. - /// - /// PCM data buffer to send. - /// Start of the data in the buffer. - /// Number of bytes from the buffer. - /// The token to monitor for cancellation requests. - public async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default) => await this.WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken).ConfigureAwait(false); - - /// - /// Writes PCM data to the sink. The data is prepared for transmission, and enqueued. - /// - /// PCM data buffer to send. - /// The token to monitor for cancellation requests. - public async Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) - { - await this._writeSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - var remaining = buffer.Length; - var buffSpan = buffer; - var pcmSpan = this._pcmMemory; - - while (remaining > 0) - { - var len = Math.Min(pcmSpan.Length - this._pcmBufferLength, remaining); - - var tgt = pcmSpan[this._pcmBufferLength..]; - var src = buffSpan[..len]; - - src.CopyTo(tgt); - this._pcmBufferLength += len; - remaining -= len; - buffSpan = buffSpan[len..]; - - if (this._pcmBufferLength == this._pcmBuffer.Length) - { - this.ApplyFiltersSync(pcmSpan); - - this._pcmBufferLength = 0; - - var packet = ArrayPool.Shared.Rent(this._pcmMemory.Length); - var packetMemory = packet.AsMemory()[..this._pcmMemory.Length]; - this._pcmMemory.CopyTo(packetMemory); - - await this._connection.EnqueuePacketAsync(new RawVoicePacket(packetMemory, this.SampleDuration, false, packet), cancellationToken).ConfigureAwait(false); - } - } - } - finally - { - this._writeSemaphore.Release(); - } - } - - /// - /// Flushes the rest of the PCM data in this buffer to VoiceNext packet queue. - /// - /// The token to monitor for cancellation requests. - public async Task FlushAsync(CancellationToken cancellationToken = default) - { - var pcm = this._pcmMemory; - Helpers.ZeroFill(pcm[this._pcmBufferLength..].Span); - - this.ApplyFiltersSync(pcm); - - var packet = ArrayPool.Shared.Rent(pcm.Length); - var packetMemory = packet.AsMemory()[..pcm.Length]; - pcm.CopyTo(packetMemory); - - await this._connection.EnqueuePacketAsync(new RawVoicePacket(packetMemory, this.SampleDuration, false, packet), cancellationToken).ConfigureAwait(false); - } - - /// - /// Pauses playback. - /// - public void Pause() - => this._connection.Pause(); - - /// - /// Resumes playback. - /// - /// - public async Task ResumeAsync() - => await this._connection.ResumeAsync().ConfigureAwait(false); - - /// - /// Gets the collection of installed PCM filters, in order of their execution. - /// - /// Installed PCM filters, in order of execution. - public IEnumerable GetInstalledFilters() - { - foreach (var filter in this._filters) - yield return filter; - } - - /// - /// Installs a new PCM filter, with specified execution order. - /// - /// Filter to install. - /// Order of the new filter. This determines where the filter will be inserted in the filter pipeline. - public void InstallFilter(IVoiceFilter filter, int order = int.MaxValue) - { - if (filter == null) - throw new ArgumentNullException(nameof(filter)); - - if (order < 0) - throw new ArgumentOutOfRangeException(nameof(order), "Filter order must be greater than or equal to 0."); - - lock (this._filters) - { - var filters = this._filters; - if (order >= filters.Count) - filters.Add(filter); - else - filters.Insert(order, filter); - } - } - - /// - /// Uninstalls an installed PCM filter. - /// - /// Filter to uninstall. - /// Whether the filter was uninstalled. - public bool UninstallFilter(IVoiceFilter filter) - { - if (filter == null) - throw new ArgumentNullException(nameof(filter)); - - lock (this._filters) - { - var filters = this._filters; - return filters.Contains(filter) && filters.Remove(filter); - } - } - - /// - /// Applies the filters sync. - /// - /// The pcm span. - private void ApplyFiltersSync(Memory pcmSpan) - { - var pcm16 = MemoryMarshal.Cast(pcmSpan.Span); - - // pass through any filters, if applicable - lock (this._filters) - { - if (this._filters.Any()) - { - foreach (var filter in this._filters) - filter.Transform(pcm16, this._connection.AudioFormat, this.SampleDuration); - } - } - - if (this.VolumeModifier != 1) - { - // alter volume - for (var i = 0; i < pcm16.Length; i++) - pcm16[i] = (short)(pcm16[i] * this.VolumeModifier); - } - } - - /// - /// Disposes . - /// - public void Dispose() - => this._writeSemaphore?.Dispose(); -} diff --git a/DisCatSharp.VoiceNext/global.json b/DisCatSharp.VoiceNext/global.json deleted file mode 100644 index 0f01f74d0..000000000 --- a/DisCatSharp.VoiceNext/global.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "sdk": { - "version": "7.0.100", - "rollForward": "latestMinor", - "allowPrerelease": true - } - } \ No newline at end of file diff --git a/DisCatSharp.sln b/DisCatSharp.sln index 05b3379c0..28d58b0ca 100644 --- a/DisCatSharp.sln +++ b/DisCatSharp.sln @@ -1,171 +1,159 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.31911.260 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp", "DisCatSharp\DisCatSharp.csproj", "{EB3D8310-DFAD-4295-97F9-82E253647583}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.VoiceNext", "DisCatSharp.VoiceNext\DisCatSharp.VoiceNext.csproj", "{FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4255B64D-92EC-46B3-BC3B-ED2C3A8073EE}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .gitattributes = .gitattributes .gitignore = .gitignore BUILDING.md = BUILDING.md CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md CONTRIBUTING.md = CONTRIBUTING.md global.json = global.json LICENSE.md = LICENSE.md README.md = README.md SECURITY.md = SECURITY.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext\DisCatSharp.CommandsNext.csproj", "{C8ED55FB-E028-468D-955F-1534C20274EF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Interactivity", "DisCatSharp.Interactivity\DisCatSharp.Interactivity.csproj", "{DD32BEC3-0189-479F-86DC-CCF95E5634A9}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F953F5D0-F0C9-41E6-ADBF-60A76D295899}" ProjectSection(SolutionItems) = preProject .nuget\NuGet.config = .nuget\NuGet.config EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build Items", "Build Items", "{84464D70-687B-40A8-836D-C4F737698969}" ProjectSection(SolutionItems) = preProject appveyor.yml = appveyor.yml .github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml .github\dependabot.yml = .github\dependabot.yml DisCatSharp.targets = DisCatSharp.targets docs-oneclick-rebuild.ps1 = docs-oneclick-rebuild.ps1 .github\workflows\docs.yml = .github\workflows\docs.yml .github\workflows\dotnet.yml = .github\workflows\dotnet.yml Library.targets = Library.targets NuGet.targets = NuGet.targets oneclick-rebuild.ps1 = oneclick-rebuild.ps1 Package.targets = Package.targets rebuild-all.ps1 = rebuild-all.ps1 rebuild-docs.ps1 = rebuild-docs.ps1 rebuild-lib.ps1 = rebuild-lib.ps1 Version.targets = Version.targets EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{430C28D8-5F85-4D6E-AA68-211549435245}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .gitattributes = .gitattributes .gitignore = .gitignore .github\ISSUE_TEMPLATE\bug_report.md = .github\ISSUE_TEMPLATE\bug_report.md .github\CODEOWNERS = .github\CODEOWNERS .github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml .devcontainer\devcontainer.json = .devcontainer\devcontainer.json .github\workflows\docs-preview.yml = .github\workflows\docs-preview.yml .github\workflows\docs.yml = .github\workflows\docs.yml .github\workflows\dotnet.yml = .github\workflows\dotnet.yml .github\ISSUE_TEMPLATE\feature_request.md = .github\ISSUE_TEMPLATE\feature_request.md .github\FUNDING.yml = .github\FUNDING.yml .github\pull_request_template.md = .github\pull_request_template.md renovate.json = renovate.json _appveyor_old.yml = _appveyor_old.yml _typos.toml = _typos.toml EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Lavalink", "DisCatSharp.Lavalink\DisCatSharp.Lavalink.csproj", "{A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.VoiceNext.Natives", "DisCatSharp.VoiceNext.Natives\DisCatSharp.VoiceNext.Natives.csproj", "{BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Common", "DisCatSharp.Common\DisCatSharp.Common.csproj", "{CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.ApplicationCommands", "DisCatSharp.ApplicationCommands\DisCatSharp.ApplicationCommands.csproj", "{AD530FD0-523C-4DE7-9AF6-B9A3785492C2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Configuration", "DisCatSharp.Configuration\DisCatSharp.Configuration.csproj", "{603287D3-1EF2-47F1-A611-C7F25869DE14}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Hosting", "DisCatSharp.Hosting\DisCatSharp.Hosting.csproj", "{72CCE5D5-926B-432A-876A-065FA2BC9B7B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Hosting.DependencyInjection", "DisCatSharp.Hosting.DependencyInjection\DisCatSharp.Hosting.DependencyInjection.csproj", "{2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DisCatSharp.Tests", "DisCatSharp.Tests", "{8696E582-BCAA-402E-A938-17020CF3E35D}" ProjectSection(SolutionItems) = preProject DisCatSharp.Tests\global.json = DisCatSharp.Tests\global.json EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.EventHandlers.Tests", "DisCatSharp.Tests\DisCatSharp.EventHandlers.Tests\DisCatSharp.EventHandlers.Tests.csproj", "{B49162CD-3966-4A98-A46E-9D189C4A84CD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Configuration.Tests", "DisCatSharp.Tests\DisCatSharp.Configuration.Tests\DisCatSharp.Configuration.Tests.csproj", "{DC9A804F-A835-427D-BCF3-FDE7D0E34E49}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Hosting.Tests", "DisCatSharp.Tests\DisCatSharp.Hosting.Tests\DisCatSharp.Hosting.Tests.csproj", "{1D416BA1-6FFF-4BA2-A0B1-9D31215A84E0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {EB3D8310-DFAD-4295-97F9-82E253647583}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EB3D8310-DFAD-4295-97F9-82E253647583}.Debug|Any CPU.Build.0 = Debug|Any CPU {EB3D8310-DFAD-4295-97F9-82E253647583}.Release|Any CPU.ActiveCfg = Release|Any CPU {EB3D8310-DFAD-4295-97F9-82E253647583}.Release|Any CPU.Build.0 = Release|Any CPU - {FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Release|Any CPU.Build.0 = Release|Any CPU {C8ED55FB-E028-468D-955F-1534C20274EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C8ED55FB-E028-468D-955F-1534C20274EF}.Debug|Any CPU.Build.0 = Debug|Any CPU {C8ED55FB-E028-468D-955F-1534C20274EF}.Release|Any CPU.ActiveCfg = Release|Any CPU {C8ED55FB-E028-468D-955F-1534C20274EF}.Release|Any CPU.Build.0 = Release|Any CPU {DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Release|Any CPU.Build.0 = Release|Any CPU {A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Debug|Any CPU.Build.0 = Debug|Any CPU {A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Release|Any CPU.ActiveCfg = Release|Any CPU {A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Release|Any CPU.Build.0 = Release|Any CPU - {BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Release|Any CPU.Build.0 = Release|Any CPU {CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Debug|Any CPU.Build.0 = Debug|Any CPU {CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Release|Any CPU.ActiveCfg = Release|Any CPU {CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Release|Any CPU.Build.0 = Release|Any CPU {AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Release|Any CPU.Build.0 = Release|Any CPU {603287D3-1EF2-47F1-A611-C7F25869DE14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {603287D3-1EF2-47F1-A611-C7F25869DE14}.Debug|Any CPU.Build.0 = Debug|Any CPU {603287D3-1EF2-47F1-A611-C7F25869DE14}.Release|Any CPU.ActiveCfg = Release|Any CPU {603287D3-1EF2-47F1-A611-C7F25869DE14}.Release|Any CPU.Build.0 = Release|Any CPU {72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Debug|Any CPU.Build.0 = Debug|Any CPU {72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Release|Any CPU.ActiveCfg = Release|Any CPU {72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Release|Any CPU.Build.0 = Release|Any CPU {2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Release|Any CPU.Build.0 = Release|Any CPU {B49162CD-3966-4A98-A46E-9D189C4A84CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B49162CD-3966-4A98-A46E-9D189C4A84CD}.Debug|Any CPU.Build.0 = Debug|Any CPU {B49162CD-3966-4A98-A46E-9D189C4A84CD}.Release|Any CPU.ActiveCfg = Release|Any CPU {B49162CD-3966-4A98-A46E-9D189C4A84CD}.Release|Any CPU.Build.0 = Release|Any CPU {DC9A804F-A835-427D-BCF3-FDE7D0E34E49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DC9A804F-A835-427D-BCF3-FDE7D0E34E49}.Debug|Any CPU.Build.0 = Debug|Any CPU {DC9A804F-A835-427D-BCF3-FDE7D0E34E49}.Release|Any CPU.ActiveCfg = Release|Any CPU {DC9A804F-A835-427D-BCF3-FDE7D0E34E49}.Release|Any CPU.Build.0 = Release|Any CPU {1D416BA1-6FFF-4BA2-A0B1-9D31215A84E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1D416BA1-6FFF-4BA2-A0B1-9D31215A84E0}.Debug|Any CPU.Build.0 = Debug|Any CPU {1D416BA1-6FFF-4BA2-A0B1-9D31215A84E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {1D416BA1-6FFF-4BA2-A0B1-9D31215A84E0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {B49162CD-3966-4A98-A46E-9D189C4A84CD} = {8696E582-BCAA-402E-A938-17020CF3E35D} {DC9A804F-A835-427D-BCF3-FDE7D0E34E49} = {8696E582-BCAA-402E-A938-17020CF3E35D} {1D416BA1-6FFF-4BA2-A0B1-9D31215A84E0} = {8696E582-BCAA-402E-A938-17020CF3E35D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {23F3A981-51B8-4285-A38C-3267F1D25FE7} EndGlobalSection EndGlobal diff --git a/LICENSE.md b/LICENSE.md index 980420ad1..73b375fc5 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,346 +1,271 @@ The MIT License (MIT) Copyright (c) 2021-2022 Aiko IT Systems Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # Package licenses for DisCatSharp Parts of this package are not provided under DisCatSharp's license. Said licenses are listed here. ## Package base [Original license reading](https://github.com/DSharpPlus/DSharpPlus/blob/master/LICENSE) ``` The MIT License (MIT) Copyright (c) 2015 Mike Santiago Copyright (c) 2016-2021 DSharpPlus Development Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` -## Opus -[Original license reading](https://github.com/xiph/opus/blob/master/COPYING) - -``` -Copyright 2001-2011 Xiph.Org, Skype Limited, Octasic, - Jean-Marc Valin, Timothy B. Terriberry, - CSIRO, Gregory Maxwell, Mark Borgerding, - Erik de Castro Lopo - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -- Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -- Neither the name of Internet Society, IETF or IETF Trust, nor the -names of specific contributors, may be used to endorse or promote -products derived from this software without specific prior written -permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Opus is subject to the royalty-free patent licenses which are -specified at: - -Xiph.Org Foundation: -https://datatracker.ietf.org/ipr/1524/ - -Microsoft Corporation: -https://datatracker.ietf.org/ipr/1914/ - -Broadcom Corporation: -https://datatracker.ietf.org/ipr/1526/ -``` - -## Libsodium -[Original license reading](https://github.com/jedisct1/libsodium/blob/master/LICENSE) - -``` -/* - * ISC License - * - * Copyright (c) 2013-2020 - * Frank Denis - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -``` - - ## DisCatSharp.Common ``` Copyright 2021 Aiko IT Systems Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ``` ## DisCatSharp.Common Source [Original license reading](https://github.com/Emzi0767/Common/blob/master/LICENSE.TXT) ``` Copyright 2020-2021 Emzi0767 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS ```