| 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. |