//////////////////////////////////////////////////////////////////////////////// // // 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.

* * @default 0 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public var loops:int = 0; //---------------------------------- // isLoading //---------------------------------- /** * This property is 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(); } } }