Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
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 |
*/ |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-03-14