diff 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 diff
--- /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 <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;
+}
+/*-------------------------------------------------------------------------*/
+