PageRenderTime 54ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/src/externallibs/Mp3FileUtils.pas

http://mp3toolbox.googlecode.com/
Pascal | 1779 lines | 1165 code | 175 blank | 439 comment | 66 complexity | f0a3aed31fce034795900d1e73ef152d MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. {
  2. -------------------------------------------------------
  3. The contents of this file are subject to the Mozilla Public License
  4. Version 1.1 (the "License"); you may not use this file except in
  5. compliance with the License. You may obtain a copy of the License at
  6. http://www.mozilla.org/MPL/
  7. Software distributed under the License is distributed on an "AS IS"
  8. basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
  9. License for the specific language governing rights and limitations
  10. under the License.
  11. The Original Code is MP3FileUtils.
  12. The Initial Developer of the Original Code is Daniel Gaussmann,
  13. mail@gausi.de. Portions created by the Initial Developer are
  14. Copyright (C) 2005-2009 the Initial Developer. All Rights Reserved.
  15. Contributor(s): (none yet)
  16. Alternatively, the contents of this file may be used under the terms
  17. of the GNU Lesser General Public License Version 2.1 or later
  18. (the "LGPL"), in which case the provisions of LGPL are applicable
  19. instead of those above. If you wish to allow use of your version of
  20. this file only under the terms of the LGPL and not to allow others to use
  21. your version of this file under the MPL, indicate your decision by
  22. deleting the provisions above and replace them with the notice and
  23. other provisions required by the LGPL. If you do not delete
  24. the provisions above, a recipient may use your version of this file
  25. under either the MPL or the LGPL License.
  26. -------------------------------------------------------
  27. }
  28. {
  29. Extract and set several information in mp3-Files.
  30. - TID3v1Tag:
  31. Read and write ID3v1-Tags
  32. All information are supported
  33. Version 1.1 is supported
  34. - TMpegInfo
  35. Read MPEG-information (bitrate, duration, ...)
  36. - TID3v2Tag:
  37. Read and write ID3v2-Tags.
  38. Support for all sub-versions (2.2, 2.3, 2.4)
  39. - TID3v2Frame:
  40. Edit ID3v2-Tag on Frame-level (experienced users only)
  41. Supported Third-Party-Tools
  42. ========================================================================================================
  43. - TntWare Delphi Unicode Controls
  44. Download: http://www.tntware.com/delphicontrols/unicode/
  45. Note: Tnt is only used for File-Access.
  46. If you just need Ansi-Filenames, you will NOT need TNTs
  47. Delphi 2009 do not need the TNTs.
  48. Version-History
  49. ========================================================================================================
  50. June 2011: v0.5b -> v0.6
  51. ========================
  52. New features
  53. - Private Frames
  54. - VBRI-Header-Detection
  55. - added some more genres to the Genres-List
  56. - added methods GetUserText, SetUserText, GetAllUserTextFrames,
  57. deleted TXXX-Frames from GetAllTextFrames
  58. (see TXXX-Bugfix below)
  59. Changes
  60. - ID3v2Tag.ReadFromStream: Copy all frames into a memorystream before reading
  61. - deleted methods SetRating and SetPersonalPlayCounter
  62. use SetRatingAndCounter instead to set both values at once
  63. (both values are stored in the same frame)
  64. Bugfixes
  65. - correct reading of UTF8-encoded Textframes with Delphi2009
  66. - ExtendedHeader-Size has been misinterpreted
  67. - User defined Textframes (TXXX) have NOT the same structure as other Textframes
  68. - result of TMpegInfo.GetframeLength is integer (negative value is used as error indication),
  69. but TMpegHeader.Framelentgh is Word. The direct assignment provided some errors whith
  70. range checking enabled
  71. August 2009: v0.5a -> 0.5b
  72. ========================
  73. - Fatal Bug fixed: ID3-v1-Tag could not be deleted in Delphi 2009, and every
  74. write-access (WriteToFile/-Stream) to ID3v1-Tags created a new one at the
  75. end of the file. (This was the case only in Delphi 2009, it was a
  76. Char-AnsiChar-thingy.)
  77. - DefaultRatingDescription is a var now.
  78. - Added frame-support for PlayCounter. Not the PCNT-Frame, but the
  79. counter included within Rating-Frames.
  80. (Use with care, this is not tested very well. This should have come along
  81. with some more new features (private-frames), but someone showed me
  82. this ID3v1-bug, which really must be fixed.)
  83. April 2009: v0.5 -> 0.5a (not published)
  84. ========================
  85. - fixed a possible memory-leak in TID3v2Tag.RemoveFromStream
  86. April 2009: v0.4a -> 0.5
  87. ========================
  88. - update to Delphi 2009
  89. - kicked out DIConverters.
  90. Conversion will now use the MultiByteToWideChar-Function from the Windows-API
  91. (-) some codepages are not supported any longer
  92. (+) easier to use, no third-party-stuff required
  93. (+) smaller binary
  94. - kicked out some methods, which were declared "deprecated" in v0.4
  95. - AcceptAllEncodings replaced by AutoCorrectCodepage
  96. - CharCode replaced by CodePage
  97. - GetCharcode replaced by GetCodePage
  98. - added TMpegInfo.Duration (same as .Dauer, just for the english users ;-) )
  99. - fixed a bug which possibly caused invalid encoded URLs on "Unicode-filenames"
  100. - fixed a problem with activated range-checking and PaddingSize of zero
  101. - translated (most) comments to english
  102. Dezember 2008: v0.4 -> 0.4a (sorry, from here on only german ;-))
  103. ===========================
  104. - Fehler behoben, der bei Tags mit einer bestimmten Text-Kodierung das letzte
  105. Zeichen der Textfelder abschnitt
  106. Juni 2008: v0.3b -> 0.4
  107. =======================
  108. - Code anders strukturiert - einiges in die Klasse TID3v2Frame ausgelagert
  109. - Unterst?tzung von Unsynchronisation
  110. - Unterst?tzung von GroupID und DataLength-Flags in Frame-Headern
  111. - Bei Compression und/oder Encryption wird das Auslesen abgebrochen
  112. - Unterst?tzung von URLs
  113. - Unterst?tzung von Bewertungen
  114. - Fehler in der Behandlung bei "Beschreibungen" mit Unicode - das f?hrte u.a. dazu,
  115. dass viele Cover in Dateien von jamendo.com nicht angezeigt wurden.
  116. Februar 2008: v0.3a -> 0.3b
  117. ==========================
  118. - Funktion GetTrackFromV2TrackString hinzugef?gt
  119. - Bug in der Funktion GetPaddingSize behoben
  120. Juni 2007: v0.3 -> 0.3a
  121. ==========================
  122. - Bei den Gettern des ID3v1-Tags wurden h?ufig Leerstellen und/oder Nullbytes mit ?bergeben, was ein Trim() au?erhalb
  123. der Klasse n?tig machte. Das wurde korrigiert - das trim() wird jetzt hier intern gemacht.
  124. - Sch?nheitsfehler bei der Benennung beseitigt: TPictureFrameDes()ription hei?t jetzt TPictureFrameDes(c)ription
  125. Februar 2007: v0.2a -> 0.3
  126. ==========================
  127. - INT/WE- Versionen ?ber Compiler-Schalter vereint.
  128. Siehe dazu das Ende dieses einleitenden Kommentars
  129. - Fehler entfernt, die bei einer Verkleinerung des ID3v2-Tags unter gewissen Umst?nden zu unsch?nen
  130. Effekten bei den getaggten mp3s f?hrte - die letzten Frames/Sekunden des Liedes wurden dann doppelt
  131. abgespielt.
  132. - intelligenteres Padding-System (abh?ngig von der Clustergr??e)
  133. f?r den ID3v1Tag werden 128 Byte im Cluster freigehalten (falls er nicht existiert), so
  134. dass ein nachtr?gliches Einf?gen keinen Zusatzplatz ben?tigt.
  135. ========================================================================================================
  136. September 2006: v0.2 -> 0.2a (beide Versionen)
  137. ==============================================
  138. - katastrophalen Bug behoben, der ung?ltige ID3v2-TextFrames erzeugt.
  139. August 2006: v0.1 -> v0.2(International)
  140. =========================================
  141. Kleinere Bugs:
  142. ==============
  143. - Der ID3v1-Tag wurde vor dem Lesen nicht gel?scht, so dass u.U noch alte Informationen ?brigblieben
  144. - Unter gewissen Umst?nden wurden Lyrics und Comments bei ID3v2 nicht richtig ausgelesen.
  145. Das lag aber an fehlerhaften Language-Informationen, die jetzt ausgeb?gelt werden.
  146. - Fehler beim Lesen einer Variante von Unicode behoben (Stichwort: Byte-Order)
  147. - Finalize-Abschnitt (wieder) hinzugef?gt. Der ist zwischendurch mal irgendwo verlorengegangen.
  148. Updates/?nderungen:
  149. ===================
  150. Klasse TID3v1Tag:
  151. -----------------
  152. - in der 'International'-Version werden alle Textinformationen als WideString zur?ckgeliefert
  153. - beim Lesen findet ggf. eine Konvertierung statt, die vom CharCode abh?ngt.
  154. Mit Hilfe der Funktion GetCharCode aus der Unit U_CharCode (mitgeliefert) kann
  155. der beim Taggen verwendete Zeichensatz anhand des Dateinamens bestimmt werden.
  156. Das funktioniert nat?rlich nur mit einer gewissen Fehlerquote. Mehr dazu in der Datei 'Unicode.txt'.
  157. - beim Schreiben wird ebenfalls dieser Zeichensatz verwendet und der WideString entsprechend konvertiert
  158. - Flag 'AcceptAllEncodings' hinzugef?gt. Ist dieser 'False', findet keine Konvertierung statt
  159. (weder beim schreiben, noch beim lesen).
  160. Was das f?r einen Effekt auf Systemen au?erhalb Westeuropas hat, kann ich nicht genau sagen.
  161. Klasse TID3v2Tag:
  162. -----------------
  163. - S?mtliche TextInformationen werden jetzt als WideString geliefert.
  164. - Textinformationen werden automatisch im Unicode-Format gespeichert, falls dies n?tig ist.
  165. - Flag 'AlwaysWriteUnicode' hinzugef?gt. Ist dies gesetzt, wird immer im Unicode-Format gespeichert,
  166. auch wenn das nicht n?tig ist (d.h. wenn nur "Standard"-Buchstaben verwendet werden)
  167. - beim Lesen findet ggf. eine Konvertierung statt, die vom CharCode abh?ngt.
  168. Mit Hilfe der Funktion GetCharCode aus der Unit U_CharCode (mitgeliefert) kann
  169. der beim Taggen verwendete Zeichensatz anhand des Dateinamens bestimmt werden.
  170. Im Gegensatz zur Klasse ID3v1Tag tritt dies nur dann auf, wenn beim Taggen auf Unicode verzichtet
  171. wurde. D.h. auch wenn das Flag 'AcceptAllEncodings' nicht gesetzt ist, kann man kyrillische oder asiatische (oder...)
  172. Zeichen erhalten. Dies sollte eigentlich sogar die Regel sein - ist es aber nicht, weswegen ich den ganzen
  173. Kram mit der Konvertierung ?berhaupt implementieren musste.
  174. Das funktioniert nat?rlich nur mit einer gewissen Fehlerquote. Mehr dazu in der Datei 'Unicode.txt'.
  175. - Flag 'AcceptAllEncodings' hinzugef?gt. Ist dieser 'False', findet keine Konvertierung statt
  176. }
  177. unit Mp3FileUtils;
  178. {$I config.inc}
  179. interface
  180. uses
  181. SysUtils, Classes, Windows, Contnrs, dialogs, U_CharCode
  182. {$IFDEF USE_TNT_COMPOS}, TntSysUtils, TntClasses{$ENDIF}, Id3v2Frames;
  183. type
  184. {$IFDEF USE_TNT_COMPOS}
  185. TMPFUFileStream = TTNTFileStream;
  186. {$ELSE}
  187. TMPFUFileStream = TFileStream;
  188. {$ENDIF}
  189. //--------------------------------------------------------------------
  190. // Teil 1: Some small helpers
  191. //--------------------------------------------------------------------
  192. TBuffer = Array of byte;
  193. TMP3Error = (MP3ERR_None, MP3ERR_NoFile, MP3ERR_FOpenCrt, MP3ERR_FOpenR,
  194. MP3ERR_FOpenRW, MP3ERR_FOpenW, MP3ERR_SRead, MP3ERR_SWrite,
  195. ID3ERR_Cache, ID3ERR_NoTag, ID3ERR_Invalid_Header, ID3ERR_Compression,
  196. ID3ERR_Unclassified,
  197. MPEGERR_NoFrame );
  198. TID3Version = record
  199. Major: Byte;
  200. Minor: Byte;
  201. end;
  202. //--------------------------------------------------------------------
  203. //--------------------------------------------------------------------
  204. // Teil 2: Types for ID3v1-tag
  205. //--------------------------------------------------------------------
  206. String4 = String[4]; // OK. ShortStrings are short AnsiStrings in Delphi2009
  207. String30 = String[30];
  208. // Structure of ID3v1Tags in the file
  209. TID3v1Structure = record
  210. ID: array[1..3] of AnsiChar; // all together 128 Bytes
  211. Title: Array [1..30] of AnsiChar; // Use AnsiChars
  212. Artist: Array [1..30] of AnsiChar;
  213. Album: Array [1..30] of AnsiChar;
  214. Year: array [1..4] of AnsiChar;
  215. Comment: Array [1..30] of AnsiChar;
  216. Genre: Byte;
  217. end;
  218. TID3v1Tag = class(TObject)
  219. private
  220. FTitle: String30;
  221. FArtist: String30;
  222. FAlbum: String30;
  223. FComment: String30;
  224. FTrack: Byte;
  225. FYear: String4;
  226. FGenre: Byte;
  227. FExists: Boolean;
  228. FVersion: Byte;
  229. // convert the ansi-data to UnicodeString using a codepage
  230. // * use GetCodepage(Filename) to get the probably used codepage
  231. // * fAutoCorrectCodepage = False: Use the System-Codepage
  232. fAutoCorrectCodepage: Boolean;
  233. FCharCode: TCodePage;
  234. function GetConvertedUnicodeText(Value: String30): UnicodeString;
  235. function GetTitle: UnicodeString;
  236. function GetArtist: UnicodeString;
  237. function GetAlbum: UnicodeString;
  238. function GetComment: UnicodeString;
  239. function GetGenre: String; // Delphi-Default-String. Just for display, as Genre is stored as one byte
  240. function GetTrack: String; // Delphi-Default-String. Just for display, as Track is stored as one byte
  241. function GetYear: String4;
  242. function SetString30(value: UnicodeString): String30;
  243. procedure SetTitle(Value: UnicodeString);
  244. procedure SetArtist(Value: UnicodeString);
  245. procedure SetAlbum(Value: UnicodeString);
  246. procedure SetGenre(Value: String); // Delphi-Default-String.
  247. procedure SetYear(Value: String4);
  248. procedure SetComment(Value: UnicodeString);
  249. procedure SetTrack(Value: String); // Delphi-Default-String.
  250. public
  251. constructor Create;
  252. destructor Destroy; override;
  253. property TagExists: Boolean read FExists;
  254. property Exists: Boolean read FExists;
  255. property Version: Byte read FVersion;
  256. property Title: UnicodeString read GetTitle write SetTitle;
  257. property Artist: UnicodeString read GetArtist write SetArtist;
  258. property Album: UnicodeString read GetAlbum write SetAlbum;
  259. property Genre: String read GetGenre write SetGenre; // Delphi-Default-String.
  260. property Track: String read GetTrack write SetTrack; // Delphi-Default-String.
  261. property Year: String4 read GetYear write SetYear;
  262. property Comment: UnicodeString read GetComment write SetComment;
  263. property CharCode: TCodePage read FCharCode write FCharCode;
  264. property AutoCorrectCodepage: Boolean read FAutoCorrectCodepage write FAutoCorrectCodepage;
  265. procedure Clear;
  266. function ReadFromStream(Stream: TStream): TMP3Error;
  267. function WriteToStream(Stream: TStream): TMP3Error;
  268. function RemoveFromStream(Stream: TStream): TMP3Error;
  269. function ReadFromFile(Filename: UnicodeString): TMP3Error; // UnicodeString
  270. function WriteToFile(Filename: UnicodeString): TMP3Error;
  271. function RemoveFromFile(Filename: UnicodeString): TMP3Error;
  272. end;
  273. //--------------------------------------------------------------------
  274. //--------------------------------------------------------------------
  275. // Teil 3: Types for ID3v2-tags
  276. //--------------------------------------------------------------------
  277. TInt28 = array[0..3] of Byte; // Sync-Safe Integer
  278. // Header-Structure of ID3v2-Tags
  279. // same on all subversions
  280. TID3v2Header = record
  281. ID: array[1..3] of AnsiChar;
  282. Version: Byte;
  283. Revision: Byte;
  284. Flags: Byte;
  285. TagSize: TInt28;
  286. end;
  287. TID3v2Tag = class(TObject)
  288. private
  289. Frames: TObjectList;
  290. fExists: Boolean;
  291. fVersion: TID3Version;
  292. fFlgUnsynch: Boolean;
  293. fFlgCompression: Boolean;
  294. fFlgExtended: Boolean;
  295. fFlgExperimental: Boolean;
  296. fFlgFooterPresent: Boolean;
  297. fFlgUnknown: Boolean;
  298. fPaddingSize: LongWord;
  299. fTagSize: LongWord;
  300. fDataSize: LongWord;
  301. fUsePadding: Boolean;
  302. fUseClusteredPadding: Boolean;
  303. fFilename: UnicodeString;
  304. // Always write Unicode?
  305. // True: frames will be written as utf-16 always
  306. // False: ..only if needed, Ansi otherwise (recommended for compatibilty to other taggers ;-))
  307. fAlwaysWriteUnicode: Boolean;
  308. fAutoCorrectCodepage: Boolean;
  309. fCharCode: TCodePage;
  310. function GetFrameIDString(ID:TFrameIDs):AnsiString;
  311. function GetFrameIndex(ID:TFrameIDs):integer;
  312. function GetUserTextFrameIndex(aDescription: UnicodeString): integer;
  313. function GetDescribedTextFrameIndex(ID:TFrameIDs; Language:AnsiString; Description:UnicodeString): Integer;
  314. function GetPictureFrameIndex(aDescription: UnicodeString): Integer;
  315. function GetUserDefinedURLFrameIndex(Description: UnicodeString): Integer;
  316. function GetPopularimaterFrameIndex(aEMail: AnsiString):integer;
  317. function GetPrivateFrameIndex(aOwnerID: AnsiString): Integer;
  318. function GetDescribedTextFrame(ID:TFrameIDs; Language:AnsiString; Description: UnicodeString): UnicodeString;
  319. procedure SetDescribedTextFrame(ID:TFrameIDs; Language:AnsiString; Description: UnicodeString; Value: UnicodeString);
  320. function ReadFrames(From: LongInt; Stream: TStream): TMP3Error;
  321. function ReadHeader(Stream: TStream): TMP3Error;
  322. procedure SyncStream(Source, Target: TStream);
  323. // property read functions
  324. function GetTitle: UnicodeString;
  325. function GetArtist: UnicodeString;
  326. function GetAlbum: UnicodeString;
  327. function ParseID3v2Genre(value: UnicodeString): UnicodeString;
  328. function GetGenre: UnicodeString;
  329. function GetTrack: UnicodeString;
  330. function GetYear: UnicodeString;
  331. function GetStandardComment: UnicodeString;
  332. function GetStandardLyrics: UnicodeString;
  333. function GetComposer: UnicodeString;
  334. function GetOriginalArtist: UnicodeString;
  335. function GetCopyright: UnicodeString;
  336. function GetEncodedBy: UnicodeString;
  337. function GetLanguages: UnicodeString;
  338. function GetSoftwareSettings: UnicodeString;
  339. function GetMediatype: UnicodeString;
  340. function Getid3Length: UnicodeString;
  341. function GetPublisher: UnicodeString;
  342. function GetOriginalFilename: UnicodeString;
  343. function GetOriginalLyricist: UnicodeString;
  344. function GetOriginalReleaseYear: UnicodeString;
  345. function GetOriginalAlbumTitel: UnicodeString;
  346. //property set functions
  347. procedure SetTitle(Value: UnicodeString);
  348. procedure SetArtist(Value: UnicodeString);
  349. procedure SetAlbum(Value: UnicodeString);
  350. function BuildID3v2Genre(value: UnicodeString): UnicodeString;
  351. procedure SetGenre(Value: UnicodeString);
  352. procedure SetTrack(Value: UnicodeString);
  353. procedure SetYear(Value: UnicodeString);
  354. procedure SetStandardComment(Value: UnicodeString);
  355. procedure SetStandardLyrics(Value: UnicodeString);
  356. procedure SetComposer(Value: UnicodeString);
  357. procedure SetOriginalArtist(Value: UnicodeString);
  358. procedure SetCopyright(Value: UnicodeString);
  359. procedure SetEncodedBy(Value: UnicodeString);
  360. procedure SetLanguages(Value: UnicodeString);
  361. procedure SetSoftwareSettings(Value: UnicodeString);
  362. procedure SetMediatype(Value: UnicodeString);
  363. procedure Setid3Length(Value: UnicodeString);
  364. procedure SetPublisher(Value: UnicodeString);
  365. procedure SetOriginalFilename(Value: UnicodeString);
  366. procedure SetOriginalLyricist(Value: UnicodeString);
  367. procedure SetOriginalReleaseYear(Value: UnicodeString);
  368. procedure SetOriginalAlbumTitel(Value: UnicodeString);
  369. function GetStandardUserDefinedURL: AnsiString;
  370. procedure SetStandardUserDefinedURL(Value: AnsiString);
  371. // SetRatingAndCounter: use aRating = -1 or aCounter = -1 to let this value untouched
  372. // use aRating = 0 AND aCounter = 0 to delete the frame
  373. //procedure SetRatingAndCounter(aEMail: AnsiString; aRating: Integer {Byte}; aCounter: Integer{Cardinal});
  374. function GetArbitraryRating: Byte;
  375. procedure SetArbitraryRating(Value: Byte);
  376. function GetArbitraryPersonalPlayCounter: Cardinal;
  377. procedure SetArbitraryPersonalPlayCounter(Value: Cardinal);
  378. procedure SetCharCode(Value: TCodePage);
  379. procedure SetAutoCorrectCodepage(Value: Boolean);
  380. public
  381. constructor Create;
  382. destructor Destroy; override;
  383. // "Level 1": Easy access through properties.
  384. // The setter and getter will do all the complicated stuff for you
  385. property Title: UnicodeString read GetTitle write SetTitle;
  386. property Artist: UnicodeString read GetArtist write SetArtist;
  387. property Album: UnicodeString read GetAlbum write SetAlbum;
  388. property Genre: UnicodeString read GetGenre write SetGenre;
  389. property Track: UnicodeString read GetTrack write SetTrack;
  390. property Year: UnicodeString read GetYear write SetYear;
  391. property Comment: UnicodeString read GetStandardComment write SetStandardComment;
  392. property Lyrics : UnicodeString read GetStandardLyrics write SetStandardLyrics;
  393. property URL: AnsiString read GetStandardUserDefinedURL write SetStandardUserDefinedURL;
  394. property Rating: Byte read GetArbitraryRating write SetArbitraryRating;
  395. property PlayCounter: Cardinal read GetArbitraryPersonalPlayCounter write SetArbitraryPersonalPlayCounter;
  396. property Composer: UnicodeString read GetComposer write SetComposer ;
  397. property OriginalArtist: UnicodeString read GetOriginalArtist write SetOriginalArtist ;
  398. property Copyright: UnicodeString read GetCopyright write SetCopyright ;
  399. property EncodedBy: UnicodeString read GetEncodedBy write SetEncodedBy ;
  400. property Languages: UnicodeString read GetLanguages write SetLanguages ;
  401. property SoftwareSettings: UnicodeString read GetSoftwareSettings write SetSoftwareSettings;
  402. property Mediatype: UnicodeString read GetMediatype write SetMediatype ;
  403. property id3Length: UnicodeString read Getid3Length write Setid3Length ;
  404. property Publisher: UnicodeString read GetPublisher write SetPublisher ;
  405. property OriginalFilename: UnicodeString read GetOriginalFilename write SetOriginalFilename ;
  406. property OriginalLyricist: UnicodeString read GetOriginalLyricist write SetOriginalLyricist ;
  407. property OriginalReleaseYear: UnicodeString read GetOriginalReleaseYear write SetOriginalReleaseYear;
  408. property OriginalAlbumTitel: UnicodeString read GetOriginalAlbumTitel write SetOriginalAlbumTitel ;
  409. property FlgUnsynch : Boolean read fFlgUnsynch write fFlgUnsynch;
  410. property FlgCompression : Boolean read fFlgCompression;
  411. property FlgExtended : Boolean read fFlgExtended;
  412. property FlgExperimental : Boolean read fFlgExperimental;
  413. property FlgFooterPresent : Boolean read fFlgFooterPresent;
  414. property FlgUnknown : Boolean read fFlgUnknown;
  415. property Size: LongWord read fTagSize;
  416. property Exists: Boolean read fExists; // two properties twice
  417. property TagExists: Boolean read fExists; // due to backward compatibility
  418. property Padding: Longword read fPaddingSize; //
  419. property PaddingSize:Longword read fPaddingSize; //
  420. property Version: TID3Version read fVersion;
  421. property UsePadding: Boolean read fUsePadding write fUsePadding;
  422. property UseClusteredPadding: Boolean read fUseClusteredPadding write fUseClusteredPadding;
  423. property AlwaysWriteUnicode: Boolean read fAlwaysWriteUnicode write fAlwaysWriteUnicode;
  424. property CharCode: TCodePage read fCharCode write SetCharCode;
  425. property AutoCorrectCodepage: Boolean read fAutoCorrectCodepage write SetAutoCorrectCodepage;
  426. function ReadFromStream(Stream: TStream): TMP3Error;
  427. function WriteToStream(Stream: TStream): TMP3Error;
  428. function RemoveFromStream(Stream: TStream): TMP3Error;
  429. function ReadFromFile(Filename: UnicodeString): TMP3Error;
  430. function WriteToFile(Filename: UnicodeString): TMP3Error;
  431. function RemoveFromFile(Filename: UnicodeString): TMP3Error;
  432. procedure Clear;
  433. // "Level 2": Some advanced Frames. Get/edit them on Tag-Level
  434. // Setting a value to '' will delete the frame
  435. function GetText(FrameID: TFrameIDs): UnicodeString;
  436. procedure SetText(FrameID:TFrameIDs; Value: UnicodeString);
  437. // User defined TextFrames (TXXX)
  438. function GetUserText(Description: UnicodeString): UnicodeString;
  439. procedure SetUserText(Description, Value: UnicodeString);
  440. function GetURL(FrameID: TFrameIDs): AnsiString;
  441. procedure SetURL(FrameID:TFrameIDs; Value: AnsiString);
  442. // Comments (COMM)
  443. // Note: Delete by Set(..., '');
  444. procedure SetExtendedComment(Language: AnsiString; Description: UnicodeString; value: UnicodeString);
  445. function GetExtendedComment(Language: AnsiString; Description: UnicodeString): UnicodeString;
  446. // Lyrics
  447. // Note: Delete by Set(..., '');
  448. procedure SetLyrics(Language: AnsiString; Description: UnicodeString; value: UnicodeString);
  449. function GetLyrics(Language: AnsiString; Description: UnicodeString): UnicodeString;
  450. // Pictures (APIC)
  451. // Note: Delete by setting Stream = Nil
  452. function GetPicture(stream: TStream; Description: UnicodeString): AnsiString; // R?ckgabe: Mime-Type
  453. procedure SetPicture(MimeTyp: AnsiString; PicType: Byte; Description: UnicodeString; stream: TStream);
  454. // User-defined URL (WXXX)
  455. // Note: Delete by Set(..., '');
  456. function GetUserDefinedURL(Description: UnicodeString): AnsiString;
  457. procedure SetUserDefinedURL(Description: UnicodeString; Value: AnsiString);
  458. // Ratings (POPM)
  459. // Note: GetRating('*') gets an arbitrary rating (in case more than one exist in the tag)
  460. function GetRating(aEMail: AnsiString): Byte;
  461. //procedure SetRating(aEMail: AnsiString; Value: Byte); (method from version 0.5)
  462. function GetPersonalPlayCounter(aEMail: AnsiString): Cardinal;
  463. // procedure SetPersonalPlayCounter(aEMail: AnsiString; Value: Cardinal); (method from version 0.5)
  464. // SetRatingAndCounter('*', .., ..) overwrites an arbitrary rating/counter
  465. // SetRatingAndCounter(.., -1, ..) lets the rating untouched
  466. // SetRatingAndCounter(.., .., -1) lets the counter untouched
  467. // SetRatingAndCounter(.., 0, 0) deletes the rating/counter-frame
  468. procedure SetRatingAndCounter(aEMail: AnsiString; aRating: Integer {Byte}; aCounter: Integer{Cardinal});
  469. // Private Frames
  470. function GetPrivateFrame(aOwnerID: AnsiString; Content: TStream): Boolean;
  471. procedure SetPrivateFrame(aOwnerID: AnsiString; Content: TStream);
  472. // "Level 3": Manipulation on Frame-Level
  473. // Be careful with writing on this level
  474. // These Methods gives you some lists with different types of frames
  475. // See ID3v2Frames.pas how to edit these Frames
  476. function GetAllFrames: TObjectlist;
  477. function GetAllTextFrames: TObjectlist;
  478. function GetAllUserTextFrames: TObjectlist;
  479. function GetAllCommentFrames: TObjectlist;
  480. function GetAllLyricFrames: TObjectlist;
  481. function GetAllUserDefinedURLFrames: TObjectlist;
  482. function GetAllPictureFrames: TObjectlist;
  483. function GetAllPopularimeterFrames: TObjectlist;
  484. function GetAllURLFrames: TObjectlist;
  485. function GetAllPrivateFrames: TObjectList;
  486. // Check, wether a new frame is valid, i.e. unique
  487. function ValidNewCommentFrame(Language: AnsiString; Description: UnicodeString): Boolean;
  488. function ValidNewLyricFrame(Language: AnsiString; Description: UnicodeString): Boolean;
  489. function ValidNewPictureFrame(Description: UnicodeString): Boolean;
  490. function ValidNewUserDefUrlFrame(Description: UnicodeString): Boolean;
  491. function ValidNewPopularimeterFrame(EMail: AnsiString): Boolean;
  492. // Get allowed Frame-IDs (not every frame is allowed in every subversion)
  493. function GetAllowedTextFrames: TList;
  494. function GetAllowedURLFrames: TList; // WOAR, ... Not the user definied WXXX-Frame
  495. function AddFrame(aID: TFrameIDs): TID3v2Frame;
  496. procedure DeleteFrame(aFrame: TID3v2Frame);
  497. end;
  498. //--------------------------------------------------------------------
  499. //--------------------------------------------------------------------
  500. // Teil 4. Types for MPEG
  501. //--------------------------------------------------------------------
  502. TMpegHeader = record
  503. version: byte;
  504. layer: byte;
  505. protection: boolean;
  506. bitrate: LongInt;
  507. samplerate: LongInt;
  508. channelmode: byte;
  509. extension: byte;
  510. copyright: boolean;
  511. original: boolean;
  512. emphasis: byte;
  513. padding: boolean;
  514. framelength: word;
  515. valid: boolean;
  516. end;
  517. TXingHeader = record
  518. Frames: integer;
  519. Size: integer;
  520. valid: boolean;
  521. end;
  522. TVBRIHeader = TXingHeader;
  523. TMpegInfo = class(TObject)
  524. Private
  525. FFilesize: int64;
  526. Fversion:integer;
  527. Flayer:integer;
  528. Fprotection:boolean;
  529. Fbitrate:word;
  530. Fsamplerate:integer;
  531. Fchannelmode:byte;
  532. Fextension:byte;
  533. Fcopyright:boolean;
  534. Foriginal:boolean;
  535. Femphasis:byte;
  536. Fframes:Integer;
  537. Fdauer:Longint;
  538. Fvbr:boolean;
  539. Fvalid: boolean;
  540. FfirstHeaderPosition: int64;
  541. // Check, wether there is in aBuffer on position a valid MPEG-header
  542. function GetValidatedHeader(aBuffer: TBuffer; position: integer): TMpegHeader;
  543. // Check, wether the MPEG-header is followed by a Xing-Frame
  544. function GetXingHeader(aMpegheader: TMpegHeader; aBuffer: TBuffer; position: integer ): TXingHeader;
  545. function GetVBRIHeader(aMpegheader: TMpegHeader; aBuffer: TBuffer; position: integer ): TVBRIHeader;
  546. function GetFramelength(version:byte;layer:byte;bitrate:integer;Samplerate:integer;padding:boolean):integer;
  547. public
  548. constructor create;
  549. function LoadFromStream(stream: tStream): TMP3Error;
  550. function LoadFromFile(FileName: UnicodeString): TMP3Error;
  551. property Filesize: int64 read FFilesize;
  552. property Version: integer read Fversion;
  553. property Layer: integer read Flayer;
  554. property Protection: boolean read Fprotection;
  555. property Bitrate: word read Fbitrate;
  556. property Samplerate: integer read Fsamplerate;
  557. property Channelmode: byte read Fchannelmode;
  558. property Extension: byte read Fextension;
  559. property Copyright: boolean read Fcopyright;
  560. property Original: boolean read Foriginal;
  561. property Emphasis: byte read Femphasis;
  562. property Frames: Integer read Fframes;
  563. property Dauer: Longint read Fdauer;
  564. property Duration: Longint read Fdauer; // Same as "Dauer" - for the english user ;-)
  565. property Vbr: boolean read Fvbr;
  566. property Valid: boolean read Fvalid;
  567. property FirstHeaderPosition: int64 read FfirstHeaderPosition;
  568. end;
  569. // Some useful functions.
  570. // Use them e.g. in OnChange of a TEdit
  571. function IsValidV2TrackString(value:string):boolean;
  572. function IsValidV1TrackString(value:string):boolean;
  573. function IsValidYearString(value:string):boolean;
  574. // Get a TrackNr. from a ID3v2-Tag-trackstring
  575. // e.g.: 3/15 => 3
  576. function GetTrackFromV2TrackString(value: string): Byte;
  577. const
  578. MPEG_BIT_RATES : array[1..3] of array[1..3] of array[0..15] of word =
  579. { Version 1, Layer I }
  580. (((0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0),
  581. { Version 1, Layer II }
  582. (0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,0),
  583. { Version 1, Layer III }
  584. (0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,0)),
  585. { Version 2, Layer I }
  586. ((0,32,48, 56, 64, 80, 96,112,128,144,160,176,192,224,256,0),
  587. { Version 2, Layer II }
  588. (0, 8,16,24, 32, 40, 48, 56, 64, 80, 96, 112,128,144,160,0),
  589. { Version 2, Layer III }
  590. (0, 8,16,24, 32, 40, 48, 56, 64, 80, 96, 112,128,144,160,0)),
  591. { Version 2.5, Layer I }
  592. ((0,32,48, 56, 64, 80, 96,112,128,144,160,176,192,224,256,0),
  593. { Version 2.5, Layer II }
  594. (0, 8,16,24, 32, 40, 48, 56, 64, 80, 96, 112,128,144,160,0),
  595. { Version 2.5, Layer III }
  596. (0, 8,16,24, 32, 40, 48, 56, 64, 80, 96, 112,128,144,160,0)));
  597. sample_rates: array[1..3] of array [0..3] of word=
  598. ((44100,48000,32000,0),
  599. (22050,24000,16000,0),
  600. (11025,12000,8000,0));
  601. channel_modes:array[0..3] of string=('Stereo','Joint stereo','Dual channel (Stereo)','Single channel (Mono)');
  602. extensions:array[1..3] of array [0..3] of string=
  603. (('bands 4 to 31','bands 8 to 32','bands 12 to 31','bands 16 to 31'),
  604. ('bands 4 to 31','bands 8 to 32','bands 12 to 31','bands 16 to 31'),
  605. ('IS:off, MS:off','IS:on, MS:off','IS:off, MS:on','IS:on, MS:on'));
  606. emphasis_values: array[0..3] of string = ('None', '50/15ms','reserved','CCIT J.17');
  607. {$Message Hint 'You should change the default rating description for your projects'}
  608. var
  609. DefaultRatingDescription: AnsiString = 'Mp3ileUtils, www.gausi.de';
  610. // Changig this should be done e.g. in MainFormCreate or in the initialization-part
  611. // It should be like "<Name of the program>, <URL to your webpage>"
  612. var
  613. Genres: TStringList;
  614. LanguageCodes: TStringlist;
  615. LanguageNames: TStringlist;
  616. implementation
  617. //--------------------------------------------------------------------
  618. // Some useful functions outside the classes
  619. //--------------------------------------------------------------------
  620. //--------------------------------------------------------------------
  621. // before Delphi 2009:
  622. // * String is AnsiString
  623. // * If no TNTs are used, Delphi cannot handle Unicode-Filenames
  624. // * If TNTs are used, the following two methods will not be compiled
  625. // and WideFileExists/-ExtractFileDrive will be the TNT-function with WideString-Parameter
  626. // Delphi 2009:
  627. // * TNTs are not used (as Delphi itself can handle Unicode)
  628. // * String is UnicodeString
  629. //--------------------------------------------------------------------
  630. {$IFNDEF USE_TNT_COMPOS}
  631. function WideFileExists(aString: string):boolean;
  632. begin
  633. result := FileExists(aString);
  634. end;
  635. function WideExtractFileDrive(aString: String): string;
  636. begin
  637. result := ExtractFileDrive(aString);
  638. end;
  639. {$ENDIF}
  640. //--------------------------------------------------------------------
  641. // Check, wether Frame-ID is valid
  642. //--------------------------------------------------------------------
  643. function ValidFrame(ID: AnsiString): Boolean;
  644. var
  645. i: Cardinal;
  646. begin
  647. result := true;
  648. for i := 1 to length(ID) do
  649. if not (ID[i] in ['0'..'9', 'A'..'Z']) then
  650. begin
  651. result := false;
  652. Break;
  653. end;
  654. end;
  655. function ValidTextFrame(ID: AnsiString): Boolean;
  656. begin
  657. result := (length(ID) >= 3) and (ID[1] = 'T');
  658. end;
  659. //--------------------------------------------------------------------
  660. // Convert a 28bit-integer to a 32bit-integer
  661. //--------------------------------------------------------------------
  662. function Int28ToInt32(Value: TInt28): LongWord;
  663. begin
  664. // Take the rightmost byte and let it there,
  665. // take the second rightmost byte and move it 7bit to left
  666. // (in an 32bit-variable)
  667. // a.s.o.
  668. result := (Value[3]) shl 0 or
  669. (Value[2]) shl 7 or
  670. (Value[1]) shl 14 or
  671. (Value[0]) shl 21;
  672. end;
  673. //--------------------------------------------------------------------
  674. // Convert a 32bit-integer to a 28bit-integer
  675. //--------------------------------------------------------------------
  676. function Int32ToInt28(Value: LongWord): TInt28;
  677. begin
  678. // move every byte in Value to the right, take the 7 LSBs
  679. // and assign them to the result
  680. Result[3] := (Value shr 0) and $7F;
  681. Result[2] := (Value shr 7) and $7F;
  682. Result[1] := (Value shr 14) and $7F;
  683. Result[0] := (Value shr 21) and $7F;
  684. end;
  685. //--------------------------------------------------------------------
  686. // Get a temporary filename
  687. //--------------------------------------------------------------------
  688. function GetTempFile: String;
  689. var
  690. Path: String;
  691. i: Integer;
  692. begin
  693. SetLength(Path, 256);
  694. FillChar(PChar(Path)^, 256 * sizeOf(Char), 0);
  695. GetTempPath(256, PChar(Path));
  696. Path := Trim(Path);
  697. if Path[Length(Path)] <> '\' then
  698. Path := Path + '\';
  699. i := 0;
  700. repeat
  701. result := Path + 'TagTemp.t' + IntToHex(i, 2);
  702. inc(i);
  703. until not FileExists(result);
  704. end;
  705. //--------------------------------------------------------------------
  706. // ID3v1 or ID3v1.1 ?
  707. //--------------------------------------------------------------------
  708. function GetID3v1Version(Tag: TID3v1Structure): Byte;
  709. begin
  710. // If the 29th byte of the comment is =0 an
  711. // 30th <> 0, then this is the Track-nr.
  712. if (Tag.Comment[29] = #00) and (Tag.Comment[30] <> #00) then
  713. result := 1
  714. else
  715. result := 0;
  716. end;
  717. //---------------------------------------------------
  718. // check, wether value is a valid track-string for id3v2 ...
  719. //---------------------------------------------------
  720. function IsValidV2TrackString(value:string):boolean;
  721. var
  722. del: Integer;
  723. Track, Overall: String;
  724. begin
  725. del := Pos('/', Value); // getting the position of the delimiter
  726. if del = 0 then
  727. // If there is none, then the whole string is the TrackNumber
  728. result := (StrToIntDef(Value, -1) > -1)
  729. else begin
  730. Overall := Trim(Copy(Value, del + 1, Length(Value) - (del)));
  731. Track := Trim(Copy(Value, 1, del - 1));
  732. result := ((StrToIntDef(Track, -1) > -1) AND (StrToIntDef(Overall, -1) > -1))
  733. end;
  734. end;
  735. //--------------------------------------------------------------------
  736. // ... and for v1
  737. //--------------------------------------------------------------------
  738. function IsValidV1TrackString(value:string):boolean;
  739. begin
  740. result := (StrToIntDef(Value, -1) > -1);
  741. end;
  742. //--------------------------------------------------------------------
  743. // Check for valid year
  744. //--------------------------------------------------------------------
  745. function IsValidYearString(value:string):boolean;
  746. var tmp:integer;
  747. begin
  748. tmp := StrToIntDef(Value, -1);
  749. result := (tmp > -1) AND (tmp < 10000);
  750. end;
  751. //--------------------------------------------------------------------
  752. // Get Track-Nr. from track-string
  753. //--------------------------------------------------------------------
  754. function GetTrackFromV2TrackString(value: string): Byte;
  755. var
  756. del: Integer;
  757. Track: String;
  758. begin
  759. del := Pos('/', Value); // getting the position of the delimiter
  760. if del = 0 then
  761. // If there is none, then the whole string is the TrackNumber
  762. result := StrToIntDef(Value, 0)
  763. else begin
  764. //Overall := Trim(Copy(Value, del + 1, Length(Value) - (del)));
  765. Track := Trim(Copy(Value, 1, del - 1));
  766. result := StrToIntDef(Track, 0);
  767. end;
  768. end;
  769. //--------------------------------------------------------------------
  770. // Get a "reasonable" padding-size (i.e.: fill the last used cluster)
  771. //--------------------------------------------------------------------
  772. function GetPaddingSize(DataSize: Int64; aFilename: UnicodeString; UseClusterSize: Boolean): Cardinal;
  773. var
  774. Drive: string;
  775. ClusterSize : Cardinal;
  776. SectorPerCluster : Cardinal;
  777. BytesPerSector : Cardinal;
  778. NumberOfFreeClusters : Cardinal;
  779. TotalNumberOfClusters : Cardinal;
  780. begin
  781. Drive := WideExtractFileDrive(aFileName);
  782. if UseClusterSize and (trim(Drive) <> '')then
  783. begin
  784. if Drive[Length(Drive)]<>'\' then Drive := Drive+'\';
  785. try
  786. if GetDiskFreeSpace(PChar(Drive),
  787. SectorPerCluster,
  788. BytesPerSector,
  789. NumberOfFreeClusters,
  790. TotalNumberOfClusters) then
  791. ClusterSize := SectorPerCluster * BytesPerSector
  792. else
  793. ClusterSize := 2048;
  794. except
  795. ClusterSize := 2048;
  796. end;
  797. end else
  798. ClusterSize := 2048;
  799. Result := (((DataSize DIV ClusterSize) + 1) * Clustersize) - DataSize;
  800. end;
  801. //--------------------------------------------------------------------
  802. //--------------------------------------------------------------------
  803. // *** TID3v1Tag ***
  804. //--------------------------------------------------------------------
  805. //--------------------------------------------------------------------
  806. constructor TID3v1Tag.Create;
  807. begin
  808. inherited Create;
  809. // Set default-values
  810. Clear;
  811. FCharCode := DefaultCharCode;
  812. AutoCorrectCodepage := False;
  813. end;
  814. destructor TID3v1Tag.destroy;
  815. begin
  816. inherited destroy;
  817. end;
  818. // Read the Tag from a stream
  819. function TID3v1Tag.ReadFromStream(Stream: TStream): TMP3Error;
  820. var
  821. RawTag: TID3v1Structure;
  822. begin
  823. clear;
  824. result := MP3ERR_None;
  825. FExists := False;
  826. try
  827. Stream.Seek(-128, soFromEnd);
  828. if (Stream.Read(RawTag, 128) = 128) then
  829. if (RawTag.ID = 'TAG') then
  830. begin
  831. FExists := True;
  832. FVersion := GetID3v1Version(RawTag);
  833. FTitle := (RawTag.Title);
  834. FArtist := (RawTag.Artist);
  835. FAlbum := (RawTag.Album);
  836. FYear := (RawTag.Year);
  837. //String4(Trim(String(FYear)));
  838. if FVersion = 0 then
  839. begin
  840. FComment := (RawTag.Comment);
  841. FTrack := 0;
  842. end
  843. else
  844. begin
  845. Move(RawTag.Comment[1], FComment[1], 28);
  846. FComment[29] := #0;
  847. FComment[30] := #0;
  848. FTrack := Ord(RawTag.Comment[30]);
  849. end;
  850. FGenre := RawTag.Genre;
  851. end
  852. else
  853. result := ID3ERR_NoTag
  854. else
  855. result := MP3ERR_SRead;
  856. except
  857. on E: Exception do
  858. begin
  859. result := ID3ERR_Unclassified;
  860. MessageBox(0, PChar(E.Message), PChar('Error'), MB_OK or MB_ICONERROR or MB_TASKMODAL or MB_SETFOREGROUND);
  861. end;
  862. end;
  863. end;
  864. // Write Tag to a stream
  865. function TID3v1Tag.WriteToStream(Stream: TStream): TMP3Error;
  866. var
  867. RawTag: TID3v1Structure;
  868. Buffer: Array [1..3] of AnsiChar;
  869. begin
  870. result := MP3ERR_NONE;
  871. try
  872. FillChar(RawTag, 128, 0);
  873. RawTag.ID := 'TAG';
  874. Move(FTitle[1], RawTag.Title, Length(FTitle));
  875. Move(FArtist[1], RawTag.Artist, Length(FArtist));
  876. Move(FAlbum[1], RawTag.Album, Length(FAlbum));
  877. Move(FYear[1], RawTag.Year, Length(FYear));
  878. Move(FComment[1], RawTag.Comment, Length(FComment));
  879. if FTrack > 0 then
  880. begin
  881. RawTag.Comment[29] := #0;
  882. RawTag.Comment[30] := AnsiChar(Chr(FTrack));
  883. end;
  884. RawTag.Genre := FGenre;
  885. // Search for an existing tag and set position to write the new one
  886. Stream.Seek(-128, soFromEnd);
  887. Stream.Read(Buffer[1], 3);
  888. if (Buffer[1]='T') AND (Buffer[2]='A') AND (Buffer[3]='G') then
  889. Stream.Seek(-128, soFromEnd)
  890. else
  891. Stream.Seek(0, soFromEnd);
  892. if Stream.Write(RawTag, 128) <> 128 then
  893. result := MP3ERR_SWrite;
  894. except
  895. on E: Exception do
  896. begin
  897. result := ID3ERR_Unclassified;
  898. MessageBox(0, PChar(E.Message), PChar('Error'), MB_OK or MB_ICONERROR or MB_TASKMODAL or MB_SETFOREGROUND);
  899. end;
  900. end;
  901. end;
  902. // Delete Tag, if existing
  903. function TID3v1Tag.RemoveFromStream(Stream: TStream): TMP3Error;
  904. var
  905. Buffer: Array [1..3] of AnsiChar;
  906. begin
  907. result := MP3ERR_NONE;
  908. try
  909. Stream.Seek(-128, soFromEnd);
  910. Stream.Read(Buffer[1], 3);
  911. if (Buffer[1]='T') AND (Buffer[2]='A') AND (Buffer[3]='G') then
  912. begin
  913. Stream.Seek(-128, soFromEnd);
  914. SetStreamEnd(Stream);
  915. end
  916. else
  917. result := ID3ERR_NoTag;
  918. except
  919. on E: Exception do
  920. begin
  921. result := ID3ERR_Unclassified;
  922. MessageBox(0, PChar(E.Message), PChar('Error'), MB_OK or MB_ICONERROR or MB_TASKMODAL or MB_SETFOREGROUND);
  923. end;
  924. end;
  925. end;
  926. // Set default-values
  927. procedure TID3v1Tag.Clear;
  928. begin
  929. FTitle := String30(StringOfChar(#0, 30));
  930. FArtist := String30(StringOfChar(#0, 30));
  931. FAlbum := String30(StringOfChar(#0, 30));
  932. FYear := String4(StringOfChar(#0, 4));
  933. FComment := String30(StringOfChar(#0, 30));
  934. FTrack := 0;
  935. FGenre := 0;
  936. FVersion := 0;
  937. FExists := False;
  938. end;
  939. // read tag from a file
  940. // -> use stream-function
  941. function TID3v1Tag.ReadFromFile(Filename: UnicodeString): TMP3Error;
  942. var
  943. Stream: TMPFUFileStream;
  944. begin
  945. if WideFileExists(Filename) then
  946. try
  947. Stream := TMPFUFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
  948. try
  949. result := ReadFromStream(Stream);
  950. finally
  951. Stream.Free;
  952. end;
  953. except
  954. result := MP3ERR_FOpenR;
  955. end
  956. else
  957. result := MP3ERR_NoFile;
  958. end;
  959. // Write a tag to a file
  960. // -> use stream-function
  961. function TID3v1Tag.WriteToFile(Filename: UnicodeString): TMP3Error;
  962. var
  963. Stream: TMPFUFileStream;
  964. begin
  965. if WideFileExists(Filename) then
  966. try
  967. Stream := TMPFUFileStream.Create(Filename, fmOpenReadWrite or fmShareDenyWrite);
  968. try
  969. result := WriteToStream(Stream);
  970. finally
  971. Stream.Free;
  972. end;
  973. except
  974. result := MP3ERR_FOpenRW;
  975. end
  976. else
  977. result := MP3ERR_NoFile;
  978. end;
  979. // Delete Tag from a file
  980. // -> use stream-function
  981. function TID3v1Tag.RemoveFromFile(Filename: UnicodeString): TMP3Error;
  982. var
  983. Stream: TMPFUFileStream;
  984. begin
  985. if WideFileExists(Filename) then
  986. try
  987. Stream := TMPFUFileStream.Create(Filename, fmOpenReadWrite or fmShareDenyWrite);
  988. try
  989. result := RemoveFromStream(Stream);
  990. finally
  991. Stream.Free;
  992. end;
  993. except
  994. result := MP3ERR_FOpenRW;
  995. end
  996. else
  997. result := MP3ERR_NoFile;
  998. end;
  999. // Converts a String[30] to UnicodeString
  1000. // * if AutoCorrectCodepage=True then the conversion is done by the
  1001. // given CodePage
  1002. // * otherwise it will be done by delphi, i.e. the system-codepage
  1003. function TID3v1Tag.GetConvertedUnicodeText(Value: String30): UnicodeString;
  1004. var
  1005. tmp: AnsiString;
  1006. L: Integer;
  1007. begin
  1008. if AutoCorrectCodepage then
  1009. begin
  1010. L := MultiByteToWideChar(FCharCode.CodePage,
  1011. MB_PRECOMPOSED, // Flags
  1012. @Value[1], // data to convert
  1013. Length(Value), // Size in bytes
  1014. nil, // output - not used here
  1015. 0); // 0=> Get required BufferSize
  1016. if L = 0 then
  1017. begin
  1018. // Something's wrong => Fall back to ANSI
  1019. setlength(tmp, 30);
  1020. move(Value[1], tmp[1], 30);
  1021. {$IFDEF UNICODE}
  1022. // use explicit typecast
  1023. result := trim(String(tmp));
  1024. {$ELSE}
  1025. result := trim(tmp);
  1026. {$ENDIF}
  1027. end else
  1028. begin
  1029. // SetBuffer, Size in WChars, not Bytes.
  1030. SetLength(Result, L);
  1031. // Convert
  1032. MultiByteToWideChar(FCharCode.CodePage,
  1033. MB_PRECOMPOSED,
  1034. @Value[1],
  1035. length(Value),
  1036. @Result[1],
  1037. L);
  1038. // trim string
  1039. result := Trim(Result);
  1040. end;
  1041. end
  1042. else
  1043. begin
  1044. // copy to AnsiString and typecast
  1045. setlength(tmp,30);
  1046. move(Value[1], tmp[1], 30);
  1047. {$IFDEF UNICODE}
  1048. // use explicit typecast
  1049. result := trim(String(tmp));
  1050. {$ELSE}
  1051. result := trim(tmp);
  1052. {$ENDIF}
  1053. end;
  1054. end;
  1055. function TID3v1Tag.GetTitle: UnicodeString;
  1056. begin
  1057. result := GetConvertedUnicodeText(FTitle);
  1058. end;
  1059. function TID3v1Tag.GetArtist: UnicodeString;
  1060. begin
  1061. result := GetConvertedUnicodeText(FArtist);
  1062. end;
  1063. function TID3v1Tag.GetAlbum: UnicodeString;
  1064. begin
  1065. result := GetConvertedUnicodeText(FAlbum);
  1066. end;
  1067. function TID3v1Tag.GetComment: UnicodeString;
  1068. begin
  1069. result := GetConvertedUnicodeText(FComment);
  1070. end;
  1071. function TID3v1Tag.GetGenre: String;
  1072. begin
  1073. if FGenre <= 125 then
  1074. result := Genres[FGenre]
  1075. else
  1076. result := '';
  1077. end;
  1078. function TID3v1Tag.GetTrack: String;
  1079. begin
  1080. result := IntToStr(FTrack);
  1081. end;
  1082. function TID3v1Tag.GetYear: String4;
  1083. begin
  1084. result := FYear;
  1085. end;
  1086. // Converts a UnicodeString to String[30]
  1087. // * if AutoCorrectCodepage=True then the conversion is done by the
  1088. // given CodePage
  1089. // * otherwise it will be done by delphi, i.e. the system-codepage
  1090. function TID3v1Tag.SetString30(value: UnicodeString): String30;
  1091. var i, max, L: integer;
  1092. tmpstr: AnsiString;
  1093. begin
  1094. result := String30(StringOfChar(#0, 30));
  1095. if fAutoCorrectCodepage then
  1096. begin
  1097. if length(value) > 0 then
  1098. begin
  1099. L := WideCharToMultiByte(FCharCode.CodePage, // CodePage
  1100. 0, // Flags
  1101. @Value[1], // String to Convert
  1102. -1,//length(Value), // ... and its length
  1103. Nil, // output, not needed here
  1104. 0, // and its length, 0 to get required length
  1105. Nil, // DefaultChar, Nil=SystemDefault
  1106. Nil); // DefaultChar needed
  1107. if L = 0 then
  1108. begin
  1109. // Failure, Fall back to Ansi
  1110. tmpstr := AnsiString(value);
  1111. max := length(tmpstr);
  1112. if max > 30 then max := 30;
  1113. for i := 1 to max do
  1114. result[i] := tmpstr[i];
  1115. end
  1116. else
  1117. begin
  1118. // use a tmp-AnsiString, as the UnicodeString may be longer
  1119. SetLength(tmpstr, L);

Large files files are truncated, but you can click here to view the full file