SMFプレイヤーをつくろう!リベンジ「セットテンポと分解能」

投稿日: 更新日:

以前(2007/4/30)、HSPでMCI(Media Control Interface)を利用せずに
SMF(スタンダードMIDIファイル .mid)を再生するためのモジュールを作ってSMFプレーヤーを作りました。(ここ)

しかし実はこれには色々と問題が山積みになっていたりする。

1. なんかメタイベント,SysExイベントが正常に送れていないっぽい?
2. 何秒から再生という指定できない
3. 曲全体の再生時間を取得できない
4. プログラムチェンジできてない?(全部ピアノに…。うまくいく音楽もあるから1.のせい?謎。)
5. なんか音がぽちぽち切れる
6. 連続で聴くと音が出ないトラックが?(リセットメッセージがうまくいってない?)

遊び程度には使えるのですが、あまりにも実装が適当すぎが原因ですね。Σ(・ω・`;Ξ)
あれから2年以上も経ってるし、自分の技術(CとJavaが使えるようになた^^)は進歩しているはずっ。きっと大丈夫!
というわけで、色々な問題は今回作成しようとしているJava版にかけるとして、
今回は2,3と再生時間の取得について書きます。

SMFは、音のイベント情報が秒単位で記録されていないのというのが一番の問題です。
調べるためには、SMFの分解能(解像度)と、デルタタイムと、テンポ情報を把握しつつ
最初から計算していくしかありません^^;

前置きはこの辺にして、今回は、Javaでこれを実装しようと思っています。
いやシーケンサっていうのが用意されているのでこっち使えみたいなのもありますけど^^;
ほら、SMFに埋め込んだ歌詞情報とか曲名とか著作権情報とか見られないし。
(↑Metaイベント用のリスナー(MetaEventListener)を作ればいけそう。
というか、便利なものが…。Java1.5からMidiFileFormat getMidiFileFormatを利用すれば取得できるし。)

何分の何拍子の音楽で、今何泊目を演奏中とか表示は絶対に出来ない。(こっちは難しいかな)
さらに2回目の作成となれば、SMFについてさらに知識が深まるかなと。p(*^-^*)q

それで、またSMFプレイヤーの設計なのですが、
以前と同じように、読み込み時にSMFを解析して再生しやすい形に変更させたいと思います。
1, 可変長で格納されたデルタタイムを、固定長にする。
2, MIDIイベントと、SysExイベントと、メタイベントを、可変長のデータだけ他の配列に移動させておいて、
あとは、指定された順番に配列を見るだけで、これらのイベントが分かるようにする。
3, 音符と音符の間の時間であるデルタタイムから、曲の始めからその音符までの時間になおす。
4, トラックごとに、トラックで起きるイベント全てを格納という形で、2次元配列に格納する。
(SMFのフォーマット1からフォーマット0のような変更はしない形でトラックという概念はそのままで……。
HSPのモジュールバージョンでは、ソートして1トラックにしているので、もうちょっと丁寧に。)

こんな感じです。
Javaは、2次元配列でも1つずつメモリを使用する領域を変更できるのでらくちん(o^-^)尸~’
Cなら、ポインタの配列を使わないといけないですし。
ジャグ配列と呼ぶらしい。

というわけで、ここからテンポ情報やデルタタイムなど時間に関するメモが始まります。

セットテンポ (set tempo)
セットテンポは、メタイベントでSMFのテンポを指定するものです。
指定方法は、4分音符の時間[μsec]です。
では、この『4分音符の時間[μsec]』から『BPM(Beats Per Minute)』を求めるにはどうすればいいでしょうか。
『BPM』は1[min]=60[sec]に4分音符が60回あるとBPMは60[beat]となります。
つまり、4分音符の長さをx[sec]のときは、BPMが(60[sec]/x[sec])[beat]となります。
ここで、1[sec] = 1000[ms] = 1000000[μsec]なので
60[sec]÷BPM[beat] = settempo[μsec] ÷ 1000000
を計算すると、セットテンポの値から、BPMの計算が出来ます。
つまり、60000000÷settempo[μsec] = BPM[beat] です。
ちなみに、曲中にセットテンポ情報がありBPMが変わることもあります。
これが単純に計算できない理由です(。>ω<)

分解能 (time base)
SMFでの4分音符の分解能(タイムベース)です。
この情報は、ヘッダチャンクに記述されており、分解能は途中で変わることありません。
よくあるのは、分解能=480とかです。分解能=480とした場合、
SMFの中では、4分音符分の長さを480、8部音符分の長さを240と記述することになります。

さて、ここで問題です。
上の480とか240とか数値が出ましたが、
分解能がTIMEBASEのとき、この数値が1は何秒に対応するでしょうか。
ほら、ややこしくなってきましたヽ(^o^)丿
SMFの中では、この単位をデルタタイムとして記述されているだけなのです。

では計算方法。

分解能がaのとき、4分音符の長さはaです。単位は秒ではありません。
分解能がTIMEBASEのとき、4分音符の長さをx[sec]で表そうとした場合
60[sec]÷x[sec]=BPM[beat]
⇒  60[sec]÷BPM[beat]=x[sec] … (1)
また
1[deltatime] = x[sec]÷TIMEBASE … (2)

(1) (2) から
1[deltatime] = (60[sec] ÷ (BPM[beat] × TIMEBASE))[sec] です。

これでデルタタイムが1の時の秒での時間が分かりました。
というわけで、SMFで全体の長さを秒単位で調べるには、
1デルタタイムずつ調べていき、そのたびにBPMも更新して、
その時の秒を加算していく必要があるわけです。

このようにしないと、分解能が480なのに、
1デルタタイムごとにテンポを変更しまくるといった
怪しげなMIDファイルの長さを正確に調べることが出来ないのです。

テンポ情報がでるまで調べて、そのテンポ情報までのデルタタイムを利用するっていう
手の方が計算時間は早そうだけど、まあいいや。

というか一度BPMを経由してるけど
settempoの情報をそのまま使った方が1[deltatime]あたりの秒が自然に計算できるってことに気づいた(・・;)

続く… SMFプレイヤーをつくろう!リベンジ「拍子とタイムベース」

完成したJava製のSMFプレイヤーのダウンロード

広告

SMFプレイヤーをつくろう!リベンジ「セットテンポと分解能」” への2件のフィードバック

    […] 実は、昨日のうちにあっけなく完成。ワ――ヾ(o・ω・)ノ――イ SMFプレイヤーをつくろう!リベンジ「セットテンポと分解能」の問題もいつのまにか全部解決してました。 […]

    いいね

    […] natade コンテンツへ移動 ホーム紹介 ← SMFプレイヤーをつくろう!リベンジ「セットテンポと分解能」 SMFプレイヤーをつくろう!リベンジ「完成した」 […]

    いいね

コメントをどうぞ(承認された後に公開されます。メールアドレスの記入は自由ですが、記入した場合でも一般公開されることはありません)

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中