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