🎵

SuperColliderでLive Coding!- 基礎構文編 -

に公開

はじめに

みなさん、今日も今日とてライブコーディングやってますか?

ライブコーディングのツールとしてTidalCycles, Sonic Pi, ORCAとかも人気ありますが、SuperColliderは、より音響処理に特化したプログラミング言語になります。

私自身、大分昔にSuperColliderで音作りをやっていた時期があったのですが、この令和の時代、色々なツールやソフトウェア音源が溢れる中、改めてSuperColliderに触れてみたくなりました。

あと、SuperCollider何気に音がいいんだわ!

今回は、そんなSuperColliderの世界に足を踏み入れる第一歩として、プログラムを書くための基礎構文を一緒に見ていきましょう。

セットアップ

インストール

まずはSuperColliderを手に入れましょう。SuperColliderは無料で使えるオープンソースソフトウェアです。公式サイトから自分のOS(Mac, Windows, Linux)に合ったものをダウンロードしてインストールしてください。

SuperCollider公式サイト Downloadページ

インストールすると、コードを書くためのエディタや、プログラムを実行する環境が全部入ったSuperCollider IDEが使えるようになります。

コードの実行

SuperCollider IDEを開いたら、早速コードを実行してみましょう。

  1. コード入力: IDEの左側の広いエリアがコードエディタです。ここにコードを書きます。
  2. 一行実行: 実行したい行にカーソルを合わせて Cmd + Enter (Mac) または Ctrl + Enter (Windows/Linux) を押します。
  3. ブロック実行: 複数行をまとめて実行したい場合は、実行したい範囲を () で囲み、そのカッコのすぐ後ろあたりにカーソルを置いて Cmd/Ctrl + Enter を押します。
  4. Post Window: IDEの下部(または右側)にあるPost Windowというエリアに、実行結果や postln で出力したメッセージが表示されます。エラーが出たときもここに表示されるので、よく見るようにしましょう。

試しにこれを実行してみてください。

// この行にカーソルを置いて Cmd/Ctrl + Enter !
"SuperCollider、はじめました!".postln;

// 次はこのブロックを実行! (カッコの後ろで Cmd/Ctrl + Enter)
(
	var greeting = "Hello";
	var target = "World";
	(greeting ++ ", " ++ target ++ "!").postln; // ++ は文字列を繋げる演算子
)

Post Windowにメッセージが表示されましたか?
これがSuperColliderとの基本的な対話方法です。

とりあえず音を鳴らしてみよう

構文を学ぶ前に、まずはSuperColliderで音を出す雰囲気を掴んでみましょう!

以下のコードブロックを () ごとコピーして、SuperCollider IDEに貼り付け、カッコの後ろで Cmd/Ctrl + Enter を押してみてください。
もしまだサーバーが起動していない場合は、先に s.boot; というコードを実行する必要があります。

// サーバー起動
s.boot;

// 音色を定義して再生
(
SynthDef(\sine, { |freq = 440, amp = 0.1, gate = 1, pan = 0, out = 0|
	var sig, env;
	env = EnvGen.kr(Env.perc(0, 0.25), gate, doneAction: Done.freeSelf);
	sig = SinOsc.ar(freq + (env * 20), 0, amp);
	Out.ar(out, Pan2.ar(sig * env, pan));
}).add;

SynthDef(\hihat, { |amp = 0.2, out = 0, decay = 0.1|
	var noise, env;
	noise = WhiteNoise.ar(amp);
	env = EnvGen.kr(Env.perc(0.005, decay), doneAction: Done.freeSelf);
	Out.ar(out, Pan2.ar(noise * env));
}).add;

t = Task({
    loop {
		[60, 62, 63, 65, 67].scramble.do({ |midi|
			Synth(\sine, [freq: midi.midicps, amp: 0.3, pan: -1]);
			Synth(\sine, [freq: 60, amp: 0.3]);
			Synth(\hihat, [decay: 0.01, amp: 0.05]);
			0.125.wait;
			Synth(\sine, [freq: (midi + 5).midicps, amp: 0.3, pan: 1]);
			Synth(\hihat, [decay: 0.01, amp: 0.05]);
            0.125.wait;
			Synth(\sine, [freq: (midi + 3).midicps, amp: 0.1]);
			Synth(\hihat, [decay: 0.1, amp: 0.05]);
			0.125.wait;
        });
    }
}).play;
)

// 停止
t.stop;

