SC IDEでたよ


sc3.6alphaが公開されましたが、3.6からIDEが実装されます。こんな画面でSCコーディング。scvimとかscelなんていらなかったんや!

基本

補完やシンタックスハイライトがデフォで実装されていますが、ファイルは.scdを使用しないとダメです。新規にファイル作成した場合は.scdファイルが作成されます。htmlファイルはソース表示、rtfファイルはプレーンテキストに変換されます。今後は、.scd使ってね、ということらしいです。
undoとかredoとかは今までどおりのcmd+z、cmd+shift+zとかデフォルトショートカット。ただし、ことえりキーバインドは使用できないので注意しましょう。

セッション

新規にセッションというものが追加されています。これは、画面のレイアウトやファイルを開いている状態を保持する単位。要は、プロジェクトみたいなものですかね。
デフォルトでの保存はメニューからのみ、後述する設定にてショートカット割り当て可能。セッションの切り替えは、cmd+shift+Q。名前の変更や削除は、Session -> Manage Sessionから
他のIDEには普通にある機能ですが便利っすね!

ビュー

表示画面全般ね。お待ちかね、エディタのタブ化!ファイルを複数開くと勝手にタブを追加して開いてくれます。
タブを複数開いているときは、マウスじゃなくて、キーボードで切り替えたいよね。タブの切り替えは、cmd+opt+→かcmd+opt+←。
View -> Docsにてヘルプブラウザを表示できる。cmd+fで検索窓を開ける、置換はcmd+rな。
コマンドラインもcmd+eで開ける!コマンドラインで{SinOsc.ar()}.playとか書いてreturnすると音が鳴るぜ!
これらはTool Panelって呼ばれるらしい、邪魔になったらescキーで削除だ。
エディタのフォントサイズを変えたい場合は、cmd++とかcmd+-な。今まで通りだ。Show Spaces and Tabsを有効にすると、タブが→、スペースが・とか表示されて気持ち悪いぜ!

画面分割

cmd+p 2で下に分割、cmd+p 3で右に分割。cmd+p押して離して、すかさず3を押す!
分割したビューを戻すにはcmd+p 1だ。勢い余って分割しすぎたら、cmd+p 0で全部戻せますよ。
分割したビューにフォーカスして、タブの切り替え(ドキュメントの切り替えね)のcmd+opt+→とかやってみよう。トラックパッドやマウスなんていらないんや。

フルスクリーン表示もできるでよ!

cmd+shift+f。プレゼンとかする場合は全画面でお願いします。左下になんかデジタル時計も表示されますよ!戻すには、もうっかいcmd+shift+f。

参照

クラスの実装とか確認したい場合は、文字列を選択して、cmd+iです。リファレンスはcmd+u。試しに、SynthDefを選択して、cmd+i。Look Up Implementationsウィンドウなんてものが表示されます。チラ見して、ドキュメントを閉じる場合は、cmd+w。
ヘルプ確認したい場合は、今まで通りcmd+d。ただし、Post Windowと同じ場所にヘルプブラウザ開いている場合は、自動的にフォーカスしてくれないから、マウスでクリックして選択するんだ。面倒。。。

実行

じゃあ、補完しまくりつつコード書いたら実行しよう。
まずは、サーバのブートしましょう。cmd+bでをブート(なんかショートカット削除されていたので、たぶん)。
()で囲った部分をするには、cmd+shift+rだ。(の近くじゃなくても構わないです。選択されたらすかさず、cmd+return。ctl+cとかは使えなくなっているので注意しましょう。

ショートカット

当然、編集できます。Preferences(cmd+,)を開いて、Shortcuts。例えばセッションを保存するショートカットを作成するには、Filterでsesionとか絞って、Save Session As...を選択。Customにチェックいれて、cmdとshift押しながらsを押すと反映されます。他のショートカットと被ってしまうので、慎重に設定しましょう。

レイアウト

好きなようにpost windowとかヘルプブラウザを配置出来ます。試しにpost windowというバー部分をドラッグして、右端に移してみましょう。好きに配置かえてね、ってことです。

いじょう

cmd+mとかも押してみましょう。SCも少しフレンドリーになりましたね!

SuperColliderのエラー出力を理解した気になろう!

なんとなくコードを書いていたり、とりあえずコピペしてみて、いざ実行、でエラーが出力されると、エラーなんて読む気がなく、諦めてボケーッとどっかのサイト見てたりして気づいたら、もうこんな時間か!だったりしますよね。そうなる前になんとなくでも良いのでエラー出力を読んでみましょう。

まずは

{SinOsc.ar()}}.play;

よくやるのが、}を閉じ忘れたり、なんか1個多かったりですね。これは親切にマッチしないよ?と言ってくれるので、修正しやすいです。

/*
unmatched '}'
  in file 'selected text' line 1 char 14
・ ERROR: Parse error
  in file 'selected text'
  line 1 char 14:

  {SinOsc.ar()}}.play;
               ^
   
-----------------------------------
・ ERROR: Command line parse failed
nil
*/

}がマッチしなくて、それは何番目の文字か、を出力してくれますので、探しやすいですね。

{SinOsc,ar()}.play;

タイポするとパースエラーを返してくれるので、これの間違った部分がわかりやすいですね。

/*
・ ERROR: Parse error
  in file 'selected text'
  line 1 char 8:

  {SinOsc,ar()}.play;
         ^
*/

これも何番目の文字がダメかを出力してくれます。
この手の軽微なミスは間違いを見つけやすいので良いとして、内部のエラーの場合で、かつコードが長くなってくるとどこを修正していいのかさっぱり分からなかったりします。

