本資料は2019/11/22 株式会社ウフルにて開催された「TwitterとMIDI/MQTTを組み合わせて自動作曲してみようハンズオン」の資料です。
https://enebular.connpass.com/event/144948/
今回作るもの
Twitterの特定のハッシュタグを取得して、内容をもとにメロディを自動で作曲してくれる装置を作ってみましょう!
こちらが実際のデモ動画です。(今回はもう少し簡単なものを作ります。)
明日の #IoTLT で発表予定のハッシュタグのツイートの勢いで自動で作曲する機械の動画です。ご査収ください。 pic.twitter.com/ZCHaG7tYMD
— takayama.k (@pco2699) August 26, 2019
システム構成
事前準備
Node.jsなどその他のライブラリのインストール・APIへのサインアップは本資料を参考にしてください。
ハンズオンの流れ
- MIDI/MQTTでリモート演奏できる仕組みを作る
- enebularとつないでみる
- Twitterで自動作曲してみる
MIDI/MQTTでリモート演奏できる仕組みをつくる
まずは、構成の中でMIDI/MQTT部を作りましょう。
この図を更に詳しく説明すると次の通りです。
MQTTブローカーと呼ばれる仲介サーバーを通して、シンセとenebularを接続してリモートで演奏できるようにします。
シンセ
今回は環境構築手順を簡単にするためWeb上のシンセを使います。
Chromeで↓のページを開いてください。
https://webaudiodemos.appspot.com/midi-synth/index.html
動作確認しよう
106.jsとLoopMidi(Windowsのみ)とPocketMIDIを立ち上げて
必要な設定を行います。
PocketMidi
PocketMidi
PocketMidi -> OutputPort -> LoopMidi Port or 先程設定したポート名
Pocket MIDI上の鍵盤を叩いて音が鳴ればOKです!
midi2mqtt
事前にインストールしたmidi2mqttに必要な引数を与えて、実際に立ち上げてみましょう
$ midi2mqtt -i "loopMIDI Port 0" -o "loopMIDI Port 1" -u "mqtt://cmxwoqlw:XXXXXX@m16.cloudmqtt.com:16282" -t "pco2699/midi"
(XXXXXXはパスワードのため隠してあります。)
-i -> インプット用のMIDIポート
-o -> アウトプット用のMIDIポート
-u -> MQTTサーバの情報
-t -> (任意の名前(Twitterのアカウント名とか))/midi にしてください
Macの場合
midi2mqtt -i "IACDriver 1" -o "IACDriver 2" -u "mqtt://cmxwoqlw:Mm3nZNSYHcpp@m16.cloudmqtt.com:16282" -t "pco2699/midi"
以下のコマンドでMIDIインプット/アウトプットポートを確認することができます。
外部のシンセを利用する場合は、このコマンドで、オプション -i
、オプション -o
をそれぞれ適宜自分のインプットポート、アウトプットポートに書き換えましょう。
$ midi2mqtt
2019-11-23 14:24:51.399 <info> midi2mqtt 1.1.0 starting
2019-11-23 14:24:51.405 <info> mqtt trying to connect to mqtt://127.0.0.1
2019-11-23 14:24:51.488 <info> Available MIDI inputs: [ 'loopMIDI Port 0' ] -> MIDIインプットポート
2019-11-23 14:24:51.493 <info> Available MIDI outputs: [ 'Microsoft GS Wavetable Synth 0', 'loopMIDI Port 1' ] -> MIDIアウトプットポート
```cmd
以下の形でコマンドが出力されれば問題なく動いています。
λ midi2mqtt -i "loopMIDI Port 0" -o "loopMIDI Port 1" -u "mqtt://cmxwoqlw:XXXXXX@m16.cloudmqtt.com:16282" -t "pco2699/midi"
2019-04-21 18:14:21.456 midi2mqtt 1.1.0 starting
2019-04-21 18:14:21.463 mqtt trying to connect to mqtt://cmxwoqlw:XXXXXX@m16.cloudmqtt.com:16282
RtMidiIn::cancelCallback: no callback function was set!
2019-04-21 18:14:21.550 Available MIDI inputs: [ 'loopMIDI Port 0' ]
2019-04-21 18:14:21.552 Available MIDI outputs: [ 'Microsoft GS Wavetable Synth 0', 'loopMIDI Port 1' ]
2019-04-21 18:14:21.917 mqtt connected mqtt://cmxwoqlw:XXXXXX@m16.cloudmqtt.com:16282
2019-04-21 18:14:21.918 mqtt subscribe pco2699/midi/in/+/+/+
```
これでPocketMIDIのキーボードを叩いて、前のmother32から音が鳴ればリモートでの演奏環境が完成です。
-t の後の文字列(トピック名と言います。)はあとで使うのでメモっておいてください。
補足説明: MQTTとは
enebularとつないでみる
さきほどのMIDI/MQTTをenebularとつないでみましょう
https://enebular.com/
お手本flowをDiscoverで取り込んでみよう
今回つくるものを事前に私がDiscover機能という機能で公開してあります。
それを、取り込んでみて、音が鳴るか確認してみましょう。
プロジェクトの作成
Project名 -> midibular (その他適当な名前でOK)
Discoverで取り込み
Projectが出来たら、以下のリンクをクリックしましょう。
https://enebular.com/discover/flow/be605e0c-c316-4c72-a7ab-07bb34bf7a4d
クリックしたら次の画像の通り、自分のプロジェクトに取り込みましょう。
MQTTブローカーのユーザ名やMQTTトピック情報を設定する
flowを取り込んだだけだと、現状は動きません。
MQTTブローカーのユーザ名などの情報が消えてしまっているためです。
そのため、ユーザ名を設定しましょう。
(変更すべき箇所はすべてflowにコメントしてあるので、それを参考にしても問題ありません。)
MQTTブローカーのユーザ名・パスワードの設定
まずはMQTTブローカーのユーザ名・パスワードを設定します。
以下をダブルクリックしましょう。
ユーザ名・パスワードの以下の通り設定して「更新」をクリック。
ID: cmxwoqlw
PW: ハンズオン当日にお知らせします
MQTTのトピック名の変更
取り込んだフローだとMQTTのトピック名がすべて pco2699/midi
に設定されています。
これだと、私のローカルに設定されているシンセが鳴ってしまいます。
そのため、トピック名を自分のものに合わせて設定します。
先程と同じノードをクリックします。
トピックと書いてある箇所にpco2699/midi
と記載されているので、自分がmidi2mqtt
で設定したものに書き換えましょう。
他にもトピック名がいろいろな所に書かれているので、次のコメントが記載されているノードをすべて書き換えましょう。
すべてのトピック名の変更が終わったら次のinjectを教えてみてください。
音が鳴ったらここまではOKです。
flowの一番下にある「Twitterのツイートから自動作曲するフロー」は後で動かします。
音を鳴らすシンプルなflowをつくる
音を鳴らすシンプルなenebularのflowを作って、今回の仕組みの基本を理解しましょう。
プロジェクトを一から作成する
こんな感じで、enebularのflowエディターが立ち上がればOKです。
MQTT/debugノードを使って、ローカルから送出したMIDI情報を見れるようにする
まずは、MQTTノードとdebugノードを使ってローカルから送出したMIDI情報を見れるようにしましょう。
mqtt -brokerを追加のよこのペンマークを押す
情報は以下の通り入力
サーバ: m16.cloudmqtt.com
ポート: 16282
ID: cmxwoqlw
PW: 当日に教えます
トピック名: {設定したユーザ名}/midi/out/+/+/+
設定が完了したら、出力 -> debugとつないでみましょう
これで右上をデプロイを押して動作確認しましょう
PocketMidiを押して、↓のような形でメッセージが出れば連携ができてます!
MQTT/injectノードを使って、enebularから音を出してみる
今度は逆にenebularから音を出せるようにしてみましょう
入力側はinjectノードを利用します。
ペイロードはとりあえず0-127までの数字を入れてください。
(数字じゃないと音がでない)
出力側にmqttを設定します
サーバ: さきほど設定したMQTTサーバ
トピック: {ユーザ名}/midi/in/0/noteon/60
noteonで音がなり始めます、これだと音がなり続けてしまうのでnoteoffも用意します。
MQTTノードをCtrl+C Ctrl+Vでコピーできるので、コピーしてください。
トピックは↓
トピック: {ユーザ名}/midi/in/0/noteoff/60
同じメッセージを時間差で送るためにdelayノードを用意します。
これでデプロイしてみて、injectノードをクリックして音が鳴ればOKです!
topic: {midi2mqttの起動時に設定したtopic}/in/{midiチャンネル}/{noteon|noteoff}/{音の高さ}
msg.topic -> 音の高さ
msg.payload -> 音のベロシティ
を設定してるイメージです!
Twitterで自動作曲してみる
enebularとMIDI/MQTTを使って音を鳴らすことができました。
次はいよいよTwitterと連携させて自動作曲してみましょう。
Discover機能で取り込んだお手本flowを動かす
先程、取り込んだお手本flowのTwitterで自動作曲するflowをまだ動かしていませんでした。
まずはこちらを動かしてみましょう。
TwitterのAPIキーの設定
事前準備を実施していればTwitter APIの「Consumer API Keys」、「アクセストークン情報」が準備できているはずです。
https://developer.twitter.com/en/apps
[Details] -> [Keys and Token]
このAPIキーをお手本flowに設定しましょう。
お手本flowを再び、開いてください。
次の通り、Twitterのノードをダブルクリックしてください。
Twitter ID: 自分のTwitter ID
その他は、説明欄に従って設定しましょう。
取得ツイートの検索設定
Twitterの取得ツイートの検索設定をしましょう。「フォローしているすべてのユーザ」や「公開されているすべてのツイート」などを選べます。自分の好きなものを設定しましょう。
ただ、ツイート数が多いと動作確認しづらいので、最初は自分のアカウントのツイートだけを検索対象にすることをおすすめします。
その他、トピック名の変更をしていない方はトピック名の変更もしておきましょう。
これで、ツイートして自分の手元で音が鳴ったら成功です!
一からTwitterの自動作曲flowを自作する
それでは、一からTwitterの自動作曲flowを自作してみましょう。
さきほど、簡単な音を鳴らすflowに追加します。元の簡単な音を鳴らすflowを開きましょう。
Twitterノードでツイートを収集する
まずは、Twitterノードを追加します。
APIキーの設定は先程と同様の方法で設定しましょう。
APIキーの設定ができたら、次の通り繋いで、ツイートが流れるか確認しましょう。
右のメニューからデバッグを開いて、ツイートが流れてくればOKです。
splitノードでツイートを文字単位で分割する
取得できたツイートを元に、つぶやかれた一文字一文字を音にマッピングします。
そのためにまず、一つのメッセージにまとまっているツイートを複数のメッセージに分割します。
メッセージの分割には splitノードを利用します。
splitノードをドラッグして配置しましょう。
そして、splitノードを次の通り、設定します。
文字列/バッファ 分割: 固定長, 1
メッセージをストリームに分割: on
設定できたら、次の通りつないでみましょう。
デバッグを開くと一文字ずつ文字が分割されているのが確認できるはずです。
delayノードで分割したメッセージをちょっとづつ送信する
今のままだと、メッセージが一気に音になってしまうので、メロディになりません。
そのため、メッセージをちょっとづつ送ることで、メロディが鳴るようにしましょう。
メッセージをちょっとづつ送るには delayノード を利用します。
delayノードをドラッグして配置しましょう。
delayノードを次の通り、設定します。
さきほどと同様にdebugノードをつないで、動作確認してみましょう。メッセージがちょっとづつ送られるようになっていたらOKです。
functionノードで文字を数字に変換する
ツイートの文字を数字に変換します。数字に変換できれば、MIDIの音の高さにマッピングすることができます。
ツイートの文字を数字に変換するには、 functionノード を利用します。
functionノードは、JavaScriptを記述できるノードです。
ここの中に少しだけJavaScriptのコードを書きます。
functionノードの中身は次のように書きます。
var p = msg.payload;
msg.value = p.charCodeAt(0) % 6
return msg;
やっていることはシンプルで、ツイートの文字を charCodeAt(0)
で文字コードとして取得、それを12で割った余りを msg.value
に設定しています。
これで msg.value
には 0~5の間の数字が文字に応じて設定されるようになります。
この msg.value
はのちほど利用します。
changeノードでスケールを設定する
さきほど、functionノードで文字を数字に設定しました。
こちらをそのままMIDIとして鳴らしてしまっても良いのですが、鳴らすと奇怪な旋律になってしまいそれっぽく聞こえません。
そのため、スケールと呼ばれる決められた音の高さの集まりを設定しておいて、それを先程のfunctionノードで計算された数字で選ぶ形にすると
旋律っぽく聞こえるようになります。
まず、このスケールを changeノード で設定しましょう。
changeノードはメッセージに任意の値を設定するのに使います。
対象の値という入力欄でスケールの配列を設定しています。
ちなみに今回は、Cマイナーペンタトニック・スケール というスケールを設定します。
これでスケールの配列がmsg.payload
に設定されました。
もし、興味がある方は、以下の資料を参考にスケールを調べてみて自分の好きなスケールを設定してみてください。
音階 - Wikipedia
MIDIノート番号と音名、周波数の対応表 - asahi-net.or.jp
changeノード(ふたたび)で設定したスケールから一音選ぶ
changeノードをまた利用して、functionノードで計算した数字でスケールの中から一音選びましょう。
changeノードは、任意の値を設定するだけでなく、配列から値を取得するのにも使えます。
同じようにchangeノードをドラッグしてきて次のように設定しましょう。
対象の値のところをクリックして「expression」を選ぶのを忘れないでください。
payload[$$.value]
はmsg.payload
にあるスケールの配列をmsg.value
に設定されている数字をindexとして選択する、という式です。
これでfunctionノードで計算された数字を元に、スケールの中から一音を選ぶことができます。
templateノードで、音の高さをトピックに設定する
さきほどの音を簡単に鳴らすflowで説明しましたが、音の高さを設定するには、音の高さの数字だけでなくpco2699/midi
のようなトピック名をmsg.topic
に設定する必要があります。msg.topic
に音の高さの情報とpco2699/midi
というトピック名をまとめて設定しましょう。
msg
中に設定されている情報を何か固定の文字列に当て込みたい場合には、templateノードを利用します。
templateは次の通り設定します。
テンプレートの文字列の最初の部分は、自分自身のトピック名にするのを忘れないでください。
changeノードで音のベロシティを設定する
msg.payloadに音のベロシティ(強さ)を設定しましょう。
数字は0-127の間であればなんでも大丈夫です。数字が大きければ音が強く、少なければ音が弱く鳴ります。
MQTTノードでMQTTに情報を流す
ここまでで必要な情報がメッセージ中に含まれているので、これをMQTTに流して、ローカルで音が鳴るようにしましょう。
MQTTに流すには、先程も利用したMQTTノードを利用します。
先程の音を簡単に鳴らすflowでは「トピック」に値を設定していましたが、今回はそこは空にします。
msg.topic
にすでにトピックの値が設定されているからです。
音をOFFにするflowもつくる
ここまでで完成...!と言いたいところですが、このflowだと、音をONする情報だけ(noteon)で、音をOFFにする情報が含まれていません。
これだと、ツイートするとずっと音が鳴りっぱなしになってしまいます。そのため、音をOFFにする(noteoff)flowも追加します。
といっても、ほとんどコピペでできます。
まず、いままで作ったflowの次の部分を選択してコピーペーストします。
そして、delayノードを配置します。次のような形です。
delayノードを配置する理由は、noteonの情報の後に少し待ってからnoteoffの情報を送らないと、音がそもそも聞こえなくなってしまうからです。
delayノードの設定は次の通りです。
このdelayノードの時間を長く設定すればするほど、音が長く鳴るようになります。
そして、コピーしたtemplateを書き換えてnoteoffの情報を送信できるようにします。
動作確認してみよう
自分のアカウントのツイートだけを検索対象にしている場合は、実際に自分のアカウントでツイートしてみて、自分の手元で音が流れるか確認してみましょう。
音が流れない場合は、お手本のflowとノードを一つづつ比較してみて、ノードの設定に間違いがないか確認してみましょう!
以上で、ハンズオンは終わりです。
お疲れさまでした。