diff --git a/NEWS b/NEWS
index 4f9c7da02..fb513cd79 100644
--- a/NEWS
+++ b/NEWS
@@ -68,10 +68,16 @@ ver 0.19 (not yet released)
* install systemd unit for socket activation
* Android port
-ver 0.18.13 (not yet released)
+ver 0.18.13 (2014/08/31)
+* protocol
+ - don't change song on "seekcur" in random mode
+
* decoder
- dsdiff, dsf: fix endless loop on malformed file
- ffmpeg: support ffmpeg/libav version 11
+ - gme: fix song duration
+* output
+ - alsa: fix endless loop at end of file in dsd_usb mode
* fix state file saver
* fix build failure on Darwin
diff --git a/doc/protocol.xml b/doc/protocol.xml
index af2a312d2..8e114dfbd 100644
--- a/doc/protocol.xml
+++ b/doc/protocol.xml
@@ -576,7 +576,12 @@
- songs: number of albums
+ albums: number of albums
+
+
+
+
+ songs: number of songs
diff --git a/src/output/plugins/AlsaOutputPlugin.cxx b/src/output/plugins/AlsaOutputPlugin.cxx
index a18cedc70..0ebf3ddbe 100644
--- a/src/output/plugins/AlsaOutputPlugin.cxx
+++ b/src/output/plugins/AlsaOutputPlugin.cxx
@@ -825,6 +825,7 @@ alsa_play(AudioOutput *ao, const void *chunk, size_t size,
{
AlsaOutput *ad = (AlsaOutput *)ao;
+ assert(size > 0);
assert(size % ad->in_frame_size == 0);
if (ad->must_prepare) {
@@ -838,12 +839,22 @@ alsa_play(AudioOutput *ao, const void *chunk, size_t size,
}
const auto e = ad->pcm_export->Export({chunk, size});
+ if (e.size == 0)
+ /* the DoP (DSD over PCM) filter converts two frames
+ at a time and ignores the last odd frame; if there
+ was only one frame (e.g. the last frame in the
+ file), the result is empty; to avoid an endless
+ loop, bail out here, and pretend the one frame has
+ been played */
+ return size;
+
chunk = e.data;
size = e.size;
assert(size % ad->out_frame_size == 0);
size /= ad->out_frame_size;
+ assert(size > 0);
while (true) {
snd_pcm_sframes_t ret = ad->writei(ad->pcm, chunk, size);
diff --git a/src/output/plugins/OssOutputPlugin.cxx b/src/output/plugins/OssOutputPlugin.cxx
index f2618491c..39d87fc35 100644
--- a/src/output/plugins/OssOutputPlugin.cxx
+++ b/src/output/plugins/OssOutputPlugin.cxx
@@ -724,6 +724,8 @@ oss_output_play(AudioOutput *ao, const void *chunk, size_t size,
OssOutput *od = (OssOutput *)ao;
ssize_t ret;
+ assert(size > 0);
+
/* reopen the device since it was closed by dropBufferedAudio */
if (od->fd < 0 && !oss_reopen(od, error))
return 0;
@@ -734,6 +736,8 @@ oss_output_play(AudioOutput *ao, const void *chunk, size_t size,
size = e.size;
#endif
+ assert(size > 0);
+
while (true) {
ret = write(od->fd, chunk, size);
if (ret > 0) {
diff --git a/src/queue/Playlist.hxx b/src/queue/Playlist.hxx
index 581d2a73b..ea19d9bba 100644
--- a/src/queue/Playlist.hxx
+++ b/src/queue/Playlist.hxx
@@ -251,6 +251,10 @@ public:
void PlayPrevious(PlayerControl &pc);
+ PlaylistResult SeekSongOrder(PlayerControl &pc,
+ unsigned song_order,
+ SongTime seek_time);
+
PlaylistResult SeekSongPosition(PlayerControl &pc,
unsigned song_position,
SongTime seek_time);
diff --git a/src/queue/PlaylistControl.cxx b/src/queue/PlaylistControl.cxx
index df6b6ed0f..f7e80dc46 100644
--- a/src/queue/PlaylistControl.cxx
+++ b/src/queue/PlaylistControl.cxx
@@ -190,18 +190,12 @@ playlist::PlayPrevious(PlayerControl &pc)
}
PlaylistResult
-playlist::SeekSongPosition(PlayerControl &pc,
- unsigned song, SongTime seek_time)
+playlist::SeekSongOrder(PlayerControl &pc, unsigned i, SongTime seek_time)
{
- if (!queue.IsValidPosition(song))
- return PlaylistResult::BAD_RANGE;
+ assert(queue.IsValidOrder(i));
const DetachedSong *queued_song = GetQueuedSong();
- unsigned i = queue.random
- ? queue.PositionToOrder(song)
- : song;
-
pc.ClearError();
stop_on_error = true;
error_count = 0;
@@ -228,6 +222,20 @@ playlist::SeekSongPosition(PlayerControl &pc,
return PlaylistResult::SUCCESS;
}
+PlaylistResult
+playlist::SeekSongPosition(PlayerControl &pc, unsigned song,
+ SongTime seek_time)
+{
+ if (!queue.IsValidPosition(song))
+ return PlaylistResult::BAD_RANGE;
+
+ unsigned i = queue.random
+ ? queue.PositionToOrder(song)
+ : song;
+
+ return SeekSongOrder(pc, i, seek_time);
+}
+
PlaylistResult
playlist::SeekSongId(PlayerControl &pc, unsigned id, SongTime seek_time)
{
@@ -257,5 +265,8 @@ playlist::SeekCurrent(PlayerControl &pc,
seek_time = SignedSongTime::zero();
}
- return SeekSongPosition(pc, current, SongTime(seek_time));
+ if (seek_time.IsNegative())
+ seek_time = SignedSongTime::zero();
+
+ return SeekSongOrder(pc, current, SongTime(seek_time));
}