DiscordCoreAPI
A Discord bot library written in C++, with custom asynchronous coroutines.
Loading...
Searching...
No Matches
VoiceConnection.hpp
Go to the documentation of this file.
1/*
2 DiscordCoreAPI, A bot library for Discord, written in C++, and featuring explicit multithreading through the usage of custom, asynchronous C++ CoRoutines.
3
4 Copyright 2021, 2022 Chris M. (RealTimeChris)
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 USA
20*/
21/// VoiceConnection.hpp - Header for the voice connection class.
22/// Jul 15, 2021
23/// https://discordcoreapi.com
24/// \file VoiceConnection.hpp
25
26#pragma once
27
33#include <sodium.h>
34
35namespace DiscordCoreAPI {
36
37 struct DiscordCoreAPI_Dll VoiceSocketReadyData {
38 VoiceSocketReadyData(simdjson::ondemand::value);
39 std::string mode{};
40 std::string ip{};
41 uint64_t port{};
42 uint32_t ssrc{};
43 };
44
45 struct DiscordCoreAPI_Dll VoiceUser {
46
47 VoiceUser() noexcept = default;
48
49 VoiceUser(Snowflake userId) noexcept;
50
51 VoiceUser& operator=(VoiceUser&&) noexcept;
52
53 VoiceUser(VoiceUser&&) noexcept;
54
55 VoiceUser& operator=(const VoiceUser&) noexcept = delete;
56
57 VoiceUser(const VoiceUser&) noexcept = delete;
58
59 DiscordCoreInternal::OpusDecoderWrapper& getDecoder() noexcept;
60
61 void insertPayload(std::basic_string_view<uint8_t>) noexcept;
62
63 std::basic_string_view<uint8_t> extractPayload() noexcept;
64
65 Snowflake getUserId() noexcept;
66
67 protected:
68 DiscordCoreInternal::RingBuffer<uint8_t, 4> payloads{};
69 DiscordCoreInternal::OpusDecoderWrapper decoder{};
70 Snowflake userId{};
71 };
72
73 struct DiscordCoreAPI_Dll RTPPacketEncrypter {
74 RTPPacketEncrypter() noexcept = default;
75
76 RTPPacketEncrypter(uint32_t ssrcNew, const std::basic_string<uint8_t>& keysNew) noexcept;
77
78 std::basic_string_view<uint8_t> encryptPacket(const AudioFrameData& audioData) noexcept;
79
80 protected:
81 std::basic_string<uint8_t> data{};
82 std::basic_string<uint8_t> keys{};
83 uint8_t version{ 0x80 };
84 uint8_t flags{ 0x78 };
85 uint32_t timeStamp{};
86 uint16_t sequence{};
87 uint32_t ssrc{};
88 };
89
90 /// \brief The various opcodes that could be sent/received by the voice-websocket.
91 enum class VoiceSocketOpCodes {
92 Identify = 0,///< Begin a voice websocket connection.
93 Select_Protocol = 1,///< Select the voice protocol.
94 Ready_Server = 2,///< Complete the websocket handshake.
95 Heartbeat = 3,///< Keep the websocket connection alive.
96 Session_Description = 4,///< Describe the session.
97 Speaking = 5,///< Indicate which users are speaking.
98 Heartbeat_ACK = 6,///< Sent to acknowledge a received client heartbeat.
99 Resume = 7,///< Resume a connection.
100 Hello = 8,///< Time to wait between sending heartbeats in milliseconds.
101 Resumed = 9,///< Acknowledge a successful session resume.
102 Client_Disconnect = 13,///< A client has disconnected from the voice channel.
103 };
104
105 /// \brief For the various connection states of the VoiceConnection class.
106 enum class VoiceConnectionState : uint8_t {
107 Collecting_Init_Data = 0,///< Collecting initialization data.
108 Initializing_WebSocket = 1,///< Initializing the WebSocket.
109 Collecting_Hello = 2,///< Collecting the client hello.
110 Sending_Identify = 3,///< Sending the identify payload.
111 Collecting_Ready = 4,///< Collecting the client ready.
112 Initializing_DatagramSocket = 5,///< Initializing the datagram udp socket.
113 Sending_Select_Protocol = 6,///< Sending the select-protocol payload.
114 Collecting_Session_Description = 7///< Collecting the session-description payload.
115 };
116
117 /// \brief For the various active states of the VoiceConnection class.
118 enum class VoiceActiveState : int8_t {
119 Connecting = -1,///< Connecting - it hasn't started or it's reconnecting.
120 Playing = 1,///< Playing.
121 Stopped = 2,///< Stopped.
122 Paused = 3,///< Paused.
123 Exiting = 4///< Exiting.
124 };
125
126 class DiscordCoreAPI_Dll VoiceConnectionBridge : public DiscordCoreInternal::UDPConnection {
127 public:
128 VoiceConnectionBridge(DiscordCoreClient* voiceConnectionPtrNew, StreamType streamType, Snowflake guildIdNew);
129
130 void parseOutgoingVoiceData() noexcept;
131
132 void handleAudioBuffer() noexcept;
133
134 protected:
135 DiscordCoreClient* clientPtr{ nullptr };
136 Snowflake guildId{};
137 };
138
139 /**
140 * \addtogroup voice_connection
141 * @{
142 */
143 /// \brief VoiceConnection class - represents the connection to a given voice Channel.
144 class DiscordCoreAPI_Dll VoiceConnection : public DiscordCoreInternal::WebSocketCore, public DiscordCoreInternal::UDPConnection {
145 public:
146 friend class DiscordCoreInternal::BaseSocketAgent;
147 friend class DiscordCoreInternal::SoundCloudAPI;
148 friend class DiscordCoreInternal::YouTubeAPI;
149 friend class VoiceConnectionBridge;
150 friend class DiscordCoreClient;
151 friend class GuildData;
152 friend class SongAPI;
153
154 /// The constructor.
155 VoiceConnection(DiscordCoreClient* clientPtrNew, DiscordCoreInternal::WebSocketClient* baseShardNew, std::atomic_bool* doWeQuitNew) noexcept;
156
157 /// \brief Collects the currently connected-to voice Channel's id.
158 /// \returns Snowflake A Snowflake containing the Channel's id.
159 Snowflake getChannelId() noexcept;
160
161 /// \brief Connects to a currently held voice channel.
162 /// \param initData A DiscordCoerAPI::VoiceConnectInitDat structure.
163 void connect(const VoiceConnectInitData& initData) noexcept;
164
165 ~VoiceConnection() noexcept;
166
167 protected:
168 std::atomic<VoiceConnectionState> connectionState{ VoiceConnectionState::Collecting_Init_Data };
170 Nanoseconds intervalCount{ static_cast<int64_t>(960.0l / 48000.0l * 1000000000.0l) };
171 std::atomic<VoiceActiveState> activeState{ VoiceActiveState::Connecting };
172 std::unordered_map<uint64_t, std::unique_ptr<VoiceUser>> voiceUsers{};
173 DiscordCoreInternal::VoiceConnectionData voiceConnectionData{};
174 std::unique_ptr<VoiceConnectionBridge> streamSocket{ nullptr };
175 DiscordCoreInternal::WebSocketClient* baseShard{ nullptr };
176 std::unique_ptr<std::jthread> taskThread01{ nullptr };
177 DiscordCoreInternal::OpusEncoderWrapper encoder{};
178 std::basic_string<uint8_t> decryptedDataString{};
179 DiscordCoreClient* discordCoreClient{ nullptr };
180 VoiceConnectInitData voiceConnectInitData{};
181 std::vector<opus_int16> downSampledVector{};
182 std::basic_string<uint8_t> encryptionKey{};
183 std::vector<opus_int32> upSampledVector{};
184 std::basic_string<uint8_t> externalIp{};
185 std::atomic_bool areWePlaying{ false };
186 std::atomic_bool* doWeQuit{ nullptr };
187 int64_t sampleRatePerSecond{ 48000 };
188 RTPPacketEncrypter packetEncrypter{};
189 simdjson::ondemand::parser parser{};
190 int64_t nsPerSecond{ 1000000000 };
191 std::string audioEncryptionMode{};
192 Snowflake currentGuildMemberId{};
193 AudioFrameData audioData{};
194 int8_t voiceUserCount{};
195 std::string voiceIp{};
196 std::string baseUrl{};
197 uint32_t audioSSRC{};
198 float previousGain{};
199 float currentGain{};
200 uint16_t port{};
201
202 void parseIncomingVoiceData(std::basic_string_view<uint8_t> rawDataBufferNew) noexcept;
203
204 void sendVoiceData(std::basic_string_view<uint8_t> responseData) noexcept;
205
206 UnboundedMessageBlock<AudioFrameData>& getAudioBuffer() noexcept;
207
208 void checkForAndSendHeartBeat(const bool isItImmediage) noexcept;
209
210 void sendSingleFrame(AudioFrameData& frameData) noexcept;
211
212 void sendSpeakingMessage(const bool isSpeaking) noexcept;
213
214 void checkForConnections(std::stop_token token) noexcept;
215
216 bool onMessageReceived(std::string_view data) noexcept;
217
218 void connectInternal(std::stop_token token) noexcept;
219
220 void runVoice(std::stop_token) noexcept;
221
222 bool areWeCurrentlyPlaying() noexcept;
223
224 void handleAudioBuffer() noexcept;
225
226 void clearAudioData() noexcept;
227
228 bool areWeConnected() noexcept;
229
230 bool voiceConnect() noexcept;
231
232 void sendSilence() noexcept;
233
234 void pauseToggle() noexcept;
235
236 void disconnect() noexcept;
237
238 void reconnect() noexcept;
239
240 void onClosed() noexcept;
241
242 void mixAudio() noexcept;
243
244 bool stop() noexcept;
245
246 bool play() noexcept;
247 };
248 /**@}*/
249
250};// namespace DiscordCoreAPI
The main namespace for this library.
VoiceConnectionState
For the various connection states of the VoiceConnection class.
@ Collecting_Init_Data
Collecting initialization data.
@ Collecting_Ready
Collecting the client ready.
@ Sending_Identify
Sending the identify payload.
@ Collecting_Hello
Collecting the client hello.
@ Initializing_WebSocket
Initializing the WebSocket.
@ Initializing_DatagramSocket
Initializing the datagram udp socket.
@ Collecting_Session_Description
Collecting the session-description payload.
@ Sending_Select_Protocol
Sending the select-protocol payload.
VoiceSocketOpCodes
The various opcodes that could be sent/received by the voice-websocket.
@ Speaking
Indicate which users are speaking.
@ Ready_Server
Complete the websocket handshake.
@ Heartbeat
Keep the websocket connection alive.
@ Hello
Time to wait between sending heartbeats in milliseconds.
@ Identify
Begin a voice websocket connection.
@ Session_Description
Describe the session.
@ Heartbeat_ACK
Sent to acknowledge a received client heartbeat.
@ Resumed
Acknowledge a successful session resume.
@ Client_Disconnect
A client has disconnected from the voice channel.
@ Select_Protocol
Select the voice protocol.
@ Resume
Resume a connection.
VoiceActiveState
For the various active states of the VoiceConnection class.
@ Connecting
Connecting - it hasn't started or it's reconnecting.
DiscordCoreClient - The main class for this library.
Data structure representing a single Guild.
A class representing the Song APIs.
Definition: SongAPI.hpp:40
Represents a single frame of audio data.
Definition: Utilities.hpp:1284
For connecting to a voice-channel. "streamInfo" is used when a socket is created to connect this bot ...
Definition: Utilities.hpp:1312
A thread-safe messaging block for data-structures.
Definition: Utilities.hpp:1910
VoiceConnection class - represents the connection to a given voice Channel.