Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

loop returns Howl instance instead of bool #3

Closed
FlorianBT opened this issue Oct 8, 2015 · 12 comments
Closed

loop returns Howl instance instead of bool #3

FlorianBT opened this issue Oct 8, 2015 · 12 comments

Comments

@FlorianBT
Copy link

I found out that Howl.loop returns a Howl object instead of a boolean value as expected

function foo(audio:Howl, id:Int):Void
{
    trace(audio.loop(id));
}

This would log the whole Howl object. In my case, this:

 {
    _autoplay : false, 
    _ext : null, 
    _html5 : false, 
    _muted : false, 
    _loop : true, 
    _pool : 2, 
    _preload : true, 
    _rate : 1, 
    _sprite : {
        garage : [0,119328.00453514738,true], 
        harbor : [121000,31364.69387755102,true], 
        kitchen : [154000,30228.5941043084,true], 
        machine-rumble : [186000,60734.69387755102,true], 
        space-loops : [248000,11640.997732426285,true], 
        world : [261000,9176.19047619047,true]
    }, 
    _src : assets/sounds/ambient.ogg, 
    _volume : 1, 
    _duration : 272.00002267573694, 
    _loaded : true, 
    _sounds : [{
            _parent : {
                _autoplay : false, 
                _ext : null, 
                _html5 : false, 
                _muted : false, 
                _loop : true, 
                _pool : 2, 
                _preload : true, 
                _rate : 1, 
                _sprite : {
                    garage : <...>, 
                    harbor : <...>, 
                    kitchen : <...>, 
                    machine-rumble : <...>, 
                    space-loops : <...>, 
                    world : <...>
                }, 
                _src : assets/sounds/ambient.ogg, 
                _volume : 1, 
                _duration : 272.00002267573694, 
                _loaded : true, 
                _sounds : [<...>], 
                _endTimers : {
                    89891563789 : <...>
                }, 
                _onend : [<...>], 
                _onfaded : [<...>], 
                _onload : [<...>], 
                _onloaderror : [<...>], 
                _onpause : [], 
                _onplay : [], 
                _onstop : [], 
                _webAudio : true
            }, 
            _muted : false, 
            _loop : null, 
            _volume : 0.2, 
            _rate : 1, 
            _seek : 261, 
            _paused : false, 
            _ended : false, 
            _id : 89891563789, 
            _node : [object GainNode], 
            _sprite : world, 
            _start : 261, 
            _stop : 270.17619047619047, 
            _playStart : 4.289886621315193
        }], 
    _endTimers : {
        89891563789 : 12
    }, 
    _onend : [{
            fn : <function>
        }], 
    _onfaded : [{
            fn : <function>
        }], 
    _onload : [{
            fn : <function>
        }], 
    _onloaderror : [{
            fn : <function>
        }], 
    _onpause : [], 
    _onplay : [], 
    _onstop : [], 
    _webAudio : true
}

As you can see, the Howl instance itself is set to loop. Calling audio.loop() would return true. But when passing a sound id, it returns the Howl instance.

Tracking down what happens in the javascript code, my guess is that a type check "fails" in howler code.

/**
     * Get/set the loop parameter on a sound. This method can optionally take 0, 1 or 2 arguments.
     *   loop() -> Returns the group's loop value.
     *   loop(id) -> Returns the sound id's loop value.
     *   loop(loop) -> Sets the loop value for all sounds in this Howl group.
     *   loop(loop, id) -> Sets the loop value of passed sound id.
     * @return {Howl/Boolean} Returns self or current loop value.
     */
    loop: function() {
      var self = this;
      var args = arguments;
      var loop, id, sound;

      // Determine the values for loop and id.
      if (args.length === 0) {
        // Return the grou's loop value.
        return self._loop;
      } else if (args.length === 1) {
        if (typeof args[0] === 'boolean') {
          loop = args[0];
          self._loop = loop;
        } else {
          // Return this sound's loop value.
          sound = self._soundById(parseInt(args[0], 10));
          return sound ? sound._loop : false;
        }
      } else if (args.length === 2) {
        loop = args[0];
        id = parseInt(args[1], 10);
      }

      // If no id is passed, get all ID's to be looped.
      var ids = self._getSoundIds(id);
      for (var i=0; i<ids.length; i++) {
        sound = self._soundById(ids[i]);

        if (sound) {
          sound._loop = loop;
          if (self._webAudio && sound._node && sound._node.bufferSource) {
            sound._node.bufferSource.loop = loop;
          }
        }
      }

      return self;
    }

