Using the MCI to Play MIDI Music


The device used to play MIDI music in a game is called the MIDI synthesizer, and its job is to take notes of music and play them aloud . The MIDI synthesizer isn't a physical device that you plug in to your computer ”it's built in to your sound card. Just about every sound card these days includes a MIDI synthesizer, so you shouldn't have any trouble playing MIDI music. A big part of using the MCI to play MIDI music is establishing a line of communication with the MIDI synthesizer device. For example, you must first open the device before you can issue a command such as playing a MIDI song. The next section shows you how to open the MIDI sequencer device for playing MIDI music, and subsequent sections reveal how to play MIDI songs.

Opening the MIDI Device

When working with MIDI devices using the MCI, it's important to understand that a device is referenced using a unique device identifier. So, when you open a MIDI device to play MIDI music, you'll want to keep track of the device ID that is returned when you first open the device. This ID is your ticket for communicating with the device from then on. You can think of the device ID as the phone number you need in order to call the device and tell it what you want it to do.

To perform any MIDI tasks with the MCI, you must get comfortable with the mciSendCommand() function, which sends a command string to a MIDI device. This function is described in the Win32 API as follows :

 MCIERROR mciSendCommand(MCIDEVICEID IDDevice, UINT uiMsg, DWORD dwCommand,   DWORD_PTR pdwParam); 

The mciSendCommand() function is used to send command messages to a MIDI device. The arguments to this function vary depending on what kind of message you're sending, so we'll analyze them on a message-by-message basis. Before you can send a message to play a MIDI song, you must first open a MIDI device by sending an open message using the mciSendCommand() function. The open message is called MCI_OPEN , and it requires the use of a special data structure called MCI_OPEN_PARMS , which is defined as follows:

 typedef struct {   DWORD_PTR    dwCallback;   MCIDEVICEID  wDeviceID;   LPCSTR       lpstrDeviceType;   LPCSTR       lpstrElementName;   LPCSTR       lpstrAlias; } MCI_OPEN_PARMS; 

The only members of this structure that we're interested in are the middle three: wDeviceID , lpstrDeviceType , and lpstrElementName . The wDeviceID member stores the device ID for the MIDI sequencer, and in our case will be used to retrieve this ID. In other words, the wDeviceID member gets filled in with the device ID when you open the device. The other two fields have to be specified when you open the device. The lpstrDeviceType field must be set to the string "sequencer" to indicate that you want to open the MIDI sequencer. The lpstrElementName field must contain the name of the MIDI file that you want to play. This reveals an interesting aspect of the MCI ”you must specify a multimedia object when you open a device. In other words, you don't just open a device and then decide what multimedia file you want to play.

Although I could go on and on about how interesting the mciSendCommand() function is, I'd rather just show you the code for opening the MIDI sequencer device, so here goes:

 UINT uiMIDIPlayerID; MCI_OPEN_PARMS mciOpenParms; mciOpenParms.lpstrDeviceType = "sequencer"; mciOpenParms.lpstrElementName = "Music.mid"; if (mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE  MCI_OPEN_ELEMENT,   (DWORD_PTR)&mciOpenParms) == 0)   // Get the ID for the MIDI player   uiMIDIPlayerID = mciOpenParms.wDeviceID; 

Hopefully this code isn't too terribly shocking considering that I just primed you with a brief explanation of the MCI_OPEN_PARMS data structure. This code initializes the two important fields of this structure ( lpstrDeviceType and lpstrElementName ), and then passes the entire structure as the last argument to the mciSendCommand() function. Notice that the MIDI music file in this example is named Music.mid. The first argument to the mciSendCommand() function is set to zero because you don't yet know the ID of the device. The second argument, MCI_OPEN , is the message you're sending to the MIDI sequencer. Finally, the third argument identifies the fields of the MCI_OPEN_PARMS data structure that are to be taken into consideration for the message.

graphics/book.gif

You might have noticed that the MCI approach to playing MIDI songs involves opening and playing a MIDI file, as opposed to using MIDI songs as resources. Unfortunately, there is no good workaround for this problem, so you won't be able to compile .MID music files into your executable game files as you've done with bitmaps and wave sounds. This means that you'll have to provide separate .MID files with your games when you distribute the games .


