24 Arduino 電子オルゴールで曲を演奏
この記事では、Arduino で電子オルゴールのスケッチを作り曲を演奏させようと思います。以前の記事 圧電スピーカーを接続 では、Arduino で「ドレミ」の音階を発生させました。また、PICマイコンでは既に記事 オルゴール [6] 楽譜の演奏プログラム で曲を演奏するプログラムを作成しています。
今回は、これら今まで学習してきたことの応用で、Arduino でも電子オルゴールのスケッチを作ります、
使用するハードウエア
ハードウエアは。 圧電スピーカーを接続 で使用したものです
圧電スピーカーは、Arduino ピン8 - GND 間に取り付けています。
楽譜ファイルの作成
Arduino で「ドレミ」の音階を発生させた時は、発音の順にその音階の周波数を調べスケッチ内の配列に記入していました。この方法だと、演奏曲を変更するたびにスケッチを変更しなければなりません。
今回は、楽譜データだけを独立したファイル、gakufu.h に分けることでこの点を改善します。
音の高さ
音の高さを周波数で列記するのはやめ、音符の名称「ドレミ...」を整数値に置き換えて記録する方法に変更します。この「ドレミ...」を整数値に置き換える方法は、音楽関連員インターフェースの「MIDIフォーマット」で採用されており、このスケッチでも中心の「ド」の音をMIDIと同様に、「60」にします。
この値を読み込みスケッチ本体で、対応する周波数に変換します。
音の長さ
「ドレミ」の音階を発生させた記事では、音の長さは500msで一定でした。しかし曲を演奏するには、音符の種類により音の長さを変更する必要があります。音符は、二分音符、四分音符、八分音符、十六分音符 と順にその長さが、半分ずつになります。基準単位を設けて、四分音符は8単位、八分音符は4単位・・・とすれば良さそうです。
楽譜の独自フォーマット
以上の音の高さ長さの考察から、音階を、16bits (1 word) で表現し、MSB 8 bitが音の高さ、 LSB 8 bitが音の長さを表します。
| 76543210| 76543210|
| 高さ | 長さ |
楽譜の情報は、"gakufu.h" に、ある、unsigned int 配列 「p1gkf」に記述します。
- 最初のデータは、曲のテンポを表し、1分間に演奏される4分音符の数で示します。
- 以降のデータは、演奏する音符情報を順に記録します。
- MSB 8 bitが音の高さ。 半音単位で、中央のドが 60 (0x3C)
- MSB 8 bitが「0」は、休符を表す
- LSB 8 bitが音の長さ。 4分音符を8とした相対的な長さ
- 曲終了は、「0xFFFF」 で表示します。
"gakufu.h" の例
const unsigned int p1gkf[] = { 96, // ハッピーバースデイー 0x4304,0x4304,0x4508,0x4308,0x4808,0x4710,0x4304,0x4304, 0x4508,0x4308,0x4A08,0x4810,0x4304,0x4304,0x4F08,0x4C08, 0x4808,0x4708,0x4508,0x4D04,0x4D04,0x4C08,0x4808,0x4A08, 0x4810, 0xFFFF, 0xFFFF, };
スケッチ内容
「musicBox」というスケッチを作成しました。以下に内容を示します。
このスケッチで、圧電スピーカーから楽譜ファイルに記録された曲を演奏させます。
- 最初に、楽譜ファイル gakufu.h をインクルードします。
ファイル名は、" "(ダブルコウテーション)で括ります。 gakufu.h ファイルは、スケッチファイルを同じディレクトリに保存してください。 - 音階の周波数を一覧にした配列 dW_tbl[ ] を参照しながら、楽譜ファイルの音階を音の周波数に変換します。
- 楽譜ファイルの最初のデータは曲のテンポとして、1分間に演奏される4分音符の数が楽譜の最初に書かれています。この値はプログラムで使用するテンポ単位 (ms) に変換しますが、4分音符は 8テンポ単位、8分音符は 4テンポ単位の演奏時間になります。
テンポ単位(ms) = 60 x 1000 / テンポ / 8 = 7500 / テンポ
たとえば 4分音符=120 は、テンポ単位は 62.5 msになります。 - 楽譜ファイルの続くデータは、演奏する音符の情報なので、MSBとLSBにわけ、MSBは周波数に変換し、LSBは演奏時間に変換します。
- 変換された周波数と時間を tone関数で処理し演奏が終わるまで delay関数で待機し、次の音符データの処理に移ります。
なお、delay時間が音符演奏時間より長いのは、音と音の間に無音時間を入れて音のキレを良くする役割があります。 - 楽譜ファイルから FFFF が読み込まれると曲が終了したことを示し、プログラムの実行が停止します。
再実行するには、Arduino のリセットボタンを押してください。
"musicBox.ino"
// 電子オルゴールで曲を演奏 // 2021-6-18 #include "gakufu.h" // 楽譜 // 音階周波数一覧 262Hz(index 37) が中心のド(60 0x3C) const uint16_t dW_tbl[] = { // Do, Do#, Re, Re#, Mi, Fa, Fa#, So, So#, Ra, Ra#, Si, 0, 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, // 1-12 65, 69, 73, 78, 82, 87, 92, 98, 104, 110, 117, 123, //13-24 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, //25-36 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, //37-48 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, //49-60 1047,1109,1175,1245,1319,1397,1480,1568,1661,1760,1865,1976 //61-72 }; int PBuzzer = 8; // 圧電スピーカー接続pin int buttonApin = 14; // A0 pinをデジタルで使用 void setup() { } void loop() { int tempo = 7500/p1gkf[0]; // テンポ曲の速さを取得 int thisNote = 1; while(p1gkf[thisNote] != 0xFFFF){ // 楽譜を順にし手と取得 int duration = p1gkf[thisNote] & 0xFF; // 音符の長さ取得 duration = duration * tempo; // 音符の長さ計算 int freq = p1gkf[thisNote] >> 8; // 音の高さ取得 if(freq){ // 休符でないなら freq = dW_tbl[freq - 24]; // 音の周波数計算 tone(PBuzzer, freq, duration); // 音符演奏 } delay(duration+20); // 音符休符の演奏時間 thisNote++; // 音符位置を進める } while(1); // Stop }
スケッチの実行手順
電子オルゴールで曲を演奏するスケッチの実行手順の一例を示します。
- Arduinoアプリを起動し、上記 "musicBox.ino" の内容をコピーして新規ファイルに貼り付けます。
- Arduinoアプリのメニュー -> ファイル -> 名を付けて保存 を選択してスケッチを保存します。
この例では、Arduinoディレクトリに "musicBox"との名称で保存します。 - テキストエディターで新規ファイルを作成し、上記 "gakufu.h" の内容をコピーして、そのファイル名を "gakufu.h" とし、 "musicBox"が保存されている Arduinoディレクトリに保存します。
Arduino
┗ musicBox
┣ gakufu.h
┗ musicBox.ino - 以上で準備完了です。Arduinoアプリの書き込みボタンをクリックしてスケッチを実行してください。
これで、ハピバースデーの歌が演奏されます。
次回は、このスケッチで演奏できる楽譜データを簡単に作成する方法を紹介します。