From 3fe6c8114e1041859197b3401b78a7b1dd3c69c8 Mon Sep 17 00:00:00 2001 From: Paul W Date: Wed, 13 Feb 2019 22:05:32 -0500 Subject: [PATCH] UTF8 for Wavetables; Noisier Mis-installation; Win Categories UTF8 filename conversion is required on windows for vstgui so factor the UTF8 string conversion into a function and adjust the callpoints. Those callpoints intersect with places where we load patches and wavetables, so at the same time, make the system report user errors if wavetables or factory patches are non-loadable In the course of testing this, I also realized that nested directories didn't work properly on windows due to path separator being "\" rather than "/", which this commit also addresses. Closes #584 UTF8 for wavetables Closes #543 Alert user to mis-installation pathsetp --- src/common/SurgeStorage.cpp | 148 ++++++++++++++++++++----------- src/common/SurgeStorage.h | 12 ++- src/common/gui/CPatchBrowser.cpp | 8 +- 3 files changed, 112 insertions(+), 56 deletions(-) diff --git a/src/common/SurgeStorage.cpp b/src/common/SurgeStorage.cpp index 59138cee066..868c58ad711 100644 --- a/src/common/SurgeStorage.cpp +++ b/src/common/SurgeStorage.cpp @@ -27,6 +27,8 @@ #include #endif +#include + float sinctable alignas(16)[(FIRipol_M + 1) * FIRipol_N * 2]; float sinctable1X alignas(16)[(FIRipol_M + 1) * FIRipol_N]; short sinctableI16 alignas(16)[(FIRipol_M + 1) * FIRipolI16_N]; @@ -233,6 +235,23 @@ void SurgeStorage::refresh_patchlist() refreshPatchlistAddDir(false, "patches_factory"); firstThirdPartyCategory = patch_category.size(); + + /* + ** Do a quick sanity check here - if there are no patches in factory we are mis-installed + */ + int totalFactory = 0; + for(auto &cat : patch_category) + totalFactory += cat.numberOfPatchesInCatgory; + if(totalFactory == 0) + { + std::ostringstream ss; + ss << "Surge was unable to load factory patches from '" << datapath + << "'. Surge found 0 factory patches. Please reinstall using the Surge installer."; + Surge::UserInteractions::promptError(ss.str(), + "Surge Installation Error"); + + } + refreshPatchlistAddDir(false, "patches_3rdparty"); firstUserCategory = patch_category.size(); refreshPatchlistAddDir(true, ""); @@ -330,15 +349,7 @@ void SurgeStorage::refreshPatchlistAddDir(bool userDir, string subdir) ** windows widechar API. Linux and Mac do not require this. */ std::wstring str = p.wstring().substr(patchpathSubstrLength); - std::string ret; - int len = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0, NULL, NULL); - if (len > 0) - { - ret.resize(len); - WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), &ret[0], len, NULL, NULL); - } - - c.name = ret; + c.name = Surge::Storage::wstringToUTF8(str); #else c.name = p.generic_string().substr(patchpathSubstrLength); c.numberOfPatchesInCatgory = 0; @@ -354,14 +365,7 @@ void SurgeStorage::refreshPatchlistAddDir(bool userDir, string subdir) #if WINDOWS std::wstring str = f.path().filename().wstring(); str = str.substr(0, str.size()-4); - std::string ret; - int len = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0, NULL, NULL); - if (len > 0) - { - ret.resize(len); - WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), &ret[0], len, NULL, NULL); - } - e.name = ret; + e.name = Surge::Storage::wstringToUTF8(str); #else e.name = f.path().filename().generic_string(); e.name = e.name.substr(0, e.name.size() - 4); @@ -386,16 +390,22 @@ void SurgeStorage::refreshPatchlistAddDir(bool userDir, string subdir) int idx=0; for (auto &pc : local_patch_category) nameToLocalIndex[ pc.name ] = idx++; + + std::string pathSep = "/"; +#if WINDOWS + pathSep = "\\"; +#endif + for (auto &pc : local_patch_category) { - if (pc.name.find("/") == std::string::npos) + if (pc.name.find(pathSep) == std::string::npos) { pc.isRoot = true; } else { pc.isRoot = false; - std::string parent = pc.name.substr(0, pc.name.find_last_of("/") ); + std::string parent = pc.name.substr(0, pc.name.find_last_of(pathSep) ); local_patch_category[ nameToLocalIndex[ parent ] ].children.push_back( pc ); } } @@ -439,36 +449,50 @@ void SurgeStorage::refresh_wtlist() if (!fs::is_directory(patchpath)) { + std::ostringstream ss; + ss << "Surge was unable to load wavetables from '" << patchpath.generic_string() + << "'. The directory does not exist. Please reinstall using the Surge installer."; + Surge::UserInteractions::promptError(ss.str(), + "Surge Installation Error"); + return; } + std::vector supportedTableFileTypes; + supportedTableFileTypes.push_back(".wt"); + supportedTableFileTypes.push_back(".wav"); + for (auto& p : fs::directory_iterator(patchpath)) { if (fs::is_directory(p)) { PatchCategory c; +#if WINDOWS + std::wstring str = p.path().filename().wstring(); + c.name = Surge::Storage::wstringToUTF8(str); +#else c.name = p.path().filename().generic_string(); +#endif wt_category.push_back(c); for (auto& f : fs::directory_iterator(p)) { - if (_stricmp(f.path().extension().generic_string().c_str(), ".wt") == 0) - { - Patch e; - e.category = category; - e.path = f.path(); - e.name = f.path().filename().generic_string(); - e.name = e.name.substr(0, e.name.size() - 3); - wt_list.push_back(e); - } - else if (_stricmp(f.path().extension().generic_string().c_str(), ".wav") == 0) + for(auto &ft : supportedTableFileTypes ) { - Patch e; - e.category = category; - e.path = f.path(); - e.name = f.path().filename().generic_string(); - e.name = e.name.substr(0, e.name.size() - 4); - wt_list.push_back(e); + if (_stricmp(f.path().extension().generic_string().c_str(), ft.c_str()) == 0) + { + Patch e; + e.category = category; + e.path = f.path(); +#if WINDOWS + std::wstring str = f.path().filename().wstring(); + e.name = Surge::Storage::wstringToUTF8(str.substr(0, str.size() - ft.size())); +#else + e.name = f.path().filename().generic_string(); + e.name = e.name.substr(0, e.name.size() - ft.size()); +#endif + wt_list.push_back(e); + } } } @@ -476,11 +500,13 @@ void SurgeStorage::refresh_wtlist() } } - if (wt_category.size() < 1 && wt_list.size() < 1) + if (wt_category.size() == 0 || wt_list.size() == 0) { - Surge::UserInteractions::promptError(std::string("Surge was unable to load wavetables from disk. ") + - "Please reinstall using the surge installer.", - "Surge Installation Error" ); + std::ostringstream ss; + ss << "Surge was unable to load wavetables from '" << patchpath.generic_string() + << "'. The directory contains no wavetables. Please reinstall using the Surge installer."; + Surge::UserInteractions::promptError(ss.str(), + "Surge Installation Error" ); } wtCategoryOrdering = std::vector(wt_category.size()); @@ -1106,20 +1132,36 @@ float envelope_rate_linear(float x) namespace Surge { - namespace Storage +namespace Storage +{ +bool isValidName(const std::string &patchName) +{ + bool valid = false; + + // No need to validate size separately as an empty string won't have visible characters. + for (const char &c : patchName) + if (std::isalnum(c) || std::ispunct(c)) + valid = true; + else if (c != ' ') + return false; + + return valid; +} + +#if WINDOWS +std::string wstringToUTF8(const std::wstring &str) +{ + std::string ret; + int len = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0, NULL, NULL); + if (len > 0) { - bool isValidName(const std::string &patchName) - { - bool valid = false; - - // No need to validate size separately as an empty string won't have visible characters. - for (const char &c : patchName) - if (std::isalnum(c) || std::ispunct(c)) - valid = true; - else if (c != ' ') - return false; - - return valid; - } + ret.resize(len); + WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), &ret[0], len, NULL, NULL); } + return ret; } +#endif + +} // end ns Surge::Storage +} // end ns Surge + diff --git a/src/common/SurgeStorage.h b/src/common/SurgeStorage.h index e1d233ea616..8d50963ceaa 100644 --- a/src/common/SurgeStorage.h +++ b/src/common/SurgeStorage.h @@ -538,6 +538,7 @@ class SurgeStorage StepSequencerStorage clipboard_stepsequences[n_lfos]; std::vector clipboard_modulation_scene, clipboard_modulation_voice; Wavetable clipboard_wt[n_oscs]; + }; float note_to_pitch(float); @@ -553,7 +554,16 @@ namespace Surge { namespace Storage { - bool isValidName(const std::string &name); +bool isValidName(const std::string &name); + +#if WINDOWS +/* +** Windows filesystem names are properly wstrings which, if we want them to +** display properly in vstgui, need to be converted to UTF8 using the +** windows widechar API. Linux and Mac do not require this. +*/ +std::string wstringToUTF8(const std::wstring &ws); +#endif } } diff --git a/src/common/gui/CPatchBrowser.cpp b/src/common/gui/CPatchBrowser.cpp index 05989bfb0ea..1547aae055f 100644 --- a/src/common/gui/CPatchBrowser.cpp +++ b/src/common/gui/CPatchBrowser.cpp @@ -189,8 +189,12 @@ bool CPatchBrowser::populatePatchMenuForCategory( int c, COptionMenu *contextMen } std::string menuName = storage->patch_category[c].name; - if (menuName.find_last_of("/") != string::npos) - menuName = menuName.substr(menuName.find_last_of("/") + 1); + std::string pathSep = "/"; +#if WINDOWS + pathSep = "\\"; +#endif + if (menuName.find_last_of(pathSep) != string::npos) + menuName = menuName.substr(menuName.find_last_of(pathSep) + 1); if (n_subc > 1) sprintf(name, "%s - %i", menuName.c_str(), subc + 1);