home *** CD-ROM | disk | FTP | other *** search
- /**********
- * Sound Confusion
- *
- * by Bernie Bernstein
- * 9/11/92
- ***********/
-
-
- #include <GestaltEqu.h>
- #include <Memory.h>
- #include <OSEvents.h>
- #include <Sound.h>
- #include <SoundInput.h>
- #include <StdIO.h>
- #include <Types.h>
-
- #include "Confusion.h"
-
- /* how much memory should the program reserve after making the buffers? */
- #define kExtraMem 0x30000
-
-
- /*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
-
- typedef struct {
- short OnOff;
- short Level;
- short Length;
- } Level;
-
-
- Boolean inStereo;
- short gThreshold;
- long gBuffSize;
- SCGlobals *globs;
- BufInfo gExtraBuffer;
-
-
- /***
- * BufPlay
- * This routine takes an snd buffer and a sound channel and turns the information
- * into a bufferCmd to the channel.
- ***/
- void BufPlay (Handle Buf, SndChannelPtr channel)
- {
- SndCommand localSndCmd;
- OSErr err;
-
- localSndCmd.cmd = bufferCmd;
- localSndCmd.param1 = 0;
- localSndCmd.param2 = (long) ((*Buf) + kHeaderSize);
-
- err = SndDoCommand (channel, &localSndCmd, false);
- if (err != noErr)
- AlertUser(err, iPlaying);
- return;
- }
-
-
- /***
- * SetUpSounds
- * 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.
- ***/
- Handle SetUpSounds (Handle Buf, short *HeaderSize, Fixed sampRate)
- {
- OSErr err;
- short dummy;
-
- Buf = NewHandle(gBuffSize);
- if ((err = MemError()) != noErr || Buf == nil)
- AlertUser(err, iSetupBuffer);
- MoveHHi (Buf);
- if ((err = MemError()) != noErr)
- AlertUser(err, iSetupBuffer);
- HLock (Buf);
- if ((err = MemError()) != noErr)
- AlertUser(err, iSetupBuffer);
-
- /* 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)
- AlertUser(err, iSetupBuffer);
- err = SetupSndHeader (Buf, 1, sampRate, 8, 'NONE', 0x3C, gBuffSize - *HeaderSize, &dummy);
- if (err != noErr)
- AlertUser(err, iSetupBuffer);
-
- return (Buf);
- }
-
- /***
- * RndRange
- * Pick a random number from min to max.
- ***/
- short RndRange(short min, short max)
- {
- unsigned short r = (unsigned short)Random();
- short d = max-min+1;
- return r / (0x10000/d) + min;
- }
-
-
- /***
- * PickPlayableBuffer
- * Return the index of a buffer which is ready to be played. It randomly
- * picks one from the playable buffers, that is, ones which have some
- * sound in them.
- ***/
- short PickPlayableBuffer(BufInfo *buffers, short max)
- {
- short rnd;
- short i;
-
- for(rnd = RndRange(0,max-1), i=0;
- !buffers[rnd].playable;
- i++)
- {
- if (i>100)
- return max+1;
-
- rnd = RndRange(0,max-1);
- }
- return rnd;
- }
-
-
- /***
- * PickReadableBuffer
- * Return the index of a buffer which can read some new sounds
- * First it fills up each buffer, then it randomly picks new ones
- * once they are all full.
- ***/
- short PickReadableBuffer(BufInfo *buffers, short max)
- {
- short rnd;
- short i;
-
- for(i=0; i<max; i++)
- {
- if (!buffers[i].playable)
- return i;
- }
-
- return RndRange(0,max-1);
- }
-
-
- #define Abs(x) ((x<0)?-(x):x)
- #define Min(x,y) ((x<y)?(x):(y))
- #define Max(x,y) ((x<y)?(y):(x))
- #define BlankSpace 0x1000
-
- /***
- * TrimBuffer
- * If the buffer is silent, then return false. Otherwise, reduce the
- * buffer to just the parts that have sound.
- ***/
- long TrimBuffer(Handle buffer, long headerlen)
- {
- Boolean result = false;
- long oldBuffSize = GetHandleSize(buffer);
- long oldEnd = 0;
- long oldStart = headerlen;
- long newIndex = 0;
- long copySize = 0;
- long bufByte = 0;
- Boolean firstTime = true;
- Handle tmpbuffer = gExtraBuffer.buffer;
-
- /* copy header into new buffer */
- BlockMove(*buffer, *tmpbuffer, headerlen);
-
- /* determine if the buffer was loud enough */
- /* loud means that a byte in the buffer is far enough away from the */
- /* center to get above the gThreshold */
- for (bufByte=0; bufByte<oldBuffSize-headerlen; bufByte++)
- {
- short delta = 0x80 - (unsigned char)(*buffer)[headerlen + bufByte];
- if (Abs(delta) << 1 > gThreshold)
- {
- /* the first time we hear something, save the previous BlankSpace bytes */
- /* until now. */
- if (firstTime)
- {
- /* If we are close to the beginning, copy from the beginning. */
- /* otherwise, copy blank space until this spot */
- if (bufByte > BlankSpace)
- oldStart = bufByte - BlankSpace;
- else
- oldStart = 0;
-
- /* If we are near the end, copy to the end. */
- /* otherwise, copy blank space after this spot */
- oldEnd = bufByte + BlankSpace;
- if (headerlen + oldEnd > oldBuffSize)
- oldEnd = oldBuffSize - headerlen;
- copySize = oldEnd - oldStart;
-
- BlockMove(&(*buffer)[headerlen+oldStart], &(*tmpbuffer)[headerlen+newIndex], copySize);
- newIndex += copySize;
- bufByte = oldEnd;
- firstTime = false;
- }
- else
- {
- /* After the first time, bufByte > BlankSpace. */
- oldStart = bufByte - BlankSpace;
- if (oldStart < oldEnd)
- oldStart = oldEnd+1;
-
- oldEnd = bufByte + BlankSpace;
- if (headerlen + oldEnd > oldBuffSize)
- oldEnd = oldBuffSize - headerlen;
- copySize = oldEnd - oldStart;
-
- BlockMove(&(*buffer)[headerlen+oldStart], &(*tmpbuffer)[headerlen+newIndex], copySize);
- newIndex += copySize;
- bufByte = oldEnd;
- }
- result = true;
- }
- }
-
- if (result)
- {
- BlockMove(*tmpbuffer, *buffer, newIndex+headerlen);
- return newIndex;
- }
- else
- {
- return 0L;
- }
- }
-
-
-
- #define TestBit(l,b) ((l>>b)&0x01)
-
- /***
- * InitSoundConfusion
- * Allocate memory and initialize the sound driver
- ***/
- void InitSoundConfusion(void)
- {
- Level myLevel;
- OSErr err;
- long feature;
- BufInfo *Buffers;
- short i;
- short numBuffers;
- short meterState;
- SPBPtr RecordRec;
- Str255 settingStr;
- long tmplong;
-
- /* use Gestalt to make sure the app will work */
- err = Gestalt(gestaltSoundAttr, &feature);
- if (err == noErr)
- {
- if (!TestBit(feature, gestaltHasSoundInputDevice))
- {
- AlertUser(0, iNoInput);
- ExitToShell();
- }
- else
- inStereo = TestBit(feature, gestaltStereoCapability);
- }
- else
- {
- AlertUser(err, iGestaltFailed);
- ExitToShell();
- }
-
-
- GetIndString(settingStr, rSettingStrings, iThreshold);
- StringToNum(settingStr, &tmplong);
- gThreshold = tmplong;
-
- GetIndString(settingStr, rSettingStrings, iBufferSize);
- StringToNum(settingStr, &tmplong);
- gBuffSize = tmplong;
-
- globs = (SCGlobals*)NewPtr(sizeof(SCGlobals));
- if ((err = MemError()) != noErr || globs == nil)
- {
- AlertUser(err, iStarting);
- return;
- }
-
- /* Open sound input drive (whichever one is selected in the sound cdev) */
- err = SPBOpenDevice (nil, siWritePermission, &globs->SoundRefNum);
- if (err != noErr)
- {
- AlertUser(err, iStarting);
- return;
- }
-
- // turn on sound metering
- meterState = 1; // turn it on
- err = SPBSetDeviceInfo(globs->SoundRefNum, siLevelMeterOnOff, (char *)&meterState);
- if (err != noErr)
- Debugger();
-
- /* Get the sample rate information for the snd header */
- err = SPBGetDeviceInfo (globs->SoundRefNum,siSampleRate, (Ptr) &globs->sampleRate);
- if (err != noErr)
- {
- AlertUser(err, iStarting);
- return;
- }
-
-
- /* build the RecordRec pointer and fill in the fields */
- RecordRec = (SPBPtr) NewPtr(sizeof (SPB));
- if ((err = MemError()) != noErr || RecordRec == nil)
- {
- AlertUser(err, iStarting);
- return;
- }
-
- /* how many buffers can we make? (leave space for an extra buffer and then some memory) */
- numBuffers = ((FreeMem() - kExtraMem) / gBuffSize) - 1;
- if (numBuffers < 1)
- {
- AlertUser(0, iMemory);
- return;
- }
-
- Buffers = (BufInfo*)NewPtr(sizeof(BufInfo)*numBuffers);
- if ((err = MemError()) != noErr || Buffers == nil)
- {
- AlertUser(err, iStarting);
- return;
- }
-
- globs->fullBuffer = false;
- globs->loudEnough = false;
- globs->bufferGettingFilled = 0;
- globs->buffers = Buffers;
- globs->numBuffers = numBuffers;
- globs->RecordRec = RecordRec;
-
- /* build the snd buffers */
- for (i=0; i<numBuffers; i++)
- {
- Buffers[i].buffer = SetUpSounds(Buffers[i].buffer, &Buffers[i].headerlength, globs->sampleRate);
- Buffers[i].buffSize = gBuffSize - Buffers[i].headerlength;
- Buffers[i].playable = false;
- }
-
- gExtraBuffer.buffer = SetUpSounds(gExtraBuffer.buffer, &gExtraBuffer.headerlength, globs->sampleRate);
-
- RecordRec->inRefNum = globs->SoundRefNum;
- RecordRec->count = gBuffSize - Buffers[0].headerlength;
- RecordRec->milliseconds = 0;
- RecordRec->bufferLength = gBuffSize - Buffers[0].headerlength;
- RecordRec->bufferPtr = (Ptr) ((*Buffers[0].buffer) + Buffers[0].headerlength);
- RecordRec->completionRoutine = (ProcPtr) &MyRecComp;
- RecordRec->interruptRoutine = nil;
- RecordRec->userLong = (long)globs;
- RecordRec->error = 0;
- RecordRec->unused1 = 0;
-
-
- if (inStereo)
- {
- /* open the sound channel which I will need to play from */
- globs->leftChan = nil;
- err = SndNewChannel (&globs->leftChan, sampledSynth, initChanLeft, nil);
- if (err != noErr)
- {
- AlertUser(err, iStarting);
- return;
- }
-
- /* open the sound channel which I will need to play from */
- globs->rightChan = nil;
- err = SndNewChannel (&globs->rightChan, sampledSynth, initChanRight, nil);
- if (err != noErr)
- {
- AlertUser(err, iStarting);
- return;
- }
- }
- else
- {
- /* open the sound channel which I will need to play from */
- globs->leftChan = nil;
- err = SndNewChannel (&globs->leftChan, sampledSynth, 0, nil);
- if (err != noErr)
- {
- AlertUser(err, iStarting);
- return;
- }
- }
-
- globs->firstTime = true;
-
-
- }
-
-
- /***
- * ConfuseSound
- * This is called during idle time from the event loop.
- * It starts recording from a buffer and playing from another one.
- ***/
- void ConfuseSound(void)
- {
- short dummy;
- OSErr err;
- BufInfo *Buffers;
- short i;
- SndCommand mycmd;
- short recordStat, MetLevel;
- unsigned long totalSampleRecord, TotalMilRecord, ActSampleRecord, ActMilRecord;
- long bufByte;
- short bufNum;
-
- Buffers = globs->buffers;
-
- /* get the loudness of the current input */
- err = SPBGetRecordingStatus(globs->SoundRefNum, &recordStat, &MetLevel, &totalSampleRecord,
- &ActSampleRecord, &TotalMilRecord, &ActMilRecord);
- if (err != noErr)
- AlertUser(err, iReadingLevel);
-
- if (globs->fullBuffer || globs->firstTime)
- {
- globs->fullBuffer = false;
-
- if (!globs->firstTime)
- {
- long newSize;
- bufNum = globs->bufferGettingFilled;
- newSize = TrimBuffer(Buffers[bufNum].buffer, Buffers[bufNum].headerlength);
- Buffers[bufNum].playable = globs->loudEnough = (newSize > 0);
- Buffers[bufNum].buffSize = newSize;
-
- /* if the buffer was loud enough, then prepare to record the next readable buffer */
- if (globs->loudEnough)
- {
- short nextbuf = PickReadableBuffer(Buffers, globs->numBuffers);
- globs->bufferGettingFilled = nextbuf;
- globs->loudEnough = false;
- Buffers[nextbuf].playable = false;
- HUnlock(Buffers[nextbuf].buffer);
- SetHandleSize(Buffers[nextbuf].buffer, gBuffSize);
- MoveHHi(Buffers[nextbuf].buffer);
- HLock(Buffers[nextbuf].buffer);
- globs->RecordRec->bufferPtr = (*Buffers[nextbuf].buffer) + Buffers[nextbuf].headerlength;
- }
- }
-
- globs->firstTime = false;
- /* start recording into the buffer asynchronously */
- err = SPBRecord (globs->RecordRec, true); // start recording
- if (err != noErr)
- AlertUser(err, iRecording);
-
- /* pick a buffer to play from */
- i = PickPlayableBuffer(Buffers, globs->numBuffers);
- if (i<=globs->numBuffers)
- {
- long bsize = Buffers[i].buffSize;
- err = SetupSndHeader (Buffers[i].buffer, 1, globs->sampleRate, 8, 'NONE', 0x3C, bsize, &dummy);
- if (err != noErr)
- AlertUser(err, iPlaying);
- /* play it asynchronously */
- BufPlay(Buffers[i].buffer,
- inStereo ? (RndRange(0,1) ? globs->leftChan : globs->rightChan) : globs->leftChan);
- }
- }
- }
-
-
- /***
- * StopConfusion
- * Deallocate memory and close stuff.
- ***/
- void StopConfusion(void)
- {
- OSErr err;
- SndCommand mycmd;
- short i;
-
- /* stop playing sounds now */
- mycmd.cmd = quietCmd;
- mycmd.param1 = 0;
- mycmd.param2 = 0;
- err = SndDoImmediate (globs->leftChan, &mycmd);
- if (err != noErr)
- AlertUser(err, iClosing);
- if (inStereo)
- {
- err = SndDoImmediate (globs->leftChan, &mycmd);
- if (err != noErr)
- AlertUser(err, iClosing);
- }
-
- /* kill the channel */
- err = SndDisposeChannel (globs->leftChan,false);
- if (err != noErr)
- AlertUser(err, iClosing);
- if (inStereo)
- {
- err = SndDisposeChannel (globs->rightChan,false);
- if (err != noErr)
- AlertUser(err, iClosing);
- }
-
- /* close the input device */
- SPBCloseDevice (globs->SoundRefNum);
-
- /* kill the snd buffers */
- for (i=0; i<globs->numBuffers; i++)
- {
- HUnlock(globs->buffers[i].buffer);
- DisposeHandle(globs->buffers[i].buffer);
- }
- HUnlock(gExtraBuffer.buffer);
- DisposeHandle(gExtraBuffer.buffer);
-
- /* kill the buffers */
- DisposePtr ((Ptr) globs->buffers);
- DisposePtr ((Ptr) globs->RecordRec);
- DisposePtr ((Ptr) globs);
- }
-
-
- /**********************************/
-
- void DoAbout()
- {
- Str31 numBufsStr;
- Str31 numFilledStr;
- short numFilled = 0;
- short i;
- short itemHit;
- DialogPtr dlg;
-
- for (i=0; i<globs->numBuffers; i++)
- {
- if (globs->buffers[i].playable)
- numFilled++;
- }
- NumToString((long)globs->numBuffers, numBufsStr);
- NumToString((long)numFilled, numFilledStr);
- ParamText(numBufsStr, numFilledStr, "", "");
-
- dlg = GetNewDialog(rAboutAlert, NULL, (WindowPtr)-1L);
- ModalDialog(NULL, &itemHit);
- DisposDialog(dlg);
- }
-
-
- /***
- * MyRecComp
- *
- * This is the Completion Routine which is called every time the buffer,
- * which is being recorded into, is full.
- ***/
- pascal void MyRecComp (SPBPtr inParamPtr)
- {
- SCGlobals *glob;
-
- glob = (SCGlobals*)inParamPtr->userLong;
- glob->fullBuffer = true;
-
- return;
- }
-