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

2008/06/30

先日の続きです。音のことまるでわかってなかったんで、本を数冊かってきて勉強しました。音が波で、波形で音色が違うぐらいまではわかったんですが、いざ実際に組み込もうとするとなんともかんともです。
大学の頃、電気電子工学科とかいうところにいて、思いっきりど真ん中直球のはずなんです。しかし、まるで覚えていない。テスト前に過去問の解き方覚えていただけ。フーリエ変換とか、ローパス・ハイパスフィルタとか、矩形波のフーリエ変換とか絶対やっていたはずなのに、まるで覚えていません。そしてあの頃は、その式が何を表しているのかまるでわかっていませんでした。

さて、popforgeです。周期の波形はサイン波を重ねていくとどんなものでもできるそうですが、私には高度すぎます。
サイン波、矩形波、のこぎり波、三角波、インパルス信号をつくってみたかったのですが、頑張ってもできませんでした。それで、適当に作ってみたのが、以下のもの。
ちなみに矩形波は、さくーしゃさんの下のエントリより拝借です。ありがとうございます。
→SoundMixer.computeSpectrum の FFT モードの周波数帯
最初に公式を真似して5回ぐらい波を重ねて作ってみようとしていました。それにしても、さくーしゃさんの日付が昨年の9月。大分周回遅れをしていることを今更ながら認識です。頑張ろう!自分。

作ってはみたものの、これで正しいのかどうかさっぱりわかりません。どなたか添削してくださると嬉しいのですが、、。
考えてみると、popforgeのサンプルに載っていたこのスライダーって、周波数をTweenさせているってことですね。うー、考えてみるとすごいことだ。

先日の雑誌の記事の作者様に連絡をとったところ、次のバージョンAstroでpopforgeなしで、音をいじれるようになるかも?との情報をいただいた。

package
{
  import de.popforge.audio.output.Audio;
  import de.popforge.audio.output.AudioBuffer;
  import de.popforge.audio.output.Sample;
  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.text.TextField;
  import flash.text.TextFieldAutoSize;
  import flash.text.TextFormat;
  import flash.text.TextFormatAlign;

  [SWF(width="280", height="250", frameRate="24", backgroundColor="#f0f0f0")]
  public class CreateAStream extends Sprite
  {
    public var parameterFrequency: Parameter;

    private var phase: Number;
    private var frequency: Number;
    private var waveType:String = "sine";
    private var option:Boolean = false;
    private var isPlay:Boolean = false;
    private var buffer:AudioBuffer;

    public function CreateAStream()
    {
      setStartBtn();
      //init();
    }

    private function init(): void
    {
      buffer = new AudioBuffer( 4, Audio.MONO, Audio.BIT8, Audio.RATE11025);
      buffer.onInit = onAudioBufferInit;
      buffer.onComplete = onAudioBufferComplete;

      parameterFrequency = new Parameter( new MappingNumberExponential( 110, 8000 ), frequency = 110 );

      var sliderFrequency: Slider = new Slider( parameterFrequency );
      sliderFrequency.x = 60;
      sliderFrequency.y = 180;
      addChild( sliderFrequency );

      frequency = 0;
      phase = 0;

      var sineBtn:Button = new Button("サイン波", 0x0c3c61);
      addChild(sineBtn);
      sineBtn.name = "sine";
      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 = "square";
      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 = "saw";
      sawBtn.addEventListener(MouseEvent.MOUSE_DOWN, onBtnDown);
      sawBtn.buttonMode = sawBtn.useHandCursor = true;

      var moterBtn:Button = new Button("モーター音みたい",0x0c3c61);
      addChild(moterBtn);
      moterBtn.x = 20;
      moterBtn.y = sineBtn.y + sineBtn.height + 20;
      moterBtn.name = "moter";
      moterBtn.addEventListener(MouseEvent.MOUSE_DOWN, onBtnDown);
      moterBtn.buttonMode = moterBtn.useHandCursor = true;

      var noizeBtn:Button = new Button("ノイズ?さざ波",0x0c3c61);
      addChild(noizeBtn);
      noizeBtn.x = moterBtn.x + moterBtn.width + 20;
      noizeBtn.y = sineBtn.y + sineBtn.height + 20;
      noizeBtn.name = "noize";
      noizeBtn.addEventListener(MouseEvent.MOUSE_DOWN, onBtnDown);
      noizeBtn.buttonMode = noizeBtn.useHandCursor = true;

      var optionBtn:Button = new Button("オプション効果。リバーブ?",0x000000);
      addChild(optionBtn);
      optionBtn.x = 20;
      optionBtn.y = moterBtn.y + moterBtn.height + 20;
      optionBtn.addEventListener(MouseEvent.MOUSE_DOWN, onOptionDown);
      optionBtn.buttonMode = optionBtn.useHandCursor = true;

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

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

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

    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 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 onAudioBufferComplete( buffer: AudioBuffer ): void
    {
      //-- get array to store samples
      var samples: Array = buffer.getSamples();

      //-- some locals
      var sample: Sample;
      var amplitude: Number;

      //-- CREATE ONE SECOND OF AUDIO (SINUS WAVE)
      for( var i: int = 0 ; i < samples.length ; i++ )
      {
        //-- store local
        sample = samples[i];

        switch(waveType)
        {
          case "sine": amplitude = getSineWave();
          break;
          case "square": amplitude = getSquareWave();
          break;
          case "saw": amplitude = getSawWave();
          break;
          case "moter": amplitude = getMoterWave();
          break;
          case "noize": amplitude = getNoizeWave();
          break;
          default: break;
        }

        //-- write to sample
        sample.left = sample.right = amplitude;

        //-- interpolate
        frequency += ( 400 + parameterFrequency.getValue() - frequency ) * .0002;

        //-- increase phase
        if(!option)
        {
          phase += frequency / Audio.RATE11025;
        }
        else
        {
          phase += frequency / samples.length;
        }
      }

      //-- update audio buffer
      buffer.update();
    }

    private function getSineWave():Number
    {
      var amplitude: Number;
      amplitude = Math.sin( phase * Math.PI * 2 );
      return amplitude;
    }

    private function getSquareWave():Number
    {
      var amplitude: Number;
      amplitude = Math.sin( phase * Math.PI * 2 );
      amplitude = amplitude < 0 ? -1 : 1;
      return amplitude;
    }

    private function getSawWave():Number
    {
      var amplitude: Number;
      phase %= Math.PI * 2;
      amplitude = -2 * phase / Math.PI + 1
      return amplitude;
    }

    private function getMoterWave():Number
    {
      var amplitude: Number;
      amplitude = 50 * Math.sin(phase / (1 * Math.PI)) - 25;
      return amplitude;
    }

    private function getNoizeWave():Number
    {
      var amplitude: Number;
      phase %= (Math.PI * 2) % phase;
      amplitude = Math.sin( phase * Math.PI * 2 );
      return amplitude;
    }

      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);
        init();
      }
  }
}

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);
  }
}
LINEで送る
Pocket

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

ページトップへ戻る