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

Go to the source code of this file.

Data Structures

struct  audio_processor
 For audio processor, this is the live record that ties config, spectral_left, spectral_right, compressor together. The owning code creates it, updates it while work is active, and releases its handles and pointed-to storage before freeing the record. More...

Functions

audio_processor_config_t audio_processor_default_config (int sample_rate)
void audio_processor_reset (audio_processor_t *p)
asciichat_error_t audio_processor_create (const audio_processor_config_t *c, audio_processor_t **out)
void audio_processor_set_flags (audio_processor_t *p, audio_processor_flags_t flags)
asciichat_error_t audio_processor_process (audio_processor_t *p, const float *input, float *output, size_t count)
void audio_processor_destroy (audio_processor_t *p)

Function Documentation

◆ audio_processor_create()

asciichat_error_t audio_processor_create ( const audio_processor_config_t * c,
audio_processor_t ** out )

Definition at line 71 of file processor.c.

71 {
72 if (!out)
73 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing audio processor output");
74 *out = NULL;
75 if (!c || c->channels < 0 || c->channels > 2 || c->sample_rate < 16000 || c->sample_rate > 192000 ||
76 c->spectral.sample_rate != c->sample_rate || c->agc_level < 1 || c->agc_level > 32767 || c->agc_max_gain < 0 ||
77 c->agc_max_gain > 36 || !isfinite(c->highpass_hz) || c->highpass_hz <= 0 ||
78 c->highpass_hz >= c->sample_rate / 2.0f || !isfinite(c->lowpass_hz) || c->lowpass_hz <= 0 ||
79 c->lowpass_hz > c->sample_rate / 2.0f || !isfinite(c->comp_ratio) || c->comp_ratio < 1 || c->comp_ratio > 20 ||
80 !isfinite(c->comp_threshold_db) || !isfinite(c->comp_makeup_db) || !isfinite(c->gate_threshold) ||
81 c->gate_threshold < 0 || c->gate_threshold > 1 || !isfinite(c->gate_hysteresis) || c->gate_hysteresis < 0 ||
82 c->gate_hysteresis > 1)
83 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid audio processor configuration");
84 audio_processor_t *p = SAFE_CALLOC(1, sizeof(*p), audio_processor_t *);
85 p->config = *c;
86 if (p->config.channels == 0)
87 p->config.channels = 1;
88 if (p->config.channels == 2) {
90 log_warn_once("Stereo capture uses linked broadband dynamics; spectral AGC, multiband compression and spectral "
91 "gating options are unavailable");
92 // Stereo dynamics share one detector after the independent channel filters.
93 p->config.spectral.auto_gain = false;
94 p->config.spectral.compressor = false;
95 p->config.spectral.noise_gate = false;
96 }
97 p->max_gain = powf(10, c->agc_max_gain / 20.0f);
98 p->gain_attack = 1 - powf(0.75f, 100.0f / c->sample_rate);
99 p->gain_release = 1 - powf(0.95f, 100.0f / c->sample_rate);
100 if (c->spectral_enabled) {
102 if (result != ASCIICHAT_OK) {
103 SAFE_FREE(p);
104 return result;
105 }
106 if (p->config.channels == 2) {
108 if (result != ASCIICHAT_OK) {
110 SAFE_FREE(p);
111 return result;
112 }
113 }
114 }
116 *out = p;
117 return ASCIICHAT_OK;
118}
#define SET_ERRNO(code,...)
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
#define log_warn_once(fmt,...)
Log WARN message exactly once.
Definition log/log.h:886
void audio_processor_reset(audio_processor_t *p)
Definition processor.c:51
struct audio_processor audio_processor_t
Definition processor.h:13
static bool size_t gotel_query_t * out
Definition queries.h:8
asciichat_error_t spectral_create(const spectral_config_t *c, spectral_processor_t **out)
Definition spectral.c:68
void spectral_destroy(spectral_processor_t *p)
Definition spectral.c:104
int channels
Interleaved PCM channels; zero retains the mono API default.
Definition processor.h:25
spectral_config_t spectral
Definition processor.h:29
float gain_release
Definition processor.c:26
float gain_attack
Definition processor.c:26
audio_processor_config_t config
Definition processor.c:15
spectral_processor_t * spectral_right
Definition processor.c:17
spectral_processor_t * spectral_left
Definition processor.c:16

