93 lines
3.7 KiB
C++
93 lines
3.7 KiB
C++
/************************************************************************
|
|
* Copyright (c) 2005-2007 tok@openlinux.org.uk *
|
|
* *
|
|
* This software is provided as-is, without any express or implied *
|
|
* warranty. In no event will the authors be held liable for any *
|
|
* damages arising from the use of this software. *
|
|
* *
|
|
* Permission is granted to anyone to use this software for any purpose, *
|
|
* including commercial applications, and to alter it and redistribute *
|
|
* it freely, subject to the following restrictions: *
|
|
* *
|
|
* 1. The origin of this software must not be misrepresented; you must *
|
|
* not claim that you wrote the original software. If you use this *
|
|
* software in a product, an acknowledgment in the product documentation *
|
|
* would be appreciated but is not required. *
|
|
* *
|
|
* 2. Altered source versions must be plainly marked as such, and must *
|
|
* not be misrepresented as being the original software. *
|
|
* *
|
|
* 3. This notice may not be removed or altered from any source *
|
|
* distribution. *
|
|
************************************************************************/
|
|
#include "sound_resample2.h"
|
|
#include "interpolate.hpp"
|
|
|
|
namespace Audio {
|
|
// resample uint8 mono to Sint16 stereo
|
|
// assumption: src_rate < wanted_rate
|
|
|
|
// derived from some sweet code found in the exult repository,
|
|
// now rewritten to avoid license trouble...
|
|
// yes, it is (still) similiar - who would have guessed?
|
|
Sint16 *resample_new (Uint8 * src, size_t sourcelen, size_t & size,
|
|
int rate, int wanted_rate) {
|
|
int fp_pos = 0;
|
|
int fp_speed = (1 << 16) * rate / wanted_rate;
|
|
size = sourcelen;
|
|
// adjust the magnitudes of size and rate to prevent division error
|
|
while (size & 0xFFFF0000)
|
|
size >>= 1, rate = (rate >> 1) + 1;
|
|
// Compute the output size (times 4 since it is 16 stereo)
|
|
size = (size * wanted_rate / rate) << 2;
|
|
|
|
Sint16 *stereo_data = new Sint16[size];
|
|
Sint16 *data = stereo_data;
|
|
Uint8 *src_end = src + sourcelen;
|
|
|
|
int result;
|
|
|
|
// Compute the initial data feed for the interpolator. We don't simply
|
|
// shift by 8, but rather duplicate the byte, this way we cover the full
|
|
// range. Probably doesn't make a big difference, listening wise :-)
|
|
int a = *(src + 0);
|
|
a = (a | (a << 8)) - 32768;
|
|
int b = *(src + 1);
|
|
b = (a | (b << 8)) - 32768;
|
|
int c = *(src + 2);
|
|
c = (a | (c << 8)) - 32768;
|
|
|
|
// scale down by 0.8333 to avoid oversampling
|
|
Math::Interpolator::Cubic<float> inter(a * 0.8333f, b * 0.8333f, c * 0.8333f);
|
|
do
|
|
{
|
|
do
|
|
{
|
|
result = int(inter.getAt(fp_pos / float(0x0000FFFF)));
|
|
// limit the range
|
|
if (result < -32768)
|
|
result = -32768;
|
|
else if (result > 32767)
|
|
result = 32767;
|
|
*data++ = result;
|
|
*data++ = result;
|
|
|
|
fp_pos += fp_speed;
|
|
}
|
|
while (!(fp_pos & 0xFFFF0000)); // need more input data
|
|
src++;
|
|
fp_pos &= 0x0000FFFF;
|
|
if (src + 2 < src_end) // still data to fetch
|
|
{
|
|
c = *(src + 2);
|
|
c = (c | (c << 8)) - 32768;
|
|
inter.shiftAndFeed (c * 0.8333f);
|
|
}
|
|
else // guess & interpolate... this the end, my only friend the end
|
|
inter.shift ();
|
|
}
|
|
while (src < src_end);
|
|
return stereo_data;
|
|
}
|
|
}
|