MP3デコーダでジョイント音発生スケッチ

###概要
MP3デコーダを使って16kHzでサンプリングしたジョイント音を速度によってガタンガタン再生させる
一応、ステートマシンで考える。
MP3では連続再生にむかないので、MP3、IMA ADPCMどっちでも再生できるようにした。
台車の数もサポートしたので、シキのジョイント音も発生可能
***
###動作
・パラメータとして軸距、車両間隔、車両の軸距の長さ、台車の軸数、台車の数を設定
・MP3のジョイント音も発生させてみた。
・途中でスロットル値が変わった時のタイマー値反映処理追加
***
###今後
・もういいかな
***
###リンク
https://twitter.com/masashi_214
http://ayabu.blog.shinobi.jp/
http://dcc.client.jp/
http://www007.upp.so-net.ne.jp/nagoden/

```c++

//---------------------------------------------------------------------------
// ジョイント音ステートマシン
//uint8_t gBogieWheelbase = 19; //軸距　1900mm -> 19
//uint8_t gChassisWheelbase = 76; //車両の軸距 7600mm -> 76 ★使わない→レール長を使う
//uint8_t gVehicleSpace = 30; // 車両間隔 2200mm -> 22 
//uint8_t gBogieWheelbaseNum = 1; //台車の数 
//uint8_t gAxle = 2; // 台車の軸数
//uint8_t gRailLength = 200; // レール長 25m 25000mm -> 250
//uint8_t gJointFlg;
//---------------------------------------------------------------------------
void joint_state(){

#define ADPCM ON
#define MP3   OFF

#define ADPCMorMP3 ADPCM

  enum{
      ST_IDLE = 0,
      ST_One1,
      ST_One2,
      ST_One3,
      ST_Two1, // 車両1 台車1番目
      ST_Two2, // 車両1 台車2番目
      ST_Two3,
      ST_Two4, // 車両2 台車1番目
      ST_Two5,     
      ST_Two6, // 車両2 台車2番目
      ST_Two7, // 車両1 台車1番目
  };

  static char state = ST_IDLE;  
  static unsigned long JointTimer = 0;
  static unsigned long Previous = 0;
  static char BWnum = 0;
  static uint16_t prevSpeedCmd = 0;

  static float TimerCalc = 0;

  float ChangTimerCalc = 0;

  if(gSpeedCmd == 0){ // 速度0だったらST_IDLE
    state = ST_IDLE;
  }
  
  switch(state){
    case ST_IDLE:

                if(gSpeedCmd != 0){
                  if(gAxle == 1){  // 1軸
                    state = ST_One1;
                  } else {         // 2軸
                    BWnum = gBogieWheelbaseNum; // 台車の数        
                    state = ST_Two1;
                  }
                }
                break;

// 1軸 ////////////////////////////////////////////////////////////////////////////////////////////////////////

    case ST_One1: // １軸仕込み
#if ADPCMorMP3
                gJointFlg = ON; // ADPCM ジョイント音発生
#else
                mp3_play (111); // MP3 ジョイント音発生
#endif
                TimerCalc = (float)gVehicleSpace / (( gSpeedCmd * 100/ 255 ) * 100 / 36);  // 車両間タイマー
                JointTimer = TimerCalc * 1000; 
                state = ST_One2;
                Previous = millis();
                break;

    case ST_One2:    // 1軸用車両間ジョイント発生 + レール長タイマー仕込み
                if( (millis() - Previous) >= JointTimer){
#if ADPCMorMP3
                  gJointFlg = ON; // ADPCM ジョイント音発生
#else
                  mp3_play (111); // MP3 ジョイント音発生
#endif
                  TimerCalc = (float)gRailLength / (( gSpeedCmd * 100 / 255 ) * 100 / 36);  // レール長タイマー
                  JointTimer = TimerCalc * 1000; 
                  Previous = millis();
                  state = ST_One3;                                  
                }
                break;           

    case ST_One3:    // レール長ジョイント音発生　→ 台車ジョイント音タイマー仕込み
                if(prevSpeedCmd+10 <= gSpeedCmd){         //速度早めた時　現在の時刻と速度から残りのレール長を算出
                  ChangTimerCalc = (float)(gRailLength - ((( gSpeedCmd * 100/ 255 ) * 100 / 36)*(millis()-Previous))) / gSpeedCmd;
                  ChangTimerCalc = ChangTimerCalc * 1000;
                  
                  if((millis() - Previous) < ChangTimerCalc){ // 再計算した結果、残り時間よりChangTimerCalcが大きかった.
                     JointTimer = ChangTimerCalc;               
                  }
                  prevSpeedCmd = gSpeedCmd;
                }
#if 0 // スピードが遅くなる方は時間の再計算は不要
                } else if(prevSpeedCmd-10 >= gSpeedCmd){  //速度遅めた時　現在の時刻と速度から残りのレール長を算出
                  ChangTimerCalc = (float)(gRailLength - ((( gSpeedCmd * 100/ 255 ) * 100 / 36)*(millis()-Previous))) / gSpeedCmd;
                  ChangTimerCalc = ChangTimerCalc * 1000;
                  if(JointTimer+500 < ChangTimerCalc){ // 再計算した結果、500ms引くとJointTimerより長くなった.
                     JointTimer = ChangTimerCalc;               
                     prevSpeedCmd = gSpeedCmd;
                  }
                }
#endif
                if( (millis() - Previous) >= JointTimer){
#if ADPCMorMP3
                  gJointFlg = ON; // ADPCM ジョイント音発生
#else
                  mp3_play (111); // MP3 ジョイント音発生
#endif
                  TimerCalc = (float)gVehicleSpace / (( gSpeedCmd * 100/ 255 ) * 100 / 36);  // 車両間タイマー
                  JointTimer = TimerCalc * 1000; 
                  state = ST_One1;
                }
                break;    

// 2軸 ////////////////////////////////////////////////////////////////////////////////////////////////////////
    case ST_Two1:    // 1車両目・1台車 1回目ジョイント音処理　→ 台車間 ジョイント音タイマー仕込み
#if ADPCMorMP3
                gJointFlg = ON; // ADPCM ジョイント音発生
#else
                mp3_play (111); // MP3 ジョイント音発生
#endif
                TimerCalc = (float)gBogieWheelbase / (( gSpeedCmd * 100 / 255 ) * 100 / 36); // 台車間タイマー
                JointTimer = TimerCalc * 1000; 
                state = ST_Two2;
                Previous = millis();
                break;

    case ST_Two2:    // １車両目・１台車 2回目ジョイント音処理　→ 1台車だったら車両間 ジョイント音タイマー仕込み、２台車以上だったら台車間タイマー仕込み
                if( (millis() - Previous) >= JointTimer){
#if ADPCMorMP3
                gJointFlg = ON; // ADPCM ジョイント音発生
#else
                mp3_play (111); // MP3 ジョイント音発生
#endif
                  if(BWnum == 1){ // 台車の数1
                    TimerCalc = (float)gVehicleSpace / (( gSpeedCmd * 100 / 255 ) * 100 / 36);  // 車両間タイマー
                    JointTimer = TimerCalc * 1000; 
                    BWnum = gBogieWheelbaseNum; // 台車の数
                    state = ST_Two4;
                  } else {
                    TimerCalc = (float)gBogieWheelbase / (( gSpeedCmd * 100 / 255 ) * 100 / 36); // 台車間タイマー（台車と台車の間を台車間タイマーとした）
                    JointTimer = TimerCalc * 1000; 
                    state = ST_Two3;
                  }
                  Previous = millis();
                }
                break;


    case ST_Two3:    // 2台車以上 1回目ジョイント音処理　→ 台車間タイマー仕込み
                if( (millis() - Previous) >= JointTimer){
#if ADPCMorMP3
                  gJointFlg = ON; // ADPCM ジョイント音発生
#else
                  mp3_play (111); // MP3 ジョイント音発生
#endif
                  TimerCalc = (float)gBogieWheelbase / (( gSpeedCmd * 100 / 255 ) * 100 / 36); // 台車間タイマー
                  JointTimer = TimerCalc * 1000; 
                  BWnum --;
                  state = ST_Two2;
                  Previous = millis();
                }
                break;

    case ST_Two4:    // ２車両目・１台車 1回目ジョイント音処理　→ 1台車だったら車両間 ジョイント音タイマー仕込み、２台車以上だったら台車間タイマー仕込み
                if( (millis() - Previous) >= JointTimer){
#if ADPCMorMP3
                  gJointFlg = ON; // ADPCM ジョイント音発生
#else
                  mp3_play (111); // MP3 ジョイント音発生
#endif
                  if(BWnum == 1){ // 台車の数1
                    TimerCalc = (float)gBogieWheelbase / (( gSpeedCmd * 100 / 255 ) * 100 / 36); // 台車間タイマー
                    JointTimer = TimerCalc * 1000; 
                    BWnum = gBogieWheelbaseNum; // 台車の数
                    state = ST_Two6;
                  } else {
                    TimerCalc = (float)gBogieWheelbase / (( gSpeedCmd * 100 / 255 ) * 100 / 36); // 台車間タイマー（台車と台車の間を台車間タイマーとした）
                    JointTimer = TimerCalc * 1000; 
                    state = ST_Two5;
                  }
                  Previous = millis();
                }
                break;

    case ST_Two5:    // 2台車以上 1回目ジョイント音処理　→ 台車間タイマー仕込み
                if( (millis() - Previous) >= JointTimer){
#if ADPCMorMP3
                  gJointFlg = ON; // ADPCM ジョイント音発生
#else
                  mp3_play (111); // MP3 ジョイント音発生
#endif
                  TimerCalc = (float)gBogieWheelbase / (( gSpeedCmd * 100 / 255 ) * 100 / 36); // 台車間タイマー
                  JointTimer = TimerCalc * 1000; 
                  BWnum --;
                  state = ST_Two4;
                  Previous = millis();
                }
                break;


    case ST_Two6:    // 2軸用 台車 2回目ジョイント音処理　→ 車両間 ジョイント音タイマー仕込み
                if( (millis() - Previous) >= JointTimer){
#if ADPCMorMP3
                  gJointFlg = ON; // ADPCM ジョイント音発生
#else
                  mp3_play (111); // MP3 ジョイント音発生
#endif
                  TimerCalc = (float)gRailLength / (( gSpeedCmd * 100 / 255 ) * 100 / 36);  // レール長タイマー
                  JointTimer = TimerCalc * 1000; 
                  BWnum = gBogieWheelbaseNum; // 台車の数
                  prevSpeedCmd = gSpeedCmd; // 現在の速度を保持
                  state = ST_Two7;
                  Previous = millis();
                }
                break;

    case ST_Two7:    // 1軸用 台車 1回目ジョイント音処理　→ 台車間 ジョイント音タイマー仕込み
                if(prevSpeedCmd+10 < gSpeedCmd){//速度早めた時　現在の時刻と速度から残りのレール長を算出
                  ChangTimerCalc = (float)(gRailLength - ((( gSpeedCmd * 100/ 255 ) * 100 / 36)*(millis()-Previous))) / gSpeedCmd;
                  ChangTimerCalc = ChangTimerCalc * 1000;

                  if((millis() - Previous) < ChangTimerCalc){ // 再計算した結果、残り時間よりChangTimerCalcが大きかった.
                     JointTimer = ChangTimerCalc;               
                  }
                  prevSpeedCmd = gSpeedCmd;
                }
#if 0 // スピードが遅くなる方は時間の再計算は不要
                } else if(prevSpeedCmd-10 > gSpeedCmd){//速度遅めた時　現在の時刻と速度から残りのレール長を算出
                  ChangTimerCalc = (float)(gRailLength - ((( gSpeedCmd * 100/ 255 ) * 100 / 36)*(millis()-Previous))) / gSpeedCmd;
                  ChangTimerCalc = ChangTimerCalc * 1000;

                  if((millis() - Previous) < ChangTimerCalc){ // 再計算した結果、500ms引くとJointTimerより長くなった.
                     JointTimer = ChangTimerCalc;               
                  }
                  prevSpeedCmd = gSpeedCmd;
                }
#endif
                 if( (millis() - Previous) >= JointTimer){
#if ADPCMorMP3
                  gJointFlg = ON; // ADPCM ジョイント音発生
#else
                  mp3_play (111); // MP3 ジョイント音発生
#endif
                  TimerCalc = (float)gBogieWheelbase / (( gSpeedCmd * 100 / 255 ) * 100 / 36); // 台車間タイマー
                  JointTimer = TimerCalc * 1000; 
                  BWnum = gBogieWheelbaseNum; // 台車の数
                  state = ST_Two2;
                  Previous = millis();

                }
                break;

 
    default:
                break;
  }
}