Mercurial > SDL_sound_CoreAudio
view alt_audio_convert.c @ 346:b6341f628568
Updated.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Thu, 23 May 2002 12:36:50 +0000 |
parents | fbbb1f25b944 |
children | 778cee61e1be |
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 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); } /*-------------------------------------------------------------------------*/ static 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; } /*-------------------------------------------------------------------------*/ static 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; } /*-------------------------------------------------------------------------*/ static 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; } /*-------------------------------------------------------------------------*/ static int changeSigned( AdapterC Data, int length ) { int i; short* buffer = Data.buffer; for( i = 0; i < length; i++ ) buffer[i] ^= 0x8000; return length; } /*-------------------------------------------------------------------------*/ static 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; } /*-------------------------------------------------------------------------*/ static 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; } /*-------------------------------------------------------------------------*/ static 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; } /*-------------------------------------------------------------------------*/ static int doubleRateStereo( AdapterC Data, int length ) { _doubleRate2( Data.buffer, Data.mode, length/2 ); return 2*_doubleRate2( Data.buffer+1, Data.mode, length/2 ); } static int doubleRateMono( AdapterC Data, int length ) { return _doubleRate1( Data.buffer, Data.mode, length ); } /*-------------------------------------------------------------------------*/ static int halfRateStereo( AdapterC Data, int length ) { _halfRate2( Data.buffer, Data.mode, length/2 ); return 2*_halfRate2( Data.buffer+1, Data.mode, length/2 ); } static int halfRateMono( AdapterC Data, int length ) { return _halfRate2( Data.buffer, Data.mode, length ); } /*-------------------------------------------------------------------------*/ static 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 ); } static int varRateMono( AdapterC Data, int length ) { return _varRate1( Data.buffer, Data.mode, Data.filter, length ); } /*-------------------------------------------------------------------------*/ typedef struct{ short denominator; short numerator; } Fraction; /*-------------------------------------------------------------------------*/ static 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; } static float sinc( float x ) { if( x > -1e-24 && x < 1e-24 ) return 1.; else return sin(x)/x; } static 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; static const VarFilterMode Up = { 0.0211952, 0 }; static const VarFilterMode Down = { 0.0364733, 2 }; static 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; } } static 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; } static int 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: fprintf (stderr, "Filter: expand8BitTo16Bit\n"); Data->adapter[filter_index++] = expand8BitTo16Bit; Data->len_mult *= 2; break; case AUDIO_16WRONG: fprintf (stderr, "Filter: swapBytes\n"); Data->adapter[filter_index++] = swapBytes; break; } /* Second adapter: Sign conversion -- unsigned/signed */ if( src_format & AUDIO_SIGN ) { fprintf (stderr, "Filter: changeSigned\n"); Data->adapter[filter_index++] = changeSigned; } /* Third adapter: Stereo->Mono conversion */ if( src_channels == 2 && dst_channels == 1 ) { fprintf (stderr, "convertStereoToMono\n"); 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 ){ fprintf (stderr, "Filter: convertMonoToStereo\n"); Data->adapter[filter_index++] = convertMonoToStereo; Data->add *= 2; Data->len_mult *= 2; } /* adapter: final Sign conversion -- unsigned/signed */ if( dst_format & AUDIO_SIGN ) { fprintf (stderr, "Filter: changeSigned\n"); Data->adapter[filter_index++] = changeSigned; } /* final adapter: Size/Endian conversion */ switch( dst_format & AUDIO_FORMAT) { case AUDIO_8: fprintf (stderr, "Filter: cut16BitTo8Bit\n"); Data->adapter[filter_index++] = cut16BitTo8Bit; break; case AUDIO_16WRONG: fprintf (stderr, "Filter: swapBytes\n"); Data->adapter[filter_index++] = swapBytes; break; } /* 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; } /* * Frank's audio converter has its own ideas about how to represent audio * format, so at least for a transition period we use this to glue his code * to our's. * * + The expand8BitTo16Bit filter will only convert to system byte order. * + The cut16BitTo8Bit filter will only convert from system byte order. * + The changeSigned filter only works on 16-bit samples, system byte order. */ static char *fmt_to_str(Uint16 fmt) { switch (fmt) { case AUDIO_U8: return " U8"; break; case AUDIO_S8: return " S8"; break; case AUDIO_U16MSB: return "U16MSB"; break; case AUDIO_S16MSB: return "S16MSB"; break; case AUDIO_U16LSB: return "U16LSB"; break; case AUDIO_S16LSB: return "S16LSB"; break; } return "??????"; } #define IS_8BIT(x) ((x) & 0x0008) #define IS_16BIT(x) ((x) & 0x0010) #define ENDIAN(x) ((x) & 0x1000) #define SIGNED(x) ((x) & 0x8000) int Sound_BuildAudioCVT(Sound_AudioCVT *Data, Uint16 src_in_format, Uint8 src_channels, int src_rate, Uint16 dst_in_format, Uint8 dst_channels, int dst_rate) { Uint16 src_format = 0; Uint16 dst_format = 0; fprintf (stderr, "format: %s -> %s\n" "channels: %6d -> %6d\n" "rate: %6d -> %6d\n", fmt_to_str (src_in_format), fmt_to_str (dst_in_format), src_channels, dst_channels, src_rate, dst_rate); if ( IS_8BIT(src_in_format) && IS_16BIT(dst_in_format) ) { src_format |= AUDIO_8; /* * Signedness and byte-order changes must wait until the data * has been converted to 16-bit samples. */ if ( SIGNED(src_in_format) != SIGNED(dst_in_format) ) { dst_format |= AUDIO_SIGN; } /* if */ if ( ENDIAN(dst_in_format) != ENDIAN(AUDIO_U16SYS) ) { dst_format |= AUDIO_16WRONG; } /* if */ } /* if */ else if ( IS_16BIT(src_in_format) && IS_8BIT(dst_in_format) ) { dst_format |= AUDIO_8; /* * Byte-order and signedness changes must be made before the data * has been converted to 8-bit samples. */ if ( ENDIAN(src_in_format) != ENDIAN(AUDIO_U16SYS) ) { src_format |= AUDIO_16WRONG; } /* if */ if ( SIGNED(src_in_format) != SIGNED(dst_in_format) ) { src_format |= AUDIO_SIGN; } /* if */ } /* else if */ else if ( IS_16BIT(src_in_format) && IS_16BIT(dst_in_format) ) { if ( ENDIAN(src_in_format) != ENDIAN(dst_in_format) ) { if ( ENDIAN(src_in_format) == ENDIAN(AUDIO_U16SYS) ) { dst_format |= AUDIO_16WRONG; /* * The data is already is system byte order, so any * signedness change has to be made before changing byte * order. */ if ( SIGNED(src_in_format) != SIGNED(dst_in_format) ) { src_format |= AUDIO_SIGN; } /* if */ } /* if */ else { src_format |= AUDIO_16WRONG; /* * The data is not in system byte order, so any signedness * change has to be made after changing byte order. */ if ( SIGNED(src_in_format) != SIGNED(dst_in_format) ) { dst_format |= AUDIO_SIGN; } /* if */ } /* else */ } /* if */ else if ( ENDIAN(src_in_format) != SIGNED(AUDIO_U16SYS) ) { if ( SIGNED(src_in_format) != SIGNED(dst_in_format) ) { /* * !!! FIXME !!! * * The changeSigned filter only works on system byte * order. In this case, both source and destination is * in opposite byte order, but the sign has to changed * so we need to convert to system byte order, change * sign, and then convert back to the original byte * order again. This is not an optimal solution. */ src_format |= ( AUDIO_16WRONG | AUDIO_SIGN ); dst_format |= AUDIO_16WRONG; } /* if */ } /* else if */ else if ( SIGNED(src_in_format) != SIGNED(dst_in_format) ) { src_format |= AUDIO_SIGN; } /* else if */ } /* else if */ else if ( IS_8BIT(src_in_format) && IS_8BIT(dst_in_format) ) { /* * !!! FIXME !!! * * The changeSigned filter only works on 16-bit samples, so if * the signedness differs we have to convert from 8 to 16 bits, * change the sign and then convert back to 8 bits again. This * is not an optimal solution. */ if ( SIGNED(src_in_format) != SIGNED(dst_in_format) ) { src_format |= ( AUDIO_8 | AUDIO_SIGN ); dst_format |= AUDIO_8; } /* if */ /* * !!! FIXME !!! * * The convertMonoToStereo and convertStereoToMono filters only * work with 16-bit samples. So if those are to be applied, we * need to convert to 16-bit samples, and then back again. */ if ( src_channels != dst_channels ) { src_format |= AUDIO_8; dst_format |= AUDIO_8; } /* if */ /* * !!! FIXME !!! * * The rate conversion filters almost certainly only work with * 16-bit samples. Yadda, yadda, yadda. */ if ( src_rate != dst_rate ) { src_format |= AUDIO_8; dst_format |= AUDIO_8; } /* if */ } /* else if */ return BuildAudioCVT(Data, src_format, src_channels, src_rate, dst_format, dst_channels, dst_rate); } /*-------------------------------------------------------------------------*/