mirror of
https://github.com/yann64/haikuports.git
synced 2026-03-19 01:46:00 +01:00
1345 lines
44 KiB
Plaintext
1345 lines
44 KiB
Plaintext
From c6938ce49e88383ed109ffb0b4663b9091668b43 Mon Sep 17 00:00:00 2001
|
|
From: zeldakatze <mail@zeldakatze.de>
|
|
Date: Wed, 10 Apr 2024 23:11:58 +0200
|
|
Subject: fix build for haiku
|
|
|
|
|
|
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
|
index 1551d0a..f476113 100644
|
|
--- a/CMakeLists.txt
|
|
+++ b/CMakeLists.txt
|
|
@@ -33,6 +33,10 @@ if (MINGW OR WIN32)
|
|
link_libraries(ws2_32)
|
|
endif ()
|
|
|
|
+if (HAIKU)
|
|
+ link_libraries(network)
|
|
+endif ()
|
|
+
|
|
if (WIN32)
|
|
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
|
add_compile_definitions(NOMINMAX)
|
|
@@ -70,7 +74,8 @@ if (NOT (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU"))
|
|
target_precompile_headers(abaddon PRIVATE <gtkmm.h> src/abaddon.hpp src/util.hpp)
|
|
endif ()
|
|
|
|
-if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.1"))
|
|
+if (NOT (CMAKE_SYSTEM_NAME STREQUAL "Haiku") AND
|
|
+((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.1")))
|
|
target_link_libraries(abaddon stdc++fs)
|
|
endif ()
|
|
|
|
diff --git a/cmake/Findatkmm.cmake b/cmake/Findatkmm.cmake
|
|
index 68c3bc6..623d0e1 100644
|
|
--- a/cmake/Findatkmm.cmake
|
|
+++ b/cmake/Findatkmm.cmake
|
|
@@ -25,7 +25,8 @@ find_path(ATKMM_CONFIG_INCLUDE_DIR
|
|
/usr/lib
|
|
/usr/local/lib
|
|
/opt/local/lib
|
|
- PATH_SUFFIXES ${ATKMM_LIBRARY_NAME}/include)
|
|
+ PATH_SUFFIXES ${ATKMM_LIBRARY_NAME}/include
|
|
+ ${ATKMM_LIBRARY_NAME})
|
|
|
|
find_library(ATKMM_LIBRARY
|
|
NAMES ${ATKMM_LIBRARY_NAME}
|
|
diff --git a/cmake/Findgdkmm.cmake b/cmake/Findgdkmm.cmake
|
|
index 5316bb7..23077ca 100644
|
|
--- a/cmake/Findgdkmm.cmake
|
|
+++ b/cmake/Findgdkmm.cmake
|
|
@@ -1,4 +1,5 @@
|
|
set(gdkmm_LIBRARY_NAME gdkmm-3.0)
|
|
+set(gtkmm_LIBRARY_NAME gtkmm-3.0)
|
|
|
|
find_package(PkgConfig)
|
|
if (PKG_CONFIG_FOUND)
|
|
@@ -23,7 +24,8 @@ find_path(gdkmm_CONFIG_INCLUDE_DIR
|
|
/usr/lib
|
|
/usr/local/lib
|
|
/opt/local/lib
|
|
- PATH_SUFFIXES ${gdkmm_LIBRARY_NAME}/include)
|
|
+ PATH_SUFFIXES ${gdkmm_LIBRARY_NAME}/include
|
|
+ ${gtkmm_LIBRARY_NAME})
|
|
|
|
find_library(gdkmm_LIBRARY
|
|
NAMES ${gdkmm_LIBRARY_NAME}
|
|
diff --git a/cmake/Findglibmm.cmake b/cmake/Findglibmm.cmake
|
|
index 27220e6..11bd996 100644
|
|
--- a/cmake/Findglibmm.cmake
|
|
+++ b/cmake/Findglibmm.cmake
|
|
@@ -22,7 +22,8 @@ find_path(GLIBMM_INCLUDE_DIR
|
|
find_path(GLIBMM_CONFIG_INCLUDE_DIR
|
|
NAMES glibmmconfig.h
|
|
HINTS ${GLIBMM_LIBRARY_HINTS}
|
|
- PATH_SUFFIXES ${GLIBMM_LIBRARY_NAME}/include)
|
|
+ PATH_SUFFIXES ${GLIBMM_LIBRARY_NAME}/include
|
|
+ ${GLIBMM_LIBRARY_NAME})
|
|
|
|
find_library(GLIBMM_LIBRARY
|
|
NAMES ${GLIBMM_LIBRARY_NAME}
|
|
diff --git a/cmake/Findgtkmm.cmake b/cmake/Findgtkmm.cmake
|
|
index addbede..908a08c 100644
|
|
--- a/cmake/Findgtkmm.cmake
|
|
+++ b/cmake/Findgtkmm.cmake
|
|
@@ -32,12 +32,15 @@ find_path(GTKMM_INCLUDE_DIR
|
|
find_path(GTKMM_CONFIG_INCLUDE_DIR
|
|
NAMES gtkmmconfig.h
|
|
HINTS ${GTKMM_LIBRARY_HINTS}
|
|
+ PATH_SUFFIXES ${GTKMM_LIBRARY_NAME}
|
|
PATH_SUFFIXES ${GTKMM_LIBRARY_NAME}/include)
|
|
|
|
find_library(GTKMM_LIB
|
|
NAMES ${GTKMM_LIBRARY_NAME}
|
|
gtkmm
|
|
HINTS ${GTKMM_LIBRARY_HINTS}
|
|
+ HINTS ${GTKMM_INCLUDE_DIR}
|
|
+ PATH ${GTKMM_INCLUDE_DIR}
|
|
PATH_SUFFIXES ${GTKMM_LIBRARY_NAME}
|
|
${GTKMM_LIBRARY_NAME}/include)
|
|
|
|
diff --git a/cmake/Findsigc++.cmake b/cmake/Findsigc++.cmake
|
|
index e0f56c0..d81eb15 100644
|
|
--- a/cmake/Findsigc++.cmake
|
|
+++ b/cmake/Findsigc++.cmake
|
|
@@ -19,7 +19,8 @@ find_path(SIGC++_INCLUDE_DIR
|
|
find_path(SIGC++_CONFIG_INCLUDE_DIR
|
|
NAMES sigc++config.h
|
|
HINTS ${SIGC++_LIBRARY_HINTS}
|
|
- PATH_SUFFIXES ${SIGC++_LIBRARY_NAME}/include)
|
|
+ PATH_SUFFIXES ${SIGC++_LIBRARY_NAME}/include
|
|
+ ${SIGC++_LIBRARY_NAME})
|
|
|
|
find_library(SIGC++_LIBRARY
|
|
NAMES ${SIGC++_LIBRARY_FILE}
|
|
--
|
|
2.48.1
|
|
|
|
|
|
From a3bb805cba1bcdf7c3db1258538a11223257718f Mon Sep 17 00:00:00 2001
|
|
From: zeldakatze <mail@zeldakatze.de>
|
|
Date: Thu, 11 Apr 2024 00:16:45 +0200
|
|
Subject: abaddon: add haiku platform code
|
|
|
|
|
|
diff --git a/src/platform.cpp b/src/platform.cpp
|
|
index 726655b..de75e8f 100644
|
|
--- a/src/platform.cpp
|
|
+++ b/src/platform.cpp
|
|
@@ -224,6 +224,56 @@ std::string Platform::FindStateCacheFolder() {
|
|
return home_path;
|
|
}
|
|
|
|
+#elif __HAIKU__
|
|
+#include <FindDirectory.h>
|
|
+#include <fs_info.h>
|
|
+
|
|
+std::string Platform::FindResourceFolder() {
|
|
+ static std::string found_path;
|
|
+ static bool found = false;
|
|
+ if (found) return found_path;
|
|
+
|
|
+ dev_t volume = dev_for_path("/boot");
|
|
+ char buffer[B_PATH_NAME_LENGTH+B_FILE_NAME_LENGTH];
|
|
+ find_directory(B_SYSTEM_DATA_DIRECTORY, volume, false, buffer, sizeof(buffer));
|
|
+ strcat(buffer, "/abaddon/");
|
|
+ found_path = std::string(buffer);
|
|
+ return found_path;
|
|
+}
|
|
+
|
|
+std::string Platform::FindConfigFile() {
|
|
+ const auto cfg = std::getenv("ABADDON_CONFIG");
|
|
+ if (cfg != nullptr) return cfg;
|
|
+
|
|
+ static std::string found_path;
|
|
+ static bool found = false;
|
|
+ if (found) return found_path;
|
|
+
|
|
+ dev_t volume = dev_for_path("/boot");
|
|
+ char buffer[B_PATH_NAME_LENGTH+B_FILE_NAME_LENGTH];
|
|
+ char cmdbuffer[B_PATH_NAME_LENGTH+B_FILE_NAME_LENGTH] = "mkdir -p ";
|
|
+ find_directory(B_USER_SETTINGS_DIRECTORY, volume, false, buffer, sizeof(buffer));
|
|
+ strcat(buffer, "/abaddon/");
|
|
+ strcat(cmdbuffer, buffer);
|
|
+ system(cmdbuffer);
|
|
+ strcat(buffer, "abaddon.ini");
|
|
+ found_path = std::string(buffer);
|
|
+ return found_path;
|
|
+}
|
|
+
|
|
+std::string Platform::FindStateCacheFolder() {
|
|
+ static std::string found_path;
|
|
+ static bool found = false;
|
|
+ if (found) return found_path;
|
|
+
|
|
+ dev_t volume = dev_for_path("/boot");
|
|
+ char buffer[B_PATH_NAME_LENGTH+B_FILE_NAME_LENGTH];
|
|
+ find_directory(B_SYSTEM_CACHE_DIRECTORY, volume, false, buffer, sizeof(buffer));
|
|
+ strcat(buffer, "/abaddon/");
|
|
+ found_path = std::string(buffer);
|
|
+ return found_path;
|
|
+}
|
|
+
|
|
|
|
#else
|
|
std::string Platform::FindResourceFolder() {
|
|
--
|
|
2.48.1
|
|
|
|
|
|
From 014f8b78347807035b49daaae3bda14800d30672 Mon Sep 17 00:00:00 2001
|
|
From: zeldakatze <mail@zeldakatze.de>
|
|
Date: Thu, 7 Aug 2025 20:39:54 +0200
|
|
Subject: add a portaudio sound interface
|
|
|
|
|
|
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
|
index be6b650..9b1a81d 100644
|
|
--- a/CMakeLists.txt
|
|
+++ b/CMakeLists.txt
|
|
@@ -13,6 +13,7 @@ option(USE_KEYCHAIN "Store the token in the keychain (default)" ON)
|
|
option(ENABLE_NOTIFICATION_SOUNDS "Enable notification sounds (default)" ON)
|
|
option(ENABLE_RNNOISE "Enable RNNoise for voice activity detection (default)" ON)
|
|
option(ENABLE_QRCODE_LOGIN "Enable QR code login (default)" ON)
|
|
+option(USE_PORTAUDIO "Use portaudio instead of miniaudio" OFF)
|
|
|
|
find_package(nlohmann_json REQUIRED)
|
|
find_package(CURL)
|
|
@@ -162,7 +163,12 @@ if (ENABLE_VOICE)
|
|
|
|
find_package(PkgConfig)
|
|
|
|
- set(USE_MINIAUDIO TRUE)
|
|
+ if (USE_PORTAUDIO)
|
|
+ set(USE_PORTAUDIO TRUE)
|
|
+ else ()
|
|
+ set(USE_MINIAUDIO TRUE)
|
|
+ endif ()
|
|
+
|
|
pkg_check_modules(Opus REQUIRED IMPORTED_TARGET opus)
|
|
target_link_libraries(abaddon PkgConfig::Opus)
|
|
|
|
@@ -206,7 +212,12 @@ if (ENABLE_VOICE)
|
|
endif ()
|
|
|
|
if (${ENABLE_NOTIFICATION_SOUNDS})
|
|
- set(USE_MINIAUDIO TRUE)
|
|
+ if (USE_PORTAUDIO)
|
|
+ set(USE_PORTAUDIO TRUE)
|
|
+ else ()
|
|
+ set(USE_MINIAUDIO TRUE)
|
|
+ endif ()
|
|
+
|
|
target_compile_definitions(abaddon PRIVATE ENABLE_NOTIFICATION_SOUNDS)
|
|
endif ()
|
|
|
|
@@ -221,6 +232,12 @@ if (USE_MINIAUDIO)
|
|
target_compile_definitions(abaddon PRIVATE WITH_MINIAUDIO)
|
|
endif ()
|
|
|
|
+if (USE_PORTAUDIO)
|
|
+# find_package(portaudio-2.0)
|
|
+ target_compile_definitions(abaddon PRIVATE USE_PORTAUDIO)
|
|
+ target_link_libraries(abaddon portaudio)
|
|
+endif()
|
|
+
|
|
set(ABADDON_COMPILER_DEFS "" CACHE STRING "Additional compiler definitions")
|
|
foreach (COMPILER_DEF IN LISTS ABADDON_COMPILER_DEFS)
|
|
target_compile_definitions(abaddon PRIVATE "${COMPILER_DEF}")
|
|
diff --git a/src/audio/devices.cpp b/src/audio/devices.cpp
|
|
index dfb7164..09fc156 100644
|
|
--- a/src/audio/devices.cpp
|
|
+++ b/src/audio/devices.cpp
|
|
@@ -21,6 +21,75 @@ Glib::RefPtr<Gtk::ListStore> AudioDevices::GetCaptureDeviceModel() {
|
|
return m_capture;
|
|
}
|
|
|
|
+#ifdef USE_PORTAUDIO
|
|
+void AudioDevices::SetDevices() {
|
|
+ const PaDeviceInfo *deviceInfo;
|
|
+ int i;
|
|
+
|
|
+ /* clear the lists */
|
|
+ m_playback->clear();
|
|
+ m_capture->clear();
|
|
+
|
|
+ /* iterate over all available devices */
|
|
+ int numDevices = Pa_GetDeviceCount();
|
|
+
|
|
+#if 0
|
|
+ for(i=0; i<numDevices; i++) {
|
|
+ deviceInfo = Pa_GetDeviceInfo(i);
|
|
+
|
|
+ /* add it as an input if maxInputChannels > 0 */
|
|
+ if(deviceInfo->maxInputChannels > 0) {
|
|
+ auto row = *m_playback->append();
|
|
+ row[m_playback_columns.Name] = deviceInfo->name;
|
|
+ row[m_playback_columns.DeviceID] = i;
|
|
+
|
|
+ /* if it is the default, mark it as such */
|
|
+ if(i == Pa_GetHostApiInfo(deviceInfo->hostApi)->defaultInputDevice) {
|
|
+ m_default_capture_iter = row;
|
|
+ SetActiveCaptureDevice(row);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* add it as an input if maxInputChannels > 0 */
|
|
+ if(deviceInfo->maxOutputChannels > 0) {
|
|
+ auto row = *m_capture->append();
|
|
+ row[m_capture_columns.Name] = deviceInfo->name;
|
|
+ row[m_capture_columns.DeviceID] = i;
|
|
+
|
|
+ /* if it is the default, mark it as such */
|
|
+ if(i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultOutputDevice) {
|
|
+ m_default_playback_iter = row;
|
|
+ SetActivePlaybackDevice(row);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ }
|
|
+#endif
|
|
+ {
|
|
+ auto row = *m_playback->append();
|
|
+ row[m_playback_columns.Name] = "Default Output";
|
|
+ row[m_playback_columns.DeviceID] = Pa_GetDefaultOutputDevice();
|
|
+ m_default_playback_iter = row;
|
|
+ SetActivePlaybackDevice(row);
|
|
+ }
|
|
+
|
|
+ {
|
|
+ auto row = *m_capture->append();
|
|
+ row[m_capture_columns.Name] = "Default Input";
|
|
+ row[m_capture_columns.DeviceID] = Pa_GetDefaultInputDevice();
|
|
+ m_default_capture_iter = row;
|
|
+ SetActiveCaptureDevice(row);
|
|
+ }
|
|
+
|
|
+ if (!m_default_playback_iter) {
|
|
+ spdlog::get("audio")->warn("No default playback device found");
|
|
+ }
|
|
+
|
|
+ if (!m_default_capture_iter) {
|
|
+ spdlog::get("audio")->warn("No default capture device found");
|
|
+ }
|
|
+}
|
|
+#else /* USE_PORTAUDIO */
|
|
void AudioDevices::SetDevices(ma_device_info *pPlayback, ma_uint32 playback_count, ma_device_info *pCapture, ma_uint32 capture_count) {
|
|
m_playback->clear();
|
|
|
|
@@ -60,7 +129,39 @@ void AudioDevices::SetDevices(ma_device_info *pPlayback, ma_uint32 playback_coun
|
|
spdlog::get("audio")->warn("No default capture device found");
|
|
}
|
|
}
|
|
+#endif /* USE_PORTAUDIO */
|
|
|
|
+#ifdef USE_PORTAUDIO
|
|
+std::optional<PaDeviceIndex> AudioDevices::GetPlaybackDeviceIDFromModel(const Gtk::TreeModel::iterator &iter) const {
|
|
+ if (iter) {
|
|
+ return static_cast<PaDeviceIndex>((*iter)[m_playback_columns.DeviceID]);
|
|
+ }
|
|
+
|
|
+ return std::nullopt;
|
|
+}
|
|
+
|
|
+std::optional<PaDeviceIndex> AudioDevices::GetCaptureDeviceIDFromModel(const Gtk::TreeModel::iterator &iter) const {
|
|
+ if (iter) {
|
|
+ return static_cast<PaDeviceIndex>((*iter)[m_capture_columns.DeviceID]);
|
|
+ }
|
|
+
|
|
+ return std::nullopt;
|
|
+}
|
|
+
|
|
+std::optional<PaDeviceIndex> AudioDevices::GetDefaultPlayback() const {
|
|
+ PaDeviceIndex pi = Pa_GetHostApiInfo(Pa_GetHostApiCount()-1)->defaultOutputDevice;
|
|
+ if(pi == paNoDevice)
|
|
+ return std::nullopt;
|
|
+ return pi;
|
|
+}
|
|
+
|
|
+std::optional<PaDeviceIndex> AudioDevices::GetDefaultCapture() const {
|
|
+ PaDeviceIndex pi = Pa_GetHostApiInfo(Pa_GetHostApiCount()-1)->defaultInputDevice;
|
|
+ if(pi == paNoDevice)
|
|
+ return std::nullopt;
|
|
+ return pi;
|
|
+}
|
|
+#else /* USE_PORTAUDIO */
|
|
std::optional<ma_device_id> AudioDevices::GetPlaybackDeviceIDFromModel(const Gtk::TreeModel::iterator &iter) const {
|
|
if (iter) {
|
|
return static_cast<ma_device_id>((*iter)[m_playback_columns.DeviceID]);
|
|
@@ -92,6 +193,7 @@ std::optional<ma_device_id> AudioDevices::GetDefaultCapture() const {
|
|
|
|
return std::nullopt;
|
|
}
|
|
+#endif /* USE_PORTAUDIO */
|
|
|
|
void AudioDevices::SetActivePlaybackDevice(const Gtk::TreeModel::iterator &iter) {
|
|
m_active_playback_iter = iter;
|
|
diff --git a/src/audio/devices.hpp b/src/audio/devices.hpp
|
|
index c83bdb4..54d1713 100644
|
|
--- a/src/audio/devices.hpp
|
|
+++ b/src/audio/devices.hpp
|
|
@@ -4,7 +4,13 @@
|
|
// clang-format off
|
|
|
|
#include <gtkmm/liststore.h>
|
|
+
|
|
+#ifndef USE_PORTAUDIO
|
|
#include <miniaudio.h>
|
|
+#else
|
|
+#include <portaudio.h>
|
|
+#endif
|
|
+
|
|
#include <optional>
|
|
|
|
// clang-format on
|
|
@@ -16,13 +22,26 @@ public:
|
|
Glib::RefPtr<Gtk::ListStore> GetPlaybackDeviceModel();
|
|
Glib::RefPtr<Gtk::ListStore> GetCaptureDeviceModel();
|
|
|
|
+ #ifndef USE_PORTAUDIO
|
|
void SetDevices(ma_device_info *pPlayback, ma_uint32 playback_count, ma_device_info *pCapture, ma_uint32 capture_count);
|
|
+ #else
|
|
+ /* call Pa directly */
|
|
+ void SetDevices();
|
|
+ #endif
|
|
|
|
+#ifdef USE_PORTAUDIO
|
|
+ [[nodiscard]] std::optional<PaDeviceIndex> GetPlaybackDeviceIDFromModel(const Gtk::TreeModel::iterator &iter) const;
|
|
+ [[nodiscard]] std::optional<PaDeviceIndex> GetCaptureDeviceIDFromModel(const Gtk::TreeModel::iterator &iter) const;
|
|
+
|
|
+ [[nodiscard]] std::optional<PaDeviceIndex> GetDefaultPlayback() const;
|
|
+ [[nodiscard]] std::optional<PaDeviceIndex> GetDefaultCapture() const;
|
|
+#else
|
|
[[nodiscard]] std::optional<ma_device_id> GetPlaybackDeviceIDFromModel(const Gtk::TreeModel::iterator &iter) const;
|
|
[[nodiscard]] std::optional<ma_device_id> GetCaptureDeviceIDFromModel(const Gtk::TreeModel::iterator &iter) const;
|
|
|
|
[[nodiscard]] std::optional<ma_device_id> GetDefaultPlayback() const;
|
|
[[nodiscard]] std::optional<ma_device_id> GetDefaultCapture() const;
|
|
+#endif
|
|
|
|
void SetActivePlaybackDevice(const Gtk::TreeModel::iterator &iter);
|
|
void SetActiveCaptureDevice(const Gtk::TreeModel::iterator &iter);
|
|
@@ -36,7 +55,11 @@ private:
|
|
PlaybackColumns();
|
|
|
|
Gtk::TreeModelColumn<Glib::ustring> Name;
|
|
+#ifdef USE_PORTAUDIO
|
|
+ Gtk::TreeModelColumn<PaDeviceIndex> DeviceID;
|
|
+#else
|
|
Gtk::TreeModelColumn<ma_device_id> DeviceID;
|
|
+#endif
|
|
};
|
|
PlaybackColumns m_playback_columns;
|
|
Glib::RefPtr<Gtk::ListStore> m_playback;
|
|
@@ -48,7 +71,11 @@ private:
|
|
CaptureColumns();
|
|
|
|
Gtk::TreeModelColumn<Glib::ustring> Name;
|
|
+#ifndef USE_PORTAUDIO
|
|
Gtk::TreeModelColumn<ma_device_id> DeviceID;
|
|
+#else /* USE_PORTAUDIO */
|
|
+ Gtk::TreeModelColumn<PaDeviceIndex> DeviceID;
|
|
+#endif /* USE_PORTAUDIO */
|
|
};
|
|
CaptureColumns m_capture_columns;
|
|
Glib::RefPtr<Gtk::ListStore> m_capture;
|
|
diff --git a/src/audio/ma_impl.cpp b/src/audio/ma_impl.cpp
|
|
index 531b24f..767dec1 100644
|
|
--- a/src/audio/ma_impl.cpp
|
|
+++ b/src/audio/ma_impl.cpp
|
|
@@ -1,7 +1,9 @@
|
|
-#ifdef WITH_MINIAUDIO
|
|
- #define MINIAUDIO_IMPLEMENTATION
|
|
- #ifdef __APPLE__
|
|
- #define MA_NO_RUNTIME_LINKING
|
|
- #endif
|
|
- #include <miniaudio.h>
|
|
+#ifndef USE_PORTAUDIO
|
|
+ #ifdef WITH_MINIAUDIO
|
|
+ #define MINIAUDIO_IMPLEMENTATION
|
|
+ #ifdef __APPLE__
|
|
+ #define MA_NO_RUNTIME_LINKING
|
|
+ #endif
|
|
+ #include <miniaudio.h>
|
|
+ #endif
|
|
#endif
|
|
diff --git a/src/audio/manager.hpp b/src/audio/manager.hpp
|
|
index 5716fc5..a657dcc 100644
|
|
--- a/src/audio/manager.hpp
|
|
+++ b/src/audio/manager.hpp
|
|
@@ -11,7 +11,13 @@
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
+
|
|
+#ifndef USE_PORTAUDIO
|
|
#include <miniaudio.h>
|
|
+#else
|
|
+#include <portaudio.h>
|
|
+#endif
|
|
+
|
|
#include <opus.h>
|
|
#include <sigc++/sigc++.h>
|
|
#include <spdlog/spdlog.h>
|
|
@@ -80,7 +86,9 @@ public:
|
|
void SetVADMethod(VADMethod method);
|
|
VADMethod GetVADMethod() const;
|
|
|
|
+#ifndef USE_PORTAUDIO
|
|
static std::vector<ma_backend> ParseBackendsList(const Glib::ustring &list);
|
|
+#endif /* USE_PORTAUDIO */
|
|
|
|
#ifdef WITH_RNNOISE
|
|
float GetCurrentVADProbability() const;
|
|
@@ -94,10 +102,18 @@ public:
|
|
bool GetMixMono() const;
|
|
|
|
private:
|
|
+#ifndef USE_PORTAUDIO
|
|
void OnCapturedPCM(const int16_t *pcm, ma_uint32 frames);
|
|
+#else /* USE_PORTAUDIO */
|
|
+ void OnCapturedPCM(const int16_t *pcm, uint32_t frames);
|
|
+#endif /* USE_PORTAUDIO */
|
|
|
|
void UpdateReceiveVolume(uint32_t ssrc, const int16_t *pcm, int frames);
|
|
+#ifndef USE_PORTAUDIO
|
|
void UpdateCaptureVolume(const int16_t *pcm, ma_uint32 frames);
|
|
+#else
|
|
+ void UpdateCaptureVolume(const int16_t *pcm, uint32_t frames);
|
|
+#endif /* USE_PORTAUDIO */
|
|
std::atomic<int> m_capture_peak_meter = 0;
|
|
|
|
bool DecayVolumeMeters();
|
|
@@ -111,13 +127,24 @@ private:
|
|
void RNNoiseUninitialize();
|
|
#endif
|
|
|
|
+#ifndef USE_PORTAUDIO
|
|
friend void data_callback(ma_device *, void *, const void *, ma_uint32);
|
|
friend void capture_data_callback(ma_device *, void *, const void *, ma_uint32);
|
|
+#else /* USE_PORTAUDIO */
|
|
+ friend int playCallback(const void *pInput, void *pOutput, unsigned long frameCount,
|
|
+ const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData );
|
|
+ friend int recordCallback(const void *pInput, void *pOutput,
|
|
+ unsigned long frameCount,
|
|
+ const PaStreamCallbackTimeInfo* timeInfo,
|
|
+ PaStreamCallbackFlags statusFlags,
|
|
+ void *userData );
|
|
+#endif /* USE_PORTAUDIO */
|
|
|
|
std::thread m_thread;
|
|
|
|
bool m_ok;
|
|
|
|
+#ifndef USE_PORTAUDIO
|
|
// playback
|
|
ma_device m_playback_device;
|
|
ma_device_config m_playback_config;
|
|
@@ -126,8 +153,18 @@ private:
|
|
ma_device m_capture_device;
|
|
ma_device_config m_capture_config;
|
|
ma_device_id m_capture_id;
|
|
-
|
|
+
|
|
ma_context m_context;
|
|
+#else
|
|
+ // playback
|
|
+ PaStream* pa_playback_device;
|
|
+ PaStreamParameters m_playback_config;
|
|
+ // capture
|
|
+ PaStream* pa_capture_device;
|
|
+ PaStreamParameters m_capture_config;
|
|
+
|
|
+#endif
|
|
+
|
|
|
|
mutable std::mutex m_mutex;
|
|
mutable std::mutex m_enc_mutex;
|
|
@@ -166,7 +203,9 @@ private:
|
|
#endif
|
|
std::atomic<uint32_t> m_rtp_timestamp = 0;
|
|
|
|
+#ifndef USE_PORTAUDIO
|
|
ma_log m_ma_log;
|
|
+#endif
|
|
std::shared_ptr<spdlog::logger> m_log;
|
|
|
|
public:
|
|
diff --git a/src/audio/manager.cpp b/src/audio/miniaudioManager.cpp
|
|
similarity index 99%
|
|
rename from src/audio/manager.cpp
|
|
rename to src/audio/miniaudioManager.cpp
|
|
index d32c5e2..3a155fc 100644
|
|
--- a/src/audio/manager.cpp
|
|
+++ b/src/audio/miniaudioManager.cpp
|
|
@@ -1,3 +1,4 @@
|
|
+#ifndef USE_PORTAUDIO
|
|
#ifdef WITH_VOICE
|
|
// clang-format off
|
|
|
|
@@ -704,3 +705,4 @@ AudioManager::type_signal_opus_packet AudioManager::signal_opus_packet() {
|
|
}
|
|
|
|
#endif
|
|
+#endif /* USE_PORTAUDIO */
|
|
diff --git a/src/audio/portaudioManager.cpp b/src/audio/portaudioManager.cpp
|
|
new file mode 100644
|
|
index 0000000..bcd1254
|
|
--- /dev/null
|
|
+++ b/src/audio/portaudioManager.cpp
|
|
@@ -0,0 +1,601 @@
|
|
+#ifdef WITH_VOICE
|
|
+#ifdef USE_PORTAUDIO
|
|
+
|
|
+#include "manager.hpp"
|
|
+#include "abaddon.hpp"
|
|
+#include <array>
|
|
+#include <glibmm/main.h>
|
|
+#include <spdlog/sinks/stdout_color_sinks.h>
|
|
+#include <portaudio.h>
|
|
+#include <opus.h>
|
|
+#include <cstring>
|
|
+
|
|
+int playCallback(const void *pInput, void *pOutput,
|
|
+ unsigned long frameCount,
|
|
+ const PaStreamCallbackTimeInfo* timeInfo,
|
|
+ PaStreamCallbackFlags statusFlags,
|
|
+ void *userData ) {
|
|
+ AudioManager *mgr = reinterpret_cast<AudioManager *>(userData);
|
|
+ if (mgr == nullptr) {printf("NULL\n");return paContinue;};
|
|
+ std::lock_guard<std::mutex> _(mgr->m_mutex);
|
|
+
|
|
+ auto *pOutputF32 = static_cast<int16_t *>(pOutput);
|
|
+
|
|
+ /* clear the output buffer */
|
|
+ for(int i = 0; i<frameCount * 2; i++)
|
|
+ pOutputF32[i] = 0;
|
|
+
|
|
+ /* write every SSRC into the output stream */
|
|
+ for (auto &[ssrc, pair] : mgr->m_sources) {
|
|
+ double volume = 1.0;
|
|
+ if (const auto vol_it = mgr->m_volume_ssrc.find(ssrc); vol_it != mgr->m_volume_ssrc.end()) {
|
|
+ volume = vol_it->second;
|
|
+ }
|
|
+ auto &buf = pair.first;
|
|
+ const size_t n = std::min(static_cast<size_t>(buf.size()), static_cast<size_t>(frameCount * 2ULL));
|
|
+ for (size_t i = 0; i < n; i++) {
|
|
+ pOutputF32[i] += (int16_t) (volume * buf[i]);
|
|
+ //pOutputF32[i] += volume * buf[i] / 32768.F;
|
|
+ }
|
|
+ buf.erase(buf.begin(), buf.begin() + n);
|
|
+ }
|
|
+
|
|
+ return paContinue;
|
|
+}
|
|
+
|
|
+int recordCallback(const void *pInput, void *pOutput,
|
|
+ unsigned long frameCount,
|
|
+ const PaStreamCallbackTimeInfo* timeInfo,
|
|
+ PaStreamCallbackFlags statusFlags,
|
|
+ void *userData ) {
|
|
+ auto *mgr = reinterpret_cast<AudioManager *>(userData);
|
|
+ if (mgr == nullptr) return paContinue;
|
|
+
|
|
+ mgr->OnCapturedPCM(static_cast<const int16_t *>(pInput), frameCount);
|
|
+
|
|
+ /*
|
|
+ * You can simply increment it by 480 in UDPSocket::SendEncrypted but this is wrong
|
|
+ * The timestamp is supposed to be strictly linear eg. if there's discontinuous
|
|
+ * transmission for 1 second then the timestamp should be 48000 greater than the
|
|
+ * last packet. So it's incremented here because this is fired 100x per second
|
|
+ * and is always called in sync with UDPSocket::SendEncrypted
|
|
+ */
|
|
+ mgr->m_rtp_timestamp += 480;
|
|
+ return paContinue;
|
|
+}
|
|
+
|
|
+AudioManager::AudioManager(const Glib::ustring &backends_string)
|
|
+ : m_log(spdlog::stdout_color_mt("portaudio")) {
|
|
+ m_ok = true;
|
|
+
|
|
+ PaError pa_err;
|
|
+ int opus_err;
|
|
+
|
|
+ /* try to initialize PortAudio */
|
|
+ pa_err = Pa_Initialize();
|
|
+ if(pa_err != paNoError) {
|
|
+ spdlog::get("audio")->error("ERROR: Pa_Initialize returned 0x%x\n", pa_err);
|
|
+ Pa_Terminate();
|
|
+ m_ok = false;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* initialize RNNoise */
|
|
+#ifdef WITH_RNNOISE
|
|
+ RNNoiseInitialize();
|
|
+#endif
|
|
+
|
|
+ /* try to initialize opus */
|
|
+ m_encoder = opus_encoder_create(48000, 2, OPUS_APPLICATION_VOIP, &opus_err);
|
|
+ if (opus_err != OPUS_OK) {
|
|
+ spdlog::get("audio")->error("failed to initialize opus encoder: {}", opus_err);
|
|
+ m_ok = false;
|
|
+ return;
|
|
+ }
|
|
+ opus_encoder_ctl(m_encoder, OPUS_SET_BITRATE(64000));
|
|
+
|
|
+ /* get the backends */
|
|
+ Enumerate();
|
|
+
|
|
+ /* create the configurations for capture and playback */
|
|
+ m_playback_config.sampleFormat = paInt16;
|
|
+ m_playback_config.channelCount = 2;
|
|
+ m_playback_config.suggestedLatency = Pa_GetDeviceInfo( m_capture_config.device )->defaultLowInputLatency;
|
|
+ m_playback_config.hostApiSpecificStreamInfo = NULL;
|
|
+ m_playback_config.device = Pa_GetDefaultOutputDevice();
|
|
+
|
|
+ m_capture_config.sampleFormat = paInt16;
|
|
+ m_capture_config.channelCount = 2;
|
|
+ m_capture_config.suggestedLatency = Pa_GetDeviceInfo( m_capture_config.device )->defaultLowInputLatency;
|
|
+ m_capture_config.hostApiSpecificStreamInfo = NULL;
|
|
+ m_capture_config.device = Pa_GetDefaultInputDevice();
|
|
+
|
|
+ /* create the streams */
|
|
+ pa_err = Pa_OpenStream(&pa_capture_device, &m_capture_config, NULL, 48000, 480, paClipOff, recordCallback, this);
|
|
+ if(pa_err != paNoError) {
|
|
+ m_ok = false;
|
|
+ spdlog::get("audio")->error("ERROR: Pa_OpenStream for capture returned %d\n", pa_err);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ pa_err = Pa_OpenStream(&pa_playback_device, NULL, &m_playback_config, 48000, 480, paClipOff, playCallback, this);
|
|
+ if(pa_err != paNoError) {
|
|
+ m_ok = false;
|
|
+ spdlog::get("audio")->error("ERROR: Pa_OpenStream for playback returned %d\n", pa_err);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if(Pa_StartStream(pa_playback_device) != paNoError) {
|
|
+ spdlog::get("audio")->error("ERROR: Could not start playback\n");
|
|
+ }
|
|
+/**TODO
|
|
+ if (const auto capture_id = m_devices.GetDefaultCapture(); capture_id.has_value()) {
|
|
+ m_capture_id = *capture_id;
|
|
+ m_capture_config.capture.pDeviceID = &m_capture_id;
|
|
+ }*/
|
|
+
|
|
+ Glib::signal_timeout().connect(sigc::mem_fun(*this, &AudioManager::DecayVolumeMeters), 40);
|
|
+}
|
|
+
|
|
+AudioManager::~AudioManager() {
|
|
+ Pa_Terminate();
|
|
+ RemoveAllSSRCs();
|
|
+
|
|
+#ifdef WITH_RNNOISE
|
|
+ RNNoiseUninitialize();
|
|
+#endif
|
|
+}
|
|
+
|
|
+void AudioManager::AddSSRC(uint32_t ssrc) {
|
|
+ std::lock_guard<std::mutex> _(m_mutex);
|
|
+ int error;
|
|
+ if (m_sources.find(ssrc) == m_sources.end()) {
|
|
+ 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) {
|
|
+ std::lock_guard<std::mutex> _(m_mutex);
|
|
+ if (auto it = m_sources.find(ssrc); it != m_sources.end()) {
|
|
+ opus_decoder_destroy(it->second.second);
|
|
+ m_sources.erase(it);
|
|
+ }
|
|
+}
|
|
+
|
|
+void AudioManager::RemoveAllSSRCs() {
|
|
+ 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);
|
|
+ }
|
|
+ m_sources.clear();
|
|
+}
|
|
+
|
|
+void AudioManager::SetOpusBuffer(uint8_t *ptr) {
|
|
+ m_opus_buffer = ptr;
|
|
+}
|
|
+
|
|
+void AudioManager::FeedMeOpus(uint32_t ssrc, const std::vector<uint8_t> &data) {
|
|
+ if (!m_should_playback || Pa_IsStreamActive(pa_playback_device) != 1) return;
|
|
+
|
|
+ std::lock_guard<std::mutex> _(m_mutex);
|
|
+ if (m_muted_ssrcs.find(ssrc) != m_muted_ssrcs.end()) return;
|
|
+
|
|
+ static std::array<opus_int16, 120 * 48 * 2> pcm;
|
|
+ if (auto it = m_sources.find(ssrc); it != m_sources.end()) {
|
|
+ int decoded = opus_decode(it->second.second, data.data(), static_cast<opus_int32>(data.size()), pcm.data(), 120 * 48, 0);
|
|
+ if (decoded <= 0) {
|
|
+ } else {
|
|
+ UpdateReceiveVolume(ssrc, pcm.data(), decoded);
|
|
+ auto &buf = it->second.first;
|
|
+ buf.insert(buf.end(), pcm.begin(), pcm.begin() + decoded * 2);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void AudioManager::StartCaptureDevice() {
|
|
+ if (Pa_StartStream(pa_capture_device) != paNoError) {
|
|
+ spdlog::get("audio")->error("Failed to start capture device");
|
|
+ }
|
|
+}
|
|
+
|
|
+void AudioManager::StopCaptureDevice() {
|
|
+ if (Pa_StopStream(pa_capture_device) != paNoError) {
|
|
+ spdlog::get("audio")->error("Failed to stop capture device");
|
|
+ }
|
|
+}
|
|
+
|
|
+void AudioManager::SetPlaybackDevice(const Gtk::TreeModel::iterator &iter) {
|
|
+ PaError pa_err;
|
|
+ spdlog::get("audio")->debug("Setting new playback device");
|
|
+
|
|
+ const auto device_id = m_devices.GetPlaybackDeviceIDFromModel(iter);
|
|
+ if (!device_id) {
|
|
+ spdlog::get("audio")->error("Requested ID from iterator is invalid");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ m_devices.SetActivePlaybackDevice(iter);
|
|
+
|
|
+ // TODO m_playback_config.device = *device_id;
|
|
+
|
|
+ Pa_AbortStream(pa_playback_device);
|
|
+ pa_err = Pa_OpenStream(&pa_playback_device, NULL, &m_playback_config, 48000, 480, paClipOff, playCallback, this);
|
|
+ if(pa_err != paNoError) {
|
|
+ spdlog::get("audio")->error("Error setting playback device");
|
|
+ }
|
|
+
|
|
+ Pa_StartStream(pa_playback_device);
|
|
+
|
|
+}
|
|
+
|
|
+void AudioManager::SetCaptureDevice(const Gtk::TreeModel::iterator &iter) {
|
|
+ PaError pa_err;
|
|
+ spdlog::get("audio")->debug("Setting new capture device");
|
|
+
|
|
+ const auto device_id = m_devices.GetCaptureDeviceIDFromModel(iter);
|
|
+ if (!device_id) {
|
|
+ spdlog::get("audio")->error("Requested ID from iterator is invalid");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ m_devices.SetActiveCaptureDevice(iter);
|
|
+
|
|
+ //TODO m_playback_config.device = *device_id;
|
|
+
|
|
+ Pa_AbortStream(pa_playback_device);
|
|
+ pa_err = Pa_OpenStream(&pa_capture_device, &m_capture_config, NULL, 48000, 480, paClipOff, recordCallback, this);
|
|
+
|
|
+ Pa_StartStream(pa_playback_device);
|
|
+}
|
|
+
|
|
+void AudioManager::SetCapture(bool capture) {
|
|
+ m_should_capture = capture;
|
|
+}
|
|
+
|
|
+void AudioManager::SetPlayback(bool playback) {
|
|
+ m_should_playback = playback;
|
|
+}
|
|
+
|
|
+void AudioManager::SetCaptureGate(double gate) {
|
|
+ m_capture_gate = gate;
|
|
+}
|
|
+
|
|
+void AudioManager::SetCaptureGain(double gain) {
|
|
+ m_capture_gain = gain;
|
|
+}
|
|
+
|
|
+double AudioManager::GetCaptureGate() const noexcept {
|
|
+ return m_capture_gate;
|
|
+}
|
|
+
|
|
+double AudioManager::GetCaptureGain() const noexcept {
|
|
+ return m_capture_gain;
|
|
+}
|
|
+
|
|
+void AudioManager::SetMuteSSRC(uint32_t ssrc, bool mute) {
|
|
+ std::lock_guard<std::mutex> _(m_mutex);
|
|
+ if (mute) {
|
|
+ m_muted_ssrcs.insert(ssrc);
|
|
+ } else {
|
|
+ m_muted_ssrcs.erase(ssrc);
|
|
+ }
|
|
+}
|
|
+
|
|
+void AudioManager::SetVolumeSSRC(uint32_t ssrc, double volume) {
|
|
+ std::lock_guard<std::mutex> _(m_mutex);
|
|
+ m_volume_ssrc[ssrc] = volume;
|
|
+}
|
|
+
|
|
+double AudioManager::GetVolumeSSRC(uint32_t ssrc) const {
|
|
+ std::lock_guard<std::mutex> _(m_mutex);
|
|
+ if (const auto iter = m_volume_ssrc.find(ssrc); iter != m_volume_ssrc.end()) {
|
|
+ return iter->second;
|
|
+ }
|
|
+ return 1.0;
|
|
+}
|
|
+
|
|
+void AudioManager::SetEncodingApplication(int application) {
|
|
+ std::lock_guard<std::mutex> _(m_enc_mutex);
|
|
+ int prev_bitrate = 64000;
|
|
+ if (int err = opus_encoder_ctl(m_encoder, OPUS_GET_BITRATE(&prev_bitrate)); err != OPUS_OK) {
|
|
+ spdlog::get("audio")->error("Failed to get old bitrate when reinitializing: {}", err);
|
|
+ }
|
|
+ opus_encoder_destroy(m_encoder);
|
|
+ int err = 0;
|
|
+ m_encoder = opus_encoder_create(48000, 2, application, &err);
|
|
+ if (err != OPUS_OK) {
|
|
+ spdlog::get("audio")->critical("opus_encoder_create failed: {}", err);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (int err = opus_encoder_ctl(m_encoder, OPUS_SET_BITRATE(prev_bitrate)); err != OPUS_OK) {
|
|
+ spdlog::get("audio")->error("Failed to set bitrate when reinitializing: {}", err);
|
|
+ }
|
|
+}
|
|
+
|
|
+int AudioManager::GetEncodingApplication() {
|
|
+ std::lock_guard<std::mutex> _(m_enc_mutex);
|
|
+ int temp = OPUS_APPLICATION_VOIP;
|
|
+ if (int err = opus_encoder_ctl(m_encoder, OPUS_GET_APPLICATION(&temp)); err != OPUS_OK) {
|
|
+ spdlog::get("audio")->error("opus_encoder_ctl(OPUS_GET_APPLICATION) failed: {}", err);
|
|
+ }
|
|
+ return temp;
|
|
+}
|
|
+
|
|
+void AudioManager::SetSignalHint(int signal) {
|
|
+ std::lock_guard<std::mutex> _(m_enc_mutex);
|
|
+ if (int err = opus_encoder_ctl(m_encoder, OPUS_SET_SIGNAL(signal)); err != OPUS_OK) {
|
|
+ spdlog::get("audio")->error("opus_encoder_ctl(OPUS_SET_SIGNAL) failed: {}", err);
|
|
+ }
|
|
+}
|
|
+
|
|
+int AudioManager::GetSignalHint() {
|
|
+ std::lock_guard<std::mutex> _(m_enc_mutex);
|
|
+ int temp = OPUS_AUTO;
|
|
+ if (int err = opus_encoder_ctl(m_encoder, OPUS_GET_SIGNAL(&temp)); err != OPUS_OK) {
|
|
+ spdlog::get("audio")->error("opus_encoder_ctl(OPUS_GET_SIGNAL) failed: {}", err);
|
|
+ }
|
|
+ return temp;
|
|
+}
|
|
+
|
|
+void AudioManager::SetBitrate(int bitrate) {
|
|
+ std::lock_guard<std::mutex> _(m_enc_mutex);
|
|
+ if (int err = opus_encoder_ctl(m_encoder, OPUS_SET_BITRATE(bitrate)); err != OPUS_OK) {
|
|
+ spdlog::get("audio")->error("opus_encoder_ctl(OPUS_SET_BITRATE) failed: {}", err);
|
|
+ }
|
|
+}
|
|
+
|
|
+int AudioManager::GetBitrate() {
|
|
+ std::lock_guard<std::mutex> _(m_enc_mutex);
|
|
+ int temp = 64000;
|
|
+ if (int err = opus_encoder_ctl(m_encoder, OPUS_GET_BITRATE(&temp)); err != OPUS_OK) {
|
|
+ spdlog::get("audio")->error("opus_encoder_ctl(OPUS_GET_BITRATE) failed: {}", err);
|
|
+ }
|
|
+ return temp;
|
|
+}
|
|
+
|
|
+void AudioManager::Enumerate() {
|
|
+ spdlog::get("audio")->debug("Enumerating devices");
|
|
+ m_devices.SetDevices();
|
|
+}
|
|
+
|
|
+void AudioManager::OnCapturedPCM(const int16_t *pcm, uint32_t frames) {
|
|
+ if (m_opus_buffer == nullptr || !m_should_capture) return;
|
|
+
|
|
+ const double gain = m_capture_gain;
|
|
+
|
|
+ std::vector<int16_t> new_pcm(pcm, pcm + frames * 2);
|
|
+ for (auto &val : new_pcm) {
|
|
+ const int32_t unclamped = static_cast<int32_t>(val * gain);
|
|
+ val = std::clamp(unclamped, INT16_MIN, INT16_MAX);
|
|
+ }
|
|
+
|
|
+ if (m_mix_mono) {
|
|
+ for (size_t i = 0; i < frames * 2; i += 2) {
|
|
+ const int sample_L = new_pcm[i];
|
|
+ const int sample_R = new_pcm[i + 1];
|
|
+ const int16_t mixed = static_cast<int16_t>((sample_L + sample_R) / 2);
|
|
+ new_pcm[i] = mixed;
|
|
+ new_pcm[i + 1] = mixed;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ UpdateCaptureVolume(new_pcm.data(), frames);
|
|
+
|
|
+ static std::array<float, 480> denoised_L;
|
|
+ static std::array<float, 480> denoised_R;
|
|
+
|
|
+ bool m_rnnoise_passed = false;
|
|
+#ifdef WITH_RNNOISE
|
|
+ if (m_vad_method == VADMethod::RNNoise || m_enable_noise_suppression) {
|
|
+ m_rnnoise_passed = CheckVADRNNoise(new_pcm.data(), denoised_L.data(), denoised_R.data());
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ switch (m_vad_method) {
|
|
+ case VADMethod::Gate:
|
|
+ if (!CheckVADVoiceGate()) return;
|
|
+ break;
|
|
+#ifdef WITH_RNNOISE
|
|
+ case VADMethod::RNNoise:
|
|
+ if (!m_rnnoise_passed) return;
|
|
+ break;
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ m_enc_mutex.lock();
|
|
+ int payload_len = -1;
|
|
+
|
|
+ if (m_enable_noise_suppression) {
|
|
+ static std::array<int16_t, 960> denoised_interleaved;
|
|
+ for (size_t i = 0; i < 480; i++) {
|
|
+ denoised_interleaved[i * 2] = static_cast<int16_t>(denoised_L[i]);
|
|
+ }
|
|
+ for (size_t i = 0; i < 480; i++) {
|
|
+ denoised_interleaved[i * 2 + 1] = static_cast<int16_t>(denoised_R[i]);
|
|
+ }
|
|
+ payload_len = opus_encode(m_encoder, denoised_interleaved.data(), 480, static_cast<unsigned char *>(m_opus_buffer), 1275);
|
|
+ } else {
|
|
+ payload_len = opus_encode(m_encoder, new_pcm.data(), 480, static_cast<unsigned char *>(m_opus_buffer), 1275);
|
|
+ }
|
|
+
|
|
+ m_enc_mutex.unlock();
|
|
+ if (payload_len < 0) {
|
|
+ spdlog::get("audio")->error("encoding error: {}", payload_len);
|
|
+ } else {
|
|
+ m_signal_opus_packet.emit(payload_len);
|
|
+ }
|
|
+}
|
|
+
|
|
+void AudioManager::UpdateReceiveVolume(uint32_t ssrc, const int16_t *pcm, int frames) {
|
|
+ std::lock_guard<std::mutex> _(m_vol_mtx);
|
|
+
|
|
+ auto &meter = m_volumes[ssrc];
|
|
+ for (int i = 0; i < frames * 2; i += 2) {
|
|
+ const int amp = std::abs(pcm[i]);
|
|
+ meter = std::max(meter, std::abs(amp) / 32768.0);
|
|
+ }
|
|
+}
|
|
+
|
|
+void AudioManager::UpdateCaptureVolume(const int16_t *pcm, uint32_t frames) {
|
|
+ for (uint32_t i = 0; i < frames * 2; i += 2) {
|
|
+ const int amp = std::abs(pcm[i]);
|
|
+ m_capture_peak_meter = std::max(m_capture_peak_meter.load(std::memory_order_relaxed), amp);
|
|
+ }
|
|
+}
|
|
+
|
|
+bool AudioManager::DecayVolumeMeters() {
|
|
+ m_capture_peak_meter -= 600;
|
|
+ if (m_capture_peak_meter < 0) m_capture_peak_meter = 0;
|
|
+
|
|
+ const auto x = m_vad_prob.load() - 0.05f;
|
|
+ m_vad_prob.store(x < 0.0f ? 0.0f : x);
|
|
+
|
|
+ std::lock_guard<std::mutex> _(m_vol_mtx);
|
|
+
|
|
+ for (auto &[ssrc, meter] : m_volumes) {
|
|
+ meter -= 0.01;
|
|
+ if (meter < 0.0) meter = 0.0;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+bool AudioManager::CheckVADVoiceGate() {
|
|
+ return m_capture_peak_meter / 32768.0 > m_capture_gate;
|
|
+}
|
|
+
|
|
+#ifdef WITH_RNNOISE
|
|
+bool AudioManager::CheckVADRNNoise(const int16_t *pcm, float *denoised_left, float *denoised_right) {
|
|
+ // use left channel for vad, only denoise right if noise suppression enabled
|
|
+ std::unique_lock<std::mutex> _(m_rnn_mutex);
|
|
+
|
|
+ static float rnnoise_input[480];
|
|
+ for (size_t i = 0; i < 480; i++) {
|
|
+ rnnoise_input[i] = static_cast<float>(pcm[i * 2]);
|
|
+ }
|
|
+ m_vad_prob = std::max(m_vad_prob.load(), rnnoise_process_frame(m_rnnoise[0], denoised_left, rnnoise_input));
|
|
+
|
|
+ if (m_enable_noise_suppression) {
|
|
+ for (size_t i = 0; i < 480; i++) {
|
|
+ rnnoise_input[i] = static_cast<float>(pcm[i * 2 + 1]);
|
|
+ }
|
|
+ rnnoise_process_frame(m_rnnoise[1], denoised_right, rnnoise_input);
|
|
+ }
|
|
+
|
|
+ return m_vad_prob > m_prob_threshold;
|
|
+}
|
|
+
|
|
+void AudioManager::RNNoiseInitialize() {
|
|
+ spdlog::get("audio")->debug("Initializing RNNoise");
|
|
+ RNNoiseUninitialize();
|
|
+ std::unique_lock<std::mutex> _(m_rnn_mutex);
|
|
+ m_rnnoise[0] = rnnoise_create(nullptr);
|
|
+ m_rnnoise[1] = rnnoise_create(nullptr);
|
|
+ const auto expected = rnnoise_get_frame_size();
|
|
+ if (expected != 480) {
|
|
+ spdlog::get("audio")->warn("RNNoise expects a frame count other than 480");
|
|
+ }
|
|
+}
|
|
+
|
|
+void AudioManager::RNNoiseUninitialize() {
|
|
+ if (m_rnnoise[0] != nullptr) {
|
|
+ spdlog::get("audio")->debug("Uninitializing RNNoise");
|
|
+ std::unique_lock<std::mutex> _(m_rnn_mutex);
|
|
+ rnnoise_destroy(m_rnnoise[0]);
|
|
+ rnnoise_destroy(m_rnnoise[1]);
|
|
+ m_rnnoise[0] = nullptr;
|
|
+ m_rnnoise[1] = nullptr;
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+bool AudioManager::OK() const {
|
|
+ return m_ok;
|
|
+}
|
|
+
|
|
+double AudioManager::GetCaptureVolumeLevel() const noexcept {
|
|
+ return m_capture_peak_meter / 32768.0;
|
|
+}
|
|
+
|
|
+double AudioManager::GetSSRCVolumeLevel(uint32_t ssrc) const noexcept {
|
|
+ std::lock_guard<std::mutex> _(m_vol_mtx);
|
|
+ if (const auto it = m_volumes.find(ssrc); it != m_volumes.end()) {
|
|
+ return it->second;
|
|
+ }
|
|
+ return 0.0;
|
|
+}
|
|
+
|
|
+AudioDevices &AudioManager::GetDevices() {
|
|
+ return m_devices;
|
|
+}
|
|
+
|
|
+uint32_t AudioManager::GetRTPTimestamp() const noexcept {
|
|
+ return m_rtp_timestamp;
|
|
+}
|
|
+
|
|
+void AudioManager::SetVADMethod(const std::string &method) {
|
|
+ spdlog::get("audio")->debug("Setting VAD method to {}", method);
|
|
+ if (method == "gate") {
|
|
+ SetVADMethod(VADMethod::Gate);
|
|
+ } else if (method == "rnnoise") {
|
|
+#ifdef WITH_RNNOISE
|
|
+ SetVADMethod(VADMethod::RNNoise);
|
|
+#else
|
|
+ SetVADMethod(VADMethod::Gate);
|
|
+ spdlog::get("audio")->error("Tried to set RNNoise VAD method with support disabled");
|
|
+#endif
|
|
+ } else {
|
|
+ SetVADMethod(VADMethod::Gate);
|
|
+ spdlog::get("audio")->error("Tried to set unknown VAD method {}", method);
|
|
+ }
|
|
+}
|
|
+
|
|
+void AudioManager::SetVADMethod(VADMethod method) {
|
|
+ const auto method_int = static_cast<int>(method);
|
|
+ spdlog::get("audio")->debug("Setting VAD method to enum {}", method_int);
|
|
+ m_vad_method = method;
|
|
+}
|
|
+
|
|
+AudioManager::VADMethod AudioManager::GetVADMethod() const {
|
|
+ return m_vad_method;
|
|
+}
|
|
+
|
|
+#ifdef WITH_RNNOISE
|
|
+float AudioManager::GetCurrentVADProbability() const {
|
|
+ return m_vad_prob;
|
|
+}
|
|
+
|
|
+double AudioManager::GetRNNProbThreshold() const {
|
|
+ return m_prob_threshold;
|
|
+}
|
|
+
|
|
+void AudioManager::SetRNNProbThreshold(double value) {
|
|
+ m_prob_threshold = value;
|
|
+}
|
|
+
|
|
+void AudioManager::SetSuppressNoise(bool value) {
|
|
+ m_enable_noise_suppression = value;
|
|
+}
|
|
+
|
|
+bool AudioManager::GetSuppressNoise() const {
|
|
+ return m_enable_noise_suppression;
|
|
+}
|
|
+#endif
|
|
+
|
|
+void AudioManager::SetMixMono(bool value) {
|
|
+ m_mix_mono = value;
|
|
+}
|
|
+
|
|
+bool AudioManager::GetMixMono() const {
|
|
+ return m_mix_mono;
|
|
+}
|
|
+
|
|
+AudioManager::type_signal_opus_packet AudioManager::signal_opus_packet() {
|
|
+ return m_signal_opus_packet;
|
|
+}
|
|
+
|
|
+#endif /* USE_PORTAUDIO */
|
|
+#endif /* WITH_VOICE */
|
|
--
|
|
2.48.1
|
|
|
|
|
|
From 34c05a18705deaee0583c14898de89cb2c8d5609 Mon Sep 17 00:00:00 2001
|
|
From: zeldakatze <mail@zeldakatze.de>
|
|
Date: Wed, 13 Aug 2025 13:50:28 +0200
|
|
Subject: do resampling for portaudio
|
|
|
|
|
|
diff --git a/src/audio/manager.hpp b/src/audio/manager.hpp
|
|
index a657dcc..2d1ae71 100644
|
|
--- a/src/audio/manager.hpp
|
|
+++ b/src/audio/manager.hpp
|
|
@@ -163,6 +163,14 @@ private:
|
|
PaStream* pa_capture_device;
|
|
PaStreamParameters m_capture_config;
|
|
|
|
+ // portaudio does not do resampling. We have to do that ourselves.
|
|
+ // create target buffers
|
|
+ int16_t *resample_capture_buffer;
|
|
+ int16_t *resample_playback_buffer;
|
|
+ uint32_t resample_capture_source_rate, resample_playback_source_rate;
|
|
+ uint32_t resample_capture_framesPerBuffer;
|
|
+ uint32_t resample_playback_framesPerBuffer;
|
|
+
|
|
#endif
|
|
|
|
|
|
diff --git a/src/audio/portaudioManager.cpp b/src/audio/portaudioManager.cpp
|
|
index bcd1254..f21f9ff 100644
|
|
--- a/src/audio/portaudioManager.cpp
|
|
+++ b/src/audio/portaudioManager.cpp
|
|
@@ -10,19 +10,39 @@
|
|
#include <opus.h>
|
|
#include <cstring>
|
|
|
|
+// TODO replace with libresample
|
|
+// a simple nearest-neigbhor resampler
|
|
+static void resampleBuffer(const int16_t *input, const int inputFrames,
|
|
+ int16_t *output, const int outputFrames) {
|
|
+ float ratio = (float) inputFrames / (float) outputFrames;
|
|
+ for(int oi = 0; oi < outputFrames; oi++) {
|
|
+ int ii = (int) (((float) oi) * ratio);
|
|
+
|
|
+ // clamp the input index
|
|
+ if(ii >= inputFrames) {
|
|
+ ii = inputFrames - 1;
|
|
+ }
|
|
+
|
|
+ // copy the samples. * 2 because stereo
|
|
+ output[oi * 2] = input[ii * 2];
|
|
+ output[oi * 2 + 1] = input[ii * 2 + 1];
|
|
+ }
|
|
+}
|
|
+
|
|
int playCallback(const void *pInput, void *pOutput,
|
|
unsigned long frameCount,
|
|
const PaStreamCallbackTimeInfo* timeInfo,
|
|
PaStreamCallbackFlags statusFlags,
|
|
void *userData ) {
|
|
AudioManager *mgr = reinterpret_cast<AudioManager *>(userData);
|
|
- if (mgr == nullptr) {printf("NULL\n");return paContinue;};
|
|
+ if (mgr == nullptr) {return paContinue;};
|
|
std::lock_guard<std::mutex> _(mgr->m_mutex);
|
|
|
|
- auto *pOutputF32 = static_cast<int16_t *>(pOutput);
|
|
+ int16_t *resample_playback_buffer = mgr->resample_playback_buffer;
|
|
+ auto *pOutputF32 = static_cast<int16_t *>(resample_playback_buffer);
|
|
|
|
/* clear the output buffer */
|
|
- for(int i = 0; i<frameCount * 2; i++)
|
|
+ for(int i = 0; i<480 * 2; i++)
|
|
pOutputF32[i] = 0;
|
|
|
|
/* write every SSRC into the output stream */
|
|
@@ -32,7 +52,7 @@ int playCallback(const void *pInput, void *pOutput,
|
|
volume = vol_it->second;
|
|
}
|
|
auto &buf = pair.first;
|
|
- const size_t n = std::min(static_cast<size_t>(buf.size()), static_cast<size_t>(frameCount * 2ULL));
|
|
+ const size_t n = std::min(static_cast<size_t>(buf.size()), static_cast<size_t>(480 * 2ULL));
|
|
for (size_t i = 0; i < n; i++) {
|
|
pOutputF32[i] += (int16_t) (volume * buf[i]);
|
|
//pOutputF32[i] += volume * buf[i] / 32768.F;
|
|
@@ -40,6 +60,9 @@ int playCallback(const void *pInput, void *pOutput,
|
|
buf.erase(buf.begin(), buf.begin() + n);
|
|
}
|
|
|
|
+ /* scale the resample buffer to the output buffer */
|
|
+ resampleBuffer(resample_playback_buffer, 480, static_cast<int16_t *>(pOutput), frameCount);
|
|
+
|
|
return paContinue;
|
|
}
|
|
|
|
@@ -51,7 +74,11 @@ int recordCallback(const void *pInput, void *pOutput,
|
|
auto *mgr = reinterpret_cast<AudioManager *>(userData);
|
|
if (mgr == nullptr) return paContinue;
|
|
|
|
- mgr->OnCapturedPCM(static_cast<const int16_t *>(pInput), frameCount);
|
|
+ int16_t *resample_capture_buffer = mgr->resample_capture_buffer;
|
|
+
|
|
+ resampleBuffer(static_cast<const int16_t *>(pInput), frameCount, resample_capture_buffer, 480);
|
|
+
|
|
+ mgr->OnCapturedPCM(static_cast<const int16_t *>(resample_capture_buffer), 480);
|
|
|
|
/*
|
|
* You can simply increment it by 480 in UDPSocket::SendEncrypted but this is wrong
|
|
@@ -103,22 +130,30 @@ AudioManager::AudioManager(const Glib::ustring &backends_string)
|
|
m_playback_config.suggestedLatency = Pa_GetDeviceInfo( m_capture_config.device )->defaultLowInputLatency;
|
|
m_playback_config.hostApiSpecificStreamInfo = NULL;
|
|
m_playback_config.device = Pa_GetDefaultOutputDevice();
|
|
+ resample_playback_source_rate = Pa_GetDeviceInfo( m_playback_config.device )->defaultSampleRate;
|
|
+ resample_playback_framesPerBuffer = (int) (((float) resample_playback_source_rate / 48000.0) * 480.0);
|
|
|
|
m_capture_config.sampleFormat = paInt16;
|
|
m_capture_config.channelCount = 2;
|
|
m_capture_config.suggestedLatency = Pa_GetDeviceInfo( m_capture_config.device )->defaultLowInputLatency;
|
|
m_capture_config.hostApiSpecificStreamInfo = NULL;
|
|
m_capture_config.device = Pa_GetDefaultInputDevice();
|
|
+ resample_capture_source_rate = Pa_GetDeviceInfo( m_capture_config.device )->defaultSampleRate;
|
|
+ resample_capture_framesPerBuffer = (int) (((float) resample_capture_source_rate / 48000.0) * 480.0);
|
|
+
|
|
+ /* initialize the buffers for resampling */
|
|
+ resample_capture_buffer = (int16_t*) malloc(480 * 2 * sizeof(uint16_t));
|
|
+ resample_playback_buffer = (int16_t*) malloc(480 * 2 * sizeof(uint16_t));
|
|
|
|
/* create the streams */
|
|
- pa_err = Pa_OpenStream(&pa_capture_device, &m_capture_config, NULL, 48000, 480, paClipOff, recordCallback, this);
|
|
+ pa_err = Pa_OpenStream(&pa_capture_device, &m_capture_config, NULL, resample_capture_source_rate, resample_capture_framesPerBuffer, paClipOff, recordCallback, this);
|
|
if(pa_err != paNoError) {
|
|
m_ok = false;
|
|
spdlog::get("audio")->error("ERROR: Pa_OpenStream for capture returned %d\n", pa_err);
|
|
return;
|
|
}
|
|
|
|
- pa_err = Pa_OpenStream(&pa_playback_device, NULL, &m_playback_config, 48000, 480, paClipOff, playCallback, this);
|
|
+ pa_err = Pa_OpenStream(&pa_playback_device, NULL, &m_playback_config, resample_playback_source_rate, resample_playback_framesPerBuffer, paClipOff, playCallback, this);
|
|
if(pa_err != paNoError) {
|
|
m_ok = false;
|
|
spdlog::get("audio")->error("ERROR: Pa_OpenStream for playback returned %d\n", pa_err);
|
|
@@ -140,6 +175,9 @@ AudioManager::AudioManager(const Glib::ustring &backends_string)
|
|
AudioManager::~AudioManager() {
|
|
Pa_Terminate();
|
|
RemoveAllSSRCs();
|
|
+
|
|
+ free(resample_capture_buffer);
|
|
+ free(resample_playback_buffer);
|
|
|
|
#ifdef WITH_RNNOISE
|
|
RNNoiseUninitialize();
|
|
--
|
|
2.48.1
|
|
|