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 * Adapted from code based on libswresample's rematrix.c
8 */
9
10 #define NOMINMAX
11
12 #include "cubeb_mixer.h"
13 #include "cubeb-internal.h"
14 #include "cubeb_utils.h"
15 #include <algorithm>
16 #include <cassert>
17 #include <climits>
18 #include <cmath>
19 #include <cstdlib>
20 #include <memory>
21 #include <type_traits>
22
23 #ifndef FF_ARRAY_ELEMS
24 #define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
25 #endif
26
27 #define CHANNELS_MAX 32
28 #define FRONT_LEFT 0
29 #define FRONT_RIGHT 1
30 #define FRONT_CENTER 2
31 #define LOW_FREQUENCY 3
32 #define BACK_LEFT 4
33 #define BACK_RIGHT 5
34 #define FRONT_LEFT_OF_CENTER 6
35 #define FRONT_RIGHT_OF_CENTER 7
36 #define BACK_CENTER 8
37 #define SIDE_LEFT 9
38 #define SIDE_RIGHT 10
39 #define TOP_CENTER 11
40 #define TOP_FRONT_LEFT 12
41 #define TOP_FRONT_CENTER 13
42 #define TOP_FRONT_RIGHT 14
43 #define TOP_BACK_LEFT 15
44 #define TOP_BACK_CENTER 16
45 #define TOP_BACK_RIGHT 17
46 #define NUM_NAMED_CHANNELS 18
47
48 #ifndef M_SQRT1_2
49 #define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
50 #endif
51 #ifndef M_SQRT2
52 #define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
53 #endif
54 #define SQRT3_2 1.22474487139158904909 /* sqrt(3/2) */
55
56 #define C30DB M_SQRT2
57 #define C15DB 1.189207115
58 #define C__0DB 1.0
59 #define C_15DB 0.840896415
60 #define C_30DB M_SQRT1_2
61 #define C_45DB 0.594603558
62 #define C_60DB 0.5
63
64 static cubeb_channel_layout
cubeb_channel_layout_check(cubeb_channel_layout l,uint32_t c)65 cubeb_channel_layout_check(cubeb_channel_layout l, uint32_t c)
66 {
67 if (l == CUBEB_LAYOUT_UNDEFINED) {
68 switch (c) {
69 case 1:
70 return CUBEB_LAYOUT_MONO;
71 case 2:
72 return CUBEB_LAYOUT_STEREO;
73 }
74 }
75 return l;
76 }
77
78 unsigned int
cubeb_channel_layout_nb_channels(cubeb_channel_layout x)79 cubeb_channel_layout_nb_channels(cubeb_channel_layout x)
80 {
81 #if __GNUC__ || __clang__
82 return __builtin_popcount(x);
83 #else
84 x -= (x >> 1) & 0x55555555;
85 x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
86 x = (x + (x >> 4)) & 0x0F0F0F0F;
87 x += x >> 8;
88 return (x + (x >> 16)) & 0x3F;
89 #endif
90 }
91
92 struct MixerContext {
MixerContextMixerContext93 MixerContext(cubeb_sample_format f, uint32_t in_channels,
94 cubeb_channel_layout in, uint32_t out_channels,
95 cubeb_channel_layout out)
96 : _format(f), _in_ch_layout(cubeb_channel_layout_check(in, in_channels)),
97 _out_ch_layout(cubeb_channel_layout_check(out, out_channels)),
98 _in_ch_count(in_channels), _out_ch_count(out_channels)
99 {
100 if (in_channels != cubeb_channel_layout_nb_channels(in) ||
101 out_channels != cubeb_channel_layout_nb_channels(out)) {
102 // Mismatch between channels and layout, aborting.
103 return;
104 }
105 _valid = init() >= 0;
106 }
107
evenMixerContext108 static bool even(cubeb_channel_layout layout)
109 {
110 if (!layout) {
111 return true;
112 }
113 if (layout & (layout - 1)) {
114 return true;
115 }
116 return false;
117 }
118
119 // Ensure that the layout is sane (that is have symmetrical left/right
120 // channels), if not, layout will be treated as mono.
clean_layoutMixerContext121 static cubeb_channel_layout clean_layout(cubeb_channel_layout layout)
122 {
123 if (layout && layout != CHANNEL_FRONT_LEFT && !(layout & (layout - 1))) {
124 LOG("Treating layout as mono");
125 return CHANNEL_FRONT_CENTER;
126 }
127
128 return layout;
129 }
130
sane_layoutMixerContext131 static bool sane_layout(cubeb_channel_layout layout)
132 {
133 if (!(layout & CUBEB_LAYOUT_3F)) { // at least 1 front speaker
134 return false;
135 }
136 if (!even(layout & (CHANNEL_FRONT_LEFT |
137 CHANNEL_FRONT_RIGHT))) { // no asymetric front
138 return false;
139 }
140 if (!even(layout &
141 (CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT))) { // no asymetric side
142 return false;
143 }
144 if (!even(layout & (CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT))) {
145 return false;
146 }
147 if (!even(layout &
148 (CHANNEL_FRONT_LEFT_OF_CENTER | CHANNEL_FRONT_RIGHT_OF_CENTER))) {
149 return false;
150 }
151 if (cubeb_channel_layout_nb_channels(layout) >= CHANNELS_MAX) {
152 return false;
153 }
154 return true;
155 }
156
157 int auto_matrix();
158 int init();
159
160 const cubeb_sample_format _format;
161 const cubeb_channel_layout _in_ch_layout; ///< input channel layout
162 const cubeb_channel_layout _out_ch_layout; ///< output channel layout
163 const uint32_t _in_ch_count; ///< input channel count
164 const uint32_t _out_ch_count; ///< output channel count
165 const float _surround_mix_level = C_30DB; ///< surround mixing level
166 const float _center_mix_level = C_30DB; ///< center mixing level
167 const float _lfe_mix_level = 1; ///< LFE mixing level
168 double _matrix[CHANNELS_MAX][CHANNELS_MAX] = {
169 {0}}; ///< floating point rematrixing coefficients
170 float _matrix_flt[CHANNELS_MAX][CHANNELS_MAX] = {
171 {0}}; ///< single precision floating point rematrixing coefficients
172 int32_t _matrix32[CHANNELS_MAX][CHANNELS_MAX] = {
173 {0}}; ///< 17.15 fixed point rematrixing coefficients
174 uint8_t _matrix_ch[CHANNELS_MAX][CHANNELS_MAX + 1] = {
175 {0}}; ///< Lists of input channels per output channel that have non zero
176 ///< rematrixing coefficients
177 bool _clipping = false; ///< Set to true if clipping detection is required
178 bool _valid = false; ///< Set to true if context is valid.
179 };
180
181 int
auto_matrix()182 MixerContext::auto_matrix()
183 {
184 double matrix[NUM_NAMED_CHANNELS][NUM_NAMED_CHANNELS] = {{0}};
185 double maxcoef = 0;
186 float maxval;
187
188 cubeb_channel_layout in_ch_layout = clean_layout(_in_ch_layout);
189 cubeb_channel_layout out_ch_layout = clean_layout(_out_ch_layout);
190
191 if (!sane_layout(in_ch_layout)) {
192 // Channel Not Supported
193 LOG("Input Layout %x is not supported", _in_ch_layout);
194 return -1;
195 }
196
197 if (!sane_layout(out_ch_layout)) {
198 LOG("Output Layout %x is not supported", _out_ch_layout);
199 return -1;
200 }
201
202 for (uint32_t i = 0; i < FF_ARRAY_ELEMS(matrix); i++) {
203 if (in_ch_layout & out_ch_layout & (1U << i)) {
204 matrix[i][i] = 1.0;
205 }
206 }
207
208 cubeb_channel_layout unaccounted = in_ch_layout & ~out_ch_layout;
209
210 // Rematrixing is done via a matrix of coefficient that should be applied to
211 // all channels. Channels are treated as pair and must be symmetrical (if a
212 // left channel exists, the corresponding right should exist too) unless the
213 // output layout has similar layout. Channels are then mixed toward the front
214 // center or back center if they exist with a slight bias toward the front.
215
216 if (unaccounted & CHANNEL_FRONT_CENTER) {
217 if ((out_ch_layout & CUBEB_LAYOUT_STEREO) == CUBEB_LAYOUT_STEREO) {
218 if (in_ch_layout & CUBEB_LAYOUT_STEREO) {
219 matrix[FRONT_LEFT][FRONT_CENTER] += _center_mix_level;
220 matrix[FRONT_RIGHT][FRONT_CENTER] += _center_mix_level;
221 } else {
222 matrix[FRONT_LEFT][FRONT_CENTER] += M_SQRT1_2;
223 matrix[FRONT_RIGHT][FRONT_CENTER] += M_SQRT1_2;
224 }
225 }
226 }
227 if (unaccounted & CUBEB_LAYOUT_STEREO) {
228 if (out_ch_layout & CHANNEL_FRONT_CENTER) {
229 matrix[FRONT_CENTER][FRONT_LEFT] += M_SQRT1_2;
230 matrix[FRONT_CENTER][FRONT_RIGHT] += M_SQRT1_2;
231 if (in_ch_layout & CHANNEL_FRONT_CENTER)
232 matrix[FRONT_CENTER][FRONT_CENTER] = _center_mix_level * M_SQRT2;
233 }
234 }
235
236 if (unaccounted & CHANNEL_BACK_CENTER) {
237 if (out_ch_layout & CHANNEL_BACK_LEFT) {
238 matrix[BACK_LEFT][BACK_CENTER] += M_SQRT1_2;
239 matrix[BACK_RIGHT][BACK_CENTER] += M_SQRT1_2;
240 } else if (out_ch_layout & CHANNEL_SIDE_LEFT) {
241 matrix[SIDE_LEFT][BACK_CENTER] += M_SQRT1_2;
242 matrix[SIDE_RIGHT][BACK_CENTER] += M_SQRT1_2;
243 } else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
244 matrix[FRONT_LEFT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
245 matrix[FRONT_RIGHT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
246 } else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
247 matrix[FRONT_CENTER][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
248 }
249 }
250 if (unaccounted & CHANNEL_BACK_LEFT) {
251 if (out_ch_layout & CHANNEL_BACK_CENTER) {
252 matrix[BACK_CENTER][BACK_LEFT] += M_SQRT1_2;
253 matrix[BACK_CENTER][BACK_RIGHT] += M_SQRT1_2;
254 } else if (out_ch_layout & CHANNEL_SIDE_LEFT) {
255 if (in_ch_layout & CHANNEL_SIDE_LEFT) {
256 matrix[SIDE_LEFT][BACK_LEFT] += M_SQRT1_2;
257 matrix[SIDE_RIGHT][BACK_RIGHT] += M_SQRT1_2;
258 } else {
259 matrix[SIDE_LEFT][BACK_LEFT] += 1.0;
260 matrix[SIDE_RIGHT][BACK_RIGHT] += 1.0;
261 }
262 } else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
263 matrix[FRONT_LEFT][BACK_LEFT] += _surround_mix_level;
264 matrix[FRONT_RIGHT][BACK_RIGHT] += _surround_mix_level;
265 } else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
266 matrix[FRONT_CENTER][BACK_LEFT] += _surround_mix_level * M_SQRT1_2;
267 matrix[FRONT_CENTER][BACK_RIGHT] += _surround_mix_level * M_SQRT1_2;
268 }
269 }
270
271 if (unaccounted & CHANNEL_SIDE_LEFT) {
272 if (out_ch_layout & CHANNEL_BACK_LEFT) {
273 /* if back channels do not exist in the input, just copy side
274 channels to back channels, otherwise mix side into back */
275 if (in_ch_layout & CHANNEL_BACK_LEFT) {
276 matrix[BACK_LEFT][SIDE_LEFT] += M_SQRT1_2;
277 matrix[BACK_RIGHT][SIDE_RIGHT] += M_SQRT1_2;
278 } else {
279 matrix[BACK_LEFT][SIDE_LEFT] += 1.0;
280 matrix[BACK_RIGHT][SIDE_RIGHT] += 1.0;
281 }
282 } else if (out_ch_layout & CHANNEL_BACK_CENTER) {
283 matrix[BACK_CENTER][SIDE_LEFT] += M_SQRT1_2;
284 matrix[BACK_CENTER][SIDE_RIGHT] += M_SQRT1_2;
285 } else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
286 matrix[FRONT_LEFT][SIDE_LEFT] += _surround_mix_level;
287 matrix[FRONT_RIGHT][SIDE_RIGHT] += _surround_mix_level;
288 } else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
289 matrix[FRONT_CENTER][SIDE_LEFT] += _surround_mix_level * M_SQRT1_2;
290 matrix[FRONT_CENTER][SIDE_RIGHT] += _surround_mix_level * M_SQRT1_2;
291 }
292 }
293
294 if (unaccounted & CHANNEL_FRONT_LEFT_OF_CENTER) {
295 if (out_ch_layout & CHANNEL_FRONT_LEFT) {
296 matrix[FRONT_LEFT][FRONT_LEFT_OF_CENTER] += 1.0;
297 matrix[FRONT_RIGHT][FRONT_RIGHT_OF_CENTER] += 1.0;
298 } else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
299 matrix[FRONT_CENTER][FRONT_LEFT_OF_CENTER] += M_SQRT1_2;
300 matrix[FRONT_CENTER][FRONT_RIGHT_OF_CENTER] += M_SQRT1_2;
301 }
302 }
303 /* mix LFE into front left/right or center */
304 if (unaccounted & CHANNEL_LOW_FREQUENCY) {
305 if (out_ch_layout & CHANNEL_FRONT_CENTER) {
306 matrix[FRONT_CENTER][LOW_FREQUENCY] += _lfe_mix_level;
307 } else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
308 matrix[FRONT_LEFT][LOW_FREQUENCY] += _lfe_mix_level * M_SQRT1_2;
309 matrix[FRONT_RIGHT][LOW_FREQUENCY] += _lfe_mix_level * M_SQRT1_2;
310 }
311 }
312
313 // Normalize the conversion matrix.
314 for (uint32_t out_i = 0, i = 0; i < CHANNELS_MAX; i++) {
315 double sum = 0;
316 int in_i = 0;
317 if ((out_ch_layout & (1U << i)) == 0) {
318 continue;
319 }
320 for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
321 if ((in_ch_layout & (1U << j)) == 0) {
322 continue;
323 }
324 if (i < FF_ARRAY_ELEMS(matrix) && j < FF_ARRAY_ELEMS(matrix[0])) {
325 _matrix[out_i][in_i] = matrix[i][j];
326 } else {
327 _matrix[out_i][in_i] =
328 i == j && (in_ch_layout & out_ch_layout & (1U << i));
329 }
330 sum += fabs(_matrix[out_i][in_i]);
331 in_i++;
332 }
333 maxcoef = std::max(maxcoef, sum);
334 out_i++;
335 }
336
337 if (_format == CUBEB_SAMPLE_S16NE) {
338 maxval = 1.0;
339 } else {
340 maxval = INT_MAX;
341 }
342
343 // Normalize matrix if needed.
344 if (maxcoef > maxval) {
345 maxcoef /= maxval;
346 for (uint32_t i = 0; i < CHANNELS_MAX; i++)
347 for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
348 _matrix[i][j] /= maxcoef;
349 }
350 }
351
352 if (_format == CUBEB_SAMPLE_FLOAT32NE) {
353 for (uint32_t i = 0; i < FF_ARRAY_ELEMS(_matrix); i++) {
354 for (uint32_t j = 0; j < FF_ARRAY_ELEMS(_matrix[0]); j++) {
355 _matrix_flt[i][j] = _matrix[i][j];
356 }
357 }
358 }
359
360 return 0;
361 }
362
363 int
init()364 MixerContext::init()
365 {
366 int r = auto_matrix();
367 if (r) {
368 return r;
369 }
370
371 // Determine if matrix operation would overflow
372 if (_format == CUBEB_SAMPLE_S16NE) {
373 int maxsum = 0;
374 for (uint32_t i = 0; i < _out_ch_count; i++) {
375 double rem = 0;
376 int sum = 0;
377
378 for (uint32_t j = 0; j < _in_ch_count; j++) {
379 double target = _matrix[i][j] * 32768 + rem;
380 int value = lrintf(target);
381 rem += target - value;
382 sum += std::abs(value);
383 }
384 maxsum = std::max(maxsum, sum);
385 }
386 if (maxsum > 32768) {
387 _clipping = true;
388 }
389 }
390
391 // FIXME quantize for integers
392 for (uint32_t i = 0; i < CHANNELS_MAX; i++) {
393 int ch_in = 0;
394 for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
395 _matrix32[i][j] = lrintf(_matrix[i][j] * 32768);
396 if (_matrix[i][j]) {
397 _matrix_ch[i][++ch_in] = j;
398 }
399 }
400 _matrix_ch[i][0] = ch_in;
401 }
402
403 return 0;
404 }
405
406 template <typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
407 void
sum2(TYPE_SAMPLE * out,uint32_t stride_out,const TYPE_SAMPLE * in1,const TYPE_SAMPLE * in2,uint32_t stride_in,TYPE_COEFF coeff1,TYPE_COEFF coeff2,F && operand,uint32_t frames)408 sum2(TYPE_SAMPLE * out, uint32_t stride_out, const TYPE_SAMPLE * in1,
409 const TYPE_SAMPLE * in2, uint32_t stride_in, TYPE_COEFF coeff1,
410 TYPE_COEFF coeff2, F && operand, uint32_t frames)
411 {
412 static_assert(
413 std::is_same<TYPE_COEFF, decltype(operand(coeff1))>::value,
414 "function must return the same type as used by coeff1 and coeff2");
415 for (uint32_t i = 0; i < frames; i++) {
416 *out = operand(coeff1 * *in1 + coeff2 * *in2);
417 out += stride_out;
418 in1 += stride_in;
419 in2 += stride_in;
420 }
421 }
422
423 template <typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
424 void
copy(TYPE_SAMPLE * out,uint32_t stride_out,const TYPE_SAMPLE * in,uint32_t stride_in,TYPE_COEFF coeff,F && operand,uint32_t frames)425 copy(TYPE_SAMPLE * out, uint32_t stride_out, const TYPE_SAMPLE * in,
426 uint32_t stride_in, TYPE_COEFF coeff, F && operand, uint32_t frames)
427 {
428 static_assert(std::is_same<TYPE_COEFF, decltype(operand(coeff))>::value,
429 "function must return the same type as used by coeff");
430 for (uint32_t i = 0; i < frames; i++) {
431 *out = operand(coeff * *in);
432 out += stride_out;
433 in += stride_in;
434 }
435 }
436
437 template <typename TYPE, typename TYPE_COEFF, size_t COLS, typename F>
438 static int
rematrix(const MixerContext * s,TYPE * aOut,const TYPE * aIn,const TYPE_COEFF (& matrix_coeff)[COLS][COLS],F && aF,uint32_t frames)439 rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn,
440 const TYPE_COEFF (&matrix_coeff)[COLS][COLS], F && aF, uint32_t frames)
441 {
442 static_assert(
443 std::is_same<TYPE_COEFF, decltype(aF(matrix_coeff[0][0]))>::value,
444 "function must return the same type as used by matrix_coeff");
445
446 for (uint32_t out_i = 0; out_i < s->_out_ch_count; out_i++) {
447 TYPE * out = aOut + out_i;
448 switch (s->_matrix_ch[out_i][0]) {
449 case 0:
450 for (uint32_t i = 0; i < frames; i++) {
451 out[i * s->_out_ch_count] = 0;
452 }
453 break;
454 case 1: {
455 int in_i = s->_matrix_ch[out_i][1];
456 copy(out, s->_out_ch_count, aIn + in_i, s->_in_ch_count,
457 matrix_coeff[out_i][in_i], aF, frames);
458 } break;
459 case 2:
460 sum2(out, s->_out_ch_count, aIn + s->_matrix_ch[out_i][1],
461 aIn + s->_matrix_ch[out_i][2], s->_in_ch_count,
462 matrix_coeff[out_i][s->_matrix_ch[out_i][1]],
463 matrix_coeff[out_i][s->_matrix_ch[out_i][2]], aF, frames);
464 break;
465 default:
466 for (uint32_t i = 0; i < frames; i++) {
467 TYPE_COEFF v = 0;
468 for (uint32_t j = 0; j < s->_matrix_ch[out_i][0]; j++) {
469 uint32_t in_i = s->_matrix_ch[out_i][1 + j];
470 v += *(aIn + in_i + i * s->_in_ch_count) * matrix_coeff[out_i][in_i];
471 }
472 out[i * s->_out_ch_count] = aF(v);
473 }
474 break;
475 }
476 }
477 return 0;
478 }
479
480 struct cubeb_mixer {
cubeb_mixercubeb_mixer481 cubeb_mixer(cubeb_sample_format format, uint32_t in_channels,
482 cubeb_channel_layout in_layout, uint32_t out_channels,
483 cubeb_channel_layout out_layout)
484 : _context(format, in_channels, in_layout, out_channels, out_layout)
485 {
486 }
487
488 template <typename T>
copy_and_trunccubeb_mixer489 void copy_and_trunc(size_t frames, const T * input_buffer,
490 T * output_buffer) const
491 {
492 if (_context._in_ch_count <= _context._out_ch_count) {
493 // Not enough channels to copy, fill the gaps with silence.
494 if (_context._in_ch_count == 1 && _context._out_ch_count >= 2) {
495 // Special case for upmixing mono input to stereo and more. We will
496 // duplicate the mono channel to the first two channels. On most system,
497 // the first two channels are for left and right. It is commonly
498 // expected that mono will on both left+right channels
499 for (uint32_t i = 0; i < frames; i++) {
500 output_buffer[0] = output_buffer[1] = *input_buffer;
501 PodZero(output_buffer + 2, _context._out_ch_count - 2);
502 output_buffer += _context._out_ch_count;
503 input_buffer++;
504 }
505 return;
506 }
507 for (uint32_t i = 0; i < frames; i++) {
508 PodCopy(output_buffer, input_buffer, _context._in_ch_count);
509 output_buffer += _context._in_ch_count;
510 input_buffer += _context._in_ch_count;
511 PodZero(output_buffer, _context._out_ch_count - _context._in_ch_count);
512 output_buffer += _context._out_ch_count - _context._in_ch_count;
513 }
514 } else {
515 for (uint32_t i = 0; i < frames; i++) {
516 PodCopy(output_buffer, input_buffer, _context._out_ch_count);
517 output_buffer += _context._out_ch_count;
518 input_buffer += _context._in_ch_count;
519 }
520 }
521 }
522
mixcubeb_mixer523 int mix(size_t frames, const void * input_buffer, size_t input_buffer_size,
524 void * output_buffer, size_t output_buffer_size) const
525 {
526 if (frames <= 0 || _context._out_ch_count == 0) {
527 return 0;
528 }
529
530 // Check if output buffer is of sufficient size.
531 size_t size_read_needed =
532 frames * _context._in_ch_count * cubeb_sample_size(_context._format);
533 if (input_buffer_size < size_read_needed) {
534 // We don't have enough data to read!
535 return -1;
536 }
537 if (output_buffer_size * _context._in_ch_count <
538 size_read_needed * _context._out_ch_count) {
539 return -1;
540 }
541
542 if (!valid()) {
543 // The channel layouts were invalid or unsupported, instead we will simply
544 // either drop the extra channels, or fill with silence the missing ones
545 if (_context._format == CUBEB_SAMPLE_FLOAT32NE) {
546 copy_and_trunc(frames, static_cast<const float *>(input_buffer),
547 static_cast<float *>(output_buffer));
548 } else {
549 assert(_context._format == CUBEB_SAMPLE_S16NE);
550 copy_and_trunc(frames, static_cast<const int16_t *>(input_buffer),
551 reinterpret_cast<int16_t *>(output_buffer));
552 }
553 return 0;
554 }
555
556 switch (_context._format) {
557 case CUBEB_SAMPLE_FLOAT32NE: {
558 auto f = [](float x) { return x; };
559 return rematrix(&_context, static_cast<float *>(output_buffer),
560 static_cast<const float *>(input_buffer),
561 _context._matrix_flt, f, frames);
562 }
563 case CUBEB_SAMPLE_S16NE:
564 if (_context._clipping) {
565 auto f = [](int x) {
566 int y = (x + 16384) >> 15;
567 // clip the signed integer value into the -32768,32767 range.
568 if ((y + 0x8000U) & ~0xFFFF) {
569 return (y >> 31) ^ 0x7FFF;
570 }
571 return y;
572 };
573 return rematrix(&_context, static_cast<int16_t *>(output_buffer),
574 static_cast<const int16_t *>(input_buffer),
575 _context._matrix32, f, frames);
576 } else {
577 auto f = [](int x) { return (x + 16384) >> 15; };
578 return rematrix(&_context, static_cast<int16_t *>(output_buffer),
579 static_cast<const int16_t *>(input_buffer),
580 _context._matrix32, f, frames);
581 }
582 break;
583 default:
584 assert(false);
585 break;
586 }
587
588 return -1;
589 }
590
591 // Return false if any of the input or ouput layout were invalid.
validcubeb_mixer592 bool valid() const { return _context._valid; }
593
~cubeb_mixercubeb_mixer594 virtual ~cubeb_mixer(){};
595
596 MixerContext _context;
597 };
598
599 cubeb_mixer *
cubeb_mixer_create(cubeb_sample_format format,uint32_t in_channels,cubeb_channel_layout in_layout,uint32_t out_channels,cubeb_channel_layout out_layout)600 cubeb_mixer_create(cubeb_sample_format format, uint32_t in_channels,
601 cubeb_channel_layout in_layout, uint32_t out_channels,
602 cubeb_channel_layout out_layout)
603 {
604 return new cubeb_mixer(format, in_channels, in_layout, out_channels,
605 out_layout);
606 }
607
608 void
cubeb_mixer_destroy(cubeb_mixer * mixer)609 cubeb_mixer_destroy(cubeb_mixer * mixer)
610 {
611 delete mixer;
612 }
613
614 int
cubeb_mixer_mix(cubeb_mixer * mixer,size_t frames,const void * input_buffer,size_t input_buffer_size,void * output_buffer,size_t output_buffer_size)615 cubeb_mixer_mix(cubeb_mixer * mixer, size_t frames, const void * input_buffer,
616 size_t input_buffer_size, void * output_buffer,
617 size_t output_buffer_size)
618 {
619 return mixer->mix(frames, input_buffer, input_buffer_size, output_buffer,
620 output_buffer_size);
621 }
622