Specifically this bit of code:

else if (args.length === 1) {
        if (typeof args[0] === 'boolean') {
          loop = args[0];
          self._loop = loop;
        } else { 
...
}

The id parameter of loop() is probably converted from Int to Bool as part of the optional parameters substitution.

Thanks in advance for any quick fix :)

@adireddy
Copy link
Owner

adireddy commented Oct 8, 2015

Looks like this is a bug in howler. Can you log this in https://github.com/goldfire/howler.js repo.

@FlorianBT
Copy link
Author

Sure! Do I present this issue linked to the use of haxe-howler? I'll do
some more testing directly in JS to be sure though. I don't know if the
type cast is done on Have or JS side

Le jeu. 8 oct. 2015 13:57, Adi notifications@github.com a écrit :

Looks like this is a bug in howler. Can you log this in
https://github.com/goldfire/howler.js repo.


Reply to this email directly or view it on GitHub
#3 (comment).

@adireddy
Copy link
Owner

adireddy commented Oct 8, 2015

May be if you look in the generated javascript file, you can find how the parameters are casted.

Post the js file here if you can.

@FlorianBT
Copy link
Author

The generated JS file is more than 2MB big in debug (HTML5 game). I would rather not post the whole file here, especially as it is for a client.
Which bits interest you the most? The actual calls to howler? Or the js_Boot parts where the JS magic seems to happen?

@adireddy
Copy link
Owner

adireddy commented Oct 8, 2015

howler calls may help

@FlorianBT
Copy link
Author

SoundMng is the base class for playing sound. It just takes a Howl instance and plays it with some tweaking parameters. It can also load a sound directly from path. Nothing magical in here, just fyi as it is the base class dealing with Howler.

$hxClasses["casuHaxeFlxEngine.SoundMng"] = casuHaxeFlxEngine_SoundMng;
casuHaxeFlxEngine_SoundMng.__name__ = ["casuHaxeFlxEngine","SoundMng"];
casuHaxeFlxEngine_SoundMng.__properties__ = {get_instance:"get_instance"}
casuHaxeFlxEngine_SoundMng.instance = null;
casuHaxeFlxEngine_SoundMng.get_instance = function() {
    return casuHaxeFlxEngine_SoundMng.instance;
};
casuHaxeFlxEngine_SoundMng.prototype = {
    load: function(Path,Volume,Looping) {
        var audio = null;
        if(Path != null && Path.length > 0) {
            var options = { };
            options.autoplay = false;
            options.preload = true;
            options.pool = 2;
            options.loop = Looping;
            options.volume = Volume;
            options.src = [Path + ".ogg",Path + ".m4a",Path + ".mp3",Path + ".ac3"];
            options.onend = function(id) {
                if(!audio.loop(id)) audio.stop(id);
            };
            options.onfaded = function(id1) {
                if(audio.volume(id1) <= 0) audio.stop(id1);
            };
            audio = new Howl(options);
        }
        return audio;
    }
    ,play: function(Sound,AudioID,Volume,Looping) {
        if(Sound != null && js_Boot.__instanceof(Sound,Howl)) {
            var soundId = Sound.play(AudioID);
            Sound.volume(Volume,soundId);
            Sound.loop(Looping,soundId);
            return soundId;
        }
        return null;
    }
    ,stop: function(Sound,AudioID,Fade,FadeDuration) {
        if(Sound != null && js_Boot.__instanceof(Sound,Howl)) {
            if(Fade && Sound.playing(AudioID)) Sound.fade(Sound.volume(AudioID),0,FadeDuration,AudioID); else Sound.stop(AudioID);
        }
    }
    ,playSound: function(Sound,AudioID,Volume,Looping) {
        if(Looping == null) Looping = false;
        if(Volume == null) Volume = 1.0;
        var sound;
        if(js_Boot.__instanceof(Sound,Howl)) sound = Sound; else sound = this.load(Sound,Volume,Looping);
        var id = this.play(sound,AudioID,Volume,Looping);
        if(sound != Sound) return sound; else return id;
    }
    ,stopSound: function(Sound,AudioID,Fade,FadeDuration) {
        if(FadeDuration == null) FadeDuration = 500;
        if(Fade == null) Fade = true;
        this.stop(Sound,AudioID,Fade,FadeDuration);
    }
    ,__class__: casuHaxeFlxEngine_SoundMng
};

