xref: /aosp_15_r20/external/libopus/src/opus_projection_encoder.c (revision a58d3d2adb790c104798cd88c8a3aff4fa8b82cc)
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