VoxII.c

/*______________________________________________________*/
/*                     Sound  Demo                      */
/*                          by                          */
/*                  RICHARD P. COLLYER                  */
/*              Developer Technical Support             */
/*                 Apple Computer, Inc.                 */
/*                       09/24/91                       */
/*______________________________________________________*/
 
#include    <GestaltEqu.h>
#include    <Memory.h>
#include    <OSEvents.h>
#include    <Sound.h>
#include    <SoundInput.h>
#include    <StdIO.h>
#include    <Types.h>
 
//The smaller buffSize, the sooner the echo, but the more clicks you hear
#define kbuffSize       0x10000
 
/*The HeaderSize constant is used to skip 20 bytes of the buffer when calling bufferCmd.
The first 20 byte of the sound header need to be skipped so that the bufferCmd will be
pointing at a SoundHeader Record and not an 'snd ' resource header. */
#define kHeaderSize     20
 
// Forward reference
pascal void MyRecComp (SPBPtr inParamPtr);
 
typedef struct {
    short       OnOff;
    short       Level;
    short       Length;
} Level;
 
 
void BufPlay (Handle Buf, SndChannelPtr channel)
 
/*This routine takes an snd buffer and a sound channel and turns the information into a 
bufferCmd to the channel. */
 
{
    SndCommand      localSndCmd;
    OSErr           err;
    
    localSndCmd.cmd = bufferCmd;
    localSndCmd.param1 = 0;
    localSndCmd.param2 = (long) ((*Buf) + kHeaderSize);
    
    err = SndDoCommand (channel, &localSndCmd, false);
    if (err != noErr)
        Debugger();
    return;
}
 
Handle SetUpSounds (Handle Buf, short *HeaderSize, Fixed sampRate)
 
/* SetUpSounds is a routine which takes a buffer handle, sound headers size and sample rate 
value and turns it into a snd buffer with the proper header.  It then returns the buffer
handle back to the caller. */
 
{
    OSErr       err;
    short       dummy;
 
    
    Buf = NewHandle(kbuffSize);
    if (MemError() != noErr || Buf == nil)
        Debugger();
    MoveHHi (Buf);
    if (MemError() != noErr)
        Debugger();
    HLock (Buf);
    if (MemError() != noErr)
        Debugger();
    
    /* The call to SetupSndHeader is done twice in order to make sure that the 
    Header size is set correctly.  */
 
    err = SetupSndHeader (Buf, 1, sampRate, 8, 'NONE', 0x3C, 0, HeaderSize);
    if (err != noErr)
        Debugger();
    err = SetupSndHeader (Buf, 1, sampRate, 8, 'NONE', 0x3C, kbuffSize - *HeaderSize, &dummy);
    if (err != noErr)
        Debugger();
        
    return (Buf);
}
 
