popforgeでのこぎり波とか2(とにかく音量注意ね!!)

2008/07/1

こんにちは、あいかわらず井の中の蛙だったり、お釈迦様の手のひらの上の孫悟空だったりする、きんくまです。

いろいろと情報をみた結果、次のAstroではpopforge使わずにかなりのことができるみたいですね。
→AS3で扱うサウンド
→DynamicSoundをTweenerで変化

それで、次はProgressionとそうめんを勉強したいと思っているので、とりあえずpopforgeは今回でしばらく置いておこうかと思います。

さて、やっぱりすごい人はいるもので、すでにpopforgeでここまでできるんですね。
→FlMML
fla上でpopforgeを使って動的に、MMLといわれる楽譜を演奏しちゃうすごいやつ!ていうか、これ以前に見かけていました。それをすっかり頭から抜け落ちてしまっていました。3歩歩くと忘れる鳥みたいな記憶力なんですみません。

それで、この素敵なテキスケさん(?なのかな)が開発していたFIMMLのソースコードが読めちゃうので、早速音をどうやってだしているかを見てみました。前回、のこぎり波とかどうやってだすのかわからんかったので、興味津々だったわけです。

なんとかそれらしい記述を発見して、前回のものと組み合わせたものが以下のものです。これ修正BSDライセンスというものらしいんですが、大丈夫なんでしょうか?
プチプチいっているのは、バッファというところに音をためて、一定時間ごとにバッファを書き変えているからなんですが、今の私の実力だとどうやってこれを解除していいかがわかりません。前のバッファの位相を次のバッファに渡せればたぶんそんな心配はいらないかと思うのですが、どうやるのでしょう?

まとめ
音は画像と同じくらい奥が深い!!基礎知識を身につけるのも奥が深すぎる。画像にもエフェクトがあるように、音にもエフェクトがあって、それは結構難しい。
次のAstroでは、音を作り出せるので、これからますます音と画像の融合みたいなことがすすみそう。今度Astro触ってみよう。

以下ソースです。

