diff --git a/DisCatSharp.Targets/InternalsVisibleTo.targets b/DisCatSharp.Targets/InternalsVisibleTo.targets index 98a726adc3..05e9d3ec9e 100644 --- a/DisCatSharp.Targets/InternalsVisibleTo.targets +++ b/DisCatSharp.Targets/InternalsVisibleTo.targets @@ -18,6 +18,7 @@ + diff --git a/DisCatSharp.VoiceNext/VoiceNextConnection.cs b/DisCatSharp.VoiceNext/VoiceNextConnection.cs index 7780d05bd2..d609cf710a 100644 --- a/DisCatSharp.VoiceNext/VoiceNextConnection.cs +++ b/DisCatSharp.VoiceNext/VoiceNextConnection.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Security.Cryptography; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; @@ -417,7 +418,7 @@ internal Task ConnectAsync() { Scheme = "wss", Host = this.WebSocketEndpoint.Hostname, - Query = "encoding=json&v=4" + Query = "encoding=json&v=10" }; return this._voiceWs.ConnectAsync(gwuri.Uri); @@ -638,6 +639,7 @@ private async Task VoiceSenderTask() /// A bool. private bool ProcessPacket(ReadOnlySpan data, ref Memory opus, ref Memory pcm, IList> pcmPackets, out AudioSender voiceSender, out AudioFormat outputFormat) { + this._discord.Logger.LogDebug("Processing packet.."); voiceSender = null; outputFormat = default; @@ -648,6 +650,7 @@ private bool ProcessPacket(ReadOnlySpan data, ref Memory opus, ref M if (!this._transmittingSsrCs.TryGetValue(ssrc, out var vtx)) { + this._discord.Logger.LogDebug("Don't know user yet, creating dummy for {ssrc}", ssrc); var decoder = this._opus.CreateDecoder(); vtx = new AudioSender(ssrc, decoder) @@ -656,29 +659,30 @@ private bool ProcessPacket(ReadOnlySpan data, ref Memory opus, ref M User = null }; } - - voiceSender = vtx; var sequence = vtx.GetTrueSequenceAfterWrapping(shortSequence); - ushort gap = 0; - if (vtx.LastTrueSequence is ulong lastTrueSequence) + + try { - if (sequence <= lastTrueSequence) // out-of-order packet; discard - return false; + voiceSender = vtx; + 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"); - } + 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); + 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 - { + var opusSize = Sodium.CalculateSourceSize(encryptedOpus); + opus = opus[..opusSize]; + var opusSpan = opus.Span; + this._sodium.Decrypt(encryptedOpus, opusSpan, nonce); // Strip extensions, if any @@ -720,6 +724,7 @@ private bool ProcessPacket(ReadOnlySpan data, ref Memory opus, ref M if (gap == 1) { + this._discord.Logger.LogDebug("Gap 1"); var lastSampleCount = this._opus.GetLastPacketSampleCount(vtx.Decoder); var fecpcm = new byte[this.AudioFormat.SampleCountToSampleSize(lastSampleCount)]; var fecpcmMem = fecpcm.AsSpan(); @@ -728,6 +733,7 @@ private bool ProcessPacket(ReadOnlySpan data, ref Memory opus, ref M } else if (gap > 1) { + this._discord.Logger.LogDebug("Gap {gap}", gap); var lastSampleCount = this._opus.GetLastPacketSampleCount(vtx.Decoder); for (var i = 0; i < gap; i++) { @@ -741,6 +747,12 @@ private bool ProcessPacket(ReadOnlySpan data, ref Memory opus, ref M var pcmSpan = pcm.Span; this._opus.Decode(vtx.Decoder, opusSpan, ref pcmSpan, false, out outputFormat); pcm = pcm[..pcmSpan.Length]; + this._discord.Logger.LogDebug("Done with processing packet..."); + } + catch (Exception ex) + { + this._discord.Logger.LogDebug("Exception in ProcessPacket: {ex}", ex.Message); + this._discord.Logger.LogDebug("Stack: {ex}", ex.StackTrace); } finally { @@ -762,6 +774,7 @@ private async Task ProcessVoicePacket(byte[] data) try { + this._discord.Logger.LogDebug("Received voice data preparing.."); var pcm = new byte[this.AudioFormat.CalculateMaximumFrameSize()]; var pcmMem = pcm.AsMemory(); var opus = new byte[pcm.Length]; @@ -770,6 +783,7 @@ private async Task ProcessVoicePacket(byte[] data) if (!this.ProcessPacket(data, ref opusMem, ref pcmMem, pcmFillers, out var vtx, out var audioFormat)) return; + this._discord.Logger.LogDebug("Received voice data processing.."); foreach (var pcmFiller in pcmFillers) await this._voiceReceived.InvokeAsync(this, new VoiceReceiveEventArgs(this._discord.ServiceProvider) { @@ -1140,19 +1154,19 @@ private async Task HandleDispatch(JObject jo) switch (opc) { case 2: // READY - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received READY (OP2)"); + this._discord.Logger.LogDebug(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); + this._heartbeatTask = Task.Run(this.HeartbeatAsync, this.TOKEN); await this.Stage1(vrp).ConfigureAwait(false); break; case 4: // SESSION_DESCRIPTION - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received SESSION_DESCRIPTION (OP4)"); + this._discord.Logger.LogDebug(VoiceNextEvents.VoiceDispatch, "Received SESSION_DESCRIPTION (OP4)"); var vsd = opp.ToObject(); this._key = vsd.SecretKey; this._sodium = new Sodium(this._key.AsMemory()); @@ -1162,7 +1176,7 @@ private async Task HandleDispatch(JObject jo) 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)"); + this._discord.Logger.LogDebug(VoiceNextEvents.VoiceDispatch, "Received SPEAKING (OP5): {oop}", opp.ToString(Formatting.Indented)); var spd = opp.ToObject(); var foundUserInCache = this._discord.TryGetCachedUserInternal(spd.UserId.Value, out var resolvedUser); var spk = new UserSpeakingEventArgs(this._discord.ServiceProvider) @@ -1201,17 +1215,17 @@ private async Task HandleDispatch(JObject jo) 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._discord.Logger.LogDebug(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._discord.Logger.LogDebug(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)"); + this._discord.Logger.LogDebug(VoiceNextEvents.VoiceDispatch, "Received CLIENT_CONNECTED (OP12)"); var ujpd = opp.ToObject(); var usrj = await this._discord.GetUserAsync(ujpd.UserId, true).ConfigureAwait(false); { @@ -1229,7 +1243,7 @@ private async Task HandleDispatch(JObject jo) break; case 13: // CLIENT_DISCONNECTED - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received CLIENT_DISCONNECTED (OP13)"); + this._discord.Logger.LogDebug(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)) @@ -1247,7 +1261,7 @@ private async Task HandleDispatch(JObject jo) break; default: - this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received unknown voice opcode (OP{0})", opc); + this._discord.Logger.LogDebug(VoiceNextEvents.VoiceDispatch, "Received unknown voice opcode (OP {0}): {data}", opc, opp.ToString(Formatting.Indented)); break; } } diff --git a/DisCatSharp.sln b/DisCatSharp.sln index f830cccaaf..17e4560ea4 100644 --- a/DisCatSharp.sln +++ b/DisCatSharp.sln @@ -93,6 +93,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.SafetyTests", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Lavalink", "DisCatSharp.Lavalink\DisCatSharp.Lavalink.csproj", "{1ADC1D06-3DB8-4741-B740-13161B40CBA3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscatSharp.Voice", "DiscatSharp.Voice\DiscatSharp.Voice.csproj", "{69DF6B77-4A27-4D48-BB05-D25DF93A2AD0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -163,6 +165,10 @@ Global {1ADC1D06-3DB8-4741-B740-13161B40CBA3}.Debug|Any CPU.Build.0 = Debug|Any CPU {1ADC1D06-3DB8-4741-B740-13161B40CBA3}.Release|Any CPU.ActiveCfg = Release|Any CPU {1ADC1D06-3DB8-4741-B740-13161B40CBA3}.Release|Any CPU.Build.0 = Release|Any CPU + {69DF6B77-4A27-4D48-BB05-D25DF93A2AD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {69DF6B77-4A27-4D48-BB05-D25DF93A2AD0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {69DF6B77-4A27-4D48-BB05-D25DF93A2AD0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {69DF6B77-4A27-4D48-BB05-D25DF93A2AD0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/DisCatSharp/Net/Rest/DiscordApiClient.cs b/DisCatSharp/Net/Rest/DiscordApiClient.cs index 047f92b2d0..ed10cfced5 100644 --- a/DisCatSharp/Net/Rest/DiscordApiClient.cs +++ b/DisCatSharp/Net/Rest/DiscordApiClient.cs @@ -6026,7 +6026,6 @@ internal async Task ModifyCurrentApplicationInfoAsync( ConverImageBase64 = coverImageb64, Flags = flags, InstallParams = installParams - }; var route = $"{Endpoints.APPLICATIONS}{Endpoints.ME}"; diff --git a/DiscatSharp.Voice/Class1.cs b/DiscatSharp.Voice/Class1.cs new file mode 100644 index 0000000000..1c0e09c90f --- /dev/null +++ b/DiscatSharp.Voice/Class1.cs @@ -0,0 +1,27 @@ +// This file is part of the DisCatSharp project, based off DSharpPlus. +// +// Copyright (c) 2021-2023 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 NON-INFRINGEMENT. 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.Voice; +public class Class1 +{ + +} diff --git a/DiscatSharp.Voice/DiscatSharp.Voice.csproj b/DiscatSharp.Voice/DiscatSharp.Voice.csproj new file mode 100644 index 0000000000..3e09efbd53 --- /dev/null +++ b/DiscatSharp.Voice/DiscatSharp.Voice.csproj @@ -0,0 +1,47 @@ + + + + + + + + + + + DisCatSharp.Voice + DisCatSharp.Voice + true + + + + DisCatSharp.Voice + + DisCatSharp Voice Extension + + Easy made audio player for discord bots. + + Documentation: https://docs.dcs.aitsys.dev/articles/modules/audio/voice/intro.html + + DisCatSharp,Discord API Wrapper,Discord,Bots,Discord Bots,AITSYS,Net6,Net7,Voice,Audio Player + annotations + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + +