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.

474 lines
11KB

  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. #ifndef CARLA_OS_WIN
  201. string bankdir;
  202. bankdir = config.cfg.bankRootDirList[0];
  203. if(((bankdir[bankdir.size() - 1]) != '/')
  204. && ((bankdir[bankdir.size() - 1]) != '\\'))
  205. bankdir += "/";
  206. bankdir += newbankdirname;
  207. if(mkdir(bankdir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0)
  208. return -1;
  209. const string tmpfilename = bankdir + '/' + FORCE_BANK_DIR_FILE;
  210. FILE *tmpfile = fopen(tmpfilename.c_str(), "w+");
  211. fclose(tmpfile);
  212. return loadbank(bankdir);
  213. #else
  214. return -1;
  215. #endif
  216. }
  217. /*
  218. * Check if the bank is locked (i.e. the file opened was readonly)
  219. */
  220. int Bank::locked()
  221. {
  222. return dirname.empty();
  223. }
  224. /*
  225. * Swaps a slot with another
  226. */
  227. void Bank::swapslot(unsigned int n1, unsigned int n2)
  228. {
  229. if((n1 == n2) || (locked()))
  230. return;
  231. if(emptyslot(n1) && (emptyslot(n2)))
  232. return;
  233. if(emptyslot(n1)) //change n1 to n2 in order to make
  234. swap(n1, n2);
  235. if(emptyslot(n2)) { //this is just a movement from slot1 to slot2
  236. setname(n1, getname(n1), n2);
  237. ins[n2] = ins[n1];
  238. ins[n1] = ins_t();
  239. }
  240. else { //if both slots are used
  241. if(ins[n1].name == ins[n2].name) //change the name of the second instrument if the name are equal
  242. ins[n2].name += "2";
  243. setname(n1, getname(n1), n2);
  244. setname(n2, getname(n2), n1);
  245. swap(ins[n2], ins[n1]);
  246. }
  247. }
  248. bool Bank::bankstruct::operator<(const bankstruct &b) const
  249. {
  250. return name < b.name;
  251. }
  252. /*
  253. * Re-scan for directories containing instrument banks
  254. */
  255. void Bank::rescanforbanks()
  256. {
  257. //remove old banks
  258. banks.clear();
  259. for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i)
  260. if(!config.cfg.bankRootDirList[i].empty())
  261. scanrootdir(config.cfg.bankRootDirList[i]);
  262. //sort the banks
  263. sort(banks.begin(), banks.end());
  264. //remove duplicate bank names
  265. int dupl = 0;
  266. for(int j = 0; j < (int) banks.size() - 1; ++j)
  267. for(int i = j + 1; i < (int) banks.size(); ++i) {
  268. if(banks[i].name == banks[j].name) {
  269. //add a [1] to the first bankname and [n] to others
  270. banks[i].name = banks[i].name + '['
  271. + stringFrom(dupl + 2) + ']';
  272. if(dupl == 0)
  273. banks[j].name += "[1]";
  274. dupl++;
  275. }
  276. else
  277. dupl = 0;
  278. }
  279. }
  280. // private stuff
  281. void Bank::scanrootdir(string rootdir)
  282. {
  283. DIR *dir = opendir(rootdir.c_str());
  284. if(dir == NULL)
  285. return;
  286. bankstruct bank;
  287. const char *separator = "/";
  288. if(rootdir.size()) {
  289. char tmp = rootdir[rootdir.size() - 1];
  290. if((tmp == '/') || (tmp == '\\'))
  291. separator = "";
  292. }
  293. struct dirent *fn;
  294. while((fn = readdir(dir))) {
  295. const char *dirname = fn->d_name;
  296. if(dirname[0] == '.')
  297. continue;
  298. bank.dir = rootdir + separator + dirname + '/';
  299. bank.name = dirname;
  300. //find out if the directory contains at least 1 instrument
  301. bool isbank = false;
  302. DIR *d = opendir(bank.dir.c_str());
  303. if(d == NULL)
  304. continue;
  305. struct dirent *fname;
  306. while((fname = readdir(d))) {
  307. if((strstr(fname->d_name, INSTRUMENT_EXTENSION) != NULL)
  308. || (strstr(fname->d_name, FORCE_BANK_DIR_FILE) != NULL)) {
  309. isbank = true;
  310. break; //could put a #instrument counter here instead
  311. }
  312. }
  313. if(isbank)
  314. banks.push_back(bank);
  315. closedir(d);
  316. }
  317. closedir(dir);
  318. }
  319. void Bank::clearbank()
  320. {
  321. for(int i = 0; i < BANK_SIZE; ++i)
  322. ins[i] = ins_t();
  323. bankfiletitle.clear();
  324. dirname.clear();
  325. }
  326. int Bank::addtobank(int pos, string filename, string name)
  327. {
  328. if((pos >= 0) && (pos < BANK_SIZE)) {
  329. if(ins[pos].used)
  330. pos = -1; //force it to find a new free position
  331. }
  332. else
  333. if(pos >= BANK_SIZE)
  334. pos = -1;
  335. if(pos < 0) //find a free position
  336. for(int i = BANK_SIZE - 1; i >= 0; i--)
  337. if(!ins[i].used) {
  338. pos = i;
  339. break;
  340. }
  341. if(pos < 0)
  342. return -1; //the bank is full
  343. deletefrombank(pos);
  344. ins[pos].used = true;
  345. ins[pos].name = name;
  346. ins[pos].filename = dirname + '/' + filename;
  347. //see if PADsynth is used
  348. if(config.cfg.CheckPADsynth) {
  349. XMLwrapper xml;
  350. xml.loadXMLfile(ins[pos].filename);
  351. ins[pos].info.PADsynth_used = xml.hasPadSynth();
  352. }
  353. else
  354. ins[pos].info.PADsynth_used = false;
  355. return 0;
  356. }
  357. bool Bank::isPADsynth_used(unsigned int ninstrument)
  358. {
  359. if(config.cfg.CheckPADsynth == 0)
  360. return 0;
  361. else
  362. return ins[ninstrument].info.PADsynth_used;
  363. }
  364. void Bank::deletefrombank(int pos)
  365. {
  366. if((pos < 0) || (pos >= BANK_SIZE))
  367. return;
  368. ins[pos] = ins_t();
  369. }
  370. Bank::ins_t::ins_t()
  371. :used(false), name(""), filename("")
  372. {
  373. info.PADsynth_used = false;
  374. }