Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 #include "webrtc/modules/audio_processing/audio_buffer.h" | 11 #include "webrtc/modules/audio_processing/audio_buffer.h" |
| 12 | 12 |
| 13 #include "webrtc/common_audio/include/audio_util.h" | 13 #include "webrtc/common_audio/include/audio_util.h" |
| 14 #include "webrtc/common_audio/resampler/push_sinc_resampler.h" | 14 #include "webrtc/common_audio/resampler/push_sinc_resampler.h" |
| 15 #include "webrtc/common_audio/signal_processing/include/signal_processing_librar y.h" | 15 #include "webrtc/common_audio/signal_processing/include/signal_processing_librar y.h" |
| 16 #include "webrtc/common_audio/channel_buffer.h" | 16 #include "webrtc/common_audio/channel_buffer.h" |
| 17 #include "webrtc/modules/audio_processing/common.h" | 17 #include "webrtc/modules/audio_processing/common.h" |
| 18 | 18 |
| 19 namespace webrtc { | 19 namespace webrtc { |
| 20 namespace { | 20 namespace { |
| 21 | 21 |
| 22 const int kSamplesPer16kHzChannel = 160; | 22 const int kSamplesPer16kHzChannel = 160; |
| 23 const int kSamplesPer32kHzChannel = 320; | 23 const int kSamplesPer32kHzChannel = 320; |
| 24 const int kSamplesPer48kHzChannel = 480; | 24 const int kSamplesPer48kHzChannel = 480; |
| 25 | 25 |
| 26 bool HasKeyboardChannel(AudioProcessing::ChannelLayout layout) { | 26 int KeyboardChannelIndex(const StreamConfig& stream_config) { |
|
aluebs-webrtc
2015/07/14 23:12:42
You could check inside here if stream_config.has_k
mgraczyk
2015/07/15 01:12:45
Done, although the only callsite is conditional on
| |
| 27 switch (layout) { | 27 switch (stream_config.num_channels()) { |
| 28 case AudioProcessing::kMono: | 28 case 1: |
| 29 case AudioProcessing::kStereo: | |
| 30 return false; | |
| 31 case AudioProcessing::kMonoAndKeyboard: | |
| 32 case AudioProcessing::kStereoAndKeyboard: | |
| 33 return true; | |
| 34 } | |
| 35 assert(false); | |
| 36 return false; | |
| 37 } | |
| 38 | |
| 39 int KeyboardChannelIndex(AudioProcessing::ChannelLayout layout) { | |
| 40 switch (layout) { | |
| 41 case AudioProcessing::kMono: | |
| 42 case AudioProcessing::kStereo: | |
| 43 assert(false); | |
| 44 return -1; | |
| 45 case AudioProcessing::kMonoAndKeyboard: | |
| 46 return 1; | 29 return 1; |
| 47 case AudioProcessing::kStereoAndKeyboard: | 30 case 2: |
| 48 return 2; | 31 return 2; |
| 49 } | 32 } |
| 50 assert(false); | 33 assert(false); |
| 51 return -1; | 34 return -1; |
| 52 } | 35 } |
| 53 | 36 |
| 54 template <typename T> | 37 template <typename T> |
| 55 void StereoToMono(const T* left, const T* right, T* out, | 38 void DownmixInterleavedToMono(const T* interleaved, |
|
aluebs-webrtc
2015/07/14 23:12:42
Is this specialization used anywhere?
mgraczyk
2015/07/15 01:12:46
This isn't the specialization, it's a normal templ
aluebs-webrtc
2015/07/15 18:04:05
I meant, I don't think we have any interleaved flo
mgraczyk
2015/07/15 20:03:19
That's true. I removed the definition so now the
| |
| 56 int num_frames) { | 39 T* deinterleaved, |
| 57 for (int i = 0; i < num_frames; ++i) | 40 int num_multichannel_frames, |
| 58 out[i] = (left[i] + right[i]) / 2; | 41 int num_channels) { |
|
aluebs-webrtc
2015/07/14 23:12:42
Inputs before outputs?
mgraczyk
2015/07/15 01:12:45
Done.
| |
| 42 return DownmixInterleavedToMonoImpl<T, T>( | |
|
aluebs-webrtc
2015/07/14 23:12:42
This return isn't needed, right?
mgraczyk
2015/07/15 01:12:45
Done.
| |
| 43 interleaved, deinterleaved, num_multichannel_frames, num_channels); | |
| 44 } | |
| 45 | |
| 46 template <> | |
|
aluebs-webrtc
2015/07/14 23:12:42
Why is this template needed? Just curious.
mgraczyk
2015/07/15 01:12:46
I could write a function called "DownmixInterleave
aluebs-webrtc
2015/07/15 18:04:05
Thanks for the explanation! Makes sense :)
| |
| 47 void DownmixInterleavedToMono<int16_t>(const int16_t* interleaved, | |
| 48 int16_t* deinterleaved, | |
| 49 int num_multichannel_frames, | |
| 50 int num_channels) { | |
| 51 return DownmixInterleavedToMonoImpl<int16_t, int32_t>( | |
|
aluebs-webrtc
2015/07/14 23:12:42
This return isn't needed, right?
mgraczyk
2015/07/15 01:12:45
Done.
| |
| 52 interleaved, deinterleaved, num_multichannel_frames, num_channels); | |
| 59 } | 53 } |
| 60 | 54 |
| 61 int NumBandsFromSamplesPerChannel(int num_frames) { | 55 int NumBandsFromSamplesPerChannel(int num_frames) { |
| 62 int num_bands = 1; | 56 int num_bands = 1; |
| 63 if (num_frames == kSamplesPer32kHzChannel || | 57 if (num_frames == kSamplesPer32kHzChannel || |
| 64 num_frames == kSamplesPer48kHzChannel) { | 58 num_frames == kSamplesPer48kHzChannel) { |
| 65 num_bands = rtc::CheckedDivExact(num_frames, | 59 num_bands = rtc::CheckedDivExact(num_frames, |
| 66 static_cast<int>(kSamplesPer16kHzChannel)); | 60 static_cast<int>(kSamplesPer16kHzChannel)); |
| 67 } | 61 } |
| 68 return num_bands; | 62 return num_bands; |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 84 num_bands_(NumBandsFromSamplesPerChannel(proc_num_frames_)), | 78 num_bands_(NumBandsFromSamplesPerChannel(proc_num_frames_)), |
| 85 num_split_frames_(rtc::CheckedDivExact(proc_num_frames_, num_bands_)), | 79 num_split_frames_(rtc::CheckedDivExact(proc_num_frames_, num_bands_)), |
| 86 mixed_low_pass_valid_(false), | 80 mixed_low_pass_valid_(false), |
| 87 reference_copied_(false), | 81 reference_copied_(false), |
| 88 activity_(AudioFrame::kVadUnknown), | 82 activity_(AudioFrame::kVadUnknown), |
| 89 keyboard_data_(NULL), | 83 keyboard_data_(NULL), |
| 90 data_(new IFChannelBuffer(proc_num_frames_, num_proc_channels_)) { | 84 data_(new IFChannelBuffer(proc_num_frames_, num_proc_channels_)) { |
| 91 assert(input_num_frames_ > 0); | 85 assert(input_num_frames_ > 0); |
| 92 assert(proc_num_frames_ > 0); | 86 assert(proc_num_frames_ > 0); |
| 93 assert(output_num_frames_ > 0); | 87 assert(output_num_frames_ > 0); |
| 94 assert(num_input_channels_ > 0 && num_input_channels_ <= 2); | 88 assert(num_input_channels_ > 0); |
| 95 assert(num_proc_channels_ > 0 && num_proc_channels_ <= num_input_channels_); | 89 assert(num_proc_channels_ > 0 && num_proc_channels_ <= num_input_channels_); |
| 96 | 90 |
| 97 if (input_num_frames_ != proc_num_frames_ || | 91 if (input_num_frames_ != proc_num_frames_ || |
| 98 output_num_frames_ != proc_num_frames_) { | 92 output_num_frames_ != proc_num_frames_) { |
| 99 // Create an intermediate buffer for resampling. | 93 // Create an intermediate buffer for resampling. |
| 100 process_buffer_.reset(new ChannelBuffer<float>(proc_num_frames_, | 94 process_buffer_.reset(new ChannelBuffer<float>(proc_num_frames_, |
| 101 num_proc_channels_)); | 95 num_proc_channels_)); |
| 102 | 96 |
| 103 if (input_num_frames_ != proc_num_frames_) { | 97 if (input_num_frames_ != proc_num_frames_) { |
| 104 for (int i = 0; i < num_proc_channels_; ++i) { | 98 for (int i = 0; i < num_proc_channels_; ++i) { |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 123 num_bands_)); | 117 num_bands_)); |
| 124 splitting_filter_.reset(new SplittingFilter(num_proc_channels_, | 118 splitting_filter_.reset(new SplittingFilter(num_proc_channels_, |
| 125 num_bands_, | 119 num_bands_, |
| 126 proc_num_frames_)); | 120 proc_num_frames_)); |
| 127 } | 121 } |
| 128 } | 122 } |
| 129 | 123 |
| 130 AudioBuffer::~AudioBuffer() {} | 124 AudioBuffer::~AudioBuffer() {} |
| 131 | 125 |
| 132 void AudioBuffer::CopyFrom(const float* const* data, | 126 void AudioBuffer::CopyFrom(const float* const* data, |
| 133 int num_frames, | 127 const StreamConfig& stream_config) { |
| 134 AudioProcessing::ChannelLayout layout) { | 128 assert(stream_config.samples_per_channel() == input_num_frames_); |
| 135 assert(num_frames == input_num_frames_); | 129 assert(stream_config.num_channels() == num_input_channels_); |
| 136 assert(ChannelsFromLayout(layout) == num_input_channels_); | |
| 137 InitForNewData(); | 130 InitForNewData(); |
| 138 // Initialized lazily because there's a different condition in | 131 // Initialized lazily because there's a different condition in |
| 139 // DeinterleaveFrom. | 132 // DeinterleaveFrom. |
| 140 if ((num_input_channels_ == 2 && num_proc_channels_ == 1) && !input_buffer_) { | 133 const bool need_to_downmix = |
| 134 num_input_channels_ > 1 && num_proc_channels_ == 1; | |
| 135 if (need_to_downmix && !input_buffer_) { | |
| 141 input_buffer_.reset( | 136 input_buffer_.reset( |
| 142 new IFChannelBuffer(input_num_frames_, num_proc_channels_)); | 137 new IFChannelBuffer(input_num_frames_, num_proc_channels_)); |
| 143 } | 138 } |
| 144 | 139 |
| 145 if (HasKeyboardChannel(layout)) { | 140 if (stream_config.has_keyboard()) { |
| 146 keyboard_data_ = data[KeyboardChannelIndex(layout)]; | 141 keyboard_data_ = data[KeyboardChannelIndex(stream_config)]; |
| 147 } | 142 } |
| 148 | 143 |
| 149 // Downmix. | 144 // Downmix. |
| 150 const float* const* data_ptr = data; | 145 const float* const* data_ptr = data; |
| 151 if (num_input_channels_ == 2 && num_proc_channels_ == 1) { | 146 if (need_to_downmix) { |
| 152 StereoToMono(data[0], | 147 DownmixToMono<float, float>(input_num_frames_, |
| 153 data[1], | 148 input_buffer_->fbuf()->channels()[0], data, |
| 154 input_buffer_->fbuf()->channels()[0], | 149 num_input_channels_); |
| 155 input_num_frames_); | |
| 156 data_ptr = input_buffer_->fbuf_const()->channels(); | 150 data_ptr = input_buffer_->fbuf_const()->channels(); |
| 157 } | 151 } |
| 158 | 152 |
| 159 // Resample. | 153 // Resample. |
| 160 if (input_num_frames_ != proc_num_frames_) { | 154 if (input_num_frames_ != proc_num_frames_) { |
| 161 for (int i = 0; i < num_proc_channels_; ++i) { | 155 for (int i = 0; i < num_proc_channels_; ++i) { |
| 162 input_resamplers_[i]->Resample(data_ptr[i], | 156 input_resamplers_[i]->Resample(data_ptr[i], |
| 163 input_num_frames_, | 157 input_num_frames_, |
| 164 process_buffer_->channels()[i], | 158 process_buffer_->channels()[i], |
| 165 proc_num_frames_); | 159 proc_num_frames_); |
| 166 } | 160 } |
| 167 data_ptr = process_buffer_->channels(); | 161 data_ptr = process_buffer_->channels(); |
| 168 } | 162 } |
| 169 | 163 |
| 170 // Convert to the S16 range. | 164 // Convert to the S16 range. |
| 171 for (int i = 0; i < num_proc_channels_; ++i) { | 165 for (int i = 0; i < num_proc_channels_; ++i) { |
| 172 FloatToFloatS16(data_ptr[i], | 166 FloatToFloatS16(data_ptr[i], |
| 173 proc_num_frames_, | 167 proc_num_frames_, |
| 174 data_->fbuf()->channels()[i]); | 168 data_->fbuf()->channels()[i]); |
| 175 } | 169 } |
| 176 } | 170 } |
| 177 | 171 |
| 178 void AudioBuffer::CopyTo(int num_frames, | 172 void AudioBuffer::CopyTo(const StreamConfig& stream_config, |
| 179 AudioProcessing::ChannelLayout layout, | |
| 180 float* const* data) { | 173 float* const* data) { |
| 181 assert(num_frames == output_num_frames_); | 174 assert(stream_config.samples_per_channel() == output_num_frames_); |
| 182 assert(ChannelsFromLayout(layout) == num_channels_); | 175 assert(stream_config.num_channels() == num_channels_); |
| 183 | 176 |
| 184 // Convert to the float range. | 177 // Convert to the float range. |
| 185 float* const* data_ptr = data; | 178 float* const* data_ptr = data; |
| 186 if (output_num_frames_ != proc_num_frames_) { | 179 if (output_num_frames_ != proc_num_frames_) { |
| 187 // Convert to an intermediate buffer for subsequent resampling. | 180 // Convert to an intermediate buffer for subsequent resampling. |
| 188 data_ptr = process_buffer_->channels(); | 181 data_ptr = process_buffer_->channels(); |
| 189 } | 182 } |
| 190 for (int i = 0; i < num_channels_; ++i) { | 183 for (int i = 0; i < num_channels_; ++i) { |
| 191 FloatS16ToFloat(data_->fbuf()->channels()[i], | 184 FloatS16ToFloat(data_->fbuf()->channels()[i], |
| 192 proc_num_frames_, | 185 proc_num_frames_, |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 332 | 325 |
| 333 if (num_proc_channels_ == 1) { | 326 if (num_proc_channels_ == 1) { |
| 334 return split_bands_const(0)[kBand0To8kHz]; | 327 return split_bands_const(0)[kBand0To8kHz]; |
| 335 } | 328 } |
| 336 | 329 |
| 337 if (!mixed_low_pass_valid_) { | 330 if (!mixed_low_pass_valid_) { |
| 338 if (!mixed_low_pass_channels_.get()) { | 331 if (!mixed_low_pass_channels_.get()) { |
| 339 mixed_low_pass_channels_.reset( | 332 mixed_low_pass_channels_.reset( |
| 340 new ChannelBuffer<int16_t>(num_split_frames_, 1)); | 333 new ChannelBuffer<int16_t>(num_split_frames_, 1)); |
| 341 } | 334 } |
| 342 StereoToMono(split_bands_const(0)[kBand0To8kHz], | 335 DownmixStereoToMono<int16_t, int32_t>( |
|
aluebs-webrtc
2015/07/14 23:12:42
Shouldn't this support multiple channels?
mgraczyk
2015/07/15 01:12:46
Done. Does this work?
aluebs-webrtc
2015/07/15 18:04:05
You don't need to do this manually, audio_buffer/c
mgraczyk
2015/07/15 20:03:19
Nice, Done.
| |
| 343 split_bands_const(1)[kBand0To8kHz], | 336 num_split_frames_, mixed_low_pass_channels_->channels()[0], |
| 344 mixed_low_pass_channels_->channels()[0], | 337 split_bands_const(0)[kBand0To8kHz], split_bands_const(1)[kBand0To8kHz]); |
| 345 num_split_frames_); | |
| 346 mixed_low_pass_valid_ = true; | 338 mixed_low_pass_valid_ = true; |
| 347 } | 339 } |
| 348 return mixed_low_pass_channels_->channels()[0]; | 340 return mixed_low_pass_channels_->channels()[0]; |
| 349 } | 341 } |
| 350 | 342 |
| 351 const int16_t* AudioBuffer::low_pass_reference(int channel) const { | 343 const int16_t* AudioBuffer::low_pass_reference(int channel) const { |
| 352 if (!reference_copied_) { | 344 if (!reference_copied_) { |
| 353 return NULL; | 345 return NULL; |
| 354 } | 346 } |
| 355 | 347 |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 404 new IFChannelBuffer(input_num_frames_, num_proc_channels_)); | 396 new IFChannelBuffer(input_num_frames_, num_proc_channels_)); |
| 405 } | 397 } |
| 406 activity_ = frame->vad_activity_; | 398 activity_ = frame->vad_activity_; |
| 407 | 399 |
| 408 int16_t* const* deinterleaved; | 400 int16_t* const* deinterleaved; |
| 409 if (input_num_frames_ == proc_num_frames_) { | 401 if (input_num_frames_ == proc_num_frames_) { |
| 410 deinterleaved = data_->ibuf()->channels(); | 402 deinterleaved = data_->ibuf()->channels(); |
| 411 } else { | 403 } else { |
| 412 deinterleaved = input_buffer_->ibuf()->channels(); | 404 deinterleaved = input_buffer_->ibuf()->channels(); |
| 413 } | 405 } |
| 414 if (num_input_channels_ == 2 && num_proc_channels_ == 1) { | 406 if (num_proc_channels_ == 1) { |
|
mgraczyk
2015/07/15 01:12:46
This function works for any number of input channe
| |
| 415 // Downmix directly; no explicit deinterleaving needed. | 407 // Downmix and deinterleave simultaneously. |
| 416 for (int i = 0; i < input_num_frames_; ++i) { | 408 DownmixInterleavedToMono(frame->data_, deinterleaved[0], input_num_frames_, |
| 417 deinterleaved[0][i] = (frame->data_[i * 2] + frame->data_[i * 2 + 1]) / 2; | 409 num_input_channels_); |
| 418 } | |
| 419 } else { | 410 } else { |
| 420 assert(num_proc_channels_ == num_input_channels_); | 411 assert(num_proc_channels_ == num_input_channels_); |
| 421 Deinterleave(frame->data_, | 412 Deinterleave(frame->data_, |
| 422 input_num_frames_, | 413 input_num_frames_, |
| 423 num_proc_channels_, | 414 num_proc_channels_, |
| 424 deinterleaved); | 415 deinterleaved); |
| 425 } | 416 } |
| 426 | 417 |
| 427 // Resample. | 418 // Resample. |
| 428 if (input_num_frames_ != proc_num_frames_) { | 419 if (input_num_frames_ != proc_num_frames_) { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 470 | 461 |
| 471 void AudioBuffer::SplitIntoFrequencyBands() { | 462 void AudioBuffer::SplitIntoFrequencyBands() { |
| 472 splitting_filter_->Analysis(data_.get(), split_data_.get()); | 463 splitting_filter_->Analysis(data_.get(), split_data_.get()); |
| 473 } | 464 } |
| 474 | 465 |
| 475 void AudioBuffer::MergeFrequencyBands() { | 466 void AudioBuffer::MergeFrequencyBands() { |
| 476 splitting_filter_->Synthesis(split_data_.get(), data_.get()); | 467 splitting_filter_->Synthesis(split_data_.get(), data_.get()); |
| 477 } | 468 } |
| 478 | 469 |
| 479 } // namespace webrtc | 470 } // namespace webrtc |
| OLD | NEW |