Previous Book Contents Book Index Next

Inside Macintosh: Sound /
Chapter 5 - Sound Components / Writing a Sound Component


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.

Dispatching to Sound Component-Defined Routines

As explained earlier, the code stored in the sound component should be contained in a resource of type kSoundComponentCodeType. The Component Manager expects the entry point in this resource to be a function with this format:

pascal ComponentResult MySurfDispatch (ComponentParameters *params, 
                                          SoundComponentGlobalsPtr globals);
The Component Manager calls your sound component by passing MySurfDispatch a selector in the params->what field; MySurfDispatch must interpret the selector and possibly dispatch to some other routine in the resource. Your sound component must be able to handle the required selectors, defined by these constants:

#define kComponentOpenSelect           -1
#define kComponentCloseSelect          -2
#define kComponentCanDoSelect          -3
#define kComponentVersionSelect        -4
#define kComponentRegisterSelect       -5
#define kComponentTargetSelect         -6
#define kComponentUnregisterSelect     -7
Note
For complete details on required component selectors, see the chapter "Component Manager" in Inside Macintosh: More Macintosh Toolbox.
In addition, your sound component must be able to respond to component-specific selectors. Some of these selectors must be handled by your component; if your component doesn't implement one of these selectors, it should return the badComponentSelector result code. Other selectors should be delegated up the component chain. This allows the Sound Manager to query a particular component chain by passing a selector to the first component in the chain. If your component does not implement a delegable selector, it should call the Component Manager routine DelegateComponentCall to delegate the selector to its source component. If your sound component does implement a particular delegable selector, it should perform the operation associated with it. The Sound Manager defines a constant to designate the delegable selectors.

/*first selector that can be delegated up the chain*/
#define kDelegatedSoundComponentSelectors       0x0100
The Sound Manager can pass these selectors to your sound component:

enum {
   /*the following calls cannot be delegated*/
   kSoundComponentInitOutputDeviceSelect     = 1,
   kSoundComponentSetSourceSelect,
   kSoundComponentGetSourceSelect,
   kSoundComponentGetSourceDataSelect,
   kSoundComponentSetOutputSelect,
   /*the following calls can be delegated*/
   kSoundComponentAddSourceSelect = kDelegatedSoundComponentSelectors + 1,
   kSoundComponentRemoveSourceSelect,
   kSoundComponentGetInfoSelect,
   kSoundComponentSetInfoSelect,
   kSoundComponentStartSourceSelect,
   kSoundComponentStopSourceSelect,
   kSoundComponentPauseSourceSelect,
   kSoundComponentPlaySourceBufferSelect
};
You can respond to these selectors by calling the Component Manager routine CallComponentFunctionWithStorage or by delegating the selector to your component's source component. Listing 5-2 illustrates how to define a sound component entry point routine.

Listing 5-2 Handling Component Manager selectors

pascal ComponentResult MySurfDispatch (ComponentParameters *params,
                                           SoundComponentGlobalsPtr globals)
{
   ComponentRoutine     myRoutine;
   ComponentResult      myResult;

   /*Get address of component-defined routine.*/
   myRoutine = MyGetComponentRoutine(params->what);

   if (myRoutine == nil)                     /*selector not implemented*/
      myResult = badComponentSelector;
   else if (myRoutine == kDelegateCall)      /*selector should be delegated*/
      myResult = DelegateComponentCall(params, globals->sourceComponent);
   else
      myResult = CallComponentFunctionWithStorage((Handle) globals, params, 
                                             (ComponentRoutine) myRoutine);
   return (myResult);
}
As you can see, the MySurfDispatch function defined in Listing 5-2 simply retrieves the address of the appropriate component-defined routine, as determined by the params->what field. If the routine MyGetComponentRoutine returns nil, then MySurfDispatch itself returns the badComponentSelector result code. Otherwise, if the selector should be delegated, MySurfDispatch calls DelegateComponentCall to do so. Finally, if the selector hasn't yet been handled, the appropriate component-defined routine is executed via CallComponentFunctionWithStorage.

Listing 5-3 defines the function MyGetComponentRoutine.

Listing 5-3 Finding the address of a component-defined routine

ComponentRoutine MyGetComponentRoutine (short selector)
{
   void                 *myRoutine;

   if (selector < 0)
      switch (selector)          /*required component selectors*/
      {
         case kComponentRegisterSelect:
            myRoutine = MyRegisterSoundComponent;
            break;
         case kComponentVersionSelect:
            myRoutine = MySoundComponentVersion;
            break;
         case kComponentCanDoSelect:
            myRoutine = MySoundComponentCanDo;
            break;
         case kComponentCloseSelect:
            myRoutine = MyCloseSoundComponent;
            break;
         case kComponentOpenSelect:
            myRoutine = MyOpenSoundComponent;
            break;
         default:
            myRoutine = nil;     /*unknown selector, so fail*/
            break;
      }
   else if (selector < kDelegatedSoundComponentSelectors)
                           /*selectors that can't be delegated*/
      switch (selector)
      {
         case kSoundComponentInitOutputDeviceSelect:
            myRoutine = MySoundComponentInitOutputDevice;
            break;

         case kSoundComponentSetSourceSelect:
         case kSoundComponentGetSourceSelect:
         case kSoundComponentGetSourceDataSelect:
         case kSoundComponentSetOutputSelect:
         default:
            myRoutine = nil;     /*unknown selector, so fail*/
            break;
      }
   else                    /*selectors that can be delegated*/
      switch (selector)
      {
         case kSoundComponentStartSourceSelect:
            myRoutine = MySoundComponentStartSource;
            break;
         case kSoundComponentPlaySourceBufferSelect:
            myRoutine = MySoundComponentPlaySourceBuffer;
            break;
         case kSoundComponentGetInfoSelect:
            myRoutine = MySoundComponentGetInfo;
            break;
         case kSoundComponentSetInfoSelect:
            myRoutine = MySoundComponentSetInfo;
            break;
         case kSoundComponentAddSourceSelect:
         case kSoundComponentRemoveSourceSelect:
         case kSoundComponentStopSourceSelect:
         case kSoundComponentPauseSourceSelect:
         default:
            myRoutine = kDelegateCall;             /*delegate it*/
            break;
      }

   return (myRoutine);
}
In all likelihood, your component is loaded into the system heap, although it might be loaded into an application heap if memory is low in the system heap. You can call the Component Manager function GetComponentInstanceA5 to determine the A5 value of the current application. If this function returns 0, your component is in the system heap; otherwise, your component is in an application's heap. Its location might affect how you allocate memory. For example, calling the MoveHHi routine on handles in the system heap has no result. Thus, you should either call the ReserveMemSys routine before calling NewHandleSys (so that the handle is allocated as low in the system heap as possible) or else just allocate a nonrelocatable block by calling the NewPtrSys routine.

If you need to access resources that are stored in your sound component, you can use OpenComponentResFile and CloseComponentResFile. OpenComponentResFile requires the ComponentInstance parameter supplied to your routine. You should not call Resource Manager routines such as OpenResFile or CloseResFile.

WARNING
Do not leave any resource files open when your sound component is closed. Their maps will be left in the subheap when the subheap is freed, causing the Resource Manager to crash.
The following sections illustrate how to define some of the sound component functions.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
2 JUL 1996