[comp.sys.mac.programmer] MacPlus SoundExample

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);

}