Building a CD Player - Part 1

From EDM2
Jump to: navigation, search

Written by Stephane Bessette

Part 1

Part 2

Part 3

Part 4

[NOTE: Here is a link to a zip of the cdsrc.zip source code for this article. Ed.]

Introduction

This series of articles will present the steps necessary to build a simple application that plays audio CDs. The interface will present menus for all possible actions, buttons for the most common action, and display some information about the currently inserted CD, such as the number of the current track and some form of time display (eg. the time remaining in the current track). Finally, dialogs will present and request various information.

Here's the list of the actions commonly found in audio CD players that will be implemented: Play, Stop, Pause, Resume, Next and Previous track, Backward and Forward seek, Insert, Eject. In addition, Mute, Unmute, and Volume level will also be implemented.

This program will rely on various controls: menu (unchecked/checked, active/disabled), pushbutton (visible/invisible), graphical button, radio button, circular slider, and static text fields. And dialogs will be used to (1) show information about each audio track on the CD (table of contents), (2) select the connector to use: headphones or stream, and (3) select the volume level.

Finally, when communicating with the CD device, two MCI commands will be used: mciSendCommand() to issue various commands to the CD device, and mciGetErrorString() to convert an error number into an error string. Let's start by looking at these two commands more closely.

mciSendCommand()

All commands to the CD device will be issued with the mciSendCommand API. Its syntax is:

ulrc = mciSendCommand(USHORT usDeviceID,  // (1)
                      USHORT usMessage,   // (2)
                      ULONG ulParam1,     // (3)
                      PVOID pParam2,      // (4)
                      USHORT usUserParm); // (5)

  1. The ID of the device
  2. The message to send
  3. Parameters; MCI_WAIT or MCI_NOTIFY, and other flags
  4. The address of a structure containing additional data
  5. User parameter, probably used when there's more than one device in use

When issuing a MCI command, you have to specify the ID of the device that is to receive the command. In this program, we'll obtain the ID of the CD device and send commands to that device. Quite a few commands can be issued, and the most relevant ones will be covered. These commands can function in two modes. By specifying MCI_WAIT, the execution of the program will be stopped until the command is completely processed. This is conceptually similar to the WinSendMsg() API. And by specifying MCI_NOTIFY, the execution of the program will resume immediately. When the command is completely processed, a message will be posted to the window specified in the hwndCallback member of the structure (4) passed along with the command. This is conceptually similar to the WinPostMsg() API.

MCI_WAIT or MCI_NOTIFY?

So how do we determine which to use? It could be argued that all commands should use the MCI_NOTIFY flag, since this would not tie up OS/2's message queue. However, some commands execute quite rapidly, and would not have a great impact on the overall responsiveness of the system. But there are some exceptions. Open (MCI_OPEN) takes some time before returning, so it is better written with the MCI_NOTIFY flag. And in some cases you absolutely have to use MCI_NOTIFY. For instance, the play (MCI_PLAY) command terminates only when the end of the audio CD is reached; using MCI_WAIT would essentially freeze the system since messages would no longer be processed.

Notification Messages

When the MCI_NOTIFY is specified, the command will send an MM_MCINOTIFY message to the window specified in the hwndCallback member of the structure passed as one of the parameters of the mciSendCommand() function. The first short of mp1 will contain the notification message.

MCI_NOTIFY_SUCCESSFUL
The command completed successfully
MCI_NOTIFY_SUPERSEDED
Another similar command was issued and will be processed (eg. Two successive play commands)
MCI_NOTIFY_ABORTED
The command was aborted by a second command (eg. A Play command followed by a Stop command)

And if none of these messages are received, then an error occurred. The second short of mp2 contains the ID of the commands, such as MCI_PLAY.

mciGetErrorString()

To determine whether the MCI command was successful or not, the low part of the return code must be examined:

  if(LOUSHORT(ulrc) == MCIERR_SUCCESS) {
      // The command was successful
  }
  else {
      // The command was not successfull
  }

If the command was not successful, you can obtain a textual description of the error code with mciGetErrorString():

ulrc = mciGetErrorString(ULONG ulError,     // (1)
                         PSZ pszBuffer,     // (2)
                         USHORT usLength);  // (3)

  1. The error code from mciSendCommand()
  2. A string to contain the description
  3. The size of this string

MCI Structures

To finish on the topic of the MCI commands, I'll mention that most commands require additional information to be specified in the structure (4) passed as a parameter of the mciSendCommand(). But for this member to be considered, you often need to specify an additional flag in the ulParam1 (3) parameter. We'll see many examples of this in the sections to come, where we actually issue commands. The next section will open and query the CD device's capacities.

Source

Basic.hpp
Defines two often used macros (InfoBox and ErrorBox)
Defines two user message
BasicDialogFramework.cpp
Standard file to support a dialog as the main interface
Main.cpp
Handles the interaction from the three dialogs:
CD Player dialog
User commands
Notifications from various MCI commands
Connector selection
Volume level selection
Class_CDROM.hpp
Header for the Class_CDROM class
Definition of three supporting structures:
Struct_Volume
Struct_TOC
Struct_DeviceCapacities
Definition of inline functions
Class_CDROM.cpp
Implementation of the members of the Class_CDROM class
Class_Controls.hpp
Header for the Class_CDROM class
Implementation of inline functions
Resource.def
Application definition
Resource.hpp
Header for the various resources used:
icon
bitmaps
dialogs
controls
menuitems
Resource.rc
Application resources:
icon
bitmaps
menu
accelerator table
Resource.dlg
Application resources:
dialogs