wonderflで音楽♪SpaceHarrior+ショット+着地 from:from:Ground

by keim_at_Si
200行のソフトシンセ+MMLシーケンサを実装して音楽つけてみました。
http://soundimpulse.sakura.ne.jp/wonderfl-music-spaceharrior/
♥14 | Line 712 | Modified 2009-02-17 00:13:11 | MIT License
play

ActionScript3 source code

/**
 * Copyright keim_at_Si ( http://wonderfl.net/user/keim_at_Si )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/aawB
 */

// forked from h_sakurai's 背景を付けざるを得ない from:from:Ground
// forked from ABA's forked from: 音楽を付けざるを得ない from:from:Ground
// forked from keim_at_Si's 音楽を付けざるを得ない from:from:Ground
// forked from ABA's Ground
// Ground.as
//  Display a 3d ground surface and pillars.
//  Control: Arrow keys or [WASD] keys. [Z] or [Shift] key to shot.
package
{
  import flash.display.Sprite;
  import flash.display.BitmapData;
  import flash.display.Bitmap;
  import flash.text.TextField;
  import flash.geom.Rectangle;
  import flash.events.Event;
  import flash.events.KeyboardEvent;
  import flash.events.SampleDataEvent;
  import flash.media.Sound;
  import flash.geom.Matrix;
  import flash.geom.ColorTransform;
  [SWF(width="465", height="465", backgroundColor="0x000000", frameRate="30")]
  public class Ground extends Sprite
  {
    private const SCREEN_WIDTH:int = 465;
    private const SCREEN_HEIGHT:int = 465;
    private const COUNT:int = 512;
    private const NEAR_PLANE_DIST:Number = 1;
    private const PROJECTION_RATIO:Number = 250;
    private const LOD_RATIO:Number = 1.05;
    private const LOD_START_COUNT:Number = 16;
    private const PILLARS_COUNT:int = 64;
    private const WIRE_WIDTH:int = 2;
    private const HAR_SPEED:Number = 11;
    private const BACK_R:int = 210, BACK_G:int = 0, BACK_B:int = 240;
    private var buffer:BitmapData;
    private var left:Boolean, up:Boolean, right:Boolean, down:Boolean, shot:Boolean;
    private var rect:Rectangle;
    private var degs:Array;
    private var index:int;
    private var offset:Number;
    private var gys:Array, gzs:Array, gcs:Array, gas:Array, gIndices:Array;
    private var gCount:int;
    private var sightX:Number, sightY:Number;
    private var pillars:Array;
    private var pillarRect:Rectangle;
    private var harX:Number, harY:Number;
    private var wireRect:Rectangle;
    private var _sound:Sound;
    private var _sequencer:Sequencer;
    private var _module:TinySiOPM;
    private var scoreText:TextField;
    private var score:int;

    public function Ground()
    {
      buffer = new BitmapData(SCREEN_WIDTH, SCREEN_HEIGHT, false, 0);
      var screen:Bitmap = new Bitmap(buffer);
      addChild(screen);
      rect = new Rectangle;
      pillarRect = new Rectangle;
      gys = new Array;
      gzs = new Array;
      gcs = new Array;
      gas = new Array;
      gIndices = new Array;
      var di:Number = 0;
      var did:Number = 1.0;
      gCount = 0;
      while (di < COUNT)
      {
        gys.push(0.0);
        gzs.push(0.0);
        gcs.push(int(0));
        gas.push(int(0));
        gIndices.push(int(0));
        if (di > LOD_START_COUNT)
          did *= LOD_RATIO;
        di += did;
        gCount++;
      }
      var i:int;
      degs = new Array;
      for (i = 0; i < COUNT; i++)
      {
        var d:Number;
        if (i < COUNT / 4)
          d = -0.001;
        else if (i < COUNT / 2)
          d = 0.002;
        else if (i < COUNT / 4 * 3)
          d = -0.003;
        else
          d = 0.004;
        degs.push(d);
      }
      pillars = new Array;
      for (i = 0; i < PILLARS_COUNT; i++)
      {
        var p:Pillar = new Pillar;
        p.x = (Math.random() * 2.0 - 1.0) * 200;
        p.z = Math.random() * COUNT;
        p.width = 2;
        p.height = 10 + Math.random() * 15;
        pillars.push(p);
      }
      index = 0;
      offset = 0;
      sightX = sightY = 0;
      harX = harY = 0;
      wireRect = new Rectangle;
      initializeSound();
      _sound.play();
      stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
      stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
      addEventListener(Event.ENTER_FRAME, onEnterFrame);

      score = 0;
      scoreText = new TextField();
      with(scoreText) {
        width = 300;
        height = 300;
        multiline = true;
        textColor = 0xffffff;
        text="SCORE 0000000000";
      }
      addChild(scoreText);
      initBackGround();
    }

    private function onEnterFrame(evt:Event):void
    {
      score += 1;
      drawBackGround();
      goForward(3.3);
      drawGroundAndPillars();
      drawShots();
      updateHar();
      Shot.updateAll();
      updateScore();
    }

    private function updateScore():void
    {
      var txt:String = "";
      txt = "" + score;
      while(txt.length < 10) txt = "0" + txt;
      scoreText.text = "SCORE " + txt;
    }

    private var bgMatrix:Matrix;
    private var bgBuffer:BitmapData;
    private var bgRect:Rectangle = new Rectangle();
    private function initBackGround():void
    {
      bgMatrix = new Matrix();
      bgBuffer = new BitmapData(SCREEN_WIDTH, SCREEN_HEIGHT, false, 0);

      var BACK_R :int = 210, BACK_G :int = 128, BACK_B :int = 240;
      var BACK_R2:int = 210, BACK_G2:int = 255, BACK_B2:int = 255;
      var rect:Rectangle = bgRect;
      var i:int;
      rect.x=0;rect.y=0;
      rect.width = SCREEN_WIDTH;
      rect.height = SCREEN_HEIGHT - 180;
      bgBuffer.fillRect(rect, BACK_R * 0x10000 + BACK_G * 0x100 + BACK_B);
      rect.y = rect.height;
      rect.height = 3;
      for (i = 0; i < 60; i++)
      {
        bgBuffer.fillRect(rect,
          int(((60-i)*BACK_R+i*BACK_R2)/60) * 0x10000
          + int(((60-i)*BACK_G+i*BACK_G2)/60) * 0x100
          + int(((60-i)*BACK_B+i*BACK_B2)/60));
        rect.y += 3;
      }
    }
    private function drawBackGround():void
    {
      var y:Number = gys[gCount - 1];
      var z:Number = gzs[gCount - 1];
      var sy:Number = y * NEAR_PLANE_DIST / z * PROJECTION_RATIO + SCREEN_HEIGHT / 2;
      bgMatrix.ty = -SCREEN_HEIGHT + sy + 10;
      buffer.draw(bgBuffer, bgMatrix);
    }

    private function drawGroundAndPillars():void
    {
      var idx:int = index;
      var d:Number = degs[idx];
      var dv:Number = -offset * d;
      var y:Number = 5 - offset * d;
      var z:Number = 1.0 - offset;
      var di:Number = 0;
      var did:Number = 1.0;
      var i:int;
      var gi:int = 0;
      var r:Number;
      for (i = 0; i < COUNT; i++)
      {
        var py:Number = y;
        var pz:Number = z;
        dv += degs[idx] + sightY * 0.00002;
        y += dv;
        z += 1.0;
        if (i > di)
        {
          r = Number(i) - di;
          gys[gi] = py * r + y * (1 - r);
          gzs[gi] = pz * r + z * (1 - r);
          var a:int = 255 - int(i * 255 / COUNT);
          gas[gi] = a;
          if (idx % 16 < 8)
            gcs[gi] = createColor(0x77, 0xbb, 0x77, a);
          else
            gcs[gi] = createColor(0x99, 0x99, 0x99, a);
          gIndices[gi] = idx;
          gi++;
          if (di > LOD_START_COUNT)
            did *= LOD_RATIO;
          di += did;
        }
        idx++;
        if (idx >= COUNT)
          idx = 0;
      }
      rect.x = 0;
      rect.width = SCREEN_WIDTH;
      var sx:Number;
      var sy:Number;
      var psy:Number = 99999;

      for (i = gCount - 1; i >= 0; i--)
      {
        y = gys[i];
        z = gzs[i];
        sy = y * NEAR_PLANE_DIST / z * PROJECTION_RATIO + SCREEN_HEIGHT / 2;
        if (sy > psy)
        {
          rect.y = psy;
          rect.height = sy - psy;
          buffer.fillRect(rect, gcs[i]);
        }
        if (i < gCount - 1)
        {
          for (var j:int = 0; j < PILLARS_COUNT; j++)
          {
            var p:Pillar = pillars[j];
            if (gIndices[i] > gIndices[i + 1])
            {
              drawPillar(p, gIndices[i], gIndices[i + 1] + COUNT, i);
              drawPillar(p, gIndices[i] - COUNT, gIndices[i + 1], i);
            }
            else
            {
              drawPillar(p, gIndices[i], gIndices[i + 1], i);
            }
          }
        }
        psy = sy;
      }
    }

    private var tree:Tree = new Tree();

    private function drawPillar(p:Pillar, si:int, ei:int, i:int):void
    {
      if (p.z >= si && p.z < ei)
      {
        var r:Number = Number(ei - p.z) / (ei - si);
        var y:Number = gys[i] * r + gys[i + 1] * (1 - r);
        var z:Number = gzs[i] * r + gzs[i + 1] * (1 - r);
        var sx:Number = (p.x - sightX) * NEAR_PLANE_DIST / z * PROJECTION_RATIO + SCREEN_WIDTH / 2;
        var sy:Number = y * NEAR_PLANE_DIST / z * PROJECTION_RATIO + SCREEN_HEIGHT / 2;
        var sh:Number = NEAR_PLANE_DIST / z * PROJECTION_RATIO;
//*
        tree.matrix.tx = sx;
        tree.matrix.ty = sy-sh*10;
        tree.matrix.a = sh/20;
        tree.matrix.d = sh/10;

        var ct:ColorTransform = new ColorTransform();
        ct.redOffset = ct.blueOffset = ct.greenOffset = 192-gas[i]*3/4;
        buffer.draw(tree.canvas, tree.matrix, ct);
//*/
/*
        for (var j:int = 0; j < 5; j++)
        {
          pillarRect.x = sx - sh * p.width * 2 / 5 * j;
          pillarRect.y = sy - sh * p.height;
          pillarRect.width = sh * p.width * 2 / 5;
          pillarRect.height = sh * p.height * 2;
          buffer.fillRect(pillarRect, createColor(0x77 + 0x11 * j, 0x77 + 0x11 * j, 0xcc, gas[i]));
        }
//*/
      }
    }

    private function drawShots():void
    {
       for each (var s:Shot in Shot.displayList) drawShot(s);
    }

    private function drawShot(s:Shot):void
    {
      var sh:Number = NEAR_PLANE_DIST / s.z * PROJECTION_RATIO;
      var sx:Number = (s.x - sightX) * sh + SCREEN_WIDTH * 0.5;
      var sy:Number = s.y * sh + SCREEN_HEIGHT * 0.5;
      pillarRect.x = sx - sh*6;
      pillarRect.y = sy - sh*6;
      pillarRect.width = sh*12;
      pillarRect.height = sh*12;
      buffer.fillRect(pillarRect, createColor(0xcc, 0x77, 0x77, 256-s.z*0.5));
    }

    private function createColor(r:int, g:int, b:int, a:int):int
    {
      return int((r * a + BACK_R * (256 - a)) / 256) * 0x10000 +
             int((g * a + 230 * (256 - a)) / 256) * 0x100 +
             int((b * a + BACK_B * (256 - a)) / 256);
    }

    private function goForward(v:Number):void
    {
      offset += v;
      while (offset >= 1.0)
      {
        offset -= 1.0;
        index++;
        if (index >= COUNT)
          index -= COUNT;
      }
    }

    private function updateHar():void
    {
      if (right && harX < SCREEN_WIDTH / 2 - 20)
        harX += HAR_SPEED;
      if (left && harX > -SCREEN_WIDTH / 2 + 20)
        harX -= HAR_SPEED;
      if (up && harY < SCREEN_HEIGHT / 2 - 40)
        harY += HAR_SPEED;
      if (down && harY > -SCREEN_HEIGHT / 2 + 40)
        harY -= HAR_SPEED;
      sightX = harX * 0.4;
      sightY = harY * 0.6;
      var landing:Boolean = (harY >= SCREEN_HEIGHT / 2 - 40);
      var hx:int = harX + SCREEN_WIDTH / 2;
      var hy:int = harY + SCREEN_HEIGHT / 2;
      rect.x = hx - 12;
      rect.y = hy - 30;
      rect.width = 20;
      rect.height = 40;
      drawWireBox(rect, 0xdd7777);
      rect.x++;
      drawWireBox(rect, 0xaa4444);
      rect.x = hx - 14;
      rect.y = hy - 45;
      rect.width = 16;
      rect.height = 20;
      drawWireBox(rect, 0xdddd77);
      rect.x++;
      drawWireBox(rect, 0xaaaa44);
      rect.x = (landing && (index&16)==0)?(hx + 3):(hx - 20);
      rect.y = hy + 5;
      rect.width = 12;
      rect.height = 40;
      drawWireBox(rect, 0x77dddd);
      rect.x++;
      drawWireBox(rect, 0x44aaaa);
      rect.x = (landing && (index&16)==0)?(hx - 20):(hx - 1);
      rect.y = hy + 10;
      rect.width = 15;
      rect.height = 25;
      drawWireBox(rect, 0x77dddd);
      rect.x++;
      drawWireBox(rect, 0x44aaaa);
      rect.x = hx - 1;
      rect.y = hy - 12;
      rect.width = 16;
      rect.height = 16;
      drawWireBox(rect, 0xee4444);
      rect.x++;
      drawWireBox(rect, 0xbb2222);

      if (shot && (index&3)==0) {
        Shot.alloc().reset(harX*(0.4+0.12), harY*0.12, 1);
      }
    }

    private function drawWireBox(rect:Rectangle, color:int):void
    {
      wireRect.x = rect.x;
      wireRect.y = rect.y;
      wireRect.width = rect.width;
      wireRect.height = WIRE_WIDTH;
      buffer.fillRect(wireRect, color);
      wireRect.y = rect.y + rect.height - WIRE_WIDTH;
      buffer.fillRect(wireRect, color);
      wireRect.y = rect.y;
      wireRect.width = WIRE_WIDTH;
      wireRect.height = rect.height;
      buffer.fillRect(wireRect, color);
      wireRect.x = rect.x + rect.width - WIRE_WIDTH;
      buffer.fillRect(wireRect, color);
    }

    private function onKeyUp(evt:KeyboardEvent):void
    {
      switch (evt.keyCode)
      {
      case 0x25:
      case 0x41:
        left = false;
        break;
      case 0x26:
      case 0x57:
        up = false;
        break;
      case 0x27:
      case 0x44:
        right = false;
        break;
      case 0x28:
      case 0x53:
        down = false;
        break;
      case 0x10:
      case 0x5a:
        shot = false;
        break;
      }
    }

    private function onKeyDown(evt:KeyboardEvent):void
    {
      switch (evt.keyCode)
      {
      case 0x25:
      case 0x41:
        left = true;
        break;
      case 0x26:
      case 0x57:
        up = true;
        break;
      case 0x27:
      case 0x44:
        right = true;
        break;
      case 0x28:
      case 0x53:
        down = true;
        break;
      case 0x10:
      case 0x5a:
        shot = true;
        break;
      }
    }

    private function initializeSound():void
    {
      var A:String,B:String,C:String,D:String,E:String,F:String,G:String,H:String,X:String;
      var M1:String="l2[f6ee16>g4a4<f6ee16>a+<crrf6ee16d4e4f16g+6g6f4]";
      var M2:String="[e20>l4eg<d|c+12>a+a10gf2ef2d6el3fef2gfg2a12g+4<f8d8]";
      var M3:String="c+16l2rec+>a+agfed12e4l3fef2gfg2a12g2<";
      A ="$@11s8o6l2[[c12cc18|c10crc18]|c10<crc18]dddrrdrcr16> [[s6"+M1+M2+M3+"|g10l1gab<cdefgl2o7]g18|";
      B ="$@7s6o5l2[4e12ef18|f10fre18]fffrrfrer16 [[l2s6v6[[c6>brr<d6crr>|b<crrc6>a+rr<d6crr>a+<crr]b4<c4c10dedd6c6d4]";
      B+="s2l4[e28dc+16>a+8ag<d16c8d8|c16d8f8]c14g18]|";
      C ="$@7s6o5l2[4c12cc18|c10crc18]dddrrdrcr16> [[l2s6v6[[g6grrg6grrggrr]8a6arra6al4raaf10l6ag+g+f4l2]";
      C+="s2[l32gef|l16fg+]f14g18]|";
      D ="$@7s6o5l2k0[4g12ga18|g+10g+rg18]g+g+g+rrg+rgr16 [[v6o6k2"+M1+M2+M3+"|g10l1gab<cdefgl2o7]g18|";
      E ="$@1s16l2o3[56c<c>]<cccrrcrc>r16 [[[[8c<c>]>[8a+<a+>][7a<a>]g<g>[8f<f>]|<]";
      E+="[[8e<e>][8a<a>][8d<d>]|[8f<f>]][4f<f>]g<g>a<a>a+<a+>b<b]|";
      F ="$v10@3s32o0k4l4r116cr[4c1][12r4c4]l2cccl1ccc2l2crcrr[12c1] [[l4[32rc][30rc]ccc2c2l1cccc]|";
      G ="$v4@3s64o0l1[224d]r32 [[1024d]|[124drdd]r16]";
      H ="$v16w32s48o4l4[56c]l2cccl1<gec2>l2crcrrl1<[4g][4e][4c]> [l8[128c]|";
      A+=">r256[v6s0a18s2l2v4cdv5ev6gev5ds0c20s2v6g>a<cv5e>a<cs0d22s1v6c4>b4<c4>bagar24<]]";
      X ="[aagar14|eee6]fff6bbab";
      B+="l2s12["+X+"r14ggg6aagar24]["+X+"r12s4g4g4g4s12aaga|r24]r10aa4bb6]";
      X ="eeder14>ab<c6ffefr14>ab<c6ggfg";
      C+="l2s12<["+X+"r14>b<cd6eeder24]["+X+"r12s4>b4<c4d4s12eede|r24]r10ff4gg6>]";
      D+="l2s12>[4[cc>b<cr24]ddcdr24cc>b<c|r24]r10cc4dd6]";
      E+=">[4[8a<a>][8f<f>][8g<g>]|[8a<a>]][4a<a>]rff4gg6<]";
      F+="[l8[8rc6|r14c4]|r2l4rcrc]c1c3l2cc4ccl1cccc]";
      H+="[l3[8v16cc1c2c2r8|ccc2r2c1v8c1r4]|l1ccccrcrcrrccr2cr]l2rcc4ccl1<gec>g]";
      _sound = new Sound();
      _sound.addEventListener("sampleData", _onSoundStream);
      _module = new TinySiOPM(8192, _onSoundFrame);
      _sequencer = new Sequencer(4, [A,B,C,D,E,F,G,H]);
    }

    private function _onSoundFrame():void
    {
      _sequencer.onSoundFrame();
    }

    private function _onSoundStream(e:SampleDataEvent):void
    {
      var out:Vector.<Number> = _module.render();
      for (var i:int=0; i<8192; i++) {
        e.data.writeFloat(out[i]);
        e.data.writeFloat(out[i]);
      }
    }
  }
}

class Pillar
{
  public var x:Number;
  public var z:Number;
  public var width:Number;
  public var height:Number;
}

class Shot
{
  static private const SHOT_SPEED:Number = 30;
  static private const SHOT_Z_LIMIT:Number = 512;
  static private var _freeList:Array=[];
  static public var displayList:Array=[];
  static public function alloc():Shot {
    return _freeList.pop()||new Shot();
  }
  static public function updateAll():void {
    displayList = displayList.filter(function(e:*, i:int, a:Array):Boolean{
      return e.update();
    });
  }

  public var x:Number;
  public var y:Number;
  public var z:Number;
  public function reset(x_:Number, y_:Number, z_:Number) : Shot {
    x=x_; y=y_; z=z_;
    displayList.push(this);
    return this;
  }
  public function free() : void { 
    _freeList.push(this);
  }
  public function update() : Boolean {
    z+=SHOT_SPEED;
    if (z>SHOT_Z_LIMIT) {
      free();
      return false;
    }
    return true;
  }
}

import flash.display.*;
import flash.geom.Rectangle;
import flash.geom.Matrix;
class Tree{ 
  public var matrix:Matrix = new Matrix();
  public var canvas:BitmapData = new BitmapData(200, 200, true, 0);
  public function Tree() {
    seed = Math.random() *0xfffffff;
    const a:Vector.<Number> = Vector.<Number>([0.05, 0.05, 0.46, 0.47, 0.43, 0.42]);
    const b:Vector.<Number> = Vector.<Number>([0, 0, -0.32, -0.15, 0.28, 0.26]);
    const c:Vector.<Number> = Vector.<Number>([0, 0, 0.39, 0.17, -0.25, -0.35]);
    const d:Vector.<Number> = Vector.<Number>([0.6, -0.5, 0.38, 0.42, 0.45, 0.31]);
    const e:Vector.<Number> = Vector.<Number>([0, 0, 0, 0, 0, 0]);
    const f:Vector.<Number> = Vector.<Number>([0, 1, 0.6, 1.1, 1, 0.7]);
    const N:uint = 6;
    const M:uint = N*25;

    var i:int, j:int, k:int, r:int;
    var x:Number, y:Number, s:Number = 0, t:Number;
    var ip:Vector.<int>    = new Vector.<int>(N);
    var table:Vector.<int> = new Vector.<int>(M);
    var p:Vector.<Number> = new Vector.<Number>(N);

    for (i = 0; i < N; i++)
    {
      p[i] = abs(a[i] * d[i] - b[i] * c[i]);
      s += p[i];
      ip[i] = i;
    }

    for (i = 0; i < N-1; i++) 
    {
      k = i;
      for (j = i + 1; j < N; j++ )
        if (p[j] < p[k]) k = j;
      t =  p[i];  p[i] =  p[k];  p[k] = t;
      r = ip[i]; ip[i] = ip[k]; ip[k] = r;
    }

    r = M;

    for (i = 0; i < N; i++) 
    {
      k = (r * p[i] / s + 0.5) >> 0;
      s -= p[i];
      do
      {
        table[--r] = ip[i];
      }
      while (--k > 0);
    }

    x = y = 0;
    var rect:Rectangle = new Rectangle();
    var w:int;
    const offsetX:int = 50;
    const scale:int = 50;
    const offsetY:int = 100;
    rect.width = 8;
    rect.height = 4;

    for (i = 0; i < 1000; i++) 
    {
      j = table[random() % M];
      t = a[j] * x + b[j] * y + e[j];
      y = c[j] * x + d[j] * y + f[j];
      x = t;
      rect.x = offsetX + x * scale;
      rect.y = offsetY - y * scale;

      if(j > 1) w++; else w = 0;
      if(w > 3)
             if(i % 16 == 0) k = 0xff68AC36;
        else if(i % 4  == 0) k = 0xff326C06;
        else                 k = 0xff143606;
      else                   k = 0xff441111;

      canvas.fillRect(rect, k);
    }

  }
  private var seed:uint;
  private function abs (v:Number):Number {
    if (v < 0) return -v; return v;
  }
  private function random ():uint {
    return seed = seed * 144314351 + 43214141;
  }
}

class Sequencer {
  private var tracks:Array, count:int=Track.speed+1;
  function Sequencer(speed:int, mmls:Array) { Track.speed=speed; mml=mmls; }
  public function onSoundFrame() : void {
    if (++count == Track.speed) {
      for each (var tr:Track in tracks) tr.execute();
      count = 0;
    }
  }
  public function set mml(list:Array) : void {
    tracks = [];
    for each (var seq:String in list) tracks.push(new Track(seq));
    count = 0;
  }
}

class Track {
  static public var codeA:int="a".charCodeAt(), nt:Array=[9,11,0,2,4,5,7], speed:int=3;
  public var oct:int, len:int, tl:int, dt:int, cnt:int, seq:String, sgn:int, stac:Array, note:Note;
  private var _rex:RegExp=/([a-gklorsvw<>[|\]$@])([#+])?(\d+)?/g;
  function Track(seq:String) {
    note = new Note(false);
    reset(seq);
  }
  public function reset(seq_:String) : void {
    seq=seq_; oct=5; len=4; tl=256; dt=0; cnt=0; sgn=0; _rex.lastIndex=0; stac=[];
  }
  public function execute() : void {
    if (--cnt <= 0) {
      for (var i:int=0; i<100; i++) {
        var res:* = _rex.exec(seq);
        if (!res) {
          if (sgn) { _rex.lastIndex = sgn; continue; }
          else     { cnt = int.MAX_VALUE; break; }
        }
        var cmd:int = res[1].charCodeAt();
        if (cmd>=codeA && cmd<=codeA+6) {
          cnt = (res[3])?int(res[3]):len;
          if (note.isPlaying()) note.stop();
          note.len = cnt*speed;
          note.tl = tl;
          note.play(((nt[cmd-codeA]+oct*12+((res[2])?1:0))<<4)+dt);
          break;
        } else if (res[1] == 'r') {
          cnt = (res[3])?int(res[3]):len;
          if (note.isPlaying()) note.stop();
          break;
        } else {
          switch(res[1]){
          case 'k': dt  = int(res[3]); break;
          case 'l': len = int(res[3]); break;
          case 'o': oct = int(res[3]); break;
          case 'v': tl  = TinySiOPM.log(int(res[3])*0.0625); break;
          case '<': oct++; break;
          case '>': oct--; break;
          case '@': note.ws = int(res[3]);    break;
          case 's': note.dr = int(res[3])<<2; break;
          case 'w': note.sw = -int(res[3]);   break;
          case '$': sgn = _rex.lastIndex; break;
          case '[': stac.unshift({p:_rex.lastIndex,c:((res[3])?int(res[3]):2),j:0}); break;
          case ']': stac[0].j = _rex.lastIndex; if (--stac[0].c == 0) stac.shift(); else _rex.lastIndex = stac[0].p; break;
          case '|':  if (stac[0].c == 1) { _rex.lastIndex = stac[0].j; stac.shift(); } break;
          }
        }
      }
    }
  }
}

class TinySiOPM {
  private var _output:Vector.<Number>, _bufferSize:int, _frameCallBack:Function;
  private var _pitchTable:Vector.<int> = new Vector.<int>(2048, true);
  private var _logTable:Vector.<Number> = new Vector.<Number>(6144, true);
  
  // Pass the buffer size and the function calls in each frame.
  function TinySiOPM(bufferSize:int=2048, frameCallBack:Function=null) {
    var i:int, j:int, p:Number, v:Number, t:Vector.<int>, ft:Array=[0,1,2,3,4,5,6,7,7,6,5,4,3,2,1,0];
    for (i=0, p=0; i<192; i++, p+=0.00520833333)                            // create pitchTable[128*16]
      for(v=Math.pow(2, p)*12441.464342886, j=i; j<2048; v*=2, j+=192) _pitchTable[j] = int(v);
    for (i=0; i<32; i++) _pitchTable[i] = (i+1)<<6;                         // [0:31] for white noize
    for (i=0, p=0.0078125; i<256; i+=2, p+=0.0078125)                       // create logTable[12*256*2]
      for(v=Math.pow(2, 13-p)*0.0001220703125, j=i; j<3328; v*=0.5, j+=256) _logTable[j+1] = -(_logTable[j] = v);
    for (i=3328; i<6144; i++) _logTable[i] = 0;                             // [3328:6144] is 0-fill area
    for (t=Note.table(10), i=0, p=0; i<1024; i++, p+=0.00613592315) t[i] = log(Math.sin(p));  // sin=0
    for (t=Note.table(10), i=0, p=0.75; i<1024; i++, p-=0.00146484375) t[i] = log(p);         // saw=1
    for (t=Note.table(5),  i=0; i<16; i++) t[i+16] = (t[i] = log(ft[i]*0.0625)) + 1;          // famtri=2
    for (t=Note.table(15), i=0; i<32768; i++) t[i] = log(Math.random()-0.5);                  // wnoize=3
    for (i=0; i<8; i++) for (t=Note.table(4), j=0; j<16; j++) t[j] = (j<=i) ? 192 : 193;      // pulse=4-11
    _output = new Vector.<Number>(bufferSize, true);                        // allocate monoral buffer
    _bufferSize = bufferSize; _frameCallBack = frameCallBack;               // set parameters
  }
  
  // calculate index of logTable
  static public function log(n:Number) : int {
    return (n<0) ? ((n<-0.00390625) ? (((int(Math.log(-n) * -184.66496523 + 0.5) + 1) << 1) + 1) : 2047)
                 : ((n> 0.00390625) ? (( int(Math.log( n) * -184.66496523 + 0.5) + 1) << 1)      : 2046);
  }
  
  // Returns monoral output as Vector.<Number>(bufferSize).
  public function render() : Vector.<Number> {
    var note:Note, rep:int, i:int, imax:int, ph:int, dph:int, lout:int, v:int;
    for (i=0; i<_bufferSize; i++) _output[i] = 0;
    for (imax=1024; imax<=_bufferSize; imax+=1024) {
      if (_frameCallBack!=null) _frameCallBack();
      var terminal:Note = Note._tm;
      for (note=terminal.n; note!=terminal; note=note.step()) {
        dph = _pitchTable[note.pt];
        for (i = imax-1024; i < imax; i++) {
          ph = note.ph>>note.sh;
          lout = note.wv[ph] + note.tl;
          _output[i] += _logTable[lout];
          note.ph = (note.ph+dph)&0x3ffffff;
        }
      }
    }
    return _output;
  }
  
  // note on
  public function noteOn(pitch:int, length:int, vol:Number=0.5, wave:int=0, decay:int=6, sweep:int=0) : Note {
    var note:Note = Note.alloc();
    note.len = length;
    note.tl = log(vol);
    note.ws = wave;
    note.dr = decay<<2;
    note.sw = sweep; 
    return note.play(pitch);
  }
}

class Note {
  static public var _w:Array=[], _s:Array=[], _fl:Note=new Note(), _tm:Note=new Note();
  static public function alloc():Note {if(_fl.p==_fl)return new Note();var r:Note=_fl.p;_fl.p=r.p;r.p.n=_fl;return r.reset();}
  static public function table(b:int):Vector.<int>{_w.push(new Vector.<int>(1<<b,true));_s.push(26-b);return _w[_w.length-1];}
  public function into(x:Note):Note{ p=x.p;n=x;p.n=this;n.p=this;return this; }
  public function step():Note { tl+=dr; pt+=sw; pt&=2047; return (--len==0||tl>3328) ? (stop().n) : n; }
  public var p:Note, n:Note, pt:int, tl:int, sw:int, dr:int, wv:Vector.<int>, sh:int, len:int, ph:int, fl:Note;
  function Note(useFreeList:Boolean=true) { p=n=this; fl=(useFreeList)?_fl:null; reset(); }
  public function set ws(t:int) : void { wv=_w[t]; sh=_s[t]; }
  public function play(pitch:int) : Note { into(_tm); pt=pitch; return this; }
  public function stop() : Note { var r:Note=p; p.n=n; n.p=p; if(fl)into(fl); pt=-1; return r; }
  public function reset() : Note { ph=0; pt=-1; len=0; tl=256; sw=0; dr=24; ws=0; return this; }
  public function isPlaying() : Boolean { return (pt>=0); }
}