你的第一个MP3 player

来源:互联网 发布:淘宝康泽大药房 编辑:程序博客网 时间:2024/04/30 08:56

 

第一章:想放MP3?Mediaplayer足够了!

时不我待

或许你知道MP3是一种音乐格式,在这里,空声音的白占数据被删掉了,其余部分被压缩了。压缩效果是摄人的:一个64MB的声音文件压缩到4MB左右。

那些日子MP3是大势所趋。至于你信不信,MP3播放已经成为多媒体当中的重要任务之一。

还等什么?让我们看看如何在Delphi中,用控件TMediaplayer如何造一个播放器吧。这篇文章会告诉你,如何从MP3文件中解压出信息。最流行的一种标签加密就是ID3了。一首歌的标题、作者、专辑、年份等等都在这里存储。

切记:想写一个MP3播放器,我们有许多可能。首选,我们要写一段代码,它可以遥控一个像winamp这样的应用;次选,我们可以用一个第三方插件Xaudio;下策,我们可以用DirectX或者IMediaControl去写一个播放器。

 

用TMediaplayer?

 

还记得mediaplayer吗?咦,它不是不支持MP3播放吗?俱往矣!

VCL提供了TMediaplayer这个控件,它支持许多格式的媒体。阅读delphi帮助,你会发现:‘Mediaplayer准许你的应用去控制一个媒体录放设备,比如CD-ROM播放器,视频播放器,MIDI序列器’。

 

MediaPlayer on Screen
 

 

在Mediaplayer的属性中,有一个DeviceType。这个属性限制/约束了多媒体设备的种类。这个属性枚举了好几个选项,但没有一个是针对MP3的。每当我们把Mediaplayer拖到窗体上,默认地,这个属性就被设为Autoselect,意为随着文件扩展名作出自动选择。在设计时光,我们用“打开文件”对话框去设置“文件名”这个属性。在对话框的“文件类型”里,MP3类型也是没有的,只有AVI、MIDI、WAVE而已。由此我们就可以推出,Mediaplayer是无法播放MP3的——错!

 

第二章:使用工程GUI

手把手

新工程建立后,默认建立窗体名为Form1。这是我们播放器工程需要的唯一窗体。

我们的MP3播放器,绝不仅仅是播放MP3声音就行了的,还要它能显示(甚至改变)MP3文件的ID3标签。这还不算,播放时应该有个进度条。

请把以下控件拖进唯一窗体:

 

