diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-05 00:01:18 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-05 00:01:18 +0000 |
commit | 42995d7bf396933ee60c5f89c354ea89cf13df0d (patch) | |
tree | cfdcea0ac57420e7baf570bfe435e107bb842541 /flow/resample.cc | |
download | arts-42995d7bf396933ee60c5f89c354ea89cf13df0d.tar.gz arts-42995d7bf396933ee60c5f89c354ea89cf13df0d.zip |
Copy of aRts for Trinity modifications
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/dependencies/arts@1070145 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'flow/resample.cc')
-rw-r--r-- | flow/resample.cc | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/flow/resample.cc b/flow/resample.cc new file mode 100644 index 0000000..45cd099 --- /dev/null +++ b/flow/resample.cc @@ -0,0 +1,305 @@ + /* + + Copyright (C) 2000 Stefan Westerfeld + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + */ + +#include "resample.h" +#include "debug.h" +#include <math.h> +#include <assert.h> +#include <stdio.h> + +#define compose_16le(first,second) \ + (((((second)+128)&0xff) << 8)+(first)) + +#define compose_16be(first,second) \ + (((((first)+128)&0xff) << 8)+(second)) + +#define conv_16_float(x) \ + ((float)((x)-32768)/32768.0) + +#define conv_8_float(x) \ + ((float)((x)-128)/128.0) + +using namespace Arts; + +class Arts::ResamplerPrivate { +public: + bool underrun; + Resampler::Endianness endianness; +}; + +const unsigned int Resampler::bufferSize; +const unsigned int Resampler::bufferWrap; + +Resampler::Resampler(Refiller *refiller) : + dropBytes(0), refiller(refiller), pos(0.0), step(1.0), channels(2), + bits(16), + block(0), haveBlock(-1) +{ + d = new ResamplerPrivate(); + d->underrun = false; + d->endianness = littleEndian; + updateSampleSize(); +} + +Resampler::~Resampler() +{ + delete d; +} + +void Resampler::updateSampleSize() +{ + sampleSize = channels * bits / 8; + bufferSamples = bufferSize / sampleSize; +} + +void Resampler::setStep(double newStep) +{ + arts_return_if_fail(newStep > 0); + + step = newStep; +} + +void Resampler::setChannels(int newChannels) +{ + arts_return_if_fail(newChannels == 1 || newChannels == 2); + + channels = newChannels; + updateSampleSize(); +} + +void Resampler::setBits(int newBits) +{ + arts_return_if_fail(newBits == 8 || newBits == 16); + + bits = newBits; + updateSampleSize(); +} + +void Resampler::setEndianness(Endianness newEndianness) +{ + arts_return_if_fail(newEndianness == bigEndian || newEndianness == littleEndian); + + d->endianness = newEndianness; +} + +bool Resampler::underrun() +{ + return d->underrun; +} + +void Resampler::ensureRefill() +{ + if(haveBlock == block) return; + + unsigned long missing; + if(block == 0) + { + missing = bufferSize+sampleSize + - refiller->read(buffer,bufferSize+sampleSize); + + d->underrun = (missing == bufferSize+sampleSize); + } + else + { + /* + * try to drop away "half-sample" reads from the last refill + */ + if(dropBytes > 0) + dropBytes -= refiller->read(buffer,dropBytes); + + /* + * only if this worked there is hope that we can read sane data + */ + if(dropBytes == 0) + { + missing = bufferSize + - refiller->read(&buffer[sampleSize], bufferSize); + + d->underrun = (missing == bufferSize); + } + else + { + missing = bufferSize; + d->underrun = true; + } + } + haveBlock++; + assert(haveBlock == block); + + /* + * If we don't have enough input to fill the block fully, it might be + * that the input stall occurred in the middle of a sample. For instance, + * if samples are 4 bytes long, it might be that we would have needed + * 13 more bytes to do a full refill. + * + * In this situation, there are four samples and one byte missing to + * refill the buffer - the one byte is what we need to care about here: + * on the next read, we'll have one byte too much (if we simply ignore + * the fact, we end up with misaligned reading, causing noise, or + * swapped stereo channels or similar). + * + * So we set dropBytes here, which is a variable which indicates how + * many bytes to drop away upon next refill. + */ + if(missing & (sampleSize - 1)) + dropBytes = missing & (sampleSize - 1); + + unsigned int i = 0, wrap = (block == 0)?0:sampleSize; + if(bits == 16) + { + // wrap the last part of the buffer back to the beginning (history) + while(i<wrap) + { + fbuffer[i/2] = fbuffer[(bufferSize+i)/2]; + i += 2; + } + + // convert data from incoming + if(d->endianness == littleEndian) + { + while(i<bufferSize+sampleSize-missing) + { + fbuffer[i/2] = conv_16_float(compose_16le(buffer[i],buffer[i+1])); + i += 2; + } + } + else + { + while(i<bufferSize+sampleSize-missing) + { + fbuffer[i/2] = conv_16_float(compose_16be(buffer[i],buffer[i+1])); + i += 2; + } + } + + // fill up missing bytes with zero samples + while(i<bufferSize+sampleSize) + { + fbuffer[i/2] = 0.0; + i += 2; + } + } + else if(bits == 8) + { + // wrap the last part of the buffer back to the beginning (history) + while(i<wrap) + { + fbuffer[i] = fbuffer[bufferSize+i]; + i++; + } + + // convert data from incoming + while(i<bufferSize+sampleSize-missing) + { + fbuffer[i] = conv_8_float(buffer[i]); + i++; + } + + // fill up missing bytes with zero samples + while(i<bufferSize+sampleSize) + { + fbuffer[i++] = 0.0; + } + } + else + { + assert(false); + } +} + +#define RESAMPLER_STEP() \ + pos += step; \ + i++; \ + while(pos >= bufferSamples) \ + { \ + pos -= bufferSamples; \ + block++; \ + ensureRefill(); \ + } + +void Resampler::run(float *left, float *right, unsigned long samples) +{ + ensureRefill(); + unsigned long i = 0; + double delta = step - floor(step); + bool interpolate = fabs(delta) > 0.001; + + if(channels == 2 && interpolate) + { + while(i < samples) + { + double error = pos - floor(pos); + unsigned long offset = 2*(unsigned long)pos; + + left[i] = fbuffer[offset+0]*(1.0-error)+fbuffer[offset+2]*error; + right[i] = fbuffer[offset+1]*(1.0-error)+fbuffer[offset+3]*error; + RESAMPLER_STEP(); + } + } + else if(channels == 1 && interpolate) + { + while(i < samples) + { + double error = pos - floor(pos); + unsigned long offset = (unsigned long)pos; + + left[i] = right[i] = fbuffer[offset]*(1.0-error) + + fbuffer[offset+1]*error; + RESAMPLER_STEP(); + } + } + else if(channels == 2) + { + while(i < samples) + { + unsigned long offset = 2*(unsigned long)pos; + + left[i] = fbuffer[offset+0]; + right[i] = fbuffer[offset+1]; + RESAMPLER_STEP(); + } + } + else if(channels == 1) + { + while(i < samples) + { + unsigned long offset = (unsigned long)pos; + + left[i] = right[i] = fbuffer[offset]; + RESAMPLER_STEP(); + } + } + else + { + assert(false); + } +} + +Refiller::~Refiller() +{ +} + +#undef RESAMPLER_STEP +#undef compose_16le +#undef compose_16be +#undef conv_16_float +#undef conv_8_float |