• 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          * Note that this value is ignored when upsampling, which is when
115          * the outputRate is higher than the inputRate.
116          *
117          * @param normalizedCutoff anti-aliasing filter cutoff
118          * @return address of this builder for chaining calls
119          */
setNormalizedCutoff(float normalizedCutoff)120         Builder *setNormalizedCutoff(float normalizedCutoff) {
121             mNormalizedCutoff = normalizedCutoff;
122             return this;
123         }
124 
getNumTaps()125         int32_t getNumTaps() const {
126             return mNumTaps;
127         }
128 
getChannelCount()129         int32_t getChannelCount() const {
130             return mChannelCount;
131         }
132 
getInputRate()133         int32_t getInputRate() const {
134             return mInputRate;
135         }
136 
getOutputRate()137         int32_t getOutputRate() const {
138             return mOutputRate;
139         }
140 
getNormalizedCutoff()141         float getNormalizedCutoff() const {
142             return mNormalizedCutoff;
143         }
144 
145     protected:
146         int32_t mChannelCount = 1;
147         int32_t mNumTaps = 16;
148         int32_t mInputRate = 48000;
149         int32_t mOutputRate = 48000;
150         float   mNormalizedCutoff = kDefaultNormalizedCutoff;
151     };
152 
153     virtual ~MultiChannelResampler() = default;
154 
155     /**
156      * Factory method for making a resampler that is optimal for the given inputs.
157      *
158      * @param channelCount number of channels, 2 for stereo
159      * @param inputRate sample rate of the input stream
160      * @param outputRate  sample rate of the output stream
161      * @param quality higher quality sounds better but uses more CPU
162      * @return an optimal resampler
163      */
164     static MultiChannelResampler *make(int32_t channelCount,
165                                        int32_t inputRate,
166                                        int32_t outputRate,
167                                        Quality quality);
168 
isWriteNeeded()169     bool isWriteNeeded() const {
170         return mIntegerPhase >= mDenominator;
171     }
172 
173     /**
174      * Write a frame containing N samples.
175      *
176      * @param frame pointer to the first sample in a frame
177      */
writeNextFrame(const float * frame)178     void writeNextFrame(const float *frame) {
179         writeFrame(frame);
180         advanceWrite();
181     }
182 
183     /**
184      * Read a frame containing N samples.
185      *
186      * @param frame pointer to the first sample in a frame
187      */
readNextFrame(float * frame)188     void readNextFrame(float *frame) {
189         readFrame(frame);
190         advanceRead();
191     }
192 
getNumTaps()193     int getNumTaps() const {
194         return mNumTaps;
195     }
196 
getChannelCount()197     int getChannelCount() const {
198         return mChannelCount;
199     }
200 
201     static float hammingWindow(float radians, float spread);
202 
203     static float sinc(float radians);
204 
205 protected:
206 
207     explicit MultiChannelResampler(const MultiChannelResampler::Builder &builder);
208 
209     /**
210      * Write a frame containing N samples.
211      * Call advanceWrite() after calling this.
212      * @param frame pointer to the first sample in a frame
213      */
214     virtual void writeFrame(const float *frame);
215 
216     /**
217      * Read a frame containing N samples using interpolation.
218      * Call advanceRead() after calling this.
219      * @param frame pointer to the first sample in a frame
220      */
221     virtual void readFrame(float *frame) = 0;
222 
advanceWrite()223     void advanceWrite() {
224         mIntegerPhase -= mDenominator;
225     }
226 
advanceRead()227     void advanceRead() {
228         mIntegerPhase += mNumerator;
229     }
230 
231     /**
232      * Generate the filter coefficients in optimal order.
233      *
234      * Note that normalizedCutoff is ignored when upsampling, which is when
235      * the outputRate is higher than the inputRate.
236      *
237      * @param inputRate sample rate of the input stream
238      * @param outputRate  sample rate of the output stream
239      * @param numRows number of rows in the array that contain a set of tap coefficients
240      * @param phaseIncrement how much to increment the phase between rows
241      * @param normalizedCutoff filter cutoff frequency normalized to Nyquist rate of output
242      */
243     void generateCoefficients(int32_t inputRate,
244                               int32_t outputRate,
245                               int32_t numRows,
246                               double phaseIncrement,
247                               float normalizedCutoff);
248 
249 
getIntegerPhase()250     int32_t getIntegerPhase() {
251         return mIntegerPhase;
252     }
253 
254     static constexpr int kMaxCoefficients = 8 * 1024;
255     std::vector<float>   mCoefficients;
256 
257     const int            mNumTaps;
258     int                  mCursor = 0;
259     std::vector<float>   mX;           // delayed input values for the FIR
260     std::vector<float>   mSingleFrame; // one frame for temporary use
261     int32_t              mIntegerPhase = 0;
262     int32_t              mNumerator = 0;
263     int32_t              mDenominator = 0;
264 
265 
266 private:
267 
268 #if MCR_USE_KAISER
269     KaiserWindow           mKaiserWindow;
270 #else
271     HyperbolicCosineWindow mCoshWindow;
272 #endif
273 
274     static constexpr float kDefaultNormalizedCutoff = 0.70f;
275 
276     const int              mChannelCount;
277 };
278 
279 } /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */
280 
281 #endif //RESAMPLER_MULTICHANNEL_RESAMPLER_H
282