summaryrefslogtreecommitdiff
path: root/src/til_audio.c
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2023-08-28 18:57:55 -0700
committerVito Caputo <vcaputo@pengaru.com>2023-11-14 01:20:48 -0800
commitb22df31feeb7e695faabecfd7cc6fdd24609b0e1 (patch)
treeb1c33983aec45ba17de58276d2c3696695e573dd /src/til_audio.c
parent8245857f9f07043039affd7b92a740e002b1b81b (diff)
til: add preliminary audio backend
This is an early implementation of something resembling an audio backend for rototiller/libtil. The assumption for now is that everything will use signed 16-bit native-endian stereo output @ 44.1khz.
Diffstat (limited to 'src/til_audio.c')
-rw-r--r--src/til_audio.c148
1 files changed, 148 insertions, 0 deletions
diff --git a/src/til_audio.c b/src/til_audio.c
new file mode 100644
index 0000000..ed1ff9b
--- /dev/null
+++ b/src/til_audio.c
@@ -0,0 +1,148 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "til_audio.h"
+#include "til_audio_context.h"
+
+
+/* initialize audio via ops using setup, context stored in res_audio_context on success.
+ * returns -errno on error, 0 on success
+ * playback is left in a paused state, with an empty queue
+ */
+int til_audio_open(const til_audio_ops_t *ops, til_setup_t *setup, til_audio_context_t **res_audio_context)
+{
+ til_audio_context_t *c;
+ int r;
+
+ assert(ops);
+ assert(ops->init);
+ assert(ops->queue);
+ assert(ops->n_queued);
+ assert(setup);
+ assert(res_audio_context);
+
+ r = ops->init(setup, &c);
+ if (r < 0)
+ return r;
+
+ *res_audio_context = c;
+
+ return 0;
+}
+
+
+/* closes audio and frees context,
+ * callers are expected to use this and not til_audio_context_free().
+ */
+void til_audio_shutdown(til_audio_context_t *audio_context)
+{
+ assert(audio_context);
+ assert(audio_context->ops);
+
+ if (audio_context->ops->shutdown)
+ audio_context->ops->shutdown(audio_context);
+
+ til_audio_context_free(audio_context);
+}
+
+
+/* install audio hooks to receive notification on events like seek/pause/unpause */
+int til_audio_set_hooks(til_audio_context_t *audio_context, til_audio_hooks_t *hooks, void *hooks_context)
+{
+ assert(audio_context);
+ assert(hooks);
+
+ if (audio_context->hooks && audio_context->hooks != hooks)
+ return -EEXIST;
+
+ audio_context->hooks = hooks;
+ audio_context->hooks_context = hooks_context;
+
+ return 0;
+}
+
+
+/* remove audio hooks */
+int til_audio_unset_hooks(til_audio_context_t *audio_context, til_audio_hooks_t *hooks, void *hooks_context)
+{
+ assert(audio_context);
+ assert(hooks);
+
+ /* this is kind of silly, but seems potentially useful in the defensive department */
+ if (audio_context->hooks != hooks ||
+ audio_context->hooks_context != hooks_context)
+ return -EINVAL;
+
+ audio_context->hooks = NULL;
+ audio_context->hooks_context = NULL;
+
+ return 0;
+}
+
+
+/* seek to an absolute ticks, playback is left in a paused state with an empty queue */
+void til_audio_seek(til_audio_context_t *audio_context, unsigned ticks)
+{
+ assert(audio_context);
+
+ if (audio_context->ops->pause)
+ audio_context->ops->pause(audio_context);
+
+ if (audio_context->ops->drop)
+ audio_context->ops->drop(audio_context);
+
+ if (audio_context->hooks && audio_context->hooks->seeked)
+ audio_context->hooks->seeked(audio_context->hooks_context, audio_context, ticks);
+}
+
+
+/* queue n_frames frames to audio_context,
+ * returns 0 on success, -errno on error
+ */
+int til_audio_queue(til_audio_context_t *audio_context, int16_t *frames, int n_frames)
+{
+ assert(audio_context);
+ assert(frames);
+ assert(n_frames > 0);
+
+ return audio_context->ops->queue(audio_context, frames, n_frames);
+}
+
+
+/* query how many frames are currently queued
+ * returns 0 on success, -errno on error
+ */
+unsigned til_audio_n_queued(til_audio_context_t *audio_context)
+{
+ assert(audio_context);
+
+ return audio_context->ops->n_queued(audio_context);
+}
+
+
+/* pause the underlying audio playback, queue is left as-is, should be idempotent */
+void til_audio_pause(til_audio_context_t *audio_context)
+{
+ assert(audio_context);
+
+ if (audio_context->ops->pause)
+ audio_context->ops->pause(audio_context);
+
+ if (audio_context->hooks && audio_context->hooks->paused)
+ audio_context->hooks->paused(audio_context->hooks_context, audio_context);
+}
+
+
+/* unpause the underlying audio playback, queue is left as-is, should be idempotent */
+void til_audio_unpause(til_audio_context_t *audio_context)
+{
+ assert(audio_context);
+
+ if (audio_context->ops->unpause)
+ audio_context->ops->unpause(audio_context);
+
+ if (audio_context->hooks && audio_context->hooks->unpaused)
+ audio_context->hooks->unpaused(audio_context->hooks_context, audio_context);
+}
© All Rights Reserved