Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

Playing a CD, part 5 - MCI commands.

Playing a CD, part 5 - MCI commands.

  • Comments 1
I'm almost done with the easy part of the "playing a CD" series.  Today, we'll look at the other half of the MCI APIs, the MCI command set.

HRESULT CMCICommandCDPlayer::Initialize(void)
{
    MCIERROR mciError;
    MCI_STATUS_PARMS statusParms = {0};
    MCI_OPEN_PARMS openParms = {0};

    openParms.lpstrDeviceType = "cdaudio";
    mciError = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE, (DWORD_PTR)&openParms);
    if (mciError != 0)
    {
        printf("MCI Error %x determining CD media status\n",mciError);
        return HRESULT_FROM_WIN32(mciError);
    }
    _MciHandle = openParms.wDeviceID;

    statusParms.dwItem = MCI_STATUS_MEDIA_PRESENT;
    mciError = mciSendCommand(_MciHandle, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR) &statusParms);
    if (mciError != 0)
    {
        printf("MCI Error %x determining CD media status\n",mciError);
        return HRESULT_FROM_WIN32(mciError);
    }
    if (statusParms.dwReturn == 0)
    {
        printf("No media in CDRom drive\n");
        return E_FAIL;
    }
    return S_OK;
}

HRESULT CMCICommandCDPlayer::DumpTrackList()
{
    MCIERROR mciError;
    MCI_STATUS_PARMS statusParms = {0};

    statusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
    mciError = mciSendCommand(_MciHandle, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&statusParms);
    if (mciError != 0)
    {
        printf("MCI Error %x determining number of tracks\n",mciError);
        return HRESULT_FROM_WIN32(mciError);
    }
    if (statusParms.dwReturn == 0)
    {
        printf("No media in CDRom drive\n");
        return E_FAIL;
    }
    _TrackCount = statusParms.dwReturn;

    for (DWORD_PTR i = 0 ; i < _TrackCount ; i += 1)
    {
        statusParms.dwTrack = (DWORD)i+1;
        statusParms.dwItem = MCI_STATUS_LENGTH;
        mciError = mciSendCommand(_MciHandle, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD_PTR)&statusParms);
        if (mciError != 0)
        {
            printf("MCI Error %x determining track length\n",mciError);
            return HRESULT_FROM_WIN32(mciError);
        }
        DWORD trackLength = (DWORD)statusParms.dwReturn;
        printf("Track %d: Length %02d:%02d:%02d\n", i, MCI_MSF_MINUTE(trackLength), MCI_MSF_SECOND(trackLength), MCI_MSF_FRAME(trackLength));
        _TrackLengths.Add(trackLength);
    }
    return S_OK;
}

Pretty simple, and not surprisingly, the code bears a close resemblance to the command based code. 

Things to note: When using the MCI command functions you need to open the device ahead of time.  The string functions will happily redirect to the appropriate device when parsing, but the command functions aren't as clever.

The other rather unique thing is the behavior of the fdwCommand parameter to mciSendCommand.  The fdwCommand parameter basically describes what kind of structure lives in the dwParam parameter to mciSendCommand.  But not always.  As you can see from the call to determine the length of the track, it also is used to describe the semantics of the fields within the track. 

Tomorrow, playing the CD using the CD commands.  Then we get to the hard part, playing CDs using digital audio extraction (AKA going straight to the metal).