• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef RESAMPLER_MULTICHANNEL_RESAMPLER_H
18 #define RESAMPLER_MULTICHANNEL_RESAMPLER_H
19 
20 #include <memory>
21 #include <vector>
22 #include <sys/types.h>
23 #include <unistd.h>
24 
25 #ifndef MCR_USE_KAISER
26 // It appears from the spectrogram that the HyperbolicCosine window leads to fewer artifacts.
27 // And it is faster to calculate.
28 #define MCR_USE_KAISER 0
29 #endif
30 
31 #if MCR_USE_KAISER
32 #include "KaiserWindow.h"
33 #else
34 #include "HyperbolicCosineWindow.h"
35 #endif
36 
37 #include "ResamplerDefinitions.h"
38 
39 namespace RESAMPLER_OUTER_NAMESPACE::resampler {
40 
41 class MultiChannelResampler {
42 
43 public:
44 
45     enum class Quality : int32_t {
46         Fastest,
47         Low,
48         Medium,
49         High,
50         Best,
51     };
52 
53     class Builder {
54     public:
55         /**
56          * Construct an optimal resampler based on the specified parameters.
57          * @return address of a resampler
58          */
59         MultiChannelResampler *build();
60 
61         /**
62          * The number of taps in the resampling filter.
63          * More taps gives better quality but uses more CPU time.
64          * This typically ranges from 4 to 64. Default is 16.
65          *
66          * For polyphase filters, numTaps must be a multiple of four for loop unrolling.
67          * @param numTaps number of taps for the filter
68          * @return address of this builder for chaining calls
69          */
setNumTaps(int32_t numTaps)70         Builder *setNumTaps(int32_t numTaps) {
71             mNumTaps = numTaps;
72             return this;
73         }
74 
75         /**
76          * Use 1 for mono, 2 for stereo, etc. Default is 1.
77          *
78          * @param channelCount number of channels
79          * @return address of this builder for chaining calls
80          */
setChannelCount(int32_t channelCount)81         Builder *setChannelCount(int32_t channelCount) {
82             mChannelCount = channelCount;
83             return this;
84         }
85 
86         /**
87          * Default is 48000.
88          *
89          * @param inputRate sample rate of the input stream
90          * @return address of this builder for chaining calls
91          */
setInputRate(int32_t inputRate)92         Builder *setInputRate(int32_t inputRate) {
93             mInputRate = inputRate;
94             return this;
95         }
96 
97         /**
98          * Default is 48000.
99          *
100          * @param outputRate sample rate of the output stream
101          * @return address of this builder for chaining calls
102          */
setOutputRate(int32_t outputRate)103         Builder *setOutputRate(int32_t outputRate) {
104             mOutputRate = outputRate;
105             return this;
106         }
107 
108         /**
109          * Set cutoff frequency relative to the Nyquist rate of the output sample rate.
110          * Set to 1.0 to match the Nyquist frequency.
111          * Set lower to reduce aliasing.
112          * Default is 0.70.
113          *
114          * @param normalizedCutoff anti-aliasing filter cutoff
115          * @return address of this builder for chaining calls
116          */
setNormalizedCutoff(float normalizedCutoff)117         Builder *setNormalizedCutoff(float normalizedCutoff) {
118             mNormalizedCutoff = normalizedCutoff;
119             return this;
120         }
121 
getNumTaps()122         int32_t getNumTaps() const {
123             return mNumTaps;
124         }
125 
getChannelCount()126         int32_t getChannelCount() const {
127             return mChannelCount;
128         }
129 
getInputRate()130         int32_t getInputRate() const {
131             return mInputRate;
132         }
133 
getOutputRate()134         int32_t getOutputRate() const {
135             return mOutputRate;
136         }
137 
getNormalizedCutoff()138         float getNormalizedCutoff() const {
139             return mNormalizedCutoff;
140         }
141 
142     protected:
143         int32_t mChannelCount = 1;
144         int32_t mNumTaps = 16;
145         int32_t mInputRate = 48000;
146         int32_t mOutputRate = 48000;
147         float   mNormalizedCutoff = kDefaultNormalizedCutoff;
148     };
149 
150     virtual ~MultiChannelResampler() = default;
151 
152     /**
153      * Factory method for making a resampler that is optimal for the given inputs.
154      *
155      * @param channelCount number of channels, 2 for stereo
156      * @param inputRate sample rate of the input stream
157      * @param outputRate  sample rate of the output stream
158      * @param quality higher quality sounds better but uses more CPU
159      * @return an optimal resampler
160      */
161     static MultiChannelResampler *make(int32_t channelCount,
162                                        int32_t inputRate,
163                                        int32_t outputRate,
164                                        Quality quality);
165 
isWriteNeeded()166     bool isWriteNeeded() const {
167         return mIntegerPhase >= mDenominator;
168     }
169 
170     /**
171      * Write a frame containing N samples.
172      *
173      * @param frame pointer to the first sample in a frame
174      */
writeNextFrame(const float * frame)175     void writeNextFrame(const float *frame) {
176         writeFrame(frame);
177         advanceWrite();
178     }
179 
180     /**
181      * Read a frame containing N samples.
182      *
183      * @param frame pointer to the first sample in a frame
184      */
readNextFrame(float * frame)185     void readNextFrame(float *frame) {
186         readFrame(frame);
187         advanceRead();
188     }
189 
getNumTaps()190     int getNumTaps() const {
191         return mNumTaps;
192     }
193 
getChannelCount()194     int getChannelCount() const {
195         return mChannelCount;
196     }
197 
198     static float hammingWindow(float radians, float spread);
199 
200     static float sinc(float radians);
201 
202 protected:
203 
204     explicit MultiChannelResampler(const MultiChannelResampler::Builder &builder);
205 
206     /**
207      * Write a frame containing N samples.
208      * Call advanceWrite() after calling this.
209      * @param frame pointer to the first sample in a frame
210      */
211     virtual void writeFrame(const float *frame);
212 
213     /**
214      * Read a frame containing N samples using interpolation.
215      * Call advanceRead() after calling this.
216      * @param frame pointer to the first sample in a frame
217      */
218     virtual void readFrame(float *frame) = 0;
219 
advanceWrite()220     void advanceWrite() {
221         mIntegerPhase -= mDenominator;
222     }
223 
advanceRead()224     void advanceRead() {
225         mIntegerPhase += mNumerator;
226     }
227 
228     /**
229      * Generate the filter coefficients in optimal order.
230      * @param inputRate sample rate of the input stream
231      * @param outputRate  sample rate of the output stream
232      * @param numRows number of rows in the array that contain a set of tap coefficients
233      * @param phaseIncrement how much to increment the phase between rows
234      * @param normalizedCutoff filter cutoff frequency normalized to Nyquist rate of output
235      */
236     void generateCoefficients(int32_t inputRate,
237                               int32_t outputRate,
238                               int32_t numRows,
239                               double phaseIncrement,
240                               float normalizedCutoff);
241 
242 
getIntegerPhase()243     int32_t getIntegerPhase() {
244         return mIntegerPhase;
245     }
246 
247     static constexpr int kMaxCoefficients = 8 * 1024;
248     std::vector<float>   mCoefficients;
249 
250     const int            mNumTaps;
251     int                  mCursor = 0;
252     std::vector<float>   mX;           // delayed input values for the FIR
253     std::vector<float>   mSingleFrame; // one frame for temporary use
254     int32_t              mIntegerPhase = 0;
255     int32_t              mNumerator = 0;
256     int32_t              mDenominator = 0;
257 
258 
259 private:
260 
261 #if MCR_USE_KAISER
262     KaiserWindow           mKaiserWindow;
263 #else
264     HyperbolicCosineWindow mCoshWindow;
265 #endif
266 
267     static constexpr float kDefaultNormalizedCutoff = 0.70f;
268 
269     const int              mChannelCount;
270 };
271 
272 } /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */
273 
274 #endif //RESAMPLER_MULTICHANNEL_RESAMPLER_H
275