/**************************** Sound.c ********************************* Sound is copyright (c) 1988 by Richard Lee Stockton, 21305 60th Ave W., Mountlake Terrace, Washington 98043, 206/776-1253(voice), but may be freely distributed as long as no profit is made from its distribution or sale without my written permission. I call this concept 'FreeWare', you can call it whatcha want. I also include the source code, (Manx 3.4/3.6), in the hope that it will be of benefit to someone in the Amiga programming community. Feel free to alter it at will, (but at your own risk!). I _would_ appreciate receiving a copy of whatever it may become and it is always nice to receive credit, (perhaps just a few lines of glowing tribute.^) Long Live Leo and All the Little Schwabies! To Manufacture with Manx 3.4/3.6: cc Sound.c ln +cd Sound.o -lc You'll get a warning that you've re-defined IconBase. Ignore it. **************************************************************************/ #include #include #include #include #include #include #include /* Less than WorkBench 1.2 need not apply. That's nobody, right? ;-> */ #define REVISION 33L /* We'll need 4 buffers in CHIP memory, all else in FAST, if ya got it */ #define BUFSIZE 1024L /* Some MANX specific, byte saving, (usually), string routines */ #define strcpy _BUILTIN_strcpy #define strcmp _BUILTIN_strcmp #define strlen _BUILTIN_strlen /* A pretty little HELP message showing valid variable ranges */ #define USAGE "\ \033[42m\033[31m\ USAGE: Sound [0-55] [56-65535] [on/off] \ \033[m\n\033[42m\ ABORT: 'c'\033[33m\ CYCLES SAMPLES_PER_SECOND STEREO \ \033[m\n\033[42m\033[31m\ 05/18/88 04:28 copyright \xa9 1988 Richard Lee Stockton \ \033[m\n" /* Probably more GLOBALS than are required. 'C' is funny stuff. */ extern struct IconBase *IconBase=NULL; extern struct IntuitionBase *IntuitionBase=NULL; extern struct WBStartup *WBenchMsg=NULL; extern struct WBArg *argp=NULL; struct IntuiMessage *message=NULL; struct Window *StatusWindo=NULL; struct DiskObject *infofile=NULL; struct IOAudio *sound[4]={NULL,NULL,NULL,NULL}; long sactual=0L, sstart=0L, savelock=0L, atol(), sps=0L, cycles=1L, lock=0L; short k=0; UBYTE sunit[4]={12,10,5,3}; BOOL stereo=FALSE, help=FALSE; char *sbuffer=NULL, *ltoa(), title[50]="Sound: ", *cbuf[4]={NULL,NULL,NULL,NULL}, *sname[108]=NULL, *SafeAllocMem(); void loadSound(), setStatusWindo(), soundSound(), quit(); /*********** quit, give-up, go home, finish... Neatness counts! ******/ void quit(string) char *string; { if(sound[0]) /* This cleans up the audio device stuff */ { for(k=3;k>(-1);k--) if(sound[k]) AbortIO(sound[k]); if(sound[0]->ioa_Request.io_Device) CloseDevice(sound[0]); for(k=3;k>(-1);k--) { if(sound[k]->ioa_Request.io_Message.mn_ReplyPort) DeletePort(sound[k]->ioa_Request.io_Message.mn_ReplyPort); } for(k=3;k>(-1);k--) { if(sound[k]) FreeMem(sound[k],(long)sizeof(struct IOAudio)); if(cbuf[k]) FreeMem(cbuf[k],BUFSIZE); } sound[0]=NULL; } /* Write any message to out. May be error or could be samples/second */ /* You'll be sorry if you try to Write(Output()) to WorkBench! 8-) */ if(help) Write(Output(),string,(long)strlen(string)); else if(*string) { strcpy(title,"Sound Error: "); strcat(title,string); setStatusWindo(); } if(!help&&(cycles>-1L)&&(sactual<(16000L*(stereo+1L)))) Delay(77L); /* Clean up everything else */ if(StatusWindo) CloseWindow(StatusWindo); if(IntuitionBase) CloseLibrary(IntuitionBase); if(sbuffer) FreeMem(sbuffer,sactual); if(infofile) FreeDiskObject(infofile); if(IconBase) CloseLibrary(IconBase); if(sound[3]) exit(0); else exit(10); } /* Don't Allocate if Low Mem - by Bryce Nesbitt */ /* Aberations by RLS. 4096 should be fudge enough */ char *SafeAllocMem(size,flags) long size, flags; { register char *p; if(p=(char *)AllocMem(size,flags)) if(AvailMem(MEMF_CHIP)<4096L) { FreeMem(p,size); return(NULL); } return(p); } /**** Status Window. Uses 'c' to abort. ****/ void setStatusWindo() { struct NewWindow w; /* Try to Open Intuition and if successful, make a 'status' window */ if(StatusWindo) CloseWindow(StatusWindo); StatusWindo=NULL; if(!IntuitionBase) { IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library",REVISION); } if(!IntuitionBase) quit(NULL); w.LeftEdge = 82L; w.TopEdge = 0L; /* if title==USAGE, the large Width value will abort the window open */ /* This is just fine, because we don't WANT it to open in this case. */ w.Width = (long)(strlen(title)*8+8); w.Height = 10L; w.DetailPen = 0x01; w.BlockPen = 0x03; w.Title = (UBYTE *)title; w.Flags = SMART_REFRESH|NOCAREREFRESH|ACTIVATE; w.IDCMPFlags = VANILLAKEY; /* So we can get 'c' message */ w.Type = WBENCHSCREEN; w.FirstGadget = NULL; w.CheckMark = NULL; w.Screen = NULL; w.BitMap = NULL; w.MinWidth = 0; w.MinHeight = 0; w.MaxWidth = 0; w.MaxHeight = 0; StatusWindo = OpenWindow(&w); /* Abort ONLY if cycles = 0 (loop) AND the window failed to open. */ /* This set of conditions would otherwise result in an Endless and */ /* Escape-proof loop. If cycles != 0, sound will end, sometime... */ if(!StatusWindo&&(cycles==0L)) quit(NULL); } /******** Load SoundFile 'sPath' & set cycles-sps-stereo *******/ void loadSound(sPath) char *sPath[80]; { struct FileHandle *sFile=NULL; struct FileInfoBlock *finfo=NULL; struct FileLock *lock=NULL; long i, j; char string[5]; /* Allocate 256 bytes as work memory */ if(!(sbuffer=SafeAllocMem(256L,MEMF_CLEAR|MEMF_PUBLIC))) quit("No Work Memory!"); /* Check for and parse IFF data in first 256 bytes of file */ if(!(sFile=Open(sPath,MODE_OLDFILE))) { sactual=256L; quit("Can't Find SoundFile!"); } Read(sFile,sbuffer,256L); /* load the 1st 256 bytes */ for(sstart=0L, sps=0L, i=0L; i<252L; i+=4L) { strncpy(string,sbuffer+i,4); string[4]=NULL; if(!(strcmp(string,"VHDR"))) /* get samples per second */ { for(j=0;j<(long)((UBYTE)sbuffer[i+20]);j++) sps+=256L; sps += ((UBYTE)sbuffer[i+21L]); } if(!(strcmp(string,"CHAN"))) /* Channel Assignment */ { if((sbuffer[i+7]==6)||(sbuffer[i+11]==6)) stereo=TRUE; } if(!(strcmp(string,"BODY"))) /* get size of sound data */ { for(j=0;j<4;j++) sactual+=(((UBYTE)sbuffer[i+7L-j])<<(8*j)); sstart = i+8L; i=252L; } } /* if not in IFF format, get filesize from FileInfoBlock */ if(!sactual) { /* Allocate a file info block, get size from it, and de-allocate */ if((!(finfo=(struct FileInfoBlock *) SafeAllocMem((long)sizeof(struct FileInfoBlock),MEMF_CLEAR))) ||(!(lock=Lock(sname,ACCESS_READ)))||(!(Examine(lock,finfo))) ) quit("FileInfoBlock Problem!"); sactual = finfo->fib_Size; if(lock) UnLock(lock); if(finfo) FreeMem(finfo,(long)sizeof(struct FileInfoBlock)); } /* clean up work area */ FreeMem(sbuffer,256L); sbuffer=NULL; /* Allocate _contiguous_ memory for SOUND data. */ /* We'll transfer in BUFSIZE chunks to CHIP memory a little later. */ /* We have to do the contiguity(?) check since AllocMem() does not. */ if((AvailMem(MEMF_LARGEST)ioa_Request.io_Message.mn_ReplyPort = CreatePort("Sound0",0L))) || (!(sound[1]->ioa_Request.io_Message.mn_ReplyPort = CreatePort("Sound1",0L))) || (!(sound[2]->ioa_Request.io_Message.mn_ReplyPort = CreatePort("Sound2",0L))) || (!(sound[3]->ioa_Request.io_Message.mn_ReplyPort = CreatePort("Sound3",0L))) ) quit("No Port Memory!"); /* Open Audio using the first IOAudio as the 'initializer' request */ sound[0]->ioa_Request.io_Message.mn_Node.ln_Pri = 20; sound[0]->ioa_Data = &sunit[0]; sound[0]->ioa_Length = 4L; if((OpenDevice(AUDIONAME,0L,sound[0],0L))!=NULL) quit("No Audio Device!"); /* Set all IOAudios. */ for(k=0;k<4;k++) { sound[k]->ioa_Request.io_Message.mn_Node.ln_Pri = 20; sound[k]->ioa_Request.io_Command = CMD_WRITE; sound[k]->ioa_Request.io_Flags = ADIOF_PERVOL; /* Note copies of Device & AllocKey from initializer. */ sound[k]->ioa_Request.io_Device = sound[0]->ioa_Request.io_Device; sound[k]->ioa_AllocKey = sound[0]->ioa_AllocKey; /* Each IOAudio has its own CHIP buffer, Port, and Unit (left/right) */ sound[k]->ioa_Data = (UBYTE *)cbuf[k]; /* 3579547 divided by 55 = 65083, nearly the maximum Period (65535) */ sound[k]->ioa_Period = 3579547L/sps; /* As LOUD as possible. Use your monitor/stereo volume. Rock 'n Roll! */ sound[k]->ioa_Volume = 64L; /* One time through this BUFSIZE (or smaller) part of the whole */ sound[k]->ioa_Cycles = 1L; } /* The compiler wants 'Unit' to be a structure, we just want to mask */ /* into the allocated left/right channels. left=1 or 8, right=2 or 4 */ /* ...zap! You're a Unit structure! Feel any different? */ for(k=2;k>(-1);k-=2) { sound[k+1]->ioa_Request.io_Unit = (struct Unit *) ((ULONG)(sound[0]->ioa_Request.io_Unit)&6L); sound[k]->ioa_Request.io_Unit = (struct Unit *) ((ULONG)(sound[0]->ioa_Request.io_Unit)&9L); } /* If in STEREO, split file. If in MONO, 'b' buffers use 'a' data */ if(stereo) remaining=(sactual/2L)-(sactual&1L); else { remaining=sactual; sound[1]->ioa_Data = (UBYTE *)cbuf[0]; sound[3]->ioa_Data = (UBYTE *)cbuf[2]; } /* dactual is the length of one channel's complete data */ dactual=remaining; k=count=0; /* we be doing loops here */ do { /* be CERTAIN ioa_Length is an even number & set datalength */ if(remaining>BUFSIZE) dlength=BUFSIZE; else { dlength=remaining; dlength-=(dlength&1L); } /* Move the data into the proper CHIP buffer of BUFSIZE */ movmem(sbuffer+(dactual-remaining),cbuf[k],(int)dlength); /* Don't load or use the right CHIP buffers if MONO. Saves time. */ if(stereo) movmem(sbuffer+(sactual-remaining), cbuf[k+1],(int)dlength); /* Data has been moved, so adjust 'remaining' */ remaining-=dlength; /* Left and Right Lengths are the same, no matter what! */ sound[k]->ioa_Length = sound[k+1]->ioa_Length = dlength; /* Start one set of Left/Right Channels. */ BeginIO(sound[k]); BeginIO(sound[k+1]); /* Check Intuition for the ABORT message */ while(message=(struct IntuiMessage *)GetMsg(StatusWindo->UserPort)) { class = message->Class; code = message->Code; /* Hi Intuition! Thanks for the message, Have a nice day! */ ReplyMsg(message); /* 'c' abort. 1,2,3, easy as a,b,c, baby you and me! */ if((class==VANILLAKEY)&&(code==3)) quit(NULL); } /* Is this the last time AND the last cycle? If yes & no, reset. */ if(remaining<2L) if(--cycles!=0L) { remaining=dactual; dlength=BUFSIZE; } /* Is this the last time, or what? */ if(remaining<2L) WaitIO(sound[k+1]); /* wait for LAST request */ else { if(k) k=0; else k=2; /* switch buffers & wait for PREVIOUS */ if(count++) WaitIO(sound[k+1]); } /* Keep going until we run out of data */ } while(remaining>1L); /* End of Loop */ } /********** long int to char string (up to 7 digits) ***********/ char *ltoa(num) long num; { static char ostring[8]=""; short next = 7, shift = 0; if (!num) ostring[next--] = '0'; while ((num+9L)/10) { ostring[next--] = num % 10L + '0'; num /= 10L; } next+=1; while(next<8) ostring[shift++] = ostring[next++]; while(shift<8) ostring[shift++] = '\0'; return(ostring); } /************************ MAIN ****************************/ main(argc,argv) int argc; char *argv[]; { long temp=0L; short i=0; char string[10]; if((argc==0)&&(WBenchMsg->sm_NumArgs>1)) /* from WorkBench */ { argp=(WBenchMsg->sm_ArgList)+1; /* CD to lock and */ if(lock=argp->wa_Lock) savelock=(long)CurrentDir(lock); strcpy(sname,argp->wa_Name); /* get the filename */ } /* If Called From CLI */ if(argc==1) {help=TRUE; quit(USAGE);} /* No SOUNDfile so quit */ if(argc>1) strcpy(sname,argv[1]); /* 2nd arg MUST be filename */ /* Load 'em up! */ loadSound(sname); /* If WorkBench, try to get info from the .info file */ if((argc==0)&&(WBenchMsg->sm_NumArgs>1)) { IconBase=(struct IconBase *)OpenLibrary("icon.library",REVISION); if((!IconBase) || ((infofile=(struct DiskObject *)GetDiskObject(sname))==NULL)) quit(NULL); /* then check 'ToolTypes' to set cycles, stereo, & samples per second. */ /* Note use of strncpy to copy only what we need. This is an attempt to */ /* take care of a .info editing bug which may cause extra long strings. */ strncpy(string,(char *)FindToolType(infofile->do_ToolTypes, "CYCLES"),3); /* We do this check for '0' since atol() thinks ' ', or NULL = '0' */ /* But if data is blank, or NULL, we want cycles set to 1 (default) */ if(string[0]=='0') cycles = 0L; temp=atol(string); if(temp) cycles = temp; /* Valid STEREO strings will have o or O as the first letter. */ strncpy(string,(char *)FindToolType(infofile->do_ToolTypes, "STEREO"),4); if(string[0]=='o'||string[0]=='O') { if(string[1]=='n'||string[1]=='N') stereo = TRUE; else stereo = FALSE; } /* We'll take any old sps because we check for validity before playing */ strncpy(string,(char *)FindToolType(infofile->do_ToolTypes, "SAMPLES_PER_SECOND"),6); temp = atol(string); if(temp) sps = temp; } /* Parse arguments from CLI. Note that from WorkBench, argc will be 0, */ /* so the following for() loop will only execute if called from CLI. */ for(i=2;i65534L)) sps = 10000L; if(cycles>65535L) cycles = 1L; /* 55 limit only applies to CLI */ soundSound(); /* MAKE THAT NOISE! */ if(lock) CurrentDir(savelock); /* restore current DIR */ /* Then release everything you've used here. (Be a tidy camper.) */ quit(NULL); } /************************** end of Sound.c ******************************/