From 074353394ff369f34dea5d05d052556466904b08 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Fri, 9 Jun 2023 19:04:40 -0700 Subject: modules/mixer: add rudimentary mixer meta module this is very early/unfinished, hence experimental flag --- src/modules/mixer/Makefile.am | 3 + src/modules/mixer/mixer.c | 302 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 305 insertions(+) create mode 100644 src/modules/mixer/Makefile.am create mode 100644 src/modules/mixer/mixer.c (limited to 'src/modules/mixer') diff --git a/src/modules/mixer/Makefile.am b/src/modules/mixer/Makefile.am new file mode 100644 index 0000000..8d265d5 --- /dev/null +++ b/src/modules/mixer/Makefile.am @@ -0,0 +1,3 @@ +noinst_LTLIBRARIES = libmixer.la +libmixer_la_SOURCES = mixer.c +libmixer_la_CPPFLAGS = -I@top_srcdir@/src -I@top_srcdir@/src/libs diff --git a/src/modules/mixer/mixer.c b/src/modules/mixer/mixer.c new file mode 100644 index 0000000..55234d3 --- /dev/null +++ b/src/modules/mixer/mixer.c @@ -0,0 +1,302 @@ +#include +#include +#include +#include + +#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" + +/* Copyright (C) 2023 - Vito Caputo */ + +/* This implements a rudimentary mixing module for things like fades */ + +typedef enum mixer_style_t { + MIXER_STYLE_FLICKER, +} mixer_style_t; + +typedef struct mixer_input_t { + til_module_context_t *module_ctxt; + /* XXX: it's expected that inputs will get more settable attributes to stick in here */ +} mixer_input_t; + +typedef struct mixer_context_t { + til_module_context_t til_module_context; + + struct { + til_tap_t T; + } taps; + + struct { + float T; + } vars; + + float *T; + + size_t n_inputs; + mixer_input_t inputs[]; +} mixer_context_t; + +typedef struct mixer_setup_input_t { + char *module; + til_setup_t *setup; +} mixer_setup_input_t; + +typedef struct mixer_setup_t { + til_setup_t til_setup; + + mixer_style_t style; + size_t n_inputs; + mixer_setup_input_t inputs[]; +} mixer_setup_t; + +#define MIXER_DEFAULT_STYLE MIXER_STYLE_FLICKER + +static til_module_context_t * mixer_create_context(const til_module_t *module, til_stream_t *stream, unsigned seed, unsigned ticks, unsigned n_cpus, til_setup_t *setup); +static void mixer_destroy_context(til_module_context_t *context); +static void mixer_render_fragment(til_module_context_t *context, til_stream_t *stream, unsigned ticks, unsigned cpu, til_fb_fragment_t **fragment_ptr); +static int mixer_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 mixer_module = { + .create_context = mixer_create_context, + .destroy_context = mixer_destroy_context, + .render_fragment = mixer_render_fragment, + .name = "mixer", + .description = "Module blender", + .setup = mixer_setup, + .flags = TIL_MODULE_EXPERIMENTAL, +}; + + +static til_module_context_t * mixer_create_context(const til_module_t *module, til_stream_t *stream, unsigned seed, unsigned ticks, unsigned n_cpus, til_setup_t *setup) +{ + mixer_setup_t *s = (mixer_setup_t *)setup; + mixer_context_t *ctxt; + int r; + + assert(setup); + + ctxt = til_module_context_new(module, sizeof(mixer_context_t) + s->n_inputs * sizeof(mixer_input_t), stream, seed, ticks, n_cpus, setup); + if (!ctxt) + return NULL; + + for (size_t i = 0; i < s->n_inputs; i++) { + const til_module_t *input_module; + input_module = til_lookup_module(((mixer_setup_t *)setup)->inputs[i].module); + r = til_module_create_context(input_module, stream, rand_r(&seed), ticks, n_cpus, s->inputs[i].setup, &ctxt->inputs[i].module_ctxt); + if (r < 0) + return til_module_context_free(&ctxt->til_module_context); + + ctxt->n_inputs++; + } + + ctxt->taps.T = til_tap_init_float(ctxt, &ctxt->T, 1, &ctxt->vars.T, "T"); + + return &ctxt->til_module_context; +} + + +static void mixer_destroy_context(til_module_context_t *context) +{ + mixer_context_t *ctxt = (mixer_context_t *)context; + + for (size_t i = 0; i < ctxt->n_inputs; i++) + til_module_context_free(ctxt->inputs[i].module_ctxt); + + free(context); +} + +static inline float randf(unsigned *seed) +{ + return 1.f / RAND_MAX * rand_r(seed); +} + +static void mixer_render_fragment(til_module_context_t *context, til_stream_t *stream, unsigned ticks, unsigned cpu, til_fb_fragment_t **fragment_ptr) +{ + mixer_context_t *ctxt = (mixer_context_t *)context; + til_fb_fragment_t *fragment = *fragment_ptr; + size_t i = 0; + + if (!til_stream_tap_context(stream, context, NULL, &ctxt->taps.T)) + *ctxt->T = cosf(ticks * .001f) * .5f + .5f; + + switch (((mixer_setup_t *)context->setup)->style) { + case MIXER_STYLE_FLICKER: + if (randf(&context->seed) < *ctxt->T) + i = 1; + else + i = 0; + break; + + default: + assert(0); + } + + //for (size_t i = 0; i < ctxt->n_inputs; i++) + til_module_render(ctxt->inputs[i].module_ctxt, stream, ticks, &fragment); + + *fragment_ptr = fragment; +} + + +static void mixer_setup_free(til_setup_t *setup) +{ + mixer_setup_t *s = (mixer_setup_t *)setup; + + if (s) { + for (size_t i = 0; i < s->n_inputs; i++) { + free(s->inputs[i].module); + til_setup_free(s->inputs[i].setup); + } + free(setup); + } +} + + +static int mixer_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 *style_values[] = { + "flicker", + NULL + }; + const char *style; + const til_settings_t *inputs_settings; + const char *inputs; + int r; + + r = til_settings_get_and_describe_value(settings, + &(til_setting_spec_t){ + .name = "Mixer blend style", + .key = "style", + .values = style_values, + .preferred = style_values[MIXER_DEFAULT_STYLE], + .annotations = NULL + }, + &style, + res_setting, + res_desc); + if (r) + return r; + + r = til_settings_get_and_describe_value(settings, + &(til_setting_spec_t){ + .name = "Comma-separated list of module inputs", + .key = "inputs", + .preferred = "stars,plato", + .annotations = NULL, + //.random = mixer_random_inputs_setting, + .as_nested_settings = 1, + }, + &inputs, /* XXX: unused in raw-value form, we want the settings instance */ + res_setting, + res_desc); + if (r) + return r; + + assert(res_setting && *res_setting && (*res_setting)->value_as_nested_settings); + inputs_settings = (*res_setting)->value_as_nested_settings; + { + til_setting_t *input_setting; + + for (size_t i = 0; til_settings_get_value_by_idx(inputs_settings, i, &input_setting); i++) { + if (!input_setting->value_as_nested_settings) { + r = til_setting_desc_new( inputs_settings, + &(til_setting_spec_t){ + .as_nested_settings = 1, + }, res_desc); + if (r < 0) + return r; + + *res_setting = input_setting; + + return 1; + } + } + + for (size_t i = 0; til_settings_get_value_by_idx(inputs_settings, i, &input_setting); i++) { + til_setting_t *input_module_setting; + const char *input = til_settings_get_value_by_idx(input_setting->value_as_nested_settings, 0, &input_module_setting); + const til_module_t *input_module; + + if (!input_module_setting) + return -EINVAL; + + if (!input_module_setting->desc) { + r = til_setting_desc_new( input_setting->value_as_nested_settings, + &(til_setting_spec_t){ + .name = "Input module name", + .preferred = "none", + .as_label = 1, + }, res_desc); + if (r < 0) + return r; + + *res_setting = input_module_setting; + + return 1; + } + + input_module = til_lookup_module(input); + if (!input_module) + return -EINVAL; + + if (input_module->setup) { + r = input_module->setup(input_setting->value_as_nested_settings, res_setting, res_desc, NULL); + if (r) + return r; + } + } + } + + if (res_setup) { + size_t n_inputs = til_settings_get_count(inputs_settings); + til_setting_t *input_setting; + mixer_setup_t *setup; + + setup = til_setup_new(settings, sizeof(*setup) + n_inputs * sizeof(setup->inputs[0]), mixer_setup_free); + if (!setup) + return -ENOMEM; + + for (int i = 0; style_values[i]; i++) { + if (!strcasecmp(style_values[i], style)) + setup->style = i; + } + + setup->n_inputs = n_inputs; + + for (size_t i = 0; til_settings_get_value_by_idx(inputs_settings, i, &input_setting); i++) { + const char *input = til_settings_get_value_by_idx(input_setting->value_as_nested_settings, 0, NULL); + const til_module_t *input_module; + + setup->inputs[i].module = strdup(input); + if (!setup->inputs[i].module) { + til_setup_free(&setup->til_setup); + + return -ENOMEM; + } + + input_module = til_lookup_module(input); + if (!input_module) { + til_setup_free(&setup->til_setup); + + return -EINVAL; + } + + r = til_module_setup_finalize(input_module, input_setting->value_as_nested_settings, &setup->inputs[i].setup); + if (r < 0) { + til_setup_free(&setup->til_setup); + + return r; + } + } + + *res_setup = &setup->til_setup; + } + + return 0; +} -- cgit v1.2.1