/* Seedbox engine: C port of sim/engine.js. Portable C99, no SDK, no allocation, integer-only. sim/engine.js is the reference; this file must reproduce test/vectors bit for bit. Not reentrant: render from one core only (scratch buffers are static). */ #ifndef SEEDBOX_ENGINE_H #define SEEDBOX_ENGINE_H #include #define SB_TPB 840 /* ticks per quarter */ #define SB_TPS 210 /* ticks per 16th */ #define SB_CLOCK_TICKS 35 /* MIDI clock every 35 ticks */ #define SB_PHRASE_BARS 4 #define SB_NT 11 /* tracks */ #define SB_NSCALES 21 #define SB_MAX_EVENTS 128 /* per bar and per scratch list; the vectors peak at 54 */ enum { SB_T_KICK, SB_T_KICK2, SB_T_SNARE, SB_T_GHOST, SB_T_HAT, SB_T_HAT16, SB_T_PERC, SB_T_BASS, SB_T_MELA, SB_T_MELB, SB_T_PAD }; enum { SB_GEN_EUCLID, SB_GEN_SKELETON, SB_GEN_SUBDIV, SB_GEN_AUTOMATA, SB_GEN_AMENIZER, SB_NGEN }; enum { SB_KEYS_MOTIF, SB_KEYS_303, SB_KEYS_TURING, SB_KEYS_ARP, SB_NKEYS }; /* hash and RNG */ uint32_t sb_mix(uint32_t a, uint32_t b); typedef struct { uint32_t s; } sb_rng; void sb_rng_init(sb_rng *r, uint32_t seed); uint32_t sb_rng_next(sb_rng *r); int sb_rng_below(sb_rng *r, int n); int sb_rng_chance(sb_rng *r, int p); /* genome: field names follow engine.js */ typedef struct { int steps; int form_idx; /* index into FORMS; form[] is a copy */ int form[4]; int scale, root; int dens[2]; int fillP, monoLen, monoRange, rot, hocket, bassDeg; int prog_idx; /* index into PROGS; prog[] is a copy */ int prog[4]; int swing, arc, motifLen, accentCycle, layback, frame, freeK, freeRot; } sb_genome; typedef struct { uint32_t seed; uint32_t salts[2]; } sb_state; /* shared part from the seed; kit and keys parts also take their salt (ROLL KIT / ROLL KEYS) */ void sb_genome_make(const sb_state *st, sb_genome *g); const char *sb_form_name(int form_idx); typedef struct { int gen, keys, energy, drift, fill, scale, root; } sb_params; /* scale, root: -1 = AUTO (the seed picks) */ /* one output row: [t, lane, v, vel, len, tr] */ typedef struct { int16_t t, len; uint8_t lane, v, vel, tr; } sb_event; typedef struct { int bar, pos, phrase, sec, fill, variants[2], barTicks, steps; int overflow; /* nonzero if any list hit SB_MAX_EVENTS (should never happen) */ int n; sb_event ev[SB_MAX_EVENTS]; } sb_bar; /* ROOT / SCALE pages: overrides the genome's scale and root when they're >= 0 (app.js applyKey). */ void sb_apply_key(sb_genome *g, const sb_params *p); const char *sb_scale_name(int scale); const char *sb_keys_name(int keys); /* MOTIF, 303, TURING, ARP */ /* screen name, at most 10 characters */ void sb_render_bar(const sb_state *st, const sb_params *p, int bar, sb_bar *out); /* FNV-1a over rows, each field & 0xFFFF. Feed a whole case's events in order, starting from SB_FNV_INIT. */ #define SB_FNV_INIT 0x811C9DC5u uint32_t sb_checksum_event(uint32_t h, const sb_event *e); #endif