Support for Channel Mapping 253
[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
51 #define INT16_TO_FLOAT(x) ((1/32768.f)*(float)x)
52
53 void print_matrix_short(const opus_int16 *data, int rows, int cols)
54 {
55   int i, j;
56   for (i = 0; i < rows; i++)
57   {
58     for (j = 0; j < cols; j++)
59     {
60       fprintf(stderr, "%8.5f  ", (float)INT16_TO_FLOAT(data[j * rows + i]));
61     }
62     fprintf(stderr, "\n");
63   }
64   fprintf(stderr, "\n");
65 }
66
67 void print_matrix_float(const float *data, int rows, int cols)
68 {
69   int i, j;
70   for (i = 0; i < rows; i++)
71   {
72     for (j = 0; j < cols; j++)
73     {
74       fprintf(stderr, "%8.5f ", data[j * rows + i]);
75     }
76     fprintf(stderr, "\n");
77   }
78   fprintf(stderr, "\n");
79 }
80
81 void print_matrix(MappingMatrix *matrix)
82 {
83   opus_int16 *data;
84
85   fprintf(stderr, "%d x %d, gain: %d\n", matrix->rows, matrix->cols,
86     matrix->gain);
87
88   data = mapping_matrix_get_data(matrix);
89   print_matrix_short(data, matrix->rows, matrix->cols);
90 }
91
92 int assert_transform_short(
93   const opus_int16 *a, const opus_int16 *b, int size, opus_int16 tolerance)
94 {
95   int i;
96   for (i = 0; i < size; i++)
97   {
98     if (abs(a[i] - b[i]) > tolerance)
99     {
100       return 0;
101     }
102   }
103   return 1;
104 }
105
106 int assert_transform_float(
107   const float *a, const float *b, int size, float tolerance)
108 {
109   int i;
110   for (i = 0; i < size; i++)
111   {
112     if (fabsf(a[i] - b[i]) > tolerance)
113     {
114       return 0;
115     }
116   }
117   return 1;
118 }
119
120 void test_matrix_transform(void)
121 {
122   /* Create testing mixing matrix (4 x 3), gain 0dB:
123   *   [ 0 1 0 ]
124   *   [ 1 0 0 ]
125   *   [ 0 0 0 ]
126   *   [ 0 0 1 ]
127   */
128   opus_int32 matrix_size;
129   MappingMatrix *testing_matrix;
130   const opus_int16 testing_matrix_data[12] = {
131     0, 32767, 0, 0, 32767, 0, 0, 0, 0, 0, 0, 32767 };
132
133   const int frame_size = 10;
134   const opus_int16 input[30] = {
135     32767, 0, -32768, 29491, -3277, -29491, 26214, -6554, -26214, 22938, -9830,
136     -22938, 19661, -13107, -19661, 16384, -16384, -16384, 13107, -19661, -13107,
137     9830, -22938, -9830, 6554, -26214, -6554, 3277, -29491, -3277};
138   const opus_int16 expected_output[40] = {
139     0, 32767, 0, -32768, -3277, 29491, 0, -29491, -6554, 26214, 0, -26214,
140     -9830, 22938, 0, -22938, -13107, 19661, 0, -19661, -16384, 16384, 0, -16384,
141     -19661, 13107, 0, -13107, -22938, 9830, 0, -9830, -26214, 6554, 0, -6554,
142     -29491, 3277, 0, -3277};
143   opus_int16 output[40] = {0};
144
145 #ifndef DISABLE_FLOAT_API
146   int i;
147   /* Sample-accurate to -93.9794 dB */
148   float flt_tolerance = 2e-5f;
149   float input32[30] = {0};
150   float output32[40] = {0};
151   float expected_output32[40] = {0};
152
153   /* Convert short to float representations. */
154   for (i = 0; i < 30; i++)
155   {
156     input32[i] = INT16_TO_FLOAT(input[i]);
157   }
158   for (i = 0; i < 40; i++)
159   {
160     expected_output32[i] = INT16_TO_FLOAT(expected_output[i]);
161   }
162 #endif /* DISABLE_FLOAT_API */
163
164   /* Create the matrix. */
165   matrix_size = mapping_matrix_get_size(4, 3);
166   testing_matrix = (MappingMatrix *)opus_alloc(matrix_size);
167   mapping_matrix_init(testing_matrix, 4, 3, 0, testing_matrix_data,
168     12 * sizeof(opus_int16));
169
170   mapping_matrix_multiply_short(testing_matrix, input, testing_matrix->cols,
171     output, testing_matrix->rows, frame_size);
172   if (!assert_transform_short(output, expected_output, 40, 1))
173   {
174     fprintf(stderr, "Matrix:\n");
175     print_matrix(testing_matrix);
176
177     fprintf(stderr, "Input (short):\n");
178     print_matrix_short(input, testing_matrix->cols, frame_size);
179
180     fprintf(stderr, "Expected Output (short):\n");
181     print_matrix_short(expected_output, testing_matrix->rows, frame_size);
182
183     fprintf(stderr, "Output (short):\n");
184     print_matrix_short(output, testing_matrix->rows, frame_size);
185
186     goto bad_cleanup;
187   }
188
189 #ifndef DISABLE_FLOAT_API
190   mapping_matrix_multiply_float(testing_matrix, input32, testing_matrix->cols,
191     output32, testing_matrix->rows, frame_size);
192   if (!assert_transform_float(output32, expected_output32, 40, flt_tolerance))
193   {
194     fprintf(stderr, "Matrix:\n");
195     print_matrix(testing_matrix);
196
197     fprintf(stderr, "Input (float):\n");
198     print_matrix_float(input32, testing_matrix->cols, frame_size);
199
200     fprintf(stderr, "Expected Output (float):\n");
201     print_matrix_float(expected_output32, testing_matrix->rows, frame_size);
202
203     fprintf(stderr, "Output (float):\n");
204     print_matrix_float(output32, testing_matrix->rows, frame_size);
205
206     goto bad_cleanup;
207   }
208 #endif
209   opus_free(testing_matrix);
210   return;
211 bad_cleanup:
212   opus_free(testing_matrix);
213   test_failed();
214 }
215
216 void test_creation_arguments(const int channels, const int mapping_family)
217 {
218   int streams;
219   int coupled_streams;
220   int enc_error;
221   int dec_error;
222   int ret;
223   OpusProjectionEncoder *st_enc = NULL;
224   OpusProjectionDecoder *st_dec = NULL;
225
226   const opus_int32 Fs = 48000;
227   const int application = OPUS_APPLICATION_AUDIO;
228
229   int order_plus_one = (int)floor(sqrt((float)channels));
230   int nondiegetic_channels = channels - order_plus_one * order_plus_one;
231
232   int is_channels_valid = 0;
233   int is_projection_valid = 0;
234
235   st_enc = opus_projection_ambisonics_encoder_create(Fs, channels,
236     mapping_family, &streams, &coupled_streams, application, &enc_error);
237   if (st_enc != NULL)
238   {
239     opus_int32 matrix_size;
240     unsigned char *matrix;
241
242     ret = opus_projection_encoder_ctl(st_enc,
243       OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST, &matrix_size);
244     if (ret != OPUS_OK || !matrix_size)
245       test_failed();
246
247     matrix = (unsigned char *)opus_alloc(matrix_size);
248     ret = opus_projection_encoder_ctl(st_enc,
249       OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST, matrix, matrix_size);
250
251     opus_projection_encoder_destroy(st_enc);
252
253     st_dec = opus_projection_decoder_create(Fs, channels, streams,
254       coupled_streams, matrix, matrix_size, &dec_error);
255     if (st_dec != NULL)
256     {
257       opus_projection_decoder_destroy(st_dec);
258     }
259     opus_free(matrix);
260   }
261
262   is_channels_valid = (order_plus_one >= 2 && order_plus_one <= 4) &&
263     (nondiegetic_channels == 0 || nondiegetic_channels == 2);
264   is_projection_valid = (enc_error == OPUS_OK && dec_error == OPUS_OK);
265   if (is_channels_valid ^ is_projection_valid)
266   {
267     fprintf(stderr, "Channels: %d, Family: %d\n", channels, mapping_family);
268     fprintf(stderr, "Order+1: %d, Non-diegetic Channels: %d\n",
269       order_plus_one, nondiegetic_channels);
270     fprintf(stderr, "Streams: %d, Coupled Streams: %d\n",
271       streams, coupled_streams);
272     test_failed();
273   }
274 }
275
276 void generate_music(short *buf, opus_int32 len, opus_int32 channels)
277 {
278    opus_int32 i,j,k;
279    opus_int32 *a,*b,*c,*d;
280    a = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
281    b = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
282    c = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
283    d = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
284    memset(a, 0, sizeof(opus_int32) * channels);
285    memset(b, 0, sizeof(opus_int32) * channels);
286    memset(c, 0, sizeof(opus_int32) * channels);
287    memset(d, 0, sizeof(opus_int32) * channels);
288    j=0;
289
290    for(i=0;i<len;i++)
291    {
292      for(k=0;k<channels;k++)
293      {
294       opus_uint32 r;
295       opus_int32 v;
296       v=(((j*((j>>12)^((j>>10|j>>12)&26&j>>7)))&128)+128)<<15;
297       r=fast_rand();v+=r&65535;v-=r>>16;
298       b[k]=v-a[k]+((b[k]*61+32)>>6);a[k]=v;
299       c[k]=(30*(c[k]+b[k]+d[k])+32)>>6;d[k]=b[k];
300       v=(c[k]+128)>>8;
301       buf[i*channels+k]=v>32767?32767:(v<-32768?-32768:v);
302       if(i%6==0)j++;
303      }
304    }
305
306    free(a);
307    free(b);
308    free(c);
309    free(d);
310 }
311
312 void test_encode_decode(opus_int32 bitrate, opus_int32 channels,
313                         const int mapping_family)
314 {
315   const opus_int32 Fs = 48000;
316   const int application = OPUS_APPLICATION_AUDIO;
317
318   OpusProjectionEncoder *st_enc;
319   OpusProjectionDecoder *st_dec;
320   int streams;
321   int coupled;
322   int error;
323   short *buffer_in;
324   short *buffer_out;
325   unsigned char data[MAX_DATA_BYTES] = { 0 };
326   int len;
327   int out_samples;
328   opus_int32 matrix_size = 0;
329   unsigned char *matrix = NULL;
330
331   buffer_in = (short *)malloc(sizeof(short) * BUFFER_SIZE * channels);
332   buffer_out = (short *)malloc(sizeof(short) * BUFFER_SIZE * channels);
333
334   st_enc = opus_projection_ambisonics_encoder_create(Fs, channels,
335     mapping_family, &streams, &coupled, application, &error);
336   if (error != OPUS_OK) {
337     fprintf(stderr,
338       "Couldn\'t create encoder with %d channels and mapping family %d.\n",
339       channels, mapping_family);
340     free(buffer_in);
341     free(buffer_out);
342     test_failed();
343   }
344
345   error = opus_projection_encoder_ctl(st_enc,
346     OPUS_SET_BITRATE(bitrate * 1000 * (streams + coupled)));
347   if (error != OPUS_OK)
348   {
349     goto bad_cleanup;
350   }
351
352   error = opus_projection_encoder_ctl(st_enc,
353     OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST, &matrix_size);
354   if (error != OPUS_OK || !matrix_size)
355   {
356     goto bad_cleanup;
357   }
358
359   matrix = (unsigned char *)opus_alloc(matrix_size);
360   error = opus_projection_encoder_ctl(st_enc,
361     OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST, matrix, matrix_size);
362
363   st_dec = opus_projection_decoder_create(Fs, channels, streams, coupled,
364     matrix, matrix_size, &error);
365   opus_free(matrix);
366
367   if (error != OPUS_OK) {
368     fprintf(stderr,
369       "Couldn\'t create decoder with %d channels, %d streams "
370       "and %d coupled streams.\n", channels, streams, coupled);
371     goto bad_cleanup;
372   }
373
374   generate_music(buffer_in, BUFFER_SIZE, channels);
375
376   len = opus_projection_encode(
377     st_enc, buffer_in, BUFFER_SIZE, data, MAX_DATA_BYTES);
378   if(len<0 || len>MAX_DATA_BYTES) {
379     fprintf(stderr,"opus_encode() returned %d\n", len);
380     goto bad_cleanup;
381   }
382
383   out_samples = opus_projection_decode(
384     st_dec, data, len, buffer_out, MAX_FRAME_SAMPLES, 0);
385   if(out_samples!=BUFFER_SIZE) {
386     fprintf(stderr,"opus_decode() returned %d\n", out_samples);
387     goto bad_cleanup;
388   }
389
390   free(buffer_in);
391   free(buffer_out);
392   return;
393 bad_cleanup:
394   free(buffer_in);
395   free(buffer_out);
396   test_failed();
397 }
398
399 int main(int _argc, char **_argv)
400 {
401   unsigned int i;
402
403   (void)_argc;
404   (void)_argv;
405
406   /* Test matrix creation/multiplication. */
407   test_matrix_transform();
408
409   /* Test full range of channels in creation arguments. */
410   for (i = 0; i < 255; i++)
411     test_creation_arguments(i, 253);
412
413   /* Test encode/decode pipeline. */
414   test_encode_decode(64 * 16, 16, 253);
415
416   fprintf(stderr, "All projection tests passed.\n");
417   return 0;
418 }
419
420 #else
421
422 int main(int _argc, char **_argv)
423 {
424   (void)_argc;
425   (void)_argv;
426   fprintf(stderr, "Projection tests are disabled. "
427           "Configure with --enable-ambisonics for support.\n");
428   return 0;
429 }
430
431 #endif /* ENABLE_EXPERIMENTAL_AMBISONICS */