タイムタグ仕様書(開発者向け)

> Version 1.0β1(2000/5/24)

> 本文は、これが唯一非なるタイムタグの規格仕様書ではありません。いろいろなソフトウェアで許されるフォーマットが様々であり、正式なフォーマットが存在しないので、あくまで、一般的に通用している常識範囲で、タイムタグのフォーマットを説明したものです。

> ただし、このようなフォーマットや解釈が多様化している状況は、必ずしもよいこととは言えません。今後は、なるべく多くのソフトが本仕様に準拠することによって、各ソフト間での矛盾がなくなっていくことを期待します。

> なお、本仕様書は、一般者向けの解説に加え、開発者向けの解説も加えております。本仕様に準拠するソフトウェアを開発された場合は、少なくとも、一般者向けの「TimeTag.html」を改変することなく添付してください。

> また、本文中で、緑色で書かれている部分は、開発者向けのコメントです。また、3-4以降は、緑色で書かれていませんが、開発者用の解説です。

> 本仕様書はまだ原案段階（β版）ですので、今後しばらくは細かい修正など改訂が続く可能性がありますが、ソフトへの添付の際には、なるべく最新版を添付してください。

> 特に（懸案事項）とある部分（茶色で書かれている部分）では、まだ仕様に不備がありますのでご注意ください。

> なお、対応ソフトの作成に際して、懸案事項への対応は、まだ確定されておりませんので、開発者のとった対応方法を、ヘルプや Readme などに明記してください。懸案事項以外に関しては、TimeTag.html を添付することによって、開発者は説明文を書く手間から開放されるでしょう。

> 本仕様書配布場所 ： http://get.to/dagashiya

目次

> １．タイムタグの定義
> | 1-1. タイムタグの原則
> | 1-2. 秒単位タイムタグ
> | 1-3. 拡張タイムタグ
> ２．タイムタグ付き歌詞（カラオケタグ付き歌詞）の定義
> | 2-1. 歌詞ファイルフォーマット
> | 2-2. タイムタグ付き歌詞の種類
> | 2-3. タイムタグ付き歌詞の表記法
> | 2-4. カラオケタグ付き歌詞の表記法
> | 2-5. ファイル名など
> |  | 2-5-1. ファイル名
> |  | 2-5-2. 拡張子
> |  | 2-5-3. フォルダ
> |  | 2-5-4. 優先順序
> | 2-6. 表示方法
> |  | 2-6-1. タイムタグ付き歌詞（行頭タイムタグ付き歌詞）の場合
> |  | 2-6-2. カラオケタグ付き歌詞の場合
> |  | 2-6-3. 行送りタイミング
> ３．@タグの定義
> | 3-1. @タグの必要性
> | 3-2. 書式および規則
> |  | 3-2-1. 書式
> |  | 3-2-2. 規則
> | 3-3. 各@タグの定義
> |  | 3-3-1. KRAAMP および Gucchi's Lyrics Plugin で規定されたタグ
> |  | 3-3-2. タイムタグ補正に関するタグ
> |  | 3-3-3. タイムタグ付け情報に関するタグ
> |  | 3-3-4. 現在では新たに付加してはいけないタグ（読み込みの対応は可）
> | 3-4. 歌詞ファイルからの @TimeRatio の取得方法
> | 3-5. 曲ファイルからの TimeRatio の取得方法
> | 3-6. 標準プレイヤーとしての対応方法
> | 3-7. WinAmp 歌詞表示プラグインとしての対応方法
> | 3-8. タイムタグ付けソフトとしての対応方法
> | 3-9. タイムタグ変換ソフトとしての対応方法
> | 3-10. VBMP3.DLL を利用している MP3 プレイヤーとしての対応方法
> | 3-11. プログラム例
> |  | 3-11-1. 標準時間の取得 (C)
> |  | 3-11-2. 標準時間の取得 (VB)
> |  | 3-11-3. WinAmp時間の取得 (C)
> |  | 3-11-4. WinAmp時間の取得 (VB)
> |  | 3-11-5. 曲ファイルの TimeRatio を算出 (C)
> |  | 3-11-6. 曲ファイルの TimeRatio を算出 (VB)
> |  | 3-11-7. サンプルレートを推定する (C)
> |  | 3-11-8. サンプルレートを推定する (VB)
> | 3-12. Winamp 時間と標準時間についての詳細説明
> ４．参考資料
> | 4-1. 従来流通していたが、本仕様書では認めない書き方
> |  | 4-1-1. 行頭タイムタグ付き歌詞におけるリピート拡張
> ４．更新履歴

> 現在の懸案事項の目次
> | 懸案事項2-5-2 対応拡張子
> | 懸案事項2-5-4 優先順序
> | 懸案事項 2-6-2-2 最終タイムタグ行の行末にタイムタグがない場合の補完
> | 懸案事項 2-6-3 行送りタイミング
> | 懸案事項 2-6-2-1 先頭タイムタグ行の行頭にタイムタグがない場合の補完 (Ver1.0β2 にて解決)

１．タイムタグの定義

> 1-1. タイムタグの原則

> > ・ 一つのタイムタグは、半角の "[" "]" で囲まれる。

> > ・ [ ] の間に、分と秒が記述されているものを、「タイムタグ」と呼ぶ。ここでは、「拡張タイムタグ」と区別するために、これを、「秒単位タイムタグ」と表記する。

> > > 例) [03:25]　… 演奏開始から、3分25秒を表す。

> > ・ [ ] の間に、分と秒と1秒以下(1ミリ秒以上)が記述されているものを、「拡張タイムタグ」と呼ぶ。

> > > 例) [03:25:54] … 演奏開始から、3分25秒540ミリ秒を表す。

> 1-2. 秒単位タイムタグ

> > ・ 形式例 : [xx:yy]

> > ・ [ と ] の間は、必ず半角の数字2文字 (上の例の xx の部分) と、半角のコロン ":" と、半角の数字2文字 (上の例の yy の部分) が入る。よって、[ ] を含め、全部で半角 7 文字でなければならない。

> > ・ xx は分単位の数字が入り、1桁の場合は、頭に 0 を付け、必ず2桁にする。同様に、分が 0 分だった場合、xx は 00 となる。よって、xx の最大値は 99 となり、これ以上の時間を表現することはできない。(してはいけない)

> > ・ yy は秒単位の数字が入り、1桁の場合は、頭に 0 を付け、必ず2桁にする。同様に、秒が 0 秒だった場合、yy は 00 となる。yy の最大値は 59 であり、たとえば、[00:65]という表現をしてはいけない。(この場合、[01:05] と表現しなければならない。)

> > ・ よって、秒単位タイムタグの最大値は、[99:59] であり、これ以上の時間を表現してはいけない。最小値は [00:00] である。

> > ・ 1分5秒を示すのに、[1:05] や [01:5] や [1:5] のような表記は本仕様書では許可しません。これらのタグについて拡大解釈して、エラーとならないようにすることも可能ですが、このような仕様から外れた書き方を許さないことも、正しい仕様の普及には必要であると思われますので、タイムタグとして認識しないことを推奨します。

> 1-3. 拡張タイムタグ

> > ・ 形式例 : [xx:yy:zz]

> > ・ [ と ] の間は、必ず半角の数字2文字 (上の例の xx の部分) と、半角のコロン ":" と、半角の数字2文字 (上の例の yy の部分) と、半角のコロン ":" と、半角の数字2文字 (上の例の zz の部分)が入る。よって、[ ] を含め、全部で半角10文字でなくてはならない。

> > ・ xx yy については、1-2.と同様である。