SoundManagerONI is child of SoundMng. This one is the interesting part, as it loads all the sound sprites and play them. I use enum values or string values to determine which sound to play. This part works quite well, I have no issues with it, I always retrieve the correct Howl instance for my needs.

$hxClasses["onisep.SoundManagerONI"] = onisep_SoundManagerONI;
onisep_SoundManagerONI.__name__ = ["onisep","SoundManagerONI"];
onisep_SoundManagerONI.get_instance = function() {
    if(casuHaxeFlxEngine_SoundMng.get_instance() == null) return new onisep_SoundManagerONI(); else return casuHaxeFlxEngine_SoundMng.get_instance();
};
onisep_SoundManagerONI.__super__ = casuHaxeFlxEngine_SoundMng;
onisep_SoundManagerONI.prototype = $extend(casuHaxeFlxEngine_SoundMng.prototype,{
    _soundSprites: null
    ,_preloadCallback: null
    ,_preloadCount: null
    ,_preloadTotal: null
    ,loadSoundSprite: function(Path,Volume,Looping) {
        if(Looping == null) Looping = false;
        if(Volume == null) Volume = 1.0;
        this._preloadTotal++;
        haxe_Log.trace("loading " + Path,{ fileName : "SoundManagerONI.hx", lineNumber : 83, className : "onisep.SoundManagerONI", methodName : "loadSoundSprite"});
        var audio = null;
        if(Path != null && Path.length > 0) {
            var json = JSON.parse(openfl_Assets.getText(Path + ".json"));
            var options = { };
            options.autoplay = false;
            options.preload = true;
            options.pool = 2;
            options.loop = Looping;
            options.volume = Volume;
            options.src = [Path + ".ogg",Path + ".m4a",Path + ".mp3",Path + ".ac3"];
            options.sprite = json.sprite;
            options.onend = function(id) {
                if(!audio.loop(id)) audio.stop(id);
            };
            options.onfaded = function(id1) {
                if(audio.volume(id1) <= 0) audio.stop(id1);
            };
            options.onload = $bind(this,this.onSoundLoaded);
            options.onloaderror = $bind(this,this.onSoundLoaded);
            audio = new Howl(options);
        }
        return audio;
    }
    ,playSound: function(Type,AudioID,Volume,Looping) {
        if(Looping == null) Looping = false;
        if(Volume == null) Volume = 1.0;
        var audio;
        var key = this.determineType(Type);
        audio = this._soundSprites.get(key);
        if(audio == null) return null;
        return this.play(audio,this.getSoundId(AudioID),Volume,Looping);
    }
    ,stopSound: function(Type,AudioID,Fade,FadeDuration) {
        if(FadeDuration == null) FadeDuration = 500;
        if(Fade == null) Fade = true;
        var audio;
        var key = this.determineType(Type);
        audio = this._soundSprites.get(key);
        if(audio == null) return;
        this.stop(audio,AudioID,Fade,FadeDuration);
    }
    ,getSoundId: function(Name) {
        var id = null;
        switch(Type.enumIndex(Name)) {
        case 0:
            id = "clickButton";
            break;
        case 1:
            id = "click-label";
            break;
        case 2:
            id = "goodanswer";
            break;
        case 3:
            id = "badanswer";
            break;
        case 4:
            id = "success_small";
            break;
        case 5:
            id = "landing";
            break;
        case 6:
            id = "unlock";
            break;
        case 7:
            id = "open-popup";
            break;
        case 8:
            id = "fiche-perso";
            break;
        case 9:
            id = "object-found";
            break;
        case 10:
            id = "object-inventory";
            break;
        case 11:
            id = "glory";
            break;
        default:
            return Name;
        }
        return id;
    }
    ,determineType: function(T) {
        if(T != null) {
            if(js_Boot.__instanceof(T,onisep_SoundType)) return T; else if(typeof(T) == "string") return Type.createEnum(onisep_SoundType,T);
        }
        return null;
    }
    ,setPreloadCallback: function(Callback) {
        this._preloadCallback = Callback;
    }
    ,onSoundLoaded: function() {
        this._preloadCount++;
        if(this._preloadCallback != null) this._preloadCallback(this._preloadCount / this._preloadTotal);
    }
    ,__class__: onisep_SoundManagerONI
});