main()
{
    short               dummy;
    Level               myLevel;
    OSErr               err;
    SPBPtr              RecordRec;
    long                SoundRefNum, feature;
    Handle              Buffer, Buffer2, Buffer3, Buffer4;
    SndChannelPtr       chan;
    short               headerlength, headerlength2, headerlength3, headerlength4;
    Fixed               sampleRate;
    SndCommand          mycmd;
    short               recordStat, MetLevel;
    unsigned long       totalSampleRecord, TotalMilRecord, ActSampleRecord, ActMilRecord;
    SICompletionUPP     mySICompletionUPP;
 
    
    /* use Gestalt to make sure the app will work */
    
    err = Gestalt(gestaltSoundAttr, &feature);
    if (!err) {
        if ((feature & 0x0020) != 0x0020)
            ExitToShell();  // a nice app would inform the user before quiting
        }
    else 
        Debugger();
        
    /* Open sound input drive (whichever one is selected in the sound cdev) */
    
    err = SPBOpenDevice (nil, siWritePermission, &SoundRefNum);
    if (err != noErr)
        Debugger();
        
    myLevel.OnOff = 1;
    myLevel.Level = 150;
    myLevel.Length = 0;
    err = SPBSetDeviceInfo (SoundRefNum,siVoxRecordInfo, (Ptr) &myLevel);
    if (err != noErr)
        Debugger();
        
    myLevel.OnOff = 1;
    myLevel.Level = 50;
    myLevel.Length = 100;
    err = SPBSetDeviceInfo (SoundRefNum,siVoxStopInfo, (Ptr) &myLevel);
    if (err != noErr)
        Debugger();
        
    /* Get the sample rate information for the snd header */
        
    err = SPBGetDeviceInfo (SoundRefNum,siSampleRate, (Ptr) &sampleRate);
    if (err != noErr)
        Debugger();
        
    /* build the four snd buffers */
        
    Buffer = SetUpSounds (Buffer, &headerlength, sampleRate);
    Buffer2 = SetUpSounds (Buffer2, &headerlength2, sampleRate);
    Buffer3 = SetUpSounds (Buffer3, &headerlength3, sampleRate);
    Buffer4 = SetUpSounds (Buffer4, &headerlength4, sampleRate);
    
    /* build the RecordRec pointer and fill in the fields */
        
    RecordRec = (SPBPtr) NewPtr(sizeof (SPB));
    if (MemError() != noErr || RecordRec == nil)
        Debugger();
        
    /* create a Sound input completion UPP */
    mySICompletionUPP = NewSICompletionProc(MyRecComp);
 
    RecordRec->inRefNum = SoundRefNum;
    RecordRec->count =  kbuffSize - headerlength;
    RecordRec->milliseconds = 0;
    RecordRec->bufferLength = kbuffSize - headerlength;
    RecordRec->bufferPtr = (Ptr) ((*Buffer) + headerlength);
    RecordRec->completionRoutine = mySICompletionUPP;
    RecordRec->interruptRoutine = nil;
    RecordRec->userLong = 0;
    RecordRec->error = 0;
    RecordRec->unused1 = 0;
    
    /* open the sound channel which I will need to play from */
 
    chan = nil;
    err = SndNewChannel (&chan, sampledSynth, 0, nil);
    if (err != noErr)
        Debugger();
            
    do {    /* main loop of the app */
        err = SPBRecord (RecordRec, true); // start recording
        if (err != noErr)
            Debugger();
            
        /* The set of if-then-else statements are to determine if the first three buffers
        have been filled with recorded sounds.  If so then which buffer was the last buffer
        to be filled and set the appropriate buffer to be the next buffer to be played. */
        
        if ((RecordRec->userLong & 0x00000008) == 0x00000008) {
            if (RecordRec->bufferPtr == (Ptr) ((*Buffer) + headerlength)) {
                err = SetupSndHeader (Buffer3, 1, sampleRate, 8, 'NONE', 0x3C, ActSampleRecord, &dummy);
                if (err != noErr)
                    Debugger();
 
                BufPlay (Buffer3, chan);
                }
            else  if (RecordRec->bufferPtr == (Ptr) ((*Buffer2) + headerlength2)) {
                err = SetupSndHeader (Buffer4, 1, sampleRate, 8, 'NONE', 0x3C, ActSampleRecord, &dummy);
                if (err != noErr)
                    Debugger();
 
                BufPlay (Buffer4, chan);
                }
            else  if (RecordRec->bufferPtr == (Ptr) ((*Buffer3) + headerlength3)) {
                err = SetupSndHeader (Buffer, 1, sampleRate, 8, 'NONE', 0x3C, ActSampleRecord, &dummy);
                if (err != noErr)
                    Debugger();
 
                BufPlay (Buffer, chan);
                }
            else {
                err = SetupSndHeader (Buffer2, 1, sampleRate, 8, 'NONE', 0x3C, ActSampleRecord, &dummy);
                if (err != noErr)
                    Debugger();
 
                BufPlay (Buffer2, chan);
                }
            }
            
        do {    /* loop until the recording is done on current buffer */
            } while ((RecordRec->userLong & 0x00000001) == 0);
            
        // Use this for testing the resulting recorded sound, but only when testing
            
        err = SPBGetRecordingStatus(SoundRefNum, &recordStat, &MetLevel, &totalSampleRecord,
                &ActSampleRecord, &TotalMilRecord, &ActMilRecord);
        if (err != noErr)
            Debugger();
            
        /* Once the Completion rountine has been called I need to set up the next buffer
        to be recorded into.  I do this by going through the if-then-else cases to see 
        which buffer was last filled and set up the next buffer. */
        
        if (RecordRec->bufferPtr == (Ptr) ((*Buffer) + headerlength))
            RecordRec->bufferPtr = (Ptr) ((*Buffer2) + headerlength2);
            
        else  if (RecordRec->bufferPtr == (Ptr) ((*Buffer2) + headerlength2))
            RecordRec->bufferPtr = (Ptr) ((*Buffer3) + headerlength3);
            
        else  if (RecordRec->bufferPtr == (Ptr) ((*Buffer3) + headerlength3))
            RecordRec->bufferPtr = (Ptr) ((*Buffer4) + headerlength4);
            
        else
            RecordRec->bufferPtr = (Ptr) ((*Buffer) + headerlength);    
        
        /* I need to reset the recording current buffer field*/
        
        RecordRec->userLong &= 0xFFFFFFFE;
        
        /* When the mouse button is found down, it is time to quit */
        
        } while (!Button());
        
    /* Once I am out of the loop it is time to clean up - stop the currently playing sound,
    Dispose of the Channel, close the input driver, and dispose of the buffer handles and
    RecordRec Ptr. */
        
    mycmd.cmd = quietCmd;
    mycmd.param1 = 0;
    mycmd.param2 = 0;
    
    err = SndDoImmediate (chan, &mycmd);
    if (err != noErr)
        Debugger();
 
    err = SndDisposeChannel (chan,false);
    if (err != noErr)
        Debugger();
 
    SPBCloseDevice (SoundRefNum);
        
    HUnlock (Buffer);
    HUnlock (Buffer2);
    HUnlock (Buffer3);
    HUnlock (Buffer4);
    DisposeHandle (Buffer);
    DisposeHandle (Buffer2);
    DisposeHandle (Buffer3);
    DisposeHandle (Buffer4);
    DisposePtr ((Ptr) RecordRec);
    
    DisposeRoutineDescriptor(mySICompletionUPP);
}
/**********************************/
 
pascal void MyRecComp (SPBPtr inParamPtr)
 
/* This is the Completion Routine which is called every time the buffer, which is being 
recorded into, is full.  */
 
{
    long    local;
    
    local = inParamPtr->userLong;
    
    local &= 0x00000038;
    
    /* I need to check for the first three times this routine is called so I know when it
    is safe to start playing buffers. */
    
    if (local != 0x00000008) { 
        if (local == 0x00000000)
            local = 0x00000010;
        else if (local == 0x00000010)
            local = 0x00000020;
        else
            local = 0x00000008;
        }
        
    inParamPtr->userLong &= 0xFFFFFFC5; 
    inParamPtr->userLong |= local;  
    
    /* inform the app that the recording is done */
        
    inParamPtr->userLong |= 0x00000001; 
 
    return;
}
 
/**********************************/
/* Bit definitions .....
31
30
29
28
27
26
25
24
23
22
21
20
19
18
17
16
15
14
13
12
11
10
09
08
07
06
05 - finished recording to the second buffer
04 - finished recording to the first buffer
03 - finished recording to the first three buffers
02
01
00 - finished recording to current buffer
*/