Raspberry Piを実用的な単体シンセサイザーとして使う


Raspberry Piをスタンドアロンのシンセサイザーにしてみました。
自宅以外でのライブ等でも使用できるように出来る限り実用面を考慮して記載したので、少々長い記事になっています。
自分はLinuxにはあまり詳しくないので、改善点、指摘事項がありましたらお気軽にご指摘ください。

## 要旨
- セットアップは１〜３時間程度。
- リアルタイム演奏にはDACまたはオーディオI/Fは必須。これとUSB-MIDI鍵盤があれば演奏可能。
- 起動時間が遅い。頑張ってチューニングしても40秒くらいかかる（OSの起動に30秒弱、ソフトシンセの起動に10秒強。OS起動状態とソフトシンセ起動状態はパイロットLEDの挙動で判別できるようにした）
- ここで使用したPi3BはUSBスレーブになることが出来ないので、普通のハードMIDI音源のようにシーケンサーやパソコンからUSB制御できない。（Pi4やPi Zeroはできるらしい）苦肉の策としては、USB-MIDIケーブルを２本用意して、それぞれの制御する側のMIDI-OUTをPi3BのMIDI-INに繋げばできるかもしれない。（未実験）
- 本記事では、単にソフトシンセをインストールするだけでなく、MIDIコントローラの使用例、高速起動化、シャットダウン方法やUSBの接続ミスへの対処など、単体のシンセサイザーとしてできる限り実用に必要な部分まで記載した。



![IMG_2303.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1019283/72927ee5-4cc9-c349-b4c2-e77f880a0ea0.jpeg)




## 必要なもの（写真参照）
- Raspberry Pi本体（ここではPi3Bを使用。本記事の説明はPi3B前提としています）、電源、電源ケーブル
- USB接続MIDIキーボード
- USB-DACまたはUSBオーディオインターフェイス（写真にあるスマホに繋ぐような簡易的なものでも無問題）

※ディスプレイは不要です。


## fluidsynthのインストール

Raspberry Piにはいろんなソフトシンセがありますが、最も一般的と思われるfluidsynthを使いました。

インストールに先立って、
~~~shell
$sudo raspi-config
~~~

でGUIは切っておきます。
（現時点のRaspberry Pi OSの最新版「bullseye」ではraspi-configのメニューが以前と大きく変わっているようです。よって、詳しい手順も割愛）
以下は全てSSH上で作業しました。HDMIケーブルも抜いてしまって構いません。

fluidsynthをインストールします。
~~~shell
$sudo apt-get install fluidsynth
~~~


midi（SMF）ファイルを鳴らしてみる（midiファイルがなければ跳ばして構いません）
~~~shell
$fluidsynth -a alsa -n -i /usr/share/sounds/sf2/FluidR3_GM.sf2 EvilWays.mid
~~~

alsaというのがオーディオドライバー（ASIOのようなもの）で、デフォルトではラズパイの3.5mmステレオジャックから音が出る。

## midi鍵盤の接続
~~~shell
$amidi -l
Dir Device    Name
IO  hw:1,0,0  Keystation Mini 32 MIDI 1
~~~

IOが、`hw:2,0,0` になるときもある。（カード番号、デバイス番号、サブデバイス番号の意味。４つあるUSBのうちどこに挿すかで違ってくるらしい。<b>適当に刺すと番号が変わることに注意</b>）
WinやMacと違い、ブート時からUSBに刺さってないと認識しない(と思ったら、初回のみがリブートが必要、それ以降は繋いだだけでOKだった)

鍵盤を弾いたらシンセが鳴るようにしてみる。上記のカード番号によってコマンドが少し違ってくる。

~~~shell
$fluidsynth -s -a alsa -m alsa_raw -g 0.4 -o midi.alsa.device=hw:1,0,0 /usr/share/sounds/sf2/FluidR3_GM.sf2
または
$fluidsynth -s -a alsa -m alsa_raw -g 0.4 -o midi.alsa.device=hw:2,0,0 /usr/share/sounds/sf2/FluidR3_GM.sf2
~~~
鍵盤で音は出る。<b>でも、鍵盤の反応がわずかに遅れる。0.2秒くらいか。</b>

次の警告がでた。
`fluidsynth: warning: Failed to set thread to high priority`

対処は
https://forums.raspberrypi.com/viewtopic.php?t=211327
にしたがって、

