1*05767d91SRobert Wu /*
2*05767d91SRobert Wu * Copyright 2019 The Android Open Source Project
3*05767d91SRobert Wu *
4*05767d91SRobert Wu * Licensed under the Apache License, Version 2.0 (the "License");
5*05767d91SRobert Wu * you may not use this file except in compliance with the License.
6*05767d91SRobert Wu * You may obtain a copy of the License at
7*05767d91SRobert Wu *
8*05767d91SRobert Wu * http://www.apache.org/licenses/LICENSE-2.0
9*05767d91SRobert Wu *
10*05767d91SRobert Wu * Unless required by applicable law or agreed to in writing, software
11*05767d91SRobert Wu * distributed under the License is distributed on an "AS IS" BASIS,
12*05767d91SRobert Wu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*05767d91SRobert Wu * See the License for the specific language governing permissions and
14*05767d91SRobert Wu * limitations under the License.
15*05767d91SRobert Wu */
16*05767d91SRobert Wu
17*05767d91SRobert Wu #include <math.h>
18*05767d91SRobert Wu
19*05767d91SRobert Wu #include "IntegerRatio.h"
20*05767d91SRobert Wu #include "LinearResampler.h"
21*05767d91SRobert Wu #include "MultiChannelResampler.h"
22*05767d91SRobert Wu #include "PolyphaseResampler.h"
23*05767d91SRobert Wu #include "PolyphaseResamplerMono.h"
24*05767d91SRobert Wu #include "PolyphaseResamplerStereo.h"
25*05767d91SRobert Wu #include "SincResampler.h"
26*05767d91SRobert Wu #include "SincResamplerStereo.h"
27*05767d91SRobert Wu
28*05767d91SRobert Wu using namespace RESAMPLER_OUTER_NAMESPACE::resampler;
29*05767d91SRobert Wu
MultiChannelResampler(const MultiChannelResampler::Builder & builder)30*05767d91SRobert Wu MultiChannelResampler::MultiChannelResampler(const MultiChannelResampler::Builder &builder)
31*05767d91SRobert Wu : mNumTaps(builder.getNumTaps())
32*05767d91SRobert Wu , mX(static_cast<size_t>(builder.getChannelCount())
33*05767d91SRobert Wu * static_cast<size_t>(builder.getNumTaps()) * 2)
34*05767d91SRobert Wu , mSingleFrame(builder.getChannelCount())
35*05767d91SRobert Wu , mChannelCount(builder.getChannelCount())
36*05767d91SRobert Wu {
37*05767d91SRobert Wu // Reduce sample rates to the smallest ratio.
38*05767d91SRobert Wu // For example 44100/48000 would become 147/160.
39*05767d91SRobert Wu IntegerRatio ratio(builder.getInputRate(), builder.getOutputRate());
40*05767d91SRobert Wu ratio.reduce();
41*05767d91SRobert Wu mNumerator = ratio.getNumerator();
42*05767d91SRobert Wu mDenominator = ratio.getDenominator();
43*05767d91SRobert Wu mIntegerPhase = mDenominator; // so we start with a write needed
44*05767d91SRobert Wu }
45*05767d91SRobert Wu
46*05767d91SRobert Wu // static factory method
make(int32_t channelCount,int32_t inputRate,int32_t outputRate,Quality quality)47*05767d91SRobert Wu MultiChannelResampler *MultiChannelResampler::make(int32_t channelCount,
48*05767d91SRobert Wu int32_t inputRate,
49*05767d91SRobert Wu int32_t outputRate,
50*05767d91SRobert Wu Quality quality) {
51*05767d91SRobert Wu Builder builder;
52*05767d91SRobert Wu builder.setInputRate(inputRate);
53*05767d91SRobert Wu builder.setOutputRate(outputRate);
54*05767d91SRobert Wu builder.setChannelCount(channelCount);
55*05767d91SRobert Wu
56*05767d91SRobert Wu switch (quality) {
57*05767d91SRobert Wu case Quality::Fastest:
58*05767d91SRobert Wu builder.setNumTaps(2);
59*05767d91SRobert Wu break;
60*05767d91SRobert Wu case Quality::Low:
61*05767d91SRobert Wu builder.setNumTaps(4);
62*05767d91SRobert Wu break;
63*05767d91SRobert Wu case Quality::Medium:
64*05767d91SRobert Wu default:
65*05767d91SRobert Wu builder.setNumTaps(8);
66*05767d91SRobert Wu break;
67*05767d91SRobert Wu case Quality::High:
68*05767d91SRobert Wu builder.setNumTaps(16);
69*05767d91SRobert Wu break;
70*05767d91SRobert Wu case Quality::Best:
71*05767d91SRobert Wu builder.setNumTaps(32);
72*05767d91SRobert Wu break;
73*05767d91SRobert Wu }
74*05767d91SRobert Wu
75*05767d91SRobert Wu // Set the cutoff frequency so that we do not get aliasing when down-sampling.
76*05767d91SRobert Wu if (inputRate > outputRate) {
77*05767d91SRobert Wu builder.setNormalizedCutoff(kDefaultNormalizedCutoff);
78*05767d91SRobert Wu }
79*05767d91SRobert Wu return builder.build();
80*05767d91SRobert Wu }
81*05767d91SRobert Wu
build()82*05767d91SRobert Wu MultiChannelResampler *MultiChannelResampler::Builder::build() {
83*05767d91SRobert Wu if (getNumTaps() == 2) {
84*05767d91SRobert Wu // Note that this does not do low pass filteringh.
85*05767d91SRobert Wu return new LinearResampler(*this);
86*05767d91SRobert Wu }
87*05767d91SRobert Wu IntegerRatio ratio(getInputRate(), getOutputRate());
88*05767d91SRobert Wu ratio.reduce();
89*05767d91SRobert Wu bool usePolyphase = (getNumTaps() * ratio.getDenominator()) <= kMaxCoefficients;
90*05767d91SRobert Wu if (usePolyphase) {
91*05767d91SRobert Wu if (getChannelCount() == 1) {
92*05767d91SRobert Wu return new PolyphaseResamplerMono(*this);
93*05767d91SRobert Wu } else if (getChannelCount() == 2) {
94*05767d91SRobert Wu return new PolyphaseResamplerStereo(*this);
95*05767d91SRobert Wu } else {
96*05767d91SRobert Wu return new PolyphaseResampler(*this);
97*05767d91SRobert Wu }
98*05767d91SRobert Wu } else {
99*05767d91SRobert Wu // Use less optimized resampler that uses a float phaseIncrement.
100*05767d91SRobert Wu // TODO mono resampler
101*05767d91SRobert Wu if (getChannelCount() == 2) {
102*05767d91SRobert Wu return new SincResamplerStereo(*this);
103*05767d91SRobert Wu } else {
104*05767d91SRobert Wu return new SincResampler(*this);
105*05767d91SRobert Wu }
106*05767d91SRobert Wu }
107*05767d91SRobert Wu }
108*05767d91SRobert Wu
writeFrame(const float * frame)109*05767d91SRobert Wu void MultiChannelResampler::writeFrame(const float *frame) {
110*05767d91SRobert Wu // Move cursor before write so that cursor points to last written frame in read.
111*05767d91SRobert Wu if (--mCursor < 0) {
112*05767d91SRobert Wu mCursor = getNumTaps() - 1;
113*05767d91SRobert Wu }
114*05767d91SRobert Wu float *dest = &mX[static_cast<size_t>(mCursor) * static_cast<size_t>(getChannelCount())];
115*05767d91SRobert Wu int offset = getNumTaps() * getChannelCount();
116*05767d91SRobert Wu for (int channel = 0; channel < getChannelCount(); channel++) {
117*05767d91SRobert Wu // Write twice so we avoid having to wrap when reading.
118*05767d91SRobert Wu dest[channel] = dest[channel + offset] = frame[channel];
119*05767d91SRobert Wu }
120*05767d91SRobert Wu }
121*05767d91SRobert Wu
sinc(float radians)122*05767d91SRobert Wu float MultiChannelResampler::sinc(float radians) {
123*05767d91SRobert Wu if (fabsf(radians) < 1.0e-9f) return 1.0f; // avoid divide by zero
124*05767d91SRobert Wu return sinf(radians) / radians; // Sinc function
125*05767d91SRobert Wu }
126*05767d91SRobert Wu
127*05767d91SRobert Wu // Generate coefficients in the order they will be used by readFrame().
128*05767d91SRobert Wu // This is more complicated but readFrame() is called repeatedly and should be optimized.
generateCoefficients(int32_t inputRate,int32_t outputRate,int32_t numRows,double phaseIncrement,float normalizedCutoff)129*05767d91SRobert Wu void MultiChannelResampler::generateCoefficients(int32_t inputRate,
130*05767d91SRobert Wu int32_t outputRate,
131*05767d91SRobert Wu int32_t numRows,
132*05767d91SRobert Wu double phaseIncrement,
133*05767d91SRobert Wu float normalizedCutoff) {
134*05767d91SRobert Wu mCoefficients.resize(static_cast<size_t>(getNumTaps()) * static_cast<size_t>(numRows));
135*05767d91SRobert Wu int coefficientIndex = 0;
136*05767d91SRobert Wu double phase = 0.0; // ranges from 0.0 to 1.0, fraction between samples
137*05767d91SRobert Wu // Stretch the sinc function for low pass filtering.
138*05767d91SRobert Wu const float cutoffScaler = (outputRate < inputRate)
139*05767d91SRobert Wu ? (normalizedCutoff * (float)outputRate / inputRate)
140*05767d91SRobert Wu : 1.0f; // Do not filter when upsampling.
141*05767d91SRobert Wu const int numTapsHalf = getNumTaps() / 2; // numTaps must be even.
142*05767d91SRobert Wu const float numTapsHalfInverse = 1.0f / numTapsHalf;
143*05767d91SRobert Wu for (int i = 0; i < numRows; i++) {
144*05767d91SRobert Wu float tapPhase = phase - numTapsHalf;
145*05767d91SRobert Wu float gain = 0.0; // sum of raw coefficients
146*05767d91SRobert Wu int gainCursor = coefficientIndex;
147*05767d91SRobert Wu for (int tap = 0; tap < getNumTaps(); tap++) {
148*05767d91SRobert Wu float radians = tapPhase * M_PI;
149*05767d91SRobert Wu
150*05767d91SRobert Wu #if MCR_USE_KAISER
151*05767d91SRobert Wu float window = mKaiserWindow(tapPhase * numTapsHalfInverse);
152*05767d91SRobert Wu #else
153*05767d91SRobert Wu float window = mCoshWindow(static_cast<double>(tapPhase) * numTapsHalfInverse);
154*05767d91SRobert Wu #endif
155*05767d91SRobert Wu float coefficient = sinc(radians * cutoffScaler) * window;
156*05767d91SRobert Wu mCoefficients.at(coefficientIndex++) = coefficient;
157*05767d91SRobert Wu gain += coefficient;
158*05767d91SRobert Wu tapPhase += 1.0;
159*05767d91SRobert Wu }
160*05767d91SRobert Wu phase += phaseIncrement;
161*05767d91SRobert Wu while (phase >= 1.0) {
162*05767d91SRobert Wu phase -= 1.0;
163*05767d91SRobert Wu }
164*05767d91SRobert Wu
165*05767d91SRobert Wu // Correct for gain variations.
166*05767d91SRobert Wu float gainCorrection = 1.0 / gain; // normalize the gain
167*05767d91SRobert Wu for (int tap = 0; tap < getNumTaps(); tap++) {
168*05767d91SRobert Wu mCoefficients.at(gainCursor + tap) *= gainCorrection;
169*05767d91SRobert Wu }
170*05767d91SRobert Wu }
171*05767d91SRobert Wu }
172