どうでしょう? いい感じの電子音が鳴りましたか?
SynthDef で音の作り方を定義して、 Synth でそれを実際に鳴らす、というのがSuperColliderの音作りの基本です。コードの意味は分からなくても大丈夫。今は「こんな感じで音が出るんだな」と感じてもらえればOKです。

では、ここから本格的にSuperColliderの構文を見ていきましょう!

構文

コメント

まずは、コード上にコメントを残しましょう!

// これは一行コメント。 スラッシュ2つから行末までがコメント扱い。

/*
こっちは複数行コメント。
スラッシュとアスタリスクで囲まれた範囲が
全部コメントになります。
*/

// 例
(
var speed = 10;
speed.postln; // 移動速度 (単位: m/s)
)

メッセージ出力

IDEのPost Windowにメッセージを出力します。

  • .postln: メッセージを出力して改行します (一番よく使う)。
  • .post: メッセージを出力しますが改行しません。
  • .postf(フォーマット文字列, 値...): C言語の printf みたいに書式指定して出力できます。
(
var score = 95;
var playerName = "Player One";

score.postln;          // -> 95
playerName.postln;   // -> Player One

"Raw score: ".post;    // 改行しない
score.postln;          // -> Raw score: 95

"Player: %, Score: %".postf(playerName, score).postln; // -> Player: Player One, Score: 95
)

ブロック

SuperColliderでは (){} という2種類のブロックがとても重要です。

  • () (丸括弧):

    • 計算の優先順位を指定する(数学と同じ)。 (1 + 2) * 3
    • 複数行のコードをその場で即時実行するためのグループを作る。 Cmd/Ctrl + Enter で実行するのは主にこれ。
  • {} (中括弧):

    • 関数 を作る。
    • 後で .value メソッドなどで呼び出して実行する。 if 文や SynthDef の中など、様々な場面で登場する。
(
var x, myfunc, result;

// () ブロック: 囲んだコードがすぐに実行される
x = (
	"これは () ブロックの中".postln;
	10 + 20; // このブロックの値は最後に評価された式の結果 -> 30
);
("x の値: " ++ x).postln; // -> x の値: 30

// 関数を作る
myfunc = {
	"これは {} ブロックの中 (関数が実行された!)".postln;
	100 + 200; // この関数の返り値 -> 300
};

// 関数を実行
result = myfunc.value; 

// ここで "これは {} ブロックの中..." が表示される
("関数の結果: " ++ myfunc.value).postln; // -> 関数の結果: 300

// 関数を即時実行
{ (20 + 30).postln }.value
)

変数

計算結果やオブジェクトなどを一時的に保存しておくための名前付きの入れ物です。

  • var: {}() ブロックの中でローカル変数を宣言します。そのブロック内でのみ有効です。宣言しないと使えません。
  • =: 変数に値を代入します。
  • ~ (チルダ): 環境変数var で宣言しなくても使えて、IDEのどこからでもアクセスしやすいグローバル変数のようなもの。設定値や共有したいオブジェクトの保存によく使われます。
  • az: インタプリタ変数。アルファベット1文字の変数で、宣言なしでいきなり使えます。ちょっとしたテストには便利ですが、s (サーバー) や b (バッファ) など特別な意味を持つものもあるので、多用は避けたほうが無難です。
(
var message = "Hello"; // ローカル変数 message を宣言し、"Hello" を代入
var count = 0;       // ローカル変数 count を宣言し、0 を代入

~speed = 1.5;        // 環境変数 ~speed に 1.5 を代入

x = 440;             // インタプリタ変数 x に 440 を代入

message.postln;      // -> Hello
count = count + 1;
count.postln;        // -> 1
~speed.postln;       // -> 1.5
)

// 別の実行ブロックからでも ~speed は参照可能
(~speed * 10).postln;   // -> 15.0

変数に入れる値には、数値、文字列、配列など、様々な型 (Type) があります。SuperColliderは動的型付け言語なので、変数宣言時に型を指定する必要はありませんが、値には必ず型があります。

  • Integer: 整数 (1, -10, 0)
  • Float: 浮動小数点数 (1.0, 3.14, -0.5)
  • String: 文字列 ("abc", "SuperCollider")
  • Symbol: シンボル (\note, \freq) - 文字列に似ていますが、主に名前や識別子として使われます。SynthDef名や引数名など。
  • Boolean: 真偽値 (true, false)
  • Nil: nil - 「何もない」ことを示す特別な値。
  • Array: 配列 ([1, 2, 3], ["a", \b, 1.5]) - 複数の値を順序付けて格納。
  • Function: 関数 ({ 1 + 1 })

