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