Advertisement
Guest User

Untitled

a guest
Oct 25th, 2015
136
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Haxe 15.90 KB | None | 0 0
  1. package flixel.system;
  2.  
  3. #if !FLX_NO_SOUND_SYSTEM
  4. import flash.events.Event;
  5. import flash.media.Sound;
  6. import flash.media.SoundChannel;
  7. import flash.media.SoundTransform;
  8. import flash.net.URLRequest;
  9. import flash.utils.ByteArray;
  10. import flixel.FlxBasic;
  11. import flixel.FlxG;
  12. import flixel.system.frontEnds.SoundFrontEnd;
  13. import flixel.tweens.FlxTween;
  14. import flixel.util.FlxMath;
  15. import flixel.util.FlxPoint;
  16. import openfl.Assets;
  17. #end
  18.  
  19. /**
  20.  * This is the universal flixel sound object, used for streaming, music, and sound effects.
  21.  */
  22. class FlxSound extends FlxBasic
  23. {
  24.     #if !FLX_NO_SOUND_SYSTEM
  25.     /**
  26.      * The X position of this sound in world coordinates.
  27.      * Only really matters if you are doing proximity/panning stuff.
  28.      */
  29.     public var x:Float;
  30.     /**
  31.      * The Y position of this sound in world coordinates.
  32.      * Only really matters if you are doing proximity/panning stuff.
  33.      */
  34.     public var y:Float;
  35.     /**
  36.      * Whether or not this sound should be automatically destroyed when you switch states.
  37.      */
  38.     public var persist:Bool;
  39.     /**
  40.      * The ID3 song name.  Defaults to null.  Currently only works for streamed sounds.
  41.      */
  42.     public var name:String;
  43.     /**
  44.      * The ID3 artist name.  Defaults to null.  Currently only works for streamed sounds.
  45.      */
  46.     public var artist:String;
  47.     /**
  48.      * Stores the average wave amplitude of both stereo channels
  49.      */
  50.     public var amplitude:Float;
  51.     /**
  52.      * Just the amplitude of the left stereo channel
  53.      */
  54.     public var amplitudeLeft:Float;
  55.     /**
  56.      * Just the amplitude of the left stereo channel
  57.      */
  58.     public var amplitudeRight:Float;
  59.     /**
  60.      * Whether to call destroy() when the sound has finished.
  61.      */
  62.     public var autoDestroy:Bool;
  63.     /**
  64.      * Tracker for sound complete callback. Default is null. If assigend, will be called
  65.      * each time when sound reaches its end. Works only on flash and desktop targets.
  66.      */
  67.     public var onComplete:Void->Void;
  68.     /**
  69.      * Pan amount. -1 = full left, 1 = full right. Proximity based panning overrides this.
  70.      */
  71.     public var pan(get, set):Float;
  72.     /**
  73.      * Whether or not the sound is currently playing.
  74.      */
  75.     public var playing(get, null):Bool;
  76.     /**
  77.      * Set volume to a value between 0 and 1 to change how this sound is.
  78.      */
  79.     public var volume(get, set):Float;
  80.     /**
  81.      * The position in runtime of the music playback.
  82.      */
  83.     public var time(default, null):Float;
  84.  
  85.     /**
  86.      * Internal tracker for a Flash sound object.
  87.      */
  88.     private var _sound:Sound;
  89.     /**
  90.      * Internal tracker for a Flash sound channel object.
  91.      */
  92.     private var _channel:SoundChannel;
  93.     /**
  94.      * Internal tracker for a Flash sound transform object.
  95.      */
  96.     private var _transform:SoundTransform;
  97.     /**
  98.      * Internal tracker for whether the sound is paused or not (not the same as stopped).
  99.      */
  100.     private var _paused:Bool;
  101.     /**
  102.      * Internal tracker for volume.
  103.      */
  104.     private var _volume:Float;
  105.     /**
  106.      * Internal tracker for total volume adjustment.
  107.      */
  108.     private var _volumeAdjust:Float = 1.0;
  109.     /**
  110.      * Internal tracker for whether the sound is looping or not.
  111.      */
  112.     private var _looped:Bool;
  113.     /**
  114.      * Internal tracker for the sound's "target" (for proximity and panning).
  115.      */
  116.     private var _target:FlxObject;
  117.     /**
  118.      * Internal tracker for the maximum effective radius of this sound (for proximity and panning).
  119.      */
  120.     private var _radius:Float;
  121.     /**
  122.      * Internal tracker for whether to pan the sound left and right.  Default is false.
  123.      */
  124.     private var _proximityPan:Bool;
  125.     /**
  126.      * Helper var to prevent the sound from playing after focus was regained when it was already paused.
  127.      */
  128.     private var _alreadyPaused:Bool = false;
  129.    
  130.     /**
  131.      * The FlxSound constructor gets all the variables initialized, but NOT ready to play a sound yet.
  132.      */
  133.     public function new()
  134.     {
  135.         super();
  136.         reset();
  137.     }
  138.    
  139.     /**
  140.      * An internal function for clearing all the variables used by sounds.
  141.      */
  142.     private function reset():Void
  143.     {
  144.         destroy();
  145.        
  146.         x = 0;
  147.         y = 0;
  148.        
  149.         time = 0;
  150.         _paused = false;
  151.         _volume = 1.0;
  152.         _volumeAdjust = 1.0;
  153.         _looped = false;
  154.         _target = null;
  155.         _radius = 0;
  156.         _proximityPan = false;
  157.         visible = false;
  158.         amplitude = 0;
  159.         amplitudeLeft = 0;
  160.         amplitudeRight = 0;
  161.         autoDestroy = false;
  162.        
  163.         if (_transform == null)
  164.         {
  165.             _transform = new SoundTransform();
  166.         }
  167.         _transform.pan = 0;
  168.     }
  169.    
  170.     override public function destroy():Void
  171.     {
  172.         _transform = null;
  173.         exists = false;
  174.         active = false;
  175.         _target = null;
  176.         name = null;
  177.         artist = null;
  178.        
  179.         if (_channel != null)
  180.         {
  181.             _channel.removeEventListener(Event.SOUND_COMPLETE, stopped);
  182.             _channel.stop();
  183.             _channel = null;
  184.         }
  185.        
  186.         if (_sound != null)
  187.         {
  188.             _sound.removeEventListener(Event.ID3, gotID3);
  189.             _sound = null;
  190.         }
  191.        
  192.         onComplete = null;
  193.        
  194.         super.destroy();
  195.     }
  196.    
  197.     /**
  198.      * Handles fade out, fade in, panning, proximity, and amplitude operations each frame.
  199.      */
  200.     override public function update():Void
  201.     {
  202.         if (!playing)
  203.         {
  204.             return;
  205.         }
  206.        
  207.         time = _channel.position;
  208.        
  209.         var radialMultiplier:Float = 1.0;
  210.         var fadeMultiplier:Float = 1.0;
  211.        
  212.         //Distance-based volume control
  213.         if (_target != null)
  214.         {
  215.             radialMultiplier = FlxMath.getDistance(FlxPoint.get(_target.x, _target.y), FlxPoint.get(x, y)) / _radius;
  216.             if (radialMultiplier < 0) radialMultiplier = 0;
  217.             if (radialMultiplier > 1) radialMultiplier = 1;
  218.            
  219.             radialMultiplier = 1 - radialMultiplier;
  220.            
  221.             if (_proximityPan)
  222.             {
  223.                 var d:Float = (x - _target.x) / _radius;
  224.                 if (d < -1)
  225.                 {
  226.                     d = -1;
  227.                 }
  228.                 else if (d > 1)
  229.                 {
  230.                     d = 1;
  231.                 }
  232.                 _transform.pan = d;
  233.             }
  234.         }
  235.        
  236.         _volumeAdjust = radialMultiplier * fadeMultiplier;
  237.         updateTransform();
  238.        
  239.         if (_transform.volume > 0)
  240.         {
  241.             amplitudeLeft = _channel.leftPeak / _transform.volume;
  242.             amplitudeRight = _channel.rightPeak / _transform.volume;
  243.             amplitude = (amplitudeLeft + amplitudeRight) * 0.5;
  244.         }
  245.         else
  246.         {
  247.             amplitudeLeft = 0;
  248.             amplitudeRight = 0;
  249.             amplitude = 0;         
  250.         }
  251.     }
  252.    
  253.     override public function kill():Void
  254.     {
  255.         super.kill();
  256.         cleanup(false);
  257.     }
  258.    
  259.     /**
  260.      * One of two main setup functions for sounds, this function loads a sound from an embedded MP3.
  261.      *
  262.      * @param   EmbeddedSound   An embedded Class object representing an MP3 file.
  263.      * @param   Looped          Whether or not this sound should loop endlessly.
  264.      * @param   AutoDestroy     Whether or not this FlxSound instance should be destroyed when the sound finishes playing.  Default value is false, but FlxG.sound.play() and FlxG.sound.stream() will set it to true by default.
  265.      *
  266.      * @return  This FlxSound instance (nice for chaining stuff together, if you're into that).
  267.      */
  268.     public function loadEmbedded(EmbeddedSound:Dynamic, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void):FlxSound
  269.     {
  270.         cleanup(true);
  271.        
  272.         if (Std.is(EmbeddedSound, Sound))
  273.         {
  274.             _sound = EmbeddedSound;
  275.         }
  276.         else if (Std.is(EmbeddedSound, Class))
  277.         {
  278.             _sound = Type.createInstance(EmbeddedSound, []);
  279.         }
  280.         else if (Std.is(EmbeddedSound, String))
  281.         {
  282.             _sound = Assets.getSound(EmbeddedSound);
  283.         }
  284.        
  285.         //NOTE: can't pull ID3 info from embedded sound currently
  286.         _looped = Looped;
  287.         autoDestroy = AutoDestroy;
  288.         updateTransform();
  289.         exists = true;
  290.         onComplete = OnComplete;
  291.         return this;
  292.     }
  293.    
  294.     /**
  295.      * One of two main setup functions for sounds, this function loads a sound from a URL.
  296.      *
  297.      * @param   EmbeddedSound   A string representing the URL of the MP3 file you want to play.
  298.      * @param   Looped          Whether or not this sound should loop endlessly.
  299.      * @param   AutoDestroy     Whether or not this FlxSound instance should be destroyed when the sound finishes playing.  Default value is false, but FlxG.sound.play() and FlxG.sound.stream() will set it to true by default.
  300.      *
  301.      * @return  This FlxSound instance (nice for chaining stuff together, if you're into that).
  302.      */
  303.     public function loadStream(SoundURL:String, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void):FlxSound
  304.     {
  305.         cleanup(true);
  306.        
  307.         _sound = new Sound();
  308.         _sound.addEventListener(Event.ID3, gotID3);
  309.         _sound.load(new URLRequest(SoundURL));
  310.         _looped = Looped;
  311.         autoDestroy = AutoDestroy;
  312.         updateTransform();
  313.         exists = true;
  314.         onComplete = OnComplete;
  315.         return this;
  316.     }
  317.    
  318.     /**
  319.      * One of the main setup functions for sounds, this function loads a sound from a ByteArray.
  320.      *
  321.      * @param   Bytes           A ByteArray object.
  322.      * @param   Looped          Whether or not this sound should loop endlessly.
  323.      * @param   AutoDestroy     Whether or not this FlxSound instance should be destroyed when the sound finishes playing.  Default value is false, but FlxG.sound.play() and FlxG.sound.stream() will set it to true by default.
  324.      * @return  This FlxSound instance (nice for chaining stuff together, if you're into that).
  325.      */
  326.     public function loadByteArray(Bytes:ByteArray, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void):FlxSound
  327.     {
  328.         cleanup(true);
  329.        
  330.         #if js
  331.         onComplete();
  332.         #else
  333.         _sound = new Sound();
  334.         _sound.addEventListener(Event.ID3, gotID3);
  335.         _sound.loadCompressedDataFromByteArray(Bytes, Bytes.length);
  336.         _looped = Looped;
  337.         autoDestroy = AutoDestroy;
  338.         updateTransform();
  339.         exists = true;
  340.         onComplete = OnComplete;
  341.         #end
  342.         return this;   
  343.     }
  344.    
  345.     /**
  346.      * Call this function if you want this sound's volume to change
  347.      * based on distance from a particular FlxObject.
  348.      *
  349.      * @param   X           The X position of the sound.
  350.      * @param   Y           The Y position of the sound.
  351.      * @param   TargetObject        The object you want to track.
  352.      * @param   Radius          The maximum distance this sound can travel.
  353.      * @param   Pan         Whether panning should be used in addition to the volume changes (default: true).
  354.      * @return  This FlxSound instance (nice for chaining stuff together, if you're into that).
  355.      */
  356.     public function proximity(X:Float, Y:Float, TargetObject:FlxObject, Radius:Float, Pan:Bool = true):FlxSound
  357.     {
  358.         x = X;
  359.         y = Y;
  360.         _target = TargetObject;
  361.         _radius = Radius;
  362.         _proximityPan = Pan;
  363.         return this;
  364.     }
  365.    
  366.     /**
  367.      * Call this function to play the sound - also works on paused sounds.
  368.      *
  369.      * @param   ForceRestart    Whether to start the sound over or not.  Default value is false, meaning if the sound is already playing or was paused when you call play(), it will continue playing from its current position, NOT start again from the beginning.
  370.      */
  371.     public function play(ForceRestart:Bool = false):FlxSound
  372.     {
  373.         if (!exists)
  374.         {
  375.             return this;
  376.         }
  377.         if (ForceRestart)
  378.         {
  379.             cleanup(false, true, true);
  380.         }
  381.         else if (playing)
  382.         {
  383.             // Already playing sound
  384.             return this;
  385.         }
  386.        
  387.         if (_paused)
  388.         {
  389.             resume();
  390.         }
  391.         else
  392.         {
  393.             startSound(0);
  394.         }
  395.         return this;
  396.     }
  397.    
  398.     /**
  399.      * Unpause a sound.  Only works on sounds that have been paused.
  400.      */
  401.     public function resume():FlxSound
  402.     {
  403.         if (_paused)
  404.         {
  405.             startSound(time);
  406.         }
  407.         return this;
  408.     }
  409.    
  410.     /**
  411.      * Call this function to pause this sound.
  412.      */
  413.     public function pause():FlxSound
  414.     {
  415.         if (!playing)
  416.         {
  417.             return this;
  418.         }
  419.         time = _channel.position;
  420.         _paused = true;
  421.         cleanup(false, false, false);
  422.         return this;
  423.     }
  424.    
  425.     /**
  426.      * Call this function to stop this sound.
  427.      */
  428.     public inline function stop():FlxSound
  429.     {
  430.         cleanup(autoDestroy, true, true);
  431.         return this;
  432.     }
  433.    
  434.     /**
  435.      * Helper function that tweens this sound's volume.
  436.      *
  437.      * @param   Duration    The amount of time the fade-out operation should take.
  438.      * @param   To          The volume to tween to, 0 by default.
  439.      */
  440.     public inline function fadeOut(Duration:Float = 1, ?To:Float = 0):FlxSound
  441.     {
  442.         FlxTween.num(volume, To, Duration, null, volumeTween);
  443.         return this;
  444.     }
  445.    
  446.     /**
  447.      * Helper function that tweens this sound's volume.
  448.      *
  449.      * @param   Duration    The amount of time the fade-in operation should take.
  450.      * @param   From        The volume to tween from, 0 by default.
  451.      * @param   To          The volume to tween to, 1 by default.
  452.      */
  453.     public inline function fadeIn(Duration:Float = 1, From:Float = 0, To:Float = 1):FlxSound
  454.     {
  455.         FlxTween.num(From, To, Duration, null, volumeTween);
  456.         return this;
  457.     }
  458.    
  459.     private function volumeTween(f:Float):Void
  460.     {
  461.         volume = f;
  462.     }
  463.    
  464.     /**
  465.      * Returns the currently selected "real" volume of the sound (takes fades and proximity into account).
  466.      *
  467.      * @return  The adjusted volume of the sound.
  468.      */
  469.     public inline function getActualVolume():Float
  470.     {
  471.         return _volume * _volumeAdjust;
  472.     }
  473.    
  474.     /**
  475.      * Helper function to set the coordinates of this object.
  476.      * Sound positioning is used in conjunction with proximity/panning.
  477.      *
  478.      * @param        X        The new x position
  479.      * @param        Y        The new y position
  480.      */
  481.     public inline function setPosition(X:Float = 0, Y:Float = 0):Void
  482.     {
  483.         x = X;
  484.         y = Y;
  485.     }
  486.    
  487.     /**
  488.      * Call after adjusting the volume to update the sound channel's settings.
  489.      */
  490.     private function updateTransform():Void
  491.     {
  492.         _transform.volume = (FlxG.sound.muted ? 0 : 1) * FlxG.sound.volume * _volume * _volumeAdjust;
  493.         if (_channel != null)
  494.         {
  495.             _channel.soundTransform = _transform;
  496.         }
  497.     }
  498.    
  499.     /**
  500.      * An internal helper function used to attempt to start playing the sound and populate the _channel variable.
  501.      */
  502.     private function startSound(Position:Float):Void
  503.     {
  504.         var numLoops:Int = (_looped && (Position == 0)) ? 9999 : 0;
  505.         time = Position;
  506.         _paused = false;
  507.         _channel = _sound.play(time, numLoops, _transform);
  508.         if (_channel != null)
  509.         {
  510.             _channel.addEventListener(Event.SOUND_COMPLETE, stopped);
  511.             active = true;
  512.         }
  513.         else
  514.         {
  515.             exists = false;
  516.             active = false;
  517.         }
  518.     }
  519.    
  520.     /**
  521.      * An internal helper function used to help Flash clean up finished sounds or restart looped sounds.
  522.      *
  523.      * @param   event       An Event object.
  524.      */
  525.     private function stopped(event:Event = null):Void
  526.     {
  527.         if (onComplete != null)
  528.         {
  529.             onComplete();
  530.         }
  531.        
  532.         if (_looped)
  533.         {
  534.             cleanup(false);
  535.             play();
  536.         }
  537.         else
  538.         {
  539.             cleanup(autoDestroy);
  540.         }
  541.     }
  542.    
  543.     /**
  544.      * An internal helper function used to help Flash clean up (and potentially re-use) finished sounds. Will stop the current sound and destroy the associated SoundChannel, plus, any other commands ordered by the passed in parameters.
  545.      *
  546.      * @param  destroySound    Whether or not to destroy the sound. If this is true, the position and fading will be reset as well.
  547.      * @param  resetPosition    Whether or not to reset the position of the sound.
  548.      * @param  resetFading    Whether or not to reset the current fading variables of the sound.
  549.      */
  550.     private function cleanup(destroySound:Bool, resetPosition:Bool = true, resetFading:Bool = true):Void
  551.     {
  552.         if (destroySound)
  553.         {
  554.             reset();
  555.             return;
  556.         }
  557.        
  558.         if (_channel != null)
  559.         {
  560.             _channel.removeEventListener(Event.SOUND_COMPLETE, stopped);
  561.             _channel.stop();
  562.             _channel = null;
  563.         }
  564.        
  565.         active = false;
  566.  
  567.         if (resetPosition)
  568.         {
  569.             time = 0;
  570.             _paused = false;
  571.         }
  572.     }
  573.    
  574.     /**
  575.      * Internal event handler for ID3 info (i.e. fetching the song name).
  576.      * @param   event   An Event object.
  577.      */
  578.     private function gotID3(?event:Event):Void
  579.     {
  580.         FlxG.log.notice("Got ID3 info.");
  581.         name = _sound.id3.songName;
  582.         artist = _sound.id3.artist;
  583.         _sound.removeEventListener(Event.ID3, gotID3);
  584.     }
  585.    
  586.     @:allow(flixel.system.frontEnds.SoundFrontEnd)
  587.     private function onFocus():Void
  588.     {
  589.         if (!_alreadyPaused)
  590.         {
  591.             resume();
  592.         }
  593.     }
  594.    
  595.     @:allow(flixel.system.frontEnds.SoundFrontEnd)
  596.     private function onFocusLost():Void
  597.     {
  598.         _alreadyPaused = _paused;
  599.         pause();
  600.     }
  601.    
  602.     private inline function get_playing():Bool
  603.     {
  604.         return (_channel != null);
  605.     }
  606.    
  607.     private inline function get_volume():Float
  608.     {
  609.         return _volume;
  610.     }
  611.    
  612.     private function set_volume(Volume:Float):Float
  613.     {
  614.         _volume = Volume;
  615.         if (_volume < 0)
  616.         {
  617.             _volume = 0;
  618.         }
  619.         else if (_volume > 1)
  620.         {
  621.             _volume = 1;
  622.         }
  623.         updateTransform();
  624.         return Volume;
  625.     }
  626.    
  627.     private inline function get_pan():Float
  628.     {
  629.         return _transform.pan;
  630.     }
  631.    
  632.     private inline function set_pan(pan:Float):Float
  633.     {
  634.         return _transform.pan = pan;
  635.     }
  636.     #end
  637. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement