#include "./composite.h" #include #include #include #include #include #include "util/math/algorithm.h" #include "util/memory/memory.h" #include "./effect.h" #include "./format.h" struct jukebox_composite_t { jukebox_effect_t super; jukebox_format_t format; atomic_bool playing; size_t effects_reserved; size_t effects_length; jukebox_effect_t* effects[1]; }; static void jukebox_composite_affect_( jukebox_effect_t* effect, const jukebox_effect_pcm_t* pcm) { assert(effect != NULL); assert(pcm != NULL); # define CHUNK_SIZE 256 jukebox_composite_t* c = (typeof(c)) effect; if (!atomic_load(&c->playing)) return; const int32_t channels = c->format.channels; const size_t chunk_frames = CHUNK_SIZE / channels; uint64_t read = 0; while (read < (uint64_t) pcm->frames) { float chunk[CHUNK_SIZE] = {0}; jukebox_effect_pcm_t chunk_pcm = { .ptr = chunk, .frames = MATH_MIN(chunk_frames, pcm->frames - read), }; for (size_t i = 0; i < c->effects_length; ++i) { jukebox_effect_affect(c->effects[i], &chunk_pcm); } const size_t len = chunk_pcm.frames * channels; const float* src = chunk; float* dst = pcm->ptr + read * channels; for (uint64_t i = 0; i < len; ++i) { *(dst++) += *(src++); } read += chunk_pcm.frames; } # undef CHUNK_SIZE } jukebox_composite_t* jukebox_composite_new( const jukebox_format_t* format, size_t reserve) { assert(jukebox_format_valid(format)); assert(reserve > 0); jukebox_composite_t* compo = memory_new(sizeof(*compo) + (reserve-1)*sizeof(compo->effects[0])); *compo = (typeof(*compo)) { .super = { .vtable = { .affect = jukebox_composite_affect_, }, }, .format = *format, .effects_reserved = reserve, }; return compo; } void jukebox_composite_delete(jukebox_composite_t* compo) { if (compo == NULL) return; memory_delete(compo); } void jukebox_composite_add_effect( jukebox_composite_t* compo, jukebox_effect_t* effect) { assert(compo != NULL); assert(effect != NULL); assert(!atomic_load(&compo->playing)); if (compo->effects_length >= compo->effects_reserved) { fprintf(stderr, "jukebox: composite effect overflow\n"); abort(); } compo->effects[compo->effects_length++] = effect; } void jukebox_composite_play(jukebox_composite_t* compo) { assert(compo != NULL); atomic_store(&compo->playing, true); } void jukebox_composite_stop(jukebox_composite_t* compo) { assert(compo != NULL); atomic_store(&compo->playing, false); }