Author Topic: Tags Library  (Read 70265 times)

Steve Grant

  • Posts: 159
Re: Tags Library
« Reply #450 on: 30 Jan '18 - 14:16 »
Thanks for that I will give it a try, however for the moment I have gone back to Bass getting bitrate etc.

I have a load of mp3's that Bass/dbPoweramp/mp3tag all return the correct bitrate. Tagslib returns 0. (They all play fine).

Here is one for you to play with https://www.dropbox.com/s/3g09fur83dtruav/01%20-%20Marina%20%26%20The%20Diamonds%20-%20Radioactive.mp3?dl=1

Steve Grant

  • Posts: 159
Re: Tags Library
« Reply #451 on: 30 Jan '18 - 14:48 »
Just for your info I am turning on the relevant config thus;

   
Code: [Select]
    TagsLibrary_SetConfig lTags, 1, 2, ttAutomatic
    TagsLibrary_SetConfig lTags, 1, 3, ttAutomatic
    TagsLibrary_SetConfig lTags, 1, 5, ttAutomatic
    TagsLibrary_SetConfig lTags, 1, 6, ttAutomatic
    TagsLibrary_SetConfig lTags, 1, 7, ttAutomatic

It makes no difference.

Steve Grant

  • Posts: 159
Re: Tags Library
« Reply #452 on: 1 Feb '18 - 15:59 »
Hi again,

I've come across a file that TagsLib simply will not write to. Even the old AudioGenie that I used to use before TagsLib will write to it.

I've checked the file properties also the security and all is ok.

https://www.dropbox.com/s/x6iecjjugu4r4wa/CD1423Track76.mp3?dl=1

3delite

  • Posts: 925
Re: Tags Library
« Reply #453 on: 2 Feb '18 - 23:14 »
Uploaded an update that fixes that 2 files, please download it, link in the first post.

The config option should be like:

Code: [Select]
TagsLibrary_SetConfig lTags, 1, TAGSLIBRARY_DEEP_OPUS_BITRATE_SCAN, ttAutomatic

Please check if the function returns a non-zero value, 0 means failure.

But looking at the code, sorry, it only works explicitly with Opus files (probably that's why the name contains 'Opus', it was a long ago that I coded this), and for Vorbis files only used when the 'BitRateNominal' is 0 by the Vorbis header.

Steve Grant

  • Posts: 159
Re: Tags Library
« Reply #454 on: 3 Feb '18 - 13:58 »
That works great thank you very much.

The config option should be like:

Code: [Select]
TagsLibrary_SetConfig lTags, 1, TAGSLIBRARY_DEEP_OPUS_BITRATE_SCAN, ttAutomatic

Please check if the function returns a non-zero value, 0 means failure.

Yes the function returns non-zero, after all
Code: [Select]
Public Const TAGSLIBRARY_DEEP_OPUS_BITRATE_SCAN As Long = 7means I can use TAGSLIBRARY_DEEP_OPUS_BITRATE_SCAN or 7.

Whilst we are having so much luck here are two more, they both return 128 but 1) should be 144 and 2) should be 260!

files removed 08/02/2018
« Last Edit: 8 Feb '18 - 11:51 by Steve Grant »

3delite

  • Posts: 925
Re: Tags Library
« Reply #455 on: 9 Feb '18 - 00:30 »
Yes, TAGSLIBRARY_DEEP_OPUS_BITRATE_SCAN = 7, but it's always a better idea to use the const names.

MP3 (MPEG) files are only processed by the first MPEG frame. So the bit rate is only valid for CBR MPEG files. Please use BASS_ChannelGetAttribute() with the BASS_ATTRIB_BITRATE option for getting the bit rate. You probably want to get the playtime anyway by BASS already so no need to scan the file twice.

EWeiss

  • Posts: 364
Re: Tags Library
« Reply #456 on: 12 Apr '18 - 15:01 »
Hi 3delite

one question can you write Tags for mp3 in this Format?
https://msdn.microsoft.com/en-us/library/windows/desktop/dd743220(v=vs.85).aspx

