Multimedia Audio SDK

Introduction

Any game that deprives the user of music remains incomplete. The moment the element of sound is introduced in a game, the feel of the game changes, giving the game a more realistic effect. In this article, you will built two music applications. The first application is a simple dialog-based application that can be used to play MPEG, WMA, and WAV files. In the second program, you have proceeded a step further to play more than one audio file simultaneously. This feature of the second application finds its application in the development of multiplayer games.

Before you start your first music application, take a look at the features implemeted in the MM Audio API. The MM Audio API provides support for the following:

  1. Loading and playing sound from supported audio files regardless of audio compression type. It supports automatic conversion of audio data whenever it is required.
  2. Playback of multiple sources simultaneously.
  3. Locating sound in a 3D environment.
  4. Applying pitch, reverb, and other effects to sound.
  5. Facilities to stream or statically load the sound file into memory.
  6. Control the EAX parameters on source and listener on systems with EAX support.
  7. A 11-band frequency equilizer (software-based) to have finer control over sound quality.
  8. Audio buffers or streams are represented by a unique ID instead of pointers. Now, the client application can focus more on programming than searching for NULL audio interfaces or stream pointers.

Header and Libraries

al.h must be included in the project to enable the MM Audio API routines in the application. The import library is al.lib, which must be linked to the application.

A MM Audio Application

In this program, you will create a dialog-based application that uses MM Audio API to play MPEG, WMA, and WAV files.

To create this, run Visual C++/Borland C++. You will use VC++ 6.0. For Borland users, refer to the note at the end. Run VC++ and select "File > New." Select the project type as "MFC AppWizard (Exe)". Supply the name as "music1" and click OK. Select a "Dialog based" application from the new dialog that appears. Click on "Finish."

Build the dialog

Add the following child control to the dialog in the resource editor as shown in Figure 1.

Figure 1

Give the following IDs to the controls, as shown in the following table.

S Number Contol's ID
1 Edit Box IDC_FILENAME
2 Button IDC_BROWSE
3 Button IDC_PLAY
4 Button IDC_STOP

Initializing the API

Before any call is made into Audio API, it must be initialized for use. Call ALInit() and check the return value for TRUE, which indicates successful initialization.

Creating the Audio Context

An audio context must be created before any file or device operation is performed. Use ALCreate( HDC ) and pass the parent window's graphics device context to it. If the function returns a NON-NULL value, this value is the device ID of the audio device that is being used. Pass this value to ALSetCurrent( HAL ) and the device is set as the current useful audio device.

Add the following code to the OnInitDialog() function.

///////////////////////////////////////////////////////////////////
// CMusic1Dlg message handlers

BOOL CMusic1Dlg::OnInitDialog()
{
   CDialog::OnInitDialog();

   // Add an "About..." menu item to the system menu.
   // IDM_ABOUTBOX must be in the system command range.

   ASSERT((IDM_ABOUTBOX & 0xFFF0) ==    IDM_ABOUTBOX);
   ASSERT(IDM_ABOUTBOX

   CMenu* pSysMenu = GetSystemMenu(FALSE);
   if (pSysMenu != NULL)
   {
      CString strAboutMenu;
      strAboutMenu.LoadString(IDS_ABOUTBOX);
      if (!strAboutMenu.IsEmpty())
      {
         pSysMenu->AppendMenu(MF_SEPARATOR);
         pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX,
                              strAboutMenu);
      }
   }

   // Set the icon for this dialog.
   // The framework does this automatically when the application's
   // main window is not a dialog
   SetIcon(m_hIcon, TRUE);     // Set big icon
   SetIcon(m_hIcon, FALSE);    // Set small icon

   // TODO: Add extra initialization here
   ALInit();
   CClientDC dc(this);
   HAL hAL= ALCreate(dc);
   if(hAL == NULL)
   {
      // Context was not created.
      // Error Error!!!
      AfxMessageBox("Cannot create the Audio" " Context.",
                    MB_OK|MB_ICONERROR);
      PostQuitMessage(0);
      return TRUE;
   }

   // A device is created. Use it!!!
   ALSetCurrent(hAL);
   return TRUE;    // return TRUE unless you set the focus to a
                   // control
}

In the above code, you see that the audio API is initialized with ALInit(). Then, dc—that is, a device context—is created for the parent window and this GDI context is passed to the ALCreate function. This function returns the audio device context id, which is set as current by using ALSetCurrent( HAL ).

Opening the Audio File

The CFileDialog is used to create an open dialog box with file types required. When user selects a file and clicks OK, the path name is retrieved by using CFileDialog::GetPathName(). This is typecasted respectively and passed to the ALOpenWave(ALdword, ALchar*, ALbool, ALbool, ALbool, or ALbool) function to be opened.

