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
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)
191     return 0;
192 
193   encoder_size =
194       opus_multistream_encoder_get_size(nb_streams, nb_coupled_streams);
195   if (!encoder_size)
196     return 0;
197 
198   return align(sizeof(OpusProjectionEncoder)) +
199     mixing_matrix_size + demixing_matrix_size + encoder_size;
200 }
201 
opus_projection_ambisonics_encoder_init(OpusProjectionEncoder * st,opus_int32 Fs,int channels,int mapping_family,int * streams,int * coupled_streams,int application)202 int 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;
210   int i;
211   int ret;
212   int order_plus_one;
213   unsigned char mapping[255];
214 
215   if (streams == NULL || coupled_streams == NULL) {
216     return OPUS_BAD_ARG;
217   }
218 
219   if (get_streams_from_channels(channels, mapping_family, streams,
220     coupled_streams, &order_plus_one) != OPUS_OK)
221     return OPUS_BAD_ARG;
222 
223   if (mapping_family == 3)
224   {
225     /* 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,
231         mapping_matrix_foa_mixing_data,
232         sizeof(mapping_matrix_foa_mixing_data));
233     }
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,
238         mapping_matrix_soa_mixing_data,
239         sizeof(mapping_matrix_soa_mixing_data));
240     }
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,
245         mapping_matrix_toa_mixing_data,
246         sizeof(mapping_matrix_toa_mixing_data));
247     }
248     else
249       return OPUS_BAD_ARG;
250 
251     st->mixing_matrix_size_in_bytes = mapping_matrix_get_size(
252       mixing_matrix->rows, mixing_matrix->cols);
253     if (!st->mixing_matrix_size_in_bytes)
254       return OPUS_BAD_ARG;
255 
256     /* Assign demixing matrix based on available pre-computed matrices. */
257     demixing_matrix = get_enc_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,
262         mapping_matrix_foa_demixing_data,
263         sizeof(mapping_matrix_foa_demixing_data));
264     }
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,
269         mapping_matrix_soa_demixing_data,
270         sizeof(mapping_matrix_soa_demixing_data));
271     }
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,
276         mapping_matrix_toa_demixing_data,
277         sizeof(mapping_matrix_toa_demixing_data));
278     }
279     else
280       return OPUS_BAD_ARG;
281 
282     st->demixing_matrix_size_in_bytes = mapping_matrix_get_size(
283       demixing_matrix->rows, demixing_matrix->cols);
284     if (!st->demixing_matrix_size_in_bytes)
285       return OPUS_BAD_ARG;
286   }
287   else
288     return OPUS_UNIMPLEMENTED;
289 
290   /* Ensure matrices are large enough for desired coding scheme. */
291   if (*streams + *coupled_streams > mixing_matrix->rows ||
292       channels > mixing_matrix->cols ||
293       channels > demixing_matrix->rows ||
294       *streams + *coupled_streams > demixing_matrix->cols)
295     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++)
299     mapping[i] = i;
300 
301   /* Initialize multistream encoder with provided settings. */
302   ms_encoder = get_multistream_encoder(st);
303   ret = opus_multistream_encoder_init(ms_encoder, Fs, channels, *streams,
304                                       *coupled_streams, mapping, application);
305   return ret;
306 }
307 
opus_projection_ambisonics_encoder_create(opus_int32 Fs,int channels,int mapping_family,int * streams,int * coupled_streams,int application,int * error)308 OpusProjectionEncoder *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 
opus_projection_encode(OpusProjectionEncoder * st,const opus_int16 * pcm,int frame_size,unsigned char * data,opus_int32 max_data_bytes)344 int opus_projection_encode(OpusProjectionEncoder *st, const opus_int16 *pcm,
345                            int frame_size, unsigned char *data,
346                            opus_int32 max_data_bytes)
347 {
348   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));
351 }
352 
353 #ifndef DISABLE_FLOAT_API
354 #ifdef FIXED_POINT
opus_projection_encode_float(OpusProjectionEncoder * st,const float * pcm,int frame_size,unsigned char * data,opus_int32 max_data_bytes)355 int opus_projection_encode_float(OpusProjectionEncoder *st, const float *pcm,
356                                  int frame_size, unsigned char *data,
357                                  opus_int32 max_data_bytes)
358 {
359   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));
362 }
363 #else
opus_projection_encode_float(OpusProjectionEncoder * st,const float * pcm,int frame_size,unsigned char * data,opus_int32 max_data_bytes)364 int 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
373 #endif
374 
opus_projection_encoder_destroy(OpusProjectionEncoder * st)375 void opus_projection_encoder_destroy(OpusProjectionEncoder *st)
376 {
377   opus_free(st);
378 }
379 
opus_projection_encoder_ctl(OpusProjectionEncoder * st,int request,...)380 int opus_projection_encoder_ctl(OpusProjectionEncoder *st, int request, ...)
381 {
382   va_list ap;
383   MappingMatrix *demixing_matrix;
384   OpusMSEncoder *ms_encoder;
385   int ret = OPUS_OK;
386 
387   ms_encoder = get_multistream_encoder(st);
388   demixing_matrix = get_enc_demixing_matrix(st);
389 
390   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   {
417     int i, j, k, l;
418     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 *);
431     external_size = va_arg(ap, opus_int32);
432     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. */
444     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       }
452     }
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 
464 bad_arg:
465   va_end(ap);
466   return OPUS_BAD_ARG;
467 }
468 
469