> > ・ zz は10ミリ秒単位の数字が入り、1桁の場合は、頭に 0 を付け、必ず2桁にする。同様に、ミリ秒が 0 ミリ秒だった場合、zz は 00 となる。zz の最大値は 99 である。

> > ・ よって、拡張タイムタグの最大値は、[99:59:99] であり、これ以上の時間を表現してはいけない。最小値は [00:00:00] である。

> > ・ [00:00:123]のように、1ミリ秒単位での表記は本仕様書では許可しません。また、[00:00:1]のような表記は、解釈によって、[00:00:10] とも [00:00:01] とも受け取れるため、これも本仕様書では許可しません。これらのタグについて拡大解釈して、エラーとならないようにすることも可能ですが、上述のように解釈が異なることは危険ですので、エラー（あるいはタイムタグとして認識しない）ような処理にする必要があります。

> > ・ 1分5秒80ミリ秒 を示すのに、[1:05:80] や [01:5:80] や [1:5:80] などのような表記も本仕様書では許可しません。これらのタグについて拡大解釈して、エラーとならないようにすることも可能ですが、このような仕様から外れた書き方を許さないことも、正しい仕様の普及には必要であると思われますので、タイムタグとして認識しないことを推奨します。

２．タイムタグ付き歌詞（カラオケタグ付き歌詞）の定義

> 2-1. 歌詞ファイルフォーマット

> > ・ 歌詞ファイルは、一般的な テキスト形式とする。

> > ・ 文字コードは、原則として、SHIFT-JIS を用いる。ただし、ソフトによって、他コードへも対応することに対して制限はない。

> > ・ 改行文字は、0x0d,0x0a / 0x0d / 0x0a （ vbCrLf / vbCr / vbLf ）のいずれかとする。Windows機で自分で作成した歌詞は、おそらく、0x0d,0x0a という改行コードである場合がほとんどだが、歌詞サイトから得たファイルの場合、それ以外である可能性が高いので、どの改行文字にも対応するようにしておかなくてはならない。

> > ・ 歌詞を表現する文字列、タイムタグ、@タグを含む。（@タグについては、３．を参照すること。）

> > ・ タイムタグに対応させるのは当然であるが、本仕様書では、@タグへの対応も必須とする。

> 2-2. タイムタグ付き歌詞の種類

> > ・ タイムタグが記述されたいずれかの行において、歌詞の一部分よりも後にタイムタグがあり、さらにその行に複数のタイムタグがある場合、この歌詞ファイルを「カラオケタグ付き歌詞」という。この条件に合う行が一行でもあれば、それは「カラオケタグ付き歌詞」である。

> > 例) | [00:17:47]だっ[00:17:93]て「[00:18:54]つまずき[00:19:11]な[00:19:38]が[00:19:64]ら」っ[00:20:15]て
> > [00:20:70]口[00:21:01]で[00:21:09]言う[00:21:49]程[00:22:11]
> > [00:22:53]楽[00:23:06]じゃ[00:23:31]な[00:23:57]い
> > [00:24:15]は[00:24:42]ず[00:24:64]で[00:25:18]しょ[00:25:83]

> > ・上記条件を満たさないタイムタグ付きの歌詞ファイルを「タイムタグ付き歌詞」という。（「行頭タイムタグ付き歌詞」ともいう。）

> > 例) | [00:17:47]だって 「つまずきながら」って
> > [00:20:70]口で言う程 楽じゃないはずでしょ

> 2-3. タイムタグ付き歌詞（行頭タイムタグ付き歌詞）の表記法

> ・ タイムタグがある行は、一つのタイムタグと一行の歌詞を表す文字列からなり、常にタイムタグは行の先頭になくてはならない。

> ・ 一つの歌詞ファイル中に、通常タイムタグと拡張タイムタグの両方が混在してはいけない。一つの歌詞ファイル中のタイムタグは、全て同一の桁数にする。

> > [00:10]あいうえお
> > [00:10:50]かきくけこ

> > というような書き方をしてはいけない。

> > このような歌詞を読み込んでしまった場合、本来はエラーとすべきだが、[00:10] を [00:10:00] とミリ秒部分を補完して解釈しても構わない。

> ・ タイムタグの逆転はあってはいけない。ファイル中で後に続くタイムタグは、常にそれより前のタイムタグより大きい時間を指定しなくてはならない。

> > [00:10:00]あいうえお
> > [00:09:00]かきくけこ

> > というような書き方をしてはいけない。

> > このような歌詞を読み込んでしまった場合、本来はエラーとすべきだが、[00:09:00]の方のタイムタグを無視して表示を行っても構わない。

> ・ 同じ時間を示すタイムタグが続いてはいけない。

> > [00:10:00]あいうえお
> > [00:10:00]かきくけこ

> > というような書き方をしてはいけない。

> > このような歌詞を読み込んでしまった場合、後の方のタイムタグを優先して表示する。

> ・ 一行中で、歌詞と歌詞の間に、タイムタグが1つだけある場合、

> > あいう[00:10:00]えお

> このような場合、他の行にカラオケタグがついていなければ、この歌詞はタイムタグ付き歌詞と判断されるが、このような表記をしてはいけない。なお、他にカラオケタグ付きの行があれば、この書き方は許される。

> このような歌詞を読み込んでしまった場合、本来ならエラーとすべきだが、このタイムタグを行頭に移動して解釈しても構わない。

> ・ 行頭にタイムタグが連続してある場合、

> > [00:10:00][01:40:00]あいうえお
> > [00:11:00][01:41:00]かきくけこ

> > 本仕様書では、このような書き方を認めない。詳細は、4-1-1. を参照すること。

> 2-4. カラオケタグ付き歌詞の表記法

> ・ カラオケタグ付き歌詞への対応するか否かは、各ソフトで決めてかまわない。しかし、技術的に可能であれば、対応することを推奨する。

> ・ すべてのタイムタグは拡張タイムタグでなくてはならない。（秒単位タイムタグでカラオケ的表現をするのは、たいていの場合、困難である。）

> 秒単位タイムタグで記述されたカラオケタグ付き歌詞を読み込んでしまった場合、エラーとしなくても、拡張タイムタグに変換して解釈すればよい。（エラーだと判定するほど対応が難しいわけではないため。）

> ・ 一行中では、タイムタグは、歌詞を挟みながら、いくつあってもよい。

> ・ 歌詞を挟まずに2つのタイムタグを記述してよい。歌詞を挟まずに3つ以上タイムタグがある場合は、最初と最後のタイムタグだけが有効となる。

> > [00:10:00]あいうえお[00:20:00][00:25:00][00:30:00]かきくけこ[00:40:00]

> > は、

> > [00:10:00]あいうえお[00:20:00][00:30:00]かきくけこ[00:40:00]

> > と同じである。

> ・ 必ずしも行の先頭にタイムタグが無くてもよい。また、行の最後にもタイムタグが無くてもよい。

> > あいうえお[00:10:00]かきくけこ[00:20:00]

> > や、

> > [00:10:00]あいうえお[00:20:00]かきくけこ

> > という書き方をしてもよい。

> ・ タイムタグの逆転はあってはならない。

> > [00:10:00]あいうえお[00:09:00]かきくけこ[00:20:00]

> > というような書き方をしてはいけない。

> > このような歌詞を読み込んでしまった場合、本来はエラーとすべきだが、[00:09:00]を無視して処理を行ってもよい。

> ・ 同じ時間を示すタイムタグがあってもよい。

> > [00:10:00]あいうえお[00:10:00]

> > というような書き方をしてもよい。

> 2-5. ファイル名など

> > 2-5-1. ファイル名

> > ・歌詞ファイル名は、拡張子より前の部分が、対応させる曲ファイルと同じでなければならない。

