音を鳴らす作曲家の問題
並行性を元ネタに何かできないか考えている。
とりあえず単純に食事する哲学者の問題を解く。当然音は鳴らしたいとは思うものの良いアイデアに至らず、「食事する」部分で音を鳴らすだけにした。
脳に湧いたむちゃくちゃな設定を書いておく。強引にもほどがある。もういいんだ。元の設定だって(略)
- 五人の作曲家がヴィブラフォンの周りを円形に囲んで各々曲の構想を考えている。変人だ
- 隣り合う作曲家の間の床にはヴィブラフォンのばちが一本ずつ置かれている
- 作曲家は考えているうちに楽音を鳴らしたい衝動に駆られて、自身の両隣にあるばちを手に取ってヴィブラフォンの特定の音板を叩く
- 気分の問題で、両手にばちを持ってからでないと音板は叩けない。取るのは一本ずつ
- 心行くまで音を鳴らして我に返った作曲家はばちを元の位置に戻して再び曲を考える。以降無限ループ
問題: 音板を叩けない作曲家が出ないようにするにはどうすればよいか。
解法: いくつかあるけど今回は「リソース階層による解法」を採用。
ChucKのプログラム。
5 => int N; int mallets[N]; 10::ms => dur WAIT; Gain g => dac; .4 / N => g.gain; fun void composer(int id, int low, int high) { ModalBar bar => g; 1 => bar.preset; // ヴィブラフォン 60 + id * 2 => Std.mtof => bar.freq; while (true) { // 考える。最長500ミリ秒しか持たないけど考える Std.rand2f(100, 500)::ms => now; // 衝動に駆られたので番号が小さい順にばちを取る while (mallets[low] != 0) WAIT => now; id + 1 => mallets[low]; while (mallets[high] != 0) WAIT => now; id + 1 => mallets[high]; // 心行くまで音を鳴らす do { 1 => bar.strike; Std.rand2f(300, 500)::ms => now; } until(maybe); // 我に返って番号が大きい順にばちを置く 0 => mallets[high]; 0 => mallets[low]; } } for (int i; i < N - 1; i++) { spork ~ composer(i, i, i + 1); } spork ~ composer(N - 1, 0, N - 1); while (ms => now) ;
ChucKの並行性についてメモ。
ばちを取る部分はクリティカルセクションなので不可分操作にする必要があるが、上のプログラムでは特に何も工夫していない。実はこれで不可分操作になっている。かなり前に少し触れた通り、ChucKのshredはノンプリエンプティブに並行実行される。shredの切り替えは、nowキーワードにChucKして時間を進めているときか、またはme.yield()を実行したときに起こる。
Ge Wangの論文*1にはChucKの並行性について以下の記述がある。
An extremely important property here is that while shreds are interleaved in time and therefore appear to be concurrent, code executes without preemption; thus every sequence of statements (up to time advance or yields) behaves as a critical section or atomic transaction. The programmer is not required to designate explicit critical sections with any synchronization in the code.
この仕様も分かりやすいし、並行性を元ネタに音を鳴らして遊ぶには良さそう。
音にして面白くなるアイデアが欲しい。
*1:Ge Wang. The ChucK Audio Programming Language: A Strongly-timed and On-the-fly Environ/mentality. PhD Thesis, Princeton University, 2008.