/* ZynAddSubFX - a software synthesizer Bank.cpp - Instrument Bank Copyright (C) 2002-2005 Nasca Octavian Paul Copyright (C) 2010-2010 Mark McCurry Author: Nasca Octavian Paul Mark McCurry This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License (version 2 or later) for more details. You should have received a copy of the GNU General Public License (version 2) along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "Bank.h" #include #include #include #include #include #include #include #include #include #include #include #include "Config.h" #include "Util.h" #include "Part.h" #define INSTRUMENT_EXTENSION ".xiz" //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 #define FORCE_BANK_DIR_FILE ".bankdir" using namespace std; Bank::Bank() :defaultinsname(" ") { clearbank(); bankfiletitle = dirname; loadbank(config.cfg.currentBankDir); } Bank::~Bank() { clearbank(); } /* * Get the name of an instrument from the bank */ string Bank::getname(unsigned int ninstrument) { if(emptyslot(ninstrument)) return defaultinsname; return ins[ninstrument].name; } /* * Get the numbered name of an instrument from the bank */ string Bank::getnamenumbered(unsigned int ninstrument) { if(emptyslot(ninstrument)) return defaultinsname; return stringFrom(ninstrument + 1) + ". " + getname(ninstrument); } /* * Changes the name of an instrument (and the filename) */ void Bank::setname(unsigned int ninstrument, const string &newname, int newslot) { if(emptyslot(ninstrument)) return; string newfilename; char tmpfilename[100 + 1]; tmpfilename[100] = 0; if(newslot >= 0) snprintf(tmpfilename, 100, "%4d-%s", newslot + 1, newname.c_str()); else snprintf(tmpfilename, 100, "%4d-%s", ninstrument + 1, newname.c_str()); //add the zeroes at the start of filename for(int i = 0; i < 4; ++i) if(tmpfilename[i] == ' ') tmpfilename[i] = '0'; newfilename = dirname + '/' + legalizeFilename(tmpfilename) + ".xiz"; rename(ins[ninstrument].filename.c_str(), newfilename.c_str()); ins[ninstrument].filename = newfilename; ins[ninstrument].name = newname; } /* * Check if there is no instrument on a slot from the bank */ bool Bank::emptyslot(unsigned int ninstrument) { if(ninstrument >= BANK_SIZE) return true; if(ins[ninstrument].filename.empty()) return true; if(ins[ninstrument].used) return false; else return true; } /* * Removes the instrument from the bank */ void Bank::clearslot(unsigned int ninstrument) { if(emptyslot(ninstrument)) return; remove(ins[ninstrument].filename.c_str()); deletefrombank(ninstrument); } /* * Save the instrument to a slot */ void Bank::savetoslot(unsigned int ninstrument, Part *part) { clearslot(ninstrument); const int maxfilename = 200; char tmpfilename[maxfilename + 20]; ZERO(tmpfilename, maxfilename + 20); snprintf(tmpfilename, maxfilename, "%4d-%s", ninstrument + 1, (char *)part->Pname); //add the zeroes at the start of filename for(int i = 0; i < 4; ++i) if(tmpfilename[i] == ' ') tmpfilename[i] = '0'; string filename = dirname + '/' + legalizeFilename(tmpfilename) + ".xiz"; remove(filename.c_str()); part->saveXML(filename.c_str()); addtobank(ninstrument, legalizeFilename(tmpfilename) + ".xiz", (char *) part->Pname); } /* * Loads the instrument from the bank */ void Bank::loadfromslot(unsigned int ninstrument, Part *part) { if(emptyslot(ninstrument)) return; part->AllNotesOff(); part->defaultsinstrument(); part->loadXMLinstrument(ins[ninstrument].filename.c_str()); } /* * Makes current a bank directory */ int Bank::loadbank(string bankdirname) { DIR *dir = opendir(bankdirname.c_str()); clearbank(); if(dir == NULL) return -1; dirname = bankdirname; bankfiletitle = dirname; struct dirent *fn; while((fn = readdir(dir))) { const char *filename = fn->d_name; //check for extension if(strstr(filename, INSTRUMENT_EXTENSION) == NULL) continue; //verify if the name is like this NNNN-name (where N is a digit) int no = 0; unsigned int startname = 0; for(unsigned int i = 0; i < 4; ++i) { if(strlen(filename) <= i) break; if((filename[i] >= '0') && (filename[i] <= '9')) { no = no * 10 + (filename[i] - '0'); startname++; } } if((startname + 1) < strlen(filename)) startname++; //to take out the "-" string name = filename; //remove the file extension for(int i = name.size() - 1; i >= 2; i--) if(name[i] == '.') { name = name.substr(0, i); break; } if(no != 0) //the instrument position in the bank is found addtobank(no - 1, filename, name.substr(startname)); else addtobank(-1, filename, name); } closedir(dir); if(!dirname.empty()) config.cfg.currentBankDir = dirname; return 0; } /* * Makes a new bank, put it on a file and makes it current bank */ int Bank::newbank(string newbankdirname) { string bankdir; bankdir = config.cfg.bankRootDirList[0]; if(((bankdir[bankdir.size() - 1]) != '/') && ((bankdir[bankdir.size() - 1]) != '\\')) bankdir += "/"; bankdir += newbankdirname; if(mkdir(bankdir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) return -1; const string tmpfilename = bankdir + '/' + FORCE_BANK_DIR_FILE; FILE *tmpfile = fopen(tmpfilename.c_str(), "w+"); fclose(tmpfile); return loadbank(bankdir); } /* * Check if the bank is locked (i.e. the file opened was readonly) */ int Bank::locked() { return dirname.empty(); } /* * Swaps a slot with another */ void Bank::swapslot(unsigned int n1, unsigned int n2) { if((n1 == n2) || (locked())) return; if(emptyslot(n1) && (emptyslot(n2))) return; if(emptyslot(n1)) //change n1 to n2 in order to make swap(n1, n2); if(emptyslot(n2)) { //this is just a movement from slot1 to slot2 setname(n1, getname(n1), n2); ins[n2] = ins[n1]; ins[n1] = ins_t(); } else { //if both slots are used if(ins[n1].name == ins[n2].name) //change the name of the second instrument if the name are equal ins[n2].name += "2"; setname(n1, getname(n1), n2); setname(n2, getname(n2), n1); swap(ins[n2], ins[n1]); } } bool Bank::bankstruct::operator<(const bankstruct &b) const { return name < b.name; } /* * Re-scan for directories containing instrument banks */ void Bank::rescanforbanks() { //remove old banks banks.clear(); for(int i = 0; i < MAX_BANK_ROOT_DIRS; ++i) if(!config.cfg.bankRootDirList[i].empty()) scanrootdir(config.cfg.bankRootDirList[i]); //sort the banks sort(banks.begin(), banks.end()); //remove duplicate bank names int dupl = 0; for(int j = 0; j < (int) banks.size() - 1; ++j) for(int i = j + 1; i < (int) banks.size(); ++i) { if(banks[i].name == banks[j].name) { //add a [1] to the first bankname and [n] to others banks[i].name = banks[i].name + '[' + stringFrom(dupl + 2) + ']'; if(dupl == 0) banks[j].name += "[1]"; dupl++; } else dupl = 0; } } // private stuff void Bank::scanrootdir(string rootdir) { DIR *dir = opendir(rootdir.c_str()); if(dir == NULL) return; bankstruct bank; const char *separator = "/"; if(rootdir.size()) { char tmp = rootdir[rootdir.size() - 1]; if((tmp == '/') || (tmp == '\\')) separator = ""; } struct dirent *fn; while((fn = readdir(dir))) { const char *dirname = fn->d_name; if(dirname[0] == '.') continue; bank.dir = rootdir + separator + dirname + '/'; bank.name = dirname; //find out if the directory contains at least 1 instrument bool isbank = false; DIR *d = opendir(bank.dir.c_str()); if(d == NULL) continue; struct dirent *fname; while((fname = readdir(d))) { if((strstr(fname->d_name, INSTRUMENT_EXTENSION) != NULL) || (strstr(fname->d_name, FORCE_BANK_DIR_FILE) != NULL)) { isbank = true; break; //could put a #instrument counter here instead } } if(isbank) banks.push_back(bank); closedir(d); } closedir(dir); } void Bank::clearbank() { for(int i = 0; i < BANK_SIZE; ++i) ins[i] = ins_t(); bankfiletitle.clear(); dirname.clear(); } int Bank::addtobank(int pos, string filename, string name) { if((pos >= 0) && (pos < BANK_SIZE)) { if(ins[pos].used) pos = -1; //force it to find a new free position } else if(pos >= BANK_SIZE) pos = -1; if(pos < 0) //find a free position for(int i = BANK_SIZE - 1; i >= 0; i--) if(!ins[i].used) { pos = i; break; } if(pos < 0) return -1; //the bank is full deletefrombank(pos); ins[pos].used = true; ins[pos].name = name; ins[pos].filename = dirname + '/' + filename; //see if PADsynth is used if(config.cfg.CheckPADsynth) { XMLwrapper xml; xml.loadXMLfile(ins[pos].filename); ins[pos].info.PADsynth_used = xml.hasPadSynth(); } else ins[pos].info.PADsynth_used = false; return 0; } bool Bank::isPADsynth_used(unsigned int ninstrument) { if(config.cfg.CheckPADsynth == 0) return 0; else return ins[ninstrument].info.PADsynth_used; } void Bank::deletefrombank(int pos) { if((pos < 0) || (pos >= (int) banks.size())) return; ins[pos] = ins_t(); } Bank::ins_t::ins_t() :used(false), name(""), filename("") { info.PADsynth_used = false; }