package
{
  import de.popforge.audio.output.Audio;
  import de.popforge.audio.output.AudioBuffer;
  import de.popforge.audio.output.Sample;
  import de.popforge.audio.output.SoundFactory;
  import de.popforge.gui.Slider;
  import de.popforge.parameter.MappingNumberExponential;
  import de.popforge.parameter.Parameter;

  import flash.display.Sprite;
  import flash.events.MouseEvent;
  import flash.media.Sound;
  import flash.text.TextField;
  import flash.text.TextFieldAutoSize;
  import flash.text.TextFormat;
  import flash.text.TextFormatAlign;

  [SWF(width="300", height="220", frameRate="24", backgroundColor="#f0f0f0")]
  public class WaveTypeTest extends Sprite
  {
        public static const SINE:int     = 0;
        public static const SAW:int      = 1;
        public static const TRIANGLE:int = 2;
        public static const PULSE:int    = 3;
        public static const NOISE:int    = 4;
        public static const MAX:int      = 5;

        private var m_form:Number;
        private var m_frequency:Number;
        private var m_noiseFreq:Number;
        private var m_phase:Number;
        private var m_pwm:Number;
        private static var s_init:int = 0;
        private static var s_sinTable:Array = new Array(Audio.RATE44100 * 5);
        private static var s_sinLen:int;
        private static var s_rndTable:Array = new Array(Audio.RATE44100);
        private static var s_rndLen:int;
    private var isPlay:Boolean = false;

    private var buffer:AudioBuffer;
    public var parameterFrequency: Parameter;
    private var option:Boolean = false;

    public function WaveTypeTest()
    {
      super();
      //init2();
      setStartBtn();
    }

     private function init():void
    {
      boot();
            m_form = PULSE;
            m_frequency = 440.0;
            m_noiseFreq = 1.0;
            m_phase = 0.0;
            m_pwm = 0.5;

            var amplitude: Number;
            var sample:Sample;
            var samples:Array = new Array();
      for( var i: int = 0 ; i < Audio.RATE44100 ; i++ )
      {
        amplitude = getNextSample();
        sample = new Sample( amplitude, amplitude );
        samples.push( sample );
         }

            SoundFactory.fromArray( samples, Audio.STEREO, Audio.BIT16, Audio.RATE44100, onSoundGenerated );
    }

    private function init2():void
    {
      boot();
            m_form = SINE;
            m_frequency = 440.0;
            m_noiseFreq = 1.0;
            m_phase = 0.0;
            m_pwm = 0.5;


      parameterFrequency = new Parameter( new MappingNumberExponential( 110, 8000 ), m_frequency = 110 );
      var sliderFrequency: Slider = new Slider( parameterFrequency );
      sliderFrequency.x = 60;
      sliderFrequency.y = 180;
      addChild( sliderFrequency );

      buffer = new AudioBuffer( 12, Audio.STEREO, Audio.BIT16, Audio.RATE44100);
      buffer.onInit = onAudioBufferInit;
      buffer.onComplete = onAudioBufferComplete;

      var sineBtn:Button = new Button("サイン波", 0x0c3c61);
      addChild(sineBtn);
      sineBtn.name = WaveTypeTest.SINE.toString();
      sineBtn.x = 20;
      sineBtn.y = 20;
      sineBtn.addEventListener(MouseEvent.MOUSE_DOWN, onBtnDown);
      sineBtn.buttonMode = sineBtn.useHandCursor = true;

      var squareBtn:Button = new Button("方形波", 0x0c3c61);
      addChild(squareBtn);
      squareBtn.x = sineBtn.x + sineBtn.width + 20;
      squareBtn.y = sineBtn.y
      squareBtn.name = WaveTypeTest.PULSE.toString();
      squareBtn.addEventListener(MouseEvent.MOUSE_DOWN, onBtnDown);
      squareBtn.buttonMode = squareBtn.useHandCursor = true;

      var sawBtn:Button = new Button("のこぎり波",0x0c3c61);
      addChild(sawBtn);
      sawBtn.x = squareBtn.x + squareBtn.width + 20;
      sawBtn.y = sineBtn.y
      sawBtn.name = WaveTypeTest.SAW.toString();
      sawBtn.addEventListener(MouseEvent.MOUSE_DOWN, onBtnDown);
      sawBtn.buttonMode = sawBtn.useHandCursor = true;

      var triBtn:Button = new Button("三角波",0x0c3c61);
      addChild(triBtn);
      triBtn.x = 20;
      triBtn.y = sineBtn.y + sineBtn.height + 20;
      triBtn.name = WaveTypeTest.TRIANGLE.toString();
      triBtn.addEventListener(MouseEvent.MOUSE_DOWN, onBtnDown);
      triBtn.buttonMode = triBtn.useHandCursor = true;

      var noizeBtn:Button = new Button("ホワイトノイズ",0x0c3c61);
      addChild(noizeBtn);
      noizeBtn.x = triBtn.x + triBtn.width + 20;
      noizeBtn.y = sineBtn.y + sineBtn.height + 20;
      noizeBtn.name = WaveTypeTest.NOISE.toString();
      noizeBtn.addEventListener(MouseEvent.MOUSE_DOWN, onBtnDown);
      noizeBtn.buttonMode = noizeBtn.useHandCursor = true;

      var stopBtn:Button = new Button("ストップ",0x000000);
      addChild(stopBtn);
      stopBtn.x = 20;
      stopBtn.y = noizeBtn.y + noizeBtn.height + 20;
      stopBtn.addEventListener(MouseEvent.MOUSE_DOWN, onStopDown);
      stopBtn.buttonMode = stopBtn.useHandCursor = true;

      var optionBtn:Button = new Button("なだらかに音階変化",0x000000);
      addChild(optionBtn);
      optionBtn.x = stopBtn.x + stopBtn.width + 20;
      optionBtn.y = stopBtn.y;
      optionBtn.addEventListener(MouseEvent.MOUSE_DOWN, onOptionDown);
      optionBtn.buttonMode = optionBtn.useHandCursor = true;
    }

    private function onAudioBufferInit( buffer: AudioBuffer ): void
    {
      buffer.start();
    }

    private function onAudioBufferComplete( buffer: AudioBuffer ): void
    {
            var amplitude: Number;
            var samples: Array = buffer.getSamples();
      for( var i: int = 0 ; i < Audio.RATE44100 ; i++ )
      {
        amplitude = getNextSample();
        samples[i] = new Sample( amplitude, amplitude );
         }
         buffer.update();
         if(option)
         {
           m_frequency += ( 20 + parameterFrequency.getValue() - m_frequency ) * .05;
         }
         else
         {
           m_frequency = parameterFrequency.getValue();
         }
    }

    private function onBtnDown(e:MouseEvent):void
    {
      var button:Sprite = e.currentTarget as Sprite;
      m_form = Number(button.name);
    }

    private function onStopDown(e:MouseEvent):void
    {
      var btn:Button = e.currentTarget as Button;
      isPlay = !isPlay;
      if(isPlay)
      {
        buffer.start();
        btn.setText("ストップ");
      }
      else
      {
        buffer.stop();
        btn.setText("スタート");
      }

    }

    private function onOptionDown(e:MouseEvent):void
    {
      var btn:Button = e.currentTarget as Button;
      option = !option;
      var color:Number = option ? 0xed8f15 : 0x000000;
      btn.setColor(color, 1);
    }

       private function boot():void {
            if (!s_init) {
                s_sinLen = s_sinTable.length;
                var d:Number = 2.0 * Math.PI / s_sinLen;
                var p:Number = 0.0;
                var i:int;
                for(i = 0; i < s_sinLen; i++) {
                    s_sinTable[i] = Math.sin(p);
                    p += d;
                }
                s_rndLen = s_rndTable.length;
                for(i = 0; i < s_rndLen; i++) {
                    s_rndTable[i] = Math.random() * 2.0 - 1.0;
                }
                s_init = 1;
            }
        }

        private function getNextSampleValue():Number {
            switch(m_form) {
            case SINE:
                return Number(s_sinTable[int(m_phase * s_sinLen)]);
            case SAW:
                return m_phase / 0.5 - 1.0;
            case TRIANGLE:
                return (m_phase < 0.5) ? (1.0 - 4.0 * m_phase) : (1.0 - 4.0 * (1.0 - m_phase));
            case PULSE:
                return (m_phase < m_pwm) ? 1.0 : -1.0;
            case NOISE:
                return Number(s_rndTable[int(m_phase * s_rndLen)]);
            }
            return 0.0;
        }

        private function getNextSample():Number {
            var val:Number = getNextSampleValue();
            if (m_form != NOISE)
                m_phase += (m_frequency * (1.0 / Audio.RATE44100));
            else
                m_phase += m_noiseFreq * (1.0 / Audio.RATE44100);
            if (m_phase > 1.0) {
                m_phase -= 1.0; // prevent phase from overflowing..0;
            }
            return val;
        }

    private function onSoundGenerated( sound: Sound ): void
    {
      sound.play();
    }

      private function setStartBtn():void
      {
        var sp:Sprite = new Sprite();
        sp.graphics.beginFill(0x00ff00, 1);
        sp.graphics.drawRect(0,0,250,50);
        sp.graphics.endFill();

        var tff:TextFormat= new TextFormat();
        tff.align = TextFormatAlign.CENTER;
        tff.size = 14;
        var tf:TextField = new TextField();
        tf.autoSize = TextFieldAutoSize.LEFT;
        tf.selectable = false;
        tf.text = "音量をしぼってから慎重にスタート。";
        tf.setTextFormat(tff);
        sp.addChild(tf);
        tf.x = 20;
        tf.y = 15;
        addChild(sp);
        sp.x = (stage.stageWidth - sp.width) / 2;
        sp.y = (stage.stageHeight - sp.height) / 2 - 20;
        sp.addEventListener(MouseEvent.MOUSE_DOWN, onStartDown);
      }

      private function onStartDown(e:MouseEvent):void
      {
        isPlay = true;
        var sp:Sprite = e.currentTarget as Sprite;
        removeChild(sp);
        init2();
      }
  }
}

import flash.display.Sprite;
import flash.text.TextField;
import flash.geom.Rectangle;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;

class Button extends Sprite
{
  private var textbox:TextField;
  private var textf:TextFormat;
  private var background:Sprite;


  public function Button(text:String, color:Number)
  {
    textbox = new TextField();
    addChild(textbox);
    textbox.autoSize = TextFieldAutoSize.LEFT;
    textbox.selectable = false;
    textbox.x = 5;
    textbox.y = 5;
    textbox.text = text;
    textf = new TextFormat();
    textf.color = 0xffffff;
    textbox.setTextFormat(textf);
    background = new Sprite();
    drawRect(background, color);
    addChildAt(background,0);
    var bk:Sprite = new Sprite();
    drawRect(bk, 0, 0);
    addChild(bk);
  }

  private function drawRect(sp:Sprite, color:Number, alpha:Number = 1):void
  {
    sp.graphics.clear();
    var rect:Rectangle = this.getBounds(textbox);
    sp.graphics.beginFill(color, alpha);
    sp.graphics.drawRect(0, 0, rect.width + 10, 30);
    sp.graphics.endFill();
  }

  public function setColor(color:Number, alpha:Number = 1):void
  {
    drawRect(background, color, alpha);
  }

  public function setText(text:String):void
  {
    textbox.text = text;
    textbox.setTextFormat(textf);
  }
}


自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

ページトップへ戻る