Some checks failed
check / check (push) Has been cancelled
- genome(seed, salts) now has three parts with their own RNG streams: shared (steps, form, swing, arc, layback, fill odds, phase offset), kit (density, frame, accent cycle, free snare) and keys (scale/root, progression, motif, hocket, bass tone, note lengths, range). ROLL KEYS now renews the harmony too; ROLL KIT leaves the keys alone. - Keys generators share one signature and can fix note lengths: MOTIF (the old line), 303 (acid bass, answers in the rests, drone or silent pad), TURING (8/16-bit shift register, DRIFT flips bits, 16-bar window), ARP (triad/7th arpeggio, roots on kicks). - Pages: KIT GEN, KEYS GEN; ROLL shows KIT / KEYS; slots keep both gens. - Flash record v3 (old settings ignored). Bring-up golden checksums updated. - C port matches; host test and ASan/UBSan pass. Intended music change: every seed sounds different (genome split, new generators). Vectors regenerated with npm run vectors; the case grid now crosses all kit and keys generators, with TURING runs past its window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
908 lines
46 KiB
C
908 lines
46 KiB
C
/* Seedbox engine: C port of sim/engine.js (the reference). Read the two side by side:
|
|
functions keep their JS names (snake_case) and order. Comments explaining the music live in engine.js.
|
|
|
|
Port rules:
|
|
- Hashes and RNG values are uint32_t. Math.imul(a,b)>>>0 == (uint32_t)a*(uint32_t)b.
|
|
- fdiv/mod are floor division and positive modulo; use them wherever the left side can be negative.
|
|
- One RNG call per statement. C leaves the evaluation order of operands and arguments unspecified,
|
|
so f(r.below(), r.below()) would not match JS's left-to-right order.
|
|
- Sorts are stable insertion sorts (JS Array.prototype.sort is stable and the engine relies on it).
|
|
- Object key sets keyed by step (fs.K, fs.S) are flag arrays walked in ascending order, which is
|
|
how JS enumerates integer keys. */
|
|
#include "engine.h"
|
|
#include <string.h>
|
|
|
|
#define TPB SB_TPB
|
|
#define TPS SB_TPS
|
|
#define PHRASE_BARS SB_PHRASE_BARS
|
|
#define MAX_STEPS 20
|
|
|
|
static int g_overflow;
|
|
|
|
/* ---------- primitives ---------- */
|
|
uint32_t sb_mix(uint32_t a, uint32_t b){
|
|
uint32_t h = a ^ (b * 0x9E3779B1u);
|
|
h ^= h >> 16; h *= 0x85EBCA6Bu; h ^= h >> 13; h *= 0xC2B2AE35u; h ^= h >> 16;
|
|
return h ? h : 0x6D2B79F5u;
|
|
}
|
|
void sb_rng_init(sb_rng *r, uint32_t s){ r->s = s ? s : 0x6D2B79F5u; }
|
|
uint32_t sb_rng_next(sb_rng *r){ uint32_t x = r->s; x ^= x << 13; x ^= x >> 17; x ^= x << 5; r->s = x; return x; }
|
|
int sb_rng_below(sb_rng *r, int n){
|
|
if(n <= 0) return 0;
|
|
return (int)((uint32_t)((uint64_t)(sb_rng_next(r) >> 16) * (uint32_t)n) >> 16);
|
|
}
|
|
int sb_rng_chance(sb_rng *r, int p){ return (int)(sb_rng_next(r) >> 16) < p; }
|
|
static int pick_w(sb_rng *r, const int *w, int n){
|
|
int t = 0, i, x;
|
|
for(i = 0; i < n; i++) t += w[i];
|
|
x = sb_rng_below(r, t);
|
|
for(i = 0; i < n; i++){ if(x < w[i]) return i; x -= w[i]; }
|
|
return n - 1;
|
|
}
|
|
#define below sb_rng_below
|
|
#define chance sb_rng_chance
|
|
#define mix sb_mix
|
|
static int clamp(int x, int a, int b){ return x < a ? a : x > b ? b : x; }
|
|
static int imin(int a, int b){ return a < b ? a : b; }
|
|
static int imax(int a, int b){ return a > b ? a : b; }
|
|
static int P(int x){ return clamp(x, 0, 127) * 516; }
|
|
static int fdiv(int a, int b){ int q = a / b; if((a % b != 0) && ((a < 0) != (b < 0))) q--; return q; }
|
|
static int mod(int a, int b){ return ((a % b) + b) % b; }
|
|
static sb_rng rng(uint32_t s){ sb_rng r; sb_rng_init(&r, s); return r; }
|
|
|
|
/* ---------- tables ---------- */
|
|
static const int STEPS[4] = {16,12,14,20}, STEPS_W[4] = {12,3,2,2};
|
|
static const int FORMS[6][4] = {{0,0,0,1},{0,0,1,0},{0,1,0,1},{0,0,0,0},{0,1,0,2},{0,1,0,0}}, FORMS_W[6] = {5,5,3,2,3,2};
|
|
static const char *const FORM_NAMES[6] = {"AAAB","AABA","ABAB","AAAA","ABAC","ABAA"};
|
|
static const int SCALES[SB_NSCALES][8] = {
|
|
{0,2,4,5,7,9,11},{0,2,3,5,7,8,10},{0,2,3,5,7,9,10},{0,2,4,5,7,9,10},{0,2,4,6,7,9,11},{0,1,3,5,7,8,10},{0,2,3,5,7,8,11},
|
|
{0,3,5,7,10},{0,2,4,7,9},
|
|
{0,1,4,5,7,8,10},{0,1,4,5,7,8,11},{0,2,3,6,7,8,11},{0,2,3,6,7,9,10},{0,2,4,6,7,9,10},
|
|
{0,2,3,7,8},{0,1,5,7,10},{0,1,5,6,10},{0,2,4,6,9,10},{0,2,4,6,8,10},{0,1,3,4,6,7,9,10},{0,1,3,5,6,8,10}};
|
|
static const int SCALE_LEN[SB_NSCALES] = {7,7,7,7,7,7,7, 5,5, 7,7,7,7,7, 5,5,5,6,6,8,7};
|
|
static const char *const SCALE_NAMES[SB_NSCALES] = {"MAJOR","MINOR","DORIAN","MIXOLYDIAN","LYDIAN","PHRYGIAN","HARM MINOR","MINOR PENT","MAJOR PENT",
|
|
"PHRYG DOM","DBL HARM","HUNG MINOR","UKR DORIAN","LYD DOM","HIRAJOSHI","IN SEN","IWATO","PROMETHEUS","WHOLE TONE","OCTATONIC","LOCRIAN"};
|
|
const char *sb_scale_name(int scale){ return scale >= 0 && scale < SB_NSCALES ? SCALE_NAMES[scale] : "?"; }
|
|
void sb_apply_key(sb_genome *g, const sb_params *p){
|
|
if(p->scale >= 0 && p->scale < SB_NSCALES) g->scale = p->scale;
|
|
if(p->root >= 0 && p->root < 12) g->root = p->root;
|
|
}
|
|
static const int HOCKETS[8] = {0xAA,0x55,0xB4,0x6C,0x96,0xD2,0x5A,0xA5};
|
|
static const int PROGS[10][4] = {{0,0,5,4},{0,5,3,4},{0,3,0,4},{0,6,5,4},{0,2,5,4},{0,0,3,3},{0,5,0,5},{0,3,5,6},{0,1,0,6},{0,4,5,3}};
|
|
static const int SWINGS[5] = {0,21,35,50,70}, SWING_W[5] = {4,3,3,2,1};
|
|
static const int SPLITS[6] = {1,2,3,4,5,7};
|
|
typedef struct { int k[4], nk, s[2], ns, g[3], ng; } skel_t;
|
|
static const skel_t SKEL[6] = {
|
|
{{0,10},2, {4,12},2, {7,15},2}, /* two-step */
|
|
{{0,2,10,11},4, {4,12},2, {7,9,15},3}, /* amen-ish */
|
|
{{0,11},2, {8},1, {14,3},2}, /* half-time */
|
|
{{0,3,10},3, {4,12},2, {7},1}, /* think-ish */
|
|
{{0,6,11},3, {4,13},2, {9,15},2}, /* broken */
|
|
{{0,7,10},3, {4,12},2, {2,14},2} /* steppy */
|
|
};
|
|
typedef struct { int k[4], nk, s[2], ns, layer, noOne, cyc, free_; } frame_t;
|
|
static const frame_t FRAMES[8] = {
|
|
{{0},1, {4,12},2, 0,0,0,0}, /* backbeat */
|
|
{{0},1, {8},1, 0,0,0,0}, /* half-time */
|
|
{{0,4,8,12},4, {4,12},2, 1,0,0,0}, /* four-floor */
|
|
{{0,3,8,11},4, {6,14},2, 0,0,0,0}, /* 3-3-2 */
|
|
{{0,10},2, {6,12},2, 0,0,0,0}, /* broken */
|
|
{{3,10},2, {8},1, 0,1,0,0}, /* displaced */
|
|
{{0},0, {0},0, 0,0,12,0}, /* over the bar */
|
|
{{0},1, {0},0, 0,0,0,1} /* free snare */
|
|
};
|
|
static const int FRAME_W[8] = {3,3,2,2,2,1,1,2};
|
|
static const int RULES[14] = {30,45,54,57,60,73,90,105,110,122,126,150,182,225};
|
|
static const int POLY_N[6] = {5,7,9,11,12,6};
|
|
static const int NUDGE_GRP[SB_NT] = {0,0,1,3,2,2,3,0,4,4,5};
|
|
|
|
const char *sb_form_name(int form_idx){ return FORM_NAMES[form_idx]; }
|
|
|
|
/* ---------- event lists ---------- */
|
|
typedef struct { int16_t t, v, vel, a, tr, len; } ev_t; /* len: keys generators' fixed length, 0 = the default rule */
|
|
typedef struct { int n; ev_t e[SB_MAX_EVENTS]; } evlist;
|
|
|
|
static void push_tr(evlist *l, int t, int v, int vel, int a, int tr){
|
|
ev_t *e;
|
|
if(l->n >= SB_MAX_EVENTS){ g_overflow = 1; return; }
|
|
e = &l->e[l->n++];
|
|
e->t = (int16_t)t; e->v = (int16_t)v; e->vel = (int16_t)vel; e->a = (int16_t)a; e->tr = (int16_t)tr; e->len = 0;
|
|
}
|
|
static void push_len(evlist *l, int t, int v, int vel, int tr, int len){ int n0 = l->n; push_tr(l, t, v, vel, 0, tr); if(l->n > n0) l->e[l->n - 1].len = (int16_t)len; }
|
|
static void push(evlist *l, int t, int v, int vel, int a){ push_tr(l, t, v, vel, a, 0); }
|
|
static void push_ev(evlist *l, const ev_t *e){ if(l->n >= SB_MAX_EVENTS){ g_overflow = 1; return; } l->e[l->n++] = *e; }
|
|
|
|
/* keep the events where cond holds; inside cond, e points at the current event */
|
|
#define FILTER(l, cond) do{ int o_ = 0, i_; for(i_ = 0; i_ < (l)->n; i_++){ const ev_t *e = &(l)->e[i_]; if(cond) (l)->e[o_++] = *e; } (l)->n = o_; }while(0)
|
|
|
|
static void sort_tv(evlist *l){ /* stable, by (t, v) */
|
|
int i, j;
|
|
for(i = 1; i < l->n; i++){
|
|
ev_t x = l->e[i];
|
|
for(j = i - 1; j >= 0 && (l->e[j].t > x.t || (l->e[j].t == x.t && l->e[j].v > x.v)); j--) l->e[j + 1] = l->e[j];
|
|
l->e[j + 1] = x;
|
|
}
|
|
}
|
|
|
|
/* ---------- genome ---------- */
|
|
static uint32_t role_seed(const sb_state *st, int r){ return mix(mix(st->seed, 0x524F4C45u + (uint32_t)r), st->salts[r]); }
|
|
void sb_genome_make(const sb_state *st, sb_genome *g){
|
|
static const int ROT_W[4] = {4,2,2,1}, BASS_DEG[3] = {4,2,3}, ARC_W[3] = {3,2,2};
|
|
static const int ACC[5] = {0,3,5,3,7}, ACC_W[5] = {3,3,2,2,1};
|
|
sb_rng r = rng(mix(st->seed, 0x47454E)), k, l;
|
|
int fi;
|
|
/* shared */
|
|
g->steps = STEPS[pick_w(&r, STEPS_W, 4)];
|
|
fi = pick_w(&r, FORMS_W, 6); g->form_idx = fi; memcpy(g->form, FORMS[fi], sizeof g->form);
|
|
g->fillP = 40 + below(&r, 80);
|
|
g->rot = pick_w(&r, ROT_W, 4);
|
|
g->swing = SWINGS[pick_w(&r, SWING_W, 5)];
|
|
g->arc = pick_w(&r, ARC_W, 3);
|
|
g->layback = below(&r, 2);
|
|
/* kit */
|
|
k = rng(mix(role_seed(st, 0), 0x4B4954));
|
|
g->dens[0] = 40 + below(&k, 60); g->dens[1] = 0;
|
|
g->accentCycle = ACC[pick_w(&k, ACC_W, 5)];
|
|
g->frame = pick_w(&k, FRAME_W, 8); g->freeK = 2 + below(&k, 3); g->freeRot = below(&k, 16);
|
|
/* keys */
|
|
l = rng(mix(role_seed(st, 1), 0x4B4559));
|
|
g->scale = below(&l, SB_NSCALES); g->root = below(&l, 12);
|
|
g->dens[1] = 30 + below(&l, 70);
|
|
g->monoLen = below(&l, 3); g->monoRange = 1 + below(&l, 2);
|
|
g->hocket = HOCKETS[below(&l, 8)];
|
|
g->bassDeg = BASS_DEG[below(&l, 3)];
|
|
g->prog_idx = below(&l, 10); memcpy(g->prog, PROGS[g->prog_idx], sizeof g->prog);
|
|
g->motifLen = 3 + below(&l, 3);
|
|
}
|
|
static int eu(int i, int k, int n, int rot){ int j = (i + rot) % n; return ((j * k) % n) < k; }
|
|
/* groups(steps): [s, min(4, steps-s)] for s = 0, 4, 8, ... */
|
|
static int n_groups(int steps){ return (steps + 3) / 4; }
|
|
static int group_len(int steps, int gi){ return imin(4, steps - gi * 4); }
|
|
|
|
typedef struct { uint8_t K[MAX_STEPS], S[MAX_STEPS]; int noOne; } fsets;
|
|
static int sc16(int x, int steps){ return x * steps / 16; }
|
|
static void frame_sets(const sb_genome *g, int steps, int pos, fsets *fs){
|
|
const frame_t *F = &FRAMES[g->frame];
|
|
int i;
|
|
memset(fs, 0, sizeof *fs);
|
|
if(F->cyc){ for(i = 0; i < steps; i++){ int x = pos * steps + i; if(x % F->cyc == 0) fs->K[i] = 1; else if(x % F->cyc == (F->cyc >> 1)) fs->S[i] = 1; } }
|
|
else {
|
|
for(i = 0; i < F->nk; i++) fs->K[sc16(F->k[i], steps)] = 1;
|
|
if(F->free_){ for(i = 1; i < steps; i++) if(eu(i, g->freeK, steps, g->freeRot) && !fs->K[i]) fs->S[i] = 1; }
|
|
else for(i = 0; i < F->ns; i++) fs->S[sc16(F->s[i], steps)] = 1;
|
|
}
|
|
if(!F->layer) for(i = 0; i < steps; i++) if(fs->S[i]) fs->K[i] = 0;
|
|
fs->noOne = F->noOne;
|
|
}
|
|
|
|
/* divide(): velFn is chosen by mode */
|
|
enum { VEL_SUB_KICK, VEL_SUB_GHOST, VEL_SUB_BURST, VEL_SUB_HAT, VEL_SUB_PERC };
|
|
static int divide_vel(sb_rng *r, int mode, int depth){
|
|
switch(mode){
|
|
case VEL_SUB_KICK: return 96 + below(r, 16);
|
|
case VEL_SUB_GHOST: return 34 + below(r, 22);
|
|
case VEL_SUB_BURST: return 50 + below(r, 30);
|
|
case VEL_SUB_HAT: return depth == 0 ? 90 : 58 + below(r, 20);
|
|
default: return 60 + below(r, 45);
|
|
}
|
|
}
|
|
static void divide(sb_rng *r, int start, int len, int depth, int maxD, const int *W, int prob, evlist *out, int v, int mode){
|
|
if(depth < maxD){
|
|
int w[6], f, i;
|
|
memcpy(w, W, sizeof w); if(depth > 0) w[0] += depth * 3;
|
|
f = SPLITS[pick_w(r, w, 6)];
|
|
if(f > 1){ for(i = 0; i < f; i++){ int a = start + i * len / f, b = start + (i + 1) * len / f; divide(r, a, b - a, depth + 1, maxD, W, prob, out, v, mode); } return; }
|
|
}
|
|
if(chance(r, prob)){ int vel = divide_vel(r, mode, depth); push(out, start, v, vel, 0); }
|
|
}
|
|
|
|
/* ---------- kit generators ---------- */
|
|
typedef struct { int k, n, rot; } euc_t;
|
|
typedef struct { euc_t kick, ghost, hat, perc; int accent; } euclid_params_t;
|
|
static void euclid_params(uint32_t coreSeed, int steps, int d, int hd, euclid_params_t *E){
|
|
static const int K_OPTS[5] = {3,5,4,2,7}, HN_W[5] = {3,3,1,1,1};
|
|
int w[5] = {4,3,2,1,1 + (d >> 5)};
|
|
sb_rng c = rng(coreSeed);
|
|
int k = K_OPTS[pick_w(&c, w, 5)], rots[MAX_STEPS] = {0}, nr = 0, r0, hn, pn;
|
|
k = clamp(k, 1, steps >> 1);
|
|
for(r0 = 0; r0 < steps; r0++) if(((r0 * k) % steps) < k) rots[nr++] = r0;
|
|
E->kick.k = k; E->kick.n = steps; E->kick.rot = rots[below(&c, nr)];
|
|
E->ghost.k = 1 + below(&c, 3); E->ghost.n = steps; E->ghost.rot = below(&c, steps);
|
|
hn = pick_w(&c, HN_W, 5) < 2 ? steps : POLY_N[below(&c, 3)];
|
|
E->hat.k = clamp((hn * (32 + (hd >> 2))) >> 7, 1, hn); E->hat.n = hn; E->hat.rot = below(&c, hn);
|
|
pn = POLY_N[below(&c, 6)];
|
|
E->perc.k = 1 + ((pn * d) >> 9); E->perc.k += below(&c, 2); E->perc.k = clamp(E->perc.k, 1, pn - 1);
|
|
E->perc.n = pn; E->perc.rot = below(&c, pn);
|
|
E->accent = below(&c, E->hat.n);
|
|
}
|
|
static void kit_euclid(uint32_t coreSeed, uint32_t varSeed, int steps, int d, int hd, int pos, const fsets *fs, evlist *ev){
|
|
euclid_params_t E;
|
|
sb_rng vr = rng(varSeed), hr = rng(mix(varSeed, 0x484154u + (uint32_t)pos));
|
|
int i;
|
|
euclid_params(coreSeed, steps, d, hd, &E);
|
|
for(i = 0; i < steps; i++){
|
|
int t = i * TPS, x = pos * steps + i;
|
|
if(fs->S[i]) push(ev, t, 1, 112, 0);
|
|
else if(eu(i, E.ghost.k, E.ghost.n, E.ghost.rot) && chance(&vr, P(d >> 1))){ int vel = 38 + below(&vr, 20); push(ev, t, 1, vel, 0); }
|
|
if(fs->K[i]) push(ev, t, 0, i == 0 ? 120 : 112, 1);
|
|
else if(eu(i, E.kick.k, E.kick.n, E.kick.rot) && !fs->S[i] && !(i == 0 && fs->noOne)) push(ev, t, 0, 104, 0);
|
|
if(eu(x, E.hat.k, E.hat.n, E.hat.rot)){
|
|
int acc = eu(x, imax(1, E.hat.k >> 1), E.hat.n, E.accent);
|
|
push(ev, t, 2, acc ? 92 : 66, 0);
|
|
if(chance(&hr, P(hd >> 3))) push(ev, t + (TPS >> 1), 2, 44, 0);
|
|
}
|
|
if(eu(x, E.perc.k, E.perc.n, E.perc.rot)){ int vel = 70 + below(&vr, 40); push(ev, t, 3, vel, 0); }
|
|
}
|
|
}
|
|
static void kit_subdiv(uint32_t coreSeed, uint32_t varSeed, int steps, int d, int hd, int pos, const fsets *fs, evlist *ev){
|
|
static const int W_KICK[6] = {6,5,1,0,0,0}, W_GHOST[6] = {4,4,1,0,0,0}, W_BURST[6] = {1,3,4,2,3,2}, W_HAT[6] = {3,6,0,2,0,0}, W_PERC[6] = {6,2,3,0,1,1};
|
|
static evlist tmp;
|
|
sb_rng core = rng(coreSeed), vr = rng(varSeed), hr = rng(mix(varSeed, 0x484154u + (uint32_t)pos));
|
|
int ng = n_groups(steps), snT[MAX_STEPS], nsn = 0, gi, j, k, burst = -1;
|
|
if(chance(&core, P(d >> 1))) burst = below(&core, ng);
|
|
for(j = 0; j < steps; j++){
|
|
if(fs->S[j]){ int vel = 110 + below(&core, 14); push(ev, j * TPS, 1, vel, 0); snT[nsn++] = j * TPS; }
|
|
if(fs->K[j]) push(ev, j * TPS, 0, j == 0 ? 118 : 112, 1);
|
|
}
|
|
for(gi = 0; gi < ng; gi++){
|
|
int s = gi * 4 * TPS, L = group_len(steps, gi) * TPS;
|
|
tmp.n = 0;
|
|
divide(&core, s, L, 0, 1, W_KICK, P(10 + (d >> 2)), &tmp, 0, VEL_SUB_KICK);
|
|
for(j = 0; j < tmp.n; j++){
|
|
int inSn = 0;
|
|
for(k = 0; k < nsn; k++) if(snT[k] == tmp.e[j].t) inSn = 1;
|
|
if(!(tmp.e[j].t == 0 && fs->noOne) && !inSn) push_ev(ev, &tmp.e[j]);
|
|
}
|
|
divide(&vr, s, L, 0, 1, W_GHOST, P(d >> 3), ev, 1, VEL_SUB_GHOST);
|
|
if(gi == burst) divide(&hr, s, L, 0, 2, W_BURST, P(90), ev, 2, VEL_SUB_BURST);
|
|
else divide(&hr, s, L, 0, 1, W_HAT, P(36 + (hd >> 2)), ev, 2, VEL_SUB_HAT);
|
|
divide(&vr, s, L, 0, 1, W_PERC, P(d >> 2), ev, 3, VEL_SUB_PERC);
|
|
}
|
|
}
|
|
static int contains(const int *a, int n, int x){ int i; for(i = 0; i < n; i++) if(a[i] == x) return 1; return 0; }
|
|
static void kit_skeleton(uint32_t coreSeed, uint32_t varSeed, int steps, int d, int hd, int pos, const fsets *fs, int frame, evlist *ev){
|
|
static const int DIVS[3] = {2,2,1};
|
|
sb_rng core = rng(coreSeed), vr = rng(varSeed), hr = rng(mix(varSeed, 0x484154u + (uint32_t)pos));
|
|
const skel_t *sk = &SKEL[below(&core, 6)];
|
|
int sn[MAX_STEPS], nsn = 0, i, div, hp, ek, rot;
|
|
if(frame == 0) for(i = 0; i < sk->ns; i++) sn[nsn++] = sc16(sk->s[i], steps);
|
|
else for(i = 0; i < steps; i++) if(fs->S[i]) sn[nsn++] = i;
|
|
for(i = 0; i < steps; i++) if(fs->K[i]) push(ev, i * TPS, 0, 116, 1);
|
|
for(i = 0; i < sk->nk; i++){
|
|
int s = sc16(sk->k[i], steps);
|
|
if(sk->k[i] == 0) continue;
|
|
if(chance(&core, P(d >> 2))){ int dir = below(&core, 2) ? 1 : -1; s = clamp(s + dir, 1, steps - 1); }
|
|
if(!contains(sn, nsn, s)){ int vel = 104 + below(&core, 12); push(ev, s * TPS, 0, vel, 0); }
|
|
}
|
|
for(i = 0; i < nsn; i++){ int vel = 110 + below(&core, 14); push(ev, sn[i] * TPS, 1, vel, 0); }
|
|
for(i = 0; i < sk->ng; i++) if(chance(&vr, P(d))){ int vel = 36 + below(&vr, 24); push(ev, sc16(sk->g[i], steps) * TPS, 1, vel, 0); }
|
|
if(chance(&vr, P(d >> 1))){ int xs = 1 + below(&vr, steps - 1); if(!contains(sn, nsn, xs)){ int vel = 90 + below(&vr, 20); push(ev, xs * TPS, 0, vel, 0); } }
|
|
div = DIVS[below(&core, 3)]; hp = P(36 + (hd >> 2));
|
|
for(i = 0; i < steps; i += div){
|
|
if(i % 4 == 0) push(ev, i * TPS, 2, 92, 0);
|
|
else if(chance(&hr, hp)){
|
|
int vel = 60 + below(&hr, 22);
|
|
if(chance(&hr, P(hd >> 3))){ int L = TPS * div; push(ev, i * TPS, 2, vel, 0); push(ev, i * TPS + (L >> 1), 2, vel - 8, 0); }
|
|
else push(ev, i * TPS, 2, vel, 0);
|
|
}
|
|
}
|
|
ek = 1 + ((d * steps) >> 8); ek += below(&core, 3); ek = clamp(ek, 1, steps - 1);
|
|
rot = below(&core, steps);
|
|
for(i = 0; i < steps; i++) if(eu(i, ek, steps, rot)){ int vel = 64 + below(&vr, 40); push(ev, i * TPS, 3, vel, 0); }
|
|
}
|
|
static void ca_row(int rule, uint8_t *row, int steps, int gens){ /* in place */
|
|
uint8_t nx[MAX_STEPS];
|
|
int g, i;
|
|
for(g = 0; g < gens; g++){
|
|
for(i = 0; i < steps; i++){ int l = row[(i + steps - 1) % steps], c = row[i], r = row[(i + 1) % steps]; nx[i] = (uint8_t)((rule >> ((l << 2) | (c << 1) | r)) & 1); }
|
|
memcpy(row, nx, (size_t)steps);
|
|
}
|
|
}
|
|
static void rand_row(sb_rng *r, int steps, int p, uint8_t *a){ int i; for(i = 0; i < steps; i++) a[i] = (uint8_t)(chance(r, p) ? 1 : 0); }
|
|
static void kit_ca(uint32_t coreSeed, uint32_t varSeed, int steps, int d, int hd, int pos, int sec, const fsets *fs, evlist *ev){
|
|
sb_rng core = rng(coreSeed), vr = rng(varSeed), thin = rng(mix(varSeed, 0x5448u + (uint32_t)pos));
|
|
int gen = sec * 5 + pos, i, rk, rs, rh, rp;
|
|
uint8_t K[MAX_STEPS], S[MAX_STEPS], H[MAX_STEPS], C[MAX_STEPS];
|
|
rk = RULES[below(&core, 14)]; rs = RULES[below(&core, 14)]; rh = RULES[below(&vr, 14)]; rp = RULES[below(&vr, 14)];
|
|
rand_row(&core, steps, P(40), K); ca_row(rk, K, steps, gen);
|
|
rand_row(&core, steps, P(40), S); ca_row(rs, S, steps, gen);
|
|
rand_row(&vr, steps, P(64), H); ca_row(rh, H, steps, gen + pos);
|
|
rand_row(&vr, steps, P(40), C); ca_row(rp, C, steps, gen);
|
|
for(i = 0; i < steps; i++){
|
|
int t = i * TPS, back = fs->S[i];
|
|
if(back) push(ev, t, 1, 112, 0);
|
|
else if(S[i] && chance(&thin, P(d >> 1))){ int vel = 40 + below(&thin, 20); push(ev, t, 1, vel, 0); }
|
|
if(fs->K[i]) push(ev, t, 0, 118, 1);
|
|
else if(K[i] && !back && !(i == 0 && fs->noOne) && chance(&thin, P(40 + (d >> 1)))){ int vel = 96 + below(&thin, 20); push(ev, t, 0, vel, 0); }
|
|
if(H[i] && chance(&thin, P(56 + (hd >> 2)))){ int nb = H[(i + steps - 1) % steps] + H[(i + 1) % steps]; push(ev, t, 2, 58 + nb * 16, 0); }
|
|
if(C[i] && !C[(i + 1) % steps] && chance(&thin, P(d))){ int vel = 70 + below(&thin, 40); push(ev, t, 3, vel, 0); }
|
|
}
|
|
}
|
|
|
|
/* ---------- AMENIZER ---------- */
|
|
/* [step, voice, velocity, main flag]; the listed hits, then the ride from ride() in engine.js */
|
|
typedef struct { uint8_t s, v, vel, a; } amen_hit;
|
|
#define RIDE8 {0,2,82,1},{2,2,60,0},{4,2,82,1},{6,2,60,0},{8,2,82,1},{10,2,60,0},{12,2,82,1},{14,2,60,0}
|
|
static const amen_hit AMEN_A[] = {{0,0,112,1},{2,0,84,0},{10,0,104,1},{11,0,80,0},{4,1,112,1},{7,1,52,0},{9,1,86,1},{12,1,110,1},{15,1,58,0}, RIDE8};
|
|
static const amen_hit AMEN_C[] = {{0,0,112,1},{2,0,84,0},{10,0,104,1},{4,1,112,1},{7,1,52,0},{9,1,86,1},{14,1,110,1}, RIDE8};
|
|
static const amen_hit AMEN_D[] = {{2,0,104,1},{3,0,84,0},{10,0,108,1},{1,1,48,0},{4,1,112,1},{7,1,54,0},{9,1,88,1},{14,1,112,1},{10,3,110,0},
|
|
{0,2,82,1},{2,2,60,0},{4,2,82,1},{6,2,60,0},{8,2,82,1},{12,2,82,1},{14,2,60,0}}; /* crash at 10 replaces the ride */
|
|
static const amen_hit *const AMEN[4] = {AMEN_A, AMEN_A, AMEN_C, AMEN_D};
|
|
static const int AMEN_N[4] = {sizeof AMEN_A / sizeof AMEN_A[0], sizeof AMEN_A / sizeof AMEN_A[0], sizeof AMEN_C / sizeof AMEN_C[0], sizeof AMEN_D / sizeof AMEN_D[0]};
|
|
#define AMEN_SLICE_MAX 16
|
|
|
|
static void humanize_amen(evlist *ev, uint32_t seed, int bar){
|
|
int last[4] = {-1000000,-1000000,-1000000,-1000000}, lastV[4] = {0,0,0,0}, i, rideBias;
|
|
sb_rng br = rng(mix(mix(seed, 0x48554D), (uint32_t)bar));
|
|
sort_tv(ev);
|
|
rideBias = below(&br, 11) - 5;
|
|
for(i = 0; i < ev->n; i++){
|
|
ev_t *x = &ev->e[i];
|
|
int base = x->vel;
|
|
uint32_t h = mix(mix(mix(seed, 0x564C), (uint32_t)bar), (uint32_t)(x->t * 4 + x->v));
|
|
int v = base + (int)(h % 13) - 6;
|
|
if(x->v == 2) v += rideBias;
|
|
if(x->v < 2 && x->t - last[x->v] <= 2 * TPS && lastV[x->v] * 10 >= base * 9) v = fdiv(v * 80, 100);
|
|
last[x->v] = x->t; lastV[x->v] = base; x->vel = (int16_t)clamp(v, 1, 127);
|
|
}
|
|
}
|
|
static int amen_feel(uint32_t seed, int bar, int t, int v, int a){
|
|
uint32_t h = mix(mix(mix(seed, 0x46454C), (uint32_t)bar), (uint32_t)(t * 4 + v));
|
|
#define J(n) ((int)(h % (uint32_t)(2 * (n) + 1)) - (n))
|
|
if(v == 0) return J(4);
|
|
if(v == 1) return a ? 10 + J(5) : J(10);
|
|
if(v == 2) return -4 + J(6);
|
|
return J(4);
|
|
#undef J
|
|
}
|
|
static int amen_slice(int bar, int beat, amen_hit *out){
|
|
const amen_hit *b = AMEN[bar & 3];
|
|
int i, n = 0;
|
|
for(i = 0; i < AMEN_N[bar & 3]; i++) if(b[i].s >= beat * 4 && b[i].s < beat * 4 + 4) out[n++] = b[i];
|
|
return n;
|
|
}
|
|
static void kit_amen(uint32_t seed, int bar, int pos, int dr, int fill, evlist *ev){
|
|
static const int PB[4] = {2,3,5,6};
|
|
amen_hit sl[AMEN_SLICE_MAX], prev[AMEN_SLICE_MAX];
|
|
int src = fill ? 3 : pos, nprev = -1, j, k; /* nprev < 0: no previous beat yet (JS prev === null) */
|
|
for(j = 0; j < 4; j++){
|
|
int nsl = amen_slice(src, j, sl), op = -1;
|
|
sb_rng r = rng(mix(mix(seed, 0x414D454E), (uint32_t)(bar * 4 + j)));
|
|
int pc = fdiv(dr * 80, 127), pb = fdiv(pc * PB[j], 4), keep = (pos == 0 && j == 0) || (j == 0 && dr < 88), base;
|
|
if(!keep && chance(&r, P(pb))){ int w[5] = {4,2,2,2,dr > 80 ? 1 : 0}; op = pick_w(&r, w, 5); }
|
|
if(op == 0){ int b = below(&r, 4); nsl = amen_slice(b, j, sl); }
|
|
else if(op == 4){ int b = below(&r, 4), bt = below(&r, 4); nsl = amen_slice(b, bt, sl); op = 0; }
|
|
else if(op == 1 && nprev >= 0){ memcpy(sl, prev, sizeof sl); nsl = nprev; }
|
|
base = j * 4 * TPS;
|
|
for(k = 0; k < nsl; k++){
|
|
const amen_hit *s = &sl[k];
|
|
int rel = s->s % 4, t = base + rel * TPS;
|
|
if(op == 2){ if(rel < 2){ push(ev, t, s->v, s->vel, s->a); push(ev, t + 2 * TPS, s->v, imax(1, s->vel - 10), s->a); } continue; }
|
|
push(ev, t, s->v, s->vel, s->a);
|
|
if(op == 3 && s->v == 1) push(ev, t + (TPS >> 1), 1, imax(1, s->vel - 24), 0);
|
|
}
|
|
memcpy(prev, sl, sizeof prev); nprev = nsl;
|
|
}
|
|
}
|
|
|
|
/* ---------- sections, arc, shaping, fill ---------- */
|
|
static void transform_kit(evlist *ev, int steps, int sec, uint32_t seed, const fsets *fs){
|
|
sb_rng r;
|
|
int ops[2] = {0, 0}, nops = 1, i, half = (steps >> 1) * TPS, barT = steps * TPS;
|
|
if(sec == 0) return;
|
|
r = rng(mix(seed, 0x5345u + (uint32_t)sec));
|
|
ops[0] = below(&r, 8);
|
|
if(chance(&r, P(64))) ops[nops++] = below(&r, 8);
|
|
#define HAS(o) (ops[0] == (o) || (nops > 1 && ops[1] == (o)))
|
|
if(HAS(0)){
|
|
int lastK = -1;
|
|
for(i = 0; i < ev->n; i++) if(ev->e[i].v == 0 && ev->e[i].t > 0) lastK = i;
|
|
if(lastK >= 0 && ev->e[lastK].t + TPS < barT) ev->e[lastK].t += TPS;
|
|
push(ev, 3 * TPS, 0, 98, 0);
|
|
}
|
|
if(HAS(1)){
|
|
static int hs[SB_MAX_EVENTS]; /* static: keeps the Pico's 2 KB stacks free */
|
|
int nh = 0;
|
|
for(i = 0; i < ev->n; i++) if(ev->e[i].v == 2 && ev->e[i].t >= half && ev->e[i].t % TPS == 0) hs[nh++] = ev->e[i].t;
|
|
for(i = 0; i < nh; i++) if(hs[i] + TPS < barT) push(ev, hs[i] + TPS, 2, 60, 0);
|
|
}
|
|
if(HAS(2)) FILTER(ev, !(e->t < half && (e->v == 3 || (e->v == 1 && e->vel < 90))));
|
|
if(HAS(3)) for(i = 0; i < ev->n; i++) if(ev->e[i].v == 3) ev->e[i].t = (int16_t)((ev->e[i].t + 2 * TPS) % barT);
|
|
if(HAS(4)) for(i = 1; i < steps; i++) if(fs->S[i]) push(ev, (i - 1) * TPS, 1, 44, 0);
|
|
if(HAS(5)){
|
|
int ls = -1;
|
|
for(i = 0; i < ev->n; i++) if(ev->e[i].v == 1 && ev->e[i].vel >= 90) ls = i;
|
|
if(ls >= 0 && ev->e[ls].t + TPS < barT) ev->e[ls].t += TPS;
|
|
}
|
|
if(HAS(6)){
|
|
int kn = 0;
|
|
for(i = 0; i < ev->n; i++) if(ev->e[i].v == 0) kn++;
|
|
if(kn > 1) FILTER(ev, !(e->v == 0 && e->t == 0));
|
|
}
|
|
if(HAS(7)){
|
|
FILTER(ev, !(e->v == 1 && e->vel >= 90));
|
|
push(ev, (steps >> 1) * TPS, 1, 116, 0);
|
|
}
|
|
#undef HAS
|
|
}
|
|
static void arc_kit(evlist *ev, const sb_genome *g, int pos){
|
|
if(g->arc == 1 && pos < 2) FILTER(ev, !((e->v == 3 || (e->v == 1 && e->vel < 90)) && (pos == 0 || e->t % (2 * TPS) != 0)));
|
|
else if(g->arc == 2 && pos == 2){
|
|
int k0 = 1000000000, i;
|
|
for(i = 0; i < ev->n; i++) if(ev->e[i].v == 0 && ev->e[i].t < k0) k0 = ev->e[i].t;
|
|
FILTER(ev, (e->v == 0 && e->t == k0) || (e->v == 1 && e->vel >= 90) || (e->v == 2 && e->t % (2 * TPS) == 0));
|
|
}
|
|
}
|
|
static void shape_kit(evlist *ev, const sb_genome *g, int pos, int aoff, int jit, sb_rng *jr){
|
|
int i;
|
|
for(i = 0; i < ev->n; i++){
|
|
ev_t *e = &ev->e[i];
|
|
int st, sw, w, x, v;
|
|
if(e->v < 2 || e->t % TPS != 0) continue;
|
|
st = e->t / TPS; sw = st + aoff; w = (sw % 4 == 0) ? 100 : (sw % 2 == 0) ? 86 : 74; x = pos * g->steps + sw;
|
|
v = fdiv(e->vel * w, 100);
|
|
if(g->accentCycle && (x % g->accentCycle) == 0) v += 24;
|
|
if(jit) v += below(jr, 2 * jit + 1) - jit;
|
|
e->vel = (int16_t)clamp(v, 1, 127);
|
|
}
|
|
}
|
|
static void kit_fill(evlist *ev, int steps, int e, sb_rng *fr){
|
|
static evlist src;
|
|
int ng = n_groups(steps), lastGi = ng - 1, st = lastGi * 4 * TPS, L = group_len(steps, lastGi) * TPS, i, type, ps;
|
|
type = below(fr, 4);
|
|
src = *ev; ev->n = 0;
|
|
for(i = 0; i < src.n; i++) if(src.e[i].t < st) push_ev(ev, &src.e[i]);
|
|
if(type == 0){
|
|
int m = 4 + fdiv(e, 32);
|
|
push(ev, st, 0, 110, 0);
|
|
for(i = 0; i < m; i++){ int q = m - i; push(ev, st + L - L * q * q / (m * m), 1, 90 + 30 * i / m, 0); }
|
|
}
|
|
else if(type == 1){
|
|
int k = 0, t;
|
|
push(ev, st, 0, 110, 0);
|
|
for(t = st; t < st + L; t += (TPS >> 1), k++) push(ev, t, (k % 2) ? 3 : 1, (k % 2) ? 70 + k * 4 : 92 + k * 3, 0);
|
|
}
|
|
else if(type == 2){ push(ev, st, 0, 116, 0); push(ev, st + L - TPS, 1, 100, 0); push(ev, st + L - (TPS >> 1), 1, 116, 0); }
|
|
else {
|
|
ps = (ng > 1 ? ng - 2 : lastGi) * 4 * TPS;
|
|
for(i = 0; i < src.n; i++){
|
|
const ev_t *x = &src.e[i];
|
|
if(x->t >= ps && x->t < ps + (L >> 1)){ push(ev, st + (x->t - ps), x->v, x->vel, 0); push(ev, st + (L >> 1) + (x->t - ps), x->v, clamp(x->vel + 10, 1, 127), 0); }
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ---------- synth tracks ---------- */
|
|
static int chord_deg(const sb_genome *g, int pos){ return g->prog[pos % PHRASE_BARS]; }
|
|
static int deg_pitch(const sb_genome *g, int base, int idx){ int n = SCALE_LEN[g->scale]; return base + g->root + 12 * fdiv(idx, n) + SCALES[g->scale][mod(idx, n)]; }
|
|
static int mel_pitch(const sb_genome *g, int idx){ int p = deg_pitch(g, 60, idx); while(p < 60) p += 12; while(p > 96) p -= 12; return p; }
|
|
static int is_chord_tone(const sb_genome *g, int pos, int idx){ int n = SCALE_LEN[g->scale], c = chord_deg(g, pos), x = mod(idx - c, n); return x == 0 || x == 2 || x == 4; }
|
|
static int nearest_chord_tone(const sb_genome *g, int pos, int idx){
|
|
int d;
|
|
for(d = 0; d < 4; d++){ if(is_chord_tone(g, pos, idx - d)) return idx - d; if(is_chord_tone(g, pos, idx + d)) return idx + d; }
|
|
return idx;
|
|
}
|
|
typedef struct { int pos[5], cont[5], n, resp; } motif_t;
|
|
static void make_motif(uint32_t rs, const sb_genome *g, motif_t *M){
|
|
static const int STP[6] = {-2,-1,1,2,-4,4}, STP_W[6] = {3,4,4,3,1,1};
|
|
sb_rng r = rng(mix(rs, 0x4D4F54));
|
|
uint8_t used[8] = {1,0,0,0,0,0,0,0};
|
|
int np = 1, i, j;
|
|
M->n = g->motifLen; M->pos[0] = 0; M->cont[0] = 0;
|
|
if(chance(&r, P(40))){ M->pos[0] = below(&r, 2) ? 1 : 2; memset(used, 0, sizeof used); used[M->pos[0]] = 1; }
|
|
while(np < M->n){ int p = 1 + below(&r, 7); if(!used[p]){ used[p] = 1; M->pos[np++] = p; } }
|
|
for(i = 1; i < M->n; i++){ int x = M->pos[i]; for(j = i - 1; j >= 0 && M->pos[j] > x; j--) M->pos[j + 1] = M->pos[j]; M->pos[j + 1] = x; }
|
|
for(i = 1; i < M->n; i++) M->cont[i] = M->cont[i - 1] + STP[pick_w(&r, STP_W, 6)];
|
|
M->resp = below(&r, 3);
|
|
}
|
|
static void sort_dedupe(evlist *l){
|
|
int i, o = 0;
|
|
sort_tv(l);
|
|
for(i = 0; i < l->n; i++){
|
|
if(o > 0 && l->e[o - 1].t == l->e[i].t && l->e[o - 1].v == l->e[i].v){ if(l->e[i].vel > l->e[o - 1].vel) l->e[o - 1].vel = l->e[i].vel; }
|
|
else l->e[o++] = l->e[i];
|
|
}
|
|
l->n = o;
|
|
for(i = 0; i < l->n; i++) l->e[i].vel = (int16_t)clamp(l->e[i].vel, 1, 127);
|
|
}
|
|
static void one_at_a_time(evlist *l){
|
|
int i, o = 0;
|
|
sort_dedupe(l);
|
|
for(i = 0; i < l->n; i++){ if(o > 0 && l->e[o - 1].t == l->e[i].t) l->e[o - 1] = l->e[i]; else l->e[o++] = l->e[i]; }
|
|
l->n = o;
|
|
}
|
|
|
|
typedef struct { const sb_genome *g; const motif_t *M; sb_rng *vr; evlist *mel; int steps, d, pos, fill, last0; } place_ctx;
|
|
static void place(const place_ctx *c, int start, const int *cont, int ncont, int anchor, int velBase){
|
|
const sb_genome *g = c->g;
|
|
int k;
|
|
for(k = 0; k < c->M->n && k < ncont; k++){
|
|
int s = start + c->M->pos[k], idx, vel;
|
|
if(s >= c->steps) break;
|
|
if(c->fill && s >= c->last0) break;
|
|
idx = anchor + cont[k];
|
|
if(s % 4 == 0) idx = nearest_chord_tone(g, c->pos, idx);
|
|
if(chance(c->vr, P(c->d >> 3))) continue;
|
|
vel = velBase + (s % 4 == 0 ? 18 : 0) + below(c->vr, 12);
|
|
push(c->mel, s * TPS, mel_pitch(g, idx), vel, 0);
|
|
if(k + 1 < c->M->n && k + 1 < ncont && chance(c->vr, P(c->d >> 2))){
|
|
int s2 = start + c->M->pos[k + 1];
|
|
if(s2 - s >= 2 && (s + 1) < c->steps) push(c->mel, (s + 1) * TPS, mel_pitch(g, idx + (cont[k + 1] > cont[k] ? 1 : -1)), velBase - 14, 0);
|
|
}
|
|
}
|
|
}
|
|
static int bp(const sb_genome *g, int idx){ int p = deg_pitch(g, 36, idx); while(p > 47) p -= 12; while(p < 36) p += 12; return p; }
|
|
static void line_tracks(uint32_t rs, int variant, const sb_genome *g, int steps, int d, int sec, int pos, const evlist *kit, int fill,
|
|
evlist *mel, evlist *bass, evlist *pad){
|
|
sb_rng core = rng(mix(rs, 0x4D4Fu + (uint32_t)sec)), vr = rng(mix(mix(rs, 0x5641u + (uint32_t)sec), (uint32_t)variant));
|
|
int n = SCALE_LEN[g->scale], barT = steps * TPS, i, j;
|
|
int ng = n_groups(steps), last0 = (ng - 1) * 4, last1 = group_len(steps, ng - 1);
|
|
static int kickList[SB_MAX_EVENTS]; /* static: keeps the Pico's 2 KB stacks free */
|
|
int nk = 0, c = chord_deg(g, pos), anchor, half, rot, nextC, tones[3];
|
|
uint8_t seen[128];
|
|
motif_t M;
|
|
place_ctx pc;
|
|
mel->n = bass->n = pad->n = 0;
|
|
for(i = 0; i < kit->n; i++) if(kit->e[i].v == 0 && kit->e[i].t % TPS == 0) kickList[nk++] = kit->e[i].t;
|
|
make_motif(rs, g, &M);
|
|
pc.g = g; pc.M = &M; pc.vr = &vr; pc.mel = mel; pc.steps = steps; pc.d = d; pc.pos = pos; pc.fill = fill; pc.last0 = last0;
|
|
anchor = c + (chance(&core, P(50)) ? 2 : 0); half = steps >> 1;
|
|
place(&pc, 0, M.cont, M.n, anchor, 82);
|
|
if(d >= 40 || steps >= 16){
|
|
int c2[5], nc2 = M.n, a2 = anchor, st2 = half;
|
|
memcpy(c2, M.cont, sizeof c2);
|
|
if(M.resp == 0) a2 = anchor + 2;
|
|
else if(M.resp == 1) for(j = 0; j < nc2; j++) c2[j] = -c2[j];
|
|
else st2 = half + 1;
|
|
if(pos == PHRASE_BARS - 1) nc2 = imax(1, nc2 - 1);
|
|
place(&pc, st2, c2, nc2, a2, 74);
|
|
}
|
|
if(pos == PHRASE_BARS - 1 && !fill){
|
|
int cs = imax(0, steps - 4);
|
|
FILTER(mel, e->t < cs * TPS);
|
|
push(mel, cs * TPS, mel_pitch(g, chord_deg(g, 0)), 96, 0);
|
|
}
|
|
if(fill){
|
|
int nxt = chord_deg(g, 0) + n, k2 = 0, tt;
|
|
for(tt = last0 * TPS; tt < (last0 + last1) * TPS; tt += TPS, k2++) push(mel, tt, mel_pitch(g, nxt - (last1 - 1 - k2)), 76 + k2 * 8, 0);
|
|
}
|
|
rot = g->rot * TPS;
|
|
for(i = 0; i < mel->n; i++) mel->e[i].t = (int16_t)((mel->e[i].t + rot) % barT);
|
|
one_at_a_time(mel);
|
|
for(i = 0; i < mel->n; i++) mel->e[i].tr = (int16_t)(((g->hocket >> (i & 7)) & 1) ? SB_T_MELA : SB_T_MELB);
|
|
nextC = chord_deg(g, pos + 1);
|
|
for(i = 0; i < nk; i++){
|
|
int t = kickList[i], idx = c;
|
|
if(t > 0 && (i % 2) == 1 && chance(&core, P(70))) idx = c + g->bassDeg;
|
|
push_tr(bass, t, bp(g, idx), t == 0 ? 112 : 98, 0, SB_T_BASS);
|
|
}
|
|
if(steps >= 8 && chance(&core, P(60))){
|
|
int at = (steps - 1) * TPS;
|
|
if(!contains(kickList, nk, at)){ int dir = below(&core, 2) ? 1 : -1; push_tr(bass, at, bp(g, nextC + dir), 84, 0, SB_T_BASS); }
|
|
}
|
|
tones[0] = c; tones[1] = c + 2; tones[2] = c + 4;
|
|
memset(seen, 0, sizeof seen);
|
|
for(i = 0; i < 3; i++){ int p = 48 + mod(deg_pitch(g, 0, tones[i]), 12); if(!seen[p]){ seen[p] = 1; push_tr(pad, 0, p, i == 0 ? 58 : 50, 0, SB_T_PAD); } }
|
|
one_at_a_time(bass);
|
|
}
|
|
|
|
/* ---------- keys generators ---------- */
|
|
typedef struct { uint32_t rs; int variant; const sb_genome *g; int steps, d, sec, pos; const evlist *kit; int fill, bar, drift; } keys_in;
|
|
static void keys_motif(const keys_in *in, evlist *mel, evlist *bass, evlist *pad){
|
|
line_tracks(in->rs, in->variant, in->g, in->steps, in->d, in->sec, in->pos, in->kit, in->fill, mel, bass, pad);
|
|
}
|
|
static void pad_chord(const sb_genome *g, const int *tones, int nt, evlist *pad){
|
|
uint8_t seen[128];
|
|
int i;
|
|
memset(seen, 0, sizeof seen);
|
|
for(i = 0; i < nt; i++){ int p = 48 + mod(deg_pitch(g, 0, tones[i]), 12); if(!seen[p]){ seen[p] = 1; push_tr(pad, 0, p, i == 0 ? 58 : 50, 0, SB_T_PAD); } }
|
|
}
|
|
|
|
typedef struct { int rank, off, up, acc, sl; } step303;
|
|
static void keys_303(const keys_in *in, evlist *mel, evlist *bass, evlist *pad){
|
|
static const int OFF[4] = {0,2,4,6}, OFF_W[4] = {10,2,3,1}, TONE[3] = {0,2,4};
|
|
const sb_genome *g = in->g;
|
|
sb_rng r = rng(mix(in->rs, 0x333033)), ar;
|
|
step303 pat[16];
|
|
uint8_t on[MAX_STEPS];
|
|
int c = chord_deg(g, in->pos), steps = in->steps, i, s, drone, thr;
|
|
mel->n = bass->n = pad->n = 0;
|
|
for(i = 0; i < 16; i++){
|
|
pat[i].rank = 0;
|
|
if(i > 0) pat[i].rank = below(&r, 128);
|
|
pat[i].off = OFF[pick_w(&r, OFF_W, 4)];
|
|
pat[i].up = chance(&r, P(24));
|
|
pat[i].acc = chance(&r, P(40));
|
|
pat[i].sl = chance(&r, P(28));
|
|
}
|
|
drone = chance(&r, P(64));
|
|
if(in->variant){
|
|
sb_rng vr = rng(mix(mix(in->rs, 0x5641u + (uint32_t)in->sec), (uint32_t)in->variant));
|
|
for(i = 0; i < 16; i++){
|
|
if(chance(&vr, P(20))) pat[i].acc = !pat[i].acc;
|
|
if(chance(&vr, P(16))) pat[i].sl = !pat[i].sl;
|
|
if(chance(&vr, P(16))) pat[i].up = !pat[i].up;
|
|
if(i > 0 && chance(&vr, P(16))) pat[i].rank = 127 - pat[i].rank;
|
|
}
|
|
}
|
|
thr = clamp(in->d + 16, 0, 128);
|
|
for(s = 0; s < steps; s++) on[s] = (uint8_t)(pat[s % 16].rank < thr);
|
|
for(s = 0; s < steps; s++) if(on[s]){
|
|
const step303 *p = &pat[s % 16];
|
|
int nx = s + 1;
|
|
while(nx < steps && !on[nx]) nx++;
|
|
push_len(bass, s * TPS, bp(g, c + p->off) + (p->up ? 12 : 0), p->acc ? 120 : 80, SB_T_BASS, p->sl ? (nx - s) * TPS + 20 : TPS >> 1);
|
|
}
|
|
ar = rng(mix(in->rs, 0x414E53u + (uint32_t)in->sec));
|
|
for(s = 0; s < steps;){
|
|
int e2 = s, tr;
|
|
if(on[s]){ s++; continue; }
|
|
while(e2 < steps && !on[e2]) e2++;
|
|
if(e2 - s >= 2 && chance(&ar, P(in->d + 32))){
|
|
tr = ((g->hocket >> (s & 7)) & 1) ? SB_T_MELA : SB_T_MELB;
|
|
push_len(mel, s * TPS, mel_pitch(g, c + TONE[below(&ar, 3)]), 84, tr, TPS - 40);
|
|
if(e2 - s >= 3 && chance(&ar, P(64))) push_len(mel, (s + 1) * TPS, mel_pitch(g, c + TONE[below(&ar, 3)]), 72, tr, TPS - 40);
|
|
}
|
|
s = e2;
|
|
}
|
|
if(drone) push_tr(pad, 0, 48 + g->root, 52, 0, SB_T_PAD);
|
|
}
|
|
|
|
typedef struct { int L, flipP; uint32_t mask, fs; } turing_t;
|
|
static uint32_t rotl_bits(uint32_t v, int s, int L){ return s ? (((v << s) | (v >> (L - s))) & ((1u << L) - 1u)) : v; }
|
|
static uint32_t turing_step(const turing_t *T, uint32_t v, int k){
|
|
uint32_t top = (v >> (T->L - 1)) & 1u;
|
|
if(T->flipP && (int)(mix(T->fs, (uint32_t)k) >> 16) < T->flipP) top ^= 1u;
|
|
return ((v << 1) | top) & T->mask;
|
|
}
|
|
static void keys_turing(const keys_in *in, evlist *mel, evlist *bass, evlist *pad){
|
|
const sb_genome *g = in->g;
|
|
sb_rng r = rng(mix(in->rs, 0x545552));
|
|
turing_t T;
|
|
uint32_t reg;
|
|
int n = SCALE_LEN[g->scale], c = chord_deg(g, in->pos), from, to, k, s, j = 0, span, tones[3];
|
|
mel->n = bass->n = pad->n = 0;
|
|
T.L = chance(&r, P(64)) ? 8 : 16; T.mask = (1u << T.L) - 1u;
|
|
reg = sb_rng_next(&r) & T.mask;
|
|
if(!reg) reg = 1;
|
|
T.flipP = P(in->drift >> 2); T.fs = mix(in->rs, 0x464C50);
|
|
from = imax(0, in->bar - 16) * in->steps; to = in->bar * in->steps;
|
|
reg = rotl_bits(reg, from % T.L, T.L);
|
|
for(k = from; k < to; k++) reg = turing_step(&T, reg, k);
|
|
span = n * g->monoRange;
|
|
for(s = 0; s < in->steps; s++){
|
|
if((int)((reg >> 2) & 7u) * 18 < in->d){
|
|
int v = mel_pitch(g, c + (int)(((reg & 0xFFu) * (uint32_t)(span + 1)) >> 8));
|
|
push_tr(mel, s * TPS, v, 70 + (int)((reg >> 5) & 3u) * 10 + (s % 4 == 0 ? 12 : 0), 0, ((g->hocket >> (j & 7)) & 1) ? SB_T_MELA : SB_T_MELB);
|
|
j++;
|
|
}
|
|
if(reg & 1u) push_tr(bass, s * TPS, bp(g, c) + (((reg >> (T.L >> 1)) & 1u) ? 12 : 0), s == 0 ? 110 : 94, 0, SB_T_BASS);
|
|
reg = turing_step(&T, reg, to + s);
|
|
}
|
|
tones[0] = c; tones[1] = c + 2; tones[2] = c + 4;
|
|
pad_chord(g, tones, 3, pad);
|
|
}
|
|
|
|
static void keys_arp(const keys_in *in, evlist *mel, evlist *bass, evlist *pad){
|
|
const sb_genome *g = in->g;
|
|
sb_rng r = rng(mix(in->rs, 0x415250));
|
|
int n = SCALE_LEN[g->scale], c = chord_deg(g, in->pos), i, o, seventh, dir, nch, chord[4], up[8], seq[16], nup = 0, nseq = 0;
|
|
int off = 0, rate, last0, s = 0, k = 0, nk = 0;
|
|
mel->n = bass->n = pad->n = 0;
|
|
seventh = chance(&r, P(64));
|
|
dir = below(&r, 4);
|
|
nch = seventh ? 4 : 3; chord[0] = c; chord[1] = c + 2; chord[2] = c + 4; chord[3] = c + 6;
|
|
for(o = 0; o < g->monoRange; o++) for(i = 0; i < nch; i++) up[nup++] = chord[i] + o * n;
|
|
if(dir == 1) for(i = nup - 1; i >= 0; i--) seq[nseq++] = up[i];
|
|
else {
|
|
for(i = 0; i < nup; i++) seq[nseq++] = up[i];
|
|
if(dir == 2) for(i = nup - 2; i > 0; i--) seq[nseq++] = up[i];
|
|
else if(dir == 3) for(i = nseq - 1; i > 0; i--){ int j = below(&r, i + 1), tmp = seq[i]; seq[i] = seq[j]; seq[j] = tmp; }
|
|
}
|
|
if(in->variant){ sb_rng vr = rng(mix(mix(in->rs, 0x5641u + (uint32_t)in->sec), (uint32_t)in->variant)); off = below(&vr, nseq); }
|
|
rate = in->d >= 64 ? 1 : 2; last0 = (n_groups(in->steps) - 1) * 4;
|
|
while(s < in->steps){
|
|
int stp = (in->fill && s >= last0) ? 1 : rate;
|
|
push_len(mel, s * TPS, mel_pitch(g, seq[(k + off) % nseq]), s % 4 == 0 ? 96 : 78, (k & 1) ? SB_T_MELB : SB_T_MELA, stp * TPS - 20);
|
|
k++; s += stp;
|
|
}
|
|
for(i = 0; i < in->kit->n; i++){
|
|
const ev_t *e = &in->kit->e[i];
|
|
if(e->v == 0 && e->t % TPS == 0){ push_tr(bass, e->t, bp(g, c), e->t == 0 ? 112 : 98, 0, SB_T_BASS); nk++; }
|
|
}
|
|
if(!nk) push_tr(bass, 0, bp(g, c), 104, 0, SB_T_BASS);
|
|
pad_chord(g, chord, nch, pad);
|
|
}
|
|
static const char *const KEYS_NAMES[SB_NKEYS] = {"MOTIF","303","TURING","ARP"};
|
|
const char *sb_keys_name(int keys){ return keys >= 0 && keys < SB_NKEYS ? KEYS_NAMES[keys] : "?"; }
|
|
|
|
static int kit_track(const ev_t *e){
|
|
if(e->v == 0) return (e->a || e->t == 0) ? SB_T_KICK : SB_T_KICK2;
|
|
if(e->v == 1) return (e->a || e->vel >= 90) ? SB_T_SNARE : SB_T_GHOST;
|
|
if(e->v == 2) return (e->a || e->vel >= 85) ? SB_T_HAT : SB_T_HAT16;
|
|
return SB_T_PERC;
|
|
}
|
|
static int groove(int t, const sb_genome *g, int isSnare, int barTicks, int swing){
|
|
if(t % TPS == 0 && (t / TPS) % 2 == 1) t += swing;
|
|
if(isSnare && g->layback) t += 12;
|
|
return imin(t, barTicks - 1);
|
|
}
|
|
|
|
/* ---------- drift ---------- */
|
|
static int amt(int d, int a, int b){ return clamp(fdiv((d - a) * 127, b - a), 0, 127); }
|
|
static int nudge(uint32_t barSeed, int t, int grp, int micro){
|
|
int mx, n;
|
|
uint32_t h;
|
|
if(!micro || grp == 5) return 0;
|
|
mx = fdiv(micro * (grp == 4 ? 30 : 48), 127);
|
|
h = mix(mix(barSeed, (uint32_t)t), (uint32_t)grp);
|
|
n = (int)(h % (uint32_t)(2 * mx + 1)) - mx;
|
|
if(grp == 2) n += fdiv(micro * 10, 127);
|
|
if(grp == 1) n -= fdiv(micro * 8, 127);
|
|
return n;
|
|
}
|
|
enum { SG_HAT, SG_PERC, SG_GHOST, SG_KICK2, SG_SNARE };
|
|
static int shift_test(const ev_t *x, int which){
|
|
switch(which){
|
|
case SG_HAT: return x->v == 2;
|
|
case SG_PERC: return x->v == 3;
|
|
case SG_GHOST: return x->v == 1 && x->vel < 90;
|
|
case SG_KICK2: return x->v == 0 && !x->a && x->t > 0;
|
|
default: return x->v == 1 && x->vel >= 90;
|
|
}
|
|
}
|
|
static void shift_group(evlist *ev, int which, int by, int barT){
|
|
int i;
|
|
for(i = 0; i < ev->n; i++) if(shift_test(&ev->e[i], which)) ev->e[i].t = (int16_t)mod(ev->e[i].t + by, barT);
|
|
}
|
|
static void triplet_warp(evlist *ev, int beat){
|
|
int s = beat * TPB, i;
|
|
for(i = 0; i < ev->n; i++){
|
|
ev_t *e = &ev->e[i];
|
|
if(e->t >= s && e->t < s + TPB){ int k = imin(2, fdiv((e->t - s) * 3 + TPB / 2, TPB)); e->t = (int16_t)(s + k * 280); }
|
|
}
|
|
}
|
|
|
|
/* ---------- render ---------- */
|
|
static void emit(sb_bar *out, int t, int lane, int v, int vel, int len, int tr){
|
|
sb_event *e;
|
|
if(out->n >= SB_MAX_EVENTS){ g_overflow = 1; return; }
|
|
e = &out->ev[out->n++];
|
|
e->t = (int16_t)t; e->lane = (uint8_t)lane; e->v = (uint8_t)v; e->vel = (uint8_t)vel; e->len = (int16_t)len; e->tr = (uint8_t)tr;
|
|
}
|
|
typedef struct { const sb_genome *g; sb_bar *out; uint32_t barSeed; int barTicks, swingBar, micro, melShift; } mono_ctx;
|
|
enum { LEN_MEL, LEN_BASS };
|
|
static void mono(const mono_ctx *c, const evlist *list, int lenRule){
|
|
const sb_genome *g = c->g;
|
|
int k;
|
|
for(k = 0; k < list->n; k++){
|
|
const ev_t *cur = &list->e[k], *nx = k + 1 < list->n ? &list->e[k + 1] : 0;
|
|
int gap = (nx ? nx->t : c->barTicks) - cur->t, len, t0;
|
|
if(cur->len > 0) len = cur->len; /* fixed by the keys generator */
|
|
else if(lenRule == LEN_BASS) len = gap - 10;
|
|
else if(g->monoLen == 0) len = imin(90, gap - 5);
|
|
else if(g->monoLen == 1) len = imax(30, fdiv(gap, 2));
|
|
else len = (nx && nx->v != cur->v) ? gap + 20 : gap - 5;
|
|
t0 = cur->t;
|
|
if(c->melShift && (cur->tr == SB_T_MELA || cur->tr == SB_T_MELB)) t0 = mod(t0 + c->melShift, c->barTicks);
|
|
emit(c->out, clamp(groove(t0, g, 0, c->barTicks, c->swingBar) + nudge(c->barSeed, cur->t, NUDGE_GRP[cur->tr], c->micro), 0, c->barTicks - 1),
|
|
1, cur->v, cur->vel, imax(10, cur->len > 0 ? len : len - c->swingBar), cur->tr);
|
|
}
|
|
}
|
|
static void sort_out(sb_bar *out){ /* stable, by (t, tr, v) */
|
|
int i, j;
|
|
for(i = 1; i < out->n; i++){
|
|
sb_event x = out->ev[i];
|
|
for(j = i - 1; j >= 0; j--){
|
|
const sb_event *y = &out->ev[j];
|
|
if(y->t > x.t || (y->t == x.t && (y->tr > x.tr || (y->tr == x.tr && y->v > x.v)))) out->ev[j + 1] = out->ev[j];
|
|
else break;
|
|
}
|
|
out->ev[j + 1] = x;
|
|
}
|
|
}
|
|
|
|
void sb_render_bar(const sb_state *st, const sb_params *p, int bar, sb_bar *out){
|
|
static evlist kit, mel, bass, pad, melA, melB;
|
|
sb_genome g;
|
|
fsets fs;
|
|
sb_rng fr, dRng;
|
|
mono_ctx mc;
|
|
uint32_t barSeed, krs, cS, vS;
|
|
int amen = p->gen == 4, pos, phrase, sec, barTicks, i, j, r, fill;
|
|
int dr, dVar, micro, wob, slip, disp, warp, e, arcD, kd, hd, warpBeat, melShift, swingBar, variants[2];
|
|
|
|
g_overflow = 0;
|
|
sb_genome_make(st, &g);
|
|
sb_apply_key(&g, p);
|
|
if(amen){ g.steps = 16; g.swing = 0; g.layback = 0; }
|
|
pos = bar % PHRASE_BARS; phrase = bar / PHRASE_BARS; sec = g.form[pos]; barTicks = g.steps * TPS;
|
|
fr = rng(mix(mix(st->seed, 0x46494C), (uint32_t)phrase));
|
|
fill = p->fill ? 1 : (!amen && pos == PHRASE_BARS - 1 && chance(&fr, P(g.fillP >> 1)));
|
|
dr = clamp(p->drift, 0, 127); dVar = imin(127, dr * 3);
|
|
micro = amt(dr, 20, 127); wob = amt(dr, 40, 127); slip = amt(dr, 55, 127); disp = amt(dr, 80, 127); warp = amt(dr, 105, 127);
|
|
if(amen){ micro = fdiv(micro, 2); wob = fdiv(wob, 2); slip = fdiv(slip, 2); disp = fdiv(amt(dr, 90, 127), 2); warp = amt(dr, 115, 127); }
|
|
dRng = rng(mix(mix(st->seed, 0x445249), (uint32_t)bar)); barSeed = mix(st->seed, 0x4E55u + (uint32_t)bar);
|
|
for(r = 0; r < 2; r++){
|
|
variants[r] = 0;
|
|
if(phrase > 0){ sb_rng vrng = rng(mix(mix(role_seed(st, r), 0xD1F7), (uint32_t)phrase)); if(chance(&vrng, P(dVar))) variants[r] = phrase; }
|
|
}
|
|
e = clamp(p->energy, 0, 127); krs = role_seed(st, 0); cS = mix(krs, 0x434F); vS = mix(mix(krs, 0x5641u + (uint32_t)sec), (uint32_t)variants[0]);
|
|
arcD = (g.arc == 1) ? (pos * 10 - 15) : 0;
|
|
kd = clamp(g.dens[0] + e - 64 + arcD, 0, 127); hd = clamp(g.dens[0] + fdiv(e - 64, 4) + arcD, 0, 127);
|
|
frame_sets(&g, g.steps, pos, &fs);
|
|
kit.n = 0;
|
|
if(amen){
|
|
kit_amen(krs, bar, pos, dr, fill, &kit);
|
|
humanize_amen(&kit, krs, bar);
|
|
if(slip){ int jt = fdiv(slip * 24, 127); for(i = 0; i < kit.n; i++){ int dv = below(&dRng, 2 * jt + 1) - jt; kit.e[i].vel = (int16_t)clamp(kit.e[i].vel + dv, 1, 127); } }
|
|
} else {
|
|
int aoff = 0;
|
|
if(p->gen == 0) kit_euclid(cS, vS, g.steps, kd, hd, pos, &fs, &kit);
|
|
else if(p->gen == 1) kit_skeleton(cS, vS, g.steps, kd, hd, pos, &fs, g.frame, &kit);
|
|
else if(p->gen == 2) kit_subdiv(cS, vS, g.steps, kd, hd, pos, &fs, &kit);
|
|
else kit_ca(cS, vS, g.steps, kd, hd, pos, sec, &fs, &kit);
|
|
transform_kit(&kit, g.steps, sec, krs, &fs);
|
|
arc_kit(&kit, &g, pos);
|
|
if(fill) kit_fill(&kit, g.steps, p->energy, &fr);
|
|
if(chance(&dRng, P(slip))) aoff = 1 + below(&dRng, 3);
|
|
shape_kit(&kit, &g, pos, aoff, fdiv(slip * 24, 127), &dRng);
|
|
}
|
|
if(disp){
|
|
static const int DIRS[2] = {TPS, -TPS};
|
|
if(chance(&dRng, P(disp))) shift_group(&kit, SG_HAT, DIRS[below(&dRng, 2)], barTicks);
|
|
if(chance(&dRng, P(disp))) shift_group(&kit, SG_PERC, DIRS[below(&dRng, 2)], barTicks);
|
|
if(chance(&dRng, P(disp >> 1))) shift_group(&kit, SG_GHOST, DIRS[below(&dRng, 2)], barTicks);
|
|
if(chance(&dRng, P(disp >> 1))) shift_group(&kit, SG_KICK2, DIRS[below(&dRng, 2)], barTicks);
|
|
if(chance(&dRng, P(disp >> 2))) shift_group(&kit, SG_SNARE, TPS, barTicks);
|
|
}
|
|
warpBeat = -1;
|
|
if(warp && chance(&dRng, P(warp))) warpBeat = below(&dRng, imax(1, fdiv(g.steps, 4)));
|
|
if(warpBeat >= 0) triplet_warp(&kit, warpBeat);
|
|
sort_dedupe(&kit);
|
|
melShift = 0;
|
|
if(disp && chance(&dRng, P(disp >> 1))) melShift = below(&dRng, 2) ? TPS : -TPS;
|
|
swingBar = g.swing;
|
|
if(wob){ int w = fdiv(wob * 40, 127); swingBar = clamp(g.swing + below(&dRng, 2 * w + 1) - w, 0, 90); }
|
|
{
|
|
keys_in ki;
|
|
ki.rs = role_seed(st, 1); ki.variant = variants[1]; ki.g = &g; ki.steps = g.steps; ki.d = clamp(g.dens[1] + e - 64, 0, 127);
|
|
ki.sec = sec; ki.pos = pos; ki.kit = &kit; ki.fill = fill; ki.bar = bar; ki.drift = dr;
|
|
switch(clamp(p->keys, 0, SB_NKEYS - 1)){
|
|
case SB_KEYS_303: keys_303(&ki, &mel, &bass, &pad); break;
|
|
case SB_KEYS_TURING: keys_turing(&ki, &mel, &bass, &pad); break;
|
|
case SB_KEYS_ARP: keys_arp(&ki, &mel, &bass, &pad); break;
|
|
default: keys_motif(&ki, &mel, &bass, &pad); break;
|
|
}
|
|
}
|
|
|
|
out->n = 0;
|
|
for(i = 0; i < kit.n; i++){
|
|
const ev_t *k = &kit.e[i];
|
|
int tr = kit_track(k), t = k->t, nt = barTicks, len, ft;
|
|
if(tr == SB_T_PERC) t = (t + g.rot * TPS) % barTicks;
|
|
for(j = i + 1; j < kit.n; j++) if(kit.e[j].v == k->v){ nt = kit.e[j].t; break; }
|
|
len = imax(10, imin(105, nt > t ? nt - t : 105));
|
|
ft = groove(t, &g, tr == SB_T_SNARE, barTicks, swingBar) + nudge(barSeed, t, NUDGE_GRP[tr], micro) + (amen ? amen_feel(krs, bar, t, k->v, k->a) : 0);
|
|
emit(out, clamp(ft, 0, barTicks - 1), 0, k->v, k->vel, len, tr);
|
|
}
|
|
mc.g = &g; mc.out = out; mc.barSeed = barSeed; mc.barTicks = barTicks; mc.swingBar = swingBar; mc.micro = micro; mc.melShift = melShift;
|
|
melA.n = melB.n = 0;
|
|
for(i = 0; i < mel.n; i++) push_ev(mel.e[i].tr == SB_T_MELA ? &melA : &melB, &mel.e[i]);
|
|
mono(&mc, &melA, LEN_MEL); mono(&mc, &melB, LEN_MEL);
|
|
mono(&mc, &bass, LEN_BASS);
|
|
for(i = 0; i < pad.n; i++) emit(out, 0, 1, pad.e[i].v, pad.e[i].vel, barTicks - 30, SB_T_PAD);
|
|
sort_out(out);
|
|
|
|
out->bar = bar; out->pos = pos; out->phrase = phrase; out->sec = sec; out->fill = fill;
|
|
out->variants[0] = variants[0]; out->variants[1] = variants[1]; out->barTicks = barTicks; out->steps = g.steps;
|
|
out->overflow = g_overflow;
|
|
}
|
|
|
|
uint32_t sb_checksum_event(uint32_t h, const sb_event *e){
|
|
int f[6], i;
|
|
f[0] = e->t; f[1] = e->lane; f[2] = e->v; f[3] = e->vel; f[4] = e->len; f[5] = e->tr;
|
|
for(i = 0; i < 6; i++) h = (h ^ ((uint32_t)f[i] & 0xFFFFu)) * 0x01000193u;
|
|
return h;
|
|
}
|