|
|
Building a CD Player - Part 3/4Written by Stephane Bessette |
OptionsWe'll now take a look at the various options that can be set before moving on to the CD commands. ConnectorThe CD device supports two connectors: headphones and stream. The headphones connector refers to the headphone jack that is usually found on the front panel of the CD-ROM. And the stream connector refers to the stream of data that is moving from the CD device to the sound card. This second option is not available to all CD devices. To select the stream connector:
mciConnectorParameters.ulConnectorType =
MCI_CD_STREAM_CONNECTOR;
ulrc = mciSendCommand(usDeviceID,
MCI_CONNECTOR,
MCI_ENABLE_CONNECTOR |
MCI_CONNECTOR_TYPE |
MCI_WAIT,
&mciConnectorParameters,
0);
And to select the headphones connector, you'd set the ulConnectorType
member of the MCI_CONNECTOR_PARMS structure to MCI_HEADPHONES_CONNECTOR.
When the connector is changed, we also have to query the connected-to device in order to send volume commands to the appropriate device (the one that is now in charge of setting the volume level):
mciConnectionParameters.ulConnectorType =
MCI_CD_STREAM_CONNECTOR;
ulrc = mciSendCommand(usDeviceID,
MCI_CONNECTION,
MCI_QUERY_CONNECTION |
MCI_WAIT,
&mciConnectionParameters,
0);
if(LOUSHORT(ulrc) == MCIERR_SUCCESS) {
// The command was successful
// Obtain the current device's ID, the one in
// charge of setting the volume
VolumeDeviceID =
mciConnectionParameters.usToDeviceID;
}
else {
// The command was not successful
// We'll use the ID of the CD device
VolumeDeviceID = usDeviceID;
}
Commands to set the Volume level, Mute, and Unmute the device will have to
use this VolumeDeviceID.
Time DisplayFour time displays have been implemented:
Updating the Time DisplayWhen do we update the time display? Well, when it needs to be updated. Ok, so how do we know when it needs to be updated? There are two possible ways. The first would be to start a timer when the Play or Resume commands are issued and stop that timer when the Stop or Pause commands are issued. We'd also have to manipulate the timer when we seek. That seems like a lot of work, especially once we've seen the alternative. We can request to be notified every time the CD plays a certain amount of time. In our case, we'd like to be notified every time the CD plays one second. To request the notification:
mciPositionParameters.ulUnits = MSECTOMM(1000);
ulrc = mciSendCommand(usDeviceID,
MCI_SET_POSITION_ADVISE,
MCI_WAIT | MCI_SET_POSITION_ADVISE_ON,
&mciPositionParameters,
0);
Every time the CD plays one second, we'll receive an MM_MCIPOSITIONCHANGE
message. mp2 will contain the current time position in MMTIME format.
We'll use this information to properly keep track of the current time.
The MSECTOMM() macro converts a time unit specified in milliseconds to a time unit in MMTIME. MMTIME is the time format used by the CD device by default. Although we could instruct it to use a different time format, I prefer to work in MMTIME. One reason is that it often doesn't make a difference which time format is being used. And another reason is that some API work only in MMTIME. (Note to C++ users. This macro generates an error message but nevertheless appears to work correctly.) Muting and Unmuting the VolumeBoth of these commands have the same syntax:
mciSetParameters.ulAudio = MCI_SET_AUDIO_ALL;
ulrc = mciSendCommand(VolumeDeviceID,
MCI_SET,
MCI_WAIT | MCI_SET_AUDIO | MCI_SET_OFF,
&mciSetParameters,
0);
When using the MCI_SET_OFF flag, we are muting. If we'd used the
MCI_SET_ON flag, then we would have unmuted the volume level. Also, we
could have targeted an individual channel, left (MCI_SET_AUDIO_LEFT) or
right (MCI_SET_AUDIO_RIGHT) to mute/unmute only one channel. And the same
holds true for setting the volume: we could modify one channel and leave
the other channel unaffected.
Volume LevelTo change the volume level, we first have to compound the left and right volume levels into a single ULONG variable, and then we specify the channels involved:
mciSetParameters.ulLevel =
(ULONG)MPFROM2SHORT(left, right);
mciSetParameters.ulAudio = MCI_SET_AUDIO_ALL;
ulrc = mciSendCommand(VolumeDeviceID,
MCI_SET,
MCI_WAIT | MCI_SET_AUDIO |
MCI_SET_VOLUME | MCI_SET_ON,
&mciSetParameters,
NULL);
Now that we've covered most of the necessary pieces, let's put them
together as we finally cover the audio CD commands.
PlayThis could be considered to be the most essential command of an audio CD player: if we can't play, then there's not much point in having the program. As mentioned before, the MCI_NOTIFY flag must be specified. If you specify the MCI_WAIT flag, you'll wait until the last audio track has been played before being able to process another message. In this example, we'll play from the first track:
mciPlayParameters.ulFrom =
TOC.tocHeader[0].ulStartAddr;
ulrc = mciSendCommand(usDeviceID,
MCI_PLAY,
MCI_NOTIFY | MCI_FROM,
&mciPlayParameters,
0);
After this command has been issued, we'll start receiving
MM_MCIPOSITIONCHANGE messages since we previously requested notifications
for every 1 second of playback. We'll also receive an MM_MCINOTIFY message
from the MCI_PLAY command when the last track has been played. When that
message is received, two actions could be taken. Either we implement a
continuous play feature, where we restart playback at the next track, or
we stop the CD by issuing the Stop command.
StopStopping playback is fairly straightforward:
ulrc = mciSendCommand(usDeviceID,
MCI_STOP,
MCI_WAIT,
&mciGenericParameters,
0);
Pause and ResumeWhen pausing playback, the current time position is noted, so that when the Resume command is issued, we resume playback from that paused location. So, to pause:
ulrc = mciSendCommand(usDeviceID,
MCI_PAUSE,
MCI_WAIT,
&mciGenericParameters,
0);
And to resume from the paused time location:
ulrc = mciSendCommand(usDeviceID,
MCI_RESUME,
MCI_WAIT,
&mciGenericParameters,
0);
Although Pause and Resume are simple to use, there is a little gotcha. If
we paused playback, seeked to a different location or changed track, we
wouldn't want to resume playback from the location where playback was
paused. Rather, we'd want playback to resume from the time location we
specified by seeking or changing the current track. One solution is to
Stop the CD if we seek or change track while we're paused. The option to
pause would be disabled, and the only recourse to continue playback would
be via the Play command. The second solution is to Pause playback from
this new time location (this is how I've done ).
That's it for today; so long until next time! |