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 "mapping_matrix.h"
39 #include "stack_alloc.h"
40 
41 struct OpusProjectionDecoder
42 {
43   opus_int32 demixing_matrix_size_in_bytes;
44   /* Encoder states go here */
45 };
46 
47 #if !defined(DISABLE_FLOAT_API)
opus_projection_copy_channel_out_float(void * dst,int dst_stride,int dst_channel,const opus_val16 * src,int src_stride,int frame_size,void * user_data)48 static void opus_projection_copy_channel_out_float(
49   void *dst,
50   int dst_stride,
51   int dst_channel,
52   const opus_val16 *src,
53   int src_stride,
54   int frame_size,
55   void *user_data)
56 {
57   float *float_dst;
58   const MappingMatrix *matrix;
59   float_dst = (float *)dst;
60   matrix = (const MappingMatrix *)user_data;
61 
62   if (dst_channel == 0)
63     OPUS_CLEAR(float_dst, frame_size * dst_stride);
64 
65   if (src != NULL)
66     mapping_matrix_multiply_channel_out_float(matrix, src, dst_channel,
67       src_stride, float_dst, dst_stride, frame_size);
68 }
69 #endif
70 
opus_projection_copy_channel_out_short(void * dst,int dst_stride,int dst_channel,const opus_val16 * src,int src_stride,int frame_size,void * user_data)71 static void opus_projection_copy_channel_out_short(
72   void *dst,
73   int dst_stride,
74   int dst_channel,
75   const opus_val16 *src,
76   int src_stride,
77   int frame_size,
78   void *user_data)
79 {
80   opus_int16 *short_dst;
81   const MappingMatrix *matrix;
82   short_dst = (opus_int16 *)dst;
83   matrix = (const MappingMatrix *)user_data;
84   if (dst_channel == 0)
85     OPUS_CLEAR(short_dst, frame_size * dst_stride);
86 
87   if (src != NULL)
88     mapping_matrix_multiply_channel_out_short(matrix, src, dst_channel,
89       src_stride, short_dst, dst_stride, frame_size);
90 }
91 
get_dec_demixing_matrix(OpusProjectionDecoder * st)92 static MappingMatrix *get_dec_demixing_matrix(OpusProjectionDecoder *st)
93 {
94   /* void* cast avoids clang -Wcast-align warning */
95   return (MappingMatrix*)(void*)((char*)st +
96     align(sizeof(OpusProjectionDecoder)));
97 }
98 
get_multistream_decoder(OpusProjectionDecoder * st)99 static OpusMSDecoder *get_multistream_decoder(OpusProjectionDecoder *st)
100 {
101   /* void* cast avoids clang -Wcast-align warning */
102   return (OpusMSDecoder*)(void*)((char*)st +
103     align(sizeof(OpusProjectionDecoder) +
104     st->demixing_matrix_size_in_bytes));
105 }
106 
opus_projection_decoder_get_size(int channels,int streams,int coupled_streams)107 opus_int32 opus_projection_decoder_get_size(int channels, int streams,
108                                             int coupled_streams)
109 {
110   opus_int32 matrix_size;
111   opus_int32 decoder_size;
112 
113   matrix_size =
114     mapping_matrix_get_size(streams + coupled_streams, channels);
115   if (!matrix_size)
116     return 0;
117 
118   decoder_size = opus_multistream_decoder_get_size(streams, coupled_streams);
119   if (!decoder_size)
120     return 0;
121 
122   return align(sizeof(OpusProjectionDecoder)) + matrix_size + decoder_size;
123 }
124 
opus_projection_decoder_init(OpusProjectionDecoder * st,opus_int32 Fs,int channels,int streams,int coupled_streams,unsigned char * demixing_matrix,opus_int32 demixing_matrix_size)125 int opus_projection_decoder_init(OpusProjectionDecoder *st, opus_int32 Fs,
126   int channels, int streams, int coupled_streams,
127   unsigned char *demixing_matrix, opus_int32 demixing_matrix_size)
128 {
129   int nb_input_streams;
130   opus_int32 expected_matrix_size;
131   int i, ret;
132   unsigned char mapping[255];
133   VARDECL(opus_int16, buf);
134   ALLOC_STACK;
135 
136   /* Verify supplied matrix size. */
137   nb_input_streams = streams + coupled_streams;
138   expected_matrix_size = nb_input_streams * channels * sizeof(opus_int16);
139   if (expected_matrix_size != demixing_matrix_size)
140   {
141     RESTORE_STACK;
142     return OPUS_BAD_ARG;
143   }
144 
145   /* Convert demixing matrix input into internal format. */
146   ALLOC(buf, nb_input_streams * channels, opus_int16);
147   for (i = 0; i < nb_input_streams * channels; i++)
148   {
149     int s = demixing_matrix[2*i + 1] << 8 | demixing_matrix[2*i];
150     s = ((s & 0xFFFF) ^ 0x8000) - 0x8000;
151     buf[i] = (opus_int16)s;
152   }
153 
154   /* Assign demixing matrix. */
155   st->demixing_matrix_size_in_bytes =
156     mapping_matrix_get_size(channels, nb_input_streams);
157   if (!st->demixing_matrix_size_in_bytes)
158   {
159     RESTORE_STACK;
160     return OPUS_BAD_ARG;
161   }
162 
163   mapping_matrix_init(get_dec_demixing_matrix(st), channels, nb_input_streams, 0,
164     buf, demixing_matrix_size);
165 
166   /* Set trivial mapping so each input channel pairs with a matrix column. */
167   for (i = 0; i < channels; i++)
168     mapping[i] = i;
169 
170   ret = opus_multistream_decoder_init(
171     get_multistream_decoder(st), Fs, channels, streams, coupled_streams, mapping);
172   RESTORE_STACK;
173   return ret;
174 }
175 
opus_projection_decoder_create(opus_int32 Fs,int channels,int streams,int coupled_streams,unsigned char * demixing_matrix,opus_int32 demixing_matrix_size,int * error)176 OpusProjectionDecoder *opus_projection_decoder_create(
177   opus_int32 Fs, int channels, int streams, int coupled_streams,
178   unsigned char *demixing_matrix, opus_int32 demixing_matrix_size, int *error)
179 {
180   int size;
181   int ret;
182   OpusProjectionDecoder *st;
183 
184   /* Allocate space for the projection decoder. */
185   size = opus_projection_decoder_get_size(channels, streams, coupled_streams);
186   if (!size) {
187     if (error)
188       *error = OPUS_ALLOC_FAIL;
189     return NULL;
190   }
191   st = (OpusProjectionDecoder *)opus_alloc(size);
192   if (!st)
193   {
194     if (error)
195       *error = OPUS_ALLOC_FAIL;
196     return NULL;
197   }
198 
199   /* Initialize projection decoder with provided settings. */
200   ret = opus_projection_decoder_init(st, Fs, channels, streams, coupled_streams,
201                                      demixing_matrix, demixing_matrix_size);
202   if (ret != OPUS_OK)
203   {
204     opus_free(st);
205     st = NULL;
206   }
207   if (error)
208     *error = ret;
209   return st;
210 }
211 
212 #ifdef FIXED_POINT
opus_projection_decode(OpusProjectionDecoder * st,const unsigned char * data,opus_int32 len,opus_int16 * pcm,int frame_size,int decode_fec)213 int opus_projection_decode(OpusProjectionDecoder *st, const unsigned char *data,
214                            opus_int32 len, opus_int16 *pcm, int frame_size,
215                            int decode_fec)
216 {
217   return opus_multistream_decode_native(get_multistream_decoder(st), data, len,
218     pcm, opus_projection_copy_channel_out_short, frame_size, decode_fec, 0,
219     get_dec_demixing_matrix(st));
220 }
221 #else
opus_projection_decode(OpusProjectionDecoder * st,const unsigned char * data,opus_int32 len,opus_int16 * pcm,int frame_size,int decode_fec)222 int opus_projection_decode(OpusProjectionDecoder *st, const unsigned char *data,
223                            opus_int32 len, opus_int16 *pcm, int frame_size,
224                            int decode_fec)
225 {
226   return opus_multistream_decode_native(get_multistream_decoder(st), data, len,
227     pcm, opus_projection_copy_channel_out_short, frame_size, decode_fec, 1,
228     get_dec_demixing_matrix(st));
229 }
230 #endif
231 
232 #ifndef DISABLE_FLOAT_API
opus_projection_decode_float(OpusProjectionDecoder * st,const unsigned char * data,opus_int32 len,float * pcm,int frame_size,int decode_fec)233 int opus_projection_decode_float(OpusProjectionDecoder *st, const unsigned char *data,
234                                  opus_int32 len, float *pcm, int frame_size, int decode_fec)
235 {
236   return opus_multistream_decode_native(get_multistream_decoder(st), data, len,
237     pcm, opus_projection_copy_channel_out_float, frame_size, decode_fec, 0,
238     get_dec_demixing_matrix(st));
239 }
240 #endif
241 
opus_projection_decoder_ctl(OpusProjectionDecoder * st,int request,...)242 int opus_projection_decoder_ctl(OpusProjectionDecoder *st, int request, ...)
243 {
244   va_list ap;
245   int ret = OPUS_OK;
246 
247   va_start(ap, request);
248   ret = opus_multistream_decoder_ctl_va_list(get_multistream_decoder(st),
249     request, ap);
250   va_end(ap);
251   return ret;
252 }
253 
opus_projection_decoder_destroy(OpusProjectionDecoder * st)254 void opus_projection_decoder_destroy(OpusProjectionDecoder *st)
255 {
256   opus_free(st);
257 }
258 
259