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