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