きほん

SC3がユーザに返すエラーは3つに分類できます。
1. エラーメッセージ
2. エラーを引き起こしたメソッドのレシーバ、もしくはメソッドを呼んだ引数
3. コールスタック
※コールスタックについてはググってください。

例えば、下のbleachメソッドを実行してみましょう。なんかpostウィンドウにエラーメッセージが出力されます。結論から書くと、blechメソッドをレシーバである'1'がもっていないということが分かります。

1.blech
//1.blech(2)
/*
ERROR: Message 'blech' not understood.
RECEIVER:
   Integer 1
ARGS:
PATH: xxx
CALL STACK:
	DoesNotUnderstandError:reportError   0x104ca2798
		arg this = <instance of DoesNotUnderstandError>
	Nil:handleError   0x104ca29a8
		arg this = nil
		arg error = <instance of DoesNotUnderstandError>
	Thread:handleError   0x104ca2a58
		arg this = <instance of Thread>
		arg error = <instance of DoesNotUnderstandError>
	Object:throw   0x104ca2c68
		arg this = <instance of DoesNotUnderstandError>
	Object:doesNotUnderstand   0x104ca2f28
		arg this = 1
		arg selector = 'blech'
		arg args = [*0]
	Interpreter:interpretPrintCmdLine   0x116a92368
		arg this = <instance of Interpreter>
		var res = nil
		var func = <instance of Function>
		var code = "1.blech
"
		var doc = <instance of CocoaDocument>
	Process:interpretPrintCmdLine   0x116998148
		arg this = <instance of Main>
For advice: [http://supercollider.sf.net/wiki/index.php/DoesNotUnderstandError#blech]

*/

ここで、ちょっと復習してみましょう。レシーバ.メッセージ;というのが基本の文となります。この文は"レシーバ"というオブジェクトに対して、"メッセージ"という命令を実行させます。
要は、"レシーバちゃん"に対して、「おう、お前"メッセージ"しろよ!」と極悪プログラマがいっているわけですね。ここで問題となるので、"レシーバちゃん"が"メッセージ"という行動を理解しているかどうかです。
・"レシーバちゃん"が聡い場合
極悪プログラマに"メッセージ"しろと言われた"レシーバちゃん"は"メッセージ"という行動(メソッド)を理解しているため、
命令された後に"メッセージ"という行動(メソッド)をします。極悪プログラマが望んだ結果が返ってくるわけですね。
・"レシーバちゃん"が残念な場合
極悪プログラマに"メッセージ"しろと言われた"レシーバちゃん"は"メッセージ"という行動(メソッド)を理解できないため、
命令されても"ふぇぇ"とかいって、わけがわからないことを言い出しはじめます。
うん、お前は何を書いているんだ?と。ここでは"ふぇぇ"はpostウィンドウに出力されるエラー内容ですよ。"レシーバちゃん"がわからなかった内容を詳細に教えてくれているわけですね。ここでは"1"が"レシーバちゃん"で、"blech"が”メッセージ”のに該当します。"1"が"blech"メソッドを持っていれば、なんらかの結果が返ってきますが、持っていなければpostウィンドウにエラーメッセージが出力されます。

よむ

ERROR: Message 'blech' not understood.

ですが、これは素直に"レシーバちゃん"が理解出来なかったことが出力されます。"blech"ってなに、わかんない?ってことです。

RECEIVER:
   Integer 1

は、"レシーバちゃん"は何者?ということ。
ここでは整数の1が"レシーバちゃん"の正体であるとわかりますね。

ARGS:

"メッセージ"の引数です。
今回は"メッセージ"に引数がないので、ARGS:以下な何も表示されません。1.bleach(2);とか実行してみると

ARGS:
   Integer 2

と出力されます。

PATH: xxx

これは、ソースコードの場所ですね。気にしなくてもいいです。

CALL STACK:

コールスタックが逆順に表示されています。一番上のスタックは一番最近の呼び出し命令ってことですね。
大抵上から4つはの同じ呼び出し結果を表示させます。まあ、上から4つは無視していいってことです!
DoesNotUnderstandError:reportError
Nil:handleError
Thread:handleError
Object:throw
ですね。

コードブロックを選択して、ハイライトさせて、コードを実行しますよね。そうすると、コードはSCの裏側でごにょごにょされているんですけれど、その一部でInterpreterクラスのinterpretPrintCmdLineが呼び出されてたりします。
Process:interpretPrintCmdLine
Interpret:interpretPrintCmdLine
の部分も無視していいかと思います。呼び出されなかったりもしますが、ここでは気にしないで下さい。

で、コールスタックの内容が一番のキモなわけですが、まあ親切すぎてわかりにくいんですよ。

もうちょいよむ

