ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
mirror.c
Go to the documentation of this file.
1
6#include <emscripten.h>
7#include <stdlib.h>
8#include <string.h>
9
10#include "common/init.h"
11
17#include <ascii-chat/log/log.h>
25#include <ascii-chat/common.h>
27
28// Global digital rain effect context
29static digital_rain_t *g_digital_rain = NULL;
30static char *g_last_rain_output = NULL;
31static double g_last_rain_update_time = 0.0;
32#define RAIN_UPDATE_INTERVAL_MS 100.0 // Update every 100ms for smooth animation
33
34// Frame counter for time-based effects (emscripten_get_now is unreliable in pthreads builds)
35static uint32_t g_frame_count = 0;
36
37// ============================================================================
38// Initialization
39// ============================================================================
40
46EMSCRIPTEN_KEEPALIVE
47int mirror_init_with_args(const char *args_json) {
48 // Initialize platform layer
50 if (err != ASCIICHAT_OK) {
51 return -1;
52 }
53
54 // Initialize logging to stderr (console.error in browser)
55 log_init(NULL, LOG_DEBUG, true, false);
56 // Use debug-style format in WASM so browser console shows full log headers
58
59 // Parse space-separated arguments
60 char *args_copy = NULL;
61 char *argv[64] = {NULL};
62 int argc = wasm_parse_args(args_json, argv, 64, &args_copy);
63 if (argc < 0) {
64 return -1;
65 }
66
67 // Initialize options (sets up RCU, defaults, etc.)
68 err = options_init(argc, argv);
69 free(args_copy);
70
71 if (err != ASCIICHAT_OK) {
72 return -1;
73 }
74
75 // Log option values after initialization
76 log_info("[mirror_init_with_args] After options_init: argc=%d, matrix_rain=%d", argc, GET_OPTION(matrix_rain));
77
78 // Initialize ANSI color code generation (dec3 cache for RGB values)
80
81 return 0;
82}
83
84EMSCRIPTEN_KEEPALIVE
85void mirror_cleanup(void) {
86 if (g_digital_rain) {
87 digital_rain_destroy(g_digital_rain);
88 g_digital_rain = NULL;
89 }
90 if (g_last_rain_output) {
91 SAFE_FREE(g_last_rain_output);
92 g_last_rain_output = NULL;
93 }
96}
97
98// ============================================================================
99// Frame Conversion API
100// ============================================================================
101
102EMSCRIPTEN_KEEPALIVE
103char *mirror_convert_frame(uint8_t *rgba_data, int src_width, int src_height) {
104 if (!rgba_data || src_width <= 0 || src_height <= 0) {
105 return NULL;
106 }
107
108 g_frame_count++;
109 // Derive a time value from frame count using actual target FPS from options
110 int target_fps = (int)GET_OPTION(fps);
111 if (target_fps <= 0) {
112 target_fps = 60; // Default to 60 FPS if not set
113 }
114 float time_seconds = (float)g_frame_count / (float)target_fps;
115
116 // Get current settings from options or terminal detection
117 int dst_width = (int)terminal_get_effective_width();
118 int dst_height = (int)terminal_get_effective_height();
119 color_filter_t filter = (color_filter_t)GET_OPTION(color_filter);
121 if (g_frame_count <= 3 || g_frame_count % 120 == 0) {
122 log_debug("frame=%u color_filter=%d color_mode=%d", g_frame_count, (int)filter, (int)color_mode);
123 }
124 if (color_mode == TERM_COLOR_AUTO) {
125 color_mode = TERM_COLOR_TRUECOLOR; // xterm.js supports truecolor
126 }
127 palette_type_t palette_type = (palette_type_t)GET_OPTION(palette_type);
128 bool preserve_aspect_ratio = true; // Preserve webcam aspect ratio
129 bool stretch = false; // Don't stretch - maintain proportions
130
131 // Build terminal capabilities structure with user's color mode
132 terminal_capabilities_t caps = {0}; // Zero-initialize all fields first
133 caps.color_level = color_mode;
134 caps.capabilities = 0;
135 caps.color_count = (color_mode == TERM_COLOR_NONE) ? 0
136 : (color_mode == TERM_COLOR_16) ? 16
137 : (color_mode == TERM_COLOR_256) ? 256
138 : 16777216;
139 caps.utf8_support = true;
140 caps.detection_reliable = true;
141 caps.render_mode = (render_mode_t)GET_OPTION(render_mode);
143 caps.wants_padding = true;
144 caps.palette_type = palette_type;
145 caps.desired_fps = 60;
146 caps.color_filter = filter;
147
148 // Convert RGBA to RGB (strip alpha channel) and optionally flip horizontally
149 rgb_pixel_t *rgb_pixels = SAFE_MALLOC(src_width * src_height * sizeof(rgb_pixel_t), rgb_pixel_t *);
150 if (!rgb_pixels) {
151 return NULL;
152 }
153
154 bool flip_x = GET_OPTION(flip_x);
155 bool flip_y = GET_OPTION(flip_y);
156 for (int y = 0; y < src_height; y++) {
157 for (int x = 0; x < src_width; x++) {
158 int src_x = flip_x ? (src_width - 1 - x) : x;
159 int src_y = flip_y ? (src_height - 1 - y) : y;
160 int src_idx = (src_y * src_width + src_x) * 4;
161 int dst_idx = y * src_width + x;
162
163 rgb_pixels[dst_idx].r = rgba_data[src_idx + 0];
164 rgb_pixels[dst_idx].g = rgba_data[src_idx + 1];
165 rgb_pixels[dst_idx].b = rgba_data[src_idx + 2];
166 // Alpha (rgba_data[src_idx + 3]) is discarded
167 }
168 }
169
170 // Apply color filter to pixels if needed (except rainbow - handled in ANSI stage)
171 if (filter != COLOR_FILTER_NONE && filter != COLOR_FILTER_RAINBOW) {
172 // color_filter operates on packed RGB24 format
173 uint8_t *rgb24 = (uint8_t *)rgb_pixels;
174 int stride = src_width * 3;
175 apply_color_filter(rgb24, src_width, src_height, stride, filter, time_seconds);
176 }
177
178 // Create image structure
179 image_t img = {.w = src_width, .h = src_height, .pixels = rgb_pixels, .alloc_method = IMAGE_ALLOC_SIMD};
180
181 // Get palette characters based on selected palette type
182 const char *palette_chars;
183 switch (palette_type) {
184 case PALETTE_BLOCKS:
185 palette_chars = PALETTE_CHARS_BLOCKS;
186 break;
187 case PALETTE_DIGITAL:
188 palette_chars = PALETTE_CHARS_DIGITAL;
189 break;
190 case PALETTE_MINIMAL:
191 palette_chars = PALETTE_CHARS_MINIMAL;
192 break;
193 case PALETTE_COOL:
194 palette_chars = PALETTE_CHARS_COOL;
195 break;
196 case PALETTE_CUSTOM:
197 // Custom palette uses user-provided characters from options
198 palette_chars = GET_OPTION(palette_custom);
199 if (!palette_chars || palette_chars[0] == '\0') {
200 palette_chars = PALETTE_CHARS_STANDARD; // Fallback
201 }
202 break;
203 case PALETTE_STANDARD:
204 default:
205 palette_chars = PALETTE_CHARS_STANDARD;
206 break;
207 }
208
209 // Convert to ASCII using capability-aware function
210 char *ascii_output = ascii_convert_with_capabilities(&img, dst_width, dst_height, &caps, preserve_aspect_ratio,
211 stretch, palette_chars);
212
213 // Clean up
214 SAFE_FREE(rgb_pixels);
215
216 if (!ascii_output) {
217 log_error("ascii_convert_with_capabilities returned NULL");
218 return NULL;
219 }
220
221 // Apply rainbow color filter to ANSI output by replacing RGB values
222 // This preserves character selection while applying rainbow colors
223 if (filter == COLOR_FILTER_RAINBOW) {
224 char *rainbow_output = rainbow_replace_ansi_colors(ascii_output, time_seconds);
225 if (rainbow_output) {
226 SAFE_FREE(ascii_output);
227 ascii_output = rainbow_output;
228 }
229 }
230
231 bool matrix_rain = GET_OPTION(matrix_rain);
232 if (matrix_rain) {
233 // Initialize digital rain context if needed or dimensions changed
234 if (!g_digital_rain || g_digital_rain->num_columns != dst_width || g_digital_rain->num_rows != dst_height) {
235 if (g_digital_rain) {
236 digital_rain_destroy(g_digital_rain);
237 }
238 if (g_last_rain_output) {
239 SAFE_FREE(g_last_rain_output);
240 g_last_rain_output = NULL;
241 }
242 g_digital_rain = digital_rain_init(dst_width, dst_height);
243 if (!g_digital_rain) {
244 log_error("Failed to initialize digital rain effect");
245 return ascii_output; // Return without effect on error
246 }
247 g_last_rain_update_time = emscripten_get_now();
248 }
249
250 // Update color from active filter (allows filter changes after initialization)
251 digital_rain_set_color_from_filter(g_digital_rain, filter);
252
253 // Time-based updates: only update effect every RAIN_UPDATE_INTERVAL_MS
254 double current_time = emscripten_get_now();
255 double elapsed_ms = current_time - g_last_rain_update_time;
256
257 if (elapsed_ms >= RAIN_UPDATE_INTERVAL_MS) {
258 // Calculate actual delta time in seconds
259 float delta_time = (float)(elapsed_ms / 1000.0);
260 g_last_rain_update_time = current_time;
261
262 // Apply effect and cache a copy
263 char *rain_output = digital_rain_apply(g_digital_rain, ascii_output, delta_time);
264 if (rain_output) {
265 // Free old cached output
266 if (g_last_rain_output) {
267 SAFE_FREE(g_last_rain_output);
268 }
269 // Cache a copy (we'll return the original rain_output)
270 g_last_rain_output = strdup(rain_output);
271 // Return the rain output (caller will free it)
272 SAFE_FREE(ascii_output);
273 ascii_output = rain_output;
274 }
275 } else if (g_last_rain_output) {
276 // Use cached output
277 SAFE_FREE(ascii_output);
278 ascii_output = strdup(g_last_rain_output);
279 }
280 } else {
281 // Clean up digital rain context if it exists but effect is disabled
282 if (g_digital_rain) {
283 digital_rain_destroy(g_digital_rain);
284 g_digital_rain = NULL;
285 }
286 if (g_last_rain_output) {
287 SAFE_FREE(g_last_rain_output);
288 g_last_rain_output = NULL;
289 }
290 g_last_rain_update_time = 0.0;
291 }
292
293 return ascii_output;
294}
295
296EMSCRIPTEN_KEEPALIVE
297void mirror_submit_audio_samples(const float *samples, int count, int source) {
298 if (!samples || count <= 0 || count > 8192)
299 return;
302 samples, (size_t)count);
303}
304
305EMSCRIPTEN_KEEPALIVE
306char *mirror_render_audio_visualization(unsigned int width, unsigned int height, int source) {
310 if (GET_OPTION(fft))
311 return audio_visualization_render_fft(width, height, audio_source, true, AUDIO_VISUALIZATION_COLOR_BRIGHTER,
312 GET_OPTION(flip_x), GET_OPTION(flip_y));
313 return audio_visualization_render_waveform(width, height, audio_source, true, AUDIO_VISUALIZATION_COLOR_BRIGHTER,
314 GET_OPTION(flip_x), GET_OPTION(flip_y));
315}
316
317EMSCRIPTEN_KEEPALIVE
318void mirror_free_string(char *ptr) {
319 SAFE_FREE(ptr);
320}
321
322// Embedded font data (defined in generated/data/fonts/default.c and matrix.c)
323extern const unsigned char g_font_default[];
324extern const size_t g_font_default_size;
325extern const unsigned char g_font_matrix_resurrected[];
326extern const size_t g_font_matrix_resurrected_size;
327
332EMSCRIPTEN_KEEPALIVE
333const unsigned char *get_font_default_ptr(void) {
334 // Auto-select matrix font when --matrix flag is set
335 int matrix_rain_val = GET_OPTION(matrix_rain);
336 log_info("[get_font_default_ptr] CALLED: matrix_rain=%d, g_font_matrix_resurrected=%p, g_font_default=%p",
337 matrix_rain_val, (void *)g_font_matrix_resurrected, (void *)g_font_default);
338 if (matrix_rain_val) {
339 log_info("[get_font_default_ptr] RETURNING matrix font");
341 }
342 log_info("[get_font_default_ptr] RETURNING default font");
343 return g_font_default;
344}
345
350EMSCRIPTEN_KEEPALIVE
351unsigned int get_font_default_size(void) {
352 // Auto-select matrix font when --matrix flag is set
353 if (GET_OPTION(matrix_rain)) {
354 return (unsigned int)g_font_matrix_resurrected_size;
355 }
356 return (unsigned int)g_font_default_size;
357}
ANSI escape sequence utilities and fast color code generation.
βš οΈβ€ΌοΈ Comprehensive thread-local error context system for ascii-chat
πŸ“ Aspect Ratio Calculation Functions
βš™οΈ Common definitions, error codes, macros, and types shared throughout the application
Matrix-style digital rain effect for ASCII frames.
void ansi_fast_init(void)
Definition ansi.c:319
unsigned int uint32_t
Definition common.h:58
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
unsigned char uint8_t
Definition common.h:56
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ASCIICHAT_OK
Definition error_codes.h:51
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
void log_init(const char *filename, log_level_t level, bool force_stderr, bool use_mmap)
Initialize the logging system.
Definition log/log.c:533
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
asciichat_error_t log_set_format(const char *format_str, bool console_only)
Set a custom log format string.
Definition log/log.c:744
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
asciichat_error_t options_init(int argc, char **argv)
Initialize options by parsing command-line arguments with unified mode detection.
#define OPT_LOG_TEMPLATE_DEFAULT_DEBUG
Default log format string - debug mode (verbose with thread name, file relative path,...
#define PALETTE_CHARS_MINIMAL
Minimal ASCII palette character string.
Definition palette.h:188
#define PALETTE_CHARS_DIGITAL
Digital/glitch palette character string.
Definition palette.h:179
palette_type_t
Built-in palette type enumeration.
Definition palette.h:84
#define PALETTE_CHARS_STANDARD
Standard ASCII palette character string.
Definition palette.h:161
#define PALETTE_CHARS_BLOCKS
Unicode block palette character string.
Definition palette.h:170
#define PALETTE_CHARS_COOL
Cool ascending blocks palette character string.
Definition palette.h:197
@ PALETTE_BLOCKS
Unicode block characters: " β–‘β–‘β–’β–’β–“β–“β–ˆβ–ˆ".
Definition palette.h:90
@ PALETTE_CUSTOM
User-defined via –palette-chars.
Definition palette.h:98
@ PALETTE_COOL
Ascending blocks: " β–β–‚β–ƒβ–„β–…β–†β–‡β–ˆ".
Definition palette.h:96
@ PALETTE_STANDARD
Standard ASCII palette: " ...',;:clodxkO0KXNWM".
Definition palette.h:88
@ PALETTE_DIGITAL
Digital/glitch aesthetic: " -=≑≣▰▱◼".
Definition palette.h:92
@ PALETTE_MINIMAL
Simple ASCII: " .-+*#".
Definition palette.h:94
void platform_destroy(void)
Cleanup platform-specific subsystems.
asciichat_error_t platform_init(void)
Initialize platform-specific subsystems.
char * ascii_convert_with_capabilities(image_t *original, const ssize_t width, const ssize_t height, const terminal_capabilities_t *caps, const bool use_aspect_ratio, const bool stretch, const char *palette_chars)
Convert image to ASCII art with terminal capability awareness.
Definition ascii.c:233
char * rainbow_replace_ansi_colors(const char *ansi_string, float time_seconds)
Replace all RGB ANSI codes with rainbow color.
int apply_color_filter(uint8_t *pixels, uint32_t width, uint32_t height, uint32_t stride, color_filter_t filter, float time)
Apply color filter to entire image in-place.
void digital_rain_set_color_from_filter(digital_rain_t *rain, color_filter_t filter)
Set digital rain color from color filter.
void digital_rain_destroy(digital_rain_t *rain)
Destroy digital rain effect context.
char * digital_rain_apply(digital_rain_t *rain, const char *frame, float delta_time)
Apply digital rain effect to ASCII frame.
digital_rain_t * digital_rain_init(int num_columns, int num_rows)
Initialize digital rain effect context.
@ IMAGE_ALLOC_SIMD
Pixels allocated with SAFE_MALLOC_SIMD()
βš™οΈ Unified options parsing system for ascii-chat with builder pattern and lock-free access
Platform initialization and static synchronization helpers.
πŸ“ Logging API with multiple log levels and terminal output control
EMSCRIPTEN_KEEPALIVE void mirror_cleanup(void)
Definition mirror.c:85
EMSCRIPTEN_KEEPALIVE char * mirror_convert_frame(uint8_t *rgba_data, int src_width, int src_height)
Definition mirror.c:103
EMSCRIPTEN_KEEPALIVE void mirror_free_string(char *ptr)
Definition mirror.c:318
const size_t g_font_matrix_resurrected_size
const unsigned char g_font_matrix_resurrected[]
const size_t g_font_default_size
EMSCRIPTEN_KEEPALIVE unsigned int get_font_default_size(void)
Definition mirror.c:351
const unsigned char g_font_default[]
#define RAIN_UPDATE_INTERVAL_MS
Definition mirror.c:32
EMSCRIPTEN_KEEPALIVE const unsigned char * get_font_default_ptr(void)
Definition mirror.c:333
EMSCRIPTEN_KEEPALIVE int mirror_init_with_args(const char *args_json)
Definition mirror.c:47
EMSCRIPTEN_KEEPALIVE char * mirror_render_audio_visualization(unsigned int width, unsigned int height, int source)
Definition mirror.c:306
EMSCRIPTEN_KEEPALIVE void mirror_submit_audio_samples(const float *samples, int count, int source)
Definition mirror.c:297
ASCII Palette Management for Video-to-ASCII Conversion.
unsigned short int terminal_get_effective_width(void)
Get effective terminal width with fallback priority.
unsigned short int terminal_get_effective_height(void)
Get effective terminal height with fallback priority.
void options_state_destroy(void)
Shutdown RCU options system.
Definition rcu.c:461
int wasm_parse_args(const char *args_str, char **argv, int max_args, char **out_args_copy)
WASM initialization helpers.
Digital rain effect context.
int num_columns
Number of columns in grid.
int num_rows
Number of rows in grid.
Image structure.
int w
Image width in pixels (must be > 0)
RGB pixel structure.
uint8_t b
Blue color component (0-255)
uint8_t g
Green color component (0-255)
uint8_t r
Red color component (0-255)
Complete terminal capabilities structure.
Definition terminal.h:709
int palette_type
Palette type enum value (palette_type_t)
Definition terminal.h:729
terminal_color_mode_t color_level
Detected color support level (terminal_color_mode_t)
Definition terminal.h:711
uint8_t desired_fps
Client's desired frame rate (1-144 FPS)
Definition terminal.h:733
render_mode_t render_mode
Preferred rendering mode (render_mode_t)
Definition terminal.h:721
bool utf8_support
True if terminal supports UTF-8 encoding.
Definition terminal.h:717
bool wants_padding
Whether client wants frame padding (centering) - false for snapshot/piped modes.
Definition terminal.h:737
uint32_t capabilities
Capability flags bitmask (terminal_capability_flags_t)
Definition terminal.h:713
uint32_t color_count
Maximum number of colors (2, 16, 256, or 16777216)
Definition terminal.h:715
color_filter_t color_filter
Monochromatic color filter enum value (color_filter_t)
Definition terminal.h:735
bool detection_reliable
True if detection is confident (reliable detection)
Definition terminal.h:719
bool wants_background
True if background colors are preferred.
Definition terminal.h:727
πŸ–₯️ Cross-platform terminal interface for ascii-chat
color_filter_t
Monochromatic color filter enumeration.
Definition terminal.h:599
@ COLOR_FILTER_NONE
No filtering (default)
Definition terminal.h:601
@ COLOR_FILTER_RAINBOW
Rainbow (cycles through spectrum over 3.5s)
Definition terminal.h:625
render_mode_t
Render mode preferences.
Definition terminal.h:662
@ RENDER_MODE_BACKGROUND
Background colors (block colors)
Definition terminal.h:666
terminal_color_mode_t
Terminal color support levels.
Definition terminal.h:578
@ TERM_COLOR_NONE
No color support (monochrome terminal)
Definition terminal.h:582
@ TERM_COLOR_16
16-color support (standard ANSI colors)
Definition terminal.h:584
@ TERM_COLOR_256
256-color support (extended ANSI palette)
Definition terminal.h:586
@ TERM_COLOR_AUTO
Auto-detect color support from terminal capabilities.
Definition terminal.h:580
@ TERM_COLOR_TRUECOLOR
24-bit truecolor support (RGB colors)
Definition terminal.h:588
#define LOG_DEBUG
Definition types.h:39
void audio_visualization_submit(audio_visualization_source_t source, const float *samples, size_t count)
char * audio_visualization_render_waveform(unsigned int width, unsigned int height, audio_visualization_source_t source, bool use_color, int color_mode, bool flip_x, bool flip_y)
char * audio_visualization_render_fft(unsigned int width, unsigned int height, audio_visualization_source_t source, bool use_color, int color_mode, bool flip_x, bool flip_y)
audio_visualization_source_t
@ AUDIO_VISUALIZATION_SOURCE_MEDIA
@ AUDIO_VISUALIZATION_SOURCE_MIC
#define AUDIO_VISUALIZATION_COLOR_BRIGHTER