ChucK入門 #7 - マルチしゅれっど(shred, not thread)

前回は発音部を関数に切り出しました。で、呼び出すと音がなりますが、音が鳴り始めて消えるまで呼び出し元はブロックされています(同期)。

和音はもとより、これでは複数のパートで演奏するとか不可能です。非同期で動作させるために、マルチスレッドです。いや、multi-shredです。 ChucKでは Threadという表現のかわりに Shred という呼び方をするようです。ダジャレではないと思います。

まず 前回のソースをそのまま引用します。

fun void buzz( float hz )
{
  SinOsc s => dac;
  hz => s.freq;

  SawOsc env;
  env => blackhole;
  0.0 => env.width;
  0.8 => env.freq;

  1::samp => now;
  while (true) {
    env.last() => s.gain;
    if (env.last() <= 0) {
      break;
    }    
    1::ms => now;
  }
}

buzz( Std.mtof(60) );
buzz( Std.mtof(64) );
buzz( Std.mtof(67) );
buzz( Std.mtof(71) );

これは単音ずつ鳴ります。一番下の4行を次のように書き換えてみて実行してみてください。

spork ~ buzz( Std.mtof(60) );
spork ~ buzz( Std.mtof(64) );
spork ~ buzz( Std.mtof(67) );
spork ~ buzz( Std.mtof(71) );

spork ~ 関数呼び出し() で、その関数呼び出しを非同期に、つまりShredを立ち上げて実行せよ、という指示になります。

ん、なにも鳴りません。それもそのはず、これらの関数呼び出しは非同期に実施されているから、このあとメインの処理の流れは即座に終了し、なにも鳴らないわけです。


なので、以下の行を追加してみてください

3::second => now;

もう説明は不要ですね。

和音がなりましたね! miniAudicleのVirtual Machineウインドウを見ると一瞬5shred立ち上がるのが見えます。

これで和音も鳴らせるようになりました!!

今度はこの和音(Major7thの基本形)の発音をさらに関数にしてしまいましょう。

fun void maj7( int root ) {
    spork ~ buzz( Std.mtof(root) );    // Root
    spork ~ buzz( Std.mtof(root+4) );  // M3
    spork ~ buzz( Std.mtof(root+7) );  // P5
    buzz( Std.mtof(root+11) );         // M7
}

おっと、最後だけ shredを作らずに同期実行していますね。要するにこれは4つのbuzz()呼び出しが音が消えるタイミングが同じなので、この関数自体を消音のタイミングにあわせて終了するためにこう書いています。

Javaなどほかの言語なら Thread#join() のようなものを使って終了待ちをするのが普通かと思いますが、ChucKだとjoin()があるかよくわからなかったのでこんな書き方をしてみました。

さっそく呼び出してみましょう。

maj7( 60 );  // C
maj7( 65 );  // F
maj7( 58 );  // Bb
maj7( 63 );  // Eb

はい!これで CMaj7→FMaj7→B♭Maj7→E♭Maj7 と、和音のシーケンスが作れました!
ちょっと忙しないので、エンベロープに使ってたオシレータのfreqを下げて調整とかしてみてくださいね。

やりましたねー!また次回!

※おさらいのために今日の結果の全ソースコード

fun void buzz( float hz )
{
  SinOsc s => dac;
  hz * 2 => s.freq;

  SawOsc env;
  env => blackhole;
  0.0 => env.width;
  0.4 => env.freq;

  1::samp => now;
  while (true) {
    env.last() => s.gain;
    if (env.last() <= 0) {
      break;
    }    
    1::ms => now;
  }
}

fun void maj7( int root ) {
    spork ~ buzz( Std.mtof(root) );
    spork ~ buzz( Std.mtof(root+4) );
    spork ~ buzz( Std.mtof(root+7) );
    buzz( Std.mtof(root+11) );    
}

maj7( 60 );
maj7( 65 );
maj7( 58 );
maj7( 63 );