mirror of https://github.com/jetkvm/kvm.git
Improve soft-clipper with validation, error handling, and overflow protection
- Add input validation: NULL checks, bounds checking (max 7680 samples) - Change return type to int for error propagation - Use saturating NEON arithmetic (vqaddq_s16, vqsubq_s16) to prevent overflow - Fix type consistency: use int16_t instead of short throughout - Update documentation: precise threshold (0.9375 or 15/16), describe 4:1 compression - Remove redundant clamping operations (mathematically proven unnecessary) - Add stdbool.h include for bool type support - Handle soft-clip errors at call site to prevent encoding corrupted audio
This commit is contained in:
parent
9e6ffb34e3
commit
2ef6cb2d4d
|
|
@ -21,6 +21,7 @@
|
||||||
#include <speex/speex_resampler.h>
|
#include <speex/speex_resampler.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
@ -208,45 +209,44 @@ static inline void simd_clear_samples_s16(short * __restrict__ buffer, uint32_t
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Soft-clip audio samples to prevent digital clipping distortion
|
* Soft-clip audio samples to prevent digital clipping distortion
|
||||||
* Uses smooth saturation curve for transients that exceed ±30720 (~0.94 of max)
|
* Samples within ±30720 (0.9375 or 15/16 of max) pass through unchanged
|
||||||
|
* Samples exceeding threshold are compressed 4:1 (excess reduced by 75%)
|
||||||
* Processes 8 samples per iteration using ARM NEON
|
* Processes 8 samples per iteration using ARM NEON
|
||||||
*/
|
*/
|
||||||
static inline void simd_soft_clip_s16(short * __restrict__ buffer, uint32_t samples) {
|
static inline int simd_soft_clip_s16(int16_t * __restrict__ buffer, uint32_t samples) {
|
||||||
const int16_t threshold = 30720; // 0.9375 * 32768
|
if (__builtin_expect(buffer == NULL || samples == 0, 0)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__builtin_expect(samples > 7680, 0)) {
|
||||||
|
fprintf(stderr, "ERROR: simd_soft_clip_s16: sample count %u exceeds maximum\n", samples);
|
||||||
|
fflush(stderr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int16_t threshold = 30720;
|
||||||
const int16x8_t thresh_pos = vdupq_n_s16(threshold);
|
const int16x8_t thresh_pos = vdupq_n_s16(threshold);
|
||||||
const int16x8_t thresh_neg = vdupq_n_s16(-threshold);
|
const int16x8_t thresh_neg = vdupq_n_s16(-threshold);
|
||||||
const int16x8_t max_val = vdupq_n_s16(32767);
|
|
||||||
const int16x8_t min_val = vdupq_n_s16(-32768);
|
|
||||||
|
|
||||||
uint32_t i = 0;
|
uint32_t i = 0;
|
||||||
uint32_t simd_samples = samples & ~7U;
|
uint32_t simd_samples = samples & ~7U;
|
||||||
|
|
||||||
for (; i < simd_samples; i += 8) {
|
for (; i < simd_samples; i += 8) {
|
||||||
int16x8_t samples_vec = vld1q_s16(&buffer[i]);
|
int16x8_t samples_vec = vld1q_s16(&buffer[i]);
|
||||||
|
|
||||||
// Detect samples exceeding positive threshold
|
|
||||||
uint16x8_t exceeds_pos = vcgtq_s16(samples_vec, thresh_pos);
|
uint16x8_t exceeds_pos = vcgtq_s16(samples_vec, thresh_pos);
|
||||||
// Detect samples below negative threshold
|
|
||||||
uint16x8_t exceeds_neg = vcltq_s16(samples_vec, thresh_neg);
|
uint16x8_t exceeds_neg = vcltq_s16(samples_vec, thresh_neg);
|
||||||
|
|
||||||
// Apply soft saturation to samples exceeding thresholds
|
|
||||||
// For positive: scale down to range [threshold, max_val]
|
|
||||||
// For negative: scale up to range [min_val, -threshold]
|
|
||||||
int16x8_t clipped = samples_vec;
|
int16x8_t clipped = samples_vec;
|
||||||
clipped = vbslq_s16(exceeds_pos,
|
clipped = vbslq_s16(exceeds_pos,
|
||||||
vaddq_s16(thresh_pos, vshrq_n_s16(vsubq_s16(samples_vec, thresh_pos), 2)),
|
vqaddq_s16(thresh_pos, vshrq_n_s16(vqsubq_s16(samples_vec, thresh_pos), 2)),
|
||||||
clipped);
|
clipped);
|
||||||
clipped = vbslq_s16(exceeds_neg,
|
clipped = vbslq_s16(exceeds_neg,
|
||||||
vaddq_s16(thresh_neg, vshrq_n_s16(vsubq_s16(samples_vec, thresh_neg), 2)),
|
vqaddq_s16(thresh_neg, vshrq_n_s16(vqsubq_s16(samples_vec, thresh_neg), 2)),
|
||||||
clipped);
|
clipped);
|
||||||
|
|
||||||
// Clamp to int16 range
|
|
||||||
clipped = vminq_s16(vmaxq_s16(clipped, min_val), max_val);
|
|
||||||
|
|
||||||
vst1q_s16(&buffer[i], clipped);
|
vst1q_s16(&buffer[i], clipped);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scalar: remaining samples
|
|
||||||
for (; i < samples; i++) {
|
for (; i < samples; i++) {
|
||||||
int32_t sample = buffer[i];
|
int32_t sample = buffer[i];
|
||||||
if (sample > threshold) {
|
if (sample > threshold) {
|
||||||
|
|
@ -254,11 +254,10 @@ static inline void simd_soft_clip_s16(short * __restrict__ buffer, uint32_t samp
|
||||||
} else if (sample < -threshold) {
|
} else if (sample < -threshold) {
|
||||||
sample = -threshold + ((sample + threshold) >> 2);
|
sample = -threshold + ((sample + threshold) >> 2);
|
||||||
}
|
}
|
||||||
// Clamp to int16 range
|
buffer[i] = (int16_t)sample;
|
||||||
if (sample > 32767) sample = 32767;
|
|
||||||
if (sample < -32768) sample = -32768;
|
|
||||||
buffer[i] = (short)sample;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// INITIALIZATION STATE TRACKING
|
// INITIALIZATION STATE TRACKING
|
||||||
|
|
@ -810,8 +809,12 @@ retry_read:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply soft-clipping to prevent digital clipping distortion on sharp transients
|
if (simd_soft_clip_s16(pcm_to_encode, opus_frame_size * capture_channels) < 0) {
|
||||||
simd_soft_clip_s16(pcm_to_encode, opus_frame_size * capture_channels);
|
fprintf(stderr, "ERROR: capture: Soft-clipping failed\n");
|
||||||
|
fflush(stderr);
|
||||||
|
pthread_mutex_unlock(&capture_mutex);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
nb_bytes = opus_encode(enc, pcm_to_encode, opus_frame_size, out, max_packet_size);
|
nb_bytes = opus_encode(enc, pcm_to_encode, opus_frame_size, out, max_packet_size);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue