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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  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 <cstring>
  21. #include <cstdio>
  22. #include <cstdlib>
  23. #include <dirent.h>
  24. #include <sys/stat.h>
  25. #include <algorithm>
  26. #include <sys/types.h>
  27. #include <fcntl.h>
  28. #include <unistd.h>
  29. #include <errno.h>
  30. #include "Config.h"
  31. #include "Util.h"
  32. #include "Part.h"
  33. #define INSTRUMENT_EXTENSION ".xiz"
  34. //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
  35. #define FORCE_BANK_DIR_FILE ".bankdir"
  36. using namespace std;
  37. Bank::Bank(Config *config)
  38. :bankpos(0), defaultinsname(" "), config(config),
  39. bank_msb(0), bank_lsb(0)
  40. {
  41. clearbank();
  42. bankfiletitle = dirname;
  43. rescanforbanks();
  44. loadbank(config->cfg.currentBankDir);
  45. for(unsigned i=0; i<banks.size(); ++i) {
  46. if(banks[i].dir == config->cfg.currentBankDir) {
  47. bankpos = i;
  48. break;
  49. }
  50. }
  51. }
  52. Bank::~Bank()
  53. {
  54. clearbank();
  55. }
  56. /*
  57. * Get the name of an instrument from the bank
  58. */
  59. string Bank::getname(unsigned int ninstrument)
  60. {
  61. if(emptyslot(ninstrument))
  62. return defaultinsname;
  63. return ins[ninstrument].name;
  64. }
  65. /*
  66. * Get the numbered name of an instrument from the bank
  67. */
  68. string Bank::getnamenumbered(unsigned int ninstrument)
  69. {
  70. if(emptyslot(ninstrument))
  71. return defaultinsname;
  72. return stringFrom(ninstrument + 1) + ". " + getname(ninstrument);
  73. }
  74. /*
  75. * Changes the name of an instrument (and the filename)
  76. */
  77. int Bank::setname(unsigned int ninstrument, const string &newname, int newslot)
  78. {
  79. if(emptyslot(ninstrument))
  80. return 0;
  81. string newfilename;
  82. char tmpfilename[100 + 1];
  83. tmpfilename[100] = 0;
  84. if(newslot >= 0)
  85. snprintf(tmpfilename, 100, "%4d-%s", newslot + 1, newname.c_str());
  86. else
  87. snprintf(tmpfilename, 100, "%4d-%s", ninstrument + 1, newname.c_str());
  88. //add the zeroes at the start of filename
  89. for(int i = 0; i < 4; ++i)
  90. if(tmpfilename[i] == ' ')
  91. tmpfilename[i] = '0';
  92. newfilename = dirname + legalizeFilename(tmpfilename) + ".xiz";
  93. int err = rename(ins[ninstrument].filename.c_str(), newfilename.c_str());
  94. if(err)
  95. return err;
  96. ins[ninstrument].filename = newfilename;
  97. ins[ninstrument].name = newname;
  98. return err;
  99. }
  100. /*
  101. * Check if there is no instrument on a slot from the bank
  102. */
  103. bool Bank::emptyslot(unsigned int ninstrument)
  104. {
  105. if(ninstrument >= BANK_SIZE)
  106. return true;
  107. if(ins[ninstrument].filename.empty())
  108. return true;
  109. return false;
  110. }
  111. /*
  112. * Removes the instrument from the bank
  113. */
  114. int Bank::clearslot(unsigned int ninstrument)
  115. {
  116. if(emptyslot(ninstrument))
  117. return 0;
  118. //no error when no file
  119. FILE *f = fopen(ins[ninstrument].filename.c_str(), "r");
  120. if(!f)
  121. return 0;
  122. fclose(f);
  123. int err = remove(ins[ninstrument].filename.c_str());
  124. if(!err)
  125. deletefrombank(ninstrument);
  126. return err;
  127. }
  128. /*
  129. * Save the instrument to a slot
  130. */
  131. int Bank::savetoslot(unsigned int ninstrument, Part *part)
  132. {
  133. int err = clearslot(ninstrument);
  134. if(err)
  135. return err;
  136. const int maxfilename = 200;
  137. char tmpfilename[maxfilename + 20];
  138. ZERO(tmpfilename, maxfilename + 20);
  139. snprintf(tmpfilename,
  140. maxfilename,
  141. "%04d-%s",
  142. ninstrument + 1,
  143. (char *)part->Pname);
  144. string filename = dirname + '/' + legalizeFilename(tmpfilename) + ".xiz";
  145. FILE *f = fopen(filename.c_str(), "r");
  146. if(f) {
  147. fclose(f);
  148. err = remove(filename.c_str());
  149. if(err)
  150. return err;
  151. }
  152. err = part->saveXML(filename.c_str());
  153. if(err)
  154. return err;
  155. addtobank(ninstrument, legalizeFilename(tmpfilename) + ".xiz", (char *) part->Pname);
  156. return 0;
  157. }
  158. /*
  159. * Loads the instrument from the bank
  160. */
  161. int Bank::loadfromslot(unsigned int ninstrument, Part *part)
  162. {
  163. if(emptyslot(ninstrument))
  164. return 0;
  165. part->AllNotesOff();
  166. part->defaultsinstrument();
  167. part->loadXMLinstrument(ins[ninstrument].filename.c_str());
  168. return 0;
  169. }
  170. /*
  171. * Makes current a bank directory
  172. */
  173. int Bank::loadbank(string bankdirname)
  174. {
  175. DIR *dir = opendir(bankdirname.c_str());
  176. clearbank();
  177. if(dir == NULL)
  178. return -1;
  179. //set msb when possible
  180. bank_msb = 0;
  181. for(unsigned i=0; i<banks.size(); i++)
  182. if(banks[i].dir == bankdirname)
  183. bank_msb = i;
  184. dirname = bankdirname;
  185. bankfiletitle = dirname;
  186. struct dirent *fn;
  187. while((fn = readdir(dir))) {
  188. const char *filename = fn->d_name;
  189. //check for extension
  190. if(strstr(filename, INSTRUMENT_EXTENSION) == NULL)
  191. continue;
  192. //verify if the name is like this NNNN-name (where N is a digit)
  193. int no = 0;
  194. unsigned int startname = 0;
  195. for(unsigned int i = 0; i < 4; ++i) {
  196. if(strlen(filename) <= i)
  197. break;
  198. if((filename[i] >= '0') && (filename[i] <= '9')) {
  199. no = no * 10 + (filename[i] - '0');
  200. startname++;
  201. }
  202. }
  203. if((startname + 1) < strlen(filename))
  204. startname++; //to take out the "-"
  205. string name = filename;
  206. //remove the file extension
  207. for(int i = name.size() - 1; i >= 2; i--)
  208. if(name[i] == '.') {
  209. name = name.substr(0, i);
  210. break;
  211. }
  212. if(no != 0) //the instrument position in the bank is found
  213. addtobank(no - 1, filename, name.substr(startname));
  214. else
  215. addtobank(-1, filename, name);
  216. }
  217. closedir(dir);
  218. if(!dirname.empty())
  219. config->cfg.currentBankDir = dirname;
  220. return 0;
  221. }
  222. /*
  223. * Makes a new bank, put it on a file and makes it current bank
  224. */
  225. int Bank::newbank(string newbankdirname)
  226. {
  227. string bankdir;
  228. bankdir = config->cfg.bankRootDirList[0];
  229. if(((bankdir[bankdir.size() - 1]) != '/')
  230. && ((bankdir[bankdir.size() - 1]) != '\\'))
  231. bankdir += "/";
  232. bankdir += newbankdirname;
  233. #ifdef _WIN32
  234. if(mkdir(bankdir.c_str()) < 0)
  235. #else
  236. if(mkdir(bankdir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0)
  237. #endif
  238. return -1;
  239. const string tmpfilename = bankdir + '/' + FORCE_BANK_DIR_FILE;
  240. FILE *tmpfile = fopen(tmpfilename.c_str(), "w+");
  241. fclose(tmpfile);
  242. return loadbank(bankdir);
  243. }
  244. /*
  245. * Check if the bank is locked (i.e. the file opened was readonly)
  246. */
  247. int Bank::locked()
  248. {
  249. //XXX Fixme
  250. return dirname.empty();
  251. }
  252. /*
  253. * Swaps a slot with another
  254. */
  255. int Bank::swapslot(unsigned int n1, unsigned int n2)
  256. {
  257. int err = 0;
  258. if((n1 == n2) || (locked()))
  259. return 0;
  260. if(emptyslot(n1) && (emptyslot(n2)))
  261. return 0;
  262. if(emptyslot(n1)) //change n1 to n2 in order to make
  263. swap(n1, n2);
  264. if(emptyslot(n2)) { //this is just a movement from slot1 to slot2
  265. err |= setname(n1, getname(n1), n2);
  266. if(err)
  267. return err;
  268. ins[n2] = ins[n1];
  269. ins[n1] = ins_t();
  270. }
  271. else { //if both slots are used
  272. if(ins[n1].name == ins[n2].name) //change the name of the second instrument if the name are equal
  273. ins[n2].name += "2";
  274. err |= setname(n1, getname(n1), n2);
  275. err |= setname(n2, getname(n2), n1);
  276. if(err)
  277. return err;
  278. swap(ins[n2], ins[n1]);
  279. }
  280. return err;
  281. }
  282. bool Bank::bankstruct::operator<(const bankstruct &b) const
  283. {
  284. return name < b.name;
  285. }
  286. /*
  287. * Re-scan for directories containing instrument banks
  288. */
  289. void Bank::rescanforbanks()
  290. {
  291. //remove old banks
  292. banks.clear();
  293. for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i)
  294. if(!config->cfg.bankRootDirList[i].empty())
  295. scanrootdir(config->cfg.bankRootDirList[i]);
  296. //sort the banks
  297. sort(banks.begin(), banks.end());
  298. //remove duplicate bank names
  299. int dupl = 0;
  300. for(int j = 0; j < (int) banks.size() - 1; ++j)
  301. for(int i = j + 1; i < (int) banks.size(); ++i) {
  302. if(banks[i].name == banks[j].name) {
  303. //add a [1] to the first bankname and [n] to others
  304. banks[i].name = banks[i].name + '['
  305. + stringFrom(dupl + 2) + ']';
  306. if(dupl == 0)
  307. banks[j].name += "[1]";
  308. dupl++;
  309. }
  310. else
  311. dupl = 0;
  312. }
  313. }
  314. void Bank::setMsb(uint8_t msb)
  315. {
  316. if(msb < banks.size() && banks[msb].dir != bankfiletitle)
  317. loadbank(banks[msb].dir);
  318. }
  319. void Bank::setLsb(uint8_t lsb)
  320. {
  321. //should only involve values of 0/1 for the time being...
  322. bank_lsb = limit<uint8_t>(lsb,0,1);
  323. }
  324. // private stuff
  325. void Bank::scanrootdir(string rootdir)
  326. {
  327. DIR *dir = opendir(rootdir.c_str());
  328. if(dir == NULL)
  329. return;
  330. bankstruct bank;
  331. const char *separator = "/";
  332. if(rootdir.size()) {
  333. char tmp = rootdir[rootdir.size() - 1];
  334. if((tmp == '/') || (tmp == '\\'))
  335. separator = "";
  336. }
  337. struct dirent *fn;
  338. while((fn = readdir(dir))) {
  339. const char *dirname = fn->d_name;
  340. if(dirname[0] == '.')
  341. continue;
  342. bank.dir = rootdir + separator + dirname + '/';
  343. bank.name = dirname;
  344. //find out if the directory contains at least 1 instrument
  345. bool isbank = false;
  346. DIR *d = opendir(bank.dir.c_str());
  347. if(d == NULL)
  348. continue;
  349. struct dirent *fname;
  350. while((fname = readdir(d))) {
  351. if((strstr(fname->d_name, INSTRUMENT_EXTENSION) != NULL)
  352. || (strstr(fname->d_name, FORCE_BANK_DIR_FILE) != NULL)) {
  353. isbank = true;
  354. break; //could put a #instrument counter here instead
  355. }
  356. }
  357. if(isbank)
  358. banks.push_back(bank);
  359. closedir(d);
  360. }
  361. closedir(dir);
  362. }
  363. void Bank::clearbank()
  364. {
  365. for(int i = 0; i < BANK_SIZE; ++i)
  366. ins[i] = ins_t();
  367. bankfiletitle.clear();
  368. dirname.clear();
  369. }
  370. int Bank::addtobank(int pos, string filename, string name)
  371. {
  372. if((pos >= 0) && (pos < BANK_SIZE)) {
  373. if(!ins[pos].filename.empty())
  374. pos = -1; //force it to find a new free position
  375. }
  376. else
  377. if(pos >= BANK_SIZE)
  378. pos = -1;
  379. if(pos < 0) //find a free position
  380. for(int i = BANK_SIZE - 1; i >= 0; i--)
  381. if(ins[i].filename.empty()) {
  382. pos = i;
  383. break;
  384. }
  385. if(pos < 0)
  386. return -1; //the bank is full
  387. deletefrombank(pos);
  388. ins[pos].name = name;
  389. ins[pos].filename = dirname + filename;
  390. return 0;
  391. }
  392. void Bank::deletefrombank(int pos)
  393. {
  394. if((pos < 0) || (pos >= BANK_SIZE))
  395. return;
  396. ins[pos] = ins_t();
  397. }
  398. Bank::ins_t::ins_t()
  399. :name(""), filename("")
  400. {}