`/etc/security/limits.conf`
に
~~~/etc/security/limits.conf
@audio   -  rtprio      90
@audio   -  memlock     unlimited
~~~
を追加。
上記の警告はでなくなった。
<b>しかし、鍵盤の反応の遅れは変わらない。</b>

fluidsynthの`settings`をみると
~~~shell
audio.realtime-prio                 60
midi.realtime-prio                  50
~~~
この値はaudioやmidiの優先度を表していて、この数値が高いほど優先度が高い（はず）
50という数値はあまり良くないです。
（※実は私はあまり詳しくなくいです。）

CPUクロックは
~~~shell
$vcgencmd measure_clock arm
frequency(48)=600064000
~~~
600MHzくらい。
temp=62.3'Cなので、このあたりで限界っぽい。
3Bなので、本来性能は1.2GHzなんだけど、それをするにはちゃんとした冷却ファンが必要っぽい。今は純正プラケースとちっちゃな放熱版だけなので。

## USB-DACの接続
ここでUSB-DACを接続する。（実はUSB-DACのことを忘れていた）
~~~shell
$cat /proc/asound/cards
 0 [Headphones     ]: bcm2835_headpho - bcm2835 Headphones
                      bcm2835 Headphones
 1 [vc4hdmi        ]: vc4-hdmi - vc4-hdmi
                      vc4-hdmi
 2 [Audio          ]: USB-Audio - Realtek USB2.0 Audio
                      Realtek Realtek USB2.0 Audio at usb-3f980000.usb-1.4, high speed
~~~
刺すだけですぐに認識された。
~~~shell
$aplay -l
**** ハードウェアデバイス PLAYBACK のリスト ****
カード 0: Headphones [bcm2835 Headphones], デバイス 0: bcm2835 Headphones [bcm2835 Headphones]
  サブデバイス: 8/8
  サブデバイス #0: subdevice #0
  サブデバイス #1: subdevice #1
  サブデバイス #2: subdevice #2
  サブデバイス #3: subdevice #3
  サブデバイス #4: subdevice #4
  サブデバイス #5: subdevice #5
  サブデバイス #6: subdevice #6
  サブデバイス #7: subdevice #7
カード 1: vc4hdmi [vc4-hdmi], デバイス 0: MAI PCM i2s-hifi-0 [MAI PCM i2s-hifi-0]
  サブデバイス: 1/1
  サブデバイス #0: subdevice #0
カード 2: Audio [Realtek USB2.0 Audio], デバイス 0: USB Audio [USB Audio]
  サブデバイス: 1/1
  サブデバイス #0: subdevice #0
~~~
2, 0, 0の番号が USB Audioだということがわかる。

以下のコマンドで、fluidsynthを起動
~~~shell
$fluidsynth -s -a alsa -m alsa_raw -g 0.4 -o audio.alsa.device=hw:2,0,0 -o midi.alsa.device=hw:3,0,0 /usr/share/sounds/sf2/FluidR3_GM.sf2
~~~
<b>おー、やった！ 反応の遅さが全く感じられない。</b>和音を押すとmidiイベントをとりこぼすとか書いているところもあったけど、全然そんなことない。両手で２０個以上の鍵盤を同時に押しても全然平気。
<b>Raspberry Pi本体だけだと遅延が発生するけど、DACやオーディオI/Fを繋げればOKということですね。</b>
シンセサイザーとしてはここまでです。この先は、実用性向上などに関する話になります。

## 高速起動化
起動が遅いので高速起動化を試みる。
高速起動は本当はそれだけひとつのトピックなのだけれど、ここでは深くは立ち入らずに大きなところのみ。

参考
https://qiita.com/peace098beat/items/c95f0ba4d9edcf89b023
http://www.momobro.com/rasbro/tips-rp-raspberry-pi-stop-service/


まず、次のコマンドでボトルネックを見つける。
~~~shell
$ systemd-analyze critical-chain
The time when unit became active or started is printed after the "@" character.
The time the unit took to start is printed after the "+" character.

