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.
AIFFWriter.c
////////// |
// |
// File: AIFFWriter.c |
// |
// Contains: Code for dispatching a sound hardware output ('sdev') component. |
// |
// Written by: Mark Cookson, Apple Developer Technical Support |
// Based on code by Kip Olson. |
// Revised by: Tim Monroe |
// |
// Copyright: © 1993-1999 by Apple Computer, Inc., all rights reserved. |
// |
// Change History (most recent first): |
// |
// <2> 03/05/99 rtm further work to bring into line with existing component sample code |
// <1> 02/25/99 rtm first inherited file; changes to support Windows compiles |
// |
// You may incorporate this sample code into your applications without |
// restriction, though the sample code has been provided "AS IS" and the |
// responsibility for its operation is 100% yours. However, what you are |
// not permitted to do is to redistribute the source as "Apple Sample |
// Code" after having made changes. If you're going to re-distribute the |
// source, we require that you make it clear in the source that the code |
// was descended from Apple Sample Code, but that you've made changes. |
// |
////////// |
////////// |
// |
// header files |
// |
////////// |
#include "AIFFWriter.h" |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Component dispatch helper defines |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
#if TARGET_CPU_68K |
#define COMPONENT_C_DISPATCHER |
#define COMPONENT_DISPATCH_MAIN |
#endif |
#define SOUNDCOMPONENT_BASENAME() __SoundOutput |
#define SOUNDCOMPONENT_GLOBALS() SoundOutputGlobalsPtr storage |
#define CALLCOMPONENT_BASENAME() SOUNDCOMPONENT_BASENAME() |
#define CALLCOMPONENT_GLOBALS() SOUNDCOMPONENT_GLOBALS() |
#define COMPONENT_UPP_SELECT_ROOT() SoundComponent |
#define COMPONENT_DISPATCH_FILE "SoundOutputDispatch.h" |
#define GET_DELEGATE_COMPONENT() (storage->sourceComponent) |
#include "Components.k.h" |
#include "Sound.k.h" |
#include "ComponentDispatchHelper.c" |
// It is possible to debug this component using an application. To do this, you cannot call the Time |
// Manager to generate interrupts, since source level debuggers are not re-entrant. This means you must call |
// the FakeInterrupt routine described below during main event loop time and define the gGlobals variable. |
#ifdef FakeInterrupts |
SoundOutputGlobalsPtr gGlobals; |
#endif |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Required component calls |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
/* SoundOutputOpen |
This routine is called when the Component Manager creates an instance of this |
component. The routine should allocate global variables in the appropriate heap |
and call SetComponentInstanceStorage() so the Component Manager can remember |
the globals and pass them to all the method calls. |
Determining the heap to use can be tricky. The Component Manager will normally |
load the component code into the system heap, which is good, since many applications |
will be sharing this component to play sound. In this case, the component's global |
variable storage should also be created in the system heap. |
However, if system heap memory is tight, the Component Manager will load |
the component into the application heap of the first application that plays sound. |
When this happens, the component should create global storage in the application heap |
instead. The Sound Manager will make sure that other applications will not try |
to play sound while the component is in this application heap. |
To determine the proper heap to use, call GetComponentInstanceA5(). If the value |
returned is 0, then the component was loaded into the system heap, and all storage |
should be allocated there. If the value returned is non-zero, the component is in |
the application heap specified by returned A5 value, and all storage should be |
allocated in this application heap. |
NOTE: If the component is loaded into the application heap, the value returned by |
GetComponentRefCon() will be 0. |
NOTE: Do not attempt to initialize or access hardware in this call, since the |
Component Manager will call SoundOutputOpen() BEFORE calling RegisterComponent(). |
Instead, initialize the hardware during InitOutputDevice(), described below. |
NOTE: This routine is never called at interrupt time. |
*/ |
PASCAL_RTN ComponentResult __SoundOutputOpen (SoundOutputGlobalsPtr globals, ComponentInstance self) |
{ |
OSErr result; |
// get space for globals in appropriate heap |
globals = (SoundOutputGlobalsPtr)NewPtrSysClear(sizeof(SoundOutputGlobals)); |
FailWithAction(globals == nil, result = MemError(), Failure); |
// If a component is loaded into an application's heap, then it gets an A5 value. |
// This also means that the component's refCon is 0. All of this means we need |
// to go and get the preferences just like we're being registered for the first time. |
if (GetComponentInstanceA5(self) != 0) |
{ |
globals->inAppHeap = true; |
result = GetPreferences(globals); |
FailIf(result != noErr, NoPrefs); |
} |
globals->self = self; |
globals->prefs = (HardwarePrefsHandle)GetComponentRefcon((Component)self); |
#ifdef FakeInterrupts |
gGlobals = globals; |
#endif |
SetComponentInstanceStorage(self, (Handle) globals); // save pointer to our globals |
return (noErr); |
NoPrefs: |
DisposePtr((Ptr)globals); |
Failure: |
return (result); |
} |
/* SoundOutputClose |
This routine is called when the Component Manager is closing the instance of |
this component. The routine should make sure all remaining data is written |
to the hardware and that the hardware is completely turned off. It should |
delete all global storage and close any other components that were opened. |
NOTE: Be sure to check that the globals pointer passed in to this routine is |
not set to NIL. If the SoundOutputOpen() routine fails for any reason, the Component |
Manager will call this routine passing in a NIL for the globals. |
NOTE: This routine is never called at interrupt time. |
*/ |
PASCAL_RTN ComponentResult __SoundOutputClose (SoundOutputGlobalsPtr globals, ComponentInstance self) |
{ |
if (globals != nil) // we have some globals |
{ |
ReleaseHardware(globals); // make sure the hardware is off and release it |
if (globals->sourceComponent) |
CloseMixerSoundComponent(globals->sourceComponent); // close mixer |
if (globals->prefsChanged) |
ErrorMessage(SetSoundPreference(kAIFFWriterSubType, kAIFFWriterName, (Handle)globals->prefs)); |
if (globals->inAppHeap) |
DisposeHandle((Handle)globals->prefs); |
if (globals->ioBuffers[0].buffer != nil) |
DisposePtr(globals->ioBuffers[0].buffer); // dispose of buffers |
if (globals->ioBuffers[1].buffer != nil) |
DisposePtr(globals->ioBuffers[1].buffer); // dispose of buffers |
DisposePtr((Ptr)globals); // torch our storage |
} |
return (noErr); |
} |
/* SoundOutputRegister |
This routine is called once, usually at boot time, when the Component Manager |
is first registering this component. This routine should check to see if the proper |
hardware is installed and return 0 if it is. If the hardware is not installed, |
the routine should return 1 and this component will not be registered. This is |
also an opportunity to do one-time initializations and perhaps register this |
component again if more than one hardware device is available. Global state information |
can also be saved in the component refcon by calling SetComponentRefCon(); |
NOTE: The cmpWantsRegisterMessage bit must be set in the component flags of the |
component in order for this routine to be called. |
NOTE: This routine is never called at interrupt time. |
*/ |
PASCAL_RTN ComponentResult __SoundOutputRegister (SoundOutputGlobalsPtr globals) |
{ |
NumVersion installedVersion; |
ComponentResult result; |
OSErr err; |
// we can only run if version 3.0 or greater of the Sound Manager is running; |
// we can check the entire long because the format of NumVersion is BCD data |
installedVersion = SndSoundManagerVersion(); |
if (installedVersion.majorRev < 3) |
result = 1; // component doesn't want to be registered |
else |
{ |
result = 0; // component wants to be registered |
err = GetPreferences(globals); |
FailWithAction(err != noErr, result = 1, Exit); |
} |
Exit: |
return result; |
} |
PASCAL_RTN ComponentResult __SoundOutputVersion (SoundOutputGlobalsPtr globals) |
{ |
globals; // suppress "unused variable" warning for all compilers |
return (kSoundComponentVersion); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Sound component functions |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
/* InitOutputDevice |
This routine is called once when the Sound Manager first opens this component. |
The routine should initialize the hardware to default values, allocate the |
appropriate mixer component and create any other memory that is required. |
NOTE: This routine is never called at interrupt time. |
*/ |
PASCAL_RTN ComponentResult __SoundOutputInitOutputDevice (SoundOutputGlobalsPtr globals, long actions) |
{ |
ComponentResult result; |
actions; // suppress "unused variable" warning for all compilers |
result = SetupHardware(globals); |
FailIf(result != noErr, Failure); |
// if any of the preferences are needed during the interrupts, |
// then locked the preferences handle now |
// first create a mixer and tell it the type of data it should output. The |
// description includes sample format, sample rate, sample size, number of channels |
// and the size of your optimal interrupt buffer. If a mixer cannot be found that |
// will output this type of data, an error will be returned. |
result = OpenMixerSoundComponent(&globals->outputData, 0, &globals->sourceComponent); |
FailIf(result != noErr, Failure); |
return (noErr); |
Failure: |
return (result); |
} |
/* SoundOutputGetInfo |
This routine returns information about this component to the Sound Manager. A |
4-byte OSType selector is used to determine the type and size of the information |
to return. If the component does not support a selector, it should delegate this |
call on up the component chain. |
NOTE: This can be called at interrupt time. However, selectors that return |
a handle will not be called at interrupt time. |
*/ |
PASCAL_RTN ComponentResult __SoundOutputGetInfo (SoundOutputGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr) |
{ |
SoundInfoListPtr listPtr; |
UnsignedFixed *lp; |
Handle infoHandle; |
ComponentResult result; |
short *sp; |
result = noErr; |
switch (selector) |
{ |
case siSampleSize: // return current sample size |
*((short *) infoPtr) = (**globals->prefs).sampleSize; |
break; |
case siSampleSizeAvailable: // return samples sizes available |
infoHandle = NewHandle(sizeof(short) * 2); // space for sample sizes |
FailWithAction(infoHandle == nil, result = MemError(), Exit); |
listPtr = (SoundInfoListPtr)infoPtr; |
listPtr->count = 2; // no. sample sizes in handle |
listPtr->infoHandle = infoHandle; // handle to be returned |
sp = (short *) *infoHandle; // store sample sizes in handle |
*sp++ = 8; |
*sp++ = 16; |
break; |
case siSampleRate: // return current sample rate |
*((Fixed *) infoPtr) = (**globals->prefs).sampleRate; |
break; |
case siSampleRateAvailable: // return sample rates available |
infoHandle = NewHandle(sizeof(UnsignedFixed) * 5); // space for sample rates |
FailWithAction(infoHandle == nil, result = MemError(), Exit); |
listPtr = (SoundInfoListPtr)infoPtr; |
listPtr->count = 5; // no. sample rates in handle |
listPtr->infoHandle = infoHandle; // handle to be returned |
// If the hardware supports a limited set of sample rates, then the list count |
// should be set to the number of sample rates and this list of rates should be |
// stored in the handle. |
lp = (UnsignedFixed *) *infoHandle; |
*lp++ = rate48khz; |
*lp++ = rate44khz; |
*lp++ = rate22050hz; |
*lp++ = rate11025hz; |
*lp++ = 0x1F400000; // 8kHz sample rate not defined |
break; |
case siNumberChannels: // return current no. channels |
*((short *) infoPtr) = (**globals->prefs).numChannels; |
break; |
case siChannelAvailable: // return channels available |
infoHandle = NewHandle(sizeof(short) * 2); // space for channels |
FailWithAction(infoHandle == nil, result = MemError(), Exit); |
listPtr = (SoundInfoListPtr)infoPtr; |
listPtr->count = 2; // no. channels in handle |
listPtr->infoHandle = infoHandle; // handle to be returned |
sp = (short *) *infoHandle; // store channels in handle |
*sp++ = 1; // mono |
*sp++ = 2; // stereo |
break; |
case siHardwareVolume: |
*((long *)infoPtr) = (**globals->prefs).volume; |
break; |
case siHardwareVolumeSteps: |
*((short *) infoPtr) = kHardwareVolumeSteps; |
break; |
case siHardwareMute: |
*((short *) infoPtr) = (**globals->prefs).muteState; |
break; |
case siHardwareBusy: |
*((short *)infoPtr) = globals->hardwareOn; |
break; |
// if you do not handle this selector, then delegate it up the chain |
default: |
result = SoundComponentGetInfo(globals->sourceComponent, sourceID, selector, infoPtr); |
break; |
} |
Exit: |
return (result); |
} |
/* SoundOutputSetInfo |
This routine sets information about this component. A 4-byte OSType selector is |
used to determine the type and size of the information to apply. If the component |
does not support a selector, it should delegate this call on up the component chain. |
NOTE: This can be called at interrupt time. |
*/ |
PASCAL_RTN ComponentResult __SoundOutputSetInfo (SoundOutputGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr) |
{ |
ComponentResult result; |
result = noErr; |
switch (selector) |
{ |
case siSampleSize: // set sample size |
switch ((short)infoPtr) |
{ |
case 8: // valid sample sizes |
(**globals->prefs).sampleSize = (short)infoPtr; |
globals->outputData.format = k8BitOffsetBinaryFormat; |
globals->prefsChanged = true; |
break; |
case 16: |
(**globals->prefs).sampleSize = (short)infoPtr; |
globals->outputData.format = k16BitBigEndianFormat; |
globals->prefsChanged = true; |
break; |
default: |
result = siInvalidSampleSize; |
} |
break; |
case siSampleRate: // set sample rate |
switch ((UnsignedFixed)infoPtr) |
{ |
case rate48khz: // valid sample rates |
case rate44khz: |
case rate22050hz: |
case rate11025hz: |
case 0x1F400000: |
(**globals->prefs).sampleRate = (UnsignedFixed)infoPtr; |
globals->prefsChanged = true; |
break; |
default: |
result = siInvalidSampleRate; |
} |
break; |
case siNumberChannels: // set no. channels |
if ( ((short)infoPtr == 1) || ((short)infoPtr == 2) ) |
{ |
(**globals->prefs).numChannels = (short)infoPtr; |
globals->prefsChanged = true; |
} |
else |
result = notEnoughHardware; |
break; |
case siHardwareVolume: |
// the volume is two 16 bit values combined into a long |
// the range is 0x0000 - 0x0100, where 0x0100 is the max volume level |
(**globals->prefs).volume = (long)infoPtr; |
globals->prefsChanged = true; |
break; |
case siHardwareMute: |
(**globals->prefs).muteState = (short)infoPtr; |
globals->prefsChanged = true; |
break; |
// if you do not handle this selector, then call up the chain |
default: |
result = SoundComponentSetInfo(globals->sourceComponent, sourceID, selector, infoPtr); |
break; |
} |
return (result); |
} |
/* StartSource |
This routine is used to start sounds playing that are currently paused. It should |
first delegate this call up the component chain so the rest of the chain can prepare |
to play this sound. Then, if the hardware is not already started it should be |
turned on. |
NOTE: This can be called at interrupt time. |
*/ |
PASCAL_RTN ComponentResult __SoundOutputStartSource (SoundOutputGlobalsPtr globals, short count, SoundSource *sources) |
{ |
ComponentResult result; |
// tell the mixer to start these sources |
result = SoundComponentStartSource(globals->sourceComponent, count, sources); |
FailIf(result != noErr, Exit); |
// make sure hardware interrupts are running |
StartHardware(globals); |
Exit: |
return (result); |
} |
/* PlaySourceBuffer |
This routine is used to specify a new sound to play and conditionally start |
the hardware playing that sound. It should first delegate this call up the component |
chain so the rest of the chain can prepare to play this sound. Then, if the |
hardware is not already started it should be turned on. |
NOTE: This can be called at interrupt time. */ |
PASCAL_RTN ComponentResult __SoundOutputPlaySourceBuffer (SoundOutputGlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb, long actions) |
{ |
ComponentResult result; |
// tell mixer to we'll start playing this new buffer |
result = SoundComponentPlaySourceBuffer(globals->sourceComponent, sourceID, pb, actions); |
FailIf(result != noErr, Exit); |
// if the kSourcePaused bit is set, then do not turn on your hardware just yet |
// (the assumption is that StartSource() will later be used to start this sound playing). |
// If this bit is not set, turn your hardware interrupts on. |
if ( !(actions & kSourcePaused) ) |
StartHardware(globals); |
Exit: |
return (result); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14