References audio_processor_config_t::agc_level, audio_processor_config_t::agc_max_gain, ASCIICHAT_OK, audio_processor_reset(), spectral_config_t::auto_gain, audio_processor_config_t::channels, audio_processor_config_t::comp_makeup_db, audio_processor_config_t::comp_ratio, audio_processor_config_t::comp_threshold_db, spectral_config_t::compressor, audio_processor::config, ERROR_INVALID_PARAM, audio_processor::gain_attack, audio_processor::gain_release, audio_processor_config_t::gate_hysteresis, audio_processor_config_t::gate_threshold, audio_processor_config_t::highpass_hz, log_warn_once, audio_processor_config_t::lowpass_hz, audio_processor::max_gain, spectral_config_t::noise_gate, out, SAFE_CALLOC, SAFE_FREE, audio_processor_config_t::sample_rate, spectral_config_t::sample_rate, SET_ERRNO, audio_processor_config_t::spectral, spectral_create(), spectral_destroy(), audio_processor_config_t::spectral_enabled, audio_processor::spectral_left, and audio_processor::spectral_right.

Referenced by client_audio_pipeline_create().

◆ audio_processor_default_config()

audio_processor_config_t audio_processor_default_config ( int sample_rate)

Microphone defaults shared by native and browser hosts; no global options.

Definition at line 29 of file processor.c.

29 {
31 .sample_rate = sample_rate,
32 .flags = {.agc = true, .compressor = true, .highpass = true},
33 .spectral_enabled = true,
34 .spectral = spectral_default_config(sample_rate),
35 .agc_level = 16000,
36 .agc_max_gain = 35,
37 .highpass_hz = 150,
38 .lowpass_hz = 8000,
39 .comp_threshold_db = -12,
40 .comp_ratio = 3,
41 .comp_makeup_db = 6,
42 .comp_attack_ns = 5 * NS_PER_MS_INT,
43 .comp_release_ns = 150 * NS_PER_MS_INT,
44 .gate_threshold = 0.08f,
45 .gate_hysteresis = 0.3f,
46 .gate_attack_ns = 500 * NS_PER_US_INT,
47 .gate_release_ns = 30 * NS_PER_MS_INT,
48 };
49}
#define NS_PER_MS_INT
Definition time.h:168
#define NS_PER_US_INT
Definition time.h:167
spectral_config_t spectral_default_config(int sample_rate)
Definition spectral.c:36

References NS_PER_MS_INT, NS_PER_US_INT, and spectral_default_config().

Referenced by client_audio_pipeline_default_config().

◆ audio_processor_destroy()

void audio_processor_destroy ( audio_processor_t * p)

◆ audio_processor_process()

asciichat_error_t audio_processor_process ( audio_processor_t * processor,
const float * input,
float * output,
size_t count )

Arbitrary block sizes, in-place buffers, and no allocations or locks. AEC precedes this operation.

Definition at line 125 of file processor.c.

