Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

Playing Audio CDs

Playing Audio CDs

  • Comments 12

One of the cool things about working in multimedia is that you sometimes have opportunities to play with cool bits of hardware.

This article isn't about one of them :).

Instead, it's about something really quite mundane.  Playing a CD programmatically.

It turns out that there are at least three different ways of playing a CD programmatically on Windows, ranging from easy to hard.  I'll start with the easy version and move to progressively harder versions.  The three mechanisms are:
  1. Letting the Windows Media Player play the CD.
  2. Playing the CD with the MCI API set
  3. Playing the CD with Digital Audio Extraction.

Let me start with a bit of details about an audio CD.

An audio CD consists of a set of audio tracks which can be read from the CD.  The track database on the CD contains the start offset within the CD of each of the tracks on the CD, that's what a player uses to determine where to seek on the CD to play.

Clearly, however you are going to play a track from the CD, you need to start by retrieving the track list.  So that's where we'll start.  I started with a console application built in visual studio 2003, adding ATL support (to hide the COM management goo).

#import "wmp.dll" // Needed to import the WMP interface definitions.
CWinApp theApp;
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    int nRetCode = 0;
    CComPtr<WMPLib::IWMPCdromCollection> cdRomCollection;
    HRESULT hr;

    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

    // initialize MFC and print and error on failure
    if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    {
        _tprintf(_T("Fatal Error: MFC initialization failed\n"));
        nRetCode = 1;
    }
    else
    {
        hr = cdRomCollection.CoCreateInstance(__uuidof(WMPLib::WindowsMediaPlayer));
        if (hr != S_OK)
        {
            printf("Couldn't instantiate CDRom player: %x\n", hr);
            return 1;
        }
        long driveCount;
        hr = cdRomCollection->get_count(&driveCount);
        if (hr != S_OK)
        {
            printf("Couldn't get drive count: %x\n", hr);
            return 1;
        }
        if (driveCount == 0)
        {
            printf("Machine has no CDROM drives\n");
            return 1;
        }

        CComPtr<WMPLib::IWMPCdrom> cdromDrive;
        cdromDrive = cdRomCollection->Item(0);

        CComPtr<WMPLib::IWMPPlaylist> playlist;
        hr = cdromDrive->get_Playlist(&playlist);
        if (hr != S_OK)
        {
            printf("Couldn't get playlist for CDRom drive: %x\n", hr);
            return 1;
        }
        for (int i = 0 ; i < playlist->count ; i += 1)
        {
            CComPtr<WMPLib::IWMPMedia> item;
            hr = playlist->get_Item(i, &item);
            if (hr != S_OK)
            {
                printf("Couldn't get playlist item %d: %x\n", i, hr);
                return 1;
            }
            printf(_T("Track %d (%S)\n"), i, item->name.GetBSTR()); // leaks :)
        }
    }
    CoUninitialize();
    return nRetCode;
}

So what's going on here?  First off, we import the type library from wmp.dll.  This is because VS 2003 doesn't include the wmp.h header file that includes the type information, so instead we need to import it from the DLL directly.  Note that I'm NOT using the wrapper classes that #import gives you (except for using item->name in the printf above).  This is simply because the wrapper functions throw exceptions instead of returning errors and I prefer my error handling to be more explicit (it's a personal preference thingy).

Everything starts with a WMP CDRom collection, which is a list of CDRom drives available on the computer.  We enumerate the collection to ensure that there's at least one drive available, then access the first drive in the collection (usually the D drive).  Assuming that there's an audio CD in the drive, this will return an IWMPCdrom object.  On the CDRom object, there's a playlist, which is the collection of audio tracks.  To enumerate the tracks on the CD, you can simply enumerate the items in the playlist collection, and print that information out.

One of the cool things about letting Windows Media Player do the heavy listing is that it looks up the track info in the CD database.  Which means that you get a friendly name for the track.  That can be pretty darned cool.  It's also slow.  Another downside is that this example is written to the Windows Media Player 10 SDK, if you don't have WMP 10 on the target machine, it's very likely that it won't work.

Tomorrow, I'll add the code to actually play audio.

  • Or you could play the CD in iTunes using the iTunes for Windows COM SDK. :)

    <http://developer.apple.com/sdk/itunescomsdk.html>
  • Cool Jack, I hadn't realized that Apple had published such a beast. Neat.

    The interfaces to it are remarkably similar to those for WMP - the iTunes version is somewhat more device centric (CDs are lumped in with other sources like the iPod, the library, etc) but...
  • What could be the reason I'm getting a REGDB_E_CLASSNOTREG error ? I'm running WS03SP1 on a amd64 box.
  • Vatsan, do you have WMP 10 installed?

    I don't know if WS 2K3 has it or not.

    It's possible you might have to update to WS2K3 SP1 to get WMP 10 (I don't know for sure however).
  • Yes, I do have WMP 10 on the machine.

    WS03SP1 = Win2003 with SP1 :) It is the x64 sku actually.

    And BTW, no, WMP10 doesn't exist on Win2003 RTM.
  • Vatsan: Are you running WS03 SP1 32-bit or x64?

    SP1 (and thus x64 which is based on SP1) comes with WMP10 I believe.
  • Found the solution. I was bulding a 64bit app, and wmp.dll is a 32 bit dll. Loading 32 bit dll's from 64bit apps is not expected to work.

    Recompiling as a 32 bit app works!
  • Hmm, I would've expected that wmp.dll comes in both 32-bit and 64-bit versions, and that 64-bit apps would automatically use the 64-bit version, and 32-bit the 32-bit versions. Isn't that the whole point of a fully 64-bit OS?

    I know that the original XP 64-bit for Itanium had limited 64-bit support for apps like WMP, but isn't this new version supposed to be completely 64-bit?
  • Yesterday, I looked a a simple application for dumping the track information on a CD.
    Since I'm going...
  • PatriotB,
    I would have thought so too. But parts of 64bit windows aren't 64bit. For instance there are two copies of IE, a 32bit and a 64bit version, because 32bit ActiveX controls only work in the 32bit version of the browser.
  • Might want to release the cdRomCollection smart pointer before the call to CoUninitialize :)
  • Yesterday , I looked a a simple application for dumping the track information on a CD. Since I'm going

Page 1 of 1 (12 items)