Routine({
	var a;
	a = 5;
	loop {
		var b;
		b = 20.rand;
		b.postln.ecky_ecky_phtang;	// "NI!!!!"
		a.wait;
	}
}).play;
/*
a Routine
12
ERROR: Message 'ecky_ecky_phtang' not understood.
RECEIVER:
   Integer 12
ARGS:
PATH: xxx

上記参考

CALL STACK:
	DoesNotUnderstandError:reportError   0x12064f6d8
		arg this = <instance of DoesNotUnderstandError>
	Nil:handleError   0x11be47ea8
		arg this = nil
		arg error = <instance of DoesNotUnderstandError>
	Thread:handleError   0x11be4a448
		arg this = <instance of Thread>
		arg error = <instance of DoesNotUnderstandError>
	Thread:handleError   0x1208b51c8
		arg this = <instance of Routine>
		arg error = <instance of DoesNotUnderstandError>
	Object:throw   0x120966ee8
		arg this = <instance of DoesNotUnderstandError>

とりあえず無視

	Object:doesNotUnderstand   0x120a81a18
		arg this = 12
		arg selector = 'ecky_ecky_phtang'
		arg args = [*0]

ここでエラーが発生しました。thisに12、selectorに'ecky_ecky_phtang'、argsに[*0]がそれぞれ代入されています。このオブジェクトは12で、セレクタとして'ecky_ecky_phtang'、引数はないよ、ってことです。で、セレクタの'ecky_ecky_phtang'は12がもっていないので、エラーになると。

	< FunctionDef in closed FunctionDef >   0x12045d7a8
		var b = 12

ここではbという変数に12が代入されています。変数に代入される処理があると、< FunctionDef in closed FunctionDef >が呼び出されるようです(たぶん)。

	Function:loop   0x120936c78
		arg this = <instance of Function>

ここでは、Functionのloopメソッドが実行されています。

	Routine:prStart   0x1206de328
		arg this = <instance of Routine>
		arg inval = 2421.955160245

この部分が、コードを実行したときに一番始めに処理されます。RoutineのprStartが実行されていることが分かります(prStartってなに?ですが、Routineクラスのメソッドですよ)。prStartの引数であるinvalに2421…が代入されていることが分かります。

For advice: [http://supercollider.sf.net/wiki/index.php/DoesNotUnderstandError#ecky_ecky_phtang]
*/

たんなる参考ですね。


CALL STACK以下の内容をもうちょっと細かくみてみましょう。CALL STACKは、下から上に沿って実行結果が出力されます。まず、XXX:YYY という部分ですが、これは "ClassName:MethodName"です。Routine:prStartはRoutineクラスのprStartメソッドだということですね。postウィンドウの"ClassName:MethodName"をダブルクリックして、cmd+jすると、ソースコードの該当メソッドを開いてくれます。
エラーになった部分の、Object:doesNotUnderstandですが、実はObjectクラスはdoesNotUnderstandメソッドを持っています。このメソッドを追っていくと、ERRORクラスが呼ばれてpostウィンドウになんかpostlnしているってことが分かったりします。
この"ClassName:MethodName"の下に、インデントされたargが数個出力されています。まず、大抵arg thisがありますが、オブジェクトはなにかを示していると思ってください。Routine:prStart部分ではRoutineのインスタンス、Object:doesNotUnderstandでは数字の12。this以降にさらにargがある場合は、MethodNameの引数になにが代入されているか、を示しています。prStartメソッドには引数としてinvalが内包されています。doesNotUnderstandメソッドにはselectorが。

おまけ

SCではエラー出力に、ErrorクラスかErrorクラスのサブクラスのインスタンスである、Errorオブジェクトを使います。
Error("This is a basic error.").throw;
これを実行するとERROR: This is a basic errorを出力されます、ERRORの部分に出力させるメッセージを自分でいじれるってことです。
他の言語と同じくtry、protect文を使って例外処理も書けますよ。

try { 1.blech; } { |error| error.postln };
protect { 1.blech } { |error| error.postln };

Returns: SC3 on Linux

帰ってきたりなっくす!
昨年は爆発的にSCユーザが増えた年となり、かなりの方がSCに興味を持ち、触ってみて、挫折したのではないでしょうか?max6もリリースされ、SCなんていらなかったんや!との気持ちもなきにしもあらずです。
今年一発目は仮想環境上にSC on Linux環境を構築する方法を記載しました。もうこれでmac持ってないよ?とか言い訳はできませんね!linuxで音を扱う際にはなめてかかってはいけません。相当苦労するはめになります。SCをリポジトリからインスコしたからといって音がでるわけではありません。
環境は、仮想化ソフトとしてvirtualbox4.1.8、仮想マシンとしてubuntu11.10になります。vmwareでも構いませんが、virtualboxだとデフォで実機側に音を渡せます。また、SC開発版は結構仕様が変わっていますので、これを機にビルドして開発版をインストールしてみましょう!

準備

virtualbox

https://www.virtualbox.org/
バイナリ落としてインストールする。問題ない。

ubuntu

http://www.ubuntulinux.jp/
日本語Remix CDイメージのダウンロード。virtualboxの新規仮想マシンをデフォルトで作成して、isoからインストール。最近のlinuxディストリは驚くほど親切だ。数クリックでインストールが完了する。ubuntuのインストールが完了したら、とりあえずアップデートしておく。インストールからアップデート完了まで結構時間がかかる。コーヒーでも淹れてこよう。ココアでも良い。
なお、以降ターミナルで作業することになるが、なぜかxtermが前面にあるが使いづらいためアプリケーションの検索でterminal(端末)を探しておこう。

インスコ

supercollider

先はまだまだ長い。ダレる前にSCを入れてしまおう。

$ sudo apt-get install  git clone --recursive git://supercollider.git.sourceforge.net/gitroot/supercollider/supercollider
$ cd supercollider
$ cat README_LINUX.txt

なにやら面倒なことが書いてあるがbuild requirements (debian users)に書いてあるパッケージを入れよう。

$ sudo apt-get install build-essential libqt4-dev libqtwebkit-dev libjack-dev libsndfile1-dev libasound2-dev libavahi-client-dev libicu-dev libreadline6-dev libfftw3-dev libxt-dev pkg-config cmake

ここでcmakeするのはまだ早い。実は足りないものがあるので入れておこう。

$ sudo apt-get install emacs libcwiid-dev

これでビルドする準備は整ったはずだ。

