# HG changeset patch # User Ryan C. Gordon # Date 1021911489 0 # Node ID 7b9a0f3f030e9507a6d643f0f4673fc2790532c0 # Parent 83233d8a592906199492b466a8dadc4700b7d96c Initial add. diff -r 83233d8a5929 -r 7b9a0f3f030e alt_audio_convert.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/alt_audio_convert.c Mon May 20 16:18:09 2002 +0000 @@ -0,0 +1,463 @@ +/* + Extended Audio Converter for SDL (Simple DirectMedia Layer) + Copyright (C) 2002 Frank Ranostaj + Institute of Applied Physik + Johann Wolfgang Goethe-Universität + Frankfurt am Main, Germany + + 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; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Frank Ranostaj + ranostaj@stud.uni-frankfurt.de + +(This code blatantly abducted for SDL_sound. Thanks, Frank! --ryan.) +*/ + +#include "alt_audio_convert.h" + +#include +#include + +/*provisorisch*/ +#define AUDIO_8 (4) +#define AUDIO_16WRONG (8) +#define AUDIO_FORMAT (AUDIO_8 | AUDIO_16WRONG) +#define AUDIO_SIGN (1) + + +/*-------------------------------------------------------------------------*/ +/* this filter (Kaiser-window beta=6.8) gives a decent -80dB attentuation */ +static const int filter[_fsize/2] = { + 0, 20798, 0, -6764, 0, 3863, 0, -2560, 0, 1800, 0, -1295, 0, 936, 0, +-671, + 0, 474, 0, -326, 0, 217, 0, -138, 0, 83, 0, -46, 0, 23, 0, -9 +}; + +/* Mono (1 channel ) */ +#define Suffix(x) x##1 +#include "filter_templates.h" +#undef Suffix + +/* Stereo (2 channel ) */ +#define Suffix(x) x##2 +#include "filter_templates.h" +#undef Suffix + +/* !!! FIXME: Lose all the "short" vars for "Sint16", etc. */ + +/*-------------------------------------------------------------------------*/ +int DECLSPEC Sound_ConvertAudio( Sound_AudioCVT *Data ) +{ + AdapterC Temp; + int i; + + /* !!! FIXME: Try the looping stuff under certain circumstances? --ryan. */ + int mode = 0; + + /* Make sure there's a converter */ + if( Data == NULL ) { + SDL_SetError("No converter given"); + return(-1); + } + + /* Make sure there's data to convert */ + if( Data->buf == NULL ) { + SDL_SetError("No buffer allocated for conversion"); + return(-1); + } + + /* Set up the conversion and go! */ + Temp.buffer = (short*)Data->buf; + Temp.mode = mode; + Temp.filter = &Data->filter; + + Data->len_cvt = Data->len; + for( i = 0; Data->adapter[i] != NULL; i++ ) + Data->len_cvt = (*Data->adapter[i])( Temp, Data->len_cvt); + + return(0); +} + +/*-------------------------------------------------------------------------*/ +int expand8BitTo16Bit( AdapterC Data, int length ) +{ + int i; + char* inp = (char*)Data.buffer-1; + short* buffer = Data.buffer-1; + for( i = length; i; i-- ) + buffer[i] = inp[i]<<8; + return length*2; +} + +/*-------------------------------------------------------------------------*/ +int swapBytes( AdapterC Data, int length ) +{ + int i; + unsigned short a,b; + short* buffer = Data.buffer; + for( i = 0; i < length; i++ ) + { + a = b = buffer[i]; + a <<= 8; + b >>= 8; + buffer[i] = a | b; + } + return length; +} + +/*-------------------------------------------------------------------------*/ +int cut16BitTo8Bit( AdapterC Data, int length ) +{ + int i; + short* inp = Data.buffer-1; + char* buffer = (char*)Data.buffer-1; + for( i = 0; i < length; i++ ) + buffer[i] = inp[i]>>8; + return length/2; +} + +/*-------------------------------------------------------------------------*/ +int changeSigned( AdapterC Data, int length ) +{ + int i; + short* buffer = Data.buffer; + for( i = 0; i < length; i++ ) + buffer[i] ^= 0x8000; + return length; +} + +/*-------------------------------------------------------------------------*/ +int convertStereoToMono( AdapterC Data, int length ) +{ + int i; + short* buffer = Data.buffer; + + /* + * !!! FIXME: Can we avoid the division in this loop and just keep + * !!! FIXME: a second index variable? --ryan. + */ + for( i = 0; i < length; i+=2 ) + buffer[i/2] = ((int)buffer[i] + buffer[i+1] ) >> 1; + return length/2; +} + +/*-------------------------------------------------------------------------*/ +int convertMonoToStereo( AdapterC Data, int length ) +{ + int i; + short* buffer = Data.buffer-2; + length *= 2; + + /* + * !!! FIXME: Can we avoid the division in this loop and just keep + * !!! FIXME: a second index variable? --ryan. + */ + for( i = length; i; i-=2 ) + buffer[i] = buffer [i+1] = buffer[i/2]; + return length*2; +} + +/*-------------------------------------------------------------------------*/ +int minus5dB( AdapterC Data, int length ) +{ + int i; + short* buffer = Data.buffer; + for(i = length; i >= 0; i--) + buffer[i]= 38084 * buffer[i] >> 16; + return length; +} + +/*-------------------------------------------------------------------------*/ +int doubleRateStereo( AdapterC Data, int length ) +{ + _doubleRate2( Data.buffer, Data.mode, length/2 ); + return 2*_doubleRate2( Data.buffer+1, Data.mode, length/2 ); +} + +int doubleRateMono( AdapterC Data, int length ) +{ + return _doubleRate1( Data.buffer, Data.mode, length ); +} + +/*-------------------------------------------------------------------------*/ +int halfRateStereo( AdapterC Data, int length ) +{ + _halfRate2( Data.buffer, Data.mode, length/2 ); + return 2*_halfRate2( Data.buffer+1, Data.mode, length/2 ); +} + +int halfRateMono( AdapterC Data, int length ) +{ + return _halfRate2( Data.buffer, Data.mode, length ); +} + +/*-------------------------------------------------------------------------*/ +int varRateStereo( AdapterC Data, int length ) +{ + _varRate2( Data.buffer, Data.mode, Data.filter, length/2 ); + return 2*_varRate2( Data.buffer+1, Data.mode, Data.filter, length/2 ); +} + +int varRateMono( AdapterC Data, int length ) +{ + return _varRate1( Data.buffer, Data.mode, Data.filter, length ); +} + +/*-------------------------------------------------------------------------*/ +typedef struct{ + short denominator; + short numerator; +} Fraction; + +/*-------------------------------------------------------------------------*/ +Fraction findFraction( float Value ) +{ +/* gives a maximal error of 3% and typical less than 0.2% */ + const char frac[96]={ + 1, 2, -1, /* /1 */ + 1, 3, -1, /* /2 */ + 2, 4, 5, -1, /* /3 */ + 3, 5, 7, -1, /* /4 */ + 3, 4, 6, 7, 8, 9, -1, /* /5 */ + 5, 7, 11, -1, /* /6 */ + 4, 5, 6, 8, 9, 10, 11, 12, 13, -1, /* /7 */ + 5, 7, 9, 11, 13, 15, -1, /* /8 */ + 5, 7, 8, 10, 11, 13, 14, 16, -1, /* /9 */ + 7, 9, 11, 13, -1, /* /10 */ + 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, -1, /* /11 */ + 7, 11, 13, -1, /* /12 */ + 7, 8, 9, 10, 11, 12, 14, 15, 16, -1, /* /13 */ + 9, 11, 13, 15, -1, /* /14 */ + 8, 11, 13, 14, 16, -1, /* /15 */ + 9, 11, 13, 15 }; /* /16 */ + + + Fraction Result = {0,0}; + int n,num,den=2; + + float RelErr, BestErr = 0; + if( Value < 31/64. || Value > 64/31. ) return Result; + + for( n = 0; n < sizeof(frac); num=frac[++n] ) + { + if( num < 0 ) den++; + RelErr = Value * num / den; + RelErr = ( RelErr < (1/RelErr) ? RelErr : 1/RelErr ); + if( RelErr > BestErr ) + { + BestErr = RelErr; + Result.denominator = den; + Result.numerator = num; + } + } + return Result; +} + + +float sinc( float x ) +{ + if( x > -1e-24 && x < 1e-24 ) return 1.; + else return sin(x)/x; +} + +void calculateVarFilter( short* dst, float Ratio, float phase, float scale ) +{ + const unsigned short KaiserWindow7[]= { + 22930, 16292, 14648, 14288, 14470, 14945, 15608, 16404, + 17304, 18289, 19347, 20467, 21644, 22872, 24145, 25460, + 26812, 28198, 29612, 31052, 32513, 33991, 35482, 36983, + 38487, 39993, 41494, 42986, 44466, 45928, 47368, 48782, + 50165, 51513, 52821, 54086, 55302, 56466, 57575, 58624, + 59610, 60529, 61379, 62156, 62858, 63483, 64027, 64490, + 64870, 65165, 65375, 65498, 65535, 65484, 65347, 65124, + 64815, 64422, 63946, 63389, 62753, 62039, 61251, 60391 }; + int i; + float w; + const float fg = -.018 + .5 / Ratio; + const float omega = 2 * M_PI * fg; + phase -= 63; + for( i = 0; i < 64; i++) + { + w = scale * ( KaiserWindow7[i] * ( i + 1 )); + dst[i] = w * sinc( omega * (i+phase) ); + dst[127-i] = w * sinc( omega * (127-i+phase) ); + } +} + +typedef struct{ + float scale; + int incr; +} VarFilterMode; + +const VarFilterMode Up = { 0.0211952, 0 }; +const VarFilterMode Down = { 0.0364733, 2 }; + + +void setupVarFilter( VarFilter* filter, + float Ratio, VarFilterMode Direction ) +{ + int i,n,d; + Fraction IRatio; + float phase; + IRatio = findFraction( Ratio ); + + if ( (1/Ratio) < Ratio ) + Ratio = 1/Ratio; + + n = IRatio.numerator; + d = IRatio.denominator; + filter->pos_mod = n; + + for( i = 0; i < d; i++ ) + { + if( phase >= n ) + { + phase -= d; + filter->incr[i] = Direction.incr; + } + else + filter->incr[i] = 1; + + calculateVarFilter( filter->c[i], Ratio, phase/(float)n, + Direction.scale ); + phase += d; + } +} + +int createRateConverter( Sound_AudioCVT *Data, int filter_index, + int SrcRate, int DestRate, int Channel ) +{ + int VarPos = 0; + int Mono = 2 - Channel; + float Ratio = DestRate; + if( SrcRate < 1 || SrcRate > 1<<18 || + DestRate < 1 || DestRate > 1<<18 ) return -1; + if( SrcRate == DestRate ) return filter_index; + Ratio /= SrcRate; + + if( Ratio > 1.0) + VarPos = filter_index++; + else + Data->adapter[filter_index++] = minus5dB; + + while( Ratio > 64.0/31.0) + { + Data->adapter[filter_index++] = + Mono ? doubleRateMono : doubleRateStereo; + Ratio /= 2; + Data->len_mult *= 2; + Data->add *= 2; + Data->add += _fsize; + } + + while( Ratio < 31.0/64.0 ) + { + Data->adapter[filter_index++] = + Mono ? halfRateMono : halfRateStereo; + Ratio *= 2; + } + + if( Ratio > 1.0 ) + { + setupVarFilter( &Data->filter, Ratio, Up ); + Data->adapter[VarPos] = + Mono ? varRateMono : varRateStereo; + Data->len_mult *= 2; + Data->add *= 2; + Data->add += _fsize; + } + else + { + setupVarFilter( &Data->filter, Ratio, Down ); + Data->adapter[filter_index++] = + Mono ? varRateMono : varRateStereo; + } + return 0; +} + +int DECLSPEC Sound_BuildAudioCVT(Sound_AudioCVT *Data, + Uint16 src_format, Uint8 src_channels, int src_rate, + Uint16 dst_format, Uint8 dst_channels, int dst_rate) +{ + int filter_index = 0; + + if( Data == NULL ) return -1; + Data->len_mult = 1.0; + Data->add = 0; + + /* Check channels */ + if( src_channels < 1 || src_channels > 2 || + dst_channels < 1 || dst_channels > 2 ) goto error_exit; + + /* First filter: Size/Endian conversion */ + switch( src_format & AUDIO_FORMAT) + { + case AUDIO_8: + Data->adapter[filter_index++] = expand8BitTo16Bit; + Data->len_mult *= 2; + break; + case AUDIO_16WRONG: + Data->adapter[filter_index++] = swapBytes; + } + + /* Second adapter: Sign conversion -- unsigned/signed */ + if( src_format & AUDIO_SIGN ) + Data->adapter[filter_index++] = changeSigned; + + /* Third adapter: Stereo->Mono conversion */ + if( src_channels == 2 && dst_channels == 1 ) + Data->adapter[filter_index++] = convertStereoToMono; + + /* Do rate conversion */ + if( src_channels == 2 && dst_channels == 2 ) + filter_index = createRateConverter( Data, filter_index, + src_rate, dst_rate, 2 ); + else + filter_index = createRateConverter( Data, filter_index, + src_rate, dst_rate, 1 ); + + if( filter_index < 0 ) goto error_exit; /* propagate error */ + + /* adapter: Mono->Stereo conversion */ + if( src_channels == 1 && dst_channels == 2 ){ + Data->adapter[filter_index++] = convertMonoToStereo; + Data->add *= 2; + Data->len_mult *= 2; + } + + /* adapter: final Sign conversion -- unsigned/signed */ + if( dst_format & AUDIO_SIGN ) + Data->adapter[filter_index++] = changeSigned; + + /* final adapter: Size/Endian conversion */ + switch( dst_format & AUDIO_FORMAT) + { + case AUDIO_8: + Data->adapter[filter_index++] = cut16BitTo8Bit; + break; + case AUDIO_16WRONG: + Data->adapter[filter_index++] = swapBytes; + } + /* Set up the filter information */ + Data->adapter[filter_index] = NULL; + Data->needed = (filter_index > 0); + return 0; + +error_exit: + Data->adapter[0] = NULL; + return -1; +} +/*-------------------------------------------------------------------------*/ + diff -r 83233d8a5929 -r 7b9a0f3f030e alt_audio_convert.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/alt_audio_convert.h Mon May 20 16:18:09 2002 +0000 @@ -0,0 +1,67 @@ +/* + Extended Audio Converter for SDL (Simple DirectMedia Layer) + Copyright (C) 2002 Frank Ranostaj + Institute of Applied Physik + Johann Wolfgang Goethe-Universität + Frankfurt am Main, Germany + + 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; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Frank Ranostaj + ranostaj@stud.uni-frankfurt.de + +(This code blatantly abducted for SDL_sound. Thanks, Frank! --ryan.) + +*/ + +#ifndef _INCLUDE_AUDIO_CONVERT_H_ +#define _INCLUDE_AUDIO_CONVERT_H_ + +#include "SDL_audio.h" +#define Sound_AI_Loop 0x2 +#define _fsize 64 + + +typedef struct{ + short c[16][2*_fsize]; + char incr[16]; + int pos_mod; +} VarFilter; + +typedef struct{ + short* buffer; + int mode; + VarFilter *filter; +} AdapterC; + +typedef struct{ + int needed; + VarFilter filter; + double len_mult; /* buffer must be len*len_mult big*/ + Uint8* buf; + int len; + int len_cvt; /* Length of converted audio buffer */ + int add; + int (*adapter[32]) ( AdapterC Data, int length ); +} Sound_AudioCVT; + +extern DECLSPEC int Sound_ConvertAudio( Sound_AudioCVT *Data ); + +extern DECLSPEC int Sound_BuildAudioConverter( Sound_AudioCVT *Data, + Uint16 src_format, Uint8 src_channels, int src_rate, + Uint16 dst_format, Uint8 dst_channels, int dst_rate ); + +#endif /* _INCLUDE_AUDIO_CONVERT_H_ */ + diff -r 83233d8a5929 -r 7b9a0f3f030e filter_templates.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/filter_templates.h Mon May 20 16:18:09 2002 +0000 @@ -0,0 +1,212 @@ +/* + Extended Audio Converter for SDL (Simple DirectMedia Layer) + Copyright (C) 2002 Frank Ranostaj + Institute of Applied Physik + Johann Wolfgang Goethe-Universität + Frankfurt am Main, Germany + + 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; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Frank Ranostaj + ranostaj@stud.uni-frankfurt.de + +(This code blatantly abducted for SDL_sound. Thanks, Frank! --ryan.) + +*/ + +#ifndef Suffix +#error include filter_template.h with defined Suffix macro! +#else +#define CH(x) (Suffix((x)*)) +//--------------------------------------------------------------------------- +int Suffix(_doubleRate)( short *buffer, int mode, int length ) +{ + const fsize = _fsize/2; + int i,di,border; + short inbuffer[_fsize]; + + if( mode & Sound_AI_Loop ) + { + for( i = 0; i < fsize; i++ ) + { + inbuffer[CH(i+fsize)] = buffer[CH(length+i)] = buffer[CH(i)]; + inbuffer[CH(i)] = buffer[CH(length-fsize+i)]; + } + border = 0; + } + else + { + for( i = 0; i < fsize; i++ ) + { + inbuffer[CH(i)] = buffer[CH(length+i)] = 0; + inbuffer[CH(i+fsize)] = buffer[CH(i)]; + } + border = fsize/2; + } + + for(i = length + border - 1; i >= -border; i--) + { + const short* const inp = i < fsize/2 ? + &inbuffer[CH(i+fsize)] : &buffer[CH(i)]; + short* const outp = &buffer[CH(2*(i+border))]; + int out = 0; + + for( di = 1; di < 1+fsize; di+=2 ) + out+= filter[di]*( inp[CH(di)/2] + inp[CH(1-di)/2] ); + outp[CH(1)] = ( 32770*inp[CH(1)] + out) >> 16; + outp[CH(0)] = ( 32770*inp[CH(0)] + out) >> 16; + } + return 2*length + 4*border; +} + +//--------------------------------------------------------------------------- +short Suffix(filterHalfBand)( short* inp ) +{ + static const int fsize = _fsize; + int out = 32770*inp[0]; + int di; + for( di = 1; di < fsize/2; di+=2 ) + out+= filter[di]*( inp[CH(di)] + inp[CH(-di)] ); + return out >> 16; +} + +int Suffix(_halfRate)( short *buffer, int mode, int length ) +{ + static const int fsize = _fsize; + + int i,border; + + short inbuffer[3*_fsize]; + short *finp, *linp; + + if( mode & Sound_AI_Loop ) + { + if( length & 1 ) + { + // do something meaningful + } + for( i = 0; i < fsize; i++ ) + { + inbuffer[CH(i)] = buffer[CH(length-fsize+i)]; + inbuffer[CH(i+fsize)] = buffer[CH(i)]; + } + border = 0; + finp = inbuffer + CH( fsize ); + linp = inbuffer + CH( fsize-length ); + } + else + { + for( i = 0; i < fsize; i++ ) + { + inbuffer[CH(i)] = buffer[CH(length-fsize+i)]; + inbuffer[CH(i+fsize)] = 0; + inbuffer[CH(i+2*fsize)] = buffer[CH(i)]; + } + border = fsize; + finp = inbuffer + CH( (3*fsize)/2 + 2*border ); + linp = inbuffer + CH( fsize/2 - length ); + } + + length = ( length + 1 ) / 2; + + for(i = -border; i < fsize; i++) + { + buffer[CH(i+border)] = Suffix(filterHalfBand)( finp+CH(2*i) ); + } + for(; i < length-fsize; i++) + { + buffer[CH(i+border)] = Suffix(filterHalfBand)( buffer+CH(2*i) ); + } + for(; i < length+border; i++) + { + buffer[CH(i+border)] = Suffix(filterHalfBand)( linp+CH(2*i) ); + } + return length + 2*border; +} + +//--------------------------------------------------------------------------- +short Suffix(filterVarBand)( VarFilter* filt, short** inpp, char* cpos ) +{ + static const int fsize = _fsize; + + int di; + int out = 0; + short *inp = *inpp; + int pos = *cpos; + short *filter = filt->c[pos]; + + for( di = 0; di < fsize; di++ ) + out+= filter[di] * (int)inp[CH(di)]; + + *inpp += CH(filt->incr[pos]); + *cpos = ( pos + 1 ) % filt->pos_mod; + return out >> 16; +} + +int Suffix(_varRate)( short* buffer, int mode, VarFilter* filter, int +length ) +{ + static const int fsize = _fsize; + int i,border; + short inbuffer[CH(3*_fsize)]; + short *finp, *linp, *bufp; + char pos = 0; + VarFilter* filterp = filter; + + if( mode & Sound_AI_Loop ) + { + for( i = 0; i < fsize; i++ ) + { + inbuffer[CH(i)] = buffer[CH(length-fsize+i)]; + inbuffer[CH(i+fsize)] = buffer[CH(i)]; + } + border = 0; + finp = inbuffer+CH(fsize); + linp = inbuffer+CH(fsize-length); + } + else + { + for( i = 0; i < fsize; i++ ) + { + inbuffer[CH(i)] = buffer[CH(length-fsize+i)]; + inbuffer[CH(i+fsize)] = 0; + inbuffer[CH(i+2*fsize)] = buffer[CH(i)]; + } + border = fsize; + finp = inbuffer + CH( (3*fsize)/2 + 2*border ); + linp = inbuffer + CH( fsize/2 - length ); + } + + length = ( length + 1 ) / 2; + bufp = buffer; + + for(i = -border; i < fsize; i++) + { + buffer[CH(i+border)] = Suffix(filterVarBand)( filterp, &finp, &pos ); + } + for(; i < length-fsize; i++) + { + buffer[CH(i+border)] = Suffix(filterVarBand)( filterp, &bufp, &pos ); + } + for(; i < length+border; i++) + { + buffer[CH(i+border)] = Suffix(filterVarBand)( filterp, &linp, &pos ); + } + return length + 2*border; +} +//--------------------------------------------------------------------------- +#undef CH +#endif /* Suffix */ +