summaryrefslogtreecommitdiff
path: root/src/audio
diff options
context:
space:
mode:
authorouwou <26526779+ouwou@users.noreply.github.com>2022-11-05 02:32:43 -0400
committerouwou <26526779+ouwou@users.noreply.github.com>2022-11-05 02:32:43 -0400
commitf8f9a907c931623b8ef43d7af45b10c49d41afaa (patch)
treea7d6bafcc1ffdde32428f23b1d86292efaa6ab43 /src/audio
parentcb690b6defde4851889d04a68efa4f06d7e38847 (diff)
downloadabaddon-portaudio-f8f9a907c931623b8ef43d7af45b10c49d41afaa.tar.gz
abaddon-portaudio-f8f9a907c931623b8ef43d7af45b10c49d41afaa.zip
add basic combobox to choose output device, start using spdlog
Diffstat (limited to 'src/audio')
-rw-r--r--src/audio/devices.cpp33
-rw-r--r--src/audio/devices.hpp26
-rw-r--r--src/audio/manager.cpp87
-rw-r--r--src/audio/manager.hpp13
4 files changed, 150 insertions, 9 deletions
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();