Find the CD
Written by Mike James   
Monday, 17 August 2009
Article Index
Find the CD
Getting a CD fingerprint
Freedb

 

My problem is that I have so many CDs that I’m never sure exactly what I’ve got. The solution is, of course, to build a database and use it but I really don’t want to type in all the details. The quick fix is to make use of one of the CD databases available on the Internet to lookup the details of the disc that is in the drive. This way it’s possible to build up a complete list of discs, including all their details, without much effort.

Freedb is an open source free CD database which is easy to use and seems to find all the obscure CDs I’ve tried it out on.

 

Freedb doesn’t present an advance interface in the form of a web service and it doesn’t return its data using XML so quite a lot of the work involved in using it is writing custom code to communicate with it. We also need to access information stored in the Table Of Contents (TOC) of an audio CD which is then used to compute a “fingerprint” for the disc we want to lookup in the database.

While not perfect, the simplest way of doing this is to work with the MCI (Multimedia Command Interface) API – one of the oldest of the Windows multimedia APIs but still essential as it hasn’t been replaced by a COM object or a .NET class.

While working with the MCI API it is also worth discovering how to play a CD – simply because it’s easy and useful. The program is written in C#. It would be easy, however, to convert the examples to almost any language that can call the Windows API and make a TCP/IP connection.

Topics covered include:

  • Making API calls in C#
  • Creating class wrappers for an API
  • Using MCI to control a CD player
  • Computing a Freedb fingerprint
  • Making a TCP/IP connection in C#
  • Creating a TCP/IP client
  • Looking up a CD using Freedb

Using MCI

If you want to control a multimedia device you might well think of DirectX or go in search of a .NET class that does the job for you. The surprise is that there are no facilities in DirectX or .NET that allow you to control a CD drive.

To do this you have to use the old Windows API the Media Control Interface.To make it more acceptable we can “wrap” the MCI calls that we use in the form of a class.

Start a new Windows project and use the Project, Add Class command to add a class named “CCDAudio”. As we are going to call an API function, we need the help of the Framework Interop classes so add the “using” statement:

using System.Runtime.InteropServices;

Next we need a definition of the API function we want to call. In this case the only MCI function we need is mciSendString:

class CCDAudio
{
[DllImport("winmm.dll")]
extern static int mciSendString(
string command,
StringBuilder Buffer,
int bufferLength,
int nothing);

The DllImport statement specifies the name of the DLL that the function is in and then its form by giving its name and the parameters it expects.

There is often some flexibility in doing this because the Framework Interop classes perform conversion between appropriate types. In this case a StringBuilder object is being used in preference to a byte array buffer simply because it’s a more convenient way to receive back the result of the API call. You can set up any API calls that you need in a similar way.

To use the mciSendString function you simply use a command string to specify what you want to do and get the results back in the StringBuilder buffer. You can see the range of MCI command that you can use at:

http://msdn.microsoft.com/en-us/library/ms709461(VS.85).aspx

 

We need two global variables to use mciSendString, the response buffer and an error code:

private int err;
private StringBuilder response =
new StringBuilder(128);

The class can use its constructor to open the cdaudio device and its destructor to close it:

public CCDAudio()
{
err = mciSendString(
"open cdaudio", response, 128, 0);
}
 
~CCDAudio()
{
err = mciSendString(
"close all", response, 128, 0);
}

Notice that no attempt at error handling has been made – it is up to you to add instructions that deal with a non-zero error code.

Now that we have the basics for opening the cdaudio device we can start to use it. By studying the available MCI commands you should be able to follow how to create methods that play, stop, pause, eject and seek to a given track:

public int play()
{
return err = mciSendString(
"play cdaudio",response, 128, 0);
}
public int stop()
{
return err = mciSendString(
"stop cdaudio",response, 128, 0);
}
public int pause()
{
return err = mciSendString(
"pause cdaudio",response, 128, 0);
}
public int eject()
{
return err = mciSendString(
"set cdaudio door open",
response, 128, 0);
}
public int seek(int track)
{
err = mciSendString(
"set cdaudio time format tmsf",
response, 128, 0);
if (err != 0) { return err; };
err = mciSendString(
"seek cdaudio to "+track.ToString(),
response, 128, 0);
return err;
}

In each case these methods handle errors by simply returning the error code for the calling program to deal with.

The only complicated method is seek. This first sets the CD player into “track, minutes, seconds, frame” format and then sends the track number you want to send. You could send a more accurate location by including minutes, seconds and frames (one frame =1/75 second).

By comparing these implementations with the documentation you should be able to add other functions and even create wrappers for other devices such as video players.

As an example of using the class place four buttons on the form with captions Play, Pause, Stop and Eject. We first need to create an instance of the CCAudio class:

CCDAudio CDAudio;
public Form1()
{
InitializeComponent();
CDAudio = new CCDAudio();
}

The button click event handlers are very simple and just use the methods provided by the object:

private void button1_Click(
object sender, EventArgs e)
{
int err=CDAudio.play();
}
private void button2_Click(
object sender, EventArgs e)
{
int err = CDAudio.pause();
}
private void button3_Click(
object sender, EventArgs e)
{
int err = CDAudio.stop();
}
private void button4_Click(
object sender, EventArgs e)
{
int err = CDAudio.eject();
}

Again no attempt has been made to handle errors but you should find that you can now start, stop and eject a CD. This could form the start of your very own CD player program. Have a look at the MCI documentation to discover how to add features.

 

player

A simple CD player using the MCI

<ASIN:0735625301>

<ASIN:0735618070>

<ASIN:0735619115>



Last Updated ( Monday, 17 August 2009 )