LoginSignup
7
3

More than 1 year has passed since last update.

クロスプラットフォーム対応! MIDI経由のハードウエア制御システムをサっと作る

Last updated at Posted at 2022-11-30

Webからハードを操作する環境を素早くつくりたい!

Webエンジニアです!

普段仕事でプロトタイピングすることも多く、雑にWebからハードを操作するシステムを試作することも多いです。
今回紹介するのは、つい先日のお話です。

下記のような要件のシステム(プロトタイプ)を作りました。

  • 操作用のPCやスマホから別の場所にあるPCとそのPCに接続されたハードを制御する
  • ハードが接続されたPCはWindows PCの上で動作させる想定だがOSはLinuxにするかもしれない。さらに開発はMacで行いたい
  • PCとハードの接続にはUSBケーブルを用いる
  • ハードは制御コマンドに基づいて動く。コマンド体系はいくつかのコマンド+ON/OFF程度で単純
  • 数台作れれば良い。さくっと明日までにプロトタイプを1台つくれ。的なスピード感(実際は2-3日猶予がある)

sc1.png

まー、よくある話ですよね。
速度優先のプロトタイピングなので、下記のように作りました。

No. 機能 実装方法
1 操作用PC用アプリ Webアプリケーション。UIを提供。普通にHTML/CSS/JSで書く
2 ハードウエア USB-MIDIで制御コマンドを受信後ハードを制御する
3 HTTP API HTTPリクエストはnode.js+expressで処理する
4 MIDI Client APIが叩かれたらMIDIメッセージに変換してデバイスに送信する

sc2.png

最終的には、4. の実装方法が少し上記図から変わりました。

1. 操作用PC用アプリ

これは普通のWebアプリです。UIを提供してボタン等を押すとAPIを呼び出します。
UIも単純なのでHTML/CSS/JSで普通に書くだけでしたので、説明は割愛。

2. ハードウエア

ハードウエアのIF部分をUSB MIDIに対応させるため、Arduino環境で開発可能なマイコンを利用しました。

ArduinoでUSB MIDIデバイスを作るというのも、豊富なOSSのおかげで簡単です。
主要なマイコンとライブラリの組み合わせを書いておきます。

No. マイコン ライブラリ 参考情報
1 Arduino UNO Moco LUFA 導入方法
2 Arduino micro/Arduino Leonardo MIDIUSB 使い方
3 Raspberry Pi Pico/Seeed XIAO RP2040 Adafruit Tiny USB /examples にMIDIの例がある
4 ESP32 --- USB機能が無いのでUSB-MIDIデバイスには向かない。Bluetooth MIDI Deviceであれば作れる
5 Arduino Pro mini/Arduino Nano --- USB機能が無いのでUSB-MIDIデバイスには向かない

プロジェクトでは、上記2と3を利用しました。

ボード入手できれば(ATmega32u4の開発ボードであれば概ね同じように使えます)、一番簡単なのはたぶん 2. ですが、2022年12月現在 半導体不足や円安の影響でATmegaの価格が高騰しており、互換ボードの入手性もかなり悪くなっています。多少環境にクセはありますが、現時点で入手性の良いRP2040搭載ボードにも保険で対応しておくことにしました。

MIDIメッセージで伝えられたコマンドに基づいて、ハードウエア(例えばサーボモーターなどのアクチュエータ)を制御することになりますが、ここでは詳細は割愛します。

3. HTTP API

最近は FastAPI に押され気味な気もしますが、慣れてるから!という理由で、node.js + express を採用しました。こちらも特に書くことはないので、リンクだけ置いておきます。

4. MIDI Client(node.js から MIDIを呼ぶ方法)

node.js から MIDI を扱うライブラリはいくつかありますが、今回実は採用しませんでした。
その理由を共有するのが本記事の目的です。

早くいろいろ治れば嬉しいな!

4-1. node-midi

node.js の npm モジュールで一番最初に見つけたのがコレです。
RtMidiというクロスプラットフォームMIDI実装のnode.jsラッパーです。

OSX / Windows / Linux に対応していて素晴らしいのですが、ちょっと依存環境が多くインストールになかなかに苦労します。

https://www.npmjs.com/package/midi
にさらりと記載されている依存関係は下記の通りです。(そのまま引用)

OSX
- Some version of Xcode (or Command Line Tools)
- Python (for node-gyp)

Windows
- Microsoft Visual C++ (the Express edition works fine)
- Python (for node-gyp)

Linux
- A C++ compiler
- You must have installed and configured ALSA. Without it this module will NOT build.
- Install the libasound2-dev package.
- Python (for node-gyp)

開発環境だけに入れれば良いわけではなく、実行環境でもこれらの環境を作る必要があります。
npm i midi 一発で環境構築できないのは辛いので、Windows環境でのインストールに手こずった段階で、今回は見送りました。
(これ普通にLinuxでも苦労するだろ。やってないけど。というのもあり。)

ようするに、各プラットフォーム用のビルド環境が必要なので Winodwsの場合は npm install --global --production windows-build-tools でビルドツールを事前に入れておけばOKのようです。

4-2. webmidi

まさかの node-midi 不採用でもう一度 npm を探して次に見つけたのがこちら webmidi
です。

各種ブラウザとnode.jsもマルチプラットフォームに対応しています。

