概要
ラズパイと圧電スピーカーをつなぎ、タイマーや時報のためにMIDIで音を鳴らす方法を紹介します。
私は複数のタイマーや時報をラズパイで鳴らしているのですが、もっと複雑なメロディーを楽に作れたらと思いました。音程と鳴らすタイミングが必要ですが、周波数と秒数を配列としてソースコードに直書きする方法では、実際にならしてみるまで正しく指定できているかがわかりません。プレビューも難しく、気軽にメロディを試せません。そこで、DAWなどのMIDIエディタでメロディーをいったん作成してから、そのMIDIを読み込んで鳴らせることができたら楽だと思い、試しました。
以下に周波数と秒数を直書きするコード例を示します。
import RPi.GPIO as GPIO
import time
import math
pin = 13 # 任意のピン番号
TIME_SLEEP = 2.5
def onkai(n):
""" 鍵盤番号nに応じた周波数[Hz]を返します"""
# 鍵盤番号は、ラ0(A0)を1とした連番
# n=1(ラ0(A0)) -> 27.500Hz
# n=2(ラ#0(A#0)) -> 29.135...Hz
# n=3(シ0(B0)) -> 30.867...Hz
# ...
return 27.500 * math.pow(math.pow(2, 1 / 12), (n - 1))
# それぞれの音の周波数を定義
DO = onkai(52)
RE = onkai(54)
MI = onkai(56)
SO = onkai(59)
# メロディとリズムを配列に
mery_merody = [
MI,
RE,
DO,
RE,
MI,
MI,
# ...
]
mery_rhythm = [
0.3,
0.6,
0.9,
0.6,
0.9,
0.3,
# ...
]
GPIO.setmode(GPIO.BCM)
GPIO.setup(pin, GPIO.OUT, initial=GPIO.LOW)
p = GPIO.PWM(pin, 1)
p.start(50)
for i, oto in enumerate(mery_merody):
p.start(50)
p.ChangeFrequency(oto)
time.sleep(mery_rhythm[i] / TIME_SLEEP)
p.stop()
time.sleep(0.01)
p.stop()
GPIO.cleanup()
手順
MIDIで奏でる例を示します。
MIDIを読み込む
Pythonライブラリのmidoを使用します。以下のコマンドでインストールします。
pip install mido
圧電スピーカーで鳴らす為のMIDIを作成
圧電スピーカーで鳴らすため、少々特殊なMIDIを作成します。手元の環境がWindowsだったため、Dominoで編集しました。
以下を満たすようなMIDIを作成します。
- 使用するのは1CHのみ
- ノートとノートが重ならないように調整する
- 拍子とテンポは変更しても問題ない
- 「End of Trackを調節」にて曲の終了を指定するのを忘れずに
以下は参考画像。
以下の手順でMIDIファイルとしてデータを出力します。
- ファイル > SMF書き出し >
- 「format1(通常はこちら)」で保存
MIDIをPythonで読み込み
先ほど作成したMIDIをPythonで読み込みます。MIDIをpythonで読み込むためにmido/mido: MIDI Objects for Pythonを使用します。
pip3 install mido --break-system-packages
MIDIを読み込むときは、SysExやProgram Changeなどの制御系メッセージはすべて無視し、Note onやNote offのみを取り出します。
import math
import sys
import time
import mido
import RPi.GPIO as GPIO
def midi_number_to_note_number(midi_number: int):
"""MIDIのnote numberを鍵盤番号へ変換します"""
# 鍵盤番号: 1:ラ0(A0) = MIDIのnote number: 21
return midi_number - 20
def onkai(n):
"""鍵盤番号nに応じた周波数[Hz]を返します"""
# 鍵盤番号は、ラ0(A0)を1とした連番
# n=1(ラ0(A0)) -> 27.500Hz
# n=2(ラ#0(A#0)) -> 29.135...Hz
# n=3(シ0(B0)) -> 30.867...Hz
# ...
return 27.500 * math.pow(math.pow(2, 1 / 12), n)
# GPIO XX の XXの数値でPIN番号を指定する
GPIO.setmode(GPIO.BCM)
# GPIO 13 = PWM 1 の33番PINに圧電スピーカーの+端子を接続する
GPIO.setup(13, GPIO.OUT, initial=GPIO.LOW)
p = GPIO.PWM(13, 1)
# 圧電スピーカーの-端子はGroundならどこでもよい
# 今回は34番PINに接続する
for msg in mido.MidiFile(sys.argv[1]):
time.sleep(msg.time)
if msg.type == "note_on":
# 圧電スピーカー
p.start(50)
freq = onkai(midi_number_to_note_number(msg.note))
p.ChangeFrequency(freq)
print(f"note_on {freq} {msg}")
if msg.type == "note_off":
p.stop()
print(f"note_off {msg}")
以下のコマンドで実行します。
python3 midi.py sample.mid
以下のログ出力とともに、圧電スピーカーから音が鳴ります。
note_on 554.3652619537454 note_on channel=0 note=72 velocity=100 time=0
note_off note_off channel=0 note=72 velocity=0 time=0.25
note_on 554.3652619537454 note_on channel=0 note=72 velocity=100 time=0.25
note_off note_off channel=0 note=72 velocity=0 time=0.25
note_on 830.6093951598923 note_on channel=0 note=79 velocity=100 time=0.25
note_off note_off channel=0 note=79 velocity=0 time=0.25
note_on 830.6093951598923 note_on channel=0 note=79 velocity=100 time=0.25
note_off note_off channel=0 note=79 velocity=0 time=0.25
note_on 932.3275230361822 note_on channel=0 note=81 velocity=100 time=0.25
note_off note_off channel=0 note=81 velocity=0 time=0.25
note_on 932.3275230361822 note_on channel=0 note=81 velocity=100 time=0.25
note_off note_off channel=0 note=81 velocity=0 time=0.25
note_on 830.6093951598923 note_on channel=0 note=79 velocity=100 time=0.25
note_off note_off channel=0 note=79 velocity=0 time=0.5
note_on 739.9888454232706 note_on channel=0 note=77 velocity=100 time=0.5
note_off note_off channel=0 note=77 velocity=0 time=0.25
note_on 739.9888454232706 note_on channel=0 note=77 velocity=100 time=0.25
note_off note_off channel=0 note=77 velocity=0 time=0.25
note_on 698.4564628660095 note_on channel=0 note=76 velocity=100 time=0.25
note_off note_off channel=0 note=76 velocity=0 time=0.25
note_on 698.4564628660095 note_on channel=0 note=76 velocity=100 time=0.25
note_off note_off channel=0 note=76 velocity=0 time=0.25
note_on 622.2539674441632 note_on channel=0 note=74 velocity=100 time=0.25
note_off note_off channel=0 note=74 velocity=0 time=0.25104166666666666
note_on 622.2539674441632 note_on channel=0 note=74 velocity=100 time=0.24895833333333334
note_off note_off channel=0 note=74 velocity=0 time=0.25104166666666666
note_on 554.3652619537454 note_on channel=0 note=72 velocity=100 time=0.24895833333333334
note_off note_off channel=0 note=72 velocity=0 time=0.5
参考
-
音階周波数
- 鍵盤番号と周波数について
- Mido - MIDI Objects for Python — Mido 1.3.4.dev6+ga0158ff documentation
- MIDIのnote編集に関する基本tips【Python, mido】 #Python - Qiita
- Midoを使ってPythonでMIDIを扱ってみよう #mido - Qiita
動作環境
- Raspberry Pi 4 Model B Rev 1.5
- Raspberry Pi OS Lite (64-bit)(Debian GNU/Linux 12.2.0 (bookworm))
- Python 3.11.2
- mido 1.3.3