125 {
126 if (!p || (!input && count) || (!output && count))
127 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid audio processor buffers");
128 if (!count)
129 return ASCIICHAT_OK;
130 if (p->config.channels == 2) {
131 if (count % 2)
132 return SET_ERRNO(ERROR_INVALID_PARAM, "Incomplete stereo PCM frame");
133 const audio_processor_config_t *c = &p->config;
134 for (size_t base = 0; base < count;) {
135 size_t frames = (count - base) / 2;
136 if (frames > 480)
137 frames = 480;
138 float left[480], right[480];
139 for (size_t i = 0; i < frames; i++) {
140 left[i] = input[base + i * 2];
141 right[i] = input[base + i * 2 + 1];
142 }
143 if (p->spectral_left) {
144 asciichat_error_t result = spectral_process(p->spectral_left, left, left, frames);
145 if (result == ASCIICHAT_OK)
146 result = spectral_process(p->spectral_right, right, right, frames);
147 if (result != ASCIICHAT_OK)
148 return result;
149 }
150 for (size_t i = 0; i < frames; i++) {
151 float l = isfinite(left[i]) ? left[i] : 0;
152 float r = isfinite(right[i]) ? right[i] : 0;
153 if (c->flags.highpass) {
156 }
157 if (c->flags.lowpass) {
160 }
161 float gain = 1;
162 float peak = fmaxf(fabsf(l), fabsf(r));
163 if (c->flags.agc) {
164 p->energy += ((double)l * l + (double)r * r) * 0.5;
165 if (++p->level_samples == (size_t)c->sample_rate / 100) {
166 float rms = (float)sqrt(p->energy / p->level_samples);
167 float target = fminf(0.5f, fmaxf(0.05f, c->agc_level / 32768.0f));
168 p->target_gain = rms > 1e-5f ? fminf(p->max_gain, fmaxf(1, target / rms)) : 1;
169 p->energy = 0;
170 p->level_samples = 0;
171 }
172 float coefficient = p->target_gain < p->gain ? p->gain_attack : p->gain_release;
173 p->gain += coefficient * (p->target_gain - p->gain);
174 gain *= p->gain;
175 }
176 if (c->flags.noise_gate)
177 gain *= noise_gate_process_sample(&p->gate, 1, peak * gain);
178 if (c->flags.compressor)
179 gain *= compressor_process_sample(&p->compressor, peak * gain);
180 if (peak > 0)
181 gain = fminf(gain, 1.0f / peak);
182 output[base + i * 2] = l * gain;
183 output[base + i * 2 + 1] = r * gain;
184 }
185 base += frames * 2;
186 }
187 return ASCIICHAT_OK;
188 }
189 if (p->spectral_left) {
190 asciichat_error_t result = spectral_process(p->spectral_left, input, output, count);
191 if (result != ASCIICHAT_OK)
192 return result;
193 } else if (input != output) {
194 memcpy(output, input, count * sizeof(float));
195 }
196 const audio_processor_config_t *c = &p->config;
197 // Accumulate levels on the sample clock, independent of host callback sizes.
198 const size_t level_frame = (size_t)c->sample_rate / 100;
199 for (size_t i = 0; i < count; ++i) {
200 float sample = isfinite(output[i]) ? output[i] : 0;
201 if (c->flags.agc && !(p->spectral_left && c->spectral.auto_gain)) {
202 p->energy += (double)sample * sample;
203 if (++p->level_samples == level_frame) {
204 float rms = (float)sqrt(p->energy / level_frame);
205 float target = fminf(0.5f, fmaxf(0.05f, c->agc_level / 32768.0f));
206 p->target_gain = rms > 1e-5f ? fminf(p->max_gain, fmaxf(1, target / rms)) : 1;
207 p->energy = 0;
208 p->level_samples = 0;
209 }
210 float coefficient = p->target_gain < p->gain ? p->gain_attack : p->gain_release;
211 p->gain += coefficient * (p->target_gain - p->gain);
212 sample *= p->gain;
213 }
214 if (c->flags.highpass)
215 sample = highpass_filter_process_sample(&p->highpass, sample);
216 if (c->flags.lowpass)
217 sample = lowpass_filter_process_sample(&p->lowpass, sample);
218 if (c->flags.noise_gate)
219 sample = noise_gate_process_sample(&p->gate, sample, fabsf(sample));
220 if (c->flags.compressor && !(p->spectral_left && c->spectral.compressor)) {
221 sample *= compressor_process_sample(&p->compressor, sample);
222 sample = soft_clip(sample, 0.7f, 3);
223 }
224 output[i] = sample;
225 }
226 return ASCIICHAT_OK;
227}
float highpass_filter_process_sample(highpass_filter_t *filter, float input)
Process a single sample through high-pass filter.
Definition effects.c:209
float soft_clip(float sample, float threshold, float steepness)
Apply soft clipping to a sample.
Definition effects.c:286
float compressor_process_sample(compressor_t *comp, float sidechain)
Process a single sample through compressor.
Definition effects.c:74
float lowpass_filter_process_sample(lowpass_filter_t *filter, float input)
Process a single sample through low-pass filter.
Definition effects.c:260
float noise_gate_process_sample(noise_gate_t *gate, float input, float peak_amplitude)
Process a single sample through noise gate.
Definition effects.c:134
asciichat_error_t spectral_process(spectral_processor_t *p, const float *input, float *output, size_t count)
Definition spectral.c:279
audio_processor_flags_t flags
Definition processor.h:27
size_t level_samples
Definition processor.c:25
highpass_filter_t highpass
Definition processor.c:20
noise_gate_t gate
Definition processor.c:19
float target_gain
Definition processor.c:26
compressor_t compressor
Definition processor.c:18
lowpass_filter_t lowpass
Definition processor.c:21
lowpass_filter_t lowpass_right
Definition processor.c:23
highpass_filter_t highpass_right
Definition processor.c:22

