スレッド

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

もし、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に命令することができます。それではドラムとベースラインを同時に鳴らすことに挑戦してみましょう!

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

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番目の繰り返しが演奏されません。ですので以下のように、もう1つのスレッドが必要となります。

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をマスターしようとするとき、スレッドが、音楽のために最も重要な構成要素であることに気がつくでしょう。スレッドの重要な役割の1つとして、他のスレッドから現在の設定を分離することがあります。どういうことかというと、例えば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つの名前につき1つのスレッド

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

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

Bufferに上のコードを貼り付けて、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という名前があるスレッドが既に存在するため、別のものを作成しませんと伝えています。

この動作はすぐに使う必要性はないように思えますが、ライブコーディングを始めると、非常に便利なものになるでしょう。