In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

SiON OPM Random FM Tone

ランダムFM音色生成装置
(SiONのFM音源音色エディタとしては「SiOPM音色エディタ」があります)
Get Adobe Flash player
by cat2151 15 Sep 2009
/**
 * Copyright cat2151 ( http://wonderfl.net/user/cat2151 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/9gTW
 */

// forked from cat2151's SiON OPM Tone MML
// forked from cat2151's SiON TheABCSong2

// ランダムFM音色生成装置
//   (SiONのFM音源音色エディタとしては「SiOPM音色エディタ」があります)

package { 
    import flash.display.Sprite; 
    import org.si.sion.*;            //OPM音色を鳴らすため
    import org.si.sion.events.*;     //演奏終了event取得のため
    import flash.text.TextField;     //文字列表示のため
    import flash.text.TextFormat;    //等幅font表示のため
    import flash.events.MouseEvent;  //Mouse入力取得のため
    import flash.events.KeyboardEvent;  //キー入力取得のため
    import flash.utils.Timer;        //一定時間ごとのEvent取得のため
    import flash.events.TimerEvent;  //一定時間ごとのEvent取得のため
    import flash.system.System;      //MMLをclipboardに格納するため
     
    public class playRandomTone extends Sprite { 
        public var driver:SiONDriver = new SiONDriver(); 
        public var data:SiONData; 

        private var myTimer:Timer = new Timer(100, 0);    //一定時間ごとに動作させるタイマに使う領域
        private var tf:TextField = new TextField;      //表示用領域
        private var tfmt:TextFormat = new TextFormat;  //表示用領域のtextFormat指定に使う領域
        private var toneHis:history = new history(10000);    //音色履歴用領域

        //関数定義

        //数値を整数にし文字列3文字になるよう左space埋め(padding)
        private function lpad3(inum:int) :String { return ("  " + int(inum)).substr(-3,3);          }

        //MMLを元にした文字列に、アルゴリズム解説文字列を追加する
        private function addAlgText(mml:String, td:Array) :String {
            var mmlText:String;
            var connection:Array = new Array(
                "[#0]\n  |\n[#1]\n  |\n[#2]\n  |\n[#3]\n",
                "[#1][#0]\n \  /\n  [#2] \n  [#3]\n",
                "[#1]\n  |\n[#2][#0]\n  \ /\n  [#3]\n",
                "    [#0]\n      |\n[#2][#1]\n  \ /\n  [#3]\n",
                "[#2][#0]\n  |   |\n[#3][#1]\n",
                "    [#0]\n   /  | \\n[#3][#2][#1]\n",
                "        [#0]\n          |\n[#3][#2][#1]\n",
                "[#3][#2][#1][#0]\n" );

            mmlText = mml + "\nアルゴリズム" + td[0] + "番\n" + connection[td[0]];
            return mmlText
        }
        
        //文字列を表示する
        private function dispText(text:String) :void {
            //表示文字列を更新する
            tf.text = text;
            tf.width = 400;
            tf.height = 400;
            tf.wordWrap = true;
            tf.multiline = true;
            tfmt.font = "MS Gothic";
            tf.setTextFormat ( tfmt );
            addChild( tf );
        }

        //MMLを表示する
        private function dispMml(mml:String, td:Array, am:String) :void {
            var text:String = "";
            text += addAlgText(mml, td); //アルゴリズム文字列を追加
            text += am;                  //引数で与えられた文字列を追加
            dispText(text);
        }

        //FM音色データ配列を元に、SiON用MMLを生成する
        private function makeMml(td:Array) :String {
            var mml:String = "//     ALG FB\n";
            mml += "#OPM@0 { " + td[0] + ", " + td[1] + ",\n";
            for (var op:int=0;op<4;op++) {
                mml += "//AR DR  SR  RR  SL  TL  KS MUL DT1 DT2 AMS\n"
                for (var counter:int=0;counter<11;counter++) {
                    mml = mml + lpad3(td[2 + 11*op + counter]) + ","
                }
                mml += "\n";
            }
            mml += "};\n";
            mml += "t120 %6 @0 a1 r2;\n"
            return mml;
        }

        //SiON用MMLを元にSiONで音を鳴らす
        private function playMml(mml:String) :void {
            data = driver.compile(mml); 
            driver.play(data); 
        }

        //ランダムにFM音色データを生成する
        private function createRandomFMtone(td:Array) :Array {
            var freedom:int = Math.random() * 100;    //音色のフリーダム度を格納
            var isModulator:Array = [  //アルゴリズムごとにオペレータのどれがモジュレータかを格納
                [ true, true, true, false ],    //ALG0
                [ true, true, true, false ],    //ALG1
                [ true, true, true, false ],    //ALG2
                [ true, true, true, false ],    //ALG3
                [ true, false, true, false ],   //ALG3
                [ true, false, false, false ],  //ALG5
                [ true, false, false, false ],  //ALG6
                [ false, false, false, false ]  //ALG7
                ];
            td[0] = int(Math.random() * 8);    //ALG
            td[1] = int(Math.random() * 8);    //FB
            for (var op:int=0;op<4;op++) {
                td[2+11*op+ 0] = Math.random() *  20+6;   //AR
                td[2+11*op+ 1] = Math.random() *   4+8;   //DR
                td[2+11*op+ 2] = Math.random() *   4+8;   //SR
                td[2+11*op+ 3] = Math.random() *   4+2;   //RR
                td[2+11*op+ 4] = Math.random() *   8+8;   //SL
                if (isModulator[td[0]][op]) {
                    td[2+11*op+ 5] = Math.random() * 50;  //TL (Modulator)
                } else {
                    td[2+11*op+ 5] = 0;                   //TL (Carrier)
                }
                td[2+11*op+ 6] = 0;                     //KS
                td[2+11*op+ 7] = Math.random() *   6;   //MUL
                td[2+11*op+ 8] = Math.random() *   8;   //DT1
                td[2+11*op+ 9] = 0;                     //DT2
                td[2+11*op+10] = 0;                     //AMS
                if (freedom>70){    //エンベロープを早すぎず遅すぎず、などの制約を外す場合
                    if (Math.random()*4<1){ td[2+11*op+ 7] = Math.random() *  16; }   //MUL
                    if (freedom>85){
                        td[2+11*op+ 0] = Math.random() *  32;   //AR
                        td[2+11*op+ 1] = Math.random() *  32;   //DR
                        td[2+11*op+ 2] = Math.random() *  32;   //SR
                        td[2+11*op+ 3] = Math.random() *  16;   //RR
                        td[2+11*op+ 4] = Math.random() *  16;   //SL
                        if (Math.random()*4<1){ td[2+11*op+ 5] = Math.random() *   4; }   //TL
                        if (Math.random()*4<1){ td[2+11*op+ 6] = Math.random() *   4; }   //KS
                        if (Math.random()*4<1){ td[2+11*op+ 9] = Math.random() *   4; }   //DT2
                        if (Math.random()*4<1){ td[2+11*op+10] = Math.random() *   4; }   //AMS
                        if (freedom>95 && Math.random()*4<1){ td[2+11*op+ 5] = Math.random() * 16; }   //TL (17~128は省略)
                    }
                }
            }
            return td;
        }
        
        //音色履歴から演奏、クリップボードにMML格納
        private function playHistory(add:int) :void {
            //音色データを履歴から取得
            var td:Array = toneHis.get(add);    //音色データ領域
            var mml:String;         //MMLを格納する領域
            var am:String = "";     //追加表示文字列領域
            am += "\n(履歴"+toneHis.readIndex+"番の音色データを鳴らしました)\n";
            am += "\n(クリップボードにSiON用MMLをコピーしました)\n";

            mml = makeMml(td);        //FM音色データ配列を元に、SiON演奏用MMLを生成する
            playMml(mml);             //SiON用MMLを元にSiONで音を鳴らす
            dispMml(mml,td,am);       //MMLを表示する
            System.setClipboard(mml); //clipboardにMMLを格納する
        }

        //音色生成、演奏、表示、クリップボードに格納
        private function playAndDisp(clipFlag:Boolean) :void {
            var td:Array = new Array(2+11*4);      //ランダム音色データを格納する領域
            var mml:String;       //MMLを格納する領域
            var am:String = "";   //追加表示文字列領域
            am += "\n(カーソル左右:履歴内の音色データを鳴らす)\n";
            if (clipFlag){ am += "\n(クリップボードにSiON用MMLをコピーしました)\n"; }

            td = createRandomFMtone(td); //ランダムにFM音色データを生成する
            toneHis.add(td);             //音色データを履歴に保存する
            mml = makeMml(td);           //FM音色データ配列を元に、SiON演奏用MMLを生成する
            playMml(mml);                //SiON用MMLを元にSiONで音を鳴らす
            dispMml(mml,td,am);          //MMLを表示する
            if (clipFlag){ System.setClipboard(mml); } //clipboardにMMLを格納する
        }

        //コンストラクタ(初期設定)
        function playRandomTone() {
            //イベントハンドラを登録する
            //    MOUSE_DOWN(マウス左ボタンプッシュ)を登録
            stage.addEventListener( MouseEvent.MOUSE_DOWN, function(e :MouseEvent) :void {
                //クリックした場合
                playAndDisp(true);     //演奏と表示とクリップボード格納
                });
            //    キーボードのキー押下を登録
            stage.addEventListener( KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent) :void {
                //キーボードのキーが押された場合
                switch (e.keyCode) {
                case 37: playHistory(-1); break;
                case 39: playHistory( 1); break;
                }
                });
            //    演奏終了を登録
            driver.addEventListener( SiONEvent.FINISH_SEQUENCE, function(e :SiONEvent) :void {
                //演奏が終了した場合
                myTimer.start();   //タイマをスタートさせる
                });
            //    一定時間ごとに発生するタイマイベントを登録
            myTimer.addEventListener(TimerEvent.TIMER, function(e :TimerEvent) :void {
                //タイマのスタートから一定時間が経過した場合
                myTimer.stop();     //タイマを停止させる
                playAndDisp(false); //演奏と表示
                });

            //予め音色を生成して履歴に格納しておく
            for (var i:int=0;i<10000;i++){
                var td:Array = new Array(2+11*4); //ランダム音色データを格納する領域
                td = createRandomFMtone(td);  //ランダムにFM音色データを生成する
                toneHis.add(td);              //音色データを履歴に保存する
            }

            //初回演奏
            playAndDisp(false);     //演奏と表示
        }


    }
}
    
    //履歴バッファ
    //FIFO(lengthはコンストラクタで指定)に対して読み書き
    //制約:履歴バッファにaddする値の型はArray限定
    class history { 
        //公開プロパティ
        public var readIndex:int;      //履歴index(読み込み用)
        //非公開プロパティ
        private var writeIndex:int;    //履歴index(書き込み用)
        private var writeIndexMax:int; //何段目まで履歴が入っているか
        private var fifoBuf:Array;     //履歴を格納
        private var bufLength:int;     //履歴段数を格納

        //メソッド

        //コンストラクタ
        public function history(length:int) {
            fifoBuf = new Array(length);
            bufLength = length;
        }

        //値を履歴に書き込み、readIndexも同期させる
        public function add(data:Array) :void {
            //履歴にデータ書き込み
            fifoBuf[writeIndex] = data;
            //readIndexを同期させる
            readIndex = writeIndex;
            //max更新
            if (writeIndexMax < bufLength - 1){ writeIndexMax = writeIndex; }
            //writeIndexをラップアラウンド
            writeIndex = (writeIndex + 1) % bufLength;
        }

        //指定された分、履歴を遡り、値を取得する(readIndexを変更し、それを元に取得)
        public function get(add:int) :Array {
                                      //備考:ArrayではなくObjectと記述した場合、呼び出し側記述時にコンパイルエラー
            //readIndex更新
            readIndex += add;
            //readIndexを前後ともラップアラウンド
            if (readIndex < 0){ readIndex = writeIndexMax; }
            else if (readIndex>writeIndexMax){ readIndex = 0; }
            //履歴からデータ取得
            return fifoBuf[readIndex];
        }
    }