Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/cubeb/src/cubeb_mixer.cpp
4246 views
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
}
622
623