Mechanism for case-sensitivity on Unix-style OS, integration code by @paulfd

This commit is contained in:
Jean Pierre Cimalando 2020-01-26 20:38:59 +01:00
parent fe6cc016d7
commit 06755812db
5 changed files with 100 additions and 8 deletions

View file

@ -31,6 +31,7 @@
#include "Oversampler.h"
#include "AtomicGuard.h"
#include "absl/types/span.h"
#include "absl/strings/match.h"
#include <memory>
#include <sndfile.hh>
#include <thread>
@ -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::FileInformation> 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::FileInformation> 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;

View file

@ -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.
*

View file

@ -224,17 +224,30 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
auto currentRegion = regions.begin();
auto lastRegion = regions.rbegin();
auto removeCurrentRegion = [&currentRegion, &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++) {

View file

@ -483,3 +483,13 @@ TEST_CASE("[Files] Looped regions taken from files and possibly overriden")
REQUIRE( synth.getRegionView(1)->loopRange == sfz::Range<uint32_t>{ 77554, 186582 } );
REQUIRE( synth.getRegionView(2)->loopRange == sfz::Range<uint32_t>{ 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");
}

View file

@ -0,0 +1,3 @@
<region> key=61 sample=Dummy1.wav
<region> key=61 sample=REGIONS/Dummy.wav
<region> key=61 sample=REGIonS\Dummy.wav