From 859c9d273793dd4d28ae67b9008be333b3aa68cb Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Tue, 4 Oct 2016 18:33:14 +0200
Subject: Enhance time tracking
Now with eliminated timer drift.
---
nncmpp.c | 44 ++++++++++++++++++++++++++++++--------------
1 file changed, 30 insertions(+), 14 deletions(-)
diff --git a/nncmpp.c b/nncmpp.c
index d14a824..bc29791 100644
--- a/nncmpp.c
+++ b/nncmpp.c
@@ -130,6 +130,14 @@ update_curses_terminal_size (void)
#endif // HAVE_RESIZETERM && TIOCGWINSZ
}
+static int64_t
+clock_msec (clockid_t clock)
+{
+ struct timespec tp;
+ hard_assert (clock_gettime (clock, &tp) != -1);
+ return (int64_t) tp.tv_sec * 1000 + (int64_t) tp.tv_nsec / 1000000;
+}
+
// --- Application -------------------------------------------------------------
// Function names are prefixed mostly because of curses which clutters the
@@ -215,6 +223,8 @@ static struct app_context
struct str_map song_info; ///< Current song info
struct poller_timer elapsed_event; ///< Seconds elapsed event
+ int64_t elapsed_since; ///< Time of the next tick
+
// TODO: initialize these to -1
int song_elapsed; ///< Song elapsed in seconds
int song_duration; ///< Song duration in seconds
@@ -1584,8 +1594,12 @@ mpd_on_info_response (const struct mpd_response *response,
// Note that we may receive a "time" field twice, however the right one
// wins here due to the order we send the commands in
+
+ // The contents of these values overlap and we try to get what we can
+ // FIXME: don't change the values, for fuck's sake
char *time = str_map_find (&map, "time");
char *duration = str_map_find (&map, "duration");
+ char *elapsed = str_map_find (&map, "elapsed");
if (time)
{
char *colon = strchr (time, ':');
@@ -1602,8 +1616,10 @@ mpd_on_info_response (const struct mpd_response *response,
if (duration && xstrtoul (&tmp, duration, 10))
g_ctx.song_duration = tmp;
- // TODO: use "time" as a fallback (no milliseconds there)
- char *period, *elapsed = str_map_find (&map, "elapsed");
+ // We could also just poll the server each half a second but let's not
+ int msec_past_second = 0;
+
+ char *period;
if (elapsed && (period = strchr (elapsed, '.')))
{
// For some reason this is much more precise
@@ -1613,12 +1629,12 @@ mpd_on_info_response (const struct mpd_response *response,
if (g_ctx.state == PLAYER_PLAYING
&& xstrtoul (&tmp, period, 10))
- {
- // TODO: initialize the timer and create a callback
- poller_timer_set (&g_ctx.elapsed_event, 1000 - tmp);
- }
+ msec_past_second = tmp;
}
+ poller_timer_set (&g_ctx.elapsed_event, 1000 - msec_past_second);
+ g_ctx.elapsed_since = clock_msec (CLOCK_BEST) - msec_past_second;
+ // The server sends -1 when nothing is being played right now
char *volume = str_map_find (&map, "volume");
if (volume && xstrtoul (&tmp, volume, 10))
g_ctx.volume = tmp;
@@ -1631,10 +1647,14 @@ static void
mpd_on_tick (void *user_data)
{
(void) user_data;
- // FIXME: this is doomed to drift unless we use POSIX CLOCK_MONOTONIC
- poller_timer_set (&g_ctx.elapsed_event, 1000);
+ int64_t diff_msec = clock_msec (CLOCK_BEST) - g_ctx.elapsed_since;
+ int elapsed_sec = diff_msec / 1000;
+ int elapsed_msec = diff_msec % 1000;
+
+ g_ctx.song_elapsed += elapsed_sec;
+ g_ctx.elapsed_since += elapsed_sec * 1000;
+ poller_timer_set (&g_ctx.elapsed_event, 1000 - elapsed_msec);
- g_ctx.song_elapsed++;
// TODO: try to be more efficient in the redrawing procedures
app_redraw ();
}
@@ -1891,11 +1911,7 @@ debug_tab_push (const char *message, chtype attrs)
&g_debug_tab.items[g_debug_tab.super.item_count++];
item->text = xstrdup (message);
item->attrs = attrs;
-
- struct timespec tp;
- hard_assert (clock_gettime (CLOCK_REALTIME, &tp) != -1);
- item->timestamp = (int64_t) tp.tv_sec * 1000
- + (int64_t) tp.tv_nsec / 1000000;
+ item->timestamp = clock_msec (CLOCK_REALTIME);
app_redraw_view ();
}
--
cgit v1.2.3-70-g09d2