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