● 実験テーマ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へ →