diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/abaddon.cpp | 8 | ||||
-rw-r--r-- | src/audio/devices.cpp | 33 | ||||
-rw-r--r-- | src/audio/devices.hpp | 26 | ||||
-rw-r--r-- | src/audio/manager.cpp | 87 | ||||
-rw-r--r-- | src/audio/manager.hpp | 13 | ||||
-rw-r--r-- | src/discord/voiceclient.cpp | 22 | ||||
-rw-r--r-- | src/windows/voicewindow.cpp | 12 | ||||
-rw-r--r-- | src/windows/voicewindow.hpp | 3 |
8 files changed, 183 insertions, 21 deletions
diff --git a/src/abaddon.cpp b/src/abaddon.cpp index b39b854..a0e73f2 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -1,5 +1,8 @@ #include <gtkmm.h> #include <memory> +#include <spdlog/spdlog.h> +#include <spdlog/cfg/env.h> +#include <spdlog/sinks/stdout_color_sinks.h> #include <string> #include <algorithm> #include "platform.hpp" @@ -1098,6 +1101,11 @@ int main(int argc, char **argv) { if (buf[0] != '1') SetEnvironmentVariableA("GTK_CSD", "0"); #endif + + spdlog::cfg::load_env_levels(); + auto log_audio = spdlog::stdout_color_mt("audio"); + auto log_voice = spdlog::stdout_color_mt("voice"); + Gtk::Main::init_gtkmm_internals(); // why??? return Abaddon::Get().StartGTK(); } diff --git a/src/audio/devices.cpp b/src/audio/devices.cpp new file mode 100644 index 0000000..f315a17 --- /dev/null +++ b/src/audio/devices.cpp @@ -0,0 +1,33 @@ +#include "devices.hpp" + +AudioDevices::AudioDevices() + : m_playback(Gtk::ListStore::create(m_playback_columns)) { +} + +Glib::RefPtr<Gtk::ListStore> AudioDevices::GetPlaybackDeviceModel() { + return m_playback; +} + +void AudioDevices::SetDevices(ma_device_info *pPlayback, ma_uint32 playback_count, ma_device_info *pCapture, ma_uint32 capture_count) { + m_playback->clear(); + + for (ma_uint32 i = 0; i < playback_count; i++) { + auto &d = pPlayback[i]; + auto row = *m_playback->append(); + row[m_playback_columns.Name] = d.name; + row[m_playback_columns.DeviceID] = d.id; + } +} + +std::optional<ma_device_id> AudioDevices::GetDeviceIDFromModel(const Gtk::TreeModel::iterator &iter) { + if (iter) { + return static_cast<ma_device_id>((*iter)[m_playback_columns.DeviceID]); + } else { + return std::nullopt; + } +} + +AudioDevices::PlaybackColumns::PlaybackColumns() { + add(Name); + add(DeviceID); +} diff --git a/src/audio/devices.hpp b/src/audio/devices.hpp new file mode 100644 index 0000000..0a88873 --- /dev/null +++ b/src/audio/devices.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include <gtkmm/liststore.h> +#include <miniaudio.h> +#include <optional> + +class AudioDevices { +public: + AudioDevices(); + + Glib::RefPtr<Gtk::ListStore> GetPlaybackDeviceModel(); + + void SetDevices(ma_device_info *pPlayback, ma_uint32 playback_count, ma_device_info *pCapture, ma_uint32 capture_count); + std::optional<ma_device_id> GetDeviceIDFromModel(const Gtk::TreeModel::iterator &iter); + +private: + class PlaybackColumns : public Gtk::TreeModel::ColumnRecord { + public: + PlaybackColumns(); + + Gtk::TreeModelColumn<Glib::ustring> Name; + Gtk::TreeModelColumn<ma_device_id> DeviceID; + }; + PlaybackColumns m_playback_columns; + Glib::RefPtr<Gtk::ListStore> m_playback; +}; diff --git a/src/audio/manager.cpp b/src/audio/manager.cpp index 21c522e..c965658 100644 --- a/src/audio/manager.cpp +++ b/src/audio/manager.cpp @@ -8,6 +8,7 @@ #include "manager.hpp" #include <array> #include <glibmm/main.h> +#include <spdlog/spdlog.h> #define MINIAUDIO_IMPLEMENTATION #include <miniaudio.h> #include <opus.h> @@ -58,12 +59,20 @@ AudioManager::AudioManager() { int err; m_encoder = opus_encoder_create(48000, 2, OPUS_APPLICATION_VOIP, &err); if (err != OPUS_OK) { - printf("failed to initialize opus encoder: %d\n", err); + spdlog::get("audio")->error("failed to initialize opus encoder: {}", err); m_ok = false; return; } opus_encoder_ctl(m_encoder, OPUS_SET_BITRATE(64000)); + if (ma_context_init(nullptr, 0, nullptr, &m_context) != MA_SUCCESS) { + spdlog::get("audio")->error("failed to initialize context"); + m_ok = false; + return; + } + + Enumerate(); + m_device_config = ma_device_config_init(ma_device_type_playback); m_device_config.playback.format = ma_format_f32; m_device_config.playback.channels = 2; @@ -72,13 +81,13 @@ AudioManager::AudioManager() { m_device_config.pUserData = this; if (ma_device_init(nullptr, &m_device_config, &m_device) != MA_SUCCESS) { - puts("open playback fail"); + spdlog::get("audio")->error("failed to initialize playback device"); m_ok = false; return; } if (ma_device_start(&m_device) != MA_SUCCESS) { - puts("failed to start playback"); + spdlog::get("audio")->error("failed to start playback"); ma_device_uninit(&m_device); m_ok = false; return; @@ -93,14 +102,14 @@ AudioManager::AudioManager() { m_capture_config.pUserData = this; if (ma_device_init(nullptr, &m_capture_config, &m_capture_device) != MA_SUCCESS) { - puts("open capture fail"); + spdlog::get("audio")->error("failed to initialize capture device"); m_ok = false; return; } char device_name[MA_MAX_DEVICE_NAME_LENGTH + 1]; ma_device_get_name(&m_capture_device, ma_device_type_capture, device_name, sizeof(device_name), nullptr); - printf("using %s for capture\n", device_name); + spdlog::get("audio")->info("using {} as capture device", device_name); Glib::signal_timeout().connect(sigc::mem_fun(*this, &AudioManager::DecayVolumeMeters), 40); } @@ -108,6 +117,7 @@ AudioManager::AudioManager() { AudioManager::~AudioManager() { ma_device_uninit(&m_device); ma_device_uninit(&m_capture_device); + ma_context_uninit(&m_context); RemoveAllSSRCs(); } @@ -129,7 +139,7 @@ void AudioManager::RemoveSSRC(uint32_t ssrc) { } void AudioManager::RemoveAllSSRCs() { - puts("remove all ssrc"); + spdlog::get("audio")->info("removing all ssrc"); std::lock_guard<std::mutex> _(m_mutex); for (auto &[ssrc, pair] : m_sources) { opus_decoder_destroy(pair.second); @@ -163,13 +173,45 @@ void AudioManager::FeedMeOpus(uint32_t ssrc, const std::vector<uint8_t> &data) { void AudioManager::StartCaptureDevice() { if (ma_device_start(&m_capture_device) != MA_SUCCESS) { - puts("failed to start capture"); + spdlog::get("audio")->error("Failed to start capture device"); } } void AudioManager::StopCaptureDevice() { if (ma_device_stop(&m_capture_device) != MA_SUCCESS) { - puts("failed to stop capture"); + spdlog::get("audio")->error("Failed to stop capture device"); + } +} + +void AudioManager::SetPlaybackDevice(const Gtk::TreeModel::iterator &iter) { + spdlog::get("audio")->debug("Setting new playback device"); + + const auto device_id = m_devices.GetDeviceIDFromModel(iter); + if (!device_id) { + spdlog::get("audio")->error("Requested ID from iterator is invalid"); + return; + } + + m_playback_id = *device_id; + + ma_device_uninit(&m_device); + + m_device_config = ma_device_config_init(ma_device_type_playback); + m_device_config.playback.format = ma_format_f32; + m_device_config.playback.channels = 2; + m_device_config.playback.pDeviceID = &m_playback_id; + m_device_config.sampleRate = 48000; + m_device_config.dataCallback = data_callback; + m_device_config.pUserData = this; + + if (ma_device_init(&m_context, &m_device_config, &m_device) != MA_SUCCESS) { + spdlog::get("audio")->error("Failed to initialize new device"); + return; + } + + if (ma_device_start(&m_device) != MA_SUCCESS) { + spdlog::get("audio")->error("Failed to start new device"); + return; } } @@ -205,6 +247,29 @@ void AudioManager::SetVolumeSSRC(uint32_t ssrc, double volume) { m_volume_ssrc[ssrc] = (std::exp(volume) - 1) / (E - 1); } +void AudioManager::Enumerate() { + ma_device_info *pPlaybackDeviceInfo; + ma_uint32 playbackDeviceCount; + ma_device_info *pCaptureDeviceInfo; + ma_uint32 captureDeviceCount; + + spdlog::get("audio")->debug("Enumerating devices"); + + if (ma_context_get_devices( + &m_context, + &pPlaybackDeviceInfo, + &playbackDeviceCount, + &pCaptureDeviceInfo, + &captureDeviceCount) != MA_SUCCESS) { + spdlog::get("audio")->error("Failed to enumerate devices"); + return; + } + + spdlog::get("audio")->debug("Found {} playback devices and {} capture devices", playbackDeviceCount, captureDeviceCount); + + m_devices.SetDevices(pPlaybackDeviceInfo, playbackDeviceCount, pCaptureDeviceInfo, captureDeviceCount); +} + void AudioManager::OnCapturedPCM(const int16_t *pcm, ma_uint32 frames) { if (m_opus_buffer == nullptr || !m_should_capture) return; @@ -222,7 +287,7 @@ void AudioManager::OnCapturedPCM(const int16_t *pcm, ma_uint32 frames) { int payload_len = opus_encode(m_encoder, new_pcm.data(), 480, static_cast<unsigned char *>(m_opus_buffer), 1275); if (payload_len < 0) { - printf("encoding error: %d\n", payload_len); + spdlog::get("audio")->error("encoding error: {}", payload_len); } else { m_signal_opus_packet.emit(payload_len); } @@ -275,6 +340,10 @@ double AudioManager::GetSSRCVolumeLevel(uint32_t ssrc) const noexcept { return 0.0; } +AudioDevices &AudioManager::GetDevices() { + return m_devices; +} + AudioManager::type_signal_opus_packet AudioManager::signal_opus_packet() { return m_signal_opus_packet; } diff --git a/src/audio/manager.hpp b/src/audio/manager.hpp index 7d8f479..ff50390 100644 --- a/src/audio/manager.hpp +++ b/src/audio/manager.hpp @@ -5,6 +5,7 @@ #include <array> #include <atomic> #include <deque> +#include <gtkmm/treemodel.h> #include <mutex> #include <thread> #include <unordered_map> @@ -13,6 +14,7 @@ #include <miniaudio.h> #include <opus.h> #include <sigc++/sigc++.h> +#include "devices.hpp" // clang-format on class AudioManager { @@ -30,6 +32,8 @@ public: void StartCaptureDevice(); void StopCaptureDevice(); + void SetPlaybackDevice(const Gtk::TreeModel::iterator &iter); + void SetCapture(bool capture); void SetPlayback(bool playback); @@ -39,11 +43,15 @@ public: void SetMuteSSRC(uint32_t ssrc, bool mute); void SetVolumeSSRC(uint32_t ssrc, double volume); + void Enumerate(); + [[nodiscard]] bool OK() const; [[nodiscard]] double GetCaptureVolumeLevel() const noexcept; [[nodiscard]] double GetSSRCVolumeLevel(uint32_t ssrc) const noexcept; + [[nodiscard]] AudioDevices &GetDevices(); + private: void OnCapturedPCM(const int16_t *pcm, ma_uint32 frames); @@ -63,10 +71,13 @@ private: // playback ma_device m_device; ma_device_config m_device_config; + ma_device_id m_playback_id; // capture ma_device m_capture_device; ma_device_config m_capture_config; + ma_context m_context; + mutable std::mutex m_mutex; std::unordered_map<uint32_t, std::pair<std::deque<int16_t>, OpusDecoder *>> m_sources; @@ -86,6 +97,8 @@ private: mutable std::mutex m_vol_mtx; std::unordered_map<uint32_t, double> m_volumes; + AudioDevices m_devices; + public: using type_signal_opus_packet = sigc::signal<void(int payload_size)>; type_signal_opus_packet signal_opus_packet(); diff --git a/src/discord/voiceclient.cpp b/src/discord/voiceclient.cpp index c0679ce..291b975 100644 --- a/src/discord/voiceclient.cpp +++ b/src/discord/voiceclient.cpp @@ -4,6 +4,8 @@ #include "voiceclient.hpp" #include "json.hpp" #include <sodium.h> +#include <spdlog/spdlog.h> +#include <spdlog/fmt/bin_to_hex.h> #include "abaddon.hpp" #include "audio/manager.hpp" @@ -127,11 +129,11 @@ DiscordVoiceClient::DiscordVoiceClient() { sodium_init(); m_ws.signal_open().connect([this]() { - puts("vws open"); + spdlog::get("voice")->info("Websocket open"); }); m_ws.signal_close().connect([this](uint16_t code) { - printf("vws close %u\n", code); + spdlog::get("voice")->info("Websocket closed with code {}", code); Stop(); }); @@ -258,9 +260,9 @@ void DiscordVoiceClient::HandleGatewayReady(const VoiceGatewayMessage &m) { m_port = d.Port; m_ssrc = d.SSRC; if (std::find(d.Modes.begin(), d.Modes.end(), "xsalsa20_poly1305") == d.Modes.end()) { - puts("xsalsa20_poly1305 not in encryption modes"); + spdlog::get("voice")->error("xsalsa20_poly1305 not in encryption modes"); } - printf("connect to %s:%u ssrc %u\n", m_ip.c_str(), m_port, m_ssrc); + spdlog::get("voice")->info("connect to {}:{} ssrc {}", m_ip, m_port, m_ssrc); m_udp.Connect(m_ip, m_port); m_keepalive_thread = std::thread(&DiscordVoiceClient::KeepaliveThread, this); @@ -270,11 +272,7 @@ void DiscordVoiceClient::HandleGatewayReady(const VoiceGatewayMessage &m) { void DiscordVoiceClient::HandleGatewaySessionDescription(const VoiceGatewayMessage &m) { VoiceSessionDescriptionData d = m.Data; - printf("receiving with %s secret key: ", d.Mode.c_str()); - for (auto b : d.SecretKey) { - printf("%02X", b); - } - printf("\n"); + spdlog::get("voice")->debug("receiving with {}, secret key: {:ns}", d.Mode, spdlog::to_hex(std::begin(d.SecretKey), std::end(d.SecretKey))); VoiceSpeakingMessage msg; msg.Delay = 0; @@ -330,10 +328,10 @@ void DiscordVoiceClient::Discovery() { if (response.size() >= 74 && response[0] == 0x00 && response[1] == 0x02) { const char *our_ip = reinterpret_cast<const char *>(&response[8]); uint16_t our_port = (response[73] << 8) | response[74]; - printf("we are %s:%u\n", our_ip, our_port); + spdlog::get("voice")->debug("IP address discovered: {}:{}\n", our_ip, our_port); SelectProtocol(our_ip, our_port); } else { - puts("received non-discovery packet after discovery"); + spdlog::get("voice")->error("Received non-discovery packet after discovery"); } } @@ -355,7 +353,7 @@ void DiscordVoiceClient::OnUDPData(std::vector<uint8_t> data) { static std::array<uint8_t, 24> nonce = {}; std::memcpy(nonce.data(), data.data(), 12); if (crypto_secretbox_open_easy(payload, payload, data.size() - 12, nonce.data(), m_secret_key.data())) { - puts("decrypt fail"); + // spdlog::get("voice")->trace("UDP payload decryption failure"); } else { Abaddon::Get().GetAudio().FeedMeOpus(ssrc, { payload, payload + data.size() - 12 - crypto_box_MACBYTES }); } diff --git a/src/windows/voicewindow.cpp b/src/windows/voicewindow.cpp index 9f53638..95d1036 100644 --- a/src/windows/voicewindow.cpp +++ b/src/windows/voicewindow.cpp @@ -121,6 +121,17 @@ VoiceWindow::VoiceWindow(Snowflake channel_id) m_signal_gain.emit(val / 100.0); }); + auto *renderer = Gtk::make_managed<Gtk::CellRendererText>(); + m_playback_combo.set_valign(Gtk::ALIGN_END); + m_playback_combo.set_hexpand(true); + m_playback_combo.set_halign(Gtk::ALIGN_FILL); + m_playback_combo.set_model(Abaddon::Get().GetAudio().GetDevices().GetPlaybackDeviceModel()); + m_playback_combo.pack_start(*renderer); + m_playback_combo.add_attribute(*renderer, "text", 0); + m_playback_combo.signal_changed().connect([this]() { + Abaddon::Get().GetAudio().SetPlaybackDevice(m_playback_combo.get_active()); + }); + m_scroll.add(m_user_list); m_controls.add(m_mute); m_controls.add(m_deafen); @@ -129,6 +140,7 @@ VoiceWindow::VoiceWindow(Snowflake channel_id) m_main.add(m_capture_gate); m_main.add(m_capture_gain); m_main.add(m_scroll); + m_main.add(m_playback_combo); add(m_main); show_all_children(); diff --git a/src/windows/voicewindow.hpp b/src/windows/voicewindow.hpp index 8eb02f3..e36846c 100644 --- a/src/windows/voicewindow.hpp +++ b/src/windows/voicewindow.hpp @@ -6,6 +6,7 @@ #include "discord/snowflake.hpp" #include <gtkmm/box.h> #include <gtkmm/checkbutton.h> +#include <gtkmm/combobox.h> #include <gtkmm/listbox.h> #include <gtkmm/progressbar.h> #include <gtkmm/scale.h> @@ -45,6 +46,8 @@ private: Gtk::Scale m_capture_gate; Gtk::Scale m_capture_gain; + Gtk::ComboBox m_playback_combo; + Snowflake m_channel_id; std::unordered_map<Snowflake, VoiceWindowUserListEntry *> m_rows; |