> > > ABC.mp3 と対応する歌詞ファイルは、ABC.lrc ABC.txt ABC.kra である。

> > 2-5-2. 拡張子（懸案事項2-5-2）

> > ・歌詞ファイルとして認められる拡張子は、lrc kra txtである。ただし、カラオケ非対応のソフトでは、kra に対応してはいけない。これらの優先順序については、2-5-4. を参照すること。

> > おそらく他に流通している拡張子はないと思われるが、本当にこれだけでいいのだろうか？

> > 2-5-3. フォルダ

> > ・原則として歌詞ファイルは曲ファイルと同じフォルダになくてはならない。

> > ・ただし、プレイヤーのオプションとして、特定の歌詞ファイル用フォルダを指定し、その中から歌詞ファイルを検索させてもよい。また、その場合に限り、サブフォルダを検索対象にしてもよい。

> > ・ただし、このオプション仕様を実装する場合に、複数のフォルダに対応する歌詞ファイルが存在した場合、優先順序問題が発生する。これについては、2-5-4. を参照すること。

> > 2-5-4. 優先順序（懸案事項2-5-4）

> > ・拡張子の優先順序は、カラオケ対応ソフトでは、 kra > lrc > txt 、非対応ソフトでは、lrc > txt とすべきだと思われる。

> > > なぜなら、まず txt は、最も汎用性のある拡張子であり、歌詞ファイル以外の情報ファイルである可能性も考えられるため、優先順序は低くした方がよいと思われる。

> > > また、kra と lrc では、kra がカラオケタグのみだと思われるのに対し、lrc はカラオケタグあるいは行頭タグのどちらかであると思われるので、 kra > lrc とすべきである。

> > ・フォルダの優先順序は、まず、歌詞用フォルダ内に関しては、指定されたフォルダを優先し、次いで最初に見つかったサブフォルダ内を検索し、最初に見つかった歌詞とする。この見つけ方のアルゴリズムによっては、様々な順序になる可能性があるが、これに関しては、ここで規定すべきではないと思われる。（規定するのが難しいからということもあるが^^;）

> > また、曲ファイルと同じフォルダ と 歌詞用フォルダ のどちらを優先するかは、難しい問題である。

> > 一見すると、曲ファイルと同じフォルダの方を優先する方が自然かもしれないが、例えば、CD-R にタイムタグ付き歌詞と曲ファイルを同時に焼いたとして、その後、カラオケタグ付き歌詞を作成した場合、このカラオケタグ付き歌詞を優先したくなるだろう。

> > このような場合を考えると、歌詞用フォルダ（およびそのサブフォルダ）を優先させた方がよいかもしれない。

> > ・また、拡張子とフォルダとどちらを優先すべきか、という問題がある。

> > > 例えば、歌詞用フォルダ＞曲ファイルフォルダ 、 lrc > txt という優先順序だった場合、

> > > 歌詞用フォルダに ABC.txt が、
> > > 曲ファイルフォルダに ABC.lrc が、

> > > あった場合、どちらが優先されるかである。

> > > これに関しては、フォルダのシーク処理を何度も繰り返すと、重くなるという観点から、ABC.txt すなわち、フォルダの方を優先すべきだと思われる。

> > ・ 最後に、これらのような曲ファイルとは別の歌詞ファイル と LYRICS3v2.00 などの内蔵された歌詞 との優先順序の問題がある。

> > > これも、一見すると、内蔵歌詞を優先するのが自然かもしれないが、上述の CD-R のような例と同様な解釈も生じる。

> 2-6. 表示方法

> 本項目は、演奏しながら歌詞を表示する場合の、表示方法について解説している。

> 本項目では、タイムタグ自体の解釈（歌詞表示のタイミング）についてのみ言及している。それ以外に関する表示方法は、各ソフトウェアの個性を生かしていただきたい。

> また、本項目で「アクティブにする」という表現を用いるが、これは、その歌詞を表示するということを意味している。

> ワイプ表示（本物のカラオケのように、色等が比較的なめらかにスクロールする表示）では、この「アクティブ」になった歌詞をさらに次のタイムタグまでの時間をかけて、ワイプ表示する。このとき、ワイプ後の色（スクロールしていく色）を「アクティブ色」とする。

> 固定表示の場合、この「アクティブ」になった歌詞をすべて特定の色づけなどして表示する。この時の色を「アクティブ色」とする。

> この「ワイプ表示」と「固定表示」については、あくまで一例である。もしより工夫を施した表示方法があれば、これらに準拠した形で自由に表現することについて、本仕様書はいっさい規制しない。

> また、本仕様では、カラオケタグ付き歌詞への対応に関して、技術的に可能であれば対応することを推奨はするが、強制はしていない。

> > 2-6-1. タイムタグ付き歌詞（行頭タイムタグ付き歌詞）の場合

> > ・行頭についているタイムタグの時間になったら、その行全体をアクティブにすること。ただし、ワイプ表示をしてはいけない。

> > ・@タグ行はいっさい表示してはいけない。

> > ・@タグ行以外のタイムタグがついていない行については、注釈行と見なす。この注釈行を表示するかどうかは、プレイヤー依存してよい。

> > ・タイムタグ付き歌詞と、カラオケタグ付き歌詞で、２通りの処理をしなくても、次のような工夫を施すことによって、すべてカラオケタグ付き歌詞として解釈可能である。

> > > [00:10:00]あああああ
> > > [00:11:00]いいいいい
> > > [00:12:00]ううううう

> > > ↓

> > > [00:10:00]あああああ[00:10:00][00:11:00]
> > > [00:11:00]いいいいい[00:11:00][00:12:00]
> > > [00:12:00]ううううう[00:12:00][99:99:99]

> > 2-6-2. カラオケタグ付き歌詞の場合

> > ・原則として、「タイムタグの指定する時間になったら、次のタイムタグまでの間の文字列をアクティブにする」。

> > ・@タグ行はいっさい表示してはいけない。

> > ・@タグ行以外のタイムタグがついていない行については、注釈行と見なす。この注釈行を表示するかどうかは、プレイヤー依存してよい。

> > ・タイムタグが連続する場合、

> > > [00:05:00]あああ[00:10:00][00:15:00]いいい[00:20:00]

> > > このような場合、5秒〜10秒までは、「あああ」をアクティブにし、15秒〜20秒までは、「いいい」をアクティブにする。

> > > 10秒から15秒は、アクティブな歌詞はないので、ワイプ表示であれば、ワイプを停止し、固定表示であれば、アクティブ色がない状態とする。

> > > ただし、

> > > [00:05:00]あああ[00:10:00]
> > > [00:15:00]いいい[00:20:00]

> > > のように、連続するタイムタグの間に、改行が入る場合も、これと同様な解釈をすべきだが、この場合、別途「行送りタイミング」問題が発生する。この問題に関しては、2-6-3. を参照すること。

> > ・同じタイムタグがある場合、

> > > [00:05:00]あああ[00:05:00]いいい[00:10:00]

> > > このような場合、5秒の時点で、「あああ」をアクティブとする。しかし、同時に「いいい」もアクティブとなるので、ワイプ表示の場合、「あああ」はワイプせずアクティブ色で表示し、「いいい」の先頭からワイプする。固定表示の場合、「あああいいい」のすべてをアクティブ色で表す。

> > > また、

> > > [00:05:00]あああ[00:05:00]いいい[00:05:00]

> > > のように、３つ以上、同じタイムタグが続く場合には、

> > > [00:05:00]あああいいい[00:05:00]

> > > のように、途中のタイムタグを無視する。

> > ・行頭にタイムタグがない場合、

> > > [00:05:00]あああ[00:10:00]
> > > いいい[00:15:00]ううう[00:20:00]

