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