Split kit and keys; KEYS GEN with MOTIF, 303, TURING, ARP
Some checks failed
check / check (push) Has been cancelled
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>
This commit is contained in:
@@ -99,17 +99,18 @@ 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 { 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->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){ push_tr(l, e->t, e->v, e->vel, e->a, e->tr); }
|
||||
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)
|
||||
@@ -124,28 +125,35 @@ static void sort_tv(evlist *l){ /* stable, by (t, v) */
|
||||
}
|
||||
|
||||
/* ---------- genome ---------- */
|
||||
void sb_genome_make(uint32_t seed, sb_genome *g){
|
||||
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(seed, 0x47454E));
|
||||
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->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->fillP = 40 + below(&r, 80);
|
||||
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);
|
||||
/* 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 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; }
|
||||
@@ -578,6 +586,136 @@ static void line_tracks(uint32_t rs, int variant, const sb_genome *g, int steps,
|
||||
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;
|
||||
@@ -640,14 +778,15 @@ static void mono(const mono_ctx *c, const evlist *list, int lenRule){
|
||||
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;
|
||||
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, len - c->swingBar), cur->tr);
|
||||
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) */
|
||||
@@ -674,7 +813,7 @@ void sb_render_bar(const sb_state *st, const sb_params *p, int bar, sb_bar *out)
|
||||
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_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;
|
||||
@@ -725,7 +864,17 @@ void sb_render_bar(const sb_state *st, const sb_params *p, int bar, sb_bar *out)
|
||||
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);
|
||||
{
|
||||
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++){
|
||||
|
||||
Reference in New Issue
Block a user