so it's compatible to WMP to ?
i can not see any cover in WIndowsMediaplayer_10 Plugin while the original Tag of mp3 is not supported.

i am missing private Frames in the Tag like this!
without the cover not show..

see Picture WMP.png

also what i am missing.. is write this to the Tag! (mp3Files)

PRIV WM/MediaClassSecondaryID
PRIV WM/MediaClassPrimaryID
PRIV WM/WMContentID
PRIV WM/WMCollectionID
PRIV WM/WMCollectionGroupID
PRIV WM/UniqueFileIdentifier

greets
« Last Edit: 13 Apr '18 - 09:48 by EWeiss »

3delite

  • Posts: 925
Re: Tags Library
« Reply #457 on: 13 Apr '18 - 10:49 »
ID3v2.2 format tag is only supported for loading, they are converted to according ID3v2.3/4 frames. When saving back these tags they are written in ID3v2.3 format.
id3.org says ID3v2.2 format is obsolete and should not be used.
Check "function TID3v2Tag.Convertv2Tov3()" in ID3v2Library.pas for the conversion details.

EWeiss

  • Posts: 364
Re: Tags Library
« Reply #458 on: 13 Apr '18 - 14:16 »
ID3v2.2 format tag is only supported for loading, they are converted to according ID3v2.3/4 frames. When saving back these tags they are written in ID3v2.3 format.
id3.org says ID3v2.2 format is obsolete and should not be used.
Check "function TID3v2Tag.Convertv2Tov3()" in ID3v2Library.pas for the conversion details.

i hope your understand what i want?
i want add the
PRIV WM/MediaClassSecondaryID Frames to my mp3 Files so Bliss\Windowsmediaplayer_10.dll Visualization read my cover correctly.
without use WMP for Tagging.

EDIT:
It's finish Forget it also.. thanks
i have create it self with help of other People to

that is what i want.. see shot.
Show AlbumArt without taggin over WMP.

greets
« Last Edit: 14 Apr '18 - 13:44 by EWeiss »

3delite

  • Posts: 925
Re: Tags Library
« Reply #459 on: 16 Apr '18 - 20:37 »
Sorry, not clear to me.

You want to add an ID3v2 PRIV frame that contains a GUID?

If this is the case, you can set it with something like:

Code: [Select]
procedure TForm1.Button3Click(Sender: TObject);
var
    Error: Integer;
    GUID: TGUID;
    GUIDString: ANSIString;
    FrameIndex: Integer;
    TagData: TTagData;
begin
    CreateGUID(GUID);
    GUIDString := GUIDToString(GUID);

    FrameIndex := TagsLibrary_AddTag(Tags, PChar('PRIV'), PChar(''), ttID3v2); //* Add an empty PRIV frame

    TagData.Name := PChar('PRIV');
    TagData.Data := PChar(GUIDString); //* Pointer to first byte of the data
    TagData.DataSize := Length(GUIDString); //* Size of this data
    TagData.DataType := 0; //* Not used for ID3v2

    TagsLibrary_SetTagData(Tags, FrameIndex, ttID3v2, TagData);

    Error := TagsLibrary_Save(Tags, PWideChar(Edit1.Text), ttID3v2);
end;

It's possible to create any frame with any data this way if this is what you wish.

EDIT: So a PRIV frame should be like this correctly:

Code: [Select]
procedure TForm1.Button3Click(Sender: TObject);
var
    Error: Integer;
    GUID: TGUID;
    GUIDString: ANSIString;
    FrameIndex: Integer;
    TagData: TTagData;
begin
    CreateGUID(GUID);
    GUIDString := 'WM/MediaClassSecondaryID' + #0 + GUIDToString(GUID);

    FrameIndex := TagsLibrary_AddTag(Tags, PChar('PRIV'), PChar(''), ttID3v2); //* Add an empty PRIV frame

    TagData.Name := PChar('PRIV');
    TagData.Data := PChar(GUIDString); //* Pointer to first byte of the data
    TagData.DataSize := Length(GUIDString); //* Size of this data
    TagData.DataType := 0; //* Not used for ID3v2

    TagsLibrary_SetTagData(Tags, FrameIndex, ttID3v2, TagData);

    Error := TagsLibrary_Save(Tags, PWideChar(Edit1.Text), ttID3v2);
