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 
cubeb_channel_map_to_layout(cubeb_channel_map const * channel_map)30 cubeb_channel_layout cubeb_channel_map_to_layout(cubeb_channel_map const * channel_map)
31 {
32   uint32_t channel_mask = 0;
33   for (uint8_t i = 0 ; i < channel_map->channels ; ++i) {
34     if (channel_map->map[i] == CHANNEL_INVALID ||
35         channel_map->map[i] == CHANNEL_UNMAPPED) {
36       return CUBEB_LAYOUT_UNDEFINED;
37     }
38     channel_mask |= 1 << channel_map->map[i];
39   }
40 
41   switch(channel_mask) {
42     case MASK_MONO: return CUBEB_LAYOUT_MONO;
43     case MASK_MONO_LFE: return CUBEB_LAYOUT_MONO_LFE;
44     case MASK_STEREO: return CUBEB_LAYOUT_STEREO;
45     case MASK_STEREO_LFE: return CUBEB_LAYOUT_STEREO_LFE;
46     case MASK_3F: return CUBEB_LAYOUT_3F;
47     case MASK_3F_LFE: return CUBEB_LAYOUT_3F_LFE;
48     case MASK_2F1: return CUBEB_LAYOUT_2F1;
49     case MASK_2F1_LFE: return CUBEB_LAYOUT_2F1_LFE;
50     case MASK_3F1: return CUBEB_LAYOUT_3F1;
51     case MASK_3F1_LFE: return CUBEB_LAYOUT_3F1_LFE;
52     case MASK_2F2: return CUBEB_LAYOUT_2F2;
53     case MASK_2F2_LFE: return CUBEB_LAYOUT_2F2_LFE;
54     case MASK_3F2: return CUBEB_LAYOUT_3F2;
55     case MASK_3F2_LFE: return CUBEB_LAYOUT_3F2_LFE;
56     case MASK_3F3R_LFE: return CUBEB_LAYOUT_3F3R_LFE;
57     case MASK_3F4_LFE: return CUBEB_LAYOUT_3F4_LFE;
58     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
downmix_3f2(unsigned long inframes,T const * const in,unsigned long in_len,T * out,unsigned long out_len,cubeb_channel_layout in_layout,cubeb_channel_layout out_layout)252 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   if ((in_layout != CUBEB_LAYOUT_3F2 && in_layout != CUBEB_LAYOUT_3F2_LFE) ||
258       out_layout < CUBEB_LAYOUT_MONO || out_layout > CUBEB_LAYOUT_2F2_LFE) {
259     return false;
260   }
261 
262   unsigned int in_channels = CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels;
263   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   assert(out_channels <= in_channels);
267 
268   auto & downmix_matrix = DOWNMIX_MATRIX_3F2_LFE[out_layout - CUBEB_LAYOUT_MONO]; // The matrix is started from mono.
269   unsigned long out_index = 0;
270   for (unsigned long i = 0 ; i < inframes * in_channels; i += in_channels) {
271     for (unsigned int j = 0; j < out_channels; ++j) {
272       T sample = 0;
273       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         assert((in_layout == CUBEB_LAYOUT_3F2_LFE || k < 3) ? (i + k < in_len) : (k == 3) ? true : (i + k - 1 < in_len));
278         T data = (in_layout == CUBEB_LAYOUT_3F2_LFE) ? in[i + k] : (k == 3) ? 0 : in[i + ((k < 3) ? k : k - 1)];
279         sample += downmix_matrix[j][k] * data;
280       }
281       assert(out_index + j < out_len);
282       out[out_index + j] = sample;
283     }
284 #if defined(USE_AUDIOUNIT)
285     out_index += in_channels;
286 #else
287     out_index += out_channels;
288 #endif
289   }
290 
291   return true;
292 }
293 
294 /* Map the audio data by channel name. */
295 template<class T>
296 bool
mix_remap(long inframes,T const * const in,unsigned long in_len,T * out,unsigned long out_len,cubeb_channel_layout in_layout,cubeb_channel_layout out_layout)297 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   assert(in_layout != out_layout && inframes >= 0);
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   if (in == out) {
307     return false;
308   }
309 
310   unsigned int in_channels = CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels;
311   unsigned int out_channels = CUBEB_CHANNEL_LAYOUT_MAPS[out_layout].channels;
312 
313   uint32_t in_layout_mask = 0;
314   for (unsigned int i = 0 ; i < in_channels ; ++i) {
315     in_layout_mask |= 1 << CHANNEL_INDEX_TO_ORDER[in_layout][i];
316   }
317 
318   uint32_t out_layout_mask = 0;
319   for (unsigned int i = 0 ; i < out_channels ; ++i) {
320     out_layout_mask |= 1 << CHANNEL_INDEX_TO_ORDER[out_layout][i];
321   }
322 
323   // If there is no matched channel, then do nothing.
324   if (!(out_layout_mask & in_layout_mask)) {
325     return false;
326   }
327 
328   for (unsigned long i = 0, out_index = 0; i < (unsigned long)inframes * in_channels; i += in_channels, out_index += out_channels) {
329     for (unsigned int j = 0; j < out_channels; ++j) {
330       cubeb_channel channel = CHANNEL_INDEX_TO_ORDER[out_layout][j];
331       uint32_t channel_mask = 1 << channel;
332       int channel_index = CHANNEL_ORDER_TO_INDEX[in_layout][channel];
333       assert(channel_index >= -1);
334       assert(out_index + j < out_len);
335       if (in_layout_mask & channel_mask) {
336         assert(channel_index != -1);
337         assert(i + channel_index < in_len);
338         out[out_index + j] = in[i + channel_index];
339       } else {
340         assert(channel_index == -1);
341         out[out_index + j] = 0;
342       }
343     }
344   }
345 
346   return true;
347 }
348 
349 /* Drop the extra channels beyond the provided output channels. */
350 template<typename T>
351 void
downmix_fallback(long inframes,T const * const in,unsigned long in_len,T * out,unsigned long out_len,unsigned int in_channels,unsigned int out_channels)352 downmix_fallback(long inframes,
353                  T const * const in, unsigned long in_len,
354                  T * out, unsigned long out_len,
355                  unsigned int in_channels, unsigned int out_channels)
356 {
357   assert(in_channels >= out_channels && inframes >= 0);
358 
359   if (in_channels == out_channels && in == out) {
360     return;
361   }
362 
363   for (unsigned long i = 0, out_index = 0; i < (unsigned long)inframes * in_channels; i += in_channels, out_index += out_channels) {
364     for (unsigned int j = 0; j < out_channels; ++j) {
365       assert(i + j < in_len && out_index + j < out_len);
366       out[out_index + j] = in[i + j];
367     }
368   }
369 }
370 
371 
372 template<typename T>
373 void
cubeb_downmix(long inframes,T const * const in,unsigned long in_len,T * out,unsigned long out_len,cubeb_stream_params const * stream_params,cubeb_stream_params const * mixer_params)374 cubeb_downmix(long inframes,
375               T const * const in, unsigned long in_len,
376               T * out, unsigned long out_len,
377               cubeb_stream_params const * stream_params,
378               cubeb_stream_params const * mixer_params)
379 {
380   assert(in && out);
381   assert(inframes);
382   assert(stream_params->channels >= mixer_params->channels &&
383          mixer_params->channels > 0);
384   assert(stream_params->layout != CUBEB_LAYOUT_UNDEFINED);
385 
386   unsigned int in_channels = stream_params->channels;
387   cubeb_channel_layout in_layout = stream_params->layout;
388 
389   unsigned int out_channels = mixer_params->channels;
390   cubeb_channel_layout out_layout = mixer_params->layout;
391 
392   // If the channel number is different from the layout's setting,
393   // then we use fallback downmix mechanism.
394   if (out_channels == CUBEB_CHANNEL_LAYOUT_MAPS[out_layout].channels &&
395       in_channels == CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels) {
396     if (downmix_3f2(inframes, in, in_len, out, out_len, in_layout, out_layout)) {
397       return;
398     }
399 
400 #if defined(USE_AUDIOUNIT)
401   // We only support downmix for audio 5.1 on OS X currently.
402   return;
403 #endif
404 
405     if (mix_remap(inframes, in, in_len, out, out_len, in_layout, out_layout)) {
406       return;
407     }
408   }
409 
410   downmix_fallback(inframes, in, in_len, out, out_len, in_channels, out_channels);
411 }
412 
413 /* Upmix function, copies a mono channel into L and R. */
414 template<typename T>
415 void
mono_to_stereo(long insamples,T const * in,unsigned long in_len,T * out,unsigned long out_len,unsigned int out_channels)416 mono_to_stereo(long insamples, T const * in, unsigned long in_len,
417                T * out, unsigned long out_len, unsigned int out_channels)
418 {
419   for (long i = 0, j = 0; i < insamples; ++i, j += out_channels) {
420     assert((unsigned long)i < in_len && (unsigned long)j + 1 < out_len);
421     out[j] = out[j + 1] = in[i];
422   }
423 }
424 
425 template<typename T>
426 void
cubeb_upmix(long inframes,T const * const in,unsigned long in_len,T * out,unsigned long out_len,cubeb_stream_params const * stream_params,cubeb_stream_params const * mixer_params)427 cubeb_upmix(long inframes,
428             T const * const in, unsigned long in_len,
429             T * out, unsigned long out_len,
430             cubeb_stream_params const * stream_params,
431             cubeb_stream_params const * mixer_params)
432 {
433   assert(in && out);
434   assert(inframes);
435   assert(mixer_params->channels >= stream_params->channels &&
436          stream_params->channels > 0);
437 
438   unsigned int in_channels = stream_params->channels;
439   unsigned int out_channels = mixer_params->channels;
440 
441   /* Either way, if we have 2 or more channels, the first two are L and R. */
442   /* If we are playing a mono stream over stereo speakers, copy the data over. */
443   if (in_channels == 1 && out_channels >= 2) {
444     mono_to_stereo(inframes, in, in_len, out, out_len, out_channels);
445   } else {
446     /* Copy through. */
447     for (unsigned int i = 0, o = 0; i < inframes * in_channels;
448         i += in_channels, o += out_channels) {
449       for (unsigned int j = 0; j < in_channels; ++j) {
450         assert(i + j < in_len && o + j < out_len);
451         out[o + j] = in[i + j];
452       }
453     }
454   }
455 
456   /* Check if more channels. */
457   if (out_channels <= 2) {
458     return;
459   }
460 
461   /* Put silence in remaining channels. */
462   for (long i = 0, o = 0; i < inframes; ++i, o += out_channels) {
463     for (unsigned int j = in_channels; j < out_channels; ++j) {
464       assert((unsigned long)o + j < out_len);
465       out[o + j] = 0.0;
466     }
467   }
468 }
469 
470 bool
cubeb_should_upmix(cubeb_stream_params const * stream,cubeb_stream_params const * mixer)471 cubeb_should_upmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
472 {
473   return mixer->channels > stream->channels;
474 }
475 
476 bool
cubeb_should_downmix(cubeb_stream_params const * stream,cubeb_stream_params const * mixer)477 cubeb_should_downmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
478 {
479   if (mixer->channels > stream->channels || mixer->layout == stream->layout) {
480     return false;
481   }
482 
483   return mixer->channels < stream->channels ||
484          // When mixer.channels == stream.channels
485          mixer->layout == CUBEB_LAYOUT_UNDEFINED ||  // fallback downmix
486          (stream->layout == CUBEB_LAYOUT_3F2 &&      // 3f2 downmix
487           (mixer->layout == CUBEB_LAYOUT_2F2_LFE ||
488            mixer->layout == CUBEB_LAYOUT_3F1_LFE));
489 }
490 
491 bool
cubeb_should_mix(cubeb_stream_params const * stream,cubeb_stream_params const * mixer)492 cubeb_should_mix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
493 {
494   return stream->layout != CUBEB_LAYOUT_UNDEFINED &&
495          (cubeb_should_upmix(stream, mixer) || cubeb_should_downmix(stream, mixer));
496 }
497 
498 struct cubeb_mixer {
499   virtual void mix(long frames,
500                    void * input_buffer, unsigned long input_buffer_length,
501                    void * output_buffer, unsigned long output_buffer_length,
502                    cubeb_stream_params const * stream_params,
503                    cubeb_stream_params const * mixer_params) = 0;
~cubeb_mixercubeb_mixer504   virtual ~cubeb_mixer() {};
505 };
506 
507 template<typename T>
508 struct cubeb_mixer_impl : public cubeb_mixer {
cubeb_mixer_implcubeb_mixer_impl509   explicit cubeb_mixer_impl(unsigned int d)
510     : direction(d)
511   {
512   }
513 
mixcubeb_mixer_impl514   void mix(long frames,
515            void * input_buffer, unsigned long input_buffer_length,
516            void * output_buffer, unsigned long output_buffer_length,
517            cubeb_stream_params const * stream_params,
518            cubeb_stream_params const * mixer_params)
519   {
520     if (frames <= 0) {
521       return;
522     }
523 
524     T * in = static_cast<T*>(input_buffer);
525     T * out = static_cast<T*>(output_buffer);
526 
527     if ((direction & CUBEB_MIXER_DIRECTION_DOWNMIX) &&
528         cubeb_should_downmix(stream_params, mixer_params)) {
529       cubeb_downmix(frames, in, input_buffer_length, out, output_buffer_length, stream_params, mixer_params);
530     } else if ((direction & CUBEB_MIXER_DIRECTION_UPMIX) &&
531                cubeb_should_upmix(stream_params, mixer_params)) {
532       cubeb_upmix(frames, in, input_buffer_length, out, output_buffer_length, stream_params, mixer_params);
533     }
534   }
535 
~cubeb_mixer_implcubeb_mixer_impl536   ~cubeb_mixer_impl() {};
537 
538   unsigned char const direction;
539 };
540 
cubeb_mixer_create(cubeb_sample_format format,unsigned char direction)541 cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format,
542                                  unsigned char direction)
543 {
544   assert(direction & CUBEB_MIXER_DIRECTION_DOWNMIX ||
545          direction & CUBEB_MIXER_DIRECTION_UPMIX);
546   switch(format) {
547     case CUBEB_SAMPLE_S16NE:
548       return new cubeb_mixer_impl<short>(direction);
549     case CUBEB_SAMPLE_FLOAT32NE:
550       return new cubeb_mixer_impl<float>(direction);
551     default:
552       assert(false);
553       return nullptr;
554   }
555 }
556 
cubeb_mixer_destroy(cubeb_mixer * mixer)557 void cubeb_mixer_destroy(cubeb_mixer * mixer)
558 {
559   delete mixer;
560 }
561 
cubeb_mixer_mix(cubeb_mixer * mixer,long frames,void * input_buffer,unsigned long input_buffer_length,void * output_buffer,unsigned long output_buffer_length,cubeb_stream_params const * stream_params,cubeb_stream_params const * mixer_params)562 void cubeb_mixer_mix(cubeb_mixer * mixer, long frames,
563                      void * input_buffer, unsigned long input_buffer_length,
564                      void * output_buffer, unsigned long output_buffer_length,
565                      cubeb_stream_params const * stream_params,
566                      cubeb_stream_params const * mixer_params)
567 {
568   assert(mixer);
569   mixer->mix(frames, input_buffer, input_buffer_length, output_buffer, output_buffer_length,
570              stream_params, mixer_params);
571 }
572