From 26d3f180de32f102ddb4fa3db222690b34128274 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Mon, 13 Mar 2023 12:54:30 -0700 Subject: modules/rocket: mv modules/rocket modules/rkt Finishes build/fs part of modules/rocket->modules/rkt rename started in previous commit. --- configure.ac | 2 +- src/Makefile.am | 2 +- src/modules/Makefile.am | 2 +- src/modules/rkt/Makefile.am | 3 + src/modules/rkt/rkt.c | 429 +++++++++++++++++++++++++++++++++++++++++ src/modules/rocket/Makefile.am | 3 - src/modules/rocket/rocket.c | 429 ----------------------------------------- 7 files changed, 435 insertions(+), 435 deletions(-) create mode 100644 src/modules/rkt/Makefile.am create mode 100644 src/modules/rkt/rkt.c delete mode 100644 src/modules/rocket/Makefile.am delete mode 100644 src/modules/rocket/rocket.c diff --git a/configure.ac b/configure.ac index 8ebd846..7e09e6a 100644 --- a/configure.ac +++ b/configure.ac @@ -55,7 +55,7 @@ AC_CONFIG_FILES([ src/modules/plasma/Makefile src/modules/plato/Makefile src/modules/ray/Makefile - src/modules/rocket/Makefile + src/modules/rkt/Makefile src/modules/roto/Makefile src/modules/rtv/Makefile src/modules/shapes/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 1b9d932..d7437ec 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,7 +3,7 @@ SUBDIRS = libs modules noinst_LTLIBRARIES = libtil.la libtil_la_SOURCES = til_args.c til_args.h til_fb.c til_fb.h til_jenkins.c til_jenkins.h til.c til.h til_module_context.c til_module_context.h til_settings.h til_settings.c til_setup.c til_setup.h til_stream.c til_stream.h til_tap.h til_threads.c til_threads.h til_util.c til_util.h libtil_la_CPPFLAGS = -I@top_srcdir@/src -libtil_la_LIBADD = modules/blinds/libblinds.la modules/checkers/libcheckers.la modules/compose/libcompose.la modules/drizzle/libdrizzle.la modules/flui2d/libflui2d.la modules/julia/libjulia.la modules/meta2d/libmeta2d.la modules/moire/libmoire.la modules/montage/libmontage.la modules/pixbounce/libpixbounce.la modules/plasma/libplasma.la modules/plato/libplato.la modules/ray/libray.la modules/rocket/librocket.la modules/roto/libroto.la modules/rtv/librtv.la modules/shapes/libshapes.la modules/snow/libsnow.la modules/sparkler/libsparkler.la modules/spiro/libspiro.la modules/stars/libstars.la modules/strobe/libstrobe.la modules/submit/libsubmit.la modules/swab/libswab.la modules/swarm/libswarm.la modules/voronoi/libvoronoi.la libs/grid/libgrid.la libs/puddle/libpuddle.la libs/ray/libray.la libs/rocket/librocket.la libs/sig/libsig.la libs/txt/libtxt.la libs/ascii/libascii.la libs/din/libdin.la +libtil_la_LIBADD = modules/blinds/libblinds.la modules/checkers/libcheckers.la modules/compose/libcompose.la modules/drizzle/libdrizzle.la modules/flui2d/libflui2d.la modules/julia/libjulia.la modules/meta2d/libmeta2d.la modules/moire/libmoire.la modules/montage/libmontage.la modules/pixbounce/libpixbounce.la modules/plasma/libplasma.la modules/plato/libplato.la modules/ray/libray.la modules/rkt/librkt.la modules/roto/libroto.la modules/rtv/librtv.la modules/shapes/libshapes.la modules/snow/libsnow.la modules/sparkler/libsparkler.la modules/spiro/libspiro.la modules/stars/libstars.la modules/strobe/libstrobe.la modules/submit/libsubmit.la modules/swab/libswab.la modules/swarm/libswarm.la modules/voronoi/libvoronoi.la libs/grid/libgrid.la libs/puddle/libpuddle.la libs/ray/libray.la libs/rocket/librocket.la libs/sig/libsig.la libs/txt/libtxt.la libs/ascii/libascii.la libs/din/libdin.la bin_PROGRAMS = rototiller rototiller_SOURCES = fps.c fps.h main.c mem_fb.c setup.h setup.c til.h til_fb.c til_fb.h til_settings.c til_settings.h til_threads.c til_threads.h til_util.c til_util.h diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am index 1ce37de..c1326ac 100644 --- a/src/modules/Makefile.am +++ b/src/modules/Makefile.am @@ -1 +1 @@ -SUBDIRS = blinds checkers compose drizzle flui2d julia meta2d moire montage pixbounce plasma plato ray rocket roto rtv shapes snow sparkler spiro stars strobe submit swab swarm voronoi +SUBDIRS = blinds checkers compose drizzle flui2d julia meta2d moire montage pixbounce plasma plato ray rkt roto rtv shapes snow sparkler spiro stars strobe submit swab swarm voronoi diff --git a/src/modules/rkt/Makefile.am b/src/modules/rkt/Makefile.am new file mode 100644 index 0000000..ed446c4 --- /dev/null +++ b/src/modules/rkt/Makefile.am @@ -0,0 +1,3 @@ +noinst_LTLIBRARIES = librkt.la +librkt_la_SOURCES = rkt.c +librkt_la_CPPFLAGS = -I@top_srcdir@/src -I@top_srcdir@/src/libs -I@top_srcdir@/src/libs/rocket/rocket/lib diff --git a/src/modules/rkt/rkt.c b/src/modules/rkt/rkt.c new file mode 100644 index 0000000..ac361c0 --- /dev/null +++ b/src/modules/rkt/rkt.c @@ -0,0 +1,429 @@ +#include +#include +#include + +#include "rocket/rocket/lib/device.h" +#include "rocket/rocket/lib/sync.h" +#include "rocket/rocket/lib/track.h" + +#include "til.h" +#include "til_fb.h" +#include "til_module_context.h" +#include "til_settings.h" +#include "til_stream.h" +#include "til_tap.h" +#include "til_util.h" + +#include "txt/txt.h" + +/* Copyright (C) 2023 - Vito Caputo */ + +/* This implements a rudimentary sequencing module varying + * "tapped" variables of other modules on a timeline via + * GNU Rocket (https://github.com/rocket/rocket) + */ + +typedef struct rkt_context_t { + til_module_context_t til_module_context; + + const til_module_t *seq_module; + til_module_context_t *seq_module_ctxt; + + struct sync_device *sync_device; + double rows_per_ms; + double rocket_row; + unsigned last_ticks; + unsigned paused:1; +} rkt_context_t; + +typedef struct rkt_setup_t { + til_setup_t til_setup; + const char *seq_module_name; + const char *base; + double rows_per_ms; + unsigned connect:1; + const char *host; + unsigned short port; +} rkt_setup_t; + +static rkt_setup_t rkt_default_setup = { .seq_module_name = "compose" }; + + +static til_module_context_t * rkt_create_context(const til_module_t *module, til_stream_t *stream, unsigned seed, unsigned ticks, unsigned n_cpus, char *path, til_setup_t *setup) +{ + rkt_context_t *ctxt; + const til_module_t *seq_module; + + if (!setup) + setup = &rkt_default_setup.til_setup; + + seq_module = til_lookup_module(((rkt_setup_t *)setup)->seq_module_name); + if (!seq_module) + return NULL; + + ctxt = til_module_context_new(module, sizeof(rkt_context_t), stream, seed, ticks, n_cpus, path); + if (!ctxt) + return NULL; + + ctxt->sync_device = sync_create_device(((rkt_setup_t *)setup)->base); + if (!ctxt->sync_device) + return til_module_context_free(&ctxt->til_module_context); + + if (((rkt_setup_t *)setup)->connect) { + /* XXX: it'd be better if we just reconnected periodically instead of hard failing */ + if (sync_tcp_connect(ctxt->sync_device, ((rkt_setup_t *)setup)->host, ((rkt_setup_t *)setup)->port)) + return til_module_context_free(&ctxt->til_module_context); + } + + ctxt->seq_module = seq_module; + + { + til_setup_t *module_setup = NULL; + + (void) til_module_randomize_setup(ctxt->seq_module, rand_r(&seed), &module_setup, NULL); + + (void) til_module_create_context(ctxt->seq_module, stream, rand_r(&seed), ticks, 0, path, module_setup, &ctxt->seq_module_ctxt); + til_setup_free(module_setup); + } + + ctxt->rows_per_ms = ((rkt_setup_t *)setup)->rows_per_ms; + ctxt->last_ticks = ticks; + + return &ctxt->til_module_context; +} + + +static void rkt_destroy_context(til_module_context_t *context) +{ + rkt_context_t *ctxt = (rkt_context_t *)context; + + if (ctxt->sync_device) + sync_destroy_device(ctxt->sync_device); + til_module_context_free(ctxt->seq_module_ctxt); + free(context); +} + + +static void rkt_sync_pause(void *context, int flag) +{ + rkt_context_t *ctxt = context; + + if (flag) + ctxt->paused = 1; + else + ctxt->paused = 0; +} + + +static void rkt_sync_set_row(void *context, int row) +{ + rkt_context_t *ctxt = context; + + ctxt->rocket_row = row; +} + + +static int rkt_sync_is_playing(void *context) +{ + rkt_context_t *ctxt = context; + + /* returns bool, 1 for is playing */ + return !ctxt->paused; +} + + +static struct sync_cb rkt_sync_cb = { + rkt_sync_pause, + rkt_sync_set_row, + rkt_sync_is_playing, +}; + + +typedef struct rkt_pipe_t { + /* rocket basically only applies to floats, so we only need a float, its tap, and a sync track */ + til_tap_t tap; + + union { + float f; + double d; + } var; + + union { + float *f; + double *d; + } ptr; + + const struct sync_track *track; + char track_name[]; +} rkt_pipe_t; + + +int rkt_stream_pipe_ctor(void *context, til_stream_t *stream, const void *owner, const void *owner_foo, const char *parent_path, uint32_t parent_hash, const til_tap_t *tap, const void **res_owner, const void **res_owner_foo, const til_tap_t **res_driving_tap) +{ + rkt_context_t *ctxt = context; + rkt_pipe_t *rkt_pipe; + size_t track_name_len; + + assert(stream); + assert(tap); + assert(res_owner); + assert(res_owner_foo); + assert(res_driving_tap); + + if (tap->type != TIL_TAP_TYPE_FLOAT && + tap->type != TIL_TAP_TYPE_DOUBLE) + return 0; /* not interesting to us */ + + /* we take ownership, and create our own tap and rocket track to stow @ owner_foo */ + + /* rocket has its own syntax for track names so instead of consttructing a concatenated path + * in til_stream_pipe_t and passing it to the ctor, just construct our own in the end of rkt_pipe_t + */ + track_name_len = strlen(parent_path) + 1 + strlen(tap->name) + 1; + rkt_pipe = calloc(1, sizeof(rkt_pipe_t) + track_name_len); + if (!rkt_pipe) + return -ENOMEM; + + snprintf(rkt_pipe->track_name, track_name_len, "%s:%s", parent_path, tap->name); + rkt_pipe->tap = til_tap_init(ctxt, tap->type, &rkt_pipe->ptr, 1, &rkt_pipe->var, tap->name); + rkt_pipe->track = sync_get_track(ctxt->sync_device, rkt_pipe->track_name); + + *res_owner = ctxt; + *res_owner_foo = rkt_pipe; + *res_driving_tap = rkt_pipe->track->num_keys ? &rkt_pipe->tap : tap; + + return 1; +} + + +static const til_stream_hooks_t rkt_stream_hooks = { + .pipe_ctor = rkt_stream_pipe_ctor, + /* .pipe_dtor unneeded */ +}; + + +static int rkt_pipe_update(void *context, til_stream_pipe_t *pipe, const void *owner, const void *owner_foo, const til_tap_t *driving_tap) +{ + rkt_pipe_t *rkt_pipe = (rkt_pipe_t *)owner_foo; + rkt_context_t *ctxt = context; + double val; + + /* just ignore pipes we don't own (they're not types we can drive w/rocket) */ + if (owner != ctxt) + return 0; + + /* when there's no keys in the track, flag as inactive so someone else can drive */ + if (!rkt_pipe->track->num_keys) { + rkt_pipe->tap.inactive = 1; + + return 0; + } + + rkt_pipe->tap.inactive = 0; + if (driving_tap != &rkt_pipe->tap) + til_stream_pipe_set_driving_tap(pipe, &rkt_pipe->tap); + + /* otherwise get the current interpolated value from the rocket track @ owner_foo->track + * to update owner_foo->var.[fd], which _should_ be the driving tap. + */ + val = sync_get_val(rkt_pipe->track, ctxt->rocket_row); + switch (rkt_pipe->tap.type) { + case TIL_TAP_TYPE_FLOAT: + rkt_pipe->var.f = val; + break; + case TIL_TAP_TYPE_DOUBLE: + rkt_pipe->var.d = val; + break; + default: + assert(0); + } + + return 0; +} + + +static void rkt_render_fragment(til_module_context_t *context, til_stream_t *stream, unsigned ticks, unsigned cpu, til_fb_fragment_t **fragment_ptr) +{ + rkt_context_t *ctxt = (rkt_context_t *)context; + + if (!ctxt->paused) + ctxt->rocket_row += ((double)(ticks - ctxt->last_ticks)) * ctxt->rows_per_ms; + + ctxt->last_ticks = ticks; + + /* hooks-setting is idempotent and cheap so we just always do it, and technicallly the stream can get changed out on us frame-to-frame */ + til_stream_set_hooks(stream, &rkt_stream_hooks, ctxt); + + /* ctxt->rocket_row needs to be updated */ + sync_update(ctxt->sync_device, ctxt->rocket_row, &rkt_sync_cb, ctxt); + + /* this drives our per-rocket-track updates, with the tracks registered as owner_foo on the pipes, respectively */ + til_stream_for_each_pipe(stream, rkt_pipe_update, ctxt); + + til_module_render(ctxt->seq_module_ctxt, stream, ticks, fragment_ptr); +} + + +static int rkt_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup) +{ + const char *connect_values[] = { + "off", + "on", + NULL + }; + const char *seq_module; + const char *base; + const char *bpm; + const char *rpb; + const char *connect; + const char *host; + const char *port; + int r; + + /* TODO: + * Instead of driving a single module, we could accept a list of module specifiers + * including settings for each (requiring the recursive settings support to land). + * Then just use a module selector track for switching between the modules... that + * might work for getting full-blown demos sequenced via rocket. + */ + r = til_settings_get_and_describe_value(settings, + &(til_setting_desc_t){ + .name = "Module to sequence", + .key = "seq_module", + .preferred = "compose", + .annotations = NULL, + }, + &seq_module, + res_setting, + res_desc); + if (r) + return r; + + r = til_settings_get_and_describe_value(settings, + &(til_setting_desc_t){ + .name = "Rocket \"base\" label", + .key = "base", + .preferred = "tiller", + .annotations = NULL, + }, + &base, + res_setting, + res_desc); + if (r) + return r; + + r = til_settings_get_and_describe_value(settings, + &(til_setting_desc_t){ + .name = "Beats per minute", + .key = "bpm", + .preferred = "125", + .annotations = NULL, + }, + &bpm, + res_setting, + res_desc); + if (r) + return r; + + r = til_settings_get_and_describe_value(settings, + &(til_setting_desc_t){ + .name = "Rows per beat", + .key = "rpb", + .preferred = "8", + .annotations = NULL, + }, + &rpb, + res_setting, + res_desc); + if (r) + return r; + + r = til_settings_get_and_describe_value(settings, + &(til_setting_desc_t){ + .name = "Editor connection toggle", + .key = "connect", + /* TODO: regex */ + .preferred = connect_values[1], + .values = connect_values, + .annotations = NULL, + }, + &connect, + res_setting, + res_desc); + if (r) + return r; + + if (!strcasecmp(connect, "on")) { + r = til_settings_get_and_describe_value(settings, + &(til_setting_desc_t){ + .name = "Editor host", + .key = "host", + .preferred = "localhost", + /* TODO: regex */ + .annotations = NULL, + }, + &host, + res_setting, + res_desc); + if (r) + return r; + + r = til_settings_get_and_describe_value(settings, + &(til_setting_desc_t){ + .name = "Editor port", + .key = "port", + .preferred = TIL_SETTINGS_STR(SYNC_DEFAULT_PORT), + /* TODO: regex */ + .annotations = NULL, + }, + &port, + res_setting, + res_desc); + if (r) + return r; + } + + if (res_setup) { + const til_module_t *til_seq_module; + rkt_setup_t *setup; + unsigned ibpm, irpb; + + if (!strcmp(seq_module, "rkt")) + return -EINVAL; + + til_seq_module = til_lookup_module(seq_module); + if (!til_seq_module) + return -ENOENT; + + /* TODO: we're going to need a custom setup_free to cleanup host+base etc. */ + setup = til_setup_new(sizeof(*setup), (void(*)(til_setup_t *))free); + if (!setup) + return -ENOMEM; + + setup->seq_module_name = til_seq_module->name; + setup->base = strdup(base); /* FIXME errors */ + if (!strcasecmp(connect, "on")) { + setup->connect = 1; + setup->host = strdup(host); /* FIXME errors */ + sscanf(port, "%hu", &setup->port); /* FIXME parse errors */ + } + sscanf(bpm, "%u", &ibpm); + sscanf(rpb, "%u", &irpb); + setup->rows_per_ms = ((double)(ibpm * irpb)) * (1.0 / (60.0 * 1000.0)); + + *res_setup = &setup->til_setup; + } + + return 0; +} + + +til_module_t rkt_module = { + .create_context = rkt_create_context, + .destroy_context = rkt_destroy_context, + .render_fragment = rkt_render_fragment, + .name = "rkt", + .description = "GNU Rocket module sequencer", + .setup = rkt_setup, + .flags = TIL_MODULE_HERMETIC | TIL_MODULE_EXPERIMENTAL, /* this needs refinement esp. if rkt gets split into a player and editor */ +}; diff --git a/src/modules/rocket/Makefile.am b/src/modules/rocket/Makefile.am deleted file mode 100644 index 96da9aa..0000000 --- a/src/modules/rocket/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ -noinst_LTLIBRARIES = librocket.la -librocket_la_SOURCES = rocket.c -librocket_la_CPPFLAGS = -I@top_srcdir@/src -I@top_srcdir@/src/libs -I@top_srcdir@/src/libs/rocket/rocket/lib diff --git a/src/modules/rocket/rocket.c b/src/modules/rocket/rocket.c deleted file mode 100644 index ac361c0..0000000 --- a/src/modules/rocket/rocket.c +++ /dev/null @@ -1,429 +0,0 @@ -#include -#include -#include - -#include "rocket/rocket/lib/device.h" -#include "rocket/rocket/lib/sync.h" -#include "rocket/rocket/lib/track.h" - -#include "til.h" -#include "til_fb.h" -#include "til_module_context.h" -#include "til_settings.h" -#include "til_stream.h" -#include "til_tap.h" -#include "til_util.h" - -#include "txt/txt.h" - -/* Copyright (C) 2023 - Vito Caputo */ - -/* This implements a rudimentary sequencing module varying - * "tapped" variables of other modules on a timeline via - * GNU Rocket (https://github.com/rocket/rocket) - */ - -typedef struct rkt_context_t { - til_module_context_t til_module_context; - - const til_module_t *seq_module; - til_module_context_t *seq_module_ctxt; - - struct sync_device *sync_device; - double rows_per_ms; - double rocket_row; - unsigned last_ticks; - unsigned paused:1; -} rkt_context_t; - -typedef struct rkt_setup_t { - til_setup_t til_setup; - const char *seq_module_name; - const char *base; - double rows_per_ms; - unsigned connect:1; - const char *host; - unsigned short port; -} rkt_setup_t; - -static rkt_setup_t rkt_default_setup = { .seq_module_name = "compose" }; - - -static til_module_context_t * rkt_create_context(const til_module_t *module, til_stream_t *stream, unsigned seed, unsigned ticks, unsigned n_cpus, char *path, til_setup_t *setup) -{ - rkt_context_t *ctxt; - const til_module_t *seq_module; - - if (!setup) - setup = &rkt_default_setup.til_setup; - - seq_module = til_lookup_module(((rkt_setup_t *)setup)->seq_module_name); - if (!seq_module) - return NULL; - - ctxt = til_module_context_new(module, sizeof(rkt_context_t), stream, seed, ticks, n_cpus, path); - if (!ctxt) - return NULL; - - ctxt->sync_device = sync_create_device(((rkt_setup_t *)setup)->base); - if (!ctxt->sync_device) - return til_module_context_free(&ctxt->til_module_context); - - if (((rkt_setup_t *)setup)->connect) { - /* XXX: it'd be better if we just reconnected periodically instead of hard failing */ - if (sync_tcp_connect(ctxt->sync_device, ((rkt_setup_t *)setup)->host, ((rkt_setup_t *)setup)->port)) - return til_module_context_free(&ctxt->til_module_context); - } - - ctxt->seq_module = seq_module; - - { - til_setup_t *module_setup = NULL; - - (void) til_module_randomize_setup(ctxt->seq_module, rand_r(&seed), &module_setup, NULL); - - (void) til_module_create_context(ctxt->seq_module, stream, rand_r(&seed), ticks, 0, path, module_setup, &ctxt->seq_module_ctxt); - til_setup_free(module_setup); - } - - ctxt->rows_per_ms = ((rkt_setup_t *)setup)->rows_per_ms; - ctxt->last_ticks = ticks; - - return &ctxt->til_module_context; -} - - -static void rkt_destroy_context(til_module_context_t *context) -{ - rkt_context_t *ctxt = (rkt_context_t *)context; - - if (ctxt->sync_device) - sync_destroy_device(ctxt->sync_device); - til_module_context_free(ctxt->seq_module_ctxt); - free(context); -} - - -static void rkt_sync_pause(void *context, int flag) -{ - rkt_context_t *ctxt = context; - - if (flag) - ctxt->paused = 1; - else - ctxt->paused = 0; -} - - -static void rkt_sync_set_row(void *context, int row) -{ - rkt_context_t *ctxt = context; - - ctxt->rocket_row = row; -} - - -static int rkt_sync_is_playing(void *context) -{ - rkt_context_t *ctxt = context; - - /* returns bool, 1 for is playing */ - return !ctxt->paused; -} - - -static struct sync_cb rkt_sync_cb = { - rkt_sync_pause, - rkt_sync_set_row, - rkt_sync_is_playing, -}; - - -typedef struct rkt_pipe_t { - /* rocket basically only applies to floats, so we only need a float, its tap, and a sync track */ - til_tap_t tap; - - union { - float f; - double d; - } var; - - union { - float *f; - double *d; - } ptr; - - const struct sync_track *track; - char track_name[]; -} rkt_pipe_t; - - -int rkt_stream_pipe_ctor(void *context, til_stream_t *stream, const void *owner, const void *owner_foo, const char *parent_path, uint32_t parent_hash, const til_tap_t *tap, const void **res_owner, const void **res_owner_foo, const til_tap_t **res_driving_tap) -{ - rkt_context_t *ctxt = context; - rkt_pipe_t *rkt_pipe; - size_t track_name_len; - - assert(stream); - assert(tap); - assert(res_owner); - assert(res_owner_foo); - assert(res_driving_tap); - - if (tap->type != TIL_TAP_TYPE_FLOAT && - tap->type != TIL_TAP_TYPE_DOUBLE) - return 0; /* not interesting to us */ - - /* we take ownership, and create our own tap and rocket track to stow @ owner_foo */ - - /* rocket has its own syntax for track names so instead of consttructing a concatenated path - * in til_stream_pipe_t and passing it to the ctor, just construct our own in the end of rkt_pipe_t - */ - track_name_len = strlen(parent_path) + 1 + strlen(tap->name) + 1; - rkt_pipe = calloc(1, sizeof(rkt_pipe_t) + track_name_len); - if (!rkt_pipe) - return -ENOMEM; - - snprintf(rkt_pipe->track_name, track_name_len, "%s:%s", parent_path, tap->name); - rkt_pipe->tap = til_tap_init(ctxt, tap->type, &rkt_pipe->ptr, 1, &rkt_pipe->var, tap->name); - rkt_pipe->track = sync_get_track(ctxt->sync_device, rkt_pipe->track_name); - - *res_owner = ctxt; - *res_owner_foo = rkt_pipe; - *res_driving_tap = rkt_pipe->track->num_keys ? &rkt_pipe->tap : tap; - - return 1; -} - - -static const til_stream_hooks_t rkt_stream_hooks = { - .pipe_ctor = rkt_stream_pipe_ctor, - /* .pipe_dtor unneeded */ -}; - - -static int rkt_pipe_update(void *context, til_stream_pipe_t *pipe, const void *owner, const void *owner_foo, const til_tap_t *driving_tap) -{ - rkt_pipe_t *rkt_pipe = (rkt_pipe_t *)owner_foo; - rkt_context_t *ctxt = context; - double val; - - /* just ignore pipes we don't own (they're not types we can drive w/rocket) */ - if (owner != ctxt) - return 0; - - /* when there's no keys in the track, flag as inactive so someone else can drive */ - if (!rkt_pipe->track->num_keys) { - rkt_pipe->tap.inactive = 1; - - return 0; - } - - rkt_pipe->tap.inactive = 0; - if (driving_tap != &rkt_pipe->tap) - til_stream_pipe_set_driving_tap(pipe, &rkt_pipe->tap); - - /* otherwise get the current interpolated value from the rocket track @ owner_foo->track - * to update owner_foo->var.[fd], which _should_ be the driving tap. - */ - val = sync_get_val(rkt_pipe->track, ctxt->rocket_row); - switch (rkt_pipe->tap.type) { - case TIL_TAP_TYPE_FLOAT: - rkt_pipe->var.f = val; - break; - case TIL_TAP_TYPE_DOUBLE: - rkt_pipe->var.d = val; - break; - default: - assert(0); - } - - return 0; -} - - -static void rkt_render_fragment(til_module_context_t *context, til_stream_t *stream, unsigned ticks, unsigned cpu, til_fb_fragment_t **fragment_ptr) -{ - rkt_context_t *ctxt = (rkt_context_t *)context; - - if (!ctxt->paused) - ctxt->rocket_row += ((double)(ticks - ctxt->last_ticks)) * ctxt->rows_per_ms; - - ctxt->last_ticks = ticks; - - /* hooks-setting is idempotent and cheap so we just always do it, and technicallly the stream can get changed out on us frame-to-frame */ - til_stream_set_hooks(stream, &rkt_stream_hooks, ctxt); - - /* ctxt->rocket_row needs to be updated */ - sync_update(ctxt->sync_device, ctxt->rocket_row, &rkt_sync_cb, ctxt); - - /* this drives our per-rocket-track updates, with the tracks registered as owner_foo on the pipes, respectively */ - til_stream_for_each_pipe(stream, rkt_pipe_update, ctxt); - - til_module_render(ctxt->seq_module_ctxt, stream, ticks, fragment_ptr); -} - - -static int rkt_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup) -{ - const char *connect_values[] = { - "off", - "on", - NULL - }; - const char *seq_module; - const char *base; - const char *bpm; - const char *rpb; - const char *connect; - const char *host; - const char *port; - int r; - - /* TODO: - * Instead of driving a single module, we could accept a list of module specifiers - * including settings for each (requiring the recursive settings support to land). - * Then just use a module selector track for switching between the modules... that - * might work for getting full-blown demos sequenced via rocket. - */ - r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ - .name = "Module to sequence", - .key = "seq_module", - .preferred = "compose", - .annotations = NULL, - }, - &seq_module, - res_setting, - res_desc); - if (r) - return r; - - r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ - .name = "Rocket \"base\" label", - .key = "base", - .preferred = "tiller", - .annotations = NULL, - }, - &base, - res_setting, - res_desc); - if (r) - return r; - - r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ - .name = "Beats per minute", - .key = "bpm", - .preferred = "125", - .annotations = NULL, - }, - &bpm, - res_setting, - res_desc); - if (r) - return r; - - r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ - .name = "Rows per beat", - .key = "rpb", - .preferred = "8", - .annotations = NULL, - }, - &rpb, - res_setting, - res_desc); - if (r) - return r; - - r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ - .name = "Editor connection toggle", - .key = "connect", - /* TODO: regex */ - .preferred = connect_values[1], - .values = connect_values, - .annotations = NULL, - }, - &connect, - res_setting, - res_desc); - if (r) - return r; - - if (!strcasecmp(connect, "on")) { - r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ - .name = "Editor host", - .key = "host", - .preferred = "localhost", - /* TODO: regex */ - .annotations = NULL, - }, - &host, - res_setting, - res_desc); - if (r) - return r; - - r = til_settings_get_and_describe_value(settings, - &(til_setting_desc_t){ - .name = "Editor port", - .key = "port", - .preferred = TIL_SETTINGS_STR(SYNC_DEFAULT_PORT), - /* TODO: regex */ - .annotations = NULL, - }, - &port, - res_setting, - res_desc); - if (r) - return r; - } - - if (res_setup) { - const til_module_t *til_seq_module; - rkt_setup_t *setup; - unsigned ibpm, irpb; - - if (!strcmp(seq_module, "rkt")) - return -EINVAL; - - til_seq_module = til_lookup_module(seq_module); - if (!til_seq_module) - return -ENOENT; - - /* TODO: we're going to need a custom setup_free to cleanup host+base etc. */ - setup = til_setup_new(sizeof(*setup), (void(*)(til_setup_t *))free); - if (!setup) - return -ENOMEM; - - setup->seq_module_name = til_seq_module->name; - setup->base = strdup(base); /* FIXME errors */ - if (!strcasecmp(connect, "on")) { - setup->connect = 1; - setup->host = strdup(host); /* FIXME errors */ - sscanf(port, "%hu", &setup->port); /* FIXME parse errors */ - } - sscanf(bpm, "%u", &ibpm); - sscanf(rpb, "%u", &irpb); - setup->rows_per_ms = ((double)(ibpm * irpb)) * (1.0 / (60.0 * 1000.0)); - - *res_setup = &setup->til_setup; - } - - return 0; -} - - -til_module_t rkt_module = { - .create_context = rkt_create_context, - .destroy_context = rkt_destroy_context, - .render_fragment = rkt_render_fragment, - .name = "rkt", - .description = "GNU Rocket module sequencer", - .setup = rkt_setup, - .flags = TIL_MODULE_HERMETIC | TIL_MODULE_EXPERIMENTAL, /* this needs refinement esp. if rkt gets split into a player and editor */ -}; -- cgit v1.2.1