From 2b8feba1200ecfa231aa41aed4b6ab9b183d5a05 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Sun, 5 Nov 2023 23:27:19 -0800 Subject: 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. --- configure.ac | 1 + src/Makefile.am | 1 + src/modules/Makefile.am | 1 + src/modules/playit/Makefile.am | 3 + src/modules/playit/playit.c | 224 +++++++++++++++++++++++++++++++++++++++++ src/til.c | 2 + 6 files changed, 232 insertions(+) create mode 100644 src/modules/playit/Makefile.am create mode 100644 src/modules/playit/playit.c diff --git a/configure.ac b/configure.ac index ddaa7bb..1e68420 100644 --- a/configure.ac +++ b/configure.ac @@ -57,6 +57,7 @@ AC_CONFIG_FILES([ src/modules/pixbounce/Makefile src/modules/plasma/Makefile src/modules/plato/Makefile + src/modules/playit/Makefile src/modules/ray/Makefile src/modules/rkt/Makefile src/modules/roto/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index c8a232d..759e921 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -49,6 +49,7 @@ libtil_la_LIBADD = \ modules/pixbounce/libpixbounce.la \ modules/plasma/libplasma.la \ modules/plato/libplato.la \ + modules/playit/libplayit.la \ modules/ray/libray.la \ modules/rkt/librkt.la \ modules/roto/libroto.la \ 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 +#include + +#include + +#include "til.h" +#include "til_audio.h" +#include "til_fb.h" +#include "til_stream.h" + +/* Copyright (C) 2023 - Vito Caputo */ + +/* .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 ", + .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; +} diff --git a/src/til.c b/src/til.c index 0e856a9..9355f5f 100644 --- a/src/til.c +++ b/src/til.c @@ -42,6 +42,7 @@ extern til_module_t montage_module; extern til_module_t pixbounce_module; extern til_module_t plasma_module; extern til_module_t plato_module; +extern til_module_t playit_module; extern til_module_t ray_module; extern til_module_t rkt_module; extern til_module_t roto_module; @@ -81,6 +82,7 @@ static const til_module_t *modules[] = { &pixbounce_module, &plasma_module, &plato_module, + &playit_module, &ray_module, &rkt_module, &roto_module, -- cgit v1.2.1