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