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