0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

analogRead()用の関数作ってみた5 もっと関数

Last updated at Posted at 2016-04-09

#やりたいこと

  • analogRead()した値の平均を返す関数
  • 値が変更されたかを返す関数

を作って、可変抵抗を動かした時だけMIDI CCを出力する、ということです。

analogRead()用の関数作ってみた4の続きです。
前の記事でだいぶ見易くなってやりたい事もできてるんで、いいっちゃいいんですけど。

#見直してみたら,やっぱりダサい
ところが残っていてお恥しいかぎりです。

  • 同じ式foo = ++foo % barが複数箇所で使われている!関数にくくりださなくちゃ
  • break;でforループを抜ける?なんか答が出たってことだからそこは関数でしょ

そうしたら(うすうす気がついてはいたのですが)、すごいH本に出てくるsuccとかmodとかelemに見えてきました。
#そっちに寄せた感じで書き直してみた
のがこれです。

#include <MIDIUSB.h>

int mod_succ(int x,int MOD){ //x+1をMODで割った余りを返す
  return (x+1) % MOD;
}
boolean elem(int x,const int ary[],int arySize){  //xはary[]に含まれているか?
  for(int i=0;i < arySize;i++){
    if(x == ary[i])return true;
  }
  return false;
}

mod_succ()は文字通りsuccしてmodする関数。
elem()はhaskellのelemにがんばって寄せてみました。

class GetAverage{        //関数GetAverageクラスを作る
  static const int MOD = 8;   //クラス変数 値の履歴の数
  
  private:    //インスタンスに内包される変数たち
    int history[MOD];  //値の履歴
    int sum;      //合計
    int index; //値を出し入れする場所のインデックス
  public:
    GetAverage(){   // コンストラクタ
      history[MOD] = {}; //配列を0で初期化するにはこうだそうな
      sum = 0;    // 0で初期化
      index = 0;
    }
    int operator()(int newVal){    //かっこ演算子()の(多重?)定義
      sum += ( newVal - history[index] ); //新しい値と一番古い値の差分を合計に加算
      history[index] = newVal;    //一番古い値を捨て新しい値を登録
      index = mod_succ(index, MOD);   //インデックスを一つすすめる
      return sum / MOD; //平均を返す
    }

};

class IsChanged{
  static const int MOD = 4;
  private:
    int history[MOD];
    int index;
  public:
    IsChanged(){
      history[MOD] = {};
      index = 0;
    }
    boolean operator()(int newVal){  //かっこ演算子()の(多重?)定義
      const boolean ANSWER = !elem(newVal, history, MOD); //新しい値が履歴に含まれていなければ真
      history[index] = newVal; //一番古い値を捨て新しい値を登録
      index = mod_succ(index, MOD);  ////インデックスを一つ進める
      return ANSWER;  //答を返す
    }
};

mod_succ()に関しては、見た目が変わる程度でした。
elem()はかなり効果ありです。

  • 制御構造がなくなって(隠蔽されて)見易くなった
  • 制御構造がシンプルになった break;がなくなった
  • 値の入れ替えがなくなったので返り値がconstで良くなった

関数オブジェクトGetAverage,IsChangedが、同じような動作

  • 引数と内部に記憶された配列から返り値を計算し
  • 引数を配列のある場所に保存し
  • その場所をひとつすすめ
  • 値を返す

をしているというのがより明確になるような気がします。
(っていうか、こう書き直してみて気がついた...)

使い方は前と同じです。必要な数だけインスタンス化して使います。

GetAverage getAverage[4];
IsChanged isChanged[4];
const int ANAPIN[4]={0,1,2,3};
const byte CHANNEL = 0;
const byte CC[4] ={25,26,27,28};

void setup() {
  Serial.begin(115200);
}

void loop() {
  int temp;
  for(int i=0;i < 4; i++){
    if( isChanged[i]( temp = getAverage[i]( analogRead(ANAPIN[i]) >> 3 ))){
      controlChange(CHANNEL,CC[i],temp);
      MidiUSB.flush();
    };
  delay(10);
  };
}

//controll value or velocity
// First parameter is the event type (0x0B = control change).
// Second parameter is the event type, combined with the channel.
// Third parameter is the control number number (0-119).
// Fourth parameter is the control value (0-127).

void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
  MidiUSB.sendMIDI(event);
}

#まとめ
Arduinoでも関数でけっこういける。かな...
あと、配列まわりが難しいです。

  • 要素数は固定。変更不可。
  • 作る前に要素数がわかってないといけない。定数かstaticかenum(か#define)。
  • 関数に渡すときは配列名と要素数を渡す。関数側で要素数が調べられないため。

だそうです。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?