1 /*
2  * sample_test.c - Sample audio conversion tests
3  *
4  * Tests every combination of audio format conversions (484 combinations).
5  * This is done by creating a double audio format triangle waveform and then
6  * for each transformation format pair, converting this waveform to the
7  * first format, then the second, then back to double again and comparing
8  * against the original.
9  *
10  * libInstPatch
11  * Copyright (C) 1999-2014 Element Green <element@elementsofsound.org>
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public License
15  * as published by the Free Software Foundation; version 2.1
16  * of the License only.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26  * 02110-1301, USA or on the web at http://www.gnu.org.
27  */
28 #include <libinstpatch/libinstpatch.h>
29 
30 #define DEFAULT_AUDIO_SIZE	(32 * 1024)	/* default test waveform size in samples */
31 #define WAVEFORM_PERIOD		1684		/* something to exercise LSB bytes too */
32 #define MAX_DIFF_ALLOWED	0.016		/* maximum difference allowed */
33 
34 #define WAVEFORM_QUARTER	(WAVEFORM_PERIOD / 4)
35 
36 /* all available sample format combinations */
37 int testformats[] =
38 {
39     IPATCH_SAMPLE_8BIT,
40     IPATCH_SAMPLE_16BIT,
41     IPATCH_SAMPLE_24BIT,
42     IPATCH_SAMPLE_32BIT,
43     IPATCH_SAMPLE_FLOAT,
44     IPATCH_SAMPLE_DOUBLE,
45     IPATCH_SAMPLE_REAL24BIT,
46 
47     IPATCH_SAMPLE_8BIT | IPATCH_SAMPLE_UNSIGNED,
48     IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_UNSIGNED,
49     IPATCH_SAMPLE_24BIT | IPATCH_SAMPLE_UNSIGNED,
50     IPATCH_SAMPLE_32BIT | IPATCH_SAMPLE_UNSIGNED,
51     IPATCH_SAMPLE_REAL24BIT | IPATCH_SAMPLE_UNSIGNED,
52 
53     IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_BENDIAN,
54     IPATCH_SAMPLE_24BIT | IPATCH_SAMPLE_BENDIAN,
55     IPATCH_SAMPLE_32BIT | IPATCH_SAMPLE_BENDIAN,
56     IPATCH_SAMPLE_FLOAT | IPATCH_SAMPLE_BENDIAN,
57     IPATCH_SAMPLE_DOUBLE | IPATCH_SAMPLE_BENDIAN,
58     IPATCH_SAMPLE_REAL24BIT | IPATCH_SAMPLE_BENDIAN,
59 
60     IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_UNSIGNED | IPATCH_SAMPLE_BENDIAN,
61     IPATCH_SAMPLE_24BIT | IPATCH_SAMPLE_UNSIGNED | IPATCH_SAMPLE_BENDIAN,
62     IPATCH_SAMPLE_32BIT | IPATCH_SAMPLE_UNSIGNED | IPATCH_SAMPLE_BENDIAN,
63     IPATCH_SAMPLE_REAL24BIT | IPATCH_SAMPLE_UNSIGNED | IPATCH_SAMPLE_BENDIAN
64 };
65 
66 #define SAMPLE_FORMAT_COUNT  G_N_ELEMENTS (testformats)
67 
68 static int test_size = DEFAULT_AUDIO_SIZE;
69 static gboolean verbose = FALSE;
70 
71 static GOptionEntry entries[] =
72 {
73     { "size", 's', 0, G_OPTION_ARG_INT, &test_size, "Size of test waveform in samples (default=32768)", NULL },
74     { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be verbose", NULL },
75     { NULL }
76 };
77 
main(int argc,char * argv[])78 int main(int argc, char *argv[])
79 {
80     IpatchSampleTransform *trans;
81     gpointer srctrans, desttrans;
82     double *dwave, *fintrans;
83     guint periodpos;
84     int srcform, destform;
85     int isrc, idest;
86     double d, maxdiff;
87     int i, maxindex, failcount = 0;
88     GError *error = NULL;
89     GOptionContext *context;
90 
91     context = g_option_context_new("- test libInstPatch sample format conversions");
92     g_option_context_add_main_entries(context, entries, "sample_test");
93 
94     if(!g_option_context_parse(context, &argc, &argv, &error))
95     {
96         printf("Failed to parse command line arguments: %s\n",
97                ipatch_gerror_message(error));
98         return 1;
99     }
100 
101     g_option_context_free(context);
102 
103     ipatch_init();
104 
105     dwave = g_new(double, test_size);	/* allocate audio buffer (double format) */
106     srctrans = g_malloc(8 * test_size);	/* source format transform buf */
107     desttrans = g_malloc(8 * test_size);	/* destination format transform buf */
108     fintrans = g_malloc(8 * test_size);	/* final transform buf */
109 
110     /* create sample transform object and allocate its buffer */
111     trans = ipatch_sample_transform_new(0, 0, 0);
112     ipatch_sample_transform_alloc_size(trans, 32 * 1024);
113 
114     /* generate triangle waveform */
115     for(i = 0; i < test_size; i++)
116     {
117         periodpos = i % WAVEFORM_PERIOD;
118 
119         if(periodpos <= WAVEFORM_QUARTER)
120         {
121             dwave[i] = periodpos / (double)WAVEFORM_QUARTER;
122         }
123         else if(periodpos <= WAVEFORM_QUARTER * 3)
124         {
125             dwave[i] = -((periodpos - WAVEFORM_QUARTER) / (double)WAVEFORM_QUARTER - 1.0);
126         }
127         else
128         {
129             dwave[i] = (periodpos - WAVEFORM_QUARTER * 3) / (double)WAVEFORM_QUARTER - 1.0;
130         }
131     }
132 
133     /* Convert between all possible format pairs using the following steps:
134      * - Convert double to first format of format pair
135      * - Convert from first format to second format
136      * - Convert second format back to double
137      * - Calculate maximum sample difference between new double audio and original
138      */
139 
140     for(isrc = 0; isrc < SAMPLE_FORMAT_COUNT; isrc++)
141     {
142         srcform = testformats[isrc];
143 
144         for(idest = 0; idest < SAMPLE_FORMAT_COUNT; idest++)
145         {
146             destform = testformats[idest];
147 
148             /* convert double waveform to source format */
149             ipatch_sample_transform_set_formats(trans, IPATCH_SAMPLE_DOUBLE
150                                                 | IPATCH_SAMPLE_ENDIAN_HOST, srcform,
151                                                 IPATCH_SAMPLE_UNITY_CHANNEL_MAP);
152             ipatch_sample_transform_convert(trans, dwave, srctrans, test_size);
153 
154             /* convert source format to destination format */
155             ipatch_sample_transform_set_formats(trans, srcform, destform,
156                                                 IPATCH_SAMPLE_UNITY_CHANNEL_MAP);
157             ipatch_sample_transform_convert(trans, srctrans, desttrans, test_size);
158 
159             /* convert destination format to final double output */
160             ipatch_sample_transform_set_formats(trans, destform, IPATCH_SAMPLE_DOUBLE
161                                                 | IPATCH_SAMPLE_ENDIAN_HOST,
162                                                 IPATCH_SAMPLE_UNITY_CHANNEL_MAP);
163             ipatch_sample_transform_convert(trans, desttrans, fintrans, test_size);
164 
165             /* compare final waveform against original */
166             for(i = 0, maxdiff = 0.0, maxindex = 0; i < test_size; i++)
167             {
168                 d = dwave[i] - fintrans[i];
169 
170                 if(d < 0.0)
171                 {
172                     d = -d;
173                 }
174 
175                 if(d > maxdiff)
176                 {
177                     maxdiff = d;
178                     maxindex = i;
179                 }
180             }
181 
182             if(verbose)
183                 printf("Convert format %03x to %03x%s: maxdiff=%0.6f, sample=%d\n",
184                        srcform, destform, maxdiff > MAX_DIFF_ALLOWED ? " FAILED" : "",
185                        maxdiff, maxindex);
186 
187             if(maxdiff > MAX_DIFF_ALLOWED)
188             {
189                 if(!verbose)
190                     printf("Convert format %03x to %03x FAILED: maxdiff=%0.6f, sample=%d\n",
191                            srcform, destform, maxdiff, maxindex);
192 
193                 failcount++;
194             }
195         }
196     }
197 
198     ipatch_sample_transform_free(trans);
199 
200     g_free(dwave);
201     g_free(srctrans);
202     g_free(desttrans);
203     g_free(fintrans);
204 
205     if(failcount > 0)
206     {
207         printf("%d of %d format conversions FAILED\n", failcount,
208                SAMPLE_FORMAT_COUNT * SAMPLE_FORMAT_COUNT);
209         return 1;
210     }
211 
212     printf("All %d sample format conversions PASSED\n",
213            SAMPLE_FORMAT_COUNT * SAMPLE_FORMAT_COUNT);
214 
215     return 0;
216 }
217