BankDb.cpp (7972B)
1 #include "BankDb.h" 2 #include "XMLwrapper.h" 3 #include "Util.h" 4 #include "../globals.h" 5 #include <cstring> 6 #include <dirent.h> 7 #include <sys/stat.h> 8 9 namespace zyn { 10 11 static const char* INSTRUMENT_EXTENSION = ".xiz"; 12 13 using std::string; 14 typedef BankDb::svec svec; 15 typedef BankDb::bvec bvec; 16 17 BankEntry::BankEntry(void) 18 :id(0), add(false), pad(false), sub(false), time(0) 19 {} 20 21 bool platform_strcasestr(const char *hay, const char *needle) 22 { 23 int n = strlen(hay); 24 int m = strlen(needle); 25 for(int i=0; i<n; i++) { 26 int good = 1; 27 for(int j=0; j<m; ++j) { 28 if(toupper(hay[i+j]) != toupper(needle[j])) { 29 good = 0; 30 break; 31 } 32 33 } 34 if(good) 35 return 1; 36 } 37 return 0; 38 39 } 40 41 bool sfind(std::string hay, std::string needle) 42 { 43 //return strcasestr(hay.c_str(), needle.c_str()); 44 return platform_strcasestr(hay.c_str(), needle.c_str()); 45 return false; 46 } 47 48 bool BankEntry::match(string s) const 49 { 50 if(s == "#pad") 51 return pad; 52 else if(s == "#sub") 53 return sub; 54 else if(s == "#add") 55 return add; 56 return sfind(file,s) || sfind(name,s) || sfind(bank, s) || 57 sfind(type, s) || sfind(comments,s) || sfind(author,s); 58 } 59 60 bool BankEntry::operator<(const BankEntry &b) const 61 { 62 return (this->bank+this->file) < (b.bank+b.file); 63 } 64 65 static svec split(string s) 66 { 67 svec vec; 68 string ss; 69 for(char c:s) { 70 if(isspace(c) && !ss.empty()) { 71 vec.push_back(ss); 72 ss.clear(); 73 } else if(!isspace(c)) { 74 ss.push_back(c); 75 } 76 } 77 if(!ss.empty()) 78 vec.push_back(ss); 79 80 return vec; 81 } 82 83 //static string line(string s) 84 //{ 85 // string ss; 86 // for(char c:s) { 87 // if(c != '\n') 88 // ss.push_back(c); 89 // else 90 // return ss; 91 // } 92 // return ss; 93 //} 94 95 bvec BankDb::search(std::string ss) const 96 { 97 bvec vec; 98 const svec sterm = split(ss); 99 for(auto field:fields) { 100 bool match = true; 101 for(auto s:sterm) 102 match &= field.match(s); 103 if(match) 104 vec.push_back(field); 105 } 106 107 std::sort(vec.begin(), vec.end()); 108 109 return vec; 110 } 111 112 void BankDb::addBankDir(std::string bnk) 113 { 114 bool repeat = false; 115 for(auto b:banks) 116 repeat |= b == bnk; 117 118 if(!repeat) 119 banks.push_back(bnk); 120 } 121 122 void BankDb::clear(void) 123 { 124 banks.clear(); 125 fields.clear(); 126 } 127 128 static std::string getCacheName(void) 129 { 130 char name[512] = {}; 131 snprintf(name, sizeof(name), "%s%s", getenv("HOME"), 132 "/.zynaddsubfx-bank-cache.xml"); 133 return name; 134 } 135 136 static bvec loadCache(void) 137 { 138 bvec cache; 139 XMLwrapper xml; 140 xml.loadXMLfile(getCacheName()); 141 if(xml.enterbranch("bank-cache")) { 142 auto nodes = xml.getBranch(); 143 144 for(auto node:nodes) { 145 BankEntry be; 146 #define bind(x,y) if(node.has(#x)) {be.x = y(node[#x].c_str());} 147 bind(file, string); 148 bind(bank, string); 149 bind(name, string); 150 bind(comments, string); 151 bind(author, string); 152 bind(type, atoi); 153 bind(id, atoi); 154 bind(add, atoi); 155 bind(pad, atoi); 156 bind(sub, atoi); 157 bind(time, atoi); 158 #undef bind 159 cache.push_back(be); 160 } 161 } 162 return cache; 163 } 164 165 static void saveCache(bvec vec) 166 { 167 XMLwrapper xml; 168 xml.beginbranch("bank-cache"); 169 for(auto value:vec) { 170 XmlNode binding("instrument-entry"); 171 #define bind(x) binding[#x] = to_s(value.x); 172 bind(file); 173 bind(bank); 174 bind(name); 175 bind(comments); 176 bind(author); 177 bind(type); 178 bind(id); 179 bind(add); 180 bind(pad); 181 bind(sub); 182 bind(time); 183 #undef bind 184 xml.add(binding); 185 } 186 xml.endbranch(); 187 xml.saveXMLfile(getCacheName(), 0); 188 } 189 190 void BankDb::scanBanks(void) 191 { 192 fields.clear(); 193 bvec cache = loadCache(); 194 bmap cc; 195 for(auto c:cache) 196 cc[c.bank + c.file] = c; 197 198 bvec ncache; 199 for(auto bank:banks) 200 { 201 DIR *dir = opendir(bank.c_str()); 202 203 if(!dir) 204 continue; 205 206 207 struct dirent *fn; 208 209 while((fn = readdir(dir))) { 210 const char *filename = fn->d_name; 211 212 //check for extension 213 if(!strstr(filename, INSTRUMENT_EXTENSION)) 214 continue; 215 216 auto xiz = processXiz(filename, bank, cc); 217 fields.push_back(xiz); 218 ncache.push_back(xiz); 219 } 220 221 closedir(dir); 222 223 } 224 saveCache(ncache); 225 } 226 227 BankEntry BankDb::processXiz(std::string filename, 228 std::string bank, bmap &cache) const 229 { 230 string fname = bank+filename; 231 232 //Grab a timestamp 233 struct stat st; 234 int time = 0; 235 236 //gah windows, just implement the darn standard APIs 237 #ifndef WIN32 238 int ret = lstat(fname.c_str(), &st); 239 if(ret != -1) 240 # ifdef __APPLE__ 241 time = st.st_mtimespec.tv_sec; 242 # else 243 time = st.st_mtim.tv_sec; 244 # endif 245 #else 246 int ret = 0; 247 time = rand(); 248 #endif 249 250 251 //quickly check if the file exists in the cache and if it is up-to-date 252 if(cache.find(fname) != cache.end() && 253 cache[fname].time == time) 254 return cache[fname]; 255 256 257 258 //verify if the name is like this NNNN-name (where N is a digit) 259 int no = 0; 260 unsigned int startname = 0; 261 262 for(unsigned int i = 0; i < 4; ++i) { 263 if(filename.length() <= i) 264 break; 265 266 if(isdigit(filename[i])) { 267 no = no * 10 + (filename[i] - '0'); 268 startname++; 269 } 270 } 271 272 if(startname + 1 < filename.length()) 273 startname++; //to take out the "-" 274 275 std::string name = filename; 276 277 //remove the file extension 278 for(int i = name.size() - 1; i >= 2; i--) { 279 if(name[i] == '.') { 280 name = name.substr(0, i); 281 break; 282 } 283 } 284 285 286 BankEntry entry; 287 entry.file = filename; 288 entry.bank = bank; 289 entry.id = no; 290 entry.time = time; 291 292 if(no != 0) //the instrument position in the bank is found 293 entry.name = name.substr(startname); 294 else 295 entry.name = name; 296 297 const char *types[] = { 298 "None", 299 "Piano", 300 "Chromatic Percussion", 301 "Organ", 302 "Guitar", 303 "Bass", 304 "Solo Strings", 305 "Ensemble", 306 "Brass", 307 "Reed", 308 "Pipe", 309 "Synth Lead", 310 "Synth Pad", 311 "Synth Effects", 312 "Ethnic", 313 "Percussive", 314 "Sound Effects", 315 }; 316 317 318 //Try to obtain other metadata (expensive) 319 XMLwrapper xml; 320 ret = xml.loadXMLfile(fname); 321 if(xml.enterbranch("INSTRUMENT")) { 322 if(xml.enterbranch("INFO")) { 323 char author[1024]; 324 char comments[1024]; 325 int type = 0; 326 xml.getparstr("author", author, 1024); 327 xml.getparstr("comments", comments, 1024); 328 type = xml.getpar("type", 0, 0, 16); 329 entry.author = author; 330 entry.comments = comments; 331 entry.type = types[type]; 332 xml.exitbranch(); 333 } 334 if(xml.enterbranch("INSTRUMENT_KIT")) { 335 for(int i = 0; i < NUM_KIT_ITEMS; ++i) { 336 if(xml.enterbranch("INSTRUMENT_KIT_ITEM", i) == 0) { 337 entry.add |= xml.getparbool("add_enabled", false); 338 entry.sub |= xml.getparbool("sub_enabled", false); 339 entry.pad |= xml.getparbool("pad_enabled", false); 340 xml.exitbranch(); 341 } 342 } 343 xml.exitbranch(); 344 } 345 xml.exitbranch(); 346 } 347 348 //printf("Bank Entry:\n"); 349 //printf("\tname - %s\n", entry.name.c_str()); 350 //printf("\tauthor - %s\n", line(entry.author).c_str()); 351 //printf("\tbank - %s\n", entry.bank.c_str()); 352 //printf("\tadd/pad/sub - %d/%d/%d\n", entry.add, entry.pad, entry.sub); 353 354 return entry; 355 } 356 357 }