home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: Multimed / Multimed.zip / mp3osr05.zip / src / id3v1.pas < prev    next >
Pascal/Delphi Source File  |  1999-12-26  |  9KB  |  390 lines

  1. (*
  2.  * ID3v1 tag manipulations
  3.  *  (c) 1999 by Alexander Trunov, 2:5069/10, jnc@mail.ru
  4.  *)
  5.  
  6. (*
  7.  * updated on 1999/12/09
  8.  *)
  9.  
  10. unit ID3v1;
  11.  
  12. interface
  13.  
  14. uses
  15.   Objects;
  16.  
  17. {$I id3v1.inc}
  18.  
  19. const
  20.   headerSearchRange: Longint = 512; // for RIFF
  21.  
  22. type
  23.  
  24.   TChannelMode = (cmStereo, cmJointStereo, cmDualChannel, cmSingleChannel);
  25.  
  26.   PjID3v1 = ^TjID3v1;
  27.   TjID3v1 = record
  28.     Songname,
  29.       Artist,
  30.       Album,
  31.       Comment: string[30];
  32.     Year: string[4];
  33.     Genre: Byte;
  34.     hProtected,             // protected by CRC
  35.       hCopyrighted,         // copyrighted
  36.       hOriginal: Boolean;   // original version
  37.     hBitrate,               // kbps
  38.       hSampleRate,
  39.       hMPEGVersion,
  40.       hLayer: Longint;
  41.     hMode: TChannelMode;
  42.     Size, RiffHeaderSize: Longint;
  43.     isMpeg, tagExists: Boolean;
  44.   end;
  45.  
  46.   PTagData = ^TTagData;
  47.   TTagData = object(TObject)
  48.     tag: TjID3v1;
  49.     constructor Init(mp3: PStream);
  50.     procedure ReadTag;
  51.     procedure WriteTag;
  52.   private
  53.     oldTag: TID3v1;
  54.     localHeader: array [1..4] of Byte;
  55.     stream: PStream;
  56.   end;
  57.  
  58. function DecodeTag(const tag: PjID3v1): Byte;
  59. function IsThereATag(Where: PStream): Boolean;
  60.  
  61. implementation
  62.  
  63. uses
  64.   Strings, Codepage;
  65.  
  66. {$I trims.inc}
  67.  
  68. function DecodeTag(const tag: PjID3v1): Byte;
  69. var
  70.   line: string;
  71. begin
  72.   with tag^ do
  73.   begin
  74.     line := Songname + Album + Artist + Comment;
  75.     Result := DetermineCodepage(line);
  76.     case Result of
  77.       cpWin:
  78.         begin
  79.           Songname := Win2Alt(Songname);
  80.           Artist := Win2Alt(Artist);
  81.           Album := Win2Alt(Album);
  82.           Comment := Win2Alt(Comment);
  83.         end;
  84.       cpKoi:
  85.         begin
  86.           Songname := Koi2Alt(Songname);
  87.           Artist := Koi2Alt(Artist);
  88.           Album := Koi2Alt(Album);
  89.           Comment := Koi2Alt(Comment);
  90.         end;
  91.     end;
  92.   end;
  93. end;
  94.  
  95. function IsThereATag(Where: PStream): Boolean;
  96. var
  97.   sign: array[1..3] of Char;
  98. begin
  99.   IsThereATag := false;
  100.   Where^.Seek(Where^.GetSize - 128);
  101.   Where^.Read(sign, 3);
  102.   if (sign[1] = 'T') and (sign[2] = 'A') and (sign[3] = 'G') then
  103.   begin
  104.     IsThereATag := true;
  105.   end;
  106. end;
  107.  
  108. constructor TTagData.Init(mp3: PStream);
  109. begin
  110.   inherited Init;
  111.   stream := mp3;
  112. end;
  113.  
  114. procedure TTagData.ReadTag;
  115. var
  116.   line, s: string;
  117.   i: Integer;
  118.   noTag: Boolean;
  119.  
  120.  function MakeBitrate(hVersion, hLayer, hBitrate: Longint): Longint;
  121.  begin
  122.    Result := 0;
  123.  
  124.    case hVersion of
  125.      $10:
  126.        case hLayer of
  127.          $01:
  128.            case hBitrate of
  129.              01: Result := 032;
  130.              02: Result := 064;
  131.              03: Result := 096;
  132.              04: Result := 128;
  133.              05: Result := 160;
  134.              06: Result := 192;
  135.              07: Result := 224;
  136.              08: Result := 256;
  137.              09: Result := 288;
  138.              10: Result := 320;
  139.              11: Result := 352;
  140.              12: Result := 384;
  141.              13: Result := 416;
  142.              14: Result := 448;
  143.            end;
  144.          $02:
  145.            case hBitrate of
  146.              01: Result := 032;
  147.              02: Result := 048;
  148.              03: Result := 056;
  149.              04: Result := 064;
  150.              05: Result := 080;
  151.              06: Result := 096;
  152.              07: Result := 112;
  153.              08: Result := 128;
  154.              09: Result := 160;
  155.              10: Result := 192;
  156.              11: Result := 224;
  157.              12: Result := 256;
  158.              13: Result := 320;
  159.              14: Result := 284;
  160.            end;
  161.          $03:
  162.            case hBitrate of
  163.              01: Result := 032;
  164.              02: Result := 040;
  165.              03: Result := 048;
  166.              04: Result := 056;
  167.              05: Result := 064;
  168.              06: Result := 080;
  169.              07: Result := 096;
  170.              08: Result := 112;
  171.              09: Result := 128;
  172.              10: Result := 160;
  173.              11: Result := 192;
  174.              12: Result := 224;
  175.              13: Result := 256;
  176.              14: Result := 320;
  177.            end;
  178.        end;
  179.      $20, $25:
  180.        case hLayer of
  181.          $01:
  182.            case hBitrate of
  183.              01: Result := 032;
  184.              02: Result := 048;
  185.              03: Result := 056;
  186.              04: Result := 064;
  187.              05: Result := 080;
  188.              06: Result := 096;
  189.              07: Result := 112;
  190.              08: Result := 128;
  191.              09: Result := 144;
  192.              10: Result := 160;
  193.              11: Result := 176;
  194.              12: Result := 192;
  195.              13: Result := 224;
  196.              14: Result := 256;
  197.            end;
  198.          $02, $03:
  199.            case hBitrate of
  200.              01: Result := 008;
  201.              02: Result := 016;
  202.              03: Result := 024;
  203.              04: Result := 032;
  204.              05: Result := 040;
  205.              06: Result := 048;
  206.              07: Result := 056;
  207.              08: Result := 064;
  208.              09: Result := 080;
  209.              10: Result := 096;
  210.              11: Result := 112;
  211.              12: Result := 128;
  212.              13: Result := 144;
  213.              14: Result := 160;
  214.            end;
  215.        end;
  216.    end;
  217.  end;
  218.  
  219.  function MakeSampleRate(hVersion, hSampleRate: Longint): Longint;
  220.  begin
  221.    Result := 0;
  222.    case hVersion of
  223.      $10:
  224.        case hSampleRate of
  225.          0: Result := 44100;
  226.          1: Result := 48000;
  227.          2: Result := 32000;
  228.        end;
  229.      $20:
  230.        case hSampleRate of
  231.          0: Result := 22050;
  232.          1: Result := 24000;
  233.          2: Result := 16000;
  234.        end;
  235.      $25:
  236.        case hSampleRate of
  237.          0: Result := 11025;
  238.          1: Result := 12000;
  239.          2: Result := 08000;
  240.        end;
  241.    end;
  242.  end;
  243.  
  244. begin
  245.   if not IsThereATag(stream) then
  246.     noTag := true
  247.   else
  248.     noTag := false;
  249.  
  250.   stream^.Seek(0);
  251.   stream^.Read(localHeader, SizeOf(localHeader));
  252.  
  253.   stream^.Seek(stream^.GetSize - SizeOf(oldTag));
  254.   stream^.Read(oldTag, SizeOf(oldTag));
  255.  
  256.   // check for RIFF :-E~~
  257.  
  258.   if (localHeader[1] = Byte('R')) and
  259.     (localHeader[2] = Byte('I')) and
  260.     (localHeader[3] = Byte('F')) and
  261.     (localHeader[4] = Byte('F')) then
  262.   begin // RIFF processing
  263.     stream^.Seek(4);
  264.     tag.isMpeg := false;
  265.     while (not tag.isMpeg) and (stream^.GetPos <= headerSearchRange) do
  266.     begin
  267.       stream^.Read(localHeader, SizeOf(localHeader));
  268.       line := '';
  269.       for i := 1 to 4 do
  270.       begin
  271.         line := line + BinaryB(localHeader[i]);
  272.       end;
  273.       tag.isMpeg := Bin2Long(Copy(line, 1, 11)) = $7ff;
  274.       if not tag.isMpeg then
  275.         stream^.Seek(stream^.GetPos - 3);
  276.     end;
  277.     tag.RiffHeaderSize := stream^.GetPos - 3;
  278.   end
  279.   else
  280.   begin
  281.     tag.RiffHeaderSize := 0;
  282.   end;
  283.  
  284.   FillChar(tag, SizeOf(tag), 0);
  285.  
  286.   with tag do
  287.   begin
  288.  
  289.     Size := stream^.GetSize;
  290.  
  291.     SetLength(line, 0); // aka line := '';
  292.  
  293.     for i := 1 to 4 do
  294.     begin
  295.       line := line + BinaryB(localHeader[i]);
  296.     end;
  297.  
  298.     hProtected := line[16] = '0';
  299.     hCopyrighted := line[29] = '1';
  300.     hOriginal := line[30] = '1';
  301.  
  302.     case Bin2Long(line[12] + line[13]) of
  303.       0: hMPEGVersion := $25;
  304.       1: hMPEGVersion := $ff;
  305.       2: hMPEGVersion := $20;
  306.       3: hMPEGVersion := $10;
  307.     end;
  308.  
  309.     case Bin2Long(line[14] + line[15]) of
  310.       0: hLayer := $ff;
  311.       1: hLayer := $03;
  312.       2: hLayer := $02;
  313.       3: hLayer := $01;
  314.     end;
  315.  
  316.     hBitrate := MakeBitrate(hMPEGVersion, hLayer,
  317.       Bin2Long(line[17] + line[18] + line[19] + line[20]));
  318.  
  319.     hSampleRate := MakeSampleRate(hMPEGVersion,
  320.       Bin2Long(line[21] + line[22]));
  321.  
  322.     case Bin2Long(line[25] + line[26]) of
  323.       0: hMode := cmStereo;
  324.       1: hMode := cmJointStereo;
  325.       2: hMode := cmDualChannel;
  326.       3: hMode := cmSingleChannel;
  327.     end;
  328.  
  329.     isMpeg := Bin2Long(Copy(line, 1, 11)) = $7ff;
  330.  
  331.     tagExists := not noTag;
  332.     if noTag then
  333.       Exit;
  334.  
  335.     Songname := StrPas(PChar(@oldTag.Songname));
  336.     Artist := StrPas(PChar(@oldTag.Artist));
  337.     Album := StrPas(PChar(@oldTag.Album));
  338.     Year := StrPas(PChar(@oldTag.Year));
  339.     Comment := StrPas(PChar(@oldTag.Comment));
  340.     Genre := oldTag.Genre;
  341.     // vp was designed marazmatically ;-)
  342.     Songname := Trim(Songname);
  343.     Artist := Trim(Artist);
  344.     Album := Trim(Album);
  345.     Year := Trim(Year);
  346.     Comment := Trim(Comment);
  347.  
  348.     if Length(Songname) = 0 then
  349.       Songname := '?';
  350.     if Length(Artist) = 0 then
  351.       Artist := '?';
  352.     if Length(Album) = 0 then
  353.       Album := '?';
  354.     if Length(Year) = 0 then
  355.       Year := '?';
  356.     if Length(Comment) = 0 then
  357.       Comment := '?';
  358.  
  359.   end;
  360.  
  361. end;
  362.  
  363. procedure TTagData.WriteTag;
  364. var
  365.   buf30: array[1..30] of Char;
  366.   buf3: array[1..3] of Char;
  367.   buf4: array[1..4] of Char;
  368.   status: Integer;
  369. begin
  370.   FillChar(oldTag, SizeOf(oldTag), 00);
  371.   with tag do
  372.   begin
  373.     StrPCopy(PChar(@oldTag.Tag), 'TAG');
  374.     StrPCopy(PChar(@oldTag.Songname), Songname);
  375.     StrPCopy(PChar(@oldTag.Artist), Artist);
  376.     StrPCopy(PChar(@oldTag.Album), Album);
  377.     StrPCopy(PChar(@oldTag.Year), Year);
  378.     StrPCopy(PChar(@oldTag.Comment), Comment);
  379.     oldTag.Genre := Genre;
  380.   end;
  381.   if IsThereATag(stream) then
  382.     stream^.Seek(stream^.GetSize - 128)
  383.   else
  384.     stream^.Seek(stream^.GetSize);
  385.   stream^.Write(oldTag, SizeOf(oldTag));
  386.   status := stream^.Status;
  387. end;
  388.  
  389. end.
  390.