Furyu

[フリュー公式] Tech Blog

フリュー株式会社の技術ブログです

2011年07月7日

MIDIキーボードで3Dプリンタを演奏する

はじめまして。フリューモバイル事業部の庄司です。
不定期にアレなネタを投下していきます。よろしくお願いします。

MIDIキーボードで3Dプリンタを演奏する

わけがわからないよ…。

概要


みなさんのご家庭にも1台や0台はある、3Dプリンタ。
今回はこれにMIDIキーボードをつなげて演奏してみます。

必要なもの

Javaが動作し、下記の機器が接続できる環境。

  • MIDIキーボード
  • MIDIインタフェース
  • G-Codeで制御できる3Dプリンタ、今回はThing-O-Maticを使用しました。

JavaからMIDI信号の受信

javax.sound.midiパッケージ以下のクラスを使います。
このパッケージはJava 1.3にて追加されたものです。わりとシンプルで使いやすいです。
今回は、キーボードからの受信のみを行うので、Receiverクラスを実装します。

このコードを実行すると、MIDIを受信するたびにsendメソッドが呼び出され、MIDI note numberが表示されます。

今回使ったキーボードのせいか、鍵盤を数秒間押し続けると音が止まらなくなってしまったので、ある程度長く発音されたら音を止めることにする、という処理を入れておきます。

音程を作る

演奏するためには、音程が作れないといけません。
音程=空気の振動数(周波数)です。
例えば、秒間440回(440Hz)空気を振動させることにより、「中央のラ(→ A4 → MIDI note number 69)」の音が発生します。
2倍の880Hzにすると1オクターブ上がります。
440Hz = ラであるという定義は、時代や地域によって変わります。が、この記事の本筋とは関係ありません。

今回は、不本意ではありますが、計算が楽なため平均律を使います。
平均律とは、1オクターブを12等分に分割したものです。

こんな感じです。

このMIDI note number→周波数変換関数をいくつか用意すると、複数の音律に対応できるようになります。
3Dプリンタによる発声で音律に拘るのはアレな感じがしますが、この部分は他の音声合成を行う際にも適用できます。

3Dプリンタによる音程作り

振動数を、「3Dプリンタの軸の移動速度」に割り当ててやることにより、3Dプリンタのステッピングモーターで音程を作ることができます。

連続で発声させる場合、同じ方向ばかり続けて動かすと終端まで行ってしまうので、移動可能な範囲を制限し、これを超えないよう制御してやる必要があります。
これはZ軸で特に気をつける必要があります。最悪、X-Yのテーブルを突き破ってしまいます。

音の長さ

同じ距離を別の速度で移動するので、このままでは音程が低いほど長い音になってしまいます。
移動速度に比例した移動距離を設定してやることにより、どの音でも同じ長さにすることができます。

和音

単音って寂しいですよね?
3DプリンタにはX, Y, Zという3つの軸があります。
プラスチック素材を送るためのステッピングモーターがあるので、正確に言うと4つですが、今回は使用していません。
これらを使うと3和音が表現できます。初期の携帯電話並です(無理矢理モバイルと結びつけてみます)。

各軸の移動距離は単音のときと同じように求めることができます。
しかし、移動距離は各軸個別に指定できるのですが、移動速度は1つのみです。
X, Y, Z各軸の速度を合成ベクトルにして、その長さを求めることによって、合成された軸の速度を求めることができます。

こんな感じですね。

3Dプリンタの制御

今回使用する3Dプリンタはオープンソースハードウェア、そしてファームウェアと制御ソフトウェアもオープンソースソフトウェアで構成されています。
というわけで、プリンタ制御のためのコードは公開されています。そして都合が良いことにJavaで書かれています。
ReplicatorGというソフトウェアになります。

Macであれば、ReplicatorG.app/Contents/Resources/Java以下に、使用するJarパッケージやJNIライブラリが転がっています。これらをプロジェクトにコピーし、Jarにクラスパスを通します。

Jarのみだとコードが追いにくいので、以下から最新を取ってきて眺めるのも良いかと思います。
git clone git://github.com/makerbot/ReplicatorG.git

制御のメインになるクラスは、replicatorg.machineパッケージ以下にあります。

このようにして、制御に使用するMachineInterfaceを実装しているクラスのインスタンスを取得します。

あとは、

などとすると、3Dプリンタの制御が行われます。

