Line data Source code
1 : /*
2 : * Copyright © 2016 Mozilla Foundation
3 : *
4 : * This program is made available under an ISC-style license. See the
5 : * accompanying file LICENSE for details.
6 : */
7 :
8 : #include <cassert>
9 : #include "cubeb-internal.h"
10 : #include "cubeb_mixer.h"
11 :
12 : // DUAL_MONO(_LFE) is same as STEREO(_LFE).
13 : #define MASK_MONO (1 << CHANNEL_MONO)
14 : #define MASK_MONO_LFE (MASK_MONO | (1 << CHANNEL_LFE))
15 : #define MASK_STEREO ((1 << CHANNEL_LEFT) | (1 << CHANNEL_RIGHT))
16 : #define MASK_STEREO_LFE (MASK_STEREO | (1 << CHANNEL_LFE))
17 : #define MASK_3F (MASK_STEREO | (1 << CHANNEL_CENTER))
18 : #define MASK_3F_LFE (MASK_3F | (1 << CHANNEL_LFE))
19 : #define MASK_2F1 (MASK_STEREO | (1 << CHANNEL_RCENTER))
20 : #define MASK_2F1_LFE (MASK_2F1 | (1 << CHANNEL_LFE))
21 : #define MASK_3F1 (MASK_3F | (1 << CHANNEL_RCENTER))
22 : #define MASK_3F1_LFE (MASK_3F1 | (1 << CHANNEL_LFE))
23 : #define MASK_2F2 (MASK_STEREO | (1 << CHANNEL_LS) | (1 << CHANNEL_RS))
24 : #define MASK_2F2_LFE (MASK_2F2 | (1 << CHANNEL_LFE))
25 : #define MASK_3F2 (MASK_2F2 | (1 << CHANNEL_CENTER))
26 : #define MASK_3F2_LFE (MASK_3F2 | (1 << CHANNEL_LFE))
27 : #define MASK_3F3R_LFE (MASK_3F2_LFE | (1 << CHANNEL_RCENTER))
28 : #define MASK_3F4_LFE (MASK_3F2_LFE | (1 << CHANNEL_RLS) | (1 << CHANNEL_RRS))
29 :
30 0 : cubeb_channel_layout cubeb_channel_map_to_layout(cubeb_channel_map const * channel_map)
31 : {
32 0 : uint32_t channel_mask = 0;
33 0 : for (uint8_t i = 0 ; i < channel_map->channels ; ++i) {
34 0 : if (channel_map->map[i] == CHANNEL_INVALID ||
35 0 : channel_map->map[i] == CHANNEL_UNMAPPED) {
36 0 : return CUBEB_LAYOUT_UNDEFINED;
37 : }
38 0 : channel_mask |= 1 << channel_map->map[i];
39 : }
40 :
41 0 : switch(channel_mask) {
42 0 : case MASK_MONO: return CUBEB_LAYOUT_MONO;
43 0 : case MASK_MONO_LFE: return CUBEB_LAYOUT_MONO_LFE;
44 0 : case MASK_STEREO: return CUBEB_LAYOUT_STEREO;
45 0 : case MASK_STEREO_LFE: return CUBEB_LAYOUT_STEREO_LFE;
46 0 : case MASK_3F: return CUBEB_LAYOUT_3F;
47 0 : case MASK_3F_LFE: return CUBEB_LAYOUT_3F_LFE;
48 0 : case MASK_2F1: return CUBEB_LAYOUT_2F1;
49 0 : case MASK_2F1_LFE: return CUBEB_LAYOUT_2F1_LFE;
50 0 : case MASK_3F1: return CUBEB_LAYOUT_3F1;
51 0 : case MASK_3F1_LFE: return CUBEB_LAYOUT_3F1_LFE;
52 0 : case MASK_2F2: return CUBEB_LAYOUT_2F2;
53 0 : case MASK_2F2_LFE: return CUBEB_LAYOUT_2F2_LFE;
54 0 : case MASK_3F2: return CUBEB_LAYOUT_3F2;
55 0 : case MASK_3F2_LFE: return CUBEB_LAYOUT_3F2_LFE;
56 0 : case MASK_3F3R_LFE: return CUBEB_LAYOUT_3F3R_LFE;
57 0 : case MASK_3F4_LFE: return CUBEB_LAYOUT_3F4_LFE;
58 0 : default: return CUBEB_LAYOUT_UNDEFINED;
59 : }
60 : }
61 :
62 : cubeb_layout_map const CUBEB_CHANNEL_LAYOUT_MAPS[CUBEB_LAYOUT_MAX] = {
63 : { "undefined", 0, CUBEB_LAYOUT_UNDEFINED },
64 : { "dual mono", 2, CUBEB_LAYOUT_DUAL_MONO },
65 : { "dual mono lfe", 3, CUBEB_LAYOUT_DUAL_MONO_LFE },
66 : { "mono", 1, CUBEB_LAYOUT_MONO },
67 : { "mono lfe", 2, CUBEB_LAYOUT_MONO_LFE },
68 : { "stereo", 2, CUBEB_LAYOUT_STEREO },
69 : { "stereo lfe", 3, CUBEB_LAYOUT_STEREO_LFE },
70 : { "3f", 3, CUBEB_LAYOUT_3F },
71 : { "3f lfe", 4, CUBEB_LAYOUT_3F_LFE },
72 : { "2f1", 3, CUBEB_LAYOUT_2F1 },
73 : { "2f1 lfe", 4, CUBEB_LAYOUT_2F1_LFE },
74 : { "3f1", 4, CUBEB_LAYOUT_3F1 },
75 : { "3f1 lfe", 5, CUBEB_LAYOUT_3F1_LFE },
76 : { "2f2", 4, CUBEB_LAYOUT_2F2 },
77 : { "2f2 lfe", 5, CUBEB_LAYOUT_2F2_LFE },
78 : { "3f2", 5, CUBEB_LAYOUT_3F2 },
79 : { "3f2 lfe", 6, CUBEB_LAYOUT_3F2_LFE },
80 : { "3f3r lfe", 7, CUBEB_LAYOUT_3F3R_LFE },
81 : { "3f4 lfe", 8, CUBEB_LAYOUT_3F4_LFE }
82 : };
83 :
84 : static int const CHANNEL_ORDER_TO_INDEX[CUBEB_LAYOUT_MAX][CHANNEL_MAX] = {
85 : // M | L | R | C | LS | RS | RLS | RC | RRS | LFE
86 : { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, // UNDEFINED
87 : { -1, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, // DUAL_MONO
88 : { -1, 0, 1, -1, -1, -1, -1, -1, -1, 2 }, // DUAL_MONO_LFE
89 : { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, // MONO
90 : { 0, -1, -1, -1, -1, -1, -1, -1, -1, 1 }, // MONO_LFE
91 : { -1, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, // STEREO
92 : { -1, 0, 1, -1, -1, -1, -1, -1, -1, 2 }, // STEREO_LFE
93 : { -1, 0, 1, 2, -1, -1, -1, -1, -1, -1 }, // 3F
94 : { -1, 0, 1, 2, -1, -1, -1, -1, -1, 3 }, // 3F_LFE
95 : { -1, 0, 1, -1, -1, -1, -1, 2, -1, -1 }, // 2F1
96 : { -1, 0, 1, -1, -1, -1, -1, 3, -1, 2 }, // 2F1_LFE
97 : { -1, 0, 1, 2, -1, -1, -1, 3, -1, -1 }, // 3F1
98 : { -1, 0, 1, 2, -1, -1, -1, 4, -1, 3 }, // 3F1_LFE
99 : { -1, 0, 1, -1, 2, 3, -1, -1, -1, -1 }, // 2F2
100 : { -1, 0, 1, -1, 3, 4, -1, -1, -1, 2 }, // 2F2_LFE
101 : { -1, 0, 1, 2, 3, 4, -1, -1, -1, -1 }, // 3F2
102 : { -1, 0, 1, 2, 4, 5, -1, -1, -1, 3 }, // 3F2_LFE
103 : { -1, 0, 1, 2, 5, 6, -1, 4, -1, 3 }, // 3F3R_LFE
104 : { -1, 0, 1, 2, 6, 7, 4, -1, 5, 3 }, // 3F4_LFE
105 : };
106 :
107 : // The downmix matrix from TABLE 2 in the ITU-R BS.775-3[1] defines a way to
108 : // convert 3F2 input data to 1F, 2F, 3F, 2F1, 3F1, 2F2 output data. We extend it
109 : // to convert 3F2-LFE input data to 1F, 2F, 3F, 2F1, 3F1, 2F2 and their LFEs
110 : // output data.
111 : // [1] https://www.itu.int/dms_pubrec/itu-r/rec/bs/R-REC-BS.775-3-201208-I!!PDF-E.pdf
112 :
113 : // Number of converted layouts: 1F, 2F, 3F, 2F1, 3F1, 2F2 and their LFEs.
114 : unsigned int const SUPPORTED_LAYOUT_NUM = 12;
115 : // Number of input channel for downmix conversion.
116 : unsigned int const INPUT_CHANNEL_NUM = 6; // 3F2-LFE
117 : // Max number of possible output channels.
118 : unsigned int const MAX_OUTPUT_CHANNEL_NUM = 5; // 2F2-LFE or 3F1-LFE
119 : float const INV_SQRT_2 = 0.707106f; // 1/sqrt(2)
120 : // Each array contains coefficients that will be multiplied with
121 : // { L, R, C, LFE, LS, RS } channels respectively.
122 : static float const DOWNMIX_MATRIX_3F2_LFE[SUPPORTED_LAYOUT_NUM][MAX_OUTPUT_CHANNEL_NUM][INPUT_CHANNEL_NUM] =
123 : {
124 : // 1F Mono
125 : {
126 : { INV_SQRT_2, INV_SQRT_2, 1, 0, 0.5, 0.5 }, // M
127 : },
128 : // 1F Mono-LFE
129 : {
130 : { INV_SQRT_2, INV_SQRT_2, 1, 0, 0.5, 0.5 }, // M
131 : { 0, 0, 0, 1, 0, 0 } // LFE
132 : },
133 : // 2F Stereo
134 : {
135 : { 1, 0, INV_SQRT_2, 0, INV_SQRT_2, 0 }, // L
136 : { 0, 1, INV_SQRT_2, 0, 0, INV_SQRT_2 } // R
137 : },
138 : // 2F Stereo-LFE
139 : {
140 : { 1, 0, INV_SQRT_2, 0, INV_SQRT_2, 0 }, // L
141 : { 0, 1, INV_SQRT_2, 0, 0, INV_SQRT_2 }, // R
142 : { 0, 0, 0, 1, 0, 0 } // LFE
143 : },
144 : // 3F
145 : {
146 : { 1, 0, 0, 0, INV_SQRT_2, 0 }, // L
147 : { 0, 1, 0, 0, 0, INV_SQRT_2 }, // R
148 : { 0, 0, 1, 0, 0, 0 } // C
149 : },
150 : // 3F-LFE
151 : {
152 : { 1, 0, 0, 0, INV_SQRT_2, 0 }, // L
153 : { 0, 1, 0, 0, 0, INV_SQRT_2 }, // R
154 : { 0, 0, 1, 0, 0, 0 }, // C
155 : { 0, 0, 0, 1, 0, 0 } // LFE
156 : },
157 : // 2F1
158 : {
159 : { 1, 0, INV_SQRT_2, 0, 0, 0 }, // L
160 : { 0, 1, INV_SQRT_2, 0, 0, 0 }, // R
161 : { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 } // S
162 : },
163 : // 2F1-LFE
164 : {
165 : { 1, 0, INV_SQRT_2, 0, 0, 0 }, // L
166 : { 0, 1, INV_SQRT_2, 0, 0, 0 }, // R
167 : { 0, 0, 0, 1, 0, 0 }, // LFE
168 : { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 } // S
169 : },
170 : // 3F1
171 : {
172 : { 1, 0, 0, 0, 0, 0 }, // L
173 : { 0, 1, 0, 0, 0, 0 }, // R
174 : { 0, 0, 1, 0, 0, 0 }, // C
175 : { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 } // S
176 : },
177 : // 3F1-LFE
178 : {
179 : { 1, 0, 0, 0, 0, 0 }, // L
180 : { 0, 1, 0, 0, 0, 0 }, // R
181 : { 0, 0, 1, 0, 0, 0 }, // C
182 : { 0, 0, 0, 1, 0, 0 }, // LFE
183 : { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 } // S
184 : },
185 : // 2F2
186 : {
187 : { 1, 0, INV_SQRT_2, 0, 0, 0 }, // L
188 : { 0, 1, INV_SQRT_2, 0, 0, 0 }, // R
189 : { 0, 0, 0, 0, 1, 0 }, // LS
190 : { 0, 0, 0, 0, 0, 1 } // RS
191 : },
192 : // 2F2-LFE
193 : {
194 : { 1, 0, INV_SQRT_2, 0, 0, 0 }, // L
195 : { 0, 1, INV_SQRT_2, 0, 0, 0 }, // R
196 : { 0, 0, 0, 1, 0, 0 }, // LFE
197 : { 0, 0, 0, 0, 1, 0 }, // LS
198 : { 0, 0, 0, 0, 0, 1 } // RS
199 : }
200 : };
201 :
202 : // Convert audio data from 3F2(-LFE) to 1F, 2F, 3F, 2F1, 3F1, 2F2 and their LFEs.
203 : //
204 : // ITU-R BS.775-3[1] provides spec for downmixing 3F2 data to 1F, 2F, 3F, 2F1,
205 : // 3F1, 2F2 data. We simply add LFE to its defined matrix. If both the input
206 : // and output have LFE channel, then we pass it's data. If only input or output
207 : // has LFE, then we either drop it or append 0 to the LFE channel.
208 : //
209 : // Fig. 1:
210 : // |<-------------- 1 -------------->|<-------------- 2 -------------->|
211 : // +----+----+----+------+-----+-----+----+----+----+------+-----+-----+
212 : // | L0 | R0 | C0 | LFE0 | LS0 | RS0 | L1 | R1 | C1 | LFE1 | LS1 | RS1 | ...
213 : // +----+----+----+------+-----+-----+----+----+----+------+-----+-----+
214 : //
215 : // Fig. 2:
216 : // |<-- 1 -->|<-- 2 -->|
217 : // +----+----+----+----+
218 : // | L0 | R0 | L1 | R1 | ...
219 : // +----+----+----+----+
220 : //
221 : // The figures above shows an example for downmixing from 3F2-LFE(Fig. 1) to
222 : // to stereo(Fig. 2), where L0 = L0 + 0.707 * (C0 + LS0),
223 : // R0 = R0 + 0.707 * (C0 + RS0), L1 = L1 + 0.707 * (C1 + LS1),
224 : // R1 = R1 + 0.707 * (C1 + RS1), ...
225 : //
226 : // Nevertheless, the downmixing method is a little bit different on OSX.
227 : // The audio rendering mechanism on OS X will drop the extra channels beyond
228 : // the channels that audio device can provide. The trick here is that OSX allows
229 : // us to set the layout containing other channels that the output device can
230 : // NOT provide. For example, setting 3F2-LFE layout to a stereo device is fine.
231 : // Therefore, OSX expects we fill the buffer for playing sound by the defined
232 : // layout, so there are some will-be-dropped data in the buffer:
233 : //
234 : // +---+---+---+-----+----+----+
235 : // | L | R | C | LFE | LS | RS | ...
236 : // +---+---+---+-----+----+----+
237 : // ^ ^ ^ ^
238 : // The data for these four channels will be dropped!
239 : //
240 : // To keep all the information, we need to downmix the data before it's dropped.
241 : // The figure below shows an example for downmixing from 3F2-LFE(Fig. 1)
242 : // to stereo(Fig. 3) on OSX, where the LO, R0, L1, R0 are same as above.
243 : //
244 : // Fig. 3:
245 : // |<---------- 1 ---------->|<---------- 2 ---------->|
246 : // +----+----+---+---+---+---+----+----+---+---+---+---+
247 : // | L0 | R0 | x | x | x | x | L1 | R1 | x | x | x | x | ...
248 : // +----+----+---+---+---+---+----+----+---+---+---+---+
249 : // |<-- dummy -->| |<-- dummy -->|
250 : template<typename T>
251 : bool
252 0 : downmix_3f2(unsigned long inframes,
253 : T const * const in, unsigned long in_len,
254 : T * out, unsigned long out_len,
255 : cubeb_channel_layout in_layout, cubeb_channel_layout out_layout)
256 : {
257 0 : if ((in_layout != CUBEB_LAYOUT_3F2 && in_layout != CUBEB_LAYOUT_3F2_LFE) ||
258 0 : out_layout < CUBEB_LAYOUT_MONO || out_layout > CUBEB_LAYOUT_2F2_LFE) {
259 0 : return false;
260 : }
261 :
262 0 : unsigned int in_channels = CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels;
263 0 : unsigned int out_channels = CUBEB_CHANNEL_LAYOUT_MAPS[out_layout].channels;
264 :
265 : // Conversion from 3F2 to 2F2-LFE or 3F1-LFE is allowed, so we use '<=' instead of '<'.
266 0 : assert(out_channels <= in_channels);
267 :
268 0 : auto & downmix_matrix = DOWNMIX_MATRIX_3F2_LFE[out_layout - CUBEB_LAYOUT_MONO]; // The matrix is started from mono.
269 0 : unsigned long out_index = 0;
270 0 : for (unsigned long i = 0 ; i < inframes * in_channels; i += in_channels) {
271 0 : for (unsigned int j = 0; j < out_channels; ++j) {
272 0 : T sample = 0;
273 0 : for (unsigned int k = 0 ; k < INPUT_CHANNEL_NUM ; ++k) {
274 : // 3F2-LFE has 6 channels: L, R, C, LFE, LS, RS, while 3F2 has only 5
275 : // channels: L, R, C, LS, RS. Thus, we need to append 0 to LFE(index 3)
276 : // to simulate a 3F2-LFE data when input layout is 3F2.
277 0 : assert((in_layout == CUBEB_LAYOUT_3F2_LFE || k < 3) ? (i + k < in_len) : (k == 3) ? true : (i + k - 1 < in_len));
278 0 : T data = (in_layout == CUBEB_LAYOUT_3F2_LFE) ? in[i + k] : (k == 3) ? 0 : in[i + ((k < 3) ? k : k - 1)];
279 0 : sample += downmix_matrix[j][k] * data;
280 : }
281 0 : assert(out_index + j < out_len);
282 0 : out[out_index + j] = sample;
283 : }
284 : #if defined(USE_AUDIOUNIT)
285 : out_index += in_channels;
286 : #else
287 0 : out_index += out_channels;
288 : #endif
289 : }
290 :
291 0 : return true;
292 : }
293 :
294 : /* Map the audio data by channel name. */
295 : template<class T>
296 : bool
297 0 : mix_remap(long inframes,
298 : T const * const in, unsigned long in_len,
299 : T * out, unsigned long out_len,
300 : cubeb_channel_layout in_layout, cubeb_channel_layout out_layout)
301 : {
302 0 : assert(in_layout != out_layout);
303 :
304 : // We might overwrite the data before we copied them to the mapped index
305 : // (e.g. upmixing from stereo to 2F1 or mapping [L, R] to [R, L])
306 0 : if (in == out) {
307 0 : return false;
308 : }
309 :
310 0 : unsigned int in_channels = CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels;
311 0 : unsigned int out_channels = CUBEB_CHANNEL_LAYOUT_MAPS[out_layout].channels;
312 :
313 0 : uint32_t in_layout_mask = 0;
314 0 : for (unsigned int i = 0 ; i < in_channels ; ++i) {
315 0 : in_layout_mask |= 1 << CHANNEL_INDEX_TO_ORDER[in_layout][i];
316 : }
317 :
318 0 : uint32_t out_layout_mask = 0;
319 0 : for (unsigned int i = 0 ; i < out_channels ; ++i) {
320 0 : out_layout_mask |= 1 << CHANNEL_INDEX_TO_ORDER[out_layout][i];
321 : }
322 :
323 : // If there is no matched channel, then do nothing.
324 0 : if (!(out_layout_mask & in_layout_mask)) {
325 0 : return false;
326 : }
327 :
328 0 : for (long i = 0, out_index = 0; i < inframes * in_channels; i += in_channels, out_index += out_channels) {
329 0 : for (unsigned int j = 0; j < out_channels; ++j) {
330 0 : cubeb_channel channel = CHANNEL_INDEX_TO_ORDER[out_layout][j];
331 0 : uint32_t channel_mask = 1 << channel;
332 0 : int channel_index = CHANNEL_ORDER_TO_INDEX[in_layout][channel];
333 0 : assert((unsigned long)out_index + j < out_len);
334 0 : if (in_layout_mask & channel_mask) {
335 0 : assert((unsigned long)i + channel_index < in_len);
336 0 : assert(channel_index != -1);
337 0 : out[out_index + j] = in[i + channel_index];
338 : } else {
339 0 : assert(channel_index == -1);
340 0 : out[out_index + j] = 0;
341 : }
342 : }
343 : }
344 :
345 0 : return true;
346 : }
347 :
348 : /* Drop the extra channels beyond the provided output channels. */
349 : template<typename T>
350 : void
351 0 : downmix_fallback(long inframes,
352 : T const * const in, unsigned long in_len,
353 : T * out, unsigned long out_len,
354 : unsigned int in_channels, unsigned int out_channels)
355 : {
356 0 : assert(in_channels >= out_channels);
357 :
358 0 : if (in_channels == out_channels && in == out) {
359 0 : return;
360 : }
361 :
362 0 : for (long i = 0, out_index = 0; i < inframes * in_channels; i += in_channels, out_index += out_channels) {
363 0 : for (unsigned int j = 0; j < out_channels; ++j) {
364 0 : assert((unsigned long)i + j < in_len && (unsigned long)out_index + j < out_len);
365 0 : out[out_index + j] = in[i + j];
366 : }
367 : }
368 : }
369 :
370 :
371 : template<typename T>
372 : void
373 0 : cubeb_downmix(long inframes,
374 : T const * const in, unsigned long in_len,
375 : T * out, unsigned long out_len,
376 : cubeb_stream_params const * stream_params,
377 : cubeb_stream_params const * mixer_params)
378 : {
379 0 : assert(in && out);
380 0 : assert(inframes);
381 0 : assert(stream_params->channels >= mixer_params->channels &&
382 : mixer_params->channels > 0);
383 0 : assert(stream_params->layout != CUBEB_LAYOUT_UNDEFINED);
384 :
385 0 : unsigned int in_channels = stream_params->channels;
386 0 : cubeb_channel_layout in_layout = stream_params->layout;
387 :
388 0 : unsigned int out_channels = mixer_params->channels;
389 0 : cubeb_channel_layout out_layout = mixer_params->layout;
390 :
391 : // If the channel number is different from the layout's setting,
392 : // then we use fallback downmix mechanism.
393 0 : if (out_channels == CUBEB_CHANNEL_LAYOUT_MAPS[out_layout].channels &&
394 0 : in_channels == CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels) {
395 0 : if (downmix_3f2(inframes, in, in_len, out, out_len, in_layout, out_layout)) {
396 0 : return;
397 : }
398 :
399 : #if defined(USE_AUDIOUNIT)
400 : // We only support downmix for audio 5.1 on OS X currently.
401 : return;
402 : #endif
403 :
404 0 : if (mix_remap(inframes, in, in_len, out, out_len, in_layout, out_layout)) {
405 0 : return;
406 : }
407 : }
408 :
409 0 : downmix_fallback(inframes, in, in_len, out, out_len, in_channels, out_channels);
410 : }
411 :
412 : /* Upmix function, copies a mono channel into L and R. */
413 : template<typename T>
414 : void
415 0 : mono_to_stereo(long insamples, T const * in, unsigned long in_len,
416 : T * out, unsigned long out_len, unsigned int out_channels)
417 : {
418 0 : for (long i = 0, j = 0; i < insamples; ++i, j += out_channels) {
419 0 : assert((unsigned long)i < in_len && (unsigned long)j + 1 < out_len);
420 0 : out[j] = out[j + 1] = in[i];
421 : }
422 0 : }
423 :
424 : template<typename T>
425 : void
426 0 : cubeb_upmix(long inframes,
427 : T const * const in, unsigned long in_len,
428 : T * out, unsigned long out_len,
429 : cubeb_stream_params const * stream_params,
430 : cubeb_stream_params const * mixer_params)
431 : {
432 0 : assert(in && out);
433 0 : assert(inframes);
434 0 : assert(mixer_params->channels >= stream_params->channels &&
435 : stream_params->channels > 0);
436 :
437 0 : unsigned int in_channels = stream_params->channels;
438 0 : unsigned int out_channels = mixer_params->channels;
439 :
440 : /* Either way, if we have 2 or more channels, the first two are L and R. */
441 : /* If we are playing a mono stream over stereo speakers, copy the data over. */
442 0 : if (in_channels == 1 && out_channels >= 2) {
443 0 : mono_to_stereo(inframes, in, in_len, out, out_len, out_channels);
444 : } else {
445 : /* Copy through. */
446 0 : for (unsigned int i = 0, o = 0; i < inframes * in_channels;
447 : i += in_channels, o += out_channels) {
448 0 : for (unsigned int j = 0; j < in_channels; ++j) {
449 0 : assert(i + j < in_len && o + j < out_len);
450 0 : out[o + j] = in[i + j];
451 : }
452 : }
453 : }
454 :
455 : /* Check if more channels. */
456 0 : if (out_channels <= 2) {
457 0 : return;
458 : }
459 :
460 : /* Put silence in remaining channels. */
461 0 : for (long i = 0, o = 0; i < inframes; ++i, o += out_channels) {
462 0 : for (unsigned int j = 2; j < out_channels; ++j) {
463 0 : assert((unsigned long)o + j < out_len);
464 0 : out[o + j] = 0.0;
465 : }
466 : }
467 : }
468 :
469 : bool
470 0 : cubeb_should_upmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
471 : {
472 0 : return mixer->channels > stream->channels;
473 : }
474 :
475 : bool
476 0 : cubeb_should_downmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
477 : {
478 0 : if (mixer->channels > stream->channels || mixer->layout == stream->layout) {
479 0 : return false;
480 : }
481 :
482 0 : return mixer->channels < stream->channels ||
483 : // When mixer.channels == stream.channels
484 0 : mixer->layout == CUBEB_LAYOUT_UNDEFINED || // fallback downmix
485 0 : (stream->layout == CUBEB_LAYOUT_3F2 && // 3f2 downmix
486 0 : (mixer->layout == CUBEB_LAYOUT_2F2_LFE ||
487 0 : mixer->layout == CUBEB_LAYOUT_3F1_LFE));
488 : }
489 :
490 : bool
491 0 : cubeb_should_mix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
492 : {
493 0 : return cubeb_should_upmix(stream, mixer) || cubeb_should_downmix(stream, mixer);
494 : }
495 :
496 0 : struct cubeb_mixer {
497 : virtual void mix(long frames,
498 : void * input_buffer, unsigned long input_buffer_length,
499 : void * output_buffer, unsigned long output_buffer_length,
500 : cubeb_stream_params const * stream_params,
501 : cubeb_stream_params const * mixer_params) = 0;
502 0 : virtual ~cubeb_mixer() {};
503 : };
504 :
505 : template<typename T>
506 : struct cubeb_mixer_impl : public cubeb_mixer {
507 0 : explicit cubeb_mixer_impl(unsigned int d)
508 0 : : direction(d)
509 : {
510 0 : }
511 :
512 0 : void mix(long frames,
513 : void * input_buffer, unsigned long input_buffer_length,
514 : void * output_buffer, unsigned long output_buffer_length,
515 : cubeb_stream_params const * stream_params,
516 : cubeb_stream_params const * mixer_params)
517 : {
518 0 : if (frames <= 0) {
519 0 : return;
520 : }
521 :
522 0 : T * in = static_cast<T*>(input_buffer);
523 0 : T * out = static_cast<T*>(output_buffer);
524 :
525 0 : if ((direction & CUBEB_MIXER_DIRECTION_DOWNMIX) &&
526 0 : cubeb_should_downmix(stream_params, mixer_params)) {
527 0 : cubeb_downmix(frames, in, input_buffer_length, out, output_buffer_length, stream_params, mixer_params);
528 0 : } else if ((direction & CUBEB_MIXER_DIRECTION_UPMIX) &&
529 0 : cubeb_should_upmix(stream_params, mixer_params)) {
530 0 : cubeb_upmix(frames, in, input_buffer_length, out, output_buffer_length, stream_params, mixer_params);
531 : }
532 : }
533 :
534 0 : ~cubeb_mixer_impl() {};
535 :
536 : unsigned char const direction;
537 : };
538 :
539 0 : cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format,
540 : unsigned char direction)
541 : {
542 0 : assert(direction & CUBEB_MIXER_DIRECTION_DOWNMIX ||
543 : direction & CUBEB_MIXER_DIRECTION_UPMIX);
544 0 : switch(format) {
545 : case CUBEB_SAMPLE_S16NE:
546 0 : return new cubeb_mixer_impl<short>(direction);
547 : case CUBEB_SAMPLE_FLOAT32NE:
548 0 : return new cubeb_mixer_impl<float>(direction);
549 : default:
550 0 : assert(false);
551 : return nullptr;
552 : }
553 : }
554 :
555 0 : void cubeb_mixer_destroy(cubeb_mixer * mixer)
556 : {
557 0 : delete mixer;
558 0 : }
559 :
560 0 : void cubeb_mixer_mix(cubeb_mixer * mixer, long frames,
561 : void * input_buffer, unsigned long input_buffer_length,
562 : void * output_buffer, unsigned long outputput_buffer_length,
563 : cubeb_stream_params const * stream_params,
564 : cubeb_stream_params const * mixer_params)
565 : {
566 0 : assert(mixer);
567 : mixer->mix(frames, input_buffer, input_buffer_length, output_buffer, outputput_buffer_length,
568 0 : stream_params, mixer_params);
569 0 : }
|