multi-user.target @24.115s
└─smbd.service @23.136s +977ms
  └─nmbd.service @10.402s +12.723s
    └─network-online.target @10.357s
      └─network.target @10.354s
        └─wpa_supplicant.service @8.420s +1.924s
          └─dbus.service @8.207s
            └─basic.target @8.184s
              └─sockets.target @8.183s
                └─triggerhappy.socket @8.182s
                  └─sysinit.target @8.136s
                    └─plymouth-read-write.service @6.529s +1.606s
                      └─local-fs.target @6.457s
                        └─run-user-1000.mount @16.113s
                          └─local-fs-pre.target @3.303s
                            └─systemd-tmpfiles-setup-dev.service @3.184s +118ms
                              └─systemd-sysusers.service @3.042s +134ms
                                └─systemd-remount-fs.service @2.788s +229ms
                                  └─systemd-fsck-root.service @2.161s +621ms
                                    └─systemd-journald.socket @2.036s
                                      └─system.slice @1.176s
                                        └─-.slice @1.176s
~~~
smbdがものすごい時間を食っている。
smbdとbluetoothを停止させる
~~~shell
$sudo systemctl stop bluetooth.service
$sudo systemctl stop smbd.service
$sudo systemctl stop nmbd.service
~~~
さらに非活性化。これをしないと今は死んでても、再起動すると復活したりする。
~~~shell
$sudo systemctl disable bluetooth.service
$sudo systemctl disable smbd.service
$sudo systemctl disable nmbd.service
~~~

再びブートして計測
~~~shell
$ systemd-analyze critical-chain
The time when unit became active or started is printed after the "@" character.
The time the unit took to start is printed after the "+" character.

multi-user.target @11.572s
└─ModemManager.service @9.895s +1.673s
  └─polkit.service @8.068s +1.812s
    └─basic.target @7.773s
      └─sockets.target @7.773s
        └─triggerhappy.socket @7.772s
          └─sysinit.target @7.736s
            └─plymouth-read-write.service @6.255s +1.478s
              └─local-fs.target @6.196s
                └─run-user-1000.mount @15.068s
                  └─local-fs-pre.target @3.643s
                    └─systemd-tmpfiles-setup-dev.service @3.146s +496ms
                      └─systemd-sysusers.service @3.019s +120ms
                        └─systemd-remount-fs.service @2.765s +226ms
                          └─systemd-fsck-root.service @2.162s +596ms
                            └─systemd-journald.socket @2.039s
                              └─system.slice @1.188s
                                └─-.slice @1.188s
~~~

１２秒。こんなものですか。
<b>でも、ストップウォッチ実測だと起動２７秒。それからfluidsynthが立ち上がるまでさらに２０秒弱。遅いです。M5Stackなどに比べたら圧倒的に遅いです。</b>ここは課題。小さいディスプレイを用意してプログレスバーを出すとかしないとダメかも。

補：
ここでチューニングできるのは、
~~~shell
$ sudo systemd-analyze
Startup finished in 4.009s (kernel) + 18.553s (userspace) = 22.562s 
multi-user.target reached after 12.799s in userspace
~~~
この中のmulti-user.targetという部分だけのようです。（上記とは別の起動時なので時間が異なっています）

plymouth-read-write.service（起動スプラッシュスクリーン）と
ModemManager.service（電話を掛ける？）
は抜いてもいいかも。


## fluidsynthの自動起動
いろいろやり方があるけどシンプルに`.bashrc`の最後に書いておく。
自動ログオンするようにしておけばOK

`startsynth.sh`というファイルを作って以下を書き込む
~~~startsynth.sh
#!/bin/sh

killall fluidsynth

fluidsynth -s -a alsa -m alsa_raw -g 0.4 -o audio.alsa.device=hw:2,0,0 -o midi.alsa.device=hw:1,0,0 /usr/share/sounds/sf2/FluidR3_GM.sf2

sleep 10
~~~

実行可能にする。
~~~shell
$chmod +x startsynth.sh
~~~

`.bashrc`の一番後に追加

~~~shell
./startsynth.sh
~~~

