0

Arduino MicroをUSB MIDIデバイスにする

MIDIデバイスを自作し、ブラウザのWeb MIDI APIと通信した。
rkistner/arcoreを使ったら簡単にできた。

もともとLeonardo以降のATMega32U4を使っているArduinoはMPU内にUSB機能が内蔵されていて、その部分もプログラマブルなのでキーボードやマウスにしたりとかできる。

セットアップ


% git clone git@github.com:rkistner/arcore.git
% cd arcore/
% ./install.sh

~/Documents/Arduino/hardware/arcore にシンボリックリンクが貼られる。Arduino IDEを再起動するとターゲットボードが増えてるので選択する。



プログラムを書く


analogReadでセンサーを読んでMIDI channel1にコントロールメッセージを送る。analogReadは10bitだがMIDIデータは7bitなので下3bitを捨てる。
MIDIEventの0x0Bはコントロールメッセージという意味。0xB0は10110000で下4bitの1〜16でMIDIチャンネルを表すので論理和する。(0から始まるので、この場合チャンネル2になる)
3、4番目の引数は7bit(0~127)ならなんでもいい。
void setup() {
}

int an;

void loop() {
an = analogRead(0);
if(MIDIUSB.available()){
MIDIEvent e = {0x0B, 0xB0|1, 10, an>>3};
MIDIUSB.write(e);
}
}

ChromeのWeb MIDI APIがちょうどコントロールメッセージをonmessageイベントで受信できるので、ブラウザからMIDIデバイス化したArduinoのセンサーが読めるようになった。
http://shokai.github.io/WebMidiAPIStudy/dump/

関連:AndroidタブレットでWeb MIDI API


USBシリアル通信と併用もできる。ブラウザにMIDIデータが流れるし、同時にArduino等のシリアルコンソールを開くとそっちにも数値が流れてくる。
void setup() {
pinMode(13, true);
Serial.begin(9600);
}

int an;

void loop() {
an = analogRead(0);
if(MIDIUSB.available()){
MIDIEvent e = {0x0B, 0xB0|1, 10, an>>3};
MIDIUSB.write(e); // MIDI送信
}
Serial.println(an); // シリアル通信からもAD変換器の値を送ってみる

// 動作確認用にLEDを点滅させる
digitalWrite(13, true);
delay(100);
digitalWrite(13, false);
delay(100);
}

0

MIDIデバイスを演奏したらパスワードが入力されるChrome拡張を作った

最近のChromeはWeb MIDI APIといってMIDIキーボード等の楽器を接続し、その入力が取れる。

Chrome拡張でもそれは使える。
inputタグにフォーカスしている時に楽器からの入力をキャプチャして、パスワードを生成&入力するChrome拡張を作ってみた。

https://github.com/shokai/webmidi-password-generator

パスワードは覚えるのが難しいけど曲のフレーズとかなら覚えやすいだろ、多分・・という気持ちを形にした。

といっても見た目に派手な動きがあるわけじゃなくてUSBコネクタにMIDIデバイス刺して演奏したらいきなりinputタグにパスワードが入力されてる状態になるので、blogで説明は難しい。

こんな感じでMIDIメッセージを受信してパスワードっぽい文字列が生成される。2回演奏して、ちゃんと同じパスワードが出てる。

これの問題はあらかじめパスワードを記憶させておいて取り出せるわけじゃないので、28c813172158みたいな到底覚えられない文字列になってしまう事で、作りながらうすうす気づいてたけどまあ自分でも使う気になれない。ビミョーすぎる。
パスワード覚えなくていいソリューションを作ってもパスワード覚えること前提のサービスやデバイスが世の中にたくさんあるから利便性の面でどうしても無理がある。使うなら完全移行しないとならないのが面倒くさい。

MIDIのメッセージは標準化されているものだから、別のMIDIキーボードと別のPCで同じフレーズを演奏したらちゃんと同じパスワードが入力できる、という点は良いとは思う。


久しぶりにChrome拡張を作ったら簡単だった

なんだか以前作った時より簡単になった気がする。
あまりしっかり差を把握していないんだけど、以前はもっとゴチャゴチャ色々なファイルを作らなければならなかったような。

Chrome拡張が簡単になったというより、browserifyでnpmのライブラリやnode中でpure jsで書かれているライブラリをそのままChrome拡張に持っていけるのですっきりしたのかも。

MIDIデバイスを開いて入力読んで現在ブラウザで開いているHTMLを少し書きなおすぐらいなら、
簡単なmanifest.jsonを書いて、その中で指定したjavascript1つに全部やらせればいい。

