Seedbox v0.1 + firmware, generic kit/synth, ROOT/SCALE pages
Some checks failed
check / check (push) Has been cancelled
Some checks failed
check / check (push) Has been cancelled
First commit of the repo. Contents beyond the v0.1 simulator and layout: - Full Pico 2 W firmware (firmware/app, drivers, engine port, host test). - BANK page and 404 mode A/B removed; learned pads play exactly what was learned. - 404/MiniFreak renamed to KIT/SYNTH throughout (jacks, code, sim, docs, layout labels; wiring rebuilt). - ROLL returns to page select after a re-roll; a freshly learned track turns on. - LCD contrast 66. - ROOT and SCALE pages: 21 scales, AUTO = the seed picks; global and sticky. Intended music change: the scale table grew from 8 to 21 and AUTO picks from all of them, so every seed's scale changed. Vectors regenerated with npm run vectors, plus new key-override cases. Flash record is now version 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
162
firmware/test/check_vectors.c
Normal file
162
firmware/test/check_vectors.c
Normal file
@@ -0,0 +1,162 @@
|
||||
/* Host test runner: checks firmware/engine against test/vectors (the same files check-vectors.js uses).
|
||||
Usage: check_vectors [vectors-dir] (default ../../test/vectors). Exit 0 = every check passes. */
|
||||
#define _POSIX_C_SOURCE 200809L /* strdup, opendir */
|
||||
#include "../engine/engine.h"
|
||||
#include "json.h"
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static int fail, n, max_events;
|
||||
|
||||
static uint32_t hex(const jval *v){ return (uint32_t)strtoul(v->str, NULL, 16); }
|
||||
static long long num(const jval *v){ return v ? v->num : -999999; }
|
||||
|
||||
static void check_primitives(const char *dir){
|
||||
char path[1024];
|
||||
jval *root, *a;
|
||||
int i, j;
|
||||
snprintf(path, sizeof path, "%s/primitives.json", dir);
|
||||
root = json_parse_file(path);
|
||||
if(!root){ printf("cannot read %s\n", path); fail++; return; }
|
||||
|
||||
a = json_get(root, "mix");
|
||||
for(i = 0; i < a->n; i++){
|
||||
jval *v = a->items[i];
|
||||
uint32_t got = sb_mix(hex(json_get(v, "a")), (uint32_t)(int32_t)num(json_get(v, "b")));
|
||||
n++;
|
||||
if(got != hex(json_get(v, "out"))){ fail++; printf("mix %s %lld differs: got 0x%08X\n", json_get(v, "a")->str, num(json_get(v, "b")), got); }
|
||||
}
|
||||
|
||||
a = json_get(root, "rng");
|
||||
for(i = 0; i < a->n; i++){
|
||||
jval *v = a->items[i], *seq = json_get(v, "next"), *bl = json_get(v, "below_1_to_16"), *ch = json_get(v, "chance_i_times_4096");
|
||||
uint32_t seed = hex(json_get(v, "seed"));
|
||||
sb_rng r;
|
||||
n++;
|
||||
sb_rng_init(&r, seed);
|
||||
for(j = 0; j < seq->n; j++) if(sb_rng_next(&r) != hex(seq->items[j])){ fail++; printf("rng 0x%08X next step %d differs\n", seed, j); break; }
|
||||
n++;
|
||||
sb_rng_init(&r, seed);
|
||||
for(j = 0; j < bl->n; j++) if(sb_rng_below(&r, j + 1) != num(bl->items[j])){ fail++; printf("rng 0x%08X below step %d differs\n", seed, j); break; }
|
||||
n++;
|
||||
sb_rng_init(&r, seed);
|
||||
for(j = 0; j < ch->n; j++) if(sb_rng_chance(&r, j * 4096) != num(ch->items[j])){ fail++; printf("rng 0x%08X chance step %d differs\n", seed, j); break; }
|
||||
}
|
||||
|
||||
a = json_get(root, "genomes");
|
||||
for(i = 0; i < a->n; i++){
|
||||
jval *v = a->items[i], *gj = json_get(v, "genome");
|
||||
uint32_t seed = hex(json_get(v, "seed"));
|
||||
sb_genome g;
|
||||
int bad = 0, k;
|
||||
n++;
|
||||
sb_genome_make(seed, &g);
|
||||
for(k = 0; k < gj->n; k++){
|
||||
jval *f = gj->items[k];
|
||||
const char *key = f->key;
|
||||
#define INT(name) else if(!strcmp(key, #name)){ if(num(f) != g.name) bad = 1; }
|
||||
#define ARR(name, len) else if(!strcmp(key, #name)){ if(f->n != len) bad = 1; else for(j = 0; j < len; j++) if(num(f->items[j]) != g.name[j]) bad = 1; }
|
||||
if(!strcmp(key, "formName")){ if(strcmp(f->str, sb_form_name(g.form_idx))) bad = 1; }
|
||||
INT(steps) INT(scale) INT(root) INT(fillP) INT(monoLen) INT(monoRange) INT(rot) INT(hocket) INT(bassDeg)
|
||||
INT(swing) INT(arc) INT(motifLen) INT(accentCycle) INT(layback) INT(frame) INT(freeK) INT(freeRot)
|
||||
ARR(form, 4) ARR(prog, 4) ARR(dens, 2)
|
||||
else { printf("genome field %s is not checked by the C runner\n", key); bad = 1; }
|
||||
#undef INT
|
||||
#undef ARR
|
||||
if(bad){ printf("genome 0x%08X differs at %s\n", seed, key); break; }
|
||||
}
|
||||
if(bad) fail++;
|
||||
}
|
||||
json_free(root);
|
||||
}
|
||||
|
||||
static int cmp_str(const void *a, const void *b){ return strcmp(*(char *const *)a, *(char *const *)b); }
|
||||
|
||||
static void print_event(const char *tag, const sb_event *e){ printf(" %s [%d,%d,%d,%d,%d,%d]\n", tag, e->t, e->lane, e->v, e->vel, e->len, e->tr); }
|
||||
|
||||
static void check_case(const jval *c){
|
||||
jval *bars = json_get(c, "bars"), *salts = json_get(c, "salts"), *fb = json_get(c, "fillBars");
|
||||
sb_state st;
|
||||
sb_params p;
|
||||
sb_bar out;
|
||||
uint32_t h = SB_FNV_INIT;
|
||||
int b, i, firstBad = -1;
|
||||
n++;
|
||||
st.seed = hex(json_get(c, "seed"));
|
||||
st.salts[0] = (uint32_t)num(salts->items[0]); st.salts[1] = (uint32_t)num(salts->items[1]);
|
||||
p.gen = (int)num(json_get(c, "gen")); p.energy = (int)num(json_get(c, "energy")); p.drift = (int)num(json_get(c, "drift"));
|
||||
p.scale = (int)num(json_get(c, "scale")); p.root = (int)num(json_get(c, "root"));
|
||||
for(b = 0; b < bars->n; b++){
|
||||
jval *jb = bars->items[b], *ev = json_get(jb, "events");
|
||||
p.fill = 0;
|
||||
for(i = 0; i < fb->n; i++) if(num(fb->items[i]) == b) p.fill = 1;
|
||||
sb_render_bar(&st, &p, b, &out);
|
||||
if(out.n > max_events) max_events = out.n;
|
||||
for(i = 0; i < out.n; i++) h = sb_checksum_event(h, &out.ev[i]);
|
||||
if(firstBad >= 0) continue;
|
||||
if(out.overflow){ firstBad = b; printf("FAIL %s bar %d: event buffer overflow\n", json_get(c, "id")->str, b); continue; }
|
||||
if(out.sec != num(json_get(jb, "sec")) || out.fill != num(json_get(jb, "fill")) || out.barTicks != num(json_get(jb, "barTicks"))){
|
||||
firstBad = b;
|
||||
printf("FAIL %s bar %d: sec/fill/barTicks got %d/%d/%d want %lld/%lld/%lld\n", json_get(c, "id")->str, b,
|
||||
out.sec, out.fill, out.barTicks, num(json_get(jb, "sec")), num(json_get(jb, "fill")), num(json_get(jb, "barTicks")));
|
||||
continue;
|
||||
}
|
||||
for(i = 0; i < out.n || i < ev->n; i++){
|
||||
sb_event want = {0, 0, 0, 0, 0, 0};
|
||||
int have = i < ev->n, same;
|
||||
if(have){
|
||||
jval *row = ev->items[i];
|
||||
want.t = (int16_t)num(row->items[0]); want.lane = (uint8_t)num(row->items[1]); want.v = (uint8_t)num(row->items[2]);
|
||||
want.vel = (uint8_t)num(row->items[3]); want.len = (int16_t)num(row->items[4]); want.tr = (uint8_t)num(row->items[5]);
|
||||
}
|
||||
same = have && i < out.n && !memcmp(&want, &out.ev[i], sizeof want);
|
||||
if(!same){
|
||||
firstBad = b;
|
||||
printf("FAIL %s first difference in bar %d, event %d (got %d events, want %d)\n", json_get(c, "id")->str, b, i, out.n, ev->n);
|
||||
if(have) print_event("want", &want);
|
||||
if(i < out.n) print_event("got ", &out.ev[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(h != hex(json_get(c, "checksum")) || firstBad >= 0){
|
||||
fail++;
|
||||
if(firstBad < 0) printf("FAIL %s checksum 0x%08X want %s (events matched?)\n", json_get(c, "id")->str, h, json_get(c, "checksum")->str);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
const char *dir = argc > 1 ? argv[1] : "../../test/vectors";
|
||||
char *names[64];
|
||||
int nn = 0, i, k;
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
|
||||
check_primitives(dir);
|
||||
|
||||
d = opendir(dir);
|
||||
if(!d){ printf("cannot open %s\n", dir); return 1; }
|
||||
while((de = readdir(d)) && nn < 64)
|
||||
if(!strncmp(de->d_name, "gen", 3) && de->d_name[3] >= '0' && de->d_name[3] <= '9') names[nn++] = strdup(de->d_name);
|
||||
closedir(d);
|
||||
qsort(names, (size_t)nn, sizeof names[0], cmp_str);
|
||||
if(!nn){ printf("no gen*.json in %s\n", dir); fail++; }
|
||||
|
||||
for(i = 0; i < nn; i++){
|
||||
char path[1024];
|
||||
jval *root, *cases;
|
||||
snprintf(path, sizeof path, "%s/%s", dir, names[i]);
|
||||
root = json_parse_file(path);
|
||||
if(!root){ printf("cannot read %s\n", path); fail++; continue; }
|
||||
cases = json_get(root, "cases");
|
||||
for(k = 0; k < cases->n; k++) check_case(cases->items[k]);
|
||||
json_free(root);
|
||||
free(names[i]);
|
||||
}
|
||||
printf("max events in a bar: %d (buffer %d)\n", max_events, SB_MAX_EVENTS);
|
||||
if(fail) printf("%d of %d checks FAILED\n", fail, n);
|
||||
else printf("all %d checks pass\n", n);
|
||||
return fail ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user