Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2007 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 | |
SathishKumar Mani | 41dfd12 | 2012-01-17 10:49:47 -0800 | [diff] [blame] | 17 | #define LOG_TAG "AudioResamplerSinc" |
| 18 | //#define LOG_NDEBUG 0 |
| 19 | |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 20 | #include <string.h> |
| 21 | #include "AudioResamplerSinc.h" |
SathishKumar Mani | 41dfd12 | 2012-01-17 10:49:47 -0800 | [diff] [blame] | 22 | #include <dlfcn.h> |
| 23 | #include <cutils/properties.h> |
| 24 | #include <stdlib.h> |
| 25 | #include <utils/Log.h> |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 26 | |
| 27 | namespace android { |
| 28 | // ---------------------------------------------------------------------------- |
| 29 | |
| 30 | |
| 31 | /* |
| 32 | * These coeficients are computed with the "fir" utility found in |
| 33 | * tools/resampler_tools |
| 34 | * TODO: A good optimization would be to transpose this matrix, to take |
| 35 | * better advantage of the data-cache. |
| 36 | */ |
| 37 | const int32_t AudioResamplerSinc::mFirCoefsUp[] = { |
| 38 | 0x7fffffff, 0x7f15d078, 0x7c5e0da6, 0x77ecd867, 0x71e2e251, 0x6a6c304a, 0x61be7269, 0x58170412, 0x4db8ab05, 0x42e92ea6, 0x37eee214, 0x2d0e3bb1, 0x22879366, 0x18951e95, 0x0f693d0d, 0x072d2621, |
| 39 | 0x00000000, 0xf9f66655, 0xf51a5fd7, 0xf16bbd84, 0xeee0d9ac, 0xed67a922, 0xece70de6, 0xed405897, 0xee50e505, 0xeff3be30, 0xf203370f, 0xf45a6741, 0xf6d67d53, 0xf957db66, 0xfbc2f647, 0xfe00f2b9, |
| 40 | 0x00000000, 0x01b37218, 0x0313a0c6, 0x041d930d, 0x04d28057, 0x053731b0, 0x05534dff, 0x05309bfd, 0x04da440d, 0x045c1aee, 0x03c1fcdd, 0x03173ef5, 0x02663ae8, 0x01b7f736, 0x0113ec79, 0x007fe6a9, |
| 41 | 0x00000000, 0xff96b229, 0xff44f99f, 0xff0a86be, 0xfee5f803, 0xfed518fd, 0xfed521fd, 0xfee2f4fd, 0xfefb54f8, 0xff1b159b, 0xff3f4203, 0xff6539e0, 0xff8ac502, 0xffae1ddd, 0xffcdf3f9, 0xffe96798, |
| 42 | 0x00000000, 0x00119de6, 0x001e6b7e, 0x0026cb7a, 0x002b4830, 0x002c83d6, 0x002b2a82, 0x0027e67a, 0x002356f9, 0x001e098e, 0x001875e4, 0x0012fbbe, 0x000de2d1, 0x00095c10, 0x00058414, 0x00026636, |
| 43 | 0x00000000, 0xfffe44a9, 0xfffd206d, 0xfffc7b7f, 0xfffc3c8f, 0xfffc4ac2, 0xfffc8f2b, 0xfffcf5c4, 0xfffd6df3, 0xfffdeab2, 0xfffe6275, 0xfffececf, 0xffff2c07, 0xffff788c, 0xffffb471, 0xffffe0f2, |
| 44 | 0x00000000, 0x000013e6, 0x00001f03, 0x00002396, 0x00002399, 0x000020b6, 0x00001c3c, 0x00001722, 0x00001216, 0x00000d81, 0x0000099c, 0x0000067c, 0x00000419, 0x0000025f, 0x00000131, 0x00000070, |
| 45 | 0x00000000, 0xffffffc7, 0xffffffb3, 0xffffffb3, 0xffffffbe, 0xffffffcd, 0xffffffdb, 0xffffffe7, 0xfffffff0, 0xfffffff7, 0xfffffffb, 0xfffffffe, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, |
| 46 | 0x00000000 // this one is needed for lerping the last coefficient |
| 47 | }; |
| 48 | |
| 49 | /* |
| 50 | * These coefficients are optimized for 48KHz -> 44.1KHz (stop-band at 22.050KHz) |
| 51 | * It's possible to use the above coefficient for any down-sampling |
| 52 | * at the expense of a slower processing loop (we can interpolate |
| 53 | * these coefficient from the above by "Stretching" them in time). |
| 54 | */ |
| 55 | const int32_t AudioResamplerSinc::mFirCoefsDown[] = { |
| 56 | 0x7fffffff, 0x7f55e46d, 0x7d5b4c60, 0x7a1b4b98, 0x75a7fb14, 0x7019f0bd, 0x698f875a, 0x622bfd59, 0x5a167256, 0x5178cc54, 0x487e8e6c, 0x3f53aae8, 0x36235ad4, 0x2d17047b, 0x245539ab, 0x1c00d540, |
| 57 | 0x14383e57, 0x0d14d5ca, 0x06aa910b, 0x0107c38b, 0xfc351654, 0xf835abae, 0xf5076b45, 0xf2a37202, 0xf0fe9faa, 0xf00a3bbd, 0xefb4aa81, 0xefea2b05, 0xf0959716, 0xf1a11e83, 0xf2f6f7a0, 0xf481fff4, |
| 58 | 0xf62e48ce, 0xf7e98ca5, 0xf9a38b4c, 0xfb4e4bfa, 0xfcde456f, 0xfe4a6d30, 0xff8c2fdf, 0x009f5555, 0x0181d393, 0x0233940f, 0x02b62f06, 0x030ca07d, 0x033afa62, 0x03461725, 0x03334f83, 0x030835fa, |
| 59 | 0x02ca59cc, 0x027f12d1, 0x022b570d, 0x01d39a49, 0x017bb78f, 0x0126e414, 0x00d7aaaf, 0x008feec7, 0x0050f584, 0x001b73e3, 0xffefa063, 0xffcd46ed, 0xffb3ddcd, 0xffa29aaa, 0xff988691, 0xff949066, |
| 60 | 0xff959d24, 0xff9a959e, 0xffa27195, 0xffac4011, 0xffb72d2b, 0xffc28569, 0xffcdb706, 0xffd85171, 0xffe20364, 0xffea97e9, 0xfff1f2b2, 0xfff80c06, 0xfffcec92, 0x0000a955, 0x00035fd8, 0x000532cf, |
| 61 | 0x00064735, 0x0006c1f9, 0x0006c62d, 0x000673ba, 0x0005e68f, 0x00053630, 0x000475a3, 0x0003b397, 0x0002fac1, 0x00025257, 0x0001be9e, 0x0001417a, 0x0000dafd, 0x000089eb, 0x00004c28, 0x00001f1d, |
| 62 | 0x00000000, 0xffffec10, 0xffffe0be, 0xffffdbc5, 0xffffdb39, 0xffffdd8b, 0xffffe182, 0xffffe638, 0xffffeb0a, 0xffffef8f, 0xfffff38b, 0xfffff6e3, 0xfffff993, 0xfffffba6, 0xfffffd30, 0xfffffe4a, |
| 63 | 0xffffff09, 0xffffff85, 0xffffffd1, 0xfffffffb, 0x0000000f, 0x00000016, 0x00000015, 0x00000012, 0x0000000d, 0x00000009, 0x00000006, 0x00000003, 0x00000002, 0x00000001, 0x00000000, 0x00000000, |
| 64 | 0x00000000 // this one is needed for lerping the last coefficient |
| 65 | }; |
| 66 | |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 67 | // we use 15 bits to interpolate between these samples |
| 68 | // this cannot change because the mul below rely on it. |
| 69 | static const int pLerpBits = 15; |
| 70 | |
| 71 | static pthread_once_t once_control = PTHREAD_ONCE_INIT; |
| 72 | static readCoefficientsFn readResampleCoefficients = NULL; |
| 73 | |
| 74 | /*static*/ AudioResamplerSinc::Constants AudioResamplerSinc::highQualityConstants; |
| 75 | /*static*/ AudioResamplerSinc::Constants AudioResamplerSinc::veryHighQualityConstants; |
| 76 | |
| 77 | void AudioResamplerSinc::init_routine() |
| 78 | { |
| 79 | // for high quality resampler, the parameters for coefficients are compile-time constants |
| 80 | Constants *c = &highQualityConstants; |
| 81 | c->coefsBits = RESAMPLE_FIR_LERP_INT_BITS; |
| 82 | c->cShift = kNumPhaseBits - c->coefsBits; |
| 83 | c->cMask = ((1<< c->coefsBits)-1) << c->cShift; |
| 84 | c->pShift = kNumPhaseBits - c->coefsBits - pLerpBits; |
| 85 | c->pMask = ((1<< pLerpBits)-1) << c->pShift; |
| 86 | c->halfNumCoefs = RESAMPLE_FIR_NUM_COEF; |
| 87 | |
| 88 | // for very high quality resampler, the parameters are load-time constants |
| 89 | veryHighQualityConstants = highQualityConstants; |
| 90 | |
| 91 | // Open the dll to get the coefficients for VERY_HIGH_QUALITY |
| 92 | void *resampleCoeffLib = dlopen("libaudio-resampler.so", RTLD_NOW); |
| 93 | ALOGV("Open libaudio-resampler library = %p", resampleCoeffLib); |
| 94 | if (resampleCoeffLib == NULL) { |
| 95 | ALOGE("Could not open audio-resampler library: %s", dlerror()); |
| 96 | return; |
| 97 | } |
| 98 | |
| 99 | readResampleCoefficients = (readCoefficientsFn) dlsym(resampleCoeffLib, |
| 100 | "readResamplerCoefficients"); |
| 101 | readResampleFirNumCoeffFn readResampleFirNumCoeff = (readResampleFirNumCoeffFn) |
| 102 | dlsym(resampleCoeffLib, "readResampleFirNumCoeff"); |
| 103 | readResampleFirLerpIntBitsFn readResampleFirLerpIntBits = (readResampleFirLerpIntBitsFn) |
| 104 | dlsym(resampleCoeffLib, "readResampleFirLerpIntBits"); |
| 105 | if (!readResampleCoefficients || !readResampleFirNumCoeff || !readResampleFirLerpIntBits) { |
| 106 | readResampleCoefficients = NULL; |
| 107 | dlclose(resampleCoeffLib); |
| 108 | resampleCoeffLib = NULL; |
| 109 | ALOGE("Could not find symbol: %s", dlerror()); |
| 110 | return; |
| 111 | } |
| 112 | |
| 113 | c = &veryHighQualityConstants; |
| 114 | // we have 16 coefs samples per zero-crossing |
| 115 | c->coefsBits = readResampleFirLerpIntBits(); |
| 116 | ALOGV("coefsBits = %d", c->coefsBits); |
| 117 | c->cShift = kNumPhaseBits - c->coefsBits; |
| 118 | c->cMask = ((1<<c->coefsBits)-1) << c->cShift; |
| 119 | c->pShift = kNumPhaseBits - c->coefsBits - pLerpBits; |
| 120 | c->pMask = ((1<<pLerpBits)-1) << c->pShift; |
| 121 | // number of zero-crossing on each side |
| 122 | c->halfNumCoefs = readResampleFirNumCoeff(); |
| 123 | ALOGV("halfNumCoefs = %d", c->halfNumCoefs); |
| 124 | // note that we "leak" resampleCoeffLib until the process exits |
| 125 | } |
SathishKumar Mani | 41dfd12 | 2012-01-17 10:49:47 -0800 | [diff] [blame] | 126 | |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 127 | // ---------------------------------------------------------------------------- |
| 128 | |
| 129 | static inline |
| 130 | int32_t mulRL(int left, int32_t in, uint32_t vRL) |
| 131 | { |
| 132 | #if defined(__arm__) && !defined(__thumb__) |
| 133 | int32_t out; |
| 134 | if (left) { |
| 135 | asm( "smultb %[out], %[in], %[vRL] \n" |
| 136 | : [out]"=r"(out) |
| 137 | : [in]"%r"(in), [vRL]"r"(vRL) |
| 138 | : ); |
| 139 | } else { |
| 140 | asm( "smultt %[out], %[in], %[vRL] \n" |
| 141 | : [out]"=r"(out) |
| 142 | : [in]"%r"(in), [vRL]"r"(vRL) |
| 143 | : ); |
| 144 | } |
| 145 | return out; |
| 146 | #else |
| 147 | if (left) { |
| 148 | return int16_t(in>>16) * int16_t(vRL&0xFFFF); |
| 149 | } else { |
| 150 | return int16_t(in>>16) * int16_t(vRL>>16); |
| 151 | } |
| 152 | #endif |
| 153 | } |
| 154 | |
| 155 | static inline |
| 156 | int32_t mulAdd(int16_t in, int32_t v, int32_t a) |
| 157 | { |
| 158 | #if defined(__arm__) && !defined(__thumb__) |
| 159 | int32_t out; |
| 160 | asm( "smlawb %[out], %[v], %[in], %[a] \n" |
| 161 | : [out]"=r"(out) |
| 162 | : [in]"%r"(in), [v]"r"(v), [a]"r"(a) |
| 163 | : ); |
| 164 | return out; |
| 165 | #else |
| 166 | return a + in * (v>>16); |
| 167 | // improved precision |
| 168 | // return a + in * (v>>16) + ((in * (v & 0xffff)) >> 16); |
| 169 | #endif |
| 170 | } |
| 171 | |
| 172 | static inline |
| 173 | int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a) |
| 174 | { |
| 175 | #if defined(__arm__) && !defined(__thumb__) |
| 176 | int32_t out; |
| 177 | if (left) { |
| 178 | asm( "smlawb %[out], %[v], %[inRL], %[a] \n" |
| 179 | : [out]"=r"(out) |
| 180 | : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a) |
| 181 | : ); |
| 182 | } else { |
| 183 | asm( "smlawt %[out], %[v], %[inRL], %[a] \n" |
| 184 | : [out]"=r"(out) |
| 185 | : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a) |
| 186 | : ); |
| 187 | } |
| 188 | return out; |
| 189 | #else |
| 190 | if (left) { |
| 191 | return a + (int16_t(inRL&0xFFFF) * (v>>16)); |
| 192 | //improved precision |
| 193 | // return a + (int16_t(inRL&0xFFFF) * (v>>16)) + ((int16_t(inRL&0xFFFF) * (v & 0xffff)) >> 16); |
| 194 | } else { |
| 195 | return a + (int16_t(inRL>>16) * (v>>16)); |
| 196 | } |
| 197 | #endif |
| 198 | } |
| 199 | |
| 200 | // ---------------------------------------------------------------------------- |
| 201 | |
| 202 | AudioResamplerSinc::AudioResamplerSinc(int bitDepth, |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 203 | int inChannelCount, int32_t sampleRate, src_quality quality) |
| 204 | : AudioResampler(bitDepth, inChannelCount, sampleRate, quality), |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 205 | mState(0) |
| 206 | { |
| 207 | /* |
| 208 | * Layout of the state buffer for 32 tap: |
| 209 | * |
| 210 | * "present" sample beginning of 2nd buffer |
| 211 | * v v |
| 212 | * 0 01 2 23 3 |
| 213 | * 0 F0 0 F0 F |
| 214 | * [pppppppppppppppInnnnnnnnnnnnnnnnpppppppppppppppInnnnnnnnnnnnnnnn] |
| 215 | * ^ ^ head |
| 216 | * |
| 217 | * p = past samples, convoluted with the (p)ositive side of sinc() |
| 218 | * n = future samples, convoluted with the (n)egative side of sinc() |
| 219 | * r = extra space for implementing the ring buffer |
| 220 | * |
| 221 | */ |
| 222 | |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 223 | // Load the constants for coefficients |
| 224 | int ok = pthread_once(&once_control, init_routine); |
| 225 | if (ok != 0) { |
| 226 | ALOGE("%s pthread_once failed: %d", __func__, ok); |
SathishKumar Mani | 41dfd12 | 2012-01-17 10:49:47 -0800 | [diff] [blame] | 227 | } |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 228 | mConstants = (quality == VERY_HIGH_QUALITY) ? &veryHighQualityConstants : &highQualityConstants; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 229 | } |
| 230 | |
SathishKumar Mani | 41dfd12 | 2012-01-17 10:49:47 -0800 | [diff] [blame] | 231 | |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 232 | AudioResamplerSinc::~AudioResamplerSinc() |
| 233 | { |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 234 | delete[] mState; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 235 | } |
| 236 | |
| 237 | void AudioResamplerSinc::init() { |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 238 | const Constants *c = mConstants; |
SathishKumar Mani | 41dfd12 | 2012-01-17 10:49:47 -0800 | [diff] [blame] | 239 | |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 240 | const size_t numCoefs = 2*c->halfNumCoefs; |
SathishKumar Mani | 41dfd12 | 2012-01-17 10:49:47 -0800 | [diff] [blame] | 241 | const size_t stateSize = numCoefs * mChannelCount * 2; |
| 242 | mState = new int16_t[stateSize]; |
| 243 | memset(mState, 0, sizeof(int16_t)*stateSize); |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 244 | mImpulse = mState + (c->halfNumCoefs-1)*mChannelCount; |
SathishKumar Mani | 41dfd12 | 2012-01-17 10:49:47 -0800 | [diff] [blame] | 245 | mRingFull = mImpulse + (numCoefs+1)*mChannelCount; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 246 | } |
| 247 | |
| 248 | void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, |
| 249 | AudioBufferProvider* provider) |
| 250 | { |
SathishKumar Mani | 41dfd12 | 2012-01-17 10:49:47 -0800 | [diff] [blame] | 251 | |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 252 | // FIXME store current state (up or down sample) and only load the coefs when the state |
| 253 | // changes. Or load two pointers one for up and one for down in the init function. |
| 254 | // Not critical now since the read functions are fast, but would be important if read was slow. |
Mathias Agopian | ebed918 | 2012-10-21 03:04:05 -0700 | [diff] [blame^] | 255 | if (mConstants == &veryHighQualityConstants && readResampleCoefficients) { |
SathishKumar Mani | 41dfd12 | 2012-01-17 10:49:47 -0800 | [diff] [blame] | 256 | ALOGV("get coefficient from libmm-audio resampler library"); |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 257 | mFirCoefs = (mInSampleRate <= mSampleRate) ? readResampleCoefficients(true) : |
| 258 | readResampleCoefficients(false); |
| 259 | } else { |
SathishKumar Mani | 41dfd12 | 2012-01-17 10:49:47 -0800 | [diff] [blame] | 260 | ALOGV("Use default coefficients"); |
| 261 | mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown; |
| 262 | } |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 263 | |
| 264 | // select the appropriate resampler |
| 265 | switch (mChannelCount) { |
| 266 | case 1: |
| 267 | resample<1>(out, outFrameCount, provider); |
| 268 | break; |
| 269 | case 2: |
| 270 | resample<2>(out, outFrameCount, provider); |
| 271 | break; |
| 272 | } |
SathishKumar Mani | 41dfd12 | 2012-01-17 10:49:47 -0800 | [diff] [blame] | 273 | |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 274 | } |
| 275 | |
| 276 | |
| 277 | template<int CHANNELS> |
| 278 | void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, |
| 279 | AudioBufferProvider* provider) |
| 280 | { |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 281 | const Constants *c = mConstants; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 282 | int16_t* impulse = mImpulse; |
| 283 | uint32_t vRL = mVolumeRL; |
| 284 | size_t inputIndex = mInputIndex; |
| 285 | uint32_t phaseFraction = mPhaseFraction; |
| 286 | uint32_t phaseIncrement = mPhaseIncrement; |
| 287 | size_t outputIndex = 0; |
| 288 | size_t outputSampleCount = outFrameCount * 2; |
| 289 | size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; |
| 290 | |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 291 | while (outputIndex < outputSampleCount) { |
| 292 | // buffer is empty, fetch a new one |
Glenn Kasten | d198b61 | 2012-02-02 14:09:43 -0800 | [diff] [blame] | 293 | while (mBuffer.frameCount == 0) { |
| 294 | mBuffer.frameCount = inFrameCount; |
John Grossman | 4ff14ba | 2012-02-08 16:37:41 -0800 | [diff] [blame] | 295 | provider->getNextBuffer(&mBuffer, |
| 296 | calculateOutputPTS(outputIndex / 2)); |
Glenn Kasten | d198b61 | 2012-02-02 14:09:43 -0800 | [diff] [blame] | 297 | if (mBuffer.raw == NULL) { |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 298 | goto resample_exit; |
| 299 | } |
| 300 | const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; |
| 301 | if (phaseIndex == 1) { |
| 302 | // read one frame |
Glenn Kasten | d198b61 | 2012-02-02 14:09:43 -0800 | [diff] [blame] | 303 | read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex); |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 304 | } else if (phaseIndex == 2) { |
| 305 | // read 2 frames |
Glenn Kasten | d198b61 | 2012-02-02 14:09:43 -0800 | [diff] [blame] | 306 | read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex); |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 307 | inputIndex++; |
| 308 | if (inputIndex >= mBuffer.frameCount) { |
| 309 | inputIndex -= mBuffer.frameCount; |
Glenn Kasten | d198b61 | 2012-02-02 14:09:43 -0800 | [diff] [blame] | 310 | provider->releaseBuffer(&mBuffer); |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 311 | } else { |
Glenn Kasten | d198b61 | 2012-02-02 14:09:43 -0800 | [diff] [blame] | 312 | read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex); |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 313 | } |
Glenn Kasten | e53b9ea | 2012-03-12 16:29:55 -0700 | [diff] [blame] | 314 | } |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 315 | } |
Glenn Kasten | d198b61 | 2012-02-02 14:09:43 -0800 | [diff] [blame] | 316 | int16_t *in = mBuffer.i16; |
| 317 | const size_t frameCount = mBuffer.frameCount; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 318 | |
| 319 | // Always read-in the first samples from the input buffer |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 320 | int16_t* head = impulse + c->halfNumCoefs*CHANNELS; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 321 | head[0] = in[inputIndex*CHANNELS + 0]; |
| 322 | if (CHANNELS == 2) |
| 323 | head[1] = in[inputIndex*CHANNELS + 1]; |
| 324 | |
| 325 | // handle boundary case |
| 326 | int32_t l, r; |
| 327 | while (outputIndex < outputSampleCount) { |
| 328 | filterCoefficient<CHANNELS>(l, r, phaseFraction, impulse); |
| 329 | out[outputIndex++] += 2 * mulRL(1, l, vRL); |
| 330 | out[outputIndex++] += 2 * mulRL(0, r, vRL); |
| 331 | |
| 332 | phaseFraction += phaseIncrement; |
| 333 | const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; |
| 334 | if (phaseIndex == 1) { |
| 335 | inputIndex++; |
| 336 | if (inputIndex >= frameCount) |
| 337 | break; // need a new buffer |
| 338 | read<CHANNELS>(impulse, phaseFraction, in, inputIndex); |
Glenn Kasten | e53b9ea | 2012-03-12 16:29:55 -0700 | [diff] [blame] | 339 | } else if (phaseIndex == 2) { // maximum value |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 340 | inputIndex++; |
| 341 | if (inputIndex >= frameCount) |
| 342 | break; // 0 frame available, 2 frames needed |
| 343 | // read first frame |
| 344 | read<CHANNELS>(impulse, phaseFraction, in, inputIndex); |
| 345 | inputIndex++; |
| 346 | if (inputIndex >= frameCount) |
| 347 | break; // 0 frame available, 1 frame needed |
| 348 | // read second frame |
| 349 | read<CHANNELS>(impulse, phaseFraction, in, inputIndex); |
| 350 | } |
| 351 | } |
| 352 | |
| 353 | // if done with buffer, save samples |
| 354 | if (inputIndex >= frameCount) { |
| 355 | inputIndex -= frameCount; |
Glenn Kasten | d198b61 | 2012-02-02 14:09:43 -0800 | [diff] [blame] | 356 | provider->releaseBuffer(&mBuffer); |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 357 | } |
| 358 | } |
| 359 | |
| 360 | resample_exit: |
| 361 | mImpulse = impulse; |
| 362 | mInputIndex = inputIndex; |
| 363 | mPhaseFraction = phaseFraction; |
| 364 | } |
| 365 | |
| 366 | template<int CHANNELS> |
| 367 | /*** |
| 368 | * read() |
| 369 | * |
| 370 | * This function reads only one frame from input buffer and writes it in |
| 371 | * state buffer |
| 372 | * |
| 373 | **/ |
| 374 | void AudioResamplerSinc::read( |
| 375 | int16_t*& impulse, uint32_t& phaseFraction, |
Glenn Kasten | 54c3b66 | 2012-01-06 07:46:30 -0800 | [diff] [blame] | 376 | const int16_t* in, size_t inputIndex) |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 377 | { |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 378 | const Constants *c = mConstants; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 379 | const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; |
| 380 | impulse += CHANNELS; |
| 381 | phaseFraction -= 1LU<<kNumPhaseBits; |
| 382 | if (impulse >= mRingFull) { |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 383 | const size_t stateSize = (c->halfNumCoefs*2)*CHANNELS; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 384 | memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize); |
| 385 | impulse -= stateSize; |
| 386 | } |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 387 | int16_t* head = impulse + c->halfNumCoefs*CHANNELS; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 388 | head[0] = in[inputIndex*CHANNELS + 0]; |
| 389 | if (CHANNELS == 2) |
| 390 | head[1] = in[inputIndex*CHANNELS + 1]; |
| 391 | } |
| 392 | |
| 393 | template<int CHANNELS> |
| 394 | void AudioResamplerSinc::filterCoefficient( |
Glenn Kasten | 54c3b66 | 2012-01-06 07:46:30 -0800 | [diff] [blame] | 395 | int32_t& l, int32_t& r, uint32_t phase, const int16_t *samples) |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 396 | { |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 397 | const Constants *c = mConstants; |
| 398 | |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 399 | // compute the index of the coefficient on the positive side and |
| 400 | // negative side |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 401 | uint32_t indexP = (phase & c->cMask) >> c->cShift; |
| 402 | uint16_t lerpP = (phase & c->pMask) >> c->pShift; |
| 403 | uint32_t indexN = (-phase & c->cMask) >> c->cShift; |
| 404 | uint16_t lerpN = (-phase & c->pMask) >> c->pShift; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 405 | if ((indexP == 0) && (lerpP == 0)) { |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 406 | indexN = c->cMask >> c->cShift; |
| 407 | lerpN = c->pMask >> c->pShift; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 408 | } |
| 409 | |
| 410 | l = 0; |
| 411 | r = 0; |
Glenn Kasten | 54c3b66 | 2012-01-06 07:46:30 -0800 | [diff] [blame] | 412 | const int32_t* coefs = mFirCoefs; |
| 413 | const int16_t *sP = samples; |
| 414 | const int16_t *sN = samples+CHANNELS; |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 415 | for (unsigned int i=0 ; i < c->halfNumCoefs/4 ; i++) { |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 416 | interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); |
| 417 | interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 418 | sP -= CHANNELS; sN += CHANNELS; coefs += 1 << c->coefsBits; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 419 | interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); |
| 420 | interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 421 | sP -= CHANNELS; sN += CHANNELS; coefs += 1 << c->coefsBits; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 422 | interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); |
| 423 | interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 424 | sP -= CHANNELS; sN += CHANNELS; coefs += 1 << c->coefsBits; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 425 | interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); |
| 426 | interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); |
Glenn Kasten | a6d4133 | 2012-10-01 14:04:31 -0700 | [diff] [blame] | 427 | sP -= CHANNELS; sN += CHANNELS; coefs += 1 << c->coefsBits; |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 428 | } |
| 429 | } |
| 430 | |
| 431 | template<int CHANNELS> |
| 432 | void AudioResamplerSinc::interpolate( |
| 433 | int32_t& l, int32_t& r, |
Glenn Kasten | 54c3b66 | 2012-01-06 07:46:30 -0800 | [diff] [blame] | 434 | const int32_t* coefs, int16_t lerp, const int16_t* samples) |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 435 | { |
| 436 | int32_t c0 = coefs[0]; |
| 437 | int32_t c1 = coefs[1]; |
| 438 | int32_t sinc = mulAdd(lerp, (c1-c0)<<1, c0); |
| 439 | if (CHANNELS == 2) { |
Glenn Kasten | 54c3b66 | 2012-01-06 07:46:30 -0800 | [diff] [blame] | 440 | uint32_t rl = *reinterpret_cast<const uint32_t*>(samples); |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 441 | l = mulAddRL(1, rl, sinc, l); |
| 442 | r = mulAddRL(0, rl, sinc, r); |
| 443 | } else { |
| 444 | r = l = mulAdd(samples[0], sinc, l); |
| 445 | } |
| 446 | } |
Mathias Agopian | 65ab471 | 2010-07-14 17:59:35 -0700 | [diff] [blame] | 447 | // ---------------------------------------------------------------------------- |
| 448 | }; // namespace android |