[comp.sources.mac] Example use of Mac II sound chip

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