You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
306 lines
6.6 KiB
306 lines
6.6 KiB
/*
|
|
|
|
Copyright (C) 2000 Stefan Westerfeld
|
|
stefan@space.twc.de
|
|
|
|
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., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, 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
|