Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107#!/usr/bin/env node// render-c.mjs — the C-backend render flow (hellsine / americomputadora// pattern): bake (JS preps score.h) → cc → ./maytrax (C renders the song to// a float32 stereo WAV) → ffmpeg pop-master (the same MASTER chain as// bin/maytrax.mjs: tonal EQ + glue comp + soft-clip + width, then loudnorm +// alimiter) → mp3.//// usage: node c/render-c.mjs [--no-bake] [--out ../out/maytrax.mp3] [--wav x.wav]
import { spawnSync } from "node:child_process";import { mkdirSync } from "node:fs";import { dirname, join, resolve } from "node:path";import { fileURLToPath } from "node:url";import { homedir } from "node:os";
const HERE = dirname(fileURLToPath(import.meta.url));const ROOT = dirname(HERE); // pop/maytrax
const argv = process.argv.slice(2);const flags = {};for (let i = 0; i < argv.length; i++) { const a = argv[i]; if (a.startsWith("--")) { const k = a.slice(2), n = argv[i + 1]; if (n !== undefined && !n.startsWith("--")) { flags[k] = n; i++; } else flags[k] = true; }}const expand = (p) => (p && p.startsWith("~/") ? resolve(homedir(), p.slice(2)) : p);// --sine renders the sinetrax fork (pure sine-bell, no samples/drums/vocals).// --engine <name> renders any sine-family fork (dewtrax, mosstrax, …): a// self-contained <name>.c that needs no bake and masters on the sine chain.const SINE = !!flags.sine;const ENGINE = (typeof flags.engine === "string" && flags.engine) || (SINE ? "sinetrax" : "maytrax");// everything except the sample/drum/vocal maytrax engine is sine-family.const SINEFAM = SINE || ENGINE !== "maytrax";const outMp3 = resolve(expand(flags.out) ?? join(ROOT, "out", `${ENGINE}.mp3`));mkdirSync(dirname(outMp3), { recursive: true });mkdirSync(join(HERE, "out"), { recursive: true });
function run(label, cmd, args, opts = {}) { console.log(`# ${label}`); const r = spawnSync(cmd, args, { stdio: "inherit", ...opts }); if (r.status !== 0) { console.error(`✗ ${label} failed`); process.exit(1); }}
if (!SINEFAM && !flags["no-bake"]) run("bake (score.h)", "node", [join(HERE, "bake.mjs")]);run("cc", "cc", ["-O3", "-std=c11", "-Wall", "-Wextra", "-Wno-unused-parameter", "-o", join(HERE, ENGINE), join(HERE, `${ENGINE}.c`), "-lm"]);
const rawWav = join(HERE, "out", `${ENGINE}-raw.wav`);// pianotrax voices the real Salamander grand from fedac/native/samples/piano —// pass an absolute path so it loads regardless of the cwd it's spawned from.const engineArgs = ["--out", rawWav];if (ENGINE === "pianotrax") { engineArgs.push("--piano-dir", resolve(HERE, "..", "..", "..", "fedac", "native", "samples", "piano"));}run("render (C engine)", join(HERE, ENGINE), engineArgs);
// ── pop master chain (ported from bin/maytrax.mjs) ────────────────────────// chiller, warmer, more dynamic master than the old hot pop chain: lower// loudness target (room to breathe), softer presence, gentle air, a glue// comp with a slow attack so transients punch through, and a soft limiter.const MASTER_CORE = [ "highpass=f=30", "equalizer=f=55:t=q:w=0.8:g=1.4", // tight sub weight "equalizer=f=300:t=q:w=1.1:g=-1.0", // gentle de-mud (warm) "equalizer=f=3200:t=q:w=1.6:g=0.6", // barely-there presence "equalizer=f=8500:t=q:w=0.8:g=-1.6", // tame harsh upper-treble (smooth) "treble=g=-0.6:f=11000", // gentle high-shelf DOWN — de-harsh "lowpass=f=16500", // shave the brittle top "acompressor=threshold=-20dB:ratio=2.0:attack=30:release=260:makeup=2.2:knee=8", // smooth glue; slow attack keeps the groove punching "asoftclip=type=tanh:threshold=0.98", // minimal density "stereotools=slev=1.04", // tighter width].join(",");// quiet, slow entrance — a 13s fade-in AFTER the limiter so loudnorm can't// pull the intro back up. cub (cubic) stays very quiet early then rises into// the build, so the opening sounds stay soft far longer than a linear ramp.const INTRO_FADE = "afade=t=in:curve=cub:st=0:d=13";// sinetrax is gentle bells (and fades itself) — master it softer + more// dynamic with no extra fade; maytrax gets the slow cubic intro fade.// pianotrax wants extra volume but a WIDE dynamic range (the beat drops in for// the waves, the hushes breathe) — louder loudnorm target, high LRA, a gentle// limiter ceiling so the key-press transients still punch.const MASTER_MP3 = ENGINE === "pianotrax" ? `${MASTER_CORE},loudnorm=I=-11.5:TP=-1.3:LRA=13,alimiter=limit=0.97:attack=6:release=110` : SINEFAM ? `${MASTER_CORE},loudnorm=I=-13:TP=-1.5:LRA=13,alimiter=limit=0.95:attack=8:release=120` : `${MASTER_CORE},loudnorm=I=-11:TP=-1.5:LRA=11,alimiter=limit=0.93:attack=5:release=90,${INTRO_FADE}`;
run("master (ffmpeg) → mp3", "ffmpeg", [ "-hide_banner", "-y", "-loglevel", "error", "-i", rawWav, "-af", MASTER_MP3, "-c:a", "libmp3lame", "-b:a", "320k", "-metadata", `title=${ENGINE}`, "-metadata", "artist=jeffrey", "-metadata", "album=pixsies", outMp3,]);console.log(`✓ ${outMp3} (pop-mastered · 320k · stereo)`);
const wavOut = expand(flags.wav);if (wavOut) { mkdirSync(dirname(wavOut), { recursive: true }); run("master → DistroKid wav", "ffmpeg", [ "-hide_banner", "-y", "-loglevel", "error", "-i", rawWav, "-af", `${MASTER_CORE},loudnorm=I=-14:TP=-1.5:LRA=11,alimiter=limit=0.95:attack=3:release=55`, "-ar", "44100", "-ac", "2", "-c:a", "pcm_s16le", wavOut, ]); console.log(`✓ ${wavOut} (DistroKid master · 44.1k/16-bit · -14 LUFS)`);}