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