変数

コードを書いていく中で役に立つことは、覚えやすい名前を作成することです。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

In the above example we assign a ring of numbers to a variable a and then used it within two separate live_loops. In the first live loop every 0.5s we sort the ring (to (ring 1, 2, 3, 4, 5, 6)) and then print it out to the log. If you run the code, you’ll find that the printed list is not always sorted!. This may surprise you - especially that sometimes the list is printed as sorted, and sometimes it is not. This is called non-deterministic behaviour and is the result of a rather nasty problem called a race-condition. The problem is due to the fact that the second live loop is also manipulating the list (in this case shuffling it) and by the time the list is printed, sometimes it has just been sorted and sometimes it has just been shuffled. Both live loops are racing to do something different to the same variable and every time round a different loop ‘wins’.

There are two solutions to this. Firstly, don’t use the same variable in multiple live loops or threads. For example, the following code will always print a sorted list as each live loop has its own separate variable:

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

However, sometimes we do want to share things across threads. For example, the current key, BPM, synth etc. In these cases, the solution is to use Sonic Pi’s special thread-safe state system via the fns get and set. This is discussed later on in section 10.