.class メソッドを使うと、その値(オブジェクト)がどのクラス(型)に属しているか調べられます。

(
(5).class.postln;        // -> Integer
(5.0).class.postln;      // -> Float
"text".class.postln;     // -> String
\symbol.class.postln;    // -> Symbol
true.class.postln;       // -> Boolean
nil.class.postln;        // -> Nil
[10, 20].class.postln;   // -> Array
{ nil }.class.postln;    // -> Function
)

演算子

数値計算や比較、論理的な判断を行うための記号です。

  • 算術演算子: + (足し算), - (引き算), * (掛け算), / (割り算), % (剰余), ** (べき乗)
  • 比較演算子: == (等しい), != (等しくない), < (小なり), > (大なり), <= (小なりイコール), >= (大なりイコール) - 結果は truefalse
  • 論理演算子: && (AND: かつ), || (OR: または) - truefalse を組み合わせる。
(
(100 / 5).postln;     // -> 20
(7 % 3).postln;      // 7 ÷ 3 の余り -> 1
(2 ** 4).postln;     // 2の4乗 -> 16

(5 == 5.0).postln;   // -> true (IntegerとFloatでも値が同じならtrue)
(10 < 20).postln;    // -> true

((1 > 0) && (1 < 10)).postln; // (true AND true) -> true
((1 > 0) || (1 > 10)).postln; // (true OR false) -> true
)

オブジェクト

SuperColliderの超重要コンセプト! ほぼすべてのものがオブジェクトです。
数値、文字列、配列、関数、SynthDef、サーバー(s) など、全部オブジェクトです。

オブジェクトに対して何か操作をしたいときは、メッセージを送ります

構文: オブジェクト.メッセージ(引数)

  • オブジェクト: メッセージを受け取る相手 (レシーバー)
  • .: ドットで区切る
  • メッセージ: 実行したい操作の名前 (メソッド名)
  • (引数): 操作に必要な追加情報 (引数がない場合は () も省略可能)
(
// 文字列オブジェクトにメッセージを送る
"hello".size.postln;             // .size メッセージ -> 5 (文字数)
"world".toUpper.postln;        // .toUpper メッセージ -> "WORLD" (大文字化)

// 配列オブジェクトにメッセージを送る
[1, 5, 2, 4, 3].sort.postln;      // .sort メッセージ -> [ 1, 2, 3, 4, 5 ] (ソート)
[1, 2, 3].includes(2).postln;   // .includes(引数) メッセージ -> true (2が含まれるか)

// 数値オブジェクトにメッセージを送る
(-5).abs.postln;                 // .abs メッセージ -> 5 (絶対値)
3.14159.round(0.01).postln;      // .round(引数) メッセージ -> 3.14 (指定桁で四捨五入)
10.rand.postln;                  // 0から9までの整数乱数を生成

// メソッドチェーン: 返ってきたオブジェクトに続けてメッセージを送る
"supercollider".scramble.reverse.postln; // ランダム化して、さらに逆順にする
)

SuperColliderでは、常に「どのオブジェクトに、どんなメッセージを送るか?」を考えてコードを書くことになります。

関数

{} で囲んで作る、処理のまとまりです。
これもオブジェクトの一種 (Function クラス)。

  • 定義: { |引数1, 引数2| var ローカル変数; 処理... 最後の式の値が返り値 }
    • 引数は | または arg で囲んで宣言します。|a, b|arg a, b;
    • 関数内ローカル変数は var で宣言します。
  • 実行: .value(引数) または .value (引数なし)。 .() という書き方もあります。
  • 返り値: 関数内で最後に評価された式の値が、関数の実行結果として返されます。
(
var add, sum, greet, power;

// 2つの数を足す関数
add = { |x, y|
	var result;
	result = x + y;
	"Adding % and %".postf(x, y).postln;
	result; // ← これがこの関数の返り値になる
};

// 関数を実行
sum = add.value(11, 22); // "Adding 11 and 22" が表示される
sum.postln; // -> 33

// 引数なし、処理だけする関数
greet = { "Hello!".postln; };
greet.value; // -> "Hello!" が表示される

// デフォルト引数
power = { |base, exponent = 2| base ** exponent };
power.value(10).postln;      // exponent はデフォルトの 2 -> 100
power.value(10, 3).postln;   // exponent を 3 で指定 -> 1000
)

