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.

Bank.cpp 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. /*
  2. ZynAddSubFX - a software synthesizer
  3. Bank.cpp - Instrument Bank
  4. Copyright (C) 2002-2005 Nasca Octavian Paul
  5. Copyright (C) 2010-2010 Mark McCurry
  6. Author: Nasca Octavian Paul
  7. Mark McCurry
  8. This program is free software; you can redistribute it and/or modify
  9. it under the terms of version 2 of the GNU General Public License
  10. as published by the Free Software Foundation.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. GNU General Public License (version 2 or later) for more details.
  15. You should have received a copy of the GNU General Public License (version 2)
  16. along with this program; if not, write to the Free Software Foundation,
  17. Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. */
  19. #include "Bank.h"
  20. #include <string.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <dirent.h>
  24. #include <sys/stat.h>
  25. #include <algorithm>
  26. #include <iostream>
  27. #include <sys/types.h>
  28. #include <fcntl.h>
  29. #include <unistd.h>
  30. #include <errno.h>
  31. #include "Config.h"
  32. #include "Util.h"
  33. #include "Part.h"
  34. #define INSTRUMENT_EXTENSION ".xiz"
  35. //if this file exists into a directory, this make the directory to be considered as a bank, even if it not contains a instrument file
  36. #define FORCE_BANK_DIR_FILE ".bankdir"
  37. using namespace std;
  38. Bank::Bank()
  39. :defaultinsname(" ")
  40. {
  41. clearbank();
  42. bankfiletitle = dirname;
  43. loadbank(config.cfg.currentBankDir);
  44. }
  45. Bank::~Bank()
  46. {
  47. clearbank();
  48. }
  49. /*
  50. * Get the name of an instrument from the bank
  51. */
  52. string Bank::getname(unsigned int ninstrument)
  53. {
  54. if(emptyslot(ninstrument))
  55. return defaultinsname;
  56. return ins[ninstrument].name;
  57. }
  58. /*
  59. * Get the numbered name of an instrument from the bank
  60. */
  61. string Bank::getnamenumbered(unsigned int ninstrument)
  62. {
  63. if(emptyslot(ninstrument))
  64. return defaultinsname;
  65. return stringFrom(ninstrument + 1) + ". " + getname(ninstrument);
  66. }
  67. /*
  68. * Changes the name of an instrument (and the filename)
  69. */
  70. void Bank::setname(unsigned int ninstrument, const string &newname, int newslot)
  71. {
  72. if(emptyslot(ninstrument))
  73. return;
  74. string newfilename;
  75. char tmpfilename[100 + 1];
  76. tmpfilename[100] = 0;
  77. if(newslot >= 0)
  78. snprintf(tmpfilename, 100, "%4d-%s", newslot + 1, newname.c_str());
  79. else
  80. snprintf(tmpfilename, 100, "%4d-%s", ninstrument + 1, newname.c_str());
  81. //add the zeroes at the start of filename
  82. for(int i = 0; i < 4; ++i)
  83. if(tmpfilename[i] == ' ')
  84. tmpfilename[i] = '0';
  85. newfilename = dirname + '/' + legalizeFilename(tmpfilename) + ".xiz";
  86. rename(ins[ninstrument].filename.c_str(), newfilename.c_str());
  87. ins[ninstrument].filename = newfilename;
  88. ins[ninstrument].name = newname;
  89. }
  90. /*
  91. * Check if there is no instrument on a slot from the bank
  92. */
  93. bool Bank::emptyslot(unsigned int ninstrument)
  94. {
  95. if(ninstrument >= BANK_SIZE)
  96. return true;
  97. if(ins[ninstrument].filename.empty())
  98. return true;
  99. if(ins[ninstrument].used)
  100. return false;
  101. else
  102. return true;
  103. }
  104. /*
  105. * Removes the instrument from the bank
  106. */
  107. void Bank::clearslot(unsigned int ninstrument)
  108. {
  109. if(emptyslot(ninstrument))
  110. return;
  111. remove(ins[ninstrument].filename.c_str());
  112. deletefrombank(ninstrument);
  113. }
  114. /*
  115. * Save the instrument to a slot
  116. */
  117. void Bank::savetoslot(unsigned int ninstrument, Part *part)
  118. {
  119. clearslot(ninstrument);
  120. const int maxfilename = 200;
  121. char tmpfilename[maxfilename + 20];
  122. ZERO(tmpfilename, maxfilename + 20);
  123. snprintf(tmpfilename,
  124. maxfilename,
  125. "%4d-%s",
  126. ninstrument + 1,
  127. (char *)part->Pname);
  128. //add the zeroes at the start of filename
  129. for(int i = 0; i < 4; ++i)
  130. if(tmpfilename[i] == ' ')
  131. tmpfilename[i] = '0';
  132. string filename = dirname + '/' + legalizeFilename(tmpfilename) + ".xiz";
  133. remove(filename.c_str());
  134. part->saveXML(filename.c_str());
  135. addtobank(ninstrument, legalizeFilename(tmpfilename) + ".xiz", (char *) part->Pname);
  136. }
  137. /*
  138. * Loads the instrument from the bank
  139. */
  140. void Bank::loadfromslot(unsigned int ninstrument, Part *part)
  141. {
  142. if(emptyslot(ninstrument))
  143. return;
  144. part->AllNotesOff();
  145. part->defaultsinstrument();
  146. part->loadXMLinstrument(ins[ninstrument].filename.c_str());
  147. }
  148. /*
  149. * Makes current a bank directory
  150. */
  151. int Bank::loadbank(string bankdirname)
  152. {
  153. DIR *dir = opendir(bankdirname.c_str());
  154. clearbank();
  155. if(dir == NULL)
  156. return -1;
  157. dirname = bankdirname;
  158. bankfiletitle = dirname;
  159. struct dirent *fn;
  160. while((fn = readdir(dir))) {
  161. const char *filename = fn->d_name;
  162. //check for extension
  163. if(strstr(filename, INSTRUMENT_EXTENSION) == NULL)
  164. continue;
  165. //verify if the name is like this NNNN-name (where N is a digit)
  166. int no = 0;
  167. unsigned int startname = 0;
  168. for(unsigned int i = 0; i < 4; ++i) {
  169. if(strlen(filename) <= i)
  170. break;
  171. if((filename[i] >= '0') && (filename[i] <= '9')) {
  172. no = no * 10 + (filename[i] - '0');
  173. startname++;
  174. }
  175. }
  176. if((startname + 1) < strlen(filename))
  177. startname++; //to take out the "-"
  178. string name = filename;
  179. //remove the file extension
  180. for(int i = name.size() - 1; i >= 2; i--)
  181. if(name[i] == '.') {
  182. name = name.substr(0, i);
  183. break;
  184. }
  185. if(no != 0) //the instrument position in the bank is found
  186. addtobank(no - 1, filename, name.substr(startname));
  187. else
  188. addtobank(-1, filename, name);
  189. }
  190. closedir(dir);
  191. if(!dirname.empty())
  192. config.cfg.currentBankDir = dirname;
  193. return 0;
  194. }
  195. /*
  196. * Makes a new bank, put it on a file and makes it current bank
  197. */
  198. int Bank::newbank(string newbankdirname)
  199. {
  200. string bankdir;
  201. bankdir = config.cfg.bankRootDirList[0];
  202. if(((bankdir[bankdir.size() - 1]) != '/')
  203. && ((bankdir[bankdir.size() - 1]) != '\\'))
  204. bankdir += "/";
  205. bankdir += newbankdirname;
  206. if(mkdir(bankdir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0)
  207. return -1;
  208. const string tmpfilename = bankdir + '/' + FORCE_BANK_DIR_FILE;
  209. FILE *tmpfile = fopen(tmpfilename.c_str(), "w+");
  210. fclose(tmpfile);
  211. return loadbank(bankdir);
  212. }
  213. /*
  214. * Check if the bank is locked (i.e. the file opened was readonly)
  215. */
  216. int Bank::locked()
  217. {
  218. return dirname.empty();
  219. }
  220. /*
  221. * Swaps a slot with another
  222. */
  223. void Bank::swapslot(unsigned int n1, unsigned int n2)
  224. {
  225. if((n1 == n2) || (locked()))
  226. return;
  227. if(emptyslot(n1) && (emptyslot(n2)))
  228. return;
  229. if(emptyslot(n1)) //change n1 to n2 in order to make
  230. swap(n1, n2);
  231. if(emptyslot(n2)) { //this is just a movement from slot1 to slot2
  232. setname(n1, getname(n1), n2);
  233. ins[n2] = ins[n1];
  234. ins[n1] = ins_t();
  235. }
  236. else { //if both slots are used
  237. if(ins[n1].name == ins[n2].name) //change the name of the second instrument if the name are equal
  238. ins[n2].name += "2";
  239. setname(n1, getname(n1), n2);
  240. setname(n2, getname(n2), n1);
  241. swap(ins[n2], ins[n1]);
  242. }
  243. }
  244. bool Bank::bankstruct::operator<(const bankstruct &b) const
  245. {
  246. return name < b.name;
  247. }
  248. /*
  249. * Re-scan for directories containing instrument banks
  250. */
  251. void Bank::rescanforbanks()
  252. {
  253. //remove old banks
  254. banks.clear();
  255. for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i)
  256. if(!config.cfg.bankRootDirList[i].empty())
  257. scanrootdir(config.cfg.bankRootDirList[i]);
  258. //sort the banks
  259. sort(banks.begin(), banks.end());
  260. //remove duplicate bank names
  261. int dupl = 0;
  262. for(int j = 0; j < (int) banks.size() - 1; ++j)
  263. for(int i = j + 1; i < (int) banks.size(); ++i) {
  264. if(banks[i].name == banks[j].name) {
  265. //add a [1] to the first bankname and [n] to others
  266. banks[i].name = banks[i].name + '['
  267. + stringFrom(dupl + 2) + ']';
  268. if(dupl == 0)
  269. banks[j].name += "[1]";
  270. dupl++;
  271. }
  272. else
  273. dupl = 0;
  274. }
  275. }
  276. // private stuff
  277. void Bank::scanrootdir(string rootdir)
  278. {
  279. DIR *dir = opendir(rootdir.c_str());
  280. if(dir == NULL)
  281. return;
  282. bankstruct bank;
  283. const char *separator = "/";
  284. if(rootdir.size()) {
  285. char tmp = rootdir[rootdir.size() - 1];
  286. if((tmp == '/') || (tmp == '\\'))
  287. separator = "";
  288. }
  289. struct dirent *fn;
  290. while((fn = readdir(dir))) {
  291. const char *dirname = fn->d_name;
  292. if(dirname[0] == '.')
  293. continue;
  294. bank.dir = rootdir + separator + dirname + '/';
  295. bank.name = dirname;
  296. //find out if the directory contains at least 1 instrument
  297. bool isbank = false;
  298. DIR *d = opendir(bank.dir.c_str());
  299. if(d == NULL)
  300. continue;
  301. struct dirent *fname;
  302. while((fname = readdir(d))) {
  303. if((strstr(fname->d_name, INSTRUMENT_EXTENSION) != NULL)
  304. || (strstr(fname->d_name, FORCE_BANK_DIR_FILE) != NULL)) {
  305. isbank = true;
  306. break; //could put a #instrument counter here instead
  307. }
  308. }
  309. if(isbank)
  310. banks.push_back(bank);
  311. closedir(d);
  312. }
  313. closedir(dir);
  314. }
  315. void Bank::clearbank()
  316. {
  317. for(int i = 0; i < BANK_SIZE; ++i)
  318. ins[i] = ins_t();
  319. bankfiletitle.clear();
  320. dirname.clear();
  321. }
  322. int Bank::addtobank(int pos, string filename, string name)
  323. {
  324. if((pos >= 0) && (pos < BANK_SIZE)) {
  325. if(ins[pos].used)
  326. pos = -1; //force it to find a new free position
  327. }
  328. else
  329. if(pos >= BANK_SIZE)
  330. pos = -1;
  331. if(pos < 0) //find a free position
  332. for(int i = BANK_SIZE - 1; i >= 0; i--)
  333. if(!ins[i].used) {
  334. pos = i;
  335. break;
  336. }
  337. if(pos < 0)
  338. return -1; //the bank is full
  339. deletefrombank(pos);
  340. ins[pos].used = true;
  341. ins[pos].name = name;
  342. ins[pos].filename = dirname + '/' + filename;
  343. //see if PADsynth is used
  344. if(config.cfg.CheckPADsynth) {
  345. XMLwrapper xml;
  346. xml.loadXMLfile(ins[pos].filename);
  347. ins[pos].info.PADsynth_used = xml.hasPadSynth();
  348. }
  349. else
  350. ins[pos].info.PADsynth_used = false;
  351. return 0;
  352. }
  353. bool Bank::isPADsynth_used(unsigned int ninstrument)
  354. {
  355. if(config.cfg.CheckPADsynth == 0)
  356. return 0;
  357. else
  358. return ins[ninstrument].info.PADsynth_used;
  359. }
  360. void Bank::deletefrombank(int pos)
  361. {
  362. if((pos < 0) || (pos >= BANK_SIZE))
  363. return;
  364. ins[pos] = ins_t();
  365. }
  366. Bank::ins_t::ins_t()
  367. :used(false), name(""), filename("")
  368. {
  369. info.PADsynth_used = false;
  370. }