Parameters of ALOpenWave:

ALbool ALOpenWave(ALdword id,
                  ALchar* filename,
                  ALbool enable3D,
                  ALbool streaming,
                  ALbool enableGlobal,
                  ALbool enableEAX);

The first parameter represents the stream ID that is unique. This means that the ID must not represent another stream (valid or invalid).

The second parameter is the wave filename with the full path of the file to open. The file name must be passed with an extension.

The third parameter represents the support of 3D interfaces on an opened stream. If set to TRUE, the stream will support a 3D parameter set.

The fourth parameter represents a streaming property flag. If this flag is set to TRUE, the opened buffer will be streaming data from a file. Setting it to FALSE requires that all the wave data will be loaded at once to the audio buffer.

The fifth parameter represents a Global audible flag; setting it to TRUE enables the hearing of the stream, on playing, even if the parent window loses focus. If set to FALSE, the audio will be muted if the window loses focus.

The sixth parameter represents thw EAX setting. If set to TRUE, it will set the Creative EAX (2.0) property set on an opened stream. This will again automatically set the 3D HW flag set on the audio stream. If EAX effect cannot be set or is not available on the selected device, the function ignores the requested action.

If the stream can be opened successfully, the function returns TRUE. This indicates that the Audio Stream ID now represents a valid audio stream that can be selected as the current audio stream by using the ALSetWave(b&) function.

Open the resource editor and open the dialog in Figure 1. Double-click on the OPEN button and a window appears. There, type the function name as OnBrowse() and click Okay. Now, modify the code of OnBrowse() as follows:

void CMusic1Dlg::OnBrowse()
{
   //  TODO: Add your control notification handler code here
   ALCloseWave();
   SetDlgItemText(IDC_FILENAME,"Loading audio file...");
   CFileDialog dlg(TRUE,NULL,NULL,OFN_HIDEREADONLY,
                   "MPEG files.|*.mp?|"
                   "Windows Media Files.|*.wm?|"
                   "Wave Files.|*.*||");
   if(dlg.DoModal() == IDOK)
      {
      // Load the file.
      if(ALOpenWave(IDC_WAVE,
                   (ALchar*) (LPCTSTR) dlg.GetPathName(),
                    FALSE,
                    TRUE,
                    TRUE,
                    FALSE))
      {
         // Open the file.
         // Set it as current wave
         ALSetWave(IDC_WAVE);
         SetDlgItemText(IDC_FILENAME,dlg.GetPathName());
      }
      else
      {
         // File was not opened.
         // Error Error!!!
         AfxMessageBox("Cannot open the selected.",
                       MB_OK|MB_ICONERROR);
         SetDlgItemText(IDC_FILENAME,"File cannot be opened.");
      }
   }
   else
   {
      // Cancelled opening of file.
      SetDlgItemText(IDC_FILENAME,"Load aborted.");
   }
}

Playing the Audio File

The audio file had been loaded by your function ALBrowse and been set as current. And, because there is only one current stream, you assume that if any stream is set as current, it should be your stream. So, you call ALPlayWave( ALbool ) to the play the wave and ALStopWave() to stop it.

The ALPlayWave( ALbool ) takes one parameter, which represents that nature of the playback. If this value is TRUE, the stream is played an infinite number of times; that is, it is looped forever. For the value FALSE, it is played once.

The ALStopWave() function will stop the playing audio imediately when called.

To check the statues of playback, you may call ALIsWavePlaying(). This function returns TRUE if the wave is playing. Otherwise, it returns FALSE.

Open the resourse editor to the dialog in Figure 1. There, double-click the Play button; a window opens where you type OnPlay() and select "OK". Again, re-open the dialog in Figure 1 and double-click the Stop button; a window opens where you type OnStop and select "OK". Now, modify the code of OnPlay() and OnStop() as follows:

void CMusic1Dlg::OnStop()
{
   // TODO: Add your control notification handler code here
   ALStopWave();        //Stop the playing wave
}

void CMusic1Dlg::OnPlay()
{
   // TODO: Add your control notification handler code here
   ALPlayWave(TRUE);    //Play my wave in an infinite loop.
}

Closing the Wave File

One call to the ALCloseWave() or ALDeleteWave() function will close the current wave stream.

Closing the Audio Device

Once all the opened streams are closed, it's time to end the application. Call ALDelete() to release the current audio device.

Deinitializing the MM Audio API

When your application is done with the API, it's time to shut it down. So, call ALUninit() to deinitialize it. But remember, once this function is called none of the Audio API functions should be called. This should be the last API call made.

To achieve the Deinitialization action in your application, you modify the OnDestroy() function as follows:

