staticbool even(cubeb_channel_layout layout)
{ if (!layout) { returntrue;
} if (layout & (layout - 1)) { returntrue;
} returnfalse;
}
// Ensure that the layout is sane (that is have symmetrical left/right // channels), if not, layout will be treated as mono. static cubeb_channel_layout clean_layout(cubeb_channel_layout layout)
{ if (layout && layout != CHANNEL_FRONT_LEFT && !(layout & (layout - 1))) {
LOG("Treating layout as mono"); return CHANNEL_FRONT_CENTER;
}
return layout;
}
staticbool sane_layout(cubeb_channel_layout layout)
{ if (!(layout & CUBEB_LAYOUT_3F)) { // at least 1 front speaker returnfalse;
} if (!even(layout & (CHANNEL_FRONT_LEFT |
CHANNEL_FRONT_RIGHT))) { // no asymetric front returnfalse;
} if (!even(layout &
(CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT))) { // no asymetric side returnfalse;
} if (!even(layout & (CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT))) { returnfalse;
} if (!even(layout &
(CHANNEL_FRONT_LEFT_OF_CENTER | CHANNEL_FRONT_RIGHT_OF_CENTER))) { returnfalse;
} if (cubeb_channel_layout_nb_channels(layout) >= CHANNELS_MAX) { returnfalse;
} returntrue;
}
int auto_matrix(); int init();
const cubeb_sample_format _format; const cubeb_channel_layout _in_ch_layout; ///< input channel layout const cubeb_channel_layout _out_ch_layout; ///< output channel layout const uint32_t _in_ch_count; ///< input channel count const uint32_t _out_ch_count; ///< output channel count constfloat _surround_mix_level = C_30DB; ///< surround mixing level constfloat _center_mix_level = C_30DB; ///< center mixing level constfloat _lfe_mix_level = 1; ///< LFE mixing level double _matrix[CHANNELS_MAX][CHANNELS_MAX] = {
{0}}; ///< floating point rematrixing coefficients float _matrix_flt[CHANNELS_MAX][CHANNELS_MAX] = {
{0}}; ///< single precision floating point rematrixing coefficients
int32_t _matrix32[CHANNELS_MAX][CHANNELS_MAX] = {
{0}}; ///< 17.15 fixed point rematrixing coefficients
uint8_t _matrix_ch[CHANNELS_MAX][CHANNELS_MAX + 1] = {
{0}}; ///< Lists of input channels per output channel that have non zero ///< rematrixing coefficients bool _clipping = false; ///< Set to true if clipping detection is required bool _valid = false; ///< Set to true if context is valid.
};
// Rematrixing is done via a matrix of coefficient that should be applied to // all channels. Channels are treated as pair and must be symmetrical (if a // left channel exists, the corresponding right should exist too) unless the // output layout has similar layout. Channels are then mixed toward the front // center or back center if they exist with a slight bias toward the front.
// Normalize matrix if needed. if (maxcoef > maxval) {
maxcoef /= maxval; for (uint32_t i = 0; i < CHANNELS_MAX; i++) for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
_matrix[i][j] /= maxcoef;
}
}
if (_format == CUBEB_SAMPLE_FLOAT32NE) { for (uint32_t i = 0; i < FF_ARRAY_ELEMS(_matrix); i++) { for (uint32_t j = 0; j < FF_ARRAY_ELEMS(_matrix[0]); j++) {
_matrix_flt[i][j] = _matrix[i][j];
}
}
}
return 0;
}
int
MixerContext::init()
{ int r = auto_matrix(); if (r) { return r;
}
// Determine if matrix operation would overflow if (_format == CUBEB_SAMPLE_S16NE) { int maxsum = 0; for (uint32_t i = 0; i < _out_ch_count; i++) { double rem = 0; int sum = 0;
for (uint32_t j = 0; j < _in_ch_count; j++) { double target = _matrix[i][j] * 32768 + rem; int value = lrintf(target);
rem += target - value;
sum += std::abs(value);
}
maxsum = std::max(maxsum, sum);
} if (maxsum > 32768) {
_clipping = true;
}
}
// FIXME quantize for integers for (uint32_t i = 0; i < CHANNELS_MAX; i++) { int ch_in = 0; for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
_matrix32[i][j] = lrintf(_matrix[i][j] * 32768); if (_matrix[i][j]) {
_matrix_ch[i][++ch_in] = j;
}
}
_matrix_ch[i][0] = ch_in;
}
return 0;
}
template <typename TYPE_SAMPLE, typename TYPE_COEFF, typename F> 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)
{
static_assert(
std::is_same<TYPE_COEFF, decltype(operand(coeff1))>::value, "function must return the same type as used by coeff1 and coeff2"); for (uint32_t i = 0; i < frames; i++) {
*out = operand(coeff1 * *in1 + coeff2 * *in2);
out += stride_out;
in1 += stride_in;
in2 += stride_in;
}
}
template <typename TYPE_SAMPLE, typename TYPE_COEFF, typename F> 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)
{
static_assert(std::is_same<TYPE_COEFF, decltype(operand(coeff))>::value, "function must return the same type as used by coeff"); for (uint32_t i = 0; i < frames; i++) {
*out = operand(coeff * *in);
out += stride_out;
in += stride_in;
}
}
template <typename TYPE, typename TYPE_COEFF, size_t COLS, typename F> staticint
rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn, const TYPE_COEFF (&matrix_coeff)[COLS][COLS], F && aF, uint32_t frames)
{
static_assert(
std::is_same<TYPE_COEFF, decltype(aF(matrix_coeff[0][0]))>::value, "function must return the same type as used by matrix_coeff");
for (uint32_t out_i = 0; out_i < s->_out_ch_count; out_i++) {
TYPE * out = aOut + out_i; switch (s->_matrix_ch[out_i][0]) { case 0: for (uint32_t i = 0; i < frames; i++) {
out[i * s->_out_ch_count] = 0;
} break; case 1: { int in_i = s->_matrix_ch[out_i][1];
copy(out, s->_out_ch_count, aIn + in_i, s->_in_ch_count,
matrix_coeff[out_i][in_i], aF, frames);
} break; case 2:
sum2(out, s->_out_ch_count, aIn + s->_matrix_ch[out_i][1],
aIn + s->_matrix_ch[out_i][2], s->_in_ch_count,
matrix_coeff[out_i][s->_matrix_ch[out_i][1]],
matrix_coeff[out_i][s->_matrix_ch[out_i][2]], aF, frames); break; default: for (uint32_t i = 0; i < frames; i++) {
TYPE_COEFF v = 0; for (uint32_t j = 0; j < s->_matrix_ch[out_i][0]; j++) {
uint32_t in_i = s->_matrix_ch[out_i][1 + j];
v += *(aIn + in_i + i * s->_in_ch_count) * matrix_coeff[out_i][in_i];
}
out[i * s->_out_ch_count] = aF(v);
} break;
}
} return 0;
}
template <typename T> void copy_and_trunc(size_t frames, const T * input_buffer,
T * output_buffer) const
{ if (_context._in_ch_count <= _context._out_ch_count) { // Not enough channels to copy, fill the gaps with silence. if (_context._in_ch_count == 1 && _context._out_ch_count >= 2) { // Special case for upmixing mono input to stereo and more. We will // duplicate the mono channel to the first two channels. On most system, // the first two channels are for left and right. It is commonly // expected that mono will on both left+right channels for (uint32_t i = 0; i < frames; i++) {
output_buffer[0] = output_buffer[1] = *input_buffer;
PodZero(output_buffer + 2, _context._out_ch_count - 2);
output_buffer += _context._out_ch_count;
input_buffer++;
} return;
} for (uint32_t i = 0; i < frames; i++) {
PodCopy(output_buffer, input_buffer, _context._in_ch_count);
output_buffer += _context._in_ch_count;
input_buffer += _context._in_ch_count;
PodZero(output_buffer, _context._out_ch_count - _context._in_ch_count);
output_buffer += _context._out_ch_count - _context._in_ch_count;
}
} else { for (uint32_t i = 0; i < frames; i++) {
PodCopy(output_buffer, input_buffer, _context._out_ch_count);
output_buffer += _context._out_ch_count;
input_buffer += _context._in_ch_count;
}
}
}
// Check if output buffer is of sufficient size.
size_t size_read_needed =
frames * _context._in_ch_count * cubeb_sample_size(_context._format); if (input_buffer_size < size_read_needed) { // We don't have enough data to read! return -1;
} if (output_buffer_size * _context._in_ch_count <
size_read_needed * _context._out_ch_count) { return -1;
}
if (!valid()) { // The channel layouts were invalid or unsupported, instead we will simply // either drop the extra channels, or fill with silence the missing ones if (_context._format == CUBEB_SAMPLE_FLOAT32NE) {
copy_and_trunc(frames, static_cast<constfloat *>(input_buffer), static_cast<float *>(output_buffer));
} else {
assert(_context._format == CUBEB_SAMPLE_S16NE);
copy_and_trunc(frames, static_cast<const int16_t *>(input_buffer), reinterpret_cast<int16_t *>(output_buffer));
} return 0;
}
switch (_context._format) { case CUBEB_SAMPLE_FLOAT32NE: { auto f = [](float x) { return x; }; return rematrix(&_context, static_cast<float *>(output_buffer), static_cast<constfloat *>(input_buffer),
_context._matrix_flt, f, frames);
} case CUBEB_SAMPLE_S16NE: if (_context._clipping) { auto f = [](int x) { int y = (x + 16384) >> 15; // clip the signed integer value into the -32768,32767 range. if ((y + 0x8000U) & ~0xFFFF) { return (y >> 31) ^ 0x7FFF;
} return y;
}; return rematrix(&_context, static_cast<int16_t *>(output_buffer), static_cast<const int16_t *>(input_buffer),
_context._matrix32, f, frames);
} else { auto f = [](int x) { return (x + 16384) >> 15; }; return rematrix(&_context, static_cast<int16_t *>(output_buffer), static_cast<const int16_t *>(input_buffer),
_context._matrix32, f, frames);
} break; default:
assert(false); break;
}
return -1;
}
// Return false if any of the input or ouput layout were invalid. bool valid() const { return _context._valid; }
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.