測距センサーMIDIデバイス＋ブラウザ（WebAudio）でテルミン風楽器を作ってみよう！

この投稿はKDDIテクノロジーアドベントカレンダーの12/03分の記事です。
他の記事はこちらです。
https://qiita.com/advent-calendar/2024/kddi-technology

![sensor-midi.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31758/20c91bdc-3a3e-45a8-bbf5-afa916f7b4cc.jpeg)

サンデープログラマーです。日曜にほっとくと変な工作を始めます。

今回は、テルミンみたいな楽器には憧れ非接触で音が変わるってかっこいい！というわけで、作ってみた変なモノの作り方の紹介です。

さて、この変なモノ、超ざっくり言うと

- センサーをMIDIに変換して流すデバイス
- そのデバイスから受けた情報をAudioに変換して鳴らすWebアプリ

というものなのですが、これ何で呼べば良いのかな？
デバイス？Webアプリ？音が出るのでこの組み合わせで「楽器」ということにしました。

さっそく制作方法を紹介します。
興味を持った方はぜひ制作してみてください。
多少電子工作ありますが、ちょこっとしか配線もないので簡単です！

### 微妙に反応は悪いので演奏用というより効果音用

先に言い訳。

MIDI→WebAudioで鳴らす構造からか、反応は悪いです。
ちゃんと「演奏」できる感じのデバイスではないですが、効果音には使えそう？ということで紹介します！

> MIDIよりもWebAudioの反応が悪い気がする。別途検証予定。

### 必要なモノ