G-Codeの生成

G-Codeとは、CNC(Computerized Numerically Controlled)マシンを制御するための言語です。
G1、G2…、と命令に連番が振られていて、命令ごとに個別にパラメータが定義されています。Gで始まるからG-Codeです。詳しくはWikipedia英語版あたりで調べてみてください。
ReplicatorGのMachineInterfaceクラスを使うと、前述のようにテキストベースの記述から制御することができます。
各軸を動かすためのG-Codeは「G1」になります。今回使うのは3Dプリンタ初期化用のG-Codeを除いて、この「G1」のみを使っています。

例えば、「ドミソ(C4 E4 G4)」を1秒間鳴らすためのG-Codeはこのようになります。

ファイル名をceg.gcodeなどとしてテキストファイルで保存し、ReplicatorGで開いて実行すると「ドミソ」が鳴るはずです。
(事故防止のため、事前に各軸を中央付近にリセットしておく必要があります)。

実装の結合そして演奏

ここまでで

  • MIDIの受信
  • 音程の生成
  • 和音を表すG-Codeの生成
  • 3Dプリンタの制御

までができました。

これらを結合、つまりMIDIを受信して、現在鳴っている音を3つまで格納するようなSetを保持し、そこから和音の周波数を求め、G-Codeを生成、プリンタに指令することにより、演奏できるようになるはずです。

Java実行形式(jar)とコードの一式は以下になります。
ソースコードのライセンスは、ReplicatorGと同じく「GPLv2」となります。
作成したばかりなので、動作の改善やドキュメントなど、しばらくは随時更新します。

ダウンロード

使い方

  • zipを展開して出来る、readme.txtの通りにファイルを設置します。
  • MIDIインタフェースとキーボード、3DプリンタをPCに繋ぎます。
  • 3DプリンタはあらかじめReplicatorGで設定しておきます(最後に使った設定を使います)。
  • JNIライブラリ(librxtxserial.jnilib)が32bitでしか動かないので、vmargsに「-d32」を指定してmainクラスを実行します。

Swingで画面インターフェースを作ってみました。MIDIインターフェースを選んで、「Choose & start to play.」ボタンを押して、一旦軸が端まで動き、中央に止まったら、演奏できます。
例外出たときとか、特に処理入れてないので変化しません…。「Reset machine.」ボタンで、軸の位置を再度初期化します。演奏していてズレてきたら押してみるといいかも。
変な挙動になるかもしれないので、いつでも3Dプリンタの方のリセットボタンが押せるよう、準備してからお使いください。

まとめ

というわけで、3Dプリンタの可能性を試してみました。プリンタとしては使っていませんが…。3Dプリンタ、一昔前と比べるとお手頃価格なのに、結構な精度が出るので良いですよ。一家に一台、いかがでしょうか。

今回、javax.sound.midiを初めて使ってみましたが、クセもあまりなく使いやすかったです。
MIDI自体がシンプルということもあって対応機器は楽器に限らず色々とある(し、簡単に作れる)ので、遊びに・実用に使ってみるのも良いかもしれません。
iMIDIとかネットワーク上に乗るMIDIのプロトコルも出てきているので(MacではOSがiMIDIをサポートしている)、「楽器との接続」以上のことが出来るようになってきています。

ReplicatorGはArduino IDEからのforkということもあってJavaで書かれています。JavaなのでServletと組み合わせて遠隔3Dプリントサーバとか作れるかもしれません。エラーの監視が大変でしょうけど。


2011年07月4日

AntでSCP・SSHEXECタスクをHTTPプロキシ経由で実行する

みなさん、こんにちは!フリューでモバイルサイト開発を行っている鷲見といいます。

現在Amazon Web Services 1ヶ月導入記という記事を書いているのですが、今回はAWSを導入したことで経験した少し困ったことを記事にしてみました。

今までは某データセンターにサーバを置いていたため、設定でHTTPプロキシを介さずにデータのやり取りを行っていました。しかし、今回導入したAWSのEC2では、HTTPプロキシを介してのやりとりを行う必要がありました。

フリューではプログラムのデプロイや各種ミドルウェアの再起動にApache Antを利用しています。ここで困ったのがAntのSCP/SSHEXECのタスクにはHTTPプロキシを設定できないということでした。