end;
« Last Edit: 16 Apr '18 - 20:59 by 3delite »

EWeiss

  • Posts: 364
Re: Tags Library
« Reply #460 on: 17 Apr '18 - 21:40 »
yes that is what i want but i have do it self

Code: [Select]
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, mp3FileUtils, StdCtrls, ShlObj, ActiveX;

const
  cMP3Error : Array[TMP3Error] of String = (
    'MP3ERR_None',
    'MP3ERR_NoFile',
    'MP3ERR_FOpenCrt',
    'MP3ERR_FOpenR',
    'MP3ERR_FOpenRW',
    'MP3ERR_FOpenW',
    'MP3ERR_SRead',
    'MP3ERR_SWrite',
    'ID3ERR_Cache',
    'ID3ERR_NoTag',
    'ID3ERR_Invalid_Header',
    'ID3ERR_Compression',
    'ID3ERR_Unclassified',
    'MPEGERR_NoFrame'
   );

type
  TForm1 = class(TForm)
    btnSearch: TButton;
    edSearchpath: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    lblProgress: TLabel;
    btnDoIt: TButton;
    Label3: TLabel;
    lblCount: TLabel;
    btnHelp: TButton;
    lstSearch: TListBox;
    lblMessage: TLabel;
    lblMessageVal: TLabel;
    procedure btnSearchClick(Sender: TObject);
    procedure btnHelpClick(Sender: TObject);
    procedure btnDoItClick(Sender: TObject);
    procedure lstSearchDrawItem(Control: TWinControl; Index: Integer; Rect: TRect;
      State: TOwnerDrawState);
  private
    { Private-Deklarationen }
    failed: array of BOOL;
    ProgressCounter: Integer;
    procedure WinProcessMessages;
    procedure SetCoverpic(Picfile: string; mp3file: string; Index: Integer);
    function GetFolder(const ARoot: integer; const ACaption: String): String;
    procedure FindMediaFiles(const FileList: tstrings; RootFolder: string;
      Maske: array of string; Recurse: Boolean);
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnDoItClick(Sender: TObject);
var
  IntI: Integer;
  strCover, oldCover: string;
begin

  oldCover := '';
  ProgressCounter:= lstSearch.Count;

  for IntI := 0 to (lstSearch.Count - 1) do
  begin
    failed[IntI] := false;
    if oldCover <> ExtractFilePath(lstSearch.Items[IntI]) then
    begin
      strCover := ExtractFilePath(lstSearch.Items[IntI]);
      strCover := strCover + lowercase('AlbumArtSmall.jpg');
      oldCover := ExtractFilePath(lstSearch.Items[IntI]);
    end;

    if FileExists(strCover) then
    begin
      SetCoverpic(strCover, lstSearch.Items[IntI], IntI);
      dec(ProgressCounter);
      lblProgress.Caption := IntToStr(ProgressCounter);
      lblProgress.Repaint;
      lstSearch.Selected[IntI] := true;
    end else
    begin
      failed[IntI] := true;
      dec(ProgressCounter);
      lblProgress.Caption := IntToStr(ProgressCounter);
      lblProgress.Repaint;
      lstSearch.Repaint;
      lstSearch.Selected[IntI] := true;
    end;
  end;
end;

procedure TForm1.btnHelpClick(Sender: TObject);
var
  pString: string;
begin

  pString := 'the application writes PRIV: Frames to MP3 Tag' + #13#10 +
    'necessary to make the visual plugins for WMP work properly'  + #13#10 +
    'without having created the Mp3 tag via WMP (Windows Media Player)'  + #13#10 +
    'a file called AlbumArtSmall.jpg is required in the Search path' + #13#10 +
    #13#10 +
    'WARNING: after executing the function over DoIt, process can''t be stopped' + #13#10 +
    #13#10 +
    'Artist' + #13#10 +
    'Album' + #13#10 +
    'Title' + #13#10 +
    'Year' + #13#10 +
    'Track' + #13#10 +
    'Genre' + #13#10 +
    'Comment' + #13#10 +
     #13#10 +
    'are not changed' + #13#10 +
    'if your use more entries in the Mp3 tag' + #13#10 +
    'please close the application, or your data will be lost!';
  MessageBox(Handle, PWideChar(pString), 'Readme first', MB_OK);
