dhare@Sun.COM (Dwight Hare) (03/31/88)
[Example use of Mac II sound chip] I found programming the Mac II sound chip using the new interface described in IM Vol. V hard to figure out, so I thought others might be interested in what appears to be a successful program to play buffers of sampled sound. If all of this is obvious to anyone, I'd like to hear from you! Dwight dhare@sun.com --- /* * play.c - use the mac II sound chip to play a sampled sound in chunks. * The eventual purpose is to do some animation in synch with the music. * Written in MPW C by Dwight Hare, March, 1988. */ /* The following defines and data structures are used from sound.h: * * #define callBackCmd 13 * #define bufferCmd 81 * #define initMono 0x80 * * typedef struct SndCommand { * unsigned short cmd; * short param1; * long param2; * } SndCommand; * * typedef struct SndChannel { * struct SndChannel *nextChan; * ModifierStubPtr firstMod; * ProcPtr callBack; * long userInfo; * Time wait; * SndCommand cmdInProgress; * short flags; * short qLength; * short qHead; * short qTail; * SndCommand queue[stdQLength]; * } SndChannel, *SndChannelPtr; * * typedef struct SoundHeader { * char * samplePtr; * unsigned long length; * Fixed sampleRate; * unsigned long loopStart; * unsigned long loopEnd; * short baseNote; * char sampleArea[0]; * } SoundHeader, *SoundHeaderPtr; */ #define __ALLNU__ /* needed to get new sound stuff in Sound.h */ #include <memory.h> #include <sound.h> #include <toolutils.h> #define NIL (Ptr)0 /* * This callback routine is used to indicate when a sound has finished * (I haven't found any other way to do it). */ pascal void snd_done(chan, cmd) SndChannelPtr chan; SndCommand *cmd; { chan->userInfo = 1; } /* * play plays the given sound in "sample" of length "len" at the sampled * rate "rate" (typically 22255 for a Soundwave sample at the highest * rate) and plays the sound in "incr" chunks (22255 would play in * one second chunks at the rate of 22255). */ OSErr play(sample, len, rate, incr) unsigned char *sample; long len, rate, incr; { unsigned char *Sp; SndChannelPtr sndp = NIL; SndCommand playcmd, callcmd; SoundHeaderPtr playbuf, playbuf1, playbuf2; long i; OSErr err; /* * We need two buffers for the bufferCmd since the first one * isn't quite finished when we set up the second one. */ playbuf1 = (SoundHeaderPtr)NewPtr(sizeof(SoundHeader)); playbuf2 = (SoundHeaderPtr)NewPtr(sizeof(SoundHeader)); if (playbuf1 == NIL || playbuf2 == NIL) { return(-1); } /* * The loopStart, loopEnd, and baseNote are for playing the * sampled sounds as notes, we are not using that feature. */ playbuf1->sampleRate = FixRatio(rate, 1); playbuf2->sampleRate = FixRatio(rate, 1); playbuf1->loopStart = 0; playbuf2->loopStart = 0; playbuf1->loopEnd = 0; playbuf2->loopEnd = 0; playbuf1->baseNote = 60; playbuf2->baseNote = 60; /* * Create a new sound channel using the sampled synthesizer played * in mono. The callback routine will be used below to determine * the end of the sound. */ if ((err = SndNewChannel(&sndp, sampledSynth, initMono, snd_done)) != noErr) { return(err); } /* * The playcmd is used in a SndDoCommand. The bufferCmd plays the * buffer pointed to by param2 (set below). */ playcmd.cmd = bufferCmd; playcmd.param1 = 0; /* * The callcmd is used in a SndDoCommand. The callBackCmd calls * the callback routine (snd_done) specified in the SndNewChannel * call. This callback will be put after each play cmd to indicate * when a sound has (almost) finished. */ callcmd.cmd = callBackCmd; callcmd.param1 = 0; callcmd.param2 = 0; /* We start with the first playbuf */ playbuf = playbuf1; for (i = len, Sp = sample; i > 0; i -= incr, Sp += incr) { /* change buffers */ if (playbuf == playbuf1) playbuf = playbuf2; else playbuf = playbuf1; /* set the buffer length to either the increment or the rest of the sample */ playbuf->length = i < incr? i : incr; /* Set the pointer to the sound to be played */ playbuf->samplePtr = Sp; /* * Set the bufferCmd pointer to this buffer. This command * buffer is copied by the SndDoCommand so we don't need * two of them */ playcmd.param2 = (long)playbuf; /* set the flag we use in snd_done to indicate the finish */ sndp->userInfo = 0; /* Play the buffer */ if ((err = SndDoCommand(sndp, &playcmd, true)) != noErr) { return(err); } /* Put the callback in the queue after the sound */ if ((err = SndDoCommand(sndp, &callcmd, true)) != noErr) { return(err); } /* Now wait for the sound to finish (later we'll do some animation) */ while(sndp->userInfo != 1); } /* * Cleanup */ if ((err = SndDisposeChannel(sndp, false)) != noErr) { return(err); } DisposPtr(playbuf1); DisposPtr(playbuf2); return(noErr); } ---