ストレンジ・アトラクター( Strange Atractor )はカオスの一種で、システムの軌跡をプロットすると「決して同じ軌跡を通らないのに、なにかに引き寄せられる( attracted )ような」繰り返し運動を行うものです。特に、軌跡がフラクタルになっているものは非常に美しい絵を描きます。
ストレンジ・アトラクターはそのカオス的な性格から波形にランダム性が入り込みます。一方でフラクタル性から擬似周期的な性質も持ちます。口で言うとわかりにくいので、しばらくローレンツ・アトラクターに見とれてみるのもいいでしょう。
アトラクターやカオスの本を読んで20年ほど経ちますが、ようやく念願かなってその「音」を聞くことができました。下にそのプログラムを示します。ここではローレンツのアトラクターを取り上げています。アトラクターそのものは簡単(に見える)微分方程式であらわすことができますので、それを浮動小数点演算でシミュレートします。
ローレンツアトラクターは不思議な音を立てます。一見空電ノイズにしか聞こえないのですが、よく聞くと間に周期的とも非周期的ともつかない純音に近い音が混じってきます。でも隣で聞くと雑音にしか聞こえないそうです。
このプログラムもダウンロード可能です。アトラクター部分の実装はあらかじめ抽象クラスを定義してそこから導出する形にしました。レスラーのアトラクターなど、ほんの少し違うものを実装するときなど、オブジェクト指向の恩恵をたっぷり受け取ることができます。なお、このプログラムは最初はボリュームを絞っていますので、SW3を押して聞きやすい音量まで上げてください。
#include "atractor.h" //************************************************************************** // // Implementation of CAtractor class // // アトラクターの親の型になる抽象クラス。初期化と値の取得はここで定義する // // //************************************************************************** CAtractor::CAtractor( float aX, float aY, float aZ, float aDt ) { x=aX; y=aY; z=aZ; dt=aDt; } // get Z position float CAtractor::getX(void) { return(x); } // get X position float CAtractor::getY(void) { return(y); } // get Z position float CAtractor::getZ(void) { return(z); } //************************************************************************** // // Implementation of CLorentz class // // ローレンツアトラクターのアルゴリズムの実装 // // //************************************************************************** CLorentz::CLorentz( float aX, float aY, float aZ, float aDt ) :CAtractor(aX, aY, aZ, aDt ) { } // updates the point. void CLorentz::tick(void) { float dx, dy, dz; dx = (-10*x +10*y)*dt; dy = (28*x -y -x*z)*dt; dz = (-8.0/3*z +x*y)*dt; x+=dx; y+=dy; z+=dz; } //************************************************************************** // // Implementation of CRossler class // // レスラーアトラクターのアルゴリズムの実装 // // //************************************************************************** CRossler::CRossler( float aX, float aY, float aZ, float aDt ) :CAtractor(aX, aY, aZ, aDt ) { } void CRossler::tick(void) { const float a = 0.15; const float b = 0.2; const float c = 10.0; float dx, dy, dz; dx= ( -y -z )*dt; dy= ( x +a*y )*dt; dz= ( b + (-c+x)*z )*dt; x+=dx; y+=dy; z+=dz; }
#include "player.h" CPlayer::CPlayer():mcm::fwEzKit2191( ) { attractor = new CLorentz( 0.0, 0.01, 0.0, 5e-3 ); } //************************************************************************** // // CPlayer::handleBufferの定義 // // handleBufferメンバー関数はDMA転送が終わるたびに受信バッファと送信 // バッファをパラメータに呼ばれる。受信バッファには直前のDMAで受信した // データが入っている。handleBufferから戻るときに送信バッファにデータを // おいておくと、次のDMA転送で送信される。 // // このプログラムではbufTx[i]にアトラクターのXの値を置く。 // //************************************************************************** void CPlayer::handleBuffer( struct mcm::sample * bufTx, struct mcm::sample * bufRx ) { for ( int i=0; i< this->bufSize; i++ ){ attractor->tick(); bufTx[i].r = bufTx[i].l =attractor->getX()*1000.0; } }
⇒次はアトラクター(その2)