Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240// samples.mjs — Sample library manager for AC Native// Lists saved audio samples from /mnt/samples/, plays them back,// and saves new snapshots of the current sample buffer.// Jumped to from prompt.mjs via "samples" command.
const SAMPLES_DIR = "/mnt/samples";let samples = []; // { name, path, size, date }let selectedIdx = 0;let frame = 0;let message = "";let messageFrame = 0;let storageInfo = "";
function scanSamples(system) { samples = []; // Read manifest file that we maintain const manifest = system?.readFile?.(`${SAMPLES_DIR}/manifest.json`); if (manifest) { try { samples = JSON.parse(manifest); } catch {} }}
function saveManifest(system) { system?.writeFile?.(`${SAMPLES_DIR}/manifest.json`, JSON.stringify(samples, null, 2));}
function boot({ system }) { scanSamples(system);
// Storage info const bootDev = system?.bootDevice || "?"; storageInfo = `${bootDev} -> ${SAMPLES_DIR}`;}
function act({ event: e, sound, system }) { if (!e.is("keyboard:down")) return;
if (e.is("keyboard:down:escape") || e.is("keyboard:down:backspace")) { system?.jump?.("prompt"); return; }
// Navigate if (e.is("keyboard:down:arrowdown") || e.is("keyboard:down:tab")) { if (samples.length > 0) { selectedIdx = (selectedIdx + 1) % samples.length; sound?.synth({ type: "sine", tone: 440, duration: 0.03, volume: 0.08, attack: 0.002, decay: 0.025 }); } return; } if (e.is("keyboard:down:arrowup")) { if (samples.length > 0) { selectedIdx = (selectedIdx - 1 + samples.length) % samples.length; sound?.synth({ type: "sine", tone: 440, duration: 0.03, volume: 0.08, attack: 0.002, decay: 0.025 }); } return; }
// Save current sample as timestamped file if (e.is("keyboard:down:s")) { const mic = sound?.microphone || {}; const len = mic.sampleLength || 0; if (len <= 0) { message = "no sample loaded"; messageFrame = frame; sound?.synth({ type: "square", tone: 220, duration: 0.1, volume: 0.06, attack: 0.005, decay: 0.08 }); return; } // Generate timestamp filename const now = new Date(); const ts = [ now.getFullYear(), String(now.getMonth() + 1).padStart(2, "0"), String(now.getDate()).padStart(2, "0"), "-", String(now.getHours()).padStart(2, "0"), String(now.getMinutes()).padStart(2, "0"), String(now.getSeconds()).padStart(2, "0"), ].join(""); const path = `${SAMPLES_DIR}/${ts}.raw`; const saved = sound?.sample?.saveTo?.(path) || -1; if (saved > 0) { const secs = (mic.sampleRate > 0) ? (saved / mic.sampleRate).toFixed(2) : "?"; message = `saved ${secs}s -> ${ts}.raw`; sound?.synth({ type: "triangle", tone: 784, duration: 0.08, volume: 0.12, attack: 0.003, decay: 0.06 }); // Add to manifest samples.unshift({ name: ts, path, size: saved * 4 + 8, rate: mic.sampleRate || 0, len: saved }); saveManifest(system); selectedIdx = 0; } else { message = "save failed"; sound?.synth({ type: "square", tone: 220, duration: 0.1, volume: 0.06, attack: 0.005, decay: 0.08 }); } messageFrame = frame; return; }
// Load selected sample if (e.is("keyboard:down:enter") || e.is("keyboard:down:return")) { if (samples.length > 0 && samples[selectedIdx]) { const s = samples[selectedIdx]; const loaded = sound?.sample?.loadFrom?.(s.path) || -1; if (loaded > 0) { const mic = sound?.microphone || {}; const secs = (mic.sampleRate > 0) ? (loaded / mic.sampleRate).toFixed(2) : "?"; message = `loaded ${s.name} (${secs}s)`; sound?.synth({ type: "triangle", tone: 523, duration: 0.06, volume: 0.1, attack: 0.003, decay: 0.05 }); } else { message = "load failed"; sound?.synth({ type: "square", tone: 220, duration: 0.1, volume: 0.06, attack: 0.005, decay: 0.08 }); } messageFrame = frame; } return; }
// Play preview of selected sample if (e.is("keyboard:down:space")) { if (samples.length > 0 && samples[selectedIdx]) { // Load and play at C4 const s = samples[selectedIdx]; sound?.sample?.loadFrom?.(s.path); sound?.sample?.play?.({ tone: 261.63, base: 261.63, volume: 0.7, attack: 0.01, decay: 0.5 }); sound?.synth({ type: "sine", tone: 261.63, duration: 0.02, volume: 0.03, attack: 0.001, decay: 0.015 }); } return; }
// Delete selected sample if (e.is("keyboard:down:delete") || e.is("keyboard:down:x")) { if (samples.length > 0 && samples[selectedIdx]) { const s = samples[selectedIdx]; // Remove from manifest (file stays on disk but is unlisted) samples.splice(selectedIdx, 1); saveManifest(system); message = `removed ${s.name}`; messageFrame = frame; if (selectedIdx >= samples.length) selectedIdx = Math.max(0, samples.length - 1); sound?.synth({ type: "sine", tone: 330, duration: 0.06, volume: 0.08, attack: 0.003, decay: 0.05 }); } return; }}
function paint({ wipe, ink, box, write, screen, sound, system }) { frame++; const T = __theme.update(); const w = screen.width, h = screen.height; const pad = 10; const font = "font_1";
wipe(T.bg[0], T.bg[1], T.bg[2]);
// Title ink(T.fg, T.fg + 10, T.fg); write("samples", { x: pad, y: 10, size: 2, font: "matrix" });
// Storage location ink(T.fgMute, T.fgMute, T.fgMute + 10); write(storageInfo, { x: pad, y: 34, size: 1, font });
// Current sample info const mic = sound?.microphone || {}; const curLen = mic.sampleLength || 0; const curRate = mic.sampleRate || 0; if (curLen > 0) { const secs = curRate > 0 ? (curLen / curRate).toFixed(2) : "?"; ink(80, 200, 120); write(`active: ${secs}s @ ${curRate}Hz`, { x: pad, y: 46, size: 1, font }); } else { ink(T.fgMute); write("no sample loaded", { x: pad, y: 46, size: 1, font }); }
// Message if (message && frame - messageFrame < 180) { const fade = Math.max(0, 255 - (frame - messageFrame) * 2); ink(200, 255, 180, fade); write(message, { x: pad, y: 58, size: 1, font }); }
// Sample list const listY = 74; const rowH = 14; const maxRows = Math.floor((h - listY - 40) / rowH);
if (samples.length === 0) { ink(T.fgMute); write("no saved samples", { x: pad, y: listY, size: 1, font }); ink(T.fgMute - 20, T.fgMute, T.fgMute + 10); write("record in notepat, then press s here", { x: pad, y: listY + 14, size: 1, font }); } else { // Scroll window let startIdx = 0; if (selectedIdx >= maxRows) startIdx = selectedIdx - maxRows + 1;
for (let i = startIdx; i < Math.min(samples.length, startIdx + maxRows); i++) { const s = samples[i]; const ry = listY + (i - startIdx) * rowH; const selected = i === selectedIdx;
if (selected) { ink(30, 50, 60); box(pad - 2, ry - 1, w - pad * 2 + 4, rowH - 2, true); }
ink(selected ? 255 : T.fgMute, selected ? 255 : T.fgMute, selected ? 255 : T.fgMute); write((selected ? "> " : " ") + s.name, { x: pad, y: ry, size: 1, font });
// Duration if (s.len && s.rate) { ink(80, 80, 100); const secs = (s.len / s.rate).toFixed(1); write(secs + "s", { x: w - pad - 24, y: ry, size: 1, font }); } else if (s.size) { ink(80, 80, 100); const kb = (s.size / 1024).toFixed(0); write(kb + "KB", { x: w - pad - 36, y: ry, size: 1, font }); } }
// Scroll indicator if (samples.length > maxRows) { ink(60, 60, 80); write(`${selectedIdx + 1}/${samples.length}`, { x: w - pad - 30, y: listY - 12, size: 1, font }); } }
// Controls const ctrlY = h - 26; ink(80, 80, 100); write("s:save enter:load space:play x:del", { x: pad, y: ctrlY, size: 1, font }); ink(T.fgMute, T.fgMute + 10, T.fgMute); write("esc: back", { x: pad, y: ctrlY + 12, size: 1, font });}
function sim() {}
export { boot, paint, act, sim };