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.

514 lines
12KB

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