void CMusic1Dlg::OnDestroy()
{
   // Close the current stream
   // Close and release the current audio device
   // Shut down the parent window
   // Deinitialize the API
   ALCloseWave();
   ALDelete();

   CDialog::OnDestroy();

   // TODO: Add your message handler code here
   ALUninit();
}

Compile the program and run it. You have successfully completed your first music program using the MMAudio API.

Multimedia Audio SDK

Multistream Playback

Multistream playback is useful for games. As objects simulating those in real life emits sound waves of different pitch and frequency, when used in games requires multiple audio streams to supply audio data.

In the first program, you designed a program that uses only one audio stream, so you called ALSetWave( ... ) once, set the file ID, and forgot about throughout. But, this should not be the case for multiple audio stream playback. The audio ID of the file stream must be set each time its property changes. Therefore, the stream ID is an important matter here.

A MMAudio MultiStream Web playback application

In this program, you will create a dialog-based application that uses the MM Audio API to play four simultaneous audio files from the Web.

To create this, run Visual C++/Borland C++. You will use VC++ 6.0. For Borland users, refer to the note at the end. Run VC++ and select "File > New." Select the project type as "MFC AppWizard (Exe)." Supply the name as "music2" and click OK. Select a "Dialog based" application from the new dialog that appears. Click on "Finish."

Build the dialog

Add the following child control to the dialog in the resource editor, as shown in Figure 2.

[MM2.jpg]

Figure 2

Give the following IDS to the controls, as shown in the table below.

S Number Contol's ID
1 Button IDC_STREAM1
2 Button IDC_STREAM2
3 Button IDC_STREAM3
4 Button IDC_STREAM4

Initializing the APIand Opening the Web Stream

Before any call is made into Audio API, it must be initialized for use. Call ALInit() and check the return value for TRUE, which indicates successful initialization.

Creating the Audio Context

An audio context must be created before any file or device operation is performed. Use ALCreate( HDC ) and pass the parent window's graphics device context to it. If the function returns a NON-NULL value, this value is the device ID of the audio device that is being used. Pass this value to ALSetCurrent( HAL ) and the device is set as current useful audio device.

Add the following code to the OnInitDialog() function.

//////////////////////////////////////////////////////////////////
// CMusic2Dlg message handlers

BOOL CMusic2Dlg::OnInitDialog()
{
   CDialog::OnInitDialog();
   // Set the icon for this dialog.
   // The framework does this automatically when the application's
   // main window is not a dialog
   SetIcon(m_hIcon, TRUE);     // Set big icon
   SetIcon(m_hIcon, FALSE);    // Set small icon

   // TODO: Add extra initialization here
   ALInit();
   CClientDC dc(this);
   ALSetCurrent(ALCreate(dc));
   Open(IDW_STREAM1, "http://web2.mms3.com/data/stereo/306389.wma");
   Open(IDW_STREAM2, "http://web2.mms3.com/data/stereo/306668.wma");
   Open(IDW_STREAM3, "http://web2.mms3.com/data/stereo/305049.wma");
   Open(IDW_STREAM4, "http://web2.mms3.com/data/stereo/306329.wma");

   return TRUE;    // return TRUE unless you set the focus to a
                   // control
}

In the above code, you see that the audio API is initialized with ALInit(). Then dc—that is, a device context—is created for the parent window and this GDI context is passed to ALCreate function. This function returns the audio device context id, which is set as current using ALSetCurrent( HAL ).

You also have defined four IDs, IDW_STREAM1 through IDW_STREAM4, as 1000 to 1300. These represent the audio IDS of the wave stream. You have also defined a function named Open( ALdword, ALchar* ) that takes the Audio ID as the first parameter and the URL name of the audio file as the second parameter, and opens the file stream.

void CMusic2Dlg::Open(ALdword id, ALchar *url)
{
   // Load the Web Audio file into static Audio memory
   // Set streaming=FALSE (3rd parameter to ALOpenWave).
   if(!ALOpenWave(id, url,FALSE,FALSE,TRUE,FALSE))
   MessageBox("The file cannot be loaded.",url,
              MB_OK|MB_ICONERROR);
}

Playing the Audio File

The audio file had been loaded during the initialization of the dialog. And, due to the presence of multiple streams, you have to set the respective stream as current before you change its property or state. After you set a current stream using ALSetWave( stream id ), you call ALPlayWave( ALbool ) to the play the wave and ALStopWave() to stop it.

The ALPlayWave( ALbool ) takes one parameter, which represents that nature of the playback. If this value is TRUE, the stream is played an infinite number of times; that is, it is looped forever. For the value FALSE, it is played once.

The ALStopWave() function will stop the playing audio imediately when called.

To check the statues of playback, you may call ALIsWavePlaying(). This function returns TRUE if the wave is playing. Otherwise, it returns FALSE.

