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:
- Loading and playing sound from supported audio files regardless of audio compression type. It supports automatic conversion of audio data whenever it is required.
- Playback of multiple sources simultaneously.
- Locating sound in a 3D environment.
- Applying pitch, reverb, and other effects to sound.
- Facilities to stream or statically load the sound file into memory.
- Control the EAX parameters on source and listener on systems with EAX support.
- A 11-band frequency equilizer (software-based) to have finer control over sound quality.
- 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.