andrew@webrtc.org | aada86b | 2014-10-27 18:18:17 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2014 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 | |
Mirko Bonadei | 92ea95e | 2017-09-15 04:47:31 | [diff] [blame] | 11 | #include "common_audio/audio_converter.h" |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 12 | |
| 13 | #include <cstring> |
kwiberg | 4a206a9 | 2016-03-31 17:24:26 | [diff] [blame] | 14 | #include <memory> |
kwiberg | 0eb15ed | 2015-12-17 11:04:15 | [diff] [blame] | 15 | #include <utility> |
kwiberg | 4a206a9 | 2016-03-31 17:24:26 | [diff] [blame] | 16 | #include <vector> |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 17 | |
Mirko Bonadei | 92ea95e | 2017-09-15 04:47:31 | [diff] [blame] | 18 | #include "common_audio/channel_buffer.h" |
| 19 | #include "common_audio/resampler/push_sinc_resampler.h" |
| 20 | #include "rtc_base/checks.h" |
Karl Wiberg | e40468b | 2017-11-22 09:42:26 | [diff] [blame] | 21 | #include "rtc_base/numerics/safe_conversions.h" |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 22 | |
andrew@webrtc.org | aada86b | 2014-10-27 18:18:17 | [diff] [blame] | 23 | namespace webrtc { |
andrew@webrtc.org | aada86b | 2014-10-27 18:18:17 | [diff] [blame] | 24 | |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 25 | class CopyConverter : public AudioConverter { |
| 26 | public: |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 27 | CopyConverter(size_t src_channels, |
| 28 | size_t src_frames, |
| 29 | size_t dst_channels, |
Peter Kasting | dce40cf | 2015-08-24 21:52:23 | [diff] [blame] | 30 | size_t dst_frames) |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 31 | : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {} |
Nico Weber | 22f9925 | 2019-02-20 15:13:16 | [diff] [blame] | 32 | ~CopyConverter() override {} |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 33 | |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 34 | void Convert(const float* const* src, |
| 35 | size_t src_size, |
| 36 | float* const* dst, |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 37 | size_t dst_capacity) override { |
| 38 | CheckSizes(src_size, dst_capacity); |
| 39 | if (src != dst) { |
Peter Kasting | 6955870 | 2016-01-13 00:26:35 | [diff] [blame] | 40 | for (size_t i = 0; i < src_channels(); ++i) |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 41 | std::memcpy(dst[i], src[i], dst_frames() * sizeof(*dst[i])); |
| 42 | } |
andrew@webrtc.org | aada86b | 2014-10-27 18:18:17 | [diff] [blame] | 43 | } |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 44 | }; |
| 45 | |
| 46 | class UpmixConverter : public AudioConverter { |
| 47 | public: |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 48 | UpmixConverter(size_t src_channels, |
| 49 | size_t src_frames, |
| 50 | size_t dst_channels, |
Peter Kasting | dce40cf | 2015-08-24 21:52:23 | [diff] [blame] | 51 | size_t dst_frames) |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 52 | : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {} |
Nico Weber | 22f9925 | 2019-02-20 15:13:16 | [diff] [blame] | 53 | ~UpmixConverter() override {} |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 54 | |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 55 | void Convert(const float* const* src, |
| 56 | size_t src_size, |
| 57 | float* const* dst, |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 58 | size_t dst_capacity) override { |
| 59 | CheckSizes(src_size, dst_capacity); |
Peter Kasting | dce40cf | 2015-08-24 21:52:23 | [diff] [blame] | 60 | for (size_t i = 0; i < dst_frames(); ++i) { |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 61 | const float value = src[0][i]; |
Peter Kasting | 6955870 | 2016-01-13 00:26:35 | [diff] [blame] | 62 | for (size_t j = 0; j < dst_channels(); ++j) |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 63 | dst[j][i] = value; |
| 64 | } |
| 65 | } |
| 66 | }; |
| 67 | |
| 68 | class DownmixConverter : public AudioConverter { |
| 69 | public: |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 70 | DownmixConverter(size_t src_channels, |
| 71 | size_t src_frames, |
| 72 | size_t dst_channels, |
Peter Kasting | dce40cf | 2015-08-24 21:52:23 | [diff] [blame] | 73 | size_t dst_frames) |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 74 | : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {} |
Nico Weber | 22f9925 | 2019-02-20 15:13:16 | [diff] [blame] | 75 | ~DownmixConverter() override {} |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 76 | |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 77 | void Convert(const float* const* src, |
| 78 | size_t src_size, |
| 79 | float* const* dst, |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 80 | size_t dst_capacity) override { |
| 81 | CheckSizes(src_size, dst_capacity); |
| 82 | float* dst_mono = dst[0]; |
Peter Kasting | dce40cf | 2015-08-24 21:52:23 | [diff] [blame] | 83 | for (size_t i = 0; i < src_frames(); ++i) { |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 84 | float sum = 0; |
Peter Kasting | 6955870 | 2016-01-13 00:26:35 | [diff] [blame] | 85 | for (size_t j = 0; j < src_channels(); ++j) |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 86 | sum += src[j][i]; |
| 87 | dst_mono[i] = sum / src_channels(); |
| 88 | } |
| 89 | } |
| 90 | }; |
| 91 | |
| 92 | class ResampleConverter : public AudioConverter { |
| 93 | public: |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 94 | ResampleConverter(size_t src_channels, |
| 95 | size_t src_frames, |
| 96 | size_t dst_channels, |
Peter Kasting | dce40cf | 2015-08-24 21:52:23 | [diff] [blame] | 97 | size_t dst_frames) |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 98 | : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) { |
| 99 | resamplers_.reserve(src_channels); |
Peter Kasting | 6955870 | 2016-01-13 00:26:35 | [diff] [blame] | 100 | for (size_t i = 0; i < src_channels; ++i) |
kwiberg | 4a206a9 | 2016-03-31 17:24:26 | [diff] [blame] | 101 | resamplers_.push_back(std::unique_ptr<PushSincResampler>( |
| 102 | new PushSincResampler(src_frames, dst_frames))); |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 103 | } |
Nico Weber | 22f9925 | 2019-02-20 15:13:16 | [diff] [blame] | 104 | ~ResampleConverter() override {} |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 105 | |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 106 | void Convert(const float* const* src, |
| 107 | size_t src_size, |
| 108 | float* const* dst, |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 109 | size_t dst_capacity) override { |
| 110 | CheckSizes(src_size, dst_capacity); |
| 111 | for (size_t i = 0; i < resamplers_.size(); ++i) |
| 112 | resamplers_[i]->Resample(src[i], src_frames(), dst[i], dst_frames()); |
| 113 | } |
| 114 | |
| 115 | private: |
kwiberg | 4a206a9 | 2016-03-31 17:24:26 | [diff] [blame] | 116 | std::vector<std::unique_ptr<PushSincResampler>> resamplers_; |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 117 | }; |
| 118 | |
| 119 | // Apply a vector of converters in serial, in the order given. At least two |
| 120 | // converters must be provided. |
| 121 | class CompositionConverter : public AudioConverter { |
| 122 | public: |
oprypin | 67fdb80 | 2017-03-09 14:25:06 | [diff] [blame] | 123 | explicit CompositionConverter( |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 124 | std::vector<std::unique_ptr<AudioConverter>> converters) |
kwiberg | 0eb15ed | 2015-12-17 11:04:15 | [diff] [blame] | 125 | : converters_(std::move(converters)) { |
kwiberg | af476c7 | 2016-11-28 23:21:39 | [diff] [blame] | 126 | RTC_CHECK_GE(converters_.size(), 2); |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 127 | // We need an intermediate buffer after every converter. |
| 128 | for (auto it = converters_.begin(); it != converters_.end() - 1; ++it) |
kwiberg | 4a206a9 | 2016-03-31 17:24:26 | [diff] [blame] | 129 | buffers_.push_back( |
| 130 | std::unique_ptr<ChannelBuffer<float>>(new ChannelBuffer<float>( |
| 131 | (*it)->dst_frames(), (*it)->dst_channels()))); |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 132 | } |
Nico Weber | 22f9925 | 2019-02-20 15:13:16 | [diff] [blame] | 133 | ~CompositionConverter() override {} |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 134 | |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 135 | void Convert(const float* const* src, |
| 136 | size_t src_size, |
| 137 | float* const* dst, |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 138 | size_t dst_capacity) override { |
| 139 | converters_.front()->Convert(src, src_size, buffers_.front()->channels(), |
| 140 | buffers_.front()->size()); |
| 141 | for (size_t i = 2; i < converters_.size(); ++i) { |
kwiberg | 4a206a9 | 2016-03-31 17:24:26 | [diff] [blame] | 142 | auto& src_buffer = buffers_[i - 2]; |
| 143 | auto& dst_buffer = buffers_[i - 1]; |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 144 | converters_[i]->Convert(src_buffer->channels(), src_buffer->size(), |
| 145 | dst_buffer->channels(), dst_buffer->size()); |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 146 | } |
| 147 | converters_.back()->Convert(buffers_.back()->channels(), |
| 148 | buffers_.back()->size(), dst, dst_capacity); |
| 149 | } |
| 150 | |
| 151 | private: |
kwiberg | 4a206a9 | 2016-03-31 17:24:26 | [diff] [blame] | 152 | std::vector<std::unique_ptr<AudioConverter>> converters_; |
| 153 | std::vector<std::unique_ptr<ChannelBuffer<float>>> buffers_; |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 154 | }; |
| 155 | |
kwiberg | c2b785d | 2016-02-24 13:22:32 | [diff] [blame] | 156 | std::unique_ptr<AudioConverter> AudioConverter::Create(size_t src_channels, |
Peter Kasting | dce40cf | 2015-08-24 21:52:23 | [diff] [blame] | 157 | size_t src_frames, |
Peter Kasting | 6955870 | 2016-01-13 00:26:35 | [diff] [blame] | 158 | size_t dst_channels, |
Peter Kasting | dce40cf | 2015-08-24 21:52:23 | [diff] [blame] | 159 | size_t dst_frames) { |
kwiberg | c2b785d | 2016-02-24 13:22:32 | [diff] [blame] | 160 | std::unique_ptr<AudioConverter> sp; |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 161 | if (src_channels > dst_channels) { |
| 162 | if (src_frames != dst_frames) { |
kwiberg | 4a206a9 | 2016-03-31 17:24:26 | [diff] [blame] | 163 | std::vector<std::unique_ptr<AudioConverter>> converters; |
| 164 | converters.push_back(std::unique_ptr<AudioConverter>(new DownmixConverter( |
| 165 | src_channels, src_frames, dst_channels, src_frames))); |
| 166 | converters.push_back( |
| 167 | std::unique_ptr<AudioConverter>(new ResampleConverter( |
| 168 | dst_channels, src_frames, dst_channels, dst_frames))); |
kwiberg | 0eb15ed | 2015-12-17 11:04:15 | [diff] [blame] | 169 | sp.reset(new CompositionConverter(std::move(converters))); |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 170 | } else { |
| 171 | sp.reset(new DownmixConverter(src_channels, src_frames, dst_channels, |
| 172 | dst_frames)); |
| 173 | } |
| 174 | } else if (src_channels < dst_channels) { |
| 175 | if (src_frames != dst_frames) { |
kwiberg | 4a206a9 | 2016-03-31 17:24:26 | [diff] [blame] | 176 | std::vector<std::unique_ptr<AudioConverter>> converters; |
| 177 | converters.push_back( |
| 178 | std::unique_ptr<AudioConverter>(new ResampleConverter( |
| 179 | src_channels, src_frames, src_channels, dst_frames))); |
| 180 | converters.push_back(std::unique_ptr<AudioConverter>(new UpmixConverter( |
| 181 | src_channels, dst_frames, dst_channels, dst_frames))); |
kwiberg | 0eb15ed | 2015-12-17 11:04:15 | [diff] [blame] | 182 | sp.reset(new CompositionConverter(std::move(converters))); |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 183 | } else { |
| 184 | sp.reset(new UpmixConverter(src_channels, src_frames, dst_channels, |
| 185 | dst_frames)); |
| 186 | } |
| 187 | } else if (src_frames != dst_frames) { |
| 188 | sp.reset(new ResampleConverter(src_channels, src_frames, dst_channels, |
| 189 | dst_frames)); |
| 190 | } else { |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 191 | sp.reset( |
| 192 | new CopyConverter(src_channels, src_frames, dst_channels, dst_frames)); |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 193 | } |
| 194 | |
kwiberg | 0eb15ed | 2015-12-17 11:04:15 | [diff] [blame] | 195 | return sp; |
andrew@webrtc.org | aada86b | 2014-10-27 18:18:17 | [diff] [blame] | 196 | } |
| 197 | |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 198 | // For CompositionConverter. |
| 199 | AudioConverter::AudioConverter() |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 200 | : src_channels_(0), src_frames_(0), dst_channels_(0), dst_frames_(0) {} |
andrew@webrtc.org | aada86b | 2014-10-27 18:18:17 | [diff] [blame] | 201 | |
Yves Gerey | 665174f | 2018-06-19 13:03:05 | [diff] [blame] | 202 | AudioConverter::AudioConverter(size_t src_channels, |
| 203 | size_t src_frames, |
| 204 | size_t dst_channels, |
| 205 | size_t dst_frames) |
andrew@webrtc.org | 5804936 | 2014-11-03 21:32:14 | [diff] [blame] | 206 | : src_channels_(src_channels), |
| 207 | src_frames_(src_frames), |
| 208 | dst_channels_(dst_channels), |
| 209 | dst_frames_(dst_frames) { |
henrikg | 91d6ede | 2015-09-17 07:24:34 | [diff] [blame] | 210 | RTC_CHECK(dst_channels == src_channels || dst_channels == 1 || |
| 211 | src_channels == 1); |
andrew@webrtc.org | aada86b | 2014-10-27 18:18:17 | [diff] [blame] | 212 | } |
| 213 | |
andrew@webrtc.org | 2c29c2e | 2015-02-11 01:09:50 | [diff] [blame] | 214 | void AudioConverter::CheckSizes(size_t src_size, size_t dst_capacity) const { |
henrikg | 91d6ede | 2015-09-17 07:24:34 | [diff] [blame] | 215 | RTC_CHECK_EQ(src_size, src_channels() * src_frames()); |
| 216 | RTC_CHECK_GE(dst_capacity, dst_channels() * dst_frames()); |
andrew@webrtc.org | aada86b | 2014-10-27 18:18:17 | [diff] [blame] | 217 | } |
| 218 | |
| 219 | } // namespace webrtc |