Previous Book Contents Book Index Next

Inside Macintosh: Sound /
Chapter 3 - Sound Input Manager / Using the Sound Input Manager


Legacy Documentclose button

Important: Inside Macintosh: Sound is deprecated as of Mac OS X v10.5. For new audio development in Mac OS X, use Core Audio. See the Audio page in the ADC Reference Library.

Recording Sounds Directly From a Device

The Sound Input Manager provides a number of routines that you can use for low-level control over the recording process (such as the ability to intercept sound input data at interrupt time). You can open a sound input device and read data from it by calling these low-level Sound Input Manager routines. Several of those routines access information through a sound input parameter block, which is defined by the SPB data type:

TYPE SPB =
RECORD
   inRefNum:            LongInt;    {reference number of input device}
   count:               LongInt;    {number of bytes to record}
   milliseconds:        LongInt;    {number of milliseconds to record}
   bufferLength:        LongInt;    {length of buffer to record into}
   bufferPtr:           Ptr;        {pointer to buffer to record into}
   completionRoutine:   ProcPtr;    {pointer to a completion routine}
   interruptRoutine:    ProcPtr;    {pointer to an interrupt routine}
   userLong:            LongInt;    {for application's use}
   error:               OSErr;      {error returned after recording}
   unused1:             LongInt;    {reserved}
END;
The inRefNum field indicates the reference number of the sound input device from which the recording is to occur. You can obtain the reference number of the default sound input device by using the SPBOpenDevice function.

The count, milliseconds, and bufferLength fields jointly determine the length of recording. The count field indicates the number of bytes to record; the milliseconds field indicates the number of milliseconds to record; and the bufferLength field indicates the length in bytes of the buffer into which the recorded sound data is to be placed. If the count and milliseconds fields are not equivalent, then the field which specifies the longer recording time is used. If the buffer specified by the bufferLength field is shorter than this recording time, then the recording time is truncated so that the recorded data can fit into the buffer specified by the bufferPtr field. The Sound Input Manager provides two functions, SPBMilliSecondsToBytes and SPBBytesToMilliSeconds, that allow you to convert between byte and millisecond values.

After recording finishes, the count and milliseconds fields indicate the number of bytes and milliseconds actually recorded.

The completionRoutine and interruptRoutine fields allow your application to define a sound input completion routine and a sound input interrupt routine, respectively. More information on these routines is provided later in this section.

The userLong field contains a long integer that is provided for your application's own use. You can use this field, for instance, to pass a handle to an application-defined structure to the sound input completion or interrupt routine. Or, you can use this field to store the value of your application's A5 register, so that your sound input completion or interrupt routine can access your application's global variables. For more information on preserving the value of the A5 register, see the discussion of the SetA5 and SetCurrentA5 functions in the chapter "Memory Management Utilities" in Inside Macintosh: Memory.

The error field describes any errors that occur during the recording. This field contains a value greater than 0 while recording unless an error occurs, in which case it contains a value less than 0 that indicates an operating system error. Your application can poll this field to check on the status of an asynchronous recording. If recording terminates without an error, this field contains 0.

Listing 3-1 shows how to set up a sound parameter block and record synchronously using the SPBRecord function. This procedure takes one parameter, a handle to a block of memory in which the recorded sound data is to be stored. It is assumed that the block of memory is large enough to hold the sound to be recorded.

Listing 3-1 Recording directly from a sound input device

PROCEDURE MyRecordSnd (mySndH: Handle);
CONST
   kAsync = TRUE;
   kMiddleC = 60;
VAR
   mySPB:         SPB;        {a sound input parameter block}
   myInRefNum:    LongInt;    {device reference number}
   myBuffSize:    LongInt;    {size of buffer to record into}
   myHeadrLen:    Integer;    {length of sound header}
   myNumChans:    Integer;    {number of channels}
   mySampSize:    Integer;    {size of a sample}
   mySampRate:    Fixed;      {sample rate}
   myCompType:    OSType;     {compression type}
   myErr:         OSErr;
BEGIN
   {Open the default input device for reading and writing.}
   myErr := SPBOpenDevice('', siWritePermission, myInRefNum);

   IF myErr = noErr THEN
   BEGIN
      {Get current settings of sound input device.}
      MyGetDeviceSettings(myInRefNum, myNumChans, mySampRate, 
                           mySampSize, myCompType);

      {Set up handle to contain the 'snd ' resource header.}
      myErr := SetupSndHeader(mySndH, myNumChans, mySampRate,mySampSize,  
                                 myCompType, kMiddleC, 0, myHeadrLen);

      {Leave room in buffer for the sound resource header.}
      myBuffSize := GetHandleSize(mySndH) - myHeadrLen;

      {Lock down the sound handle until the recording is over.}
      HLockHi(mySndH);

      {Set up the sound input parameter block.}
      WITH mySPB do
      BEGIN
         inRefNum := myInRefNum;          {input device reference number}
         count := myBuffSize;             {number of bytes to record}
         milliseconds := 0;               {no milliseconds}
         bufferLength := myBuffSize;      {length of buffer}
         bufferPtr := Ptr(ORD4(mySndH^) + myHeadrLen);
                                          {put data after 'snd ' header}
         completionRoutine := NIL;        {no completion routine}
         interruptRoutine := NIL;         {no interrupt routine}
         userLong := 0;                   {no user data}
         error := noErr;                  {clear error field}
         unused1 := 0;                    {clear reserved field}
      END;

      {Record synchronously through the open sound input device.}
      myErr := SPBRecord(@mySPB, NOT kAsync);

      HUnlock(mySndH);                    {unlock the handle}

      {Indicate the number of bytes actually recorded.}
      myErr := SetupSndHeader(mySndH, myNumChans, mySampRate, mySampSize, 
                                 myCompType, kMiddleC, mySPB.count,
                                 myHeadrLen);

      {Close the input device.}
      myErr := SPBCloseDevice(myInRefNum);
   END;
END;
The MyRecordSnd procedure defined in Listing 3-1 opens the default sound input device by using the SPBOpenDevice function. You can specify one of two values for the permission parameter of SPBOpenDevice:

CONST
   siReadPermission  = 0;  {open device for reading}
   siWritePermission = 1;  {open device for reading/writing}
You must open a device for both reading and writing if you intend to use the SPBSetDeviceInfo function or the SPBRecord function. If SPBOpenDevice successfully opens the specified device for reading and writing, MyRecordSnd calls the MyGetDeviceSettings procedure (defined in Listing 3-3 on page 3-12). That procedure calls the Sound Input Manager function SPBGetDeviceInfo (explained in "Getting and Setting Sound Input Device Information" on page 3-10) to determine the current number of channels, sample rate, sample size, and compression type in use by the device.

This information is then passed to the SetupSndHeader function, which sets up the handle mySndH with a sound header describing the current device settings. After doing this, MyRecordSnd sets up a sound input parameter block and calls the SPBRecord function to record a sound. Note that the handle must be locked during the recording because the parameter block contains a pointer to the input buffer. After the recording is done, MyRecordSnd once again calls the SetupSndHeader function to fill in the actual number of bytes recorded.

If the MyRecordSnd procedure defined in Listing 3-1 executes successfully, the handle mySndH points to a resource of type 'snd '. Your application can then synchronously play the recorded sound, for example, by executing the following line of code:

myErr := SndPlay(NIL, mySndH, FALSE);
For more information on playing sounds your application has recorded, see the chapter "Sound Manager" in this book.

Defining a Sound Input Completion Routine

The completionRoutine field of the sound parameter block record contains the address of a completion routine that is executed when the recording terminates normally, either by reaching its prescribed time or size limits or by the application calling the SPBStopRecording function. A completion routine should have the following format:

PROCEDURE MySICompletionRoutine (inParamPtr: SPBPtr);
The completion routine is passed the address of the sound input parameter block that was passed to the SPBRecord function. You can gain access to other data structures in your application by passing an address in the userLong field of the parameter block. After the completion routine executes, your application should check the error field of the sound input parameter block to see if an error code was returned.

Your sound input interrupt routine is always called at interrupt time, so it should not call routines that might allocate or move memory or assume that A5 is set up. For more information on sound input interrupt routines, see "Sound Input Interrupt Routines" beginning on page 3-55.

Defining a Sound Input Interrupt Routine

The interruptRoutine field of the sound input parameter block contains the address of a routine that executes when the internal buffers of an asynchronous recording device are filled. The internal buffers contain raw sound samples taken directly from the input device. The interrupt routine can modify the samples in the buffer in any way it requires. The processed samples are then written to the application buffer. If compression is enabled, the modified data is compressed after your interrupt routine operates on the samples and before the samples are written to the application buffer.

Your sound input interrupt routine is always called at interrupt time, so it should not call routines that might allocate or move memory or assume that A5 is set up. For more information on sound input interrupt routines, see "Sound Input Interrupt Routines" beginning on page 3-55.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
2 JUL 1996