Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

Playing Audio CDs, part 3 - MCI

Playing Audio CDs, part 3 - MCI

  • Comments 8
Today, I want to talk about one of the most weird and wonderful APIs in all of Windows.  It's also one of the oldest - the creation date on the source file is April 25, 1990.

This API is the MCI command set.  It's an example of defining two separate APIs that do exactly the same thing -   There are really only two different MCI APIs, mciSendString and mciSendCommand.  They are utterly identical, the only difference between the two of them is that mciSendString sends its commands using strings, and mciSendCommand uses buffers.  There's a parser in winmm.dll that converts the parameters to mciSendString into the buffers specified by mciSendCommand.

The MCI commands can be used for most multimedia functions - you can play audio CDs with the APIs, you can play videos (not wmv videos, but AVI files), you can play wave files.  It's really quite an extraordinary API set.

So here's the CD player specialization for MCI strings:

CMCIStringCDPlayer::CMCIStringCDPlayer(void)
{
}

CMCIStringCDPlayer::~CMCIStringCDPlayer(void)
{
}

HRESULT CMCIStringCDPlayer::Initialize(void)
{
    MCIERROR mciError;
    TCHAR mciReturnBuffer[512];

    mciError = mciSendString("status cdaudio media present", mciReturnBuffer, sizeof(mciReturnBuffer), NULL);
    if (mciError != 0)
    {
        printf("MCI Error %x determining CD media status\n",mciError);
        return HRESULT_FROM_WIN32(mciError);
    }
    if (stricmp(mciReturnBuffer, "true") != 0)
    {
        printf("No media in CDRom drive\n");
        return E_FAIL;
    }
    return S_OK;
}

HRESULT CMCIStringCDPlayer::DumpTrackList()
{
    MCIERROR mciError;
    TCHAR mciReturnBuffer[512];
    mciError = mciSendString("status cdaudio number of tracks", mciReturnBuffer, sizeof(mciReturnBuffer), NULL);
    if (mciError != 0)
    {
        printf("MCI Error %x determining CD media track count\n",mciError);
        return HRESULT_FROM_WIN32(mciError);
    }
    sscanf(mciReturnBuffer, "%d", &_TrackCount);

    for (int i = 0 ; i < _TrackCount ; i += 1)
    {
        TCHAR mciCommandBuffer[512];
        DWORD trackHours, trackMinutes, trackSeconds;
        sprintf(mciCommandBuffer, "status cdaudio length track %d", i+1);
        mciError = mciSendString(mciCommandBuffer, mciReturnBuffer, sizeof(mciReturnBuffer), NULL);
        if (mciError != 0)
        {
            printf("MCI Error %x determining track length\n",mciError);
            return HRESULT_FROM_WIN32(mciError);
        }
        printf("Track %d: Length %s\n", i, mciReturnBuffer);
    }
    return S_OK;
}

HRESULT CMCIStringCDPlayer::PlayTrack(int TrackNumber)
{
    printf("Next article\n");
    return E_FAIL;
}

Following the pattern of the WMP articles, this one only dumps the contents of the CD tracks.  The first thing you'll notice is that the MCI string APIs use strings for their parameters (no duh, that's why they're called string APIs).  But the strings you specify are pretty verbose - for instance, to retrieve the number of tracks, you say "status cdaudio number of tracks".  The first word in the command string is the verb - MCI supports verbs like open , set, cut, paste, play, spin, etc.  You can even tell the MCI commands to open or close the CDRom door (with "set cdaudio door open").

The other thing to notice is that audio tracks are )ORIGIN 1, not )ORIGIN 0.  The other thing to notice is that the length of a track is returned in m:s:f format, or minutes:seconds:frames.

Tomorrow, I'll add the playback functions.

  • Wonderful articles. These go into my Code Pearls collection. Just wondering if you would be kind enough to post some code in the future as to how to play an URL programmatically. I listen to a lot of talk radio online and I would like to write an app to start playing the program automagically at a specified time.
  • Looks like those APIs were ported from 16 bit and not available on 64 bit Windows. Any interesting history?
  • The MCI APIs should be available for 64bit apps on the final x64 bits.

    Dirty little secret: At one point we believed we could end-of-life them for 64bit platforms (changing processor architectures is just about the only opportunity that we have to remove old APIs like this one, since changing processor architectures requires that a new version of the application be released), unfortunately we realized relatively late in the process that we needed to continue to support them.

    It's possible that some beta bits were released that came from the interim builds where we'd removed the APIs.
  • I've been using mci to play stuff like MIDI files for a long time now. It'd be terrible to see the API EOL-ed, because there just doesn't seem to be an easier way to play MIDIs (at least none that I'm aware of). DirectShow always seemed grossly overcomplicated for just playing a single MIDI file.
  • What always irritated me about MCI was that altough you can open and close the drive tray, there seems to be no way to determine whether it is currently opened or closed.
    I had to port some DOS code once where this was an issue (the DOS drivers returned the status just fine) and only ended up equaling "drive tray is closed" with "disc is in drive".
  • Steven,
    That's why we brought it back. We realized that (a) a number of shipping apps would have a great deal of difficulty porting to 64bit platforms without it, and (b) it was the easiest way of performing a bunch of operation.

    On the other hand, as I mentioned, most of the code was written for 16 bit windows and ported to Win32...
  • The
    other day, I wrote about dumping the track database on an audio using the
    MCI string command...
  • Cool series of articles.

    Do you know if there any way to get the MCI interface to return the data track that is included in mixed mode CDs? This turns out to be important if you want to do a freedb lookup to get the track names.
Page 1 of 1 (8 items)