113 return SET_ERRNO(ERROR_INVALID_PARAM,
"ducking_init: duck is NULL");
120 duck->threshold_dB = -45.0f;
121 duck->leader_margin_dB = 6.0f;
122 duck->atten_dB = -6.0f;
123 duck->attack_ns = 10 * NS_PER_MS_INT;
124 duck->release_ns = 200 * NS_PER_MS_INT;
125 duck->envelope = NULL;
129 float attack_tau = (float)duck->attack_ns / NS_PER_SEC;
130 float release_tau = (float)duck->release_ns / NS_PER_SEC;
131 duck->attack_coeff = expf(-1.0f / (attack_tau * sample_rate + 1e-12f));
132 duck->release_coeff = expf(-1.0f / (release_tau * sample_rate + 1e-12f));
135 size_t envelope_size = 0;
136 if (checked_size_mul((
size_t)num_sources,
sizeof(
float), &envelope_size) != ASCIICHAT_OK) {
137 return SET_ERRNO(ERROR_BUFFER_OVERFLOW,
"Ducking envelope array size overflow: %d sources", num_sources);
139 duck->envelope = SAFE_MALLOC(envelope_size,
float *);
140 if (!duck->envelope) {
141 return SET_ERRNO(ERROR_MEMORY,
"Failed to allocate ducking envelope array");
144 size_t gain_size = 0;
145 if (checked_size_mul((
size_t)num_sources,
sizeof(
float), &gain_size) != ASCIICHAT_OK) {
146 SAFE_FREE(duck->envelope);
147 duck->envelope = NULL;
148 return SET_ERRNO(ERROR_BUFFER_OVERFLOW,
"Ducking gain array size overflow: %d sources", num_sources);
150 duck->gain = SAFE_MALLOC(gain_size,
float *);
152 SAFE_FREE(duck->envelope);
153 duck->envelope = NULL;
154 return SET_ERRNO(ERROR_MEMORY,
"Failed to allocate ducking gain array");
158 SAFE_MEMSET(duck->envelope, (
size_t)num_sources *
sizeof(
float), 0, (
size_t)num_sources *
sizeof(
float));
159 for (
int i = 0; i < num_sources; i++) {
160 duck->gain[i] = 1.0f;
220 if (max_sources <= 0 || max_sources > MIXER_MAX_SOURCES) {
221 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid max_sources: %d (must be 1-%d)", max_sources, MIXER_MAX_SOURCES);
225 if (sample_rate <= 0 || sample_rate > 192000) {
226 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid sample_rate: %d (must be 1-192000)", sample_rate);
231 mixer = SAFE_MALLOC(
sizeof(mixer_t), mixer_t *);
233 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate mixer structure");
237 mixer->num_sources = 0;
238 mixer->max_sources = max_sources;
239 mixer->sample_rate = sample_rate;
242 size_t buffers_size = 0;
243 if (checked_size_mul((
size_t)max_sources,
sizeof(audio_ring_buffer_t *), &buffers_size) != ASCIICHAT_OK) {
244 SET_ERRNO(ERROR_BUFFER_OVERFLOW,
"Mixer source buffers array overflow: %d sources", max_sources);
248 mixer->source_buffers = SAFE_MALLOC(buffers_size, audio_ring_buffer_t **);
249 if (!mixer->source_buffers) {
255 if (checked_size_mul((
size_t)max_sources,
sizeof(uint32_t), &ids_size) != ASCIICHAT_OK) {
256 SET_ERRNO(ERROR_BUFFER_OVERFLOW,
"Mixer source IDs array overflow: %d sources", max_sources);
257 SAFE_FREE(mixer->source_buffers);
261 mixer->source_ids = SAFE_MALLOC(ids_size, uint32_t *);
262 if (!mixer->source_ids) {
263 SAFE_FREE(mixer->source_buffers);
268 size_t active_size = 0;
269 if (checked_size_mul((
size_t)max_sources,
sizeof(
bool), &active_size) != ASCIICHAT_OK) {
270 SET_ERRNO(ERROR_BUFFER_OVERFLOW,
"Mixer source active array overflow: %d sources", max_sources);
271 SAFE_FREE(mixer->source_buffers);
272 SAFE_FREE(mixer->source_ids);
276 mixer->source_active = SAFE_MALLOC(active_size,
bool *);
277 if (!mixer->source_active) {
278 SAFE_FREE(mixer->source_buffers);
279 SAFE_FREE(mixer->source_ids);
285 SAFE_MEMSET((
void *)mixer->source_buffers, buffers_size, 0, buffers_size);
286 SAFE_MEMSET(mixer->source_ids, ids_size, 0, ids_size);
287 SAFE_MEMSET(mixer->source_active, active_size, 0, active_size);
290 mixer->active_sources_mask = 0ULL;
291 for (
int i = 0; i < 256; i++) {
292 mixer->source_id_to_index[i] = MIXER_HASH_INVALID;
296 size_t mix_buffer_size = 0;
297 if (checked_size_mul(MIXER_FRAME_SIZE,
sizeof(
float), &mix_buffer_size) != ASCIICHAT_OK) {
298 SET_ERRNO(ERROR_BUFFER_OVERFLOW,
"Mixer mix buffer size overflow: %zu samples", (
size_t)MIXER_FRAME_SIZE);
299 SAFE_FREE(mixer->source_buffers);
300 SAFE_FREE(mixer->source_ids);
301 SAFE_FREE(mixer->source_active);
305 mixer->mix_buffer = SAFE_MALLOC(mix_buffer_size,
float *);
306 if (!mixer->mix_buffer) {
307 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate mix buffer");
308 SAFE_FREE(mixer->source_buffers);
309 SAFE_FREE(mixer->source_ids);
310 SAFE_FREE(mixer->source_active);
317 SET_ERRNO(ERROR_THREAD,
"Failed to initialize mixer source lock");
318 SAFE_FREE(mixer->source_buffers);
319 SAFE_FREE(mixer->source_ids);
320 SAFE_FREE(mixer->source_active);
321 SAFE_FREE(mixer->mix_buffer);
327 mixer->crowd_alpha = 0.5f;
328 mixer->base_gain = 1.0f;
331 if (
ducking_init(&mixer->ducking, max_sources, (
float)sample_rate) != ASCIICHAT_OK) {
332 rwlock_destroy(&mixer->source_lock);
333 SAFE_FREE(mixer->source_buffers);
334 SAFE_FREE(mixer->source_ids);
335 SAFE_FREE(mixer->source_active);
336 SAFE_FREE(mixer->mix_buffer);
342 log_debug(
"Audio mixer created: max_sources=%d, sample_rate=%d", max_sources, sample_rate);
406 rwlock_wrlock(&mixer->source_lock);
408 for (
int i = 0; i < mixer->max_sources; i++) {
409 if (mixer->source_ids[i] == client_id) {
410 mixer->source_buffers[i] = NULL;
411 mixer->source_ids[i] = 0;
412 mixer->source_active[i] =
false;
413 mixer->num_sources--;
416 mixer->active_sources_mask &= ~(1ULL << i);
417 mixer_hash_mark_invalid(mixer, client_id);
420 mixer->ducking.envelope[i] = 0.0f;
421 mixer->ducking.gain[i] = 1.0f;
423 rwlock_wrunlock(&mixer->source_lock);
425 log_info(
"Mixer: Removed source for client %u from slot %d", client_id, i);
430 rwlock_wrunlock(&mixer->source_lock);
461 if (!mixer || !output || num_samples <= 0)
466 rwlock_rdlock(&mixer->source_lock);
469 SAFE_MEMSET(output, num_samples *
sizeof(
float), 0, num_samples *
sizeof(
float));
472 int active_count = 0;
473 for (
int i = 0; i < mixer->max_sources; i++) {
474 if (mixer->source_ids[i] != 0 && mixer->source_active[i] && mixer->source_buffers[i]) {
479 if (active_count == 0) {
485 for (
int frame_start = 0; frame_start < num_samples; frame_start += MIXER_FRAME_SIZE) {
486 int frame_size = (frame_start + MIXER_FRAME_SIZE > num_samples) ? (num_samples - frame_start) : MIXER_FRAME_SIZE;
489 SAFE_MEMSET(mixer->mix_buffer, frame_size *
sizeof(
float), 0, frame_size *
sizeof(
float));
492 float source_samples[MIXER_MAX_SOURCES][MIXER_FRAME_SIZE];
493 int source_count = 0;
494 int source_map[MIXER_MAX_SOURCES];
497 for (
int i = 0; i < mixer->max_sources; i++) {
498 if (mixer->source_ids[i] != 0 && mixer->source_active[i] && mixer->source_buffers[i]) {
500 size_t samples_read_size =
502 int samples_read = (int)samples_read_size;
506 if (samples_read > 0) {
508 if (samples_read < frame_size) {
509 SAFE_MEMSET(&source_samples[source_count][samples_read], (frame_size - samples_read) *
sizeof(
float), 0,
510 (frame_size - samples_read) *
sizeof(
float));
513 source_map[source_count] = i;
516 if (source_count >= MIXER_MAX_SOURCES)
524 int speaking_count = 0;
526 for (
int i = 0; i < source_count; i++) {
527 int slot = source_map[i];
531 for (
int s = 0; s < frame_size; s++) {
532 float abs_sample = fabsf(source_samples[i][s]);
533 if (abs_sample > peak)
538 if (peak > mixer->ducking.envelope[slot]) {
539 mixer->ducking.envelope[slot] =
540 mixer->ducking.attack_coeff * mixer->ducking.envelope[slot] + (1.0f - mixer->ducking.attack_coeff) * peak;
542 mixer->ducking.envelope[slot] =
543 mixer->ducking.release_coeff * mixer->ducking.envelope[slot] + (1.0f - mixer->ducking.release_coeff) * peak;
547 if (mixer->ducking.envelope[slot] >
db_to_linear(-60.0f))
552 ducking_process_frame(&mixer->ducking, mixer->ducking.envelope, mixer->ducking.gain, mixer->max_sources);
555 float crowd_gain = (speaking_count > 0) ? (1.0f / powf((
float)speaking_count, mixer->crowd_alpha)) : 1.0f;
556 float pre_bus = mixer->base_gain * crowd_gain;
559 float combined_gains[MIXER_MAX_SOURCES];
560 for (
int i = 0; i < source_count; i++) {
561 int slot = source_map[i];
562 combined_gains[i] = mixer->ducking.gain[slot] * pre_bus;
567 for (
int s = 0; s < frame_size; s++) {
569 for (
int i = 0; i < source_count; i++) {
570 mix += source_samples[i][s] * combined_gains[i];
574 mixer->mix_buffer[s] = mix;
580 float frame_peak = 0.0f;
581 for (
int s = 0; s < frame_size; s++) {
582 float abs_val = fabsf(mixer->mix_buffer[s]);
583 if (abs_val > frame_peak)
584 frame_peak = abs_val;
591 for (
int s = 0; s < frame_size; s++) {
592 float mix = mixer->mix_buffer[s] * comp_gain;
597 output[frame_start + s] =
soft_clip(mix, 0.7f, 3.0f);
601 rwlock_rdunlock(&mixer->source_lock);
606 if (!mixer || !output || num_samples <= 0)
611 START_TIMER(
"mixer_total");
616 rwlock_rdlock(&mixer->source_lock);
619 SAFE_MEMSET(output, num_samples *
sizeof(
float), 0, num_samples *
sizeof(
float));
622 uint8_t exclude_index = mixer_hash_get_slot(mixer, exclude_client_id);
623 uint64_t active_mask = mixer->active_sources_mask;
629 bool valid_exclude = (exclude_index < MIXER_MAX_SOURCES && exclude_index != MIXER_HASH_INVALID &&
630 mixer->source_ids[exclude_index] == exclude_client_id);
634 uint64_t mask_without_excluded = active_mask & ~(1ULL << exclude_index);
635 active_mask = mask_without_excluded;
639 4500 * US_PER_MS_INT,
640 "MIXER EXCLUSION: exclude_client=%u, exclude_index=%u, active_mask_before=0x%llx, active_mask_after=0x%llx",
641 exclude_client_id, exclude_index, (
unsigned long long)(active_mask | (1ULL << exclude_index)),
642 (
unsigned long long)active_mask);
645 log_dev_every(4500 * US_PER_MS_INT,
646 "MIXER EXCLUSION FAILED: exclude_client=%u, exclude_index=%u (valid=%d), lookup_id=%u",
647 exclude_client_id, exclude_index, valid_exclude,
648 (exclude_index < MIXER_MAX_SOURCES && exclude_index != 0xFF) ? mixer->source_ids[exclude_index] : 0);
652 if (active_mask == 0) {
653 rwlock_rdunlock(&mixer->source_lock);
655 STOP_TIMER(
"mixer_total");
661 for (
int frame_start = 0; frame_start < num_samples; frame_start += MIXER_FRAME_SIZE) {
662 int frame_size = (frame_start + MIXER_FRAME_SIZE > num_samples) ? (num_samples - frame_start) : MIXER_FRAME_SIZE;
667 SAFE_MEMSET(mixer->mix_buffer, frame_size *
sizeof(
float), 0, frame_size *
sizeof(
float));
670 float source_samples[MIXER_MAX_SOURCES][MIXER_FRAME_SIZE];
671 int source_count = 0;
672 int source_map[MIXER_MAX_SOURCES];
675 uint64_t current_mask = active_mask;
676 while (current_mask && source_count < MIXER_MAX_SOURCES) {
677 int i = find_first_set_bit(current_mask);
678 current_mask &= current_mask - 1;
681 if (i < mixer->max_sources && mixer->source_ids[i] != 0 && mixer->source_buffers[i]) {
683 size_t samples_read_size =
685 int samples_read = (int)samples_read_size;
689 if (samples_read > 0) {
691 if (samples_read < frame_size) {
692 SAFE_MEMSET(&source_samples[source_count][samples_read], (frame_size - samples_read) *
sizeof(
float), 0,
693 (frame_size - samples_read) *
sizeof(
float));
697 float source_rms = 0.0f;
698 if (frame_start == 0) {
699 float sum_squares = 0.0f;
700 for (
int s = 0; s < samples_read; s++) {
701 sum_squares += source_samples[source_count][s] * source_samples[source_count][s];
703 source_rms = sqrtf(sum_squares / (
float)samples_read);
704 log_info_every(NS_PER_MS_INT,
"MIXER SOURCE READ: client_id=%u, slot=%d, samples_read=%d, RMS=%.6f",
705 mixer->source_ids[i], i, samples_read, source_rms);
708 source_map[source_count] = i;
716 uint64_t read_time_us = time_ns_to_us(read_time_ns);
718 if (read_time_ns > 10 * NS_PER_MS_INT) {
719 log_warn_every(LOG_RATE_DEFAULT,
"Mixer: Slow source reading took %lluus (%.2fms) for %d sources", read_time_us,
720 (
float)read_time_us / 1000.0f, source_count);
725 int speaking_count = 0;
727 for (
int i = 0; i < source_count; i++) {
728 int slot = source_map[i];
732 for (
int s = 0; s < frame_size; s++) {
733 float abs_sample = fabsf(source_samples[i][s]);
734 if (abs_sample > peak)
739 if (peak > mixer->ducking.envelope[slot]) {
740 mixer->ducking.envelope[slot] =
741 mixer->ducking.attack_coeff * mixer->ducking.envelope[slot] + (1.0f - mixer->ducking.attack_coeff) * peak;
743 mixer->ducking.envelope[slot] =
744 mixer->ducking.release_coeff * mixer->ducking.envelope[slot] + (1.0f - mixer->ducking.release_coeff) * peak;
748 if (mixer->ducking.envelope[slot] >
db_to_linear(-60.0f))
753 ducking_process_frame(&mixer->ducking, mixer->ducking.envelope, mixer->ducking.gain, mixer->max_sources);
756 float crowd_gain = (speaking_count > 0) ? (1.0f / powf((
float)speaking_count, mixer->crowd_alpha)) : 1.0f;
757 float pre_bus = mixer->base_gain * crowd_gain;
760 float combined_gains[MIXER_MAX_SOURCES];
761 for (
int i = 0; i < source_count; i++) {
762 int slot = source_map[i];
763 combined_gains[i] = mixer->ducking.gain[slot] * pre_bus;
768 for (
int s = 0; s < frame_size; s++) {
770 for (
int i = 0; i < source_count; i++) {
771 mix += source_samples[i][s] * combined_gains[i];
775 mixer->mix_buffer[s] = mix;
781 float frame_peak = 0.0f;
782 for (
int s = 0; s < frame_size; s++) {
783 float abs_val = fabsf(mixer->mix_buffer[s]);
784 if (abs_val > frame_peak)
785 frame_peak = abs_val;
792 for (
int s = 0; s < frame_size; s++) {
793 float mix = mixer->mix_buffer[s] * comp_gain;
798 output[frame_start + s] =
soft_clip(mix, 0.7f, 3.0f);
802 rwlock_rdunlock(&mixer->source_lock);
805 STOP_TIMER_AND_LOG_EVERY(warn, NS_PER_SEC_INT, 2 * NS_PER_MS_INT,
"mixer_total",
"Mixer took");