TweetFollow Us on Twitter

Feb 99 Getting Started

Volume Number: 15 (1999)
Issue Number: 2
Column Tag: Getting Started

Sound Basics

by Dan Parks Sydow

How a Mac program plays sounds stored in resources and files

In October and December of this past year, Getting Started covered animation. The movement of objects in a window is one very important aspect of bringing multimedia effects into your Mac program. Another key element to making multimedia a reality is the playing of sounds. This month we'll take a look at how to easily add sound-playing capabilities to any of your own Macintosh applications. To give you a choice of techniques, we'll cover the playing of sounds that are stored in an application's resource fork, and the playing of sounds that are kept in separate sound files.

The Sound Manager

The standard audio hardware on Macintosh computers includes one or more internal speakers. A Mac program includes the code that specifies what sound to play, and the hardware generates the sound itself. The Sound Manager holds the code that translates your wishes to information the hardware can use. The Sound Manager 3.0 software is built into the system of every PowerPC-based Mac, came as an extension included with many pre-Mac OS 8 versions of the Macintosh operating system, and is available as a system extension for older Macs or Macs running an older version of the system software.

The Mac has always been able to generate sound, but it hasn't always had the Sound Manager to serve as the programmer's link to the Mac's speakers. Prior to System 6.0, the sound routines were a part of the Toolbox but weren't specifically organized into what we now know as the Sound Manager. Version 2.0, or the enhanced Sound Manager, was introduced along with System 6.0.7. Version 3.0 became a part of System 7.5. A Macintosh owner who hasn't upgraded to System 7.5 or beyond can gain the capabilities of Sound Manager 3.0 by adding the Sound Manager system software extension to his System Folder.

Sound Manager 3.0 improves upon how the Mac works with sound, and adds new sound-playing capabilities to the Macintosh - so your sound-playing program will want to make use of it. Most Mac owners now have Sound Manager 3.0 as a part of their system software - but a few users may not. You'll want to save the user of an older Mac (or older system software) the aggravation of crashing their computer by checking for the presence of version 3.0 (or later) of the Sound Manager before your application calls Sound Manager Toolbox routines.

The check for a particular version of the Sound Manager involves the use of a variable of type NumVersion. Here's the definition of the NumVersion data type:

struct NumVersion
{
   UInt8   majorRev;
   UInt8   minorAndBugRev;
   UInt8   stage;
   UInt8   nonRelRev;
};

Apple defines the Byte type to be an unsigned char, so it's range is 0 to 255. Simply as a convenience, Apple defines the UInt8 type to be the same as the Byte type. By the type name it should be readily apparent that a UInt8 variable is an unsigned integer occupying eight bits, or one byte. When a Toolbox routine returns a NumVersion, you can check the majorRev field to see if the version is appropriate to meet your program's requirements. In the case of the check for Sound Manager 3.0, call the Toolbox routine SndSoundManagerVersion() and then see if the majorRev field of the returned NumVersion has a value of at least 3:

NumVersion      theSndMgrVersion;

theSndMgrVersion = SndSoundManagerVersion();
if ( theSndMgrVersion.majorRev < 3 )
   // user has an outdated version of the Sound Manager

Sound Resources

Apple has created the snd resource type to hold the data for a sound. It's very important to note that all resource types must be four characters, and that the sound resource type is somewhat unusual in that it ends with a space. If your code includes a reference to the sound resource type and you omit the trailing space character, the code won't compile. A sound can exist as a sound resource or as a sound file (we look at sound files a bit later). Storing a sound as a resource in your application's resource fork is a good way to ensure that the sound will always be available for your application's use.

The Toolbox function SndPlay() is used to play a sound resource. SndPlay() expects a sound resource to already be loaded into memory, so you'll want to call GetResource() before calling SndPlay(). Pass GetResource() the type of resource you're interested in (here, a snd resource) and the ID of the resource, and GetResource() loads the specified resource and returns a handle to the memory location of the loaded data. Assuming your program has a snd resource with an ID of 12000, here's how the call to GetResource() looks:

Handle      theHandle;

theHandle = GetResource( 'snd ', 12000 );

