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