call/radio: mic AGC compressor + selectable input device; proximity lock
This commit is contained in:
1 parent
f12f61d7ee
commit
556b0bc7b7
10 files changed
+541
-72
No files matched your search
+41
-3
@@ -18,6 +18,7 @@
|
||||
#include "../../lib/mem.h"
|
||||
#include "../../lib/u_async.h"
|
||||
#include "../../lib/speex_aec.h"
|
||||
#include "../../lib/audio_compressor.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
@@ -43,6 +44,7 @@ static struct call_tones* g_tones = NULL;
|
||||
static speex_aec_t* g_aec = NULL;
|
||||
static int g_aec_delay_frames = CALL_AEC_DELAY_FRAMES;
|
||||
static int g_aec_enabled = 1; /* по умолчанию: настройка aec_enabled */
|
||||
static struct audio_compressor* g_compressor = NULL;
|
||||
|
||||
static const struct call_audio_ops g_ops = {
|
||||
.on_media = call_audio_on_media,
|
||||
@@ -153,11 +155,31 @@ int call_audio_start(uint64_t call_id) {
|
||||
}
|
||||
}
|
||||
|
||||
/* компрессор микрофона (AGC) — как в рации: выравнивает тихий проводной микрофон
|
||||
* (HAL не применяет AGC на проводном входе). lookahead=0 — без задержки. */
|
||||
struct audio_compressor* ac = audio_compressor_create();
|
||||
if (ac) {
|
||||
audio_compressor_config_t accfg = {0};
|
||||
accfg.sample_rate = CALL_AUDIO_SAMPLE_RATE;
|
||||
accfg.channels = 1;
|
||||
accfg.block_duration_ms = CALL_AUDIO_FRAME_MS;
|
||||
accfg.lookback_ms = 200;
|
||||
accfg.lookahead_ms = 0;
|
||||
accfg.max_gain_db = (float)chat_setting_get_int(g_inst, "call_compressor_max_gain_db", 15);
|
||||
accfg.rise_rate_per_sec = (float)chat_setting_get_int(g_inst, "compressor_rise_rate", 2);
|
||||
accfg.target_level = 0.25f; /* -12 dBFS — как у голосовых сообщений */
|
||||
audio_compressor_configure(ac, &accfg);
|
||||
audio_compressor_set_enabled(ac, chat_setting_get_int(g_inst, "call_compressor_enabled", 1));
|
||||
} else {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CALL, "%s: compressor create failed — call without AGC", CALL_AUDIO_ID);
|
||||
}
|
||||
|
||||
g_encoder = enc;
|
||||
g_decoder = dec;
|
||||
g_vj = j;
|
||||
g_tones = tones;
|
||||
g_aec = aec;
|
||||
g_compressor = ac;
|
||||
g_call_id = call_id;
|
||||
g_active = 1;
|
||||
g_ending = 0;
|
||||
@@ -165,8 +187,10 @@ int call_audio_start(uint64_t call_id) {
|
||||
g_last_media_tb = 0;
|
||||
pthread_mutex_unlock(&g_mtx);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: started call=0x%016llx aec=%d delay=%d", CALL_AUDIO_ID,
|
||||
(unsigned long long)call_id, g_aec ? 1 : 0, g_aec_delay_frames);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: started call=0x%016llx aec=%d delay=%d compressor=%s gain=%d", CALL_AUDIO_ID,
|
||||
(unsigned long long)call_id, g_aec ? 1 : 0, g_aec_delay_frames,
|
||||
ac ? (audio_compressor_is_enabled(ac) ? "on" : "off") : "unavailable",
|
||||
chat_setting_get_int(g_inst, "call_compressor_max_gain_db", 15));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -185,6 +209,7 @@ void call_audio_release(uint64_t call_id) {
|
||||
struct vj* j = NULL;
|
||||
struct call_tones* tones = NULL;
|
||||
speex_aec_t* aec = NULL;
|
||||
struct audio_compressor* ac = NULL;
|
||||
int was_active = 0;
|
||||
|
||||
pthread_mutex_lock(&g_mtx);
|
||||
@@ -200,6 +225,7 @@ void call_audio_release(uint64_t call_id) {
|
||||
j = g_vj; g_vj = NULL;
|
||||
tones = g_tones; g_tones = NULL;
|
||||
aec = g_aec; g_aec = NULL;
|
||||
ac = g_compressor; g_compressor = NULL;
|
||||
}
|
||||
pthread_mutex_unlock(&g_mtx);
|
||||
|
||||
@@ -211,6 +237,7 @@ void call_audio_release(uint64_t call_id) {
|
||||
if (j) vj_destroy(j);
|
||||
if (tones) call_tones_destroy(tones);
|
||||
if (aec) speex_aec_destroy(aec);
|
||||
if (ac) audio_compressor_destroy(ac);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: released call=0x%016llx", CALL_AUDIO_ID, (unsigned long long)call_id);
|
||||
}
|
||||
|
||||
@@ -281,7 +308,18 @@ int call_audio_feed_pcm(uint64_t call_id, const int16_t* pcm, int count) {
|
||||
if (speex_aec_process_capture(g_aec, pcm, count, cleaned) == count)
|
||||
tx = cleaned;
|
||||
}
|
||||
len = opus_codec_encode(g_encoder, tx, count, opus, (int)sizeof(opus));
|
||||
/* компрессор (AGC): push → кодируем прямо из выхода до clear_output.
|
||||
* lookahead=0 ⇒ выход ровно count сэмплов. */
|
||||
if (g_compressor) {
|
||||
audio_compressor_push(g_compressor, tx, (size_t)count);
|
||||
const int16_t* out = audio_compressor_output(g_compressor);
|
||||
if (out && audio_compressor_output_size(g_compressor) >= (size_t)count)
|
||||
tx = out;
|
||||
len = opus_codec_encode(g_encoder, tx, count, opus, (int)sizeof(opus));
|
||||
audio_compressor_clear_output(g_compressor);
|
||||
} else {
|
||||
len = opus_codec_encode(g_encoder, tx, count, opus, (int)sizeof(opus));
|
||||
}
|
||||
ua = g_inst ? g_inst->ua : NULL;
|
||||
inst = g_inst;
|
||||
pthread_mutex_unlock(&g_mtx);
|
||||
|
||||
@@ -57,6 +57,8 @@ static const struct chat_setting_def g_setting_defs[] = {
|
||||
{"whisper_model_path", CHAT_SETTING_STRING, 0, 0, 0},
|
||||
{"group_autoconnect", CHAT_SETTING_BOOL, 1, 0, 1},
|
||||
{"aec_enabled", CHAT_SETTING_BOOL, 1, 0, 1}, /* AEC (SpeexDSP) в звонке, desktop GUI */
|
||||
{"call_compressor_enabled", CHAT_SETTING_BOOL, 1, 0, 1},
|
||||
{"call_compressor_max_gain_db",CHAT_SETTING_INT, 15, 1, 100},
|
||||
};
|
||||
|
||||
#define CHAT_SETTING_COUNT (sizeof(g_setting_defs) / sizeof(g_setting_defs[0]))
|
||||
|
||||
Reference in new issue
Block a user