summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/abaddon.cpp19
-rw-r--r--src/abaddon.hpp5
-rw-r--r--src/audio/manager.cpp45
-rw-r--r--src/audio/manager.hpp4
-rw-r--r--src/discord/discord.cpp59
-rw-r--r--src/discord/discord.hpp21
-rw-r--r--src/discord/objects.cpp25
-rw-r--r--src/discord/objects.hpp34
-rw-r--r--src/discord/voiceclient.cpp33
-rw-r--r--src/discord/voiceclient.hpp38
10 files changed, 246 insertions, 37 deletions
diff --git a/src/abaddon.cpp b/src/abaddon.cpp
index bdedc7c..3245ad4 100644
--- a/src/abaddon.cpp
+++ b/src/abaddon.cpp
@@ -50,6 +50,16 @@ Abaddon::Abaddon()
m_discord.signal_thread_update().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnThreadUpdate));
m_discord.signal_message_sent().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnMessageSent));
m_discord.signal_disconnected().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnDisconnect));
+
+#ifdef WITH_VOICE
+ m_discord.signal_voice_connected().connect(sigc::mem_fun(*this, &Abaddon::OnVoiceConnected));
+ m_discord.signal_voice_disconnected().connect(sigc::mem_fun(*this, &Abaddon::OnVoiceDisconnected));
+ m_discord.signal_voice_speaking().connect([this](const VoiceSpeakingData &m) {
+ printf("%llu has ssrc %u\n", (uint64_t)m.UserID, m.SSRC);
+ m_audio->AddSSRC(m.SSRC);
+ });
+#endif
+
m_discord.signal_channel_accessibility_changed().connect([this](Snowflake id, bool accessible) {
if (!accessible)
m_channels_requested.erase(id);
@@ -406,6 +416,15 @@ void Abaddon::DiscordOnThreadUpdate(const ThreadUpdateData &data) {
}
}
+#ifdef WITH_VOICE
+void Abaddon::OnVoiceConnected() {
+}
+
+void Abaddon::OnVoiceDisconnected() {
+ m_audio->RemoveAllSSRCs();
+}
+#endif
+
SettingsManager::Settings &Abaddon::GetSettings() {
return m_settings.GetSettings();
}
diff --git a/src/abaddon.hpp b/src/abaddon.hpp
index ca92370..a15670b 100644
--- a/src/abaddon.hpp
+++ b/src/abaddon.hpp
@@ -89,6 +89,11 @@ public:
void DiscordOnDisconnect(bool is_reconnecting, GatewayCloseCode close_code);
void DiscordOnThreadUpdate(const ThreadUpdateData &data);
+#ifdef WITH_VOICE
+ void OnVoiceConnected();
+ void OnVoiceDisconnected();
+#endif
+
SettingsManager::Settings &GetSettings();
Glib::RefPtr<Gtk::CssProvider> GetStyleProvider();
diff --git a/src/audio/manager.cpp b/src/audio/manager.cpp
index b665c81..797d899 100644
--- a/src/audio/manager.cpp
+++ b/src/audio/manager.cpp
@@ -107,8 +107,28 @@ AudioManager::AudioManager() {
AudioManager::~AudioManager() {
ma_device_uninit(&m_device);
ma_device_uninit(&m_capture_device);
- for (auto &[ssrc, pair] : m_sources) {
- opus_decoder_destroy(pair.second);
+ RemoveAllSSRCs();
+}
+
+void AudioManager::AddSSRC(uint32_t ssrc) {
+ int error;
+ auto *decoder = opus_decoder_create(48000, 2, &error);
+ m_sources.insert(std::make_pair(ssrc, std::make_pair(std::deque<int16_t> {}, decoder)));
+}
+
+void AudioManager::RemoveSSRC(uint32_t ssrc) {
+ if (auto it = m_sources.find(ssrc); it != m_sources.end()) {
+ opus_decoder_destroy(it->second.second);
+ m_sources.erase(it);
+ }
+}
+
+void AudioManager::RemoveAllSSRCs() {
+ puts("remove all ssrc");
+ for (auto it = m_sources.begin(); it != m_sources.end();) {
+ opus_decoder_destroy(it->second.second);
+ m_sources.erase(it);
+ it++;
}
}
@@ -120,18 +140,15 @@ void AudioManager::FeedMeOpus(uint32_t ssrc, const std::vector<uint8_t> &data) {
size_t payload_size = 0;
const auto *opus_encoded = StripRTPExtensionHeader(data.data(), static_cast<int>(data.size()), payload_size);
static std::array<opus_int16, 120 * 48 * 2> pcm;
- if (m_sources.find(ssrc) == m_sources.end()) {
- int err;
- auto *decoder = opus_decoder_create(48000, 2, &err);
- m_sources.insert(std::make_pair(ssrc, std::make_pair(std::deque<int16_t> {}, decoder)));
- }
- int decoded = opus_decode(m_sources.at(ssrc).second, opus_encoded, static_cast<opus_int32>(payload_size), pcm.data(), 120 * 48, 0);
- if (decoded <= 0) {
- } else {
- m_mutex.lock();
- auto &buf = m_sources.at(ssrc).first;
- buf.insert(buf.end(), pcm.begin(), pcm.begin() + decoded * 2);
- m_mutex.unlock();
+ if (auto it = m_sources.find(ssrc); it != m_sources.end()) {
+ int decoded = opus_decode(it->second.second, opus_encoded, static_cast<opus_int32>(payload_size), pcm.data(), 120 * 48, 0);
+ if (decoded <= 0) {
+ } else {
+ m_mutex.lock();
+ auto &buf = it->second.first;
+ buf.insert(buf.end(), pcm.begin(), pcm.begin() + decoded * 2);
+ m_mutex.unlock();
+ }
}
}
diff --git a/src/audio/manager.hpp b/src/audio/manager.hpp
index 700fcc0..3ba6e29 100644
--- a/src/audio/manager.hpp
+++ b/src/audio/manager.hpp
@@ -18,6 +18,10 @@ public:
AudioManager();
~AudioManager();
+ void AddSSRC(uint32_t ssrc);
+ void RemoveSSRC(uint32_t ssrc);
+ void RemoveAllSSRCs();
+
void SetOpusBuffer(uint8_t *ptr);
void FeedMeOpus(uint32_t ssrc, const std::vector<uint8_t> &data);
diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp
index 2fff2a1..4202f85 100644
--- a/src/discord/discord.cpp
+++ b/src/discord/discord.cpp
@@ -23,6 +23,14 @@ DiscordClient::DiscordClient(bool mem_store)
m_websocket.signal_open().connect(sigc::mem_fun(*this, &DiscordClient::HandleSocketOpen));
m_websocket.signal_close().connect(sigc::mem_fun(*this, &DiscordClient::HandleSocketClose));
+#ifdef WITH_VOICE
+ m_voice.signal_connected().connect(sigc::mem_fun(*this, &DiscordClient::OnVoiceConnected));
+ m_voice.signal_disconnected().connect(sigc::mem_fun(*this, &DiscordClient::OnVoiceDisconnected));
+ m_voice.signal_speaking().connect([this](const VoiceSpeakingData &data) {
+ m_signal_voice_speaking.emit(data);
+ });
+#endif
+
LoadEventMap();
}
@@ -2135,11 +2143,16 @@ void DiscordClient::HandleGatewayGuildMembersChunk(const GatewayMessage &msg) {
#ifdef WITH_VOICE
void DiscordClient::HandleGatewayVoiceStateUpdate(const GatewayMessage &msg) {
- VoiceStateUpdateData data = msg.Data;
+ VoiceState data = msg.Data;
if (data.UserID == m_user_data.ID) {
printf("voice session id: %s\n", data.SessionID.c_str());
m_voice.SetSessionID(data.SessionID);
}
+ if (data.ChannelID.has_value()) {
+ SetVoiceState(data.UserID, *data.ChannelID);
+ } else {
+ ClearVoiceState(data.UserID);
+ }
}
void DiscordClient::HandleGatewayVoiceServerUpdate(const GatewayMessage &msg) {
@@ -2170,6 +2183,15 @@ void DiscordClient::HandleGatewayReadySupplemental(const GatewayMessage &msg) {
m_user_to_status[p.UserID] = PresenceStatus::DND;
m_signal_presence_update.emit(*user, m_user_to_status.at(p.UserID));
}
+#ifdef WITH_VOICE
+ for (const auto &g : data.Guilds) {
+ for (const auto &s : g.VoiceStates) {
+ if (s.ChannelID.has_value()) {
+ SetVoiceState(s.UserID, *s.ChannelID);
+ }
+ }
+ }
+#endif
}
void DiscordClient::HandleGatewayReconnect(const GatewayMessage &msg) {
@@ -2602,6 +2624,29 @@ void DiscordClient::HandleReadyGuildSettings(const ReadyEventData &data) {
}
}
+#ifdef WITH_VOICE
+void DiscordClient::SetVoiceState(Snowflake user_id, Snowflake channel_id) {
+ m_voice_state_user_channel[user_id] = channel_id;
+ m_voice_state_channel_users[channel_id].insert(user_id);
+}
+
+void DiscordClient::ClearVoiceState(Snowflake user_id) {
+ if (const auto it = m_voice_state_user_channel.find(user_id); it != m_voice_state_user_channel.end()) {
+ m_voice_state_channel_users[it->second].erase(user_id);
+ // invalidated
+ m_voice_state_user_channel.erase(user_id);
+ }
+}
+
+void DiscordClient::OnVoiceConnected() {
+ m_signal_voice_connected.emit();
+}
+
+void DiscordClient::OnVoiceDisconnected() {
+ m_signal_voice_disconnected.emit();
+}
+#endif
+
void DiscordClient::LoadEventMap() {
m_event_map["READY"] = GatewayEvent::READY;
m_event_map["MESSAGE_CREATE"] = GatewayEvent::MESSAGE_CREATE;
@@ -2858,3 +2903,15 @@ DiscordClient::type_signal_channel_accessibility_changed DiscordClient::signal_c
DiscordClient::type_signal_message_send_fail DiscordClient::signal_message_send_fail() {
return m_signal_message_send_fail;
}
+
+DiscordClient::type_signal_voice_connected DiscordClient::signal_voice_connected() {
+ return m_signal_voice_connected;
+}
+
+DiscordClient::type_signal_voice_disconnected DiscordClient::signal_voice_disconnected() {
+ return m_signal_voice_disconnected;
+}
+
+DiscordClient::type_signal_voice_speaking DiscordClient::signal_voice_speaking() {
+ return m_signal_voice_speaking;
+}
diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp
index 0b88519..fb0b46d 100644
--- a/src/discord/discord.hpp
+++ b/src/discord/discord.hpp
@@ -339,6 +339,15 @@ private:
DiscordVoiceClient m_voice;
Snowflake m_voice_channel_id;
+ // todo sql i guess
+ std::unordered_map<Snowflake, Snowflake> m_voice_state_user_channel;
+ std::unordered_map<Snowflake, std::unordered_set<Snowflake>> m_voice_state_channel_users;
+
+ void SetVoiceState(Snowflake user_id, Snowflake channel_id);
+ void ClearVoiceState(Snowflake user_id);
+
+ void OnVoiceConnected();
+ void OnVoiceDisconnected();
#endif
mutable std::mutex m_msg_mutex;
@@ -413,6 +422,10 @@ public:
typedef sigc::signal<void> type_signal_connected;
typedef sigc::signal<void, std::string, float> type_signal_message_progress;
+ using type_signal_voice_connected = sigc::signal<void()>;
+ using type_signal_voice_disconnected = sigc::signal<void()>;
+ using type_signal_voice_speaking = sigc::signal<void(VoiceSpeakingData)>;
+
type_signal_gateway_ready signal_gateway_ready();
type_signal_message_create signal_message_create();
type_signal_message_delete signal_message_delete();
@@ -467,6 +480,10 @@ public:
type_signal_connected signal_connected();
type_signal_message_progress signal_message_progress();
+ type_signal_voice_connected signal_voice_connected();
+ type_signal_voice_disconnected signal_voice_disconnected();
+ type_signal_voice_speaking signal_voice_speaking();
+
protected:
type_signal_gateway_ready m_signal_gateway_ready;
type_signal_message_create m_signal_message_create;
@@ -521,4 +538,8 @@ protected:
type_signal_disconnected m_signal_disconnected;
type_signal_connected m_signal_connected;
type_signal_message_progress m_signal_message_progress;
+
+ type_signal_voice_connected m_signal_voice_connected;
+ type_signal_voice_disconnected m_signal_voice_disconnected;
+ type_signal_voice_speaking m_signal_voice_speaking;
};
diff --git a/src/discord/objects.cpp b/src/discord/objects.cpp
index 3cdc6b5..21853a7 100644
--- a/src/discord/objects.cpp
+++ b/src/discord/objects.cpp
@@ -233,6 +233,11 @@ void from_json(const nlohmann::json &j, SupplementalMergedPresencesData &m) {
JS_D("friends", m.Friends);
}
+void from_json(const nlohmann::json &j, SupplementalGuildEntry &m) {
+ JS_D("id", m.ID);
+ JS_D("voice_states", m.VoiceStates);
+}
+
void from_json(const nlohmann::json &j, ReadySupplementalData &m) {
JS_D("merged_presences", m.MergedPresences);
}
@@ -658,14 +663,24 @@ void to_json(nlohmann::json &j, const VoiceStateUpdateMessage &m) {
// j["d"]["preferred_region"] = m.PreferredRegion;
}
-void from_json(const nlohmann::json &j, VoiceStateUpdateData &m) {
- JS_ON("user_id", m.UserID);
- JS_ON("session_id", m.SessionID);
-}
-
void from_json(const nlohmann::json &j, VoiceServerUpdateData &m) {
JS_D("token", m.Token);
JS_D("guild_id", m.GuildID);
JS_D("endpoint", m.Endpoint);
}
#endif
+
+void from_json(const nlohmann::json &j, VoiceState &m) {
+ JS_O("guild_id", m.GuildID);
+ JS_N("channel_id", m.ChannelID);
+ JS_D("deaf", m.IsDeafened);
+ JS_D("mute", m.IsMuted);
+ JS_D("self_deaf", m.IsSelfDeafened);
+ JS_D("self_mute", m.IsSelfMuted);
+ JS_D("self_video", m.IsSelfVideo);
+ JS_O("self_stream", m.IsSelfStream);
+ JS_D("suppress", m.IsSuppressed);
+ JS_D("user_id", m.UserID);
+ JS_N("member", m.Member);
+ JS_D("session_id", m.SessionID);
+}
diff --git a/src/discord/objects.hpp b/src/discord/objects.hpp
index 0a947d4..14dd20c 100644
--- a/src/discord/objects.hpp
+++ b/src/discord/objects.hpp
@@ -354,8 +354,18 @@ struct SupplementalMergedPresencesData {
friend void from_json(const nlohmann::json &j, SupplementalMergedPresencesData &m);
};
+struct VoiceState;
+struct SupplementalGuildEntry {
+ // std::vector<?> EmbeddedActivities;
+ Snowflake ID;
+ std::vector<VoiceState> VoiceStates;
+
+ friend void from_json(const nlohmann::json &j, SupplementalGuildEntry &m);
+};
+
struct ReadySupplementalData {
SupplementalMergedPresencesData MergedPresences;
+ std::vector<SupplementalGuildEntry> Guilds;
friend void from_json(const nlohmann::json &j, ReadySupplementalData &m);
};
@@ -879,13 +889,6 @@ struct VoiceStateUpdateMessage {
friend void to_json(nlohmann::json &j, const VoiceStateUpdateMessage &m);
};
-struct VoiceStateUpdateData {
- Snowflake UserID;
- std::string SessionID;
-
- friend void from_json(const nlohmann::json &j, VoiceStateUpdateData &m);
-};
-
struct VoiceServerUpdateData {
std::string Token;
Snowflake GuildID;
@@ -894,3 +897,20 @@ struct VoiceServerUpdateData {
friend void from_json(const nlohmann::json &j, VoiceServerUpdateData &m);
};
#endif
+
+struct VoiceState {
+ std::optional<Snowflake> ChannelID;
+ bool IsDeafened;
+ bool IsMuted;
+ std::optional<Snowflake> GuildID;
+ std::optional<GuildMember> Member;
+ bool IsSelfDeafened;
+ bool IsSelfMuted;
+ bool IsSelfVideo;
+ bool IsSelfStream = false;
+ std::string SessionID;
+ bool IsSuppressed;
+ Snowflake UserID;
+
+ friend void from_json(const nlohmann::json &j, VoiceState &m);
+};
diff --git a/src/discord/voiceclient.cpp b/src/discord/voiceclient.cpp
index 6d45241..b7fe21b 100644
--- a/src/discord/voiceclient.cpp
+++ b/src/discord/voiceclient.cpp
@@ -1,5 +1,5 @@
#ifdef WITH_VOICE
- // clang-format off
+// clang-format off
#include "voiceclient.hpp"
#include "json.hpp"
#include <sodium.h>
@@ -186,6 +186,7 @@ DiscordVoiceClient::~DiscordVoiceClient() {
void DiscordVoiceClient::Start() {
m_ws.StartConnection("wss://" + m_endpoint + "/?v=7");
+ m_heartbeat_waiter.revive();
}
void DiscordVoiceClient::Stop() {
@@ -195,6 +196,7 @@ void DiscordVoiceClient::Stop() {
m_heartbeat_waiter.kill();
if (m_heartbeat_thread.joinable()) m_heartbeat_thread.join();
m_connected = false;
+ m_signal_disconnected.emit();
}
}
@@ -235,6 +237,9 @@ void DiscordVoiceClient::OnGatewayMessage(const std::string &str) {
case VoiceGatewayOp::SessionDescription: {
HandleGatewaySessionDescription(msg);
} break;
+ case VoiceGatewayOp::Speaking: {
+ HandleGatewaySpeaking(msg);
+ } break;
default: break;
}
}
@@ -273,7 +278,7 @@ void DiscordVoiceClient::HandleGatewaySessionDescription(const VoiceGatewayMessa
VoiceSpeakingMessage msg;
msg.Delay = 0;
msg.SSRC = m_ssrc;
- msg.Speaking = VoiceSpeakingMessage::Microphone;
+ msg.Speaking = VoiceSpeakingType::Microphone;
m_ws.Send(msg);
m_secret_key = d.SecretKey;
@@ -286,6 +291,12 @@ void DiscordVoiceClient::HandleGatewaySessionDescription(const VoiceGatewayMessa
m_udp.SendEncrypted({ 0xF8, 0xFF, 0xFE });
m_udp.Run();
m_connected = true;
+ m_signal_connected.emit();
+}
+
+void DiscordVoiceClient::HandleGatewaySpeaking(const VoiceGatewayMessage &m) {
+ VoiceSpeakingData data = m.Data;
+ m_signal_speaking.emit(data);
}
void DiscordVoiceClient::Identify() {
@@ -365,6 +376,18 @@ void DiscordVoiceClient::HeartbeatThread() {
}
}
+DiscordVoiceClient::type_signal_disconnected DiscordVoiceClient::signal_connected() {
+ return m_signal_connected;
+}
+
+DiscordVoiceClient::type_signal_disconnected DiscordVoiceClient::signal_disconnected() {
+ return m_signal_disconnected;
+}
+
+DiscordVoiceClient::type_signal_speaking DiscordVoiceClient::signal_speaking() {
+ return m_signal_speaking;
+}
+
void from_json(const nlohmann::json &j, VoiceGatewayMessage &m) {
JS_D("op", m.Opcode);
m.Data = j.at("d");
@@ -431,4 +454,10 @@ void to_json(nlohmann::json &j, const VoiceSpeakingMessage &m) {
j["d"]["delay"] = m.Delay;
j["d"]["ssrc"] = m.SSRC;
}
+
+void from_json(const nlohmann::json &j, VoiceSpeakingData &m) {
+ JS_D("user_id", m.UserID);
+ JS_D("ssrc", m.SSRC);
+ JS_D("speaking", m.Speaking);
+}
#endif
diff --git a/src/discord/voiceclient.hpp b/src/discord/voiceclient.hpp
index f81763b..c052f28 100644
--- a/src/discord/voiceclient.hpp
+++ b/src/discord/voiceclient.hpp
@@ -1,6 +1,6 @@
#pragma once
#ifdef WITH_VOICE
-// clang-format off
+ // clang-format off
#include "snowflake.hpp"
#include "waiter.hpp"
#include "websocket.hpp"
@@ -8,6 +8,7 @@
#include <queue>
#include <string>
#include <glibmm/dispatcher.h>
+#include <sigc++/sigc++.h>
// clang-format on
enum class VoiceGatewayCloseCode : uint16_t {
@@ -110,20 +111,28 @@ struct VoiceSessionDescriptionData {
friend void from_json(const nlohmann::json &j, VoiceSessionDescriptionData &m);
};
-struct VoiceSpeakingMessage {
- enum {
- Microphone = 1 << 0,
- Soundshare = 1 << 1,
- Priority = 1 << 2,
- };
+enum class VoiceSpeakingType {
+ Microphone = 1 << 0,
+ Soundshare = 1 << 1,
+ Priority = 1 << 2,
+};
- int Speaking;
+struct VoiceSpeakingMessage {
+ VoiceSpeakingType Speaking;
int Delay;
uint32_t SSRC;
friend void to_json(nlohmann::json &j, const VoiceSpeakingMessage &m);
};
+struct VoiceSpeakingData {
+ Snowflake UserID;
+ uint32_t SSRC;
+ VoiceSpeakingType Speaking;
+
+ friend void from_json(const nlohmann::json &j, VoiceSpeakingData &m);
+};
+
class UDPSocket {
public:
UDPSocket();
@@ -188,6 +197,7 @@ private:
void HandleGatewayHello(const VoiceGatewayMessage &m);
void HandleGatewayReady(const VoiceGatewayMessage &m);
void HandleGatewaySessionDescription(const VoiceGatewayMessage &m);
+ void HandleGatewaySpeaking(const VoiceGatewayMessage &m);
void Identify();
void Discovery();
@@ -228,5 +238,17 @@ private:
std::array<uint8_t, 1275> m_opus_buffer;
std::atomic<bool> m_connected = false;
+
+ using type_signal_connected = sigc::signal<void()>;
+ using type_signal_disconnected = sigc::signal<void()>;
+ using type_signal_speaking = sigc::signal<void(VoiceSpeakingData)>;
+ type_signal_connected m_signal_connected;
+ type_signal_disconnected m_signal_disconnected;
+ type_signal_speaking m_signal_speaking;
+
+public:
+ type_signal_connected signal_connected();
+ type_signal_disconnected signal_disconnected();
+ type_signal_speaking signal_speaking();
};
#endif