ようするにgreasemonkeyでやってたような事はmanifest.jsonとmain.js(と必要であればアイコン画像)さえ配置すればさくっとできる。browserifyのおかげでグローバル汚染せずにjQuery等を持ってくる事もできる。

こんな感じで書いてる。
npmでインストールしたjqueryと、Web MIDI APIからパスワード作って”password”イベントを発火してくれるライブラリをbrowserifyで1ファイルに固める。

main.es6
"use strict";
import PasswordGenerator from "./password_generator";
import $ from "jquery";

var target = null;
$("input")
.on("focus", function(e){
target = e.target;
})
.on("focusout", function(e){
target = null;
});

var passgen = new PasswordGenerator(8);
passgen.on("password", function(pass){
console.log(pass.raw);
console.log(`password => ${pass.string}`);
if(target){
target.value = pass.string;
}
});

Web MIDI APIはインタフェースにPromise、iterator、TypedArrayなどのES6の機能が使われている。ChromeでもES6が動くのでES6でいきなり書けばいいかなと思ったけど、Arrow Function等いくつかのES6の機能が動いたり動かなかったりして混乱したのでbrowserify+babelifyでES5に変換した。


Chrome拡張のインストール

拡張は昔は1ファイルに固めないと動かなかった気がするけど、最近は[ウィンドウ]→[拡張機能]→[パッケージ化されていない拡張機能を読み込む]でディレクトリを指定して読み込んでくれるので手軽。

0

ブラウザ/NodeでKORG nanoKONTROL1/2を使えるライブラリをES6で作った

作った。

https://www.npmjs.com/package/korg-nano-kontrol

% npm i korg-nano-kontrol -save

ブラウザならWeb MIDI APIで、Nodeならmidi npmでMIDIコントローラと通信するようになっているのでrequireすればあとは適当に動く。
あと、最初coffeeで書いて全部完成したんだけどbabel(ES6)で書き直してみた。というのもES6は前々から使ってみたかったんだけど、1から機能を1つずつ勉強するのは苦手なので、既に慣れてるcoffeeの機能をフルに使ってある程度の大きさの物を実装してからES6で書きなおす事で色々な機能を一気に使わざるを得ない状況を作って勉強してみた。

KORG nanoKONTROLとは

EdisonとMIDIコントローラでHueを調光する
とか
Node.jsとMIDIコントローラでHueを調光する
で使っている便利なMIDIコントローラー。ツマミとスライダーがたくさん付いている。

もともとkorg-nanoというライブラリがある

んだけど、nanoKONTROL2に対応していなかったのと、修正しようにも2つのデバイスに対応するなら1から作ったほうが早そうな設計だったのでついでにWeb MIDI APIにも対応させて作ってみた。

使いやすかったのでAPIはほぼそのままにしてある。

使う


connect()するとPromiseが返ってくる
var nanoKONTROL = require('korg-nano-kontrol');

nanoKONTROL.connect()
.then(function(device){
console.log('connected!' + device.name);
// do something
})
.catch(function(err){
console.error(err);
});

で、

device.on('slider:0', function(value){ // スライダー0番を取得
console.log("slider:0 >>> "+value);
});
とか
device.on('knob:*', function(value){ // 全ノブ取得
console.log(this.event+' => '+value);
});
とかすると取れる。



Web MIDI API

ブラウザでもBrowserifyで固めれば同様に使える



EventEmitter2

EventEmitter2にはワイルドカードでイベントを受け取れる機能がある。上でdevice.on(“knob:*”, func)とかやってるのがそれ。

ES6ではclass nanoKONTROL2 extends EventEmitter2して、コンストラクタの中でsuper({wildcard: true, delimiter: “:”})したら有効になった。


coffeeからbabel(ES6)に書き直した

簡単に感想を列挙すると

coffee-scriptよりは機能が少ない。
coffeeのfor k,v of objでkey/valueを回せるのと、do (arg) ->でクロージャ即時実行と、添字付きfor文と後置if文が使いたい。
でもfor ofはES6組み込みクラスのMapをイテレータでfor [k,v] of Mapすればいいし、doはletがあれば必要ない。

unlessがほしい。ifに!つけるのつらい。
関数宣言する時にexport付ければexportできるのは便利。
テンプレート文字列もよい。
[3…8]みたいなのはlodashの_.range(3,8)で代用できる。
関数のデフォルトパラメータ指定便利。
classは違和感なく書ける。getter/setterやstaticの定義も便利。
for(let i of array)を使うとnode 0.12やChromeでは動くけど0.10やSafariでは動かないJSができる。Symbol is not definedエラーが出る。

