Solved

Saving a file - HELP NEEDED URGENTLY!

Posted on 2004-03-24
15
291 Views
Last Modified: 2010-04-05
Hi,

I have nearly finished designing an MP3 player within Delphi, it can currently play MP3 files and allow for the ID3 Tag to be edited and saved and I also wanted to make it so that the playlist could be saved and the icons are in place, I just don't know what to put there.

The Unit that deals with that is the Playlistunit and the code is as follows:

unit PlayListUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ImgList, ComCtrls, ToolWin, StdCtrls,EditTagUnit, Menus;


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}
  );



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;


type
  TPlayListForm = class(TForm)
    PlayListBox: TListBox;
    ToolBar1: TToolBar;
    ToolButton1: TToolButton;
    ToolButton2: TToolButton;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    ToolButton5: TToolButton;
    OpenPlayList: TOpenDialog;
    SavePlayList: TSaveDialog;
    ToolButton6: TToolButton;
    ToolButton7: TToolButton;
    ImageList1: TImageList;
    PopupMenu1: TPopupMenu;
    EditID3Tag1: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ToolButton4Click(Sender: TObject);
    procedure PlayListBoxDblClick(Sender: TObject);
    procedure EditID3Tag1Click(Sender: TObject);
    procedure ToolButton6Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    FileIndex : Integer; // link between filename and ID3 Tag
    FileNames : TstringList; // the filenames of the mp3 s
    CurrentFile : String;
    CurrentMP3Name : String; //id3tag nice

    procedure FillNewList;
    procedure OpenFolderScan;

    Procedure NextFile;
    Procedure PrevFile;


  end;

var
  PlayListForm: TPlayListForm;

implementation

{$R *.dfm}

uses ShellAPI, ShlObj,Unit1,StrUtils;  // needed for the BrowseForFolder function





Function GetID3TagString(mp3File:string):string;
var //fMP3: file of Byte;
    ID3 : TID3Rec;
    fmp3: TFileStream;
    ID3FileString : string;
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
     ID3FileString := extractfilename(mp3File) //if no id3 then take filename as name
  else
   ID3FileString := trim(ID3.Title) + ' - '+ trim(ID3.Artist) + '  '+ ID3.Album;

   result := ID3FileString;
end;



procedure TPlayListForm.FillNewList;
var
  index : integer;
  Name : String;
begin
   for index := 0 to FileNames.Count -1 do
   begin
      Name := GetID3TagString(FileNames[index]);
      playlistbox.Items.Add(inttostr(index)+'. '+Name);
   end;
end;




procedure FillMP3FileList(Folder: string; sl: TStrings);
var Rec : TSearchRec;
    FileName : String;
