Mercurial > SDL_sound_CoreAudio
view alt_audio_convert.c @ 338:7b9a0f3f030e
Initial add.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Mon, 20 May 2002 16:18:09 +0000 |
parents | |
children | fbbb1f25b944 |
line wrap: on
line source
/* 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 <stdlib.h> #include <math.h> /*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; } /*-------------------------------------------------------------------------*/