From 967af603270246f1c97e11b8bad6a0eb65c81318 Mon Sep 17 00:00:00 2001 From: Stefano Miccoli Date: Tue, 12 Dec 2017 23:42:42 +0100 Subject: [PATCH 1/4] =?UTF-8?q?rounds=20alsa=20HW=20mixer=20volume=20towar?= =?UTF-8?q?ds=20=C2=B1=E2=88=9E=20depending=20on=20sgn(=E2=88=86=20vol)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This alleviates a problem in which 'volume +1' cannot be undo by 'volume -1' when using alsa hw mixer. Closes #104 --- NEWS | 2 ++ src/mixer/plugins/AlsaMixerPlugin.cxx | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 6f72320dc..5b7c83cfb 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,8 @@ ver 0.20.13 (not yet released) * output - osx: set up ring buffer to hold at least 100ms +* mixer + - alsa: fix rounding errors * database - simple: don't purge mount points on update/rescan - simple: fix "mount" bug caused by bad compiler optimization diff --git a/src/mixer/plugins/AlsaMixerPlugin.cxx b/src/mixer/plugins/AlsaMixerPlugin.cxx index 8012ca2fb..c5ef0dcef 100644 --- a/src/mixer/plugins/AlsaMixerPlugin.cxx +++ b/src/mixer/plugins/AlsaMixerPlugin.cxx @@ -292,7 +292,9 @@ AlsaMixer::SetVolume(unsigned volume) { assert(handle != nullptr); - int err = set_normalized_playback_volume(elem, 0.01*volume, 1); + double cur = get_normalized_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT); + int delta = volume - lrint(100.*cur); + int err = set_normalized_playback_volume(elem, cur + 0.01*delta, delta); if (err < 0) throw FormatRuntimeError("failed to set ALSA volume: %s", snd_strerror(err)); From c488d3123f33fe28525c2b8b229aaae2f9d4dc06 Mon Sep 17 00:00:00 2001 From: FlashSystems Date: Sat, 2 Dec 2017 19:39:17 +0100 Subject: [PATCH 2/4] Fix `lsinfo` and `add` for mounted databases. If `SimpleDatabase::Visit` is called on a database that contains a mounted directry the URIs of the elements passed to the callbacks are not prefixed by the mountpoint path. This leads to lsinfo and add not working because they use the wrong URI. This pull request is using the `WalkMount` helper function to create prefixed versions of `VisitDirectory`, `VisitSong` and `VisitPlaylist` to add the correct prefix to the parameters of the callback functions. --- NEWS | 1 + src/db/plugins/simple/Directory.cxx | 2 +- src/db/plugins/simple/Mount.cxx | 4 ++-- src/db/plugins/simple/Mount.hxx | 2 +- src/db/plugins/simple/SimpleDatabasePlugin.cxx | 13 +++++++++++++ 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 5b7c83cfb..c9d376f21 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ ver 0.20.13 (not yet released) * database - simple: don't purge mount points on update/rescan - simple: fix "mount" bug caused by bad compiler optimization + - simple: fix "lsinfo" into mount points - upnp: work around libupnp 1.6.24 API breakage * queue: fix spuriously misplaced prioritized songs * include Windows cross-build script in source tarball diff --git a/src/db/plugins/simple/Directory.cxx b/src/db/plugins/simple/Directory.cxx index 30b044b1f..a1beb344f 100644 --- a/src/db/plugins/simple/Directory.cxx +++ b/src/db/plugins/simple/Directory.cxx @@ -230,7 +230,7 @@ Directory::Walk(bool recursive, const SongFilter *filter, call will lock it again */ const ScopeDatabaseUnlock unlock; WalkMount(GetPath(), *mounted_database, - recursive, filter, + "", recursive, filter, visit_directory, visit_song, visit_playlist); return; diff --git a/src/db/plugins/simple/Mount.cxx b/src/db/plugins/simple/Mount.cxx index bf3a4b815..c07b67abc 100644 --- a/src/db/plugins/simple/Mount.cxx +++ b/src/db/plugins/simple/Mount.cxx @@ -72,7 +72,7 @@ PrefixVisitPlaylist(const char *base, const VisitPlaylist &visit_playlist, void WalkMount(const char *base, const Database &db, - bool recursive, const SongFilter *filter, + const char* uri, bool recursive, const SongFilter *filter, const VisitDirectory &visit_directory, const VisitSong &visit_song, const VisitPlaylist &visit_playlist) { @@ -93,5 +93,5 @@ WalkMount(const char *base, const Database &db, vp = std::bind(PrefixVisitPlaylist, base, std::ref(visit_playlist), _1, _2); - db.Visit(DatabaseSelection("", recursive, filter), vd, vs, vp); + db.Visit(DatabaseSelection(uri, recursive, filter), vd, vs, vp); } diff --git a/src/db/plugins/simple/Mount.hxx b/src/db/plugins/simple/Mount.hxx index 0c6abd1d1..c79e52354 100644 --- a/src/db/plugins/simple/Mount.hxx +++ b/src/db/plugins/simple/Mount.hxx @@ -27,7 +27,7 @@ class SongFilter; void WalkMount(const char *base, const Database &db, - bool recursive, const SongFilter *filter, + const char* uri, bool recursive, const SongFilter *filter, const VisitDirectory &visit_directory, const VisitSong &visit_song, const VisitPlaylist &visit_playlist); diff --git a/src/db/plugins/simple/SimpleDatabasePlugin.cxx b/src/db/plugins/simple/SimpleDatabasePlugin.cxx index 9f06105f5..a27c070c6 100644 --- a/src/db/plugins/simple/SimpleDatabasePlugin.cxx +++ b/src/db/plugins/simple/SimpleDatabasePlugin.cxx @@ -20,6 +20,7 @@ #include "config.h" #include "SimpleDatabasePlugin.hxx" #include "PrefixedLightSong.hxx" +#include "Mount.hxx" #include "db/DatabasePlugin.hxx" #include "db/Selection.hxx" #include "db/Helpers.hxx" @@ -270,6 +271,18 @@ SimpleDatabase::Visit(const DatabaseSelection &selection, ScopeDatabaseLock protect; auto r = root->LookupDirectory(selection.uri.c_str()); + + if (r.directory->IsMount()) { + /* pass the request and the remaining uri to the mounted database */ + protect.unlock(); + + WalkMount(r.directory->GetPath(), *(r.directory->mounted_database), + (r.uri == nullptr)?"":r.uri, selection.recursive, selection.filter, + visit_directory, visit_song, visit_playlist); + + return; + } + if (r.uri == nullptr) { /* it's a directory */ From 64d141f71e690a4258ba9ee8712140bc9d961883 Mon Sep 17 00:00:00 2001 From: FlashSystems Date: Sat, 25 Nov 2017 11:20:32 +0100 Subject: [PATCH 3/4] Save and restore mountpoints within the state file. Signed-off-by: FlashSystems --- Makefile.am | 1 + NEWS | 1 + src/StateFile.cxx | 19 ++++- src/StateFile.hxx | 4 + src/storage/StorageState.cxx | 138 +++++++++++++++++++++++++++++++++++ src/storage/StorageState.hxx | 46 ++++++++++++ 6 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 src/storage/StorageState.cxx create mode 100644 src/storage/StorageState.hxx diff --git a/Makefile.am b/Makefile.am index ecc398d63..396bc0c37 100644 --- a/Makefile.am +++ b/Makefile.am @@ -702,6 +702,7 @@ libstorage_a_SOURCES = \ src/storage/MemoryDirectoryReader.cxx src/storage/MemoryDirectoryReader.hxx \ src/storage/Configured.cxx src/storage/Configured.hxx \ src/storage/plugins/LocalStorage.cxx src/storage/plugins/LocalStorage.hxx \ + src/storage/StorageState.cxx src/storage/StorageState.hxx \ src/storage/FileInfo.hxx libstorage_a_CPPFLAGS = $(AM_CPPFLAGS) \ diff --git a/NEWS b/NEWS index c9d376f21..294cf7502 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ ver 0.20.13 (not yet released) - simple: fix "lsinfo" into mount points - upnp: work around libupnp 1.6.24 API breakage * queue: fix spuriously misplaced prioritized songs +* save and restore mountpoints within the state file * include Windows cross-build script in source tarball * fix Windows build failures diff --git a/src/StateFile.cxx b/src/StateFile.cxx index cc02d0ff7..e8cf7d11a 100644 --- a/src/StateFile.cxx +++ b/src/StateFile.cxx @@ -24,6 +24,7 @@ #include "fs/io/TextFile.hxx" #include "fs/io/FileOutputStream.hxx" #include "fs/io/BufferedOutputStream.hxx" +#include "storage/StorageState.hxx" #include "Partition.hxx" #include "Instance.hxx" #include "mixer/Volume.hxx" @@ -56,6 +57,9 @@ StateFile::RememberVersions() noexcept prev_output_version = audio_output_state_get_version(); prev_playlist_version = playlist_state_get_hash(partition.playlist, partition.pc); +#ifdef ENABLE_DATABASE + prev_storage_version = storage_state_get_hash(partition.instance); +#endif } bool @@ -64,7 +68,11 @@ StateFile::IsModified() const noexcept return prev_volume_version != sw_volume_state_get_hash() || prev_output_version != audio_output_state_get_version() || prev_playlist_version != playlist_state_get_hash(partition.playlist, - partition.pc); + partition.pc) +#ifdef ENABLE_DATABASE + || prev_storage_version != storage_state_get_hash(partition.instance) +#endif + ; } inline void @@ -72,6 +80,11 @@ StateFile::Write(BufferedOutputStream &os) { save_sw_volume_state(os); audio_output_state_save(os, partition.outputs); + +#ifdef ENABLE_DATABASE + storage_state_save(os, partition.instance); +#endif + playlist_state_save(os, partition.playlist, partition.pc); } @@ -123,6 +136,10 @@ try { playlist_state_restore(line, file, song_loader, partition.playlist, partition.pc); +#ifdef ENABLE_DATABASE + success = success || storage_state_restore(line, file, partition.instance); +#endif + if (!success) FormatError(state_file_domain, "Unrecognized line in state file: %s", diff --git a/src/StateFile.hxx b/src/StateFile.hxx index 342a20c22..b10a7e040 100644 --- a/src/StateFile.hxx +++ b/src/StateFile.hxx @@ -46,6 +46,10 @@ class StateFile final : private TimeoutMonitor { unsigned prev_volume_version = 0, prev_output_version = 0, prev_playlist_version = 0; +#ifdef ENABLE_DATABASE + unsigned prev_storage_version = 0; +#endif + public: static constexpr std::chrono::steady_clock::duration DEFAULT_INTERVAL = std::chrono::minutes(2); diff --git a/src/storage/StorageState.cxx b/src/storage/StorageState.cxx new file mode 100644 index 000000000..beeb4eebb --- /dev/null +++ b/src/storage/StorageState.cxx @@ -0,0 +1,138 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * Save and load mounts of the compound storage to/from the state file. + * + */ + +#include "config.h" +#include "StorageState.hxx" +#include "fs/io/TextFile.hxx" +#include "fs/io/BufferedOutputStream.hxx" +#include "storage/Registry.hxx" +#include "storage/CompositeStorage.hxx" +#include "db/plugins/simple/SimpleDatabasePlugin.hxx" +#include "util/StringCompare.hxx" +#include "util/Domain.hxx" +#include "Instance.hxx" +#include "IOThread.hxx" +#include "Log.hxx" + +#include +#include + +#define MOUNT_STATE_BEGIN "mount_begin" +#define MOUNT_STATE_END "mount_end" +#define MOUNT_STATE_STORAGE_URI "uri: " +#define MOUNT_STATE_MOUNTED_URL "mounted_url: " + +static constexpr Domain storage_domain("storage"); + +void +storage_state_save(BufferedOutputStream &os, const Instance &instance) +{ + const auto visitor = [&os](const char *mount_uri, const Storage &storage) { + std::string uri = storage.MapUTF8(""); + if (uri.empty() || StringIsEmpty(mount_uri)) + return; + + os.Format( + MOUNT_STATE_BEGIN "\n" + MOUNT_STATE_STORAGE_URI "%s\n" + MOUNT_STATE_MOUNTED_URL "%s\n" + MOUNT_STATE_END "\n", mount_uri, uri.c_str()); + }; + + ((CompositeStorage*)instance.storage)->VisitMounts(visitor); +} + +bool +storage_state_restore(const char *line, TextFile &file, Instance &instance) +{ + if (!StringStartsWith(line, MOUNT_STATE_BEGIN)) + return false; + + std::string url; + std::string uri; + const char* value; + + while ((line = file.ReadLine()) != nullptr) { + if (StringStartsWith(line, MOUNT_STATE_END)) + break; + + if ((value = StringAfterPrefix(line, MOUNT_STATE_MOUNTED_URL))) + url = value; + else if ((value = StringAfterPrefix(line, MOUNT_STATE_STORAGE_URI))) + uri = value; + else + FormatError(storage_domain, "Unrecognized line in mountpoint state: %s", line); + } + + if (url.empty() || uri.empty()) { + LogError(storage_domain, "Missing value in mountpoint state."); + return true; + } + + FormatDebug(storage_domain, "Restoring mount %s => %s", uri.c_str(), url.c_str()); + + auto &event_loop = io_thread_get(); + Storage *storage = CreateStorageURI(event_loop, url.c_str()); + if (storage == nullptr) { + FormatError(storage_domain, "Unrecognized storage URI: %s", url.c_str()); + return true; + } + +#ifdef ENABLE_DATABASE + Database *db = instance.database; + if (db != nullptr && db->IsPlugin(simple_db_plugin)) { + try { + ((SimpleDatabase *)db)->Mount(uri.c_str(), url.c_str()); + } catch (...) { + throw; + } + } +#endif + + ((CompositeStorage*)instance.storage)->Mount(uri.c_str(), storage); + + return true; +} + +unsigned +storage_state_get_hash(const Instance &instance) +{ + std::list mounts; + + const auto visitor = [&mounts](const char *mount_uri, const Storage &storage) { + mounts.push_back(std::string(mount_uri) + ":" + storage.MapUTF8("")); + }; + + ((CompositeStorage*)instance.storage)->VisitMounts(visitor); + + mounts.sort(); + + boost::crc_32_type result; + + for (auto mount: mounts) { + result.process_bytes(mount.c_str(), mount.length()); + } + + return result.checksum(); +} diff --git a/src/storage/StorageState.hxx b/src/storage/StorageState.hxx new file mode 100644 index 000000000..659cc9868 --- /dev/null +++ b/src/storage/StorageState.hxx @@ -0,0 +1,46 @@ +/* + * Copyright 2003-2017 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * Saving and loading the playlist to/from the state file. + * + */ + +#ifndef MPD_STORAGE_STATE_HXX +#define MPD_STORAGE_STATE_HXX + +struct Instance; +class BufferedOutputStream; +class TextFile; + +void +storage_state_save(BufferedOutputStream &os, const Instance &instance); + +bool +storage_state_restore(const char *line, TextFile &file, Instance &instance); + +/** + * Generates a hash number for the current state of the composite storage. + * This is used by timer_save_state_file() to determine whether the state + * has changed and the state file should be saved. + */ +unsigned +storage_state_get_hash(const Instance &instance); + +#endif From 49b9a90c3f63639a02bc4817a869a2684d4383c0 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 18 Dec 2017 23:41:56 +0100 Subject: [PATCH 4/4] release v0.20.13 --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 294cf7502..4f73e39a4 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -ver 0.20.13 (not yet released) +ver 0.20.13 (2017/12/18) * output - osx: set up ring buffer to hold at least 100ms * mixer