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 OBOE_MULTICHANNEL_RESAMPLER_H
18 #define OBOE_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 namespace resampler {
38 
39 class MultiChannelResampler {
40 
41 public:
42 
43     enum class Quality : int32_t {
44         Fastest,
45         Low,
46         Medium,
47         High,
48         Best,
49     };
50 
51     class Builder {
52     public:
53         /**
54          * Construct an optimal resampler based on the specified parameters.
55          * @return address of a resampler
56          */
57         MultiChannelResampler *build();
58 
59         /**
60          * The number of taps in the resampling filter.
61          * More taps gives better quality but uses more CPU time.
62          * This typically ranges from 4 to 64. Default is 16.
63          *
64          * For polyphase filters, numTaps must be a multiple of four for loop unrolling.
65          * @param numTaps number of taps for the filter
66          * @return address of this builder for chaining calls
67          */
setNumTaps(int32_t numTaps)68         Builder *setNumTaps(int32_t numTaps) {
69             mNumTaps = numTaps;
70             return this;
71         }
72 
73         /**
74          * Use 1 for mono, 2 for stereo, etc. Default is 1.
75          *
76          * @param channelCount number of channels
77          * @return address of this builder for chaining calls
78          */
setChannelCount(int32_t channelCount)79         Builder *setChannelCount(int32_t channelCount) {
80             mChannelCount = channelCount;
81             return this;
82         }
83 
84         /**
85          * Default is 48000.
86          *
87          * @param inputRate sample rate of the input stream
88          * @return address of this builder for chaining calls
89          */
setInputRate(int32_t inputRate)90         Builder *setInputRate(int32_t inputRate) {
91             mInputRate = inputRate;
92             return this;
93         }
94 
95         /**
96          * Default is 48000.
97          *
98          * @param outputRate sample rate of the output stream
99          * @return address of this builder for chaining calls
100          */
setOutputRate(int32_t outputRate)101         Builder *setOutputRate(int32_t outputRate) {
102             mOutputRate = outputRate;
103             return this;
104         }
105 
106         /**
107          * Set cutoff frequency relative to the Nyquist rate of the output sample rate.
108          * Set to 1.0 to match the Nyquist frequency.
109          * Set lower to reduce aliasing.
110          * Default is 0.70.
111          *
112          * @param normalizedCutoff anti-aliasing filter cutoff
113          * @return address of this builder for chaining calls
114          */
setNormalizedCutoff(float normalizedCutoff)115         Builder *setNormalizedCutoff(float normalizedCutoff) {
116             mNormalizedCutoff = normalizedCutoff;
117             return this;
118         }
119 
getNumTaps()120         int32_t getNumTaps() const {
121             return mNumTaps;
122         }
123 
getChannelCount()124         int32_t getChannelCount() const {
125             return mChannelCount;
126         }
127 
getInputRate()128         int32_t getInputRate() const {
129             return mInputRate;
130         }
131 
getOutputRate()132         int32_t getOutputRate() const {
133             return mOutputRate;
134         }
135 
getNormalizedCutoff()136         float getNormalizedCutoff() const {
137             return mNormalizedCutoff;
138         }
139 
140     protected:
141         int32_t mChannelCount = 1;
142         int32_t mNumTaps = 16;
143         int32_t mInputRate = 48000;
144         int32_t mOutputRate = 48000;
145         float   mNormalizedCutoff = kDefaultNormalizedCutoff;
146     };
147 
148     virtual ~MultiChannelResampler() = default;
149 
150     /**
151      * Factory method for making a resampler that is optimal for the given inputs.
152      *
153      * @param channelCount number of channels, 2 for stereo
154      * @param inputRate sample rate of the input stream
155      * @param outputRate  sample rate of the output stream
156      * @param quality higher quality sounds better but uses more CPU
157      * @return an optimal resampler
158      */
159     static MultiChannelResampler *make(int32_t channelCount,
160                                        int32_t inputRate,
161                                        int32_t outputRate,
162                                        Quality quality);
163 
isWriteNeeded()164     bool isWriteNeeded() const {
165         return mIntegerPhase >= mDenominator;
166     }
167 
168     /**
169      * Write a frame containing N samples.
170      *
171      * @param frame pointer to the first sample in a frame
172      */
writeNextFrame(const float * frame)173     void writeNextFrame(const float *frame) {
174         writeFrame(frame);
175         advanceWrite();
176     }
177 
178     /**
179      * Read a frame containing N samples.
180      *
181      * @param frame pointer to the first sample in a frame
182      */
readNextFrame(float * frame)183     void readNextFrame(float *frame) {
184         readFrame(frame);
185         advanceRead();
186     }
187 
getNumTaps()188     int getNumTaps() const {
189         return mNumTaps;
190     }
191 
getChannelCount()192     int getChannelCount() const {
193         return mChannelCount;
194     }
195 
196     static float hammingWindow(float radians, float spread);
197 
198     static float sinc(float radians);
199 
200 protected:
201 
202     explicit MultiChannelResampler(const MultiChannelResampler::Builder &builder);
203 
204     /**
205      * Write a frame containing N samples.
206      * Call advanceWrite() after calling this.
207      * @param frame pointer to the first sample in a frame
208      */
209     virtual void writeFrame(const float *frame);
210 
211     /**
212      * Read a frame containing N samples using interpolation.
213      * Call advanceRead() after calling this.
214      * @param frame pointer to the first sample in a frame
215      */
216     virtual void readFrame(float *frame) = 0;
217 
advanceWrite()218     void advanceWrite() {
219         mIntegerPhase -= mDenominator;
220     }
221 
advanceRead()222     void advanceRead() {
223         mIntegerPhase += mNumerator;
224     }
225 
226     /**
227      * Generate the filter coefficients in optimal order.
228      * @param inputRate sample rate of the input stream
229      * @param outputRate  sample rate of the output stream
230      * @param numRows number of rows in the array that contain a set of tap coefficients
231      * @param phaseIncrement how much to increment the phase between rows
232      * @param normalizedCutoff filter cutoff frequency normalized to Nyquist rate of output
233      */
234     void generateCoefficients(int32_t inputRate,
235                               int32_t outputRate,
236                               int32_t numRows,
237                               double phaseIncrement,
238                               float normalizedCutoff);
239 
240 
getIntegerPhase()241     int32_t getIntegerPhase() {
242         return mIntegerPhase;
243     }
244 
245     static constexpr int kMaxCoefficients = 8 * 1024;
246     std::vector<float>   mCoefficients;
247 
248     const int            mNumTaps;
249     int                  mCursor = 0;
250     std::vector<float>   mX;           // delayed input values for the FIR
251     std::vector<float>   mSingleFrame; // one frame for temporary use
252     int32_t              mIntegerPhase = 0;
253     int32_t              mNumerator = 0;
254     int32_t              mDenominator = 0;
255 
256 
257 private:
258 
259 #if MCR_USE_KAISER
260     KaiserWindow           mKaiserWindow;
261 #else
262     HyperbolicCosineWindow mCoshWindow;
263 #endif
264 
265     static constexpr float kDefaultNormalizedCutoff = 0.70f;
266 
267     const int              mChannelCount;
268 };
269 
270 }
271 #endif //OBOE_MULTICHANNEL_RESAMPLER_H
272