freeswitch/libs/voipcodecs/tests/gsm0610_tests.c

650 lines
20 KiB
C

/*
* VoIPcodecs - a series of DSP components for telephony
*
* gsm0610_tests.c - Test the GSM 06.10 FR codec.
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2006 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: gsm0610_tests.c,v 1.8 2007/11/10 11:14:58 steveu Exp $
*/
/*! \file */
/*! \page gsm0610_tests_page GSM 06.10 full rate codec tests
\section gsm0610_tests_page_sec_1 What does it do?
Two sets of tests are performed:
- The tests defined in the GSM 06.10 specification, using the test data files supplied with
the specification.
- A generally audio quality test, consisting of compressing and decompressing a speeech
file for audible comparison.
The speech file should be recorded at 16 bits/sample, 8000 samples/second, and named
"pre_gsm0610.wav".
\section gsm0610_tests_page_sec_2 How is it used?
To perform the tests in the GSM 06.10 specification you need to obtain the test data files from the
specification. These are copyright material, and so cannot be distributed with this test software.
They can, however, be freely downloaded from the ETSI web site.
The files, containing test vectors, which are supplied with the GSM 06.10 specification, should be
copied to etsitests/gsm0610/unpacked so the files are arranged in the following directories.
./fr_A:
Seq01-A.cod Seq01-A.inp Seq01-A.out
Seq02-A.cod Seq02-A.inp Seq02-A.out
Seq03-A.cod Seq03-A.inp Seq03-A.out
Seq04-A.cod Seq04-A.inp Seq04-A.out
Seq05-A.out
./fr_L:
Seq01.cod Seq01.inp Seq01.out
Seq02.cod Seq02.inp Seq02.out
Seq03.cod Seq03.inp Seq03.out
Seq04.cod Seq04.inp Seq04.out
Seq05.cod Seq05.out
./fr_U:
Seq01-U.cod Seq01-U.inp Seq01-U.out
Seq02-U.cod Seq02-U.inp Seq02-U.out
Seq03-U.cod Seq03-U.inp Seq03-U.out
Seq04-U.cod Seq04-U.inp Seq04-U.out
Seq05-U.out
./fr_homing_A:
Homing01_A.out
Seq01H_A.cod Seq01H_A.inp Seq01H_A.out
Seq02H_A.cod Seq02H_A.inp Seq02H_A.out
Seq03H_A.cod Seq03H_A.inp Seq03H_A.out
Seq04H_A.cod Seq04H_A.inp Seq04H_A.out
Seq05H_A.out
Seq06H_A.cod Seq06H_A.inp
./fr_homing_L:
Homing01.cod Homing01.out
Seq01h.cod Seq01h.inp Seq01h.out
Seq02h.cod Seq02h.inp Seq02h.out
Seq03h.cod Seq03h.inp Seq03h.out
Seq04h.cod Seq04h.inp Seq04h.out
Seq05h.cod Seq05h.out
Seq06h.cod Seq06h.inp
./fr_homing_U:
Homing01_U.out
Seq01H_U.cod Seq01H_U.inp Seq01H_U.out
Seq02H_U.cod Seq02H_U.inp Seq02H_U.out
Seq03H_U.cod Seq03H_U.inp Seq03H_U.out
Seq04H_U.cod Seq04H_U.inp Seq04H_U.out
Seq05H_U.out
Seq06H_U.cod Seq06H_U.inp
./fr_sync_A:
Seqsync_A.inp
Sync000_A.cod --to-- Sync159_A.cod
./fr_sync_L:
Bitsync.inp
Seqsync.inp
Sync000.cod --to-- Sync159.cod
./fr_sync_U:
Seqsync_U.inp
Sync000_U.cod --to-- Sync159_U.cod
This is different from the directory structure in which they are supplied. Also, the files names are a little
different. The supplied names are messy, and inconsistent across the sets. The names required by these tests
just clean up these inconsistencies. Note that you will need a Windows machine to unpack some of the supplied
files.
To perform a general audio quality test, gsm0610_tests should be run. The file ../localtests/short_nb_voice.wav
will be compressed to GSM 06.10 data, decompressed, and the resulting audio stored in post_gsm0610.wav.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <audiofile.h>
#include "voipcodecs.h"
#define BLOCK_LEN 160
#define TEST_DIR "../etsitests/gsm0610/unpacked/fr_"
#define IN_FILE_NAME "../localtests/short_nb_voice.wav"
#define OUT_FILE_NAME "post_gsm0610.wav"
#define HIST_LEN 1000
uint8_t law_in_vector[1000000];
int16_t in_vector[1000000];
uint16_t code_vector_buf[1000000];
uint8_t code_vector[1000000];
uint8_t ref_code_vector[1000000];
uint8_t decoder_code_vector[1000000];
uint8_t law_out_vector[1000000];
int16_t out_vector[1000000];
int16_t ref_out_vector[1000000];
uint8_t ref_law_out_vector[1000000];
int vector_len;
static int get_test_vector(int full, int disk, const char *name)
{
char buf[500];
int in;
int len;
int i;
if (full)
{
sprintf(buf, "%s%c/%s.inp", TEST_DIR, 'L', name);
if ((in = open(buf, O_RDONLY)) < 0)
{
fprintf(stderr, "Cannot open %s\n", buf);
exit(2);
}
len = read(in, in_vector, 1000000);
close(in);
len /= sizeof(int16_t);
vector_len = len;
}
sprintf(buf, "%s%c/%s.out", TEST_DIR, 'L', name);
if ((in = open(buf, O_RDONLY)) < 0)
{
fprintf(stderr, "Cannot open %s\n", buf);
exit(2);
}
len = read(in, ref_out_vector, 1000000);
close(in);
len /= sizeof(int16_t);
if (full)
{
if (len != vector_len)
{
fprintf(stderr, "Input and reference vector lengths do not match - %d %d\n", vector_len, len);
exit(2);
}
}
else
{
vector_len = len;
}
sprintf(buf, "%s%c/%s.cod", TEST_DIR, 'L', name);
if ((in = open(buf, O_RDONLY)) < 0)
{
fprintf(stderr, "Cannot open %s\n", buf);
exit(2);
}
len = read(in, code_vector_buf, 1000000);
close(in);
len /= sizeof(int16_t);
for (i = 0; i < len; i++)
{
ref_code_vector[i] = code_vector_buf[i];
decoder_code_vector[i] = code_vector_buf[i];
}
if (len*BLOCK_LEN != vector_len*76)
{
fprintf(stderr, "Input and code vector lengths do not match - %d %d\n", vector_len, len);
exit(2);
}
return len;
}
/*- End of function --------------------------------------------------------*/
static int get_law_test_vector(int full, int law, const char *name)
{
char buf[500];
int in;
int len;
int i;
int law_uc;
law_uc = toupper(law);
if (full)
{
sprintf(buf, "%s%c/%s-%c.inp", TEST_DIR, law_uc, name, law_uc);
if ((in = open(buf, O_RDONLY)) < 0)
{
fprintf(stderr, "Cannot open %s\n", buf);
exit(2);
}
len = read(in, law_in_vector, 1000000);
close(in);
vector_len = len;
sprintf(buf, "%s%c/%s-%c.cod", TEST_DIR, law_uc, name, law_uc);
if ((in = open(buf, O_RDONLY)) < 0)
{
fprintf(stderr, "Cannot open %s\n", buf);
exit(2);
}
len = read(in, code_vector_buf, 1000000);
close(in);
len /= sizeof(int16_t);
for (i = 0; i < len; i++)
ref_code_vector[i] = code_vector_buf[i];
if (len*BLOCK_LEN != vector_len*76)
{
fprintf(stderr, "Input and code vector lengths do not match - %d %d\n", vector_len, len);
exit(2);
}
}
sprintf(buf, "%s%c/%s-%c.out", TEST_DIR, law_uc, name, law_uc);
if ((in = open(buf, O_RDONLY)) < 0)
{
fprintf(stderr, "Cannot open %s\n", buf);
exit(2);
}
len = read(in, ref_law_out_vector, 1000000);
close(in);
if (full)
{
if (len != vector_len)
{
fprintf(stderr, "Input and reference vector lengths do not match - %d %d\n", vector_len, len);
exit(2);
}
}
else
{
vector_len = len;
}
sprintf(buf, "%s%c/%s.cod", TEST_DIR, 'L', name);
if ((in = open(buf, O_RDONLY)) < 0)
{
fprintf(stderr, "Cannot open %s\n", buf);
exit(2);
}
len = read(in, code_vector_buf, 1000000);
close(in);
len /= sizeof(int16_t);
for (i = 0; i < len; i++)
decoder_code_vector[i] = code_vector_buf[i];
return len;
}
/*- End of function --------------------------------------------------------*/
static int perform_linear_test(int full, int disk, const char *name)
{
gsm0610_state_t *gsm0610_enc_state;
gsm0610_state_t *gsm0610_dec_state;
int i;
int xxx;
int mismatches;
printf("Performing linear test '%s' from disk %d\n", name, disk);
get_test_vector(full, disk, name);
if (full)
{
if ((gsm0610_enc_state = gsm0610_init(NULL, GSM0610_PACKING_NONE)) == NULL)
{
fprintf(stderr, " Cannot create encoder\n");
exit(2);
}
xxx = gsm0610_encode(gsm0610_enc_state, code_vector, in_vector, vector_len/BLOCK_LEN);
printf("Check code vector of length %d\n", xxx);
for (i = 0, mismatches = 0; i < xxx; i++)
{
if (code_vector[i] != ref_code_vector[i])
{
printf("%8d/%3d: %6d %6d\n", i/76, i%76, code_vector[i], ref_code_vector[i]);
mismatches++;
}
}
gsm0610_release(gsm0610_enc_state);
if (mismatches)
{
printf("Test failed: %d of %d samples mismatch\n", mismatches, xxx);
exit(2);
}
printf("Test passed\n");
}
if ((gsm0610_dec_state = gsm0610_init(NULL, GSM0610_PACKING_NONE)) == NULL)
{
fprintf(stderr, " Cannot create decoder\n");
exit(2);
}
xxx = gsm0610_decode(gsm0610_dec_state, out_vector, decoder_code_vector, vector_len/BLOCK_LEN);
printf("Check output vector of length %d\n", vector_len);
for (i = 0, mismatches = 0; i < vector_len; i++)
{
if (out_vector[i] != ref_out_vector[i])
{
printf("%8d: %6d %6d\n", i, out_vector[i], ref_out_vector[i]);
mismatches++;
}
}
if (mismatches)
{
printf("Test failed: %d of %d samples mismatch\n", mismatches, vector_len);
exit(2);
}
gsm0610_release(gsm0610_dec_state);
printf("Test passed\n");
return 0;
}
/*- End of function --------------------------------------------------------*/
static int perform_law_test(int full, int law, const char *name)
{
gsm0610_state_t *gsm0610_enc_state;
gsm0610_state_t *gsm0610_dec_state;
int i;
int xxx;
int mismatches;
if (law == 'a')
printf("Performing A-law test '%s'\n", name);
else
printf("Performing u-law test '%s'\n", name);
get_law_test_vector(full, law, name);
if (full)
{
if ((gsm0610_enc_state = gsm0610_init(NULL, GSM0610_PACKING_NONE)) == NULL)
{
fprintf(stderr, " Cannot create encoder\n");
exit(2);
}
if (law == 'a')
{
for (i = 0; i < vector_len; i++)
in_vector[i] = alaw_to_linear(law_in_vector[i]);
}
else
{
for (i = 0; i < vector_len; i++)
in_vector[i] = ulaw_to_linear(law_in_vector[i]);
}
xxx = gsm0610_encode(gsm0610_enc_state, code_vector, in_vector, vector_len/BLOCK_LEN);
printf("Check code vector of length %d\n", xxx);
for (i = 0, mismatches = 0; i < xxx; i++)
{
if (code_vector[i] != ref_code_vector[i])
{
printf("%8d/%3d: %6d %6d %6d\n", i/76, i%76, code_vector[i], ref_code_vector[i], decoder_code_vector[i]);
mismatches++;
}
}
if (mismatches)
{
printf("Test failed: %d of %d samples mismatch\n", mismatches, xxx);
exit(2);
}
printf("Test passed\n");
gsm0610_release(gsm0610_enc_state);
}
if ((gsm0610_dec_state = gsm0610_init(NULL, GSM0610_PACKING_NONE)) == NULL)
{
fprintf(stderr, " Cannot create decoder\n");
exit(2);
}
xxx = gsm0610_decode(gsm0610_dec_state, out_vector, decoder_code_vector, vector_len/BLOCK_LEN);
if (law == 'a')
{
for (i = 0; i < vector_len; i++)
law_out_vector[i] = linear_to_alaw(out_vector[i]);
}
else
{
for (i = 0; i < vector_len; i++)
law_out_vector[i] = linear_to_ulaw(out_vector[i]);
}
printf("Check output vector of length %d\n", vector_len);
for (i = 0, mismatches = 0; i < vector_len; i++)
{
if (law_out_vector[i] != ref_law_out_vector[i])
{
printf("%8d: %6d %6d\n", i, law_out_vector[i], ref_law_out_vector[i]);
mismatches++;
}
}
if (mismatches)
{
printf("Test failed: %d of %d samples mismatch\n", mismatches, vector_len);
exit(2);
}
gsm0610_release(gsm0610_dec_state);
printf("Test passed\n");
return 0;
}
/*- End of function --------------------------------------------------------*/
static int repack_gsm0610_voip_to_wav49(uint8_t c[], const uint8_t d[])
{
gsm0610_frame_t frame[2];
int n;
n = gsm0610_unpack_voip(&frame[0], d);
gsm0610_unpack_voip(&frame[1], d + n);
n = gsm0610_pack_wav49(c, frame);
return n;
}
/*- End of function --------------------------------------------------------*/
static int repack_gsm0610_wav49_to_voip(uint8_t d[], const uint8_t c[])
{
gsm0610_frame_t frame[2];
int n[2];
gsm0610_unpack_wav49(frame, c);
n[0] = gsm0610_pack_voip(d, &frame[0]);
n[1] = gsm0610_pack_voip(d + n[0], &frame[1]);
return n[0] + n[1];
}
/*- End of function --------------------------------------------------------*/
static int perform_pack_unpack_test(void)
{
uint8_t a[66];
uint8_t b[66];
uint8_t c[66];
int i;
int j;
printf("Performing packing/unpacking tests (not part of the ETSI conformance tests).\n");
/* Try trans-packing a lot of random data looking for before/after mismatch. */
for (j = 0; j < 1000; j++)
{
for (i = 0; i < 65; i++)
a[i] = rand();
repack_gsm0610_wav49_to_voip(b, a);
repack_gsm0610_voip_to_wav49(c, b);
if (memcmp(a, c, 65))
{
printf("Test failed: data mismatch\n");
exit(2);
}
for (i = 0; i < 66; i++)
a[i] = rand();
/* Insert the magic code */
a[0] = (a[0] & 0xF) | 0xD0;
a[33] = (a[33] & 0xF) | 0xD0;
repack_gsm0610_voip_to_wav49(b, a);
repack_gsm0610_wav49_to_voip(c, b);
//for (i = 0; i < 66; i++)
// printf("%2d: 0x%02X 0x%02X\n", i, a[i], c[i]);
if (memcmp(a, c, 66))
{
printf("Test failed: data mismatch\n");
exit(2);
}
}
printf("Test passed\n");
return 0;
}
/*- End of function --------------------------------------------------------*/
int main(int argc, char *argv[])
{
AFfilehandle inhandle;
AFfilehandle outhandle;
AFfilesetup filesetup;
int frames;
int outframes;
float x;
int16_t pre_amp[HIST_LEN];
int16_t post_amp[HIST_LEN];
uint8_t gsm0610_data[HIST_LEN];
gsm0610_state_t *gsm0610_enc_state;
gsm0610_state_t *gsm0610_dec_state;
int etsitests;
int packing;
int i;
etsitests = TRUE;
packing = GSM0610_PACKING_NONE;
for (i = 1; i < argc; i++)
{
if (strcmp(argv[i], "-l") == 0)
{
etsitests = FALSE;
continue;
}
if (strcmp(argv[i], "-p") == 0)
{
packing = atoi(argv[++i]);
continue;
}
fprintf(stderr, "Unknown parameter %s specified.\n", argv[i]);
exit(2);
}
if (etsitests)
{
perform_linear_test(TRUE, 1, "Seq01");
perform_linear_test(TRUE, 1, "Seq02");
perform_linear_test(TRUE, 1, "Seq03");
perform_linear_test(TRUE, 1, "Seq04");
perform_linear_test(FALSE, 1, "Seq05");
perform_law_test(TRUE, 'a', "Seq01");
perform_law_test(TRUE, 'a', "Seq02");
perform_law_test(TRUE, 'a', "Seq03");
perform_law_test(TRUE, 'a', "Seq04");
perform_law_test(FALSE, 'a', "Seq05");
perform_law_test(TRUE, 'u', "Seq01");
perform_law_test(TRUE, 'u', "Seq02");
perform_law_test(TRUE, 'u', "Seq03");
perform_law_test(TRUE, 'u', "Seq04");
perform_law_test(FALSE, 'u', "Seq05");
/* This is not actually an ETSI test */
perform_pack_unpack_test();
printf("Tests passed.\n");
}
else
{
if ((inhandle = afOpenFile(IN_FILE_NAME, "r", 0)) == AF_NULL_FILEHANDLE)
{
printf(" Cannot open wave file '%s'\n", IN_FILE_NAME);
exit(2);
}
if ((x = afGetFrameSize(inhandle, AF_DEFAULT_TRACK, 1)) != 2.0)
{
printf(" Unexpected frame size in wave file '%s'\n", IN_FILE_NAME);
exit(2);
}
if ((x = afGetRate(inhandle, AF_DEFAULT_TRACK)) != (float) SAMPLE_RATE)
{
printf(" Unexpected sample rate in wave file '%s'\n", IN_FILE_NAME);
exit(2);
}
if ((x = afGetChannels(inhandle, AF_DEFAULT_TRACK)) != 1.0)
{
printf(" Unexpected number of channels in wave file '%s'\n", IN_FILE_NAME);
exit(2);
}
if ((filesetup = afNewFileSetup()) == AF_NULL_FILESETUP)
{
fprintf(stderr, " Failed to create file setup\n");
exit(2);
}
afInitSampleFormat(filesetup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, 16);
afInitRate(filesetup, AF_DEFAULT_TRACK, (float) SAMPLE_RATE);
afInitFileFormat(filesetup, AF_FILE_WAVE);
afInitChannels(filesetup, AF_DEFAULT_TRACK, 1);
outhandle = afOpenFile(OUT_FILE_NAME, "w", filesetup);
if (outhandle == AF_NULL_FILEHANDLE)
{
fprintf(stderr, " Cannot create wave file '%s'\n", OUT_FILE_NAME);
exit(2);
}
if ((gsm0610_enc_state = gsm0610_init(NULL, packing)) == NULL)
{
fprintf(stderr, " Cannot create encoder\n");
exit(2);
}
if ((gsm0610_dec_state = gsm0610_init(NULL, packing)) == NULL)
{
fprintf(stderr, " Cannot create decoder\n");
exit(2);
}
while ((frames = afReadFrames(inhandle, AF_DEFAULT_TRACK, pre_amp, 2*BLOCK_LEN)))
{
gsm0610_encode(gsm0610_enc_state, gsm0610_data, pre_amp, (packing == GSM0610_PACKING_WAV49) ? 1 : 2);
gsm0610_decode(gsm0610_dec_state, post_amp, gsm0610_data, (packing == GSM0610_PACKING_WAV49) ? 1 : 2);
outframes = afWriteFrames(outhandle, AF_DEFAULT_TRACK, post_amp, frames);
}
if (afCloseFile(inhandle) != 0)
{
printf(" Cannot close wave file '%s'\n", IN_FILE_NAME);
exit(2);
}
if (afCloseFile(outhandle) != 0)
{
printf(" Cannot close wave file '%s'\n", OUT_FILE_NAME);
exit(2);
}
afFreeFileSetup(filesetup);
gsm0610_release(gsm0610_enc_state);
gsm0610_release(gsm0610_dec_state);
}
return 0;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/