aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2008-08-23 15:52:44 -0700
committerEric Wong <normalperson@yhbt.net>2008-08-23 15:52:44 -0700
commit5655d2e5922955763455134814035e7ad76d2529 (patch)
tree020cc15899b30596a4d8477a120d50cf536293a8
parentd822bdec0700b94d83f2e15c2aa6602307179724 (diff)
downloadmpd-5655d2e5922955763455134814035e7ad76d2529.tar.gz
mpd-5655d2e5922955763455134814035e7ad76d2529.tar.xz
mpd-5655d2e5922955763455134814035e7ad76d2529.zip
outputBuffer: fix buffer_before_play handling
buffer_before_play is a prebuffer; always respecting it is almost as good as having no buffer at all. So we only respect it when we haven't played anything. Bugs that were a side effect of this also got fixed: The player would not stop when we got to the end of the last song on non-repeating playlists. The playlist would continuously show the song in the last few seconds of playback, and never move. Having crossfade enabled would also amplify the above effect. So, as a side effect, crossfade now correctly handles end-of-playlist conditions, as well. It will fade out to silence when we're at the end of a playlist.
-rw-r--r--src/outputBuffer.c46
-rw-r--r--src/outputBuffer_config_init.h2
-rw-r--r--src/outputBuffer_xfade.h45
3 files changed, 55 insertions, 38 deletions
diff --git a/src/outputBuffer.c b/src/outputBuffer.c
index 12ab6eb2d..92d90184d 100644
--- a/src/outputBuffer.c
+++ b/src/outputBuffer.c
@@ -37,6 +37,8 @@ struct ob_chunk {
char data[CHUNK_SIZE];
};
+static struct ob_chunk silence;
+
enum ob_xfade_state {
XFADE_DISABLED = 0,
XFADE_ENABLED
@@ -49,7 +51,10 @@ static struct condition ob_seq_cond = STATIC_COND_INITIALIZER;
struct output_buffer {
struct ringbuf *index; /* index for chunks */
struct ob_chunk *chunks;
- size_t nr_bpp; /* nr (chunks) buffered before play */
+
+ size_t bpp_max; /* buffer_before_play, user setting, in chunks */
+ size_t bpp_cur; /* current prebuffer size (in chunks) */
+
enum ob_state state; /* protected by ob_action_cond */
enum ob_action action; /* protected by ob_action_cond */
enum ob_xfade_state xfade_state; /* thread-internal */
@@ -325,7 +330,7 @@ static struct ob_chunk *get_chunk(struct iovec vec[2], size_t i)
{
if (vec[0].iov_len > i)
return &ob.chunks[vec[0].iov_base + i - ob.index->buf];
- if (vec[1].iov_base) {
+ if (i && vec[1].iov_base) {
assert(vec[0].iov_len > 0);
i -= vec[0].iov_len;
if (vec[1].iov_len > i)
@@ -336,8 +341,7 @@ static struct ob_chunk *get_chunk(struct iovec vec[2], size_t i)
static void prevent_buffer_underrun(void)
{
- static const char silence[CHUNK_SIZE];
- if (playAudio(silence, sizeof(silence)) < 0)
+ if (playAudio(silence.data, sizeof(silence.data)) < 0)
stop_playback();
}
@@ -407,16 +411,23 @@ static void play_next_chunk(void)
assert(pthread_equal(pthread_self(), ob.thread));
nr = ringbuf_get_read_vector(ob.index, vec);
- if (mpd_unlikely(!nr &&
- (dc.state == DC_STATE_STOP) &&
- ! playlist_playing())) {
- stop_playback();
+ if (mpd_unlikely(!nr)) {
+ if (dc.state == DC_STATE_STOP && ! playlist_playing())
+ stop_playback();
+ else
+ prevent_buffer_underrun();
return;
}
- if (nr < ((ob.xfade_time <= 0) ? ob.nr_bpp : xfade_chunks_needed(vec)))
- {
+
+ if (ob.xfade_time <= 0 && nr < ob.bpp_cur) {
prevent_buffer_underrun();
return;
+ } else if (nr < xfade_chunks_needed(vec)) {
+ if (dc.state != DC_STATE_STOP && playlist_playing()) {
+ prevent_buffer_underrun();
+ return;
+ }
+ /* nearing end of last track, xfade to silence.. */
}
a = get_chunk(vec, 0);
@@ -424,8 +435,14 @@ static void play_next_chunk(void)
if (! a->len)
goto out;
- if (nr > 1 && ob.xfade_state == XFADE_ENABLED) {
- struct ob_chunk *b = get_chunk(vec, ob.xfade_max);
+ if (ob.xfade_state == XFADE_ENABLED) {
+ struct ob_chunk *b;
+ b = get_chunk(vec, ob.xfade_max);
+ if (!b) { /* xfade to silence */
+ b = &silence;
+ b->len = a->len;
+ b->seq = a->seq + 1;
+ }
xfade_mix(a, b);
}
@@ -444,6 +461,10 @@ static void play_next_chunk(void)
out:
ringbuf_read_advance(ob.index, 1);
+ /* we've played our first chunk, stop prebuffering */
+ if (mpd_unlikely(ob.bpp_cur))
+ ob.bpp_cur = 0;
+
/* unblock ob_send() if it was waiting on a full buffer */
dc_try_unhalt();
}
@@ -466,6 +487,7 @@ static void * ob_task(mpd_unused void *arg)
case OB_STATE_PAUSE:
case OB_STATE_SEEK:
assert(as != AS_DEFERRED);
+ ob.bpp_cur = ob.bpp_max; /* enable prebuffer */
if (as == AS_INPROGRESS)
ob_finalize_action();
cond_wait(&ob_halt_cond);
diff --git a/src/outputBuffer_config_init.h b/src/outputBuffer_config_init.h
index ba5b7f137..7461c4d73 100644
--- a/src/outputBuffer_config_init.h
+++ b/src/outputBuffer_config_init.h
@@ -59,7 +59,7 @@ void config_output_buffer(void)
buffered_before_play = (perc / 100) * buffered_chunks;
if (buffered_before_play > buffered_chunks)
buffered_before_play = buffered_chunks;
- ob.nr_bpp = buffered_before_play;
+ ob.bpp_max = buffered_before_play;
assert(buffered_chunks > 0 && !ob.index && !ob.chunks);
ob.index = ringbuf_create(buffered_chunks);
diff --git a/src/outputBuffer_xfade.h b/src/outputBuffer_xfade.h
index 0f3a4d5a7..80efe704e 100644
--- a/src/outputBuffer_xfade.h
+++ b/src/outputBuffer_xfade.h
@@ -18,7 +18,7 @@ static size_t calculate_xfade_chunks(struct iovec vec[2])
if (!ob.total_time ||
(ob.elapsed_time + ob.xfade_time) < ob.total_time ||
!isCurrentAudioFormat(af))
- return ob.nr_bpp; /* too early, don't enable xfade yet */
+ return ob.bpp_cur; /* too early, don't enable xfade yet */
assert(af->bits > 0);
assert(af->channels > 0);
@@ -26,35 +26,30 @@ static size_t calculate_xfade_chunks(struct iovec vec[2])
chunks = af->sampleRate * af->bits * af->channels / 8.0 / CHUNK_SIZE;
chunks = chunks * (ob.xfade_time + 0.5);
+ assert(chunks);
- assert(ob.index->size >= ob.nr_bpp);
- if (chunks > (ob.index->size - ob.nr_bpp))
- chunks = ob.index->size - ob.nr_bpp;
+ assert(ob.index->size >= ob.bpp_cur);
+ if (chunks > (ob.index->size - ob.bpp_cur))
+ chunks = ob.index->size - ob.bpp_cur;
DEBUG("calculated xfade chunks: %d\n", chunks);
nr = vec[0].iov_len + vec[1].iov_len;
- if (chunks > nr)
- return chunks; /* not enough work with */
-
- c = get_chunk(vec, chunks);
- assert(c);
- if (c->seq == ob.seq_player) {
- do {
- if (!(c = get_chunk(vec, ++chunks)))
- return chunks; /* not enough to work with */
- } while (c->seq == ob.seq_player);
- } else {
- do {
- c = get_chunk(vec, --chunks);
- assert(c);
- } while (c->seq == ob.seq_decoder);
- assert((c = get_chunk(vec, chunks)));
- assert(c->seq != ob.seq_decoder);
- ++chunks;
- assert((c = get_chunk(vec, chunks)));
- assert(c->seq == ob.seq_decoder);
+ if (chunks <= nr) {
+ c = get_chunk(vec, chunks);
+ assert(c);
+ if (c->seq != ob.seq_player) {
+ do {
+ c = get_chunk(vec, --chunks);
+ assert(c);
+ } while (chunks && c->seq == ob.seq_decoder);
+ assert((c = get_chunk(vec, chunks)));
+ assert(c->seq != ob.seq_decoder);
+ ++chunks;
+ assert((c = get_chunk(vec, chunks)));
+ assert(c->seq == ob.seq_decoder);
+ }
+ DEBUG("adjusted xfade chunks: %d\n", chunks);
}
- DEBUG("adjusted xfade chunks: %d\n", chunks);
ob.xfade_cur = chunks;
ob.xfade_max = chunks;