summaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2023-11-05 23:27:19 -0800
committerVito Caputo <vcaputo@pengaru.com>2023-11-14 01:20:56 -0800
commit2b8feba1200ecfa231aa41aed4b6ab9b183d5a05 (patch)
treebcc67fe369e86c200ad0d4a9c7a4c099b6c4aba1 /src/modules
parent43a0ba4ee53fb0e351be1ef87a60191f2d755464 (diff)
modules/playit: add an .IT file music player module
Rudimentary .IT file music playback via libplayit. When seekable=on, the file is 100% pre-rendered at context create, then simply copied into the audio queue @ render_audio. When seekable=off, the file is mixed incrementally per-frame @ render_audio in max of bufsize=N_frames increments. The bufsize here basically just determines the maximum time spent rendering audio in a single go, and how much tolerance of frame delays due to slow rendering there will be before dropouts occur.
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/Makefile.am1
-rw-r--r--src/modules/playit/Makefile.am3
-rw-r--r--src/modules/playit/playit.c224
3 files changed, 228 insertions, 0 deletions
diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am
index 52d6180..0a01480 100644
--- a/src/modules/Makefile.am
+++ b/src/modules/Makefile.am
@@ -14,6 +14,7 @@ SUBDIRS = \
pixbounce \
plasma \
plato \
+ playit \
ray \
rkt \
roto \
diff --git a/src/modules/playit/Makefile.am b/src/modules/playit/Makefile.am
new file mode 100644
index 0000000..eec7be8
--- /dev/null
+++ b/src/modules/playit/Makefile.am
@@ -0,0 +1,3 @@
+noinst_LTLIBRARIES = libplayit.la
+libplayit_la_SOURCES = playit.c
+libplayit_la_CPPFLAGS = -I@top_srcdir@/src -I@top_srcdir@/src/libs/playit/src
diff --git a/src/modules/playit/playit.c b/src/modules/playit/playit.c
new file mode 100644
index 0000000..191bd41
--- /dev/null
+++ b/src/modules/playit/playit.c
@@ -0,0 +1,224 @@
+#include <stdint.h>
+#include <stdio.h>
+
+#include <playit.h>
+
+#include "til.h"
+#include "til_audio.h"
+#include "til_fb.h"
+#include "til_stream.h"
+
+/* Copyright (C) 2023 - Vito Caputo <vcaputo@pengaru.com> */
+
+/* .IT file playback module via libplayit (SchismTracker) */
+
+#define PLAYIT_DEFAULT_ITFILE "play.it"
+#define PLAYIT_DEFAULT_SEEKABLE 0
+#define PLAYIT_DEFAULT_BUFSIZE 4096
+
+
+typedef struct playit_context_t {
+ til_module_context_t til_module_context;
+ unsigned last_frame;
+
+ playit_t *playit;
+ til_audio_context_t *audio;
+ unsigned paused:1;
+ int16_t buf[];
+} playit_context_t;
+
+typedef struct playit_setup_t {
+ til_setup_t til_setup;
+
+ unsigned seekable;
+ unsigned bufsize;
+ char itfile[];
+} playit_setup_t;
+
+
+static void playit_audio_seeked(void *hook_context, const til_audio_context_t *audio_context, unsigned ticks);
+static void playit_audio_paused(void *hook_context, const til_audio_context_t *audio_context);
+static void playit_audio_unpaused(void *hook_context, const til_audio_context_t *audio_context);
+
+
+til_audio_hooks_t playit_audio_hooks = {
+ playit_audio_seeked,
+ playit_audio_paused,
+ playit_audio_unpaused,
+};
+
+
+static void playit_audio_seeked(void *hook_context, const til_audio_context_t *audio_context, unsigned ticks)
+{
+ playit_context_t *ctxt = hook_context;
+
+ assert(((playit_setup_t *)ctxt->til_module_context.setup)->seekable);
+
+ playit_seek(ctxt->playit, ticks * 44.1f);
+}
+
+
+static void playit_audio_paused(void *hook_context, const til_audio_context_t *audio_context)
+{
+ playit_context_t *ctxt = hook_context;
+
+ ctxt->paused = 1;
+}
+
+
+static void playit_audio_unpaused(void *hook_context, const til_audio_context_t *audio_context)
+{
+ playit_context_t *ctxt = hook_context;
+
+ ctxt->paused = 0;
+}
+
+
+static til_module_context_t * playit_create_context(const til_module_t *module, til_stream_t *stream, unsigned seed, unsigned ticks, unsigned n_cpus, til_setup_t *setup)
+{
+ playit_setup_t *s = (playit_setup_t *)setup;
+ playit_context_t *ctxt;
+
+ ctxt = til_module_context_new(module, sizeof(playit_context_t) + s->bufsize * sizeof(ctxt->buf[0]) * 2, stream, seed, ticks, n_cpus, setup);
+ if (!ctxt)
+ return NULL;
+
+ ctxt->audio = til_stream_get_audio_context(stream);
+ if (!ctxt->audio)
+ return til_module_context_free(&ctxt->til_module_context);
+
+ if (til_audio_set_hooks(ctxt->audio, &playit_audio_hooks, ctxt) < 0)
+ return til_module_context_free(&ctxt->til_module_context);
+
+ ctxt->playit = playit_open_file(s->itfile, s->seekable ? PLAYIT_FLAG_SEEKABLE : 0);
+ if (!ctxt->playit)
+ return til_module_context_free(&ctxt->til_module_context);
+
+ return &ctxt->til_module_context;
+}
+
+
+static void playit_destroy_context(til_module_context_t *context)
+{
+ playit_context_t *ctxt = (playit_context_t *)context;
+
+ til_audio_unset_hooks(ctxt->audio, &playit_audio_hooks, ctxt);
+
+ if (ctxt->playit)
+ playit_destroy(ctxt->playit);
+
+ free(ctxt);
+}
+
+
+static void playit_render_audio(til_module_context_t *context, til_stream_t *stream, unsigned ticks)
+{
+ playit_context_t *ctxt = (playit_context_t *)context;
+ playit_setup_t *s = (playit_setup_t *)context->setup;
+ size_t tomix = s->bufsize;
+ unsigned frame, frames;
+
+ if (ctxt->paused)
+ return;
+
+ tomix -= til_audio_n_queued(ctxt->audio);
+ if (tomix <= 0)
+ return;
+
+ frames = playit_update(ctxt->playit, ctxt->buf, tomix * sizeof(int16_t) * 2, &frame);
+ if (!frames)
+ return;
+
+ til_audio_queue(ctxt->audio, ctxt->buf, frames);
+}
+
+
+static int playit_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup);
+
+
+til_module_t playit_module = {
+ .create_context = playit_create_context,
+ .destroy_context = playit_destroy_context,
+ .render_audio = playit_render_audio,
+ .setup = playit_setup,
+ .name = "playit",
+ .description = ".IT tracked music file player",
+ .author = "Vito Caputo <vcaputo@pengaru.com>",
+ .flags = TIL_MODULE_EXPERIMENTAL,
+};
+
+
+static int playit_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup)
+{
+ til_setting_t *itfile;
+ til_setting_t *seekable;
+ til_setting_t *bufsize;
+ const char *seekable_values[] = {
+ "off",
+ "on",
+ NULL
+ };
+ int r;
+
+ r = til_settings_get_and_describe_setting(settings,
+ &(til_setting_spec_t){
+ .name = ".IT file path",
+ .key = "itfile",
+ .preferred = PLAYIT_DEFAULT_ITFILE,
+ .annotations = NULL
+ },
+ &itfile,
+ res_setting,
+ res_desc);
+ if (r)
+ return r;
+
+ r = til_settings_get_and_describe_setting(settings,
+ &(til_setting_spec_t){
+ .name = "Seekable",
+ .key = "seekable",
+ .regex = "^(on|off)",
+ .preferred = seekable_values[PLAYIT_DEFAULT_SEEKABLE],
+ .values = seekable_values,
+ .annotations = NULL
+ },
+ &seekable,
+ res_setting,
+ res_desc);
+ if (r)
+ return r;
+
+ r = til_settings_get_and_describe_setting(settings,
+ &(til_setting_spec_t){
+ .name = "Buffer size in frames",
+ .key = "bufsize",
+ .preferred = TIL_SETTINGS_STR(PLAYIT_DEFAULT_BUFSIZE),
+ },
+ &bufsize,
+ res_setting,
+ res_desc);
+ if (r)
+ return r;
+
+ if (res_setup) {
+ playit_setup_t *setup;
+ size_t itfile_len = strlen(itfile->value);
+
+ setup = til_setup_new(settings, sizeof(*setup) + itfile_len + 1, NULL, &playit_module);
+ if (!setup)
+ return -ENOMEM;
+
+ strncpy(setup->itfile, itfile->value, itfile_len);
+
+ r = til_value_to_pos(seekable_values, seekable->value, &setup->seekable);
+ if (r < 0)
+ return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, seekable, res_setting, r);
+
+ if (sscanf(bufsize->value, "%u", &setup->bufsize) != 1)
+ return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, bufsize, res_setting, -EINVAL);
+
+ *res_setup = &setup->til_setup;
+ }
+
+ return 0;
+}
© All Rights Reserved