● 実験テーマ40
「テンポ可変式MIDプレーヤの実験」
(SDのテンポ情報を読出し、テンポ変更可能な
MIDプレーヤの実験です)
■ 2013.9.17
・次期テーマは、前テーマの流れで、VS1011eを、MIDデコードも可能
な、VS1053b(実験テーマ19で実績あり)に変更して、ファイルリスト
選択式の、MIDプレーヤにしようと思っていたが、せっかく作るなら
以前から懸案であった、テンポ可変が可能な、MIDプレーヤに再挑戦
しようと思った。
これが実現すれば、フルートの練習にもつかえるし・・・
<この実験をやるために必要な情報(知識)>
(1) スタンダード・ミディ・フォーマット0: SMF0の
データフォーマット形式を確認する。
→ 実際のデータ(ピアノのみの極簡単な、2〜3小節位のMID
データを作成準備)を、バイナリ・エディタで開き、データ構造
を確認し、どこにテンポ情報が入っているかを知る。
<その結果を以下にまとめてみました>
・ サンプルSMF0データファイル名: 「My_temp_p.mid」
→ 音楽シーケンス・ソフト: Cakewalk Music Creator
6 にて
CWPファイル(プロジェクトファイル)を作成し、それを
MIDフォーマット0形式にエクスポートし、「My_temp_p.mid」の名
で保存します。
「Music Creator 6」で作成時の画面を右下に示しました。
(どういう訳か、元ファイルをSMF0にエクスポートして開くと
トラックはピアノパートの1トラックのはずが、2つトラックが
出来てしまいます。Track1の方は空なので削除できるので
すが、セーブして再オープンするとまた元に戻ってしまいます。
そういうソフト仕様なのか、バグなのかは分かりませんが・・・
気にしないで先に進めることにしました。)
→ 私なりに、調べた結果で大まかに、SMF0データ構造を
説明してみようと思います。
@ まず先頭から、14byte目までを
ヘッダ・チャンク(塊の意)と言います。
ここには、チャンクタイプ("MThd")、データ長、
フォーマット(フォーマット0なら、00 00)、
トラック数、時間単位(00 78hなら、120d=4分音符)
の情報が入ります。
A 続いて、トラック・チャンクと呼ばれる、データの塊があり、
チャンクタイプ(4byte:"MTrk")、データ長:4bye、
音符データ本体へと続きます。
→ 結局、テンポ情報は、
先頭から、23byte目から始まる、データ本体部の、
メタイベントと呼ばれる部分に格納されていることが解りました。
先頭からだと、95byte目からの3byteの情報が、テンポ情報
です。
ただし、この位置は、データの作り方で異なってきます。
あくまでサンプル・データの場合の話です。
(コメント情報が増えれば、後ろにずれます。)
→ ただ面倒なのは、この3byteのテンポ情報というのが
音楽で一般的に使われている、BPM値(ビート・パー・ミニッツ)
でないことです。
4分音符1拍とした長さを、マイクロ秒単位で表しているようです。
なので、プログラムで、これを、BPM値に変換する必要が
あります。
■ 2013.9.18
・昨日の、SMF0データを解析した時のメモを以下に、貼り付けておきます。
参考になればと思います。
メモなので、不適当な表現をしているところもあるかとは思いますが、
お許し下さい。
・ここで、このMIDプレーヤの、TEST仕様等を、簡単に列記してみます。
<このTESTの目的>
@ SDカードに収録されているMIDファイル(SMF0)中のテンポ情報(uS単位で表現:24bit)を読出し、
それを[BPM]単位の数値に変換して、GLCDに表示することにより思惑通り、テンポ情報が抽出できる
か検証する。
A UP, DWON SWにより、そのテンポ値を可変できるか試す。
B テンポ変更を、VS1053bで再生してみて確認する。
<テスト仕様概要>
@ SDに格納されている、MIDファイルを、8個まで読出し、そのファイルリストを、GLCDに表示する。
(GLCDの、1画面分で表示できるキャラクタ行が、最大8行なため、これで制限、また今回は、
スクロール機能は無しとする。)
A SELECT(DOWN) SWで、選択したいファイルの所へ、●キャラクタを移動
B ENTER SWで、選択を確定する。
C ここから、テンポ変更フェーズに移行する。
まず、選択されたMIDファイルの、テンポ設定を読出し、それをBPM単位に変換してテンポ表示する。
D ここで、テンポの変更が可能である。
UP SWと、DOWN SWによってテンポを変更する。
設定範囲は、自分の演奏技量に合わせ、40〜150BPM
±5BPMステップとした。
(シーケンスソフトでSMF0データを作成する時も、この範囲で設定することとする)
E 希望のテンポに設定したら、PLAY SWを押すと、再生が始まる。
F ここで何もしなければ、再生終了後、@に戻って繰返す。
再生途中で、ESC SW(UP SWと兼用)を長押しすると、再生を中断し、@に戻って繰返す。
・まずは、これまでの考察が正しいか、MIDデコーダ(VS1053b)無しで、MIDファイルのみ読込み、
MIDファイルのみの、8ファイル表示にして、最初に作ったサンプルファイル「My_temp_p.mid」を選択
して、ENT SWを押したところで、液晶右上に、MIDファイルに設定されているテンポ値を、BPM値に
変換して表示するところまでの、テストプログラムを考えようと思う。
■ 2013.9.20
・大方、BPM表示までの、ソース書き終える。
コンパイルは一発OK
<検証開始>
@ まず、テンポの異なるテスト用のMIDファイルを、8個作る。
以下のファイルです。
→ まずは、ダメ
a. ファイルリスト表示は、OK
b. T65_TST.MIDと、T90_TST.MIDを読出した時のみ、1少ない、'064'及び'089'表示
になってしまう。
→ BPM値の計算(60000000/Tempo_SMF_Data)結果が、割り切れず、
*.9999・・・になるケースで切捨てられるためと判った。
根本的には、四捨五入のプログラムを組めばよいのだが、PICでの浮動小数点の
計算プログラムは、簡単には出来そうもないので、ダメなケースのみ、計算結果に
+1することにした。(この時点では、そう考えたのだが、他のケースで上手く行かない
ことが後で判明することになるが・・・・・)
■ 2013.9.21
・BPM表示までは、大方うまく行ったので、今度は、9/18に考えた仕様で、テンポ変更の
プログラムを検討することにした。
・ソフト案も大方出来たので、新プロジェクトでこの先デバッグを行う。
プロジェクト名は、最終形にしておくが、未だ、VS1;053bは実装しないことにする。
2回目のエントリーで、スピーカキャラクタ表示+テンポ更新するところで、STOPし、ここまでの
動作確認を行いたいと思う。
・コーディング一応終了
コンパイルもOK
デバッグを開始した。
■ 2013.9.22
・昨日の結果まとめ
@ テンポ変更時のテンポステップを、±5BPMにしたが、これを24bitバイナリ値で
ステッピングすると、これをBPM値に変換し表示させた時おかしな表示になる。
→ BPM値(8bit)そのものを、アップダウンする方が素直なので、変更した。
・この直しで上手く行くが、未だおかしいところ有り。
何故か、次の2つのみ、BPM表示が、1つ増える。
130BPM→ 131
140BPM→ 141
→ この不具合は、以前の、BPM表示までのテストプログラムでも起こることが
判明。
見逃していただけである。(とほほ・・・)
→ 実際に、このケースを手計算してみると
130BPMから、SMF0の、24bitバイナリ値に逆算すると、
60000000/130= 461538.46(四捨五入された、461538がその値となる)
なので、
この値から、BPM計算すると
60000000/461538= 130.00013
となり、今のプログラムでは、割り切れない場合、全て+1してしまうように
なっているので、当然、131と表示されることになる。
これは、140BPMでも同様です。
→ 先も言ったように、四捨五入のプログラムを組めばよいのだが、簡単にやりたいので
次のようにした。(Tempo_SMF_Dataをfloat型にキャストしたものに0.5を足し、
それを、unsigner int型にキャストする方法など試したが上手く行かなかった。)
→ 今回、テンポ可変範囲の仕様を、40〜150BPM
±5BPM(SMF0データ作成時も同様)
にしたので、設定できるテンポは、合計23ポイントしかない。
なので、割り切れないケースをあらかじめ手計算で確認し、その値を元に、切捨てるか
切り上げるか分岐処理することにした。
具体的なソースを下に示します。
/***********************************************
* 音楽バッファのテンポ値(8bit*3バッファ)を、
* SMFテンポ値(24bitバイナリデータ)に変換後、
* SMFテンポ情報を、BPM値に変換する
***********************************************/
void SMFtempo_to_BPM(void){
Tempo_SMF_Data = (unsigned int)Buffer[94]<<16 | (unsigned int)Buffer[95]<<8 | (unsigned int)Buffer[96];
TMP_bpm = 60000000 / Tempo_SMF_Data;
/// 割り切れない場合(*.999のケース)は、切り上げする(割り切れなくても、*.00000xxのケースは、そのまま切捨てでよいので何もしない)
/// SMFデータを作る時、40〜150BPM/5BPM単位の範囲に限定して作成する仕様にした(テンポ可変も、その範囲内に限定)
if (TMP_bpm == 64 || TMP_bpm == 69 || TMP_bpm == 89 || TMP_bpm == 94 || TMP_bpm == 104 || TMP_bpm == 109) {
TMP_bpm = TMP_bpm + 1;
}
}
■ 2013.9.23
・これで何とか限定的ではあるが、テンポ変更まで出来たので、次のステップとして、
VS1053bを追加し、MID再生で本当に、テンポが変更されるか確認する。
・VS1053bのライブラリ(ファイル名は、VS1011のままにした)とヘッダーファイル及び
ソースを変更し、コンパイル〜デバッグを開始した。
→ テンポ変更で、再生テンポも変わることを確認した。
しかし、再生途中での、ESC SWでの再生中断が出来ない。
■ 2013.9.24
・これの根本的な問題は、MIDファイルの場合、再生中断処理は、再生終了処理と
異なるという点である。(MP3の場合は、同じ処理で動く)
PlaySkip()という関数がそれで、以前の実験では、中断処理はなかったが、
次の曲へのスキップ処理の時、使っていた。
中断処理の時も同じ関数でよいので、これを使うことにした。
この部分の具体的なソース(赤字で示した部分)を以下に示します。
/// 以下はメインループからの抜粋です。
if (fptr != 0) { // Success File Open ?
// Out of Music Data As Far as EOF of File
do{
MP3_XDCS_IO = 0;
/// File Read (Unit 256Byte)
// result = FSfread(Buffer, 1, 256, fptr);
result = FSfread(Buffer, 1, 100, fptr);
if (fast) { // 最初の、100バイト読出しの時のみ実行
/// 音楽バッファの先頭から、95byte以降に格納されている、3byteのテンポ情報を読出し、BPM値に変換する
SMFtempo_to_BPM();
/// テンポ値(BPM)を、液晶に表示する
lcd_Str(0, 15, "TMP"); // 'TMP'表示
itostring(3, TMP_bpm, TMP_Vol_buf); // ascii変換
lcd_Str(1, 15, TMP_Vol_buf); // bpm表示
/// ここでテンポ値を変更したい場合は、UP/DOWNスイッチにより
/// テンポを変更し、変更データを、音楽バッファに上書きし
/// ENTスイッチが押されたら、テンポ表示を更新する
Set_BPM();
fast = 0;
}
/// VS1053b側へ音楽バッファの内容を、100バイト単位で転送する
for( i= 0; i<result; i++) { // Repeat till Number of Reading Bytes(result)
SendData(Buffer[i]); // Send of Music Data
}
} while ((result != 0) && (PORTDbits.RD9 != 0)); // EOFまで繰返すが、途中で
// ESC_SW(SW4)を長押しすると、このループを抜ける
LATDbits.LATD3 = 0; // デバッグLED点灯
MP3_XDCS_IO = 1;
delay_ms(10);
/// 再生終了又は、再生中断処理を行う
if (result == 0){ // resultが、'0'で、ループを抜けたということは、バッファ転送中
// ESC SWが押されなかった事と判断→ 再生終了処理を行う
// Processing End of Play
FSfclose(fptr); // File Close
PlayEnd(); // Processing End of Play
}
else { // バッファ転送中に、ESC SWが押されたと判断→ 再生終了中断処理を行う
PlaySkip(); // 中断(このサブルーチンの手順でないと中断して再生が止まらない)
}
fast = 1;
fptr = 0;
}
・これで大方考えた仕様通り動かすことが出来た。
■ 2013.9.25
・実際に過去に打ち込んだ、フルートのピアノ伴奏を、SMF0データにエクスポートしてみました。
それをこのMIDプレーヤで再生した音を録音し、MP3にエンコードしてみましたので、
聞いてみてください。(サンプルなので、冒頭の前奏が終わってから数10小節分ですが・・・)
<伴奏サンプルMP3ファイル>
@ PERGO3_T95.MP3 → ペルゴレージ_フルート協奏曲_第3楽章_Allegro
spiritoso 元テンポ:95BPMのピアノ伴奏サンプルです。
A PERGO3_T140.MP3 → ペルゴレージ_フルート協奏曲_第3楽章_Allegro
spiritoso テンポ:140BPMに可変したピアノ伴奏サンプルです。
<最終回路図>
・こちらから、どうぞ→ 「テンポ可変式MIDプレーヤ実験回路図」
「VS1053b実験基板回路図」
<最終ソース>
・こちらから、どうぞ→ SD_File_List_Select_MID_Player_TEST.C
VS1011.c
VS1011.h
※ 尚、「VS1011.C」のライブラリは、後閑氏が作成されたもので、一部これに私が
VS1053用の関数を追加しました。ファイル名は、そのまま、VS1011.Cとしました。
← 実験テーマ1に戻る TOP PAGEに戻る 実験テーマ41へ →