Skip to content

Commit

Permalink
FX Copy and Paste, Preset out of the widget
Browse files Browse the repository at this point in the history
1. FX Preset Management to common/FxUserPresetManager
2. Move copy and paste into the same manager (rather than storage).
Closes surge-synthesizer#2130
  • Loading branch information
baconpaul committed Jun 4, 2021
1 parent a29e44c commit 177f44f
Show file tree
Hide file tree
Showing 6 changed files with 479 additions and 374 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ set(SURGE_SHARED_SOURCES
src/common/dsp/WavetableScriptEvaluator.cpp
src/common/CPUFeatures.cpp
src/common/DebugHelpers.cpp
src/common/FxPresetAndClipboardManager.cpp
src/common/LuaSupport.cpp
src/common/ModulatorPresetManager.cpp
src/common/Parameter.cpp
Expand Down
375 changes: 375 additions & 0 deletions src/common/FxPresetAndClipboardManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,375 @@
/*
** Surge Synthesizer is Free and Open Source Software
**
** Surge is made available under the Gnu General Public License, v3.0
** https://www.gnu.org/licenses/gpl-3.0.en.html
**
** Copyright 2004-2021 by various individuals as described by the Git transaction log
**
** All source at: https://github.com/surge-synthesizer/surge.git
**
** Surge was a commercial product from 2004-2018, with Copyright and ownership
** in that period held by Claes Johanson at Vember Audio. Claes made Surge
** open source in September 2018.
*/

#include "FxPresetAndClipboardManager.h"
#include "StringOps.h"
#include "Effect.h"

namespace Surge
{
namespace FxUserPreset
{

static std::unordered_map<int, std::vector<Preset>> scannedPresets;
static bool haveScannedPresets = false;

std::unordered_map<int, std::vector<Preset>> getPresetsByType() { return scannedPresets; }

void forcePresetRescan(SurgeStorage *storage)
{
scannedPresets.clear();
haveScannedPresets = true;

auto ud = storage->userFXPath;

std::vector<fs::path> sfxfiles;

std::deque<fs::path> workStack;
workStack.push_back(fs::path(ud));

try
{
while (!workStack.empty())
{
auto top = workStack.front();
workStack.pop_front();
if (fs::is_directory(top))
{
for (auto &d : fs::directory_iterator(top))
{
if (fs::is_directory(d))
{
workStack.push_back(d);
}
else if (path_to_string(d.path().extension()) == ".srgfx")
{
sfxfiles.push_back(d.path());
}
}
}
}
}
catch (const fs::filesystem_error &e)
{
std::ostringstream oss;
oss << "Experienced file system error when scanning user FX. " << e.what();

if (storage)
storage->reportError(oss.str(), "FileSystem Error");
}

for (const auto &f : sfxfiles)
{
{
Preset preset;
preset.file = path_to_string(f);
TiXmlDocument d;
int t;

if (!d.LoadFile(f))
goto badPreset;

auto r = TINYXML_SAFE_TO_ELEMENT(d.FirstChild("single-fx"));

if (!r)
goto badPreset;

auto s = TINYXML_SAFE_TO_ELEMENT(r->FirstChild("snapshot"));

if (!s)
goto badPreset;

if (!s->Attribute("name"))
goto badPreset;

preset.name = s->Attribute("name");

if (s->QueryIntAttribute("type", &t) != TIXML_SUCCESS)
goto badPreset;

preset.type = t;

for (int i = 0; i < n_fx_params; ++i)
{
double fl;
std::string p = "p";

if (s->QueryDoubleAttribute((p + std::to_string(i)).c_str(), &fl) == TIXML_SUCCESS)
{
preset.p[i] = fl;
}

if (s->QueryDoubleAttribute((p + std::to_string(i) + "_temposync").c_str(), &fl) ==
TIXML_SUCCESS &&
fl != 0)
{
preset.ts[i] = true;
}

if (s->QueryDoubleAttribute((p + std::to_string(i) + "_extend_range").c_str(),
&fl) == TIXML_SUCCESS &&
fl != 0)
{
preset.er[i] = true;
}

if (s->QueryDoubleAttribute((p + std::to_string(i) + "_deactivated").c_str(),
&fl) == TIXML_SUCCESS &&
fl != 0)
{
preset.da[i] = true;
}
}

if (scannedPresets.find(preset.type) == scannedPresets.end())
{
scannedPresets[preset.type] = std::vector<Preset>();
}

scannedPresets[preset.type].push_back(preset);
}

badPreset:;
}

for (auto &a : scannedPresets)
{
std::sort(a.second.begin(), a.second.end(), [](const Preset &a, const Preset &b) {
if (a.type == b.type)
{
return _stricmp(a.name.c_str(), b.name.c_str()) < 0;
}
else
{
return a.type < b.type;
}
});
}
}

std::vector<Preset> getPresetsForSingleType(int id)
{
if (scannedPresets.find(id) == scannedPresets.end())
{
return std::vector<Preset>();
}
return scannedPresets[id];
}

bool hasPresetsForSingleType(int id) { return scannedPresets.find(id) != scannedPresets.end(); }

void saveFxIn(SurgeStorage *storage, FxStorage *fx, const std::string &s)
{
char fxName[TXT_SIZE];
fxName[0] = 0;
strxcpy(fxName, s.c_str(), TXT_SIZE);

if (strlen(fxName) == 0)
{
return;
}

if (!Surge::Storage::isValidName(fxName))
{
return;
}

int ti = fx->type.val.i;

std::ostringstream oss;
oss << storage->userFXPath << PATH_SEPARATOR << fx_type_names[ti] << PATH_SEPARATOR;

auto pn = oss.str();
fs::create_directories(string_to_path(pn));

auto fn = pn + fxName + ".srgfx";
std::ofstream pfile(fn, std::ios::out);
if (!pfile.is_open())
{
storage->reportError(std::string("Unable to open FX preset file '") + fn + "' for writing!",
"Error");
return;
}

// this used to say streaming_versio before (was a typo)
// make sure both variants are checked when checking sv in the future on patch load
pfile << "<single-fx streaming_version=\"" << ff_revision << "\">\n";

// take care of 5 special XML characters
std::string fxNameSub(fxName);
Surge::Storage::findReplaceSubstring(fxNameSub, std::string("&"), std::string("&amp;"));
Surge::Storage::findReplaceSubstring(fxNameSub, std::string("<"), std::string("&lt;"));
Surge::Storage::findReplaceSubstring(fxNameSub, std::string(">"), std::string("&gt;"));
Surge::Storage::findReplaceSubstring(fxNameSub, std::string("\""), std::string("&quot;"));
Surge::Storage::findReplaceSubstring(fxNameSub, std::string("'"), std::string("&apos;"));

pfile << " <snapshot name=\"" << fxNameSub.c_str() << "\" \n";

pfile << " type=\"" << fx->type.val.i << "\"\n";
for (int i = 0; i < n_fx_params; ++i)
{
if (fx->p[i].ctrltype != ct_none)
{
switch (fx->p[i].valtype)
{
case vt_float:
pfile << " p" << i << "=\"" << fx->p[i].val.f << "\"\n";
break;
case vt_int:
pfile << " p" << i << "=\"" << fx->p[i].val.i << "\"\n";
break;
}

if (fx->p[i].can_temposync() && fx->p[i].temposync)
{
pfile << " p" << i << "_temposync=\"1\"\n";
}
if (fx->p[i].can_extend_range() && fx->p[i].extend_range)
{
pfile << " p" << i << "_extend_range=\"1\"\n";
}
if (fx->p[i].can_deactivate() && fx->p[i].deactivated)
{
pfile << " p" << i << "_deactivated=\"1\"\n";
}
}
}

pfile << " />\n";
pfile << "</single-fx>\n";
pfile.close();

Surge::FxUserPreset::forcePresetRescan(storage);
}

void loadPresetOnto(const Preset &p, SurgeStorage *storage, FxStorage *fxbuffer)
{
fxbuffer->type.val.i = p.type;

Effect *t_fx = spawn_effect(fxbuffer->type.val.i, storage, fxbuffer, 0);

if (t_fx)
{
t_fx->init_ctrltypes();
t_fx->init_default_values();
delete t_fx;
}

for (int i = 0; i < n_fx_params; i++)
{
switch (fxbuffer->p[i].valtype)
{
case vt_float:
{
fxbuffer->p[i].val.f = p.p[i];
}
break;
case vt_int:
fxbuffer->p[i].val.i = (int)p.p[i];
break;
default:
break;
}
fxbuffer->p[i].temposync = (int)p.ts[i];
fxbuffer->p[i].extend_range = (int)p.er[i];
fxbuffer->p[i].deactivated = (int)p.da[i];
}
}
} // namespace FxUserPreset

namespace FxClipboard
{
Clipboard::Clipboard() {}

void copyFx(SurgeStorage *storage, FxStorage *fx, Clipboard &cb)
{
cb.fxCopyPaste.clear();
cb.fxCopyPaste.resize(n_fx_params * 4 + 1); // type then (val; ts; extend; deact)
cb.fxCopyPaste[0] = fx->type.val.i;
for (int i = 0; i < n_fx_params; ++i)
{
int vp = i * 4 + 1;
int tp = i * 4 + 2;
int xp = i * 4 + 3;
int dp = i * 4 + 4;

switch (fx->p[i].valtype)
{
case vt_float:
cb.fxCopyPaste[vp] = fx->p[i].val.f;
break;
case vt_int:
cb.fxCopyPaste[vp] = fx->p[i].val.i;
break;
}

cb.fxCopyPaste[tp] = fx->p[i].temposync;
cb.fxCopyPaste[xp] = fx->p[i].extend_range;
cb.fxCopyPaste[dp] = fx->p[i].deactivated;
}
}

void pasteFx(SurgeStorage *storage, FxStorage *fxbuffer, Clipboard &cb)
{
if (cb.fxCopyPaste.empty())
return;

fxbuffer->type.val.i = (int)cb.fxCopyPaste[0];

Effect *t_fx = spawn_effect(fxbuffer->type.val.i, storage, fxbuffer, 0);
if (t_fx)
{
t_fx->init_ctrltypes();
t_fx->init_default_values();
delete t_fx;
}

for (int i = 0; i < n_fx_params; i++)
{
int vp = i * 4 + 1;
int tp = i * 4 + 2;
int xp = i * 4 + 3;
int dp = i * 4 + 4;

switch (fxbuffer->p[i].valtype)
{
case vt_float:
{
fxbuffer->p[i].val.f = cb.fxCopyPaste[vp];
if (fxbuffer->p[i].val.f < fxbuffer->p[i].val_min.f)
{
fxbuffer->p[i].val.f = fxbuffer->p[i].val_min.f;
}
if (fxbuffer->p[i].val.f > fxbuffer->p[i].val_max.f)
{
fxbuffer->p[i].val.f = fxbuffer->p[i].val_max.f;
}
}
break;
case vt_int:
fxbuffer->p[i].val.i = (int)cb.fxCopyPaste[vp];
break;
default:
break;
}
fxbuffer->p[i].temposync = (int)cb.fxCopyPaste[tp];
fxbuffer->p[i].extend_range = (int)cb.fxCopyPaste[xp];
fxbuffer->p[i].deactivated = (int)cb.fxCopyPaste[dp];
}

cb.fxCopyPaste.clear();
}
} // namespace FxClipboard
} // namespace Surge
Loading

0 comments on commit 177f44f

Please sign in to comment.