The issue I found is located here, in an event callback (onend callback)

var audio = null;
        if(Path != null && Path.length > 0) {
            var json = JSON.parse(openfl_Assets.getText(Path + ".json"));
            var options = { };
            options.autoplay = false;
            options.preload = true;
            options.pool = 2;
            options.loop = Looping;
            options.volume = Volume;
            options.src = [Path + ".ogg",Path + ".m4a",Path + ".mp3",Path + ".ac3"];
            options.sprite = json.sprite;
            options.onend = function(id) {
                if(!audio.loop(id)) audio.stop(id);
            };
            options.onfaded = function(id1) {
                if(audio.volume(id1) <= 0) audio.stop(id1);
            };
            options.onload = $bind(this,this.onSoundLoaded);
            options.onloaderror = $bind(this,this.onSoundLoaded);
            audio = new Howl(options);
        }
        return audio;

In Howl.hx, onend is Int->Void, so id should be an Int. Therefore audio.loop(id) should return a boolean.

Does it help?

@adireddy
Copy link
Owner

adireddy commented Oct 8, 2015

yes, got it now... I think I know the issue. Will do a quick test and then update the externs.

@adireddy
Copy link
Owner

adireddy commented Oct 8, 2015

Fixed it @FlorianBT. Please check and let me know. v2.0.1 http://lib.haxe.org/p/howlerjs/2.0.1

Also updated the sample to trace the return value of loop function call and its returning Bool.
http://adireddy.github.io/demos/haxe-howler/
Play sound 1 or sound 2 and see the traces in console.

@adireddy adireddy closed this as completed Oct 8, 2015
@FlorianBT
Copy link
Author

Hello again.

Sadly, my project doesn't compile anymore with your latest changes.

SoundManagerONI.hx:107: characters 7-28 : Cannot compare howler.Howl and Int
SoundMng.hx:86: characters 15-36 : howler.Howl should be Float
SoundMng.hx:86: characters 15-36 : For function argument 'from'
SoundMng.hx:46: characters 7-28 : Cannot compare howler.Howl and Int

Involved lines in Haxe, in order:

SoundManagerONI.hx:107: characters 7-28 : Cannot compare howler.Howl and Int

options.onfaded = function(id:Int):Void {
    //trace("faded " + id + " volume : " + audio.volume(id));
===>    if(audio.volume(id) <= 0)
            audio.stop(id);
        };
};
SoundMng.hx:86: characters 15-36 : howler.Howl should be Float
SoundMng.hx:86: characters 15-36 : For function argument 'from'

if(Fade && Sound.playing(AudioID)) 
{
===>    Sound.fade(Sound.volume(AudioID), 0, FadeDuration, AudioID);
} 
SoundMng.hx:46: characters 7-28 : Cannot compare howler.Howl and Int

options.onfaded = function(id:Int):Void {
===>    if(audio.volume(id) <= 0)
            audio.stop(id);
        };
};

I looked a bit at your new overloads and did some researches as I couldn't find a reason it selected the wrong overload. I found this :
http://blog.onthewings.net/2012/07/13/haxe-tips-advanced-method-overloading-with-macros/
Maybe we're in the same case here.

@adireddy
Copy link
Owner

adireddy commented Oct 9, 2015

I see the problem and it's really annoying... anyway as a quick fix I changed the return type to Dynamic to fix the issue.

Can you try again and let me know. v2.0.2

I did a quick test and it's fine.

@FlorianBT
Copy link
Author

Tested, compiling and working as expected. Thanks a lot for your time!

@adireddy
Copy link
Owner

adireddy commented Oct 9, 2015

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants