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.
SoundLevel.c
/* |
File: SoundLevel.c |
Contains: A simple application that echos the sound input level on the microphone using |
a display much like a VCR peak-hold bar meter. |
Its a fun app, to use up those wasted background cycles and seems to amuse |
people to no end. At 40k, its not a major memory hog so pop it in your Startup |
Items folder and enjoy. |
The About Box introduces a new power user short cut. Clap hard enough to raise |
the sound threshold about Å 95% and the dialog will dismiss. No need to reach |
and strain for that enter key anymore! |
Caveats: |
¥ Not much error checking. |
¥ Too many globals. |
¥ Saves the window position back to the app resource fork. |
¥ Requires a Sound Input Device (MacRecorder will do just fine) |
¥ It keeps the sound input device open with r/w so you wonÕt be able |
to use other sound input apps unless you quit. |
Have fun and let me know what you think. |
Written by: Ken Bereskin |
Copyright: Copyright © 1992-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/2/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
/* --------------------------------------------------------------------------------- |
ToolBox header files |
*/ |
#include <Types.h> |
#include <QuickDraw.h> |
#include <Resources.h> |
#include <Windows.h> |
#include <Fonts.h> |
#include <Events.h> |
#include <TextEdit.h> |
#include <Dialogs.h> |
#include <Menus.h> |
#include <Memory.h> |
#include <Desk.h> |
#include <ToolUtils.h> |
#include <OSUtils.h> |
#include <Errors.h> |
#include <Folders.h> |
#include <OSEvents.h> |
#include <SegLoad.h> |
#include <Traps.h> |
#include <SoundInput.h> |
#include <GestaltEqu.h> |
/* --------------------------------------------------------------------------------- |
Forward reference prototypes |
*/ |
void Initialize(void); |
void SetupEnvironment(void); |
void SetupMemory(void); |
void SetupMenus(void); |
void DoEvents(void); |
void DoMouseDown (EventRecord *event); |
void DoKeyDown (EventRecord *event); |
void DoUpdateWindow (WindowPtr window); |
void DoActivateWindow (Boolean activate, WindowPtr window); |
void DoMenuCommand(short theMenu, short theItem); |
void DoContentClick(EventRecord *event, WindowPtr window); |
void DoSuspend(EventRecord *event); |
void DoResume(EventRecord *event); |
void DoIdle(void); |
void DoDeskAccessory(short whichMenu, short whichItem); |
void NewLevelWindow(void); |
void CleanUp(void); |
void DoAbout(void); |
Boolean OptionTest(void); |
Boolean CmdTest(void); |
Boolean ShiftTest(void); |
Boolean ColourQDExists (void); |
Boolean ColourQDIsOn (void); |
short ColourDepth (void); |
void TwitchToFinder(void); |
void DrawTheMeter(Rect *meterRect, short numElements, short *peakLevel, short *lastLevel, long *peakTimeout, short meterLevel, short redZone, RGBColor *blueRGB, RGBColor *redRGB, RGBColor *blackRGB, Boolean useColour); |
void GetBarRect(Rect *meterRect, short whichBar, Rect *barRect); |
void InitLevelMeter (void); |
void CloseLevelMeter (void); |
OSErr OpenTheSoundDevice(void); |
void DrawOneBar(Rect *meterRect, short whichBar, Boolean useColour); |
pascal Boolean AboutFilterProc(DialogPtr dialog, EventRecord *event, short *item); |
void HiliteItem(DialogPtr dialog, short theitem); |
void UpdateAboutDialog(WindowPtr window); |
void MakeCursor(short cursorID); |
/* --------------------------------------------------------------------------------- |
Constants |
*/ |
#define kMaxVolume 255 |
#define kPeakTimeoutTicks 60 // number of ticks needed to timeout peak |
#define kMenuBarID 128 |
#define kAppleMenu 128 |
#define kAboutItem 1 |
#define kFileMenu 129 |
#define kQuitItem 1 |
#define kGestaltLevelMeter 'SnLv' |
/* --------------------------------------------------------------------------------- |
Global variables |
*/ |
Boolean WNEIsImplemented; // is WaitNextEvent avaiable |
Boolean gQuitFlag = false; // set to force a quit |
WindowPtr gLevelWindow; // sound level window pointer |
RGBColor blueRGB, redRGB, blackRGB; // rgb colours used for drawing meter |
short gNumElements; // number of elements in the meter |
long gPeakTimeOut; |
short gPeakLevel; |
short gLastLevel; |
short gRedZone; |
Rect gMeterRect; |
long gSoundInputRefNum; |
Boolean gUseColour; |
#ifdef powerc |
QDGlobals qd; |
#endif |
/* --------------------------------------------------------------------------------- |
Main |
Application main entry point |
*/ |
void main() |
{ |
Initialize(); // do application initialization |
NewLevelWindow(); // create the level window |
InitLevelMeter(); // start up the level meter |
DoUpdateWindow(gLevelWindow); |
while (!gQuitFlag) // handle events until quit |
DoEvents(); |
CleanUp(); // clean up before quitting |
} |
/* --------------------------------------------------------------------------------- |
Intialize |
First time application initialization |
*/ |
#pragma segment Initialize |
static void |
Initialize() |
{ |
InitGraf(&qd.thePort); |
InitFonts(); |
InitWindows(); |
InitMenus(); |
TEInit(); |
InitDialogs(nil); |
InitCursor(); |
FlushEvents(everyEvent, 0); |
SetupMemory(); |
SetupEnvironment(); |
SetupMenus(); |
gUseColour = ColourDepth() >= 4; |
} |
/* --------------------------------------------------------------------------------- |
SetupEnvironment |
This routine will determine the hardware and software operating environment |
*/ |
#define RequestedVersion 1 |
static void |
SetupEnvironment() |
{ |
WNEIsImplemented = true; |
} |
/* |
--- SetupMemory ------------------------------------------------------------------------------------ |
This routine will allocate some master pointers, grow the heap, |
and set up a grow zone function. |
*/ |
static void |
SetupMemory() |
{ |
MaxApplZone(); |
MoreMasters(); |
MoreMasters(); |
} |
/* |
--- SetupMenus ------------------------------------------------------------------------------------ |
This routine will set up the LeadSheet menus. |
*/ |
static void |
SetupMenus() |
{ |
Handle menuBarHndl; |
// load up the apple and file menu |
menuBarHndl = GetNewMBar(kMenuBarID); // read menus into menu bar |
SetMenuBar(menuBarHndl); // install menus |
DisposeHandle(menuBarHndl); |
AppendResMenu(GetMenuHandle(kAppleMenu), 'DRVR'); // add DA names to Apple menu |
DrawMenuBar(); |
} |
#pragma segment Main |
/* --------------------------------------------------------------------------------- |
DoEvents |
This is the LaunchPad Main Event Loop. We fetch events from the event queue, |
and call the proper handlers for the given event class. |
*/ |
#define kSleepTicks 0 |
static void |
DoEvents() |
{ |
Boolean newEvent; // is there an event in the queue |
EventRecord event; // the event record |
/* |
Look for an event in the event queue. |
*/ |
if (WNEIsImplemented) { |
newEvent = WaitNextEvent(everyEvent, &event, kSleepTicks, nil); |
} else { |
SystemTask(); |
newEvent = GetNextEvent(everyEvent, &event); |
} |
DoIdle(); // idle processing |
/* Dispatch on the type of event */ |
switch(event.what) { |
case mouseDown: |
DoMouseDown(&event); |
break; |
case keyDown: |
case autoKey: |
DoKeyDown(&event); |
break; |
case updateEvt: |
DoUpdateWindow((WindowPtr)event.message); |
break; |
case activateEvt: |
DoActivateWindow(event.modifiers & activeFlag, (WindowPtr)event.message); |
break; |
case diskEvt: |
break; |
case osEvt: |
/* |
Check for suspend or resume events (the only ones now defined) |
*/ |
if ((event.message >> 24) == suspendResumeMessage) { |
if (event.message & resumeFlag) |
DoResume(&event); |
else |
DoSuspend(&event); |
} |
break; |
case highLevelEventMask: |
break; |
} |
DoIdle(); // more idle processing |
} |
/* --- DoMouseDown ---------------------------------------------------------------------------------- |
This routine handles mouseDown events. |
*/ |
static void |
DoMouseDown(event) |
EventRecord *event; |
{ |
short whichPart; |
WindowPtr window; |
Rect dragBounds; |
long selection; |
short theMenu, theItem; |
/* |
Determine where the mouseDown event occurred |
*/ |
whichPart = FindWindow(event->where, &window); |
switch(whichPart) { |
case inMenuBar: |
selection = MenuSelect(event->where); |
theMenu = HiWord(selection); |
theItem = LoWord(selection); |
DoMenuCommand(theMenu, theItem); |
break; |
case inSysWindow: |
SystemClick(event, window); |
break; |
case inContent: |
DoContentClick(event, window); |
break; |
case inDrag: |
dragBounds = qd.screenBits.bounds; |
DragWindow(window, event->where, &dragBounds); |
break; |
case inGrow: |
break; |
case inGoAway: |
break; |
case inZoomIn: |
case inZoomOut: |
break; |
case inDesk: // a click in the desktop will deselect the current selection |
break; |
} |
} |
/* --------------------------------------------------------------------------------- |
DoKeyDown |
This routine handles keyDown events. |
*/ |
static void |
DoKeyDown (EventRecord *event) |
{ |
#pragma unused(event) |
gQuitFlag = true; |
} |
/* --------------------------------------------------------------------------------- |
DoUpdateWindow |
This routine handles update events for the given window |
*/ |
static void |
DoUpdateWindow (window) |
WindowPtr window; |
{ |
GrafPtr savePort; |
Rect boundsRect; |
short i; |
GetPort(&savePort); |
BeginUpdate(window); // set up the clipRgn and visRgn |
SetPort(window); |
RGBForeColor(&blackRGB); |
EraseRect(&window->portRect); |
boundsRect = window->portRect; |
InsetRect(&boundsRect, 2, 2); |
PaintRect(&boundsRect); |
// redraw the bar graph frames up to the current level |
for (i = 1; i <= gLastLevel; i++) { |
DrawOneBar(&gMeterRect, i, gUseColour); |
} |
EndUpdate(window); // restore window regions |
SetPort(savePort); |
} |
/* --- DoActivateWindow ---------------------------------------------------------------------------------- |
This routine handles activate events for the given window |
*/ |
static void |
DoActivateWindow (Boolean activate,WindowPtr window) |
{ |
#pragma unused(activate, window) |
} |
/* --------------------------------------------------------------------------------- |
DoMenuCommand |
This routine handles menu selection dispatching |
*/ |
static void |
DoMenuCommand(short theMenu, short theItem) |
{ |
switch (theMenu) { |
case kAppleMenu: |
if (theItem == kAboutItem) { |
DoAbout(); |
} else { |
DoDeskAccessory(theMenu, theItem); |
} |
break; |
case kFileMenu: |
if (theItem == kQuitItem) { |
ExitToShell(); |
} |
break; |
default: |
break; |
} |
HiliteMenu(0); |
} |
/* --- DoContentClick ------------------------------------------------------------------------------ |
This routine handles clicks in the content area of a window. If the option key is down, |
drag the launch window to another location on the screen. |
*/ |
static void |
DoContentClick(event, window) |
EventRecord *event; |
WindowPtr window; |
{ |
#pragma unused(event) |
GrafPtr savePort; |
Point clickPt; |
/* |
If the clicked window isn't frontmost, simply select it |
*/ |
if (window != FrontWindow()) { |
SelectWindow(window); |
return; |
} |
GetPort(&savePort); // save the current port |
SetPort(window); // set the port to the clicked window |
clickPt = event->where; // convert point to local coordindates |
GlobalToLocal(&clickPt); |
DragWindow(window, event->where, &qd.screenBits.bounds); |
SetPort(savePort); // restore the saved port |
} |
/* --------------------------------------------------------------------------------- |
DoSuspend |
This routine handles multifinder suspend events. |
*/ |
static void |
DoSuspend(event) |
EventRecord *event; |
{ |
#pragma unused(event) |
DoActivateWindow(false, gLevelWindow); |
} |
/* --------------------------------------------------------------------------------- |
DoResume |
This routine handles multifinder resume events. |
*/ |
static void |
DoResume(event) |
EventRecord *event; |
{ |
#pragma unused(event) |
InitCursor(); |
DoActivateWindow(true, gLevelWindow); |
} |
/* --------------------------------------------------------------------------------- |
DoDeskAccessory |
Handle desk accessory selections from the apple menu |
*/ |
static void |
DoDeskAccessory(short whichMenu,short whichItem) |
{ |
Str255 daName; |
GetMenuItemText(GetMenuHandle(whichMenu), whichItem, daName); |
(void) OpenDeskAcc(daName); |
} |
/* --------------------------------------------------------------------------------- |
NewLevelWindow |
This routine will create the sound level window. |
*/ |
static void |
NewLevelWindow() |
{ |
Point **userOffsetPtHndl; |
Rect screenRect; |
Rect globalRect; |
if (ColourQDExists()) |
gLevelWindow = GetNewCWindow(128, nil, (WindowPtr)-1); |
else |
gLevelWindow = GetNewWindow(128, nil, (WindowPtr)-1); |
SetPort(gLevelWindow); |
// load the offset resource that tells us if the user has dragged |
// the window somewhere else. make sure the window remains visible |
// and eventually put this in a pref file where it belongs |
userOffsetPtHndl = (Point **)GetResource('WPos', 128); |
if (userOffsetPtHndl != nil) { |
screenRect = qd.screenBits.bounds; |
InsetRect(&screenRect, 5, 5); // a little bit of a margin |
// the port is set for our window, so convert (0,0) to global for the |
// global topLeft of the window |
globalRect.left = (**userOffsetPtHndl).h; |
globalRect.top = (**userOffsetPtHndl).v; |
globalRect.right = globalRect.left + (gLevelWindow->portRect.right - gLevelWindow->portRect.left); |
globalRect.bottom = globalRect.top + (gLevelWindow->portRect.bottom - gLevelWindow->portRect.top); |
if (SectRect(&screenRect, &globalRect, &globalRect)) { |
MoveWindow(gLevelWindow, globalRect.left, globalRect.top, true); |
} |
ReleaseResource((Handle)userOffsetPtHndl); |
} |
ShowWindow(gLevelWindow); // make it visible |
} |
/* --------------------------------------------------------------------------------- |
CleanUp |
This routine will clean up before exiting to the Finder |
*/ |
static void |
CleanUp() |
{ |
Point **userOffsetPtHndl; |
Point globalPt; |
CloseLevelMeter(); // close up the level meter |
// save the position of the top/left corner of the window in case the user |
// has moved it. For now, back to the WPos resource in the app will have to do |
userOffsetPtHndl = (Point **)GetResource('WPos', 128); |
if (userOffsetPtHndl != nil) { |
SetPort(gLevelWindow); |
globalPt.h = globalPt.v = 0; |
LocalToGlobal(&globalPt); |
**userOffsetPtHndl = globalPt; |
ChangedResource((Handle)userOffsetPtHndl); |
WriteResource((Handle)userOffsetPtHndl); |
} |
if (gLevelWindow != nil) |
DisposeWindow(gLevelWindow); |
} |
/* --------------------------------------------------------------------------------- |
MakeCursor |
This routine will set the cursor to the 'CURS' resource whose id is given. |
*/ |
void |
MakeCursor(short cursorID) |
{ |
#pragma unused(cursorID) |
} |
/* --------------------------------------------------------------------------------- |
DoIdle |
*/ |
static void |
DoIdle() |
{ |
OSErr err; |
short recordingStatus = 0; // status of recording session |
short meterLevel = 0; // current meter level |
unsigned long totalSamplesToRecord = 0; // total number of samples |
unsigned long numberOfSamplesRecorded = 0; // number of samples recorded |
unsigned long totalMsecsToRecord; |
unsigned long numberOfMsecsRecorded; |
GrafPtr savePort; |
GetPort(&savePort); |
SetPort(gLevelWindow); |
/* get the sound input status */ |
err = SPBGetRecordingStatus(gSoundInputRefNum, |
&recordingStatus, |
&meterLevel, |
&totalSamplesToRecord, |
&numberOfSamplesRecorded, |
&totalMsecsToRecord, |
&numberOfMsecsRecorded); |
DrawTheMeter(&gMeterRect, |
gNumElements, |
&gPeakLevel, |
&gLastLevel, |
&gPeakTimeOut, |
meterLevel, |
gRedZone, |
&blueRGB, |
&redRGB, |
&blackRGB, |
gUseColour); |
SetPort(savePort); |
} |
/* --------------------------------------------------------------------------------- |
OptionTest |
Returns whether the option key is being pressed |
*/ |
static Boolean |
OptionTest() |
{ |
KeyMap theKeys; |
GetKeys(theKeys); |
if (theKeys[1] & 4) |
return(true); |
else |
return(false); |
} |
/* --------------------------------------------------------------------------------- |
CmdTest |
Returns whether the command (apple) key is being pressed |
*/ |
static Boolean |
CmdTest() |
{ |
KeyMap theKeys; |
GetKeys(theKeys); |
if (theKeys[1] & 0x8000) |
return(true); |
else |
return(false); |
} |
/* --------------------------------------------------------------------------------- |
ShiftTest |
Returns whether the shift key is being pressed |
*/ |
static Boolean |
ShiftTest() |
{ |
KeyMap theKeys; |
GetKeys(theKeys); |
if (theKeys[1] & 1) |
return(true); |
else |
return(false); |
} |
#define ROM85 (*(short *)0x28e) |
#define TWOHIGHMASK 0xc000 |
#define COLOURQDEXISTS !(ROM85 & TWOHIGHMASK) |
/* --------------------------------------------------------------------------------- |
ColourQDExists |
This routine is called to determine whether or not color quickdraw |
exists in the current roms. It checks the lom memory global just |
like Apple usually doesÉ |
*/ |
Boolean |
ColourQDExists() |
{ |
return(COLOURQDEXISTS); |
} |
/* --------------------------------------------------------------------------------- |
ColourQDIsOn |
This routine is called to determine if colour quickdraw exists |
in the current roms and if the main screen is displaying color. |
*/ |
#define QD_MIN_FOR_COLOUR 4 |
Boolean |
ColourQDIsOn() |
{ |
GDHandle maindevice; |
PixMapHandle mainpix; |
short depth; |
/* |
First confirm that color is available in the roms |
*/ |
if (COLOURQDEXISTS) { |
/* |
Determine if the main device is displaying color |
*/ |
maindevice = GetMainDevice(); |
mainpix = (*maindevice)->gdPMap; |
/* |
Check the depth of the pixel map |
*/ |
depth = (*mainpix)->pixelSize; |
if (depth < QD_MIN_FOR_COLOUR) |
return(false); |
else |
return(true); |
} else |
return(false); |
} |
short |
ColourDepth() |
{ |
GDHandle maindevice; |
PixMapHandle mainpix; |
/* |
First confirm that color is available in the roms |
*/ |
if (COLOURQDEXISTS) { |
/* |
Determine if the main device is displaying color |
*/ |
maindevice = GetMainDevice(); |
mainpix = (*maindevice)->gdPMap; |
/* |
Check the depth of the pixel map |
*/ |
return (*mainpix)->pixelSize; |
} else |
return 1; |
} |
/* --------------------------------------------------------------------------------- |
TwitchToFinder |
Switch back to Finder. Use the System 7.0 process manager if present |
*/ |
static void |
TwitchToFinder() |
{ |
} |
/* --------------------------------------------------------------------------------- |
DrawTheMeter |
This routine is called to draw the volume meter during sound input. |
*/ |
void |
DrawTheMeter(Rect *meterRect, short numElements, short *peakLevel, short *lastLevel, long *peakTimeout, short meterLevel, short redZone, RGBColor *blueRGB, RGBColor *redRGB, RGBColor *blackRGB, Boolean useColour) |
{ |
Rect barRect; |
short i; |
PenNormal(); |
// quantize the meterLevel based on number of elements |
// in our bar chart |
if (meterLevel > kMaxVolume) |
meterLevel = kMaxVolume; |
meterLevel = (meterLevel * numElements) / kMaxVolume; |
// determine if this is a new peak value and erase the |
// peak bar if it has timed out |
if (meterLevel >= *peakLevel) { |
*peakLevel = meterLevel; |
*peakTimeout = TickCount() + kPeakTimeoutTicks; |
} else { |
// has the current peak timed out? |
if (*peakLevel > 0 && *peakTimeout <= TickCount()) { |
if (*peakLevel >= *lastLevel) { |
if (useColour) |
RGBForeColor(blackRGB); |
GetBarRect(meterRect, *peakLevel, &barRect); |
PaintRect(&barRect); |
// erase the previous element (peak is two elements wide) |
if (*peakLevel > 1) { // donÕt erase a non-element |
GetBarRect(meterRect, *peakLevel - 1, &barRect); |
PaintRect(&barRect); |
} |
} |
*peakLevel = 0; |
} |
} |
// check whether the signal is now stronger than last time |
if (meterLevel > *lastLevel) { |
for (i = *lastLevel + 1; i <= meterLevel; i++) { |
GetBarRect(meterRect, i, &barRect); // get the rect for this bar |
if (useColour) { // watch out for colour |
if (i >= redZone) // are we beyond the clipping point? |
RGBForeColor(redRGB); // draw in red to show distortion |
else |
RGBForeColor(blueRGB); // draw in blue for normal signal |
PaintRect(&barRect); // fill the element in |
} else |
EraseRect(&barRect); // no colour so white will have to do |
} |
*lastLevel = meterLevel; // remember for next time |
} |
// check whether the signal level is now weaker than last time |
// and if it is remove the rightmost two level bar currently on |
if (meterLevel < *lastLevel) { |
if (*peakLevel != 0) { |
if (*lastLevel == *peakLevel) // donÕt erase the peak! |
*lastLevel -= 2; |
if (*lastLevel == *peakLevel -1) |
*lastLevel -= 1; |
} |
if (useColour) |
RGBForeColor(blackRGB); |
for (i = 0; i < 2; i++) { |
if (*lastLevel > 0) { |
GetBarRect(meterRect, *lastLevel, &barRect); |
PaintRect(&barRect); |
*lastLevel -= 1; |
} |
if (*lastLevel < 0 ) |
*lastLevel = 0; |
} |
} |
if (useColour) |
RGBForeColor(blackRGB); |
} |
/* --------------------------------------------------------------------------------- |
GetBarRect |
*/ |
void |
GetBarRect(Rect *meterRect, short whichBar, Rect *barRect) |
{ |
if (whichBar == 0) { |
return; |
} |
SetRect(barRect, meterRect->left + 2, |
meterRect->top + 1, |
meterRect->left + 4, |
meterRect->bottom - 1); |
OffsetRect(barRect, (whichBar - 1)* 3, 0); |
} |
/* ------------------------------------------------------------------------------- |
InitLevelMeter |
*/ |
void |
InitLevelMeter() |
{ |
RGBColor **colorRsrcHndl; |
OSErr err; |
// load the colours used for the bar chart |
colorRsrcHndl = (RGBColor **)GetResource(128, 'RGBv'); |
if (colorRsrcHndl) { |
blueRGB = **colorRsrcHndl; |
ReleaseResource((Handle)colorRsrcHndl); |
} else { |
blueRGB.red = 39321; // flourescant blue |
blueRGB.green = 65535; |
blueRGB.blue = 65535; |
} |
colorRsrcHndl = (RGBColor **)GetResource(129, 'RGBv'); |
if (colorRsrcHndl) { |
redRGB = **colorRsrcHndl; |
ReleaseResource((Handle)colorRsrcHndl); |
} else { |
redRGB.red = 65535; // peak level red |
redRGB.green = 0; |
redRGB.blue = 0; |
} |
blackRGB.red = 0; |
blackRGB.green = 0; |
blackRGB.blue = 0; |
gMeterRect = gLevelWindow->portRect; |
InsetRect(&gMeterRect, 2, 2); |
gNumElements = (gMeterRect.right - gMeterRect.left - 2) / 3; |
if (gNumElements > 255) |
gNumElements = 255; |
gPeakTimeOut = 0; |
gPeakLevel = 0; |
gLastLevel = 0; |
gRedZone = (gNumElements * 2) / 3; |
// open the sound input device |
err = OpenTheSoundDevice(); |
} |
// watch out for other usages of the soundInputRefNum |
static void |
CloseLevelMeter() |
{ |
OSErr err; |
err = SPBCloseDevice(gSoundInputRefNum); |
gSoundInputRefNum = 0; |
} |
#define kAboutDLOG 128 |
#define kAboutMeterItem 4 |
short gAboutPeakLevel; |
short gAboutLastLevel; |
long gAboutPeakTimeOut; |
void |
DoAbout() |
{ |
DialogPtr dialog; |
short itemHit; |
GrafPtr savePort; |
ModalFilterUPP aboutFilterProcUPP; |
/* create a UPP for our modal dialog filter */ |
aboutFilterProcUPP = NewModalFilterProc(AboutFilterProc); |
GetPort(&savePort); |
dialog = GetNewDialog(kAboutDLOG, nil, (WindowPtr)-1); |
SetPort(dialog); |
// ¥¥¥ force an update event first |
UpdateAboutDialog((WindowPtr)dialog); |
gAboutPeakLevel = 0; |
gAboutLastLevel = 0; |
gAboutPeakTimeOut = 0; |
do { |
ModalDialog(aboutFilterProcUPP, &itemHit); |
} while (itemHit != ok); |
SetPort(savePort); |
DisposeDialog(dialog); |
DisposeRoutineDescriptor(aboutFilterProcUPP); |
} |
OSErr OpenTheSoundDevice() |
{ |
OSErr err; |
short meterState; |
// set the default zone to the system heap so that the SI manager will |
// possibly get fooled into allocating there instead of my heap |
SetZone(SystemZone()); |
err = SPBOpenDevice(nil, siWritePermission, &gSoundInputRefNum); |
if (err != noErr) |
goto BailOut; |
// turn on sound metering |
meterState = 1; // turn it on |
err = SPBSetDeviceInfo(gSoundInputRefNum, siLevelMeterOnOff, (char *)&meterState); |
BailOut: |
SetZone(ApplicationZone()); |
return err; |
} |
void |
DrawOneBar(Rect *meterRect, short whichBar, Boolean useColour) |
{ |
Rect barRect; |
GetBarRect(meterRect, whichBar, &barRect); // get the rect for this bar |
if (useColour) { // watch out for colour |
if (whichBar >= gRedZone) // are we beyond the clipping point? |
RGBForeColor(&redRGB); // draw in red to show distortion |
else |
RGBForeColor(&blueRGB); // draw in blue for normal signal |
PaintRect(&barRect); // fill the element in |
} else |
EraseRect(&barRect); // no colour so white will have to do |
} |
/* --------------------------------------------------------------------------------- |
AboutFilterProc |
*/ |
#define CRKey 0x0d |
#define EnterKey 0x03 |
#define EscapeKey 0x1b |
pascal Boolean |
AboutFilterProc(DialogPtr dialog, EventRecord *event, short *item) |
{ |
unsigned char theKey; |
WindowPtr window; |
short itemType; |
Handle itemHndl; |
Rect itemRect; |
OSErr err; |
short recordingStatus = 0; // status of recording session |
short meterLevel = 0; // current meter level |
unsigned long totalSamplesToRecord = 0; // total number of samples |
unsigned long numberOfSamplesRecorded = 0; // number of samples recorded |
unsigned long totalMsecsToRecord; |
unsigned long numberOfMsecsRecorded; |
/* get the sound input status */ |
if (event->what != updateEvt) { |
err = SPBGetRecordingStatus(gSoundInputRefNum, |
&recordingStatus, |
&meterLevel, |
&totalSamplesToRecord, |
&numberOfSamplesRecorded, |
&totalMsecsToRecord, |
&numberOfMsecsRecorded); |
GetDialogItem(dialog, kAboutMeterItem, &itemType, &itemHndl, &itemRect); |
InsetRect(&itemRect, 2, 2); |
SetPort(dialog); |
DrawTheMeter(&itemRect, |
gNumElements, |
&gAboutPeakLevel, |
&gAboutLastLevel, |
&gAboutPeakTimeOut, |
meterLevel, |
gRedZone, |
&blueRGB, |
&redRGB, |
&blackRGB, |
gUseColour); |
// check for nearly maximum volume to dismiss dialog; |
if (meterLevel > 250) { |
HiliteItem(dialog, ok); |
*item = ok; |
return true; |
} |
} |
switch(event->what) { |
case mouseDown: |
break; |
case keyDown: |
case autoKey: |
theKey = event->message & charCodeMask; |
if (theKey == CRKey || theKey == EnterKey || theKey == EscapeKey) { |
HiliteItem(dialog, ok); |
*item = ok; |
return true; |
} |
break; |
case updateEvt: |
window = (WindowPtr)event->message; |
if (window == (WindowPtr)dialog) { |
UpdateAboutDialog(window); |
} |
break; |
default: |
break; |
} |
return false; |
} |
void |
UpdateAboutDialog(WindowPtr window) |
{ |
GrafPtr savePort; |
short itemType; |
Handle itemHndl; |
Rect itemRect; |
BeginUpdate(window); |
GetPort(&savePort); |
SetPort(window); |
UpdateDialog((DialogPtr)window, window->visRgn); |
PenNormal(); |
GetDialogItem((DialogPtr)window, kAboutMeterItem, &itemType, &itemHndl, &itemRect); |
FrameRect(&itemRect); |
InsetRect(&itemRect, 2, 2); |
PaintRect(&itemRect); |
SetPort(savePort); |
EndUpdate(window); |
} |
/* --------------------------------------------------------------------------------- |
HiliteItem |
This routine will hilite the given item control so the user knows |
which item has been activated by the key equivalent |
*/ |
#define ButtonInvertState 10 // inverted state for push buttons |
#define CheckRadioState 11 // inverted state for checks and radios |
#define NormalState 0 // non-inverted state |
#define SleepDuration 10 // pause for 10 ticks |
void |
HiliteItem(DialogPtr dialog, short theitem) |
{ |
Handle thehndl; |
short thetype; |
Rect thebox; |
unsigned long junkTicks; |
short hiliteState; |
/* |
Get Handle to the item |
*/ |
GetDialogItem(dialog, theitem, &thetype, &thehndl, &thebox); |
switch (thetype) { |
case btnCtrl + ctrlItem: |
hiliteState = ButtonInvertState; |
break; |
case chkCtrl + ctrlItem: |
case radCtrl + ctrlItem: |
hiliteState = CheckRadioState; |
break; |
default: |
return; |
break; |
} |
/* |
Invert the control for a little while |
*/ |
HiliteControl((ControlHandle)thehndl, hiliteState); |
Delay(SleepDuration, &junkTicks); |
HiliteControl((ControlHandle)thehndl, NormalState); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-03-14