end;

procedure TForm1.btnSearchClick(Sender: TObject);
var
  List: TStringList;
  IntI: Integer;
  Mask: array[0..0] of string;
begin
  edSearchpath.Text := GetFolder(CSIDL_DRIVES, 'Select search path');

  If edSearchpath.Text <> '' then
  begin
     if lstSearch.Count > 0 then
       lstSearch.clear;

     List := TStringList.Create;
     try
       Mask[0] := '.mp3';

       FindMediaFiles(List, edSearchpath.Text, Mask, True);
       lblCount.Caption := IntToStr(List.Count);
       ProgressCounter := List.Count;
       lblProgress.Caption := IntToStr(ProgressCounter);

       if List.Count > 0 then
       begin
         btnDoIt.Enabled := true;
         Setlength(failed, List.Count);
       end;

       for IntI := 0 to List.Count - 1 do
         lstSearch.Items.Add(List[IntI]);
     finally
       List.Free;
     end;
  end;
end;

procedure TForm1.FindMediaFiles(const FileList: tstrings; RootFolder: string;
  Maske: array of string; Recurse: Boolean);
var
  SR: TSearchRec;
  i: Integer;
begin

  RootFolder := IncludeTrailingPathDelimiter(RootFolder);

  if Recurse then
    if FindFirst(RootFolder + '*.*', faAnyFile, SR) = 0 then
      try
        repeat
          if SR.Attr and faDirectory = faDirectory then
            if (SR.Name <> '.') and (SR.Name <> '..') then
              FindMediaFiles(FileList, RootFolder + SR.Name, Maske, Recurse);
        until FindNext(SR) <> 0;
      finally
        FindClose(SR);
      end;
  i := 0;
  repeat
  begin
    if FindFirst(RootFolder + '*' + Maske[i], faAnyFile, SR) = 0 then
      try
        repeat
          if SR.Attr and faDirectory <> faDirectory then
          begin
            FileList.add(RootFolder + SR.Name);
          end;
        until FindNext(SR) <> 0;
      finally
        FindClose(SR);
      end;
    i := i + 1;
  end
  until i = high(Maske) + 1;
end;

function TForm1.GetFolder(const ARoot: integer; const ACaption: String): String;
var
  bi: TBROWSEINFO;
  lpBuffer: PChar;
  pidlPrograms,
  pidlBrowse: PItemIDList;
  ShellH: IMalloc;
begin
  pidlBrowse := nil;

  if (not SUCCEEDED(SHGetSpecialFolderLocation(GetActiveWindow,
                                               ARoot,
                                               pidlPrograms))) then
    Exit;
  try
    GetMem(lpBuffer, MAX_PATH);
    try
      bi.hwndOwner:=GetActiveWindow;
      bi.pidlRoot:=pidlPrograms;
      bi.pszDisplayName:=lpBuffer;
      bi.lpszTitle:=PChar(ACaption);
      bi.ulFlags:=BIF_RETURNONLYFSDIRS;
      bi.lpfn:=NIL;
      bi.lParam:=0;
      pidlBrowse:=SHBrowseForFolder(bi);

      if (pidlBrowse <> nil) and (SHGetPathFromIDList(pidlBrowse,
                                                      lpBuffer)) then
        Result:=lpBuffer;
    finally
      FreeMem(lpBuffer);
    end;
  finally
    if SHGetMalloc(ShellH) = NOERROR then
       ShellH.Free(pidlBrowse);
  end;
end;

procedure TForm1.lstSearchDrawItem(Control: TWinControl; Index: Integer; Rect: TRect;
  State: TOwnerDrawState);
