58#define TEST_DURATION_SECS 2
65#define MIN_SNR_LOSSY_DB 15.0
69#define MAX_SAMPLE_ERR_LOSSLESS 256
74#define MIN_DECODED_RATIO 0.90
90 for (i = 0; i < samples; i++) {
91 double t = (double)i / sample_rate;
92 double sig = 0.6 * sin(2.0 *
M_PI * 200.0 * t)
93 + 0.4 * sin(2.0 *
M_PI * 800.0 * t);
95 sig *= 0.5 * (1.0 + sin(2.0 *
M_PI * 4.0 * t));
96 buf[i] = (int16_t)(sig * 16000.0);
109static double compute_snr(
const int16_t *orig,
const int16_t *roundtrip,
int samples)
111 double signal_power = 0.0;
112 double noise_power = 0.0;
115 for (i = 0; i < samples; i++) {
116 double s = (double)orig[i];
117 double n = (double)(orig[i] - roundtrip[i]);
118 signal_power += s * s;
119 noise_power += n * n;
122 if (signal_power < 1.0) {
125 if (noise_power < 1.0) {
129 return 10.0 * log10(signal_power / noise_power);
154 int samples,
int max_delay,
int *delay_out)
156 double best_corr = -1e300;
165 if (samples <= max_delay || max_delay <= 0) {
169 for (
d = 0;
d <= max_delay;
d++) {
173 for (i = 0; i < n; i++) {
174 corr += (double)orig[i] * (
double)decoded[i +
d];
176 if (corr > best_corr) {
183 *delay_out = best_delay;
187 return compute_snr(orig, decoded + best_delay, samples - best_delay);
198 for (i = 0; i < samples; i++) {
199 int err =
abs((
int)orig[i] - (
int)roundtrip[i]);
229 struct ast_test *
test,
232 const int16_t *orig_buf,
240 int16_t *decoded_buf =
NULL;
241 int total_decoded = 0;
242 int chunk_samples = sample_rate *
CHUNK_MS / 1000;
246 const char *codec_name;
254 "Skipping %s: no translation path from slin to %s\n",
255 codec_name, codec_name);
263 "FAIL %s: found encoder but no decoder path back to slin\n",
269 buf_capacity = total_samples + chunk_samples;
270 decoded_buf =
ast_calloc(buf_capacity,
sizeof(int16_t));
276 for (offset = 0; offset < total_samples; offset += chunk_samples) {
281 int feed = total_samples -
offset;
282 if (feed > chunk_samples) {
283 feed = chunk_samples;
286 memset(&input_frame, 0,
sizeof(input_frame));
289 input_frame.
datalen = feed *
sizeof(int16_t);
293 input_frame.
src =
"test_codec_translations";
317 if (total_decoded +
copy > buf_capacity) {
318 copy = buf_capacity - total_decoded;
320 memcpy(decoded_buf + total_decoded, cur->
data.
ptr,
321 copy *
sizeof(int16_t));
322 total_decoded +=
copy;
329 "FAIL %s: decoded only %d of %d samples (%.0f%%)\n",
330 codec_name, total_decoded, total_samples,
331 100.0 * total_decoded / total_samples);
337 int cmp_samples = total_decoded < total_samples
338 ? total_decoded : total_samples;
341 int max_delay = sample_rate * 20 / 1000;
344 cmp_samples, max_delay, &delay);
347 " %s (lossy): SNR = %.1f dB (threshold %.1f dB)"
348 " [%d/%d samples, delay=%d/%.1fms]\n",
349 codec_name, snr, min_snr_db,
350 cmp_samples, total_samples,
351 delay, 1000.0 * delay / sample_rate);
353 if (snr < min_snr_db) {
355 "FAIL %s: SNR %.1f dB is below minimum %.1f dB\n",
356 codec_name, snr, min_snr_db);
361 double snr =
compute_snr(orig_buf, decoded_buf, cmp_samples);
364 " %s (lossless): max_err = %d (limit %d),"
365 " SNR = %.1f dB [%d/%d samples]\n",
367 cmp_samples, total_samples);
371 "FAIL %s: max sample error %d exceeds limit %d\n",
415 {
"codec2", 1, -2.0 },
416 {
"lpc10", 1, -2.0 },
425 {
"g722", 1, 0.0, 16000 },
426 {
"speex16", 1, 5.0, 16000 },
429 {
"speex32", 1, 5.0, 32000 },
432 {
"opus", 1, 8.0, 48000 },
444 info->name =
"codec_translations";
445 info->category =
"/main/codec/";
446 info->summary =
"Roundtrip encode/decode test and quality check for various codecs referenced in this test and present in the installation";
448 "Generates a synthetic speech-like signal (200 Hz +\n"
449 "800 Hz with 4 Hz AM envelope) at the codec's native sample\n"
450 "rate, feeds it through the codec and back,\n"
451 "then verifies the output quality over the full duration.\n"
452 "For near-lossless codecs (ulaw, alaw) it checks that the\n"
453 "maximum per-sample error is within a tight bound.\n"
454 "For lossy codecs it checks that the SNR exceeds a per-codec\n"
455 "minimum threshold. Vocoders use a near-zero threshold\n"
463 "Starting codec roundtrip tests (%d codecs, %d seconds of audio)\n",
495 " %s: no translation path available, skipping\n",
501 orig_buf =
ast_malloc(total_samples *
sizeof(int16_t));
511 test, slin_fmt, target_fmt, orig_buf,
528 "\nCodec roundtrip summary: %d tested, %d passed, %d failed\n",
529 tested, tested - failed, failed);
533 "WARNING: No codecs were available to test. "
534 "Ensure codec modules are loaded.\n");
static int copy(char *infile, char *outfile)
Utility function to copy a file.
Asterisk main include file. File version handling, generic pbx functions.
#define ast_calloc(num, len)
A wrapper for calloc()
#define ast_malloc(len)
A wrapper for malloc()
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Asterisk internal frame definitions.
Support for logging to various files, console and syslog Configuration in file logger....
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Asterisk module definitions.
#define ASTERISK_GPL_KEY
The text the key() function should return.
#define AST_MODULE_INFO_STANDARD_EXTENDED(keystr, desc)
@ AST_MODULE_LOAD_SUCCESS
static void cleanup(void)
Clean up any old apps that we don't need any more.
struct ast_format * format
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
enum ast_frame_type frametype
union ast_frame::@235 data
Default structure for translators, with the basic fields and buffers, all allocated as part of the sa...
Codec roundtrip entry: table of codecs to test.
#define AST_TEST_REGISTER(cb)
#define ast_test_status_update(a, b, c...)
#define AST_TEST_UNREGISTER(cb)
#define AST_TEST_DEFINE(hdr)
static void generate_speech_signal(int16_t *buf, int samples, int sample_rate)
Generate a synthetic speech-like test signal in linear sample.
static double compute_snr_aligned(const int16_t *orig, const int16_t *decoded, int samples, int max_delay, int *delay_out)
Compute SNR after aligning the decoded signal via cross-correlation.
#define MIN_DECODED_RATIO
static double compute_snr(const int16_t *orig, const int16_t *roundtrip, int samples)
Compute Signal-to-Noise Ratio between original and roundtripped signal.
#define MAX_SAMPLE_ERR_LOSSLESS
static enum ast_test_result_state check_codec(struct ast_test *test, struct ast_format *slin_fmt, struct ast_format *target_fmt, const int16_t *orig_buf, int total_samples, int sample_rate, int is_lossy, double min_snr_db)
Attempt a roundtrip encode/decode for one codec format.
#define TEST_DURATION_SECS
static int load_module(void)
static const struct codec_test_entry codec_table[]
static int unload_module(void)
static int compute_max_error(const int16_t *orig, const int16_t *roundtrip, int samples)
Compute maximum absolute per-sample error.
Support for translation of data formats. translate.c.
struct ast_frame * ast_translate(struct ast_trans_pvt *tr, struct ast_frame *f, int consume)
translates one or more frames Apply an input frame into the translator and receive zero or one output...
void ast_translator_free_path(struct ast_trans_pvt *tr)
Frees a translator path Frees the given translator path structure.
struct ast_trans_pvt * ast_translator_build_path(struct ast_format *dest, struct ast_format *source)
Builds a translator path Build a path (possibly NULL) from source to dest.
unsigned int ast_translate_path_steps(struct ast_format *dest, struct ast_format *src)
Returns the number of steps required to convert from 'src' to 'dest'.