ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
fft.c File Reference

Go to the source code of this file.

Macros

#define FFT_SIZE   1024
#define FFT_CACHE_CELLS   (MAX_TERMINAL_WIDTH * MAX_TERMINAL_HEIGHT * 2)
#define FFT_REFRESH_NS   (100 * NS_PER_MS_INT)

Functions

asciichat_error_t audio_visualization_fft_init (void)
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)

Variables

static_mutex_t audio_fftw_planner_lock

Macro Definition Documentation

◆ FFT_CACHE_CELLS

#define FFT_CACHE_CELLS   (MAX_TERMINAL_WIDTH * MAX_TERMINAL_HEIGHT * 2)

Definition at line 16 of file fft.c.

◆ FFT_REFRESH_NS

#define FFT_REFRESH_NS   (100 * NS_PER_MS_INT)

Definition at line 17 of file fft.c.

Referenced by audio_visualization_render_fft().

◆ FFT_SIZE

#define FFT_SIZE   1024

Definition at line 15 of file fft.c.

Referenced by audio_visualization_fft_init(), and audio_visualization_render_fft().

Function Documentation

◆ audio_visualization_fft_init()

asciichat_error_t audio_visualization_fft_init ( void )

Create the shared FFTW visualization plan before rendering begins.

Definition at line 61 of file fft.c.

61 {
62 (void)NAMED_REGISTER_MUTEX(&fft_cache_mutex, "audio.visualization.fft_cache", NULL);
63 (void)NAMED_REGISTER_MUTEX(&fftw_mutex, "audio.visualization.fftw", NULL);
64 (void)NAMED_REGISTER_MUTEX(&audio_fftw_planner_lock, "audio.fftw_planner", NULL);
65 static_mutex_lock(&fftw_mutex);
66 if (!fftw_forward) {
67 static_mutex_lock(&audio_fftw_planner_lock);
68 fftw_forward = fftw_plan_dft_r2c_1d(FFT_SIZE, fftw_input, fftw_output, FFTW_ESTIMATE);
69 static_mutex_unlock(&audio_fftw_planner_lock);
70 }
71 bool ready = fftw_forward != NULL;
72 static_mutex_unlock(&fftw_mutex);
73 return ready ? ASCIICHAT_OK : SET_ERRNO(ERROR_MEMORY, "Cannot create FFTW visualization plan");
74}
#define SET_ERRNO(code,...)
#define FFT_SIZE
Definition fft.c:15
#define NAMED_REGISTER_MUTEX(mutex, name, parent_ptr)
Register a mutex with automatic format specifier.
@ ERROR_MEMORY
Definition error_codes.h:51
@ ASCIICHAT_OK
Definition error_codes.h:46
static_mutex_t audio_fftw_planner_lock
Definition spectral.c:34

References ASCIICHAT_OK, audio_fftw_planner_lock, ERROR_MEMORY, FFT_SIZE, NAMED_REGISTER_MUTEX, and SET_ERRNO.

Referenced by mirror_init(), session_display_create(), and ui_session_key().

◆ audio_visualization_render_fft()

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 )

Definition at line 91 of file fft.c.

