package com.nikrowell.media { import flash.media.Sound; import flash.media.SoundChannel; import flash.media.SoundMixer; import flash.media.SoundTransform; import flash.events.Event; import flash.net.URLRequest; import flash.utils.ByteArray; import flash.events.IOErrorEvent; /** * The SoundData class facilitates interactions with the SoundMixer class, * primarily, the SoundMixer.computeSpectrum() static method. It also * provides simple start, stop, toggle, and reset methods and allows * control over looping, volume and panning of a streaming sound. * * The class is intended for use with audio visualization experiments. * * @author Nik Rowell * @see flash.media.Sound * @see flash.media.SoundMixer * @see flash.media.SoundTransform * @see flash.media.SoundChannel */ public class SoundData { public static const CHANNEL_LENGTH:int = 256; private var _sound:Sound; private var _channel:SoundChannel; private var _soundURL:String; private var _bytes:ByteArray; private var _looped:Boolean; private var _isPlaying:Boolean; private var _position:Number; private var _volume:Number; private var _pan:Number; /** * Constructor, creates a new SoundData instance. * * @param soundURL The URL that points to an external MP3 file. * @param looped (Optional, default = false) Boolean value indicating whether the should should loop. If set to true the sound restarts when Event.SOUND_COMPLETE fires. * @param volume (Optional, default = 1) The starting volume level for the sound. This can be modified before or during playback by setting the SoundData.volume property. */ public function SoundData(soundURL, looped:Boolean = false, volume:Number = 1) { _soundURL = soundURL; _looped = looped; _volume = volume; _pan = 0; _position = 0; init(); } /** *@private */ private function init():void { _bytes = new ByteArray(); _sound = new Sound( new URLRequest(_soundURL) ); _sound.addEventListener(IOErrorEvent.IO_ERROR, onIOError); // PRELOAD? } /** * Method, starts the sound passed to the constructor (soundURL). * * @see flash.media.Sound.play() */ public function startSound():void { if(_isPlaying) return; _channel = _sound.play(_position); _isPlaying = true; if(this._looped) { _channel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete); } } /** * Method, stops the currently playing sound. Maintains the sound's current position. * * @see flash.media.SoundChannel.stop() */ public function stopSound():void { if(!_isPlaying) return; _position = _channel.position; _channel.stop(); _isPlaying = false; if(this._looped) { _channel.removeEventListener(Event.SOUND_COMPLETE, onSoundComplete); } } /** * Method, stops the currently playing sound and resets its position to the beginning */ public function reset():void { stopSound(); _position = 0; } /** * Method, stops or starts the sound, based on the value of isPlaying. Maintains the sound's current position. */ public function toggle():void { if(this._isPlaying) { _position = _channel.position; stopSound(); } else { startSound(); } } /** * Property[read-only], returns true if the sound is currently playing. * * @return Boolean */ public function get isPlaying():Boolean { return _isPlaying; } public function get volume():Number { return _volume; } public function set volume(vol:Number) { if(vol > 1) vol = 1; if(vol < 0) vol = 0; var transform:SoundTransform = _channel.soundTransform; transform.volume = _volume; _channel.soundTransform = transform; _volume = vol; } public function get pan():Number { return _pan; } public function set pan(pan:Number) { //trace("Panning control not yet implemented"); if(pan > 1) pan = 1; if(pan < -1) pan = -1; var transform:SoundTransform = _channel.soundTransform; transform.pan = pan; _channel.soundTransform = transform; _pan = pan; } /** * Method, takes a snapshot of the current sound wave. * * @param FFTMode (Optional, default = false) A Boolean value indicating whether a Fourier transformation is performed. Setting this parameter to true causes the method to return a frequency spectrum instead of the raw sound wave with low frequencies on the left and high on the right. * @param stretchFactor (Optional, default = 0) The resolution of the sound samples. If you set the stretchFactor value to 0, data is sampled at 44.1 KHz; with a value of 1, data is sampled at 22.05 KHz; with a value of 2, data is sampled 11.025 KHz; and so on. * * @see flash.media.SoundMixer.computeSpectrum() */ public function compute(FFTMode:Boolean = false, stretchFactor:int = 0):void { SoundMixer.computeSpectrum(_bytes, FFTMode, stretchFactor); } /** * Method, reads the data from the private property _bytes:ByteArray, into which SoundData.compute() writes * * @param multiplier (Optional, default = 1) Number to multiply the floating-point value by. * @param offset (Optional, default = 1) Number to add to the floating-point value. * * @return the floating-point value multiplied by the multipler, if any, and added to the offset, if any. */ public function read(multiplier:Number = 1, offset:Number = 0):Number { var value:Number = (_bytes.bytesAvailable > 0) ? _bytes.readFloat() : 0; return (value * multiplier) + offset; } /** *@private */ private function onSoundComplete(e:Event):void { _position = 0; startSound(); } /** *@private */ private function onIOError(e:IOErrorEvent):void { throw new Error("Unable to load " + _soundURL); } } }