Open the resourse editor to the dialog in Figure 2. There double-click the Stream1 button; a window opens where you type OnStream1() and select "OK." Similarly, OnStream2(), OnStream3, and OnStream4() are defined for button named IDC_STREAM2, IDC_STREAM3, and IDC_STREAM4, respectively:

void CMusic2Dlg::OnStream1()
{
   // TODO: Add your control notification handler code here
   Play(IDC_STREAM1);    //Play stream 1
}

void CMusic2Dlg::OnStream2()
{
   // TODO: Add your control notification handler code here
   Play(IDW_STREAM2);    //Play stream 2
}

void CMusic2Dlg::OnStream3()
{
   // TODO: Add your control notification handler code here
   Play(IDW_STREAM3);    //Play stream 3
}

void CMusic2Dlg::OnStream4()
{
   // TODO: Add your control notification handler code here
   Play(IDW_STREAM4);    //Play stream 4
}

The function Play( id ) is defined to take care of playing the respective audio stream. It is defined as the following:

void CMusic2Dlg::Play(ALdword id)
{
   //Play the respective stream from the start
   ALSetWave(id);        // Set the stream as current
   ALSeekWave(0);        // Set to beginning of stream
   ALPlayWave(FALSE);    // Play the file once
}

Closing the Wave File

One call to the ALCloseWave() or ALDeleteWave() function will close the current wave stream.

Closing the Audio Device

Once all the opened streams are closed, it's time to end the application. Call ALDelete() to release the current audio device.

Deinitializing the MM Audio API

When your application is done with the API, it's time to shut it down. So, call ALUninit() to deinitialize it. But remember, once this function is called, none of the Audio API functions should be called. This should be the last API call made. But, it should be remembered that this function may cause an access violation error if the stream is web-based. So better, never call this function or call this function at the destructor to the class.

To achieve the Deinitialization action in your application, you modify the OnDestroy() function as follows:

void CMusic2Dlg::OnDestroy()
{
   Close(IDW_STREAM1);      // Close stream 1
   Close(IDW_STREAM2);      // Close stream 2
   Close(IDW_STREAM3);      // Close stream 3
   Close(IDW_STREAM4);      // Close stream 4

   ALDelete();              // Delete the audio context and release
                            // the device
   CDialog::OnDestroy();    // Shut down our window

   // TODO: Add your message handler code here

   // ALUninit();           // As I mentioned, for slow streaming
                            // speed, an access violation error
                            // occurs.
   // This function is OPTIONAL.
}

Here, you have defined a function named Close( id ). It takes the source stream id as its input parameter and closes the stream. It is defined as:

void CMusic2Dlg::Close(ALdword id)    // Close the stream with the
                                      // ID given
{
   ALSetWave(id);    // Set the respective stream as current
   ALCloseWave();    // Close the current stream
}

Compile the program and run. You have successfully completed your second music program using the MMAudio API.

MM Audio SDK for Borland C++

Borland C/C++ users cannot use the audio.lib file provided with the SDK. But, the al.h file remains unchanged. To get a Borland Import library, use the IMPLIB.EXE utility provided by the Borland C++ package. Run it at command line with the following parameter:

implib al.lib al.dll

The generated library module must be used with the BC/C++ compiler to compile the projects that use the MMAudio SDK.

Download Links

The SDK and the documentation and other sample applications are available at http://streetx.freespaces.com/.

Note: This article was written for CG (www.codeguru.com). The related applications, documentations, and SDK with rutime are present at http://streetx.freespaces.com/. All files were scanned thoroughly for virus using Avast! 4.7 Home. but you should thoroughly scan any files you download for viruses. Neither the author nor CodeGuru cannot be held responsible for damages arising out of the product or related files. Use them at your own risk.


About the Author

Arnav Guddu

A hobby programmer. Do much of programming in graphics, animation, sound and multimedia. But a lot crazy about C++ related development. I may be reached at http://pendorasoft.byethost15.com/

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • With 81% of employees using their phones at work, companies have stopped asking: "Is corporate data leaking from personal devices?" and started asking: "How do we effectively prevent corporate data from leaking from personal devices?" The answer has not been simple. ZixOne raises the bar on BYOD security by not allowing email data to reside on the device. In addition, Zix allows employees to maintain complete control of their personal device, therefore satisfying privacy demands of valued employees and the …

  • Agile development principles have gone from something used only by cutting-edge teams to a mainstream approach used by teams large and small. If you're not using agile methods already though, or if you've only been exposed to agile on small projects here and there, you may wonder if agile can ever work in your environment. Read this eBook to learn the fundamentals of agile and how to increase the productivity of your software teams while enabling them to produce higher-quality solutions that better fulfill …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds