////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mx.effects.effectClasses
{
import flash.events.Event;
import flash.events.TimerEvent;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;
import flash.utils.Timer;
import mx.core.mx_internal;
import mx.effects.EffectInstance;
import mx.effects.Tween;
use namespace mx_internal;
/**
* The SoundEffectInstance class implements the instance class
* for the SoundEffect effect.
* Flex creates an instance of this class when it plays a SoundEffect effect;
* you do not create one yourself.
*
* @see mx.effects.Fade
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class SoundEffectInstance extends EffectInstance
{
include "../../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @param target The Object to animate with this effect.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function SoundEffectInstance(target:Object)
{
super(target);
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private.
*/
private var origVolume:Number;
/**
* @private.
*/
private var origPan:Number;
/**
* @private.
*/
private var volumeTween:Tween;
/**
* @private.
*/
private var panTween:Tween;
/**
* @private.
*/
private var soundDuration:Number;
/**
* @private.
*/
private var tweenCount:int = 0;
/**
* @private.
*/
private var intervalID:Number;
/**
* @private.
*/
private var endOnTweens:Boolean = false;
/**
* @private.
*/
private var pausedPosition:Number;
/**
* @private.
*/
private var resumedPosition:Number = 0;
/**
* @private.
*/
private var pausedTransform:SoundTransform;
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// totalDuration
//----------------------------------
/**
* @private
*/
private function get totalDuration():Number
{
if (useDuration)
{
return duration;
}
else if (sound == null)
{
return 0;
}
else
{
// Include the number of loops .
var multiplier:int = loops + 1;
return sound.length * multiplier;
}
}
//----------------------------------
// bufferTime
//----------------------------------
/**
* The SoundEffect class uses an internal Sound object to control
* the MP3 file.
* This property specifies the minimum number of milliseconds
* worth of sound data
* to hold in the Sound object's buffer.
* The Sound object waits until it has at least
* this much data before beginning playback,
* and before resuming playback after a network stall.
*
* @default 1000
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var bufferTime:Number = 1000;
//----------------------------------
// loops
//----------------------------------
/**
* The number of times to play the sound in a loop, where a value of
* 0 means play the effect once, a value of 1 means play the effect twice,
* and so on. If you repeat the MP3 file, it still uses the setting of the
* useDuration
property to determine the playback time.
*
*
The duration
property takes precedence
* over this property.
* If the effect duration is not long enough to play the sound at least once,
* the sound does not loop.
true
if the MP3 has been loaded.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get isLoading():Boolean
{
if (!sound)
return false;
return source is Class ||
sound.bytesTotal > 0;
}
//----------------------------------
// panEasingFunction
//----------------------------------
/**
* The easing function for the pan effect.
* This function is used to interpolate between the values
* of panFrom
and panTo
.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var panEasingFunction:Function;
//----------------------------------
// panFrom
//----------------------------------
/**
* Initial pan of the Sound object.
* The value can range from -1.0 to 1.0, where -1.0 uses only the left channel,
* 1.0 uses only the right channel, and 0.0 balances the sound evenly
* between the two channels.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var panFrom:Number;
//----------------------------------
// panTo
//----------------------------------
/**
* Final pan of the Sound object.
* The value can range from -1.0 to 1.0, where -1.0 uses only the left channel,
* 1.0 uses only the right channel, and 0.0 balances the sound evenly
* between the two channels.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var panTo:Number;
//----------------------------------
// soundChannel
//----------------------------------
/**
* @private
* Storage for the soundChannel property.
*/
private var _soundChannel:SoundChannel;
/**
* The SoundChannel object that the MP3 file has been loaded into.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get soundChannel():SoundChannel
{
return _soundChannel;
}
//----------------------------------
// sound
//----------------------------------
/**
* Reference to the internal Sound object. The SoundEffect uses this
* instance to play the MP3 file.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var sound:Sound;
//----------------------------------
// source
//----------------------------------
/**
* @private
* Storage for the source property.
*/
private var _source:Object;
/**
* The URL or class of the MP3 file to play.
* If you have already embedded the MP3 file, using the
* Embed
keyword, you can pass the Class object
* of the MP3 file to the source
property.
* Otherwise, specify the full URL to the MP3 file.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function get source():Object
{
return _source;
}
/**
* @private
*/
public function set source(value:Object):void
{
_source = value;
}
//----------------------------------
// startTime
//----------------------------------
/**
* The initial position in the MP3 file, in milliseconds,
* at which playback should start.
*
* @default 0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var startTime:Number = 0;
//----------------------------------
// useDuration
//----------------------------------
/**
* If true
, stop the effect
* after the time specified by the duration
* property has elapsed.
* If false
, stop the effect
* after the MP3 finishes playing or looping.
*
* @default true
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var useDuration:Boolean = true;
//----------------------------------
// volumeEasingFunction
//----------------------------------
/**
* The easing function for the volume effect.
* Use this function to interpolate between the values
* of volumeFrom
and volumeTo
.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var volumeEasingFunction:Function;
//----------------------------------
// volumeFrom
//----------------------------------
/**
* Initial volume of the Sound object.
* Value can range from 0.0 to 1.0.
*
* @default 1.0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var volumeFrom:Number;
//----------------------------------
// volumeTo
//----------------------------------
/**
* Final volume of the Sound object.
* Value can range from 0.0 to 1.0.
*
* @default 1.0
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public var volumeTo:Number;
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
override public function play():void
{
// Dispatch an effectStart event from the target.
super.play();
if (!sound)
return;
/*
if (!isLoading && sound)
sound.load(new URLRequest(source), bufferTime);
*/
var transform:SoundTransform = new SoundTransform();
if (!isNaN(volumeFrom) || !isNaN(volumeTo))
{
if (isNaN(volumeFrom))
volumeFrom = 1;
else if (isNaN(volumeTo))
volumeTo = 1;
transform.volume = volumeFrom;
volumeTween = new Tween(this, volumeFrom, volumeTo, totalDuration);
if (volumeEasingFunction != null)
volumeTween.easingFunction = volumeEasingFunction;
volumeTween.setTweenHandlers(onVolumeTweenUpdate,
onVolumeTweenEnd);
tweenCount++;
}
if (!isNaN(panFrom) || !isNaN(panTo))
{
if (isNaN(panFrom))
panFrom = 0;
else if (isNaN(panTo))
panTo = 0;
transform.pan = panFrom;
panTween = new Tween(this, panFrom, panTo, totalDuration);
if (panEasingFunction != null)
panTween.easingFunction = panEasingFunction;
panTween.setTweenHandlers(onPanTweenUpdate,
onPanTweenEnd);
tweenCount++;
}
endOnTweens = (tweenCount > 0);
if (useDuration && !endOnTweens)
{
var timer:Timer = new Timer(totalDuration, 1);
timer.addEventListener(TimerEvent.TIMER, durationEndHandler);
timer.start();
}
pausedPosition = NaN; // Clear the paused position
resumedPosition = startTime;
_soundChannel = sound.play(startTime, loops, transform);
if (soundChannel)
soundChannel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler);
}
/**
* @private
*/
override public function pause():void
{
super.pause();
if (volumeTween)
volumeTween.pause();
if (panTween)
panTween.pause();
if (soundChannel)
{
pausedPosition = soundChannel.position; // Save the paused position
pausedTransform = soundChannel.soundTransform;
soundChannel.stop();
}
}
/**
* @private
*/
override public function stop():void
{
super.stop();
if (volumeTween)
volumeTween.stop();
if (panTween)
panTween.stop();
if (soundChannel)
soundChannel.stop();
}
/**
* @private
*/
override public function resume():void
{
super.resume();
if (volumeTween)
volumeTween.resume();
if (panTween)
panTween.resume();
resumedPosition += pausedPosition;
if (sound)
{
_soundChannel = sound.play(resumedPosition, loops, pausedTransform);
if (soundChannel)
soundChannel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler);
}
}
/**
* @private
*/
override public function end():void
{
super.end();
pausedPosition = NaN; // Clear the paused position
resumedPosition = startTime;
if (volumeTween)
volumeTween.endTween();
if (panTween)
panTween.endTween();
}
/**
* @private
*/
override public function finishEffect():void
{
if (soundChannel)
soundChannel.stop();
super.finishEffect();
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
mx_internal function onVolumeTweenUpdate(value:Object):void
{
if (soundChannel)
{
// Need to reset the soundTransform object in order
var copyTransform:SoundTransform = soundChannel.soundTransform;
copyTransform.volume = Number(value);
soundChannel.soundTransform = copyTransform;
}
}
/**
* @private
*/
mx_internal function onVolumeTweenEnd(value:Object):void
{
onVolumeTweenUpdate(value);
finishTween();
}
/**
* @private
*/
mx_internal function onPanTweenUpdate(value:Object):void
{
if (soundChannel)
{
// Need to reset the soundTransform object in order
var copyTransform:SoundTransform = soundChannel.soundTransform;
copyTransform.pan = Number(value);
soundChannel.soundTransform = copyTransform;
}
}
/**
* @private
*/
mx_internal function onPanTweenEnd(value:Object):void
{
onPanTweenUpdate(value);
finishTween();
}
/**
* @private
*/
private function finishTween():void
{
if (tweenCount == 0 || --tweenCount == 0)
{
if (soundChannel)
soundChannel.stop();
finishRepeat();
}
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function durationEndHandler(event:TimerEvent):void
{
finishTween();
}
/**
* @private
*/
private function soundCompleteHandler(event:Event):void
{
dispatchEvent(event);
// We don't have any tweens, so we need to explicitly
// tell the effect that we are finished.
if (!useDuration && !endOnTweens)
finishTween();
}
}
}