Develop and Download Open Source Software

Browse Subversion Repository

Annotation of /TagID3.pas

Parent Directory Parent Directory | Revision Log Revision Log


Revision 7 - (hide annotations) (download) (as text)
Sat Aug 15 03:06:09 2015 UTC (8 years, 6 months ago) by yamat0jp
File MIME type: text/x-pascal
File size: 36403 byte(s)
タグ読み取りに使用するユニットを変更しました。現在Androidでは利用できませんが、書き換えをして使えるようにしようと思います。
1 yamat0jp 7 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