duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

cubeb_mixer.cpp (20405B)


      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
     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
     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 {
     93   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 
    108   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.
    121   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 
    131   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
    182 MixerContext::auto_matrix()
    183 {
    184   double matrix[NUM_NAMED_CHANNELS][NUM_NAMED_CHANNELS] = {{0}};
    185   double maxcoef = 0;
    186   double 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
    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
    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
    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
    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 {
    481   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>
    489   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 
    523   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.
    592   bool valid() const { return _context._valid; }
    593 
    594   virtual ~cubeb_mixer(){};
    595 
    596   MixerContext _context;
    597 };
    598 
    599 cubeb_mixer *
    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
    609 cubeb_mixer_destroy(cubeb_mixer * mixer)
    610 {
    611   delete mixer;
    612 }
    613 
    614 int
    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 }