If all goes well, the mciSendCommand() function returns to indicate that the MIDI sequencer was successfully opened. You can then store away the device ID since it is now available in the wDeviceID field of the MCI_OPEN_PARMS data structure.

Playing a MIDI Song

Now that you have the MIDI sequencer opened for a specific MIDI song, you're ready to issue a play command and start the song playing. This is accomplished with the same mciSendCommand() function, but this time you provide a different command. In this case, the command is MCI_PLAY , and its required arguments are somewhat simpler than those for the MCI_OPEN command. However, the MCI_PLAY command does involve another data structure, MCI_PLAY_PARMS , although you can leave it uninitialized when you're just playing a song from start to finish.

graphics/book.gif

The MCI_PLAY_PARMS data structure comes into play whenever you want a finer degree of control over how a MIDI song is played , such as selecting a different starting and ending point for the song.


Unlike the MCI_OPEN command, which doesn't require a device ID, the MCI_PLAY command requires a device ID in order to successfully play a MIDI song. Following is code to play a song based on the previous code that opened the MIDI sequencer for the Music.mid song:

 MCI_PLAY_PARMS mciPlayParms; mciSendCommand(uiMIDIPlayerID, MCI_PLAY, 0, (DWORD_PTR)&mciPlayParms); 

You're probably thinking that there must be a catch because this code looks way too simple. Unless you're wanting to do something tricky like skip the first three seconds of a MIDI song, this is all that's required to play a song from start to finish using the MCI. Notice that the device ID is provided in the first argument to mciSendCommand() , while the MCI_PLAY command is provided as the second argument. The third argument isn't necessary, so you simply pass . And finally, the fourth argument isn't really necessary either, but you must still pass a legitimate structure, even if it's just left uninitialized.

Pausing a MIDI Song

There are situations in which you will definitely want to pause a MIDI song while it's being played. For example, you don't want the music to continue playing when the main game window is deactivated. So, you need a way to pause a MIDI song. This is accomplished with the MCI_PAUSE command, which is surprisingly simple to use. Following is an example of pausing a MIDI song using the MCI_PAUSE command and the mciSendCommand() function:

 mciSendCommand(uiMIDIPlayerID, MCI_PAUSE, 0, NULL); 

Notice that the only two arguments of significance in this code are the device ID (the first argument) and the pause message (the second argument). The remaining two arguments have no bearing on the MCI_PAUSE command, so you can pass empty values for them. In order to play a MIDI song that has been paused , you just issue another play command.

Closing the MIDI Device

Of course, all good things must come to an end, and eventually you'll want to close the MIDI sequencer device because you're finished with it or because you want to stop playing a song. The MCI_CLOSE command is used to close a MIDI device with the mciSendCommand() function. Following is an example of closing the MIDI sequencer by issuing an MCI_CLOSE command:

 mciSendCommand(uiMIDIPlayerID, MCI_CLOSE, 0, NULL); 

Similar to the MCI_PAUSE command, the only two arguments of significance in this code are the device ID (the first argument) and the close message (the second argument).

One point I haven't clarified in regard to playing and closing devices is that it's possible to encounter a problem when you attempt to play a MIDI song ”in which case, the mciSendCommand() function will return a value other than when you issue the MCI_PLAY command. In the earlier play example, I didn't bother looking at the return value of the mciSendCommand() function. But, it's a good idea to check and see if the song was successfully played because you should close the device if a problem occurred while playing the song. Following is revised play code that shows how to close the MIDI sequencer device if an error took place while playing the song:

 MCI_PLAY_PARMS mciPlayParms; if (mciSendCommand(uiMIDIPlayerID, MCI_PLAY, 0, (DWORD_PTR)&mciPlayParms) != 0) {   mciSendCommand(uiMIDIPlayerID, MCI_CLOSE, 0, NULL);   uiMIDIPlayerID = 0; } 

This code checks the return value of the mciSendCommand() function for the play command, and then closes the device if an error occurred. Notice that the device ID is also cleared after closing the device. This helps to make sure that you don't try to send any more commands to the device since the ID is no longer valid.



Sams Teach Yourself Game Programming in 24 Hours
Sams Teach Yourself Game Programming in 24 Hours
ISBN: 067232461X
EAN: 2147483647
Year: 2002
Pages: 271

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net