begin
  with (Control as TListBox).Canvas do
  begin
    if not (odFocused in State) then
    begin
      if failed[Index] then
      begin
        Brush.Color:= clWebDarkOrange;
        Font.Color:= clBlack;
      end else
      begin
        Brush.Color := clWebLightGreen;
        Font.Color:= clBlack;
      end;
      FillRect(Rect);
      TextOut(Rect.Left + 2, Rect.Top, (Control as TListBox).Items[Index]);
    end;
  end;
end;

procedure TForm1.SetCoverpic(Picfile, mp3file: string; Index: Integer);
var
   MP3Tags, v23Tag: TId3v2Tag;
   PicData: TMemorystream;
   GUID: TMemorystream;
   error : TMP3Error;
   i: Integer;
   b: Byte;
begin
    MP3Tags:= TId3v2Tag.Create;
    v23Tag := TId3v2Tag.Create;
    PicData:= TMemoryStream.Create;
    GUID := TMemorystream.Create;

    try
      Error := MP3Tags.ReadFromFile(mp3File);
      If Error = MP3ERR_None then
      begin
        Picdata.LoadFromFile(Picfile);

        // basic converting
        v23Tag.Artist := MP3Tags.Artist;
        v23Tag.Album := MP3Tags.Album;
        v23Tag.Title := MP3Tags.Title;
        v23Tag.Year  := MP3Tags.Year;
        v23Tag.Track := MP3Tags.Track;
        v23Tag.Genre := MP3Tags.Genre;
        v23Tag.Comment := MP3Tags.Comment;

        if MP3Tags.Rating = 0 then
          v23Tag.Rating := 128 // 3 Sterne
        else
        v23Tag.Rating := MP3Tags.Rating;

        for i := 1 to 16 do
        begin
           b := Random(255);
           GUID.Write(b, 1);
        end;

        v23Tag.SetPrivateFrame('WM/WMCollectionID', GUID);
        v23Tag.SetPrivateFrame('WM/WMCollectionGroupID', GUID);

        v23Tag.SetPicture('image/jpeg', 0, '*', PicData);
        v23Tag.WriteToFile(mp3file);

      end else
        lblMessageVal.Caption := cMP3Error[Error];
    finally
      FreeAndNIL(MP3Tags);
      FreeAndNIl(PicData);
      FreeAndNIl(GUID);

      FreeAndNIl(v23Tag);
      Winprocessmessages;
    end;
end;

procedure TForm1.WinProcessMessages;
// Allow Windows to process other system messages
var
  ProcMsg: TMsg;
begin
  while PeekMessage(ProcMsg, 0, 0, 0, PM_REMOVE) do
  begin
    if (ProcMsg.message = WM_QUIT) then
      Exit;
    TranslateMessage(ProcMsg);
    DispatchMessage(ProcMsg);
  end;
end;

end.

thanks for your time..

greets

3delite

  • Posts: 925
Re: Tags Library
« Reply #461 on: 18 Apr '18 - 09:06 »
Ok then. :)

Steve Grant

  • Posts: 159
Re: Tags Library
« Reply #462 on: 20 May '18 - 07:13 »
Hi 3delite I have found another file that Tags Lib will not write to.

AudioGenie, dBpoweramp etc are all ok.

https://www.dropbox.com/s/7j0f0dhjrv5ocin/CD3081Track91.flac?dl=1

3delite

  • Posts: 925
Re: Tags Library
« Reply #463 on: 21 May '18 - 21:37 »
Quick test here seemed working fine.

Please check if you are using the latest version: https://www.3delite.hu/Object%20Pascal%20Developer%20Resources/download.html#tagslibrary

Also check if the file there is not read-only, btw. what error code does Tags Library give when saving the tags?

Steve Grant

  • Posts: 159
Re: Tags Library
« Reply #464 on: 22 May '18 - 21:04 »
Thank you for testing, I have no-one else I can ask for this. Inside my Save Tags function I call the LCMapStringW api to perform TitleCase for most of the fields. (Artist - Title etc).

Because you said Tags Lib was ok at your end, I tested further and found that the above api can be tripped up. I have stopped using this for a more manual method and all now seems well.

I check the main download page at least every 2 weeks to ensure I have the latest version.

Many thanks for your help and I am sorry for wasting your time. I have removed the track.

Steve.