1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ラズパイで圧電スピーカーをMIDIで奏でる

Last updated at Posted at 2025-02-28

概要

ラズパイと圧電スピーカーをつなぎ、タイマーや時報のために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を作成

以下の手順で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

参考

動作環境

  • 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
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?