summaryrefslogtreecommitdiff
path: root/src/list
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2024-04-09 11:24:01 -0400
committerAndrew Opalach <andrew@akon.city> 2024-04-09 11:24:01 -0400
commit02f3d3565602146bbbfce85b2719246f24036cb9 (patch)
treec6588ffe297b777e36260effa4fa42b958ca6ba3 /src/list
parentbbf3314165182e402ff25acccddc004a87f81ef0 (diff)
downloadcamu-02f3d3565602146bbbfce85b2719246f24036cb9.tar.gz
camu-02f3d3565602146bbbfce85b2719246f24036cb9.tar.bz2
camu-02f3d3565602146bbbfce85b2719246f24036cb9.zip
Massive restructure and many changes
- The server-side list concept is still a wip Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/list')
-rw-r--r--src/list/list.c172
-rw-r--r--src/list/list.h54
-rw-r--r--src/list/meson.build5
3 files changed, 231 insertions, 0 deletions
diff --git a/src/list/list.c b/src/list/list.c
new file mode 100644
index 0000000..3af75e4
--- /dev/null
+++ b/src/list/list.c
@@ -0,0 +1,172 @@
+#include <al/random.h>
+#include <aki/thread.h>
+
+#include "../libsink/common.h"
+
+#include "list.h"
+
+void camu_list_init(struct camu_list *list, str *name)
+{
+ al_str_clone(&list->name, name);
+ list->current = -1;
+ list->idle = true;
+ list->sequence = 0;
+ al_array_init(list->entries);
+ al_array_init(list->sinks);
+}
+
+static void camu_list_pump(struct camu_list *list)
+{
+ s32 size = (s32)list->entries.size;
+ if (list->current == -1 || size <= list->current) return;
+ struct camu_list_entry *entry = al_array_at(list->entries, list->current);
+ struct camu_list_entry *upcoming = NULL;
+ if (list->current + 1 < size) {
+ upcoming = al_array_at(list->entries, list->current + 1);
+ }
+ struct camu_list_sink *sink;
+ al_array_foreach(list->sinks, i, sink) {
+ if (sink->set != list->current) {
+ sink->callback(sink->userdata, CAMU_SINK_SET, entry->opaque, entry->sequence);
+ sink->set = list->current;
+ }
+ if (upcoming) {
+ sink->callback(sink->userdata, CAMU_SINK_QUEUE, upcoming->opaque, upcoming->sequence);
+ }
+ }
+}
+
+void camu_list_add_sink(struct camu_list *list, void (*callback)(void *, u8, void *, s32),
+ void *userdata)
+{
+ struct camu_list_sink *sink = al_alloc_object(struct camu_list_sink);
+ sink->set = -1;
+ sink->callback = callback;
+ sink->userdata = userdata;
+ al_array_push(list->sinks, sink);
+ camu_list_pump(list);
+}
+
+void camu_list_remove_sink(struct camu_list *list, void *userdata)
+{
+ struct camu_list_sink *sink;
+ al_array_foreach(list->sinks, i, sink) {
+ if (sink->userdata == userdata) {
+ al_array_remove_at_iter(list->sinks, i);
+ break;
+ }
+ }
+}
+
+void camu_list_add(struct camu_list *list, void *opaque, void (*callback)(void *, u8, void *),
+ u64 duration, bool set)
+{
+ s32 size = (s32)list->entries.size;
+ struct camu_list_entry *entry = al_alloc_object(struct camu_list_entry);
+ entry->sequence = list->sequence++;
+ entry->opaque = opaque;
+ entry->callback = callback;
+ entry->duration = duration;
+ entry->start = aki_get_timestamp();
+ if (!list->idle) {
+ struct camu_list_entry *prev = al_array_at(list->entries, size - 1);
+ u64 end = prev->start + prev->duration;
+ if (end > entry->start) {
+ u64 diff = end - entry->start;
+ entry->start += diff;
+ }
+ }
+ al_array_push(list->entries, entry);
+ if (set) list->current = size;
+ else if (list->idle) {
+ list->current++;
+ list->idle = false;
+ }
+ camu_list_pump(list);
+ struct camu_list_timing timing = {
+ .start = entry->start
+ };
+ entry->callback(entry->opaque, CAMU_LIST_ENTRY_IMPULSE, &timing);
+}
+
+static s32 index_of_entry_from_sequence(struct camu_list *list, s32 sequence)
+{
+ if (sequence != CAMU_SEQUENCE_INVALID) {
+ struct camu_list_entry *entry;
+ al_array_foreach(list->entries, i, entry) {
+ if (entry->sequence == sequence) return i;
+ }
+ }
+ return CAMU_SEQUENCE_INVALID;
+}
+
+void camu_list_skip(struct camu_list *list, s32 sequence, s32 n)
+{
+ s32 size = (s32)list->entries.size;
+ s32 index = index_of_entry_from_sequence(list, sequence);
+ if (index == CAMU_SEQUENCE_INVALID) index = list->current;
+ if (index + n < 0 || index + n >= size || index + n == list->current) return;
+ list->current = index + n;
+ struct camu_list_entry *entry = al_array_at(list->entries, list->current);
+ struct camu_list_timing timing = {
+ .start = aki_get_timestamp()
+ };
+ entry->callback(entry->opaque, CAMU_LIST_ENTRY_IMPULSE, &timing);
+ camu_list_pump(list);
+}
+
+void camu_list_skipto(struct camu_list *list, s32 i)
+{
+ s32 size = (s32)list->entries.size;
+ if (i < 0 || i >= size) return;
+ list->current = i;
+ camu_list_pump(list);
+}
+
+void camu_list_toggle_pause(struct camu_list *list, s32 sequence, u64 pos)
+{
+ s32 index = index_of_entry_from_sequence(list, sequence);
+ if (index == CAMU_SEQUENCE_INVALID) return;
+ struct camu_list_entry *entry = al_array_at(list->entries, index);
+ entry->callback(entry->opaque, CAMU_LIST_ENTRY_TOGGLE_PAUSE, &pos);
+}
+
+void camu_list_seek(struct camu_list *list, s32 sequence, f64 percent)
+{
+ s32 index = index_of_entry_from_sequence(list, sequence);
+ if (index == CAMU_SEQUENCE_INVALID) return;
+ list->idle = false;
+ struct camu_list_entry *entry = al_array_at(list->entries, index);
+ u64 pos = percent * entry->duration;
+ entry->callback(entry->opaque, CAMU_LIST_ENTRY_SEEK, &pos);
+}
+
+void camu_list_finished(struct camu_list *list, s32 sequence)
+{
+ s32 size = (s32)list->entries.size;
+ s32 index = index_of_entry_from_sequence(list, sequence);
+ if (index == CAMU_SEQUENCE_INVALID) return;
+ if (index + 1 >= size) {
+ list->idle = true;
+ return;
+ }
+ list->current = index + 1;
+ camu_list_pump(list);
+}
+
+void camu_list_shuffle(struct camu_list *list)
+{
+ u32 size = list->entries.size;
+ if (size == 0) return;
+ for (u32 i = 0; i < size - 1; i++) {
+ u32 j = i + al_rand() / (AL_RAND_MAX / (size - i) + 1);
+ struct camu_list_entry *tmp = al_array_at(list->entries, j);
+ al_array_at(list->entries, j) = al_array_at(list->entries, i);
+ al_array_at(list->entries, i) = tmp;
+ }
+ struct camu_list_sink *sink;
+ al_array_foreach(list->sinks, i, sink) {
+ sink->set = -1;
+ }
+ camu_list_pump(list);
+}
diff --git a/src/list/list.h b/src/list/list.h
new file mode 100644
index 0000000..edd0218
--- /dev/null
+++ b/src/list/list.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <al/str.h>
+#include <al/array.h>
+
+#define CAMU_SEQUENCE_INVALID INT32_MAX
+
+enum {
+ CAMU_LIST_ENTRY_IMPULSE = 0,
+ CAMU_LIST_ENTRY_TOGGLE_PAUSE,
+ CAMU_LIST_ENTRY_SEEK
+};
+
+struct camu_list_timing {
+ u64 start;
+};
+
+struct camu_list_entry {
+ s32 sequence;
+ void *opaque;
+ void (*callback)(void *, u8, void *);
+ u64 duration;
+ u64 start;
+ u64 paused_at;
+};
+
+struct camu_list_sink {
+ s32 set;
+ bool (*action)(void *, u8, void *);
+ void (*callback)(void *, u8, void *, s32);
+ void *userdata;
+};
+
+struct camu_list {
+ str name;
+ s32 current;
+ bool idle;
+ s32 sequence; // Next entry sequence.
+ array(struct camu_list_entry *) entries;
+ array(struct camu_list_sink *) sinks;
+};
+
+void camu_list_init(struct camu_list *list, str *name);
+void camu_list_add_sink(struct camu_list *list, void (*callback)(void *, u8, void *, s32),
+ void *userdata);
+void camu_list_remove_sink(struct camu_list *list, void *userdata);
+void camu_list_add(struct camu_list *list, void *opaque, void (*callback)(void *, u8, void *),
+ u64 duration, bool set);
+void camu_list_skip(struct camu_list *list, s32 sequence, s32 n);
+void camu_list_skipto(struct camu_list *list, s32 i);
+void camu_list_toggle_pause(struct camu_list *list, s32 sequence, u64 pos);
+void camu_list_seek(struct camu_list *list, s32 sequence, f64 percent);
+void camu_list_finished(struct camu_list *list, s32 sequence);
+void camu_list_shuffle(struct camu_list *list);
diff --git a/src/list/meson.build b/src/list/meson.build
new file mode 100644
index 0000000..c61ac39
--- /dev/null
+++ b/src/list/meson.build
@@ -0,0 +1,5 @@
+list_src = [
+ 'list.c'
+]
+list_deps = [common_deps]
+list = declare_dependency(sources: list_src, dependencies: list_deps)