以前(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プレイヤーのダウンロード

関連記事

  • 指定した秒数で区切るツール Simple Wave Splitter指定した秒数で区切るツール Simple Wave Splitter Simple Wave Splitter wavファイルを指定した秒数で区切るツールを公開しました。 JRE がインストールされた環境で利用できます。 デフォルトは、10秒+モノラルになっています。 使い方 1. […] Posted in ツール制作
  • RSA暗号の解説に出てくる数値の逆数のmodRSA暗号の解説に出てくる数値の逆数のmod RSA暗号に紹介には、必ず次のような式がでてきます。 これ 今日は、この式について解説します。 といっても・・・すみません。言い訳になりますが、 数学科出身ではなく、間違ったこというかもしれません。 それをふまえていただいて・・・解説させていただきます。 まず、m = (p - 1)(q - 1) […] Posted in アルゴリズム
  • Eclipseから実行可能JAR作成とProGuardの難読化の一連の流れEclipseから実行可能JAR作成とProGuardの難読化の一連の流れ Eclipseを使ってJavaアプリケーションを作成して、 それを公開用のjarファイル作成までの流れを簡単に説明します。 まずは、単純に実行可能のJARファイルの作成方法です。 1. Eclipse で「ファイル(F)」→「エクスポート(O)...」を選択する。 2. 「Java」→「実行可能 JAR ファイル」を選んで「次へ(N) >」を選択する。 3. […] Posted in プログラミング
  • 3DCGの座標系の紹介3DCGの座標系の紹介 はじめに こんにちは! 今日は、3DCGにかかせない座標系の話をするよ! 座標系の種類 では、オブジェクトから画面まで、どのような座標系があるか紹介します。 . 座標系一覧 呼び方が変わりますが、細かく分けると次のような流れになっています。 各々の変換とよばれている部分が行列を掛け算することに相当します。 座標はベクトルとし、このベクトルに座標を掛け算して、次の座標系 […] Posted in アルゴリズム
  • Raspberry Pi 3 に CentOS 7Raspberry Pi 3 に CentOS 7 これまでも何度も Raspberry Pi を初期化していますが、 今回はサーバー運用に定評がある CentOS(for armv7hl) を入れてみました。 その時の設定メモです。 補足 armv7hl とは、文字通り ARMv7 向けかと思いますが、Raspberry Pi 3は、ARMv8 です。 なので、本来であれば […] Posted in Linux
  • セカンドライフでタッチすると音がでるやつセカンドライフでタッチすると音がでるやつ セカンドライフでタッチすると音がでるスクリプト。 特に連打防止機能をつけたものです。 具体的には1人に対して、 X 秒間次のクリックを防止します。 大勢からクリックされても、その1人1人に X […] Posted in セカンドライフ制作