Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

Playing Audio CDs, part 4 - MCI Playback.

Playing Audio CDs, part 4 - MCI Playback.

  • Comments 8
The other day, I wrote about dumping the track database on an audio using the MCI string command set.

Today, I'll include the piece I left out of the last article, actually playing a track from the CD.

HRESULT CMCIStringCDPlayer::PlayTrack(int TrackNumber)
{
    MCIERROR mciError;
    TCHAR mciReturnBuffer[512];
    TCHAR mciCommandBuffer[512];
    if (TrackNumber > _TrackCount)
    {
        printf("Track out of range\n");
        return E_FAIL;
    }
    // Set the MCI time format to track/minute/second/frame.
    mciError = mciSendString("open cdaudio", mciReturnBuffer, sizeof(mciReturnBuffer), NULL);
    if (mciError != 0)
    {
        printf("MCI Error %x opening CDRom\n",mciError);
        return HRESULT_FROM_WIN32(mciError);
    }
    // Set the MCI time format to track/minute/second/frame.
    mciError = mciSendString("set cdaudio time format tmsf", mciReturnBuffer, sizeof(mciReturnBuffer), NULL);
    if (mciError != 0)
    {
        printf("MCI Error %x setting time format\n",mciError);
        return HRESULT_FROM_WIN32(mciError);
    }
    sprintf(mciCommandBuffer, "play cdaudio from %d to %d", TrackNumber, TrackNumber+1);
    mciError = mciSendString(mciCommandBuffer, mciReturnBuffer, sizeof(mciReturnBuffer), NULL);
    if (mciError != 0)
    {
        printf("MCI Error %x playing audio\n",mciError);
        return HRESULT_FROM_WIN32(mciError);
    }

    Sleep(_TrackTimes[TrackNumber]);

    // Close the CD 
    mciError = mciSendString("close cdaudio", mciReturnBuffer, sizeof(mciReturnBuffer), NULL);
    if (mciError != 0)
    {
        printf("MCI Error %x closing CDRom\n",mciError);
        return HRESULT_FROM_WIN32(mciError);
    }
    return S_OK;
}

There are a couple of things to notice about the code.  The first thing that's done is to open the CDAudio device.  We do that to be able to set the audio time format to tmsf (track/minute/second/frame).  The default audio time format appears to be frame based, but it's not easy to tell from the documentation. 

Once I set the cdrom track, it was trivial to have it play the selected track - you just say "play".

This ease of use is why the MCICDA commands are so widely used - playing a track is literally as easy as hitting the buttons on the player.

Next time, a different version of the MCI command set.

Edit: Fixed typo in comment.

  • // Set the MCI time format to track/minute/second/frame.
    mciError = mciSendString("close cdaudio", mciReturnBuffer, sizeof(mciReturnBuffer), NULL);

    I'm guessing that the comment is just left over from cutting and pasting the code, or am I missing something? This command just closes it, correct?
  • Is the Sleep() call after starting CD playback really recommended? Would it be better to call one of the MsgWait type functions?
  • Actually, I intentionally used Sleep() to indicate that MCI doesn't require a message pump to work.

    On a non console app it would make sense, but MCI's command set doesn't require the pump.
  • Larry, where did the syntax of those commands come from? Was it some late 80s media standard or something?
  • Gunroom, I haven't the faintest idea where it came from (and nobody on the team does either :()

    I just know that it is :)
  • Gunroom,

    After you asked, I asked around to the guys who wrote most of MCI (they're still at Microsoft, although not on the audio team).

    The MCI command set was created for REXX apps (and basic apps) in a joint development effort with IBM. The actual command syntax was chosen by the developer who did the work.
  • Larry, thanks for the info. It is fascinating to me to understand why things are the way they are.
Page 1 of 1 (8 items)