この記事では、RubyのOSCライブラリを使ってOSCの送受信を行う基礎のところから、OSC経由でiTunesをリモート操作するという1つの活用例までを書こうと思います。
OSCって何?という人でも理解できると思いますが、Rubyのコードがまったく読めないとちょっと理解しにくいかもしれません。
OSCって?
OSCとは、正式名称をOpen Sound Controlといい、公式サイトでは以下のように説明されています。(訳は適当な意訳)
Open Sound Control (OSC) は、現代的なネットワーク機構を持ったコンピューター、サウンドシンセサイザー、その他のマルチメディアデバイス同士でコミュニケーションするための通信プロトコルです。現代的なネットワーク技術を電子楽器の世界で利用することで、OSCは様々な利点を持っています。
名前や、この説明からも分かるように、OSCは電子楽器を連携する目的、つまりMIDIの代替を目的として開発されました。
が、プロトコルがシンプルであることや、簡易な実装であれば難しくなく、様々な言語で利用できるようになっていることなどから、今日ではより広い用途で利用されています。
こういう使い方も。
Node.jsを使ってJenkinsのビルド結果を取得してOSCで転送する
個人的な感覚だと、Max/MSP、ReaktorやSuperColliderなどのソフトウェア、要は一般的な感覚からは少しマニアックな音楽の世界だけで使われていたOSCが一般的に利用されるようになったのはiPhoneが出たあたりからな気がします。
TCP/IPで繋がっていれば使えるっていうのは、ケーブル挿せないスマホには適してますからね。
より詳しい説明については、すでによくまとめられているサイトやWikipediaがあるので、ここで拙い説明をするより、そちらを見てもらった方が早くて正確でしょう。
http://fukuchi.org/oss4art/Open_Sound_Control
http://ja.wikipedia.org/wiki/OpenSound_Control
ここでは、実際に動かして試してみることについての記載を進めます。
そして最後に、OSCの簡単な活用例として、iTunesのプレイリストをリモートからOSC経由で再生する例を示します。
osc-rubyのインストール
前述の通り、Ruby用のOSCライブラリ osc-ruby を使って試します。
osc-rubyはUDPでOSCのメッセージをやり取りします。
これから、osc-rubyを使ってOSCサーバーとOSCをクライアントを用意し、クライアントからサーバーにOSCのメッセージを送信する簡単なサンプルを示します。
できればクライアントとサーバーは別マシンの方が面白いのですが、とりあえずは同一マシン上でどちらも動かすことにします。
$ gem install osc-ruby
osc-rubyのOSCサーバーはEventMachineを使って動作するため、EventMachineのインストールも必要です。
$ gem install eventmachine
Bundlerを使う場合にはGemfileに追加してください。
OSCサーバーを動かす
インストールが済んだら、osc_server.rb
というファイルを作成して、そこにOSCサーバーのコードを書いていきます。
とても簡単な内容なので、コードをそのまま載せます。
[osc_server.rb]
require 'osc-ruby' # osc-rubyのロード
require 'osc-ruby/em_server' # EventMachineサーバーのロード
# UDPポート: 3333 で待ち受けるOSCサーバーのインスタンスを作成
server = OSC::EMServer.new(3333)
# OSCアドレス /greeting にOSCメッセージが送信されたときのメソッドを作成
# アドレスには任意のものを指定できる。/path/to/address のような形で階層化してもよい
server.add_method '/greeting' do |message|
puts "Address: #{message.address} -- Message: #{message.to_a}"
end
# OSCサーバー起動
server.run
実行してみましょう。
$ ruby osc_server.rb
残念!何も起きませんね!
メッセージが届いてないので、もちろん何も起きないのが正しいです。
lsof -i :3333
とかやればちゃんとプロセスが待ち受けしてるのが確認できるかと思います。
これからOSCクライアントを作ってメッセージを送ります。
OSCクライアントからサーバーにメッセージを送る
これも簡単なのでコードそのまま。
[osc_client.rb]
require 'osc-ruby'
# ローカルホストの UDPポート : 3333 宛にOSCメッセージを送信するOSCクライアントのインスタンスを作成
client = OSC::Client.new('localhost', 3333)
# OSCメッセージを作成
# 第一引数にOSCアドレス、以降に送信する内容を指定する
message = OSC::Message.new('/greeting', 'Hello! OSC')
# OSCメッセージの送信
client.send(message)
実行してみましょう。
$ ruby osc_client.rb
サーバー側がこんな出力があれば、期待通りに受信できてます。
Address: /greeting -- Message: ["Hello! OSC"]
簡単!
OSCメッセージのデータ型
OSCサーバーにOSCクライアントから簡単にデータの送信ができることが確認できました。
では、どんなデータが送れるのかを見てみましょう。
全てのOSCデータは以下のデータ型から構成されています。
- int32
- OSC-timetag
- float32
- OSC-string
- OSC-blob
int32
とかfloat32
とかOSC-string
とかOSC-blob
とかは大体想像がつく通りです。
さっきの例では "Hello! OSC"
という文字列を送りました。これはOSC-string
ですね。
OSC-timetag
というのは、後述する「OSCバンドル」の中で使われるものです。
データは、各データ型が定める仕様に従って構成する必要がありますが、その辺りはosc-rubyが吸収してくれるのであまり意識しなくてもいいです。
詳しい説明はOSCの仕様書を参照。
http://opensoundcontrol.org/spec-1_0
データ型の確認
送られてきたデータ型の確認をできるように、サーバー側に修正を入れてみましょう。
サーバーの、出力をする箇所のコードで message.to_a
としていたのに気づいたかもしれません。
OSC::Message
クラスではto_a
を定義していて、それでデータの内容をArray
で取り出せます。
その要素の型を調べてみます。
osc_server.rb
のserver.add_method
の内容に1行追加して、以下のようにしてください。
送られてきたデータのクラスを出力します。
...
server.add_method '/greeting' do |message|
puts "Address: #{message.address} -- Message: #{message.to_a}"
puts message.to_a.map(&:class)
end
...
クライアント側から送信するデータも変更します。
osc_client.rb
の以下の箇所を修正してください。
...
message = OSC::Message.new('/greeting', 'Hello! OSC', 1, 1.5)
...
'Hello! OSC', 1, 1.5 の複数のデータを同時に送っています。
このように複数の値をまとめて送ることもできます。
さて、サーバー側の出力は・・・
Address: /greeting -- Message: ["Hello! OSC", 1, 1.5]
String
Fixnum
Float
Rubyの組み込みクラスであるString
, Fixnum
, Float
が出力されていますね。
これはもちろん、osc-rubyが、ユーザーが扱いやすいようにいい感じにやってくれているからで、OSCプロトコルの内部的には先ほどのOSCのデータ型の仕様に沿って送受信が行われていることを示しています。
OSCバンドル
さっき、複数のデータを同時に送りましたが、OSCには、複数のデータをひとまとまりにして扱う方法として、OSCバンドルという仕組みがあります。
さっきの例では、データが複数あっても、あくまでも1つのOSCメッセージでしたが、OSCバンドルは複数のOSCメッセージのまとまりになります。
したがって、別のOSCアドレスに対するメッセージをまとめて送ることもできます。
早速試してみましょう。
コードを以下のように書き換えます。
[osc_server.rb]
require 'osc-ruby' # osc-rubyのロード
require 'osc-ruby/em_server' # EventMachineサーバーのロード
# ポート: 3333 で待ち受けるOSCサーバーのインスタンスを作成
server = OSC::EMServer.new(3333)
# OSCアドレス /greeting にOSCメッセージが送信されたときのメソッドを作成
server.add_method '/greeting' do |message|
puts "Address: #{message.address} -- Message: #{message.to_a}"
end
# OSCアドレス /hey にOSCメッセージが送信されたときのメソッドを作成
server.add_method '/hey' do |message|
puts "Hey #{message.to_a}"
end
# OSCサーバー起動
server.run
[osc_client.rb]
require 'osc-ruby'
# OSCクライアントのインスタンスを作成
client = OSC::Client.new('localhost', 3333)
# OSCメッセージを作成
# 第一引数にOSCアドレス、以降に送信する内容を指定する
message1 = OSC::Message.new('/greeting', 'Hello! OSC', 1, 1.5)
message2 = OSC::Message.new('/hey', 'Bundle Test')
# OSCバンドルを作成
# 第一引数にtimetag、以降にOSCメッセージを指定する
bundle = OSC::Bundle.new(nil, message1, message2)
# OSCメッセージの送信
client.send(bundle)
これでサーバーを起動してクライアントからOSCバンドルを送信すると、こんな出力になります。
Address: /greeting -- Message: ["Hello! OSC", 1, 1.5]
Hey ["Bundle Test"]
期待通りに、OSCバンドルに含まれたOSCメッセージが届いています。
ちなみに、timetagにはnil
を指定していますが、timetagについてより詳細を知りたければ、上でリンクを貼ったOSCの仕様書などを見てください。
iTunesのプレイリストをOSC経由で再生する
iTunesのプレイリストを再生しろ、っていうOSCメッセージが届いたら再生するためのOSCサーバーを用意します。
これもできればリモートでやりたいけど、都合上localhost同士で・・・
[control_itunes_server.rb]
require 'osc-ruby'
require 'osc-ruby/em_server'
server = OSC::EMServer.new(3333)
server.add_method '/itunes/playlist' do |message|
playlist = message.to_a[1]
if message.to_a.first == 'play' && playlist
puts "Going to play playlist: #{playlist}"
cmd = "osascript -e 'tell application \"iTunes\" to play playlist \"#{playlist}\"'"
`#{cmd}`
end
end
server.run
中では、AppleScriptのワンライナーでiTunesのプレイリストを再生しています。
では、命令を送信するためのOSCクライアントを作ります。
[control_itunes_client.rb]
require 'osc-ruby'
client = OSC::Client.new('localhost', 3333)
message = OSC::Message.new('/itunes/playlist', *ARGV)
client.send(message)
サーバーを起動したら、以下のように引数に[play
]と[プレイリスト名]を指定してクライアントを実行します。
$ ruby control_itunes_client.rb play "East Coast Hip Hop Masterpieces"
どうでしょう?再生されましたか?
おわり
OSC、意外と簡単で利用できる場面ありそうじゃないですか?
別にOSCじゃなくてもできるよ!って思うかもしれませんが、NodeやWebSocket、それにProcessingなどと組み合わせて使うのも簡単なので、アイデア次第では面白いことが実現しやすいかなと思います。
例で示したiTunesの再生などの活用サンプルは、また別で書くかもしれません。