Files
seedbox/test/gen-vectors.js
Gee 346c820b10
Some checks failed
check / check (push) Has been cancelled
Split kit and keys; KEYS GEN with MOTIF, 303, TURING, ARP
- 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>
2026-10-11 21:50:41 +03:00

35 lines
2.6 KiB
JavaScript

/* Regenerates test/vectors/*.json from sim/engine.js.
Only run this when you MEAN to change the music. Otherwise run check-vectors.js. */
var fs=require('fs'), path=require('path');
var E=require('../sim/engine.js'), C=require('./cases.js'), R=require('./render.js');
var dir=path.join(__dirname,'vectors');
var FORMAT='events: [tick, lane, voice, vel, len, track]. lane 0 = kit (voice 0 kick, 1 snare, 2 hat, 3 perc), lane 1 = synth (voice = MIDI pitch). scale/root: -1 = AUTO (the seed picks). Every track is listed; on/off is applied at output. Ticks: 840 per quarter, 210 per 16th.';
// 1. primitives: hash, RNG, genome. Port these first.
var mixV=[], pairs=[[0,0],[1,0],[0,1],[0x5EEDB0C5,0x46494C],[0xFFFFFFFF,0xFFFFFFFF],[0xDEADBEEF,-1],[12345,0x4E55+7]];
pairs.forEach(function(p){ mixV.push({a:R.hex(p[0]>>>0),b:p[1]|0,out:R.hex(E.mix(p[0]>>>0,p[1]))}); });
var rngV=[]; [0,1,0x5EEDB0C5,0xDEADBEEF].forEach(function(s){ var r=new E.Rng(s), seq=[], below=[], ch=[];
for(var i=0;i<16;i++) seq.push(R.hex(r.next()));
r=new E.Rng(s); for(i=0;i<16;i++) below.push(r.below(i+1));
r=new E.Rng(s); for(i=0;i<16;i++) ch.push(r.chance(i*4096)?1:0);
rngV.push({seed:R.hex(s>>>0),next:seq,below_1_to_16:below,chance_i_times_4096:ch}); });
var genV=C.cases().filter(function(c,i,a){ return a.findIndex(function(d){ return d.seed===c.seed && d.salts.join()===c.salts.join(); })===i; })
.map(function(c){ return {seed:R.hex(c.seed),salts:c.salts,genome:E.genome(c.seed>>>0,c.salts)}; });
fs.writeFileSync(path.join(dir,'primitives.json'),JSON.stringify({note:'mix(a,b) hash, xorshift32 Rng (next, below(n), chance(p)) and genome(seed, salts). genome keys are the JS field names.',mix:mixV,rng:rngV,genomes:genV},null,1)+'\n');
// 2. full renders, one file per generator
var byGen={}, index=[];
C.cases().forEach(function(c){
var out=R.renderCase(c,C.BARS), id=R.caseId(c);
(byGen[c.gen]=byGen[c.gen]||[]).push({id:id,seed:R.hex(c.seed),salts:c.salts,gen:c.gen,keys:c.keys,energy:c.energy,drift:c.drift,fillBars:c.fillBars,scale:c.scale,root:c.root,checksum:out.checksum,bars:out.bars.map(function(b){
return {bar:b.bar,sec:b.sec,fill:b.fill,barTicks:b.barTicks,events:b.events}; })});
index.push({id:id,checksum:out.checksum});
});
Object.keys(byGen).forEach(function(g){
var s=JSON.stringify({generator:E.GEN_NAMES[g],format:FORMAT,cases:byGen[g]});
s=s.replace(/\],\[/g,'],\n['); // one event per line keeps git diffs readable
fs.writeFileSync(path.join(dir,'gen'+g+'-'+E.GEN_NAMES[g].toLowerCase()+'.json'),s+'\n');
});
fs.writeFileSync(path.join(dir,'index.json'),JSON.stringify({bars:C.BARS,cases:index},null,1)+'\n');
console.log('wrote',index.length,'cases');