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.h"
43 #include "mathops.h"
44 
45 #define BUFFER_SIZE 960
46 #define MAX_DATA_BYTES 32768
47 #define MAX_FRAME_SAMPLES 5760
48 #define ERROR_TOLERANCE 1
49 
50 #define SIMPLE_MATRIX_SIZE 12
51 #define SIMPLE_MATRIX_FRAME_SIZE 10
52 #define SIMPLE_MATRIX_INPUT_SIZE 30
53 #define SIMPLE_MATRIX_OUTPUT_SIZE 40
54 
assert_is_equal(const opus_val16 * a,const opus_int16 * b,int size,opus_int16 tolerance)55 int assert_is_equal(
56   const opus_val16 *a, const opus_int16 *b, int size, opus_int16 tolerance)
57 {
58   int i;
59   for (i = 0; i < size; i++)
60   {
61 #ifdef FIXED_POINT
62     opus_int16 val = a[i];
63 #else
64     opus_int16 val = FLOAT2INT16(a[i]);
65 #endif
66     if (abs(val - b[i]) > tolerance)
67       return 1;
68   }
69   return 0;
70 }
71 
assert_is_equal_short(const opus_int16 * a,const opus_int16 * b,int size,opus_int16 tolerance)72 int assert_is_equal_short(
73   const opus_int16 *a, const opus_int16 *b, int size, opus_int16 tolerance)
74 {
75   int i;
76   for (i = 0; i < size; i++)
77     if (abs(a[i] - b[i]) > tolerance)
78       return 1;
79   return 0;
80 }
81 
test_simple_matrix(void)82 void test_simple_matrix(void)
83 {
84   const MappingMatrix simple_matrix_params = {4, 3, 0};
85   const opus_int16 simple_matrix_data[SIMPLE_MATRIX_SIZE] = {0, 32767, 0, 0, 32767, 0, 0, 0, 0, 0, 0, 32767};
86   const opus_int16 input_int16[SIMPLE_MATRIX_INPUT_SIZE] = {
87     32767, 0, -32768, 29491, -3277, -29491, 26214, -6554, -26214, 22938, -9830,
88     -22938, 19661, -13107, -19661, 16384, -16384, -16384, 13107, -19661, -13107,
89     9830, -22938, -9830, 6554, -26214, -6554, 3277, -29491, -3277};
90   const opus_int16 expected_output_int16[SIMPLE_MATRIX_OUTPUT_SIZE] = {
91     0, 32767, 0, -32768, -3277, 29491, 0, -29491, -6554, 26214, 0, -26214,
92     -9830, 22938, 0, -22938, -13107, 19661, 0, -19661, -16384, 16384, 0, -16384,
93     -19661, 13107, 0, -13107, -22938, 9830, 0, -9830, -26214, 6554, 0, -6554,
94     -29491, 3277, 0, -3277};
95 
96   int i, ret;
97   opus_int32 simple_matrix_size;
98   opus_val16 *input_val16;
99   opus_val16 *output_val16;
100   opus_int16 *output_int16;
101   MappingMatrix *simple_matrix;
102 
103   /* Allocate input/output buffers. */
104   input_val16 = (opus_val16 *)opus_alloc(sizeof(opus_val16) * SIMPLE_MATRIX_INPUT_SIZE);
105   output_int16 = (opus_int16 *)opus_alloc(sizeof(opus_int16) * SIMPLE_MATRIX_OUTPUT_SIZE);
106   output_val16 = (opus_val16 *)opus_alloc(sizeof(opus_val16) * SIMPLE_MATRIX_OUTPUT_SIZE);
107 
108   /* Initialize matrix */
109   simple_matrix_size = mapping_matrix_get_size(simple_matrix_params.rows,
110     simple_matrix_params.cols);
111   if (!simple_matrix_size)
112     test_failed();
113 
114   simple_matrix = (MappingMatrix *)opus_alloc(simple_matrix_size);
115   mapping_matrix_init(simple_matrix, simple_matrix_params.rows,
116     simple_matrix_params.cols, simple_matrix_params.gain, simple_matrix_data,
117     sizeof(simple_matrix_data));
118 
119   /* Copy inputs. */
120   for (i = 0; i < SIMPLE_MATRIX_INPUT_SIZE; i++)
121   {
122 #ifdef FIXED_POINT
123     input_val16[i] = input_int16[i];
124 #else
125     input_val16[i] = (1/32768.f)*input_int16[i];
126 #endif
127   }
128 
129   /* _in_short */
130   for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
131     output_val16[i] = 0;
132   for (i = 0; i < simple_matrix->rows; i++)
133   {
134     mapping_matrix_multiply_channel_in_short(simple_matrix,
135       input_int16, simple_matrix->cols, &output_val16[i], i,
136       simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
137   }
138   ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
139   if (ret)
140     test_failed();
141 
142   /* _out_short */
143   for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
144     output_int16[i] = 0;
145   for (i = 0; i < simple_matrix->cols; i++)
146   {
147     mapping_matrix_multiply_channel_out_short(simple_matrix,
148       &input_val16[i], i, simple_matrix->cols, output_int16,
149       simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
150   }
151   ret = assert_is_equal_short(output_int16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
152   if (ret)
153     test_failed();
154 
155 #if !defined(DISABLE_FLOAT_API) && !defined(FIXED_POINT)
156   /* _in_float */
157   for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
158     output_val16[i] = 0;
159   for (i = 0; i < simple_matrix->rows; i++)
160   {
161     mapping_matrix_multiply_channel_in_float(simple_matrix,
162       input_val16, simple_matrix->cols, &output_val16[i], i,
163       simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
164   }
165   ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
166   if (ret)
167     test_failed();
168 
169   /* _out_float */
170   for (i = 0; i < SIMPLE_MATRIX_OUTPUT_SIZE; i++)
171     output_val16[i] = 0;
172   for (i = 0; i < simple_matrix->cols; i++)
173   {
174     mapping_matrix_multiply_channel_out_float(simple_matrix,
175       &input_val16[i], i, simple_matrix->cols, output_val16,
176       simple_matrix->rows, SIMPLE_MATRIX_FRAME_SIZE);
177   }
178   ret = assert_is_equal(output_val16, expected_output_int16, SIMPLE_MATRIX_OUTPUT_SIZE, ERROR_TOLERANCE);
179   if (ret)
180     test_failed();
181 #endif
182 
183   opus_free(input_val16);
184   opus_free(output_int16);
185   opus_free(output_val16);
186   opus_free(simple_matrix);
187 }
188 
test_creation_arguments(const int channels,const int mapping_family)189 void test_creation_arguments(const int channels, const int mapping_family)
190 {
191   int streams;
192   int coupled_streams;
193   int enc_error;
194   int dec_error;
195   int ret;
196   OpusProjectionEncoder *st_enc = NULL;
197   OpusProjectionDecoder *st_dec = NULL;
198 
199   const opus_int32 Fs = 48000;
200   const int application = OPUS_APPLICATION_AUDIO;
201 
202   int order_plus_one = (int)floor(sqrt((float)channels));
203   int nondiegetic_channels = channels - order_plus_one * order_plus_one;
204 
205   int is_channels_valid = 0;
206   int is_projection_valid = 0;
207 
208   st_enc = opus_projection_ambisonics_encoder_create(Fs, channels,
209     mapping_family, &streams, &coupled_streams, application, &enc_error);
210   if (st_enc != NULL)
211   {
212     opus_int32 matrix_size;
213     unsigned char *matrix;
214 
215     ret = opus_projection_encoder_ctl(st_enc,
216       OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST, &matrix_size);
217     if (ret != OPUS_OK || !matrix_size)
218       test_failed();
219 
220     matrix = (unsigned char *)opus_alloc(matrix_size);
221     ret = opus_projection_encoder_ctl(st_enc,
222       OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST, matrix, matrix_size);
223 
224     opus_projection_encoder_destroy(st_enc);
225 
226     st_dec = opus_projection_decoder_create(Fs, channels, streams,
227       coupled_streams, matrix, matrix_size, &dec_error);
228     if (st_dec != NULL)
229     {
230       opus_projection_decoder_destroy(st_dec);
231     }
232     opus_free(matrix);
233   }
234 
235   is_channels_valid = (order_plus_one >= 2 && order_plus_one <= 4) &&
236     (nondiegetic_channels == 0 || nondiegetic_channels == 2);
237   is_projection_valid = (enc_error == OPUS_OK && dec_error == OPUS_OK);
238   if (is_channels_valid ^ is_projection_valid)
239   {
240     fprintf(stderr, "Channels: %d, Family: %d\n", channels, mapping_family);
241     fprintf(stderr, "Order+1: %d, Non-diegetic Channels: %d\n",
242       order_plus_one, nondiegetic_channels);
243     fprintf(stderr, "Streams: %d, Coupled Streams: %d\n",
244       streams, coupled_streams);
245     test_failed();
246   }
247 }
248 
generate_music(short * buf,opus_int32 len,opus_int32 channels)249 void generate_music(short *buf, opus_int32 len, opus_int32 channels)
250 {
251    opus_int32 i,j,k;
252    opus_int32 *a,*b,*c,*d;
253    a = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
254    b = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
255    c = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
256    d = (opus_int32 *)malloc(sizeof(opus_int32) * channels);
257    memset(a, 0, sizeof(opus_int32) * channels);
258    memset(b, 0, sizeof(opus_int32) * channels);
259    memset(c, 0, sizeof(opus_int32) * channels);
260    memset(d, 0, sizeof(opus_int32) * channels);
261    j=0;
262 
263    for(i=0;i<len;i++)
264    {
265      for(k=0;k<channels;k++)
266      {
267       opus_uint32 r;
268       opus_int32 v;
269       v=(((j*((j>>12)^((j>>10|j>>12)&26&j>>7)))&128)+128)<<15;
270       r=fast_rand();v+=r&65535;v-=r>>16;
271       b[k]=v-a[k]+((b[k]*61+32)>>6);a[k]=v;
272       c[k]=(30*(c[k]+b[k]+d[k])+32)>>6;d[k]=b[k];
273       v=(c[k]+128)>>8;
274       buf[i*channels+k]=v>32767?32767:(v<-32768?-32768:v);
275       if(i%6==0)j++;
276      }
277    }
278 
279    free(a);
280    free(b);
281    free(c);
282    free(d);
283 }
284 
test_encode_decode(opus_int32 bitrate,opus_int32 channels,const int mapping_family)285 void test_encode_decode(opus_int32 bitrate, opus_int32 channels,
286                         const int mapping_family)
287 {
288   const opus_int32 Fs = 48000;
289   const int application = OPUS_APPLICATION_AUDIO;
290 
291   OpusProjectionEncoder *st_enc;
292   OpusProjectionDecoder *st_dec;
293   int streams;
294   int coupled;
295   int error;
296   short *buffer_in;
297   short *buffer_out;
298   unsigned char data[MAX_DATA_BYTES] = { 0 };
299   int len;
300   int out_samples;
301   opus_int32 matrix_size = 0;
302   unsigned char *matrix = NULL;
303 
304   buffer_in = (short *)malloc(sizeof(short) * BUFFER_SIZE * channels);
305   buffer_out = (short *)malloc(sizeof(short) * BUFFER_SIZE * channels);
306 
307   st_enc = opus_projection_ambisonics_encoder_create(Fs, channels,
308     mapping_family, &streams, &coupled, application, &error);
309   if (error != OPUS_OK) {
310     fprintf(stderr,
311       "Couldn\'t create encoder with %d channels and mapping family %d.\n",
312       channels, mapping_family);
313     free(buffer_in);
314     free(buffer_out);
315     test_failed();
316   }
317 
318   error = opus_projection_encoder_ctl(st_enc,
319     OPUS_SET_BITRATE(bitrate * 1000 * (streams + coupled)));
320   if (error != OPUS_OK)
321   {
322     goto bad_cleanup;
323   }
324 
325   error = opus_projection_encoder_ctl(st_enc,
326     OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST, &matrix_size);
327   if (error != OPUS_OK || !matrix_size)
328   {
329     goto bad_cleanup;
330   }
331 
332   matrix = (unsigned char *)opus_alloc(matrix_size);
333   error = opus_projection_encoder_ctl(st_enc,
334     OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST, matrix, matrix_size);
335 
336   st_dec = opus_projection_decoder_create(Fs, channels, streams, coupled,
337     matrix, matrix_size, &error);
338   opus_free(matrix);
339 
340   if (error != OPUS_OK) {
341     fprintf(stderr,
342       "Couldn\'t create decoder with %d channels, %d streams "
343       "and %d coupled streams.\n", channels, streams, coupled);
344     goto bad_cleanup;
345   }
346 
347   generate_music(buffer_in, BUFFER_SIZE, channels);
348 
349   len = opus_projection_encode(
350     st_enc, buffer_in, BUFFER_SIZE, data, MAX_DATA_BYTES);
351   if(len<0 || len>MAX_DATA_BYTES) {
352     fprintf(stderr,"opus_encode() returned %d\n", len);
353     goto bad_cleanup;
354   }
355 
356   out_samples = opus_projection_decode(
357     st_dec, data, len, buffer_out, MAX_FRAME_SAMPLES, 0);
358   if(out_samples!=BUFFER_SIZE) {
359     fprintf(stderr,"opus_decode() returned %d\n", out_samples);
360     goto bad_cleanup;
361   }
362 
363   opus_projection_decoder_destroy(st_dec);
364   opus_projection_encoder_destroy(st_enc);
365   free(buffer_in);
366   free(buffer_out);
367   return;
368 bad_cleanup:
369   free(buffer_in);
370   free(buffer_out);
371   test_failed();
372 }
373 
main(int _argc,char ** _argv)374 int main(int _argc, char **_argv)
375 {
376   unsigned int i;
377 
378   (void)_argc;
379   (void)_argv;
380 
381   /* Test simple matrix multiplication routines. */
382   test_simple_matrix();
383 
384   /* Test full range of channels in creation arguments. */
385   for (i = 0; i < 255; i++)
386     test_creation_arguments(i, 3);
387 
388   /* Test encode/decode pipeline. */
389   test_encode_decode(64 * 18, 18, 3);
390 
391   fprintf(stderr, "All projection tests passed.\n");
392   return 0;
393 }
394 
395