From 06755812db1b26de952a6da46c2670a38f040242 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 26 Jan 2020 20:38:59 +0100 Subject: [PATCH] Mechanism for case-sensitivity on Unix-style OS, integration code by @paulfd --- src/sfizz/FilePool.cpp | 60 +++++++++++++++++++++++++++- src/sfizz/FilePool.h | 9 +++++ src/sfizz/Synth.cpp | 26 +++++++++--- tests/FilesT.cpp | 10 +++++ tests/TestFiles/case_insensitive.sfz | 3 ++ 5 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 tests/TestFiles/case_insensitive.sfz diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index 772bd3ff..14bcd011 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -31,6 +31,7 @@ #include "Oversampler.h" #include "AtomicGuard.h" #include "absl/types/span.h" +#include "absl/strings/match.h" #include #include #include @@ -105,11 +106,65 @@ sfz::FilePool::~FilePool() thread.join(); } +bool sfz::FilePool::checkSample(std::string& filename) const noexcept +{ + fs::path path { rootDirectory / filename }; + std::error_code ec; + if (fs::exists(path, ec)) + return true; + + fs::path oldPath = std::move(path); + path = oldPath.root_path(); + + static const fs::path dot { "." }; + static const fs::path dotdot { ".." }; + + for (const fs::path &part : oldPath.relative_path()) { + if (part == dot || part == dotdot) { + path /= part; + continue; + } + + if (fs::exists(path / part, ec)) { + path /= part; + continue; + } + + auto it = path.empty() ? fs::directory_iterator{ dot, ec } : fs::directory_iterator{ path, ec }; + if (ec) { + DBG("Error creating a directory iterator for " << filename << " (Error code: " << ec.message() << ")"); + return false; + } + + auto searchPredicate = [&part](const fs::directory_entry &ent) -> bool { + return absl::EqualsIgnoreCase( + ent.path().filename().native(), part.native()); + }; + + while (it != fs::directory_iterator{} && !searchPredicate(*it)) + it.increment(ec); + + if (it == fs::directory_iterator{}) { + DBG("File not found, could not resolve " << filename); + return false; + } + + path /= it->path().filename(); + } + + const auto newPath = fs::relative(path, rootDirectory, ec); + if (ec) { + DBG("Error extracting the new relative path for " << filename << " (Error code: " << ec.message() << ")"); + return false; + } + DBG("Updating " << filename << " to " << newPath.native()); + filename = newPath.string(); + return true; +} + absl::optional sfz::FilePool::getFileInformation(const std::string& filename) noexcept { fs::path file { rootDirectory / filename }; - if (!fs::exists(file)) - return {}; SndfileHandle sndFile(file.string().c_str()); if (sndFile.channels() != 1 && sndFile.channels() != 2) { @@ -135,6 +190,7 @@ absl::optional sfz::FilePool::getFileInformation bool sfz::FilePool::preloadFile(const std::string& filename, uint32_t maxOffset) noexcept { fs::path file { rootDirectory / filename }; + if (!fs::exists(file)) return false; diff --git a/src/sfizz/FilePool.h b/src/sfizz/FilePool.h index 9394918b..cba35cb7 100644 --- a/src/sfizz/FilePool.h +++ b/src/sfizz/FilePool.h @@ -157,6 +157,15 @@ public: */ bool preloadFile(const std::string& filename, uint32_t maxOffset) noexcept; + /** + * @brief Check that the sample exists. If not, try to find it in a case insensitive way. + * + * @param filename the sample filename; may be updated by the method + * @return true if the sample exists or was updated properly + * @return false if no sample was found even with a case insensitive search + */ + bool checkSample(std::string& filename) const noexcept; + /** * @brief Clear all preloaded files. * diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 3b6fa500..7743e103 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -224,17 +224,30 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) auto currentRegion = regions.begin(); auto lastRegion = regions.rbegin(); + auto removeCurrentRegion = [¤tRegion, &lastRegion]() { + if (currentRegion->get() == nullptr) + return; + + DBG("Removing the region with sample " << currentRegion->get()->sample); + std::iter_swap(currentRegion, lastRegion); + ++lastRegion; + }; + while (currentRegion < lastRegion.base()) { auto region = currentRegion->get(); if (!region->isGenerator()) { - auto fileInformation = resources.filePool.getFileInformation(region->sample); - if (!fileInformation) { - DBG("Removing the region with sample " << region->sample); - std::iter_swap(currentRegion, lastRegion); - ++lastRegion; + if (!resources.filePool.checkSample(region->sample)) { + removeCurrentRegion(); continue; } + + const auto fileInformation = resources.filePool.getFileInformation(region->sample); + if (!fileInformation) { + removeCurrentRegion(); + continue; + } + region->sampleEnd = std::min(region->sampleEnd, fileInformation->end); if (fileInformation->loopBegin != Default::loopRange.getStart() && @@ -254,7 +267,8 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) // TODO: adjust with LFO targets const auto maxOffset { region->offset + region->offsetRandom }; - resources.filePool.preloadFile(region->sample, maxOffset); + if (!resources.filePool.preloadFile(region->sample, maxOffset)) + removeCurrentRegion(); } for (auto note = 0; note < 128; note++) { diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 915f1cca..c37fee04 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -483,3 +483,13 @@ TEST_CASE("[Files] Looped regions taken from files and possibly overriden") REQUIRE( synth.getRegionView(1)->loopRange == sfz::Range{ 77554, 186582 } ); REQUIRE( synth.getRegionView(2)->loopRange == sfz::Range{ 4, 124 } ); } + +TEST_CASE("[Files] Case sentitiveness") +{ + sfz::Synth synth; + synth.loadSfzFile(fs::current_path() / "tests/TestFiles/case_insensitive.sfz"); + REQUIRE(synth.getNumRegions() == 3); + REQUIRE(synth.getRegionView(0)->sample == "dummy1.wav"); + REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.wav"); + REQUIRE(synth.getRegionView(2)->sample == "Regions/dummy.wav"); +} diff --git a/tests/TestFiles/case_insensitive.sfz b/tests/TestFiles/case_insensitive.sfz new file mode 100644 index 00000000..f704ff3c --- /dev/null +++ b/tests/TestFiles/case_insensitive.sfz @@ -0,0 +1,3 @@ + key=61 sample=Dummy1.wav + key=61 sample=REGIONS/Dummy.wav + key=61 sample=REGIonS\Dummy.wav