Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

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

notepat: simpler hold (enter/backspace), add insert as tape key

HOLD rewrite:
- Enter key: snapshot currently-playing notes into heldKeys (press again
to add more notes to the hold set as you play).
- Backspace: clear all held notes.
- New notes never auto-latch — you can freely riff on top of the held
chord without sticking every note. Only explicit Enter press commits
a note to sustain.
- Remove F11/F10 bindings and the f11Held flourish-mode logic.
- HOLD indicator now shows count ("H:3") instead of just "HOLD", and
only appears when there's actually something held.

Tape recording:
- Insert and Pause now also toggle tape recording (alongside PrintScreen)
as fallbacks for laptops where PrtSc doesn't fire standalone.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+38 -41
+29 -35
fedac/native/pieces/notepat.mjs
··· 55 55 let endArmed = false; // true while End key is held (arm per-key recording) 56 56 let perKeyRecording = null; // key currently recording in per-key mode 57 57 58 - // Hold/latch: F11 (green pickup) engages, F10 (red hangup) clears 59 - // While engaged, any new notes auto-latch (sustain on key release) 58 + // Hold/latch: Enter = add currently-playing notes to held set (press again 59 + // to add more). Backspace = clear all holds. New notes never auto-latch — 60 + // you can freely riff over held notes until you explicitly press Enter to 61 + // snapshot them into the held set. 60 62 let recitalMode = false; // F12: hide all UI, show only colored backdrops 61 63 let helpPanel = false; // Meta/Win key: show keyboard shortcut help overlay 62 - let holdActive = false; // true when hold is engaged 63 64 let heldKeys = new Set(); // keys that are latched (won't stop on key-up) 64 - // Flourish mode: while F11 is physically held (after the initial engage), 65 - // new notes are NOT added to heldKeys so you can play melodic riffs over 66 - // the latched chord without sticking every new note. 67 - let f11Held = false; 68 65 69 66 // Effective pitch shift blended by FX mix (0% fx = no pitch shift) 70 67 function effectivePitchShift() { ··· 1315 1312 entry.midiNote = noteToMidiNumber(entry.note, entry.octave); 1316 1313 entry.midiChannel = 0; 1317 1314 sounds[key] = entry; 1318 - // Auto-add to hold if F11 latch is active, UNLESS the F11 key is currently 1319 - // physically held down — in that "flourish" mode we want to play melodic 1320 - // riffs over the already-latched chord without sticking the new notes. 1321 - if (holdActive && !f11Held) heldKeys.add(key); 1315 + // New notes never auto-latch. Use Enter to snapshot currently-playing 1316 + // notes into the held set. 1322 1317 system?.usbMidi?.noteOn?.(entry.midiNote, velocityToMidi(velocity), entry.midiChannel); 1323 1318 sendUdpMidiEvent(system, "note_on", entry.midiNote, velocityToMidi(velocity), entry.midiChannel); 1324 1319 pushUsbMidiRecent(">", entry.note, entry.octave); ··· 1353 1348 1354 1349 function stopAllSounds(sound, system, fade = 0.08) { 1355 1350 heldKeys.clear(); 1356 - holdActive = false; 1357 1351 stopReversePlayback(sound); 1358 1352 for (const key of Object.keys(sounds)) stopSoundKey(key, sound, system, fade); 1359 1353 activeDrumKeys = {}; ··· 1688 1682 } 1689 1683 return; 1690 1684 } 1691 - // F11 (green phone pickup): Engage hold/latch 1692 - // - Press snapshots currently-playing keys into heldKeys + enables hold 1693 - // - While physically held, f11Held suppresses auto-latch so new notes 1694 - // become flourishes over the held chord instead of sticking 1695 - if (key === "f11") { 1696 - // Only re-snapshot on the first press (ignore autorepeat while held) 1697 - if (!f11Held) { 1698 - heldKeys = new Set(Object.keys(sounds)); 1699 - holdActive = true; 1685 + // Enter: "add holds" — snapshot currently-playing keys into heldKeys. 1686 + // Press again at any time to add newly-playing notes. New notes never 1687 + // auto-latch, so you can riff freely and only commit to sustain by 1688 + // pressing Enter while the notes are still held down. 1689 + if (key === "enter") { 1690 + let added = 0; 1691 + for (const k of Object.keys(sounds)) { 1692 + if (!heldKeys.has(k)) { heldKeys.add(k); added++; } 1693 + } 1694 + if (added > 0) { 1700 1695 sound?.synth?.({ type: "sine", tone: 660, duration: 0.06, volume: 0.12, attack: 0.002, decay: 0.05 }); 1701 1696 } 1702 - f11Held = true; 1703 1697 return; 1704 1698 } 1705 - // F10 (red phone hangup): Clear hold — stop all held notes 1706 - if (key === "f10") { 1707 - if (holdActive) { 1699 + // Backspace: "clear holds" — stop all held notes 1700 + if (key === "backspace") { 1701 + if (heldKeys.size > 0) { 1708 1702 for (const k of heldKeys) stopSoundKey(k, sound, system, 0.08); 1709 1703 heldKeys.clear(); 1710 - holdActive = false; 1711 1704 sound?.synth?.({ type: "sine", tone: 330, duration: 0.06, volume: 0.12, attack: 0.002, decay: 0.05 }); 1712 1705 } 1713 1706 return; ··· 1983 1976 stopReversePlayback(sound); 1984 1977 return; 1985 1978 } 1986 - // F11 release — exit flourish mode (new notes will auto-latch again) 1987 - if (key === "f11") { f11Held = false; return; } 1988 1979 // Home key release: stop global recording + save to global sample 1989 1980 if (key === "home" && recording && recPointerId === null) { 1990 1981 stopSampleRecording(sound, "home-lift"); ··· 3984 3975 box(obx, waveRowY, 1, waveRowH, true); 3985 3976 ink(dark ? 140 : 100, dark ? 140 : 100, dark ? 150 : 110); 3986 3977 write("o:" + octave, { x: obx + 3, y: waveRowY + 3, size: 1, font: "font_1" }); 3987 - // HOLD indicator (drawn to the LEFT of the octave button) 3988 - if (holdActive) { 3989 - const holdW = 30; 3978 + // Hold count indicator: show "H:n" where n = number of held notes. 3979 + // Drawn to the LEFT of the octave button; only visible when 3980 + // there's something held, so it doesn't fight for screen real estate. 3981 + if (heldKeys.size > 0) { 3982 + const label = "H:" + heldKeys.size; 3983 + const holdW = label.length * 6 + 4; 3990 3984 const hbx = obx - holdW - 2; 3991 3985 ink(dark ? 80 : 180, dark ? 40 : 100, dark ? 40 : 100); 3992 3986 box(hbx, waveRowY, holdW, waveRowH, true); 3993 3987 ink(255, dark ? 180 : 60, dark ? 120 : 40); 3994 - write("HOLD", { x: hbx + 2, y: waveRowY + 3, size: 1, font: "font_1" }); 3988 + write(label, { x: hbx + 2, y: waveRowY + 3, size: 1, font: "font_1" }); 3995 3989 } 3996 3990 } 3997 3991 ··· 4421 4415 ["drag", "scratch platter"], 4422 4416 ]], 4423 4417 ["HOLD", [255, 220, 120], [ 4424 - ["F11 📞", "engage / flourish"], 4425 - ["F10 📞", "clear hold"], 4418 + ["enter", "add holds"], 4419 + ["backspace", "clear holds"], 4426 4420 ]], 4427 4421 ["TEMPO", [255, 180, 100], [ 4428 4422 ["F9", "metronome"], ··· 4437 4431 ["\\", "trackpad (X echo, Y pitch)"], 4438 4432 ]], 4439 4433 ["TAPE", [255, 120, 120], [ 4440 - ["prtsc", "start/stop tape (MP4 + cloud)"], 4434 + ["prtsc/ins", "start/stop tape (MP4 + cloud)"], 4441 4435 ]], 4442 4436 ["SYSTEM", [190, 190, 205], [ 4443 4437 ["meta ⊞", "toggle this help"],
+9 -6
fedac/native/src/ac-native.c
··· 3656 3656 audio_synth(audio, WAVE_SINE, up ? 1000.0 : 600.0, 3657 3657 0.04, 0.12, 0.001, 0.03, 0.0); 3658 3658 } 3659 - // Tape recording: PrintScreen key toggles MP4 tape recording. 3660 - // Saves to /mnt/tapes/<slug>.mp4 where slug follows the same 3661 - // YYYY.MM.DD.HH.MM.SS.mmm format that web AC uses for tapes. 3662 - // On stop, auto-uploads to the cloud via a background curl 3663 - // shell-out (see recorder_upload_async helper below). 3664 - else if (strcmp(input->events[i].key_name, "printscreen") == 0 && recorder) { 3659 + // Tape recording: PrintScreen (or Insert/Pause as fallbacks 3660 + // on laptops where PrtSc doesn't fire standalone) toggles 3661 + // MP4 tape recording. Saves to /mnt/tapes/<slug>.mp4 where 3662 + // slug follows the same YYYY.MM.DD.HH.MM.SS.mmm format that 3663 + // web AC uses for tapes. On stop, auto-uploads to the 3664 + // cloud via a background curl shell-out. 3665 + else if ((strcmp(input->events[i].key_name, "printscreen") == 0 || 3666 + strcmp(input->events[i].key_name, "insert") == 0 || 3667 + strcmp(input->events[i].key_name, "pause") == 0) && recorder) { 3665 3668 if (recorder_is_recording(recorder)) { 3666 3669 // Stop: remove audio tap, finalize file 3667 3670 if (audio) {