Discussion:
[emms-help] Restore playback position
Pierre Neidhardt
2017-10-23 22:55:26 UTC
Permalink
The current playback position is stored in `emms-playing-time' so I
naively added that variable to `desktop-globals-to-save' hoping it would
restore it on next Emacs startup.

Sadly emms-start does not have an `&optional seek-to', so it always
starts playing from the beginning when `emms-start' is called.

Of course we could call `emms-player-seek-to time-save-by-desktop', the
problem is that the player is started asynchronously and we need to wait
for it to be ready before we can start seeking.
How do we do that? My current workaround is to `sleep-for` 300ms. Not
pretty.

Here is my current, brittle implementation:

(with-eval-after-load 'desktop
(add-to-list 'desktop-globals-to-save 'emms-playing-time)
(when (emms-playlist-current-selected-track)
(let ((time emms-playing-time))
(setq emms-playing-time 0) ; Don't disturb the time display.
(and (memq 'emms-player-mpv emms-player-list)
(executable-find "mpv")
(push "--mute=yes" emms-player-mpv-parameters))
(emms-start)
(sleep-for 0 300) ; This is required for the player might not be ready yet.
;; TODO: This 'sleep-for' is a kludge and upstream should provide a provision for it.
(with-demoted-errors "EMMS error: %S" (emms-player-seek-to time))
(and (memq 'emms-player-mpv emms-player-list)
(executable-find "mpv")
(pop emms-player-mpv-parameters)
(call-process-shell-command (emms-player-mpv--format-command "mute") nil nil nil))
(emms-pause))))

Let me know if I'm missing something. If not, I propose we add
`&optional seek-to' to `emms-start' so that players can add a playback
position as parameters when they start.

Cheers!
--
Pierre Neidhardt

Better to be nouveau than never to have been riche at all.
Yoni Rabkin
2017-10-24 16:39:14 UTC
Permalink
Post by Pierre Neidhardt
The current playback position is stored in `emms-playing-time' so I
naively added that variable to `desktop-globals-to-save' hoping it would
restore it on next Emacs startup.
Sadly emms-start does not have an `&optional seek-to', so it always
starts playing from the beginning when `emms-start' is called.
The problem is the assumption that the next time `emms-start' is called,
the same track will be playing. Also, whatever solution shouldn't
reference a particular player, of course.

emms-bookmarks.el was started to try and tackle this by being able to
save "bookmarks" on a per-track basis. I think that a solution to this
issue should either use emms-bookmarks.el, or a similar approach.

For instance, an option that (when enabled): if there are bookmarks in
the track being played, jump to the last bookmark.
--
"Cut your own wood and it will warm you twice"
Pierre Neidhardt
2017-10-26 07:06:37 UTC
Permalink
Post by Yoni Rabkin
For instance, an option that (when enabled): if there are bookmarks in
the track being played, jump to the last bookmark.
Yes, a per-track solution is more ideal than my quick&dirty hack.
I went through this a long time ago and I don't remember well, but I had
tried bookmarks and it simply did not cut it. I'll investigate again.

That being said, I don't think this would solve the issue of the race
condition:


(when (not emms-player-playing-p)
(emms-start))
(let ((m (funcall search-f emms-playing-time track)))
(if m
(progn
(emms-player-seek-to (cdr m))
(message "%s" (car m)))
(message "%s" failure-message))))

The above code suffers from the same issue: it does not wait until the
executable and the music file have entered a state where they can "seek".

I think an optional `seek-to` added to `emms-start` would solve this.
Let me know.
--
Pierre Neidhardt

"Here's something to think about: How come you never see a headline like
`Psychic Wins Lottery'?"
-- Jay Leno
Yoni Rabkin
2017-10-26 12:09:08 UTC
Permalink
Post by Pierre Neidhardt
Post by Yoni Rabkin
For instance, an option that (when enabled): if there are bookmarks in
the track being played, jump to the last bookmark.
Yes, a per-track solution is more ideal than my quick&dirty hack.
I went through this a long time ago and I don't remember well, but I had
tried bookmarks and it simply did not cut it. I'll investigate again.
That being said, I don't think this would solve the issue of the race
(when (not emms-player-playing-p)
(emms-start))
(let ((m (funcall search-f emms-playing-time track)))
(if m
(progn
(emms-player-seek-to (cdr m))
(message "%s" (car m)))
(message "%s" failure-message))))
The above code suffers from the same issue: it does not wait until the
executable and the music file have entered a state where they can "seek".
I think an optional `seek-to` added to `emms-start` would solve this.
Let me know.
I can't think of a clean way of doing that, and in any case `emms-start'
is just an `interactive' wrapper and would the wrong place to start
thinking of this.

Can you describe the use case? I'm assuming this is for podcasts or
spoken word stuff (pop songs don't need this, and one normally doesn't
start in the middle of a classical movement (shudder)) I'm not doubting
the validity of the need for this; it's just that I would have an easier
time thinking about it if I knew the specific use.

Right now I'm thinking about a function registered with
`emms-player-started-hook' to call seek-to.

