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 "mathops.h"
33 #include "os_support.h"
34 #include "opus_private.h"
35 #include "opus_defines.h"
36 #include "opus_projection.h"
37 #include "opus_multistream.h"
38 #include "stack_alloc.h"
39 #include "mapping_matrix.h"
40
41 struct OpusProjectionEncoder
42 {
43 opus_int32 mixing_matrix_size_in_bytes;
44 opus_int32 demixing_matrix_size_in_bytes;
45 /* Encoder states go here */
46 };
47
48 #if !defined(DISABLE_FLOAT_API)
opus_projection_copy_channel_in_float(opus_val16 * dst,int dst_stride,const void * src,int src_stride,int src_channel,int frame_size,void * user_data)49 static void opus_projection_copy_channel_in_float(
50 opus_val16 *dst,
51 int dst_stride,
52 const void *src,
53 int src_stride,
54 int src_channel,
55 int frame_size,
56 void *user_data
57 )
58 {
59 mapping_matrix_multiply_channel_in_float((const MappingMatrix*)user_data,
60 (const float*)src, src_stride, dst, src_channel, dst_stride, frame_size);
61 }
62 #endif
63
opus_projection_copy_channel_in_short(opus_val16 * dst,int dst_stride,const void * src,int src_stride,int src_channel,int frame_size,void * user_data)64 static void opus_projection_copy_channel_in_short(
65 opus_val16 *dst,
66 int dst_stride,
67 const void *src,
68 int src_stride,
69 int src_channel,
70 int frame_size,
71 void *user_data
72 )
73 {
74 mapping_matrix_multiply_channel_in_short((const MappingMatrix*)user_data,
75 (const opus_int16*)src, src_stride, dst, src_channel, dst_stride, frame_size);
76 }
77
get_order_plus_one_from_channels(int channels,int * order_plus_one)78 static int get_order_plus_one_from_channels(int channels, int *order_plus_one)
79 {
80 int order_plus_one_;
81 int acn_channels;
82 int nondiegetic_channels;
83
84 /* Allowed numbers of channels:
85 * (1 + n)^2 + 2j, for n = 0...14 and j = 0 or 1.
86 */
87 if (channels < 1 || channels > 227)
88 return OPUS_BAD_ARG;
89
90 order_plus_one_ = isqrt32(channels);
91 acn_channels = order_plus_one_ * order_plus_one_;
92 nondiegetic_channels = channels - acn_channels;
93 if (nondiegetic_channels != 0 && nondiegetic_channels != 2)
94 return OPUS_BAD_ARG;
95
96 if (order_plus_one)
97 *order_plus_one = order_plus_one_;
98 return OPUS_OK;
99 }
100
get_streams_from_channels(int channels,int mapping_family,int * streams,int * coupled_streams,int * order_plus_one)101 static int get_streams_from_channels(int channels, int mapping_family,
102 int *streams, int *coupled_streams,
103 int *order_plus_one)
104 {
105 if (mapping_family == 3)
106 {
107 if (get_order_plus_one_from_channels(channels, order_plus_one) != OPUS_OK)
108 return OPUS_BAD_ARG;
109 if (streams)
110 *streams = (channels + 1) / 2;
111 if (coupled_streams)
112 *coupled_streams = channels / 2;
113 return OPUS_OK;
114 }
115 return OPUS_BAD_ARG;
116 }
117
get_mixing_matrix(OpusProjectionEncoder * st)118 static MappingMatrix *get_mixing_matrix(OpusProjectionEncoder *st)
119 {
120 /* void* cast avoids clang -Wcast-align warning */
121 return (MappingMatrix *)(void*)((char*)st +
122 align(sizeof(OpusProjectionEncoder)));
123 }
124
get_enc_demixing_matrix(OpusProjectionEncoder * st)125 static MappingMatrix *get_enc_demixing_matrix(OpusProjectionEncoder *st)
126 {
127 /* void* cast avoids clang -Wcast-align warning */
128 return (MappingMatrix *)(void*)((char*)st +
129 align(sizeof(OpusProjectionEncoder) +
130 st->mixing_matrix_size_in_bytes));
131 }
132
get_multistream_encoder(OpusProjectionEncoder * st)133 static OpusMSEncoder *get_multistream_encoder(OpusProjectionEncoder *st)
134 {
135 /* void* cast avoids clang -Wcast-align warning */
136 return (OpusMSEncoder *)(void*)((char*)st +
137 align(sizeof(OpusProjectionEncoder) +
138 st->mixing_matrix_size_in_bytes +
139 st->demixing_matrix_size_in_bytes));
140 }
141
opus_projection_ambisonics_encoder_get_size(int channels,int mapping_family)142 opus_int32 opus_projection_ambisonics_encoder_get_size(int channels,
143 int mapping_family)
144 {
145 int nb_streams;
146 int nb_coupled_streams;
147 int order_plus_one;
148 int mixing_matrix_rows, mixing_matrix_cols;
149 int demixing_matrix_rows, demixing_matrix_cols;
150 opus_int32 mixing_matrix_size, demixing_matrix_size;
151 opus_int32 encoder_size;
152 int ret;
153
154 ret = get_streams_from_channels(channels, mapping_family, &nb_streams,
155 &nb_coupled_streams, &order_plus_one);
156 if (ret != OPUS_OK)
157 return 0;
158
159 if (order_plus_one == 2)
160 {
161 mixing_matrix_rows = mapping_matrix_foa_mixing.rows;
162 mixing_matrix_cols = mapping_matrix_foa_mixing.cols;
163 demixing_matrix_rows = mapping_matrix_foa_demixing.rows;
164 demixing_matrix_cols = mapping_matrix_foa_demixing.cols;
165 }
166 else if (order_plus_one == 3)
167 {
168 mixing_matrix_rows = mapping_matrix_soa_mixing.rows;
169 mixing_matrix_cols = mapping_matrix_soa_mixing.cols;
170 demixing_matrix_rows = mapping_matrix_soa_demixing.rows;
171 demixing_matrix_cols = mapping_matrix_soa_demixing.cols;
172 }
173 else if (order_plus_one == 4)
174 {
175 mixing_matrix_rows = mapping_matrix_toa_mixing.rows;
176 mixing_matrix_cols = mapping_matrix_toa_mixing.cols;
177 demixing_matrix_rows = mapping_matrix_toa_demixing.rows;
178 demixing_matrix_cols = mapping_matrix_toa_demixing.cols;
179 }
180 else if (order_plus_one == 5)
181 {
182 mixing_matrix_rows = mapping_matrix_fourthoa_mixing.rows;
183 mixing_matrix_cols = mapping_matrix_fourthoa_mixing.cols;
184 demixing_matrix_rows = mapping_matrix_fourthoa_demixing.rows;
185 demixing_matrix_cols = mapping_matrix_fourthoa_demixing.cols;
186 }
187 else if (order_plus_one == 6)
188 {
189 mixing_matrix_rows = mapping_matrix_fifthoa_mixing.rows;
190 mixing_matrix_cols = mapping_matrix_fifthoa_mixing.cols;
191 demixing_matrix_rows = mapping_matrix_fifthoa_demixing.rows;
192 demixing_matrix_cols = mapping_matrix_fifthoa_demixing.cols;
193 }
194 else
195 return 0;
196
197 mixing_matrix_size =
198 mapping_matrix_get_size(mixing_matrix_rows, mixing_matrix_cols);
199 if (!mixing_matrix_size)
200 return 0;
201
202 demixing_matrix_size =
203 mapping_matrix_get_size(demixing_matrix_rows, demixing_matrix_cols);
204 if (!demixing_matrix_size)
205 return 0;
206
207 encoder_size =
208 opus_multistream_encoder_get_size(nb_streams, nb_coupled_streams);
209 if (!encoder_size)
210 return 0;
211
212 return align(sizeof(OpusProjectionEncoder)) +
213 mixing_matrix_size + demixing_matrix_size + encoder_size;
214 }
215
opus_projection_ambisonics_encoder_init(OpusProjectionEncoder * st,opus_int32 Fs,int channels,int mapping_family,int * streams,int * coupled_streams,int application)216 int opus_projection_ambisonics_encoder_init(OpusProjectionEncoder *st, opus_int32 Fs,
217 int channels, int mapping_family,
218 int *streams, int *coupled_streams,
219 int application)
220 {
221 MappingMatrix *mixing_matrix;
222 MappingMatrix *demixing_matrix;
223 OpusMSEncoder *ms_encoder;
224 int i;
225 int ret;
226 int order_plus_one;
227 unsigned char mapping[255];
228
229 if (streams == NULL || coupled_streams == NULL) {
230 return OPUS_BAD_ARG;
231 }
232
233 if (get_streams_from_channels(channels, mapping_family, streams,
234 coupled_streams, &order_plus_one) != OPUS_OK)
235 return OPUS_BAD_ARG;
236
237 if (mapping_family == 3)
238 {
239 /* Assign mixing matrix based on available pre-computed matrices. */
240 mixing_matrix = get_mixing_matrix(st);
241 if (order_plus_one == 2)
242 {
243 mapping_matrix_init(mixing_matrix, mapping_matrix_foa_mixing.rows,
244 mapping_matrix_foa_mixing.cols, mapping_matrix_foa_mixing.gain,
245 mapping_matrix_foa_mixing_data,
246 sizeof(mapping_matrix_foa_mixing_data));
247 }
248 else if (order_plus_one == 3)
249 {
250 mapping_matrix_init(mixing_matrix, mapping_matrix_soa_mixing.rows,
251 mapping_matrix_soa_mixing.cols, mapping_matrix_soa_mixing.gain,
252 mapping_matrix_soa_mixing_data,
253 sizeof(mapping_matrix_soa_mixing_data));
254 }
255 else if (order_plus_one == 4)
256 {
257 mapping_matrix_init(mixing_matrix, mapping_matrix_toa_mixing.rows,
258 mapping_matrix_toa_mixing.cols, mapping_matrix_toa_mixing.gain,
259 mapping_matrix_toa_mixing_data,
260 sizeof(mapping_matrix_toa_mixing_data));
261 }
262 else if (order_plus_one == 5)
263 {
264 mapping_matrix_init(mixing_matrix, mapping_matrix_fourthoa_mixing.rows,
265 mapping_matrix_fourthoa_mixing.cols, mapping_matrix_fourthoa_mixing.gain,
266 mapping_matrix_fourthoa_mixing_data,
267 sizeof(mapping_matrix_fourthoa_mixing_data));
268 }
269 else if (order_plus_one == 6)
270 {
271 mapping_matrix_init(mixing_matrix, mapping_matrix_fifthoa_mixing.rows,
272 mapping_matrix_fifthoa_mixing.cols, mapping_matrix_fifthoa_mixing.gain,
273 mapping_matrix_fifthoa_mixing_data,
274 sizeof(mapping_matrix_fifthoa_mixing_data));
275 }
276 else
277 return OPUS_BAD_ARG;
278
279 st->mixing_matrix_size_in_bytes = mapping_matrix_get_size(
280 mixing_matrix->rows, mixing_matrix->cols);
281 if (!st->mixing_matrix_size_in_bytes)
282 return OPUS_BAD_ARG;
283
284 /* Assign demixing matrix based on available pre-computed matrices. */
285 demixing_matrix = get_enc_demixing_matrix(st);
286 if (order_plus_one == 2)
287 {
288 mapping_matrix_init(demixing_matrix, mapping_matrix_foa_demixing.rows,
289 mapping_matrix_foa_demixing.cols, mapping_matrix_foa_demixing.gain,
290 mapping_matrix_foa_demixing_data,
291 sizeof(mapping_matrix_foa_demixing_data));
292 }
293 else if (order_plus_one == 3)
294 {
295 mapping_matrix_init(demixing_matrix, mapping_matrix_soa_demixing.rows,
296 mapping_matrix_soa_demixing.cols, mapping_matrix_soa_demixing.gain,
297 mapping_matrix_soa_demixing_data,
298 sizeof(mapping_matrix_soa_demixing_data));
299 }
300 else if (order_plus_one == 4)
301 {
302 mapping_matrix_init(demixing_matrix, mapping_matrix_toa_demixing.rows,
303 mapping_matrix_toa_demixing.cols, mapping_matrix_toa_demixing.gain,
304 mapping_matrix_toa_demixing_data,
305 sizeof(mapping_matrix_toa_demixing_data));
306 }
307 else if (order_plus_one == 5)
308 {
309 mapping_matrix_init(demixing_matrix, mapping_matrix_fourthoa_demixing.rows,
310 mapping_matrix_fourthoa_demixing.cols, mapping_matrix_fourthoa_demixing.gain,
311 mapping_matrix_fourthoa_demixing_data,
312 sizeof(mapping_matrix_fourthoa_demixing_data));
313 }
314 else if (order_plus_one == 6)
315 {
316 mapping_matrix_init(demixing_matrix, mapping_matrix_fifthoa_demixing.rows,
317 mapping_matrix_fifthoa_demixing.cols, mapping_matrix_fifthoa_demixing.gain,
318 mapping_matrix_fifthoa_demixing_data,
319 sizeof(mapping_matrix_fifthoa_demixing_data));
320 }
321 else
322 return OPUS_BAD_ARG;
323
324 st->demixing_matrix_size_in_bytes = mapping_matrix_get_size(
325 demixing_matrix->rows, demixing_matrix->cols);
326 if (!st->demixing_matrix_size_in_bytes)
327 return OPUS_BAD_ARG;
328 }
329 else
330 return OPUS_UNIMPLEMENTED;
331
332 /* Ensure matrices are large enough for desired coding scheme. */
333 if (*streams + *coupled_streams > mixing_matrix->rows ||
334 channels > mixing_matrix->cols ||
335 channels > demixing_matrix->rows ||
336 *streams + *coupled_streams > demixing_matrix->cols)
337 return OPUS_BAD_ARG;
338
339 /* Set trivial mapping so each input channel pairs with a matrix column. */
340 for (i = 0; i < channels; i++)
341 mapping[i] = i;
342
343 /* Initialize multistream encoder with provided settings. */
344 ms_encoder = get_multistream_encoder(st);
345 ret = opus_multistream_encoder_init(ms_encoder, Fs, channels, *streams,
346 *coupled_streams, mapping, application);
347 return ret;
348 }
349
opus_projection_ambisonics_encoder_create(opus_int32 Fs,int channels,int mapping_family,int * streams,int * coupled_streams,int application,int * error)350 OpusProjectionEncoder *opus_projection_ambisonics_encoder_create(
351 opus_int32 Fs, int channels, int mapping_family, int *streams,
352 int *coupled_streams, int application, int *error)
353 {
354 int size;
355 int ret;
356 OpusProjectionEncoder *st;
357
358 /* Allocate space for the projection encoder. */
359 size = opus_projection_ambisonics_encoder_get_size(channels, mapping_family);
360 if (!size) {
361 if (error)
362 *error = OPUS_ALLOC_FAIL;
363 return NULL;
364 }
365 st = (OpusProjectionEncoder *)opus_alloc(size);
366 if (!st)
367 {
368 if (error)
369 *error = OPUS_ALLOC_FAIL;
370 return NULL;
371 }
372
373 /* Initialize projection encoder with provided settings. */
374 ret = opus_projection_ambisonics_encoder_init(st, Fs, channels,
375 mapping_family, streams, coupled_streams, application);
376 if (ret != OPUS_OK)
377 {
378 opus_free(st);
379 st = NULL;
380 }
381 if (error)
382 *error = ret;
383 return st;
384 }
385
opus_projection_encode(OpusProjectionEncoder * st,const opus_int16 * pcm,int frame_size,unsigned char * data,opus_int32 max_data_bytes)386 int opus_projection_encode(OpusProjectionEncoder *st, const opus_int16 *pcm,
387 int frame_size, unsigned char *data,
388 opus_int32 max_data_bytes)
389 {
390 return opus_multistream_encode_native(get_multistream_encoder(st),
391 opus_projection_copy_channel_in_short, pcm, frame_size, data,
392 max_data_bytes, 16, downmix_int, 0, get_mixing_matrix(st));
393 }
394
395 #ifndef DISABLE_FLOAT_API
396 #ifdef FIXED_POINT
opus_projection_encode_float(OpusProjectionEncoder * st,const float * pcm,int frame_size,unsigned char * data,opus_int32 max_data_bytes)397 int opus_projection_encode_float(OpusProjectionEncoder *st, const float *pcm,
398 int frame_size, unsigned char *data,
399 opus_int32 max_data_bytes)
400 {
401 return opus_multistream_encode_native(get_multistream_encoder(st),
402 opus_projection_copy_channel_in_float, pcm, frame_size, data,
403 max_data_bytes, 16, downmix_float, 1, get_mixing_matrix(st));
404 }
405 #else
opus_projection_encode_float(OpusProjectionEncoder * st,const float * pcm,int frame_size,unsigned char * data,opus_int32 max_data_bytes)406 int opus_projection_encode_float(OpusProjectionEncoder *st, const float *pcm,
407 int frame_size, unsigned char *data,
408 opus_int32 max_data_bytes)
409 {
410 return opus_multistream_encode_native(get_multistream_encoder(st),
411 opus_projection_copy_channel_in_float, pcm, frame_size, data,
412 max_data_bytes, 24, downmix_float, 1, get_mixing_matrix(st));
413 }
414 #endif
415 #endif
416
opus_projection_encoder_destroy(OpusProjectionEncoder * st)417 void opus_projection_encoder_destroy(OpusProjectionEncoder *st)
418 {
419 opus_free(st);
420 }
421
opus_projection_encoder_ctl(OpusProjectionEncoder * st,int request,...)422 int opus_projection_encoder_ctl(OpusProjectionEncoder *st, int request, ...)
423 {
424 va_list ap;
425 MappingMatrix *demixing_matrix;
426 OpusMSEncoder *ms_encoder;
427 int ret = OPUS_OK;
428
429 ms_encoder = get_multistream_encoder(st);
430 demixing_matrix = get_enc_demixing_matrix(st);
431
432 va_start(ap, request);
433 switch(request)
434 {
435 case OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST:
436 {
437 opus_int32 *value = va_arg(ap, opus_int32*);
438 if (!value)
439 {
440 goto bad_arg;
441 }
442 *value =
443 ms_encoder->layout.nb_channels * (ms_encoder->layout.nb_streams
444 + ms_encoder->layout.nb_coupled_streams) * sizeof(opus_int16);
445 }
446 break;
447 case OPUS_PROJECTION_GET_DEMIXING_MATRIX_GAIN_REQUEST:
448 {
449 opus_int32 *value = va_arg(ap, opus_int32*);
450 if (!value)
451 {
452 goto bad_arg;
453 }
454 *value = demixing_matrix->gain;
455 }
456 break;
457 case OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST:
458 {
459 int i, j, k, l;
460 int nb_input_streams;
461 int nb_output_streams;
462 unsigned char *external_char;
463 opus_int16 *internal_short;
464 opus_int32 external_size;
465 opus_int32 internal_size;
466
467 /* (I/O is in relation to the decoder's perspective). */
468 nb_input_streams = ms_encoder->layout.nb_streams +
469 ms_encoder->layout.nb_coupled_streams;
470 nb_output_streams = ms_encoder->layout.nb_channels;
471
472 external_char = va_arg(ap, unsigned char *);
473 external_size = va_arg(ap, opus_int32);
474 if (!external_char)
475 {
476 goto bad_arg;
477 }
478 internal_short = mapping_matrix_get_data(demixing_matrix);
479 internal_size = nb_input_streams * nb_output_streams * sizeof(opus_int16);
480 if (external_size != internal_size)
481 {
482 goto bad_arg;
483 }
484
485 /* Copy demixing matrix subset to output destination. */
486 l = 0;
487 for (i = 0; i < nb_input_streams; i++) {
488 for (j = 0; j < nb_output_streams; j++) {
489 k = demixing_matrix->rows * i + j;
490 external_char[2*l] = (unsigned char)internal_short[k];
491 external_char[2*l+1] = (unsigned char)(internal_short[k] >> 8);
492 l++;
493 }
494 }
495 }
496 break;
497 default:
498 {
499 ret = opus_multistream_encoder_ctl_va_list(ms_encoder, request, ap);
500 }
501 break;
502 }
503 va_end(ap);
504 return ret;
505
506 bad_arg:
507 va_end(ap);
508 return OPUS_BAD_ARG;
509 }
510
511