mp3を音素材として使用すると、mp3の仕様で頭の部分にどうしても無音部分が入り、
ループで使用する場合に想定外のギャップができたりします。

そのため、ループサウンドを途切れなくループさせるには
WAV形式などmp3形式以外のサウンドファイルを使うことになります。

Flashでの音声ファイルの外部読み込みに関しては、
mp3のみが外部読み込みに対応しているため
ループ音源を外部読み込みさせたい場合、これまでは
WAVを埋め込んだSWF(Fla内でmp3形式で圧縮すると無音部分が入らずに軽くできる)を
外部読み込みして、そこからサウンドクラスを取り出して使用したりしていました。



しかしバイナリデータを扱えるようになったFlashPlayer10では
外部読み込みしたwavファイルのバイナリを解析して再生出来ると聞いたことがあったので
as3でのバイナリ操作の勉強のために1から実装してみました。

WAVのフォーマットについてはGoogle先生に聞けば、
わかりやすくまとまったものがたくさん出てきます。

WAVフォーマットをもとに、
WAVのバイナリデータを渡すと、-1~1のPCM データの配列を返すような
デコードクラスを作ってみました。


WAVデコードクラスはこちら

package
{
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	
	public class WavUtil
	{
		public function WavUtil() 
		
		/**
		 * WAVフォーマットのファイルをデコードします.
		 * 
		 * @param	binary WAVファイルのバイナリデータ.
		 * @return	チャンネルごとのPCMデータVectorの配列.
		 */
		public static function decode(binary:ByteArray):Array
		{
			//エンディアンをリトルエンディアンに設定
			binary.endian = Endian.LITTLE_ENDIAN;
			
			//RIFFフォーマットかどうか
			binary.position = 0;
			var isRiff:Boolean = Boolean(binary.readUTFBytes(4) == "RIFF");
			
			//WAVフォーマットかどうか
			binary.position = 8;
			var isWav:Boolean = Boolean(binary.readUTFBytes(4) == "WAVE");
			
			//正式なWAVでない場合は空のデータを返す
			if (!isRiff || !isWav) return new Array();
			
			
			//fmtチャンクのバイト数の取得
			binary.position = 16;
			var chunkSize:uint = binary.readUnsignedInt();
			
			//チャンネル数の取得
			binary.position = 22;
			var channels:int = int(binary.readUnsignedShort());
			
			//サンプリングレートの取得
			var samplingrate:uint = uint(binary.readUnsignedInt());
			
			//サンプリングデータあたりのビット数の取得
			binary.position = 34;
			var bits:int = binary.readUnsignedShort();
			
			//波形データのバイト数の取得
			binary.position = 24 + chunkSize;
			var dataSize:uint = binary.readUnsignedInt();
			
			//波形データ取得
			var rawBytes:ByteArray = new ByteArray();
			binary.readBytes(rawBytes, 28 + chunkSize, dataSize);
			
			
			//戻り値用データ格納配列作成
			var data:Array = [ new Vector.< Number >() ];
			if(channels == 2) data.push( new Vector.< Number >() );
			
			//波形データの解析
			var i:int;
			var v:Number;
			rawBytes.position = 0;
			rawBytes.endian = Endian.LITTLE_ENDIAN;
			while (rawBytes.position < rawBytes.length)
			{
				for (i = 0; i < channels; i++) 
				{
					switch(bits)
					{
						case 8: //8bit
							v = (rawBytes.readUnsignedByte() - 128) / 128;
						break;
						
						case 16: //16bit
							v = rawBytes.readShort() / 32768;
						break;
					}
					data[i].push(v);
				}
			}
			return data;
		}
	}
}


使い方はこんな感じ

import WavUtil;
import flash.events.Event;
import flash.events.SampleDataEvent;
import flash.media.Sound;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;

var _sound:Sound;
var _channelR:Vector.< Number >;
var _channelL:Vector.< Number >;
var _readOffset:Number;

load("drumnloop1.wav");

function load(url:String):void
{
	var loader:URLLoader = new URLLoader();
	var request:URLRequest = new URLRequest(url);
	loader.dataFormat = URLLoaderDataFormat.BINARY;
	loader.addEventListener(Event.COMPLETE, loadCompleteHandler);
	loader.load(request);
}

function loadCompleteHandler(event:Event):void 
{
	var wavData:Array = WavUtil.decode(event.target.data);
	
	if(wavData.length != 0)
	{
		_channelL = wavData[0];
		_channelR = wavData[1] || wavData[0];
		playStart();
	}
}

function playStart():void
{
	_readOffset = 0 ;
	_sound = new Sound();
	_sound.addEventListener(SampleDataEvent.SAMPLE_DATA, waveGenerate);
	_sound.play();
}
		
function waveGenerate(event:SampleDataEvent):void 
{
	var dataLength:int = _channelL.length;
	
	var c:int;
	
	for ( c = 0; c < 8192; c++ ) 
	{
		_readOffset = (_readOffset + 1) % dataLength;
		
		event.data.writeFloat(_channelL[_readOffset]);
		event.data.writeFloat(_channelR[_readOffset]);
	}
	
}


ただ、WAVは非圧縮データなので、非常に容量が大きいのが難点ですね。
8bitのモノラルで制作しても1秒あたり40kb程度の容量になってしまいますので
実際に使用するにはかなりの注意が必要そうです。


WAVのフォーマットがわかってしまえば、デコードだけでなくエンコード処理もサクっと作れます。
次はエンコード処理を実装して、WAVファイルへの書き出し機能を持った
簡易効果音ジェネレーターでもつくってみようかと思います。

 

 

HTML5飯