変数

コードを書いていく中で役に立つことは、覚えやすい名前を作成することです。Sonic Piはこれをとても簡単に作ることができます。あなたが使用したいと思う名前に続けて、等号のイコール(=)を書き、そのあとに覚えておきたい名前を書きます。

sample_name = :loop_amen

ここで、:loop_amenという記号は変数sample_nameに’記憶’されました。:loop_amenを使いたいところではどこでもsample_nameを使うことが可能です。例えば次のとおりです。

sample_name = :loop_amen
sample sample_name

Sonic Piでは変数を使う際、3つの主要な使い方があります。意味の伝達、重複の管理、そして結果の獲得です。

意味の伝達

コードを書くとき、あなたはコンピュータが理解しOKを出してくれることだけを考え、どのようにコンピュータに伝えて動作させるのかを意識するだけであれば、それは簡単な事ですね。しかし、覚えておかなくてはいけない大事なことは、コンピュータがコードを読むということだけではないということです。他の人もそれを読み、何が起こっているか理解をしようとするでしょう。あなた自身も将来、自分の書いたコードを見返して、どんなことをしたのか? 理解する必要がある時が来るかもしれません。たぶん確実にあなたにも他の人にも、そういったことが起こるのです!

あなたのコードがどのように動いているのか他人が理解をするための1つの方法として、コメントを書く(前章で確認できます)という方法があります。もう1つの方法として、理解しやすい変数名を使うという方法があります。次のコードを見てください。

sleep 1.7533

上の例だけを見ると、なぜ1.7533という数値を使ったのでしょう? その数値はどこから来たのか? それは何を意味しているのか? という疑問がわきます。しかし、次のコードを見てみましょう。

loop_amen_duration = 1.7533
sleep loop_amen_duration

こう書くとすぐに1.7533 がサンプル音源:loop_amenの長さであるということがわかりますね。もちろん、下記のように一行に書くことも可能です。

sleep sample_duration(:loop_amen)

どちらを用いたとしても、コードの意味がよりわかりやすいものになりました。

重複の管理

コードの中では沢山の重複が頻繁に見られます。また、もし何かを変更したいときは、膨大な場所を変更する必要も出てきます。次のコードをみて下さい。

sample :loop_amen
sleep sample_duration(:loop_amen)
sample :loop_amen, rate: 0.5
sleep sample_duration(:loop_amen, rate: 0.5)
sample :loop_amen
sleep sample_duration(:loop_amen)

上のコードは:loop_amenで沢山のことをしすぎています! もし:loop_garzulのような他のサンプルのループによる音が聞きたい場合はどうしましょう? そうするにはすべての:loop_amenを探しだして:loop_garzulに変更する必要があります。そんな変更ができる沢山の時間があればいいんですが…もし仮にあなたがステージの上で演奏している最中だったらどうでしょう? 特にみんなのダンスを止めないために、変更するためのそんな優雅な時間はないかもしれません。

下記のようなコードを書いたとして、

sample_name = :loop_amen
sample sample_name
sleep sample_duration(sample_name)
sample sample_name, rate: 0.5
sleep sample_duration(sample_name, rate: 0.5)
sample sample_name
sleep sample_duration(sample_name)

これは先程のコードと同じです(試してみてください)。しかし、sample_name = :loop_amenの一行をsample_name = :loop_garzulに変更するだけで多くの箇所を変更できました。そして、それは変数の力によるものです。

結果の獲得

最後に、変数を使う優れた理由はそのコードの結果の獲得をするということです。例えば、サンプル音源の長さを使って何かを行いたい時など。

sd = sample_duration(:loop_amen)

上記のように書くことで、今、:loop_amenというサンプルの長さが必要な時、どこにでもsdを使うことが出来るようになります。

おそらくもっと重要なのは、変数は、playやsampleの結果をキャプチャすることができることです。

s = play 50, release: 8

またこのように書くことで s が変数として記憶され、シンセをコントロールできるようになります。

s = play 50, release: 8
sleep 2
control s, note: 62

また、後の章ではもっと詳しくシンセをコントロールすることも学びます。

注意:変数とスレッド

変数はモノに名前を与えたり、ある処理においてどういった結果が返ってくるのか推測するのに役立ちます。ただし、通常、変数はスレッド内で完結するローカル変数として定義する必要があることを知っておく必要があります。したがって、以下のようなことをしてはいけません。

a = (ring 6, 5, 4, 3, 2, 1)
live_loop :sorted do
  a = a.sort
  sleep 0.5
  puts "sorted: ", a
end
live_loop :shuffled do
  a = a.shuffle
  sleep 0.5
end

上の例では変数aに数字のリングを割り当て、2つの別々のlive_loopの中でそれを利用しました。最初のライブループでは0.5秒ごとにリング (ring 1, 2, 3, 4, 5, 6)をソートしてログにプリント出力しています。コードを実行すれば、プリントされたリストがいつもソートされているわけではないことがわかるでしょう。もしかすると、リストがソートされていたり、されていなかったりすることにびっくりするかもしれません。これは非決定的挙動と呼ばれるもので、レースコンディションというやっかいな問題の結果でもあります。その問題の原因は2つ目のライブループもまたリストを操作(この例ではリストをシャッフル)していることにあり、リストはプリントされる時に、ちょうどソートされた状態であることもあれば、ちょうどシャッフルされた状態であることもあるのです。2つのライブループが同じ変数に対して別々のことをしようと競い合っていて、毎回どちらか異なる方が勝っているということです。

これに対して2つの解決策があります。 1つは、複数のライブループやスレッドで同じ変数を使わないことです。例えば、以下のコードはそれぞれのライブループが別々の変数を持っているので常にソートされたリストがプリントされます。

live_loop :shuffled do
  a = (ring 6, 5, 4, 3, 2, 1)
  a = a.shuffle
  sleep 0.5
end
live_loop :sorted do
  a = (ring 6, 5, 4, 3, 2, 1)
  a = a.sort
  sleep 0.5
  puts "sorted: ", a
end

しかし、スレッド間で何かを共有したいこともあります。例えば、現在のキーやBPM、シンセなどです。このような場合には、Sonic Piの特別なスレッドセーフな状態管理システム、getset という関数を使うことで出来ます。これについては、後ほどセクション10で説明します。