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