Arduino Uno R3 で音を流してみる

完全に子どもの自由研究の先行実験。
以前に、Arduinoでモールス符号をLチカ(完成)で、Arduinoに圧電スピーカーを直結して音を鳴らしたけれど。

「これで曲とか流せないの?」

鳴らせるはずだけれど、さてどうやるんだっけな?と、確認のための実験。

どうやって音を鳴らすんだっけ?

前回の作業を思い出します。

物理的接続

適当な圧電スピーカーのケーブルにピンを半田付け。

圧電スピーカーユニットには極性はないけれど、ひとまず、赤のケーブルを12ピン。黒のケーブルをGNDに接続します。

スケッチの仕方

前回を思い出して簡単にスケッチ。

int SPEAKER = 12;

void setup() {
  pinMode(SPEAKER, OUTPUT);
}

void loop() {
  tone(SPEAKER, 440, 900);
  delay(1000);
}

pinMode(SPEAKER, OUTPUT);で12ピンを出力ポートとして指定。

tone(SPEAKER, 440, 900);で音を出します。
toneの使い方としてはtone(出力するピン, 音の周波数(Hz), 出力の長さ(ms))を指定。
delay(1000);として、ウェイトをかけます。

ここでのポイントは、toneの命令は指定ピンに対してPWMの出力をかけますが、出力を仕掛けたらすぐに次の命令の実行に移っちゃう。
なので、

  tone(SPEAKER, 440, 900);
  tone(SPEAKER, 880, 900);

こんな感じで書くと、1命令分の時間440Hzで音が出て、すぐに880Hzで上書き。結果、よくわからない周波数の音が鳴り続けることになります。 なので、もっときれいな対応方法はあるだろうけど、とりあえずであれば、delayでウェイトをかけちゃうのが手っ取り早いというわけ。 そして、サンプルでは、900msの間音を出し、1,000msのdelayを加えていて時間が一致しない。これは単純に音の切れ目を作っているだけです。 音楽などを聴いていても、完全に連続した音で作った曲を聴くと、何となく息苦しく聞きにくいものです。 音と音の間も区切れていないと、何となく気持ち的に聞きにくいものです。 それを考えると、実際に音を流す時間より、ちょっと長いdelayを入れるほうがよさそうという話です。 なので、こんな感じで入れています。

曲を流してみる

今回のオーダーの最終系は「音楽を流す」なので、それに必要な要素を確認していきます。 まず、曲を流すのに必要なのは、音符の集合体とそれを演奏するスピードという基礎情報です。 これは楽譜を見れば全部入っている要素なので、素材としては「楽譜」をどこかから探してくるのが最初。 それを見つけた前提で、再生する環境を作ってみましょう。

曲を流すならテンポを指定が必要

楽譜のテンポ、譜面の上のほうに”♩=120″のように表示されているそれ。 これは、1分間に♩で120回打てるスピードでという意味。なので♩=0.5secという計算。 そして、音の出ている時間と音と音の間に入れる空白時間。 これを計算させるとすると、

// Constant
int BPM = 120;
int RATIO = 95;
//
int qn_on = ((60000 / BPM) * RATIO) / 100;
int qn_ps = 60000 / BPM;

...

tone(SPEAKER, 440, qn_on); // pin, fequency, duration(ms)
delay(qn_ps); // ms

こんな感じ。 まぁ、単純にBPMから何ms音を出す感覚にするか。音の出ている時間と音の出ていない時間の比率から長さを確定するだけと。 RATIOを”int”にしたかったので、0.95ではなく、95にしているのがちょっとしたミソではあるかも。

音階を作る

toneにぶち込める音は周波数で指定する必要があります。 C(ド)とかD(レ)とか。そういう感じでぶっこめれば簡単なんでしょうけど。それも善しあしで、ちょっと外れたことをしようとすると手も足も出なくなるってのもあるし。 何よりも、Cと言ったときに指すその音。一体、何Hz?という宗教戦争もあるし。

教科書にはA=440Hzと書かれているので、そう覚えている人も多いはず。 音叉も440Hzのものが売られてますしね。 時代によっても基準となる音はだいぶ異なっています。 昔は基準が統一されていませんでした。統一されでまだ100年にもなっていないようです。

奏者によっても、好みによってだいぶ違うようです。カラヤンの華やかな演奏はA=446Hzだったそうだし、逆にA=432Hzあたりにとる奏者もいるそうで。アマチュア向けの楽器は440Hzだけれど、オーケストラは442Hzが多く、ライブハウスは441Hzが多いらしい。(ネットに転がっている情報なので真偽は不明だが。) 統一された風で、案外にばらついているものなんですね。

