Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 68 lines 2.8 kB view raw
1#!/usr/bin/env node 2/** 3 * One-shot: parse project XML then run simulation in near real-time with sensible defaults. 4 * Usage: 5 * node run-full-track.mjs [--project <xml>] [--bpm 144] [--rate 1] [--groups <spec>] [--hat-pitches a,b] 6 * Defaults: 7 * project: ../system/public/assets/wipppps/zzzZWAP_extracted.xml 8 * rate: 1 (real-time) 9 * fps: 20 10 * aggregate-window: 8 11 * auto groups: top 2 pitches if --groups omitted & groups-grid is used 12 */ 13import { spawn } from 'node:child_process'; 14import { resolve, dirname } from 'node:path'; 15import { fileURLToPath } from 'node:url'; 16import fs from 'node:fs'; 17 18const __dirname = dirname(fileURLToPath(import.meta.url)); 19 20const args = process.argv.slice(2); 21function getFlag(name, def){ const i=args.indexOf('--'+name); return i!==-1? args[i+1]: def; } 22const has = n => args.includes('--'+n); 23 24const project = resolve(getFlag('project','../system/public/assets/wipppps/zzzZWAP_extracted.xml')); 25const bpm = getFlag('bpm'); 26const rate = getFlag('rate','1'); 27const fps = getFlag('fps','20'); 28const agg = getFlag('aggregate-window','8'); 29const hat = getFlag('hat-pitches',''); 30let groupsSpec = getFlag('groups'); 31const wantGrid = true; // always show groups grid for overview 32 33// If groups not provided, we will quick-scan pitches from analyzer JSON (or generate one) to pick top 2. 34async function ensureReportAndNotes(){ 35 if (!fs.existsSync('report.json') || !fs.existsSync('notes.json')) { 36 await new Promise((res,rej)=>{ 37 const a = spawn('node', ['analyze-ableton.mjs','--project', project,'--blank','./live-12-blank.xml','--out','report.json','--notes-out','notes.json'], { cwd: __dirname, stdio:'inherit' }); 38 a.on('exit', c=> c===0?res():rej(new Error('analyze failed'))); 39 }); 40 } 41} 42 43function deriveGroups(){ 44 if (groupsSpec) return; 45 try { 46 const notesPath = resolve(__dirname,'notes.json'); 47 const notes = JSON.parse(fs.readFileSync(notesPath,'utf8')); 48 const freq = new Map(); 49 for (const n of notes) { if (n.pitch!=null) freq.set(n.pitch,(freq.get(n.pitch)||0)+1); } 50 const top = [...freq.entries()].sort((a,b)=>b[1]-a[1]).slice(0,2); 51 if (top.length) { 52 groupsSpec = top.map(([p],i)=>`P${p}:${p}:${i===0?'P':'Q'}`).join(';'); 53 console.error('[run-full] Auto groups:', groupsSpec); 54 } 55 } catch(e){ /* ignore */ } 56} 57 58(async () => { 59 await ensureReportAndNotes(); 60 deriveGroups(); 61 const simArgs = ['simulate-ableton.mjs','--auto','--rate',rate,'--fps',fps,'--aggregate-window',agg]; 62 if (bpm) simArgs.push('--bpm', bpm); 63 if (hat) simArgs.push('--hat-pitches', hat); 64 if (groupsSpec) simArgs.push('--groups-grid','--groups', groupsSpec); 65 else simArgs.push('--groups-grid'); 66 const child = spawn('node', simArgs, { cwd: __dirname, stdio:'inherit' }); 67 child.on('exit', code => process.exit(code)); 68})();