$ pwd
  xxx/supercollider
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install

これでSCの最新版インストール完了。ビルドにちょっと時間がかかるけれども、SCのインストールは特に難しくはないはずだ。ただ、linuxはそれほど甘くない!残念ながらまだまだ音は出ない。めげずに続けて必要なことをやっていこう。

さらにインスコ

linuxで音を扱うにはカーネルをlowlatencyかrealtimeにする必要がある。ブチブチさせたくないならな。今回選択したubuntuではこの辺りが若干ごたごたしている。とくに今回いれた11.10はいまいちよく分からない部分がある。安定して、さらに日本語記事がアップされるまで気長に待とうじゃないか。
ここではlowlatencyカーネルを入れてみよう。
https://wiki.ubuntulinux.jp/UbuntuStudioTips/Setup/Kernels
”PPAからLowLatencyカーネルやRealtimeカーネルを導入する方法”に従う。

$ sudo add-apt-repository ppa:abogani/ppa
$ sudo apt-key adv --recv-key --keyserver keyserver.ubuntu.com F141B61E
$ sudo apt-get update
$ sudo apt-get install linux-lowlatency

低レイテンシカーネルを加えたのでgrubの設定を変えておこう。

$ sudo vi /boot/grub/grub.cfg
  set default="2"に変更

13行めのset default="0"をset default="2"にしておく。menuentryの3つ目が先ほどいれたLinux 3.0.0-13-lowlatencyだからだ。一旦リブートしてカーネルがlowlatencyに変更されたのを確認しよう。

$ sudo reboot
 ...
$ uname -a
  Linux xxx 3.0.0-13-lowlatency

OKだ。linuxカーネル3.0にビビるな。でもまだまだ設定は続く。

$ sudo adduser 自分 audio
$ sudo su -c 'echo @audio -rtprio 99 >> /etc/security/limits.conf
$ sudo su -c 'echo @audio -memlock 250000 >> /etc/security/limits.conf
$ sudo su -c 'echo @audio -nice -10 >> /etc/security/limits.conf

なおこれらの値は昔どこかのサイトに載っていたままなので、自分の環境に合わせて変更してほしい。なんの設定かは未だに知らない。。。

jackd

linuxでSCを扱う場合、jackdというソフトウェアを通すことで音の出力を可能にしている。オーディオサーバみたいなものだ。これがまた厄介だったりするが、linuxでSCを使う上での特徴であり、便利といえば非常に便利だ。
では、設定だが横着するためにGUIのqjackctlをいれよう。

$ sudo apt-get install qjackctl

ここでなにやら変な画面に遷移するので、リアルタイム実行優先度の設定を有効にする、で、はいにしておこう。

$ qjackctl(もしくは、dashホームのメディア関係からqjackctl)

なんかGUIが立ち上がったので、設定変更をしよう。これが大事だったりする。
まず、MessagesをクリックしてMessagesウィンドウを開いておこう。なんらかのエラーがあった場合にはこのウィンドウが必要になる。
次にSetupだ。わけがわからないと思うが、問題ない。大半の人が意味不明だろう。まずは、Miscタブに遷移し、Enable D-Bus interfaceのチェックを外そう。これは新しいものなのか私は知らないものだし、なんらかの悪さをしているようだ。続いてSettingsタブに戻り、Realtime、No Memory Lockにチェック、Periods/Bufferを3にする。さらに、右側のAudioをPlayback OnlyにしてOKボタンだ。では、Playボタンをクリックしてみよう。ポップアップが出ずに、ウィンドウの真ん中がStartedでRTがピコピコすればOKだ。OKならStopしておこう。なんらかのエラーが出たならばググるしかない、エラーの場合相当苦労することになるかもしれないので、覚悟しておこう。
なお、ここでの設定は暫定的に音を出すための設定です。入力を殺していますしちゃんとした設定があるはずです。

エディタ

emacs,vim,geditどれでも好きなものを選択可能だ。ここではvimの使い方を紹介する。今年はvimが大流行すると思うのでこの機会に覚えておくのも良いかもしれない。vimでscを扱う場合には、scvimというプラグインを利用する、もちろんvimは必要だがrubyも必要だ。

$ sudo apt-get install ruby vim

scvimはscをビルドした際にインストールされるので、これで準備は完了になる。

使う

ここまでインストール、設定にかなりの労力が必要だったかと思われる。これがlinuxだ!
では早速使ってみよう。

$ scvim

なんか別ウィンドウが立ち上がり、動いている。ヘルプを構築?しているらしいのでちょっと待とう。Welcome to SuperCollider, type :SChelp for helpと表示されればOKだ。後は音を鳴らすだけだ。

s.boot;
{SinOsc.ar()}.play;

と入力して、s.boot;行でF6ボタンを押してみよう(fn+F6ボタンの場合も)。次にplay行でF6ボタン。サイン波が聴こえれば完了だ。お疲れ様でした!

いじょう

さて、これで仮想環境でSCが実行できることになりました。別にlinuxでSCが使いたい!というわけではありません。macありますし。1台のマシンでサーバ、クライアント環境を構築したかったわけですよ。仮想上のSCをサーバとして、実機側をクライアントとして色々やれればなあ、と。サーバサイドSC実現できると良いですね!

今回は初心に返り、linux環境を構築してみました。なんと本ブログにメモを取り始めてどうやら4年経つようです。全くもってSC力は上がっていないのはなんなんでしょう?むしろ始めて数週間の方が書けた気がします。。

以上となります、どうぞ今年もよろしくお願い致します。

vim 4 SuperCollider