> > > これは、

> > > [00:05:00]あああ[00:10:00]
> > > [00:15:00]いいい[00:15:00]ううう[00:20:00]

> > > のように、直後のタイムタグを行頭に補って解釈する。

> > > この場合、2-6-3. 行送りタイミングの問題が発生することに注意すること。

> > ・行末にタイムタグがない場合、

> > > [00:05:00]あああ
> > > [00:10:00]いいい[00:15:00]

> > > これは、

> > > [00:05:00]あああ[00:10:00]
> > > [00:10:00]いいい[00:15:00]

> > > のように、直後のタイムタグを行末に補って解釈する。

> > > これより後ろにタイムタグがない場合には、（懸案事項2-6-2-2）現在のところ、プレイヤーによって異なる解釈がされている。

> > > たとえば、

> > > [00:00:00]あああ[00:07:00]いいい

> > > の場合、

> > > Gucchi's Lyrics Plugin では、
> > > [00:00:00]あああ[00:07:00]いいい[00:10:00]
> > > と補完される。この10秒というのは、固定値で、この行の最初のタイムタグ[00:00:00]に10秒を加算したタイムタグが補完されるようだ。

> > > KbMIDI では、補完はされず、7秒で「あああ」のワイプが終了した後、曲の最後までこの表示が続くようだ。（すなわち、「いいい」はアクティブ色にならないまま終わる。）

> > ・タイムタグだけの行

> > > [00:05:00]あああ[00:10:00]
> > > [00:15:00]
> > > [00:20:00]いいい[00:25:00]

> > > このような場合も、[00:15:00]の後ろにタイムタグを補って解釈するが、この補うタイムタグについては、行送り問題が発生するので、2-6-3. を参照すること。

> > 2-6-3. 行送りタイミング（懸案事項2-6-3）

> > [00:05:00]あああ[00:10:00]
> > [00:15:00]いいい[00:20:00]

> > の場合、5秒〜10秒まで「あああ」をアクティブにし、15秒〜20秒まで「いいい」をアクティブにすることになる。

> > しかし、アクティブな文字以外にも、アクティブな行を考えなくてはいけない。

> > 1行表示のプレイヤーであれば、どの行を表示しておくかが、アクティブ行であり、複数行表示であっても、どの行を中央にしておくか、など、つねに着目している行があるはずである。

> > 当然、アクティブな歌詞がある行が、アクティブ行となるのだが、問題は、上の例の10秒〜15秒までのような「アクティブな歌詞がない時間帯（空白の時間帯）」に、どの行をアクティブにするかということである。

> > この問題を「行送りタイミング問題」という。

> > これまでカラオケタグに対応したプレイヤーはそれほど多くはなかったが、この問題に対する解釈は、それでもバラバラであった。

> > Gucchi's Lyrics Plugin は上の例では、10秒を過ぎると、「いいい」をアクティブ行とし、ワイプの開始は15秒からである。

> > 現時点での KRAAMP や KbMIDI は、10秒でワイプを終えた後も、「あああ」をアクティブ行とし、15秒に「いいい」をアクティブ行とするのと同時に、ワイプを開始する。

> > VBMP3.DLL では、原則として、後者だが、vbmp3_setLyricsNextAdjustTime関数を使うことによって、改行のタイミングを意図的に調整することが可能である。

> > また、

> > [00:15:00]
> > [00:20:00]いいい[00:25:00]

> > の[00:15:00]の後ろにどんなタイムタグを補うかについても、同様の問題が発生する。

> > Gucchi's Lyrics Plugin では、「空白行は早めに移動する」というオプションがあり、これを有効にしておくことで、19秒（つまり1秒前）に「いいい」がアクティブ行となる。無効にしておけば、20秒である。

> > KRAAMP や KbMIDI では、20秒で「いいい」が有効になる。

> > これらの解釈の違いは、直接「アクティブな歌詞」に対しては影響を及ぼさない。（つまり、歌詞がズレるというような大きな問題は引き起こさない。）

> > したがって、ソフトによるこれらの解釈の違いを許すという意見もあるが、統一した方がよい（本仕様で規定した方がよい）という意見もある。

> > また、この空白の時間帯が十分に長い場合、すぐ次の行に移るのでは早すぎるし、かと言って、次の行の開始まで歌詞が表示されない（アクティブ行にならない）のも遅すぎる、という意見もある。この場合、対処法としては、最大先送り時間を決め、次のように処理するという案がある。

> > 最大先送り時間=50ms のとき、

> > | 訂正前 | 訂正後
> > 空白時間＞ 最大先送り時間 | [00:05:00]あああ[00:10:00] [00:15:00]いいい[00:20:00] | [00:05:00]あああ[00:10:00][00:14:50] [00:14:50][00:15:00]いいい[00:20:00]
> > 空白時間＜= 最大先送り時間 | [00:05:00]あああ[00:10:00] [00:10:30]いいい[00:20:00] | [00:05:00]あああ[00:10:00][00:10:30] [00:10:30][00:10:30]いいい[00:20:00]

> > （注 ： 青い部分はなくても同じである。）

> > ただし、このような処理をプレイヤーに持たせることは、基準となる最大先送り行を規定する必要があり、これをいくつにするかでまた問題が起きてしまう。

> > したがって、これはあらかじめタイムタグユーティリティーなどで変換しておくにとどめた方がよいという意見もある。

３．@タグの定義

> 3-1. @タグの必要性

> > タイムタグ付き歌詞を取り扱うソフトウェアとして、注意を払わねばならない問題点の1つとして、「Winamp時間」と「標準時間」の違いがある。

> > 今やもっともポピュラーな Windows 用 mp3 プレイヤーである Winamp には、大きなバグ（？）がある。それは、演奏中に正しい時間を示さない ということである。 正確に言うと、演奏するファイルのフォーマット( mp3, wave, wma など) や、サンプリングレートによっては、時間の進み方が遅くなるのである。特に、一番用いられているであろう、44.1kHz の場合、その3つのフォーマットとも、進み方が遅くなってしまう。また、そのズレ方も、mp3 と wave では同じだが、wma ではまた違ったズレ方をする。このような Winamp での演奏時間のことを 「WinAmp時間」 と呼ぶ。

> > 一方、当然だが、Winamp 以外のプレイヤーでは、意図的に時間の調整を行わない限り、どんなファイルでも正しく時間が進む。これを 「 標準時間 」 と呼ぶ。

> > これによって発生する問題は、

> > > 「Winamp で再生しながら、タイムタグを付けた歌詞を、Winamp 以外のプレイヤーで再生したら、」

> > > 「Winamp を用いずにタイムタグを付けた歌詞を、Winamp用プラグインソフトで表示したら、」

> > 時間が進むにつれて、歌詞を表示するタイミングがズレていってしまうことである。

> > この問題に、Winamp 以外の製作者が対応するためには、この時間のズレを是正して表示するしかない。

> > そのためには、歌詞ファイルに、「タイムタグを付けた時にどういうズレ方で演奏されていたか（またはズレていなかったか）」という情報を埋め込んでおく必要がある。このズレ比率値 ( @TimeRatio ) さえ分かれば、演奏時にそれを是正することができるからだ。

> > この @TimeRatio の他にも、歌詞に様々な情報を付加しておくための規格フォーマットが @タグ である。

> 3-2. 書式および規則

> > 3-2-1. 書式 :

> > @タグ名=内容

> > > タグ名 : タグの名前を示す文字列。

> > > 内容 : タグ名の内容を示す値や文字列。

> > 3-2-2. 規則

> > ・１つの@タグ行は１行の文字列で表される。

