/* 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 #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; } ev_t; 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; } 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){ push_tr(l, e->t, e->v, e->vel, e->a, e->tr); } /* 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 ---------- */ void sb_genome_make(uint32_t seed, 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(seed, 0x47454E)); int fi; 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->scale = below(&r, SB_NSCALES); g->root = below(&r, 12); g->dens[0] = 40 + below(&r, 60); g->dens[1] = 30 + below(&r, 70); g->fillP = 40 + below(&r, 80); g->monoLen = below(&r, 3); g->monoRange = 1 + below(&r, 2); g->rot = pick_w(&r, ROT_W, 4); g->hocket = HOCKETS[below(&r, 8)]; g->bassDeg = BASS_DEG[below(&r, 3)]; g->prog_idx = below(&r, 10); memcpy(g->prog, PROGS[g->prog_idx], sizeof g->prog); g->swing = SWINGS[pick_w(&r, SWING_W, 5)]; g->arc = pick_w(&r, ARC_W, 3); g->motifLen = 3 + below(&r, 3); g->accentCycle = ACC[pick_w(&r, ACC_W, 5)]; g->layback = below(&r, 2); g->frame = pick_w(&r, FRAME_W, 8); g->freeK = 2 + below(&r, 3); g->freeRot = below(&r, 16); } static uint32_t role_seed(const sb_state *st, int r){ return mix(mix(st->seed, 0x524F4C45u + (uint32_t)r), st->salts[r]); } 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); } 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(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, 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->seed, &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); } line_tracks(role_seed(st, 1), variants[1], &g, g.steps, clamp(g.dens[1] + e - 64, 0, 127), sec, pos, &kit, fill, &mel, &bass, &pad); 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; }