summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2025-06-26 14:18:07 -0400
committerJonathan Bradley <jcb@pikum.xyz>2025-06-26 14:18:07 -0400
commit1c87a0e431d30aaf19195f8a45c7607add21018a (patch)
treedcae1b86647e87757a0e2c6804fa6ac7de0862b9 /src
parent73795506229111ac8b65082921d563e53b5bdecd (diff)
pke: audio: auto-connect to default sink
Diffstat (limited to 'src')
-rw-r--r--src/audio-impl-pw.cpp77
-rw-r--r--src/audio-impl-pw.hpp1
-rw-r--r--src/audio.cpp8
-rw-r--r--src/audio.hpp2
-rw-r--r--src/game.cpp2
5 files changed, 87 insertions, 3 deletions
diff --git a/src/audio-impl-pw.cpp b/src/audio-impl-pw.cpp
index ba6760e..4c0d909 100644
--- a/src/audio-impl-pw.cpp
+++ b/src/audio-impl-pw.cpp
@@ -1,6 +1,7 @@
#include "audio-impl-pw.hpp"
#include "audio-types.hpp"
+#include "game-settings.hpp"
#include "pipewire/keys.h"
#include "pipewire/thread-loop.h"
#include "pk.h"
@@ -176,6 +177,80 @@ void pke_audio_pw_teardown() {
pke_audio_mstr.mtx_buffer.unlock();
}
+bool pke_audio_pw_remap_outputs() {
+ uint32_t stream_id;
+ uint32_t i;
+ uint32_t port_count = {0};
+ void *created_obj = {0};
+ pw_properties *props = pw_properties_new(NULL, NULL);
+ pke_audio_pw_object *objs = (pke_audio_pw_object *)pke_audio_pw.pw_objects.data;
+ const char *str;
+ uint32_t *out_ports = pk_new<uint32_t>(pke_audio_mstr.channel_count, pkeSettings.mem_bkt.game_transient);
+ uint32_t *in_ports = pk_new<uint32_t>(pke_audio_mstr.channel_count, pkeSettings.mem_bkt.game_transient);
+
+ pw_thread_loop_lock(pke_audio_pw.loop);
+
+ // remove all existing links
+ {
+ for (i = pke_audio_pw.created_objects.next; i > 0; --i) {
+ pw_core_destroy(pke_audio_pw.core, pke_audio_pw.created_objects[i - 1]);
+ }
+ pk_arr_clear(&pke_audio_pw.created_objects);
+ }
+
+ // find node we're going to connect to
+ // + build out_ports and in_ports
+ {
+ stream_id = pw_stream_get_node_id(pke_audio_pw.stream);
+ for (i = 0; i < pke_audio_pw.pw_objects.next; ++i) {
+ if (objs[i].type != PKE_AUDIO_PW_OBJECT_TYPE_NODE) continue;
+ if ((str = spa_dict_lookup(&objs[i].props->dict, "node.name")), str == NULL) continue;
+ if (strcmp(pke_audio_pw.default_sink_name, str) == 0) {
+ pke_audio_pw.default_sink_id = objs[i].id;
+ break;
+ }
+ }
+ // assert(stream_id != 0);
+ if (stream_id == 0) {
+ pw_properties_free(props);
+ pw_thread_loop_unlock(pke_audio_pw.loop);
+ return false;
+ }
+ for (i = 0; i < pke_audio_pw.pw_objects.next; ++i) {
+ if (objs[i].type != PKE_AUDIO_PW_OBJECT_TYPE_PORT) continue;
+ if (objs[i].data.port.node != pke_audio_pw.default_sink_id && objs[i].data.port.node != stream_id) continue;
+ if (objs[i].data.port.node == pke_audio_pw.default_sink_id && objs[i].data.port.direction == PW_DIRECTION_OUTPUT) continue;
+ if (objs[i].data.port.node == stream_id) {
+ out_ports[objs[i].data.port.id] = objs[i].id;
+ } else {
+ port_count++;
+ in_ports[objs[i].data.port.id] = objs[i].id;
+ }
+ }
+ }
+ // assert(port_count != 0);
+ if (port_count == 0) {
+ pw_properties_free(props);
+ pw_thread_loop_unlock(pke_audio_pw.loop);
+ return false;
+ }
+
+ // do links
+ {
+ for (i = 0; i < pke_audio_mstr.channel_count; ++i) {
+ pw_properties_clear(props);
+ pw_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%d", out_ports[i]);
+ pw_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%d", in_ports[i % port_count]);
+ created_obj = pw_core_create_object(pke_audio_pw.core, "link-factory", PW_TYPE_INTERFACE_Link, PW_VERSION_LINK, &props->dict, 0);
+ if (created_obj != NULL) pk_arr_append(&pke_audio_pw.created_objects, created_obj);
+ }
+ }
+
+ pw_properties_free(props);
+ pw_thread_loop_unlock(pke_audio_pw.loop);
+ return true;
+}
+
void on_pipewire_process(void *user_data) {
(void)user_data;
pw_buffer *b;
@@ -242,7 +317,7 @@ int metadata_event_property(void *data, uint32_t subject, const char *key, const
// the default just changed??
pke_audio_pw.is_needing_output_remapped = true;
}
- if (value == NULL || spa_json_str_object_find(value, strlen(value), "name", pke_audio_pw.default_sink_name, sizeof(pke_audio_pw.default_sink_name)) < 0) {
+ if (value == NULL || spa_json_str_object_find(value, strlen(value), "name", pke_audio_pw.default_sink_name, PKE_AUDIO_PW_NAME_RESERVE_LEN) < 0) {
pke_audio_pw.default_sink_name[0] = '\0';
}
}
diff --git a/src/audio-impl-pw.hpp b/src/audio-impl-pw.hpp
index 2eb9916..d9bac99 100644
--- a/src/audio-impl-pw.hpp
+++ b/src/audio-impl-pw.hpp
@@ -63,6 +63,7 @@ struct pke_audio_implementation_pipewire {
extern struct pke_audio_implementation_pipewire pke_audio_pw;
void pke_audio_pw_init();
void pke_audio_pw_teardown();
+bool pke_audio_pw_remap_outputs();
void on_pipewire_process(void *user_data);
void on_pipewire_stream_param_changed(void *, uint32_t id, const struct spa_pod *param);
void on_registry_event_global(void *data, uint32_t id, uint32_t permissions, const char *type, uint32_t version, const struct spa_dict *props);
diff --git a/src/audio.cpp b/src/audio.cpp
index 002d1be..c3f152e 100644
--- a/src/audio.cpp
+++ b/src/audio.cpp
@@ -17,7 +17,13 @@ void pke_audio_teardown() {
pke_audio_pw_teardown();
#endif
}
-void pke_audio_tick() {
+void pke_audio_tick(double delta) {
+ (void)delta;
+#ifdef PKE_AUDIO_IMPL_PIPEWIRE
+ if (pke_audio_pw.is_needing_output_remapped == true) {
+ pke_audio_pw.is_needing_output_remapped = !pke_audio_pw_remap_outputs();
+ }
+#endif
}
float pke_audio_get_volume(pke_audio_source source) {
diff --git a/src/audio.hpp b/src/audio.hpp
index 7ba0f1d..3084c84 100644
--- a/src/audio.hpp
+++ b/src/audio.hpp
@@ -7,7 +7,7 @@
void pke_audio_init();
void pke_audio_teardown();
-void pke_audio_tick();
+void pke_audio_tick(double delta);
float pke_audio_get_volume(pke_audio_source source);
void pke_audio_set_volume(pke_audio_source source, float volume);
diff --git a/src/game.cpp b/src/game.cpp
index 25473ed..67ca5fb 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -110,6 +110,8 @@ void Game_Tick(double delta) {
PkeCamera_Tick(delta);
+ pke_audio_tick(delta);
+
EntityType_Tick_Late(delta);
ECS_Tick_Late(delta);
}