xref: /aosp_15_r20/external/webrtc/common_audio/audio_converter.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker  *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker  *
4*d9f75844SAndroid Build Coastguard Worker  *  Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker  *  that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker  *  tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker  *  in the file PATENTS.  All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker  *  be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker  */
10*d9f75844SAndroid Build Coastguard Worker 
11*d9f75844SAndroid Build Coastguard Worker #include "common_audio/audio_converter.h"
12*d9f75844SAndroid Build Coastguard Worker 
13*d9f75844SAndroid Build Coastguard Worker #include <cstring>
14*d9f75844SAndroid Build Coastguard Worker #include <memory>
15*d9f75844SAndroid Build Coastguard Worker #include <utility>
16*d9f75844SAndroid Build Coastguard Worker #include <vector>
17*d9f75844SAndroid Build Coastguard Worker 
18*d9f75844SAndroid Build Coastguard Worker #include "common_audio/channel_buffer.h"
19*d9f75844SAndroid Build Coastguard Worker #include "common_audio/resampler/push_sinc_resampler.h"
20*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
21*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/numerics/safe_conversions.h"
22*d9f75844SAndroid Build Coastguard Worker 
23*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
24*d9f75844SAndroid Build Coastguard Worker 
25*d9f75844SAndroid Build Coastguard Worker class CopyConverter : public AudioConverter {
26*d9f75844SAndroid Build Coastguard Worker  public:
CopyConverter(size_t src_channels,size_t src_frames,size_t dst_channels,size_t dst_frames)27*d9f75844SAndroid Build Coastguard Worker   CopyConverter(size_t src_channels,
28*d9f75844SAndroid Build Coastguard Worker                 size_t src_frames,
29*d9f75844SAndroid Build Coastguard Worker                 size_t dst_channels,
30*d9f75844SAndroid Build Coastguard Worker                 size_t dst_frames)
31*d9f75844SAndroid Build Coastguard Worker       : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {}
~CopyConverter()32*d9f75844SAndroid Build Coastguard Worker   ~CopyConverter() override {}
33*d9f75844SAndroid Build Coastguard Worker 
Convert(const float * const * src,size_t src_size,float * const * dst,size_t dst_capacity)34*d9f75844SAndroid Build Coastguard Worker   void Convert(const float* const* src,
35*d9f75844SAndroid Build Coastguard Worker                size_t src_size,
36*d9f75844SAndroid Build Coastguard Worker                float* const* dst,
37*d9f75844SAndroid Build Coastguard Worker                size_t dst_capacity) override {
38*d9f75844SAndroid Build Coastguard Worker     CheckSizes(src_size, dst_capacity);
39*d9f75844SAndroid Build Coastguard Worker     if (src != dst) {
40*d9f75844SAndroid Build Coastguard Worker       for (size_t i = 0; i < src_channels(); ++i)
41*d9f75844SAndroid Build Coastguard Worker         std::memcpy(dst[i], src[i], dst_frames() * sizeof(*dst[i]));
42*d9f75844SAndroid Build Coastguard Worker     }
43*d9f75844SAndroid Build Coastguard Worker   }
44*d9f75844SAndroid Build Coastguard Worker };
45*d9f75844SAndroid Build Coastguard Worker 
46*d9f75844SAndroid Build Coastguard Worker class UpmixConverter : public AudioConverter {
47*d9f75844SAndroid Build Coastguard Worker  public:
UpmixConverter(size_t src_channels,size_t src_frames,size_t dst_channels,size_t dst_frames)48*d9f75844SAndroid Build Coastguard Worker   UpmixConverter(size_t src_channels,
49*d9f75844SAndroid Build Coastguard Worker                  size_t src_frames,
50*d9f75844SAndroid Build Coastguard Worker                  size_t dst_channels,
51*d9f75844SAndroid Build Coastguard Worker                  size_t dst_frames)
52*d9f75844SAndroid Build Coastguard Worker       : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {}
~UpmixConverter()53*d9f75844SAndroid Build Coastguard Worker   ~UpmixConverter() override {}
54*d9f75844SAndroid Build Coastguard Worker 
Convert(const float * const * src,size_t src_size,float * const * dst,size_t dst_capacity)55*d9f75844SAndroid Build Coastguard Worker   void Convert(const float* const* src,
56*d9f75844SAndroid Build Coastguard Worker                size_t src_size,
57*d9f75844SAndroid Build Coastguard Worker                float* const* dst,
58*d9f75844SAndroid Build Coastguard Worker                size_t dst_capacity) override {
59*d9f75844SAndroid Build Coastguard Worker     CheckSizes(src_size, dst_capacity);
60*d9f75844SAndroid Build Coastguard Worker     for (size_t i = 0; i < dst_frames(); ++i) {
61*d9f75844SAndroid Build Coastguard Worker       const float value = src[0][i];
62*d9f75844SAndroid Build Coastguard Worker       for (size_t j = 0; j < dst_channels(); ++j)
63*d9f75844SAndroid Build Coastguard Worker         dst[j][i] = value;
64*d9f75844SAndroid Build Coastguard Worker     }
65*d9f75844SAndroid Build Coastguard Worker   }
66*d9f75844SAndroid Build Coastguard Worker };
67*d9f75844SAndroid Build Coastguard Worker 
68*d9f75844SAndroid Build Coastguard Worker class DownmixConverter : public AudioConverter {
69*d9f75844SAndroid Build Coastguard Worker  public:
DownmixConverter(size_t src_channels,size_t src_frames,size_t dst_channels,size_t dst_frames)70*d9f75844SAndroid Build Coastguard Worker   DownmixConverter(size_t src_channels,
71*d9f75844SAndroid Build Coastguard Worker                    size_t src_frames,
72*d9f75844SAndroid Build Coastguard Worker                    size_t dst_channels,
73*d9f75844SAndroid Build Coastguard Worker                    size_t dst_frames)
74*d9f75844SAndroid Build Coastguard Worker       : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {}
~DownmixConverter()75*d9f75844SAndroid Build Coastguard Worker   ~DownmixConverter() override {}
76*d9f75844SAndroid Build Coastguard Worker 
Convert(const float * const * src,size_t src_size,float * const * dst,size_t dst_capacity)77*d9f75844SAndroid Build Coastguard Worker   void Convert(const float* const* src,
78*d9f75844SAndroid Build Coastguard Worker                size_t src_size,
79*d9f75844SAndroid Build Coastguard Worker                float* const* dst,
80*d9f75844SAndroid Build Coastguard Worker                size_t dst_capacity) override {
81*d9f75844SAndroid Build Coastguard Worker     CheckSizes(src_size, dst_capacity);
82*d9f75844SAndroid Build Coastguard Worker     float* dst_mono = dst[0];
83*d9f75844SAndroid Build Coastguard Worker     for (size_t i = 0; i < src_frames(); ++i) {
84*d9f75844SAndroid Build Coastguard Worker       float sum = 0;
85*d9f75844SAndroid Build Coastguard Worker       for (size_t j = 0; j < src_channels(); ++j)
86*d9f75844SAndroid Build Coastguard Worker         sum += src[j][i];
87*d9f75844SAndroid Build Coastguard Worker       dst_mono[i] = sum / src_channels();
88*d9f75844SAndroid Build Coastguard Worker     }
89*d9f75844SAndroid Build Coastguard Worker   }
90*d9f75844SAndroid Build Coastguard Worker };
91*d9f75844SAndroid Build Coastguard Worker 
92*d9f75844SAndroid Build Coastguard Worker class ResampleConverter : public AudioConverter {
93*d9f75844SAndroid Build Coastguard Worker  public:
ResampleConverter(size_t src_channels,size_t src_frames,size_t dst_channels,size_t dst_frames)94*d9f75844SAndroid Build Coastguard Worker   ResampleConverter(size_t src_channels,
95*d9f75844SAndroid Build Coastguard Worker                     size_t src_frames,
96*d9f75844SAndroid Build Coastguard Worker                     size_t dst_channels,
97*d9f75844SAndroid Build Coastguard Worker                     size_t dst_frames)
98*d9f75844SAndroid Build Coastguard Worker       : AudioConverter(src_channels, src_frames, dst_channels, dst_frames) {
99*d9f75844SAndroid Build Coastguard Worker     resamplers_.reserve(src_channels);
100*d9f75844SAndroid Build Coastguard Worker     for (size_t i = 0; i < src_channels; ++i)
101*d9f75844SAndroid Build Coastguard Worker       resamplers_.push_back(std::unique_ptr<PushSincResampler>(
102*d9f75844SAndroid Build Coastguard Worker           new PushSincResampler(src_frames, dst_frames)));
103*d9f75844SAndroid Build Coastguard Worker   }
~ResampleConverter()104*d9f75844SAndroid Build Coastguard Worker   ~ResampleConverter() override {}
105*d9f75844SAndroid Build Coastguard Worker 
Convert(const float * const * src,size_t src_size,float * const * dst,size_t dst_capacity)106*d9f75844SAndroid Build Coastguard Worker   void Convert(const float* const* src,
107*d9f75844SAndroid Build Coastguard Worker                size_t src_size,
108*d9f75844SAndroid Build Coastguard Worker                float* const* dst,
109*d9f75844SAndroid Build Coastguard Worker                size_t dst_capacity) override {
110*d9f75844SAndroid Build Coastguard Worker     CheckSizes(src_size, dst_capacity);
111*d9f75844SAndroid Build Coastguard Worker     for (size_t i = 0; i < resamplers_.size(); ++i)
112*d9f75844SAndroid Build Coastguard Worker       resamplers_[i]->Resample(src[i], src_frames(), dst[i], dst_frames());
113*d9f75844SAndroid Build Coastguard Worker   }
114*d9f75844SAndroid Build Coastguard Worker 
115*d9f75844SAndroid Build Coastguard Worker  private:
116*d9f75844SAndroid Build Coastguard Worker   std::vector<std::unique_ptr<PushSincResampler>> resamplers_;
117*d9f75844SAndroid Build Coastguard Worker };
118*d9f75844SAndroid Build Coastguard Worker 
119*d9f75844SAndroid Build Coastguard Worker // Apply a vector of converters in serial, in the order given. At least two
120*d9f75844SAndroid Build Coastguard Worker // converters must be provided.
121*d9f75844SAndroid Build Coastguard Worker class CompositionConverter : public AudioConverter {
122*d9f75844SAndroid Build Coastguard Worker  public:
CompositionConverter(std::vector<std::unique_ptr<AudioConverter>> converters)123*d9f75844SAndroid Build Coastguard Worker   explicit CompositionConverter(
124*d9f75844SAndroid Build Coastguard Worker       std::vector<std::unique_ptr<AudioConverter>> converters)
125*d9f75844SAndroid Build Coastguard Worker       : converters_(std::move(converters)) {
126*d9f75844SAndroid Build Coastguard Worker     RTC_CHECK_GE(converters_.size(), 2);
127*d9f75844SAndroid Build Coastguard Worker     // We need an intermediate buffer after every converter.
128*d9f75844SAndroid Build Coastguard Worker     for (auto it = converters_.begin(); it != converters_.end() - 1; ++it)
129*d9f75844SAndroid Build Coastguard Worker       buffers_.push_back(
130*d9f75844SAndroid Build Coastguard Worker           std::unique_ptr<ChannelBuffer<float>>(new ChannelBuffer<float>(
131*d9f75844SAndroid Build Coastguard Worker               (*it)->dst_frames(), (*it)->dst_channels())));
132*d9f75844SAndroid Build Coastguard Worker   }
~CompositionConverter()133*d9f75844SAndroid Build Coastguard Worker   ~CompositionConverter() override {}
134*d9f75844SAndroid Build Coastguard Worker 
Convert(const float * const * src,size_t src_size,float * const * dst,size_t dst_capacity)135*d9f75844SAndroid Build Coastguard Worker   void Convert(const float* const* src,
136*d9f75844SAndroid Build Coastguard Worker                size_t src_size,
137*d9f75844SAndroid Build Coastguard Worker                float* const* dst,
138*d9f75844SAndroid Build Coastguard Worker                size_t dst_capacity) override {
139*d9f75844SAndroid Build Coastguard Worker     converters_.front()->Convert(src, src_size, buffers_.front()->channels(),
140*d9f75844SAndroid Build Coastguard Worker                                  buffers_.front()->size());
141*d9f75844SAndroid Build Coastguard Worker     for (size_t i = 2; i < converters_.size(); ++i) {
142*d9f75844SAndroid Build Coastguard Worker       auto& src_buffer = buffers_[i - 2];
143*d9f75844SAndroid Build Coastguard Worker       auto& dst_buffer = buffers_[i - 1];
144*d9f75844SAndroid Build Coastguard Worker       converters_[i]->Convert(src_buffer->channels(), src_buffer->size(),
145*d9f75844SAndroid Build Coastguard Worker                               dst_buffer->channels(), dst_buffer->size());
146*d9f75844SAndroid Build Coastguard Worker     }
147*d9f75844SAndroid Build Coastguard Worker     converters_.back()->Convert(buffers_.back()->channels(),
148*d9f75844SAndroid Build Coastguard Worker                                 buffers_.back()->size(), dst, dst_capacity);
149*d9f75844SAndroid Build Coastguard Worker   }
150*d9f75844SAndroid Build Coastguard Worker 
151*d9f75844SAndroid Build Coastguard Worker  private:
152*d9f75844SAndroid Build Coastguard Worker   std::vector<std::unique_ptr<AudioConverter>> converters_;
153*d9f75844SAndroid Build Coastguard Worker   std::vector<std::unique_ptr<ChannelBuffer<float>>> buffers_;
154*d9f75844SAndroid Build Coastguard Worker };
155*d9f75844SAndroid Build Coastguard Worker 
Create(size_t src_channels,size_t src_frames,size_t dst_channels,size_t dst_frames)156*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<AudioConverter> AudioConverter::Create(size_t src_channels,
157*d9f75844SAndroid Build Coastguard Worker                                                        size_t src_frames,
158*d9f75844SAndroid Build Coastguard Worker                                                        size_t dst_channels,
159*d9f75844SAndroid Build Coastguard Worker                                                        size_t dst_frames) {
160*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<AudioConverter> sp;
161*d9f75844SAndroid Build Coastguard Worker   if (src_channels > dst_channels) {
162*d9f75844SAndroid Build Coastguard Worker     if (src_frames != dst_frames) {
163*d9f75844SAndroid Build Coastguard Worker       std::vector<std::unique_ptr<AudioConverter>> converters;
164*d9f75844SAndroid Build Coastguard Worker       converters.push_back(std::unique_ptr<AudioConverter>(new DownmixConverter(
165*d9f75844SAndroid Build Coastguard Worker           src_channels, src_frames, dst_channels, src_frames)));
166*d9f75844SAndroid Build Coastguard Worker       converters.push_back(
167*d9f75844SAndroid Build Coastguard Worker           std::unique_ptr<AudioConverter>(new ResampleConverter(
168*d9f75844SAndroid Build Coastguard Worker               dst_channels, src_frames, dst_channels, dst_frames)));
169*d9f75844SAndroid Build Coastguard Worker       sp.reset(new CompositionConverter(std::move(converters)));
170*d9f75844SAndroid Build Coastguard Worker     } else {
171*d9f75844SAndroid Build Coastguard Worker       sp.reset(new DownmixConverter(src_channels, src_frames, dst_channels,
172*d9f75844SAndroid Build Coastguard Worker                                     dst_frames));
173*d9f75844SAndroid Build Coastguard Worker     }
174*d9f75844SAndroid Build Coastguard Worker   } else if (src_channels < dst_channels) {
175*d9f75844SAndroid Build Coastguard Worker     if (src_frames != dst_frames) {
176*d9f75844SAndroid Build Coastguard Worker       std::vector<std::unique_ptr<AudioConverter>> converters;
177*d9f75844SAndroid Build Coastguard Worker       converters.push_back(
178*d9f75844SAndroid Build Coastguard Worker           std::unique_ptr<AudioConverter>(new ResampleConverter(
179*d9f75844SAndroid Build Coastguard Worker               src_channels, src_frames, src_channels, dst_frames)));
180*d9f75844SAndroid Build Coastguard Worker       converters.push_back(std::unique_ptr<AudioConverter>(new UpmixConverter(
181*d9f75844SAndroid Build Coastguard Worker           src_channels, dst_frames, dst_channels, dst_frames)));
182*d9f75844SAndroid Build Coastguard Worker       sp.reset(new CompositionConverter(std::move(converters)));
183*d9f75844SAndroid Build Coastguard Worker     } else {
184*d9f75844SAndroid Build Coastguard Worker       sp.reset(new UpmixConverter(src_channels, src_frames, dst_channels,
185*d9f75844SAndroid Build Coastguard Worker                                   dst_frames));
186*d9f75844SAndroid Build Coastguard Worker     }
187*d9f75844SAndroid Build Coastguard Worker   } else if (src_frames != dst_frames) {
188*d9f75844SAndroid Build Coastguard Worker     sp.reset(new ResampleConverter(src_channels, src_frames, dst_channels,
189*d9f75844SAndroid Build Coastguard Worker                                    dst_frames));
190*d9f75844SAndroid Build Coastguard Worker   } else {
191*d9f75844SAndroid Build Coastguard Worker     sp.reset(
192*d9f75844SAndroid Build Coastguard Worker         new CopyConverter(src_channels, src_frames, dst_channels, dst_frames));
193*d9f75844SAndroid Build Coastguard Worker   }
194*d9f75844SAndroid Build Coastguard Worker 
195*d9f75844SAndroid Build Coastguard Worker   return sp;
196*d9f75844SAndroid Build Coastguard Worker }
197*d9f75844SAndroid Build Coastguard Worker 
198*d9f75844SAndroid Build Coastguard Worker // For CompositionConverter.
AudioConverter()199*d9f75844SAndroid Build Coastguard Worker AudioConverter::AudioConverter()
200*d9f75844SAndroid Build Coastguard Worker     : src_channels_(0), src_frames_(0), dst_channels_(0), dst_frames_(0) {}
201*d9f75844SAndroid Build Coastguard Worker 
AudioConverter(size_t src_channels,size_t src_frames,size_t dst_channels,size_t dst_frames)202*d9f75844SAndroid Build Coastguard Worker AudioConverter::AudioConverter(size_t src_channels,
203*d9f75844SAndroid Build Coastguard Worker                                size_t src_frames,
204*d9f75844SAndroid Build Coastguard Worker                                size_t dst_channels,
205*d9f75844SAndroid Build Coastguard Worker                                size_t dst_frames)
206*d9f75844SAndroid Build Coastguard Worker     : src_channels_(src_channels),
207*d9f75844SAndroid Build Coastguard Worker       src_frames_(src_frames),
208*d9f75844SAndroid Build Coastguard Worker       dst_channels_(dst_channels),
209*d9f75844SAndroid Build Coastguard Worker       dst_frames_(dst_frames) {
210*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK(dst_channels == src_channels || dst_channels == 1 ||
211*d9f75844SAndroid Build Coastguard Worker             src_channels == 1);
212*d9f75844SAndroid Build Coastguard Worker }
213*d9f75844SAndroid Build Coastguard Worker 
CheckSizes(size_t src_size,size_t dst_capacity) const214*d9f75844SAndroid Build Coastguard Worker void AudioConverter::CheckSizes(size_t src_size, size_t dst_capacity) const {
215*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK_EQ(src_size, src_channels() * src_frames());
216*d9f75844SAndroid Build Coastguard Worker   RTC_CHECK_GE(dst_capacity, dst_channels() * dst_frames());
217*d9f75844SAndroid Build Coastguard Worker }
218*d9f75844SAndroid Build Coastguard Worker 
219*d9f75844SAndroid Build Coastguard Worker }  // namespace webrtc
220