References audio_processor_flags_t::agc, audio_processor_config_t::agc_level, ASCIICHAT_OK, spectral_config_t::auto_gain, audio_processor_config_t::channels, audio_processor::compressor, audio_processor_flags_t::compressor, spectral_config_t::compressor, compressor_process_sample(), audio_processor::config, audio_processor::energy, ERROR_INVALID_PARAM, audio_processor_config_t::flags, audio_processor::gain, audio_processor::gain_attack, audio_processor::gain_release, audio_processor::gate, audio_processor::highpass, audio_processor_flags_t::highpass, highpass_filter_process_sample(), audio_processor::highpass_right, audio_processor::level_samples, audio_processor::lowpass, audio_processor_flags_t::lowpass, lowpass_filter_process_sample(), audio_processor::lowpass_right, audio_processor::max_gain, audio_processor_flags_t::noise_gate, noise_gate_process_sample(), audio_processor_config_t::sample_rate, SET_ERRNO, soft_clip(), audio_processor_config_t::spectral, audio_processor::spectral_left, spectral_process(), audio_processor::spectral_right, and audio_processor::target_gain.

Referenced by client_audio_pipeline_process_duplex(), and AudioProcessorBinding::process().

◆ audio_processor_reset()

void audio_processor_reset ( audio_processor_t * p)

Definition at line 51 of file processor.c.

51 {
52 if (!p)
53 return;
56 const audio_processor_config_t *c = &p->config;
60 noise_gate_init(&p->gate, (float)c->sample_rate);
66 p->energy = 0;
67 p->level_samples = 0;
68 p->gain = p->target_gain = 1;
69}
void noise_gate_init(noise_gate_t *gate, float sample_rate)
Initialize a noise gate.
Definition effects.c:102
void compressor_init(compressor_t *comp, float sample_rate)
Initialize a compressor.
Definition effects.c:29
void compressor_set_params(compressor_t *comp, float threshold_dB, float ratio, uint64_t attack_ns, uint64_t release_ns, float makeup_dB)
Set compressor parameters.
Definition effects.c:40
void highpass_filter_init(highpass_filter_t *filter, float cutoff_hz, float sample_rate)
Initialize a high-pass filter.
Definition effects.c:187
void lowpass_filter_init(lowpass_filter_t *filter, float cutoff_hz, float sample_rate)
Initialize a low-pass filter.
Definition effects.c:237
void noise_gate_set_params(noise_gate_t *gate, float threshold, uint64_t attack_ns, uint64_t release_ns, float hysteresis)
Set noise gate parameters.
Definition effects.c:116
void spectral_reset(spectral_processor_t *p)
Definition spectral.c:46

References audio_processor_config_t::comp_attack_ns, audio_processor_config_t::comp_makeup_db, audio_processor_config_t::comp_ratio, audio_processor_config_t::comp_release_ns, audio_processor_config_t::comp_threshold_db, audio_processor::compressor, compressor_init(), compressor_set_params(), audio_processor::config, audio_processor::energy, audio_processor::gain, audio_processor::gate, audio_processor_config_t::gate_attack_ns, audio_processor_config_t::gate_hysteresis, audio_processor_config_t::gate_release_ns, audio_processor_config_t::gate_threshold, audio_processor::highpass, highpass_filter_init(), audio_processor_config_t::highpass_hz, audio_processor::highpass_right, audio_processor::level_samples, audio_processor::lowpass, lowpass_filter_init(), audio_processor_config_t::lowpass_hz, audio_processor::lowpass_right, noise_gate_init(), noise_gate_set_params(), audio_processor_config_t::sample_rate, audio_processor::spectral_left, spectral_reset(), audio_processor::spectral_right, and audio_processor::target_gain.

Referenced by audio_processor_create(), and client_audio_pipeline_reset().

◆ audio_processor_set_flags()

void audio_processor_set_flags ( audio_processor_t * p,
audio_processor_flags_t flags )

Definition at line 120 of file processor.c.

120 {
121 if (p)
122 p->config.flags = flags;
123}

References audio_processor::config, and audio_processor_config_t::flags.

Referenced by client_audio_pipeline_set_flags().