ということで、Arduinoで音を鳴らす場合、国際基準である440Hzで行っている人が多いようです。 が、私、へそ曲がりなので、A=442Hzを採用してやってみようと思います。

音階表(A=442Hz)
/ C C# D D# E F F# G G# A A# B
(4) 263 278 295 313 331 350 371 393 417 442 468 496
(5) 526 557 590 625 662 701 743 788 834 884 937 992
(6) 1051 1114 1180 1250 1325 1403 1487 1575 1669 1768 1873 1984

この周波数で突っ込んであげる。 書いてみるとこんな感じ。

int SPEAKER = 12;
 
void setup() {
  pinMode(SPEAKER, OUTPUT);
}
 
void loop() {
  tone(SPEAKER, 526, 200);
  delay(1000);
  tone(SPEAKER, 590, 200);
  delay(1000);
  tone(SPEAKER, 662, 200);
  delay(1000);
  tone(SPEAKER, 701, 200);
  delay(1000);
  tone(SPEAKER, 788, 200);
  delay(1000);
  tone(SPEAKER, 884, 200);
  delay(1000);
  tone(SPEAKER, 992, 200);
  delay(1000);
  tone(SPEAKER, 1051, 200);
  delay(5000);
  }

かなりスタッカートだけれど、ちゃんと演奏されますね。 これなら大丈夫そうだ。

この数字をいちいち指定していたら、曲を記述するのが面倒だし、あとで修正しようとしてもよくわからなくなっちゃう。 ということで、周波数と音の関係を定義して使うことにします。書き方としては、サンプルスケッチにあるtoneMerodyの書き方を参考にして、同じように使えるように書いてみます。

/*************************************************
 * Public Constants
 *************************************************/

#define NOTE_A0   28
#define NOTE_AS0  29
#define NOTE_B0   31
#define NOTE_C1   33
#define NOTE_CS1  35
#define NOTE_D1   37
#define NOTE_DS1  39
...
...
...

実際に書いたものは、こちらからダウンロードできます。

作っておいてなんですが。Arduinoではtoneに食わせる数字は整数である必要があり、周波数も実際に食わせるために四捨五入してあります。丸め込んでしまうと、実はそんなにA=440HzもA=442Hzもそんなに変わらないんですよね。 多分、Arduinoで遊んでいる以上は、何を選択しても変わらないと思われます。

スケッチしてみる

では、これらの情報をもとに実際の曲をスケッチしてみようかと。 実際にスケッチしてみるのは、Good Morning to Allあたりとします。著作権切れしていてパブリックドメインですし、きわめて単純な曲なので書くのが楽かな?ということで。 譜面はWikipediaに転がっているものを使います。

#include "pitches442.h"

// Constant
int SPEAKER = 12;
int BPM = 120;
int RATIO = 90;
//
int qn_on = ((60000 / BPM) * RATIO) / 100;
int qn_ps = 60000 / BPM;

int melody[] = {
  NOTE_D4,  NOTE_E4,  NOTE_D4,  NOTE_G4,  NOTE_FS4,
  NOTE_D4,  NOTE_E4,  NOTE_D4,  NOTE_A4,  NOTE_G4,
  NOTE_D4,  NOTE_D5,  NOTE_B4,  NOTE_G4,  NOTE_FS4,
  NOTE_E4,  NOTE_C5,  NOTE_B4,  NOTE_G4,  NOTE_A4,
  NOTE_G4
};
int Durations[] = {
  1,  1,  1,  1,  2,
  1,  1,  1,  1,  2,
  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,
  2
};


void setup() {
  pinMode(SPEAKER, OUTPUT);
}

void loop() {
  int i;
  for (i = 0; i < 21; i++) {
    tone(SPEAKER, melody[i], (qn_on*Durations[i])); // pin, fequency, duration(ms)
    delay(qn_ps*Durations[i]); // ms
  }
  delay(qn_ps*4);
}

このスケッチはここからダウンロードできます。

Durationsは4分音符を基準にした倍率で指定。4とか8とか16とか書いてもよかったんだけれど、処理が面倒だしね。 一応、これで曲はなることを確認。

まとめ

とりあえず、音は比較的簡単に流すことができそうです。 ただ、和音を流すことは難しそうだなぁ。 時間分割して音を相互に流すようなことをすればできるのかもしれないけど。 多分、懐かしのMIMPIのような再生になるんだろうな。

コメント