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.h File Reference

Go to the source code of this file.

Data Structures

struct  fft_scale_t

Enumerations

enum  fft_scale_mode_t { FFT_SCALE_LINEAR = 0 , FFT_SCALE_LOG = 1 , FFT_SCALE_DB = 2 }

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)

Enumeration Type Documentation

◆ fft_scale_mode_t

Amplitude mapping used by the terminal FFT visualization.

Enumerator
FFT_SCALE_LINEAR 
FFT_SCALE_LOG 
FFT_SCALE_DB 

Definition at line 8 of file fft.h.

fft_scale_mode_t
Definition fft.h:8
@ FFT_SCALE_LOG
Definition fft.h:8
@ FFT_SCALE_LINEAR
Definition fft.h:8
@ FFT_SCALE_DB
Definition fft.h:8

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
#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().