- GNU/Linux
- macOS
- Windows
- Raspberry Pi

ブラウザの対応状況からブラウザ版はWeb MIDI APIのラッパーかな?と予想しますが、node.js 版は下記記載の通り、JZZに依存しているようです。

Support for the Node.js environment has been made possible in large part by the good folks of Jazz-Soft via their JZZ module.

JZZがどうやら Web MIDI API の node.js 版 Polyfillのようです。なるほど。そっち使ってもいいな。

で、これを使おうとしたんですが、なぜか動かない!(Macbook Air 2021 M1 Ver.で開発しています)
JZZの方も動かない!

いろいろ調べたら原因はコレ。

var path='./bin/';
var v=process.versions.node.split('.');
if (v[0]<=10) path+='10_15/';
else if (v[0]<=11) path+='11_15/';
if(process.platform=="win32"&&process.arch=="ia32") path+='win32/jazz';
else if(process.platform=="win32"&&process.arch=="x64") path+='win64/jazz';
else if(process.platform=="darwin"&&process.arch=="x64") path+='macos64/jazz'; // <- ? arm版がない!
else if(process.platform=="linux"&&process.arch=="x64") path+='linux64/jazz';
else if(process.platform=="linux"&&process.arch=="arm") path+='linuxa7/jazz';
module.exports=require(path);
module.exports.package=require(__dirname + '/package.json');

JZZやwebmidiが依存してる jazz-midiがまだ Appleシリコンに対応しておらず、Appleシリコン版のバイナリがないようです。残念!

開発者の開発環境で動かないモノは非採用!ということで。

4-3. 最終手段!ブラウザを使う。

今回残念ながら、node-midiwebmidiJZZを見送ることになりました。

大丈夫か? node.js!(少し不安に。。。)

あまりこの手は使いたくなかったのですが、非常に簡単に導入できて、クロスプラットフォームなMIDI環境があります。そう。それはChrome(と共通のコアを使ったブラウザ群)。

下記のように対応しました。

  1. WebSocketメッセージを受信したらWeb MIDI API を使ってMIDIメッセージを送信するWebページを作る。
  2. node.js の API 起動時に WebSocketサーバーも起動しつつ、その後 puppeteer を使い、1. の Webページを表示しておく。
  3. expressでHTTPリクエストが来たら、WebSocketで 1.のページにWebSocketメッセージを送る。

sc3.png

APIの先でWebページに連携という面白アーキテクチャですが、npm i 一発で環境構築できます。最高。

注意点 1. ヘッドレスモードでの Web MIDI API の制限

Web MIDI APIは Chromeのheadlessモードだとそのままだと動作しません。
puppeteer を用いたヘッドレスブラウザ起動時に対策が必要です。

動かないコード

.js
  browser = await puppeteer.launch({headless: true, args: ['--no-sandbox']});
  page = await browser.newPage();
  await page.goto('http://yourdomain/sample/sample.html');

このコードは、navigator.requestMIDIAccess()に失敗するようです。

下記のようにヘッドレスモードでなく普通にブラウザを起動すると動作します。

.js
  browser = await puppeteer.launch({headless: false, args: ['--no-sandbox']});
  page = await browser.newPage();
  await page.goto('http://yourdomain/sample/sample.html');

が、ブラウザが常に起動/表示されているのはかっこ悪いです。

MIDIが動かないのはヘッドレスモードでの動作を意図的に制限していることが原因とわかりました。

ドメインにpermissionをつけてあげれば動くようです。
改良したコードは下記のようになりました。

動くコード

.js
  browser = await puppeteer.launch({headless: true, args: ['--no-sandbox']});
  await browser.defaultBrowserContext().overridePermissions('http://yourdomain', ['midi']); // <-追加
  page = await browser.newPage();
  await page.goto('http://yourdomain/sample/sample.html');
注意点 2. Windowsは 複数のアプリから同時にVirtual MIDIポートがオープンできない

これ、ユースケースによっては結構致命的だと思うんですけど、Windowsの場合そういうもののようです。

例えば、Chromeで Web MIDI API を使うサイトを開いてる状態で、他のブラウザでも Web MIDI API を利用しようとしたら失敗するようです。

RtMidi などブラウザ以外のMIDIポートオープンでも同様の問題が発生するので、OSの仕様? なんでしょうか?

MidiOutWinMM::openPort: error creating Windows MM MIDI output port.

上記のようなエラーメッセージ(上記は RtMidiの例)を見つけたら、他にMIDIポートをつかんでるアプリケーションが無いか?チェックしてみる必要がありそうです。

まとめ

MIDIを使ってWebからハードを操作する環境を作りました。
ただ、今回最初想定した実装とは少し違う結果になりました。

  • MIDIを使えば手軽にUSBを使ったPC→ハードウエア制御が実現できる(ただし簡単なコマンド体系のみ。大容量データの伝送等には向かないので、ユースケース次第)
  • 2022/11/30現在、MIDIのクロスプラットフォーム環境で一番手軽なのはブラウザ(chrome)
  • ブラウザはサーバーのようなヘッドレス環境でも利用可能

おまけ

Web MIDI API を使ったWebアプリを超手抜きで作ってみたい人向けラッパー(ブロードキャストがデフォルト動作という曲者)

7
3
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
7
3