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.
VU-Meter.c
/* |
File: VU-Meter.c |
Contains: Sample pre-mixer sound component |
Written by: Mark Cookson |
Copyright: Copyright © 1996-1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source 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 source |
code, but that you've made changes. |
Change History (most recent first): |
8/20/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
#include <Memory.h> |
#include <Errors.h> |
#include <SoundInput.h> |
#include <Components.h> |
#include <Gestalt.h> |
#include <Sound.h> |
#include "VU-Meter.h" |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// Constants |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// Component Dispatcher |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
#define SoundComponentEntryPoint main |
#include "ComponentDispatch.c" |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// prototypes |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
static void VUMeterBuffer(SoundComponentGlobalsPtr globals, const Byte * const inputBuffer, long samples, short sampleSize, short numChannels); |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// Component Manager Methods |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
/* ============================================================================== |
Component Open |
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 components'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 specifed 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 in this call, since the Component Manager will |
call Open() BEFORE calling Register(). |
NOTE: This routine is never called at interrupt time. |
============================================================================== */ |
static pascal ComponentResult __SoundComponentOpen(void *unused1, ComponentInstance self) { |
#pragma unused (unused1) |
Handle h; |
SoundComponentGlobalsPtr globals; |
h = NewHandleClear(sizeof(SoundComponentGlobals)); // get space for globals |
if (h == nil) |
return(MemError()); |
HLock(h); |
globals = (SoundComponentGlobalsPtr) *h; |
SetComponentInstanceStorage (self, (Handle) globals); // save pointer to our globals |
globals->globalsHandle = h; // remember the handle |
return (noErr); |
} |
/* ============================================================================== |
Component Close |
This routine is called when the Component Manager is closing the instance of |
this component. 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 Open() 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. |
============================================================================== */ |
static pascal ComponentResult __SoundComponentClose(SoundComponentGlobalsPtr globals, ComponentInstance self) { |
#pragma unused (self) |
if (globals) { // we have some globals |
if (globals->sourceComponent) // we have a source component |
CloseComponent(globals->sourceComponent); // close it |
DisposeHandle(globals->globalsHandle); // dispose our storage |
} |
return (noErr); |
} |
/* ============================================================================== |
Component Register |
This routine is called once, usually at boot time, when the Component Manager |
is first registering this sound component. This routine should check to see if the proper |
Sound Manager is installed and return 0 if it is. If the right Sound Manager is not |
installed, the routine should return 1 and this component will not be registered. |
NOTE: The cmpWantsRegisterMessage bit must be set in the component flags of the |
sound component in order for this routine to be called. |
NOTE: This routine is never called at interrupt time. |
============================================================================== */ |
static pascal ComponentResult __SoundComponentRegister(SoundComponentGlobalsPtr globals) |
{ |
#pragma unused (globals) |
NumVersionVariant version; |
version.parts = SndSoundManagerVersion(); // get the Sound Manager version |
if (version.whole < 0x03210000) // it's what we need |
return (1); // do not install component |
else |
return (0); // install this component |
} |
/* ============================================================================== |
Component GetInfo |
This is called when a program issues a SndGetInfo() call. If we see our selector |
we return the peak value of the last buffer, if it's anything else we forward |
the selector to our source component. |
============================================================================== */ |
static pascal ComponentResult __SoundComponentGetInfo(SoundComponentGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr) |
{ |
ComponentResult result = noErr; |
switch (selector) { |
case kVUSelectorSubType: |
((short*)infoPtr)[0] = globals->peakSampleLeft; |
((short*)infoPtr)[1] = globals->peakSampleRight; |
break; |
default: |
result = SoundComponentGetInfo(globals->sourceComponent, sourceID, selector, infoPtr); |
break; |
} |
return (result); |
} |
/* ============================================================================== |
StopSource |
This routine is used to stop sounds that are currently playing. It should |
clear out any internal buffers, reset any compression state information |
and then delegate the call up the chain. We have no state so we just send |
the call up the chain. |
NOTE: This can be called at interrupt time. |
============================================================================== */ |
static pascal ComponentResult __SoundComponentStopSource(SoundComponentGlobalsPtr globals, short count, SoundSource *sources) { |
// values are no longer valid |
globals->peakSampleLeft = 0; |
globals->peakSampleRight = 0; |
// delegate this call |
return (SoundComponentStopSource(globals->sourceComponent, count, sources)); |
} |
/* ============================================================================== |
PlaySourceBuffer |
This routine is used to start a new sound playing. It should clear out any internal buffers |
but should NOT reset any compression state information, since this could be a |
continuation of a sound that has been broken into pieces. Then the call should be |
delegated up the chain. We have no state so we just send the call up the chain. |
NOTE: This can be called at interrupt time. |
============================================================================== */ |
static pascal ComponentResult __SoundComponentPlaySourceBuffer(SoundComponentGlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb, long actions) { |
// values are no longer valid, starting a new sound |
globals->peakSampleLeft = 0; |
globals->peakSampleRight = 0; |
// delegate this call |
return (SoundComponentPlaySourceBuffer(globals->sourceComponent, sourceID, pb, actions)); |
} |
/* ============================================================================== |
SetSource |
This routine sets the component we should call to get more data. We must remember |
this component. |
============================================================================== */ |
static pascal ComponentResult __SoundComponentSetSource(SoundComponentGlobalsPtr globals, SoundSource sourceID, ComponentInstance source) { |
#pragma unused (sourceID) |
// remember our source component |
globals->sourceComponent = source; |
return (noErr); |
} |
/* ============================================================================== |
GetSource |
This routine returns the component we call to get more data. |
============================================================================== */ |
static pascal ComponentResult __SoundComponentGetSource(SoundComponentGlobalsPtr globals, SoundSource sourceID, ComponentInstance *source) { |
#pragma unused (sourceID) |
*source = globals->sourceComponent; |
return (noErr); |
} |
/* ============================================================================== |
SetOutput |
This routine sets the data format our component should output. If we can't output |
the requested format, we should return a pointer to the format we do support, |
and return an error, and the Sound Manager will attempt the conversion for us. |
For this component, because we don't modify the data there is no problem |
outputting any format. |
============================================================================== */ |
static pascal ComponentResult __SoundComponentSetOutput(SoundComponentGlobalsPtr globals, SoundComponentDataPtr requested, SoundComponentDataPtr *actual) { |
#pragma unused (actual, globals, requested) |
// no problem outputting anything because we only "look" at the data |
return (noErr); |
} |
/* ============================================================================== |
GetSourceData |
This routine is called when the Sound Manager wants your component to process |
some more data. |
NOTE: This will most often be called at interrupt time. |
============================================================================== */ |
static pascal ComponentResult __SoundComponentGetSourceData(SoundComponentGlobalsPtr globals, SoundComponentDataPtr *resultDataPtr) { |
#pragma unused (resultDataPtr) |
ComponentResult result; |
// Get some sound data to look at |
result = SoundComponentGetSourceData(globals->sourceComponent, resultDataPtr); |
// Sample the buffer to get VU meter data |
VUMeterBuffer(globals, (*resultDataPtr)->buffer, (*resultDataPtr)->sampleCount, (*resultDataPtr)->sampleSize, (*resultDataPtr)->numChannels); |
return result; |
} |
/* ============================================================================== |
VUMeterBuffer |
This implements a simple peak meter. |
This code works on the assumtion that the average of a sound wave is 0 (for |
each positive sample there is a coresponding negative sample), therefore we |
only have to find the largest positive sample in the buffer because that will |
be very close to the absolute value of the largest negative sample. |
============================================================================== */ |
static void VUMeterBuffer(SoundComponentGlobalsPtr globals, const Byte * const inputBuffer, long samples, short sampleSize, short numChannels) { |
int i; |
short *shortBuffer; |
Byte *charBuffer; |
shortBuffer = (short*)inputBuffer; |
charBuffer = (Byte*)inputBuffer; |
globals->peakSampleLeft = 0; |
globals->peakSampleRight = 0; |
if (sampleSize == 8) { |
if (numChannels == 1) { |
for (i = 0; i < samples; i++) { |
if (*charBuffer > globals->peakSampleLeft) |
globals->peakSampleLeft = *charBuffer; |
charBuffer++; |
} |
} else if (numChannels == 2) { |
for (i = 0; i < samples * 2; i++) { |
if (*charBuffer > globals->peakSampleLeft) |
globals->peakSampleLeft = *charBuffer; |
charBuffer++; |
if (*charBuffer > globals->peakSampleRight) |
globals->peakSampleRight = *charBuffer; |
charBuffer++; |
} |
} |
} else if (sampleSize == 16) { |
if (numChannels == 1) { |
for (i = 0; i < samples; i++) { |
if (*shortBuffer > globals->peakSampleLeft) |
globals->peakSampleLeft = *shortBuffer; |
shortBuffer++; |
} |
} else if (numChannels == 2) { |
for (i = 0; i < samples * 2; i++) { |
if (*shortBuffer > globals->peakSampleLeft) |
globals->peakSampleLeft = *shortBuffer; |
shortBuffer++; |
if (*shortBuffer > globals->peakSampleRight) |
globals->peakSampleRight = *shortBuffer; |
shortBuffer++; |
} |
} |
} |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-03-14