fix(audio): eliminate deterministic ssssh noise at session start

This commit is contained in:
cuicuiV5 committed 2026-09-04 15:49:54 +08:00
1 parent 0269b62eed
commit 47fecbcb26
4 files changed
+37 -6

No files matched your search

+3 -1
View File
@@ -22,7 +22,9 @@ extern "C" {
#define AGC_DECAY_RATE 0.9997f
#define AGC_MAX_GAIN 30.0f
#define AGC_NOISE_FLOOR 200.0f
#define AGC_INITIAL_PEAK 1000.0f
#define AGC_INITIAL_PEAK 28000.0f // Soft-start: initial gain = 1.0x (0dB) to avoid 28x burst
#define AUDIO_LEAD_MUTE_SAMPLES 2400 // 150ms silence zone (2400 samples @ 16kHz) to eliminate button click & pops
#define AUDIO_FADE_IN_SAMPLES 160 // 10ms micro fade-in (160 samples @ 16kHz) - only for transient smoothing at boundary
#define DECLIP_THRESHOLD 1000
// ==========================================
+2 -2
View File
@@ -3,16 +3,16 @@
void audio_agc_init(audio_agc_t *agc) {
if (!agc) return;
agc->peak = 1000.0f;
agc->target_level = 28000.0f;
agc->decay_rate = 0.9997f;
agc->max_gain = 30.0f;
agc->noise_floor = 200.0f;
agc->peak = agc->target_level; // Soft-start: start at 1.0x gain (0dB) to prevent burst noise
}
void audio_agc_reset(audio_agc_t *agc) {
if (!agc) return;
agc->peak = 1000.0f;
agc->peak = agc->target_level; // Soft-start: reset to 1.0x gain (0dB)
}
void audio_agc_process(audio_agc_t *agc, int16_t *samples, size_t count) {
+30 -3
View File
@@ -27,7 +27,9 @@ void audio_pipeline_start_session(audio_pipeline_t *pipeline, uint8_t session_id
audio_ring_buffer_clear(&pipeline->ring_buf);
pipeline->session_id = session_id;
pipeline->active = true;
pipeline->buffering = true; // Wait for initial 30ms prefill cushion to prevent jitter underruns
pipeline->buffering = true; // Wait for initial prefill cushion to prevent jitter underruns
pipeline->lead_mute_remaining = AUDIO_LEAD_MUTE_SAMPLES;
pipeline->fade_in_remaining = AUDIO_FADE_IN_SAMPLES;
pipeline->total_frames_decoded = 0;
pipeline->total_samples_pushed = 0;
pipeline->underrun_count = 0;
@@ -53,10 +55,33 @@ size_t audio_pipeline_feed_adpcm(audio_pipeline_t *pipeline, const uint8_t *adpc
// 3. DC-Blocker (strips ~0-80Hz sub-bass and AC hum)
audio_filter_dc_block(&pipeline->filter, pipeline->temp_pcm, samples_decoded);
// 4. Dynamic AGC + Soft Clip (target 28000, decay 0.9997, max_gain 30)
// 4. Dynamic AGC + Soft Clip
// KEY: During the lead mute period, zero the samples BEFORE AGC so the peak envelope
// sees silence and does NOT decay. This keeps gain locked at 1.0x (soft-start).
// When mute ends, the AGC is still at 1.0x and can only rise from real audio -> no burst.
for (size_t i = 0; i < samples_decoded; i++) {
if (pipeline->lead_mute_remaining > 0) {
// Feed zeros to AGC so it doesn't decay the peak during silence
pipeline->temp_pcm[i] = 0;
pipeline->lead_mute_remaining--;
}
// fade_in samples pass through AGC normally (they're real audio, just new)
}
audio_agc_process(&pipeline->agc, pipeline->temp_pcm, samples_decoded);
// 5. Enqueue into ring buffer
// 5. Post-Processing: Micro Fade-In (10ms) applied AFTER AGC
// The mute already zeroed the first 150ms. This 10ms fade-in smooths the
// hard boundary transition. It is too short (160 samples) to create audible
// noise artifacts, but long enough to eliminate any hard step discontinuity.
for (size_t i = 0; i < samples_decoded; i++) {
if (pipeline->fade_in_remaining > 0) {
uint32_t step = AUDIO_FADE_IN_SAMPLES - pipeline->fade_in_remaining;
pipeline->temp_pcm[i] = (int16_t)(((int32_t)pipeline->temp_pcm[i] * (int32_t)step) / AUDIO_FADE_IN_SAMPLES);
pipeline->fade_in_remaining--;
}
}
// 6. Enqueue into ring buffer
size_t written = audio_ring_buffer_write(&pipeline->ring_buf, pipeline->temp_pcm, samples_decoded);
pipeline->total_frames_decoded++;
@@ -109,5 +134,7 @@ void audio_pipeline_stop_session(audio_pipeline_t *pipeline) {
if (!pipeline) return;
pipeline->active = false;
pipeline->buffering = false;
pipeline->lead_mute_remaining = 0;
pipeline->fade_in_remaining = 0;
audio_ring_buffer_clear(&pipeline->ring_buf);
}
+2
View File
@@ -28,6 +28,8 @@ typedef struct {
uint32_t total_frames_decoded;
uint32_t total_samples_pushed;
uint32_t underrun_count;
uint32_t lead_mute_remaining;
uint32_t fade_in_remaining;
} audio_pipeline_t;
/**