Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

354 lines
7.7KB

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