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 }