home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Enigma Amiga Life 109
/
EnigmaAmiga109CD.iso
/
software
/
musica
/
resample
/
src
/
resample.c
< prev
next >
Wrap
C/C++ Source or Header
|
1999-12-04
|
14KB
|
505 lines
/*************************************************************************\
resample.c:
Changes the size (and sampling rate) of an IFF 8SVX sample by an
integer ratio (preferably 1:2), while avoiding aliasing effects by
calculating and applying a low pass filter first.
\*************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <math.h>
#include "remez.h"
char *version = "$VER: resample 1.1 (04.12.99)";
enum { FALSE, TRUE };
#define ID_FORM 0x464F524D
#define ID_8SVX 0x38535658
#define ID_VHDR 0x56484452
#define ID_BODY 0x424F4459
typedef unsigned long ULONG;
typedef unsigned short UWORD;
typedef unsigned char UBYTE;
struct
{
ULONG oneShotHiSamples,
repeatHiSamples,
samplesPerHiCycle;
UWORD samplesPerSec;
UBYTE ctOctave,
sCompression;
ULONG volume; /* ULONG is not quite the truth, but close enough */
} vhdr;
void info();
void create_filter();
ULONG getlong();
long filter_body(long len);
void handle_iff();
FILE *infile, *outfile;
float fstop, stopwt = 5; /* filter parameters */
float fpass = 0.7;
short a[NFMAX]; /* filter coefficients, fixed point representation */
int shift; /* denotes fixed point unit */
int n = 0;
int intpl = 0, decmt = 0;
float gain = 1;
int verbose = 0;
void help( char *s )
{
if( s )
printf( "Illegal parameter '%s'!\n", s );
printf( "Usage: resample <infile> [<outfile>] [options]\n" );
printf( "Where <outfile> defaults to <infile>.out and options are:\n" );
printf( " -g<gain> (i.e. amplification)\n" );
printf( " -i<interpolation ratio>\n" );
printf( " -d<decimation ratio>\n" );
printf( " -l<filter length>\n" );
printf( " -p<pass band width>\n" );
printf( " -w<weight of stop band>\n" );
printf( " -v[erbose]\n" );
exit( s ? 10 : 0 );
}
int main( int argc, char* argv[] )
{
ULONG l;
int rawmode = FALSE;
long rawsize = 0;
char *s;
char name1[80], name2[80];
if( argc < 2 )
help( NULL );
name1[0] = name2[0] = '\0';
while( --argc )
{
s = *++argv;
if (*s != '-')
{
if( name1[0] == '\0' )
strcpy(name1, s);
else
strcpy(name2, s);
}
else switch (*++s)
{
case 'l':
n = atoi(++s);
break;
case 'd':
decmt = atoi(++s);
break;
case 'i':
intpl = atoi(++s);
break;
case 'g':
gain = atof(++s);
break;
case 'p':
fpass = atof(++s);
break;
case 'w':
stopwt = atof(++s);
break;
case 'v':
verbose = 1;
break;
default:
help( argv[ 0 ] );
}
}
/* add some default values: */
if( intpl < 1 )
intpl = 1;
if( decmt < 1 )
decmt = (intpl == 1) ? 2 : 1;
if( n < 1 )
n = (decmt > intpl) ? 10*decmt+1 : 10*intpl+1;
/* make sure the arrays don't overflow */
if( n > NFMAX )
n = NFMAX;
if( name1[0] == '\0' )
{
printf("Please specify an input file!\n");
return 5;
}
if (name2[0] == '\0')
{
strcpy(name2, name1);
strcat(name2, ".out");
}
if (!(infile = fopen(name1,"r")))
{
printf("Can't open %s!\n", name1);
return 10;
}
if( getlong() != ID_FORM )
{
fseek( infile, 0L, SEEK_END );
rawmode = TRUE;
rawsize = ftell( infile );
}
else
{
l = getlong(); /* skip file size */
if (getlong() != ID_8SVX)
{
printf("Not an IFF 8SVX file: %s!\n", name1);
fclose(infile);
return 5;
}
}
if (!(outfile = fopen(name2,"w")))
{
printf("Can't open %s for output!\n", name2);
fclose(infile);
return 10;
}
/* tell the user what his parameters will do */
printf("Resampling \"%s\" as \"%s\",\n", name1, name2);
printf(" using a %d-point FIR filter (-l), gain %.2f (-g),\n", n, gain);
printf(" interpolation/decimation ratio is %d:%d (-i, -d),\n", intpl, decmt);
printf(" pass band ends at %.2f below the stop band (-p),\n", fpass);
printf(" relative weight of stop band errors is %.1f (-w).\n", stopwt);
create_filter();
rewind( infile );
if( rawmode )
{
printf( "Input file is not IFF, processing as raw 8bit-samples.\n" );
filter_body( rawsize );
}
else
handle_iff();
fclose( infile );
fclose( outfile );
return 0;
}
void create_filter()
/* Creates filter coefficients a[0..n-1], normalized, so that sum(a[i])==1,
* and converted to a fixed point representation.
*/
{
float sum=0, max=0;
long isum=0, unit;
int k;
/* filter parameters: */
fstop = (decmt > intpl) ? 0.5/decmt : 0.5/intpl;
fpass *= fstop; /* e. g. fpass = 0.7 * fstop */
printf("Calculating filter parameters "); fflush(stdout);
remez_dot = 1; /* will create "....." output */
if( makefilter( n ) > 0 ) /* routine from "remez.o" */
{
printf(" convergence failure, sorry.\n");
if( n%2 == 0 )
printf("Better try '-l%d'.\n", n+1);
exit( 10 );
}
/* convert the impulse response from float h[] to short a[], */
/* but make sure it's normalized: */
for( k=0; k<n; k++ )
{
sum += h[k];
if (fabs(h[k]) > max)
max = fabs(h[k]);
}
/* find the maximum possible fixed point unit */
shift = 0;
do {
unit = 1 << ++shift;
} while( unit * max*gain/sum < 0x8000 );
unit = 1 << --shift;
for( k=0; k<n; k++ )
{
a[k] = unit * h[k] * gain/sum;
isum += a[k];
}
printf( " done, unit: 0x%lx, checksum: 0x%lx\n", unit, isum );
if( verbose )
for( k=0; k<n; k++ )
printf( "h[%2d] = %8.5f (%6d)\n", k, h[k], a[k] );
printf( "Pass band [0:%.2f], ripple ± %.1f %%.\n", fpass,
100*fabs(extreme(0)-1));
printf( "Stop band [%.2f:0.5], attenuation %.0f dB.\n", fstop,
20*log10(fabs(extreme(0.5))) );
}
float desire( float freq )
{
if( freq > fstop )
return 0;
else
return 1;
}
float weight(float freq)
{
if (freq > fstop)
return stopwt;
else if (freq > fpass)
return 0;
else
return 1;
}
int ctrl_c = 0;
void oops()
/* break-handler during filter operation */
{
ctrl_c = 1;
}
long filter_body(long len)
/* Return value is the length of the filtered sample, may be more or less
* than the original value, depending on what interpolation/decimation
* ratio is applied.
* Modulo operations are avoided, because they imply integer division, for
* better runtime performance we use counters instead.
*/
{
long max, loop, outlen = 0;
short b[ NFMAX ]; /* input signal buffer for the filter */
int i, j, k, n_half = n/2;
int modi = 1, modd = 0; /* modulo counters */
char modbyte = 0;
time_t t0;
long warnct = 0, warnbase = 0;
long sum, peak = 0;
signed char c; /* for the sample bytes */
signal(SIGINT, *oops); /* intercept user break (Ctrl-C) */
t0 = time( NULL );
for (i = 0; i<n; i++)
b[i] = 0;
i = 0;
max = len*intpl + n_half;
for( loop = 0; loop < max; loop++ )
{
c = 0;
if( --modi == 0 )
{
modi = intpl;
if( len-- > 0 )
c = fgetc( infile );
}
b[i] = c;
for( j = 1; j<intpl; j++ )
b[i] += c; /* b[i] = intpl*c */
i++;
if( i == n )
i = 0; /* points to the oldest value */
if( loop == n_half )
modd = 1; /* enable output */
if( --modd == 0 )
{
modd = decmt;
/* filter operation */
sum = 0;
k = i;
for (j = 0; j<n; j++)
{
sum += a[j] * b[k];
k++;
if( k == n )
k = 0;
}
sum >>= shift;
c = sum;
if( sum > 127 )
c = 127;
if( sum < -128 )
c = -128;
fputc( c, outfile );
outlen++;
/* overshoot statistics */
warnbase++;
if( c != sum )
warnct++;
if( peak < sum )
peak = sum;
if( peak < -sum )
peak = -sum;
}
/* progress indicator, once every second */
if( len == 0 || (modbyte++ == 0 && t0 != time( NULL )) )
{
float ratio, amount;
t0 = time( NULL );
printf( "\r%ld input samples to go, ", len );
ratio = warnbase ? warnct * 100.0 / warnbase : 0;
printf( "overshoot ratio %.1f %%", ratio );
amount = peak * 100.0 / 128;
printf( " (peak at %.0f %%)", amount );
printf( "\e[K" ); /* that's ClrEoL */
fflush( stdout );
if( ratio > 2 )
printf( "\n" );
warnct = 0;
warnbase = 0;
peak = 0;
}
if( ctrl_c )
{
printf("\n*** BREAK - output sample is incomplete!");
break;
}
}
printf("\n");
signal(SIGINT, SIG_DFL); /* restore normal handling */
return outlen;
}
ULONG getlong()
/* returns 0 at EOF, not so brilliant, but sufficient for our purpose */
{
ULONG l=0;
int i, c;
for( i=0; i<4; i++ )
l = (l<<8) | (c = fgetc(infile));
return ( c == EOF ) ? 0 : l;
}
void putlong(ULONG l)
{
int i, byte[4];
for( i=0; i<4; i++ )
{
byte[3-i] = l & 0xff;
l >>= 8;
}
for( i=0; i<4; i++ )
fputc(byte[i], outfile);
}
UWORD getword()
{
UWORD w;
w = fgetc( infile ) << 8;
return w | fgetc(infile);
}
void putword( UWORD w )
{
fputc( w >> 8, outfile );
fputc( w & 0xff, outfile );
}
void copy_chunk()
/* copy an IFF chunk to the output file (its ID has already been copied) */
{
ULONG l;
putlong( l = getlong() );
while (l--)
fputc( fgetc(infile), outfile );
}
void handle_iff()
/* The input file has been recognized as valid IFF 8SVX, so copy it */
{
ULONG l, ifflen, vhdlen, bodylen = 0;
fpos_t pos1, pos2, pos3; /* markers for values calculated later */
int i, done = 0;
long delta = 0;
char s1[5];
putlong( getlong() ); /* "FORM" */
fgetpos( outfile, &pos1 ); /* pos1: for ifflen */
putlong( ifflen = getlong() );
putlong( getlong() ); /* "8SVX" */
while( !done && (l = getlong()) )
{
putlong( l );
strncpy( s1, (char *)&l, 4 );
s1[ 4 ] = '\0';
printf( "Chunk: %s\n", s1 );
if (l == ID_VHDR)
{
putlong( vhdlen = getlong() );
if( vhdlen < sizeof( vhdr ) )
{
printf("Fatal error: incomplete VHDR chunk!\n");
exit(10); /* implicitly closes the files! */
}
fgetpos( outfile, &pos2 ); /* pos2: for the VHDR */
vhdr.oneShotHiSamples = getlong();
vhdr.repeatHiSamples = getlong();
vhdr.samplesPerHiCycle = getlong();
vhdr.samplesPerSec = getword();
vhdr.ctOctave = fgetc( infile );
vhdr.sCompression = fgetc( infile );
vhdr.volume = getlong();
for( i=0; i<vhdlen; i++ )
{
fputc('\0', outfile); /* will be filled in later */
if( i >= sizeof( vhdr ) )
fgetc( infile ); /* shouldn't occur, but who knows */
}
if( vhdr.sCompression )
{
printf("Sorry, can't handle compressed samples...\n");
exit(10);
}
}
else if (l == ID_BODY)
{
fgetpos( outfile, &pos3 ); /* pos3: for bodylen */
putlong( bodylen = getlong() );
delta = filter_body(bodylen) - bodylen; /* -> usually (delta<0) */
if( (bodylen + delta) & 1 )
{ /* odd chunk size, fix it */
fputc( 0, outfile );
delta++;
}
done = 1; /* process no chunks after BODY */
}
else
copy_chunk();
}
/* now adjust the sizes: */
fsetpos( outfile, &pos1 );
putlong( ifflen + delta );
fsetpos( outfile, &pos3 );
putlong( bodylen + delta );
/* a bit more complicated for the VHDR: */
fsetpos( outfile, &pos2 );
/* split the body in "one shot" and "repeat" part */
l = intpl * vhdr.repeatHiSamples / decmt;
putlong( bodylen + delta - l ); /* oneShotHiSamples */
putlong( l ); /* repeatHiSamples */
putlong( intpl * vhdr.samplesPerHiCycle / decmt );
l = intpl * vhdr.samplesPerSec / decmt;
printf( "Playback rate is now %ld samples/sec\n", l );
putword( l );
fputc( vhdr.ctOctave, outfile );
fputc( vhdr.sCompression, outfile );
putlong( vhdr.volume );
}