> > ・@タグ行は歌詞テキスト中のどこにあってもかまわず、その順序も問わない。

> > ・ただし、行中では、以下の規則が適用される。

> > > ・@タグ名と内容の間には、必ず1つだけ半角イコール記号(=)が必要であり、幾つかの半角スペースが入ってもよい。

> > > ・@の前には、余計な文字があってはならない。

> > > ・@タグ行には、いかなるタイムタグも付けてはならない。

> > > ・タグ名 は 大文字小文字の区別はしないが、全角を使ってはいけない。

> > > ・内容が 値 である場合、半角数字以外を記述してはいけない。
> > > 単位も記述してはいけない。内容が空欄であってもいけない。

> > > ・内容が 文字列 である場合、1つの@タグ行は半角 1024文字分 以内とする。 0 文字でもよい。

> > ・以上の規則に当てはまらない場合、その@タグは無効である。

> > ・同一歌詞ファイル中に、同じ@タグが複数存在することは許されない。

> > ・歌詞表示プレイヤーは、行頭が @ である行を表示してはならない。

> 3-3. 各@タグの定義

> > 3-3-1. KRAAMP および Gucchi's Lyrics Plugin で規定されたタグ

> > @Artist | アーティスト名。
> > @Title | タイトル。
> > @Album | アルバム名。
> > @Bgfile | Gucchi's Lyrics Plugin の Karaoke Displayer での 背景ファイル名。
> > @Bgfolder | Gucchi's Lyrics Plugin の Karaoke Displayer での 背景フォルダ名。

> > 注: Gucchi's Lyrics Plugin の開発は現在終了しているが、これらの @タグ は 本仕様書でも引き継がれている。

> > 3-3-2. タイムタグ補正に関するタグ

> > @TimeRatio |
> > ・ | タイムタグが、標準時間に対してどれだけズレているかを表す割合を表す値。
> > ・ | 必ず 0 より大きくなければならない。また、現状では、1 を越えるべきではない。なぜなら、そのようなズレ方をするプレイヤーがないからである。
> > ・ | 1 である場合、その歌詞が標準時間であることを示す。
> > @Offset |
> > ・ | 演奏時に、すべてのタイムタグに適用するズレ幅を標準時間で表した値。整数。 [ms]
> > ・ | 負値ならばタイムタグを早める方向にずらす。
> > ・ | この値は主にユーザーが調整するために手動で書き加えるものである。したがってタイムタグ変換ユーティリティーやタイムタグ付けソフトでは、この値を新たに追加するべきではない。
> > ・ | プレイヤーがこの値を適用した結果、タイムタグが負になった場合は 0 とすること。その際、複数のタイムタグが 0 となった場合、最後のタイムタグのみ有効とする。
> > ・ | @Offset で変換した後、歌詞を保存するときは、@Offset=0 を書き加える。（@Offset行を削除してはいけない。）
> > @SilencemSec |
> > ・ | タイムタグ作成時に再生されたサウンドデータの曲前部の無音部長さを標準時間で表した値。整数。[ms]
> > ・ | 曲ファイルごとの無音部の長さの違いを補正するときに用いる。（歌詞中のタイムタグからこの値を減算し、演奏する曲の無音部長さを加算する。）
> > ・ | ただし、プレイヤーや歌詞表示プラグインでは @SilencemSec に対応してはいけない(あっても無視すること)。
> > ・ | @Offset行 がある場合は、@SilencemSec に関する処理をしてはいけない。（保存時には @SilencemSec行を削除すること。）
> > ・ | この値を適用した結果が負になった場合は 0 とすること。

> > 3-3-3. タイムタグ付け情報に関するタグ

> > @TaggingBy | ・タイムタグ付けした人の名前。
> > @EditedBy | ・タイムタグを修正した人の名前。

> > 3-3-4. 現在では新たに付加してはいけないタグ（読み込みの対応は可）

> > @Silence | タイムタグ作成時に再生されたサウンドデータの曲前部の無音部のフレーム数。
> > @Flames | タイムタグ作成時に再生されたサウンドデータの総フレーム数。正しくは Frames のはずですが、間違ったままずっと使われていました^^;
> > @TotalSec | タイムタグ作成時に再生されたサウンドデータの総演奏時間。[s]
> > @TimeType | タイムタグ作成時の再生時間の種類。WinAmp か Normal のどちらかを指定。この２単語のみ有効（大小文字を区別しない）。これら以外の文字列は無効である。これらの文字列の後ろに余計な文字列を記入してもいけない。

> > 注 ： これらの@タグは、「りりくす」でサポートされていたが、「りりくすEx」では新たにこれらの@タグを付加しない。

> 3-4. 歌詞ファイルからの @TimeRatio の取得方法

> > 1) 歌詞ファイル中に @TimeRatio が、
> > | ある | → | その値を @TimeRatio とする。
> > | ない | → | 2) へ。
> > 2) 古い歌詞ファイルにも
> > | 対応する | → | 3) へ。
> > | 対応しない | → | 5) へ。
> > 3) @TimeType が
> > | Normal | → | @TimeRatio=1 とする。
> > | WinAmp | → | 4) へ。
> > | ない | → | 5) へ。
> > 4) @Flames と @TotalSec が、
> > | 両方ともある | → | 3-11-7. 3-11-8. の関数よりサンプルレートを推定し、 UnitSamples = 1152 として、3-11-5. 3-11-6. より @TimeRatio を算出する。
> > | どちらかがない | → | 5) へ。
> > | 両方ともない | →
> > 5) プレイヤーの規定値とする。
> > | 標準時間を規定値とするなら、@TimeRatio = 1
> > | WinAmp時間を規定値とするなら、@TimeRatio = 0.9953125 とする。
> > | ただし、どちらを規定値とするかは、オプションなどでユーザーが任意に選択できるようにすることを推奨する。

> 3-5. 曲ファイルからの TimeRatio の取得方法

> > Winamp プラグインなど、Winamp時間で演奏時間が得られる場合、その演奏時間が標準時間からどのくらいズレているかを得る必要がある。また、タイムタグ変換ユーティリティーや、Winamp時間対応のタイムタグ付けソフトでも、曲ファイルから TimeRatio 値を得る必要がある。

> > （標準時間を用いるプレイヤーでは、この計算は必要ない。）

> > 詳しい取得方法は、プログラム例 3-11-5. 3-11-6.を参照とすること。

> > このとき、SampleRate は 曲のサンプリングレート ( kHz ) を代入し、UnitSamples は 曲が mp3 ( rmp ) , wav の場合は、1152 を、wma の場合は、1000 を代入する。

> > なお、参考として、Winamp時間での、TimeRatio 値の一覧表を示す。

> > SampleRate (kHz) | UnitSamples
> > 1152 (mp3, wav) | 1000 (wma)
> > 48.000 | 1 | 0.96
> > 44.100 | 0.9953125 | 0.9702
> > 32.000 | 1 | 0.992
> > 24.000 | 1 | 0.984
> > 22.050 | 0.9953125 | 0.99225
> > 16.000 | 1 | 0.992
> > 12.000 | 1 | 0.996
> > 11.025 | 0.9953125 | 0.99225
> > 8.000 | 1 | 1

> > また、なぜこのような補正方法で補正できるのかについては、3-12. を参照してください。

> 3-6. 標準プレイヤーとしての対応方法

> > 標準時間で演奏するソフトが、歌詞ファイル中のタイムタグを読み込む場合は、以下のようにする。

> > 1) | 3-4.の方法で 歌詞の@TimeRatio を得る。
> > 2) | 歌詞中のタイムタグをミリ秒に直し、これを @TimeRatio で除算する。
> > 3) | 歌詞中に @Offset があれば、この値を 2) に加算する。もし値が負になれば、0 にする。

