Arduino Tone Libraryでパワーコードを鳴らす

Arduino Tone Libraryを使うと、Arduino本体のみで複数の音が鳴らせるということなので使ってみました。

参考サイト
ArduinoでMIDI音源を作ってみた | メモとか倉庫とか(仮)
2012-03-07 – 熊工房 ohgumaの腹凹ませたい日記
↑こちらのサイトによると、Aruidno IDE 1.0以降ではTone.cppの
#include <wiring.h>

#include <wiring_private.h>
と書き換える必要があります。

しかし、Arduino UNOで3音同時に鳴らす場合、Timer0を使ってしまうため、お手軽にdelay()関数やmillis()関数で音を鳴らす、ということができなくなります。
毎度おなじみMetro Libraryも、内部でmillis()関数を呼んでいるのでやっぱり使えません。
(ちなみに、普通のtone()関数とか、MsTimer2 Libraryで使われているのがTimer2です
 Timer1を使ったライブラリもあるようですが、これは使ったことないです)
ここで、「外部からのMIDI入力に対応しよう」ではなく、「だったら2音でいいや」に妥協するのが私です。

2音と言えばパワーコード。
ラモーンズやピストルズなどのパンク・ロックで使われている、元になる音と5度の音(半音7つ分上)だけで構成されるコードです。

アナログ入力(A0-A3)に半固定抵抗4つ付けて各種パラメータに使います。
D8とD9はTone2音の出力。ここではそれぞれ1kΩの抵抗を挟んでミックスしてますが、別々で音を出すならステレオ出力っぽいことも出来ますね。
回路図作る必要も無い気がしますが、こんな感じ。
PowerCoder_回路図 PowerCoder_ブレッドボード
スケッチはこちら。


#include <Tone.h>

const byte AIN_TEMPO = 1;
const byte AIN_LENGTH = 0;
const byte AIN_ROOT = 2;
const byte AIN_CHORD = 3;

const byte ch_count = 2;
const byte SND_PIN[ch_count] = {8,9};
Tone t[ch_count];
int tone_interval[ch_count];

const byte LED_PIN = 3;

int Freqs[] = {
  262, 277, 294, 311, 330, 349, 370,	392,	415,	440,	466,	494,
  523, 554, 587, 622, 659, 699, 740,	784,	831,	880,	932,	988,
};

void setup()
{
  for (int i = 0; i < ch_count; i++){
    pinMode(SND_PIN[i], OUTPUT);
    t[i].begin(SND_PIN[i]);
  }
  pinMode(LED_PIN, OUTPUT);
}

int note_root = 0;
int note_chord = 3;
int note_length = 48;
int note_gate = note_length / 4;

void loop()
{
  set_note();
  snd_play(note_root, note_chord, note_gate);
  snd_stop(note_length - note_gate);
}

void snd_play(int note_number, int chord, int ms)
{
  digitalWrite(LED_PIN, HIGH);
  t[0].play(Freqs[note_number]);
  t[1].play(Freqs[note_number + chord]);
  delay(ms);
  digitalWrite(LED_PIN, LOW);
}

void snd_stop(int ms)
{
  t[0].stop();
  t[1].stop();
  delay(ms);
}

void set_note()
{
  int tmp;
  if (note_root != (tmp = map(analogRead(AIN_ROOT),0,1023, 0,11))){
    note_root = tmp;
  }
  if (note_chord != (tmp = map(analogRead(AIN_CHORD),0,1023, 1, 12))){
    note_chord = tmp;
  }
  if (note_length != (tmp = analogRead(AIN_TEMPO) + 1)){
    note_length = tmp;
  }
  if (note_gate != (tmp = map(analogRead(AIN_LENGTH), 0, 1023, 1, note_length))){
      note_gate = tmp;
  }
}

基本的にはArduinoでYMOクリックもどきとかArduinoで雷電風リズムマシンのスケッチと同じです。

キモになってくるのが、アナログ入力からパラメータを設定しているset_note()関数。
まず、ルート音をAIN_ROOT(A2)から取ってます。前はシフト演算子とか使ってましたが、そんなに速度が必要なわけでもないし、何よりmap()関数便利すぎなので。
ここではアナログ入力の0~1023を0~11に丸めています。0~11はそれぞれド~シ(C,C#,D,D#,E,F,F#,G,G#,A,A#,B)の音程になります(Freqs配列の中身は2オクターブ分の周波数です)。

次のAIN_CHORD(A3)はルート音から半音いくつ分離れた音を一緒に鳴らすか。
取る値は1~12。パワーコードに限定するなら「7」固定でいいんですが、まあせっかくなので、隣のフレット(鍵盤)を同時にならした場合から、1オクターブ上の音まで設定できるようにしました。

AIN_TEMPO(A1)から取ってるのは、そのまんま1音1音を何ミリ秒鳴らすか。+1してるのは、0にした時にフリーズしちゃうので。
AIN_LENGTH(A0)は、↑の1音の長さの内、実際に音を鳴らしている時間の割合です。MIDIで言うGate Time、MMLで言うQコマンドです。

で、実行してみると、かように珍妙な音が鳴ります。

……おかしいな、ラモーンズを目指していたはずなのに。

コメントを残す