From the Standard tab:

  • one ListBox component, name it 'mp3List'
  • one GroupBox component containing six Edit components (names: edTitle, edArtist, edAlbum, edYear, edGenre, edComment),
    From the Additional tab:
  • one BitBtn component, name it 'btnOpenFolder',
  • one StaticText component, name it 'txtFolder',
    From the System tab
  • one Timer component, name it 'ProgresTimer'.
  • one ProgressBar component, name it 'Progres'.
  • and of course, one MediaPlayer component, name it 'mp3Player'.

     

    mp3 player at design time
     

    提示,当你把Mediaplayer拖进窗体时,其9键都是可见的。这个播放器很简单,不是所有的按键都需要的。那么,你把VisibleButtons属性中的三个键设为不可见:Play、Pause、Stop。

    那个bitbtn和StaticText(统计框)是用来选择和显示MP3所在文件夹的。Listbox用来显示MP3文件名。我为什么用bitbtn而不用传统button呢,一言以蔽之:因为bitbtn属性丰富,上面可以画图。

    几个Edit用于显示ID3标签的不同项目。

    Timer(定时器),进度条需要它发出“移动”的信号。

     

     

    第三章:用工程代码

    即便是再简单的工程,也需要写代码。方才,我们已经写了GUI,现在是让程序动态起来的时候了。

    选择MP3占用的文件夹

    如前文所表,btnopenfolder和txtfolder是用来选择和显示MP3文件列表的。Delphi为用户提供了很多选择音乐的可能性。OpenDialog控件封装了一个对话框,这个对话框用于打开文件。另一种,就是用SHBrowseForFolder这个Windows API函数,调用一个Windows系统对话框,用来浏览文件及文件夹。第二种选择门儿清是btnOpenFolder的onclick事件。这里展示代码的最后一部分。

    指定文件夹内,所有的MP3文件都显示在MP3list(其控件类型是ListBox)上。要列所有的MP3文件,我们用 Searching For Files 一文中提到的方法。

     

    显示播放进度

    这个简单。

    TMediaPlayer的Position属性,就是一首歌登时的播放位置。Length属性表示了该歌的长度,以TimeFormat属性为标尺。当用户选择了一首歌,下面的配置就完成了。

    ... Progress.Max := 0; {code to open a mp3 song} Progress.Max := mp3player.Length;...


     

    而在Timer(名字叫ProgressTimer)控件的ontime事件中,我们有:

    procedure TForm1.ProgresTimerTimer         (Sender: TObject);begin if Progress.Max <> 0 then   Progress.Position := mp3player.Position;end;


    演奏MP3文件

    你可以不信。这是最便捷的一条路。既然,TMediaPlayer已经生而知之,在用户按下Play、Stop(在MediaPlayer外观上)时候,该怎么做。那么,我们需要做的就是,准备MediaPlayer接受一个MP3文件。我会给你整个无返函数,每当用户选择一首歌时,它就会启动:

    procedure TForm1.mp3ListClick(Sender: TObject); var mp3File:string;begin //if the list is empty don't do anything if mp3List.Items.Count=0 then exit; //file is FolderName + FileName mp3File := Concat(txtFolder.Caption,                   mp3List.Items.Strings                       [mp3List.ItemIndex]);  //Chechk again if it exists if not FileExists(mp3File) then begin  ShowMessage('MP3 file does not exist?!');  exit; end; //used to display the ID3 tag information FillID3TagInformation (mp3File,                        edTitle,                        edArtist,                        edAlbum,                        edYear,                        edGenre,                        edComment); Progress.Max:=0; mp3player.Close; mp3player.FileName:=mp3File; mp3player.Open; Progress.Max := mp3player.Length;end;


     

    mp3Player at run time

     



    第四章:MP3的ID3编辑

    有一件东西让MP3比CD更令人喜闻乐见,那就是ID3。每个正常的MP3文件,有自己的数据,关于艺术家、歌名、歌曲所属专辑、专辑出版年份。这个标签,大概要在MP3文件中占去128bytes的容量。

    ID3标签以字符串“TAG”开头,如果该数据缺席,意味着ID3已经被干掉了,或者从来就没写上过。但是不要着急,我们可以修补之。

    ID3标签是这样描写的:

    type TID3Rec = packed record  Tag     : array[0..2] of Char;  Title,  Artist,  Comment,  Album   : array[0..29] of Char;  Year    : array[0..3] of Char;  Genre   : Byte;end;

    注意:上面代码中的Genre(体裁),只占用一个byte。这个byte可以如下翻译:


    const  MaxID3Genre=147;  ID3Genre: array[0..MaxID3Genre] of string = (    'Blues', 'Classic Rock', 'Country', 'Dance',     ...    'Synthpop'  {and probably more to come}  );


    读取ID3


    欲读取之,我们必须使用IO例程。


    procedure FillID3TagInformation(mp3File: string;Title,Artist,Album,Year,Genre,Comment:TEdit);var ID3 : TID3Rec;    fmp3: TFileStream;begin  fmp3:=TFileStream.Create(mp3File, fmOpenRead);  try    fmp3.position:=fmp3.size-128;    fmp3.Read(ID3,SizeOf(ID3));  finally    fmp3.free;  end; if ID3.Tag <> 'TAG' then begin   Title.Text:='Wrong or no ID3 tag information';   Artist.Text:='Wrong or no ID3 tag information';   Album.Text:='Wrong or no ID3 tag information';   Year.Text:='Wrong or no ID3 tag information';   Genre.Text:='Wrong or no ID3 tag information';   Comment.Text:='Wrong or no ID3 tag information'; end else begin   Title.Text:=ID3.Title;   Artist.Text:=ID3.Artist;   Album.Text:=ID3.Album;   Year.Text:=ID3.Year;   if ID3.Genre in [0..MaxID3Genre] then     Genre.Text:=ID3Genre[ID3.Genre]   else     Genre.Text:=IntToStr(ID3.Genre);   Comment.Text:=ID3.Comment end;end;

    这段代码,使用了TFileStream,来接触MP3文件信息。当我们成功地打开一个MP3文件,将进度调到离末端还有128byte的位置,然后就读到了ID3。余下密码,就是先看ID3信息是否存在,只要存在就开始赋值。

    刻写ID3

    这个代码也简单。下面是无返函数:

    procedure ChangeID3Tag  (NewID3: TID3Rec; mp3FileName: string);

    留神,在工程代码中,这段函数的呼唤,没有被覆盖。这个还是留给你自己去实施吧。


    尾声

    终于,我们做到了!你自己的坑钱播放器终于开动了!现在我要做的,就是要让它演奏,对它做一点小修补。加设几种皮肤怎么样?请参见我的custom shaped forms一文。你还可以有很多创意。让大家共赏你的制作吧,把它贴到Free Applications板块上来。




    {Article: Your first MP3 Delphi playerhttp://delphi.about.com/library/weekly/aa112800a.htmSee how to build a full-blown mp3 player with Delphiin just a few seconds. Even more: get the ID3 taginformation from a mp3 file and change it!For the .zip file of this project click here.}unit Unit1;interfaceuses  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,  StdCtrls, Buttons, MPlayer, ComCtrls, ExtCtrls;type  TForm1 = class(TForm)    mp3player: TMediaPlayer;    mp3List: TListBox;    btnOpenFolder: TBitBtn;    GroupBox1: TGroupBox;    edTitle: TEdit;    edArtist: TEdit;    edAlbum: TEdit;    edYear: TEdit;    edGenre: TEdit;    edComment: TEdit;    Label1: TLabel;    Label2: TLabel;    Label3: TLabel;    Label4: TLabel;    Label5: TLabel;    Label6: TLabel;    txtFolder: TStaticText;    Progress: TProgressBar;    ProgresTimer: TTimer;    procedure btnOpenFolderClick(Sender: TObject);    procedure mp3ListClick(Sender: TObject);    procedure FormCreate(Sender: TObject);    procedure ProgresTimerTimer(Sender: TObject);  private    { Private declarations }  public    { Public declarations }  end;var  Form1: TForm1;type  TID3Rec = packed record    Tag     : array[0..2] of Char;    Title,    Artist,    Comment,    Album   : array[0..29] of Char;    Year    : array[0..3] of Char;    Genre   : Byte;  end;const  MaxID3Genre=147;  ID3Genre: array[0..MaxID3Genre] of string = (    'Blues', 'Classic Rock', 'Country', 'Dance', 'Disco', 'Funk', 'Grunge',    'Hip-Hop', 'Jazz', 'Metal', 'New Age', 'Oldies', 'Other', 'Pop', 'R&B',    'Rap', 'Reggae', 'Rock', 'Techno', 'Industrial', 'Alternative', 'Ska',    'Death Metal', 'Pranks', 'Soundtrack', 'Euro-Techno', 'Ambient',    'Trip-Hop', 'Vocal', 'Jazz+Funk', 'Fusion', 'Trance', 'Classical',    'Instrumental', 'Acid', 'House', 'Game', 'Sound Clip', 'Gospel',    'Noise', 'AlternRock', 'Bass', 'Soul', 'Punk', 'Space', 'Meditative',    'Instrumental Pop', 'Instrumental Rock', 'Ethnic', 'Gothic',    'Darkwave', 'Techno-Industrial', 'Electronic', 'Pop-Folk',    'Eurodance', 'Dream', 'Southern Rock', 'Comedy', 'Cult', 'Gangsta',    'Top 40', 'Christian Rap', 'Pop/Funk', 'Jungle', 'Native American',    'Cabaret', 'New Wave', 'Psychadelic', 'Rave', 'Showtunes', 'Trailer',    'Lo-Fi', 'Tribal', 'Acid Punk', 'Acid Jazz', 'Polka', 'Retro',    'Musical', 'Rock & Roll', 'Hard Rock', 'Folk', 'Folk-Rock',    'National Folk', 'Swing', 'Fast Fusion', 'Bebob', 'Latin', 'Revival',    'Celtic', 'Bluegrass', 'Avantgarde', 'Gothic Rock', 'Progressive Rock',    'Psychedelic Rock', 'Symphonic Rock', 'Slow Rock', 'Big Band',    'Chorus', 'Easy Listening', 'Acoustic', 'Humour', 'Speech', 'Chanson',    'Opera', 'Chamber Music', 'Sonata', 'Symphony', 'Booty Bass', 'Primus',    'Porn Groove', 'Satire', 'Slow Jam', 'Club', 'Tango', 'Samba',    'Folklore', 'Ballad', 'Power Ballad', 'Rhythmic Soul', 'Freestyle',    'Duet', 'Punk Rock', 'Drum Solo', 'Acapella', 'Euro-House', 'Dance Hall',    'Goa', 'Drum & Bass', 'Club-House', 'Hardcore', 'Terror', 'Indie',    'BritPop', 'Negerpunk', 'Polsk Punk', 'Beat', 'Christian Gangsta Rap',    'Heavy Metal', 'Black Metal', 'Crossover', 'Contemporary Christian',    'Christian Rock', 'Merengue', 'Salsa', 'Trash Metal', 'Anime', 'Jpop',    'Synthpop'  {and probably more to come}  );implementationuses ShellAPI, ShlObj;  // needed for the BrowseForFolder function{$R *.DFM}procedure FillID3TagInformation(mp3File:string; Title,Artist,Album,Year,Genre,Comment:TEdit);var //fMP3: file of Byte;    ID3 : TID3Rec;    fmp3: TFileStream;begin  fmp3:=TFileStream.Create(mp3File, fmOpenRead);  try    fmp3.position:=fmp3.size-128;    fmp3.Read(ID3,SizeOf(ID3));  finally    fmp3.free;  end; { or the non Stream approach - as in ChangeID3Tag procedure try   AssignFile(fMP3, mp3File);   Reset(fMP3);   try     Seek(fMP3, FileSize(fMP3) - 128);     BlockRead(fMP3, ID3, SizeOf(ID3));   finally   end; finally   CloseFile(fMP3); end; } if ID3.Tag <> 'TAG' then begin   Title.Text:='Wrong or no ID3 tag information';   Artist.Text:='Wrong or no ID3 tag information';   Album.Text:='Wrong or no ID3 tag information';   Year.Text:='Wrong or no ID3 tag information';   Genre.Text:='Wrong or no ID3 tag information';   Comment.Text:='Wrong or no ID3 tag information'; end else begin   Title.Text:=ID3.Title;   Artist.Text:=ID3.Artist;   Album.Text:=ID3.Album;   Year.Text:=ID3.Year;   if ID3.Genre in [0..MaxID3Genre] then     Genre.Text:=ID3Genre[ID3.Genre]   else     Genre.Text:=IntToStr(ID3.Genre);   Comment.Text:=ID3.Comment end;end;procedure ChangeID3Tag(NewID3: TID3Rec; mp3FileName: string);var  fMP3: file of Byte;  OldID3 : TID3Rec;begin  try    AssignFile(fMP3, mp3FileName);    Reset(fMP3);    try      Seek(fMP3, FileSize(fMP3) - 128);      BlockRead(fMP3, OldID3, SizeOf(OldID3));      if OldID3.Tag = 'TAG' then        { Replace old tag }        Seek(fMP3, FileSize(fMP3) - 128)      else        { Append tag to file because it doesn't exist }        Seek(fMP3, FileSize(fMP3));      BlockWrite(fMP3, NewID3, SizeOf(NewID3));    finally    end;  finally    CloseFile(fMP3);  end;end;procedure FillMP3FileList(Folder: string; sl: TStrings);var Rec : TSearchRec;begin sl.Clear; if SysUtils.FindFirst(Folder + '*.mp3', faAnyFile, Rec) = 0 then  try    repeat      sl.Add(Rec.Name);    until SysUtils.FindNext(Rec) <> 0;  finally    SysUtils.FindClose(Rec);  end;end;function BrowseDialog(const Title: string; const Flag: integer): string;var  lpItemID : PItemIDList;  BrowseInfo : TBrowseInfo;  DisplayName : array[0..MAX_PATH] of char;  TempPath : array[0..MAX_PATH] of char;begin  Result:='';  FillChar(BrowseInfo, sizeof(TBrowseInfo), #0);  with BrowseInfo do begin    hwndOwner := Application.Handle;    pszDisplayName := @DisplayName;    lpszTitle := PChar(Title);    ulFlags := Flag;  end;  lpItemID := SHBrowseForFolder(BrowseInfo);  if lpItemId <> nil then begin    SHGetPathFromIDList(lpItemID, TempPath);    Result := IncludeTrailingBackslash(TempPath);    GlobalFreePtr(lpItemID);  end;end;procedure TForm1.btnOpenFolderClick(Sender: TObject);var mp3Folder : string;begin mp3Folder := BrowseDialog('Choose a folder with mp3 files', BIF_RETURNONLYFSDIRS); if mp3Folder = '' then Exit; txtFolder.Caption := mp3Folder; FillMP3FileList(mp3Folder, mp3List.Items);end;procedure TForm1.mp3ListClick(Sender: TObject); var mp3File: string;begin  if mp3List.Items.Count=0 then exit;  mp3File := Concat(txtFolder.Caption, mp3List.Items.Strings[mp3List.ItemIndex]);  if not FileExists(mp3File) then begin   ShowMessage('MP3 file '+#13#10+ mp3File +#13#10+'does not exist!');   exit;  end;  FillID3TagInformation(mp3File, edTitle, edArtist, edAlbum, edYear, edGenre, edComment);  Progress.Max:=0;  mp3player.Close;  mp3player.FileName:=mp3File;  mp3player.Open;  Progress.Max := mp3player.Length;end;procedure TForm1.FormCreate(Sender: TObject);begin  txtFolder.Caption := ExtractFilePath(Application.ExeName);  FillMP3FileList(txtFolder.Caption, mp3List.Items);  Progress.Max:=0;end;procedure TForm1.ProgresTimerTimer(Sender: TObject);begin  if Progress.Max<>0 then    Progress.Position := mp3player.Position;end;end.{********************************************Zarko GajicAbout.com Guide to Delphi Programminghttp://delphi.about.comemail: delphi.guide@about.comfree newsletter: http://delphi.about.com/library/blnewsletter.htmforum: http://forums.about.com/ab-delphi/start/********************************************}




  • 原创粉丝点击