blob: ab439c3890dc8aa3b4e9f153fe7b982546440a23 [file] [log] [blame]
Andrew Allenf643c032017-11-07 13:26:23 -08001/* 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
Andrew Allenf643c032017-11-07 13:26:23 -080041struct OpusProjectionEncoder
42{
Andrew Allen65f11d32017-12-04 15:32:18 -080043 opus_int32 mixing_matrix_size_in_bytes;
44 opus_int32 demixing_matrix_size_in_bytes;
Andrew Allenf643c032017-11-07 13:26:23 -080045 /* Encoder states go here */
46};
47
Andrew Allen65f11d32017-12-04 15:32:18 -080048#if !defined(DISABLE_FLOAT_API)
49static 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
64static 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
Andrew Allenf643c032017-11-07 13:26:23 -080078static 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 */
Mark Harris697beca2018-05-27 17:47:28 -070087 if (channels < 1 || channels > 227)
88 return OPUS_BAD_ARG;
89
Andrew Allenf643c032017-11-07 13:26:23 -080090 order_plus_one_ = isqrt32(channels);
91 acn_channels = order_plus_one_ * order_plus_one_;
92 nondiegetic_channels = channels - acn_channels;
Mark Harris697beca2018-05-27 17:47:28 -070093 if (nondiegetic_channels != 0 && nondiegetic_channels != 2)
94 return OPUS_BAD_ARG;
95
Andrew Allenf643c032017-11-07 13:26:23 -080096 if (order_plus_one)
97 *order_plus_one = order_plus_one_;
Andrew Allenf643c032017-11-07 13:26:23 -080098 return OPUS_OK;
99}
100
101static int get_streams_from_channels(int channels, int mapping_family,
102 int *streams, int *coupled_streams,
103 int *order_plus_one)
104{
Jean-Marc Valin6a74b412018-06-15 16:52:14 -0400105 if (mapping_family == 3)
Andrew Allenf643c032017-11-07 13:26:23 -0800106 {
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
118static MappingMatrix *get_mixing_matrix(OpusProjectionEncoder *st)
119{
Mark Harris01b035f2018-07-21 17:55:24 -0700120 /* void* cast avoids clang -Wcast-align warning */
121 return (MappingMatrix *)(void*)((char*)st +
122 align(sizeof(OpusProjectionEncoder)));
Andrew Allenf643c032017-11-07 13:26:23 -0800123}
124
125static MappingMatrix *get_demixing_matrix(OpusProjectionEncoder *st)
126{
Mark Harris01b035f2018-07-21 17:55:24 -0700127 /* void* cast avoids clang -Wcast-align warning */
128 return (MappingMatrix *)(void*)((char*)st +
129 align(sizeof(OpusProjectionEncoder) +
Andrew Allenf643c032017-11-07 13:26:23 -0800130 st->mixing_matrix_size_in_bytes));
131}
132
133static OpusMSEncoder *get_multistream_encoder(OpusProjectionEncoder *st)
134{
Mark Harris01b035f2018-07-21 17:55:24 -0700135 /* 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));
Andrew Allenf643c032017-11-07 13:26:23 -0800140}
141
142opus_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;
Andrew Allen058e8172017-12-05 15:34:43 -0800148 int mixing_matrix_rows, mixing_matrix_cols;
149 int demixing_matrix_rows, demixing_matrix_cols;
150 opus_int32 mixing_matrix_size, demixing_matrix_size;
Andrew Allenf643c032017-11-07 13:26:23 -0800151 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);
Andrew Allen058e8172017-12-05 15:34:43 -0800156 if (ret != OPUS_OK)
Andrew Allenf643c032017-11-07 13:26:23 -0800157 return 0;
Andrew Allenf643c032017-11-07 13:26:23 -0800158
Andrew Allen058e8172017-12-05 15:34:43 -0800159 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
181 return 0;
182
183 mixing_matrix_size =
184 mapping_matrix_get_size(mixing_matrix_rows, mixing_matrix_cols);
185 if (!mixing_matrix_size)
186 return 0;
187
188 demixing_matrix_size =
189 mapping_matrix_get_size(demixing_matrix_rows, demixing_matrix_cols);
190 if (!demixing_matrix_size)
Andrew Allencae44452017-12-04 16:05:57 -0800191 return 0;
192
Andrew Allenf643c032017-11-07 13:26:23 -0800193 encoder_size =
194 opus_multistream_encoder_get_size(nb_streams, nb_coupled_streams);
195 if (!encoder_size)
196 return 0;
Andrew Allen058e8172017-12-05 15:34:43 -0800197
198 return align(sizeof(OpusProjectionEncoder)) +
199 mixing_matrix_size + demixing_matrix_size + encoder_size;
Andrew Allenf643c032017-11-07 13:26:23 -0800200}
201
202int opus_projection_ambisonics_encoder_init(OpusProjectionEncoder *st, opus_int32 Fs,
203 int channels, int mapping_family,
204 int *streams, int *coupled_streams,
205 int application)
206{
207 MappingMatrix *mixing_matrix;
208 MappingMatrix *demixing_matrix;
209 OpusMSEncoder *ms_encoder;
Andrew Allenf643c032017-11-07 13:26:23 -0800210 int i;
211 int ret;
Andrew Allen058e8172017-12-05 15:34:43 -0800212 int order_plus_one;
Andrew Allenf643c032017-11-07 13:26:23 -0800213 unsigned char mapping[255];
214
Andrew Allenf643c032017-11-07 13:26:23 -0800215 if (streams == NULL || coupled_streams == NULL) {
216 return OPUS_BAD_ARG;
217 }
Andrew Allen058e8172017-12-05 15:34:43 -0800218
219 if (get_streams_from_channels(channels, mapping_family, streams,
220 coupled_streams, &order_plus_one) != OPUS_OK)
221 return OPUS_BAD_ARG;
Andrew Allenf643c032017-11-07 13:26:23 -0800222
Jean-Marc Valin6a74b412018-06-15 16:52:14 -0400223 if (mapping_family == 3)
Andrew Allenf643c032017-11-07 13:26:23 -0800224 {
Andrew Allenf643c032017-11-07 13:26:23 -0800225 /* Assign mixing matrix based on available pre-computed matrices. */
226 mixing_matrix = get_mixing_matrix(st);
227 if (order_plus_one == 2)
228 {
229 mapping_matrix_init(mixing_matrix, mapping_matrix_foa_mixing.rows,
230 mapping_matrix_foa_mixing.cols, mapping_matrix_foa_mixing.gain,
Andrew Allen058e8172017-12-05 15:34:43 -0800231 mapping_matrix_foa_mixing_data,
232 sizeof(mapping_matrix_foa_mixing_data));
Andrew Allenf643c032017-11-07 13:26:23 -0800233 }
234 else if (order_plus_one == 3)
235 {
236 mapping_matrix_init(mixing_matrix, mapping_matrix_soa_mixing.rows,
237 mapping_matrix_soa_mixing.cols, mapping_matrix_soa_mixing.gain,
Andrew Allen058e8172017-12-05 15:34:43 -0800238 mapping_matrix_soa_mixing_data,
239 sizeof(mapping_matrix_soa_mixing_data));
Andrew Allenf643c032017-11-07 13:26:23 -0800240 }
241 else if (order_plus_one == 4)
242 {
243 mapping_matrix_init(mixing_matrix, mapping_matrix_toa_mixing.rows,
244 mapping_matrix_toa_mixing.cols, mapping_matrix_toa_mixing.gain,
Andrew Allen058e8172017-12-05 15:34:43 -0800245 mapping_matrix_toa_mixing_data,
246 sizeof(mapping_matrix_toa_mixing_data));
Andrew Allenf643c032017-11-07 13:26:23 -0800247 }
Andrew Allen058e8172017-12-05 15:34:43 -0800248 else
249 return OPUS_BAD_ARG;
250
Andrew Allenf643c032017-11-07 13:26:23 -0800251 st->mixing_matrix_size_in_bytes = mapping_matrix_get_size(
252 mixing_matrix->rows, mixing_matrix->cols);
Andrew Allencae44452017-12-04 16:05:57 -0800253 if (!st->mixing_matrix_size_in_bytes)
254 return OPUS_BAD_ARG;
Andrew Allenf643c032017-11-07 13:26:23 -0800255
256 /* Assign demixing matrix based on available pre-computed matrices. */
257 demixing_matrix = get_demixing_matrix(st);
258 if (order_plus_one == 2)
259 {
260 mapping_matrix_init(demixing_matrix, mapping_matrix_foa_demixing.rows,
261 mapping_matrix_foa_demixing.cols, mapping_matrix_foa_demixing.gain,
Andrew Allen058e8172017-12-05 15:34:43 -0800262 mapping_matrix_foa_demixing_data,
263 sizeof(mapping_matrix_foa_demixing_data));
Andrew Allenf643c032017-11-07 13:26:23 -0800264 }
265 else if (order_plus_one == 3)
266 {
267 mapping_matrix_init(demixing_matrix, mapping_matrix_soa_demixing.rows,
268 mapping_matrix_soa_demixing.cols, mapping_matrix_soa_demixing.gain,
Andrew Allen058e8172017-12-05 15:34:43 -0800269 mapping_matrix_soa_demixing_data,
270 sizeof(mapping_matrix_soa_demixing_data));
Andrew Allenf643c032017-11-07 13:26:23 -0800271 }
272 else if (order_plus_one == 4)
273 {
274 mapping_matrix_init(demixing_matrix, mapping_matrix_toa_demixing.rows,
275 mapping_matrix_toa_demixing.cols, mapping_matrix_toa_demixing.gain,
Andrew Allen058e8172017-12-05 15:34:43 -0800276 mapping_matrix_toa_demixing_data,
277 sizeof(mapping_matrix_toa_demixing_data));
Andrew Allenf643c032017-11-07 13:26:23 -0800278 }
Andrew Allen058e8172017-12-05 15:34:43 -0800279 else
280 return OPUS_BAD_ARG;
281
Andrew Allenf643c032017-11-07 13:26:23 -0800282 st->demixing_matrix_size_in_bytes = mapping_matrix_get_size(
283 demixing_matrix->rows, demixing_matrix->cols);
Andrew Allencae44452017-12-04 16:05:57 -0800284 if (!st->demixing_matrix_size_in_bytes)
285 return OPUS_BAD_ARG;
Andrew Allenf643c032017-11-07 13:26:23 -0800286 }
287 else
288 return OPUS_UNIMPLEMENTED;
289
290 /* Ensure matrices are large enough for desired coding scheme. */
Andrew Allen058e8172017-12-05 15:34:43 -0800291 if (*streams + *coupled_streams > mixing_matrix->rows ||
Andrew Allenf643c032017-11-07 13:26:23 -0800292 channels > mixing_matrix->cols ||
293 channels > demixing_matrix->rows ||
Andrew Allen058e8172017-12-05 15:34:43 -0800294 *streams + *coupled_streams > demixing_matrix->cols)
Andrew Allenf643c032017-11-07 13:26:23 -0800295 return OPUS_BAD_ARG;
296
297 /* Set trivial mapping so each input channel pairs with a matrix column. */
298 for (i = 0; i < channels; i++)
Andrew Allenf643c032017-11-07 13:26:23 -0800299 mapping[i] = i;
Andrew Allenf643c032017-11-07 13:26:23 -0800300
301 /* Initialize multistream encoder with provided settings. */
302 ms_encoder = get_multistream_encoder(st);
Andrew Allen058e8172017-12-05 15:34:43 -0800303 ret = opus_multistream_encoder_init(ms_encoder, Fs, channels, *streams,
304 *coupled_streams, mapping, application);
Andrew Allenf643c032017-11-07 13:26:23 -0800305 return ret;
306}
307
308OpusProjectionEncoder *opus_projection_ambisonics_encoder_create(
309 opus_int32 Fs, int channels, int mapping_family, int *streams,
310 int *coupled_streams, int application, int *error)
311{
312 int size;
313 int ret;
314 OpusProjectionEncoder *st;
315
316 /* Allocate space for the projection encoder. */
317 size = opus_projection_ambisonics_encoder_get_size(channels, mapping_family);
318 if (!size) {
319 if (error)
320 *error = OPUS_ALLOC_FAIL;
321 return NULL;
322 }
323 st = (OpusProjectionEncoder *)opus_alloc(size);
324 if (!st)
325 {
326 if (error)
327 *error = OPUS_ALLOC_FAIL;
328 return NULL;
329 }
330
331 /* Initialize projection encoder with provided settings. */
332 ret = opus_projection_ambisonics_encoder_init(st, Fs, channels,
333 mapping_family, streams, coupled_streams, application);
334 if (ret != OPUS_OK)
335 {
336 opus_free(st);
337 st = NULL;
338 }
339 if (error)
340 *error = ret;
341 return st;
342}
343
344int opus_projection_encode(OpusProjectionEncoder *st, const opus_int16 *pcm,
345 int frame_size, unsigned char *data,
346 opus_int32 max_data_bytes)
347{
Andrew Allen65f11d32017-12-04 15:32:18 -0800348 return opus_multistream_encode_native(get_multistream_encoder(st),
349 opus_projection_copy_channel_in_short, pcm, frame_size, data,
350 max_data_bytes, 16, downmix_int, 0, get_mixing_matrix(st));
Andrew Allenf643c032017-11-07 13:26:23 -0800351}
352
353#ifndef DISABLE_FLOAT_API
Andrew Allen65f11d32017-12-04 15:32:18 -0800354#ifdef FIXED_POINT
Andrew Allenf643c032017-11-07 13:26:23 -0800355int opus_projection_encode_float(OpusProjectionEncoder *st, const float *pcm,
356 int frame_size, unsigned char *data,
357 opus_int32 max_data_bytes)
358{
Andrew Allen65f11d32017-12-04 15:32:18 -0800359 return opus_multistream_encode_native(get_multistream_encoder(st),
360 opus_projection_copy_channel_in_float, pcm, frame_size, data,
361 max_data_bytes, 16, downmix_float, 1, get_mixing_matrix(st));
Andrew Allenf643c032017-11-07 13:26:23 -0800362}
Andrew Allen65f11d32017-12-04 15:32:18 -0800363#else
364int opus_projection_encode_float(OpusProjectionEncoder *st, const float *pcm,
365 int frame_size, unsigned char *data,
366 opus_int32 max_data_bytes)
367{
368 return opus_multistream_encode_native(get_multistream_encoder(st),
369 opus_projection_copy_channel_in_float, pcm, frame_size, data,
370 max_data_bytes, 24, downmix_float, 1, get_mixing_matrix(st));
371}
372#endif
Andrew Allenf643c032017-11-07 13:26:23 -0800373#endif
374
375void opus_projection_encoder_destroy(OpusProjectionEncoder *st)
376{
377 opus_free(st);
378}
379
380int opus_projection_encoder_ctl(OpusProjectionEncoder *st, int request, ...)
381{
Jean-Marc Valin1f0c1b12018-07-26 20:13:55 -0400382 va_list ap;
Andrew Allenf643c032017-11-07 13:26:23 -0800383 MappingMatrix *demixing_matrix;
384 OpusMSEncoder *ms_encoder;
385 int ret = OPUS_OK;
386
387 ms_encoder = get_multistream_encoder(st);
388 demixing_matrix = get_demixing_matrix(st);
389
Andrew Allenf643c032017-11-07 13:26:23 -0800390 va_start(ap, request);
391 switch(request)
392 {
393 case OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST:
394 {
395 opus_int32 *value = va_arg(ap, opus_int32*);
396 if (!value)
397 {
398 goto bad_arg;
399 }
400 *value =
401 ms_encoder->layout.nb_channels * (ms_encoder->layout.nb_streams
402 + ms_encoder->layout.nb_coupled_streams) * sizeof(opus_int16);
403 }
404 break;
405 case OPUS_PROJECTION_GET_DEMIXING_MATRIX_GAIN_REQUEST:
406 {
407 opus_int32 *value = va_arg(ap, opus_int32*);
408 if (!value)
409 {
410 goto bad_arg;
411 }
412 *value = demixing_matrix->gain;
413 }
414 break;
415 case OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST:
416 {
Andrew Allenff838432017-11-07 13:26:23 -0800417 int i, j, k, l;
Andrew Allenf643c032017-11-07 13:26:23 -0800418 int nb_input_streams;
419 int nb_output_streams;
420 unsigned char *external_char;
421 opus_int16 *internal_short;
422 opus_int32 external_size;
423 opus_int32 internal_size;
424
425 /* (I/O is in relation to the decoder's perspective). */
426 nb_input_streams = ms_encoder->layout.nb_streams +
427 ms_encoder->layout.nb_coupled_streams;
428 nb_output_streams = ms_encoder->layout.nb_channels;
429
430 external_char = va_arg(ap, unsigned char *);
Andrew Allen058e8172017-12-05 15:34:43 -0800431 external_size = va_arg(ap, opus_int32);
Andrew Allenf643c032017-11-07 13:26:23 -0800432 if (!external_char)
433 {
434 goto bad_arg;
435 }
436 internal_short = mapping_matrix_get_data(demixing_matrix);
437 internal_size = nb_input_streams * nb_output_streams * sizeof(opus_int16);
438 if (external_size != internal_size)
439 {
440 goto bad_arg;
441 }
442
443 /* Copy demixing matrix subset to output destination. */
Andrew Allenff838432017-11-07 13:26:23 -0800444 l = 0;
445 for (i = 0; i < nb_input_streams; i++) {
446 for (j = 0; j < nb_output_streams; j++) {
447 k = demixing_matrix->rows * i + j;
448 external_char[2*l] = (unsigned char)internal_short[k];
449 external_char[2*l+1] = (unsigned char)(internal_short[k] >> 8);
450 l++;
451 }
Andrew Allenf643c032017-11-07 13:26:23 -0800452 }
453 }
454 break;
455 default:
456 {
457 ret = opus_multistream_encoder_ctl_va_list(ms_encoder, request, ap);
458 }
459 break;
460 }
461 va_end(ap);
462 return ret;
463
464bad_arg:
465 va_end(ap);
466 return OPUS_BAD_ARG;
467}
468