diff options
Diffstat (limited to 'media/libcubeb/src/cubeb_mixer.cpp')
-rw-r--r-- | media/libcubeb/src/cubeb_mixer.cpp | 663 |
1 files changed, 0 insertions, 663 deletions
diff --git a/media/libcubeb/src/cubeb_mixer.cpp b/media/libcubeb/src/cubeb_mixer.cpp deleted file mode 100644 index 2ab7f673a..000000000 --- a/media/libcubeb/src/cubeb_mixer.cpp +++ /dev/null @@ -1,663 +0,0 @@ -/* - * Copyright © 2016 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - * - * Adapted from code based on libswresample's rematrix.c - */ - -#define NOMINMAX - -#include <algorithm> -#include <cassert> -#include <climits> -#include <cmath> -#include <cstdlib> -#include <memory> -#include <type_traits> -#include "cubeb-internal.h" -#include "cubeb_mixer.h" -#include "cubeb_utils.h" - -#ifndef FF_ARRAY_ELEMS -#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) -#endif - -#define CHANNELS_MAX 32 -#define FRONT_LEFT 0 -#define FRONT_RIGHT 1 -#define FRONT_CENTER 2 -#define LOW_FREQUENCY 3 -#define BACK_LEFT 4 -#define BACK_RIGHT 5 -#define FRONT_LEFT_OF_CENTER 6 -#define FRONT_RIGHT_OF_CENTER 7 -#define BACK_CENTER 8 -#define SIDE_LEFT 9 -#define SIDE_RIGHT 10 -#define TOP_CENTER 11 -#define TOP_FRONT_LEFT 12 -#define TOP_FRONT_CENTER 13 -#define TOP_FRONT_RIGHT 14 -#define TOP_BACK_LEFT 15 -#define TOP_BACK_CENTER 16 -#define TOP_BACK_RIGHT 17 -#define NUM_NAMED_CHANNELS 18 - -#ifndef M_SQRT1_2 -#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ -#endif -#ifndef M_SQRT2 -#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ -#endif -#define SQRT3_2 1.22474487139158904909 /* sqrt(3/2) */ - -#define C30DB M_SQRT2 -#define C15DB 1.189207115 -#define C__0DB 1.0 -#define C_15DB 0.840896415 -#define C_30DB M_SQRT1_2 -#define C_45DB 0.594603558 -#define C_60DB 0.5 - -static cubeb_channel_layout -cubeb_channel_layout_check(cubeb_channel_layout l, uint32_t c) -{ - if (l == CUBEB_LAYOUT_UNDEFINED) { - switch (c) { - case 1: return CUBEB_LAYOUT_MONO; - case 2: return CUBEB_LAYOUT_STEREO; - } - } - return l; -} - -unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout x) -{ -#if __GNUC__ || __clang__ - return __builtin_popcount (x); -#else - x -= (x >> 1) & 0x55555555; - x = (x & 0x33333333) + ((x >> 2) & 0x33333333); - x = (x + (x >> 4)) & 0x0F0F0F0F; - x += x >> 8; - return (x + (x >> 16)) & 0x3F; -#endif -} - -struct MixerContext { - MixerContext(cubeb_sample_format f, - uint32_t in_channels, - cubeb_channel_layout in, - uint32_t out_channels, - cubeb_channel_layout out) - : _format(f) - , _in_ch_layout(cubeb_channel_layout_check(in, in_channels)) - , _out_ch_layout(cubeb_channel_layout_check(out, out_channels)) - , _in_ch_count(in_channels) - , _out_ch_count(out_channels) - { - if (in_channels != cubeb_channel_layout_nb_channels(in) || - out_channels != cubeb_channel_layout_nb_channels(out)) { - // Mismatch between channels and layout, aborting. - return; - } - _valid = init() >= 0; - } - - static bool even(cubeb_channel_layout layout) - { - if (!layout) { - return true; - } - if (layout & (layout - 1)) { - return true; - } - return false; - } - - // 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; - } - - static bool sane_layout(cubeb_channel_layout layout) - { - if (!(layout & CUBEB_LAYOUT_3F)) { // at least 1 front speaker - return false; - } - if (!even(layout & (CHANNEL_FRONT_LEFT | - CHANNEL_FRONT_RIGHT))) { // no asymetric front - return false; - } - if (!even(layout & - (CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT))) { // no asymetric side - return false; - } - if (!even(layout & (CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT))) { - return false; - } - if (!even(layout & - (CHANNEL_FRONT_LEFT_OF_CENTER | CHANNEL_FRONT_RIGHT_OF_CENTER))) { - return false; - } - if (cubeb_channel_layout_nb_channels(layout) >= CHANNELS_MAX) { - return false; - } - return true; - } - - 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 - const float _surround_mix_level = C_30DB; ///< surround mixing level - const float _center_mix_level = C_30DB; ///< center mixing level - const float _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. -}; - -int MixerContext::auto_matrix() -{ - double matrix[NUM_NAMED_CHANNELS][NUM_NAMED_CHANNELS] = { { 0 } }; - double maxcoef = 0; - float maxval; - - cubeb_channel_layout in_ch_layout = clean_layout(_in_ch_layout); - cubeb_channel_layout out_ch_layout = clean_layout(_out_ch_layout); - - if (!sane_layout(in_ch_layout)) { - // Channel Not Supported - LOG("Input Layout %x is not supported", _in_ch_layout); - return -1; - } - - if (!sane_layout(out_ch_layout)) { - LOG("Output Layout %x is not supported", _out_ch_layout); - return -1; - } - - for (uint32_t i = 0; i < FF_ARRAY_ELEMS(matrix); i++) { - if (in_ch_layout & out_ch_layout & (1U << i)) { - matrix[i][i] = 1.0; - } - } - - cubeb_channel_layout unaccounted = in_ch_layout & ~out_ch_layout; - - // 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. - - if (unaccounted & CHANNEL_FRONT_CENTER) { - if ((out_ch_layout & CUBEB_LAYOUT_STEREO) == CUBEB_LAYOUT_STEREO) { - if (in_ch_layout & CUBEB_LAYOUT_STEREO) { - matrix[FRONT_LEFT][FRONT_CENTER] += _center_mix_level; - matrix[FRONT_RIGHT][FRONT_CENTER] += _center_mix_level; - } else { - matrix[FRONT_LEFT][FRONT_CENTER] += M_SQRT1_2; - matrix[FRONT_RIGHT][FRONT_CENTER] += M_SQRT1_2; - } - } - } - if (unaccounted & CUBEB_LAYOUT_STEREO) { - if (out_ch_layout & CHANNEL_FRONT_CENTER) { - matrix[FRONT_CENTER][FRONT_LEFT] += M_SQRT1_2; - matrix[FRONT_CENTER][FRONT_RIGHT] += M_SQRT1_2; - if (in_ch_layout & CHANNEL_FRONT_CENTER) - matrix[FRONT_CENTER][FRONT_CENTER] = _center_mix_level * M_SQRT2; - } - } - - if (unaccounted & CHANNEL_BACK_CENTER) { - if (out_ch_layout & CHANNEL_BACK_LEFT) { - matrix[BACK_LEFT][BACK_CENTER] += M_SQRT1_2; - matrix[BACK_RIGHT][BACK_CENTER] += M_SQRT1_2; - } else if (out_ch_layout & CHANNEL_SIDE_LEFT) { - matrix[SIDE_LEFT][BACK_CENTER] += M_SQRT1_2; - matrix[SIDE_RIGHT][BACK_CENTER] += M_SQRT1_2; - } else if (out_ch_layout & CHANNEL_FRONT_LEFT) { - matrix[FRONT_LEFT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2; - matrix[FRONT_RIGHT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2; - } else if (out_ch_layout & CHANNEL_FRONT_CENTER) { - matrix[FRONT_CENTER][BACK_CENTER] += - _surround_mix_level * M_SQRT1_2; - } - } - if (unaccounted & CHANNEL_BACK_LEFT) { - if (out_ch_layout & CHANNEL_BACK_CENTER) { - matrix[BACK_CENTER][BACK_LEFT] += M_SQRT1_2; - matrix[BACK_CENTER][BACK_RIGHT] += M_SQRT1_2; - } else if (out_ch_layout & CHANNEL_SIDE_LEFT) { - if (in_ch_layout & CHANNEL_SIDE_LEFT) { - matrix[SIDE_LEFT][BACK_LEFT] += M_SQRT1_2; - matrix[SIDE_RIGHT][BACK_RIGHT] += M_SQRT1_2; - } else { - matrix[SIDE_LEFT][BACK_LEFT] += 1.0; - matrix[SIDE_RIGHT][BACK_RIGHT] += 1.0; - } - } else if (out_ch_layout & CHANNEL_FRONT_LEFT) { - matrix[FRONT_LEFT][BACK_LEFT] += _surround_mix_level; - matrix[FRONT_RIGHT][BACK_RIGHT] += _surround_mix_level; - } else if (out_ch_layout & CHANNEL_FRONT_CENTER) { - matrix[FRONT_CENTER][BACK_LEFT] += _surround_mix_level * M_SQRT1_2; - matrix[FRONT_CENTER][BACK_RIGHT] += _surround_mix_level * M_SQRT1_2; - } - } - - if (unaccounted & CHANNEL_SIDE_LEFT) { - if (out_ch_layout & CHANNEL_BACK_LEFT) { - /* if back channels do not exist in the input, just copy side - channels to back channels, otherwise mix side into back */ - if (in_ch_layout & CHANNEL_BACK_LEFT) { - matrix[BACK_LEFT][SIDE_LEFT] += M_SQRT1_2; - matrix[BACK_RIGHT][SIDE_RIGHT] += M_SQRT1_2; - } else { - matrix[BACK_LEFT][SIDE_LEFT] += 1.0; - matrix[BACK_RIGHT][SIDE_RIGHT] += 1.0; - } - } else if (out_ch_layout & CHANNEL_BACK_CENTER) { - matrix[BACK_CENTER][SIDE_LEFT] += M_SQRT1_2; - matrix[BACK_CENTER][SIDE_RIGHT] += M_SQRT1_2; - } else if (out_ch_layout & CHANNEL_FRONT_LEFT) { - matrix[FRONT_LEFT][SIDE_LEFT] += _surround_mix_level; - matrix[FRONT_RIGHT][SIDE_RIGHT] += _surround_mix_level; - } else if (out_ch_layout & CHANNEL_FRONT_CENTER) { - matrix[FRONT_CENTER][SIDE_LEFT] += _surround_mix_level * M_SQRT1_2; - matrix[FRONT_CENTER][SIDE_RIGHT] += _surround_mix_level * M_SQRT1_2; - } - } - - if (unaccounted & CHANNEL_FRONT_LEFT_OF_CENTER) { - if (out_ch_layout & CHANNEL_FRONT_LEFT) { - matrix[FRONT_LEFT][FRONT_LEFT_OF_CENTER] += 1.0; - matrix[FRONT_RIGHT][FRONT_RIGHT_OF_CENTER] += 1.0; - } else if (out_ch_layout & CHANNEL_FRONT_CENTER) { - matrix[FRONT_CENTER][FRONT_LEFT_OF_CENTER] += M_SQRT1_2; - matrix[FRONT_CENTER][FRONT_RIGHT_OF_CENTER] += M_SQRT1_2; - } - } - /* mix LFE into front left/right or center */ - if (unaccounted & CHANNEL_LOW_FREQUENCY) { - if (out_ch_layout & CHANNEL_FRONT_CENTER) { - matrix[FRONT_CENTER][LOW_FREQUENCY] += _lfe_mix_level; - } else if (out_ch_layout & CHANNEL_FRONT_LEFT) { - matrix[FRONT_LEFT][LOW_FREQUENCY] += _lfe_mix_level * M_SQRT1_2; - matrix[FRONT_RIGHT][LOW_FREQUENCY] += _lfe_mix_level * M_SQRT1_2; - } - } - - // Normalize the conversion matrix. - for (uint32_t out_i = 0, i = 0; i < CHANNELS_MAX; i++) { - double sum = 0; - int in_i = 0; - if ((out_ch_layout & (1U << i)) == 0) { - continue; - } - for (uint32_t j = 0; j < CHANNELS_MAX; j++) { - if ((in_ch_layout & (1U << j)) == 0) { - continue; - } - if (i < FF_ARRAY_ELEMS(matrix) && j < FF_ARRAY_ELEMS(matrix[0])) { - _matrix[out_i][in_i] = matrix[i][j]; - } else { - _matrix[out_i][in_i] = - i == j && (in_ch_layout & out_ch_layout & (1U << i)); - } - sum += fabs(_matrix[out_i][in_i]); - in_i++; - } - maxcoef = std::max(maxcoef, sum); - out_i++; - } - - if (_format == CUBEB_SAMPLE_S16NE) { - maxval = 1.0; - } else { - maxval = INT_MAX; - } - - // 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, - typename std::result_of<F(TYPE_COEFF)>::type>::value, - "function must return the same type as used by matrix_coeff"); - 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, - typename std::result_of<F(TYPE_COEFF)>::type>::value, - "function must return the same type as used by matrix_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> -static int 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, - typename std::result_of<F(TYPE_COEFF)>::type>::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; -} - -struct cubeb_mixer -{ - cubeb_mixer(cubeb_sample_format format, - uint32_t in_channels, - cubeb_channel_layout in_layout, - uint32_t out_channels, - cubeb_channel_layout out_layout) - : _context(format, in_channels, in_layout, out_channels, out_layout) - { - } - - 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; - } - } - } - - int mix(size_t frames, - const void * input_buffer, - size_t input_buffer_size, - void * output_buffer, - size_t output_buffer_size) const - { - if (frames <= 0 || _context._out_ch_count == 0) { - return 0; - } - - // 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<const float*>(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<const float*>(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; } - - virtual ~cubeb_mixer(){}; - - MixerContext _context; -}; - -cubeb_mixer* cubeb_mixer_create(cubeb_sample_format format, - uint32_t in_channels, - cubeb_channel_layout in_layout, - uint32_t out_channels, - cubeb_channel_layout out_layout) -{ - return new cubeb_mixer( - format, in_channels, in_layout, out_channels, out_layout); -} - -void cubeb_mixer_destroy(cubeb_mixer * mixer) -{ - delete mixer; -} - -int cubeb_mixer_mix(cubeb_mixer * mixer, - size_t frames, - const void * input_buffer, - size_t input_buffer_size, - void * output_buffer, - size_t output_buffer_size) -{ - return mixer->mix( - frames, input_buffer, input_buffer_size, output_buffer, output_buffer_size); -} |