> > 途中、倍精度型と整数型の演算となることに、十分注意しなければならない。

> > C におけるプログラム例を 3-11-1. に VB の例を 3-11-2. に示してあるので、それを参照すること。

> 3-7. WinAmp 歌詞表示プラグインとしての対応方法

> > WinAmp プラグインで、演奏しながら歌詞を表示するためには、歌詞ファイルを WinAmp時間に変更しなくてはならない。歌詞がすでに WinAmp時間であったとしても、歌詞作成時の曲データと、演奏時の曲データが異なるとズレる可能性がある。したがって、次のような方法で対応する。（演奏ファイルが midi の場合は、標準時間で演奏されるので、3-4.の処理をする）

> > 1) | 3-4.方法で歌詞の TimeRatio を得る。これを LyricsTimeRatio とする。
> > 2) | 3-5.の方法で曲の TimeRatio を得る。これを SongTimeRatio とする。
> > 3) | 歌詞中のタイムタグをミリ秒に直し、これを LyricsTimeRatio で除算する。 (この時点では標準時間になっている。)
> > 4) | 歌詞中に @Offset があれば、この値を 5) に加算する。 もし値が負になれば、0 にする。
> > 5) | 4)に SongTimeRatio を乗算する。

> > 途中、倍精度型と整数型の演算となることに、十分注意しなければならない。

> > C におけるプログラム例を 3-11-3. に VB の例を 3-11-4. に示してあるので、それを参照すること。

> 3-8. タイムタグ付けソフトとしての対応方法

> > 新たにタイムタグを付けたときなど、@タグを新たに付加する場合、次のようにすること。

> > （ここにあげた@タグ以外は付けてはいけない。）

> > 〔必ず付けなくてはならない@タグ〕
> > @TimeRatio | 標準時間でタグ付けを行った場合は 1 とする。 WinAmp時間でタグ付けを行った場合は3-5.の方法で値を得る。
> > 〔対応可能であれば付けてもいい@タグ〕
> > @TaggingBy | タグ付けした人の名前を指定する。
> > @SilencemSec | WinAmp時間でタグ付けを行ったとしても、必ず標準時間に換算 （@TimeRatioで割ればよい）した値を付けること。
> > @Artist @Title @Album | 曲ファイルの ID3 や SI から取得してもよい。

> 3-9. タイムタグ変換ソフトとしての対応方法

> > タイムタグ変換ソフトなど、既に付けられたタイムタグを変換して保存する場合、以下の手順で行うこと。

> > （前もって、全ての@タグを取得後、削除しておくこと。）

> > A) @Offset がある場合
> > | A-1) 標準時間に変換する場合
> > |  | A-1-1) | 3-6.の手順でタイムタグを変換する。
> > |  | A-1-2) | @TimeRatio=1 を追加する。
> > |  | A-1-3) | @Offset=0 を追加する。
> > |  | A-1-4) | @TaggingByの内容と修正者が異なれば、 @EditedBy を付加する。
> > |  | A-1-5) | @Artist, @Title, @Album, @Bgfile, @BgFolder を再度付加する。
> > | A-2) WinAmp時間に変換する場合
> > |  | A-2-1) | 3-7.の手順でタイムタグを変換する。
> > |  | A-2-2) | @TimeRatio を追加する。値は3-7.2) で得た SongTimeRatio 。
> > |  | A-2-3) | 以降、A-1-3) と同じ。
> > B) @Offset がなく、@SilencemSec があり、曲の SilencemSecが取得可能な場合
> > | B-1) 標準時間に変換する場合
> > |  | B-1-1) | 3-6.の手順でタイムタグを変換する。 さらに変換後の値から、@SilencemSec を引き、曲の SilencemSec を足す。 もし値が 負 となったら 0 とする。
> > |  | B-1-2) | @TimeRatio=1 を追加する。
> > |  | B-1-3) | @SilencemSec に曲の SilencemSec を内容として追加する。
> > |  | B-1-4) | 以降、A-1-4) と同じ。
> > | B-2) WinAmp時間に変換する場合
> > |  | B-2-1) | 3-7.の手順でタイムタグを変換する。 ただし、3-7.3)の結果から、@SilencemSec を引き、曲の SilencemSec を足す。 もし値が 負 となったら 0 とする。
> > |  | B-2-2) | @TimeRatio を追加する。値は3-7.2) で得た SongTimeRatio 。
> > |  | B-2-3) | @SilencemSec に曲の SilencemSec を内容として追加する。
> > |  | B-2-4) | 以降、A-1-4) と同じ。
> > C) その他の場合
> > | @Offset行を追加しない点を除いて、A) と同じ。

> 3-10. VBMP3.DLL を利用している MP3 プレイヤーとしての対応方法

> > VBMP3.DLL の vbmp3_getLyrics や vbmp3_getLyrics2 を利用して、歌詞表示を行う MP3 プレイヤーソフトの場合は、以下のようにして対応する。

> > VBMP3.DLL Ver 1.611 なら、

> > > Call vbmp3_quickSeek(0)
> > > Call useAtMarkTag(1)

> > VBMP3.DLL Ver 1.61 なら、

> > > 1) 【４】の方法で、歌詞の@TimeRatio を得る。（内蔵歌詞の場合も同じ）

> > > 2) @TimeRatio = 1 なら、 Call vbmp3_setLyricsTime(1) (標準時間)
> > > @TimeRatio = 0.9953125 なら、Call vbmp3_setLyricsTime(0) (WinAmp時間)
> > > とする。
> > > それ以外の値の場合は、4) へ。

> > > 3) 歌詞中に、@Offset があれば、その値を読み込み、
> > > @TimeRatio の値を乗算して整数化し、vbmp3_setLyricsAdjustTimeに代入する。5) へ。

> > > 4) 【６】の手順で、すべてのタイムタグを標準時間に変換し、Call vbmp3_setLyricsTime(1) とする。
> > > Call vbmp3_setLyricsAdjustTime(0) とする。

> > > 5) Call vbmp3_quickSeek(0) とする。（高速シークを使うとズレます。）

> > （注：VBMP3.DLL は 必ずバージョン 1.61 以降を使ってください。また、この手順は、VBMP3.DLL の将来のバージョンでも完全に保証されるとは限りません。）

> 3-11. プログラム例

> > 3-11-1. 標準時間の取得(C)

> >     // 標準時間の取得
> >     int GetNormalTime(
> >     	int LyricsTime, // 歌詞ファイルのタイムタグ [ms]
> >     	double TimeRatio, // 歌詞に記述されている @TimeRatio
> >     	int Offset // 歌詞に記述されている @Offset
> >     	              記述がなければ 0 を代入する
> >     	)
> >     {
> >     	int NormalTime; // 標準時間

> >     	NormalTime = (int)( (double)LyricsTime / TimeRatio );
> >     	NormalTime += Offset ;
> >     	if( NormalTime < 0 ){
> >     		NormalTime = 0;
> >     	}
> >     	return NormalTime;
> >     }

> > 3-11-2. 標準時間の取得(VB)

> >     ' 標準時間の取得
> >     Private Function GetNormalTime( ByVal LyricsTime As Long, _
> >     		ByVal TimeRatio As Double, _
> >     		ByVal Offset As Long ) As Long

> >     	GetNormalTime = Int( CDbl(LyricsTime) / TimeRatio )
> >     	GetNormalTime = GetNormalTime + Offset
> >     	If GetNormalTime < 0 Then
> >     		GetNormalTime = 0
> >     	End If
> >     End Function

> > 3-11-3. WinAmp時間の取得(C)

