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