AS3覚え書き7 :音声

音声ファイルの再生

音声を扱うクラスはSoundクラスです。これはEventDispatcherクラスの派生クラスであり、DisplayObjectは継承していません。
外部から音声データを読み込む場合は、画像データのときと同様にロードが完了するまで待つ必要があります。そのため、COMPLETEイベントを受け取ってから再生を行うようにします。Soundクラスが送出するイベントは例えば次のようなものがあります。
open:ロード開始時
complete:ロード完了
ioError:ロード失敗
音声読み込みプログラムの例です。

import flash.display.Sprite;
public class SoundTest extends Sprite
{
	import flash.media.Sound;
	import flash.net.URLRequest;
	import flash.events.* ;
	private	var mySound:Sound = new Sound();
		
	public function SoundTest() 
	{
		var myUrlRequest :URLRequest = new URLRequest("test.mp3");

		mySound.load(myUrlRequest);
		mySound.addEventListener(Event.COMPLETE, CompleteHandler);
	}
	private function CompleteHandler(ev:Event):void {
		mySound.play();
	}
}

SoundChannelクラス

音声を再生すると生成されるサウンド制御用のクラスです。stopメソッドで音声を停止することができます。また、停止した時にはSoundChannelクラスからSOUND_COMPLETEイベントを送出することもできます。
下の例は描画領域をクリックすると再生、停止するプログラムです。

import flash.display.Sprite;
public class SoundTest extends Sprite
{
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.net.URLRequest;
	import flash.events.* ;
	private	var mySound:Sound = new Sound();
	private var mySoundChannel :SoundChannel = new SoundChannel();
	private var state:Boolean = false;
	public function SoundTest() 
	{
		this.graphics.beginFill(0xAAAAFF);
		this.graphics.drawRoundRect(20, 200, 80, 80, 20);
		this.graphics.endFill();	

		var myUrlRequest :URLRequest = new URLRequest("test.mp3");
		mySound.load(myUrlRequest);

		this.addEventListener(MouseEvent.CLICK, onClick);
	}
	private function onClick(ev:Event):void {
		var currentPos:int = mySoundChannel.position;
		if(state == false) {
			mySoundChannel = mySound.play(currentPos);
		   state = true;
		} 
		else {
			mySoundChannel.stop();
			state = false;
		}
	}
}

SoundChannelクラスのプロパティには以下のようなものがあります。
左右チャネルの振幅
leftPeak : Number
rightPeak : Number
現在の再生位置
position : Number
音声パラメータ変更用オブジェクト
soundTransform : SoundTransform

SoundTransformクラス

ボリュームやパンを制御することができるクラスです。SoundChannelクラスが所有しているSoundTransformオブジェクトを書き換えると、再生している音声のボリュームなどを変えることができます。描画領域の位置によってボリュームとパンの値を変化させる例です。

import flash.display.Sprite;
public class SoundTest extends Sprite
{
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	import flash.net.URLRequest;
	import flash.events.* ;
	private	var mySound:Sound = new Sound();
	private var mySoundChannel :SoundChannel = new SoundChannel();
	private var state:Boolean = false;
	public function SoundTest() 
	{
		this.graphics.beginFill(0xAAAAFF);
		this.graphics.drawRoundRect(0, 0, 200, 200, 20);
		this.graphics.endFill();	

		var myUrlRequest :URLRequest = new URLRequest("test.mp3");
		mySound.load(myUrlRequest);
		
		this.addEventListener(MouseEvent.CLICK, onClick);
	}
	private function onClick(ev:MouseEvent):void {
		var currentPos:int = mySoundChannel.position;
		if (state == false) {
			mySoundChannel = mySound.play(currentPos);
			var mySoundTransform :SoundTransform= new SoundTransform();
			mySoundTransform.pan = (ev.localX / this.width)*2-1.0;
			mySoundTransform.volume = (ev.localY / this.height)*0.7;
			mySoundChannel.soundTransform = mySoundTransform;
		   state = true;
		} 
		else {
			mySoundChannel.stop();
			state = false;
		}
	}
}

音声信号処理

音声データーをByteArrayに取り出すにはSoundMixerクラスのcomputeSpectrumメソッドを利用します。
引数には、ByteArrayオブジェクト、FFTフラグ、サンプリング周波数を指定します。引数の詳細は次のようになります。

  • bytes:ByteArray

