zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

BankDb.cpp (7972B)


      1 #include "BankDb.h"
      2 #include "XMLwrapper.h"
      3 #include "Util.h"
      4 #include "../globals.h"
      5 #include <cstring>
      6 #include <dirent.h>
      7 #include <sys/stat.h>
      8 
      9 namespace zyn {
     10 
     11 static const char* INSTRUMENT_EXTENSION = ".xiz";
     12 
     13 using std::string;
     14 typedef BankDb::svec svec;
     15 typedef BankDb::bvec bvec;
     16 
     17 BankEntry::BankEntry(void)
     18     :id(0), add(false), pad(false), sub(false), time(0)
     19 {}
     20 
     21 bool platform_strcasestr(const char *hay, const char *needle)
     22 {
     23     int n = strlen(hay);
     24     int m = strlen(needle);
     25     for(int i=0; i<n; i++) {
     26         int good = 1;
     27         for(int j=0; j<m; ++j) {
     28             if(toupper(hay[i+j]) != toupper(needle[j])) {
     29                 good = 0;
     30                 break;
     31             }
     32 
     33         }
     34         if(good)
     35             return 1;
     36     }
     37     return 0;
     38 
     39 }
     40 
     41 bool sfind(std::string hay, std::string needle)
     42 {
     43     //return strcasestr(hay.c_str(), needle.c_str());
     44     return platform_strcasestr(hay.c_str(), needle.c_str());
     45     return false;
     46 }
     47 
     48 bool BankEntry::match(string s) const
     49 {
     50     if(s == "#pad")
     51         return pad;
     52     else if(s == "#sub")
     53         return sub;
     54     else if(s == "#add")
     55         return add;
     56     return sfind(file,s) || sfind(name,s) || sfind(bank, s) ||
     57         sfind(type, s) || sfind(comments,s) || sfind(author,s);
     58 }
     59 
     60 bool BankEntry::operator<(const BankEntry &b) const
     61 {
     62     return (this->bank+this->file) < (b.bank+b.file);
     63 }
     64 
     65 static svec split(string s)
     66 {
     67     svec vec;
     68     string ss;
     69     for(char c:s) {
     70         if(isspace(c) && !ss.empty()) {
     71             vec.push_back(ss);
     72             ss.clear();
     73         } else if(!isspace(c)) {
     74             ss.push_back(c);
     75         }
     76     }
     77     if(!ss.empty())
     78         vec.push_back(ss);
     79 
     80     return vec;
     81 }
     82 
     83 //static string line(string s)
     84 //{
     85 //    string ss;
     86 //    for(char c:s) {
     87 //        if(c != '\n')
     88 //            ss.push_back(c);
     89 //        else
     90 //            return ss;
     91 //    }
     92 //    return ss;
     93 //}
     94 
     95 bvec BankDb::search(std::string ss) const
     96 {
     97     bvec vec;
     98     const svec sterm = split(ss);
     99     for(auto field:fields) {
    100         bool match = true;
    101         for(auto s:sterm)
    102             match &= field.match(s);
    103         if(match)
    104             vec.push_back(field);
    105     }
    106 
    107     std::sort(vec.begin(), vec.end());
    108 
    109     return vec;
    110 }
    111 
    112 void BankDb::addBankDir(std::string bnk)
    113 {
    114     bool repeat = false;
    115     for(auto b:banks)
    116         repeat |= b == bnk;
    117 
    118     if(!repeat)
    119         banks.push_back(bnk);
    120 }
    121 
    122 void BankDb::clear(void)
    123 {
    124     banks.clear();
    125     fields.clear();
    126 }
    127 
    128 static std::string getCacheName(void)
    129 {
    130     char name[512] = {};
    131     snprintf(name, sizeof(name), "%s%s", getenv("HOME"),
    132             "/.zynaddsubfx-bank-cache.xml");
    133     return name;
    134 }
    135 
    136 static bvec loadCache(void)
    137 {
    138     bvec cache;
    139     XMLwrapper xml;
    140     xml.loadXMLfile(getCacheName());
    141     if(xml.enterbranch("bank-cache")) {
    142         auto nodes = xml.getBranch();
    143 
    144         for(auto node:nodes) {
    145             BankEntry be;
    146 #define bind(x,y) if(node.has(#x)) {be.x = y(node[#x].c_str());}
    147             bind(file, string);
    148             bind(bank, string);
    149             bind(name, string);
    150             bind(comments, string);
    151             bind(author, string);
    152             bind(type, atoi);
    153             bind(id, atoi);
    154             bind(add, atoi);
    155             bind(pad, atoi);
    156             bind(sub, atoi);
    157             bind(time, atoi);
    158 #undef bind
    159             cache.push_back(be);
    160         }
    161     }
    162     return cache;
    163 }
    164 
    165 static void saveCache(bvec vec)
    166 {
    167     XMLwrapper xml;
    168     xml.beginbranch("bank-cache");
    169     for(auto value:vec) {
    170         XmlNode binding("instrument-entry");
    171 #define bind(x) binding[#x] = to_s(value.x);
    172         bind(file);
    173         bind(bank);
    174         bind(name);
    175         bind(comments);
    176         bind(author);
    177         bind(type);
    178         bind(id);
    179         bind(add);
    180         bind(pad);
    181         bind(sub);
    182         bind(time);
    183 #undef bind
    184         xml.add(binding);
    185     }
    186     xml.endbranch();
    187     xml.saveXMLfile(getCacheName(), 0);
    188 }
    189 
    190 void BankDb::scanBanks(void)
    191 {
    192     fields.clear();
    193     bvec cache = loadCache();
    194     bmap cc;
    195     for(auto c:cache)
    196         cc[c.bank + c.file] = c;
    197 
    198     bvec ncache;
    199     for(auto bank:banks)
    200     {
    201         DIR *dir = opendir(bank.c_str());
    202 
    203         if(!dir)
    204             continue;
    205 
    206 
    207         struct dirent *fn;
    208 
    209         while((fn = readdir(dir))) {
    210             const char *filename = fn->d_name;
    211 
    212             //check for extension
    213             if(!strstr(filename, INSTRUMENT_EXTENSION))
    214                 continue;
    215 
    216             auto xiz = processXiz(filename, bank, cc);
    217             fields.push_back(xiz);
    218             ncache.push_back(xiz);
    219         }
    220 
    221         closedir(dir);
    222 
    223     }
    224     saveCache(ncache);
    225 }
    226 
    227 BankEntry BankDb::processXiz(std::string filename,
    228         std::string bank, bmap &cache) const
    229 {
    230     string fname = bank+filename;
    231 
    232     //Grab a timestamp
    233     struct stat st;
    234     int time = 0;
    235 
    236     //gah windows, just implement the darn standard APIs
    237 #ifndef WIN32
    238     int ret  = lstat(fname.c_str(), &st);
    239     if(ret != -1)
    240 # ifdef __APPLE__
    241         time = st.st_mtimespec.tv_sec;
    242 # else
    243         time = st.st_mtim.tv_sec;
    244 # endif
    245 #else
    246     int ret = 0;
    247     time = rand();
    248 #endif
    249 
    250 
    251     //quickly check if the file exists in the cache and if it is up-to-date
    252     if(cache.find(fname) != cache.end() &&
    253             cache[fname].time == time)
    254         return cache[fname];
    255 
    256 
    257 
    258     //verify if the name is like this NNNN-name (where N is a digit)
    259     int no = 0;
    260     unsigned int startname = 0;
    261 
    262     for(unsigned int i = 0; i < 4; ++i) {
    263         if(filename.length() <= i)
    264             break;
    265 
    266         if(isdigit(filename[i])) {
    267             no = no * 10 + (filename[i] - '0');
    268             startname++;
    269         }
    270     }
    271 
    272     if(startname + 1 < filename.length())
    273         startname++;  //to take out the "-"
    274 
    275     std::string name = filename;
    276 
    277     //remove the file extension
    278     for(int i = name.size() - 1; i >= 2; i--) {
    279         if(name[i] == '.') {
    280             name = name.substr(0, i);
    281             break;
    282         }
    283     }
    284 
    285 
    286     BankEntry entry;
    287     entry.file = filename;
    288     entry.bank = bank;
    289     entry.id   = no;
    290     entry.time = time;
    291 
    292     if(no != 0) //the instrument position in the bank is found
    293         entry.name = name.substr(startname);
    294     else
    295         entry.name = name;
    296 
    297     const char *types[] = {
    298         "None",
    299         "Piano",
    300         "Chromatic Percussion",
    301         "Organ",
    302         "Guitar",
    303         "Bass",
    304         "Solo Strings",
    305         "Ensemble",
    306         "Brass",
    307         "Reed",
    308         "Pipe",
    309         "Synth Lead",
    310         "Synth Pad",
    311         "Synth Effects",
    312         "Ethnic",
    313         "Percussive",
    314         "Sound Effects",
    315     };
    316 
    317 
    318     //Try to obtain other metadata (expensive)
    319     XMLwrapper xml;
    320     ret = xml.loadXMLfile(fname);
    321     if(xml.enterbranch("INSTRUMENT")) {
    322         if(xml.enterbranch("INFO")) {
    323             char author[1024];
    324             char comments[1024];
    325             int  type = 0;
    326             xml.getparstr("author", author, 1024);
    327             xml.getparstr("comments", comments, 1024);
    328             type = xml.getpar("type", 0, 0, 16);
    329             entry.author   = author;
    330             entry.comments = comments;
    331             entry.type     = types[type];
    332             xml.exitbranch();
    333         }
    334         if(xml.enterbranch("INSTRUMENT_KIT")) {
    335             for(int i = 0; i < NUM_KIT_ITEMS; ++i) {
    336                 if(xml.enterbranch("INSTRUMENT_KIT_ITEM", i) == 0) {
    337                     entry.add |= xml.getparbool("add_enabled", false);
    338                     entry.sub |= xml.getparbool("sub_enabled", false);
    339                     entry.pad |= xml.getparbool("pad_enabled", false);
    340                     xml.exitbranch();
    341                 }
    342             }
    343             xml.exitbranch();
    344         }
    345         xml.exitbranch();
    346     }
    347 
    348     //printf("Bank Entry:\n");
    349     //printf("\tname   - %s\n", entry.name.c_str());
    350     //printf("\tauthor - %s\n", line(entry.author).c_str());
    351     //printf("\tbank   - %s\n", entry.bank.c_str());
    352     //printf("\tadd/pad/sub - %d/%d/%d\n", entry.add, entry.pad, entry.sub);
    353 
    354     return entry;
    355 }
    356 
    357 }