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.
419 lines
11 KiB
419 lines
11 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 "convert.h"
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <config.h>
|
|
|
|
#ifdef HAVE_X86_FLOAT_INT
|
|
static inline long QRound (float inval)
|
|
{
|
|
#if (__GNUC__ == 3 && __GNUC_MINOR__ == 0)
|
|
volatile
|
|
#warning "workaround for gcc-3.0 bug c++/2733 enabled"
|
|
#endif
|
|
int ret;
|
|
__asm__ ("fistpl %0" : "=m" (ret) : "t" (inval) : "st");
|
|
return ret;
|
|
}
|
|
#else
|
|
static inline long QRound (float inval)
|
|
{
|
|
return (long)inval;
|
|
}
|
|
#endif
|
|
|
|
/*---------------------------- new code begin -------------------------- */
|
|
|
|
#define compose_16le(ptr) \
|
|
((((*((ptr)+1)+128)&0xff) << 8)+*(ptr))
|
|
|
|
#define compose_16be(ptr) \
|
|
((((*(ptr)+128)&0xff) << 8)+*((ptr)+1))
|
|
|
|
#define conv_16_float(x) \
|
|
((float)((x)-32768)/32768.0)
|
|
|
|
#define convert_16le_float(x) conv_16_float(compose_16le(&(x)))
|
|
#define convert_16be_float(x) conv_16_float(compose_16be(&(x)))
|
|
|
|
#define conv_8_float(x) \
|
|
((float)((x)-128)/128.0)
|
|
|
|
#define convert_8_float(x) \
|
|
((float)((x)-128)/128.0)
|
|
|
|
#define convert_float_float(x) x
|
|
|
|
/*
|
|
* 16le, 16be, 8, float
|
|
*/
|
|
|
|
#define datatype_16le unsigned char
|
|
#define datasize_16le 2
|
|
|
|
#define datatype_16be unsigned char
|
|
#define datasize_16be 2
|
|
|
|
#define datatype_8 unsigned char
|
|
#define datasize_8 1
|
|
|
|
#define datatype_float float
|
|
#define datasize_float 1
|
|
|
|
#define mk_converter(from_format,to_format) \
|
|
void Arts::convert_mono_ ## from_format ## _ ## to_format (unsigned long samples, \
|
|
datatype_ ## from_format *from, \
|
|
datatype_ ## to_format *to) \
|
|
{ \
|
|
datatype_ ## to_format *end = to+samples * (datasize_ ## to_format); \
|
|
while(to<end) { \
|
|
*to = convert_ ## from_format ## _ ## to_format(*from); \
|
|
from += datasize_ ## from_format; \
|
|
to += datasize_ ## to_format; \
|
|
} \
|
|
} \
|
|
void Arts::interpolate_mono_ ## from_format ## _ ## to_format (unsigned long samples,\
|
|
double startpos, double speed, \
|
|
datatype_ ## from_format *from, \
|
|
datatype_ ## to_format *to) \
|
|
{ \
|
|
double flpos = startpos; \
|
|
while(samples) { \
|
|
long position = ((long)(flpos)) * (datasize_ ## from_format); \
|
|
double error = flpos - floor(flpos); \
|
|
*to = (convert_ ## from_format ## _ ## to_format(from[position])) * \
|
|
(1.0-error) + (convert_ ## from_format ## _ ## \
|
|
to_format(from[position + datasize_ ## from_format])) * error; \
|
|
to += datasize_ ## to_format; \
|
|
flpos += speed; \
|
|
samples--; \
|
|
} \
|
|
} \
|
|
void Arts::convert_stereo_i ## from_format ## _2 ## to_format (unsigned long samples,\
|
|
datatype_ ## from_format *from, \
|
|
datatype_ ## to_format *left, \
|
|
datatype_ ## to_format *right) \
|
|
{ \
|
|
datatype_ ## to_format *end = left+samples * (datasize_ ## to_format); \
|
|
while(left<end) { \
|
|
*left = convert_ ## from_format ## _ ## to_format(*from); \
|
|
from += datasize_ ## from_format; \
|
|
left += datasize_ ## to_format; \
|
|
*right = convert_ ## from_format ## _ ## to_format(*from); \
|
|
from += datasize_ ## from_format; \
|
|
right += datasize_ ## to_format; \
|
|
} \
|
|
} \
|
|
void Arts::interpolate_stereo_i ## from_format ## _2 ## to_format (unsigned long samples,\
|
|
double startpos, double speed, \
|
|
datatype_ ## from_format *from, \
|
|
datatype_ ## to_format *left, \
|
|
datatype_ ## to_format *right) \
|
|
{ \
|
|
double flpos = startpos; \
|
|
while(samples) { \
|
|
long position = ((long)(flpos)) * (datasize_ ## from_format) * 2; \
|
|
double error = flpos - floor(flpos); \
|
|
*left = (convert_ ## from_format ## _ ## to_format(from[position])) * \
|
|
(1.0-error) + (convert_ ## from_format ## _ ## \
|
|
to_format(from[position + 2*datasize_ ## from_format]))*error; \
|
|
left += datasize_ ## to_format; \
|
|
position += datasize_ ## from_format; \
|
|
*right =(convert_ ## from_format ## _ ## to_format(from[position])) * \
|
|
(1.0-error) + (convert_ ## from_format ## _ ## \
|
|
to_format(from[position + 2*datasize_ ## from_format]))*error; \
|
|
right += datasize_ ## to_format; \
|
|
flpos += speed; \
|
|
samples--; \
|
|
} \
|
|
}
|
|
|
|
mk_converter(8,float)
|
|
mk_converter(16le,float)
|
|
mk_converter(16be,float)
|
|
mk_converter(float,float)
|
|
|
|
/*----------------------------- new code end --------------------------- */
|
|
|
|
/*
|
|
void old_convert_mono_8_float(unsigned long samples, unsigned char *from, float *to)
|
|
{
|
|
float *end = to+samples;
|
|
|
|
while(to<end) *to++ = conv_8_float(*from++);
|
|
}
|
|
|
|
void old_convert_mono_16le_float(unsigned long samples, unsigned char *from, float *to)
|
|
{
|
|
float *end = to+samples;
|
|
|
|
while(to<end)
|
|
{
|
|
*to++ = conv_16le_float(compose_16le(from));
|
|
from += 2;
|
|
}
|
|
}
|
|
|
|
|
|
void old_convert_stereo_i8_2float(unsigned long samples, unsigned char *from, float *left, float *right)
|
|
{
|
|
float *end = left+samples;
|
|
while(left < end)
|
|
{
|
|
*left++ = conv_8_float(*from++);
|
|
*right++ = conv_8_float(*from++);
|
|
}
|
|
}
|
|
|
|
void old_convert_stereo_i16le_2float(unsigned long samples, unsigned char *from, float *left, float *right)
|
|
{
|
|
float *end = left+samples;
|
|
while(left < end)
|
|
{
|
|
*left++ = conv_16le_float(compose_16le(from));
|
|
*right++ = conv_16le_float(compose_16le(from+2));
|
|
from += 4;
|
|
}
|
|
}
|
|
*/
|
|
|
|
void Arts::convert_stereo_2float_i16le(unsigned long samples, float *left, float *right, unsigned char *to)
|
|
{
|
|
float *end = left+samples;
|
|
long syn;
|
|
|
|
while(left < end)
|
|
{
|
|
syn = QRound((*left++)*32767);
|
|
|
|
if(syn < -32768) syn = -32768; /* clipping */
|
|
if(syn > 32767) syn = 32767;
|
|
|
|
*to++ = syn & 0xff;
|
|
*to++ = (syn >> 8) & 0xff;
|
|
|
|
syn = QRound((*right++)*32767);
|
|
|
|
if(syn < -32768) syn = -32768; /* clipping */
|
|
if(syn > 32767) syn = 32767;
|
|
|
|
*to++ = syn & 0xff;
|
|
*to++ = (syn >> 8) & 0xff;
|
|
}
|
|
}
|
|
|
|
void Arts::convert_mono_float_16le(unsigned long samples, float *from, unsigned char *to)
|
|
{
|
|
float *end = from+samples;
|
|
|
|
while(from < end)
|
|
{
|
|
long syn = QRound((*from++)*32767);
|
|
|
|
if(syn < -32768) syn = -32768; /* clipping */
|
|
if(syn > 32767) syn = 32767;
|
|
|
|
*to++ = syn & 0xff;
|
|
*to++ = (syn >> 8) & 0xff;
|
|
}
|
|
}
|
|
|
|
void Arts::convert_stereo_2float_i16be(unsigned long samples, float *left, float *right, unsigned char *to)
|
|
{
|
|
float *end = left+samples;
|
|
long syn;
|
|
|
|
while(left < end)
|
|
{
|
|
syn = QRound((*left++)*32767);
|
|
|
|
if(syn < -32768) syn = -32768; /* clipping */
|
|
if(syn > 32767) syn = 32767;
|
|
|
|
*to++ = (syn >> 8) & 0xff;
|
|
*to++ = syn & 0xff;
|
|
|
|
syn = QRound((*right++)*32767);
|
|
|
|
if(syn < -32768) syn = -32768; /* clipping */
|
|
if(syn > 32767) syn = 32767;
|
|
|
|
*to++ = (syn >> 8) & 0xff;
|
|
*to++ = syn & 0xff;
|
|
}
|
|
}
|
|
|
|
void Arts::convert_mono_float_16be(unsigned long samples, float *from, unsigned char *to)
|
|
{
|
|
float *end = from+samples;
|
|
|
|
while(from < end)
|
|
{
|
|
long syn = QRound((*from++)*32767);
|
|
|
|
if(syn < -32768) syn = -32768; /* clipping */
|
|
if(syn > 32767) syn = 32767;
|
|
|
|
*to++ = (syn >> 8) & 0xff;
|
|
*to++ = syn & 0xff;
|
|
}
|
|
}
|
|
|
|
void Arts::convert_stereo_2float_i8(unsigned long samples, float *left, float *right, unsigned char *to)
|
|
{
|
|
float *end = left+samples;
|
|
long syn;
|
|
|
|
while(left < end)
|
|
{
|
|
syn = (int)((*left++)*127+128);
|
|
|
|
if(syn < 0) syn = 0; /* clipping */
|
|
if(syn > 255) syn = 255;
|
|
|
|
*to++ = syn;
|
|
|
|
syn = (int)((*right++)*127+128);
|
|
|
|
if(syn < 0) syn = 0; /* clipping */
|
|
if(syn > 255) syn = 255;
|
|
|
|
*to++ = syn;
|
|
}
|
|
}
|
|
|
|
void Arts::convert_mono_float_8(unsigned long samples, float *from, unsigned char *to)
|
|
{
|
|
float *end = from+samples;
|
|
|
|
while(from < end)
|
|
{
|
|
long syn = (int)((*from++)*127+128);
|
|
|
|
if(syn < 0) syn = 0; /* clipping */
|
|
if(syn > 255) syn = 255;
|
|
|
|
*to++ = syn;
|
|
}
|
|
}
|
|
|
|
unsigned long Arts::uni_convert_stereo_2float(
|
|
unsigned long samples, // number of required samples
|
|
unsigned char *from, // buffer containing the samples
|
|
unsigned long fromLen, // length of the buffer
|
|
unsigned int fromChannels, // channels stored in the buffer
|
|
unsigned int fromBits, // number of bits per sample
|
|
float *left, float *right, // output buffers for left and right channel
|
|
double speed, // speed (2.0 means twice as fast)
|
|
double startposition // startposition
|
|
)
|
|
{
|
|
unsigned long doSamples = 0, sampleSize = fromBits/8;
|
|
|
|
if(fromBits == uni_convert_float_ne)
|
|
sampleSize = sizeof(float);
|
|
|
|
// how many samples does the from-buffer contain?
|
|
double allSamples = fromLen / (fromChannels * sampleSize);
|
|
|
|
// how many samples are remaining?
|
|
// subtract one due to interpolation and another against rounding errors
|
|
double fHaveSamples = allSamples - startposition - 2.0;
|
|
fHaveSamples /= speed;
|
|
|
|
// convert do "how many samples to do"?
|
|
if(fHaveSamples > 0)
|
|
{
|
|
doSamples = (int)fHaveSamples;
|
|
if(doSamples > samples) doSamples = samples;
|
|
}
|
|
|
|
// do conversion
|
|
if(doSamples)
|
|
{
|
|
if(fromChannels == 1)
|
|
{
|
|
if(fromBits == uni_convert_float_ne) {
|
|
interpolate_mono_float_float(doSamples,
|
|
startposition,speed,(float *)from,left);
|
|
}
|
|
else if(fromBits == uni_convert_s16_be) {
|
|
interpolate_mono_16be_float(doSamples,
|
|
startposition,speed,from,left);
|
|
}
|
|
else if(fromBits == uni_convert_s16_le) {
|
|
interpolate_mono_16le_float(doSamples,
|
|
startposition,speed,from,left);
|
|
}
|
|
else {
|
|
interpolate_mono_8_float(doSamples,
|
|
startposition,speed,from,left);
|
|
}
|
|
memcpy(right,left,sizeof(float)*doSamples);
|
|
}
|
|
else if(fromChannels == 2)
|
|
{
|
|
if(fromBits == uni_convert_float_ne) {
|
|
interpolate_stereo_ifloat_2float(doSamples,
|
|
startposition,speed,(float *)from,left,right);
|
|
}
|
|
else if(fromBits == uni_convert_s16_be) {
|
|
interpolate_stereo_i16be_2float(doSamples,
|
|
startposition,speed,from,left,right);
|
|
}
|
|
else if(fromBits == uni_convert_s16_le) {
|
|
interpolate_stereo_i16le_2float(doSamples,
|
|
startposition,speed,from,left,right);
|
|
}
|
|
else {
|
|
interpolate_stereo_i8_2float(doSamples,
|
|
startposition,speed,from,left,right);
|
|
}
|
|
} else {
|
|
assert(false);
|
|
}
|
|
}
|
|
return doSamples;
|
|
}
|
|
|
|
// undefine all that stuff (due to --enable-final)
|
|
#undef compose_16le
|
|
#undef compose_16be
|
|
#undef conv_16_float
|
|
#undef convert_16le_float
|
|
#undef convert_16be_float
|
|
#undef conv_8_float
|
|
#undef convert_8_float
|
|
#undef convert_float_float
|
|
#undef datatype_16le
|
|
#undef datasize_16le
|
|
#undef datatype_16be
|
|
#undef datasize_16be
|
|
#undef datatype_8
|
|
#undef datasize_8
|
|
#undef datatype_float
|
|
#undef datasize_float
|
|
#undef mk_converter
|