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