tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.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 }