XFフォーマットから歌詞を読み出す

ヤマハミュージックデータショップから 購入したSMF(スタンダード・ミディ・ファイル)から歌詞のデータを取り出して 表示してみたいと思います。 歌詞のデータはXFというフォーマットに従ってSMFファイルの中に書き込まれています。 SMFフォーマットほ1991年に決まった仕様です。 こちらのページ からダウンロードできるMIDI1.0規格書の中に記載があります。

一方、XFフォーマットはヤマハの決めた仕様です。 こちらから ダウンロードできる仕様書は2000年のV2.03となっています。 どちらも古い仕様ですが未だ使われています。
この両方を見ないと、歌詞を読み出すことができません。

SMFファイルはバイナリファイルです。テキストエディタでは開いても内容がわかりません、 バイナリエディタ、例えばvimエディタで開くことで中身が読めます。

> vim -b filename.mid
: を押してコマンドモードに入り %!xxd

SMFファイルのいくつかのブロック(チャンクと言う)が書き込まれています。 チャンクの先頭は4つのキャラクタで、その後にチャンクのサイズが入っています。

MThd <length of header data>
     <header data>
MTrk <length of track data>
     <track data>
MTrk <length of track data>
     <track data>
     ...

さて、XFデータははMTrkチャンクの後に以下のように入っています。

XFIH <length of XF information header>
             <information header data>
XFKM <length of XF karaoke message>
             <karaoke message data>

例えば、あるファイルを開いて見ると以下のようになっていました。

歌詞のフォーマット

歌詞情報はkaraoke messageの中の0xFF 0x05の後に入っています。 karaoke messageは次の順に書き込まれています。 delta-timeは一つ前の歌詞から、ここで定義される歌詞を表示するまでの時間を示しています。

<delta-time>
0xFF
0x05
<length of charactor>
text
text
...

テキストはShift-JISでエンコードされています。ですので2バイトの文字と、1バイドの文字の場合があります。 次に例を示します。

オレンジ色はdelta-time, 黄色がキャラクタの数、赤文字がテキストです。赤文字の下にデコードした文字を書きます。

歌詞表示の制御記号

歌詞を表示する際、改行などの表示制御用の記号が歌詞のテキストの中に1バイト文字で 書き込まれています。

記号 意味 内容
"(" ")" よみがな かっこ"("および")"で囲まれた文字列は"("の直前の一文字のよみがなを表す
"[" "]" ルビ "["の直前の一文字のよみがなを表す。よみがなと違って通常の読み方とはちがった読み方に使う
"^" 空白 スペースの代わりに用いられる
"/" 改行 改行コードの代わりに用いられる
"%" 副改行 一行内での文章の意味的な区切りをあらわす
"<" 改頁 文字列の先頭に記載、新しいページの先頭から歌詞を表示させる
">" タブ 水平タブを表す
"\" バックスラッシュ その後の1文字を制御機能として扱わない

文字コード変換

XFに記載されている文字はShift-JIS、これをブラウザに表示する時はUTF-8に 変換する必要があります。以下のコードはChat GPTに書いてもらいました。


<script src="https://cdnjs.cloudflare.com/ajax/libs/encoding-japanese/2.0.0/encoding.min.js"></script>

<script
// Shift-JISのバイナリデータ(Uint8Array)をUTF-8の文字列に変換
function convertShiftJISToUTF8(sjisArrayBuffer) {
  // ArrayBuffer → Uint8Array に変換
  const sjisBytes = new Uint8Array(sjisArrayBuffer);
            
  // Shift-JIS → Unicode(内部的にUTF-16)に変換
  const unicodeString = Encoding.convert(sjisBytes, {
    to: 'UNICODE',
    from: 'SJIS',
    type: 'string'
  });
            
  // これで JavaScript の文字列(UTF-8扱い)として使える
  // console.log(unicodeString);
  return unicodeString;
}
</script>

SMFPlayer

SMFファイルを再生するSMFPlayerに歌詞表示を組み込んでみました。