Once you have a handle to a sound resource, call SndPlay() to play the sound. Here's a typical call to this routine:

SndPlay( nil, (SndListHandle)theHandle, false );

The first of the three SndPlay() parameters is a pointer to a sound channel. The Sound Manager always uses a sound channel to store information about how a sound is to be played. If you pass a value of nil as this first parameter, the Sound Manager takes care of the allocation of a sound channel. The second parameter is a handle to the sound resource to play. GetResource() returned a generic handle, while SndPlay() requires a specific type of handle - a SndListHandle. Typecasting the generic handle is all that's necessary. The last parameter to SndPlay() is a Boolean value that indicates whether the sound should be played asynchronously (true) or synchronously (false).

With asynchronous sound-playing, other actions can take place at the same time as the playing of the sound. With synchronous sound playing, no other actions take place until the sound has finished playing. The above call to SndPlay() plays the sound synchronously. To play a sound asynchronously, you need to allocate your own sound channel and you need to know about a special type of routine called a callback procedure. Both these topics are beyond the scope of this article - but watch for them next month.

Sound Files

If you'd rather your program play a sound from a separate file, the Toolbox makes that possible. Sound resources are handy because the sounds are kept within the application that plays them, but there are situations when you'll want to instead store sounds in separate files. A sound used by more than one application would be a good candidate to be stored in a file. A large sound is also a good nominee to be stored in a file rather than a resource. This is because Apple recommends that a sound resource be kept to less than half a megabyte in size, while no such limit is imposed on a sound file.

Sounds can be stored in several types of files, but on the Mac you'll work primarily with sound files of the formats AIFF (Audio Interchange File Format) and AIFF-C (Audio Interchange File Format Extension for Compression). The Toolbox makes playing AIFF and AIFF-C sound files easy, but before the sound within a file can be played your application needs to find the file on disk and open it. An example of gathering file information for a sound file and then opening that file's data fork (because the sound data in an AIFF or AIFF-C file is held in that fork) is shown here:

FSSpec      theFSSpec;
OSErr      theErr;
short      fileRefNum;
   
FSMakeFSSpec( 0, 0, "\pMySoundFile", &theFSSpec );
FSpOpenDF( &theFSSpec, fsRdPerm, &fileRefNum );

A file system specification, represented by a variable of the data structure type FSSpec, holds information about the disk location, or path, of a single file. Given a file name and a pointer to an FSSpec variable, the File Manager routine FSMakeFSSpec() fills in the fields that make up the FSSpec. The first two parameters to FSMakeFSSpec() specify the volume (or disk) the file resides on and the directory (folder) the file is in. Passing a value of 0 for each of these parameters tells FSMakeFSSpec() that the named file is in the same directory as the application.

The File Manager routine FSpOpenDF() opens a file's data fork. As shown above, FSpOpenDF() requires three parameters. The first is a pointer to a previously filled-in FSSpec. The second parameter is a permission level. Using the Apple-defined constant fsRdPerm here tells the File Manager to open the file with read permission only: the file data can be read, but the application that opens the file cannot write to (alter) it. The last parameter is a pointer to a variable that is to serve as a file reference number. After opening the specified file, the File Manager creates a reference number for the file and places that value in this last parameter.

To play an opened sound file, use the Toolbox function SndStartFilePlay(). Here's an example of the use of this function:

SndStartFilePlay( nil, fileRefNum, 0, 40960, nil, nil, 
                         nil, false );