## シンセサイザーとしての操作方法
ここまで、「シンセサイザー」と言ってきたが、鍵盤を弾いてもピアノの音しか出ておらず、シンセとは言えないという指摘もあるかもしれない。
そこで、シンセサイザーとしての操作について少し書いてみる。これについては使用しているmidi鍵盤に大きく依存する。自分は、M-AUDIO KEYSTATION MINI 32を使っているが、例えば音色を変更できるようにするには次のようにする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1019283/017d7d4b-49ba-9c6b-5b15-3664efdb6ce3.png)
例：
左端の「+」ボタンと「-」ボタンは通常オクターブ変更に使用するがこれを音色変更ボタンとする。
1. 「EDIT」ボタンを押します。「EDIT」ボタンが 100% の明るさで点灯し、キーボードがエディット・モー ドになったことを示します。  
2. 「DATA = Program」キー(F#、左から３番目の黒鍵)を押します。 「DATA = Program」キーを押すと同時にエディット・モードが解除され、オクターブの「+」ボタンと 「-」ボタンの両方が 50% の明るさで赤色になります。  
3. 「+」ボタンまたは「-」ボタンを押して、GM音源の範囲で音色を変更できます。

元のオクターブ変更に戻すには同じ手順で上記2.のところで「DATA = Octave」キー(C#、左端の黒鍵)を押す。

<br>
例2：
左端のノブは通常ボリューム変更に使用するが、これをMIDI CCのリバーブ量調整とする。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1019283/07c43d4f-d157-24ae-aef4-137bf6e1b711.png)
1. 「EDIT」ボタンを押します。「EDIT」ボタンが 100% の明るさで点灯します。 
2. 「KNOB ASSIGN」キー(F#、右から 1 番目の黒鍵)を押します。
3.  データ入力数字キー(0 - 9)を使って、ボリューム・ノブにアサインしたいパラメータ番号を入力します。 リバーブ調整の場合はMIDI CC 91なので、まず、9に相当する鍵盤を押し、次に1に相当する鍵盤を押します。パラメータ番号を誤って入力した場合は、「CANCEL」キー(C、左の最初の白鍵)を押すと、何も変更 することなくエディット・モードを解除できます。手順 3 の代わりに、「+」ボタンと「-」ボタンを使って パラメータを変更することもできます。 
4. ENTERキーを押すと、EDITボタンが50%の明るさになり、プログラミングが完了したことを示します。
5. ボリューム・ノブを時計回りに回してパラメータの値を増やします。

元の音量調整に戻すには同じ手順で上記3.のところで、7に相当する鍵盤を押す。（音量はMIDI CC 7）

`「M-AUDIO KEYSTATION MINI 32　ユーザ・ガイド」より抜粋。`

ここで注意点があって、このfluidsynthというのは通常のMIDI CCが全て使えるわけではない。たとえば、通常CC74(Timbre/Cut-off Frequency)はフィルターの調整に使われるが、fluidsynthはこれができない。ピッチベンドやビブラートなどは問題なくできる。何ができて何ができないかは、次のサウンドフォントのマニュアルに説明がある。
http://www.synthfont.com/SFSPEC21.PDF
これによると、フィルターは備わっているがそれがMIDI CCとしては出ていないということらしい。
また、midiコントローラーを複数接続して、同じmidiチャンネル（通常は1）にすれば、別のコントローラーから音色やエフェクトの変更が出来るはず（未実施）。

## シャットダウンについて

安全に電源を切るには正規のシャットダウンプロセスを実行する必要があります。

`/etc/udev/rules.d/`
配下に
`90-usb.rules`
という名前のファイルを作り、中身は次のようにします。(su権限で編集してください)
~~~90-usb.rules
ACTION=="remove", SUBSYSTEM=="usb", RUN+="/bin/sh -c poweroff”
~~~

この変更を有効にするには次のようにします。
~~~bash
$sudo udevadm control --reload
~~~

これで、RaspberryPi本体から何かのUSBデバイスを外せば、直ちにシャットダウンプロセスを実行します。本体の緑ランプが点滅しなくなったら電源用のUSBケーブルを抜いて電源オフです。

特定デバイスでのシャットダウンはうまくいきませんでした。`ACTION==“add”`の場合はできたのですが、`ACTION==”remove”`だと上手くいかないです。確か、midiキーボードのremoveタイミングで、どうしてもベンダーIDとプロダクトIDがとれなかっためだったかと。(`udevadm info -n デバイス名 -a`で、IDが取れるタイミングがなかった)

もう一つの理由としては、現場でUSBをつなぎ変えるときって、USBを刺す場所を間違えてしまったときだと思うんですよね。そうしたらもうリブートかシャットダウンするしかないのです。こういう場合は、一度シャットダウンして、USB-DACやMIDIキーボード等のUSBデバイスをちゃんと繋ぎ直して、電源ONからやり直してください、という意味です。

参考
https://gihyo.jp/admin/serial/01/ubuntu-recipe/0559


## エラー処理（ユーザビリティの向上）
RaspberryPi本体の赤いLEDランプ（通常は電源ランプとして使用）の挙動によって、正常な状態かエラーが出たかを知らせます。
ここは完璧には組めませんでした。
ホームディレクトリにある`startsynth.sh`を次のように書き換えます。

~~~startsynth.sh
#!/bin/sh

killall fluidsynth

sudo sh -c 'echo timer > /sys/class/leds/led1/trigger'
sudo sh -c 'echo 500 > /sys/class/leds/led1/delay_on'
sudo sh -c 'echo 500 > /sys/class/leds/led1/delay_off'

fluidsynth -a alsa -m alsa_raw -g 0.4 -o audio.alsa.device=hw:2,0,0 -o midi.alsa.device=hw:1,0,0 /usr/share/sounds/sf2/FluidR3_GM.sf2

if [ $? -ne 0 ]
then
        # error case
        sudo sh -c 'echo timer > /sys/class/leds/led1/trigger'
        sudo sh -c 'echo 50 > /sys/class/leds/led1/delay_on'
        sudo sh -c 'echo 50 > /sys/class/leds/led1/delay_off'
fi
~~~

電源ON直後からOSが立ち上がるまでは赤い電源LEDは普通に点灯していますが、OSが起動すると
ゆっくり点滅（1秒間隔）するようになります。このときからシンセが起動し、７〜10秒程度でMIDIキーボードを受け付けるようになります。シンセの起動に失敗すると、この電源LEDが速く点滅（0.1秒間隔）します。起動失敗はほぼUSBを挿す場所を間違った場合（と思われる）なので、いったんUSB-DACやMIDIキーボード等のUSBデバイスを抜いて（つまり、シャットダウンさせる）、電源コードを抜き、USBデバイスを繋ぎ直した後に、電源コードを繋ぎなおします。
※fluidsynthの起動パラメタを変更したのは、サーバーモード（-s）だとエラーコードが返ってこなかったためです。


本当は素直に、シンセ起動に成功したら赤いLEDがゆっくり点滅、失敗したら速く点滅、としたかったのですが、fluidsynthの挙動の関係でそのようにできませんでした。
このコマンドは起動に数秒かかり、起動失敗は戻ってくるが成功は戻らない、という挙動なのです。
もしくは、シャットダウンで使ったudevの仕組みを使って、MIDIキーボードを本体に繋いだ時に自動的にシンセサイザーを起動してもいいかと思います。





## 備考
- Raspberry Pi3BはUSBのスレーブになることはできないので、ハードMIDI音源のように他のパソコン等から直接USBで繋いで音を出すことはできない。（Pi4やPi Zeroではスレーブにできるらしい。変な配線になってしまうが、もしかしたら、USB-midiケーブルを２本使って、パソコン側のMIDI-OUTとPi3B側のMIDI-INを繋げればできるかもしれない。）<br>Pi ZeroのOTGでスレーブに出来るようです
https://artteknika.hatenablog.com/entry/2017/04/28/185509
- udevのところでも書いたが、OS起動時からシンセを起動するのではなく、USB-MIDI鍵盤を接続したときに起動としてもいいかもしれない。この場合は、OSが完全に起動してからでないとMIDI鍵盤を接続できないので、場合によってはOS起動をずっと待っていることになり、それがわずらわしいかもしれない。
- Wi-Fiを無効化するのはこちらの記事で
https://qiita.com/hayate242/items/4f3d07d63a5b9577cb55
気になったので実験してみたが、Wi-Fiに繋がらなかったといって特に問題はないようである。ただし、万が一Raspberry Pi本体やそこに刺さっているSDカードを紛失、盗難にあった場合、Wi-Fiのパスワード（暗号化されているとしても）を他人に知られてしまう、という問題が生じるかもしれない。
- さらなる改善点としては、接続物を少なくすること、金属ケースや放熱ファンなどでクロックアップを可能にすること等が考えられる。
今回は、とりあえず手持ちの部品で対応したため、簡易的なDACでUSB->USB-C変換を噛ませているため、接触不良やDACが抜ける可能性がないとは言えない。Raspberry Piには次のようなケースもあり、DACの高品質化、耐久性の向上などに効果が見込まれる。（このケースでは放熱ファンはつけられないかもしれない）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1019283/67814e7e-2d1c-cb00-47d4-18e74666a288.png)
https://m.media-amazon.com/images/I/31hmyxC-msL._AC_.jpg