#ifndef CALL_AUDIO_H #define CALL_AUDIO_H // call_audio.h — сервисный аудио-движок P2P-звонка (единый для desktop/Android/headless). // // Владеет Opus codec, адаптивным джиттер-буфером (voice_jitter: кодированные // кадры + SoundTouch time-stretch на pull) и тонами (глитч при сталле / завершение). // Излишек голоса держится до декодирования — декодируется только то, что готово // к воспроизведению. Потребитель (desktop miniaudio / Android AudioTrack) получает // финальный PCM через call_audio_pull_pcm и отдаёт захваченный PCM через // call_audio_feed_pcm. // // Потоки: // - call_audio_start / begin_end / release — GUI-поток (по ACCEPTED/ENDED); // - call_audio_feed_pcm / pull_pcm — аудио-поток (single-writer); // - call_audio_on_media / get_stats — uasync-поток (из call.c). // Общие указатели (encoder/decoder/vj/tones) защищены мьютексом; encoder и // decoder — только аудио-поток (decoder дёргается из vj_pull через коллбэк), // vj_push — только uasync-поток. #include struct UTUN_INSTANCE; #ifdef __cplusplus extern "C" { #endif #define CALL_AUDIO_SAMPLE_RATE 48000 #define CALL_AUDIO_FRAME_MS 20 #define CALL_AUDIO_FRAME_SAMPLES (CALL_AUDIO_SAMPLE_RATE * CALL_AUDIO_FRAME_MS / 1000) /* 960 */ /* Зарегистрировать аудио-движок в call.c (идемпотентно). */ int call_audio_init(struct UTUN_INSTANCE* inst); void call_audio_destroy(struct UTUN_INSTANCE* inst); /* GUI-поток: создать codec + jitter + tones для звонка. 0 = успех. */ int call_audio_start(uint64_t call_id); /* GUI-поток: по CALL_ENDED — начать тон завершения (pull_pcm выдаст 450мс тона). */ void call_audio_begin_end(uint64_t call_id); /* GUI-поток (после остановки аудиоустройства): освободить все объекты. */ void call_audio_release(uint64_t call_id); /* GUI-поток: остановить текущий активный звонок (удобная обёртка release). */ void call_audio_stop(void); /* аудио-поток: закодировать PCM-кадр (ровно CALL_AUDIO_FRAME_SAMPLES) и отправить пиру. */ int call_audio_feed_pcm(uint64_t call_id, const int16_t* pcm, int count); /* аудио-поток: выдать до max_samples финального PCM (jitter + stretch + тоны). * Возвращает число сэмплов (может быть < max — андерфлоу/конец тона). */ int call_audio_pull_pcm(uint64_t call_id, int16_t* out, int max_samples); /* uasync-поток (из call.c): принятый Opus-кадр — кладём в джиттер-буфер (vj_push, * декодирование произойдёт на pull в аудио-потоке). */ void call_audio_on_media(struct UTUN_INSTANCE* inst, uint64_t call_id, uint16_t seq, uint32_t ts_ms, const uint8_t* opus, int len); /* uasync-поток: метрики джиттер-буфера для CALL_STATS (dropped/underruns — необязательные). */ int call_audio_get_stats(uint64_t call_id, int* buffer_ms, int* tempo_x100, uint32_t* dropped, uint32_t* underruns); /* Включить/выключить AEC (SpeexDSP) в рантайме. desktop — по настройке GUI, * Android — по спикерфону (route == SPEAKER). Работает и в активном звонке. */ void call_audio_set_aec_enabled(int enabled); /* Задать задержку рендер→захват AEC в кадрах (20 мс). Android измеряет через * AudioTrack/AudioRecord getTimestamp, desktop использует дефолт. Если AEC уже * активен — пересоздаётся с новой задержкой. */ void call_audio_set_aec_delay_frames(int frames); #ifdef __cplusplus } #endif #endif /* CALL_AUDIO_H */