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