gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

lv2PresetExport.cpp (6778B)


      1 #include "lv2PresetExport.h"
      2 
      3 #include <fstream>
      4 #include <map>
      5 
      6 #include "baseLib/filesystem.h"
      7 
      8 namespace synthLib
      9 {
     10 	namespace
     11 	{
     12 		const std::string g_keyBankName = "%BANKNAME%";
     13 		const std::string g_keyBankId = "%BANKID%";
     14 		const std::string g_keyPluginId = "%PLUGINID%";
     15 		const std::string g_keyPresetFilename = "%PRESETFILENAME%";
     16 		const std::string g_keyPresetName = "%PRESETNAME%";
     17 		const std::string g_keyPresetData = "%PRESETDATA%";
     18 
     19 		const std::string g_manifestHeader =
     20 			"@prefix atom: <http://lv2plug.in/ns/ext/atom#> .\n"
     21 			"@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
     22 			"@prefix pset: <http://lv2plug.in/ns/ext/presets#> .\n"
     23 			"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
     24 			"@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
     25 			"@prefix state: <http://lv2plug.in/ns/ext/state#> .\n"
     26 			"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n"
     27 			"\n";
     28 
     29 		const std::string g_manifestBankEntry =
     30 			"<" + g_keyBankId + ">\n"
     31 			"	lv2:appliesTo <" + g_keyPluginId + "> ;\n"
     32 			"	a pset:Bank ;\n"
     33 			"	rdfs:label \"" + g_keyBankName + "\" .\n"
     34 			"\n";
     35 
     36 		const std::string g_manifestPresetEntry =
     37 		"<" + g_keyPresetFilename + ">\n"
     38 		"	lv2:appliesTo <" + g_keyPluginId + "> ;\n"
     39 		"	a pset:Preset ;\n"
     40 		"	pset:bank <" + g_keyBankId + "> ;\n"
     41 		"	rdfs:seeAlso <" + g_keyPresetFilename + "> .\n"
     42 		"\n";
     43 
     44 		const std::string g_presetFile = 
     45 			"@prefix atom: <http://lv2plug.in/ns/ext/atom#> .\n"
     46 			"@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
     47 			"@prefix pset: <http://lv2plug.in/ns/ext/presets#> .\n"
     48 			"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
     49 			"@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
     50 			"@prefix state: <http://lv2plug.in/ns/ext/state#> .\n"
     51 			"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n"
     52 			"\n"
     53 			"<>\n"
     54 			"	a pset:Preset ;\n"
     55 			"	lv2:appliesTo <" + g_keyPluginId + "> ;\n"
     56 			"	rdfs:label \"" + g_keyPresetName + "\" ;\n"
     57 			"	state:state [\n"
     58 			"		<" + g_keyPluginId + ":StateString> \"" + g_keyPresetData + "\"\n"
     59 			"	] .\n"
     60 			"\n";
     61 
     62 		std::string toFilename(std::string _name)
     63 		{
     64 			for (char& c : _name)
     65 			{
     66 				if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
     67 					continue;
     68 				c = '_';
     69 			}
     70 			return _name;
     71 		}
     72 
     73 		std::string escapeValue(const std::string& _value)
     74 		{
     75 			std::string escaped;
     76 			escaped.reserve(_value.size());
     77 			for (const auto c : _value)
     78 			{
     79 				switch (c)
     80 				{
     81 					case '"':	escaped.append("\\\""); break;
     82 					case '\\':	escaped.append("\\\\"); break;
     83 					default:	escaped.push_back(c); break;
     84 				}
     85 			}
     86 			return escaped;
     87 		}
     88 
     89 		std::string replaceVariables(std::string _string, const std::map<std::string, std::string>& _variables)
     90 		{
     91 			for (const auto& [key, value] : _variables)
     92 			{
     93 				while(true)
     94 				{
     95 					const auto pos = _string.find(key);
     96 					if(pos == std::string::npos)
     97 						break;
     98 					_string.erase(pos, key.size());
     99 					_string.insert(pos, escapeValue(value));
    100 				}
    101 			}
    102 			return _string;
    103 		}
    104 
    105 		int getBitRange (const std::vector<uint8_t>& _data, size_t bitRangeStart, size_t numBits)
    106 		{
    107 		    int res = 0;
    108 
    109 		    auto byte = bitRangeStart >> 3;
    110 		    auto offsetInByte = bitRangeStart & 7;
    111 		    size_t bitsSoFar = 0;
    112 
    113 		    while (numBits > 0 && (size_t) byte < _data.size())
    114 		    {
    115 			    const auto bitsThisTime = std::min(numBits, 8 - offsetInByte);
    116 		        const int mask = (0xff >> (8 - bitsThisTime)) << offsetInByte;
    117 
    118 		        res |= (((_data[byte] & mask) >> offsetInByte) << bitsSoFar);
    119 
    120 		        bitsSoFar += bitsThisTime;
    121 		        numBits -= bitsThisTime;
    122 		        ++byte;
    123 		        offsetInByte = 0;
    124 		    }
    125 
    126 		    return res;
    127 		}
    128 
    129 		constexpr char base64EncodingTable[] = ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+";
    130 
    131 		std::string dataToJuceEncodedString(const std::vector<uint8_t>& _data)
    132 		{
    133 		    const auto numChars = ((_data.size() << 3) + 5) / 6;
    134 
    135 			// store the length, followed by a '.', and then the data
    136 		    std::string destString = std::to_string(numChars);
    137 			destString.reserve(_data.size());
    138 
    139 		    const auto initialLen = destString.size();
    140 		    destString.reserve(initialLen + 2 + numChars);
    141 
    142 			destString.push_back('.');
    143 
    144 			for (size_t i = 0; i < numChars; ++i)
    145 				destString.push_back(base64EncodingTable[getBitRange (_data, i * 6, 6)]);
    146 
    147 			return destString;
    148 		}
    149 	}
    150 
    151 	bool Lv2PresetExport::exportPresets(const std::string& _outputPath, const std::string& _pluginId, const std::vector<Bank>& _banks)
    152 	{
    153 		const auto path = baseLib::filesystem::validatePath(_outputPath);
    154 		baseLib::filesystem::createDirectory(path);
    155 
    156 		for (const auto& bank : _banks)
    157 		{
    158 			if(!exportPresets(getBankPath(path, bank.name), _pluginId, bank))
    159 				return false;
    160 		}
    161 		return true;
    162 	}
    163 
    164 	bool Lv2PresetExport::exportPresets(const std::string& _outputPath, const std::string& _pluginId, const Bank& _bank)
    165 	{
    166 		const auto path = baseLib::filesystem::validatePath(_outputPath);
    167 		baseLib::filesystem::createDirectory(path);
    168 		std::ofstream manifest(getManifestFilename(path));
    169 		if(!manifest.is_open())
    170 			return false;
    171 
    172 		std::map<std::string, std::string> bankVars;
    173 
    174 		bankVars.insert({g_keyPluginId, _pluginId});
    175 		bankVars.insert({g_keyBankId, toFilename(_bank.name)});
    176 		bankVars.insert({g_keyBankName, _bank.name});
    177 
    178 		manifest << replaceVariables(g_manifestHeader, bankVars);
    179 		manifest << replaceVariables(g_manifestBankEntry, bankVars);
    180 
    181 		for (const auto& preset : _bank.presets)
    182 		{
    183 			const auto presetFilename = toFilename(preset.name) + ".ttl";
    184 
    185 			auto presetVars = bankVars;
    186 
    187 			presetVars.insert({g_keyPresetName, preset.name});
    188 			presetVars.insert({g_keyPresetFilename, presetFilename});
    189 			presetVars.insert({g_keyPresetData, dataToJuceEncodedString(preset.data)});
    190 
    191 			std::ofstream presetFile(path + presetFilename);
    192 
    193 			if(!presetFile.is_open())
    194 				return false;
    195 
    196 			manifest << replaceVariables(g_manifestPresetEntry, presetVars);
    197 
    198 			presetFile << replaceVariables(g_presetFile, presetVars);
    199 
    200 			presetFile.close();
    201 		}
    202 
    203 		return true;
    204 	}
    205 
    206 	std::string Lv2PresetExport::getBankPath(const std::string& _outputPath, const std::string& _bankName)
    207 	{
    208 		return baseLib::filesystem::validatePath(_outputPath) + getBankFilename(_bankName) + ".lv2/";
    209 	}
    210 
    211 	std::string Lv2PresetExport::getManifestFilename(const std::string& _path)
    212 	{
    213 		return baseLib::filesystem::validatePath(_path) + "manifest.ttl";
    214 	}
    215 
    216 	std::string Lv2PresetExport::getBankFilename(const std::string& _bankName)
    217 	{
    218 		return toFilename(_bankName);
    219 	}
    220 
    221 	bool Lv2PresetExport::manifestFileExists(const std::string& _path)
    222 	{
    223 		const auto manifestFile = getManifestFilename(_path);
    224 
    225 		FILE* const hFile = fopen(manifestFile.c_str(), "rb");
    226 		if(!hFile)
    227 			return false;
    228 		(void)fclose(hFile);
    229 		return true;
    230 	}
    231 }