Glenn Kasten d8b2e2b9e3 Remove aliasing
Code was aliasing mBuffer as buffer, but continuing to use both buffer
and mBuffer after that point.  This was at best misleading, and at worst
could confuse the compiler into generating bad code.  There was no
performance advantage to the alias, in fact removing it saves 16 bytes.

Change-Id: I55023ddba465d9be82f66745b088d18af658ac60
2012-02-09 16:58:07 -08:00

358 lines
14 KiB
C++

/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include "AudioResamplerSinc.h"
namespace android {
// ----------------------------------------------------------------------------
/*
* These coeficients are computed with the "fir" utility found in
* tools/resampler_tools
* TODO: A good optimization would be to transpose this matrix, to take
* better advantage of the data-cache.
*/
const int32_t AudioResamplerSinc::mFirCoefsUp[] = {
0x7fffffff, 0x7f15d078, 0x7c5e0da6, 0x77ecd867, 0x71e2e251, 0x6a6c304a, 0x61be7269, 0x58170412, 0x4db8ab05, 0x42e92ea6, 0x37eee214, 0x2d0e3bb1, 0x22879366, 0x18951e95, 0x0f693d0d, 0x072d2621,
0x00000000, 0xf9f66655, 0xf51a5fd7, 0xf16bbd84, 0xeee0d9ac, 0xed67a922, 0xece70de6, 0xed405897, 0xee50e505, 0xeff3be30, 0xf203370f, 0xf45a6741, 0xf6d67d53, 0xf957db66, 0xfbc2f647, 0xfe00f2b9,
0x00000000, 0x01b37218, 0x0313a0c6, 0x041d930d, 0x04d28057, 0x053731b0, 0x05534dff, 0x05309bfd, 0x04da440d, 0x045c1aee, 0x03c1fcdd, 0x03173ef5, 0x02663ae8, 0x01b7f736, 0x0113ec79, 0x007fe6a9,
0x00000000, 0xff96b229, 0xff44f99f, 0xff0a86be, 0xfee5f803, 0xfed518fd, 0xfed521fd, 0xfee2f4fd, 0xfefb54f8, 0xff1b159b, 0xff3f4203, 0xff6539e0, 0xff8ac502, 0xffae1ddd, 0xffcdf3f9, 0xffe96798,
0x00000000, 0x00119de6, 0x001e6b7e, 0x0026cb7a, 0x002b4830, 0x002c83d6, 0x002b2a82, 0x0027e67a, 0x002356f9, 0x001e098e, 0x001875e4, 0x0012fbbe, 0x000de2d1, 0x00095c10, 0x00058414, 0x00026636,
0x00000000, 0xfffe44a9, 0xfffd206d, 0xfffc7b7f, 0xfffc3c8f, 0xfffc4ac2, 0xfffc8f2b, 0xfffcf5c4, 0xfffd6df3, 0xfffdeab2, 0xfffe6275, 0xfffececf, 0xffff2c07, 0xffff788c, 0xffffb471, 0xffffe0f2,
0x00000000, 0x000013e6, 0x00001f03, 0x00002396, 0x00002399, 0x000020b6, 0x00001c3c, 0x00001722, 0x00001216, 0x00000d81, 0x0000099c, 0x0000067c, 0x00000419, 0x0000025f, 0x00000131, 0x00000070,
0x00000000, 0xffffffc7, 0xffffffb3, 0xffffffb3, 0xffffffbe, 0xffffffcd, 0xffffffdb, 0xffffffe7, 0xfffffff0, 0xfffffff7, 0xfffffffb, 0xfffffffe, 0xffffffff, 0x00000000, 0x00000000, 0x00000000,
0x00000000 // this one is needed for lerping the last coefficient
};
/*
* These coefficients are optimized for 48KHz -> 44.1KHz (stop-band at 22.050KHz)
* It's possible to use the above coefficient for any down-sampling
* at the expense of a slower processing loop (we can interpolate
* these coefficient from the above by "Stretching" them in time).
*/
const int32_t AudioResamplerSinc::mFirCoefsDown[] = {
0x7fffffff, 0x7f55e46d, 0x7d5b4c60, 0x7a1b4b98, 0x75a7fb14, 0x7019f0bd, 0x698f875a, 0x622bfd59, 0x5a167256, 0x5178cc54, 0x487e8e6c, 0x3f53aae8, 0x36235ad4, 0x2d17047b, 0x245539ab, 0x1c00d540,
0x14383e57, 0x0d14d5ca, 0x06aa910b, 0x0107c38b, 0xfc351654, 0xf835abae, 0xf5076b45, 0xf2a37202, 0xf0fe9faa, 0xf00a3bbd, 0xefb4aa81, 0xefea2b05, 0xf0959716, 0xf1a11e83, 0xf2f6f7a0, 0xf481fff4,
0xf62e48ce, 0xf7e98ca5, 0xf9a38b4c, 0xfb4e4bfa, 0xfcde456f, 0xfe4a6d30, 0xff8c2fdf, 0x009f5555, 0x0181d393, 0x0233940f, 0x02b62f06, 0x030ca07d, 0x033afa62, 0x03461725, 0x03334f83, 0x030835fa,
0x02ca59cc, 0x027f12d1, 0x022b570d, 0x01d39a49, 0x017bb78f, 0x0126e414, 0x00d7aaaf, 0x008feec7, 0x0050f584, 0x001b73e3, 0xffefa063, 0xffcd46ed, 0xffb3ddcd, 0xffa29aaa, 0xff988691, 0xff949066,
0xff959d24, 0xff9a959e, 0xffa27195, 0xffac4011, 0xffb72d2b, 0xffc28569, 0xffcdb706, 0xffd85171, 0xffe20364, 0xffea97e9, 0xfff1f2b2, 0xfff80c06, 0xfffcec92, 0x0000a955, 0x00035fd8, 0x000532cf,
0x00064735, 0x0006c1f9, 0x0006c62d, 0x000673ba, 0x0005e68f, 0x00053630, 0x000475a3, 0x0003b397, 0x0002fac1, 0x00025257, 0x0001be9e, 0x0001417a, 0x0000dafd, 0x000089eb, 0x00004c28, 0x00001f1d,
0x00000000, 0xffffec10, 0xffffe0be, 0xffffdbc5, 0xffffdb39, 0xffffdd8b, 0xffffe182, 0xffffe638, 0xffffeb0a, 0xffffef8f, 0xfffff38b, 0xfffff6e3, 0xfffff993, 0xfffffba6, 0xfffffd30, 0xfffffe4a,
0xffffff09, 0xffffff85, 0xffffffd1, 0xfffffffb, 0x0000000f, 0x00000016, 0x00000015, 0x00000012, 0x0000000d, 0x00000009, 0x00000006, 0x00000003, 0x00000002, 0x00000001, 0x00000000, 0x00000000,
0x00000000 // this one is needed for lerping the last coefficient
};
// ----------------------------------------------------------------------------
static inline
int32_t mulRL(int left, int32_t in, uint32_t vRL)
{
#if defined(__arm__) && !defined(__thumb__)
int32_t out;
if (left) {
asm( "smultb %[out], %[in], %[vRL] \n"
: [out]"=r"(out)
: [in]"%r"(in), [vRL]"r"(vRL)
: );
} else {
asm( "smultt %[out], %[in], %[vRL] \n"
: [out]"=r"(out)
: [in]"%r"(in), [vRL]"r"(vRL)
: );
}
return out;
#else
if (left) {
return int16_t(in>>16) * int16_t(vRL&0xFFFF);
} else {
return int16_t(in>>16) * int16_t(vRL>>16);
}
#endif
}
static inline
int32_t mulAdd(int16_t in, int32_t v, int32_t a)
{
#if defined(__arm__) && !defined(__thumb__)
int32_t out;
asm( "smlawb %[out], %[v], %[in], %[a] \n"
: [out]"=r"(out)
: [in]"%r"(in), [v]"r"(v), [a]"r"(a)
: );
return out;
#else
return a + in * (v>>16);
// improved precision
// return a + in * (v>>16) + ((in * (v & 0xffff)) >> 16);
#endif
}
static inline
int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
{
#if defined(__arm__) && !defined(__thumb__)
int32_t out;
if (left) {
asm( "smlawb %[out], %[v], %[inRL], %[a] \n"
: [out]"=r"(out)
: [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
: );
} else {
asm( "smlawt %[out], %[v], %[inRL], %[a] \n"
: [out]"=r"(out)
: [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
: );
}
return out;
#else
if (left) {
return a + (int16_t(inRL&0xFFFF) * (v>>16));
//improved precision
// return a + (int16_t(inRL&0xFFFF) * (v>>16)) + ((int16_t(inRL&0xFFFF) * (v & 0xffff)) >> 16);
} else {
return a + (int16_t(inRL>>16) * (v>>16));
}
#endif
}
// ----------------------------------------------------------------------------
AudioResamplerSinc::AudioResamplerSinc(int bitDepth,
int inChannelCount, int32_t sampleRate)
: AudioResampler(bitDepth, inChannelCount, sampleRate),
mState(0)
{
/*
* Layout of the state buffer for 32 tap:
*
* "present" sample beginning of 2nd buffer
* v v
* 0 01 2 23 3
* 0 F0 0 F0 F
* [pppppppppppppppInnnnnnnnnnnnnnnnpppppppppppppppInnnnnnnnnnnnnnnn]
* ^ ^ head
*
* p = past samples, convoluted with the (p)ositive side of sinc()
* n = future samples, convoluted with the (n)egative side of sinc()
* r = extra space for implementing the ring buffer
*
*/
const size_t numCoefs = 2*halfNumCoefs;
const size_t stateSize = numCoefs * inChannelCount * 2;
mState = new int16_t[stateSize];
memset(mState, 0, sizeof(int16_t)*stateSize);
mImpulse = mState + (halfNumCoefs-1)*inChannelCount;
mRingFull = mImpulse + (numCoefs+1)*inChannelCount;
}
AudioResamplerSinc::~AudioResamplerSinc()
{
delete [] mState;
}
void AudioResamplerSinc::init() {
}
void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown;
// select the appropriate resampler
switch (mChannelCount) {
case 1:
resample<1>(out, outFrameCount, provider);
break;
case 2:
resample<2>(out, outFrameCount, provider);
break;
}
}
template<int CHANNELS>
void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
int16_t* impulse = mImpulse;
uint32_t vRL = mVolumeRL;
size_t inputIndex = mInputIndex;
uint32_t phaseFraction = mPhaseFraction;
uint32_t phaseIncrement = mPhaseIncrement;
size_t outputIndex = 0;
size_t outputSampleCount = outFrameCount * 2;
size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
while (outputIndex < outputSampleCount) {
// buffer is empty, fetch a new one
while (mBuffer.frameCount == 0) {
mBuffer.frameCount = inFrameCount;
provider->getNextBuffer(&mBuffer);
if (mBuffer.raw == NULL) {
goto resample_exit;
}
const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
if (phaseIndex == 1) {
// read one frame
read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex);
} else if (phaseIndex == 2) {
// read 2 frames
read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex);
inputIndex++;
if (inputIndex >= mBuffer.frameCount) {
inputIndex -= mBuffer.frameCount;
provider->releaseBuffer(&mBuffer);
} else {
read<CHANNELS>(impulse, phaseFraction, mBuffer.i16, inputIndex);
}
}
}
int16_t *in = mBuffer.i16;
const size_t frameCount = mBuffer.frameCount;
// Always read-in the first samples from the input buffer
int16_t* head = impulse + halfNumCoefs*CHANNELS;
head[0] = in[inputIndex*CHANNELS + 0];
if (CHANNELS == 2)
head[1] = in[inputIndex*CHANNELS + 1];
// handle boundary case
int32_t l, r;
while (outputIndex < outputSampleCount) {
filterCoefficient<CHANNELS>(l, r, phaseFraction, impulse);
out[outputIndex++] += 2 * mulRL(1, l, vRL);
out[outputIndex++] += 2 * mulRL(0, r, vRL);
phaseFraction += phaseIncrement;
const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
if (phaseIndex == 1) {
inputIndex++;
if (inputIndex >= frameCount)
break; // need a new buffer
read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
} else if(phaseIndex == 2) { // maximum value
inputIndex++;
if (inputIndex >= frameCount)
break; // 0 frame available, 2 frames needed
// read first frame
read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
inputIndex++;
if (inputIndex >= frameCount)
break; // 0 frame available, 1 frame needed
// read second frame
read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
}
}
// if done with buffer, save samples
if (inputIndex >= frameCount) {
inputIndex -= frameCount;
provider->releaseBuffer(&mBuffer);
}
}
resample_exit:
mImpulse = impulse;
mInputIndex = inputIndex;
mPhaseFraction = phaseFraction;
}
template<int CHANNELS>
/***
* read()
*
* This function reads only one frame from input buffer and writes it in
* state buffer
*
**/
void AudioResamplerSinc::read(
int16_t*& impulse, uint32_t& phaseFraction,
const int16_t* in, size_t inputIndex)
{
const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
impulse += CHANNELS;
phaseFraction -= 1LU<<kNumPhaseBits;
if (impulse >= mRingFull) {
const size_t stateSize = (halfNumCoefs*2)*CHANNELS;
memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize);
impulse -= stateSize;
}
int16_t* head = impulse + halfNumCoefs*CHANNELS;
head[0] = in[inputIndex*CHANNELS + 0];
if (CHANNELS == 2)
head[1] = in[inputIndex*CHANNELS + 1];
}
template<int CHANNELS>
void AudioResamplerSinc::filterCoefficient(
int32_t& l, int32_t& r, uint32_t phase, const int16_t *samples)
{
// compute the index of the coefficient on the positive side and
// negative side
uint32_t indexP = (phase & cMask) >> cShift;
uint16_t lerpP = (phase & pMask) >> pShift;
uint32_t indexN = (-phase & cMask) >> cShift;
uint16_t lerpN = (-phase & pMask) >> pShift;
if ((indexP == 0) && (lerpP == 0)) {
indexN = cMask >> cShift;
lerpN = pMask >> pShift;
}
l = 0;
r = 0;
const int32_t* coefs = mFirCoefs;
const int16_t *sP = samples;
const int16_t *sN = samples+CHANNELS;
for (unsigned int i=0 ; i<halfNumCoefs/4 ; i++) {
interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
}
}
template<int CHANNELS>
void AudioResamplerSinc::interpolate(
int32_t& l, int32_t& r,
const int32_t* coefs, int16_t lerp, const int16_t* samples)
{
int32_t c0 = coefs[0];
int32_t c1 = coefs[1];
int32_t sinc = mulAdd(lerp, (c1-c0)<<1, c0);
if (CHANNELS == 2) {
uint32_t rl = *reinterpret_cast<const uint32_t*>(samples);
l = mulAddRL(1, rl, sinc, l);
r = mulAddRL(0, rl, sinc, r);
} else {
r = l = mulAdd(samples[0], sinc, l);
}
}
// ----------------------------------------------------------------------------
}; // namespace android