OSC入門 - osc-rubyを使った基礎からiTunesのリモート操作まで


この記事では、RubyのOSCライブラリを使ってOSCの送受信を行う基礎のところから、OSC経由でiTunesをリモート操作するという1つの活用例までを書こうと思います。

OSCって何？という人でも理解できると思いますが、Rubyのコードがまったく読めないとちょっと理解しにくいかもしれません。

## OSCって？

OSCとは、正式名称をOpen Sound Controlといい、公式サイトでは以下のように説明されています。(訳は適当な意訳)

> Open Sound Control (OSC) は、現代的なネットワーク機構を持ったコンピューター、サウンドシンセサイザー、その他のマルチメディアデバイス同士でコミュニケーションするための通信プロトコルです。現代的なネットワーク技術を電子楽器の世界で利用することで、OSCは様々な利点を持っています。

http://opensoundcontrol.org/introduction-osc

名前や、この説明からも分かるように、OSCは電子楽器を連携する目的、つまりMIDIの代替を目的として開発されました。
が、プロトコルがシンプルであることや、簡易な実装であれば難しくなく、様々な言語で利用できるようになっていることなどから、今日ではより広い用途で利用されています。

こういう使い方も。
[Node.jsを使ってJenkinsのビルド結果を取得してOSCで転送する](http://yzgwyskw.hatenablog.com/entry/2013/06/10/012508)

個人的な感覚だと、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のメッセージを送信する簡単なサンプルを示します。
できればクライアントとサーバーは別マシンの方が面白いのですが、とりあえずは同一マシン上でどちらも動かすことにします。


```console
$ gem install osc-ruby
```

osc-rubyのOSCサーバーはEventMachineを使って動作するため、EventMachineのインストールも必要です。

```console
$ gem install eventmachine
```

Bundlerを使う場合にはGemfileに追加してください。

## OSCサーバーを動かす

インストールが済んだら、`osc_server.rb`というファイルを作成して、そこにOSCサーバーのコードを書いていきます。

とても簡単な内容なので、コードをそのまま載せます。

[osc_server.rb]

```ruby
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
```

実行してみましょう。

```console
$ ruby osc_server.rb
```

残念！何も起きませんね！
メッセージが届いてないので、もちろん何も起きないのが正しいです。
`lsof -i :3333` とかやればちゃんとプロセスが待ち受けしてるのが確認できるかと思います。

これからOSCクライアントを作ってメッセージを送ります。

## OSCクライアントからサーバーにメッセージを送る

これも簡単なのでコードそのまま。

[osc_client.rb]

```ruby
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
$ ruby osc_client.rb
```

サーバー側がこんな出力があれば、期待通りに受信できてます。

```text
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行追加して、以下のようにしてください。
送られてきたデータのクラスを出力します。

```ruby
...
server.add_method '/greeting' do |message|
  puts "Address: #{message.address} --  Message: #{message.to_a}"
  puts message.to_a.map(&:class)
end
...
```

クライアント側から送信するデータも変更します。

`osc_client.rb`の以下の箇所を修正してください。

```ruby
...
message = OSC::Message.new('/greeting', 'Hello! OSC', 1, 1.5)
...
```

'Hello! OSC', 1, 1.5 の複数のデータを同時に送っています。
このように複数の値をまとめて送ることもできます。

さて、サーバー側の出力は・・・

```text
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]

```ruby
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]

```ruby
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バンドルを送信すると、こんな出力になります。

```text
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]

```ruby
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]

```ruby
require 'osc-ruby'

client = OSC::Client.new('localhost', 3333)
message = OSC::Message.new('/itunes/playlist', *ARGV)
client.send(message)
```

サーバーを起動したら、以下のように引数に[`play`]と[プレイリスト名]を指定してクライアントを実行します。

```console
$ ruby control_itunes_client.rb play "East Coast Hip Hop Masterpieces"
```

どうでしょう？再生されましたか？

## おわり

OSC、意外と簡単で利用できる場面ありそうじゃないですか？
別にOSCじゃなくてもできるよ！って思うかもしれませんが、NodeやWebSocket、それにProcessingなどと組み合わせて使うのも簡単なので、アイデア次第では面白いことが実現しやすいかなと思います。

例で示したiTunesの再生などの活用サンプルは、また別で書くかもしれません。