VST file search

This commit is contained in:
Jean Pierre Cimalando 2020-10-29 10:24:00 +01:00
parent a34ac38aec
commit 45285a4a66
8 changed files with 366 additions and 1 deletions

View file

@ -17,6 +17,8 @@ set(VSTPLUGIN_SOURCES
SfizzVstController.cpp
SfizzVstEditor.cpp
SfizzVstState.cpp
SfizzFileScan.cpp
SfizzForeignPaths.cpp
VstPluginFactory.cpp
X11RunLoop.cpp
NativeHelpers.cpp)
@ -26,11 +28,18 @@ set(VSTPLUGIN_HEADERS
SfizzVstController.h
SfizzVstEditor.h
SfizzVstState.h
SfizzFileScan.h
SfizzForeignPaths.h
X11RunLoop.h
NativeHelpers.h)
if(APPLE)
list(APPEND VSTPLUGIN_SOURCES NativeHelpers.mm)
set(VSTPLUGIN_MAC_SOURCES
SfizzForeignPaths.mm
NativeHelpers.mm)
list(APPEND VSTPLUGIN_SOURCES ${VSTPLUGIN_MAC_SOURCES})
set_property(SOURCE ${VSTPLUGIN_MAC_SOURCES} APPEND_STRING
PROPERTY COMPILE_FLAGS " -fobjc-arc")
endif()
add_library(${VSTPLUGIN_PRJ_NAME} MODULE

View file

@ -7,6 +7,9 @@
#include "NativeHelpers.h"
#import <Foundation/Foundation.h>
#include <stdexcept>
#if !__has_feature(objc_arc)
#error This source file requires ARC
#endif
#if defined(__APPLE__)
const fs::path& getUserDocumentsDirectory()

198
vst/SfizzFileScan.cpp Normal file
View file

@ -0,0 +1,198 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "SfizzFileScan.h"
#include "SfizzForeignPaths.h"
#include "NativeHelpers.h"
#include <absl/strings/ascii.h>
#include <vector>
#include <memory>
// wait at least this much before refreshing the file rescan
// it permits to not repeat the operation many times if many searches are
// requested at once, eg. on session loading with multiple plugin instances
static const std::chrono::seconds expiration_time { 10 };
SfzFileScan& SfzFileScan::getInstance()
{
static SfzFileScan instance;
return instance;
}
bool SfzFileScan::locateRealFile(const fs::path& pathOrig, fs::path& pathFound)
{
if (pathOrig.empty())
return false;
std::unique_lock<std::mutex> lock { mutex };
refreshScan();
auto it = file_index_.find(keyOf(pathOrig));
if (it == file_index_.end())
return false;
std::list<std::string> candidateStrings = it->second;
lock.unlock();
std::vector<fs::path> candidates;
candidates.reserve(candidateStrings.size());
for (const std::string& str : candidateStrings)
candidates.push_back(fs::u8path(str));
pathFound = electBestMatch(pathOrig, candidates);
return true;
}
bool SfzFileScan::isExpired() const
{
return !completion_time_ ||
(clock::now() - *completion_time_) > expiration_time;
}
void SfzFileScan::refreshScan(bool force)
{
if (!force && !isExpired())
return;
for (const fs::path& dirPath : SfizzPaths::sfzDefaultPaths()) {
std::error_code ec;
for (fs::recursive_directory_iterator it(dirPath, ec);
!ec && it != fs::recursive_directory_iterator();
it.increment(ec))
{
const fs::directory_entry& ent = *it;
const fs::path& filePath = ent.path();
std::error_code ec;
if (ent.is_regular_file(ec) /*&& pathIsSfz(filePath)*/)
file_index_[keyOf(filePath.filename())].push_back(filePath.u8string());
}
}
completion_time_ = clock::now();
}
std::string SfzFileScan::keyOf(const fs::path& path)
{
std::string key = path.u8string();
absl::AsciiStrToLower(&key);
return key;
}
namespace SfzFileScanImpl {
template <class T>
bool asciiCaseEqual(const std::basic_string<T>& a, const std::basic_string<T>& b)
{
const size_t n = a.size();
if (n != b.size())
return false;
auto lower = [](T c) -> T {
return (c >= T('A') && c <= T('Z')) ? (c - T('A') + T('a')) : c;
};
for (size_t i = 0; i < n; ++i)
if (lower(a[i]) != lower(b[i]))
return false;
return true;
}
} // namespace SfzFileScanImpl
bool SfzFileScan::pathIsSfz(const fs::path& path)
{
const fs::path::string_type& str = path.native();
using char_type = fs::path::value_type;
const size_t n = str.size();
return n > 4 &&
str[n - 4] == char_type('.') &&
(str[n - 3] == char_type('s') || str[n - 3] == char_type('S')) &&
(str[n - 2] == char_type('f') || str[n - 2] == char_type('F')) &&
(str[n - 1] == char_type('z') || str[n - 1] == char_type('Z'));
}
const fs::path& SfzFileScan::electBestMatch(const fs::path& path, absl::Span<const fs::path> candidates)
{
if (candidates.empty())
return path;
if (candidates.size() == 1)
return candidates.front();
struct Score {
size_t index = 0;
size_t components = 0;
size_t exact = 0;
explicit Score(size_t index) noexcept : index(index) {}
bool operator<(const Score& other) const noexcept
{
return (components != other.components) ?
(components < other.components) : (exact < other.exact);
}
};
std::vector<Score> scores;
scores.reserve(candidates.size());
for (size_t i = 0, n = candidates.size(); i < n; ++i) {
scores.emplace_back(i);
Score& score = scores.back();
const fs::path& p1 = path;
const fs::path& p2 = candidates[i];
auto it1 = p1.end();
auto it2 = p2.end();
while (it1-- != p1.begin() && it2-- != p2.begin()) {
const fs::path& c1 = *it1;
const fs::path& c2 = *it2;
if (c1 == c2) {
score.components += 1;
score.exact += 1;
}
else if (SfzFileScanImpl::asciiCaseEqual(c1.native(), c2.native()))
score.components += 1;
}
}
std::stable_sort(scores.begin(), scores.end());
return candidates[scores[0].index];
}
//------------------------------------------------------------------------------
namespace SfizzPaths {
absl::Span<const fs::path> sfzDefaultPaths()
{
static const auto paths = []() -> std::vector<fs::path> {
std::vector<fs::path> paths;
paths.reserve(8);
paths.push_back(getUserDocumentsDirectory() / "SFZ instruments");
for (const fs::path& foreign : {
getAriaPathSetting("user_files_dir"),
getAriaPathSetting("Converted_path") })
if (!foreign.empty() && foreign.is_absolute())
paths.push_back(foreign);
paths.shrink_to_fit();
return paths;
}();
return paths;
}
void createSfzDefaultPaths()
{
for (const fs::path& path : sfzDefaultPaths()) {
std::error_code ec;
fs::create_directory(path, ec);
}
}
} // namespace SfizzPaths

38
vst/SfizzFileScan.h Normal file
View file

@ -0,0 +1,38 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
#include <absl/types/span.h>
#include <absl/types/optional.h>
#include <ghc/fs_std.hpp>
#include <string>
#include <list>
#include <unordered_map>
#include <mutex>
#include <chrono>
class SfzFileScan {
public:
static SfzFileScan& getInstance();
bool locateRealFile(const fs::path& pathOrig, fs::path& pathFound);
private:
typedef std::chrono::steady_clock clock;
std::mutex mutex;
absl::optional<clock::time_point> completion_time_;
std::unordered_map<std::string, std::list<std::string>> file_index_;
bool isExpired() const;
void refreshScan(bool force = false);
static std::string keyOf(const fs::path& path);
static bool pathIsSfz(const fs::path& path);
static const fs::path& electBestMatch(const fs::path& path, absl::Span<const fs::path> candidates);
};
namespace SfizzPaths {
absl::Span<const fs::path> sfzDefaultPaths();
void createSfzDefaultPaths();
} // namespace SfizzPaths

51
vst/SfizzForeignPaths.cpp Normal file
View file

@ -0,0 +1,51 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "SfizzForeignPaths.h"
namespace SfizzPaths {
#if defined(_WIN32)
#include <windows.h>
fs::path getAriaPathSetting(const char* name)
{
fs::path path;
HKEY key = 0;
std::unique_ptr<WCHAR[]> nameW;
unsigned nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, nullptr, 0);
if (nameSize == 0)
return {};
nameW.reset(new WCHAR[nameSize]);
if (MultiByteToWideChar(CP_UTF8, 0, name, -1, nameW.get(), nameSize) == 0)
return {};
const WCHAR ariaKeyPath[] = L"Software\\Plogue Art et Technologie, Inc\\Aria";
if (RegOpenKeyExW(HKEY_CURRENT_USER, ariaKeyPath, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {
WCHAR valueBuffer[32768 + 1];
DWORD valueSize = sizeof(valueBuffer) - sizeof(WCHAR);
if (RegQueryValueExW(key, nameW.get(), nullptr, nullptr, reinterpret_cast<LPBYTE>(valueBuffer), &valueSize) == ERROR_SUCCESS) {
valueBuffer[32768] = L'\0';
path = fs::path(valueBuffer);
}
RegCloseKey(key);
}
return path;
}
#elif defined(__APPLE__)
// implementation in SfizzForeignPaths.mm
#else
fs::path getAriaPathSetting(const char* name)
{
return {};
}
#endif
} // namespace SfizzPaths

12
vst/SfizzForeignPaths.h Normal file
View file

@ -0,0 +1,12 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
#include <ghc/fs_std.hpp>
namespace SfizzPaths {
fs::path getAriaPathSetting(const char* name);
} // namespace SfizzPaths

27
vst/SfizzForeignPaths.mm Normal file
View file

@ -0,0 +1,27 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "SfizzForeignPaths.h"
#if defined(__APPLE__)
#import <Foundation/Foundation.h>
#if !__has_feature(objc_arc)
#error This source file requires ARC
#endif
namespace SfizzPaths {
fs::path getAriaPathSetting(const char* name)
{
NSUserDefaults* ud = [[NSUserDefaults alloc] initWithSuiteName:@"com.plogue.aria"];
NSString* value = [ud stringForKey:[NSString stringWithUTF8String:name]];
if (!value)
return {};
return fs::path(value.UTF8String);
}
} // namespace SfizzPaths
#endif

View file

@ -7,9 +7,11 @@
#include "SfizzVstProcessor.h"
#include "SfizzVstController.h"
#include "SfizzVstState.h"
#include "SfizzFileScan.h"
#include "base/source/fstreamer.h"
#include "pluginterfaces/vst/ivstevents.h"
#include "pluginterfaces/vst/ivstparameterchanges.h"
#include <ghc/fs_std.hpp>
#include <cstring>
template<class T>
@ -28,6 +30,8 @@ SfizzVstProcessor::SfizzVstProcessor()
: _fifoToWorker(64 * 1024), _fifoMidiFromUi(64 * 1024)
{
setControllerClass(SfizzVstController::cid);
SfizzPaths::createSfzDefaultPaths();
}
SfizzVstProcessor::~SfizzVstProcessor()
@ -83,6 +87,29 @@ tresult PLUGIN_API SfizzVstProcessor::setState(IBStream* stream)
if (r != kResultTrue)
return r;
// check the files to really exist, otherwise search them
for (std::string* statePath : { &s.sfzFile, &s.scalaFile }) {
if (statePath->empty())
continue;
fs::path pathOrig = fs::u8path(*statePath);
std::error_code ec;
if (fs::is_regular_file(pathOrig, ec))
continue;
fprintf(stderr, "[Sfizz] searching for missing file: %s\n", pathOrig.filename().u8string().c_str());
SfzFileScan& fileScan = SfzFileScan::getInstance();
fs::path pathFound;
if (!fileScan.locateRealFile(pathOrig, pathFound))
fprintf(stderr, "[Sfizz] file not found: %s\n", pathOrig.filename().u8string().c_str());
else {
fprintf(stderr, "[Sfizz] file found: %s\n", pathFound.filename().u8string().c_str());
*statePath = pathFound.u8string();
}
}
//
std::lock_guard<std::mutex> lock(_processMutex);
_state = s;