LCOV - code coverage report
Current view: top level - media/libcubeb/src - cubeb_mixer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 161 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 30 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
       8             : #include <cassert>
       9             : #include "cubeb-internal.h"
      10             : #include "cubeb_mixer.h"
      11             : 
      12             : // DUAL_MONO(_LFE) is same as STEREO(_LFE).
      13             : #define MASK_MONO         (1 << CHANNEL_MONO)
      14             : #define MASK_MONO_LFE     (MASK_MONO | (1 << CHANNEL_LFE))
      15             : #define MASK_STEREO       ((1 << CHANNEL_LEFT) | (1 << CHANNEL_RIGHT))
      16             : #define MASK_STEREO_LFE   (MASK_STEREO | (1 << CHANNEL_LFE))
      17             : #define MASK_3F           (MASK_STEREO | (1 << CHANNEL_CENTER))
      18             : #define MASK_3F_LFE       (MASK_3F | (1 << CHANNEL_LFE))
      19             : #define MASK_2F1          (MASK_STEREO | (1 << CHANNEL_RCENTER))
      20             : #define MASK_2F1_LFE      (MASK_2F1 | (1 << CHANNEL_LFE))
      21             : #define MASK_3F1          (MASK_3F | (1 << CHANNEL_RCENTER))
      22             : #define MASK_3F1_LFE      (MASK_3F1 | (1 << CHANNEL_LFE))
      23             : #define MASK_2F2          (MASK_STEREO | (1 << CHANNEL_LS) | (1 << CHANNEL_RS))
      24             : #define MASK_2F2_LFE      (MASK_2F2 | (1 << CHANNEL_LFE))
      25             : #define MASK_3F2          (MASK_2F2 | (1 << CHANNEL_CENTER))
      26             : #define MASK_3F2_LFE      (MASK_3F2 | (1 << CHANNEL_LFE))
      27             : #define MASK_3F3R_LFE     (MASK_3F2_LFE | (1 << CHANNEL_RCENTER))
      28             : #define MASK_3F4_LFE      (MASK_3F2_LFE | (1 << CHANNEL_RLS) | (1 << CHANNEL_RRS))
      29             : 
      30           0 : cubeb_channel_layout cubeb_channel_map_to_layout(cubeb_channel_map const * channel_map)
      31             : {
      32           0 :   uint32_t channel_mask = 0;
      33           0 :   for (uint8_t i = 0 ; i < channel_map->channels ; ++i) {
      34           0 :     if (channel_map->map[i] == CHANNEL_INVALID ||
      35           0 :         channel_map->map[i] == CHANNEL_UNMAPPED) {
      36           0 :       return CUBEB_LAYOUT_UNDEFINED;
      37             :     }
      38           0 :     channel_mask |= 1 << channel_map->map[i];
      39             :   }
      40             : 
      41           0 :   switch(channel_mask) {
      42           0 :     case MASK_MONO: return CUBEB_LAYOUT_MONO;
      43           0 :     case MASK_MONO_LFE: return CUBEB_LAYOUT_MONO_LFE;
      44           0 :     case MASK_STEREO: return CUBEB_LAYOUT_STEREO;
      45           0 :     case MASK_STEREO_LFE: return CUBEB_LAYOUT_STEREO_LFE;
      46           0 :     case MASK_3F: return CUBEB_LAYOUT_3F;
      47           0 :     case MASK_3F_LFE: return CUBEB_LAYOUT_3F_LFE;
      48           0 :     case MASK_2F1: return CUBEB_LAYOUT_2F1;
      49           0 :     case MASK_2F1_LFE: return CUBEB_LAYOUT_2F1_LFE;
      50           0 :     case MASK_3F1: return CUBEB_LAYOUT_3F1;
      51           0 :     case MASK_3F1_LFE: return CUBEB_LAYOUT_3F1_LFE;
      52           0 :     case MASK_2F2: return CUBEB_LAYOUT_2F2;
      53           0 :     case MASK_2F2_LFE: return CUBEB_LAYOUT_2F2_LFE;
      54           0 :     case MASK_3F2: return CUBEB_LAYOUT_3F2;
      55           0 :     case MASK_3F2_LFE: return CUBEB_LAYOUT_3F2_LFE;
      56           0 :     case MASK_3F3R_LFE: return CUBEB_LAYOUT_3F3R_LFE;
      57           0 :     case MASK_3F4_LFE: return CUBEB_LAYOUT_3F4_LFE;
      58           0 :     default: return CUBEB_LAYOUT_UNDEFINED;
      59             :   }
      60             : }
      61             : 
      62             : cubeb_layout_map const CUBEB_CHANNEL_LAYOUT_MAPS[CUBEB_LAYOUT_MAX] = {
      63             :   { "undefined",      0,  CUBEB_LAYOUT_UNDEFINED },
      64             :   { "dual mono",      2,  CUBEB_LAYOUT_DUAL_MONO },
      65             :   { "dual mono lfe",  3,  CUBEB_LAYOUT_DUAL_MONO_LFE },
      66             :   { "mono",           1,  CUBEB_LAYOUT_MONO },
      67             :   { "mono lfe",       2,  CUBEB_LAYOUT_MONO_LFE },
      68             :   { "stereo",         2,  CUBEB_LAYOUT_STEREO },
      69             :   { "stereo lfe",     3,  CUBEB_LAYOUT_STEREO_LFE },
      70             :   { "3f",             3,  CUBEB_LAYOUT_3F },
      71             :   { "3f lfe",         4,  CUBEB_LAYOUT_3F_LFE },
      72             :   { "2f1",            3,  CUBEB_LAYOUT_2F1 },
      73             :   { "2f1 lfe",        4,  CUBEB_LAYOUT_2F1_LFE },
      74             :   { "3f1",            4,  CUBEB_LAYOUT_3F1 },
      75             :   { "3f1 lfe",        5,  CUBEB_LAYOUT_3F1_LFE },
      76             :   { "2f2",            4,  CUBEB_LAYOUT_2F2 },
      77             :   { "2f2 lfe",        5,  CUBEB_LAYOUT_2F2_LFE },
      78             :   { "3f2",            5,  CUBEB_LAYOUT_3F2 },
      79             :   { "3f2 lfe",        6,  CUBEB_LAYOUT_3F2_LFE },
      80             :   { "3f3r lfe",       7,  CUBEB_LAYOUT_3F3R_LFE },
      81             :   { "3f4 lfe",        8,  CUBEB_LAYOUT_3F4_LFE }
      82             : };
      83             : 
      84             : static int const CHANNEL_ORDER_TO_INDEX[CUBEB_LAYOUT_MAX][CHANNEL_MAX] = {
      85             : //  M | L | R | C | LS | RS | RLS | RC | RRS | LFE
      86             :   { -1, -1, -1, -1,  -1,  -1,   -1,  -1,   -1,  -1 }, // UNDEFINED
      87             :   { -1,  0,  1, -1,  -1,  -1,   -1,  -1,   -1,  -1 }, // DUAL_MONO
      88             :   { -1,  0,  1, -1,  -1,  -1,   -1,  -1,   -1,   2 }, // DUAL_MONO_LFE
      89             :   {  0, -1, -1, -1,  -1,  -1,   -1,  -1,   -1,  -1 }, // MONO
      90             :   {  0, -1, -1, -1,  -1,  -1,   -1,  -1,   -1,   1 }, // MONO_LFE
      91             :   { -1,  0,  1, -1,  -1,  -1,   -1,  -1,   -1,  -1 }, // STEREO
      92             :   { -1,  0,  1, -1,  -1,  -1,   -1,  -1,   -1,   2 }, // STEREO_LFE
      93             :   { -1,  0,  1,  2,  -1,  -1,   -1,  -1,   -1,  -1 }, // 3F
      94             :   { -1,  0,  1,  2,  -1,  -1,   -1,  -1,   -1,   3 }, // 3F_LFE
      95             :   { -1,  0,  1, -1,  -1,  -1,   -1,   2,   -1,  -1 }, // 2F1
      96             :   { -1,  0,  1, -1,  -1,  -1,   -1,   3,   -1,   2 }, // 2F1_LFE
      97             :   { -1,  0,  1,  2,  -1,  -1,   -1,   3,   -1,  -1 }, // 3F1
      98             :   { -1,  0,  1,  2,  -1,  -1,   -1,   4,   -1,   3 }, // 3F1_LFE
      99             :   { -1,  0,  1, -1,   2,   3,   -1,  -1,   -1,  -1 }, // 2F2
     100             :   { -1,  0,  1, -1,   3,   4,   -1,  -1,   -1,   2 }, // 2F2_LFE
     101             :   { -1,  0,  1,  2,   3,   4,   -1,  -1,   -1,  -1 }, // 3F2
     102             :   { -1,  0,  1,  2,   4,   5,   -1,  -1,   -1,   3 }, // 3F2_LFE
     103             :   { -1,  0,  1,  2,   5,   6,   -1,   4,   -1,   3 }, // 3F3R_LFE
     104             :   { -1,  0,  1,  2,   6,   7,    4,  -1,    5,   3 }, // 3F4_LFE
     105             : };
     106             : 
     107             : // The downmix matrix from TABLE 2 in the ITU-R BS.775-3[1] defines a way to
     108             : // convert 3F2 input data to 1F, 2F, 3F, 2F1, 3F1, 2F2 output data. We extend it
     109             : // to convert 3F2-LFE input data to 1F, 2F, 3F, 2F1, 3F1, 2F2 and their LFEs
     110             : // output data.
     111             : // [1] https://www.itu.int/dms_pubrec/itu-r/rec/bs/R-REC-BS.775-3-201208-I!!PDF-E.pdf
     112             : 
     113             : // Number of converted layouts: 1F, 2F, 3F, 2F1, 3F1, 2F2 and their LFEs.
     114             : unsigned int const SUPPORTED_LAYOUT_NUM = 12;
     115             : // Number of input channel for downmix conversion.
     116             : unsigned int const INPUT_CHANNEL_NUM = 6; // 3F2-LFE
     117             : // Max number of possible output channels.
     118             : unsigned int const MAX_OUTPUT_CHANNEL_NUM = 5; // 2F2-LFE or 3F1-LFE
     119             : float const INV_SQRT_2 = 0.707106f; // 1/sqrt(2)
     120             : // Each array contains coefficients that will be multiplied with
     121             : // { L, R, C, LFE, LS, RS } channels respectively.
     122             : static float const DOWNMIX_MATRIX_3F2_LFE[SUPPORTED_LAYOUT_NUM][MAX_OUTPUT_CHANNEL_NUM][INPUT_CHANNEL_NUM] =
     123             : {
     124             : // 1F Mono
     125             :   {
     126             :     { INV_SQRT_2, INV_SQRT_2, 1, 0, 0.5, 0.5 }, // M
     127             :   },
     128             : // 1F Mono-LFE
     129             :   {
     130             :     { INV_SQRT_2, INV_SQRT_2, 1, 0, 0.5, 0.5 }, // M
     131             :     { 0, 0, 0, 1, 0, 0 }                        // LFE
     132             :   },
     133             : // 2F Stereo
     134             :   {
     135             :     { 1, 0, INV_SQRT_2, 0, INV_SQRT_2, 0 },     // L
     136             :     { 0, 1, INV_SQRT_2, 0, 0, INV_SQRT_2 }      // R
     137             :   },
     138             : // 2F Stereo-LFE
     139             :   {
     140             :     { 1, 0, INV_SQRT_2, 0, INV_SQRT_2, 0 },     // L
     141             :     { 0, 1, INV_SQRT_2, 0, 0, INV_SQRT_2 },     // R
     142             :     { 0, 0, 0, 1, 0, 0 }                        // LFE
     143             :   },
     144             : // 3F
     145             :   {
     146             :     { 1, 0, 0, 0, INV_SQRT_2, 0 },              // L
     147             :     { 0, 1, 0, 0, 0, INV_SQRT_2 },              // R
     148             :     { 0, 0, 1, 0, 0, 0 }                        // C
     149             :   },
     150             : // 3F-LFE
     151             :   {
     152             :     { 1, 0, 0, 0, INV_SQRT_2, 0 },              // L
     153             :     { 0, 1, 0, 0, 0, INV_SQRT_2 },              // R
     154             :     { 0, 0, 1, 0, 0, 0 },                       // C
     155             :     { 0, 0, 0, 1, 0, 0 }                        // LFE
     156             :   },
     157             : // 2F1
     158             :   {
     159             :     { 1, 0, INV_SQRT_2, 0, 0, 0 },              // L
     160             :     { 0, 1, INV_SQRT_2, 0, 0, 0 },              // R
     161             :     { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 }      // S
     162             :   },
     163             : // 2F1-LFE
     164             :   {
     165             :     { 1, 0, INV_SQRT_2, 0, 0, 0 },              // L
     166             :     { 0, 1, INV_SQRT_2, 0, 0, 0 },              // R
     167             :     { 0, 0, 0, 1, 0, 0 },                       // LFE
     168             :     { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 }      // S
     169             :   },
     170             : // 3F1
     171             :   {
     172             :     { 1, 0, 0, 0, 0, 0 },                       // L
     173             :     { 0, 1, 0, 0, 0, 0 },                       // R
     174             :     { 0, 0, 1, 0, 0, 0 },                       // C
     175             :     { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 }      // S
     176             :   },
     177             : // 3F1-LFE
     178             :   {
     179             :     { 1, 0, 0, 0, 0, 0 },                       // L
     180             :     { 0, 1, 0, 0, 0, 0 },                       // R
     181             :     { 0, 0, 1, 0, 0, 0 },                       // C
     182             :     { 0, 0, 0, 1, 0, 0 },                       // LFE
     183             :     { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 }      // S
     184             :   },
     185             : // 2F2
     186             :   {
     187             :     { 1, 0, INV_SQRT_2, 0, 0, 0 },              // L
     188             :     { 0, 1, INV_SQRT_2, 0, 0, 0 },              // R
     189             :     { 0, 0, 0, 0, 1, 0 },                       // LS
     190             :     { 0, 0, 0, 0, 0, 1 }                        // RS
     191             :   },
     192             : // 2F2-LFE
     193             :   {
     194             :     { 1, 0, INV_SQRT_2, 0, 0, 0 },              // L
     195             :     { 0, 1, INV_SQRT_2, 0, 0, 0 },              // R
     196             :     { 0, 0, 0, 1, 0, 0 },                       // LFE
     197             :     { 0, 0, 0, 0, 1, 0 },                       // LS
     198             :     { 0, 0, 0, 0, 0, 1 }                        // RS
     199             :   }
     200             : };
     201             : 
     202             : // Convert audio data from 3F2(-LFE) to 1F, 2F, 3F, 2F1, 3F1, 2F2 and their LFEs.
     203             : //
     204             : // ITU-R BS.775-3[1] provides spec for downmixing 3F2 data to 1F, 2F, 3F, 2F1,
     205             : // 3F1, 2F2 data. We simply add LFE to its defined matrix. If both the input
     206             : // and output have LFE channel, then we pass it's data. If only input or output
     207             : // has LFE, then we either drop it or append 0 to the LFE channel.
     208             : //
     209             : // Fig. 1:
     210             : // |<-------------- 1 -------------->|<-------------- 2 -------------->|
     211             : // +----+----+----+------+-----+-----+----+----+----+------+-----+-----+
     212             : // | L0 | R0 | C0 | LFE0 | LS0 | RS0 | L1 | R1 | C1 | LFE1 | LS1 | RS1 | ...
     213             : // +----+----+----+------+-----+-----+----+----+----+------+-----+-----+
     214             : //
     215             : // Fig. 2:
     216             : // |<-- 1 -->|<-- 2 -->|
     217             : // +----+----+----+----+
     218             : // | L0 | R0 | L1 | R1 | ...
     219             : // +----+----+----+----+
     220             : //
     221             : // The figures above shows an example for downmixing from 3F2-LFE(Fig. 1) to
     222             : // to stereo(Fig. 2), where L0 = L0 + 0.707 * (C0 + LS0),
     223             : // R0 = R0 + 0.707 * (C0 + RS0), L1 = L1 + 0.707 * (C1 + LS1),
     224             : // R1 = R1 + 0.707 * (C1 + RS1), ...
     225             : //
     226             : // Nevertheless, the downmixing method is a little bit different on OSX.
     227             : // The audio rendering mechanism on OS X will drop the extra channels beyond
     228             : // the channels that audio device can provide. The trick here is that OSX allows
     229             : // us to set the layout containing other channels that the output device can
     230             : // NOT provide. For example, setting 3F2-LFE layout to a stereo device is fine.
     231             : // Therefore, OSX expects we fill the buffer for playing sound by the defined
     232             : // layout, so there are some will-be-dropped data in the buffer:
     233             : //
     234             : // +---+---+---+-----+----+----+
     235             : // | L | R | C | LFE | LS | RS | ...
     236             : // +---+---+---+-----+----+----+
     237             : //           ^    ^    ^    ^
     238             : //           The data for these four channels will be dropped!
     239             : //
     240             : // To keep all the information, we need to downmix the data before it's dropped.
     241             : // The figure below shows an example for downmixing from 3F2-LFE(Fig. 1)
     242             : // to stereo(Fig. 3) on OSX, where the LO, R0, L1, R0 are same as above.
     243             : //
     244             : // Fig. 3:
     245             : // |<---------- 1 ---------->|<---------- 2 ---------->|
     246             : // +----+----+---+---+---+---+----+----+---+---+---+---+
     247             : // | L0 | R0 | x | x | x | x | L1 | R1 | x | x | x | x | ...
     248             : // +----+----+---+---+---+---+----+----+---+---+---+---+
     249             : //           |<--  dummy  -->|         |<--  dummy  -->|
     250             : template<typename T>
     251             : bool
     252           0 : downmix_3f2(unsigned long inframes,
     253             :             T const * const in, unsigned long in_len,
     254             :             T * out, unsigned long out_len,
     255             :             cubeb_channel_layout in_layout, cubeb_channel_layout out_layout)
     256             : {
     257           0 :   if ((in_layout != CUBEB_LAYOUT_3F2 && in_layout != CUBEB_LAYOUT_3F2_LFE) ||
     258           0 :       out_layout < CUBEB_LAYOUT_MONO || out_layout > CUBEB_LAYOUT_2F2_LFE) {
     259           0 :     return false;
     260             :   }
     261             : 
     262           0 :   unsigned int in_channels = CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels;
     263           0 :   unsigned int out_channels = CUBEB_CHANNEL_LAYOUT_MAPS[out_layout].channels;
     264             : 
     265             :   // Conversion from 3F2 to 2F2-LFE or 3F1-LFE is allowed, so we use '<=' instead of '<'.
     266           0 :   assert(out_channels <= in_channels);
     267             : 
     268           0 :   auto & downmix_matrix = DOWNMIX_MATRIX_3F2_LFE[out_layout - CUBEB_LAYOUT_MONO]; // The matrix is started from mono.
     269           0 :   unsigned long out_index = 0;
     270           0 :   for (unsigned long i = 0 ; i < inframes * in_channels; i += in_channels) {
     271           0 :     for (unsigned int j = 0; j < out_channels; ++j) {
     272           0 :       T sample = 0;
     273           0 :       for (unsigned int k = 0 ; k < INPUT_CHANNEL_NUM ; ++k) {
     274             :         // 3F2-LFE has 6 channels: L, R, C, LFE, LS, RS, while 3F2 has only 5
     275             :         // channels: L, R, C, LS, RS. Thus, we need to append 0 to LFE(index 3)
     276             :         // to simulate a 3F2-LFE data when input layout is 3F2.
     277           0 :         assert((in_layout == CUBEB_LAYOUT_3F2_LFE || k < 3) ? (i + k < in_len) : (k == 3) ? true : (i + k - 1 < in_len));
     278           0 :         T data = (in_layout == CUBEB_LAYOUT_3F2_LFE) ? in[i + k] : (k == 3) ? 0 : in[i + ((k < 3) ? k : k - 1)];
     279           0 :         sample += downmix_matrix[j][k] * data;
     280             :       }
     281           0 :       assert(out_index + j < out_len);
     282           0 :       out[out_index + j] = sample;
     283             :     }
     284             : #if defined(USE_AUDIOUNIT)
     285             :     out_index += in_channels;
     286             : #else
     287           0 :     out_index += out_channels;
     288             : #endif
     289             :   }
     290             : 
     291           0 :   return true;
     292             : }
     293             : 
     294             : /* Map the audio data by channel name. */
     295             : template<class T>
     296             : bool
     297           0 : mix_remap(long inframes,
     298             :           T const * const in, unsigned long in_len,
     299             :           T * out, unsigned long out_len,
     300             :           cubeb_channel_layout in_layout, cubeb_channel_layout out_layout)
     301             : {
     302           0 :   assert(in_layout != out_layout);
     303             : 
     304             :   // We might overwrite the data before we copied them to the mapped index
     305             :   // (e.g. upmixing from stereo to 2F1 or mapping [L, R] to [R, L])
     306           0 :   if (in == out) {
     307           0 :     return false;
     308             :   }
     309             : 
     310           0 :   unsigned int in_channels = CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels;
     311           0 :   unsigned int out_channels = CUBEB_CHANNEL_LAYOUT_MAPS[out_layout].channels;
     312             : 
     313           0 :   uint32_t in_layout_mask = 0;
     314           0 :   for (unsigned int i = 0 ; i < in_channels ; ++i) {
     315           0 :     in_layout_mask |= 1 << CHANNEL_INDEX_TO_ORDER[in_layout][i];
     316             :   }
     317             : 
     318           0 :   uint32_t out_layout_mask = 0;
     319           0 :   for (unsigned int i = 0 ; i < out_channels ; ++i) {
     320           0 :     out_layout_mask |= 1 << CHANNEL_INDEX_TO_ORDER[out_layout][i];
     321             :   }
     322             : 
     323             :   // If there is no matched channel, then do nothing.
     324           0 :   if (!(out_layout_mask & in_layout_mask)) {
     325           0 :     return false;
     326             :   }
     327             : 
     328           0 :   for (long i = 0, out_index = 0; i < inframes * in_channels; i += in_channels, out_index += out_channels) {
     329           0 :     for (unsigned int j = 0; j < out_channels; ++j) {
     330           0 :       cubeb_channel channel = CHANNEL_INDEX_TO_ORDER[out_layout][j];
     331           0 :       uint32_t channel_mask = 1 << channel;
     332           0 :       int channel_index = CHANNEL_ORDER_TO_INDEX[in_layout][channel];
     333           0 :       assert((unsigned long)out_index + j < out_len);
     334           0 :       if (in_layout_mask & channel_mask) {
     335           0 :         assert((unsigned long)i + channel_index < in_len);
     336           0 :         assert(channel_index != -1);
     337           0 :         out[out_index + j] = in[i + channel_index];
     338             :       } else {
     339           0 :         assert(channel_index == -1);
     340           0 :         out[out_index + j] = 0;
     341             :       }
     342             :     }
     343             :   }
     344             : 
     345           0 :   return true;
     346             : }
     347             : 
     348             : /* Drop the extra channels beyond the provided output channels. */
     349             : template<typename T>
     350             : void
     351           0 : downmix_fallback(long inframes,
     352             :                  T const * const in, unsigned long in_len,
     353             :                  T * out, unsigned long out_len,
     354             :                  unsigned int in_channels, unsigned int out_channels)
     355             : {
     356           0 :   assert(in_channels >= out_channels);
     357             : 
     358           0 :   if (in_channels == out_channels && in == out) {
     359           0 :     return;
     360             :   }
     361             : 
     362           0 :   for (long i = 0, out_index = 0; i < inframes * in_channels; i += in_channels, out_index += out_channels) {
     363           0 :     for (unsigned int j = 0; j < out_channels; ++j) {
     364           0 :       assert((unsigned long)i + j < in_len && (unsigned long)out_index + j < out_len);
     365           0 :       out[out_index + j] = in[i + j];
     366             :     }
     367             :   }
     368             : }
     369             : 
     370             : 
     371             : template<typename T>
     372             : void
     373           0 : cubeb_downmix(long inframes,
     374             :               T const * const in, unsigned long in_len,
     375             :               T * out, unsigned long out_len,
     376             :               cubeb_stream_params const * stream_params,
     377             :               cubeb_stream_params const * mixer_params)
     378             : {
     379           0 :   assert(in && out);
     380           0 :   assert(inframes);
     381           0 :   assert(stream_params->channels >= mixer_params->channels &&
     382             :          mixer_params->channels > 0);
     383           0 :   assert(stream_params->layout != CUBEB_LAYOUT_UNDEFINED);
     384             : 
     385           0 :   unsigned int in_channels = stream_params->channels;
     386           0 :   cubeb_channel_layout in_layout = stream_params->layout;
     387             : 
     388           0 :   unsigned int out_channels = mixer_params->channels;
     389           0 :   cubeb_channel_layout out_layout = mixer_params->layout;
     390             : 
     391             :   // If the channel number is different from the layout's setting,
     392             :   // then we use fallback downmix mechanism.
     393           0 :   if (out_channels == CUBEB_CHANNEL_LAYOUT_MAPS[out_layout].channels &&
     394           0 :       in_channels == CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels) {
     395           0 :     if (downmix_3f2(inframes, in, in_len, out, out_len, in_layout, out_layout)) {
     396           0 :       return;
     397             :     }
     398             : 
     399             : #if defined(USE_AUDIOUNIT)
     400             :   // We only support downmix for audio 5.1 on OS X currently.
     401             :   return;
     402             : #endif
     403             : 
     404           0 :     if (mix_remap(inframes, in, in_len, out, out_len, in_layout, out_layout)) {
     405           0 :       return;
     406             :     }
     407             :   }
     408             : 
     409           0 :   downmix_fallback(inframes, in, in_len, out, out_len, in_channels, out_channels);
     410             : }
     411             : 
     412             : /* Upmix function, copies a mono channel into L and R. */
     413             : template<typename T>
     414             : void
     415           0 : mono_to_stereo(long insamples, T const * in, unsigned long in_len,
     416             :                T * out, unsigned long out_len, unsigned int out_channels)
     417             : {
     418           0 :   for (long i = 0, j = 0; i < insamples; ++i, j += out_channels) {
     419           0 :     assert((unsigned long)i < in_len && (unsigned long)j + 1 < out_len);
     420           0 :     out[j] = out[j + 1] = in[i];
     421             :   }
     422           0 : }
     423             : 
     424             : template<typename T>
     425             : void
     426           0 : cubeb_upmix(long inframes,
     427             :             T const * const in, unsigned long in_len,
     428             :             T * out, unsigned long out_len,
     429             :             cubeb_stream_params const * stream_params,
     430             :             cubeb_stream_params const * mixer_params)
     431             : {
     432           0 :   assert(in && out);
     433           0 :   assert(inframes);
     434           0 :   assert(mixer_params->channels >= stream_params->channels &&
     435             :          stream_params->channels > 0);
     436             : 
     437           0 :   unsigned int in_channels = stream_params->channels;
     438           0 :   unsigned int out_channels = mixer_params->channels;
     439             : 
     440             :   /* Either way, if we have 2 or more channels, the first two are L and R. */
     441             :   /* If we are playing a mono stream over stereo speakers, copy the data over. */
     442           0 :   if (in_channels == 1 && out_channels >= 2) {
     443           0 :     mono_to_stereo(inframes, in, in_len, out, out_len, out_channels);
     444             :   } else {
     445             :     /* Copy through. */
     446           0 :     for (unsigned int i = 0, o = 0; i < inframes * in_channels;
     447             :         i += in_channels, o += out_channels) {
     448           0 :       for (unsigned int j = 0; j < in_channels; ++j) {
     449           0 :         assert(i + j < in_len && o + j < out_len);
     450           0 :         out[o + j] = in[i + j];
     451             :       }
     452             :     }
     453             :   }
     454             : 
     455             :   /* Check if more channels. */
     456           0 :   if (out_channels <= 2) {
     457           0 :     return;
     458             :   }
     459             : 
     460             :   /* Put silence in remaining channels. */
     461           0 :   for (long i = 0, o = 0; i < inframes; ++i, o += out_channels) {
     462           0 :     for (unsigned int j = 2; j < out_channels; ++j) {
     463           0 :       assert((unsigned long)o + j < out_len);
     464           0 :       out[o + j] = 0.0;
     465             :     }
     466             :   }
     467             : }
     468             : 
     469             : bool
     470           0 : cubeb_should_upmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
     471             : {
     472           0 :   return mixer->channels > stream->channels;
     473             : }
     474             : 
     475             : bool
     476           0 : cubeb_should_downmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
     477             : {
     478           0 :   if (mixer->channels > stream->channels || mixer->layout == stream->layout) {
     479           0 :     return false;
     480             :   }
     481             : 
     482           0 :   return mixer->channels < stream->channels ||
     483             :          // When mixer.channels == stream.channels
     484           0 :          mixer->layout == CUBEB_LAYOUT_UNDEFINED ||  // fallback downmix
     485           0 :          (stream->layout == CUBEB_LAYOUT_3F2 &&      // 3f2 downmix
     486           0 :           (mixer->layout == CUBEB_LAYOUT_2F2_LFE ||
     487           0 :            mixer->layout == CUBEB_LAYOUT_3F1_LFE));
     488             : }
     489             : 
     490             : bool
     491           0 : cubeb_should_mix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
     492             : {
     493           0 :   return cubeb_should_upmix(stream, mixer) || cubeb_should_downmix(stream, mixer);
     494             : }
     495             : 
     496           0 : struct cubeb_mixer {
     497             :   virtual void mix(long frames,
     498             :                    void * input_buffer, unsigned long input_buffer_length,
     499             :                    void * output_buffer, unsigned long output_buffer_length,
     500             :                    cubeb_stream_params const * stream_params,
     501             :                    cubeb_stream_params const * mixer_params) = 0;
     502           0 :   virtual ~cubeb_mixer() {};
     503             : };
     504             : 
     505             : template<typename T>
     506             : struct cubeb_mixer_impl : public cubeb_mixer {
     507           0 :   explicit cubeb_mixer_impl(unsigned int d)
     508           0 :     : direction(d)
     509             :   {
     510           0 :   }
     511             : 
     512           0 :   void mix(long frames,
     513             :            void * input_buffer, unsigned long input_buffer_length,
     514             :            void * output_buffer, unsigned long output_buffer_length,
     515             :            cubeb_stream_params const * stream_params,
     516             :            cubeb_stream_params const * mixer_params)
     517             :   {
     518           0 :     if (frames <= 0) {
     519           0 :       return;
     520             :     }
     521             : 
     522           0 :     T * in = static_cast<T*>(input_buffer);
     523           0 :     T * out = static_cast<T*>(output_buffer);
     524             : 
     525           0 :     if ((direction & CUBEB_MIXER_DIRECTION_DOWNMIX) &&
     526           0 :         cubeb_should_downmix(stream_params, mixer_params)) {
     527           0 :       cubeb_downmix(frames, in, input_buffer_length, out, output_buffer_length, stream_params, mixer_params);
     528           0 :     } else if ((direction & CUBEB_MIXER_DIRECTION_UPMIX) &&
     529           0 :                cubeb_should_upmix(stream_params, mixer_params)) {
     530           0 :       cubeb_upmix(frames, in, input_buffer_length, out, output_buffer_length, stream_params, mixer_params);
     531             :     }
     532             :   }
     533             : 
     534           0 :   ~cubeb_mixer_impl() {};
     535             : 
     536             :   unsigned char const direction;
     537             : };
     538             : 
     539           0 : cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format,
     540             :                                  unsigned char direction)
     541             : {
     542           0 :   assert(direction & CUBEB_MIXER_DIRECTION_DOWNMIX ||
     543             :          direction & CUBEB_MIXER_DIRECTION_UPMIX);
     544           0 :   switch(format) {
     545             :     case CUBEB_SAMPLE_S16NE:
     546           0 :       return new cubeb_mixer_impl<short>(direction);
     547             :     case CUBEB_SAMPLE_FLOAT32NE:
     548           0 :       return new cubeb_mixer_impl<float>(direction);
     549             :     default:
     550           0 :       assert(false);
     551             :       return nullptr;
     552             :   }
     553             : }
     554             : 
     555           0 : void cubeb_mixer_destroy(cubeb_mixer * mixer)
     556             : {
     557           0 :   delete mixer;
     558           0 : }
     559             : 
     560           0 : void cubeb_mixer_mix(cubeb_mixer * mixer, long frames,
     561             :                      void * input_buffer, unsigned long input_buffer_length,
     562             :                      void * output_buffer, unsigned long outputput_buffer_length,
     563             :                      cubeb_stream_params const * stream_params,
     564             :                      cubeb_stream_params const * mixer_params)
     565             : {
     566           0 :   assert(mixer);
     567             :   mixer->mix(frames, input_buffer, input_buffer_length, output_buffer, outputput_buffer_length,
     568           0 :              stream_params, mixer_params);
     569           0 : }

Generated by: LCOV version 1.13