|
- /*
- 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 the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
- */
-
- #include "Bank.h"
- #include <cstring>
- #include <cstdio>
- #include <cstdlib>
- #include <dirent.h>
- #include <sys/stat.h>
- #include <algorithm>
-
- #include <sys/types.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <errno.h>
-
- #include "Config.h"
- #include "Util.h"
- #include "Part.h"
- #include "BankDb.h"
- #ifdef WIN32
- #include <windows.h>
- #endif
-
- using namespace std;
-
- namespace zyncarla {
-
- static const char* 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
- const char* FORCE_BANK_DIR_FILE = ".bankdir";
-
- Bank::Bank(Config *config)
- :bankpos(0), defaultinsname(" "), config(config),
- db(new BankDb), bank_msb(0), bank_lsb(0)
- {
- clearbank();
- bankfiletitle = dirname;
- rescanforbanks();
- loadbank(config->cfg.currentBankDir);
-
- for(unsigned i=0; i<banks.size(); ++i) {
- if(banks[i].dir == config->cfg.currentBankDir) {
- bankpos = i;
- break;
- }
- }
- }
-
- Bank::~Bank()
- {
- clearbank();
- delete db;
- }
-
- /*
- * 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)
- */
- int Bank::setname(unsigned int ninstrument, const string &newname, int newslot)
- {
- if(emptyslot(ninstrument))
- return 0;
-
- 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";
-
- int err = rename(ins[ninstrument].filename.c_str(), newfilename.c_str());
- if(err)
- return err;
-
- ins[ninstrument].filename = newfilename;
- ins[ninstrument].name = newname;
- return err;
- }
-
- /*
- * 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;
-
- return false;
- }
-
- /*
- * Removes the instrument from the bank
- */
- int Bank::clearslot(unsigned int ninstrument)
- {
- if(emptyslot(ninstrument))
- return 0;
-
- //no error when no file
- FILE *f = fopen(ins[ninstrument].filename.c_str(), "r");
- if(!f)
- return 0;
- fclose(f);
-
- int err = remove(ins[ninstrument].filename.c_str());
- if(!err)
- deletefrombank(ninstrument);
- return err;
- }
-
- /*
- * Save the instrument to a slot
- */
- int Bank::savetoslot(unsigned int ninstrument, Part *part)
- {
- int err = clearslot(ninstrument);
- if(err)
- return err;
-
- const int maxfilename = 200;
- char tmpfilename[maxfilename + 20];
- ZERO(tmpfilename, maxfilename + 20);
-
- snprintf(tmpfilename,
- maxfilename,
- "%04d-%s",
- ninstrument + 1,
- (char *)part->Pname);
-
- string filename = dirname + '/' + legalizeFilename(tmpfilename) + ".xiz";
-
- FILE *f = fopen(filename.c_str(), "r");
- if(f) {
- fclose(f);
-
- err = remove(filename.c_str());
- if(err)
- return err;
- }
-
- err = part->saveXML(filename.c_str());
- if(err)
- return err;
- addtobank(ninstrument, legalizeFilename(tmpfilename) + ".xiz",
- (char *) part->Pname);
- return 0;
- }
-
- /*
- * Loads the instrument from the bank
- */
- int Bank::loadfromslot(unsigned int ninstrument, Part *part)
- {
- if(emptyslot(ninstrument))
- return 0;
-
- part->AllNotesOff();
- part->defaultsinstrument();
-
- part->loadXMLinstrument(ins[ninstrument].filename.c_str());
- return 0;
- }
-
- /*
- * Makes current a bank directory
- */
- int Bank::loadbank(string bankdirname)
- {
- normalizedirsuffix(bankdirname);
- DIR *dir = opendir(bankdirname.c_str());
- clearbank();
-
- if(dir == NULL)
- return -1;
-
- //set msb when possible
- bank_msb = 0;
- for(unsigned i=0; i<banks.size(); i++)
- if(banks[i].dir == bankdirname)
- bank_msb = i;
-
- 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];
-
- expanddirname(bankdir);
- normalizedirsuffix(bankdir);
-
- bankdir += newbankdirname;
- #ifdef _WIN32
- if(mkdir(bankdir.c_str()) < 0)
- #else
- if(mkdir(bankdir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0)
- #endif
- 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()
- {
- //XXX Fixme
- return dirname.empty();
- }
-
- /*
- * Swaps a slot with another
- */
- int Bank::swapslot(unsigned int n1, unsigned int n2)
- {
- int err = 0;
- if((n1 == n2) || (locked()))
- return 0;
- if(emptyslot(n1) && (emptyslot(n2)))
- return 0;
- 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
- err |= setname(n1, getname(n1), n2);
- if(err)
- return err;
- 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";
-
- err |= setname(n1, getname(n1), n2);
- err |= setname(n2, getname(n2), n1);
- if(err)
- return err;
- swap(ins[n2], ins[n1]);
- }
- return err;
- }
-
-
- bool Bank::bankstruct::operator<(const bankstruct &b) const
- {
- return name < b.name;
- }
-
- /*
- * Re-scan for directories containing instrument banks
- */
-
- void Bank::rescanforbanks()
- {
- db->clear();
- //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]);
- #ifdef WIN32
- {
- //Search the VST Directory for banks/preset/etc
- char path[1024];
- GetModuleFileName(GetModuleHandle("ZynAddSubFX.dll"), path, sizeof(path));
- if(strstr(path, "ZynAddSubFX.dll")) {
- strstr(path, "ZynAddSubFX.dll")[0] = 0;
- strcat(path, "banks");
- scanrootdir(path);
- }
- }
- #endif
-
- //sort the banks
- sort(banks.begin(), banks.end());
-
- for(int i = 0; i < (int) banks.size(); ++i)
- db->addBankDir(banks[i].dir);
-
- //remove duplicate bank names
- for(int j = 0; j < (int) banks.size() - 1; ++j) {
- int dupl = 0;
- 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) + ']';
- dupl++;
- }
- }
- if(dupl != 0)
- banks[j].name += "[1]";
- if(dupl)
- j += dupl;
- }
- db->scanBanks();
- }
-
- void Bank::setMsb(uint8_t msb)
- {
- if(msb < banks.size() && banks[msb].dir != bankfiletitle)
- loadbank(banks[msb].dir);
- }
-
- void Bank::setLsb(uint8_t lsb)
- {
- //should only involve values of 0/1 for the time being...
- bank_lsb = limit<uint8_t>(lsb,0,1);
- }
-
-
- // private stuff
-
- void Bank::scanrootdir(string rootdir)
- {
- expanddirname(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();
- }
-
- std::vector<std::string> Bank::search(std::string s) const
- {
- std::vector<std::string> out;
- auto vec = db->search(s);
- for(auto e:vec) {
- out.push_back(e.name);
- out.push_back(e.bank+e.file);
- }
- return out;
- }
-
- std::vector<std::string> Bank::blist(std::string s)
- {
- std::vector<std::string> out;
- loadbank(s);
- for(int i=0; i<128; ++i) {
- if(ins[i].filename.empty())
- out.push_back("Empty Preset");
- else
- out.push_back(ins[i].name);
- out.push_back(to_s(i));
- }
- return out;
- }
-
- int Bank::addtobank(int pos, string filename, string name)
- {
- if((pos >= 0) && (pos < BANK_SIZE)) {
- if(!ins[pos].filename.empty())
- 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].filename.empty()) {
- pos = i;
- break;
- }
-
- if(pos < 0)
- return -1; //the bank is full
-
- deletefrombank(pos);
-
- ins[pos].name = name;
- ins[pos].filename = dirname + filename;
- return 0;
- }
-
- void Bank::deletefrombank(int pos)
- {
- if((pos < 0) || (pos >= BANK_SIZE))
- return;
- ins[pos] = ins_t();
- }
-
- Bank::ins_t::ins_t()
- :name(""), filename("")
- {}
-
- void Bank::expanddirname(std::string &dirname) {
- if (dirname.empty())
- return;
-
- // if the directory name starts with a ~ and the $HOME variable is
- // defined in the environment, replace ~ by the content of $HOME
- if (dirname.at(0) == '~') {
- char *home_dirname = getenv("HOME");
- if (home_dirname != NULL) {
- dirname = std::string(home_dirname) + dirname.substr(1);
- }
- }
- }
-
- void Bank::normalizedirsuffix(string &dirname) const {
- if(((dirname[dirname.size() - 1]) != '/')
- && ((dirname[dirname.size() - 1]) != '\\'))
- dirname += "/";
- }
-
- }
|