dc4d+@andrew.cmu.edu (Darius Clynes) (09/28/89)
I am posting the following code for anyone interested in finding out when a sound starts using the sound manager on a MacPlus, SE, or 512K. The accuracy is plus or minus 1msec and reliably 10msec from the call back command. Jim Reekes of Apple suggested NOT using the SndPlay routine, and indeed this example does not use it. However, what is most important, is to let the VBLtask happen when you are in control. This is accomplished by the Ticks waiting. This technique was also used just before CopyBits in visual timing requirements. So here goes... Read ~ as plus-or-minus. -------------------------------------------------------------------- extern int PlayDone; extern int StartedPlay; SndCommand playnow_cmd; SndChannelPtr SoundChannel = NIL; /* Holds the sound channel that will be used */ int SoundAsync = TRUE; /* if true, sounds played in background */ long PlayBeginTime; /* time sound begins just before buffer command */ long PlayEndTime; /* time sound ends by call back with SndDoCommand */ extern long char_count; /* 1 millisecond clock coming in through serial port */ pascal void BackProc(chan,cmd) SndChannelPtr chan; SndCommand *cmd; { SetUpA5(); if (cmd->param1) /* set to 1 if Call Back for starting play */ { StartedPlay++; PlayBeginTime = char_count; /* char_count is incremented every time a character is received from the serial port, in our case we have a 1 millisec crystal clocking in the characters, while the Mac is set to listen at 19200 baud, our serial port interrupt routine takes care of incrementing of PBReading a character and incrementing the char_count */ /* from here to when the actual sound begins will be reliably 10 msec ~1 msec on a Mac Plus. This is reliably true only if you have first 'synched' up with a Tick count change */ SndDoImmediate(chan,&playnow_cmd); } else { PlayDone++; PlayEndTime = char_count; /* do not rely on this count, */ /* we are only within ~ 20 msec the actual ending */ /* of the sound through the speaker */ } RestoreA5(); } /* function PlaySound: This function plays the sound associated with the handle "SoundItem". The pointer to our sampled sound header and samples should be in *SoundItem. */ int PlaySound(SoundItem) Handle SoundItem; { int err; /* error from SndDoCommand */ int OldDone; /* old value of PlayDone */ long oldTicks; SndCommand begincmd,endcmd; HLock(SoundItem); /* start sound playing in background */ begincmd.cmd = endcmd.cmd = callBackCmd; begincmd.param2 = endcmd.param2 = SetCurrentA5(); begincmd.param1 = 1; endcmd.param1 = 0; /* our global cmd for bufferCmd */ playnow_cmd.cmd = bufferCmd; playnow_cmd.param2 = (long) *SoundItem; playnow_cmd.param1 = 0; /* Leave these next two lines if you need to be sure ( ~1 msec) of when a Sound starts */ oldTicks = Ticks; while(oldTicks == Ticks); /* synch up to VBL which means a variable delay here of 0 to 16.67 msec, but let's do it now when WE have control and can measure it, instead of letting the Sound Manager do it for us */ err = SndDoCommand(SoundChannel,&begincmd,FALSE); /* send call back start */ /* play sound from call back procedure */ err = SndDoCommand(SoundChannel,&endcmd,FALSE); /* keep end command to be sure done */ /* the end callback only accurate to ~ 20 msec*/ HUnlock(SoundItem); if (err) /* if there was an error, return it */ return(err); OldDone = PlayDone; /* if supposed to, wait til sound finished */ if (SoundAsync == FALSE) while (PlayDone == OldDone); return(0); } int SoundCleanUp(Item) ItemPtr Item; { if (SSound(Item)) { DisposHandle(SSound(Item)); SSound(Item) = NIL; } return(0); } CloseSound() /* for closing the sound channel before exiting, or beeping, etc */ { if(SoundChannel) SndDisposeChannel(SoundChannel,TRUE); SoundChannel = NIL; } OpenSound() /* for opening the sound channel before playing etc */ { if(SoundChannel) CloseSound(); SndNewChannel(&SoundChannel,sampledSynth,(long) initMono,BackProc); }