じゃあscvim使ってみよう。
macということで、scvimオリジナルじゃなくて、https://github.com/sbl/scvimこっち使います。linuxもそのうち試してみます。
環境はlion,vim7.3(ただし、機能をフルに使うにはmvimがいるっぽいです。http://code.google.com/p/macvim/

インスコ

pathogenでplugin管理

別に使用しなくても良いですが、scvimのreadmeに使ってねとあるので。

$ mkdir ~/.vim/autoload
$ mkdir ~/.vim/bundle
$ cd ~/.vim/bundle
$ git clone git://github.com/tpope/vim-pathogen.git
$ ln -s ~/.vim/bundle/vim-pathogen/autoload/pathogen.vim ~/.vim/autoload/
scvim
$ git clone git://github.com/sbl/scvim.git ~/.vim/bundle/supercollider
$ ln -s ~/.vim/bundle/supercollider/sc/SCVim.sc ~/Library/Application\ Support/SuperCollider/Extensions/SCVim.sc
$ vi .vim/bundle/supercollider/plugin/supercollider.vim

下記のように9-12行目に,*.rtfを加える
au BufEnter,BufWinEnter,BufNewFile,BufRead *.sc,*.scd,*.rtf runtime ftplugin/supercollider.vim
au BufEnter,BufWinEnter,BufNewFile,BufRead *.sc,*.scd,*.rtf set filetype=supercollider
au BufEnter,BufWinEnter,BufNewFile,BufRead *.sc,*.scd,*.rtf let &iskeyword="@,48-57,_,192-255"
au BufEnter,BufWinEnter,BufNewFile,BufRead *.sc,*.scd,*rtf runtime indent/sc_indent.vim

環境によっては、31行目のF12をF7とかに変える
(うちのキー配列ではfn+F12ボタンだとダッシュボートにいっちゃう)
au Filetype supercollider,supercollider_help nmap   :call SClang_thisProcess_stop()
textutil

sc界隈ではrtfファイルが多いので、見られるようにしておく

mkdir ~/.vim/bundle/textutil
http://www.vim.org/scripts/script.php?script_id=1432
textutil.vimを.vim/bundle/textutil/に置く
neocomplcache

こいつがないとvimは始まらない!

$ git clone git://github.com/Shougo/neocomplcache.git ~/.vim/bundle/neocomplcache
vimrc

とりあえずこんな感じに。ググれば沢山例が引っかかりますので参考にしていじってみてください。

$ vi ~/.vimrc
"基本設定
set nocompatible
filetype indent plugin on
set encoding=utf-8
set number
"colorscheme desert とか好きなヤツを
syntax on
set hidden
set wildmenu
set showcmd

"タブ関係
set tabstop=4 shiftwidth=4 softtabstop=0
set autoindent smartindent

"検索
set ignorecase
set incsearch

"カッコ
imap {} {}
imap  
imap () ()
imap “” “”
imap ” ”
imap <> <>
imap “ “

"全角スペースを強調
highlight zenkakuda ctermbg=7
match zenkakuda / /

"pathogen設定
filetype off
call pathogen#runtime_append_all_bundles()
call pathogen#helptags()
filetype on

"textutil設定
let g:textutil_txt_encoding='utf-8'

"neocomplcache設定
"~/.vim/bundle/neocomplcache/doc/neocomplcache.jaxのEXAMPLES 764-848行をコピペ

使い方

とりあえず、tkckの資料を開いてみましょう。

$ vi tksc#8.rtf
サーバ
  • 立ち上げは、:SClangStart(たまにちゃんと立ち上がってくれないので、開いた別窓でctl+Dとかでbashを抜けると動いてくれたりします)
  • 停止は、:SClangKill

:scの時点でtabキー押すと候補選択できます。

コード実行
  • 1行実行は、その行にカーソルを持っていき、F6キー(キー配列によってはfn+F6キー)
  • ブロック実行は、Vでビジュアルモードになり、実行したいブロックをn,pキーか上下キーで選択して、F5キー
  • コード停止は、上で変更したF7キー(デフォルトではF12キー)
補完

neocomplcacheが勝手に補完候補呼び出してくれます。sinと打つと候補として、UnaryOperatorのsin,sinhがあがります。tabキーかctl+p,nで選択。
ただし、うちの環境ではなぜかオブジェクトを候補として出してくれないので、調査中です。~/.vim/bundle/supercollider/syntax/supercollider_objects.vimはちゃんと読み込まれているし、シンタックスハイライトはちゃんと機能しているんですけれど。。。

スニペット

デフォルトで多少~/.vim/bundle/supercollider/snippets/supercollider.snippetsに書かれています。
試しにsdefを打ってみて下さい。候補が出たらtabなどで選択して、ctl+kで展開されます。シンセ名にカーソルが遷移しますので、適当に変えたら、ctl+k。snd = のところに遷移します。

gui

cocoaはダメポです。swingはいけますので、GUI.swingにしておきましょう。
[参考]http://supercollider.jp/modules/xhnewbb/viewtopic.php?topic_id=215

いじょう

vimに慣れていれば結構便利です。ただ、初期設定は面倒ですし、vimに慣れていないと全く使用するメリットがないかと思われます。ちょっとオシャレで出来るヤツ風を装いたいなら、scvimだとそれっぽく見えるかもしれません。
仕事でvimをひたすら使っているので、当面scvimで行きたいと思います。

サーバサイドSuperCollider

基本

SCはaudio serverであるscsynthと、クライアントであるsclangに分かれて成り立っています。さらに、sclangはプログラム言語部分とインタプリタ部分に分かれています。sclangの機能として2つあり、サーバのクライアントとしての機能と、言語のインタプリタとしての機能です。要はコードを解釈して、サーバであるscsynthにメッセージを送るという役割を担っています。また、sclangとscsynthはOSCプロトコルを利用して情報のやり取りをしています。
SC内の一連の流れは、SCプログラミング言語で書かれたものをsclangのインタプリタが解釈しOSCメッセージに落とし込む。次に、そのOSCメッセージをsclangがscsynthに送信。scsynth側で送られてきたメッセージに従い処理する。となっています。なんらかの方法を用いて、scsynthにシンセを作成し、そのシンセに対して挙動を制御できれば音が鳴るってことです。じゃあ、scsynthだけ動かすサーバたてて、クライアントはsclangじゃなくてなんでも使って良いんだよね!?ClientVsServer.htmlを読むとまさにそんなことが書いてあります、もっとも、sclang使うとscsynthへSynthDefsの作成と読み込みが楽だし、sclang使ってね!と書いてありますけれど。
sclangからscsynthをコントロールするには、Serverオブジェクトを利用します。Serverオブジェクトはクライアント(sclang)と同じマシンで動いているscsynthを表すことが多いですが、リモートマシン上のscsynthでもおkです。

なんか間違っているかも。。。

具体例

端末は1台でも試せますが、できれば2台端末を用意してください。サーバとして使用する側にてターミナルでifconfigして、ループバックアドレスではないアドレスが端末に割り振られていることを確認してください。以下は192.168.11.2が割り振られている場合の例になります。クライアントからServerオブジェクトを使えば、リモートホストのscsynthをコントロールできると述べました。
では、クライアント側から以下を実行してみてください。

	~myServer = Server( \test, NetAddr( "192.168.11.2", 57110 ) );
	~myServer.makeWindow;

お、いつもと違うGUIが立ち上がりましたね。boot,quitボタンがありません。

	~myServer.boot;

リモートでのサーバブートは許可されていないためfailedになりました。
じゃあリモートサーバをbootするには、どうするのっと。
scsynthを立ち上げたいサーバ(ホスト)にsshなどでログインし、ターミナルにて"$ scsynth -u ポート番号"でブートさせることが可能です。ポート番号はNetAddrで指定したポートになりますよ。
macをサーバとしたときには、ディレクトリを/Application/SuperColliderに移動しておかないとエラーが出るかもしれません。
もちろん、サーバ側でSuperCollider.appとかを立ち上げて通常通りlocalサーバをブートしても行けます。

じゃあ、早速サーバ側で音を鳴らしてみましょう。

	~myServer.sendMsg( "/s_new",  \default,  x =~myServer.nextNodeID, 0, 0 );
	~myServer.sendMsg( "/n_free", x );

簡単ですね。ただし、defaultシンセはあらかじめサーバ側に保存されているものです。クライアント側からシンセを定義して送りたいんだ! 送り先をデフォルトサーバのsではなく、先ほど定義したサーバを指定するだけです。

	(
		SynthDef( "network_test", {
			arg freq=1200;
			var signal,env;
			signal = SinOsc.ar( freq, 0, 0.5 );
			env = EnvGen.ar( Env.perc ( 0, 1, 1, -12 ), doneAction: 2 );
			Out.ar( 0, Pan2.ar( signal * env, 0 ) );
		}).send( ~myServer );
	)

	~myServer.sendMsg( "/s_new", \network_test, x = ~myServer.nextNodeID, 0, 0 );
	~myServer.sendMsg( "/n_free", x );

ちゃんと音が鳴りました!
scsynthとsclangが同じ端末だと、なんか起動していないから送れないよ。とかエラーでるかもしれませんが、たぶん送れてます。
サーバの終了はいつもの。

	~myServer.quit;

local vs internal

ん?これってlocalサーバ、internalサーバ?ここでちょっと寄り道してlocalサーバとinternalサーバのお話。
何が違うの?って、ちゃんとヘルプに書いてあります。localサーバはsclangとscsynthが違うアドレス空間で動いています。internalはsclangとscsynthが同じアドレス空間で動いています。internalはメモリを共有しているので、scopeとかSharedIn/SharedOutが使えるよ。んで、(sclangとscsynth間での)メッセージのレイテンシが小さいよ。localサーバは別々なのでsclangがこけてもscsynthは生きているから音は出続けてロバストだよ。らしいです、家で使うときはinternalで良いんじゃないですかね?
scsynthとsclangが別端末の場合は、localサーバと同じですね。scopeは使えません!

いじょう

SCは構造としてサーバとクライアントが分離されており、ある意味サーバサイドであり、クライアントからコードを送ってサーバ側で音を鳴らすことができることが分かりました。
ここで、問題となるのがレスポンス。残念ながらサーバ側で音が鳴るだけです。クライアント側に音を返してこそサーバサイドだ!どうやってクライアントに音を返すか?SC側に実装はされていないような気がします。じゃあ、ストリーミング?

超意訳OSC with SuperCollider

OSC_communication

プログラム間のOSCコミュニケーションは、あるアプリから他のアプリにメッセージを送ることでやりとりされる。違うコンピュータでアプリが走っていてもおk。OSCの基礎はUDP/TCPだよ。
SuperColliderではターゲットアプリのNetAddrと他のアプリからメッセージを受信するOSCresponderNodeを作ってコミュニケートするよ。

他のアプリにOSCメッセージを送るよ

他のアプリとコミュニケーションを確立させるには、他のアプリがどのポートでメッセージを受け取るのか知っておく必要があるよ。
例えばさ、7771ポートでリスンしているアプリがあるとするよ。そんな時は、下のようにNetAddrつくってメッセージ送るんだ。

	b = NetAddr.new( "127.0.0.1", 7771 );
	b.sendMsg( "/helle", "there" );
他のアプリからOSCメッセージを受け取るよ

SCが他のアプリからメッセージを受け取るときは、他のアプリはSuperColliderのリスニングポートにメッセージを送らなくちゃダメなんだ。
SCのデフォルトポートは57120だ。でもたまに違うよ、SCを立ち上げたとき既に使われちゃっていた時とかね。
今のデフォルトポートは下で取得できるから確認してね。

	NetAddr.langPort;

IPアドレスも一緒に取得したいならこっちだ。

	NetAddr.localAddr;

送られてきたメッセージを受け取るには、SC側でOSCresponderを作成しなくちゃならないんだ。
メッセージ送信側アプリがメッセージを送る固定ポートを持っているなら、OSCresponderでそのIPアドレスとポートからメッセージを受け取るように設定するんだ。これは、メッセージ送信アプリが7771番ポートにメッセージ送信しているときの例だよ。

	n = NetAddr.new( "127.0.0.1", 7771 );
	o = OSCresponderNode.new( n, "/goodbye", 
		{
			arg time, resp, msg;
			[ time, resp ].postln;
		}
	).add;
	o.remove;

なんでOSCresponderじゃなくて、OSCresponderNodeかって?同じメッセージ名('/aaa'とか)を受け取るOSCresponderを作ったとすると、2個目のOSCrespoderは1個目を上書きしちゃうんだ。同時に1メッセージあたり1つのOSCresponderしか作成できないってことだね。例えば、下の方でサンプルがあるけれど'/tr'を受け取る時とかは複数作成する場合があるよ。
でもOSCresponderNodeはそんな制限はないんだ!同じ名前のOSCresponderNodeを共存させられるんだ。
(もういらなくなった時にOSCresponderNodeを消すには作ったOSCresopnderNodeをずっと追跡していなくちゃいけないってことでもあるね)詳しくはOSCpathResponderをみ・て・ね!

複数ポートからのメッセージを受け取るよ

固定ポートにメッセージを送らないアプリもあるよね、問題ない、どのポートからでもメッセージを受け取れるようにOSCresponderNodeを設定できるんだ。
OSCresponderNodeの第一引数としてNetAddrを指定する代わりに、nilだ。

	o = OSCresponderNode.new( nil, "/goodbye", 
		{
			arg time, resp, msg;
			[ time, resp ].postln;
		]
	).add;
	o.remove;
受信トラフィックのテスト

全て受信したOSCメッセージはrecvOSCmessageかrecvOSCbundleを呼ぶんだ。受信トラフィックを見るには、recvOSCfuncを使おう。(何やっているのかよくわからないねw)

	(
		thisProcess.recvOSCfunc = {
			arg time, addr, msg;
			if( msg[0] != 'status.reply' )
				{ "time: % sender: %\nmessage: %\n".postf( time, addr, msg ); }
		}
	);
	thisProcess.recvOSCfunc = nil;

OSCresponderNode

OSCresponderNode

あるOSCアドレスからあるコマンドを受信したときに呼び出される関数を設定する、それがOSCresponderNodeだ。OSCresponderのように同じインターフェースがあるけれども、OSCresponderNodeは同じコマンドに対して複数のレスポンスが許可されているんだ。

システムプロセスでOSCresponderNodeが関数を評価することに気をつけるんだ!アプリケーションプロセスにアクセスするためには、deferメッセージを使うんだ。GUIのアクセスみたいにね。(なに言ってんのこれ?)

SCにメッセージを送るアプリでは、SCサーバ/SClangに送るメッセージを区別させなくちゃならない。サーバメッセージはServer-Command-Referenceに書いてあるし、SCサーバのポートに送らなきゃダメなんだ。(デフォルト値が57110でs.addr.portだね)SCサーバに送られてきたメッセージはSClang内のどのOSCresponderでも処理されないんだ。後述するけれど、SynthDefで直接OSCメッセージの送受信ができないってことなんだ。

OSCresponderで処理される外部クライアントからのOSCメッセージは、デフォルト値が57120のSClangのポートに送らなくちゃいけない。どのポートがリスン状態かはNetAddr.langPortで確認できるよ。

OSCresponderNode設定
	/*
		受信側設定
	*/
	//NetAddr.new( hostname, port ) newメッセージは省略できるよ!
	n = NetAddr( "127.0.0.1", 57120 );
	
	//OSCresponderNode.new( addr, cmdName, action )
	o = OSCresponderNode( n, '/chat', 
		{
			arg t, r, msg;
			( "time:" + t ).postln;
			msg[1].postln;
		}
	).add;
	
	/*
		送信側設定
	*/
	//NetAddr.new( hostname, port )
	m = NetAddr( "127.0.0.1", 57120 )
	
	//sendMsg( args… ) send a message without timestamp to the addr.
	//第一引数でOSCコマンドを指定して、第二引数のメッセージをOSCコマンドに送るよ。
	m.sendMsg( "/chat", "Hello App 1" );
	
	//sendBundle(timestamp, args… ) send a bundle with timestamp to the addr. 
	//タイムスタンプと一緒に複数メッセージを送るよ。詳しくはServerのヘルプみてね。
	(
		m.sendBundle( 2.0, [ "/chat", "hello app 1" ], [ "/chat", "hello world" ] );
		m.sendBundle( 0.0, [ "/chat", "hello app 1" ], [ "/chat", "hello world" ] );
	)
	
	//disconnect;	close TCP connection.
	//remove; remove and deactivate the OSCresponder
	//もう使わないから捨てるよ
	m.disconnect;
	n.disconnect; o.remove;
どんなクライアントからもデータ受け取るよ

前述したけれど、第一引数にnil

ポートはなんでもいいけれど、特定ホストからのみメッセージ受け取るよ
	//NetAddrのポートを指定せずに、nilにするんだ
	n = NetAddr( "127.0.0.1", nil );
	o = OSCresponderNode( n, '/test', { | t, r, msg | msg.postln; } ).add;
	o.remove;
SCサーバからのメッセージを受け取るよ

synthdefは一旦SCサーバに保存して、SCサーバで読み込まれるんだけれど、SCサーバから直接OSCメッセージの送受信はできないんだ。だから一旦OSCresponderNodeで受信しているんだよ。外部アプリにOSCメッセージを送りたい時は、OSCresponderNode内の関数でsendMsgしてあげると上手くいくよ。
ここでのsynthdefは'/tr'にDust.krタイミングで0.9の値を送っているだけのシンセだ。OSCresponderNodeで'/tr'に送られてきたメッセージを受信しているのが分かるかな。

	(
		s = Server.local.boot;
		s.notify;
	)
	(
		SynthDef( "help-SendTrig", 
		{
			SendTrig.kr( Dust.kr( 1.0 ), 0, 0.9 );
		}).send( s );
		
		//これなんだけれども、OSCresponderで定義すると上書きされちゃうから"this is another call"しか出力されないんだ
		a = OSCresponderNode( s.addr, '/tr', { | t, r, msg | [ t, r, msg ].postln; }).add;
		b = OSCresponderNode( s.addr, '/tr', { | t, r, msg | "this is another call".postln; }).add;
		
		x = Synth.new( "help-SendTrig");
	)

いじょう

ヘルプは不親切ですなあ。なんか間違っている気もするけれど気にしない。
OSCBundleはまだ使わないので、あとはOSCpathResponder知っておけばいいのかな。

SuperCollider + OSCeleton

ちょい前にhttp://www.kinecthacks.com/kinect-with-supercollider/がTL上に流れてきまして、ああそろそろ試すか。ってことで、OSCeletonです。たまにはkinectも使ってあげないとね!

準備

以前紹介しました、Unity3d+kinect参照に

  • libtool,libusb-devel
  • OpenNI
  • NITE
  • SensorKinect

これらをインストールします。最新版であれば、lion環境でも動作します。

OSCeleton

メインのOSCeletonですが、https://github.com/Sensebloom/OSCeletonからosceleton-v1.2.1_OSX.zipがDLできますので、そのままこいつ使ってしまいましょう。解凍するだけでおkです。

使う

SC側コード

とりあえず、シンセの作成とfreqを変化させるだけ。

/*
*	osceleton test
*/
/*
*	osc setting
*/
(
	//receive osc messages from osceleton
	~oscReceive = NetAddr("127.0.0.1", 57120);
	
	//アドレスはこいつら, /new_user, /new_skel, /lost_user, /joint
	
	//新規ユーザ発見時
	OSCresponder(nil, '/new_user',{
		arg t, r, msg;
		"create a new synth".speak;
		s.sendMsg("/s_new", \osceleton_sinosc, x = s.nextNodeID,0,0 );
		[t,msg].postln;
	}).add;
	//ユーザ見失った。。。
	OSCresponder(nil, '/lost_user', {
		arg t, r, msg;
		"delete a synth".speak;
		s.sendMsg("/n_free", x);
		[t,msg].postln;
	}).add;
	
	//ここから各種ジョイントのx,y,z取得して、諸々のシンセにパラメータを振っていく、など好きな処理を記述していく。[/joint, 体の部位, ユーザ番号, x, y, z]のメッセージを受信するため、体の部位ごとにswitch文とかで挙動を分ければ良いかと。ぼっちなのでユーザは分けません!
	a = OSCresponder(nil, '/joint', {
		arg t, r, msg;
			switch(msg[1],
			//head, neck, torso, r_shoulder, r_elbow, r_hand, l_shoulder, l_elbow, l_hand, r_hip, r_knee, r_ankle, r_foot, l_hip, l_knee, l_ankle, l_foot

			//head
			\head, { 
				( "joint:" + msg[1] + "| user:" + msg[2] ).post; msg[3..].postln; 
				s.sendMsg( "/n_set", x, "freq", msg[3]*1000 );
				},
			//neck
			\neck, { ( "joint: " + msg[1] + "| user : " + msg[2] ).post; msg[3..].postln; }
			//とまあ面倒っちゃ面倒っす
		); 
		//[t,msg].postln;
	}).add;
	
	//check
	NetAddr.localAddr;
)

/*
*	synth def
*/
(
	SynthDef("osceleton_sinosc",{
		arg pan = 0, outBus = 0, freq = 440;
		var signal;
		signal = SinOsc.ar(freq,0,0.4);
		Out.ar(outBus,
			Pan2.ar(signal,pan);
		);
	}).store;
)
/*
*	osc clear ?
*/
OSCresponder.initClass;
OSCeleton
./osceleton-v1.2.1_OSX -p 57120

デフォだとポートが7110なのでSCデフォの57120にしてやると余計なことを考えずに済む。

いじょう

OSCeletonだけだと映像出力がないので、位置とかがよくわかりませんw
受信するメッセージが多いのでちょっと面倒かもです。別に全部使わずに、絞って使えば良いだけですが。
OSCeletonは結構CPU喰いますので注意を!