スレッド

それでは、強烈なベースラインとかっこいいビートを作った場合、どのようにしてそれらを同時に鳴らしたらいいでしょう?一つの回答としては、手動でそれらを同時に鳴らす事です―まず、いくつかのベースを演奏し、その後にいくつかのドラム、またその後にベースというように…しかしながら、すぐに沢山の楽器を処理することが難しいことに気づくでしょう。

もし、Sonic Piが自動的にそれらを作り出せるとしたら?thread(スレッド)と呼ばれる特別な命令によってそれが可能になります。

無限の繰り返し

このシンプルな例で、強烈なベースラインとかっこいいビートが期待できてしまうのです。

loop do
 sample :drum_heavy_kick
 sleep 1
end

loop do
 use_synth :fm
 play 40, release: 0.2
 sleep 0.5
end

ループはプログラムのブラックホールのようだと以前にお話しました。 一度ループを入れると、stopボタンを押さない限り、そこから抜け出せなくなります。 では、どうしたら同時にふたつの繰り返しを演奏することが出来るでしょう? 私たちは、同時にそれらのコードをタイミングを合わせスタートさせるようにSonic Piに伝えなくてはいけません。これがスレッドを使った解決方法なのです。

スレッドを使った解決方法

in_thread do
 loop do
   sample :drum_heavy_kick
   sleep 1
 end
end

loop do
 use_synth :fm
 play 40, release: 0.2
 sleep 0.5
end

はじめのdo/endブロックをin_threadで囲むことで、次にくるdo/endブロックをぴったりと同時にループさせるようにSonic Piに命令することができます。 それではドラムとベースラインを同時に鳴らすことに挑戦してみましょう!

そして、もう一つの音を追加したいので、先ほどのように繰り返しを入れてみましょう。

in_thread do
 loop do
   sample :drum_heavy_kick
   sleep 1
 end
end

loop do
 use_synth :fm
 play 40, release: 0.2
 sleep 0.5
end

loop do
 use_synth :zawa
 play 52, release: 2.5, phase: 2, amp: 0.5
 sleep 2
end

前と同じ問題が出てきましたね。in_threadによって最初の繰り返しと2つ目の繰り返しが同時に演奏されています。しかし3番目の繰り返しが演奏されません。ですので以下のように、もう一つのスレッドが必要となります。

in_thread do
 loop do
   sample :drum_heavy_kick
   sleep 1
 end
end

in_thread do
 loop do
   use_synth :fm
   play 40, release: 0.2
   sleep 0.5
 end
end

loop do
 use_synth :zawa
 play 52, release: 2.5, phase: 2, amp: 0.5
 sleep 2
end

スレッッドとして実行する

驚くべきことにRunボタンを押すということは、実際にはコードを実行するための新しいスレッドを作っていることになります。 そのために複数回Runボタンを押すことは、互いの上に音を階層化することになります。Runそれ自体がスレッドであるために、音を自動的に紡ぎ合わせることになるのです。

スコープ

Sonic Piをマスターしようとするとき、スレッドが、音楽のために最も重要な構成要素であることに 気がつくでしょう。重要な役割の一つとして、他のスレッドから現在の設定セッティングを 分離することがあります。どういうことかというと、例えばuse_synthを使ってシンセの種類を変更する時、 現在のスレッド中にあるシンセだけを変更します。他のどのスレッドも変更しません。 そのことを確認してみましょう。

play 50
sleep 1

in_thread do
 use_synth :tb303
 play 50
end

sleep 1
play 50

真ん中の音だけがほかのものと違うことに注目してみましょう。use_synthの宣言はスレッドの中だけに影響し、スレッドの外にあるものには影響しません。

インヘリタンス(継承機能)

in_threadを使って新しいスレッドを作ると、そのスレッドには現在の スレッドの全ての設定が自動的に継承されます。ではその機能を見てみましょう。

use_synth :tb303

play 50
sleep 1

in_thread do
 play 55
end

2番目の音符は、それが別のスレッドから再生されたにもかかわらず :tb303 シンセで演奏されることに注目してください。 use_* 関数を使ったいかなる設定も同様に作用するでしょう。

スレッドが作成されると、彼らの親から(前述の)すべての設定を継承しますが、スレッド以降の変更を共有することはありません。

スレッドの命名

最後に、スレッドに名前つける機能を覚えましょう。

in_thread(name: :bass) do
 loop do
   use_synth :prophet
   play chord(:e2, :m7).choose, release: 0.6
   sleep 0.5
 end
end

in_thread(name: :drums) do
 loop do
   sample :elec_snare
   sleep 1
 end
end

このコードが動作した時、ログ画面を見てみましょう。レポートの中にスレッドの名前が表示されることがわかります。

[Run 36, Time 4.0, Thread :bass]
|- synth :prophet, {release: 0.6, note: 47}

名前を許された一つだけのスレッド

名前付きのスレッドについて知っておくべき最後のひとつは、名前の付いた1つのスレッドだけが同時に実行されることです。 では以下を見てみましょう。次のコードを考えてみてください。

in_thread do
 loop do
   sample :loop_amen
   sleep sample_duration :loop_amen
 end
end

ワークスペースに上記のスレッドを貼り付けて、Runボタンを押します。 数回押してみましょう。複数のアーメン・ブレイクが不協和音として反復されるでしょう。 もういいですよね。stopボタンを押しましょう。

この動作はこれまで何度も見てきました。Runボタンを押すと、一番上のレイヤーにあるサウンドが鳴ります。 このためループが含まれている場合、Run`ボタンを押すことを3回続けると、3つのレイヤーが一斉に実行されます。

ただし、名前付きのスレッドでそれは異なります。

in_thread(name: :amen) do
 loop do
   sample :loop_amen
   sleep sample_duration :loop_amen
 end
end

このコードでRunボタン複数回、押してみてください。ひとつのアーメン・ブレークのループのみが聞こえるでしょう。 そして下記のテキストがログ画面に表示されます。

==> Skipping thread creation: thread with name :amen already exists.

Sonic Pi は、:amenという名前があるスレッドが既に存在するため、別のものを作成しませんと伝えています。この動作はすぐに使う必要性はないように思えますが、ライブコーディングを始めると、非常に便利なものになるでしょう。