- PC（Arduinoでソフト書き込んだり、Webアプリ動かしたりするやつ。USB端子がついてて、Arduino IDEとブラウザが動けばOKです。ブラウザはChromeで！）
- 3Dプリンタ（ケースを作らなければ不要！というか、別の方法で違うモノをケースにしてもいいかもです！）
- RP2040-Zero x 1（Raspberry Pi Picoとその互換機で代用可能ですがケースや配線は変わります）
- VL53L0Xモジュール x 1（いろんな形のやつが売ってます。[これと同じようなやつ](https://ja.aliexpress.com/item/1005006160604822.html?src=google&pdp_npi=4%40dis%21JPY%211245%21385%21%21%21%21%21%40%2112000036921177846%21ppc%21%21%21&src=google&albch=shopping&acnt=298-731-3000&isdl=y&slnk=&plac=&mtctp=&albbt=Google_7_shopping&aff_platform=google&aff_short_key=UneMJZVf&gclsrc=aw.ds&&albagn=888888&&ds_e_adid=&ds_e_matchtype=&ds_e_device=c&ds_e_network=x&ds_e_product_group_id=&ds_e_product_id=ja1005006160604822&ds_e_product_merchant_id=5085734345&ds_e_product_country=JP&ds_e_product_language=ja&ds_e_product_channel=online&ds_e_product_store_id=&ds_url_v=2&albcp=21796356199&albag=&isSmbAutoCall=false&needSmbHouyi=false&gad_source=1&gclid=CjwKCAiA6aW6BhBqEiwA6KzDc4BLNQ_K8WKzUf899yer9baJq05Lj9dZzpC62beLc46rsDYo27svAxoCQbQQAvD_BwE)が必要）
- 抵抗（4.7kΩ）x 2 （リード付きの1/6wのやつが小さくて工作しやすそう。頑張れる人はチップ抵抗でも!）
- 被覆つきリード線 5cmくらい x 4
- はんだごてとハンダ（まーなんでもいいです）
- 外部スピーカー（大きな音で鳴らさないならPCのスピーカーで十分）

#### ケース用の3Dデータ（ケース作る人のみ）

ケース用の3Dデータは下記からDLしてください。というか、TinkerCadなのでコピーすれば編集できます。
プリンターによってはサポートつけたりが必要になるので、適宜ご利用のプリンターに合わせてやっちゃってください。

私は光造形プリンターを使ってるので、斜めにしてサポートつけてからスライスしてます。

https://www.tinkercad.com/things/hK7G6r8iRW5-sensor-case-pub

#### Arduinoのファームウエア

RP2040-Zero用のファームウエアは下記に置いてあります。

https://github.com/tadfmac/laser-synth/tree/main/Arduino/RP2040-ZERO-VL53L0X

### A. センサーMIDIデバイスの制作

#### A-1. RP2040にファームウェアをアップロード

[Arduino IDE](https://www.arduino.cc/en/software) に下記ソフトウエアを追加してください。

- [Raspi Pico用 Core](https://github.com/earlephilhower/arduino-pico)
- [Adafruit TinyUSB](https://github.com/adafruit/Adafruit_TinyUSB_Arduino)
- [Adaftuit NeoPixel](https://github.com/adafruit/Adafruit_NeoPixel)

導入方法は各ソフトウエアのサイトを見ていただくとして、概要を書くとこんな感じです。
- 上記追加のソフトウエアをArduino IDEにインストールする。
- ボード設定で、RP2040 Coreの中から `Waveshare RP2040 Zero` を選ぶ
- USB Stackで`Adafruit TinyUSB` を選ぶ
- RP2040 Zeroをアップロードモード（BOOTスイッチを押しながら電源入れる）でPCに接続したあとに、スケッチをアップロードする

これでファームが書き込めます。

#### A-2. RP2040とVL53L0Xを配線

RP2040-ZeroとVL53L0Xモジュールを図のように配線してください。
ケースに入れる方は基板などは使わずケーブルで配線してくださいね！

![schematics.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31758/2930c9e5-a3bb-35d4-4f10-3d2750de13ab.png)

I2C接続なのでプルアップ抵抗が必要です。4.7Kはセンサー側につけましょう。

これで、MIDI Control Changeを送りまくるMIDIデバイスが動くようにはなりますが、センサーをうまく固定しないと使いにくいと思います。

#### A-3. ケースを制作（ケース作らない方は飛ばしてください）

3Dプリンターで [ケースデータ](https://www.tinkercad.com/things/hK7G6r8iRW5-sensor-case-pub)の出力を行なってください。

![img1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31758/7adfffbd-5a2f-fe0c-cf52-2e384eb95c77.jpeg)

3つの丸いパーツと、小さなパーツ（9個）が出力されたはずです。

写真のように、配線したRP2040を載せます。

![img2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31758/baad3cf5-8042-711e-25a8-f6cb62cdbde2.jpeg)


次に、中間用のパーツを上に重ねます。
小さなキャップは固定用のキャップです。重ねた時に飛び出した軸にうまく押し込んで固定してください。

![img3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31758/45faeeb5-5e47-25c0-f23f-57b583477bf7.jpeg)

最後に一番上のパーツを重ねて、中央にセンサーを固定すれば完成です。

### B. Webアプリの用意

Webアプリのソースコードは下記にあります。

https://github.com/tadfmac/laser-synth/tree/main/www

https://github.com/tadfmac/laser-synth を git clone or　zipダウンロード後展開後、

```
cd laser-synth
npm i
node ./app.mjs
```

その後、ブラウザで `http://localhost:3000` にアクセスすればOKです。

と、書きましたが、Webアプリのデプロイ先がないと開発用PCでしか遊べませんね。
下記にデプロイしていますので、アクセスして遊んでみてください。

https://capp.mz4u.net/lsr/

#### Webアプリの解説

さすがにDLして動かせ！だけだとひどいので、概要を簡単に。
見ての通り、画面はほとんど作ってませんので、音を鳴らすための方法など。

##### 1. MIDIの有効化

MIDIの有効化は `requestMIDIAccess()`を呼び出すと自動的にブラウザがユーザーに対してポップアップを出して利用許可を促してくれます。

`requestMIDIAccess()`は、`laser.mjs`の先で`pomidi.mjs`の中で呼ばれています。

これらのモジュールは下記のように役割分担しています。

- `laser.mjs` : 今回作った測距センサーデバイス専用のライブラリ
- `pomidi.mjs` : 汎用WebMIDIAPIラッパー

##### 2. デバイスの接続/外され検知

`laser.mjs`でデバイスの接続/外されの検知を行なっています。

Web MIDI API では、`statechange`イベントでポートの接続状態の変化検知を行うことができます。

この変化検知時にイベントで通知される`port`を走査して対象デバイスがあるかどうかを監視しています。

##### 3. WebAudioのresume

最初にやってることは「はじめる」ボタンをユーザーに押してもらうことです。WebAudioはユーザー操作がないと音を出すことができません。
昔はサイトにアクセスした瞬間から音が鳴っちゃうサイトがありましたが、さすがにそれは迷惑ですからね。

「はじめる」ボタンを押した時に、`AudioContext.resume()`を呼び出しています。これで音が出るようになります。

##### 4. 対象となるMIDIイベントが来たら音を鳴らす

作成したMIDIデバイスは距離情報をCC（コントロールチェンジ）80とCC81を使って流します。
このイベントが来たらコールバックする処理を `laser.mjs`で提供する `setOnTrig()`で設定しています。

`setOnTrig()`に設定する関数で、WebAudioを使って音を鳴らしています。
また、`setOnGone()`では、測距センサーの測定範囲外になった時にも呼び出すコールバックが指定できるようになっています。
このトリガーを使って、WebAudioの音の鳴動を止めています。


### C. テスト

Webアプリ起動したら、USBケーブルで制作したセンサーデバイスとPCを接続しましょう。

センサーに手をかざして音が鳴れば成功です！

[動画](https://youtube.com/shorts/BO9hq4pvm6U?feature=share)

### まとめ

- 微妙に反応は悪いがセンサーを使ってテルミンのような（？）音のなるデバイスというかWebアプリというかの制作方法をまとめました。
- 筆者は普段は真面目に仕事しています！（こんな記事ばかり上げてるから遊んでばかりだと思われそうなので言い訳！www）