If it critical that the player actually start at that time, as opposed
to start and then seek, it may be necessary to make a
`define-emms-simple-player' with the right command line stuff at process
call.
--
"Cut your own wood and it will warm you twice"
Pierre Neidhardt
2017-10-26 21:24:15 UTC
Permalink
Post by Yoni Rabkin
Post by Pierre Neidhardt
I think an optional `seek-to` added to `emms-start` would solve this.
Let me know.
I can't think of a clean way of doing that, and in any case `emms-start'
is just an `interactive' wrapper and would the wrong place to start
thinking of this.
...
Right now I'm thinking about a function registered with
`emms-player-started-hook' to call seek-to.
If it critical that the player actually start at that time, as opposed
to start and then seek, it may be necessary to make a
`define-emms-simple-player' with the right command line stuff at process
call.
I meant `emms-start' and the whole hierarchy of callees. Which boils
down to extending `define-emms-simple-player as you said. That would be
quite easy I believe. I don't know if that would break existing
players, but there are not that many and a global update would not be
too demanding I guess.

A hook would not work: when does the hook get run? There is no way to
know when the player is ready to seek.
Post by Yoni Rabkin
Can you describe the use case? I'm assuming this is for podcasts or
spoken word stuff (pop songs don't need this, and one normally doesn't
start in the middle of a classical movement (shudder)) I'm not doubting
the validity of the need for this; it's just that I would have an easier
time thinking about it if I knew the specific use.
- Podcasts, audiobooks, anything in which people talk.

- Why not classical movement? When it's 1 hour long and you suffer an
interuption in the middle of it, you might not want start all over
again.

- If a pop song gets interupted near the end, restarting from the
beginning feels repetitive.

- So... anything, basically.

The point is to recover the playback gracefully (i.e. not lose the
progress) after crashes / sudden shutdowns / restarts, e.g. if the user
wants to restart Emacs for whatever reason (update, messed up the global
state, etc.).

--
Pierre Neidhardt

Monotheism is a gift from the gods.
Yoni Rabkin
2017-10-26 22:26:02 UTC
Permalink
Post by Pierre Neidhardt
Post by Yoni Rabkin
Post by Pierre Neidhardt
I think an optional `seek-to` added to `emms-start` would solve this.
Let me know.
I can't think of a clean way of doing that, and in any case `emms-start'
is just an `interactive' wrapper and would the wrong place to start
thinking of this.
...
Right now I'm thinking about a function registered with
`emms-player-started-hook' to call seek-to.
If it critical that the player actually start at that time, as opposed
to start and then seek, it may be necessary to make a
`define-emms-simple-player' with the right command line stuff at process
call.
I meant `emms-start' and the whole hierarchy of callees. Which boils
down to extending `define-emms-simple-player as you said. That would be
quite easy I believe. I don't know if that would break existing
players, but there are not that many and a global update would not be
too demanding I guess.
A hook would not work: when does the hook get run? There is no way to
know when the player is ready to seek.
How does any GUI know when the player is ready to seek? They normally
don't.

As an experiment, I added a bookmark using emms-bookmarks.el in a movie
(Backpacking the Lake District with Chris Townsend) and then:

(add-hook 'emms-player-started-hook 'emms-bookmarks-next)

Then I started the movie over and over again (mplayer as the player in
this case) and it worked every time.

I agree that emms-bookmarks is skeletal at best, but that is easy to
fix.
Post by Pierre Neidhardt
Post by Yoni Rabkin
Can you describe the use case? I'm assuming this is for podcasts or
spoken word stuff (pop songs don't need this, and one normally doesn't
start in the middle of a classical movement (shudder)) I'm not doubting
the validity of the need for this; it's just that I would have an easier
time thinking about it if I knew the specific use.
- Podcasts, audiobooks, anything in which people talk.
- Why not classical movement? When it's 1 hour long and you suffer an
interuption in the middle of it, you might not want start all over
again.
- If a pop song gets interupted near the end, restarting from the
beginning feels repetitive.
- So... anything, basically.
The point is to recover the playback gracefully (i.e. not lose the
progress) after crashes / sudden shutdowns / restarts, e.g. if the user
wants to restart Emacs for whatever reason (update, messed up the global
state, etc.).
How do you propose to remember when each track was stopped?

If it's a crash you don't have an opportunity, by definition, to know
when you stopped and that implies keeping track of the most advanced
playing time for every track played.

If it isn't a crash then we need to hash every track played and when it
was stopped. Probably removing the track from the hash when it reaches
completion.

Is that what you had in mind?
--
"Cut your own wood and it will warm you twice"
Loading...