Merge pull request #28 from jpcima/file-case
Mechanism for case-sensitivity on Unix-style OS, integration code by @paulfd
This commit is contained in:
commit
4be2ca3b1f
5 changed files with 107 additions and 8 deletions
|
|
@ -31,6 +31,7 @@
|
||||||
#include "Oversampler.h"
|
#include "Oversampler.h"
|
||||||
#include "AtomicGuard.h"
|
#include "AtomicGuard.h"
|
||||||
#include "absl/types/span.h"
|
#include "absl/types/span.h"
|
||||||
|
#include "absl/strings/match.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sndfile.hh>
|
#include <sndfile.hh>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
@ -105,11 +106,69 @@ sfz::FilePool::~FilePool()
|
||||||
thread.join();
|
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;
|
||||||
|
|
||||||
|
#if WIN32
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
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;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
absl::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(const std::string& filename) noexcept
|
absl::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(const std::string& filename) noexcept
|
||||||
{
|
{
|
||||||
fs::path file { rootDirectory / filename };
|
fs::path file { rootDirectory / filename };
|
||||||
if (!fs::exists(file))
|
|
||||||
return {};
|
|
||||||
|
|
||||||
SndfileHandle sndFile(file.string().c_str());
|
SndfileHandle sndFile(file.string().c_str());
|
||||||
if (sndFile.channels() != 1 && sndFile.channels() != 2) {
|
if (sndFile.channels() != 1 && sndFile.channels() != 2) {
|
||||||
|
|
@ -135,6 +194,7 @@ absl::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation
|
||||||
bool sfz::FilePool::preloadFile(const std::string& filename, uint32_t maxOffset) noexcept
|
bool sfz::FilePool::preloadFile(const std::string& filename, uint32_t maxOffset) noexcept
|
||||||
{
|
{
|
||||||
fs::path file { rootDirectory / filename };
|
fs::path file { rootDirectory / filename };
|
||||||
|
|
||||||
if (!fs::exists(file))
|
if (!fs::exists(file))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,15 @@ public:
|
||||||
*/
|
*/
|
||||||
bool preloadFile(const std::string& filename, uint32_t maxOffset) noexcept;
|
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.
|
* @brief Clear all preloaded files.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -224,17 +224,30 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
|
||||||
|
|
||||||
auto currentRegion = regions.begin();
|
auto currentRegion = regions.begin();
|
||||||
auto lastRegion = regions.rbegin();
|
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()) {
|
while (currentRegion < lastRegion.base()) {
|
||||||
auto region = currentRegion->get();
|
auto region = currentRegion->get();
|
||||||
|
|
||||||
if (!region->isGenerator()) {
|
if (!region->isGenerator()) {
|
||||||
auto fileInformation = resources.filePool.getFileInformation(region->sample);
|
if (!resources.filePool.checkSample(region->sample)) {
|
||||||
if (!fileInformation) {
|
removeCurrentRegion();
|
||||||
DBG("Removing the region with sample " << region->sample);
|
|
||||||
std::iter_swap(currentRegion, lastRegion);
|
|
||||||
++lastRegion;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto fileInformation = resources.filePool.getFileInformation(region->sample);
|
||||||
|
if (!fileInformation) {
|
||||||
|
removeCurrentRegion();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
region->sampleEnd = std::min(region->sampleEnd, fileInformation->end);
|
region->sampleEnd = std::min(region->sampleEnd, fileInformation->end);
|
||||||
|
|
||||||
if (fileInformation->loopBegin != Default::loopRange.getStart() &&
|
if (fileInformation->loopBegin != Default::loopRange.getStart() &&
|
||||||
|
|
@ -254,7 +267,8 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
|
||||||
|
|
||||||
// TODO: adjust with LFO targets
|
// TODO: adjust with LFO targets
|
||||||
const auto maxOffset { region->offset + region->offsetRandom };
|
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++) {
|
for (auto note = 0; note < 128; note++) {
|
||||||
|
|
|
||||||
|
|
@ -483,3 +483,14 @@ 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(1)->loopRange == sfz::Range<uint32_t>{ 77554, 186582 } );
|
||||||
REQUIRE( synth.getRegionView(2)->loopRange == sfz::Range<uint32_t>{ 4, 124 } );
|
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() == 4);
|
||||||
|
REQUIRE(synth.getRegionView(0)->sample == "dummy1.wav");
|
||||||
|
REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.wav");
|
||||||
|
REQUIRE(synth.getRegionView(2)->sample == "Regions/dummy.wav");
|
||||||
|
REQUIRE(synth.getRegionView(3)->sample == "Regions/dummy.wav");
|
||||||
|
}
|
||||||
|
|
|
||||||
5
tests/TestFiles/case_insensitive.sfz
Normal file
5
tests/TestFiles/case_insensitive.sfz
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<region> key=61 sample=Dummy1.wav
|
||||||
|
<region> key=61 sample=REGIONS/Dummy.wav
|
||||||
|
<region> key=61 sample=REGIonS\Dummy.wav
|
||||||
|
<control> default_path=REGIONS/
|
||||||
|
<region> key=61 sample=Dummy.wav
|
||||||
Loading…
Add table
Reference in a new issue