波形データを格納するバッファ

  • FFTmode:Boolean

true:FFT後の波形データを取り出す(256サンプル)
false:波形データをそのまま取り出す

  • stretchFactor:int

サンプリング周波数(0:44.1kHz,1:22.05kHz,2:11.025kHz)
例えばByteArrayに波形をそのまま取り込むには次のような処理を行います。

var bytes:ByteArray = new ByteArray();
SoundMixer.computeSpectrum(bytes, false, 0);

取り出した音声データは、2048byteで、1024byteずつ左右の波形データが入っています。

左ch(1024byte) 右ch(1024byte)

さらに各チャネルは4byteごとに256サンプルの波形データが格納されています。

左チャネル(1024byte)の構造: Sample1(4byte) Sample2(4byte) Sample3(4byte) ...  

そこで4byteごとに値を取得する必要がありますが、ByteArrayクラスのreadFloatメソッドを使うと4byteごとにfloatの値を読み出すことができます。

for (i = 0; i < 256; i++ ) {
	val = signals.readFloat();
	//処理
}

前の例にボタンを加えて、波形データをグラフとして描画したものがこちらです。

import flash.display.Sprite;
public class SoundTest extends Sprite
{
	import flash.media.*;
	import flash.net.URLRequest;
	import flash.events.* ;
	import flash.utils.ByteArray;

	private var state:Boolean = false;
	
	private	var mySound:Sound = new Sound();
	private var mySoundChannel :SoundChannel = new SoundChannel();
	private var signals:ByteArray = new ByteArray();
	private const SAMPLES:int = 256;
	
	private var myButton:Sprite = new Sprite();
	public function SoundTest() 
	{
		myButton.buttonMode = true;
		drawButton(myButton,false);
		addChild(myButton);

		var myUrlRequest :URLRequest = new URLRequest("test.mp3");
		mySound.load(myUrlRequest);
		
		myButton.addEventListener(MouseEvent.CLICK, onClick);
		this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
		mySoundChannel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete);
	}
	private function onClick(ev:MouseEvent):void {
		var currentPos:int = mySoundChannel.position;
		if (state == false) {
			state = true;
			mySoundChannel = mySound.play(currentPos);
			//パン設定
			var mySoundTransform :SoundTransform= new SoundTransform(0.5,0.5);
			mySoundTransform.pan = (ev.localX / 200)*2-1.0;
			mySoundChannel.soundTransform = mySoundTransform;

			drawButton(myButton,state);
		} 
		else {			
			state = false;
			mySoundChannel.stop();
			drawButton(myButton,state);
		}
	}
	private function onEnterFrame(ev:Event):void {
		//波形の取得
		SoundMixer.computeSpectrum(signals, false, 0);
		//波形の描画
		this.graphics.clear();
		this.graphics.beginFill(0xCDCEFF);
		var i:int;
		var val:Number;
		for (i = 0; i < SAMPLES; i++ ) {
			val = signals.readFloat();
			this.graphics.drawRect(i * 2, 200, 2, int(val*200));
		}
		for (i = 0; i < SAMPLES; i++ ) {
			val = signals.readFloat();
			this.graphics.drawRect(i * 2, 400, 2, int(val*200));
		}
		this.graphics.endFill();
	}
	private function drawButton(button:Sprite, state:Boolean):void {
		myButton.graphics.clear();
		if (state == true) {			//ボタンの描画(再生中)
			myButton.graphics.lineStyle(2, 0xFFCCAA, 0.7);
			myButton.graphics.beginFill(0xFFCFCC);
			myButton.graphics.drawRoundRect(0, 0, 200, 50, 20);
			myButton.graphics.endFill();
		}
		else {						//ボタンの描画(停止中)
			myButton.graphics.clear();
			myButton.graphics.lineStyle(2, 0xCCCCAA, 0.7);
			myButton.graphics.beginFill(0xFFFFFF);
			myButton.graphics.drawRoundRect(0, 0, 200, 50, 20);
			myButton.graphics.endFill();
		}
	}
	private function onSoundComplete(event:Event):void {
		removeEventListener(Event.ENTER_FRAME, onEnterFrame);
	}

}