channel_mixer.cc (4147B)
1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "audio/utility/channel_mixer.h" 12 13 #include <cstddef> 14 #include <cstdint> 15 #include <cstring> 16 17 #include "api/audio/audio_frame.h" 18 #include "api/audio/channel_layout.h" 19 #include "audio/utility/channel_mixing_matrix.h" 20 #include "rtc_base/checks.h" 21 #include "rtc_base/numerics/safe_conversions.h" 22 23 namespace webrtc { 24 25 ChannelMixer::ChannelMixer(ChannelLayout input_layout, 26 size_t input_channels, 27 ChannelLayout output_layout, 28 size_t output_channels) 29 : input_layout_(input_layout), 30 output_layout_(output_layout), 31 input_channels_(input_channels), 32 output_channels_(output_channels) { 33 // Create the transformation matrix. 34 ChannelMixingMatrix matrix_builder(input_layout_, input_channels_, 35 output_layout_, output_channels_); 36 remapping_ = matrix_builder.CreateTransformationMatrix(&matrix_); 37 } 38 39 ChannelMixer::ChannelMixer(ChannelLayout input_layout, 40 ChannelLayout output_layout) 41 : ChannelMixer(input_layout, 42 ChannelLayoutToChannelCount(input_layout), 43 output_layout, 44 ChannelLayoutToChannelCount(output_layout)) {} 45 46 ChannelMixer::~ChannelMixer() = default; 47 48 void ChannelMixer::Transform(AudioFrame* frame) { 49 RTC_DCHECK(frame); 50 RTC_DCHECK_EQ(matrix_[0].size(), static_cast<size_t>(input_channels_)); 51 RTC_DCHECK_EQ(matrix_.size(), static_cast<size_t>(output_channels_)); 52 53 // Leave the audio frame intact if the channel layouts for in and out are 54 // identical. 55 if (input_layout_ == output_layout_) { 56 return; 57 } 58 59 if (IsUpMixing()) { 60 RTC_CHECK_LE(frame->samples_per_channel() * output_channels_, 61 frame->max_16bit_samples()); 62 } 63 64 // Only change the number of output channels if the audio frame is muted. 65 if (frame->muted()) { 66 frame->SetLayoutAndNumChannels(output_layout_, output_channels_); 67 return; 68 } 69 70 const int16_t* in_audio = frame->data(); 71 72 // Only allocate fresh memory at first access or if the required size has 73 // increased. 74 // TODO(henrika): we might be able to do downmixing in-place and thereby avoid 75 // extra memory allocation and a memcpy. 76 const size_t num_elements = frame->samples_per_channel() * output_channels_; 77 if (audio_vector_ == nullptr || num_elements > audio_vector_size_) { 78 audio_vector_.reset(new int16_t[num_elements]); 79 audio_vector_size_ = num_elements; 80 } 81 int16_t* out_audio = audio_vector_.get(); 82 83 // Modify the number of channels by creating a weighted sum of input samples 84 // where the weights (scale factors) for each output sample are given by the 85 // transformation matrix. 86 for (size_t i = 0; i < frame->samples_per_channel(); i++) { 87 for (size_t output_ch = 0; output_ch < output_channels_; ++output_ch) { 88 float acc_value = 0.0f; 89 for (size_t input_ch = 0; input_ch < input_channels_; ++input_ch) { 90 const float scale = matrix_[output_ch][input_ch]; 91 // Scale should always be positive. 92 RTC_DCHECK_GE(scale, 0); 93 // Each output sample is a weighted sum of input samples. 94 acc_value += scale * in_audio[i * input_channels_ + input_ch]; 95 } 96 const size_t index = output_channels_ * i + output_ch; 97 RTC_CHECK_LE(index, audio_vector_size_); 98 out_audio[index] = saturated_cast<int16_t>(acc_value); 99 } 100 } 101 102 // Update channel information. 103 frame->SetLayoutAndNumChannels(output_layout_, output_channels_); 104 105 // Copy the output result to the audio frame in `frame`. 106 memcpy( 107 frame->mutable_data(), out_audio, 108 sizeof(int16_t) * frame->samples_per_channel() * frame->num_channels()); 109 } 110 111 } // namespace webrtc