Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

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

feat: recording tap callback + build changes

Add rec_callback/rec_userdata to ACAudio for PCM recording tap.

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

+92 -1
+13 -1
fedac/native/Makefile
··· 33 33 ALSA_LIBS := $(shell pkg-config --libs alsa 2>/dev/null || echo "-lasound") 34 34 FLITE_LIBS := -lflite -lflite_cmulex -lflite_usenglish -lflite_cmu_us_slt -lflite_cmu_us_kal 35 35 36 + # ffmpeg libraries for video recording (optional: disabled if not found) 37 + HAVE_AVCODEC := $(shell pkg-config --exists libavcodec libavformat libavutil libswscale libswresample 2>/dev/null && echo 1) 38 + ifeq ($(HAVE_AVCODEC),1) 39 + AV_CFLAGS := $(shell pkg-config --cflags libavcodec libavformat libavutil libswscale libswresample 2>/dev/null) 40 + AV_LIBS := $(shell pkg-config --libs libavcodec libavformat libavutil libswscale libswresample 2>/dev/null) 41 + endif 42 + 36 43 CFLAGS += $(DRM_CFLAGS) $(ALSA_CFLAGS) 37 44 LDFLAGS += $(DRM_LIBS) $(ALSA_LIBS) $(FLITE_LIBS) 45 + ifeq ($(HAVE_AVCODEC),1) 46 + CFLAGS += -DHAVE_AVCODEC $(AV_CFLAGS) 47 + LDFLAGS += $(AV_LIBS) 48 + endif 38 49 39 50 # SDL2 GPU-accelerated display (optional: make USE_SDL=1) 40 51 ifdef USE_SDL ··· 73 84 $(SRCDIR)/camera.c \ 74 85 $(SRCDIR)/pty.c \ 75 86 $(SRCDIR)/machines.c \ 76 - $(SRCDIR)/js-bindings.c 87 + $(SRCDIR)/js-bindings.c \ 88 + $(SRCDIR)/recorder.c 77 89 78 90 # Wayland display backend (conditional) 79 91 ifdef USE_WAYLAND
+2
fedac/native/scripts/build-and-flash.sh
··· 83 83 command -v iw &>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} iw" 84 84 [ -d /lib/firmware ] && ls /lib/firmware/iwlwifi-cc-a0-* &>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} iwlwifi-mvm-firmware" 85 85 [ -f /lib/firmware/regulatory.db ] || PKGS_NEEDED="${PKGS_NEEDED} wireless-regdb" 86 + # ffmpeg libs for video recording 87 + pkg-config --exists libavcodec 2>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} ffmpeg-free-devel" 86 88 # Flash tools 87 89 if [ -n "${FLASH_DEV}" ]; then 88 90 command -v mmd &>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} mtools"
+73
fedac/native/src/ac-native.c
··· 33 33 #include "tts.h" 34 34 #include "js-bindings.h" 35 35 #include "machines.h" 36 + #include "recorder.h" 36 37 #ifdef USE_WAYLAND 37 38 #include "wayland-display.h" 38 39 #endif ··· 1780 1781 } 1781 1782 } 1782 1783 1784 + // Audio recording tap callback (adapts rec_callback signature to recorder_submit_audio) 1785 + static void rec_audio_tap(const int16_t *pcm, int frames, void *userdata) { 1786 + recorder_submit_audio((ACRecorder *)userdata, pcm, frames); 1787 + } 1788 + 1783 1789 int main(int argc, char *argv[]) { 1784 1790 struct timespec boot_start; 1785 1791 clock_gettime(CLOCK_MONOTONIC, &boot_start); ··· 2331 2337 } 2332 2338 #endif 2333 2339 2340 + // Video recorder (F9 to toggle) — declared before headless branch so cleanup is in scope 2341 + ACRecorder *recorder = NULL; 2342 + 2334 2343 if (headless) { 2335 2344 for (int i = 0; i < 10 && running; i++) { 2336 2345 js_call_sim(rt); 2337 2346 js_call_paint(rt); 2338 2347 } 2339 2348 } else { 2349 + #ifdef HAVE_AVCODEC 2350 + if (screen && audio) { 2351 + recorder = recorder_create(screen->width, screen->height, 60, 2352 + audio->actual_rate ? audio->actual_rate : 192000); 2353 + if (recorder) { 2354 + ac_log("[ac-native] recorder ready (%dx%d)\n", screen->width, screen->height); 2355 + } 2356 + } 2357 + #endif 2358 + 2340 2359 // Main loop 2341 2360 struct timespec frame_time; 2342 2361 clock_gettime(CLOCK_MONOTONIC, &frame_time); ··· 2619 2638 audio_synth(audio, WAVE_SINE, up ? 1000.0 : 600.0, 2620 2639 0.04, 0.12, 0.001, 0.03, 0.0); 2621 2640 } 2641 + // Recording: F9 toggles MP4 recording 2642 + else if (strcmp(input->events[i].key_name, "f9") == 0 && recorder) { 2643 + if (recorder_is_recording(recorder)) { 2644 + // Stop: remove audio tap, finalize file 2645 + if (audio) { 2646 + audio->rec_callback = NULL; 2647 + audio->rec_userdata = NULL; 2648 + } 2649 + recorder_stop(recorder); 2650 + // Descending tone = stopped 2651 + audio_synth(audio, WAVE_SINE, 880.0, 0.12, 0.2, 0.001, 0.10, 0.0); 2652 + audio_synth(audio, WAVE_SINE, 660.0, 0.12, 0.15, 0.03, 0.09, 0.0); 2653 + } else { 2654 + // Start: generate timestamped filename, wire up audio tap 2655 + mkdir("/mnt/rec", 0755); 2656 + time_t now = time(NULL); 2657 + struct tm *tm = gmtime(&now); 2658 + char rec_path[128]; 2659 + snprintf(rec_path, sizeof(rec_path), 2660 + "/mnt/rec/%04d-%02d-%02d_%02d-%02d-%02d.mp4", 2661 + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, 2662 + tm->tm_hour, tm->tm_min, tm->tm_sec); 2663 + if (recorder_start(recorder, rec_path) == 0) { 2664 + // Wire up audio tap 2665 + if (audio) { 2666 + audio->rec_userdata = recorder; 2667 + audio->rec_callback = rec_audio_tap; 2668 + } 2669 + // Ascending tone = started 2670 + audio_synth(audio, WAVE_SINE, 660.0, 0.12, 0.2, 0.001, 0.10, 0.0); 2671 + audio_synth(audio, WAVE_SINE, 880.0, 0.12, 0.15, 0.03, 0.09, 0.0); 2672 + } 2673 + } 2674 + } 2622 2675 else if (strcmp(input->events[i].key_name, "power") == 0 || 2623 2676 input->events[i].key_code == KEY_POWER) 2624 2677 power_pressed = 1; ··· 2953 3006 } 2954 3007 } 2955 3008 3009 + // Submit frame to recorder (after cursor overlay, before display) 3010 + if (recorder_is_recording(recorder)) 3011 + recorder_submit_video(recorder, screen->pixels, screen->stride); 3012 + 3013 + // Draw recording indicator (red dot + duration) 3014 + if (recorder_is_recording(recorder)) { 3015 + graph_page(&graph, screen); 3016 + graph_ink(&graph, (ACColor){255, 40, 40, 255}); 3017 + // Red dot at top-right (4px circle) 3018 + int rx = screen->width - 8, ry = 4; 3019 + graph_circle(&graph, rx, ry, 3, 1); 3020 + } 3021 + 2956 3022 clock_gettime(CLOCK_MONOTONIC, &_pf_pres0); 2957 3023 ac_display_present(display, screen, pixel_scale); 2958 3024 clock_gettime(CLOCK_MONOTONIC, &_pf_pres1); ··· 3044 3110 3045 3111 frame_sync_60fps(&frame_time); 3046 3112 } 3113 + } 3114 + 3115 + // Stop recording if active 3116 + if (recorder) { 3117 + if (audio) { audio->rec_callback = NULL; audio->rec_userdata = NULL; } 3118 + recorder_destroy(recorder); 3119 + recorder = NULL; 3047 3120 } 3048 3121 3049 3122 // Flush final perf data
+4
fedac/native/src/audio.h
··· 152 152 int hdmi_period_pos; // samples written so far 153 153 int hdmi_period_size; // target period size in frames 154 154 155 + // Recording tap: if set, called after each mixed period with final int16 PCM 156 + void (*rec_callback)(const int16_t *pcm, int frames, void *userdata); 157 + void *rec_userdata; 158 + 155 159 // Diagnostic info (exposed to JS via system.hw) 156 160 char audio_device[32]; // ALSA device name that opened successfully 157 161 char audio_status[64]; // human-readable status ("ok", "no card", etc.)