diff --git a/NEWS b/NEWS index fcd5f4e02..4dd243f36 100644 --- a/NEWS +++ b/NEWS @@ -13,12 +13,16 @@ ver 0.21 (not yet released) * mixer - sndio: new mixer plugin -ver 0.20.11 (not yet released) +ver 0.20.11 (2017/10/18) * storage - curl: support Content-Type application/xml * decoder - ffmpeg: more reliable song duration + - gme: fix track numbering +* improve random song order when switching songs manually * fix case insensitive search without libicu +* fix Unicode file names in playlists on Windows +* fix endless loop when accessing malformed file names in ZIP files ver 0.20.10 (2017/08/24) * decoder diff --git a/src/PlaylistFile.cxx b/src/PlaylistFile.cxx index d9846044e..a748efb07 100644 --- a/src/PlaylistFile.cxx +++ b/src/PlaylistFile.cxx @@ -207,13 +207,12 @@ try { continue; #ifdef _UNICODE - wchar_t buffer[MAX_PATH]; - auto result = MultiByteToWideChar(CP_ACP, 0, s, -1, - buffer, ARRAY_SIZE(buffer)); - if (result <= 0) + /* on Windows, playlists always contain UTF-8, because + its "narrow" charset (i.e. CP_ACP) is incapable of + storing all Unicode paths */ + const auto path = AllocatedPath::FromUTF8(s); + if (path.IsNull()) continue; - - const Path path = Path::FromFS(buffer); #else const Path path = Path::FromFS(s); #endif diff --git a/src/PlaylistSave.cxx b/src/PlaylistSave.cxx index e64daba8f..d3ec30bb2 100644 --- a/src/PlaylistSave.cxx +++ b/src/PlaylistSave.cxx @@ -28,13 +28,25 @@ #include "fs/AllocatedPath.hxx" #include "fs/Traits.hxx" #include "fs/FileSystem.hxx" -#include "fs/NarrowPath.hxx" #include "fs/io/FileOutputStream.hxx" #include "fs/io/BufferedOutputStream.hxx" #include "util/UriUtil.hxx" #include +static void +playlist_print_path(BufferedOutputStream &os, const Path path) +{ +#ifdef _UNICODE + /* on Windows, playlists always contain UTF-8, because its + "narrow" charset (i.e. CP_ACP) is incapable of storing all + Unicode paths */ + os.Format("%s\n", path.ToUTF8().c_str()); +#else + os.Format("%s\n", path.c_str()); +#endif +} + void playlist_print_song(BufferedOutputStream &os, const DetachedSong &song) { @@ -44,7 +56,7 @@ playlist_print_song(BufferedOutputStream &os, const DetachedSong &song) try { const auto uri_fs = AllocatedPath::FromUTF8Throw(uri_utf8); - os.Format("%s\n", NarrowPath(uri_fs).c_str()); + playlist_print_path(os, uri_fs); } catch (const std::runtime_error &) { } } @@ -63,7 +75,7 @@ playlist_print_uri(BufferedOutputStream &os, const char *uri) AllocatedPath::FromUTF8Throw(uri); if (!path.IsNull()) - os.Format("%s\n", NarrowPath(path).c_str()); + playlist_print_path(os, path); } catch (const std::runtime_error &) { } } diff --git a/src/decoder/DecoderThread.cxx b/src/decoder/DecoderThread.cxx index e362e19e0..50dace3cc 100644 --- a/src/decoder/DecoderThread.cxx +++ b/src/decoder/DecoderThread.cxx @@ -508,6 +508,7 @@ try { decoder_run_song(dc, song, uri_utf8, path_fs); } catch (...) { dc.state = DecoderState::ERROR; + dc.command = DecoderCommand::NONE; dc.error = std::current_exception(); dc.client_cond.signal(); } diff --git a/src/queue/Playlist.cxx b/src/queue/Playlist.cxx index 667e3457f..dfeb0d163 100644 --- a/src/queue/Playlist.cxx +++ b/src/queue/Playlist.cxx @@ -310,8 +310,7 @@ playlist::SetRandom(PlayerControl &pc, bool status) playlist is played after that */ unsigned current_order = queue.PositionToOrder(current_position); - queue.MoveOrder(current_order, 0); - current = 0; + current = queue.MoveOrder(current_order, 0); } else current = -1; } else diff --git a/src/queue/Playlist.hxx b/src/queue/Playlist.hxx index 32489e354..c04a0913a 100644 --- a/src/queue/Playlist.hxx +++ b/src/queue/Playlist.hxx @@ -356,6 +356,18 @@ public: } void SetConsume(bool new_value); + +private: + /** + * Prepare a manual song change: move the given song to the + * current playback order. This is done to avoid skipping + * upcoming songs in the order list. The newly selected song + * shall be inserted in the order list, and the rest shall be + * played after that as previously planned. + * + * @return the new order number of the given song + */ + unsigned MoveOrderToCurrent(unsigned old_order); }; #endif diff --git a/src/queue/PlaylistControl.cxx b/src/queue/PlaylistControl.cxx index 99012aedf..4b17386ae 100644 --- a/src/queue/PlaylistControl.cxx +++ b/src/queue/PlaylistControl.cxx @@ -56,6 +56,30 @@ playlist::Stop(PlayerControl &pc) } } +unsigned +playlist::MoveOrderToCurrent(unsigned old_order) +{ + if (!queue.random) + /* no-op because there is no order list */ + return old_order; + + if (playing) { + /* already playing: move the specified song after the + current one (because the current one has already + been playing and shall not be played again) */ + return queue.MoveOrderAfter(old_order, current); + } else if (current >= 0) { + /* not playing: move the specified song before the + current one, so it will be played eventually */ + return queue.MoveOrderBefore(old_order, current); + } else { + /* not playing anything: move the specified song to + the front */ + queue.SwapOrders(old_order, 0); + return 0; + } +} + void playlist::PlayPosition(PlayerControl &pc, int song) { @@ -90,13 +114,7 @@ playlist::PlayPosition(PlayerControl &pc, int song) number, because random mode is enabled */ i = queue.PositionToOrder(song); - if (!playing) - current = 0; - - /* swap the new song with the previous "current" one, - so playback continues as planned */ - queue.SwapOrders(i, current); - i = current; + i = MoveOrderToCurrent(i); } stop_on_error = false; @@ -205,6 +223,8 @@ playlist::SeekSongOrder(PlayerControl &pc, unsigned i, SongTime seek_time) /* seeking is not within the current song - prepare song change */ + i = MoveOrderToCurrent(i); + playing = true; current = i; diff --git a/src/queue/Queue.cxx b/src/queue/Queue.cxx index d248f306b..720778099 100644 --- a/src/queue/Queue.cxx +++ b/src/queue/Queue.cxx @@ -195,7 +195,7 @@ Queue::MoveRange(unsigned start, unsigned end, unsigned to) noexcept } } -void +unsigned Queue::MoveOrder(unsigned from_order, unsigned to_order) noexcept { assert(from_order < length); @@ -212,6 +212,25 @@ Queue::MoveOrder(unsigned from_order, unsigned to_order) noexcept } order[to_order] = from_position; + return to_order; +} + +unsigned +Queue::MoveOrderBefore(unsigned from_order, unsigned to_order) noexcept +{ + /* if "from_order" comes before "to_order", then the new + position is "to_order-1"; otherwise the "to_order" song is + moved one ahead */ + return MoveOrder(from_order, to_order - (from_order < to_order)); +} + +unsigned +Queue::MoveOrderAfter(unsigned from_order, unsigned to_order) noexcept +{ + /* if "from_order" comes after "to_order", then the new + position is "to_order+1"; otherwise the "to_order" song is + moved one back */ + return MoveOrder(from_order, to_order + (from_order > to_order)); } void diff --git a/src/queue/Queue.hxx b/src/queue/Queue.hxx index bd2f95c1a..06e800479 100644 --- a/src/queue/Queue.hxx +++ b/src/queue/Queue.hxx @@ -284,8 +284,28 @@ struct Queue { /** * Moves a song to a new position in the "order" list. + * + * @return to_order */ - void MoveOrder(unsigned from_order, unsigned to_order) noexcept; + unsigned MoveOrder(unsigned from_order, unsigned to_order) noexcept; + + /** + * Moves a song to a new position in the "order" list before + * the given one. + * + * @return the new order number of the given "from" song + */ + unsigned MoveOrderBefore(unsigned from_order, + unsigned to_order) noexcept; + + /** + * Moves a song to a new position in the "order" list after + * the given one. + * + * @return the new order number of the given "from" song + */ + unsigned MoveOrderAfter(unsigned from_order, + unsigned to_order) noexcept; /** * Moves a song to a new position.