begin
 sl.Clear;
 if SysUtils.FindFirst(Folder + '*.mp3', faAnyFile, Rec) = 0 then
  try
    repeat
      sl.Add(Folder + 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 TPlayListForm.OpenFolderScan;
var mp3Folder : string;
begin
 mp3Folder := BrowseDialog('Choose a folder with mp3 files', BIF_RETURNONLYFSDIRS);
 if mp3Folder = '' then Exit;
 //fill the list box with mp3 files
 FillMP3FileList(mp3Folder, FileNames); // change to add files to our new list
end;



procedure TPlayListForm.FormCreate(Sender: TObject);
begin
   FileNames := TstringList.create; //create filename list
   FileIndex := 0;
end;



procedure TPlayListForm.FormDestroy(Sender: TObject);
begin
   FileNames.Free; // free filenames
end;



procedure TPlayListForm.ToolButton4Click(Sender: TObject);
begin
  OpenFolderScan;
  FillNewList;
end;

{Clicking on the button should bring up the playlist form so users can select songs}

procedure TPlayListForm.PlayListBoxDblClick(Sender: TObject);
begin
    FileIndex := PlayListBox.ItemIndex;
    CurrentFile := Filenames[PlayListBox.ItemIndex];
    CurrentMP3Name := PlayListBox.Items[PlayListBox.ItemIndex];
    Form1.PlayThisFile(CurrentFile,CurrentMP3Name);
end;

{Used to display details of next song in the list as well as all other songs that are
in the required MP3 format}

Procedure TPlayListForm.NextFile;
begin
    inc(FileIndex);
    if FileIndex > PlayListBox.items.count -1 then FileIndex := 0;
    PlayListBox.itemindex := FileIndex;
    CurrentFile := Filenames[FileIndex];
    CurrentMP3Name := PlayListBox.Items[FileIndex];
    Form1.PlayThisFile(CurrentFile,CurrentMP3Name);
end;
{Used to show song name as well as all songs that are in the chosen directory when
selecting a previous song}

Procedure TPlayListForm.PrevFile;
begin
    dec(FileIndex);
    if FileIndex < 0 then FileIndex := PlayListBox.items.count -1;
    PlayListBox.itemindex := FileIndex;
    CurrentFile := Filenames[FileIndex];
    CurrentMP3Name := PlayListBox.Items[FileIndex];
    Form1.PlayThisFile(CurrentFile,CurrentMP3Name);
end;




procedure TPlayListForm.EditID3Tag1Click(Sender: TObject);
Var
   Tagfilename : string;
begin
   Tagfilename := Filenames[PlayListBox.ItemIndex];  // Read song title
   EditTagForm.ShowTag(Tagfilename);         //Read Tag Information
   EditTagForm.ShowModal;                 // If successful then open Tag Edit Form
end;



procedure TPlayListForm.ToolButton6Click(Sender: TObject);
begin
   close;
end;

procedure TPlayListForm.ToolButton2Click(Sender: TObject);
begin

end; - OPEN A PLAYLIST FILE (no code obviously!)

procedure TPlayListForm.ToolButton3Click(Sender: TObject);
begin

end; - SAVE A PLAYLIST file (Again, no code!)

end.

The ToolButton2Click is the button to Open a Playlist file that has been saved and ToolButton3Click is used to save the songs currently displayed.

Is there anyone out there kind enough to provide me with the code needed or solution so that my program will be complete?.  A copy of the program if needed will be provided and I thank you for your help.

My email address is: ryujingt3@yahoo.com (if you would like a copy of the program to look at)

Thank you.
0
Comment
Question by:ryujingt3
  • 10
  • 5
15 Comments
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10670976

if the playlist is a listbox, then you can just use the TStrings methods

SaveToFile

or SaveToStream


 if SaveDialog1.Execute then
 MyListBox.Items.SaveToFile(SaveDialog1.FileName);

Are your icons different for each item in the listbox, or the same.

Shane

0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10670998
BTW, if you replace your TListBox with a TListView, you could also have the columns of information

(Name, Artist, Album, Genre, Size, etc).

Once you get this in place, its only a matter of code using your tag information to extract ID3 Tag from each song and populate the TListView

Shane
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10671042
Or if you want to use streams

procedure SaveToFile( FIleName: String; List: TStrings);
var
 MemStr: TMemoryStream;
begin
 MemStr:= TMemoryStream.Create;
 List.SaveToStream(MemStr);
 MemStr.SaveToFile(FileName);
 MemStr.Free;
end;

procedure LoadFromFile(FileName: String; List: TStrings);
var
 MemStr: TMemoryStream;
begin
 MemStr:= TMemoryStream.Create;
 MemStr.LoadFromFile(FileName);
 List.LoadFromStream(MemStr);
 MemStr.Free;
end;


Shane
0
Back Up Your Microsoft Windows Server®

Back up all your Microsoft Windows Server – on-premises, in remote locations, in private and hybrid clouds. Your entire Windows Server will be backed up in one easy step with patented, block-level disk imaging. We achieve RTOs (recovery time objectives) as low as 15 seconds.

 
LVL 11

Expert Comment

by:shaneholmes
ID: 10671052
SaveToFile(ListBox1.Items);
LoadFromFile(ListBox1.Items);

Shane
0
 

Author Comment

by:ryujingt3
ID: 10671091
Hi Shane,

Would you like a copy of my program to look at?.  If so then please provide me with an email address or email me and I will send it to you ASAP, the thing is, it took me six months and one tutorial and a lot of help to get where I am and I am not that technically minded in terms of Delphi, I'm sorry.

Ian
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10671115
holmesshane AT charter DOT net

Shane
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10671891
ok, using the two methods above, all you need to do is this


//save playlist button click here
procedure TPlayListForm.ToolButton3Click(Sender: TObject);
begin
 if SavePlayList.Execute then
 begin
  SaveToFile(AFileName, FileNames);
 end;
end;

//open playlist button click here
procedure TPlayListForm.ToolButton2Click(Sender: TObject);
begin
  if OpenPlayList.Execute then
 begin
  LoadFromFile(AFileName, FileNames);
  PLayListBox.Items.Assign(FileNames);
 end;
end;

Now, if you use FileNames (TStringList), you will have the paths as well.

If you replace FileNames with PlayListBox.Items, you will only have the name of the song.

Shane
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10672000
This is where the TListView comes in....

Shane
0
 

Author Comment

by:ryujingt3
ID: 10672046
Hi Shane,

One last request, and I thank you very much for the help give so far, but my Delphi skills are limited and when I try and compile it it says that 'Loadfrom File' is an undeclared identifier, where do I put the declaration?.  I know this might sound basic to you but now you know why it has taken me so long!.

Also. how would I replace FileNames with PlaylistBox.Items as you have mentioned as just showing the song name would be great!.

Thanks again and I apologise for being so Delphi illiterate.

Ian
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10672099
Im working on your example to give back to you now

Shane
0
 

Author Comment

by:ryujingt3
ID: 10672161
Wow!!.

You really are the best, thank you so much!!.

I really appreciate this.

Ian
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10672298
ah shoot, email me again, i deleted your email by mistake

Shane
0
 

Author Comment

by:ryujingt3
ID: 10672443
Hi,

Not a problem, email has been sent!.

Thanks again for all your time and effort, very much appreciated.

All the best.

Ian
0
 
LVL 11

Accepted Solution

by:
shaneholmes earned 125 total points
ID: 10672974
Below are all the changes I have made to your unit,

SHane


{******************************************************************}
procedure TPlayListForm.FillNewList;
var
  I : integer;
  Name : String;
  Item: TListItem;
begin
   for I := 0 to FileNames.Count -1 do
   begin
    Item:= lvwPlayList.Items.Add;
    Item.Caption:= GetID3TagString(FileNames[I]);
    Item.SubItems.Add(FileNames[I]);
   end;
end;
 {******************************************************************}


{******************************************************************}

procedure TPlayListForm.ToolButton3Click(Sender: TObject);
begin
 if SavePlayList.Execute then
 begin
  SaveToFile(lvwPlayList, SavePlayList.FileName);
 end;
end;

{******************************************************************}

procedure TPlayListForm.ToolButton2Click(Sender: TObject);
begin
 if OpenPlayList.Execute then
 begin
  LoadFromFile(lvwPlayList, OpenPlayList.FileName);
 end;
end;

{******************************************************************}

procedure TPlayListForm.lvwPlayListDblClick(Sender: TObject);
begin
 {******************************************************************}
 CurrentFile := lvwPlayList.Items[lvwPlayList.ItemIndex].SubItems[0];
 CurrentMP3Name := lvwPlayList.Items[lvwPlayList.ItemIndex].Caption;
 Form1.PlayThisFile(CurrentFile,CurrentMP3Name);
 {******************************************************************}
end;

 {******************************************************************}
procedure TPlayListForm.SaveToFile(AListView: TListView; AFileName: string);
var
  idxItem, idxSub, IdxImage: Integer;
  F: TFileStream;
  pText: PChar;
  sText: string;
  W, ItemCount, SubCount: Word;
  MySignature: array [0..2] of Char;
begin
  //Initialization
  with AListView do
  begin
    ItemCount := 0;
    SubCount  := 0;
    //****
    MySignature := 'LVF';
    //  ListViewFile
    F := TFileStream.Create(AFileName, fmCreate or fmOpenWrite);
    F.Write(MySignature, SizeOf(MySignature));

    if Items.Count = 0 then
      // List is empty
      ItemCount := 0
    else
      ItemCount := Items.Count;
    F.Write(ItemCount, SizeOf(ItemCount));

    if Items.Count > 0 then
    begin
      for idxItem := 1 to ItemCount do
      begin
        with Items[idxItem - 1] do
        begin
          //Save subitems count
          if SubItems.Count = 0 then
            SubCount := 0
          else
            SubCount := Subitems.Count;
          F.Write(SubCount, SizeOf(SubCount));
          //Save ImageIndex
          IdxImage := ImageIndex;
          F.Write(IdxImage, SizeOf(IdxImage));
          //Save Caption
          sText := Caption;
          w     := Length(sText);
          pText := StrAlloc(Length(sText) + 1);
          StrPLCopy(pText, sText, Length(sText));
          F.Write(w, SizeOf(w));
          F.Write(pText^, w);
          StrDispose(pText);
          if SubCount > 0 then
          begin
            for idxSub := 0 to SubItems.Count - 1 do
            begin
              //Save Item's subitems
              sText := SubItems[idxSub];
              w     := Length(sText);
              pText := StrAlloc(Length(sText) + 1);
              StrPLCopy(pText, sText, Length(sText));
              F.Write(w, SizeOf(w));
              F.Write(pText^, w);
              StrDispose(pText);
            end;
          end;
        end;
      end;
    end;
    F.Free;
  end;
end;

 {******************************************************************}
 {******************************************************************}

procedure TPlayListForm.LoadFromFile(AListView: TListView; AFileName: string);
var
  F: TFileStream;
  IdxItem, IdxSubItem, IdxImage: Integer;
  W, ItemCount, SubCount: Word;
  pText: PChar;
  PTemp: PChar;
  MySignature: array [0..2] of Char;
  sExeName: string;
begin
  with AListView do
  begin
    ItemCount := 0;
    SubCount  := 0;

    sExeName := ExtractFileName(AFileName);

    if not FileExists(AFileName) then
    begin
      MessageBox(Handle, PChar(sExeName), 'I/O Error', MB_ICONERROR);
      Exit;
    end;

    F := TFileStream.Create(AFileName, fmOpenRead);
    F.Read(MySignature, SizeOf(MySignature));

    if MySignature <> 'LVF' then
    begin
      MessageBox(Handle, PChar(sExeName), 'I/O Error', MB_ICONERROR);
      Exit;
    end;

    F.Read(ItemCount, SizeOf(ItemCount));
    Items.Clear;

    for idxItem := 1 to ItemCount do
    begin
      with Items.Add do
      begin
        //Read imageindex
        F.Read(SubCount, SizeOf(SubCount));
        //Read imageindex
        F.Read(IdxImage, SizeOf(IdxImage));
        ImageIndex := IdxImage;
        //Read the Caption
        F.Read(w, SizeOf(w));
        pText := StrAlloc(w + 1);
        pTemp := StrAlloc(w + 1);
        F.Read(pTemp^, W);
        StrLCopy(pText, pTemp, W);
        Caption := StrPas(pText);
        StrDispose(pTemp);
        StrDispose(pText);
        if SubCount > 0 then
        begin
          for idxSubItem := 1 to SubCount do
          begin
            F.Read(w, SizeOf(w));
            pText := StrAlloc(w + 1);
            pTemp := StrAlloc(w + 1);
            F.Read(pTemp^, W);
            StrLCopy(pText, pTemp, W);
            Items[idxItem - 1].SubItems.Add(StrPas(pText));
            StrDispose(pTemp);
            StrDispose(pText);
          end;
        end;
      end;
    end;
    F.Free;
  end;
end;

{******************************************************************}

end.
0
 

Author Comment

by:ryujingt3
ID: 10699434
Shane,

I have emailed you about this (sorry for the intrusion) but it seems I can no longer right-click a file and edit the ID3 Tag (the option appeared), how can it be reactivated?.

Thanks for all your help and support, I really value it.

Ian (ryujingt3)
0

Featured Post

Problems using Powershell and Active Directory?

Managing Active Directory does not always have to be complicated.  If you are spending more time trying instead of doing, then it's time to look at something else. For nearly 20 years, AD admins around the world have used one tool for day-to-day AD management: Hyena. Discover why

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
This video shows how to quickly and easily add an email signature for all users on Exchange 2016. The resulting signature is applied on a server level by Exchange Online. The email signature template has been downloaded from: www.mail-signatures…
This video shows how to use Hyena, from SystemTools Software, to bulk import 100 user accounts from an external text file. View in 1080p for best video quality.

809 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question