> >     // Winamp時間の取得
> >     int GetWinAmpTime(
> >     	int LyricsTime, // 歌詞ファイルのタイムタグ [ms]
> >     	double LyricsTimeRatio, // 歌詞に記述されている @TimeRatio
> >     	double SongTimeRatio, // 曲から取得した TimeRatio
> >     	int Offset // 歌詞に記述されている @Offset
> >     	)
> >     {
> >     	int WinAmpTime; // WinAmp時間
> >     	double NormalTime; // 標準時間 (精度を上げるためにdouble型)

> >     	NormalTime = (double)LyricsTime / LyricsTimeRatio;
> >     	NormalTime += (double)Offset ;
> >     	if( NormalTime < 0.0 ){
> >     		NormalTime = 0.0;
> >     	}
> >     	WinAmpTime = (int)( NormalTime * SongTimeRatio );
> >     	return WinAmpTime;
> >     }

> > 3-11-4. WinAmp時間の取得(VB)

> >     ' WinAmp時間の取得
> >     Private Function GetWinAmpTime( ByVal LyricsTime As Long, _
> >     		ByVal LyricsTimeRatio As Double, _
> >     		ByVal SongTimeRatio As Double, _
> >     		ByVal Offset As Long ) As Long

> >     	Dim NormalTime As Double ' 標準時間 (精度を上げるためにdouble型)

