Develop and Download Open Source Software

Browse Subversion Repository

Contents of /TagID3.pas

Parent Directory Parent Directory | Revision Log Revision Log


Revision 7 - (show annotations) (download) (as text)
Sat Aug 15 03:06:09 2015 UTC (8 years, 9 months ago) by yamat0jp
File MIME type: text/x-pascal
File size: 36403 byte(s)
タグ読み取りに使用するユニットを変更しました。現在Androidでは利用できませんが、書き換えをして使えるようにしようと思います。
1 unit TagID3;
2
3 interface
4
5 uses
6 System.Types, System.Classes, System.SysUtils;
7
8 type
9 // Ver 1
10 TMyID3v1 = record
11 Ver: Byte; // ID3v1タグがあれば1、なければ0
12 Major: Byte; // 1.0なら0、1.1なら1
13 Title: String[30];
14 Author: String[30];
15 Album: String[30];
16 Year: String[4];
17 Comment: String[30];
18 Track: Byte;
19 Genre: Byte;
20 end;
21
22 type
23 // Ver 2
24 { タグヘッダー }
25
26 // 制限情報 %ppqrrstt
27 TMyID3HeaderExtendedLimit = record // 書き込み時のみに必要な情報
28 iTagSize: Byte; // タグサイズの制限
29 {
30 00 128 フレーム以下でトータルタグサイズ 1 MB 以下
31 01 64 フレーム以下でトータルタグサイズ 128 KB 以下
32 10 32 フレーム以下でトータルタグサイズ 40 KB 以下
33 11 32 フレーム以下でトータルタグサイズ 4 KB 以下
34 }
35 bCharCode: Boolean; // 文字コードの制限
36 {
37 0 制限なし
38 1 ISO-8859-1 [ISO-8859-1] もしくはUTF-8 [UTF-8]のみ使用可
39 }
40 iTextSize: WORD; // テキストフィールドサイズの制限 実際の制限数をセット
41 {
42 00 制限なし
43 01 文字列は 1024 文字以内
44 10 文字列は 128 文字以内
45 11 文字列は 30 文字以内
46 これらの文字制限では、文字のバイト数ではなく文字数に言及している点
47 に注意すること。最大バイト数は文字コードによって変動する。ひとつの
48 テキストフレーム中に、複数の文字列が入っている場合は、それらの合計
49 値に対する制限となる。
50 }
51 bPicFormat: Boolean; // 画像フォーマットの制限
52 {
53 0 制限なし
54 1 画像はPNG [PNG] もしくは JPEG [JFIF] フォーマットのみ
55 }
56 iPicSize: Byte; // 画像サイズの制限
57 {
58 00 制限なし
59 01 全ての画像は256x256 ピクセルかそれ以下
60 10 全ての画像は 64x64 ピクセルかそれ以下
61 11 特にサイズ指定の無い画像は全て 64x64 ピクセル固定
62 }
63 end;
64
65 // 拡張ヘッダー
66 TMyID3HeaderExtended = record // 2.3〜
67 iSize: DWORD;
68 iPaddingSize: DWORD; // 2.3のみ
69 bUpdate: Boolean; // 2.4
70 bCrc: Boolean;
71 iCrcData: DWORD;
72 bLimit: Boolean; // 2.4
73 rLimit: TMyID3HeaderExtendedLimit; // 2.4
74 end;
75
76 // ヘッダーフラグ
77 TMyID3HeaderFlag = record
78 bUnsync: Boolean;
79 bCompress: Boolean; // 2.2のみ 圧縮されているか
80 bExtended: Boolean; // 2.3〜
81 bExperimental: Boolean; // 2.3〜
82 bFooter: Boolean; // 2.4のみ
83 end;
84
85 // ヘッダー
86 TMyID3Header = record
87 iSize: DWORD;
88 rFlag: TMyID3HeaderFlag;
89 rExtended: TMyID3HeaderExtended;
90 end;
91
92 { フレーム }
93 // フレームのステータスフラグ
94 TMyID3FrameStatusFlag = record
95 bDelFromTag: Boolean; // このフレームを認識できずタグを変更したときにこのフレームを削除すべきか
96 bDelFromFile: Boolean; // このフレームを認識できずタグ以外のデータを変更したときにこのフレームを削除すべきか
97 bReadOnly: Boolean; // 読み込み専用か
98 bUnknown: Boolean; // 未知のフラグが立っているか
99 end;
100
101 // フレームのフォーマットフラグ
102 TMyID3FrameFormatFlag = record
103 bGroup: Boolean; // グループ化されているか
104 iGroupID: Byte; // グループ識別子
105 bCompress: Boolean; // 圧縮されているか
106 iDecompSize: DWORD; // 圧縮伸張後のサイズ
107 bEncrypt: Boolean; // 暗号化されているか
108 iEncryptMethod: Byte; // 暗号化の方法
109 bUnsync: Boolean; // 非同期化されているか
110 bDataLen: Boolean; // フラグが何も指定されていない場合のフレームサイズを記したデータが付加されているか
111 iDataLen: DWORD; // フラグが何も指定されていない場合のフレームサイズ
112 bUnknown: Boolean; // 未知のフラグが立っているか
113 end;
114
115 // フレームヘッダー
116 TMyID3Frame = record
117 sFrameID: String[4]; // フレームを判別するための'0'..'9','A'..'Z'で構成されている識別子
118 iHeaderSize: Byte; // ヘッダーのサイズ 2.2とそれ以降(2.3〜)で違う
119 iSize: DWORD; // フレームサイズ
120 iReadSize: DWORD; // 正味のフレームサイズ
121 rStatusFlag: TMyID3FrameStatusFlag;
122 rFormatFlag: TMyID3FrameFormatFlag;
123 end;
124
125 TMyTagID3 = class(TObject)
126 private
127 F_sFileName: string;
128 // Version
129 F_sVersion: string;
130 F_iVer, F_iMajor, F_iRev: Byte;
131 // Tag
132 F_sTrack, // TRCK トラックの番号/セット中の位置
133 F_sSeries, // TIT1 内容の属するグループの説明
134 F_sTitle, // TIT2 タイトル/曲名/内容の説明
135 F_sSubTitle, // TIT3 サブタイトル/説明の追加情報
136 F_sAlbum, // TALB アルバム/映画/ショーのタイトル
137 F_sAuthor, // TPE1 主な演奏者/ソリスト
138 F_sBand, // TPE2 バンド/オーケストラ/伴奏
139 F_sConductor, // TPE3 指揮者/演奏者詳細情報
140 F_sComposer, // TCOM 作曲者
141 F_sWriter, // TEXT 作詞家/文書作成者
142 F_sTranslator, // TPE4 翻訳者, リミックス, その他の修正
143 F_sPublisher, // TPUB 出版社, レーベル
144 F_sGenre, // TCON ジャンル
145 F_sLength, // TLEN 長さ
146 F_sYear, // TYER 年
147 F_sDate, // TDAT 日付
148 F_sLylic, // USLT 非同期 歌詞/文書のコピー
149 F_sOriginalAlbum, // TOAL オリジナルのアルバム/映画/ショーのタイトル
150 F_sOriginalWriter, // TOLY オリジナルの作詞家/文書作成者
151 F_sOriginalAuthor, // TOPE オリジナルアーティスト/演奏者
152 F_sOriginalRelease, // TORY オリジナルのリリース年
153 F_sComment: string; // COMM
154
155 F_rHeader: TMyID3Header;
156 F_rFrame: TMyID3Frame;
157
158 function F_DecodeUnsyncData(pData: TBytes; iLen: integer): DWORD;
159 function F_GetSyncsafeSize(pData: TBytes;Index, iCount: integer): LongWord;
160 function F_GetSize(pData: TBytes; Index, iCount: integer): LongWord;
161 procedure F_ReadHeader;
162 function F_GetText(pData: TBytes; Index, iCount: integer): string;
163 function F_GetFullText(pData: TBytes; iCount: integer): string;
164 // function F_GetUserText(pData: PBytes; iCount: DWORD): string;
165 procedure F_ReadFrame(pData: TBytes; Index: integer);
166 procedure F_SetFileName(sFileName: string);
167 procedure F_Clear;
168 procedure F_ReadV1;
169 procedure F_ReadV2;
170 // procedure F_WriteV1;
171 function GetBit(Data: Byte; X: integer): Boolean;
172 public
173 constructor Create(sFile: string);
174
175 property FileName: string read F_sFileName write F_SetFileName;
176 property Version: string read F_sVersion;
177 property Major: Byte read F_iMajor;
178 property Ver: Byte read F_iVer;
179 property Rev: Byte read F_iRev;
180 property Track: string read F_sTrack;
181 property Series: string read F_sSeries;
182 property Title: string read F_sTitle;
183 property SubTitle: string read F_sSubTitle;
184 property Album: string read F_sAlbum;
185 property Author: string read F_sAuthor;
186 property Band: string read F_sBand; // TPE2 バンド/オーケストラ/伴奏
187 property Conductor: string read F_sConductor; // TPE3 指揮者/演奏者詳細情報
188 property Composer: string read F_sComposer; // TCOM 作曲者
189 property Writer: string read F_sWriter; // TEXT 作詞家/文書作成者
190 property Translator: string read F_sTranslator;
191 // TPE4 翻訳者, リミックス, その他の修正
192 property Publisher: string read F_sPublisher; // TPUB 出版社, レーベル
193 property Genre: string read F_sGenre;
194 property Length: string read F_sLength; // TLEN 長さ
195 property Year: string read F_sYear; // TYER 年
196 property Date: string read F_sDate; // TDAT 日付
197 property Lylic: string read F_sLylic; // USLT 非同期 歌詞/文書のコピー
198 property OriginalAlbum: string read F_sOriginalAlbum;
199 // TOAL オリジナルのアルバム/映画/ショーのタイトル
200 property OriginalWriter: string read F_sOriginalWriter;
201 // TOLY オリジナルの作詞家/文書作成者
202 property OriginalAuthor: string read F_sOriginalAuthor;
203 // TOPE オリジナルアーティスト/演奏者
204 property OriginalRelease: string read F_sOriginalRelease;
205 // TORY オリジナルのリリース年
206 property Comment: string read F_sComment;
207 end;
208
209 // ------------------------------------------------------------------------------
210 implementation
211
212 // ------------------------------------------------------------------------------
213 // 汎用ルーチン
214
215 function gfniFileRead(var pData: TBytes; sFile: string;
216 iOffset, iByte: integer): integer;
217 // ファイルからiByteバイトを読み込んでpDataにセットし、読み込んだバイト数を返す
218 // iOffsetはファイルの先頭からの0ベースのオフセット
219 var
220 s: TFileStream;
221 begin
222 s := TFileStream.Create(sFile, fmOpenRead);
223 try
224 // 呼び出し側でpDataのメモリーを開放をする必要あり
225 // string(pData)としても問題ないように
226 // pData:=AllocMem(iByte+2);
227 SetLength(pData, iByte + 2);
228 result := s.Read(pData, iOffset, iByte);
229 finally
230 s.Free;
231 end;
232 end;
233
234 function gfniFileEndRead(var pData: TBytes; sFile: string;
235 iByte: integer): integer;
236 // ファイルの後ろからiByteバイトを読み込んでpDataにセットし、読み込んだバイト数を返す
237 var
238 li_Offset: integer;
239 s: TFileStream;
240 begin
241 s := TFileStream.Create(sFile, fmOpenRead);
242 try
243 li_Offset := s.Seek(0, soEnd) - iByte;
244 // 呼び出し側でpDataのメモリーを開放をする必要あり
245 // string(pData)としても問題ないように
246 SetLength(pData, iByte + 2);
247 result := s.Read(pData, li_Offset, iByte);
248 finally
249 s.Free;
250 end;
251 end;
252
253 function gfnsByteCopy(pStr: TBytes; iIndex, iCount: integer): string;
254 // pStr[iIndex]からiCount個の文字列をコピーして返す
255 var
256 i: integer;
257 s: TBytes;
258 begin
259 SetLength(s, iCount);
260 for i := 1 to iCount do
261 begin
262 s[i] := pStr[iIndex + i - 1];
263 end;
264 result := UTF8ToString(s);
265 Finalize(s);
266 end;
267
268 type
269 TMyCharCode = (cdShift_JIS, cdUnicodeLE, cdUnicodeBE, cdUTF_16LE, cdUTF_16BE,
270 cdUTF_8, cdUTF_8N);
271
272 function gfnsWStrBCopy(pStr: TBytes; iIndex, iCount: cardinal;
273 cdCode: TMyCharCode): string;
274 // pStr[iIndex]からiCountバイトの文字列をコピーしてstringにして返す
275 var
276 i: DWORD;
277 ls_Temp: string;
278 lp_Buff: TBytes;
279 begin
280 if (pStr[iIndex] = Ord(#$EF)) and (pStr[iIndex + 1] = Ord(#$BB)) and
281 (pStr[iIndex + 2] = Ord(#$BF)) then
282 begin
283 // UTF-8 BOMあり
284 cdCode := cdUTF_8;
285 Inc(iIndex, 3);
286 Dec(iCount, 3);
287 end
288 else if (pStr[iIndex] = Ord(#$FF)) and (pStr[iIndex + 1] = Ord(#$FE)) then
289 begin
290 // UTF-16 BOMありのリトルエンディアン
291 cdCode := cdUnicodeLE;
292 Inc(iIndex, 2);
293 Dec(iCount, 2);
294 end
295 else if (pStr[iIndex] = Ord(#$FE)) and (pStr[iIndex + 1] = Ord(#$FF)) then
296 begin
297 // UTF-16 BOMありのビッグエンディアン
298 cdCode := cdUnicodeBE;
299 Inc(iIndex, 2);
300 Dec(iCount, 2);
301 end;
302
303 if (cdCode = cdUTF_16BE) or (cdCode = cdUnicodeBE) then
304 begin
305 // UTF-16 ビッグエンディアン
306 SetLength(lp_Buff, iCount + 2);
307 try
308 for i := 0 to iCount - 1 do
309 begin
310 if (i mod 2 = 0) then
311 begin
312 lp_Buff[i] := pStr[iIndex + i + 1];
313 lp_Buff[i + 1] := pStr[iIndex + i];
314 end;
315 end;
316 result := UTF8ToString(lp_Buff);
317 finally
318 Finalize(lp_Buff);
319 end;
320 end
321 else
322 begin
323 ls_Temp := gfnsByteCopy(pStr, iIndex, iCount);
324 if (cdCode = cdShift_JIS) then
325 begin
326 // Shift-JIS
327 result := ls_Temp;
328 end
329 else if (cdCode = cdUTF_8) or (cdCode = cdUTF_8N) then
330 begin
331 // UTF-8
332 result := ls_Temp;
333 end
334 else if (cdCode = cdUnicodeLE) or (cdCode = cdUTF_16LE) then
335 begin
336 // UTF-16 リトルエンディアン
337 result := ls_Temp + #0#0;
338 end;
339 end;
340 end;
341
342 // 汎用ルーチン終わり
343
344 // ==============================================================================
345 const
346 lciTAG_HEADERSIZE = 10;
347
348 // ------------------------------------------------------------------------------
349 constructor TMyTagID3.Create(sFile: string);
350 begin
351 inherited Create;
352
353 F_SetFileName(sFile);
354 end;
355
356 procedure TMyTagID3.F_Clear;
357 begin
358 F_sVersion := '';
359 F_iVer := 0;
360 F_iMajor := 0;
361 F_iRev := 0;
362
363 F_sTrack := ''; // TRCK トラックの番号/セット中の位置
364 F_sSeries := ''; // TIT1 内容の属するグループの説明
365 F_sTitle := ''; // TIT2 タイトル/曲名/内容の説明
366 F_sSubTitle := ''; // TIT3 サブタイトル/説明の追加情報
367 F_sAlbum := ''; // TALB アルバム/映画/ショーのタイトル
368 F_sAuthor := ''; // TPE1 主な演奏者/ソリスト
369 F_sBand := ''; // TPE2 バンド/オーケストラ/伴奏
370 F_sConductor := ''; // TPE3 指揮者/演奏者詳細情報
371 F_sComposer := ''; // TCOM 作曲者
372 F_sWriter := ''; // TEXT 作詞家/文書作成者
373 F_sTranslator := ''; // TPE4 翻訳者, リミックス, その他の修正
374 F_sPublisher := ''; // TPUB 出版社, レーベル
375 F_sGenre := ''; // TCON ジャンル
376 F_sLength := ''; // TLEN 長さ
377 F_sYear := ''; // TYER 年
378 F_sDate := ''; // TDAT 日付
379 F_sLylic := ''; // USLT 非同期 歌詞/文書のコピー
380 F_sOriginalAlbum := ''; // TOAL オリジナルのアルバム/映画/ショーのタイトル
381 F_sOriginalWriter := ''; // TOLY オリジナルの作詞家/文書作成者
382 F_sOriginalAuthor := ''; // TOPE オリジナルアーティスト/演奏者
383 F_sOriginalRelease := ''; // TORY オリジナルのリリース年
384 F_sComment := ''; // COMM コメント
385 end;
386
387 function TMyTagID3.F_DecodeUnsyncData(pData: TBytes; iLen: integer): DWORD;
388 {
389 非同期化処理されたデータを元に戻し、元のサイズを返す
390 ヘッダーのフラグで非同期フラグが立っている場合、ヘッダーのデータ中に同期信号と同
391 じパターンが現れるのを避けるための処理が行われているのでそれを元に戻す。
392
393 回避処理の内容は、同期信号のパターンが2進表記で
394 %11111111 111xxxxx
395 となるので xFF の後に x00 を挿入して
396 %11111111 00000000 111xxxxx
397 とする。これで同期信号と同じパターンを回避する。
398 回避処理の内容は頭からデータを1バイトづつ見ていき xFF に出くわし、その後の1バイト
399 が xE0 以上または x00 なら x00 を加える。または単純に xFF の後に x00 を加える。
400
401 このルーチンではその逆を行うため xFF の次に現れる x00 を取り除く処理を行う。
402 この結果ヘッダーの実際のサイズは取り除いた x00 の分だけ減る。
403 }
404 var
405 i, j, k, li_Shift: integer;
406 begin
407 li_Shift := 0;
408 j := 0;
409 k := 0;
410 for i := 0 to iLen - 1 do
411 if pData[i] = Ord(#$FF) then
412 if li_Shift = 0 then
413 j := i
414 else
415 begin
416 Inc(k);
417 pData[j + k] := pData[i];
418 end
419 else if pData[i] = 0 then
420 if li_Shift = 0 then
421 Inc(li_Shift)
422 else
423 Inc(k);
424 result := iLen - li_Shift;
425 end;
426
427 function lfniPower(iBase, iExponent: WORD): LongWord;
428 var
429 i: integer;
430 begin
431 result := 1;
432 for i := 1 to iExponent do
433 result := result * iBase;
434 end;
435
436 function TMyTagID3.F_GetSyncsafeSize(pData: TBytes;Index, iCount: integer): LongWord;
437 { 同期信号である %11111111 111xxxxx と同じパターンになるのを防ぐために最上位ビット
438 が常に0であるような表現の数値として返す。
439 %11111111 は16進ならxFF、10進なら255。
440 %01111111 は16進ならx7F、10進なら127。
441 通常1バイトは255(xFF)が最大値となるが上記の場合は127(x7F)が最大値であり128(x80)に
442 なると桁あがりする。
443 よってx100は通常なら256だが上記のような場合は128となる。
444 x10000は通常なら65536(256*256)だが16384(128*128)となる。
445 }
446 var
447 i: integer;
448 begin
449 result := 0;
450 for i := Index to iCount - 1 do
451 begin
452 Inc(result, pData[(iCount - i) - 1] * lfniPower(128, i));
453 end;
454
455 {
456 ↑は
457 Result :=
458 Ord(pData[0]) * $200000 + //128 * 128 * 128
459 Ord(pData[1]) * $4000 + //128 * 128
460 Ord(pData[2]) * $80 + //128
461 Ord(pData[3]);
462 というようなことを汎用的にやっている
463 }
464 end;
465
466 function TMyTagID3.F_GetSize(pData: TBytes; Index, iCount: integer): LongWord;
467 // ↑と違い通常の数値として返す。
468 var
469 i, j: integer;
470 s: array [0 .. 3] of Byte;
471 begin
472 if iCount > 4 then
473 iCount := 4;
474 j := 3;
475 for i := Index to Index + iCount - 1 do
476 begin
477 s[j] := pData[i];
478 Dec(j);
479 end;
480 result := LongWord(s);
481 end;
482
483 function TMyTagID3.F_GetText(pData: TBytes; Index, iCount: integer): string;
484 // 改行を含まないテキストを返す
485 var
486 li_Index, li_Enc: integer;
487 begin
488 li_Enc := pData[Index];
489 li_Index := 1; // 文字エンコードを飛ばす
490 Dec(iCount); // 文字エンコード(1Byte)分を減らす
491 if (li_Enc = $0) then
492 begin
493 result := gfnsByteCopy(pData, Index + li_Index, iCount);
494 end
495 else if (li_Enc = $1) then
496 begin
497 // BOMありのUTF-16
498 // とりあえずリトルエンディアンを指定しているが関数内でBOMを自動判定している
499 result := gfnsWStrBCopy(pData, Index + li_Index, iCount, cdUnicodeLE);
500 end
501 else if (li_Enc = $2) then
502 begin
503 // BOMなしのビッグエンディアン
504 result := gfnsWStrBCopy(pData, Index + li_Index, iCount, cdUTF_16BE);
505 end
506 else if (li_Enc = $3) then
507 begin
508 // UTF-8
509 result := gfnsByteCopy(pData, Index + li_Index, iCount);
510 end;
511 end;
512
513 function TMyTagID3.F_GetFullText(pData: TBytes; iCount: integer): string;
514 // USLT,COMMなどの改行を含むテキストを返す
515 var
516 i, li_Enc, li_Index: DWORD;
517 begin
518 result := '';
519
520 li_Enc := pData[0];
521 li_Index := (1 + 3); // 文字エンコード(1Byte)と言語コード(3Byte)を読み飛ばす
522 Dec(iCount, (1 + 3));
523 if (li_Enc = $0) // ISO-8859-1
524 or (li_Enc = $3) // UTF-8
525 then
526 begin
527 if (iCount > 0) then
528 begin
529 // Content decriptorを読み飛ばす
530 for i := 0 to iCount - 1 do
531 begin
532 if (pData[li_Index + i] = Ord(#0)) then
533 begin
534 Dec(iCount, (i + 1));
535 Inc(li_Index, (i + 1));
536 Break;
537 end;
538 end;
539
540 result := gfnsByteCopy(pData, li_Index, iCount);
541 // 必要なら説明文を頭に加える
542 // Result := gfnsByteCopy(pData, 0, li_Index - 2) + #13 + Result;
543 if (li_Enc = $3) then
544 begin
545 // UTF-8
546 result := UTF8ToString(result);
547 end;
548 end;
549 end
550 else
551 begin
552 // Unicode
553 // Content decriptorを読み飛ばす
554 for i := 0 to iCount - 1 do
555 begin // iCountは文字数ではなくバイト数
556 if (i mod 2 = 1) // Unicodeなので2バイトごとの処理
557 and (pData[li_Index + (i - 1)] = Ord(#0)) // 終了文字
558 and (pData[li_Index + i] = Ord(#0)) // 2つで終了
559 then
560 begin
561 Dec(iCount, (i + 1));
562 Inc(li_Index, (i + 1));
563 Break;
564 end;
565 end;
566
567 if (iCount > 0) then
568 begin
569 if (li_Enc = $1) then
570 begin
571 // BOMありのUTF-16
572 result := gfnsWStrBCopy(pData, li_Index, iCount, cdUnicodeLE);
573 end
574 else
575 begin
576 // BOMなしのビッグエンディアン
577 result := gfnsWStrBCopy(pData, li_Index, iCount, cdUTF_16BE);
578 end;
579 end;
580 end;
581 end;
582
583 procedure TMyTagID3.F_ReadHeader;
584 var
585 lp_Buff: TBytes;
586 li_Flag: integer;
587 li_Size: integer;
588 begin
589 {
590 ID3v2ヘッダ 10バイト固定
591 オフセット長さ 内容
592 0 3 ID3 の識別文字3文字
593 3 2 バージョン
594 5 1 フラグ
595 6 4 サイズ(Syncsafe)
596 }
597 li_Size := gfniFileRead(lp_Buff, F_sFileName, 0, lciTAG_HEADERSIZE);
598 // 10バイト固定
599 try
600 if (li_Size = lciTAG_HEADERSIZE) then
601 begin
602 if (lp_Buff[0] = Ord('I')) and (lp_Buff[1] = Ord('D')) and
603 (lp_Buff[2] = Ord('3')) then
604 begin
605 F_iVer := 2; // 最初の3バイトが'ID3'ならID3v2
606 F_iMajor := lp_Buff[3];
607 F_iRev := lp_Buff[4];
608 li_Flag := lp_Buff[5];
609 F_rHeader.iSize := F_GetSyncsafeSize(lp_Buff, 6, 4);
610 // サイズにはヘッダー自身の10バイトとフッターの10バイトは含まない
611
612 F_rHeader.rFlag.bUnsync := GetBit(li_Flag, 7);
613 // 7bit 非同期化処理されているか
614 if (F_iMajor = 2) then
615 begin
616 F_rHeader.rFlag.bCompress := GetBit(li_Flag, 6);
617 // 6bit 2.2のみ 圧縮
618 end
619 else if (F_iMajor >= 3) then
620 begin
621 F_rHeader.rFlag.bExtended := GetBit(li_Flag, 6);
622 // 6bit 2.3〜 拡張ヘッダーがあるか
623 F_rHeader.rFlag.bExperimental := GetBit(li_Flag, 5);
624 // 5bit 2.3〜 テスト用か
625 if (F_iMajor >= 4) then
626 begin
627 F_rHeader.rFlag.bFooter := GetBit(li_Flag, 4);
628 // 4bit 2.4 フッターがあるか
629 end;
630 end;
631 end;
632 end;
633 finally
634 Finalize(lp_Buff);
635 end;
636 end;
637
638 procedure TMyTagID3.F_ReadFrame(pData: TBytes; Index: integer);
639 function lfni_GetSize(pFrameData: TBytes; iByte: Byte): integer;
640 begin
641 result := F_GetSize(pFrameData, F_rFrame.iHeaderSize, iByte);
642 Inc(F_rFrame.iHeaderSize, iByte);
643 Dec(F_rFrame.iSize, iByte);
644 end;
645
646 begin
647 FillChar(F_rFrame, SizeOf(TMyID3Frame), 0);
648 // フレームIDが正しくない場合はフレームヘッダーのサイズは0のまま
649 if pData[0] in [0 .. 9, Ord('A') .. Ord('Z')] then
650 begin
651 if (F_iMajor = 2) then
652 begin
653 // Ver2.2
654 F_rFrame.sFrameID := gfnsByteCopy(pData, 0, 3);
655 // ID、サイズとも3バイトでフラグのヘッダーサイズは6バイト
656 F_rFrame.iHeaderSize := 3 + 3;
657 F_rFrame.iSize := F_GetSize(pData, 3, 3);
658 F_rFrame.iReadSize := F_rFrame.iSize;
659 end
660 else if (F_iMajor >= 3) then
661 begin
662 // Ver2.3以上
663 F_rFrame.sFrameID := gfnsByteCopy(pData, 0, 4);
664 // ID、サイズとも4バイト、2バイトのフラグでフラグのヘッダーサイズは10バイト
665 F_rFrame.iHeaderSize := 4 + 4 + 2;
666 if (F_iMajor = 3) then
667 begin
668 // Ver2.3
669 F_rFrame.iSize := F_GetSize(pData, 4, 4);
670 F_rFrame.rStatusFlag.bDelFromTag := GetBit(pData[8], 7);
671 // タグが変更されたらこのフレームを削除すべきか
672 F_rFrame.rStatusFlag.bDelFromFile := GetBit(pData[8], 6);
673 // 本体データの一部が変更されたらこのフレームを削除すべきか(データが完全に置き換えられた場合は関係ない)
674 F_rFrame.rStatusFlag.bReadOnly := GetBit(pData[8], 5);
675 // 読み取り専用か
676 // 未知のフラグが立っていたらクリアしない限り変更してはいけない
677 F_rFrame.rStatusFlag.bUnknown := GetBit(pData[8], 4) or
678 GetBit(pData[8], 3) or GetBit(pData[8], 2) or
679 GetBit(pData[8], 1) or GetBit(pData[8], 0);
680 // 未知のフラグが立っているか
681
682 F_rFrame.rFormatFlag.bCompress := GetBit(pData[9], 7);
683 // 圧縮されているか
684 F_rFrame.rFormatFlag.bEncrypt := GetBit(pData[9], 6);
685 // 暗号化されているか
686 F_rFrame.rFormatFlag.bGroup := GetBit(pData[9], 5);
687 // グループ化されているか
688 F_rFrame.rFormatFlag.bUnknown := GetBit(pData[9], 4)
689 // 未知のフラグが立っているか
690 or GetBit(pData[9], 3) or GetBit(pData[9], 2) or GetBit(pData[9], 1)
691 or GetBit(pData[9], 0);
692 // 以降の順番は変えてはいけない
693 if (F_rFrame.rFormatFlag.bCompress) then
694 F_rFrame.rFormatFlag.iDecompSize := lfni_GetSize(pData, 4);
695 if (F_rFrame.rFormatFlag.bEncrypt) then
696 F_rFrame.rFormatFlag.iEncryptMethod := lfni_GetSize(pData, 1);
697 if (F_rFrame.rFormatFlag.bGroup) then
698 F_rFrame.rFormatFlag.iGroupID := lfni_GetSize(pData, 1);
699 F_rFrame.iReadSize := F_rFrame.iSize;
700 end
701 else
702 begin
703 // Ver2.4以上
704 F_rFrame.iSize := F_GetSyncsafeSize(pData, 4, 4);
705 F_rFrame.rStatusFlag.bDelFromTag := GetBit(pData[8], 6);
706 // タグ変更でこのフレームを削除するか
707 F_rFrame.rStatusFlag.bDelFromFile := GetBit(pData[8], 5);
708 // 本体データの変更でこのフレームを削除するか
709 F_rFrame.rStatusFlag.bReadOnly := GetBit(pData[8], 4);
710 // 読み取り専用か
711 F_rFrame.rStatusFlag.bUnknown := GetBit(pData[8], 7)
712 // 未知のフラグが立っているか
713 or GetBit(pData[8], 3) or GetBit(pData[8], 2) or GetBit(pData[8], 1)
714 or GetBit(pData[8], 0);
715
716 F_rFrame.rFormatFlag.bGroup := GetBit(pData[9], 6);
717 // グループ化
718 F_rFrame.rFormatFlag.bCompress := GetBit(pData[9], 3);
719 // 圧縮(v2.3と違いフレームヘッダの後に圧縮前のサイズは追加されていない)
720 F_rFrame.rFormatFlag.bEncrypt := GetBit(pData[9], 2); // 暗号化
721 F_rFrame.rFormatFlag.bUnsync := GetBit(pData[9], 1);
722 // 非同期化処理
723 F_rFrame.rFormatFlag.bDataLen := GetBit(pData[9], 0);
724 // データ長指定子
725 F_rFrame.rFormatFlag.bUnknown := GetBit(pData[9], 7)
726 // 未知のフラグが立っているか
727 or GetBit(pData[9], 5) or GetBit(pData[9], 4);
728
729 // 最初に非同期化処理解除行う
730 if (F_rFrame.rFormatFlag.bUnsync) then
731 begin
732 F_rFrame.iReadSize := F_DecodeUnsyncData
733 (Copy(pData, F_rFrame.iHeaderSize, F_rFrame.iSize), F_rFrame.iSize);
734 end
735 else
736 begin
737 F_rFrame.iReadSize := F_rFrame.iSize;
738 end;
739 // 次に暗号化を解く
740 if (F_rFrame.rFormatFlag.bEncrypt) then
741 begin
742 // 暗号化を解く。。のはまぁ無理なのでこのフラグが立っていたら次のフレームに飛ばすようにした方が良いのかも
743 end;
744 // 最後に圧縮解除
745 if (F_rFrame.rFormatFlag.bCompress) then
746 begin
747 // 圧縮解除
748 end;
749
750 // 以降の順番は変えてはいけない
751 if (F_rFrame.rFormatFlag.bGroup) then
752 F_rFrame.rFormatFlag.iGroupID := lfni_GetSize(pData, 1); // グループ化
753 if (F_rFrame.rFormatFlag.bEncrypt) then
754 F_rFrame.rFormatFlag.iEncryptMethod := lfni_GetSize(pData, 1); // 暗号化
755 if (F_rFrame.rFormatFlag.bDataLen) then
756 F_rFrame.rFormatFlag.iDataLen := lfni_GetSize(pData, 4); // データ長指定子
757 end;
758 end;
759 end;
760 end;
761
762 procedure TMyTagID3.F_SetFileName(sFileName: string);
763 begin
764 F_sFileName := sFileName;
765 F_Clear;
766
767 // ID3v2としてヘッダーを読んでみる
768 F_ReadHeader;
769 if (F_iVer = 2) then
770 begin
771 // ID3v2
772 F_iVer := 2;
773 F_sVersion := Format('%d.%d.%d', [F_iVer, F_iMajor, F_iRev]);
774 F_ReadV2;
775 end
776 else
777 begin
778 // ID3v2ではないのでID3v1として読んでみる
779 F_ReadV1;
780 if (F_iVer = 1) then
781 begin
782 // ID3v1だった
783 F_sVersion := Format('%d.%d', [F_iVer, F_iMajor]);
784 end;
785 end;
786 end;
787
788 function TMyTagID3.GetBit(Data: Byte; X: integer): Boolean;
789 var
790 i: integer;
791 begin
792 i := 1;
793 i := i shl X;
794 result := Data and i > 0;
795 end;
796
797 procedure TMyTagID3.F_ReadV2;
798 var
799 lp_Buff: TBytes;
800 li_Size, li_Offset: integer;
801 ls_Text: string;
802 begin
803 if (F_rHeader.iSize > 0) then
804 begin
805 li_Size := gfniFileRead(lp_Buff, F_sFileName, lciTAG_HEADERSIZE,
806 F_rHeader.iSize); // 10バイト目から読み込む
807 try
808 if (li_Size = F_rHeader.iSize) then
809 begin
810 li_Offset := 0;
811 if (F_rHeader.rFlag.bUnsync) then
812 begin
813 // タグ全体に非同期化処理を行ってあるので元に戻し、戻した後のサイズを返す
814 F_rHeader.iSize := F_DecodeUnsyncData(lp_Buff, li_Size);
815 end;
816 if F_rHeader.rFlag.bExtended then
817 begin
818 // 拡張ヘッダー
819 F_rHeader.rExtended.iSize := F_GetSize(lp_Buff, 0, 4);
820 if (F_iMajor = 3) then
821 begin
822 // 2.3
823 F_rHeader.rExtended.iPaddingSize := F_GetSize(lp_Buff, 6, 4);
824 F_rHeader.rExtended.bCrc := GetBit(lp_Buff[4],7);
825 if (F_rHeader.rExtended.bCrc) then
826 begin
827 F_rHeader.rExtended.iCrcData := F_GetSize(lp_Buff, 10, 4);
828 end;
829 Dec(F_rHeader.iSize, F_rHeader.rExtended.iPaddingSize);
830 // パディング分を引いておく
831 {
832 拡張ヘッダーサイズ(rExtended.iSize)には拡張ヘッダーサイズを表す4バイトは含まれない。
833 そのためrExtended.iSizeは6か10のどちらかとなる。
834 6というのはCRCフラグがオフの場合でパディングサイズを表す4バイトとフラグの2バイト。
835 10というのはCRCフラグがオンで上記の6バイトにCRCデータの4バイト。
836 で、結局フレームを読むためのオフセットはヘッダーサイズを表した4バイト + rExtended.iSize となる。
837 }
838 li_Offset := 4 + F_rHeader.rExtended.iSize;
839 end
840 else if (F_iMajor = 4) then
841 begin
842 // 2.4
843 // 2.4は2.3と違い拡張ヘッダーサイズを表す4バイトも含んだサイズ。
844 // なのでオフセットは単純にrExtended.iSizeを足すだけ。
845 li_Offset := F_rHeader.rExtended.iSize;
846 // 拡張ヘッダーの詳細は読み込みには必要ないので割愛
847 end;
848 end;
849
850 repeat
851 F_ReadFrame(lp_Buff, li_Offset);
852 Inc(li_Offset, F_rFrame.iHeaderSize);
853 // 暗号化されていたら(復元できないので)処理に入らない
854 if (F_rFrame.iSize > 0) and not(F_rFrame.rFormatFlag.bEncrypt) then
855 begin
856 if (F_rFrame.sFrameID = 'COMM') or (F_rFrame.sFrameID = 'COM') then
857 begin
858 // コメント
859 // 複数のコメントタグがあったら追加する
860 F_sComment := F_sComment + #13#13 +
861 F_GetFullText(Copy(lp_Buff, li_Offset, F_rFrame.iReadSize),
862 F_rFrame.iReadSize);
863 end
864 else if (F_rFrame.sFrameID = 'USLT') or (F_rFrame.sFrameID = 'ULT')
865 then
866 begin
867 // 歌詞
868 // 複数の歌詞タグがあったら追加する
869 F_sLylic := F_sLylic + #13#13 +
870 F_GetFullText(Copy(lp_Buff, li_Offset, F_rFrame.iReadSize),
871 F_rFrame.iReadSize);
872 // end else if (F_rFrame.sFrameID = 'TXXX') or (F_rFrame.sFrameID = 'TXX') then begin
873 // //ユーザー定義テキスト(これのみ複数行テキスト)
874 // //必要ならコードを書く
875 // F_sUserText := F_sUserText + #13#13 + F_GetUserText(@lp_Buff[li_Offset], F_rFrame.iReadSize);
876 // end else if (F_rFrame.sFrameID = 'SEEK') then begin
877 // ※要検証
878 // //タグ分割
879 // li_SeekLen := F_GetSize(@lp_Buff[li_Offset], 4); //分割タグまでの距離
880 // li_SeekCount := F_rHeader.iSize - li_Offset - F_rFrame.iSize; //分割の残りのバイト数
881 // li_SeekPos := F_rHeader.iSize - li_SeekCount + li_SeekLen; //分割タグのインデックス
882 //
883 // //until〜 の条件に合致するように調整
884 // //ヘッダーのサイズから今まで読み込んだ分と、このSEEKフレーム分を差し引く。
885 // Dec(F_rHeader.iSize, (li_Offset + F_rFrame.iSize));
886 // li_Offset := 0;
887 //
888 // //残りを読み込みなおす
889 // FreeMem(lp_Buff);
890 // gfniFileRead(lp_Buff, li_SeekPos, li_SeekCount);
891 //
892 // Continue;
893 end
894 else if (F_rFrame.sFrameID[1] = 'T') then
895 begin
896 // 改行無しのテキスト
897 ls_Text := F_GetText(lp_Buff,li_Offset, F_rFrame.iReadSize);
898
899 if (F_rFrame.sFrameID = 'TRCK') or (F_rFrame.sFrameID = 'TRK')
900 then
901 begin
902 // トラックの番号/セット中の位置
903 F_sTrack := ls_Text;
904 end
905 else if (F_rFrame.sFrameID = 'TIT1') or (F_rFrame.sFrameID = 'TT1')
906 then
907 begin
908 // 内容の属するグループの説明
909 F_sSeries := ls_Text;
910 end
911 else if (F_rFrame.sFrameID = 'TIT2') or (F_rFrame.sFrameID = 'TT2')
912 then
913 begin
914 // タイトル/曲名/内容の説明
915 F_sTitle := ls_Text;
916 end
917 else if (F_rFrame.sFrameID = 'TIT3') or (F_rFrame.sFrameID = 'TT3')
918 then
919 begin
920 // サブタイトル/説明の追加情報
921 F_sSubTitle := ls_Text;
922 end
923 else if (F_rFrame.sFrameID = 'TALB') or (F_rFrame.sFrameID = 'TAL')
924 then
925 begin
926 // アルバム/映画/ショーのタイトル
927 F_sAlbum := ls_Text;
928 end
929 else if (F_rFrame.sFrameID = 'TPE1') or (F_rFrame.sFrameID = 'TP1')
930 then
931 begin
932 // 主な演奏者/ソリスト
933 F_sAuthor := ls_Text;
934 end
935 else if (F_rFrame.sFrameID = 'TPE2') or (F_rFrame.sFrameID = 'TP2')
936 then
937 begin
938 // バンド/オーケストラ/伴奏
939 F_sBand := ls_Text;
940 end
941 else if (F_rFrame.sFrameID = 'TPE3') or (F_rFrame.sFrameID = 'TP3')
942 then
943 begin
944 // 指揮者/演奏者詳細情報
945 F_sConductor := ls_Text;
946 end
947 else if (F_rFrame.sFrameID = 'TCOM') or (F_rFrame.sFrameID = 'TCM')
948 then
949 begin
950 // 作曲者
951 F_sComposer := ls_Text;
952 end
953 else if (F_rFrame.sFrameID = 'TEXT') or (F_rFrame.sFrameID = 'TXT')
954 then
955 begin
956 // 作詞家/文書作成者
957 F_sWriter := ls_Text;
958 end
959 else if (F_rFrame.sFrameID = 'TPE4') or (F_rFrame.sFrameID = 'TP4')
960 then
961 begin
962 // 翻訳者, リミックス, その他の修正
963 F_sTranslator := ls_Text;
964 end
965 else if (F_rFrame.sFrameID = 'TPUB') or (F_rFrame.sFrameID = 'TPB')
966 then
967 begin
968 // 出版社, レーベル
969 F_sPublisher := ls_Text;
970 end
971 else if (F_rFrame.sFrameID = 'TCON') or (F_rFrame.sFrameID = 'TCN')
972 then
973 begin
974 // ジャンル
975 F_sGenre := ls_Text;
976 end
977 else if (F_rFrame.sFrameID = 'TLEN') or (F_rFrame.sFrameID = 'TLE')
978 then
979 begin
980 // 長さ
981 F_sLength := ls_Text;
982 end
983 else if (F_rFrame.sFrameID = 'TYER') or (F_rFrame.sFrameID = 'TYE')
984 then
985 begin
986 // 年
987 F_sYear := ls_Text;
988 end
989 else if (F_rFrame.sFrameID = 'TDAT') or (F_rFrame.sFrameID = 'TDA')
990 then
991 begin
992 // 日付
993 F_sDate := ls_Text;
994 end
995 else if (F_rFrame.sFrameID = 'TOAL') or (F_rFrame.sFrameID = 'TOT')
996 then
997 begin
998 // オリジナルのアルバム/映画/ショーのタイトル
999 F_sOriginalAlbum := ls_Text;
1000 end
1001 else if (F_rFrame.sFrameID = 'TOLY') or (F_rFrame.sFrameID = 'TOL')
1002 then
1003 begin
1004 // オリジナルの作詞家/文書作成者
1005 F_sOriginalWriter := ls_Text;
1006 end
1007 else if (F_rFrame.sFrameID = 'TOPE') or (F_rFrame.sFrameID = 'TOA')
1008 then
1009 begin
1010 // オリジナルアーティスト/演奏者
1011 F_sOriginalAuthor := ls_Text;
1012 end
1013 else if (F_rFrame.sFrameID = 'TORY') or (F_rFrame.sFrameID = 'TOR')
1014 then
1015 begin
1016 // オリジナルのリリース年
1017 F_sOriginalRelease := ls_Text;
1018 end;
1019 end;
1020 // F_rFrame.iSizeにはフレーム個別の非同期化解除前のサイズが入っている。
1021 // 次のフレームの開始位置を得るにはF_rFrame.rFlag.iUnsyncLengthではなくこっちを用いる
1022 Inc(li_Offset, F_rFrame.iSize);
1023 end;
1024 // Application.ProcessMessages;
1025 until (li_Offset >= F_rHeader.iSize) or (F_rFrame.iHeaderSize = 0);
1026 end;
1027 finally
1028 Finalize(lp_Buff);
1029 end;
1030 end;
1031 end;
1032
1033 procedure TMyTagID3.F_ReadV1;
1034 { 2008-04-02:
1035 ID3タグVer1を取得
1036 http://ja.wikipedia.org/wiki/ID3タグ
1037 }
1038 var
1039 lp_Buff: TBytes;
1040 li_Size: integer;
1041 begin
1042 li_Size := gfniFileEndRead(lp_Buff, F_sFileName, 128);
1043 try
1044 if (li_Size = 128) then
1045 begin
1046 if (lp_Buff[0] = Ord('T')) and (lp_Buff[1] = Ord('A')) and
1047 (lp_Buff[2] = Ord('G')) then
1048 begin
1049 F_iVer := 1;
1050 F_sTitle := gfnsByteCopy(lp_Buff, 3, 30); // 曲名
1051 F_sAuthor := gfnsByteCopy(lp_Buff, 33, 30); // アーティスト
1052 F_sAlbum := gfnsByteCopy(lp_Buff, 63, 30); // アルバム
1053 F_sYear := gfnsByteCopy(lp_Buff, 93, 4); // 日付
1054 F_sComment := gfnsByteCopy(lp_Buff, 97, 30); // コメント
1055 F_sGenre := IntToStr(lp_Buff[127]); // ジャンル
1056 if (lp_Buff[125] = Ord(#0)) and (lp_Buff[126] <> Ord(#0)) then
1057 begin
1058 F_sTrack := IntToStr(lp_Buff[126]); // トラック
1059 F_iMajor := 1;
1060 end;
1061 end;
1062 end;
1063 finally
1064 Finalize(lp_Buff);
1065 end;
1066 end;
1067
1068 end.

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26