/************************/ /**SoundZAP Version 2.3**/ /************************/ /* * This program was compiled with Matthew Dillon's DICE C compiler, which * automatically opens and closes the Amiga libraries as they are needed. * If your compiler does not do this you will have to Add the appropriate * code (or get DICE!!). */ #include #include #include #include #include #include #include "SoundZAP.h" void main(int argc, char *argv[]) { char comline[33]; struct options *Opt; if (argc<2) GiveUsage(); if ((Opt=(UBYTE *)AllocMem(sizeof(struct options),MEMF_PUBLIC))==NULL) CleanUp(Opt,5); Opt->BuffSize = DEFAULT_SIZE; Opt->FlipSign = FALSE; Opt->KillChunk = FALSE; Opt->SampRate = DEFAULT_RATE; Opt->IFFOut = TRUE; Opt->InType = UNKNOWN; Opt->MuLaw = FALSE; Opt->SampChk = TRUE; Opt->Data = NULL; Opt->Size = 0; Opt->Bits = 8; strcpy(Opt->inname,""); strcpy(Opt->outname,""); while (--argc > 0) { strcpy(comline,argv[argc]); if (comline[0]=='-') ProcessOpt(Opt,comline); else { strcpy(Opt->outname,Opt->inname); strcpy(Opt->inname,comline); } } if (strlen(Opt->inname)==0) GiveUsage(); if (strlen(Opt->outname)==0) { int l; strcpy(Opt->outname,Opt->inname); l=strlen(Opt->outname); if(Opt->outname[l-4]=='.') Opt->outname[l-4]='\0'; if(Opt->outname[l-3]=='.') Opt->outname[l-3]='\0'; strcat(Opt->outname,".iff"); } printf("Input File: %s\nOutput File: %s\n",Opt->inname,Opt->outname); if (Opt->BuffSize!=0) printf("Buffer Size: %d bytes\n",Opt->BuffSize); else printf("Allocating maximum buffer size.\n"); if(!Opt->SampChk) printf("Sample Rate: %d samples per second\n",Opt->SampRate); AnalyzeData(Opt); CleanUp(Opt,0); } void GiveUsage() { printf("\nUsage: SoundZAP [] SOURCE [DESTINATION]\n"); printf(" (actually, the options can appear anywhere)\n\n"); printf(" options: -w Output RAW Opt->Data\n"); printf(" -s Toggle signed/unsigned output\n"); printf(" -n Don't create extra chunks in IFF output\n"); printf(" -f Assume input Opt->Data is RAW\n"); printf(" -b Use a buffer size of n kilobytes\n"); printf(" if n==0 then SoundZAP will try to\n"); printf(" allocate enough memory to convert\n"); printf(" the whole sample in one shot.\n"); printf(" -r Change sample rate.\n"); printf(" where n is the sample rate or\n"); printf(" one of the built in values.\n\n"); printf("\n\nSee documentation for more info.\n"); printf("mrc113@psuvm.psu.edu\n\n"); exit(0); } void ProcessOpt(struct options *Opt, char com[]) { int len; ULONG n; switch (com[1]) { case 'w' : Opt->IFFOut=FALSE; break; case 's' : Opt->FlipSign=TRUE; break; case 'n' : Opt->KillChunk=TRUE; break; case 'f' : Opt->InType=RAW; break; case 'b' : len=strlen(com)-2; if (len==0) CleanUp(Opt,2); Opt->BuffSize=(ULONG)atoi(com+2)*1024; break; case 'r' : len=strlen(com)-2; if (len==0) CleanUp(Opt,3); if (len==1) { switch (com[2]) { case '5' : Opt->SampRate=5696; break; case '7' : Opt->SampRate=7596; break; case '8' : Opt->SampRate=8000; break; case '2' : Opt->SampRate=22790; break; default : Opt->SampRate=11395; } } else { n=(LONG)atoi(com+2); if (n!=0) Opt->SampRate=n; else Opt->SampRate=11395; } Opt->SampChk=FALSE; break; default : CleanUp(Opt,4); } } void CleanUp(struct options *Opt, int Error) { if (Opt->Data) FreeMem(Opt->Data,Opt->BuffSize); if (Opt) FreeMem(Opt,sizeof(struct options)); if (in) Close(in); if (out) Close(out); if (Error==0) exit(0); else { printf("%s",ErrorMessages[Error]); exit(20); } } void AnalyzeData(struct options *Opt) { ULONG MagicWord; char IsMac[8]; if ((in=(struct FileHandle *)Open(Opt->inname,MODE_OLDFILE))==NULL) CleanUp(Opt,6); if(Opt->BuffSize==0) { Seek(in,0,OFFSET_END); Opt->BuffSize=Seek(in,0,OFFSET_BEGINNING); } if ((Opt->Data=(UBYTE *)AllocMem(Opt->BuffSize,MEMF_PUBLIC))==NULL) CleanUp(Opt,5); Read(in,&MagicWord,4); if (Opt->InType==UNKNOWN) { if (MagicWord==0x2e736e64) /* '.snd' */ ConvertAU(Opt); else if (MagicWord==0x464f524d) /* 'FORM' */ /*ConvertIFF(Opt);*/ CleanUp(Opt,0); /* Not supported yet */ else if (MagicWord==0x43726561) /* 'Crea' */ ConvertVOC(Opt); else if (MagicWord==0x52494646) /* 'RIFF' */ ConvertWAV(Opt); } Seek(in,65,OFFSET_BEGINNING); Read(in,IsMac,8); if (!strcmp(IsMac,"FSSDSFX!")) ConvertMAC(Opt); GuidoCheck(Opt); } /* This routine is a modified version of Guido van Rossum's (guido@cwi.nl) * 'whatsound' routine. It guesses the sound file type (signed/unsigned/mu-law) * by checking how the values in the file are distributed. Thanks Guido!! */ void GuidoCheck(struct options *Opt) { LONG a,n,sum; unsigned long bin[4]; int x; BPTR op=Output(); if(Opt->FlipSign) { Seek(in,0,OFFSET_END); Opt->Size=Seek(in,0,OFFSET_BEGINNING); ConvertRaw(Opt); } Write(op,"Analyzing Data...",18); for (a=0; a<4; a++) bin[a]=0; do { n=Read(in,Opt->Data,Opt->BuffSize); for(a=0; aData[a]/64]++; } while (n==Opt->BuffSize); if(bin[2]==0 && bin[3]==0) CleanUp(Opt,7); x=((bin[0]+bin[3])*100)/(bin[1]+bin[2]); if(x>=300) Opt->FlipSign=FALSE; else if ( x <= 33) Opt->FlipSign=TRUE; else if ( (x >= 50) && (x <= 200)) Opt->MuLaw=TRUE; printf("Done.\n"); if(Opt->SampChk && Opt->MuLaw) Opt->SampRate=8000; Seek(in,0,OFFSET_END); Opt->Size=Seek(in,0,OFFSET_BEGINNING); ConvertRaw(Opt); } void ConvertRaw(struct options *Opt) { LONG b_read,i,scale,b_written,total; int max; signed char logs[256]; BPTR op=Output(); if((out=(struct FileHandle *)Open(Opt->outname,MODE_NEWFILE))==NULL) CleanUp(Opt,8); if (Opt->IFFOut) WriteIFFStuff(Opt); if (Opt->MuLaw) { Write(op,"Building log tables...",22); max=getscale(Opt); maketable(logs,max); printf("Done.\n"); if (Opt->InType==AU) Seek(in,32,OFFSET_BEGINNING); else Seek(in,0,OFFSET_BEGINNING); } Write(op,"Converting...",13); scale=Opt->Bits/8; total=0; do { b_read=Read(in,Opt->Data,Opt->BuffSize)/scale; if(Opt->FlipSign) for (i=0; iData[i] ^= 0x80; else if(Opt->MuLaw) for (i=0; iData[i]=logs[Opt->Data[i]]; else if(scale!=1) for (i=0; iData[i]=Opt->Data[i*scale]; total+=b_read; if (total > Opt->Size) b_read-=total-Opt->Size; b_written=Write(out,Opt->Data,b_read); if(b_written!=b_read) CleanUp(Opt,10); } while(total < Opt->Size); if(Opt->IFFOut) Write(out,"\0\0\0",(4-total%4)&3); printf("Done.\n"); CleanUp(Opt,0); } void WriteIFFStuff(struct options *Opt) { int i,s; ChunkHeader Form, V8Hdr, Body, Auth, Anno; Voice8Header V8H = {0,0,32,8363,1,0,Unity}; if (Opt->KillChunk) s=0; else s=68; Form.ckID = FORM; Form.ckSize = 40 + s + Opt->Size + ((4-(Opt->Size%4))&3); Write(out,&Form,8); i=ID_8SVX; Write(out,&i,4); if (!Opt->KillChunk) { Auth.ckID = ID_AUTH; Auth.ckSize = 16; Write(out,&Auth,8); Write(out,Author,16); Anno.ckID = ID_ANNO; Anno.ckSize = 36; Write(out,&Anno,8); Write(out,Annotation,36); } V8Hdr.ckID = ID_VHDR; V8Hdr.ckSize = 20; Write(out,&V8Hdr,8); V8H.oneShotHiSamples = Opt->Size; V8H.samplesPerSec = Opt->SampRate; Write(out,&V8H,20); Body.ckID = ID_BODY; Body.ckSize = Opt->Size + ((4-(Opt->Size%4))&3); Write(out,&Body,8); } void ConvertVOC(struct options *Opt) { UBYTE c[2]; if(Opt->SampChk) { Seek(in,30,OFFSET_BEGINNING); Read(in,c,2); if(c[1]!=0) CleanUp(Opt,9); Opt->SampRate=1000000/(256-c[0]); } Opt->FlipSign=TRUE; Seek(in,0,OFFSET_END); Opt->Size=Seek(in,0,OFFSET_BEGINNING)-32; Seek(in,32,OFFSET_BEGINNING); ConvertRaw(Opt); } void ConvertWAV(struct options *Opt) { Opt->FlipSign=TRUE; Seek(in,0,OFFSET_END); Opt->Size=Seek(in,0,OFFSET_BEGINNING)-44; Seek(in,44,OFFSET_BEGINNING); ConvertRaw(Opt); } void ConvertMAC(struct options *Opt) { Opt->FlipSign=TRUE; Seek(in,0,OFFSET_END); Opt->Size=Seek(in,0,OFFSET_BEGINNING)-668; Seek(in,128,OFFSET_BEGINNING); ConvertRaw(Opt); } /* Thanks to Sean Connolly for sending me info on the .au sound format.*/ void ConvertAU(struct options *Opt) { ULONG i; int q; AUHeader AUHdr; Seek(in,0,OFFSET_END); Opt->Size=Seek(in,0,OFFSET_BEGINNING)-32; Read(in,&AUHdr,sizeof(AUHdr)); switch (AUHdr.encoding) { case 1 : Opt->MuLaw=TRUE; break; case 2 : Opt->Bits=8; break; case 3 : Opt->Bits=16; Opt->Size/=2; break; case 4 : Opt->Bits=24; Opt->Size/=3; break; case 5 : Opt->Bits=32; Opt->Size/=4; break; default : CleanUp(Opt,9); break; } if (Opt->SampChk) Opt->SampRate=AUHdr.sample_rate; ConvertRaw(Opt); } /*--------------------------------------------------------------------------* * The following routine was extracted from posting by Brian Foley. * * Brian Foley email: bfoley@greatlakes.Central.Sun.COM * * Systems Engineer smail: 1000 Town Center * * Sun Microsystems Suite 1700 * * GreatLakes Region Southfield, MI 48075 (313) 352-7070 * *--------------------------------------------------------------------------*/ int ulaw2linear(unsigned char ulawbyte) { static int exp_lut[8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 }; int sign, exponent, mantissa, sample; ulawbyte = ~ulawbyte; sign = ulawbyte & 0x80; exponent = (ulawbyte >> 4) & 0x07; mantissa = ulawbyte & 0x0F; sample = (exp_lut[exponent] + (mantissa << (exponent + 3))); if ( sign ) sample = -sample; return sample; } int getscale(struct options *Opt) { int count, max = 0, i; do { count = Read(in, Opt->Data, Opt->BuffSize); for ( i = 0; i < count; i++ ) max = MAX(abs(ulaw2linear(Opt->Data[i])), max); } while ( count == Opt->BuffSize ); return max; } void maketable(signed char *logs, int max) { int i, c, d; for ( i = 0; i < 256; i++ ) { c = ( ulaw2linear(i) * ulaw2linear(0) ) / max; d = abs(c) & 0xFF; if ( d > 0x7F ) if ( c > 0 ) logs[i] = (signed char) ( c / 256 + 1 ); else logs[i] = (signed char) ( c / 256 - 1 ); else logs[i] = (signed char) ( c / 256 ); } }