私が技術全般をサポートしているエレクトロニカユニット
macaroom が今年の8月にリリースしたミニアルバム"cage out"は,アメリカの現代音楽作曲家ジョン・ケージの楽曲を楽譜通りに,尚且つポップな「歌モノ」として演奏するという試みを行った画期的な内容となったアルバムでした.このアルバムの発売記念イベントとして,このプロジェクトのアドバイザをしていただいた現代音楽作曲家の
川島素晴 先生と,先生に紹介していただいたインプロビゼーション系の演奏ユニット木漏れ日エレキに,本格的なジョン・ケージ楽曲の演奏を行っていただくという事も行いました.
このイベントのアンサーイベントとして川島先生が企画したのが,先日行われたCAGE IN.このイベントは,最初から最後まで全てジョン・ケージの楽曲,しかも初期から晩年まで時系列順に聴けるという日本では非常に珍しい内容でした.私はこのイベントで音響を担当し,様々な楽曲の音源の準備や機材のプランニング等を行い,当日はそのオペレーションを行なったのですが,何曲かは出演者として演奏を行いました.その曲の1つが,VariationsⅡという曲です.この曲はイベントの第2部「ミュージサーカス」の中で演奏した曲です.ミュージサーカスとは,同時多発的に色々な楽曲や演目を行うというケージが提唱したコンサート形式で,本イベントではケージの様々な楽曲を全出演者で同時に演奏しました.その中でVariationsⅡは出演者それぞれが独自に準備し,ミュージサーカスの中で演奏することになっており,出演者それぞれのVariationsⅡが同時に演奏されていたのです.
私は,VariationsⅡで指示されている作業を全てコンピュータ・プログラムによって行い,演奏もプログラムによって自動演奏するという形で演奏しました.この試みは,川島先生から「VariationsⅡの新しいアプローチかもしれない」というご評価をいただきました.個人的には,過去にも似たことを考え実践した人は沢山いただろうと思うのですが,とにかく,当日はミュージサーカスということで音場がぐちゃぐちゃでどの音がそれだったのか分からなかったと思いますし,せっかく作ったのでその内容をご紹介します.尚,本プログラムはイベントの準備に追われ,自分のことに構う優先順位がどんどん下がる中,とにかく「16日に上演する」ことだけを目的としたため,ほとんど全てハードコーディングになっており,また,実際には特定の条件でしかありえないようなことを動作の前提としているため,あまり他の場面で再利用できるような段階ではありませんので,その点はご了承ください.
VariationsⅡの譜面には,点と直線が書かれた5枚の透明シート,直線のみが書かれた1枚の透明シートがついてきます.これをテーブルの上にばらまくと,6本の直線,5つの点からなる図形が生成されます.これは,楽譜に指示されたやり方のひとつで,実際には,6本の直線,5つの点からなる図形が生成されればそのやり方はなんでもいいのです.
例えば,ペンで直線と点を描いてもいいですし,ということは,コンピュータで自動生成してもいい訳です.
次に生成された図形から,それぞれの点と線の距離を測り,表にしていきます.この表から,どのような音を,いつ,どれくらいの時間出すべきか,以下のように決定します.点1つにつき,1つの音として,パラメータを当てはめていきます.つまり,6個のパラメータを持つ音を5つ,考えるわけです.
ここで少し難しいのは6番目の直線と点の距離.これは,音をいくつ増やすかということを決定します.例えば,点1と直線6の距離が3.1cmであったら,音1は3個の音を出さなければならない訳です.この時,3回同音連打をするのではなく,新しく上記の図形を生成し,表を作ってその表の数字を順に当てはめていきます.
ここまでの作業をMATLABで行い,それぞれのパラメータをテキストファイルとして保持するプログラムを作成しました.高校までの数学の知識のみで実現できます.尚,実際には,2回目以降の図形の生成は,実際に生成された点と直線6の距離によって何回行うかが異なるはずです.ですから,本当は2回目以降の図形の生成は無限ループにして,必要な値の数が揃ったらループを抜けるという具合にするのが正解と思われます.しかし,今回のプログラムの範囲では,3回以上生成を行う必要がほぼない(各点と直線6との距離の和が(5x6)-5=25を超えない)と判断したので,1回行うのみになっています.
----------------------------------------------------------------------------------------------------------------------
clear ;
close all ;
rng( 'shuffle' ) ;
pointx=zeros ( 1 ,5 ) ;%5点のx座標
pointy=zeros ( 1 ,5 ) ;%5点のy座標
%直線ax+by+c=0
a=zeros ( 1 ,6 ) ;
b=zeros ( 1 ,6 ) ;
c=zeros ( 1 ,6 ) ;
d=zeros ( 5 ,6 ) ;%直線と点の距離
%点を乱数で決定
for k=1 :5
pointx( k) =rand ( 1 ,1 ) *20 -10 ;
pointy( k) =rand ( 1 ,1 ) *20 -10 ;
end
figure ( 1 ) ;
hold on;
plot ( pointx,pointy,'x' ) ;
plot ( pointx,pointy,'o' ) ;
%直線を乱数で決定
for k=1 :6
a( k) =rand ( 1 ,1 ) *4 -2 ;
b( k) =rand ( 1 ,1 ) *4 -2 ;
c( k) =rand ( 1 ,1 ) *4 -2 ;
%ax+by+c=0
%y=(-ax-c)/b
plot ( [ min ( pointx) max ( pointx) ] ,[ ( -a( k) *min ( pointx) -c( k) ) /b( k) ,( -a( k) *max ( pointx) -c( k) ) /b( k) ] ) ;
end
xlim ( [ min ( pointx) -2 max ( pointx) +2 ] ) ;
ylim ( [ min ( pointy) -2 max ( pointy) +2 ] ) ;
%距離を求める
for k=1 :5
for n=1 :6
d( k,n) =abs ( a( n) *pointx( k) +b( n) *pointy( k) +c( n) ) /sqrt ( a( n) *a( n) +b( n) *b( n) ) ;
end
end
d
%6列目の数に応じてピッチの数を決定
times=floor ( d( :,6 ) ) +1
%決定された数だけピッチの変数を確保
pitch1=zeros ( times( 1 ) ,1 ) ;
pitch1( 1 ) =d( 1 ,1 ) ;
pitch2=zeros ( times( 2 ) ,1 ) ;
pitch2( 1 ) =d( 2 ,1 ) ;
pitch3=zeros ( times( 3 ) ,1 ) ;
pitch3( 1 ) =d( 3 ,1 ) ;
pitch4=zeros ( times( 4 ) ,1 ) ;
pitch4( 1 ) =d( 4 ,1 ) ;
pitch5=zeros ( times( 5 ) ,1 ) ;
pitch5( 1 ) =d( 5 ,1 ) ;
%もう一度点と線のオペレーションを実行
rng( 'shuffle' ) ;
fig=2 ;
for k=1 :5
pointx( k) =rand ( 1 ,1 ) *20 -10 ;
pointy( k) =rand ( 1 ,1 ) *20 -10 ;
end
figure ( fig) ;
hold on;
plot ( pointx,pointy,'x' ) ;
plot ( pointx,pointy,'o' ) ;
for k=1 :6
a( k) =rand ( 1 ,1 ) *4 -2 ;
b( k) =rand ( 1 ,1 ) *4 -2 ;
c( k) =rand ( 1 ,1 ) *4 -2 ;
%ax+by+c=0
%y=(-ax-c)/b
plot ( [ min ( pointx) max ( pointx) ] ,[ ( -a( k) *min ( pointx) -c( k) ) /b( k) ,( -a( k) *max ( pointx) -c( k) ) /b( k) ] ) ;
end
d2=zeros ( 5 ,6 ) ;
for k=1 :5
for n=1 :6
d2( k,n) =abs ( a( n) *pointx( k) +b( n) *pointy( k) +c( n) ) /sqrt ( a( n) *a( n) +b( n) *b( n) ) ;
end
end
d2=reshape ( d2,30 ,1 ) ;%d2を1次元に変形
%d2の値を順にそれぞれのピッチに追加していく
index=1 ;
for k=2 :length ( pitch1)
if index<=30
pitch1( k) =d2( index) ;
index=index+1 ;
end
end
for k=2 :length ( pitch2)
if index<=30
pitch2( k) =d2( index) ;
index=index+1 ;
end
end
for k=2 :length ( pitch3)
if index<=30
pitch3( k) =d2( index) ;
index=index+1 ;
end
end
for k=2 :length ( pitch4)
if index<=30
pitch4( k) =d2( index) ;
index=index+1 ;
end
end
for k=2 :length ( pitch5)
if index<=30
pitch5( k) =d2( index) ;
index=index+1 ;
end
end
%値をピッチ(Hz)として正規化(範囲は適当に決めている)
pitch1=400 +600 *pitch1/max ( d( :,1 ) )
pitch2=400 +600 *pitch2/max ( d( :,1 ) )
pitch3=400 +600 *pitch3/max ( d( :,1 ) )
pitch4=400 +600 *pitch4/max ( d( :,1 ) )
pitch5=400 +600 *pitch5/max ( d( :,1 ) )
%ピッチを保存
fp=fopen ( 'pitch1.txt' ,'w' ) ;
fprintf ( fp,'%f\r\n' ,pitch1) ;
fclose ( fp) ;
fp=fopen ( 'pitch2.txt' ,'w' ) ;
fprintf ( fp,'%f\r\n' ,pitch2) ;
fclose ( fp) ;
fp=fopen ( 'pitch3.txt' ,'w' ) ;
fprintf ( fp,'%f\r\n' ,pitch3) ;
fclose ( fp) ;
fp=fopen ( 'pitch4.txt' ,'w' ) ;
fprintf ( fp,'%f\r\n' ,pitch4) ;
fclose ( fp) ;
fp=fopen ( 'pitch5.txt' ,'w' ) ;
fprintf ( fp,'%f\r\n' ,pitch5) ;
fclose ( fp) ;
%音量を0-1に正規化して保存%
fp=fopen ( 'dynamics.txt' ,'w' ) ;
fprintf ( fp,'%f ' ,d( :,2 ) /max ( d( :,2 ) ) ) ;
fclose ( fp) ;
%音色パラメータを1-100にスケーリングして保存
fp=fopen ( 'sound.txt' ,'w' ) ;
fprintf ( fp,'%f ' ,( 99 *d( :,3 ) /max ( d( :,3 ) ) ) +1 ) ;
fclose ( fp) ;
%持続時間を0-1に正規化して保存
fp=fopen ( 'tlength.txt' ,'w' ) ;
fprintf ( fp,'%f ' ,d( :,4 ) /max ( d( :,4 ) ) ) ;
fclose ( fp) ;
%タイミングを最大45分になるようにスケーリングして保存
fp=fopen ( 'timing.txt' ,'w' ) ;
fprintf ( fp,'%f ' ,1000 *45 *60 *( d( :,5 ) /max ( d( :,5 ) ) ) ) ;
fclose ( fp) ;
----------------------------------------------------------------------------------------------------------------------
これによって,各パラメータが決まりました.
このテキストファイルをMaxで読み込んで,演奏します.まず,テキストファイルを読み込んで,それぞれのタイミングで音を生成します.今回,タイミングの最大値を45分(2700000ms)にしたので,delayオブジェクトがそんな大きな値の遅延を行えるのか,少し心配でしたが,どうやら大丈夫なようです.
音の生成は"p pulse"で行なっています.その中身がこちら.
根本的には,パルスにBPFをかけたものを音源として使っています.パルスは,全周波数のパワーが均等な信号であるため,これをフィルタに通すと,そのフィルタの特性そのままの音色となって出力されます.今回はBPFをフィルタとして使っています.ですので,フィルタのQがせまくなると,その中心周波数を周波数とする正弦波に近い音色が得られます.コンピュータ音楽で音程感のある音を得る時によく使う方法の1つです.
今回の演奏では,音色パラメータをフィルタのQとして適用しています.つまり,音色パラメータの値が小さければ,周波数帯域の広いノイズに近い音,音色パラメータの値が大き切れば,周波数帯域の狭い音程感の強い音となって演奏されます.
更に,この音にリバーブをかけています.本当は自作のリバーブを使いたかったのですが,これまで作った自作リバーブの中に,この音に合うものがなかったので,とはいえ,新しいリバーブを作っている時間がなかったので,コンピュータ音楽家の松本昭彦 さんのForeverb を使わせていただきました.このリバーブの最大の残響時間を最大の持続時間として,適用します.測定したところ,このリバーブの最大の残響時間は約20秒でしたので,持続時間が1の時(MATLABでは0-1に正規化して書き出しました),Maxで20000倍して適用しています.この適用方は,Foreverbの残響時間が線形に増えていく(Foreverbで設定したReverb Timeが0.5であれば残響時間は10秒になる)という仮定を前提とした適用方ですが,実際にはそのような増え方はしていません.ですので,厳密に行うならば,残響時間を様々な値で測定し,その増え方を反映した曲線で値を適用しなければなりません.今回のプログラムでは線形に残響時間が増えると仮定したため,下の動画でお聞きいただければ分かりますが,音から次の音がなるまでの間に音が持続しておらず,音に隙間ができてしまっています.
同様に他のパラメータも適用し,あとはスタートボタンとなるトグルをクリックすれば演奏が開始されます.
以上が今回のCAGE INで行なったVariationsⅡの全容です.ここから,更に発展する方向としては,今回MATLABで行なった図形の生成をjavascriptで実装し,Maxのjsオブジェクトを使って生成から演奏までの1連の流れを1つのプログラムで行うようにする等が考えられます.
これは,タイミングの最大値を3分にして楽譜を生成した様子です.音の雰囲気だけでも感じていただけると幸いです.(3分過ぎから音量注意です)
VIDEO
2018/01/06追記
webから今見つけている範囲で,本件と類似の取り組みもご紹介しておきます.
Variations 10b: A digital realization of John Cage's Variations II
Pythonで演奏上々の生成を行って,OSC経由で何らかの音響合成プログラムに演奏させるものです.
ジョン・ケージ《VARIATINOS II》への 演奏ソフトウェアアートによるアプローチ
VariationsⅡに着想を得て,手法は同じでも,独自のパラメータ設定により演奏を行うプログラム.openFrameworksのaddonとして開発したそうです.