The Unofficial Newsletter of Delphi Users - by Robert Vivrette



Manage Multiple CD's with TMediaPlayer

by Clinton R. Johnson - xepol@poboxes.com

Borland tends to hide the best functionality.  Don't ask me why.  The TMediaPlayer, as a result, is probably the most under appreciated component in the entire VCL.  I've noticed that many people in the Borland newsgroups have asked how to use multiple CDroms.  Well, with this example, I'll show you not just how to use multiple CDroms, but also how to find which drives are CDroms (source is listed at the bottom).

I started by writing a function called CDList.  It asks the system for a list of available drives in the system(GETLOGICALDRIVES).  This returns a map showing which drive letters are used.  Next, I use a bitmask to determine which drives are CDroms.  By calling GETDRIVETYPE with the drive letter (formatted as the root directory ie : D:\), I can determine which drives are CDroms.

Next, I use the result of my CDList function to fill a ComboBox.  I ensure that each drive letter added to the ComboBox has a colon appended to it.  This makes it easier to use with the TMediaPlayer component.

Finally, I add an OnChange event handler to the ComboBox.  First, if the TMediaPlayer is open, I stop it and close it.  Stopping the CDrom is an important step. First, its a good habit to make sure that anything you deal with is in a known state, and secondly, if the system has multiple physical CDroms, the CDrom will keep playing even if the MediaPlayer is closed.   I assign the FILENAME property of the MediaPlayer  to the selected line in the ComboBox, and open the MediaPlayer  again.  The Filename property must be the drive letter appended by a colon, an undocumented feature, although Borland has recently added this information to their FAQ.

One of the reasons most people have problems with the Win32 API is its lack of consistency.  You may have noticed that I had to pass the drive as a full path to the GETDRIVETYPE system call, but I only assign the drive letter and a colon to the MediaPlayer (and thus directly to the MCI layers).  It's always important to remember little details like this - it makes the difference between functional code, and non functional code (and occasionally functional code, leading to premature hair loss).

On a final note, I set the Shareable property on the TMediaPlayer to true.  I consider this a good habit.  It lets you share the drive with other programs. I also re-open the drive, so that I can send it text commands.  I've found that the text based commands are better documented than the binary MCI calls, and at times, more functional.  I would not be able to do this if I did not set the Sharable property to true.

Here is the DFM file and source code for the demo unit.

object Form1: TForm1
  Left = 200
  Top = 110
  Width = 441
  Height = 81
  Caption = 'Form1'
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object ComboBox1: TComboBox
    Left = 272
    Top = 16
    Width = 145
    Height = 21
    Style = csDropDownList
    ItemHeight = 13
    TabOrder = 0
    OnChange = ComboBox1Change
  end
  object MediaPlayer1: TMediaPlayer
    Left = 12
    Top = 8
    Width = 253
    Height = 30
    DeviceType = dtCDAudio
    Shareable = True
    TabOrder = 1
  end
end

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, MPlayer;

type
  TForm1 = class(TForm)
    ComboBox1: TComboBox;
    MediaPlayer1: TMediaPlayer;
    procedure FormCreate(Sender: TObject);
    procedure ComboBox1Change(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//*****************************************************************************
// Returns a string, made of all CDROM drive letters in the system.
//*****************************************************************************
Function CDList : String;

Var
  Drives                : Integer;
  Loop                  : Integer;
  Work                  : String;

begin
//*****************************************************************************
// Get a map of the drives.  Bit 0= A, Bit 25=Z.  If the bit is set, the drive
// is present on the system.
//*****************************************************************************
  Drives := GetLogicalDrives;
  Result := '';
  For Loop := 0 To 25 Do
  Begin
//*****************************************************************************
// For each drive in the system, check if it is a CDROM, if so, all the letter
// to the result string.
//*****************************************************************************
    If (((1 Shl Loop) And Drives)<>0) Then
    Begin
      Work := Char(65+Loop)+':\';
      If (GetDriveType(PChar(Work))=DRIVE_CDROM) Then
      Begin
        Result := Result+Char(65+Loop);
      End;
    End;
  End;
End;

procedure TForm1.FormCreate(Sender: TObject);

Var
  CDs                   : String;
  Loop                  : Integer;

begin
//*****************************************************************************
// Get a list of CD drives.
//*****************************************************************************
  CDs := CDList;
  ComboBox1.Clear;
//*****************************************************************************
// Fill a combo box with a list of drives.  I append : to the drive letter
// here, to make working with the TMediaPlayer later that much easier.
//*****************************************************************************
  For Loop := 1 To Length(CDs) Do
  Begin
    ComboBox1.Items.Add(Copy(CDs,Loop,1)+':');
  End;
end;

procedure TForm1.ComboBox1Change(Sender: TObject);

begin
//*****************************************************************************
// DeviceID is 0 when the device is not OPEN.  If it is open, we need to
// stop and close it. This isn't required will all CD players (ie multi tray
// players like the NEC 4x4), but if you have multiple physical CDs, like I
// have, then it is required.
//*****************************************************************************
  If (MediaPlayer1.DeviceId<>0) Then
  Begin
    MediaPlayer1.Stop;
    MediaPlayer1.Close;
  End;
//*****************************************************************************
// Here is the magic that makes the mediaplayer use a different drive.
//*****************************************************************************
  MediaPlayer1.FileName := ComboBox1.Text;
//*****************************************************************************
// Obviously no good without openning it!
//*****************************************************************************
  MediaPlayer1.Open;
end;

end.