AntのFTPタスクではプロキシを設定することができるのですが、SCP/SSHEXECタスクでは設定できません。何故こうなってるのかはよくわかりませんが、最新のAntのソースコードまで読んだので間違いありません。

ただし、Antが内部的にSCP/SSHEXECで利用しているJSchにはプロキシを設定する箇所があります。

これはAntかJSchどちらかのソースコードを改造すれば対応できそうです。というこで今回はAntとJSchの両方での修正について紹介したいとおもいます。

JSchとは?

JSch – Java Secure Channel

JSchはJavaでSSH2のクライアント側が実装されたライブラリで、Ant、Eclipse、Netbeansなどでも利用されています。ちなみに作者は株式会社ジェイクラフトの山中淳彦さんという日本の方です。

ライセンスは修正BSDライセンスとなります。

タイトルから分かるようにJSchというのはJava Secure Channelの略語のようです。

Antのコードを修正してみる

JSchではプロキシの設定はSessionクラスにProxyクラスをセットして行っています。
ということはAntのコードの中でJSchのSessionクラスの利用箇所を探して、Proxyクラスをセットするようにすればよさそうです。

ちなみに今回修正しているAntのバージョンは1.7.1です。
http://archive.apache.org/dist/ant/source/apache-ant-1.7.1-src.zip

調査したところ、org.apache.tools.ant.taskdefs.optional.ssh.SSHBaseというクラスのopenSessionというメソッドでSessionをJSch側から取得しています。どうやらこのopenSessionメソッドを修正すればよさそうです。というわけで早速修正してみました。

org.apache.tools.ant.taskdefs.optional.ssh.SSHBase.java

上のプログラムの『“HTTPプロキシサーバのIPアドレス:ポート番号”』の箇所は自分の環境にあわせてください。例えばサーバが192.168.10.1、ポートが3333の場合は『“192.168.10.1:3333”』となります。

HTTPプロキシサーバのIPアドレス・ポート番号が変更になる可能性がある場合はこの設定をファイルや環境変数にした方が良いかもしれません。

修正が完了したら、antのソースコードを解凍した直下にあるbuild.xmlのdistターゲットをAntで実行すれば、修正されたjarが

に作成されます。

現在利用されているant.jarを、修正したant.jarに置き換えることでHTTPプロキシを超えてSCP/SSHEXECを利用することができるようになります。

JSchのコードを修正してみる

JSchはプロキシ設定をする箇所があり、Antがプロキシ設定をしていないだけなので、本来的にはAntを修正すべきなのですが、Antを修正するとあとでいろいろと影響が出そうで嫌だという場合にはJSch側を修正してみます。

ちなみに今回修正しているJSchのバージョンは0.1.44です。
http://sourceforge.net/projects/jsch/files/jsch/0.1.44/jsch-0.1.44.zip/download

Antの修正時に呼び出しているJSchクラスのgetSessionメソッドでSessionクラスのインスタンスを生成しているようなので、このメソッドを修正してみましょう。

com.jcraft.jsch.JSch.java

上のプログラムの『“HTTPプロキシサーバのIPアドレス:ポート番号”』の箇所は自分の環境にあわせてください。例えばHTTPプロキシサーバが192.168.10.1、ポートが3333の場合は『“192.168.10.1:3333”』となります。

修正が完了したら、JSchのソースコードを解凍した直下にあるbuild.xmlのdistターゲットをAntで実行すれば、修正されたjarが

に作成されます。(yyyymmddはbuildを行った日付です。)

現在利用されているant-jsch.jarを、修正したjsch-yyyymmdd.jarに置き換えることでHTTPプロキシを超えてSCP/SSHEXECを利用することができるようになります。

まとめ

この記事ではAntでHTTPプロキシ経由でSCP/SSHEXECタスクを実行するため以下のようなことを行いました。

  • Ant側のJSchのSessionクラスの利用箇所でプロキシ設定の追加
  • JSch側のSessionクラスのインスタンス生成箇所でプロキシ設定の追加

Antを修正するか、JSchを修正するか。プロキシ設定を直書きにするか、ファイルにするか、環境変数にするかはみなさんの判断にお任せしたいと思います。

今回は少しAWSから離れて、AWS導入で困ったことの解決方法を記事にしてみました。AWSに関する記事も鋭意製作中ですのでしばらくお待ちくださいませ。
次回以降もAWS導入に関する小話をおりまぜつつ、AWS関連記事を作成していきます。