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