summaryrefslogtreecommitdiff
path: root/src/libsink
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2025-04-28 14:11:28 -0400
committerAndrew Opalach <andrew@akon.city> 2025-04-28 14:11:28 -0400
commitc8c0af300dde117a839f2d5d6b777694d7f118e9 (patch)
tree3297f1ba256da0b837fbaa9c581d7f420ce98a4a /src/libsink
parent61492af23ecc5dd9ab4956a84a7c0451deb8b84b (diff)
downloadcamu-c8c0af300dde117a839f2d5d6b777694d7f118e9.tar.gz
camu-c8c0af300dde117a839f2d5d6b777694d7f118e9.tar.bz2
camu-c8c0af300dde117a839f2d5d6b777694d7f118e9.zip
Minor restructure and dependency updates
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/libsink')
-rw-r--r--src/libsink/desktop.c265
-rw-r--r--src/libsink/desktop.h26
-rw-r--r--src/libsink/input_simulator.c68
-rw-r--r--src/libsink/input_simulator.h10
-rw-r--r--src/libsink/meson.build5
5 files changed, 374 insertions, 0 deletions
diff --git a/src/libsink/desktop.c b/src/libsink/desktop.c
new file mode 100644
index 0000000..2bbe906
--- /dev/null
+++ b/src/libsink/desktop.c
@@ -0,0 +1,265 @@
+#define AL_LOG_SECTION "desktop"
+#include <al/log.h>
+
+//#define DESKTOP_NULL_AUDIO
+
+#ifdef DESKTOP_NULL_AUDIO
+#include "../mixer/audio_null.h"
+#else
+#include "../mixer/audio_miniaudio.h"
+#endif
+#if defined CAMU_RENDERER_TIGER
+#include "../render/renderer_tiger.h"
+#elif defined CAMU_RENDERER_LIBPLACEBO
+#include "../render/renderer_libplacebo.h"
+#endif
+#include "../util/print_time.h"
+
+#include "desktop.h"
+#ifdef CAMU_SCREEN_DEBUG_KEY
+#include "input_simulator.h"
+#endif
+
+static u8 sink_callback(void *userdata, u8 op, u8 type, void *opaque)
+{
+ struct camu_desktop *c = (struct camu_desktop *)userdata;
+ struct camu_mixer *mixer = &c->mixer;
+ struct camu_screen *scr = &c->scr;
+ switch (op) {
+ case CAMU_SINK_ADD_BUFFER:
+ switch (type) {
+ case CAMU_SINK_AUDIO: {
+ struct camu_audio_buffer *buf = (struct camu_audio_buffer *)opaque;
+ camu_mixer_add_buffer(mixer, buf);
+ log_debug("Audio buffer added.");
+ break;
+ }
+ case CAMU_SINK_VIDEO: {
+ struct camu_video_buffer *buf = (struct camu_video_buffer *)opaque;
+ camu_screen_add_buffer(scr, buf);
+ log_debug("Video buffer added.");
+ break;
+ }
+ }
+ break;
+ case CAMU_SINK_REMOVE_BUFFER:
+ switch (type) {
+ case CAMU_SINK_AUDIO: {
+ struct camu_audio_buffer *buf = (struct camu_audio_buffer *)opaque;
+ camu_mixer_remove_buffer(mixer, buf);
+ log_debug("Audio buffer removed.");
+ break;
+ }
+ case CAMU_SINK_VIDEO: {
+ struct camu_video_buffer *buf = (struct camu_video_buffer *)opaque;
+ camu_screen_remove_buffer(scr, buf);
+ log_debug("Video buffer removed.");
+ break;
+ }
+ }
+ break;
+ case CAMU_SINK_START:
+ switch (type) {
+ case CAMU_SINK_AUDIO:
+ camu_mixer_resume(mixer);
+ log_info("Audio started.");
+ break;
+ case CAMU_SINK_VIDEO:
+ camu_screen_set_state(scr, CAMU_SCREEN_PLAYING);
+ camu_screen_wake(scr);
+ log_info("Video started.");
+ break;
+ }
+ break;
+ case CAMU_SINK_STOP:
+ switch (type) {
+ case CAMU_SINK_AUDIO:
+ camu_mixer_pause(mixer);
+ log_info("Audio stopped.");
+ break;
+ case CAMU_SINK_VIDEO:
+ camu_screen_set_state(scr, CAMU_SCREEN_PAUSED);
+ log_info("Video stopped.");
+ break;
+ }
+ break;
+ case CAMU_SINK_REFRESH_VIDEO:
+ camu_screen_force_refresh(scr);
+ break;
+ case CAMU_SINK_CLEAR:
+ switch (type) {
+ case CAMU_SINK_AUDIO:
+ camu_mixer_clear(mixer);
+ break;
+ case CAMU_SINK_VIDEO:
+ camu_screen_clear(scr);
+ break;
+ }
+ break;
+ case CAMU_SINK_MOCK_CLOSE:
+ c->should_quit = 1;
+ camu_screen_wake(&c->scr);
+ break;
+ case CAMU_SINK_EXIT:
+ camu_sink_close(&c->sink);
+ if (c->exit_callback) {
+ c->exit_callback(c->userdata, c);
+ }
+ break;
+ }
+ return CAMU_SINK_OK;
+}
+
+static char status[64];
+static void log_playback_pos(struct camu_sink *sink)
+{
+ struct camu_sink_entry *current = camu_sink_get_current(sink);
+ if (!current) {
+ camu_sink_return_current(sink);
+ goto notplaying;
+ }
+
+ u64 pts = camu_clock_get_last_pts(&current->clock) * 1000000u;
+ u64 duration = current->client.duration;
+ pts = MIN(duration, pts);
+ bool paused = current->paused;
+
+ camu_sink_return_current(sink);
+
+ u64 remaining = duration - pts;
+ if (remaining == 0) {
+ goto notplaying;
+ }
+
+ bool show_hour = duration > 60u * 60u * 1000000u;
+ s32 offset = al_snprintf(status, sizeof(status), paused ? "⏸ " : "⏵ ");
+ offset += al_snprintf(status + offset, sizeof(status) - offset, "[");
+ offset += camu_print_time(status + offset, sizeof(status) - offset, pts, show_hour, 2);
+ offset += al_snprintf(status + offset, sizeof(status) - offset, "/");
+ offset += camu_print_time(status + offset, sizeof(status) - offset, duration, show_hour, 2);
+ offset += al_snprintf(status + offset, sizeof(status) - offset, "]");
+ status[offset] = '\0';
+
+ log_info("%s", status);
+ return;
+notplaying:
+ log_info("Not playing.");
+}
+
+static void screen_callback(void *userdata, u8 op, void *opaque)
+{
+ struct camu_desktop *c = (struct camu_desktop *)userdata;
+ switch (op) {
+ case CAMU_SCREEN_SET_VOLUME:
+ camu_mixer_set_volume(&c->mixer, *(f32 *)opaque);
+ break;
+ case CAMU_SCREEN_OFFSET_VOLUME:
+ camu_mixer_offset_volume(&c->mixer, *(f32 *)opaque);
+ break;
+ case CAMU_SCREEN_SKIP:
+ camu_sink_skip(&c->sink, *(s32 *)opaque);
+ break;
+ case CAMU_SCREEN_TOGGLE_PAUSE:
+ camu_sink_toggle_pause(&c->sink);
+ break;
+ case CAMU_SCREEN_PERCENT_SEEK:
+ camu_sink_seek(&c->sink, opaque, CAMU_SEEK_PERCENT);
+ break;
+ case CAMU_SCREEN_RELATIVE_SEEK:
+ camu_sink_seek(&c->sink, opaque, CAMU_SEEK_RELATIVE);
+ break;
+ case CAMU_SCREEN_RESEEK:
+ camu_sink_reseek(&c->sink);
+ break;
+ case CAMU_SCREEN_SHUFFLE:
+ camu_sink_shuffle(&c->sink);
+ break;
+ case CAMU_SCREEN_STATUS:
+ log_playback_pos(&c->sink);
+ break;
+#ifdef CAMU_SCREEN_DEBUG_KEY
+ case CAMU_SCREEN_DEBUG:
+ if (camu_input_simulator_running()) {
+ camu_input_simulator_stop();
+ } else {
+ camu_input_simulator_run(&c->sink);
+ }
+ break;
+#endif
+ case CAMU_SCREEN_CLOSE:
+ c->should_quit = 1;
+ break;
+ }
+}
+
+bool camu_desktop_open(struct camu_desktop *c, const char *window_name)
+{
+ c->scr.callback = screen_callback;
+ c->scr.userdata = c;
+ if (!camu_screen_init(&c->scr) || !camu_screen_create_window(&c->scr, window_name)) {
+ return false;
+ }
+#if defined CAMU_RENDERER_TIGER
+ c->renderer = camu_renderer_tiger_create();
+#elif defined CAMU_RENDERER_LIBPLACEBO
+ c->renderer = camu_renderer_lp_create();
+#endif
+ if (!camu_screen_create_renderer(&c->scr, c->renderer)) {
+ return false;
+ }
+ c->renderer->render(c->renderer, &c->scr, true);
+#ifdef DESKTOP_NULL_AUDIO
+ if (!camu_mixer_init(&c->mixer, (struct camu_audio *)&audio_plugin_null)) {
+#else
+ if (!camu_mixer_init(&c->mixer, camu_audio_miniaudio_create())) {
+#endif
+ return false;
+ }
+ if (!c->mixer.audio->configure_stream(c->mixer.audio, NULL)) {
+ return false;
+ }
+ camu_mixer_pick_format(&c->mixer, &c->mixer.fmt);
+ c->should_quit = 0;
+ c->exit_callback = NULL;
+ c->userdata = NULL;
+ return true;
+}
+
+bool camu_desktop_connect(struct camu_desktop *c, str *name, struct nn_event_loop *loop, u8 type, str *addr, u16 port)
+{
+ c->loop = loop;
+ camu_sink_init(&c->sink, c->loop, &c->mixer, c->renderer);
+ al_str_from(&c->sink.default_list, "default");
+ c->sink.callback = sink_callback;
+ c->sink.userdata = c;
+ return camu_sink_connect(&c->sink, name, type, addr, port);
+}
+
+bool camu_desktop_tick(struct camu_desktop *c)
+{
+ bool force;
+ if (!c->should_quit && camu_screen_tick(&c->scr, &force)) {
+ c->renderer->render(c->renderer, &c->scr, force);
+ }
+ return !c->should_quit;
+}
+
+void camu_desktop_stop(struct camu_desktop *c)
+{
+#ifdef CAMU_SCREEN_DEBUG_KEY
+ if (camu_input_simulator_running()) {
+ camu_input_simulator_stop();
+ }
+#endif
+ // We have already stopped ticking the screen at this point.
+ camu_screen_clear(&c->scr);
+ camu_sink_stop(&c->sink);
+}
+
+void camu_desktop_free(struct camu_desktop *c)
+{
+ camu_sink_free(&c->sink);
+ camu_mixer_close(&c->mixer);
+ c->renderer->free(&c->renderer);
+ camu_screen_close(&c->scr);
+}
diff --git a/src/libsink/desktop.h b/src/libsink/desktop.h
new file mode 100644
index 0000000..279fd7b
--- /dev/null
+++ b/src/libsink/desktop.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <al/types.h>
+#include <al/str.h>
+#include <nnwt/event_loop.h>
+
+#include "../libsink/sink.h"
+
+#include "../screen/screen.h"
+
+struct camu_desktop {
+ struct nn_event_loop *loop;
+ s32 should_quit;
+ struct camu_screen scr;
+ struct camu_renderer *renderer;
+ struct camu_mixer mixer;
+ struct camu_sink sink;
+ void (*exit_callback)(void *, struct camu_desktop *);
+ void *userdata;
+};
+
+bool camu_desktop_open(struct camu_desktop *c, const char *window_name);
+bool camu_desktop_connect(struct camu_desktop *c, str *name, struct nn_event_loop *loop, u8 type, str *addr, u16 port);
+bool camu_desktop_tick(struct camu_desktop *c);
+void camu_desktop_stop(struct camu_desktop *c);
+void camu_desktop_free(struct camu_desktop *c);
diff --git a/src/libsink/input_simulator.c b/src/libsink/input_simulator.c
new file mode 100644
index 0000000..80fde02
--- /dev/null
+++ b/src/libsink/input_simulator.c
@@ -0,0 +1,68 @@
+#include "../screen/screen.h"
+#ifdef CAMU_SCREEN_DEBUG_KEY
+#include <nnwt/thread.h>
+#include <al/random.h>
+
+#include "input_simulator.h"
+
+// This is ignoring all thread-safety.
+static s32 quit = 1;
+static struct nn_thread thread;
+
+enum {
+ SEEK,
+ SKIP,
+ BACKSKIP,
+ TOGGLE_PAUSE,
+ SHUFFLE,
+ MARK, // count
+};
+
+static nn_thread_result NNWT_THREADCALL input_simulation_thread(void *userdata)
+{
+ struct camu_sink *sink = (struct camu_sink *)userdata;
+ while (!quit) {
+ nn_thread_sleep(NNWT_TS_FROM_USEC(30000));
+ switch (al_rand() % MARK) {
+ case SKIP:
+ camu_sink_skip(sink, (al_rand() % 5));
+ break;
+ case BACKSKIP:
+ camu_sink_skip(sink, -(al_rand() % 5));
+ break;
+ case SHUFFLE:
+ camu_sink_shuffle(sink);
+ break;
+ case TOGGLE_PAUSE:
+ camu_sink_toggle_pause(sink);
+ break;
+ case SEEK: {
+ f64 pos = al_rand() / (f64)AL_RAND_MAX;
+ if (pos < 0.005) pos = 0.0;
+ if (pos > 0.995) pos = 1.0;
+ else if (pos > 0.99) pos = .9999;
+ camu_sink_seek(sink, &pos, CAMU_SEEK_PERCENT);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+void camu_input_simulator_run(struct camu_sink *sink)
+{
+ quit = 0;
+ nn_thread_create(&thread, input_simulation_thread, sink);
+}
+
+bool camu_input_simulator_running(void)
+{
+ return quit == 0;
+}
+
+void camu_input_simulator_stop(void)
+{
+ quit = 1;
+ nn_thread_join(&thread);
+}
+#endif
diff --git a/src/libsink/input_simulator.h b/src/libsink/input_simulator.h
new file mode 100644
index 0000000..e05b03e
--- /dev/null
+++ b/src/libsink/input_simulator.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "../screen/screen.h"
+#ifdef CAMU_SCREEN_DEBUG_KEY
+#include "../libsink/sink.h"
+
+void camu_input_simulator_run(struct camu_sink *sink);
+bool camu_input_simulator_running(void);
+void camu_input_simulator_stop(void);
+#endif
diff --git a/src/libsink/meson.build b/src/libsink/meson.build
index a0805af..153e371 100644
--- a/src/libsink/meson.build
+++ b/src/libsink/meson.build
@@ -9,3 +9,8 @@ else
endif
libsink = declare_dependency(sources: libsink_src, dependencies: libsink_deps, compile_args: libsink_args)
+
+desktop_src = ['desktop.c', 'input_simulator.c']
+desktop_deps = [libsink]
+desktop_args = ['-DCAMU_MIXER_THREADED', '-DCAMU_SCREEN_THREADED']
+desktop = declare_dependency(sources: desktop_src, dependencies: desktop_deps, compile_args: desktop_args)