Skip to content

Commit

Permalink
Make the user data path orderly and rename to Surge XT (#4736)
Browse files Browse the repository at this point in the history
* Make the user data path orderly and rename to Surge XT
This PR creates a clean slate for Surge XT user data path:
* new folder name for user data path - Documents/Surge XT
* wavetables get their own subfolder - Documents/Surge XT/Wavetables
* patches get their own subfolder - Documents/Surge XT/Patches
* exported wavetables will be saved to Documents/Surge XT/Wavetables/Exported
* since exported WTs are now within the main user wavetables folder, remove the "Open exported wavetables folder..." option, and rename "Save wavetable to file" to "Export wavetable to file", to drive the point home and close the circle
* Improve the startup path, also to make a well-populated initial directory and always make sure it is there

Closes #4735 as a consequence of adding subfolders for each type of content.

Also, removes some unnecessary conditions and local variables from Patch Selector.cpp.

Co-authored-by: Paul Walker <[email protected]>
  • Loading branch information
mkruselj and baconpaul authored Aug 8, 2021
1 parent 05e954a commit faf520c
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 79 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ juce_add_binary_data(surge-shared-binary
HEADER_NAME SurgeCoreBinary.h
SOURCES
resources/data/configuration.xml
resources/surge-xt/README_UserArea.txt
resources/data/windows.wt
resources/data/paramdocumentation.xml)
set_target_properties(surge-shared-binary PROPERTIES
Expand Down
17 changes: 17 additions & 0 deletions resources/surge-xt/README_UserArea.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
This is your Surge XT user area!

With Surge XT, we are using a more structured user area which
separates the storage of patches, wavetables, FX settings,
MIDI settings, and modulator settings. Each of these types of
assets now have a parent level directory.

Most importantly, you can read the Surge manual at:

https://surge-synthesizer.github.io/

but if you want to import patches, drag them into the Patches
directory, and so on.

Before we release Surge XT, we will also add some sort of rudimentary
migration assistant. Until then, you are kinda on your own.
Hop on Discord if you get stuck during the alpha and early beta stage!
3 changes: 1 addition & 2 deletions src/common/PatchDB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ struct TxnGuard

struct PatchDB::workerS
{
static constexpr const char *schema_version = "3"; // I will rebuild if this is not my verion
static constexpr const char *schema_version = "4"; // I will rebuild if this is not my verion

/*
* Obviously a lot of thought needs to go into this
Expand Down Expand Up @@ -265,7 +265,6 @@ CREATE TABLE Category (

explicit workerS(SurgeStorage *storage) : storage(storage)
{
fs::create_directories(string_to_path(storage->userDataPath));
auto dbname = storage->userDataPath + "/SurgePatches.db";
auto flag = SQLITE_OPEN_FULLMUTEX; // basically lock
flag |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
Expand Down
95 changes: 66 additions & 29 deletions src/common/SurgeStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,15 +268,15 @@ SurgeStorage::SurgeStorage(std::string suppliedDataPath) : otherscene_clients(0)
if (lstat == 0 && (linfo.st_mtime > rinfo.st_mtime || rstat != 0))
datapath = localpath; // use the local
else
datapath = rootpath; // else use the root. If both are missing we will blow up later.
datapath = rootpath; // else use the root. If both are missing we will blow up later
}
else
{
datapath = suppliedDataPath;
}

// ~/Documents/Surge in full name
sprintf(path, "%s/Documents/Surge", homePath);
// ~/Documents/Surge XT in full name
sprintf(path, "%s/Documents/Surge XT", homePath);
userDataPath = path;
#elif LINUX
if (!hasSuppliedDataPath)
Expand Down Expand Up @@ -326,8 +326,8 @@ SurgeStorage::SurgeStorage(std::string suppliedDataPath) : otherscene_clients(0)
if (buildOverrideDataPath)
{
datapath = std::string(buildOverrideDataPath);
std::cout << "WARNING: Surge Overriding DataPath to " << datapath << std::endl;
std::cout << " Only use this in build pipelines please" << std::endl;
std::cout << "WARNING: Surge overriding data path to " << datapath << std::endl;
std::cout << " Only use this in build pipelines please!" << std::endl;
}
}
else
Expand All @@ -337,15 +337,15 @@ SurgeStorage::SurgeStorage(std::string suppliedDataPath) : otherscene_clients(0)

/*
** See the discussion in github issue #930. Basically
** if ~/Documents/Surge exists use that
** else if ~/.Surge exists use that
** else if ~/.Documents exists, use ~/Documents/Surge
** else use ~/.Surge
** if ~/Documents/Surge XT exists use that
** else if ~/.Surge XT exists use that
** else if ~/.Documents exists, use ~/Documents/Surge XT
** else use ~/.Surge XT
** Compensating for whether your distro makes you a ~/Documents or not
*/

std::string documentsSurge = std::string(homePath) + "/Documents/Surge";
std::string dotSurge = std::string(homePath) + "/.Surge";
std::string documentsSurge = std::string(homePath) + "/Documents/Surge XT";
std::string dotSurge = std::string(homePath) + "/.Surge XT";
std::string documents = std::string(homePath) + "/Documents/";

if (fs::is_directory(string_to_path(documentsSurge)))
Expand All @@ -364,8 +364,8 @@ SurgeStorage::SurgeStorage(std::string suppliedDataPath) : otherscene_clients(0)
{
userDataPath = dotSurge;
}
// std::cout << "DataPath is " << datapath << std::endl;
// std::cout << "UserDataPath is " << userDataPath << std::endl;
// std::cout << "Data path is " << datapath << std::endl;
// std::cout << "User data path is " << userDataPath << std::endl;

#elif WINDOWS
#if TARGET_RACK
Expand Down Expand Up @@ -435,18 +435,18 @@ SurgeStorage::SurgeStorage(std::string suppliedDataPath) : otherscene_clients(0)
}
}

// Portable - first check for dllPath\\SurgeUserData
if (!dllPath.empty() && fs::is_directory(dllPath / L"SurgeUserData"))
// Portable - first check for dllPath\\SurgeXTUserData
if (!dllPath.empty() && fs::is_directory(dllPath / L"SurgeXTUserData"))
{
userDataPath = path_to_string(dllPath / L"SurgeUserData");
userDataPath = path_to_string(dllPath / L"SurgeXTUserData");
}
else
{
PWSTR documentsFolder;
if (!SHGetKnownFolderPath(FOLDERID_Documents, 0, nullptr, &documentsFolder))
{
fs::path path(documentsFolder);
path /= L"Surge";
path /= L"Surge XT";
userDataPath = path_to_string(path);
}
}
Expand All @@ -464,10 +464,14 @@ SurgeStorage::SurgeStorage(std::string suppliedDataPath) : otherscene_clients(0)

// append separator if not present
datapath = Surge::Storage::appendDirectory(datapath, std::string());

userFXPath = Surge::Storage::appendDirectory(userDataPath, "FXSettings");

userMidiMappingsPath = Surge::Storage::appendDirectory(userDataPath, "MIDIMappings");
userPatchesPath = Surge::Storage::appendDirectory(userDataPath, "Patches");
userWavetablesPath = Surge::Storage::appendDirectory(userDataPath, "Wavetables");
userWavetablesExportPath = Surge::Storage::appendDirectory(userWavetablesPath, "Exported");
userFXPath = Surge::Storage::appendDirectory(userDataPath, "FX Presets");
userMidiMappingsPath = Surge::Storage::appendDirectory(userDataPath, "MIDI Mappings");
userModulatorSettingsPath = Surge::Storage::appendDirectory(userDataPath, "Modulator Presets");
userSkinsPath = Surge::Storage::appendDirectory(userDataPath, "Skins");
createUserDirectory();

/*
const auto snapshotmenupath{string_to_path(datapath + "configuration.xml")};
Expand All @@ -487,8 +491,8 @@ SurgeStorage::SurgeStorage(std::string suppliedDataPath) : otherscene_clients(0)
if (!snapshotloader.Parse(cxmlData.c_str()))
{
reportError("Cannot parse 'configuration.xml' in path '" + datapath +
"'. Please reinstall surge.",
"Surge is not properly installed.");
"'. Please reinstall Surge!",
"Surge Incorrectly Installed");
}

load_midi_controllers();
Expand All @@ -508,16 +512,15 @@ SurgeStorage::SurgeStorage(std::string suppliedDataPath) : otherscene_clients(0)
getPatch().scene[0].osc[0].wt.dt = 1.0f / 512.f;
load_wt(0, &getPatch().scene[0].osc[0].wt, &getPatch().scene[0].osc[0]);

// WindowWT is a WaveTable which now has a constructor so don't do this
// WindowWT is a Wavetable which now has a constructor so don't do this
// memset(&WindowWT, 0, sizeof(WindowWT));
if (loadWtAndPatch &&
!load_wt_wt_mem(SurgeCoreBinary::windows_wt, SurgeCoreBinary::windows_wtSize, &WindowWT))
{
WindowWT.size = 0;
std::ostringstream oss;
oss << "Unable to load 'windows.wt'. from memory. "
<< "This is a usually fatal internal software error in Surge XT which should"
<< " never occur!";
oss << "Unable to load 'windows.wt' from memory. "
<< "This is a fatal internal software error which should never occur!";
reportError(oss.str(), "Surge Resources Loading Error");
}

Expand Down Expand Up @@ -652,6 +655,40 @@ SurgeStorage::SurgeStorage(std::string suppliedDataPath) : otherscene_clients(0)
this, Surge::Storage::InitialPatchCategory, "Templates");
}

void SurgeStorage::createUserDirectory()
{
auto p = string_to_path(userDataPath);
auto needToBuild = false;
if (!fs::is_directory(p))
{
needToBuild = true;
}

if (needToBuild)
{
try
{
for (auto &s : {userDataPath, userDefaultFilePath, userPatchesPath, userWavetablesPath,
userModulatorSettingsPath, userFXPath, userWavetablesExportPath,
userSkinsPath, userMidiMappingsPath})
fs::create_directories(string_to_path(s));

auto rd = std::string(SurgeCoreBinary::README_UserArea_txt,
SurgeCoreBinary::README_UserArea_txtSize) +
"\n";
auto of =
std::ofstream(string_to_path(userDataPath) / "README.txt", std::ofstream::out);
if (of.is_open())
of << rd << std::endl;
of.close();
}
catch (const fs::filesystem_error &e)
{
reportError(e.what(), "Unable to set up User Directory");
}
}
}

void SurgeStorage::initializePatchDb()
{
patchDB = std::make_unique<Surge::PatchStorage::PatchDB>(this);
Expand Down Expand Up @@ -715,7 +752,7 @@ void SurgeStorage::refresh_patchlist()

refreshPatchlistAddDir(false, "patches_3rdparty");
firstUserCategory = patch_category.size();
refreshPatchlistAddDir(true, "");
refreshPatchlistAddDir(true, "Patches");

patchOrdering = std::vector<int>(patch_list.size());
std::iota(patchOrdering.begin(), patchOrdering.end(), 0);
Expand Down Expand Up @@ -945,7 +982,7 @@ void SurgeStorage::refresh_wtlist()
firstThirdPartyWTCategory = wt_category.size();
refresh_wtlistAddDir(false, "wavetables_3rdparty");
firstUserWTCategory = wt_category.size();
refresh_wtlistAddDir(true, "");
refresh_wtlistAddDir(true, "Wavetables");

wtCategoryOrdering = std::vector<int>(wt_category.size());
std::iota(wtCategoryOrdering.begin(), wtCategoryOrdering.end(), 0);
Expand Down
12 changes: 10 additions & 2 deletions src/common/SurgeStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,8 @@ class alignas(16) SurgeStorage
float modsource_vu[n_modsources];
void setSamplerate(float sr);

void createUserDirectory();

void refresh_wtlist();
void refresh_wtlistAddDir(bool userDir, std::string subdir);
void refresh_patchlist();
Expand Down Expand Up @@ -1005,12 +1007,18 @@ class alignas(16) SurgeStorage

std::string wtpath;
std::string datapath;
std::string userDataPath;
std::string userDefaultFilePath;
std::string userDataPath;
std::string userPatchesPath;
std::string userWavetablesPath;
std::string userModulatorSettingsPath;
std::string userFXPath;
std::string userWavetablesExportPath;
std::string userSkinsPath;
std::string userMidiMappingsPath;

std::string installedPath;

std::string userMidiMappingsPath;
std::map<std::string, TiXmlDocument> userMidiMappingsXMLByName;
void rescanUserMidiMappings();
void loadMidiMappingByName(std::string name);
Expand Down
3 changes: 2 additions & 1 deletion src/common/SurgeSynthesizerIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,8 @@ void SurgeSynthesizer::savePatch()
if (storage.getPatch().category.empty())
storage.getPatch().category = "Default";

fs::path savepath = string_to_path(getUserPatchDirectory());
fs::path savepath =
string_to_path(getUserPatchDirectory() + PATH_SEPARATOR + "Patches" + PATH_SEPARATOR);

try
{
Expand Down
2 changes: 1 addition & 1 deletion src/common/WAVFileSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ bool SurgeStorage::load_wt_wav_portable(std::string fn, Wavetable *wt)
std::string SurgeStorage::export_wt_wav_portable(std::string fbase, Wavetable *wt)
{
std::string path;
path = Surge::Storage::appendDirectory(userDataPath, "Exported Wavetables");
path = Surge::Storage::appendDirectory(userDataPath, "Wavetables", "Exported");
fs::create_directories(string_to_path(path));

auto fnamePre = fbase + ".wav";
Expand Down
2 changes: 1 addition & 1 deletion src/gui/SkinSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void SkinDB::rescanForSkins(SurgeStorage *storage)
availableSkins.clear();

std::array<fs::path, 2> paths = {string_to_path(storage->datapath),
string_to_path(storage->userDataPath)};
string_to_path(storage->userSkinsPath)};

for (auto &source : paths)
{
Expand Down
6 changes: 1 addition & 5 deletions src/gui/SurgeGUIEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3027,11 +3027,7 @@ juce::PopupMenu SurgeGUIEditor::makeSkinMenu(const juce::Point<int> &where)
else
{
skinSubMenu.addItem(Surge::GUI::toOSCaseForMenu("Install a New Skin..."), [this]() {
std::string skinspath =
Surge::Storage::appendDirectory(this->synth->storage.userDataPath, "Skins");
// make it if it isn't there
fs::create_directories(string_to_path(skinspath));
Surge::GUI::openFileOrFolder(skinspath);
Surge::GUI::openFileOrFolder(this->synth->storage.userSkinsPath);
});
}

Expand Down
8 changes: 1 addition & 7 deletions src/gui/widgets/OscillatorWaveformDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,13 +339,7 @@ void OscillatorWaveformDisplay::populateMenu(juce::PopupMenu &contextMenu, int s
message);
}
};
contextMenu.addItem(Surge::GUI::toOSCaseForMenu("Save Wavetable to File..."), exportAction);

contextMenu.addItem(Surge::GUI::toOSCaseForMenu("Open Exported Wavetables Folder..."),
[this]() {
Surge::GUI::openFileOrFolder(Surge::Storage::appendDirectory(
this->storage->userDataPath, "Exported Wavetables"));
});
contextMenu.addItem(Surge::GUI::toOSCaseForMenu("Export Wavetable to File..."), exportAction);

if (sge)
{
Expand Down
47 changes: 16 additions & 31 deletions src/gui/widgets/PatchSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ void PatchSelector::showClassicMenu(bool single_category)
int main_e = 0;
bool has_3rdparty = false;
int last_category = current_category;
int root_count = 0, usercat_pos = 0, col_breakpoint = 0;
auto patch_cat_size = storage->patch_category.size();

if (single_category)
Expand Down Expand Up @@ -171,43 +170,27 @@ void PatchSelector::showClassicMenu(bool single_category)

for (int i = 0; i < patch_cat_size; i++)
{
if ((!single_category) || (i == last_category))
if (i == storage->firstThirdPartyCategory || i == storage->firstUserCategory)
{
if (!single_category &&
(i == storage->firstThirdPartyCategory || i == storage->firstUserCategory))
std::string txt;

if (i == storage->firstThirdPartyCategory && storage->firstUserCategory != i)
{
std::string txt;

if (i == storage->firstThirdPartyCategory && storage->firstUserCategory != i)
{
has_3rdparty = true;
txt = "THIRD PARTY PATCHES";
}
else
{
txt = "USER PATCHES";
}

contextMenu.addColumnBreak();
contextMenu.addSectionHeader(txt);
txt = "THIRD PARTY PATCHES";
}

// Remap index to the corresponding category in alphabetical order.
int c = storage->patchCategoryOrdering[i];

// find at which position the first user category root folder shows up
if (storage->patch_category[i].isRoot)
else
{
root_count++;

if (i == storage->firstUserCategory)
{
usercat_pos = root_count;
}
txt = "USER PATCHES";
}

populatePatchMenuForCategory(c, contextMenu, single_category, main_e, true);
contextMenu.addColumnBreak();
contextMenu.addSectionHeader(txt);
}

// remap index to the corresponding category in alphabetical order.
int c = storage->patchCategoryOrdering[i];

populatePatchMenuForCategory(c, contextMenu, single_category, main_e, true);
}
}

Expand Down Expand Up @@ -251,6 +234,8 @@ void PatchSelector::showClassicMenu(bool single_category)
}
});

contextMenu.addSeparator();

contextMenu.addItem(Surge::GUI::toOSCaseForMenu("Refresh Patch List"),
[this]() { this->storage->refresh_patchlist(); });

Expand Down

0 comments on commit faf520c

Please sign in to comment.