Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // MSVC++ requires this to be set before any other includes to get M_SQRT1_2.
      6 #define _USE_MATH_DEFINES
      7 
      8 #include "media/base/channel_mixer.h"
      9 
     10 #include <algorithm>
     11 #include <cmath>
     12 
     13 #include "base/logging.h"
     14 #include "media/audio/audio_parameters.h"
     15 #include "media/base/audio_bus.h"
     16 #include "media/base/vector_math.h"
     17 
     18 namespace media {
     19 
     20 // Default scale factor for mixing two channels together.  We use a different
     21 // value for stereo -> mono and mono -> stereo mixes.
     22 static const float kEqualPowerScale = static_cast<float>(M_SQRT1_2);
     23 
     24 static void ValidateLayout(ChannelLayout layout) {
     25   CHECK_NE(layout, CHANNEL_LAYOUT_NONE);
     26   CHECK_NE(layout, CHANNEL_LAYOUT_MAX);
     27   CHECK_NE(layout, CHANNEL_LAYOUT_UNSUPPORTED);
     28   CHECK_NE(layout, CHANNEL_LAYOUT_DISCRETE);
     29 
     30   // Verify there's at least one channel.  Should always be true here by virtue
     31   // of not being one of the invalid layouts, but lets double check to be sure.
     32   int channel_count = ChannelLayoutToChannelCount(layout);
     33   DCHECK_GT(channel_count, 0);
     34 
     35   // If we have more than one channel, verify a symmetric layout for sanity.
     36   // The unit test will verify all possible layouts, so this can be a DCHECK.
     37   // Symmetry allows simplifying the matrix building code by allowing us to
     38   // assume that if one channel of a pair exists, the other will too.
     39   if (channel_count > 1) {
     40     DCHECK((ChannelOrder(layout, LEFT) >= 0 &&
     41             ChannelOrder(layout, RIGHT) >= 0) ||
     42            (ChannelOrder(layout, SIDE_LEFT) >= 0 &&
     43             ChannelOrder(layout, SIDE_RIGHT) >= 0) ||
     44            (ChannelOrder(layout, BACK_LEFT) >= 0 &&
     45             ChannelOrder(layout, BACK_RIGHT) >= 0) ||
     46            (ChannelOrder(layout, LEFT_OF_CENTER) >= 0 &&
     47             ChannelOrder(layout, RIGHT_OF_CENTER) >= 0))
     48         << "Non-symmetric channel layout encountered.";
     49   } else {
     50     DCHECK_EQ(layout, CHANNEL_LAYOUT_MONO);
     51   }
     52 
     53   return;
     54 }
     55 
     56 class MatrixBuilder {
     57  public:
     58   MatrixBuilder(ChannelLayout input_layout, int input_channels,
     59                 ChannelLayout output_layout, int output_channels)
     60       : input_layout_(input_layout),
     61         input_channels_(input_channels),
     62         output_layout_(output_layout),
     63         output_channels_(output_channels) {
     64     // Special case for 5.0, 5.1 with back channels when upmixed to 7.0, 7.1,
     65     // which should map the back LR to side LR.
     66     if (input_layout_ == CHANNEL_LAYOUT_5_0_BACK &&
     67         output_layout_ == CHANNEL_LAYOUT_7_0) {
     68       input_layout_ = CHANNEL_LAYOUT_5_0;
     69     } else if (input_layout_ == CHANNEL_LAYOUT_5_1_BACK &&
     70                output_layout_ == CHANNEL_LAYOUT_7_1) {
     71       input_layout_ = CHANNEL_LAYOUT_5_1;
     72     }
     73   }
     74 
     75   ~MatrixBuilder() { }
     76 
     77   // Create the transformation matrix of input channels to output channels.
     78   // Updates the empty matrix with the transformation, and returns true
     79   // if the transformation is just a remapping of channels (no mixing).
     80   bool CreateTransformationMatrix(std::vector< std::vector<float> >* matrix);
     81 
     82  private:
     83   // Result transformation of input channels to output channels
     84   std::vector< std::vector<float> >* matrix_;
     85 
     86   // Input and output channel layout provided during construction.
     87   ChannelLayout input_layout_;
     88   int input_channels_;
     89   ChannelLayout output_layout_;
     90   int output_channels_;
     91 
     92   // Helper variable for tracking which inputs are currently unaccounted,
     93   // should be empty after construction completes.
     94   std::vector<Channels> unaccounted_inputs_;
     95 
     96   // Helper methods for managing unaccounted input channels.
     97   void AccountFor(Channels ch);
     98   bool IsUnaccounted(Channels ch);
     99 
    100   // Helper methods for checking if |ch| exists in either |input_layout_| or
    101   // |output_layout_| respectively.
    102   bool HasInputChannel(Channels ch);
    103   bool HasOutputChannel(Channels ch);
    104 
    105   // Helper methods for updating |matrix_| with the proper value for
    106   // mixing |input_ch| into |output_ch|.  MixWithoutAccounting() does not
    107   // remove the channel from |unaccounted_inputs_|.
    108   void Mix(Channels input_ch, Channels output_ch, float scale);
    109   void MixWithoutAccounting(Channels input_ch, Channels output_ch,
    110                                           float scale);
    111 
    112   DISALLOW_COPY_AND_ASSIGN(MatrixBuilder);
    113 };
    114 
    115 ChannelMixer::ChannelMixer(ChannelLayout input_layout,
    116                            ChannelLayout output_layout) {
    117   Initialize(input_layout,
    118              ChannelLayoutToChannelCount(input_layout),
    119              output_layout,
    120              ChannelLayoutToChannelCount(output_layout));
    121 }
    122 
    123 ChannelMixer::ChannelMixer(
    124     const AudioParameters& input, const AudioParameters& output) {
    125   Initialize(input.channel_layout(),
    126              input.channels(),
    127              output.channel_layout(),
    128              output.channels());
    129 }
    130 
    131 void ChannelMixer::Initialize(
    132     ChannelLayout input_layout, int input_channels,
    133     ChannelLayout output_layout, int output_channels) {
    134   // Stereo down mix should never be the output layout.
    135   CHECK_NE(output_layout, CHANNEL_LAYOUT_STEREO_DOWNMIX);
    136 
    137   // Verify that the layouts are supported
    138   if (input_layout != CHANNEL_LAYOUT_DISCRETE)
    139     ValidateLayout(input_layout);
    140   if (output_layout != CHANNEL_LAYOUT_DISCRETE)
    141     ValidateLayout(output_layout);
    142 
    143   // Create the transformation matrix
    144   MatrixBuilder matrix_builder(input_layout, input_channels,
    145                                output_layout, output_channels);
    146   remapping_ = matrix_builder.CreateTransformationMatrix(&matrix_);
    147 }
    148 
    149 bool MatrixBuilder::CreateTransformationMatrix(
    150     std::vector< std::vector<float> >* matrix) {
    151   matrix_ = matrix;
    152 
    153   // Size out the initial matrix.
    154   matrix_->reserve(output_channels_);
    155   for (int output_ch = 0; output_ch < output_channels_; ++output_ch)
    156     matrix_->push_back(std::vector<float>(input_channels_, 0));
    157 
    158   // First check for discrete case.
    159   if (input_layout_ == CHANNEL_LAYOUT_DISCRETE ||
    160       output_layout_ == CHANNEL_LAYOUT_DISCRETE) {
    161     // If the number of input channels is more than output channels, then
    162     // copy as many as we can then drop the remaining input channels.
    163     // If the number of input channels is less than output channels, then
    164     // copy them all, then zero out the remaining output channels.
    165     int passthrough_channels = std::min(input_channels_, output_channels_);
    166     for (int i = 0; i < passthrough_channels; ++i)
    167       (*matrix_)[i][i] = 1;
    168 
    169     return true;
    170   }
    171 
    172   // Route matching channels and figure out which ones aren't accounted for.
    173   for (Channels ch = LEFT; ch < CHANNELS_MAX;
    174        ch = static_cast<Channels>(ch + 1)) {
    175     int input_ch_index = ChannelOrder(input_layout_, ch);
    176     if (input_ch_index < 0)
    177       continue;
    178 
    179     int output_ch_index = ChannelOrder(output_layout_, ch);
    180     if (output_ch_index < 0) {
    181       unaccounted_inputs_.push_back(ch);
    182       continue;
    183     }
    184 
    185     DCHECK_LT(static_cast<size_t>(output_ch_index), matrix_->size());
    186     DCHECK_LT(static_cast<size_t>(input_ch_index),
    187               (*matrix_)[output_ch_index].size());
    188     (*matrix_)[output_ch_index][input_ch_index] = 1;
    189   }
    190 
    191   // If all input channels are accounted for, there's nothing left to do.
    192   if (unaccounted_inputs_.empty()) {
    193     // Since all output channels map directly to inputs we can optimize.
    194     return true;
    195   }
    196 
    197   // Mix front LR into center.
    198   if (IsUnaccounted(LEFT)) {
    199     // When down mixing to mono from stereo, we need to be careful of full scale
    200     // stereo mixes.  Scaling by 1 / sqrt(2) here will likely lead to clipping
    201     // so we use 1 / 2 instead.
    202     float scale =
    203         (output_layout_ == CHANNEL_LAYOUT_MONO && input_channels_ == 2) ?
    204         0.5 : kEqualPowerScale;
    205     Mix(LEFT, CENTER, scale);
    206     Mix(RIGHT, CENTER, scale);
    207   }
    208 
    209   // Mix center into front LR.
    210   if (IsUnaccounted(CENTER)) {
    211     // When up mixing from mono, just do a copy to front LR.
    212     float scale =
    213         (input_layout_ == CHANNEL_LAYOUT_MONO) ? 1 : kEqualPowerScale;
    214     MixWithoutAccounting(CENTER, LEFT, scale);
    215     Mix(CENTER, RIGHT, scale);
    216   }
    217 
    218   // Mix back LR into: side LR || back center || front LR || front center.
    219   if (IsUnaccounted(BACK_LEFT)) {
    220     if (HasOutputChannel(SIDE_LEFT)) {
    221       // If we have side LR, mix back LR into side LR, but instead if the input
    222       // doesn't have side LR (but output does) copy back LR to side LR.
    223       float scale = HasInputChannel(SIDE_LEFT) ? kEqualPowerScale : 1;
    224       Mix(BACK_LEFT, SIDE_LEFT, scale);
    225       Mix(BACK_RIGHT, SIDE_RIGHT, scale);
    226     } else if (HasOutputChannel(BACK_CENTER)) {
    227       // Mix back LR into back center.
    228       Mix(BACK_LEFT, BACK_CENTER, kEqualPowerScale);
    229       Mix(BACK_RIGHT, BACK_CENTER, kEqualPowerScale);
    230     } else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
    231       // Mix back LR into front LR.
    232       Mix(BACK_LEFT, LEFT, kEqualPowerScale);
    233       Mix(BACK_RIGHT, RIGHT, kEqualPowerScale);
    234     } else {
    235       // Mix back LR into front center.
    236       Mix(BACK_LEFT, CENTER, kEqualPowerScale);
    237       Mix(BACK_RIGHT, CENTER, kEqualPowerScale);
    238     }
    239   }
    240 
    241   // Mix side LR into: back LR || back center || front LR || front center.
    242   if (IsUnaccounted(SIDE_LEFT)) {
    243     if (HasOutputChannel(BACK_LEFT)) {
    244       // If we have back LR, mix side LR into back LR, but instead if the input
    245       // doesn't have back LR (but output does) copy side LR to back LR.
    246       float scale = HasInputChannel(BACK_LEFT) ? kEqualPowerScale : 1;
    247       Mix(SIDE_LEFT, BACK_LEFT, scale);
    248       Mix(SIDE_RIGHT, BACK_RIGHT, scale);
    249     } else if (HasOutputChannel(BACK_CENTER)) {
    250       // Mix side LR into back center.
    251       Mix(SIDE_LEFT, BACK_CENTER, kEqualPowerScale);
    252       Mix(SIDE_RIGHT, BACK_CENTER, kEqualPowerScale);
    253     } else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
    254       // Mix side LR into front LR.
    255       Mix(SIDE_LEFT, LEFT, kEqualPowerScale);
    256       Mix(SIDE_RIGHT, RIGHT, kEqualPowerScale);
    257     } else {
    258       // Mix side LR into front center.
    259       Mix(SIDE_LEFT, CENTER, kEqualPowerScale);
    260       Mix(SIDE_RIGHT, CENTER, kEqualPowerScale);
    261     }
    262   }
    263 
    264   // Mix back center into: back LR || side LR || front LR || front center.
    265   if (IsUnaccounted(BACK_CENTER)) {
    266     if (HasOutputChannel(BACK_LEFT)) {
    267       // Mix back center into back LR.
    268       MixWithoutAccounting(BACK_CENTER, BACK_LEFT, kEqualPowerScale);
    269       Mix(BACK_CENTER, BACK_RIGHT, kEqualPowerScale);
    270     } else if (HasOutputChannel(SIDE_LEFT)) {
    271       // Mix back center into side LR.
    272       MixWithoutAccounting(BACK_CENTER, SIDE_LEFT, kEqualPowerScale);
    273       Mix(BACK_CENTER, SIDE_RIGHT, kEqualPowerScale);
    274     } else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
    275       // Mix back center into front LR.
    276       // TODO(dalecurtis): Not sure about these values?
    277       MixWithoutAccounting(BACK_CENTER, LEFT, kEqualPowerScale);
    278       Mix(BACK_CENTER, RIGHT, kEqualPowerScale);
    279     } else {
    280       // Mix back center into front center.
    281       // TODO(dalecurtis): Not sure about these values?
    282       Mix(BACK_CENTER, CENTER, kEqualPowerScale);
    283     }
    284   }
    285 
    286   // Mix LR of center into: front center || front LR.
    287   if (IsUnaccounted(LEFT_OF_CENTER)) {
    288     if (HasOutputChannel(LEFT)) {
    289       // Mix LR of center into front LR.
    290       Mix(LEFT_OF_CENTER, LEFT, kEqualPowerScale);
    291       Mix(RIGHT_OF_CENTER, RIGHT, kEqualPowerScale);
    292     } else {
    293       // Mix LR of center into front center.
    294       Mix(LEFT_OF_CENTER, CENTER, kEqualPowerScale);
    295       Mix(RIGHT_OF_CENTER, CENTER, kEqualPowerScale);
    296     }
    297   }
    298 
    299   // Mix LFE into: front LR || front center.
    300   if (IsUnaccounted(LFE)) {
    301     if (!HasOutputChannel(CENTER)) {
    302       // Mix LFE into front LR.
    303       MixWithoutAccounting(LFE, LEFT, kEqualPowerScale);
    304       Mix(LFE, RIGHT, kEqualPowerScale);
    305     } else {
    306       // Mix LFE into front center.
    307       Mix(LFE, CENTER, kEqualPowerScale);
    308     }
    309   }
    310 
    311   // All channels should now be accounted for.
    312   DCHECK(unaccounted_inputs_.empty());
    313 
    314   // See if the output |matrix_| is simply a remapping matrix.  If each input
    315   // channel maps to a single output channel we can simply remap.  Doing this
    316   // programmatically is less fragile than logic checks on channel mappings.
    317   for (int output_ch = 0; output_ch < output_channels_; ++output_ch) {
    318     int input_mappings = 0;
    319     for (int input_ch = 0; input_ch < input_channels_; ++input_ch) {
    320       // We can only remap if each row contains a single scale of 1.  I.e., each
    321       // output channel is mapped from a single unscaled input channel.
    322       if ((*matrix_)[output_ch][input_ch] != 1 || ++input_mappings > 1)
    323         return false;
    324     }
    325   }
    326 
    327   // If we've gotten here, |matrix_| is simply a remapping.
    328   return true;
    329 }
    330 
    331 ChannelMixer::~ChannelMixer() {}
    332 
    333 void ChannelMixer::Transform(const AudioBus* input, AudioBus* output) {
    334   CHECK_EQ(matrix_.size(), static_cast<size_t>(output->channels()));
    335   CHECK_EQ(matrix_[0].size(), static_cast<size_t>(input->channels()));
    336   CHECK_EQ(input->frames(), output->frames());
    337 
    338   // Zero initialize |output| so we're accumulating from zero.
    339   output->Zero();
    340 
    341   // If we're just remapping we can simply copy the correct input to output.
    342   if (remapping_) {
    343     for (int output_ch = 0; output_ch < output->channels(); ++output_ch) {
    344       for (int input_ch = 0; input_ch < input->channels(); ++input_ch) {
    345         float scale = matrix_[output_ch][input_ch];
    346         if (scale > 0) {
    347           DCHECK_EQ(scale, 1.0f);
    348           memcpy(output->channel(output_ch), input->channel(input_ch),
    349                  sizeof(*output->channel(output_ch)) * output->frames());
    350           break;
    351         }
    352       }
    353     }
    354     return;
    355   }
    356 
    357   for (int output_ch = 0; output_ch < output->channels(); ++output_ch) {
    358     for (int input_ch = 0; input_ch < input->channels(); ++input_ch) {
    359       float scale = matrix_[output_ch][input_ch];
    360       // Scale should always be positive.  Don't bother scaling by zero.
    361       DCHECK_GE(scale, 0);
    362       if (scale > 0) {
    363         vector_math::FMAC(input->channel(input_ch), scale, output->frames(),
    364                           output->channel(output_ch));
    365       }
    366     }
    367   }
    368 }
    369 
    370 void MatrixBuilder::AccountFor(Channels ch) {
    371   unaccounted_inputs_.erase(std::find(
    372       unaccounted_inputs_.begin(), unaccounted_inputs_.end(), ch));
    373 }
    374 
    375 bool MatrixBuilder::IsUnaccounted(Channels ch) {
    376   return std::find(unaccounted_inputs_.begin(), unaccounted_inputs_.end(),
    377                    ch) != unaccounted_inputs_.end();
    378 }
    379 
    380 bool MatrixBuilder::HasInputChannel(Channels ch) {
    381   return ChannelOrder(input_layout_, ch) >= 0;
    382 }
    383 
    384 bool MatrixBuilder::HasOutputChannel(Channels ch) {
    385   return ChannelOrder(output_layout_, ch) >= 0;
    386 }
    387 
    388 void MatrixBuilder::Mix(Channels input_ch, Channels output_ch, float scale) {
    389   MixWithoutAccounting(input_ch, output_ch, scale);
    390   AccountFor(input_ch);
    391 }
    392 
    393 void MatrixBuilder::MixWithoutAccounting(Channels input_ch, Channels output_ch,
    394                                          float scale) {
    395   int input_ch_index = ChannelOrder(input_layout_, input_ch);
    396   int output_ch_index = ChannelOrder(output_layout_, output_ch);
    397 
    398   DCHECK(IsUnaccounted(input_ch));
    399   DCHECK_GE(input_ch_index, 0);
    400   DCHECK_GE(output_ch_index, 0);
    401 
    402   DCHECK_EQ((*matrix_)[output_ch_index][input_ch_index], 0);
    403   (*matrix_)[output_ch_index][input_ch_index] = scale;
    404 }
    405 
    406 }  // namespace media
    407