From 448aefaace840ccd8b8c1fb82ff72ef986b84f12 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Thu, 8 Oct 2009 22:09:25 +0200
Subject: [PATCH] player_thread: get "elapsed" from audio outputs

Tracking the "elapsed" time from the chunks which we have sent to the
output pipe is very imprecise: since we have implemented the music
pipe, we're sending large number of chunks at once, giving the
"elapsed" time stamp a resolution of usually more than a second.

This patch changes the source of this information to the outputs.  If
a chunk has been played by all outputs, the "elapsed" time stamp is
updated.

The new command PLAYER_COMMAND_REFRESH makes the player thread update
its status information: it tells the outputs to update the chunk time
stamp.  After that, player_control.elapsed_time is current.
---
 src/output_all.c     | 14 ++++++++++++++
 src/output_all.h     |  7 +++++++
 src/player_control.c |  2 ++
 src/player_control.h |  6 ++++++
 src/player_thread.c  | 17 +++++++++++++----
 5 files changed, 42 insertions(+), 4 deletions(-)

diff --git a/src/output_all.c b/src/output_all.c
index 4b5ba3a6f..b7a42a6a3 100644
--- a/src/output_all.c
+++ b/src/output_all.c
@@ -52,6 +52,11 @@ static struct music_buffer *g_music_buffer;
  */
 static struct music_pipe *g_mp;
 
+/**
+ * The "elapsed_time" stamp of the most recently finished chunk.
+ */
+static float audio_output_all_elapsed_time;
+
 unsigned int audio_output_count(void)
 {
 	return num_audio_outputs;
@@ -385,6 +390,8 @@ audio_output_all_check(void)
 			   this chunk */
 			return music_pipe_size(g_mp);
 
+		audio_output_all_elapsed_time = chunk->times;
+
 		is_tail = chunk->next == NULL;
 		if (is_tail)
 			/* this is the tail of the pipe - clear the
@@ -468,9 +475,16 @@ audio_output_all_close(void)
 		music_pipe_clear(g_mp, g_music_buffer);
 		music_pipe_free(g_mp);
 		g_mp = NULL;
+		audio_output_all_elapsed_time = 0.0;
 	}
 
 	g_music_buffer = NULL;
 
 	audio_format_clear(&input_audio_format);
 }
+
+float
+audio_output_all_get_elapsed_time(void)
+{
+	return audio_output_all_elapsed_time;
+}
diff --git a/src/output_all.h b/src/output_all.h
index 2a09514b2..e124d3150 100644
--- a/src/output_all.h
+++ b/src/output_all.h
@@ -128,4 +128,11 @@ audio_output_all_pause(void);
 void
 audio_output_all_cancel(void);
 
+/**
+ * Returns the "elapsed_time" stamp of the most recently finished
+ * chunk.
+ */
+float
+audio_output_all_get_elapsed_time(void);
+
 #endif
diff --git a/src/player_control.c b/src/player_control.c
index 38d960031..25a0320c5 100644
--- a/src/player_control.c
+++ b/src/player_control.c
@@ -142,6 +142,8 @@ pc_set_pause(bool pause_flag)
 void
 pc_get_status(struct player_status *status)
 {
+	player_command(PLAYER_COMMAND_REFRESH);
+
 	status->state = pc.state;
 
 	if (pc.state != PLAYER_STATE_STOP) {
diff --git a/src/player_control.h b/src/player_control.h
index 1acdb5686..e9a43e844 100644
--- a/src/player_control.h
+++ b/src/player_control.h
@@ -48,6 +48,12 @@ enum player_command {
 	 * stop
 	 */
 	PLAYER_COMMAND_CANCEL,
+
+	/**
+	 * Refresh status information in the #player_control struct,
+	 * e.g. elapsed_time.
+	 */
+	PLAYER_COMMAND_REFRESH,
 };
 
 enum player_error {
diff --git a/src/player_thread.c b/src/player_thread.c
index 7368b4d4b..6777d5d61 100644
--- a/src/player_thread.c
+++ b/src/player_thread.c
@@ -151,7 +151,6 @@ player_wait_for_decoder(struct player *player)
 
 	player->song = pc.next_song;
 	pc.next_song = NULL;
-	pc.elapsed_time = 0;
 	player->queued = false;
 
 	/* set the "starting" flag, which will be cleared by
@@ -330,7 +329,6 @@ static bool player_seek_decoder(struct player *player)
 		return false;
 	}
 
-	pc.elapsed_time = where;
 	player_command_finished();
 
 	player->xfade = XFADE_UNKNOWN;
@@ -409,6 +407,14 @@ static void player_process_command(struct player *player)
 		player->queued = false;
 		player_command_finished();
 		break;
+
+	case PLAYER_COMMAND_REFRESH:
+		if (audio_format_defined(&player->play_audio_format))
+			audio_output_all_check();
+
+		pc.elapsed_time = audio_output_all_get_elapsed_time();
+		player_command_finished();
+		break;
 	}
 }
 
@@ -456,7 +462,6 @@ play_chunk(struct song *song, struct music_chunk *chunk,
 		return true;
 	}
 
-	pc.elapsed_time = chunk->times;
 	pc.bit_rate = chunk->bit_rate;
 
 	/* send the chunk to the audio outputs */
@@ -630,7 +635,6 @@ static void do_play(void)
 		return;
 	}
 
-	pc.elapsed_time = 0;
 	pc.state = PLAYER_STATE_PLAY;
 	player_command_finished();
 
@@ -813,6 +817,11 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg)
 			player_command_finished();
 			break;
 
+		case PLAYER_COMMAND_REFRESH:
+			/* no-op when not playing */
+			player_command_finished();
+			break;
+
 		case PLAYER_COMMAND_NONE:
 			notify_wait(&pc.notify);
 			break;