WAV読み込んでdelay機能をつけてみた(Player10)

2008/08/24

こんばんは。市民祭りというものに初めていったんですが、プロが全くいず、素人の出す出店やステージのディープな雰囲気にのみこまれそうになってしまい、クセになりそうなきんくまです。

さて、ようやくWAVデータを読み込めるようになったんですが、この本によると、データが読み込めるようになったら次はフィルタ処理。みたいに書いてあったので挑戦してみました。

なんかリングバッファとかいう仕組みでやるみたいです。
FIRフィルタという名前の遅延制御ができるやつを作ってみました。
サンプルはこちら(要Player10)

上のスライダで遅延時間を制御。
下のスライダでもとの音楽との割合を制御します。

それで、この遅延制御ができるようになると、次はローパス・ハイパス・バンドパスフィルターができるようになるみたいです。以前はこのあたりよくわかんない概念だったんですが、DS-10で音のイメージができるようになったんで、あとは実装だけですね。恐るべしDS-10。

以下ソースです。今回、CS3からLabelとSliderのコンポーネントをSWC化して読み込んでます。

package {
  import fl.controls.Label;
  import fl.controls.Slider;
  import fl.events.SliderEvent;

  import flash.display.Graphics;
  import flash.display.Sprite;
  import flash.display.StageScaleMode;
  import flash.events.Event;
  import flash.events.MouseEvent;
  import flash.events.SampleDataEvent;
  import flash.media.Sound;
  import flash.net.URLLoader;
  import flash.net.URLLoaderDataFormat;
  import flash.net.URLRequest;
  import flash.text.TextField;
  import flash.text.TextFieldAutoSize;
  import flash.text.TextFormat;
  import flash.text.TextFormatAlign;
  import flash.utils.ByteArray;
  import flash.utils.Endian;

  [SWF(width="400", height="300", frameRate="30", backgroundColor="#ffffff")]
  public class WAV_Sample extends Sprite
  {
    private var path:String = "theme.wav";
    private var originalData:ByteArray;
    private var wavData:ByteArray;
    private var slider:Slider;
    private var delayRateSlider:Slider;
    private var ringBuffer:ByteArray;
    private var isRingBufferFull:Boolean;
    private var delayRate:uint;

    public function WAV_Sample()
    {
      stage.scaleMode = StageScaleMode.NO_SCALE;
      setStartBtn();
    }

    private function initializeRingBuffer(delaySampleNum:uint):void
    {
      isRingBufferFull = false;
      ringBuffer = new ByteArray();
      ringBuffer.endian = Endian.LITTLE_ENDIAN;
      while(delaySampleNum)
      {
        ringBuffer.writeShort(0);//left
        ringBuffer.writeShort(0);//right
        delaySampleNum--;
      }
      ringBuffer.position = 0;
    }

    private function sliderEventHandler(e:SliderEvent):void
    {
      var sampleNum:uint = 44100 * slider.value;
      initializeRingBuffer(sampleNum);
    }

    private function delayRateSliderEventHandler(e:SliderEvent):void
    {
      delayRate = delayRateSlider.value;
    }

    private function initializeSlider():void
    {
      //delay time
      var label2:Label = new Label();
      var sliderControler:SliderController = new SliderController(label2);
      slider = sliderControler.slider;
      this.addChild(slider);
      slider.width = 200;
      slider.x = (stage.stageWidth - slider.width) / 2;
      slider.y = stage.stageHeight - 200;
      slider.minimum = 0;
      slider.maximum = 3;
      slider.value = slider.minimum;
      slider.snapInterval = 0.1;
      slider.addEventListener(SliderEvent.THUMB_DRAG, sliderEventHandler);

      var label1:Label = new Label();
      label1.text = "delayの時間(秒)";
      this.addChild(label1);
      label1.x = slider.x;
      label1.y = slider.y - label1.height - 5;
      this.addChild(label2);
      label2.x = label1.x + label1.width + 10;
      label2.y = label1.y;
      label2.text = slider.value.toString();

      //delayRateSlider
      var label3:Label = new Label();
      var label4:Label = new Label();
      sliderControler = new SliderController(label3);
      delayRateSlider = sliderControler.slider;
      this.addChild(delayRateSlider);
      delayRateSlider.width = 200;
      delayRateSlider.x = (stage.stageWidth - slider.width) / 2;
      delayRateSlider.y = stage.stageHeight - 100;
      delayRateSlider.minimum = 0;
      delayRateSlider.maximum = 100;
      delayRateSlider.value = 50;
      delayRate = delayRateSlider.value;
      delayRateSlider.snapInterval = 1;
      delayRateSlider.addEventListener(SliderEvent.THUMB_DRAG, delayRateSliderEventHandler);
      label4.text = "delayのもとの音との割合(%)";
      label4.width = 150;
      this.addChild(label4);
      label4.x = delayRateSlider.x;
      label4.y = delayRateSlider.y - label4.height - 5;
      this.addChild(label3);
      label3.x = label4.x + label4.width + 10;
      label3.y = label4.y;
      label3.text = delayRateSlider.value.toString();
    }

    private function soundWAV(e:SampleDataEvent):void
    {
      var inLeft:Number;
      var inRight:Number;
      var outLeft:Number;
      var outRight:Number;
      var readPosition:uint;
      var A0:Number = (100 - delayRate) / 100;
      var A1:Number = delayRate / 100;

      for ( var c:int=0; c < 2048; c++ )
      {
        inLeft = wavData.readShort();
        inRight = wavData.readShort();
        if(wavData.position == wavData.length)
        {
          wavData.position = 0;
        }

        //FIRフィルタ。ringBufferが1週してたまったら
        if(isRingBufferFull)
        {
          readPosition = ringBuffer.position;
          outLeft = A0 * inLeft + A1 * ringBuffer.readShort();
          outRight = A0 * inRight + A1 * ringBuffer.readShort();
          ringBuffer.position = readPosition; //戻しておく
        }
        //たまるまで
        else
        {
          outLeft = inLeft;
          outRight = inRight;
        }

        //ringBuffer
        ringBuffer.writeShort(inLeft);
        ringBuffer.writeShort(inRight);
        if(ringBuffer.position == ringBuffer.length)
        {
          isRingBufferFull = true;
          ringBuffer.position = 0;
        }

        //サンプリング。1から-1に収める
        //割る数は16bitの場合の最大のとる幅 = 32767
        outLeft = outLeft / 32767;
        outLeft = (outLeft > 1) ? 1 : outLeft;
        outLeft = (outLeft < -1) ? -1 : outLeft;

        outRight = outRight / 32767;
        outRight = (outRight > 1) ? 1 : outRight;
        outRight = (outRight < -1) ? -1 : outRight;

        e.data.writeFloat(outLeft);
        e.data.writeFloat(outRight);
      }
    }

    private function init():void
    {
      var loader:URLLoader = new URLLoader();
      loader.dataFormat = URLLoaderDataFormat.BINARY;
      loader.addEventListener(Event.COMPLETE, completeHandler);
      var request:URLRequest = new URLRequest(path);

       try {
          loader.load(request);
       } catch (error:Error) {
          trace("Unable to load requested document.");
       }

       initializeSlider();
    }

    private function completeHandler(event:Event):void
    {
      読み込んだバイトデータをWav形式で解析
      var loader:URLLoader = URLLoader(event.target);
      wavData = event.target.data as ByteArray;
      wavData.endian = Endian.LITTLE_ENDIAN;
      wavData = parse(wavData);
      wavData.position = 0;
      originalData = new ByteArray();
      originalData.endian = Endian.LITTLE_ENDIAN;
      originalData.writeBytes(wavData);

      initializeRingBuffer(0);

      //音楽スタート
      var mySound:Sound = new Sound();
      mySound.addEventListener("sampleData", soundWAV);
      mySound.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
      {
        var sp:Sprite = e.currentTarget as Sprite;
        removeChild(sp);
        init();
      }

      private function parse( bytes: ByteArray ): ByteArray
    {
      bytes.position = 0;
      bytes.endian = Endian.LITTLE_ENDIAN;

      bytes.readUTFBytes( 4 ); // RIFF
      bytes.readUnsignedInt(); // entire fileLength - 8
      bytes.readUTFBytes( 4 ); // WAVE

      var id: String;
      var length: uint;
      var position: uint;
      var parseBytes:ByteArray = new ByteArray();
      var compression:uint;
      var channels:uint;
      var rate:uint;
      var bytesPerSecond:uint;
      var blockAlign:uint;
      var bits:uint;

      while( bytes.position < bytes.length )
      {
        id = bytes.readUTFBytes( 4 );
        length = bytes.readUnsignedInt();
        position = bytes.position;

        switch( id )
        {
          case 'fmt ':
            compression = bytes.readUnsignedShort();
            channels = bytes.readUnsignedShort();
            rate = bytes.readUnsignedInt();
            bytesPerSecond = bytes.readUnsignedInt();
            blockAlign = bytes.readUnsignedShort();
            bits = bytes.readUnsignedShort();
            bytes.position = position + length;

            var log:String = "";
            log += "フォーマットタイプ(1だとPCM): " + compression;
            log += "nチャネル数(2だとステレオ) : " + channels;
            log += "nサンプリング周波数 : " + rate + "[Hz]";
            log += "n平均データ転送速度 : " + bytesPerSecond;
            log += "nブロックサイズ : " + blockAlign;
            log += "nサンプルあたりのビット数 : " + bits + "[bit]";
            trace(log);
            break;

          case 'data':
            var data: ByteArray = new ByteArray();
            data.endian = Endian.LITTLE_ENDIAN;
            data.writeBytes( bytes, position, length );
            data.position = 0;
            parseBytes = data;
            bytes.position = position + length;
            trace("総サンプル数: " + length / 2);
            break;

          default:
            bytes.position = position + length;
            break;
        }
      }
      return parseBytes;
    }
  }
}

import flash.display.Sprite;
import flash.text.TextField;
import flash.geom.Rectangle;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import fl.controls.Slider;
import fl.events.SliderEvent;
import fl.controls.Label;
import flash.utils.ByteArray;
import flash.events.Event;

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

class SliderController
{
  public var slider:Slider;
  public var label:Label;

    public function SliderController(label:Label) {
      slider = new Slider();
        this.configureListeners();
        this.label = label;
    }

    private function configureListeners():void {
        slider.addEventListener(SliderEvent.CHANGE, sliderChanged);
        slider.addEventListener(SliderEvent.THUMB_DRAG, sliderDrag);
        slider.addEventListener(SliderEvent.THUMB_PRESS, sliderPress);
        slider.addEventListener(SliderEvent.THUMB_RELEASE, sliderRelease);
    }

    private function sliderDrag(e:SliderEvent):void {
        trace("Slider dragging: " + e.target.value);
        label.text = e.target.value;
    }

    private function sliderPress(e:SliderEvent):void {
        trace("Slider pressed");
    }

    private function sliderRelease(e:SliderEvent):void {
        trace("Slider released");
    }

    private function sliderChanged(e:SliderEvent):void {
        trace("Slider changed: " + e.target.value);
        label.text = e.target.value;
    }
}
LINEで送る
Pocket

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

ページトップへ戻る