92 {
95 return NULL;
96
97 if (GET_OPTION(render_mode) == RENDER_MODE_HALF_BLOCK)
98 height *= 2;
99 static_mutex_lock(&fft_cache_mutex);
100 uint64_t now = time_get_ns();
101 if (fft_cache.valid && fft_cache.width == width && fft_cache.height == height && fft_cache.source == source &&
102 fft_cache.color_mode == color_mode && now >= fft_cache.updated_ns &&
103 now - fft_cache.updated_ns < FFT_REFRESH_NS) {
104 char *frame =
105 visualization_encode(fft_cache.cells, fft_cache.colors, false, width, height, use_color, flip_x, flip_y);
106 static_mutex_unlock(&fft_cache_mutex);
107 return frame;
108 }
109 float *samples = SAFE_MALLOC(VISUALIZATION_HISTORY * sizeof(float), float *);
110 if (!samples) {
111 static_mutex_unlock(&fft_cache_mutex);
112 return NULL;
113 }
115 size_t cell_count = (size_t)width * height;
116 char *cells = fft_cache.cells;
117 unsigned char *colors = fft_cache.colors;
118 memset(cells, ' ', cell_count);
119 memset(colors, 0, cell_count * 3);
120
121 /* Each column is a 21 ms Hann-windowed spectrum; adjacent columns overlap heavily. */
122 float windowed[FFT_SIZE], magnitude[FFT_SIZE / 2 + 1];
123 fft_scale_t scale;
124 if (!parse_fft_scale_value(GET_OPTION(fft_scale), &scale))
125 scale = (fft_scale_t){.mode = FFT_SCALE_DB, .min_db = -62.0f, .max_db = -6.0f};
126 static const char levels[] = " .:-=+*#%@";
127 const unsigned int spectrum_height = height;
128 for (unsigned int x = 0; x < width; x++) {
129 size_t center = (size_t)x * (VISUALIZATION_HISTORY - 1) / (width - 1);
130 size_t start = center > FFT_SIZE / 2 ? center - FFT_SIZE / 2 : 0;
131 if (start + FFT_SIZE > VISUALIZATION_HISTORY)
133 for (size_t i = 0; i < FFT_SIZE; i++) {
134 float window = 0.5f - 0.5f * cosf(6.28318530718f * (float)i / (float)(FFT_SIZE - 1));
135 windowed[i] = samples[start + i] * window;
136 }
137 if (!visualization_fft(windowed, magnitude)) {
138 SAFE_FREE(samples);
139 static_mutex_unlock(&fft_cache_mutex);
140 return NULL;
141 }
142
143 for (unsigned int row = 0; row < spectrum_height; row++) {
144 float top_fraction = (float)(spectrum_height - row - 1) / (float)spectrum_height;
145 float bottom_fraction = (float)(spectrum_height - row) / (float)spectrum_height;
146 float low_hz = 35.0f * powf(18000.0f / 35.0f, top_fraction);
147 float high_hz = 35.0f * powf(18000.0f / 35.0f, bottom_fraction);
148 size_t low_bin = (size_t)fmaxf(1.0f, floorf(low_hz * FFT_SIZE / AUDIO_VISUALIZATION_SAMPLE_RATE));
149 size_t high_bin =
150 (size_t)fmaxf((float)(low_bin + 1), ceilf(high_hz * FFT_SIZE / AUDIO_VISUALIZATION_SAMPLE_RATE));
151 if (high_bin > FFT_SIZE / 2)
152 high_bin = FFT_SIZE / 2;
153 float power = 0.0f;
154 size_t bins = 0;
155 for (size_t bin = low_bin; bin < high_bin; bin++) {
156 power += magnitude[bin] * magnitude[bin];
157 bins++;
158 }
159 /* The default dB range leaves room for quiet speech while keeping loud music detailed. */
160 float amplitude = bins ? sqrtf(power / (float)bins) : 0.0f;
161 float intensity;
162 if (scale.mode == FFT_SCALE_LINEAR)
163 intensity = fminf(1.0f, amplitude * 4.0f);
164 else if (scale.mode == FFT_SCALE_LOG)
165 intensity = fminf(1.0f, log1pf(amplitude * 31.0f) / log1pf(31.0f));
166 else {
167 float db = 20.0f * log10f(fmaxf(amplitude, 0.00001f));
168 intensity = fmaxf(0.0f, fminf(1.0f, (db - scale.min_db) / (scale.max_db - scale.min_db)));
169 }
170 int level = (int)lrintf(intensity * (float)(sizeof(levels) - 2));
171 size_t cell = (size_t)row * width + x;
172 cells[cell] = levels[level];
173 float band_hz = sqrtf(low_hz * high_hz);
174 visualization_color_for_level(band_hz, intensity, color_mode, &colors[cell * 3], &colors[cell * 3 + 1],
175 &colors[cell * 3 + 2]);
176 }
177 }
178
179 fft_cache.width = width;
180 fft_cache.height = height;
181 fft_cache.source = source;
182 fft_cache.color_mode = color_mode;
183 fft_cache.updated_ns = time_get_ns();
184 fft_cache.valid = true;
185 char *frame = visualization_encode(cells, colors, false, width, height, use_color, flip_x, flip_y);
186 SAFE_FREE(samples);
187 static_mutex_unlock(&fft_cache_mutex);
188 return frame;
189}
int color_mode
Definition fft.c:25
char cells[(512 *256 *2)]
Definition fft.c:21
unsigned char colors[(512 *256 *2) *3]
Definition fft.c:22
#define FFT_REFRESH_NS
Definition fft.c:17
audio_visualization_source_t source
Definition fft.c:24
unsigned int height
Definition fft.c:23
unsigned int width
Definition fft.c:23
@ FFT_SCALE_LOG
Definition fft.h:8
@ FFT_SCALE_LINEAR
Definition fft.h:8
@ FFT_SCALE_DB
Definition fft.h:8
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
unsigned long long uint64_t
Definition common.h:63
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336
fft_scale_mode_t mode
Definition fft.h:11
float max_db
Definition fft.h:13
float min_db
Definition fft.h:12
char * visualization_encode(const char *cells, const unsigned char *colors, bool column_colors, unsigned int width, unsigned int height, bool use_color, bool flip_x, bool flip_y)
void visualization_color_for_level(float frequency_hz, float level, int color_mode, unsigned char *red, unsigned char *green, unsigned char *blue)
void audio_visualization_read(audio_visualization_source_t source, float *samples, size_t count)
@ AUDIO_VISUALIZATION_SOURCE_MIX
#define AUDIO_VISUALIZATION_SAMPLE_RATE
#define MAX_TERMINAL_WIDTH
#define VISUALIZATION_HISTORY
#define MAX_TERMINAL_HEIGHT

