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 }