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>
35 lines
2.6 KiB
JavaScript
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');
|