The eight parameters make the use of SndStartFilePlay() seem difficult, but in fact it's easy to use. That's because most of the parameters can be set to nil. Let's take a quick look at each parameter:

  • The first parameter to SndStartFilePlay() is a pointer to a sound channel. As with SndPlay(), pass a nil pointer to let the Sound Manager allocate the sound channel.
  • The second parameter is a reference number to an already open AIFF or AIFF-C File. This is the reference number returned by the prior call to FSpOpenDF().
  • The third parameter specifies whether SndStartFilePlay() will be playing a sound file or a sound resource. SndStartFilePlay() can be used to play sound resources, though Apple recommends you use SndPlay() instead. A value of 0 specifies that a sound file is to be used.
  • The fourth parameter designates the size of an input buffer (a block of free bytes of memory) that the Sound Manager can use in the playing of the sound file. SndStartFilePlay() works by reading in some of the sound data from a sound file, playing the sound defined by that data, and then reading in additional sound file data. For successful sound play Apple recommends a buffer size of at least 20,480 (20 K) bytes. The above snippet doubles that minimum recommended value to 40,960 (40 K) bytes.
  • The fifth parameter allocates the input buffer (the fourth parameter only established this buffer's size). Pass a nil pointer here to let the Sound Manager handle the buffer allocation.
  • The sixth parameter specifies whether SndStartFilePlay() should play a part, or all, of a sound file's sound data. Pass a nil pointer here to play the entire sound.
  • The seventh parameter is a pointer to a completion routine - a function used for asynchronous sound playing (performing other actions while a sound plays). For synchronous play (playing a sound without other actions, as described in this article), pass a nil pointer.
  • The eighth parameter indicates whether the sound is to be played asynchronously (true) or synchronously (false).

The SndStartFilePlay() function will be supported in the upcoming Carbon API, but Apple will be encouraging developers to start relying on QuickTime for the handling of a variety of multimedia files. Look to future issues of MacTech Magazine for QuickTime coverage. See last month's Getting Started column for an introduction to Carbon.

Obtaining Sounds

If your program is to play sounds, you'll of course need to obtain those sounds from somewhere. Sounds can be obtained from a number of sources. If you subscribe to an online service such as America Online or CompuServe, check their own software libraries for free sound files. If you need a greater variety of sounds, invest a small amount (about 30 bucks will do it) for a commercial product - a CD-ROM that holds hundreds, or more likely, thousands, of sounds in a number of formats. If the sound you seek is unique, consider creating it yourself using your Mac's built-in microphone or by plugging a sound digitizer into the back of your Macintosh.

Mac users often find sounds distributed as Finder sound files - also referred to as System 7 sound files. You'll recognize such a sound file by its icon - it looks like a document with a speaker on it. A Finder sound file is so named because it can be played from the Finder - just double-click on its icon on the desktop to hear the sound. A Finder sound file stores a sound as a snd resource, so if your program plays sound resources, you can make use of a Finder sound file's sound by opening the file from a resource editor, copying the file's snd resource, and pasting that resource into your project's resource file.

Often the same sound will be offered in a few formats, such as a Finder sound file and an AIFF file. Sometimes a sound will be distributed in just one format. If the sound of interest isn't in the format you want, use a sound-converting program to change its format. The freeware program SoundApp is an example of such a utility - you can find it on the Web at just about any archive of Mac software (such as MacShare.com at http://www.macshare.com/utility.html). With a sound-converting program you can change an AIFF to a Finder sound file and then use a resource editor to copy that converted file's snd resource to your project's resource file. Alternatively, you can convert a Finder sound file to an AIFF file and then store that file with your application. In either case, for your program to play the sound you'll of course need to include the appropriate sound-playing code in your project's source code file.

SoundPlayer

This month's program is called SoundPlayer. Running SoundPlayer results in the appearance of the menu bar shown in Figure 1. The menu of significance is the Sound menu. Choosing either of the items in this menu results in the playing of the corresponding sound. As you can surmise from the menu item names, the cat meow sound is stored in the SoundPlayer's resource fork, while the dog bark sound exists as a separate sound file that needs to be present in the same folder as the SoundPlayer application. After playing the sounds as often as desired, choose Quit from the File menu to end the program.


Figure 1. The SoundPlayer menu.

Creating the SoundPlayer Resources

To begin, open your CodeWarrior development folder and create a folder named SoundPlayer. Start up ResEdit and create a new resource file named SoundPlayer.rsrc inside the SoundPlayer folder. Figure 2 shows the five types of resources used by SoundPlayer. With the exception of the snd resource, the resource types should look familiar to you.


Figure 2. The SoundPlayer resources.

SoundPlayer uses the same ALRT resource and DITL resource that have been used in the last several Getting Started examples. These two resources are used to support the error-handling alert displayed by the program's DoError() routine. Figure 3 shows the four MENU resources the program needs. The items in the Sound menu include (resource) and (file) in their names so that you'll be able to tell which item uses which type of sound - in a real-world application there's no need to relay this information to the user. After creating the MENU resources, create an MBAR resource that references all four menus. That is, make sure it includes IDs 128, 129, 130, and 131.


Figure 3. The SoundPlayer menu resources.

The snd resource used by SoundPlayer isn't a resource you can create in ResEdit. ResEdit isn't a sound editor, so to get the sound you need you'll have to rely on a sound-producing or sound-editing software program. Or, pick up a sound resource by following one of the suggestions made earlier in this column. This month's project is available for download from MacTech's ftp site at ftp://ftp.mactech.com/src/mactech/volume15_1999/15.02.sit, and it includes a single system sound file named CatMeow. You can use ResEdit to open that file, copy its one snd resource, and then paste that resource into the SoundPlayer.rsrc file.

Figure 4 shows how the CatMeow file looks when opened in ResEdit. Here you see what happens when you double-click on the snd icon and then double-click on a particular snd resource. ResEdit is composed of a number of editors, but there is no sound editor. So ResEdit opens the sound resource in a hex editor window that does nothing but show the raw data that makes up the digital sound. After satisfying your curiosity, close the hex editor. Then click on the CatMeow resource in the list of snd resources, copy it, and paste it into the SoundPlayer.rsrc file.


Figure 4. The sound resource in a system sound file.

That's it for the SoundPlayer.rsrc file. Save the file and quit ResEdit - it's time to get started on the project file.

Creating the SoundPlayer Project

Launch CodeWarrior and choose New Project from the File menu to create a new project based on the MacOS:C_C++:MacOS Toolbox:MacOS Toolbox Multi-Target stationary. Uncheck the Create Folder check box before clicking the OK button. Name the project SoundPlayer.mcp and make the SoundPlayer folder the project's destination.

Add the SoundPlayer.rsrc file to the project window and remove the SillyBalls.rsrc file. The SoundPlayer project doesn't use of any of the standard ANSI libraries, so you can feel free to remove the ANSI Libraries folder.

Choose New from the File menu to create a new, empty source code window. Save it with the name SoundPlayer.c and then choose Add Window from the Project menu to add the file to the project. Remove the SillyBalls.c file from the project window if you already haven't done so. The source code listing for the SoundPlayer program appears next in the source code walk-through. You can type the code into the SoundPlayer.c file as you read the walk-through, or you can save a little effort and download the entire SoundPlayer project from MacTech's ftp site at ftp://ftp.mactech.com/src/mactech/volume15_1999/15.02.sit.

Walking Through the Source Code

Now, the SoundPlayer code. SoundPlayer starts off with the usual constant definitions. Most of the constants have appeared in previous projects, but there are a couple of additions. The constant ksnd_ResMeowID is the resource ID of the project's one snd resource - the resource used when the user chooses Meow (resource) from the Sound menu. For sound resources, Apple reserves IDs up to 8191 - so always give your own resources of this type an ID greater than 8191. The constant kBarkFileName is a Pascal-style string that holds the name of the AIFF sound file that's to be used when the user chooses Bark (file) from the Sound menu.

/********************* constants *********************/

#define   kMBARResID            128
#define   kALRTResID            128
#define   ksnd_ResMeowID        12000

#define   kBarkFileName      "\pDogBark.AIFF"

#define   kSleep                7
#define   kMoveToFront         (WindowPtr)-1L

#define   mApple                128
#define   iAbout                1

#define   mFile                 129
#define   iQuit                 1

#define   mSound                131
#define   iMeow                 1
#define   iBark                 2

SoundPlayer needs just one global variable - the now-familiar gDone. The user's quitting of the program toggles this variable's value from false to true.

/****************** global variables *****************/

Boolean      gDone;

Next come the program's function prototypes.

/********************* functions *********************/
void      ToolBoxInit( void );
void      MenuBarInit( void );
void      PlaySoundResource( void );
void      PlaySoundFile( void );
void      EventLoop( void );
void      DoEvent( EventRecord *eventPtr );
void      HandleMouseDown( EventRecord *eventPtr );
void      HandleMenuChoice( long menuChoice );
void      HandleAppleChoice( short item );
void      HandleFileChoice( short item );
void      HandleSoundChoice( short item );
void      DoError( Str255 errorString );

The main() function begins by initializing the Toolbox. After that a check is made to ensure that the user has version 3.0 or later of the Sound Manager. If that isn't the case, the application-defined routine DoError() posts a message and exits the program.

/************************ main ***********************/

void      main( void )
{
   NumVersion theSndMgrVers;

   ToolBoxInit();
 
   theSndMgrVers = SndSoundManagerVersion(); 
   if ( theSndMgrVers.majorRev < 3 )
       DoError( "\pSound Manager is outdated" );
   
   MenuBarInit();
   
   EventLoop();
}

The ToolBoxInit() and MenuBarInit() functions are the same as prior versions.

/******************** ToolBoxInit ********************/

void      ToolBoxInit( void )
{
   InitGraf( &qd.thePort );
   InitFonts();
   InitWindows();
   InitMenus();
   TEInit();
   InitDialogs( nil );
   InitCursor();
}

/******************** MenuBarInit ********************/

void      MenuBarInit( void )
{
   Handle            menuBar;
   MenuHandle      menu;
   
   menuBar = GetNewMBar( kMBARResID );
   SetMenuBar( menuBar );

   menu = GetMenuHandle( mApple );
   AppendResMenu( menu, 'DRVR' );
   
   DrawMenuBar();
}

When the user chooses Meow (resource) from the Sound menu, it is the application-defined function PlaySoundResource() that handles the task of loading a sound resource into memory and playing the sound.

/****************** PlaySoundResource ****************/

void PlaySoundResource( void )
{
   Handle theHandle;
   OSErr theErr;
 
   theHandle = GetResource( 'snd ', ksnd_ResMeowID );
 
   if ( theHandle == nil )
      DoError( "\pAttempt to load sound resource failed" );

   HLock( theHandle );
   theErr = SndPlay( nil, (SndListHandle)theHandle, 
                               false );
   HUnlock( theHandle );

   if ( theErr != noErr )
      DoError( "\pPlaying of sound failed" );
 
   ReleaseResource( theHandle );
}

If the call to GetResource() successfully loads the sound resource, PlaySoundResource() then plays the sound. The calls to the Toolbox functions HLock() and HUnlock() force the system to leave the sound data untouched in memory. That is, during the duration of the execution of SndPlay(), the system can't move the memory referenced by the variable theHandle. That's important - you don't want the system moving the sound data in the middle of sound playing. In the SndPlay() snippet presented earlier in this article we didn't save the OSErr value returned by SndPlay(). Here we do. If SndPlay() fails to play the sound, an error will be returned and that fact can be relayed to the user. When sound playing is complete, the memory that's holding the sound resource data is released for future use by the program.

When the user chooses Bark (file) from the Sound menu, the application-defined function PlaySoundFile() gets called to open a sound file and play its sound. PlaySoundFile() begins by creating a file system specification for a file named DogBark.AIFF. Here we've set the volume and directory values of 0, so FSMakeFSSpec() expects the file named DogBark.AIFF to be in the same directory as the SoundPlayer application. If you want to keep sound files in a different directory, you'll need to study up on the FSSpec data structure in the Files volume of Inside Macintosh.

/******************** PlaySoundFile ******************/

void PlaySoundFile( void )
{
   FSSpec      barkFSSpec;
   OSErr      theErr;
   short      sndFileRefNum;
   
   theErr = FSMakeFSSpec( 0, 0, kBarkFileName, 
                                    &barkFSSpec );

If the creation of the FSSpec fails, the program ends. If the file system specification succeeds, it's time to open the file with a call to FSpOpenDF() and play its contents with a call to SndStartFilePlay().

   if ( theErr != noErr)
      DoError( "\pSound file not found" );

   theErr = FSpOpenDF( &barkFSSpec, fsRdPerm, 
                                  &sndFileRefNum );
   if ( theErr != noErr )
      DoError( "\pSound file opening error" );

   theErr = SndStartFilePlay( nil, sndFileRefNum, 0, 40960, 
                                          nil, nil, nil, false );
   if ( theErr != noErr )
      DoError( "\pSound file playing error" );
}

PlaySoundFile() includes plenty of error-handling code. Working with files on disk subjects the program to a number of potential problems, and we want to be ready for them. For instance, if the specified file is corrupt, the call to FSpOpenDF() may fail. If the file isn't a sound file, the call to SndStartFilePlay() would fail.

Now we're in the clear. The remaining SoundPlayer code is standard stuff that's necessary to keep our event-handling program running. The only additions exist to support a menu selection from the Sound menu.

/********************** EventLoop ********************/

void      EventLoop( void )
{      
   EventRecord      event;
   
   gDone = false;
   while ( gDone == false )
   {
      if ( WaitNextEvent( everyEvent, &event, kSleep, nil ) )
         DoEvent( &event );
   }
}

/*********************** DoEvent *********************/

void      DoEvent( EventRecord *eventPtr )
{
   char      theChar;
   
   switch ( eventPtr->what )
   {
      case mouseDown: 
         HandleMouseDown( eventPtr );
         break;
      case keyDown:
      case autoKey:
         theChar = eventPtr->message & charCodeMask;
         if ( (eventPtr->modifiers & cmdKey) != 0 ) 
            HandleMenuChoice( MenuKey( theChar ) );
         break;
      case updateEvt:
         BeginUpdate( (WindowPtr)(eventPtr->message) );
         EndUpdate( (WindowPtr)(eventPtr->message) );
         break;
   }
}

/******************* HandleMouseDown *****************/

void      HandleMouseDown( EventRecord *eventPtr )
{
   WindowPtr   window;
   short         thePart;
   long            menuChoice;
   
   thePart = FindWindow( eventPtr->where, &window );
   
   switch ( thePart )
   {
      case inMenuBar:
         menuChoice = MenuSelect( eventPtr->where );
         HandleMenuChoice( menuChoice );
         break;
      case inSysWindow : 
         SystemClick( eventPtr, window );
         break;
   }
}

Here in HandleMenuChoice() we've added a switch case section for the handling of a selection from the Sound menu.

/******************* HandleMenuChoice ****************/

void      HandleMenuChoice( long menuChoice )
{
   short   menu;
   short   item;
   
   if ( menuChoice != 0 )
   {
      menu = HiWord( menuChoice );
      item = LoWord( menuChoice );
      
      switch ( menu )
      {
         case mApple:
            HandleAppleChoice( item );
            break;
         case mFile:
            HandleFileChoice( item );
            break;
         case mSound:
            HandleSoundChoice( item );
            break;
      }
      HiliteMenu( 0 );
   }
}

/****************** HandleAppleChoice ****************/

void      HandleAppleChoice( short item )
{
   MenuHandle      appleMenu;
   Str255            accName;
   short            accNumber;
   
   switch ( item )
   {
      case iAbout:
         SysBeep( 10 );
         break;
      default:
         appleMenu = GetMenuHandle( mApple );
         GetMenuItemText( appleMenu, item, accName );
         accNumber = OpenDeskAcc( accName );
         break;
   }
}

/******************* HandleFileChoice ****************/

void      HandleFileChoice( short item )
{
   switch ( item )
   {
      case iQuit:
         gDone = true;
         break;
   }
}

The HandleSoundChoice() routine is new to SoundPlayer. It's code should be intuitive - a Meow (resource) menu selection invokes PlaySoundResource() to play the sound resource, while a Bark (file) menu selection invokes PlaySoundFile() to play the DogBark.AIFF sound file.

/****************** HandleSoundChoice ****************/

void      HandleSoundChoice( short item )
{
   switch ( item )
   {
      case iMeow:
         PlaySoundResource();
         break;
      case iBark:
         PlaySoundFile();
         break;
   }
}

/*********************** DoError *********************/

void      DoError( Str255 errorString )
{
   ParamText( errorString, "\p", "\p", "\p" );
   
   StopAlert( kALRTResID, nil );
   
   ExitToShell();
}

Running SoundPlayer

Run SoundPlayer by selecting Run from the Project menu. Once your code compiles, the menu bar shown in Figure 1 appears. Choose one Sound menu item, then the other, to verify that the sounds play. Make sure your Mac's speaker volume is set above zero! Choose Quit from the File menu to end the animation and the program.

Till Next Month...

If your program will be playing more than one sound, you'll want to adapt either or both of the sound-playing routines to make them more flexible. In the case of PlaySoundResource(), you'll want to pass in the resource ID of the snd resource to play. For PlaySoundFile() you'll pass in the name of the sound file to play, or perhaps predetermine the file's FSSpec and pass in that data structure. Try adapting SoundPlayer so that the playing of a sound is contingent on something other than a menu selection. For instance, allow the user to click a button in a window to play a sound.

You can learn more about playing sounds by reading the Sound volume of Inside Macintosh. You can also wait until next month, when we look at how a Mac program plays asynchronous sound - sound that plays as other action takes place in the program. Asynchronous sound is an important way of playing sound because it doesn't tie up your program and force it to wait for sound-completion before carrying out other actions. This is especially important in games, where sound plays in the background, or accompanies some event (such as the sound of a gun firing, or a bomb exploding, or a rocket launching). Until next month's look at asynchronous sound, examine, modify, and play with the SoundPlayer code so that you're familiar with the basics of sound.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Pack a magnifying glass and practice you...
Somehow it has already been a year since Torchlight: Infinite launched, and XD Games is celebrating by blending in what sounds like a truly fantastic new update. Fans of Cthulhu rejoice, as Whispering Mist brings some horror elements, and tests... | Read more »
Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »

Price Scanner via MacPrices.net

Every model of Apple’s 13-inch M3 MacBook Air...
Best Buy has Apple 13″ MacBook Airs with M3 CPUs in stock and on sale today for $100 off MSRP. Prices start at $999. Their prices are the lowest currently available for new 13″ M3 MacBook Airs among... Read more
Sunday Sale: Apple iPad Magic Keyboards for 1...
Walmart has Apple Magic Keyboards for 12.9″ iPad Pros, in Black, on sale for $150 off MSRP on their online store. Sale price for online orders only, in-store price may vary. Order online and choose... Read more
Apple Watch Ultra 2 now available at Apple fo...
Apple has, for the first time, begun offering Certified Refurbished Apple Watch Ultra 2 models in their online store for $679, or $120 off MSRP. Each Watch includes Apple’s standard one-year warranty... Read more
AT&T has the iPhone 14 on sale for only $...
AT&T has the 128GB Apple iPhone 14 available for only $5.99 per month for new and existing customers when you activate unlimited service and use AT&T’s 36 month installment plan. The fine... Read more
Amazon is offering a $100 discount on every M...
Amazon is offering a $100 instant discount on each configuration of Apple’s new 13″ M3 MacBook Air, in Midnight, this weekend. These are the lowest prices currently available for new 13″ M3 MacBook... Read more
You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Apple’s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13″ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13″ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Apple’s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14″ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more

Jobs Board

*Apple* Systems Administrator - JAMF - Activ...
…**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
IT Systems Engineer ( *Apple* Platforms) - S...
IT Systems Engineer ( Apple Platforms) at SpaceX Hawthorne, CA SpaceX was founded under the belief that a future where humanity is out exploring the stars is Read more
Nurse Anesthetist - *Apple* Hill Surgery Ce...
Nurse Anesthetist - Apple Hill Surgery Center Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
Housekeeper, *Apple* Valley Village - Cassi...
Apple Valley Village Health Care Center, a senior care campus, is hiring a Part-Time Housekeeper to join our team! We will train you for this position! In this role, Read more
Sublease Associate Optometrist- *Apple* Val...
Sublease Associate Optometrist- Apple Valley, CA- Target Optical Date: Apr 20, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92307 **Requisition Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.