coffeeで書くのに比べてコード量は1.3倍ぐらいになる。
でもまあ、node 0.12以上で動かすならどっちで書いてもいいかなという感じがした。
古い環境をターゲットにするならcoffeeの方が安心感がある。

ESLint

lintツールにESLintを使ってみた。coffee-lintより色々しっかり見てくれてる気がする。

.eslintrcを置くと全ルールがカスタムできてよい。

parserにbabel-eslintを指定するとES6がlintできる。


gruntやめてnpm runにした

参考:Grunt/Gulpで憔悴したおっさんの話 – MOL

grunt-babelが複数ファイルの変換設定がかっこよく書けないのと、gulpがやはり理解不能なのでコンパイルはbabel –watchでいいやと思った。

parallelshellで複数コマンド同時実行できるので、esw(eslint-watch)も一緒に実行してる。

あと、npm2.0からnpm run コマンド名 — 追加の引数という感じに–を挟むと引数を追加して実行できるので、babelのビルド設定をそのままに–watchを追加して実行できてよい。

watchという続けて書いたコマンドをファイル更新監視して実行してくれるnpmもあるんだけど、なんか反応が遅いのでeslint-watchとbabel –watchで良いやとなった。これだとEdison上でもサクサク動いて良い。
package.json
  "scripts": {
"test": "eslint src/*.es6 src/*/*.es6",
"build": "babel src/ --out-dir lib/ --source-maps inline",
"watch": "parallelshell 'npm run build -- --watch' 'esw src/ --watch'",
"buildSample": "browserify --debug samples/webmidi/src/main.js -o samples/webmidi/dist/bundle.js"
},

0

AndroidタブレットでWeb MIDI API

前:Node.jsとMIDIコントローラでHueを調光する

音楽やる気はないけどMIDIデバイスは入出力デバイスとして面白い。スライダーとかツマミとか色々あるし、MIDIで制御できるドラム叩きロボットとかもある。
とりあえず電子工作なしで買ってきたデバイス接続するだけでPCでもスマホでもWebブラウザから使えるというのは面白い。


Web MIDI API

Web MIDI APIはブラウザでMIDIデバイスが使えるHTML5のAPIで、現在はGoogle Chromeだけが対応している。
chrome://flags でWebMIDIを有効にし、MIDIデバイスを接続してからChromeを再起動する必要がある。
Chrome起動後にMIDIデバイスつなげても認識しないので注意。


試した

http://shokai.github.io/WebMidiAPIStudy/


Android版Chromeも対応しているので、USBホストケーブルでNexus7に接続して試してみた。
流れてきたデータを全部表示するだけだけど、Nodeのmidi npmで試した時とほぼ同じ手順でMIDIデバイスに接続できる。流れてくるデータもnode版はArrayでWebMIDIはUInt8Arrayなだけなので、MIDIデバイス用のラッパーライブラリ作るとしたらブラウザ・Nodeの両対応は簡単そう。



Mac版Chromeでも動く。Android版では取得できなかったデバイスのmanifacturerとnameプロパティがちゃんと取れた。


実装

https://github.com/shokai/WebMidiAPIStudyにある。

requestMIDIAccessはPromiseで使う
return if typeof navigator.requestMIDIAccess isnt 'function'

navigator.requestMIDIAccess()
.then (midi) ->
return new Promise (resolve, reject) ->
console.log "#{midi.inputs.size} input devices"
console.log "#{midi.outputs.size} output devices"
it = midi.inputs.values() # inputデバイスのリストがiteratorでくる
loop
input = it.next()
break if input.done
return resolve input.value # デバイス発見した
reject "input device not found"

.then (input) ->
console.log "Input Device found: #{input.manufacturer} - #{input.name}"
input.onmidimessage = (msg) ->
console.log msg.data # UInt8Arrayでデータが読める

.catch (err) ->
console.error err


気づいたこと


充電できない

AndroidではMIDIデバイスを使っているとUSBポートが埋まるので充電できない。Qiでなら充電できるかと思ったけどNexus5とNexus7ではMIDIと同時に充電は無理だった。


複数アプリから1つのMIDIデバイスを使える

Macでは複数のブラウザウィンドウで同時に1つのMIDIデバイスからのデータが読めた。またブラウザと同時にNode.jsからも使えた。
Macでは /System/Library/Frameworks/CoreMIDI.framework/MIDIServer が仲介しているっぽい。


参考になった