Fix memory issues in Projection API.
[opus.git] / tests / test_opus_projection.c
1 /* Copyright (c) 2017 Google Inc.
2    Written by Andrew Allen */
3 /*
4    Redistribution and use in source and binary forms, with or without
5    modification, are permitted provided that the following conditions
6    are met:
7
8    - Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10
11    - Redistributions in binary form must reproduce the above copyright
12    notice, this list of conditions and the following disclaimer in the
13    documentation and/or other materials provided with the distribution.
14
15    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
19    OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <assert.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stdint.h>
36 #include <string.h>
37 #include "float_cast.h"
38 #include "opus.h"
39 #include "test_opus_common.h"
40 #include "opus_projection.h"
41 #include "mathops.h"
42 #include "../src/mapping_matrix.c"
43 #include "mathops.c"
44
45 #ifdef ENABLE_EXPERIMENTAL_AMBISONICS
46
47 #define BUFFER_SIZE 960
48 #define MAX_DATA_BYTES 32768
49 #define MAX_FRAME_SAMPLES 5760
50 #define ERROR_TOLERANCE 1
51
52 #define SIMPLE_MATRIX_SIZE 12
53 #define SIMPLE_MATRIX_FRAME_SIZE 10
54 #define SIMPLE_MATRIX_INPUT_SIZE 30
55 #define SIMPLE_MATRIX_OUTPUT_SIZE 40
56
57 int assert_is_equal(
58   const opus_val16 *a, const opus_int16 *b, int size, opus_int16 tolerance)
59 {
60   int i;
61   for (i = 0; i < size; i++)
62   {
63 #ifdef FIXED_POINT
64     opus_int16 val = a[i];
65 #else
66     opus_int16 val = FLOAT2INT16(a[i]);
67 #endif
68     if (abs(val - b[i]) > tolerance)
69       return 1;
70   }
71   return 0;
72 }
73
74 int assert_is_equal_short(
75   const opus_int16 *a, const opus_int16 *b, int size, opus_int16 tolerance)
76 {
77   int i;
78   for (i = 0; i < size; i++)
79     if (abs(a[i] - b[i]) > tolerance)
80       return 1;
81   return 0;
82 }
83
84 void test_simple_matrix(void)
85 {
86   const MappingMatrix simple_matrix_params = {4, 3, 0};
87   const opus_int16 simple_matrix_data[SIMPLE_MATRIX_SIZE] = {0, 32767, 0, 0, 32767, 0, 0, 0, 0, 0, 0, 32767};
88   const opus_int16 input_int16[SIMPLE_MATRIX_INPUT_SIZE] = {
89     32767, 0, -32768, 29491, -3277, -29491, 26214, -6554, -26214, 22938, -9830,
90     -22938, 19661, -13107, -19661, 16384, -16384, -16384, 13107, -19661, -13107,
91     9830, -22938, -9830, 6554, -26214, -6554, 3277, -29491, -3277};
92   const opus_int16 expected_output_int16[SIMPLE_MATRIX_OUTPUT_SIZE] = {
93     0, 32767, 0, -32768, -3277, 29491, 0, -29491, -6554, 26214, 0, -26214,
94     -9830, 22938, 0, -22938, -13107, 19661, 0, -19661, -16384, 16384, 0, -16384,
95     -19661, 13107, 0, -13107, -22938, 9830, 0, -9830, -26214, 6554, 0, -6554,
96     -29491, 3277, 0, -3277};
97
98   int i, ret;
99   opus_val16 *input_val16;
100   opus_val16 *output_val16;
101   opus_int16 *output_int16;
102   MappingMatrix *simple_matrix;
103
104   /* Allocate input/output buffers. */
105   input_val16 = (opus_val16 *)opus_alloc(align(sizeof(opus_val16) * SIMPLE_MATRIX_INPUT_SIZE));
106   output_int16 = (opus_int16 *)opus_alloc(align(sizeof(opus_int16) * SIMPLE_MATRIX_OUTPUT_SIZE));
107   output_val16 = (opus_val16 *)opus_alloc(align(sizeof(opus_val16) * SIMPLE_MATRIX_OUTPUT_SIZE));
108
109   /* Initialize matrix */
110   simple_matrix = (MappingMatrix *)opus_alloc(
111     mapping_matrix_get_size(simple_matrix_params.rows,
112                             simple_matrix_params.cols));
113   mapping_matrix_init(simple_matrix, simple_matrix_params.rows,
114     simple_matrix_params.cols, simple_matrix_params.gain, simple_matrix_data,
115     sizeof(simple_matrix_data));
116
117   /* Copy inputs. */
118   for (i = 0; i < SIMPLE_MATRIX_INPUT_SIZE; i++)
119   {
120 #ifdef FIXED_POINT
121     input_val16[i] = input_int16[i];
122 #else
123     input_val16[i] = (1/32768.f)*input_int16[i];
124 #endif
125   }
126
127   /* _in_short */
128   for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
129     output_val16[i] = 0;
130   for (i = 0; i < simple_matrix->rows; i++)
131   {
132     mapping_matrix_multiply_channel_in_short(simple_matrix,
133       input_int16, simple_matrix->cols, &output_val16[i], i,
134       simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
135   }
136   ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
137   if (ret)
138     test_failed();
139
140   /* _out_short */
141   for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
142     output_int16[i] = 0;
143   for (i = 0; i < simple_matrix->cols; i++)
144   {
145     mapping_matrix_multiply_channel_out_short(simple_matrix,
146       &input_val16[i], i, simple_matrix->cols, output_int16,
147       simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
148   }
149   ret = assert_is_equal_short(output_int16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
150   if (ret)
151     test_failed();
152
153 #if !defined(DISABLE_FLOAT_API) && !defined(FIXED_POINT)
154   /* _in_float */
155   for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
156     output_val16[i] = 0;
157   for (i = 0; i < simple_matrix->rows; i++)
158   {
159     mapping_matrix_multiply_channel_in_float(simple_matrix,
160       input_val16, simple_matrix->cols, &output_val16[i], i,
161       simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
162   }
163   ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
164   if (ret)
165     test_failed();
166
167   /* _out_float */
168   for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
169     output_val16[i] = 0;
170   for (i = 0; i < simple_matrix->cols; i++)
171   {
172     mapping_matrix_multiply_channel_out_float(simple_matrix,
173       &input_val16[i], i, simple_matrix->cols, output_val16,
174       simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
175   }
176   ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
177   if (ret)
178     test_failed();
179 #endif
180
181   opus_free(input_val16);
182   opus_free(output_int16);
183   opus_free(output_val16);
184   opus_free(simple_matrix);
185 }
186
187 void test_creation_arguments(const int channels, const int mapping_family)
188 {
189   int streams;
190   int coupled_streams;
191   int enc_error;
192   int dec_error;
193   int ret;
194   OpusProjectionEncoder *st_enc = NULL;
195   OpusProjectionDecoder *st_dec = NULL;
196
197   const opus_int32 Fs = 48000;
198   const int application = OPUS_APPLICATION_AUDIO;
199
200   int order_plus_one = (int)floor(sqrt((float)channels));
201   int nondiegetic_channels = channels - order_plus_one * order_plus_one;
202
203   int is_channels_valid = 0;
204   int is_projection_valid = 0;
205
206   st_enc = opus_projection_ambisonics_encoder_create(Fs, channels,
207     mapping_family, &streams, &coupled_streams, application, &enc_error);
208   if (st_enc != NULL)
209   {
210     opus_int32 matrix_size;
211     unsigned char *matrix;
212
213     ret = opus_projection_encoder_ctl(st_enc,
214       OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST, &matrix_size);
215     if (ret != OPUS_OK || !matrix_size)
216       test_failed();
217
218     matrix = (unsigned char *)opus_alloc(matrix_size);
219     ret = opus_projection_encoder_ctl(st_enc,
220       OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST, matrix, matrix_size);
221
222     opus_projection_encoder_destroy(st_enc);
223
224     st_dec = opus_projection_decoder_create(Fs, channels, streams,
225       coupled_streams, matrix, matrix_size, &dec_error);
226     if (st_dec != NULL)
227     {
228       opus_projection_decoder_destroy(st_dec);
229     }
230     opus_free(matrix);
231   }
232
233   is_channels_valid = (order_plus_one >= 2 && order_plus_one <= 4) &&
234     (nondiegetic_channels == 0 || nondiegetic_channels == 2);
235   is_projection_valid = (enc_error == OPUS_OK && dec_error == OPUS_OK);
236   if (is_channels_valid ^ is_projection_valid)
237   {
238     fprintf(stderr, "Channels: %d, Family: %d\n", channels, mapping_family);
239     fprintf(stderr, "Order+1: %d, Non-diegetic Channels: %d\n",
240       order_plus_one, nondiegetic_channels);
241     fprintf(stderr, "Streams: %d, Coupled Streams: %d\n",
242       streams, coupled_streams);
243     test_failed();
244   }
245 }
246
247 void generate_music(short *buf, opus_int32 len, opus_int32 channels)
248 {
249    opus_int32 i,j,k;
250    opus_int32 *a,*b,*c,*d;
251    a = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
252    b = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
253    c = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
254    d = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
255    memset(a, 0, sizeof(opus_int32) * channels);
256    memset(b, 0, sizeof(opus_int32) * channels);
257    memset(c, 0, sizeof(opus_int32) * channels);
258    memset(d, 0, sizeof(opus_int32) * channels);
259    j=0;
260
261    for(i=0;i<len;i++)
262    {
263      for(k=0;k<channels;k++)
264      {
265       opus_uint32 r;
266       opus_int32 v;
267       v=(((j*((j>>12)^((j>>10|j>>12)&26&j>>7)))&128)+128)<<15;
268       r=fast_rand();v+=r&65535;v-=r>>16;
269       b[k]=v-a[k]+((b[k]*61+32)>>6);a[k]=v;
270       c[k]=(30*(c[k]+b[k]+d[k])+32)>>6;d[k]=b[k];
271       v=(c[k]+128)>>8;
272       buf[i*channels+k]=v>32767?32767:(v<-32768?-32768:v);
273       if(i%6==0)j++;
274      }
275    }
276
277    free(a);
278    free(b);
279    free(c);
280    free(d);
281 }
282
283 void test_encode_decode(opus_int32 bitrate, opus_int32 channels,
284                         const int mapping_family)
285 {
286   const opus_int32 Fs = 48000;
287   const int application = OPUS_APPLICATION_AUDIO;
288
289   OpusProjectionEncoder *st_enc;
290   OpusProjectionDecoder *st_dec;
291   int streams;
292   int coupled;
293   int error;
294   short *buffer_in;
295   short *buffer_out;
296   unsigned char data[MAX_DATA_BYTES] = { 0 };
297   int len;
298   int out_samples;
299   opus_int32 matrix_size = 0;
300   unsigned char *matrix = NULL;
301
302   buffer_in = (short *)malloc(sizeof(short) * BUFFER_SIZE * channels);
303   buffer_out = (short *)malloc(sizeof(short) * BUFFER_SIZE * channels);
304
305   st_enc = opus_projection_ambisonics_encoder_create(Fs, channels,
306     mapping_family, &streams, &coupled, application, &error);
307   if (error != OPUS_OK) {
308     fprintf(stderr,
309       "Couldn\'t create encoder with %d channels and mapping family %d.\n",
310       channels, mapping_family);
311     free(buffer_in);
312     free(buffer_out);
313     test_failed();
314   }
315
316   error = opus_projection_encoder_ctl(st_enc,
317     OPUS_SET_BITRATE(bitrate * 1000 * (streams + coupled)));
318   if (error != OPUS_OK)
319   {
320     goto bad_cleanup;
321   }
322
323   error = opus_projection_encoder_ctl(st_enc,
324     OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST, &matrix_size);
325   if (error != OPUS_OK || !matrix_size)
326   {
327     goto bad_cleanup;
328   }
329
330   matrix = (unsigned char *)opus_alloc(matrix_size);
331   error = opus_projection_encoder_ctl(st_enc,
332     OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST, matrix, matrix_size);
333
334   st_dec = opus_projection_decoder_create(Fs, channels, streams, coupled,
335     matrix, matrix_size, &error);
336   opus_free(matrix);
337
338   if (error != OPUS_OK) {
339     fprintf(stderr,
340       "Couldn\'t create decoder with %d channels, %d streams "
341       "and %d coupled streams.\n", channels, streams, coupled);
342     goto bad_cleanup;
343   }
344
345   generate_music(buffer_in, BUFFER_SIZE, channels);
346
347   len = opus_projection_encode(
348     st_enc, buffer_in, BUFFER_SIZE, data, MAX_DATA_BYTES);
349   if(len<0 || len>MAX_DATA_BYTES) {
350     fprintf(stderr,"opus_encode() returned %d\n", len);
351     goto bad_cleanup;
352   }
353
354   out_samples = opus_projection_decode(
355     st_dec, data, len, buffer_out, MAX_FRAME_SAMPLES, 0);
356   if(out_samples!=BUFFER_SIZE) {
357     fprintf(stderr,"opus_decode() returned %d\n", out_samples);
358     goto bad_cleanup;
359   }
360
361   free(buffer_in);
362   free(buffer_out);
363   return;
364 bad_cleanup:
365   free(buffer_in);
366   free(buffer_out);
367   test_failed();
368 }
369
370 int main(int _argc, char **_argv)
371 {
372   unsigned int i;
373
374   (void)_argc;
375   (void)_argv;
376
377   /* Test simple matrix multiplication routines. */
378   test_simple_matrix();
379
380   /* Test full range of channels in creation arguments. */
381   for (i = 0; i < 255; i++)
382     test_creation_arguments(i, 253);
383
384   /* Test encode/decode pipeline. */
385   test_encode_decode(64 * 18, 18, 253);
386
387   fprintf(stderr, "All projection tests passed.\n");
388   return 0;
389 }
390
391 #else
392
393 int main(int _argc, char **_argv)
394 {
395   (void)_argc;
396   (void)_argv;
397   fprintf(stderr, "Projection tests are disabled. "
398           "Configure with --enable-ambisonics for support.\n");
399   return 0;
400 }
401
402 #endif /* ENABLE_EXPERIMENTAL_AMBISONICS */