> >     	NormalTime = CDbl(LyricsTime) / TimeRatio
> >     	NormalTime = NormalTime + CDbl(Offset)
> >     	If NormalTime < 0# Then
> >     		NormalTime = 0#
> >     	End If
> >     	GetWinAmpTime = Int( (NormalTime * SongTimeRatio )
> >     End Function

> > 3-11-5. 曲ファイルの TimeRatio を算出 (C)

> >     // 曲ファイルの TimeRatio を算出
> >     double GetTimeRatio(
> >     	double SampleRate, // 曲のサンプルレート(kHz)
> >     	int UnitSamples // 曲の種類による UnitSamples
> >     	)
> >     {
> >     	double TPFn; // 1フレームあたりのミリ秒(標準)
> >     	int TPFw; // 1フレームあたりのミリ秒(WinAmp)
> >     	double TimeRatio; // @TimeRatio

> >     	TPFn = (double)UnitSamples / SampleRate;
> >     	TPFw = (int)TPFn;
> >     	TimeRatio = (double)TPFw / TPFn;
> >     	return TimeRatio;
> >     }

> > 3-11-6. 曲ファイルの TimeRatio を算出 (VB)

> >     ' 曲ファイルの TimeRatio を算出
> >     '   SampleRate : 曲のサンプルレート(kHz)
> >     '   UnitSamples : 曲の種類による UnitSamples
> >     Private Function GetTimeRatio( _
> >     	ByVal SampleRate As Long, _
> >     	ByVal UnitSsamples As Integer) As Double

> >     	Dim TPFn As Double ' 1フレームあたりのミリ秒(標準)
> >     	Dim TPFw As Integer ' 1フレームあたりのミリ秒(WinAmp)

> >     	TPFn = CDbl(UnitSamples) / CDbl(SampleRate)
> >     	TPFw = Int(TPFn)
> >     	GetTimeRatio = CDbl(CStr(CDbl(TPFw) / TPFn ))
> >     		' 演算にゴミが残ることがあるので、
> >     		' 一度文字列に直しています。
> >     End Function

> > 3-11-7. サンプルレートを推定する (C)

> >     // サンプルレートを推定する
> >     double CalcSampleRate(int Frames, int TotalSec)
> >     {
> >         double TPFn;
> >         int nearSampleRate;

> >         if(Frames == 0 || TotalSec == 0){
> >             return 44.1;
> >         }

> >         TPFn = (double)TotalSec*1000.0/Frames;
> >         nearSampleRate = (int)(1152.0*1000.0/TPFn);

> >         if(nearSampleRate > 46050){
> >             return 48.0;
> >         }
> >         else if(nearSampleRate > 38050){
> >             return 44.1;
> >         }
> >         else if(nearSampleRate > 28000){
> >             return 32.0;
> >         }
> >         else if(nearSampleRate > 23025){
> >             return 24.0;
> >         }
> >         else if(nearSampleRate > 19025){
> >             return 22.05;
> >         }
> >         else if(nearSampleRate > 14000){
> >             return 16.0;
> >         }
> >         else if(nearSampleRate > 11512){
> >             return 12.0;
> >         }
> >         else if(nearSampleRate > 9512){
> >             return 11.025
> >         }
> >         else{
> >             return 8.0;
> >         }
> >     }

> > 3-11-8. サンプルレートを推定する (VB)

> >     // サンプルレートを推定する
> >     Private Function CalcSampleRate( _
> >                 ByVal Frames As Integer, _
> >                 ByVal TotalSec As Integer) As Double
> >         Dim TPFn As Double, nearSampleRate As Long

> >         If Frames = 0 Or totalSec = 0 Then
> >             ' 計算不能ならば、規定値として 44100 Hz を返す
> >             CalcSampleRate = 44.1
> >             Exit Function
> >         End If

> >         TPFn = CDbl(TotalSec) * 1000# / CDbl(Frames)
> >         nearSampleRate = Int(1152# * 1000# / TPFn)

> >         Select Case nearSampleRate
> >         Case Is > 46050
> >             CalcSampleRate = 48#
> >         Case Is > 38050
> >             CalcSampleRate = 44.1
> >         Case Is > 28000
> >             CalcSampleRate = 32#
> >         Case Is > 23025
> >             CalcSampleRate = 24#
> >         Case Is > 19025
> >             CalcSampleRate = 22.05
> >         Case Is > 14000
> >             CalcSampleRate = 16#
> >         Case Is > 11512
> >             CalcSampleRate = 12#
> >         Case Is > 9512
> >             CalcSampleRate = 11.025
> >         Case Else
> >             CalcSampleRate = 8#
> >         End Select
> >     End Function

> 3-12. Winamp 時間と標準時間についての詳細説明

> > WinAmp はなぜか正しい演奏時間を表示していません。WinAmpの歌詞表示・作成プラグインでは、この「正しくない時間」をWinAmpから受け取り、処理しています。

> > もちろん、作成も表示も WinAmp で行っていれば、何も違和感を感じないでしょう。しかし、WinAmp を通して作られたタイムタグ付き歌詞を、別のプレイヤーで表示したり、逆にタイムタグ作成ソフトで作った歌詞を、WinAmp + プラグインで表示したら、演奏が進むに従って、どんどん歌詞がずれて表示されていってしまいます。

> > 何故、WinAmpは正しい時間を表示しない(できない)のか? これは僕には分かりません。意図的にそうなっているのか、バグなのか。 ただ初期の WinAmp から 現在の WinAmp (2.62) まで、ずっと一貫してこのズレた WinAmp時間 が使われているようです。

> > そこで、上記のような、別のソフトを使いたい場合、WinAmp時間 から 標準時間、あるいはその逆の変換をしなくてはなりません。

> > そのためには、どういう状況で、どのくらいズレるのか、そもそもどういう理屈（計算過程）でズレるのか、それを知らなくてはなりません。

> > 旧りりくす では、曲の総フレーム数と総演奏時間より、1フレームあたりの演奏時間（以下、TPF と呼ぶこととする）を求め、標準時間では、これを倍精度小数扱いした値、WinAmp時間は、これを整数化（四捨五入）した値としてこの違いから補正していました。（これが、3-3-4. 現在では新たに付加してはいけないタグがある理由です。）

> > この方法の着目点（ TPF を整数化するかしないか）は間違っていません。現に、通常の拡張タイムタグでやってみると、だいたいズレずに表示されているように見えるからです。しかし、カラオケタイムタグが主流となりつつある昨今では、この方法で補正してもズレが直らないということが明らかとなってきたのです。

> > ちょうどその頃、とあるプレイヤー作成者の方から、補正方法について質問を受け、お答えし、その後いろいろと話をしているうちに、この手法の根本的な間違いがなんなのかについて、ようやく分かりました。そのヒントとなった疑問点は、「WAVE ファイルでもズレる」「MP3 でもサンプルレートによっては、WinAmp が正しい時間を示すことがある」「WMA ではまた違ったズレ方をする」という３点でした。当然 WAVE には、「フレーム」という概念はなく、よって1フレームという概念もありません。また、この手法では、どんなサンプルレートでも同じ TPF になり、サンプルレート依存することの理由が説明できません。

> > そこで2人でいろいろと調べた結果、TPF は次のような計算法で算出されることが明らかとなりました。

TPF ( Time Per Frame )[ ms ] = UnitSamples ( 単位サンプル数 ) / SampleRate [ kHz ]… (1)

> > > 前述のように WAVE には 「フレーム」という概念はありませんので、「1フレームあたりの」という表現は間違っているのですが、これまでの流れから、そのように表記することとします。実際には、「1処理サンプル数あたりの」ぐらいがいいのかもしれませんが…。

> > > プログラムで式(1)や以下の式を書くときは、すべて倍精度変数相当で行ってください。

> この式は単純な割り算ですので、UnitSamples と SampleRate によっては、当然、TPF は小数になります。前述のように、標準時間では小数のままです。これを TPFn とすると、WinAmpでは、さらにこの小数点以下を切り捨てた値 ( TPFw ) を使っているようです。

> さて、では、この UnitSamples とはいったい何か、ですが、どうやら、WinAmpのInputプラグインが、一度に処理するサンプル数のことのようです。そのサンプル数分の処理をすると、いくつめの処理をしたかを時間に変換して表示していると思われます。

> 勘のよい方なら、もうお気づきかもしれませんが、この UnitSamples は、Inputプラグインによって異なっているようです。いろいろと調べてみると、in_mp3.dll と in_wave.dll では 1152、in_wma.dll では 1000 になっているようです。これが原因で、WMA ではまた違ったズレ方をするようです。また、これらの値は、ステレオ/モノラルや、量子化ビット数、ビットレートには依存しないようです。

> さて、では、TPFn と TPFw が実際にどのようなズレを引き起こすかについてですが、たとえば、標準時間で作成されたタイムタグを、WinAmp時間に直す場合、次の式から算出できます。

> > WinAmp時間 [ms] = 標準時間 [ms] / TPFn * TPFw

> > > ここで、 TimeRatio ( 時間比 ) = TPFw / TPFn …(2)とすると、

> > WinAmp時間 = 標準時間 * TimeRatio

> となり、標準時間から WinAmp時間への変換は TimeRatio を乗算すればできることになります。 TimeRatio は、「標準時間に対する WinAmp時間の比」となります。もちろん、逆に、WinAmp時間から標準時間への変換は、TimeRatio で割ればいいだけです。

> ただし、WinAmp時間からWinAmp時間への変換では、2種類の TimeRatio （歌詞作成時の TimeRatio と 演奏時の TimeRatio ）が必要となります。

> また TimeRatio は、式(1),(2) より、UnitSamples と SampleRate で決定されることが分かります。一応、どんな値になるかは、3-5. の表を参照してください。

> ちなみに TimeRatio = 1 であるということは、標準時間と WinAmp時間が同じことになります。これが、mp3 でも WinAmp と標準でズレが生じないサンプルレートがある理由です。

> 一応、44.1 32.0 22.05 11.025 kHz の mp3, wav ファイル、および、44.1 32.0 22.05 16.0 8.0 kHz の wma ファイルについてこの手法で標準時間から WinAmp時間へ変換したカラオケタイムタグ付き歌詞ファイルを WinAmp 2.62 + Gucchi's Lyrics Plugin 1.02 で再生させ、ズレが発生しないことを確認しております。

> これより、補正に必要なデータは、歌詞作成時の TimeRatio と、演奏時の TimeRatio のみということになります。したがって、歌詞作成ソフトでは、歌詞に TimeRatio 値を埋め込んでおけば、演奏ソフトでは、埋め込まれた TimeRatio と 演奏する曲から取得した　TimeRatio で補正が行えます。もちろん、標準時間で演奏するソフトであれば、曲から取得するまでもなく、後者の TimeRatio は 1 ですが。

> 以上の手法は、前述した３つの疑問点を解消し、実践テストもクリアしたことにより、これまで用いていた補正方法よりはるかに優れた方法であると言えるでしょう。

> 補足．

> この手法にとうの昔から気付き実践してきた Gucchi's Lyrics Plugin の 開発者グループ「ぐっちそふと」のプログラム担当者でいらっしゃる、moka 氏からの情報によると、

> > > Winamp(in_mp3.dll)は
> > > time = time + TPFw(26) という処理をしているのではなく、
> > > time = time + TPFw/2(13) という処理を、
> > > １フレームデコードするたびに２度行っている

> そうです。したがって、mp3 の場合の 式(1) 中の UnitSamples に相当する値は 576 であるべきかもしれませんが、その2倍値である、1152 でも、TimeRatio は全く同じになります。また、「1フレームあたりのサンプル数」はあくまで 1152 ですので、@SilencemSec の計算などでは、UnitSamples = 1152 としなくてはいけません。

> moka さん、情報ありがとうございました。

４．参考資料

> 4-1. 従来流通していたが、本仕様書では認めない書き方

> 曖昧な記述に対応する仕様を決めるより、曖昧な記述をさせないようなシンプルかつ明瞭な仕様をめざす。よって、現在流通している曖昧な書き方は勇気をもって(^^;仕様外とし、仕様文書とは別の参考資料で触れるにとどめる。

> > 4-1-1. 行頭タイムタグ付き歌詞におけるリピート拡張

> > 従来では、いわゆるサビのリピートなど、ある歌詞を後で再度表示したい場合、行の先頭に複数のタイムタグをつけて表現する書き方があった。

> > > [00:15][00:45]あいうえお
> > > [00:16]かきくけこ
> > > …

> > > このような歌詞は、従来の解釈では、15秒に、「あいうえお」 が表示され、16秒に、「かきくけこ」 が表示され、以降、続く行が表示され、45秒にまた、あいうえお が表示されていた。

> > > しかし、このような表現をするならば、

> > > [00:15]あいうえお
> > > [00:16]かきくけこ
> > > …
> > > [00:45]あいうえお

> > > と表記することが可能である。この書き方を許すメリットよりも、このような曖昧な書き方が、カラオケタグ付き歌詞との区別や、両者の共通解釈をする上での混同を引き起こしてしまうデメリットの方が遙かに大きいと考えられる。

> > > したがって、この書き方はカラオケタグ付き歌詞のなかった時代に生み出された書き方ではあるが、カラオケタグ付き歌詞に正式に対応しようとする本仕様書ではこの書き方を認めていない。

５．更新履歴

> 1.0β2 ( 2000/5/24 )

> > ・開発者向けの2-6-2. の「行頭にタイムタグがない場合」の補完方法を修正した。これに伴い、懸案事項2-6-2-1 は自動的に解消された。

> > ・仕様外の説明を、仕様本文から、参考資料へ移動した。

> > ・2-6-3. に「内蔵歌詞との優先順序」問題を追加した。

> 1.0β1 ( 2000/5/23 )

> > ・AtMarkTag.html と統合し、また、TimeTagDev.html と切り離した。その他いろいろと交通整理。