Solved

Saving a file - HELP NEEDED URGENTLY!

Posted on 2004-03-24
15
289 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
Comment Utility

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
Comment Utility
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
Comment Utility
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
 
LVL 11

Expert Comment

by:shaneholmes
Comment Utility
SaveToFile(ListBox1.Items);
LoadFromFile(ListBox1.Items);

Shane
0
 

Author Comment

by:ryujingt3
Comment Utility
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
Comment Utility
holmesshane AT charter DOT net

Shane
0
 
LVL 11

Expert Comment

by:shaneholmes
Comment Utility
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
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
LVL 11

Expert Comment

by:shaneholmes
Comment Utility
This is where the TListView comes in....

Shane
0
 

Author Comment

by:ryujingt3
Comment Utility
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
Comment Utility
Im working on your example to give back to you now

Shane
0
 

Author Comment

by:ryujingt3
Comment Utility
Wow!!.

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

I really appreciate this.

Ian
0
 
LVL 11

Expert Comment

by:shaneholmes
Comment Utility
ah shoot, email me again, i deleted your email by mistake

Shane
0
 

Author Comment

by:ryujingt3
Comment Utility
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
Comment Utility
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
Comment Utility
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

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

763 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

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now