References audio_visualization_read(), AUDIO_VISUALIZATION_SAMPLE_RATE, AUDIO_VISUALIZATION_SOURCE_MIX, cells, color_mode, colors, FFT_REFRESH_NS, FFT_SCALE_DB, FFT_SCALE_LINEAR, FFT_SCALE_LOG, FFT_SIZE, GET_OPTION, height, fft_scale_t::max_db, MAX_TERMINAL_HEIGHT, MAX_TERMINAL_WIDTH, fft_scale_t::min_db, fft_scale_t::mode, SAFE_FREE, SAFE_MALLOC, source, visualization_color_for_level(), visualization_encode(), VISUALIZATION_HISTORY, and width.

Referenced by mirror_render_audio_visualization().

Variable Documentation

◆ audio_fftw_planner_lock

static_mutex_t audio_fftw_planner_lock
extern

Definition at line 34 of file spectral.c.

Referenced by audio_visualization_fft_init(), spectral_create(), and spectral_destroy().

◆ cells

◆ color_mode

◆ colors

◆ height

unsigned int height

Definition at line 23 of file fft.c.

Referenced by acip_send_ascii_frame(), acip_send_ascii_frame_contract(), acip_send_image_frame(), acip_send_image_frame_h265(), action_show_capabilities_immediate(), apply_color_filter(), ascii_convert(), ascii_convert(), ascii_convert_with_buffers(), ascii_convert_with_buffers_stabilized(), ascii_convert_with_capabilities(), ascii_convert_with_capabilities_fit(), ascii_convert_with_capabilities_workspace(), ascii_create_grid(), ascii_grid_layout(), ascii_half_frame_init(), ascii_otel_frame_rejected(), ascii_render_halfblocks(), ascii_stabilizer_prepare(), aspect_ratio(), audio_visualization_render_fft(), audio_visualization_render_waveform(), benchmark_simd_color_conversion(), benchmark_simd_color_conversion_with_source(), benchmark_simd_conversion(), benchmark_simd_conversion_with_source(), browser_capture_transform_rgba(), client_protocol_start(), client_validate_frame(), MirrorFrameBinding::convert(), create_mixed_ascii_frame_for_client(), face_follow_detect(), face_follow_image_process(), ffmpeg_decoder_create_stdin(), ffmpeg_decoder_create_with_request_options(), ffmpeg_decoder_get_video_dimensions(), frame_extract_dimensions(), h265_client_context_create(), h265_decode(), h265_encode(), handle_size_packet(), image_calc_pixel_count(), image_calc_rgb_size(), image_calc_total_allocation(), image_new(), image_validate_dimensions(), invitation_render(), kitty_image_write(), mirror_convert_frame(), mirror_render_audio_visualization(), packet_validate_frame_dimensions(), TestPatternBinding::render(), render_ascii_cells_sve(), MirrorFrameBinding::renderAudio(), rgb_to_16color_dithered(), rgb_to_16color_halfblocks_scalar(), rgb_to_256color_halfblocks_scalar(), rgb_to_halfblocks_scalar(), rgb_to_truecolor_halfblocks_scalar(), safe_parse_size_message(), send_image_frame_packet(), server_send_terminal_capabilities(), server_send_video(), session_capture_process_for_transmission_into(), session_display_create(), session_display_init_render_file(), session_frame_validate(), session_frame_validate_contract(), session_host_receive_packet(), session_protocol_build_capabilities(), stats_runtime_media(), terminal_screen_compose(), test_pattern_create(), test_pattern_resize(), TestPatternBinding::TestPatternBinding(), threaded_send_image_frame(), threaded_send_image_frame_encoded(), threaded_send_terminal_size_with_auto_detect(), MirrorFrameBinding::transform(), ui_error_popup_render(), ui_input_render(), ui_media_panel_render(), ui_popup_frame(), ui_popup_frame_titled(), ui_popup_render(), ui_scrollbar_cell(), ui_scrollbar_measure(), ui_scrollbar_render(), ui_table_render(), visualization_encode(), webcam_get_dimensions(), and webcam_read_async().

