Skip to content
This repository has been archived by the owner on Aug 6, 2018. It is now read-only.

Player 'end_of_track' callback fails to fire #131

Open
stengland opened this issue Aug 18, 2012 · 18 comments
Open

Player 'end_of_track' callback fails to fire #131

stengland opened this issue Aug 18, 2012 · 18 comments

Comments

@stengland
Copy link

Hi,

Thanks for this excellent project, been having fun playing with it. I think I am missing something but I can't can't get the 'end_of_track' callback to fire. For example i've used this to create a player object (in irb)

player = Hallon::Player.new(Hallon::OpenAL) do
  on(:end_of_track) { puts 'lala' }
end

But I can never see the callback happening when a track finishes playing.

Hope you can give me some pointers.

Thanks
Steve

@Burgestrand
Copy link
Owner

I vaguely remembers reports of this callback being broken; however, I just tried the playing_audio example and it successfully got the end_of_track callback (at the end of the song it said "Done! This was “Track” by “Artist”". Could you try the playing_audio example and play a song to the end, just to make sure the callback actually works?

Also, if you are using Player#play! it actually overrides the actual callback (but restores it afterwards). The callback system is a bit unpolished in how it behaves, and player should probably invoke the old callback even after overriding.

@FWMatt
Copy link

FWMatt commented Oct 2, 2012

This doesnt fire for me, though play! does finish correctly and passes control back to the program.

@Burgestrand
Copy link
Owner

@FWMatt in that case it behaves as expected, because of the player overriding the callback to know when the track has finished playing.

I’ll be closing this issue on lack of activity.

@stengland
Copy link
Author

@Burgestrand Sorry, haven't had free time to look into your suggestions. Once I do I'll report back.

@Burgestrand
Copy link
Owner

Alright!

@Burgestrand Burgestrand reopened this Oct 2, 2012
@FWMatt
Copy link

FWMatt commented Oct 2, 2012

But it should make the callback as well, correct? It doesnt make the callback when I use either play or play!

@Burgestrand
Copy link
Owner

@FWMatt No, when you use play! your handler will never fire. This is because each callback only support one handler, and when you use play! that handler is overridden by the player itself so it knows when to yield back: https://github.com/Burgestrand/Hallon/blob/master/lib/hallon/player.rb#L193 — this also means that if play! blocks until the track has finished playing, it also means that the callback works.

@FWMatt
Copy link

FWMatt commented Oct 3, 2012

The code is this:-

    def play!(track = nil)
      end_of_track = false
      old_callback = on(:end_of_track) { end_of_track = true }
      play(track)
      wait_for(:end_of_track) { end_of_track }
    ensure
      on(:end_of_track, &old_callback)
    end

So it should still be making the old_callback?

I see what you mean about the original callback working though.

@Burgestrand
Copy link
Owner

@FWMatt Yes, but it will only invoke the old callback once #play! has returned. During the blocking call of #play!, your own end_of_track callback will not be invoked.

@FWMatt
Copy link

FWMatt commented Oct 7, 2012

Exactly. However, old_callback doesnt appear to get called.

@adamyeats-zz
Copy link

The callback is not called for me either. When using the .play method for playback, using Pry to check the value of Player.status seems to return :playing even after the track has audibly stopped playing, which could explain why the callback is never fired (as the track never 'ends').

Here is the code I've been using if this helps.

@Burgestrand
Copy link
Owner

Tried to answer via mail but it appears it didn’t stick. Here it is:

Make sure that you call #process_events — the end_of_track callback, for whatever reason, is not called from the internal libspotify session thread, but only through calls to process_events.

So, if you call Player#play, it will play audio continously. It will even play the whole song, even if you don’t ever call Session#process_events. However, since libspotify does not call end_of_track from the session thread (same thread that delivers music), you will never receive the end_of_track callback until you call Session#process_events after all music has been delivered for the currently playing track.

Player#status is also never updated if not explicitly changed by pausing or stopping the player, so if you start playing and never do anything else (and libspotify never asks you to pause), I believe the status should be :playing forever. The Hallon player is weird that way.

@stengland
Copy link
Author

Hi, checking back in on this thread.

@Burgestrand Does this mean the Session#process_events has to be called after a track has finished playing or after the track has loaded in order to get the callback to be fired? I think myself and @adamyeats are trying to achieve a similar goal where we want the callback to start playing the next track in a playlist. Do you have any advice as to the best way to achieve this?

@Burgestrand
Copy link
Owner

@stengland It must be called after a track has finished playing. So, once a track has finished playing, it will call the end_of_track callback during your next call to Session#process_events.

If you want to queue up another track for playback after another one has finished, you’d do so by waiting for the end_of_track callback, and after it has fired you’d load and play another track. During my recent audio playback tests (part of an effort I mentioned in #140), it appears you’ll (sometimes, if not always) need to pause playback before being able to play another song, or libspotify will do nothing and log something about a rate limit being put into place (that never expires, even though it appears it would from the log messages).

@kennylovrin
Copy link

I don't know if I am exceptionally stupid, but I just can't manage to get this to work when trying to call process events.
Either I end up with some sort of deadlock, or the callbacks just never gets called. I'm not getting the play token lost callback either even if I start a track in the Spotify app while also playing one through Hallon.

It would be great with some simple example specifically showing the proper use of process_events callback (conceptually I think I get it, I've gotten it to work with libspotify and obj-c, but just can't figure it out using Hallon and Ruby).

@adamyeats-zz
Copy link

@Burgestrand Thanks for replying. I've no idea how I've managed to miss your reply and all the activity after that, so I do apologise for looking like I've ignored this.

I agree with @kennylovrin here when he says it would be great to see an example of how you think Session#process_events should be use, as this is still unclear to me right now, and seems to be the same for everyone else in this thread too. :)

@Burgestrand
Copy link
Owner

To start with, here’s an example of how I believe process_events could be used when coding simple ruby scripts with libspotify: https://github.com/Burgestrand/spotify/blob/master/examples/audio-stream_example.rb

libspotify expects you to call process_events often, and fashioning a guess I believe a few dozens of times per second would be appropriate. For example, calling process_events every tick of the event loop when coding Obj-C would be perfect, or every tick in node.js, or every tick in e.g. eventmachine in ruby. The poll method in my example is merely a loop, with a sleep in it: https://github.com/Burgestrand/spotify/blob/master/examples/example_support.rb#L20

This brings me to another issue, which I’ll write something in Hallon’s README about: Hallon’s model was from the start something along the lines of a synchronous Ruby binding to libspotify, and I no longer believe the way Hallon achieve this to be a good way of doing things. It’s possible Hallon should have an event loop, and thus share some methodologies with e.g. eventmachine or celluloid, but I am not sure of this. Because of this, development of Hallon has been stagnant for a long time. An additional reason is that I am no longer personally using Hallon for anything myself.

Most of my effort towards libspotify and Ruby have been focused on the spotify gem that powers Hallon: https://github.com/Burgestrand/spotify. For new projects, and possibly even existing projects, I would suggest using the spotify gem instead of using Hallon. It has a few pros:

  1. Less of an abstraction around libspotify.
  2. Less bugs, because of 1.
  3. Less risk of e.g. deadlocks in the library because of 1.
  4. Less confusing, because of 1.
  5. Better than C (it’s in ruby, has automatic garbage collection, thread safe)
  6. It’s still seeing new features and improvements, in comparison to Hallon, e.g. it’s supported.

The spotify gem also has a few cons:

  1. Less of an abstraction around libspotify.
  2. Requires (even if rudimentary) knowledge of FFI.
  3. Some things are still annoying to do, e.g. calling process_events, because of 2 and 1.

I know this is a lot of information you did not ask for, but I believe it is appropriate since you are users of Hallon.

Oh, for mentions: @kennylovrin, @adamyeats.

@adamyeats-zz
Copy link

@Burgestrand Very awesome of you to take the time to write that. Thank you. :)

It’s possible Hallon should have an event loop, and thus share some methodologies with e.g. eventmachine or celluloid, but I am not sure of this.

I think this is a worthwhile thing to do. If the goal of Hallon is to abstract away some of the less friendly parts of libspotify, I think doing this would lower the barrier to entry (particularly for new Rubyists or hackday projects) massively.

EDIT: Thinking about this again after re-reading the docs, is it do-able to make the .play! a sync method (it's blocking already) and play it's async counterpart?

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

No branches or pull requests

5 participants