Skip to content
127 changes: 84 additions & 43 deletions flixel/sound/FlxSound.hx
Original file line number Diff line number Diff line change
Expand Up @@ -333,56 +333,67 @@ class FlxSound extends FlxBasic
}

/**
* One of the main setup functions for sounds, this function loads a sound from an embedded MP3.
*
* Loads a sound from the provided sound asset.
* The asset can be an OpenFL Sound instance, embedded sound, file path or byte array.
*
* **Note:** If the `FLX_DEFAULT_SOUND_EXT` flag is enabled, you may omit the file extension
*
* @param EmbeddedSound An embedded Class object representing an MP3 file.
* @param Looped Whether or not this sound should loop endlessly.
* @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.
* @param OnComplete Called when the sound finished playing
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*
* @param sound The sound asset to load.
* @param looped Whether or not this sound should loop endlessly.
* @param autoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing.
* @param onComplete Called when the sound finishes playing.
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*
* @since 6.2.0
*/
public function loadEmbedded(EmbeddedSound:FlxSoundAsset, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void):FlxSound
public function load(sound:FlxSoundAsset, looped:Bool = false, autoDestroy:Bool = false, ?onComplete:Void->Void):FlxSound
{
if (EmbeddedSound == null)
if (sound == null)
return this;

cleanup(true);

if ((EmbeddedSound is Sound))
if ((sound is Sound))
{
_sound = EmbeddedSound;
_sound = sound;
}
else if ((EmbeddedSound is Class))
else if ((sound is Class))
{
_sound = Type.createInstance(EmbeddedSound, []);
_sound = Type.createInstance(sound, []);
}
else if ((EmbeddedSound is String))
else if ((sound is String))
{
if (FlxG.assets.exists(EmbeddedSound, SOUND))
_sound = FlxG.assets.getSoundUnsafe(EmbeddedSound);
if (FlxG.assets.exists(sound, SOUND))
_sound = FlxG.assets.getSoundUnsafe(sound);
else
FlxG.log.error('Could not find a Sound asset with an ID of \'$EmbeddedSound\'.');
FlxG.log.error('Could not find a Sound asset with an ID of \'$sound\'.');
}
else if ((sound is ByteArrayData))
{
var bytes:ByteArray = cast sound;

_sound = new Sound();
_sound.addEventListener(Event.ID3, gotID3);
_sound.loadCompressedDataFromByteArray(bytes, bytes.length);
}

// NOTE: can't pull ID3 info from embedded sound currently
return init(Looped, AutoDestroy, OnComplete);
return init(looped, autoDestroy, onComplete);
}

/**
* One of the main setup functions for sounds, this function loads a sound from a URL.
*
* @param SoundURL A string representing the URL of the MP3 file you want to play.
* @param Looped Whether or not this sound should loop endlessly.
* @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.
* @param OnComplete Called when the sound finished playing
* @param OnLoad Called when the sound finished loading.
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
* Loads a sound from the provided URL.
*
* @param soundURL A string representing the URL of the sound you want to play.
* @param looped Whether or not this sound should loop endlessly.
* @param autoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing.
* @param onComplete Called when the sound finishes playing.
* @param onLoad Called when the sound finishes loading.
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*
* @since 6.2.0
*/
public function loadStream(SoundURL:String, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void, ?OnLoad:Void->Void):FlxSound
public function loadFromURL(soundURL:String, looped:Bool = false, autoDestroy:Bool = false, ?onComplete:Void->Void, ?onLoad:Void->Void):FlxSound
{
cleanup(true);

Expand All @@ -396,15 +407,50 @@ class FlxSound extends FlxBasic
if (_sound == e.target)
{
_length = _sound.length;
if (OnLoad != null)
OnLoad();
if (onLoad != null)
onLoad();
}
}
// Use a weak reference so this can be garbage collected if destroyed before loading.
_sound.addEventListener(Event.COMPLETE, loadCallback, false, 0, true);
_sound.load(new URLRequest(SoundURL));
_sound.load(new URLRequest(soundURL));

return init(Looped, AutoDestroy, OnComplete);
return init(looped, autoDestroy, onComplete);
}

/**
* One of the main setup functions for sounds, this function loads a sound from an embedded MP3.
*
* **Note:** If the `FLX_DEFAULT_SOUND_EXT` flag is enabled, you may omit the file extension
*
* @param EmbeddedSound An embedded Class object representing an MP3 file.
* @param Looped Whether or not this sound should loop endlessly.
* @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.loadFromURL()` will set it to true by default.
* @param OnComplete Called when the sound finished playing
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*/
@:deprecated("loadEmbedded() is deprecated, use load() instead.")
public function loadEmbedded(EmbeddedSound:FlxSoundAsset, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void):FlxSound
{
return load(EmbeddedSound, Looped, AutoDestroy, OnComplete);
}

/**
* One of the main setup functions for sounds, this function loads a sound from a URL.
*
* @param SoundURL A string representing the URL of the MP3 file you want to play.
* @param Looped Whether or not this sound should loop endlessly.
* @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.loadFromURL()` will set it to true by default.
* @param OnComplete Called when the sound finished playing
* @param OnLoad Called when the sound finished loading.
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*/
@:deprecated("loadStream() is deprecated, use loadFromURL() instead.")
public function loadStream(SoundURL:String, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void, ?OnLoad:Void->Void):FlxSound
{
return loadFromURL(SoundURL, Looped, AutoDestroy, OnComplete, OnLoad);
}

/**
Expand All @@ -413,18 +459,13 @@ class FlxSound extends FlxBasic
* @param Bytes A ByteArray object.
* @param Looped Whether or not this sound should loop endlessly.
* @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.
* Default value is false, but `FlxG.sound.play()` and `FlxG.sound.loadFromURL()` will set it to true by default.
* @return This FlxSound instance (nice for chaining stuff together, if you're into that).
*/
@:deprecated("loadByteArray() is deprecated, use load() instead.")
public function loadByteArray(Bytes:ByteArray, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void):FlxSound
{
cleanup(true);

_sound = new Sound();
_sound.addEventListener(Event.ID3, gotID3);
_sound.loadCompressedDataFromByteArray(Bytes, Bytes.length);

return init(Looped, AutoDestroy, OnComplete);
return load(Bytes, Looped, AutoDestroy, OnComplete);
}

function init(Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void):FlxSound
Expand Down
2 changes: 1 addition & 1 deletion flixel/system/FlxAssets.hx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class VirtualInputData extends #if nme ByteArray #else ByteArrayData #end {}

typedef FlxTexturePackerJsonAsset = FlxJsonAsset<TexturePackerAtlas>;
typedef FlxAsepriteJsonAsset = FlxJsonAsset<AseAtlas>;
typedef FlxSoundAsset = OneOfThree<String, Sound, Class<Sound>>;
typedef FlxSoundAsset = OneOfFour<String, Sound, Class<Sound>, ByteArray>;
typedef FlxGraphicAsset = OneOfThree<FlxGraphic, BitmapData, String>;
typedef FlxTilemapGraphicAsset = OneOfFour<FlxFramesCollection, FlxGraphic, BitmapData, String>;
typedef FlxBitmapFontGraphicAsset = OneOfFour<FlxFrame, FlxGraphic, BitmapData, String>;
Expand Down
9 changes: 5 additions & 4 deletions flixel/system/frontEnds/AssetFrontEnd.hx
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ class AssetFrontEnd
*
* @param id The ID or asset path for the sound
* @param useCache Whether to allow use of the asset cache (if one exists)
* @return A new `Sound` object Note: Dos not return a `FlxSound`
* @return A new `Sound` object Note: Does not return a `FlxSound`
*/
public inline function getSoundUnsafe(id:String, useCache = true):Sound
{
Expand All @@ -356,7 +356,7 @@ class AssetFrontEnd
* @param id The ID or asset path for the sound
* @param useCache Whether to allow use of the asset cache (if one exists)
* @param logStyle How to log, if the asset is not found. Uses `LogStyle.ERROR` by default
* @return A new `Sound` object Note: Dos not return a `FlxSound`
* @return A new `Sound` object Note: Does not return a `FlxSound`
*/
public inline function getSound(id:String, useCache = true, ?logStyle:LogStyle):Sound
{
Expand All @@ -369,7 +369,7 @@ class AssetFrontEnd
* @param id The ID or asset path for the sound
* @param useCache Whether to allow use of the asset cache (if one exists)
* @param logStyle How to log, if the asset is not found. Uses `LogStyle.ERROR` by default
* @return A new `Sound` object Note: Dos not return a `FlxSound`
* @return A new `Sound` object Note: Does not return a `FlxSound`
*/
public inline function getSoundAddExt(id:String, useCache = true, ?logStyle:LogStyle):Sound
{
Expand All @@ -387,7 +387,8 @@ class AssetFrontEnd

inline function addSoundExt(id:String)
{
if (!id.endsWith(".mp3") && !id.endsWith(".ogg") && !id.endsWith(".wav"))
final needsExt = Path.extension(id).length == 0;
if (needsExt)
return id + defaultSoundExtension;

return id;
Expand Down
33 changes: 27 additions & 6 deletions flixel/system/frontEnds/SoundFrontEnd.hx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class SoundFrontEnd
public var defaultMusicGroup:FlxSoundGroup = new FlxSoundGroup();

/**
* The group sounds in load() / play() / stream() are added to unless specified otherwise.
* The group sounds in load() / loadFromURL() / play() are added to unless specified otherwise.
*/
public var defaultSoundGroup:FlxSoundGroup = new FlxSoundGroup();

Expand Down Expand Up @@ -124,7 +124,7 @@ class SoundFrontEnd
music.stop();
}

music.loadEmbedded(embeddedMusic, looped);
music.load(embeddedMusic, looped);
music.volume = volume;
music.persist = true;
group.add(music);
Expand Down Expand Up @@ -161,7 +161,7 @@ class SoundFrontEnd

if (embeddedSound != null)
{
sound.loadEmbedded(embeddedSound, looped, autoDestroy, onComplete);
sound.load(embeddedSound, looped, autoDestroy, onComplete);
loadHelper(sound, volume, group, autoPlay);
// Call OnlLoad() because the sound already loaded
if (onLoad != null && sound._sound != null)
Expand All @@ -182,7 +182,7 @@ class SoundFrontEnd
}
}

sound.loadStream(url, looped, autoDestroy, onComplete, loadCallback);
sound.loadFromURL(url, looped, autoDestroy, onComplete, loadCallback);
loadHelper(sound, volume, group);
}

Expand Down Expand Up @@ -251,7 +251,7 @@ class SoundFrontEnd
{
embeddedSound = cache(embeddedSound);
}
var sound = list.recycle(FlxSound).loadEmbedded(embeddedSound, looped, autoDestroy, onComplete);
var sound = list.recycle(FlxSound).load(embeddedSound, looped, autoDestroy, onComplete);
return loadHelper(sound, volume, group, true);
}

Expand All @@ -269,12 +269,33 @@ class SoundFrontEnd
* @param onLoad Called when the sound finished loading.
* @return A FlxSound object.
*/
public function stream(url:String, volume = 1.0, looped = false, ?group:FlxSoundGroup, autoDestroy = true, ?onComplete:Void->Void,
public function loadFromURL(url:String, volume = 1.0, looped = false, ?group:FlxSoundGroup, autoDestroy = true, ?onComplete:Void->Void,
?onLoad:Void->Void):FlxSound
{
return load(null, volume, looped, group, autoDestroy, true, url, onComplete, onLoad);
}

/**
* Plays a sound from a URL. Tries to recycle a cached sound first.
* NOTE: Just calls FlxG.sound.load() with AutoPlay == true.
*
* @param url Load a sound from an external web resource instead.
* @param volume How loud to play it (0 to 1).
* @param looped Whether to loop this sound.
* @param group The group to add this sound to.
* @param autoDestroy Whether to destroy this sound when it finishes playing.
* Leave this value set to "false" if you want to re-use this FlxSound instance.
* @param onComplete Called when the sound finished playing
* @param onLoad Called when the sound finished loading.
* @return A FlxSound object.
*/
@:deprecated("FlxG.sound.stream() is deprecated, use FlxG.sound.loadFromURL() instead")
public function stream(url:String, volume = 1.0, looped = false, ?group:FlxSoundGroup, autoDestroy = true, ?onComplete:Void->Void,
?onLoad:Void->Void):FlxSound
{
return loadFromURL(url, volume, looped, group, autoDestroy, onComplete, onLoad);
}

/**
* Pause all sounds currently playing.
*/
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/src/flixel/sound/FlxSoundTest.hx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package flixel.sound;
class FlxSoundTest
{
@Test // #1511
function testLoadEmbeddedInvalidSoundPathNoCrash()
function testLoadInvalidSoundPathNoCrash()
{
var sound = new FlxSound();
sound.loadEmbedded("assets/invalid");
sound.load("assets/invalid");
sound.play();
}
}