/**************************************************************************/ /* DSound V1.00 */ /* Copyright 1991-1992 by Dave Schreiber, All Rights Reserved */ /* */ /* To compile: */ /* lmk */ /* */ /* Revision history: */ /* V1.00 - Added a new module (Mem.c) which allows a sample to be */ /* loaded entirely into memory, so samples can be played from */ /* floppy disk without first copying to a hard or RAM drive. */ /* DSound also can now play a single channel of a stereo */ /* out of two speakers. The small window, used to let the */ /* user abort a playing sample, as been redone (DSound also */ /* now responds instantly when the user clicks on the Close */ /* gadget). DSound now checks a given 8SVX sample to make */ /* sure that it is actually a valid sample. Finally, DSound */ /* has been made pure (residentiable). */ /* Second release (April 26, 1992) */ /* V0.94a - Can now play a mono sample out of both speakers at the */ /* same time (using the -2 switch). */ /* March 27, 1992 (a little later) */ /* V0.93a - Now handles stereo sound samples. Either the right or */ /* left, or both, stereo channels can be played. Also split */ /* off the code that actually plays the sound sample into a */ /* separate source file (Play.c). */ /* March 27, 1992 */ /* V0.92a - Now gets the length of the sound sample from the head of */ /* the BODY chunk, instead of the VHDR (a workaround to a bug */ /* in the Perfect Sound software that would sometimes store */ /* an incorrect length in the VHDR chunk of a sound sample). */ /* November 4, 1991 */ /* V0.91a - First release (September 11, 1991) */ /**************************************************************************/ #include #include #include #include #include #include #include #include #include #include "dsound.h" #include #include #include char filename[140]; #define DEF_BUF_SIZE 30000 void InterpretArgs(int argc,char *argv[]); BOOL noFilter=FALSE; UBYTE volume=0; UWORD speed=0; ULONG bufSize=DEF_BUF_SIZE; void filter_on(void); void filter_off(void); char *version="$VER: DSound V1.00 (26.4.91)"; char *copyright="Copyright 1991-1992 by Dave Schreiber, All Rights Reserved"; struct IntuitionBase *IntuitionBase=NULL; struct GfxBase *GfxBase=NULL; struct Window *window=NULL; BPTR file=NULL; channel audioChannel=UNSPECIFIED; BOOL bothChan=FALSE; BOOL readAll=FALSE; /*The window definition*/ struct NewWindow newWindow = { 124,31, 250,56, 0,1, CLOSEWINDOW, SMART_REFRESH|WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE, NULL, NULL, "DSound V1.00", NULL, NULL, 5,5, 640,200, WBENCHSCREEN }; main(int argc,char *argv[]) { struct Voice8Header vhdr; UBYTE foo2[5]; UBYTE foo[5]; ULONG chan; ULONG sampleLength; ULONG lock; char *chanStr; filename[0]=NULL; /*Open libraries*/ GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",0L); IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",0L); if(GfxBase==NULL || IntuitionBase==NULL) cleanup(50); /*Get and interpret the command-line arguments*/ InterpretArgs(argc,argv); /*Exit if there was no sound sample specified*/ if(filename[0]==NULL) { WriteMsg("Please specify the name of a sound sample\n"); cleanup(75); } /*Open the file*/ file=Open(filename,MODE_OLDFILE); if(file==NULL) { WriteMsg("Couldn't open the file\n"); cleanup(100); } /*Get the size of the title bar font in a rather illegal way */ /*Note: programmers at C= should put a GetDefTitleBarFontHeight() */ /*function into Intuition before complaining to me about the following*/ /*code. */ lock=LockIBase(0L); newWindow.Height=IntuitionBase->ActiveScreen->Font->ta_YSize+3; UnlockIBase(lock); window=OpenWindow(&newWindow); if(window==NULL) cleanup(110); /*Read the header*/ Read(file,foo,4); Seek(file,4,OFFSET_CURRENT); Read(file,foo2,4); foo[4]=foo2[4]=NULL; /*Check the header's validity, more or less*/ if((strcmp(foo,"FORM")!=0) || (strcmp(foo2,"8SVX")!=0)) { WriteMsg("Not a valid IFF 8SVX sound sample file.\n"); cleanup(120); } if(strcmp(FindChunk(file,"VHDR"),"VHDR")!=0) { WriteMsg("Couldn't find the 8SVX header (VHDR).\n"); cleanup(130); } /*Skip past the chunk size*/ Seek(file,4,OFFSET_CURRENT); /*Get the VHDR*/ Read(file,&vhdr,sizeof(struct Voice8Header)); /*Check for compression*/ if(vhdr.sCompression!=0) { WriteMsg("Can't play a compressed sample!\n"); cleanup(400); } /*Get the CHAN chunk (which will tell us if the sample is stereo, or,*/ /*if it is mono, which speaker it should be played out of*/ chanStr=FindChunk(file,"CHAN"); if(strcmp(chanStr,"CHAN")==0) { /*Skip past chunk size*/ Seek(file,4,OFFSET_CURRENT); Read(file,&chan,sizeof(long)); /*The information we're looking for consists of one longword*/ switch(chan) { case 2: /*Mono sample, left speaker*/ if(bothChan) /*Play out of both channels if -2 used*/ audioChannel=MONO_BOTH; else if(audioChannel==UNSPECIFIED) audioChannel=MONO_LEFT; break; case 4: /*Mono sample, right speaker*/ if(bothChan) /*Play out of both channels if -2 used*/ audioChannel=MONO_BOTH; else if(audioChannel==UNSPECIFIED) audioChannel=MONO_RIGHT; break; case 6: /*Stereo*/ switch(audioChannel) { /*This reconciles a user's choice with the fact that the*/ /*sample is in stereo*/ /*Play left stereo channel*/ case MONO_LEFT: if(bothChan) audioChannel=STEREO_LEFT_BOTH; else audioChannel=STEREO_LEFT; break; /*Play right stereo channel*/ case MONO_RIGHT: if(bothChan) audioChannel=STEREO_RIGHT_BOTH; else audioChannel=STEREO_RIGHT; break; /*Play both channels*/ case UNSPECIFIED: audioChannel=STEREO; break; } break; } /*Find the start of the BODY chunk*/ chanStr=FindChunk(file,"BODY"); } else { chan=0; if(bothChan) audioChannel=MONO_BOTH; } if(strcmp(chanStr,"BODY")!=0) { WriteMsg("Couldn't find body of sample.\n"); cleanup(140); } if(noFilter) filter_off(); /*Get the length of the sample*/ Read(file,(char *)&sampleLength,4); /*Play the sample by choosing the appropriate player function*/ switch(audioChannel) { case MONO_LEFT: case MONO_RIGHT: case UNSPECIFIED: /*Simple mono playback*/ playMonoSample(file,audioChannel,&vhdr,sampleLength); break; case MONO_BOTH: /*Mono playback using both speakers*/ playMonoTwice(file,audioChannel,&vhdr,sampleLength); break; case STEREO_RIGHT: /*Right stereo channel*/ audioChannel=MONO_RIGHT; Seek(file,sampleLength/2,OFFSET_CURRENT); playMonoSample(file,audioChannel,&vhdr,sampleLength/2); break; case STEREO_RIGHT_BOTH: /*Right stereo channel out of both speakers*/ audioChannel=MONO_RIGHT; Seek(file,sampleLength/2,OFFSET_CURRENT); playMonoTwice(file,audioChannel,&vhdr,sampleLength/2); break; case STEREO_LEFT: /*Left stereo channel*/ audioChannel=MONO_LEFT; playMonoSample(file,audioChannel,&vhdr,sampleLength/2); break; case STEREO_LEFT_BOTH: /*Left stereo channel out of both speakers*/ audioChannel=MONO_LEFT; playMonoTwice(file,audioChannel,&vhdr,sampleLength/2); break; case STEREO: /*Stereo sample (both channels)*/ playStereoSample(file,audioChannel,&vhdr,sampleLength/2,filename); break; } if(noFilter) filter_on(); /*Free allocated resources and exit*/ cleanup(0); } /* Get an audio channel */ struct IOAudio *GetAudioChannel(ULONG bufferSize,UBYTE *allocationMap) { struct IOAudio *aIOB; void *audioBuf; struct Port *aPort; aPort=(struct Port *)CreatePort("dsound",0); if(aPort==NULL) return(NULL); /* Allocate the chip memory buffer for the channel */ audioBuf=(void *)AllocMem(bufferSize,MEMF_CHIP); if(audioBuf==NULL) { DeletePort(aPort); return(NULL); } /* Allocate an IOAudio structure*/ aIOB=(struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC|MEMF_CLEAR); if(aIOB==NULL) { DeletePort(aPort); FreeMem(audioBuf,bufferSize); return(NULL); } /* Set up the IOAudio to allocate the command channel */ aIOB->ioa_Request.io_Message.mn_Node.ln_Pri=0; aIOB->ioa_Request.io_Message.mn_ReplyPort=aPort; aIOB->ioa_Data=allocationMap; aIOB->ioa_Length=4; aIOB->ioa_Request.io_Command=ADCMD_ALLOCATE; /*Open the audio device*/ OpenDevice("audio.device",0,(struct IORequest *)aIOB,0); if(aIOB->ioa_AllocKey==0) { /*There was an error*/ DeletePort(aPort); FreeMem(audioBuf,bufferSize); FreeMem(aIOB,sizeof(struct IOAudio)); return(NULL); } else { /* Set up the IOAudio for writes */ aIOB->ioa_Request.io_Command=CMD_WRITE; aIOB->ioa_Request.io_Flags=ADIOF_PERVOL; aIOB->ioa_Data=audioBuf; aIOB->ioa_Length=bufferSize; return(aIOB); } } /* Free an allocated audio channel */ void FreeAudioChannel(struct IOAudio *aIOB) { if(aIOB==NULL) return; /* Free the audi obuffer */ if(aIOB->ioa_Data!=NULL) FreeMem(aIOB->ioa_Data,aIOB->ioa_Length); /* Free the audio channel */ aIOB->ioa_Request.io_Command=ADCMD_FREE; BeginIO((struct IORequest *)aIOB); WaitIO((struct IORequest *)aIOB); DeletePort(aIOB->ioa_Request.io_Message.mn_ReplyPort); /* Close the audio channel */ CloseDevice((struct IORequest *)aIOB); /* Free the IOAudio structure */ FreeMem(aIOB,sizeof(struct IOAudio)); return; } /* Initialize an IOAudio's volume, period, and set the number of cycles */ /* to one */ void InitAudioChannel(struct IOAudio *aIOB,UWORD volume,UWORD period) { aIOB->ioa_Period=period; aIOB->ioa_Volume=volume; aIOB->ioa_Cycles=1; return; } /* Duplicate an IOAudio structure */ struct IOAudio *DuplicateAudioChannel(struct IOAudio *OrigIOB) { struct IOAudio *aIOB; void *audioBuf; if(OrigIOB==NULL) return(NULL); /* Allocate the alternate buffer */ audioBuf=(void *)AllocMem(OrigIOB->ioa_Length,MEMF_CHIP); if(audioBuf==NULL) return(NULL); /*Allocate the IOAudio structure*/ aIOB=(struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC|MEMF_CLEAR); if(aIOB==NULL) { FreeMem(audioBuf,OrigIOB->ioa_Length); return(NULL); } /*Copy the original IOAudio's contents to the new one*/ CopyMem(OrigIOB,aIOB,sizeof(struct IOAudio)); /*Except for the buffer pointer, of course*/ aIOB->ioa_Data=audioBuf; return(aIOB); } /*Delete a duplicated IOAudio*/ void DeleteDuplication(struct IOAudio *aIOB) { if(aIOB != NULL) { /* Free the memory for the buffer and IOAudio */ if(aIOB->ioa_Data != NULL) FreeMem(aIOB->ioa_Data,aIOB->ioa_Length); FreeMem(aIOB,sizeof(struct IOAudio)); } return; } /* Load an IOAudio's buffer from an open file */ ULONG LoadAudioBuffer(BPTR file,struct IOAudio *aIOB,ULONG toRead) { if(toRead==0) return(0); if(file==0L) getLeft(aIOB->ioa_Data); else if(file==4L) getRight(aIOB->ioa_Data); else aIOB->ioa_Length=Read(file,aIOB->ioa_Data,toRead); return(aIOB->ioa_Length); } /*Find the beginning of an IFF chunk. This routine will search for that*/ /*chunk's name, and if found, will leave the file cursor at the chunk size*/ /*field. If the chunk isn't found, the file cursor will be left at the*/ /*size field of the BODY chunk, if there was one*/ char *FindChunk(BPTR file,char *string) { static char buf[5]; long len,actLen; buf[4]=NULL; actLen=Read(file,buf,4); while(strcmp(string,buf)!=0 && strcmp(buf,"BODY")!=0 && actLen > 0) { Read(file,(char *)&len,4); Seek(file,len,OFFSET_CURRENT); actLen=Read(file,buf,4); } return(buf); } /* Interpret the command line arguments */ void InterpretArgs(int argc,char *argv[]) { int i; for(i=1;i 28000) speed=0; break; /* The volume at which the sample should be played */ case 'v': case 'V': volume=atol(&argv[i][2]); if(volume > 64) volume=0; break; /* The size of the chip RAM buffers */ case 'b': case 'B': bufSize=(atol(&argv[i][2])+1)&(~1); if(bufSize==0) bufSize=DEF_BUF_SIZE; break; } else if(argv[i][0]=='?') { /*On-line help*/ WriteMsg("DSound V1.00 ©1991-1992 by Dave Schreiber\n"); WriteMsg("Usage:\n"); WriteMsg(" DSound \n"); WriteMsg("Where the options are:\n"); WriteMsg(" -l -- Play the sample using the left speaker\n"); WriteMsg(" -r -- Play the sample using the right speaker\n"); WriteMsg(" -2 -- Play the sample using both speakers\n"); WriteMsg(" -f -- Shut off the low-pass filter\n"); WriteMsg(" -m -- Load the entire sample into memory\n"); WriteMsg(" -s -- Play the sample at the given speed (samples/sec)\n"); WriteMsg(" -v -- Play the sample at the given volume (0-64)\n"); WriteMsg(" -b -- Use a buffer of size (default is 30K)\n"); exit(0); } else /*Otherwise, the argument is a filename */ strcpy(filename,argv[i]); } } /*Switch on the low-pass filter */ void filter_on() { *((char *)0x0bfe001)&=0xFD; } /*Switch off the low-pass filter*/ void filter_off() { *((char *)0x0bfe001)|=0x02; } /*Write a message to the CLI*/ void WriteMsg(char *errMsg) { Write(Output(),errMsg,strlen(errMsg)); } /*Take a file handle and that handle's filename, and open that file again*/ /*The position in the second file in set to the position in the first */ /*file (so that the two file handles are essentially identical)*/ /*This requires that the first file was opened in a shared mode, like */ /*MODE_OLDFILE*/ BPTR dupFileHandle(BPTR origFile,char *filename) { BPTR dupFile; dupFile=Open(filename,MODE_OLDFILE); if(dupFile==NULL) return(NULL); Seek(dupFile,getPosInFile(origFile),OFFSET_BEGINNING); return(dupFile); } /*Get the current position in a file*/ ULONG getPosInFile(BPTR file) { LONG position; position=Seek(file,0,OFFSET_CURRENT); return((ULONG)position); } /* Free allocated resources */ void cleanup(int err) { /*If the entire sample was read into memory, this will delete whatever*/ /*part of the sample still remains in memory*/ deleteLeft(); deleteRight(); if(file!=NULL) Close(file); if(window!=NULL) CloseWindow(window); if(GfxBase!=NULL) CloseLibrary((struct Library *)GfxBase); if(IntuitionBase!=NULL) CloseLibrary((struct Library *)IntuitionBase); exit(err); } /*End of DSound.c*/