ジョルノのテーマ(Il Vento Doro)をデュアルArduinoで演奏 – 精密なピッチとタイミング
部品と材料
| 部品 | 数量 | 備考 |
|---|---|---|
| Arduino UNO | 2 | メインボードとセカンダリボード用 |
| MOSFETトランジスタ(スイッチング用) | 2 | スピーカー駆動用 |
| スピーカー:3W、4Ω | 4 | 各ボードに2個ずつ(ステレオまたはレイヤー用) |
| 抵抗 1kΩ(0.25W) | 2 | ゲート抵抗など |
| ブレッドボード用電源スティック 5V/3.3V(SparkFun) | 1 | 外部電源供給用 |
| ジャンパーワイヤー(汎用) | 1セット | 配線用 |
必要な工具と機器
- はんだごて(汎用)
使用ソフトウェアとオンラインサービス
プロジェクト概要
このプロジェクトでは、ジョジョの奇妙な冒険 第5部の主題歌「ジョルノのテーマ(Il Vento Doro)」を2台のArduino UNOを使って再現します。内蔵のtone()関数とシンプルなデジタル・ハンドシェイクを活用し、両ボードを同期させて重なる音符をリアルタイムで演奏します。
プライマリ(主)ボードはピン2からトリガー・パルスを出力し、セカンダリ(副)ボードはピン3でそれを受信します。各ボードは専用の波形配列を実行し、高いオクターブの音符をセカンダリ側で担当させることで、単一ボードでは発生しがちな周波数クリッピング(音割れ)を回避しています。
注意: スケッチ内で #include "Color.h" として読み込まれているヘッダーファイルには、Pink1, Blue1, Orange2 などの音符定数(周波数値)が定義されている必要があります。このファイルが別途用意されていないとコンパイルエラーになります。一般的な pitches.h などを流用するか、独自に定義してください。
コード
パート1:プライマリArduino(マスター)
#include "Color.h"
int S[] = {
Pink1, 0, Pink1, 0, Pink1, Blue1, 0, Pink1, 0, Orange2, 0, Pink1, 0, Green1, Blue1, 0,
Pink1, 0, Pink1, 0, Pink1, Blue1, 0, Pink1, 0, LGreen2, 0, Yellow2, 0, Orange2, Blue1, 0,
Pink1, 0, Pink1, 0, Pink1, Blue1, 0, Pink1, 0, Orange2, 0, Pink1, 0, Green1, Blue1, 0,
Pink1, 0, Pink1, 0, Pink1, Blue1, Pink1, Orange2, 0, LGreen2, 0, Yellow2, 0, Orange2, Blue1, 0,
Pink1, 0, Pink1, 0, Pink1, Blue1, 0, Pink1, 0, Orange2, 0, Pink1, 0, Green1, Blue1, 0,
Pink1, 0, Pink1, 0, Pink1, Blue1, 0, Pink1, 0, LGreen2, 0, Yellow2, 0, Orange2, Blue1, 0,
Pink1, 0, Pink1, 0, Pink1, Blue1, 0, Pink1, 0, Orange2, 0, Pink1, 0, Green1, Blue1, 0,
Pink1, 0, Pink1, 0, Pink1, 0, Blue1, Pink1, 0, 0, 0, 0, Pink1, 0, Orange2, 0, Yellow2,
LGreen2, 0, Green2, 0, Blue2, 0, Green2, 0, Blue2, Purple2, 0, Pink2, 0, Purple2, 0,
Blue2, Green2, 0, Yellow2, 0, Orange2, LBlue1, 0, Blue1, 0, Purple1, 0, Pink1, 0, Orange2, 0,
Yellow2, LGreen2, 0, Green2, 0, Blue2, 0, Green2, 0, Blue2, Purple2, 0, Pink2, 0, Blue2,
Pink2, Blue2, 0, Purple2, Pink2, 0, LBlue1, 0, LBlue1, Blue1, Blue1, Purple1, 0, Pink1, 0,
Orange2, 0, Yellow2, LGreen2, 0, Green2, 0, Blue2, 0, Green2, 0, Blue2, Purple2, 0, Pink2, 0,
Purple2, 0, Blue2, Green2, 0, Yellow2, 0, Orange2, LBlue1, 0, Blue1, 0, Purple1, 0, Pink1, 0,
Orange2, DOrange2, Pink1, 0, Blue1, 0, 0, Pink1, 0, 0, Pink1, 0, 0, 0, Green1, 0, Blue1, 0,
Pink1, 0, DOrange2, 0, 0, Blue1, 0, 0, Pink1, 0, 0, 0, 0, 0, Orange2, DOrange2, Pink1, 0,
Blue1, 0, 0, Pink1, 0, 0, Pink1, 0, 0, Green1, 0, 0, DOrange2, 0, Orange2, 0, DOrange2, 0,
0, Blue1, 0, DOrange1, Yellow1, Green1, DOrange1, Green1, LBlue1, Pink1, Orange2, DOrange2,
Pink1, 0, Blue1, 0, 0, Pink1, 0, 0, Pink1, 0, 0, 0, Green1, 0, Blue1, 0, Pink1, 0, DOrange2,
0, 0, Blue1, 0, 0, Pink1, 0, 0, 0, 0, 0, Orange2, DOrange2, Pink1, 0, Blue1, Orange2, 0,
Green2, Yellow2, Orange2, Pink1, 0, Blue2, LBlue2, 0, Green2, Orange2, DOrange2, 0, Pink1,
Blue1, Pink1, 0, Blue1, Pink1, 0, Blue1, Pink1, 0, Blue1, Pink1, 0, Blue1, Pink1, Orange2, 0,
Green2, 0, 0, 0, 0, 0, Orange2, 0, 0, 0, 0, 0, 0, Orange2, Yellow2, LGreen2, 0, 0, Yellow2,
0, 0, Orange2, 0, DOrange2, 0, 0, Orange2, 0, 0, Yellow2, 0, Green2, 0, 0, 0, 0, 0, Pink2, 0,
0, 0, 0, 0, Pink1, 0, DOrange2, 0, Orange2, 0, 0, Yellow2, 0, 0, Orange2, 0, DOrange2, 0,
0, Blue2, 0, 0, DGreen2, 0, Green2, 0, 0, 0, 0, 0, Orange2, 0, 0, 0, 0, 0, 0, Orange2,
Yellow2, LGreen2, 0, 0, Yellow2, 0, 0, Orange2, 0, DOrange2, 0, 0, Orange2, 0, 0, Yellow2, 0,
Green2, 0, 0, 0, 0, 0, Pink2, 0, 0, 0, 0, 0, Green2, 0, Purple2, 0, Pink2, 0, 0, DGreen2, 0,
0, Green2, 0, LGreen2, 0, 0, Pink2, 0, 0, Green2, 0, Green2
};
int L = sizeof(S);
int nds = 10; // 音符の基本分割数(テンポ調整用)
void setup() {
Serial.begin(115200);
pinMode(2, OUTPUT); // セカンダリへのトリガー出力ピン
L = L / 2; // 配列要素数を音符数に変換(intは2バイトのため)
Serial.println(L);
Tone();
}
void loop() {}
void Tone() {
digitalWrite(2, HIGH); // セカンダリへ演奏開始合図
for (int n = 0; n < L; n++) {
int nd = 1000 / nds; // 1音符の長さ(ミリ秒)
tone(6, S[n], nd); // ピン6から音出力
int d = nd * 1.2; // 音符間の余裕(1.2倍)
delay(d);
noTone(6);
}
}
パート2:セカンダリArduino(スレーブ)
#include "Color.h"
int S[] = {
// 最初の長い無音区間(プライマリの前半パートに合わせる)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// ここから高音パート(オクターブアップ)が始まる
Pink2, 0, Pink2, 0, Pink2, Blue2, 0, Pink2, 0, Orange3, 0, Pink2, 0, Green2, Blue2, 0,
Pink2, 0, Pink2, 0, Pink2, Blue2, 0, Pink2, 0, LGreen3, 0, Yellow3, 0, Orange3, Blue2, 0,
Pink2, 0, Pink2, 0, Pink2, Blue2, 0, Pink2, 0, Orange3, 0, Pink2, 0, Green2, Blue2, 0,
Pink2, 0, Pink2, 0, Pink2, 0, Blue2, Pink2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Orange3, DOrange3, Pink2, 0, Blue2, 0, 0, Pink2, 0, 0, Pink2, 0, 0, 0, Green2, 0, Blue2, 0,
Pink2, 0, DOrange3, 0, 0, Blue2, 0, 0, Pink2, 0, 0, 0, 0, 0, Orange3, DOrange3, Pink2, 0,
Blue2, 0, 0, Pink2, 0, 0, Pink2, 0, 0, Green2, 0, 0, DOrange3, 0, Orange3, 0, DOrange3, 0,
0, Blue2, 0, DOrange2, Yellow2, Green2, DOrange2, Green2, LBlue2, Pink2, Orange3, DOrange3,
Pink2, 0, Blue2, 0, 0, Pink2, 0, 0, Pink2, 0, 0, 0, Green2, 0, Blue2, 0, Pink2, 0, DOrange3,
0, 0, Blue2, 0, 0, Pink2, 0, 0, 0, 0, 0, Orange3, DOrange3, Pink2, 0, Blue2, Orange3, 0,
Green3, Yellow3, Orange3, Pink2, 0, Blue3, LBlue3, 0, Green3, Orange3, DOrange3, 0, Pink2,
Blue2, Pink2, 0, Blue2, Pink2, 0, Blue2, Pink2, 0, Blue2, Pink2, 0, Blue2, Pink2, Orange3, 0,
Green2, 0, 0, 0, 0, 0, Orange2, 0, 0, 0, 0, 0, 0, Orange2, Yellow2, LGreen2, 0, 0, Yellow2,
0, 0, Orange2, 0, DOrange2, 0, 0, Orange2, 0, 0, Yellow2, 0, Green2, 0, 0, 0, 0, 0, Pink2, 0,
0, 0, 0, 0, Pink1, 0, DOrange2, 0, Orange2, 0, 0, Yellow2, 0, 0, Orange2, 0, DOrange2, 0,
0, Blue2, 0, 0, DGreen2, 0, Green2, 0, 0, 0, 0, 0, Orange2, 0, 0, 0, 0, 0, 0, Orange2,
Yellow2, LGreen2, 0, 0, Yellow2, 0, 0, Orange2, 0, DOrange2, 0, 0, Orange2, 0, 0, Yellow2, 0,
Green2, 0, 0, 0, 0, 0, Pink2, 0, 0, 0, 0, 0, Pink2, 0, DOrange3, 0, Orange3, 0, 0, DGreen3, 0,
0, Green3, 0, LGreen3, 0, 0, Orange3, 0, 0, Purple2, 0, Pink2
};
int L = sizeof(S);
int nds = 10;
void setup() {
Serial.begin(115200);
pinMode(3, INPUT_PULLUP); // プライマリからのトリガー入力ピン
L = L / 2;
Serial.println(L);
}
void loop() {
if (!digitalRead(3)) { // トリガー(LOW)を検知したら演奏開始
Tone();
}
}
void Tone() {
for (int n = 0; n < L; n++) {
int nd = 1000 / nds;
tone(6, S[n], nd);
int d = nd * 1.2;
delay(d);
noTone(6);
}
}
回路図
ブレッドボード上の配線図(Fritzing形式)を参照してください。スピーカーはMOSFETを介して駆動し、2台のArduinoはピン2(出力)とピン3(入力プルアップ)で接続されています。
作り方のポイント
- Color.hを用意する: 音名(Pink1, Blue2 など)を周波数(Hz)にマッピングしたヘッダーファイルを作成し、スケッチと同じフォルダに置きます。
- 配線: 回路図に従い、2台のArduino、MOSFET、スピーカー、電源をブレッドボード上に組みます。共通GNDを忘れずに。
- 書き込み: それぞれのスケッチを対応するボードにアップロードします。
- 電源投入: 外部電源(5V)を入れると、プライマリが自動的に演奏を開始し、セカンダリが同期して高音パートを重ねます。
発展のヒント
ndsの値を変えるとテンポが変わります。- 配列
Sを編集すれば他の曲にも応用可能です(MIDIから変換するツールを自作すると便利)。 - より高音質にするには、
tone()代わりにPWMや外部DAC、VS1053などのオーディオモジュールを検討してください。
製造プロセス
- TinyML言語検出器:Edge ImpulseとArduino Nano 33 BLE Senseで「はい」を多言語判別
- Arduino NanoとMPU-6050で作るリアルタイムジャイロスコープゲーム
- Arduinoデジタルダイス: LCD表示の乱数ジェネレーター自作
- Arduino Nanoで37LEDルーレットゲームを自作:ヨーロピアンスタイル完全再現
- ATtiny85ミニアーケード:スネークゲーム - 手のひらサイズOLEDゲーム機の製作
- ポータブル距離検出器:Arduino Nano自作キット(HC-SR04、18650電源、3Dプリントケース付き)
- Arduino Nanoで作る音声反応デスクライト:音楽に合わせて光るイルミネーション
- Arduino Nano と OpenCV で実現する自律顔追跡ロボットカー
- 銅メッキプロジェクト:Arduino UNOで作るスマート電気メッキシステム
- Arduino Nanoで作るNeoPixelマトリックス版Pongゲーム
- Arduinoシフトレジスタで美しい光のシーケンスを作成