関数は、処理を再利用したり、if 文や SynthDef など他の構文に処理を渡したりするのに不可欠です。

配列

複数の値を順番に格納しておくためのコレクションです。

  • 作成: [要素1, 要素2, ...]
  • アクセス: 配列[インデックス] (インデックスは 0 から始まります!)
  • よく使う操作 (メッセージ):
    • .size: 要素数
    • .at(インデックス): 要素を取得 (配列[インデックス] と同じ)
    • .put(インデックス, 値): 要素を書き換え
    • .add(値): 末尾に要素を追加 (元の配列を変更!)
    • .++(他の配列): 2つの配列を連結して新しい配列を返す
    • .do { |要素, インデックス| ... }: 各要素に対して処理を実行
    • .collect { |要素| ... }: 各要素を変換して新しい配列を返す
    • .scramble: シャッフルして新しい配列を返す
    • .choose: ランダムに1要素を選ぶ
(
var notes, instruments, freqs;
notes = [60, 62, 64, 65]; // MIDIノート番号の配列
instruments = [\piano, \guitar, \sine];

notes[0].postln; // -> 60 (最初の要素)
instruments.size.postln; // -> 3

notes.do { |noteNum, i|
	("Index %: Note %").postf(i, noteNum).postln;
};

// 各ノート番号を周波数(Hz)に変換した新しい配列を作る
freqs = notes.collect { |noteNum| noteNum.midicps }; // midicpsでノート番号を周波数に変換する
freqs.postln;

instruments.choose.postln; // \piano, \guitar, \sine のどれかがランダムで表示される
)

制御構文

プログラムの実行の流れをコントロールします。

  • if: 条件分岐。「もし〜ならばAを、そうでなければBを実行する」。

    • 構文: if( 条件式(Booleanを返す) , { 真の時の処理 (Function) } , { 偽の時の処理 (Function) } )
    • 偽の時の処理は省略可能: if(条件, { 真の時の処理 })
  • 繰り返し:

    • N.do ({ |カウンター変数| ... }): N回繰り返す (カウンターは 0 から N-1)。
    • while ({ 条件式ブロック(Function) }, { 繰り返し処理ブロック(Function) }): 条件式が true を返す間、繰り返し処理を実行する。無限ループに注意!
    • for(開始値, 終了値, { |カウンター変数| ... }): 指定範囲で繰り返す。
    • 配列の .do も繰り返し処理の一種。
(
var temperature = 25, counter = 0, total = 0;

// if文
if(temperature > 30, {
	"暑い!".postln;
}, {
	if(temperature < 10, {
		"寒い!".postln;
	}, {
		"快適".postln;
	});
}); // -> 快適

// N.do ループ
8.do({ |i|
	("繰り返し " ++ (i + 1) ++ " 回目").postln;
});

// while ループ
while ({ counter < 5 }, {
	total = total + counter;
	("counter: %, total: %").postf(counter, total).postln;
	counter = counter + 1; // counter を増やさないと無限ループ!
});

// for ループ
for(0, 4, { |i|
    ("forループ " + i).postln;
});
)

上記以外の制御構文もあるので、詳しくはコチラを参照してください。

まとめ

お疲れ様でした!今回はSuperColliderの基本的な構文を見てきました。

  • コードの実行方法とPost Window
  • コメント、メッセージ出力(.postln)
  • 重要な {} (関数) と () (即時実行) の違い
  • 変数 (var, ~) と 型 (Number, String, Symbol, Array...)
  • 演算子 (+, *, ==, >, &&...)
  • SuperColliderの核心「オブジェクト」と「メッセージ」(オブジェクト.メッセージ(引数))
  • 関数定義 ({ |arg| ... }) と実行 (.value)
  • 配列 ([]) の操作
  • 制御構文 (if, N.do, while, for)

もちろん、SuperColliderの真髄はここから先の音作りの部分 (SynthDefUGen の詳細) や、パターンを使った高度なシーケンス (Pbind など) にありますので、またの機会に続きを書いていきたいと思います。

ぜひ、今回学んだ構文を使って、色々なコードを試したり、ヘルプ (Cmd/Ctrl+D) を参照しながら、SuperColliderの世界を探検してみてください!

Discussion