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 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  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. "%4d-%s",
  142. ninstrument + 1,
  143. (char *)part->Pname);
  144. //add the zeroes at the start of filename
  145. for(int i = 0; i < 4; ++i)
  146. if(tmpfilename[i] == ' ')
  147. tmpfilename[i] = '0';
  148. string filename = dirname + '/' + legalizeFilename(tmpfilename) + ".xiz";
  149. FILE *f = fopen(filename.c_str(), "r");
  150. if(f) {
  151. fclose(f);
  152. err = remove(filename.c_str());
  153. if(err)
  154. return err;
  155. }
  156. err = part->saveXML(filename.c_str());
  157. if(err)
  158. return err;
  159. addtobank(ninstrument, legalizeFilename(tmpfilename) + ".xiz", (char *) part->Pname);
  160. return 0;
  161. }
  162. /*
  163. * Loads the instrument from the bank
  164. */
  165. int Bank::loadfromslot(unsigned int ninstrument, Part *part)
  166. {
  167. if(emptyslot(ninstrument))
  168. return 0;
  169. part->AllNotesOff();
  170. part->defaultsinstrument();
  171. part->loadXMLinstrument(ins[ninstrument].filename.c_str());
  172. return 0;
  173. }
  174. /*
  175. * Makes current a bank directory
  176. */
  177. int Bank::loadbank(string bankdirname)
  178. {
  179. DIR *dir = opendir(bankdirname.c_str());
  180. clearbank();
  181. if(dir == NULL)
  182. return -1;
  183. //set msb when possible
  184. bank_msb = 0;
  185. for(unsigned i=0; i<banks.size(); i++)
  186. if(banks[i].dir == bankdirname)
  187. bank_msb = i;
  188. dirname = bankdirname;
  189. bankfiletitle = dirname;
  190. struct dirent *fn;
  191. while((fn = readdir(dir))) {
  192. const char *filename = fn->d_name;
  193. //check for extension
  194. if(strstr(filename, INSTRUMENT_EXTENSION) == NULL)
  195. continue;
  196. //verify if the name is like this NNNN-name (where N is a digit)
  197. int no = 0;
  198. unsigned int startname = 0;
  199. for(unsigned int i = 0; i < 4; ++i) {
  200. if(strlen(filename) <= i)
  201. break;
  202. if((filename[i] >= '0') && (filename[i] <= '9')) {
  203. no = no * 10 + (filename[i] - '0');
  204. startname++;
  205. }
  206. }
  207. if((startname + 1) < strlen(filename))
  208. startname++; //to take out the "-"
  209. string name = filename;
  210. //remove the file extension
  211. for(int i = name.size() - 1; i >= 2; i--)
  212. if(name[i] == '.') {
  213. name = name.substr(0, i);
  214. break;
  215. }
  216. if(no != 0) //the instrument position in the bank is found
  217. addtobank(no - 1, filename, name.substr(startname));
  218. else
  219. addtobank(-1, filename, name);
  220. }
  221. closedir(dir);
  222. if(!dirname.empty())
  223. config->cfg.currentBankDir = dirname;
  224. return 0;
  225. }
  226. /*
  227. * Makes a new bank, put it on a file and makes it current bank
  228. */
  229. int Bank::newbank(string newbankdirname)
  230. {
  231. string bankdir;
  232. bankdir = config->cfg.bankRootDirList[0];
  233. if(((bankdir[bankdir.size() - 1]) != '/')
  234. && ((bankdir[bankdir.size() - 1]) != '\\'))
  235. bankdir += "/";
  236. bankdir += newbankdirname;
  237. #ifdef _WIN32
  238. if(mkdir(bankdir.c_str()) < 0)
  239. #else
  240. if(mkdir(bankdir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0)
  241. #endif
  242. return -1;
  243. const string tmpfilename = bankdir + '/' + FORCE_BANK_DIR_FILE;
  244. FILE *tmpfile = fopen(tmpfilename.c_str(), "w+");
  245. fclose(tmpfile);
  246. return loadbank(bankdir);
  247. }
  248. /*
  249. * Check if the bank is locked (i.e. the file opened was readonly)
  250. */
  251. int Bank::locked()
  252. {
  253. //XXX Fixme
  254. return dirname.empty();
  255. }
  256. /*
  257. * Swaps a slot with another
  258. */
  259. int Bank::swapslot(unsigned int n1, unsigned int n2)
  260. {
  261. int err = 0;
  262. if((n1 == n2) || (locked()))
  263. return 0;
  264. if(emptyslot(n1) && (emptyslot(n2)))
  265. return 0;
  266. if(emptyslot(n1)) //change n1 to n2 in order to make
  267. swap(n1, n2);
  268. if(emptyslot(n2)) { //this is just a movement from slot1 to slot2
  269. err |= setname(n1, getname(n1), n2);
  270. if(err)
  271. return err;
  272. ins[n2] = ins[n1];
  273. ins[n1] = ins_t();
  274. }
  275. else { //if both slots are used
  276. if(ins[n1].name == ins[n2].name) //change the name of the second instrument if the name are equal
  277. ins[n2].name += "2";
  278. err |= setname(n1, getname(n1), n2);
  279. err |= setname(n2, getname(n2), n1);
  280. if(err)
  281. return err;
  282. swap(ins[n2], ins[n1]);
  283. }
  284. return err;
  285. }
  286. bool Bank::bankstruct::operator<(const bankstruct &b) const
  287. {
  288. return name < b.name;
  289. }
  290. /*
  291. * Re-scan for directories containing instrument banks
  292. */
  293. void Bank::rescanforbanks()
  294. {
  295. //remove old banks
  296. banks.clear();
  297. for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i)
  298. if(!config->cfg.bankRootDirList[i].empty())
  299. scanrootdir(config->cfg.bankRootDirList[i]);
  300. //sort the banks
  301. sort(banks.begin(), banks.end());
  302. //remove duplicate bank names
  303. int dupl = 0;
  304. for(int j = 0; j < (int) banks.size() - 1; ++j)
  305. for(int i = j + 1; i < (int) banks.size(); ++i) {
  306. if(banks[i].name == banks[j].name) {
  307. //add a [1] to the first bankname and [n] to others
  308. banks[i].name = banks[i].name + '['
  309. + stringFrom(dupl + 2) + ']';
  310. if(dupl == 0)
  311. banks[j].name += "[1]";
  312. dupl++;
  313. }
  314. else
  315. dupl = 0;
  316. }
  317. }
  318. void Bank::setMsb(uint8_t msb)
  319. {
  320. if(msb < banks.size() && banks[msb].dir != bankfiletitle)
  321. loadbank(banks[msb].dir);
  322. }
  323. void Bank::setLsb(uint8_t lsb)
  324. {
  325. //should only involve values of 0/1 for the time being...
  326. bank_lsb = limit<uint8_t>(lsb,0,1);
  327. }
  328. // private stuff
  329. void Bank::scanrootdir(string rootdir)
  330. {
  331. DIR *dir = opendir(rootdir.c_str());
  332. if(dir == NULL)
  333. return;
  334. bankstruct bank;
  335. const char *separator = "/";
  336. if(rootdir.size()) {
  337. char tmp = rootdir[rootdir.size() - 1];
  338. if((tmp == '/') || (tmp == '\\'))
  339. separator = "";
  340. }
  341. struct dirent *fn;
  342. while((fn = readdir(dir))) {
  343. const char *dirname = fn->d_name;
  344. if(dirname[0] == '.')
  345. continue;
  346. bank.dir = rootdir + separator + dirname + '/';
  347. bank.name = dirname;
  348. //find out if the directory contains at least 1 instrument
  349. bool isbank = false;
  350. DIR *d = opendir(bank.dir.c_str());
  351. if(d == NULL)
  352. continue;
  353. struct dirent *fname;
  354. while((fname = readdir(d))) {
  355. if((strstr(fname->d_name, INSTRUMENT_EXTENSION) != NULL)
  356. || (strstr(fname->d_name, FORCE_BANK_DIR_FILE) != NULL)) {
  357. isbank = true;
  358. break; //could put a #instrument counter here instead
  359. }
  360. }
  361. if(isbank)
  362. banks.push_back(bank);
  363. closedir(d);
  364. }
  365. closedir(dir);
  366. }
  367. void Bank::clearbank()
  368. {
  369. for(int i = 0; i < BANK_SIZE; ++i)
  370. ins[i] = ins_t();
  371. bankfiletitle.clear();
  372. dirname.clear();
  373. }
  374. int Bank::addtobank(int pos, string filename, string name)
  375. {
  376. if((pos >= 0) && (pos < BANK_SIZE)) {
  377. if(!ins[pos].filename.empty())
  378. pos = -1; //force it to find a new free position
  379. }
  380. else
  381. if(pos >= BANK_SIZE)
  382. pos = -1;
  383. if(pos < 0) //find a free position
  384. for(int i = BANK_SIZE - 1; i >= 0; i--)
  385. if(ins[i].filename.empty()) {
  386. pos = i;
  387. break;
  388. }
  389. if(pos < 0)
  390. return -1; //the bank is full
  391. deletefrombank(pos);
  392. ins[pos].name = name;
  393. ins[pos].filename = dirname + filename;
  394. return 0;
  395. }
  396. void Bank::deletefrombank(int pos)
  397. {
  398. if((pos < 0) || (pos >= BANK_SIZE))
  399. return;
  400. ins[pos] = ins_t();
  401. }
  402. Bank::ins_t::ins_t()
  403. :name(""), filename("")
  404. {}