|
|
|
|
@ -244,40 +244,49 @@ static void test_streaming_matches_recording(void) {
|
|
|
|
|
audio_compressor_destroy(recording); audio_compressor_destroy(streaming); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Тракт трансляции: усиление текущего mono-кадра перед Opus, live bypass и смена предела gain. */ |
|
|
|
|
/* Тракт трансляции: общий gain стереопары перед Opus, live bypass и смена предела gain. */ |
|
|
|
|
static void test_live_opus_gain(void) { |
|
|
|
|
struct audio_compressor* ac = audio_compressor_create(); |
|
|
|
|
opus_codec_encoder_t* encoder = opus_codec_encoder_create(48000, 1); |
|
|
|
|
opus_codec_decoder_t* decoder = opus_codec_decoder_create(48000, 1); |
|
|
|
|
opus_codec_encoder_t* encoder = opus_codec_encoder_create(48000, 2); |
|
|
|
|
opus_codec_decoder_t* decoder = opus_codec_decoder_create(48000, 2); |
|
|
|
|
TEST(ac && encoder && decoder, "live audio codecs created"); |
|
|
|
|
if (!ac || !encoder || !decoder) goto done; |
|
|
|
|
audio_compressor_config_t cfg = {.sample_rate = 48000, .channels = 1, .block_duration_ms = 20, |
|
|
|
|
audio_compressor_config_t cfg = {.sample_rate = 48000, .channels = 2, .block_duration_ms = 20, |
|
|
|
|
.lookback_ms = 200, .lookahead_ms = 0, .max_gain_db = 6, .rise_rate_per_sec = 6, |
|
|
|
|
.release_rate_per_sec = 30, .target_level = 0.25f}; |
|
|
|
|
TEST(audio_compressor_configure(ac, &cfg) == 0, "live radio compressor configuration"); |
|
|
|
|
TEST(opus_codec_encoder_application_set(encoder, OPUS_CODEC_APP_AUDIO) == 0, "live Opus audio mode"); |
|
|
|
|
TEST(opus_codec_encoder_bitrate_set(encoder, 64000) == 0, "live Opus bitrate"); |
|
|
|
|
int16_t input[960], compressed[960], decoded[960]; uint8_t packet[1275]; |
|
|
|
|
TEST(opus_codec_encoder_bitrate_set(encoder, 128000) == 0, "live Opus bitrate"); |
|
|
|
|
int16_t input[1920], compressed[1920], decoded[1920]; uint8_t packet[1275]; |
|
|
|
|
size_t allocations = u_get_allocated_count(); |
|
|
|
|
for (int frame = 0; frame < 30; frame++) { |
|
|
|
|
for (unsigned i = 0; i < 960; i++) input[i] = (int16_t)(500 * sin((frame * 960 + i) * 2 * M_PI * 440 / 48000)); |
|
|
|
|
TEST(audio_compressor_process_frame(ac, input, compressed, 960) == 0, "live current frame compressed"); |
|
|
|
|
for (unsigned i = 0; i < 960; i++) { |
|
|
|
|
input[2 * i] = (int16_t)(500 * sin((frame * 960 + i) * 2 * M_PI * 440 / 48000)); |
|
|
|
|
input[2 * i + 1] = (int16_t)(125 * sin((frame * 960 + i) * 2 * M_PI * 1100 / 48000)); |
|
|
|
|
} |
|
|
|
|
TEST(audio_compressor_process_frame(ac, input, compressed, 1920) == 0, "live current stereo frame compressed"); |
|
|
|
|
int length = opus_codec_encode(encoder, compressed, 960, packet, sizeof(packet)); |
|
|
|
|
TEST(length > 0, "live compressed frame encoded"); |
|
|
|
|
if (length <= 0) goto done; |
|
|
|
|
TEST(opus_codec_decode(decoder, packet, length, decoded, 960) == 960, "live decoded packet has 20ms of PCM"); |
|
|
|
|
} |
|
|
|
|
double input_energy = 0, output_energy = 0; |
|
|
|
|
for (unsigned i = 0; i < 960; i++) { input_energy += (double)input[i] * input[i]; output_energy += (double)decoded[i] * decoded[i]; } |
|
|
|
|
for (unsigned i = 0; i < 1920; i++) { input_energy += (double)input[i] * input[i]; output_energy += (double)decoded[i] * decoded[i]; } |
|
|
|
|
TEST(output_energy > input_energy * 2.5 && output_energy < input_energy * 5, "gain survives real Opus roundtrip"); |
|
|
|
|
double left_energy = 0, right_energy = 0; |
|
|
|
|
for (unsigned i = 0; i < 960; i++) { |
|
|
|
|
left_energy += (double)decoded[2 * i] * decoded[2 * i]; |
|
|
|
|
right_energy += (double)decoded[2 * i + 1] * decoded[2 * i + 1]; |
|
|
|
|
} |
|
|
|
|
TEST(left_energy > right_energy * 8 && left_energy < right_energy * 25, "stereo balance survives compressor and Opus"); |
|
|
|
|
TEST(u_get_allocated_count() == allocations, "live audio does not accumulate compressor buffers"); |
|
|
|
|
audio_compressor_set_enabled(ac, 0); |
|
|
|
|
memcpy(compressed, input, sizeof(input)); |
|
|
|
|
TEST(audio_compressor_process_frame(ac, compressed, compressed, 960) == 0, "live bypass in place"); |
|
|
|
|
TEST(audio_compressor_process_frame(ac, compressed, compressed, 1920) == 0, "live bypass in place"); |
|
|
|
|
TEST(memcmp(input, compressed, sizeof(input)) == 0, "disabled compressor preserves current PCM exactly"); |
|
|
|
|
cfg.max_gain_db = 20; |
|
|
|
|
TEST(audio_compressor_configure(ac, &cfg) == 0, "live gain limit changed"); audio_compressor_set_enabled(ac, 1); |
|
|
|
|
for (unsigned frame = 0; frame < 50; frame++) TEST(audio_compressor_process_frame(ac, input, compressed, 960) == 0, "live new gain frame"); |
|
|
|
|
for (unsigned frame = 0; frame < 50; frame++) TEST(audio_compressor_process_frame(ac, input, compressed, 1920) == 0, "live new gain frame"); |
|
|
|
|
TEST(audio_compressor_gain_db(ac) > 19.9f && audio_compressor_gain_db(ac) <= 20.01f, "live new gain limit applied"); |
|
|
|
|
TEST(audio_compressor_output_size(ac) == 0, "live gain update introduces no output backlog"); |
|
|
|
|
done: |
|
|
|
|
|