◆ source

◆ updated_ns

uint64_t updated_ns

Definition at line 26 of file fft.c.

◆ valid

bool valid

◆ width

unsigned int width

Definition at line 23 of file fft.c.

Referenced by acip_send_ascii_frame(), acip_send_ascii_frame_contract(), acip_send_image_frame(), acip_send_image_frame_h265(), action_show_capabilities_immediate(), apply_color_filter(), ascii_convert(), ascii_convert(), ascii_convert_with_buffers(), ascii_convert_with_buffers_stabilized(), ascii_convert_with_capabilities(), ascii_convert_with_capabilities_fit(), ascii_convert_with_capabilities_workspace(), ascii_create_grid(), ascii_grid_layout(), ascii_half_frame_init(), ascii_otel_frame_rejected(), ascii_render_halfblocks(), ascii_stabilizer_prepare(), aspect_ratio(), audio_visualization_render_fft(), audio_visualization_render_waveform(), benchmark_simd_color_conversion(), benchmark_simd_color_conversion_with_source(), benchmark_simd_conversion(), benchmark_simd_conversion_with_source(), browser_capture_transform_rgba(), client_protocol_start(), client_validate_frame(), MirrorFrameBinding::convert(), create_mixed_ascii_frame_for_client(), display_width(), face_follow_detect(), face_follow_image_process(), ffmpeg_decoder_create_stdin(), ffmpeg_decoder_create_with_request_options(), ffmpeg_decoder_get_video_dimensions(), frame_buffer_render_divider(), frame_extract_dimensions(), h265_client_context_create(), h265_decode(), h265_encode(), handle_size_packet(), image_calc_pixel_count(), image_calc_rgb_size(), image_calc_total_allocation(), image_new(), image_validate_dimensions(), invitation_render_logo(), kitty_image_write(), log_search_render_input_line(), mirror_convert_frame(), mirror_render_audio_visualization(), packet_validate_frame_dimensions(), TestPatternBinding::render(), render_ascii_cells_sve(), MirrorFrameBinding::renderAudio(), rgb_to_16color_dithered(), rgb_to_16color_halfblocks_scalar(), rgb_to_256color_halfblocks_scalar(), rgb_to_halfblocks_scalar(), rgb_to_truecolor_halfblocks_scalar(), safe_parse_size_message(), send_image_frame_packet(), server_send_terminal_capabilities(), server_send_video(), session_capture_process_for_transmission_into(), session_display_create(), session_display_init_render_file(), session_frame_validate(), session_frame_validate_contract(), session_host_receive_packet(), session_protocol_build_capabilities(), simd_luminance_avx2(), simd_luminance_neon(), simd_luminance_sse2(), simd_luminance_ssse3(), simd_luminance_sve(), stats_runtime_media(), test_pattern_create(), test_pattern_resize(), TestPatternBinding::TestPatternBinding(), threaded_send_image_frame(), threaded_send_image_frame_encoded(), threaded_send_terminal_size_with_auto_detect(), MirrorFrameBinding::transform(), tui_stats_minimum(), ui_buttons_render(), ui_error_popup_preserve(), ui_error_popup_render(), ui_help_menu_measure(), ui_input_render(), ui_media_panel_render(), ui_notice_render(), ui_popup_frame(), ui_popup_frame_titled(), ui_popup_render(), ui_popup_rule(), ui_progress_format(), ui_sync_render(), ui_table_render(), ui_table_row(), ui_text_cell(), ui_too_small_format(), utf8_display_width_n(), utf8_palette_create(), utf8_prefix_bytes_for_width(), validate_palette_chars(), visualization_encode(), webcam_get_dimensions(), webcam_read_async(), and write_row_rep_from_arrays_enhanced().