zynaddsubfx

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

PluginTest.cpp (8595B)


      1 /*
      2   ZynAddSubFX - a software synthesizer
      3 
      4   PluginTest.h - CxxTest for embedding zyn
      5   Copyright (C) 2013-2013 Mark McCurry
      6   Authors: Mark McCurry
      7 
      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 "test-suite.h"
     14 #include <cmath>
     15 #include <cstdlib>
     16 #include <iostream>
     17 #include <fstream>
     18 #include <regex>
     19 #include <string>
     20 #include "../Misc/MiddleWare.h"
     21 #include "../Misc/Master.h"
     22 #include "../Misc/PresetExtractor.h"
     23 #include "../Misc/PresetExtractor.cpp"
     24 #include "../Misc/Util.h"
     25 #include "../globals.h"
     26 #include "../UI/NSM.H"
     27 
     28 using namespace std;
     29 using namespace zyn;
     30 
     31 SYNTH_T *synth;
     32 NSM_Client *nsm = 0;
     33 
     34 char *instance_name=(char*)"";
     35 MiddleWare *middleware;
     36 
     37 void fill_vec_with_lines(std::vector<string> &v, string s)
     38 {
     39     std::istringstream stream(s);
     40     std::string line;
     41     while(std::getline(stream, line))
     42         v.push_back(line);
     43 }
     44 void print_string_differences(string orig, string next)
     45 {
     46     std::vector<string> a, b;
     47     fill_vec_with_lines(a, orig);
     48     fill_vec_with_lines(b, next);
     49     int N = a.size();
     50     int M = b.size();
     51     printf("%d vs %d\n", N, M);
     52 
     53     //Original String by New String
     54     //Each step is either an insertion, deletion, or match
     55     //Match     is 0 cost and moves +1 State (if symbols are the same)
     56     //Replace   is 3 cost and moves +1 State (if symbols are different)
     57     //Insertion is 2 cost and moves +2 State (+2 if symbols are different)
     58     //Deletion  is 1 cost and moves +0 State (+2 if symbols are different)
     59     char *transition = new char[N*M];
     60     float  *cost       = new float[N*M];
     61 
     62     const int match  = 1;
     63     const int insert = 2;
     64     const int del    = 3;
     65     for(int i=0; i<N; ++i) {
     66         for(int j=0; j<M; ++j) {
     67             transition[i*M + j] = 0;
     68             cost[i*M + j] = 0xffff;
     69         }
     70     }
     71 
     72     //Just assume the -1 line is the same
     73     cost[0*M + 0] = (a[0] != b[0])*3;
     74     cost[0*M + 1] = (a[1] != b[0])*2 + 2;
     75     for(int i=1; i<N; ++i) {
     76         for(int j=0; j<M; ++j) {
     77             int cost_match = 0xffffff;
     78             int cost_ins   = 0xffffff;
     79             int cost_del   = 0xffffff;
     80             cost_del = cost[(i-1)*M + j] + 2 + (a[i] != b[j])*2;
     81             if(j > 1)
     82                 cost_ins = cost[(i-1)*M + (j-2)] + 1 + 2*(a[i] != b[j]);
     83             if(j > 0)
     84                 cost_match = cost[(i-1)*M + (j-1)] + 3*(a[i] != b[j]);
     85 
     86             if(cost_match >= 0xffff && cost_ins >= 0xffff && cost_del >= 0xffff) {
     87                 ;
     88             } else if(cost_match < cost_ins && cost_match < cost_del) {
     89                 cost[i*M+j] = cost_match;
     90                 transition[i*M+j] = match;
     91             } else if(cost_ins < cost_del) {
     92                 cost[i*M+j] = cost_ins;
     93                 transition[i*M+j] = insert;
     94             } else {
     95                 cost[i*M+j] = cost_del;
     96                 transition[i*M+j] = del;
     97             }
     98         }
     99     }
    100 
    101     //int off = 0;
    102     //int len = N;
    103     //for(int i=off; i<off+len; ++i) {
    104     //    for(int j=off; j<off+len; ++j) {
    105     //        printf("%4d ", (int)(cost[i*M+j] > 4000 ? -1 : cost[i*M+j]));
    106     //    }
    107     //    printf("\n");
    108     //}
    109     //for(int i=off; i<off+len; ++i) {
    110     //    for(int j=off; j<off+len; ++j) {
    111     //        printf("%d ", transition[i*M+j]);
    112     //    }
    113     //    printf("\n");
    114     //}
    115 
    116     //for(int i=off; i<off+len; ++i)
    117     //    printf("%d: %s\n", i, a[i].c_str());
    118     //for(int i=off; i<off+len; ++i)
    119     //    printf("%d: %s\n", i, b[i].c_str());
    120     //exit(1);
    121 
    122     int total_cost = cost[(N-1)*M + (M-1)];
    123     if(total_cost < 500) {
    124         printf("total cost = %f\n", cost[(N-1)*M + (M-1)]);
    125 
    126         int b_pos = b.size()-1;
    127         int a_pos = a.size()-1;
    128         while(a_pos > 0 && b_pos > 0) {
    129             //printf("state = (%d, %d) => %f\n", a_pos, b_pos, cost[a_pos*M+b_pos]);
    130             if(transition[a_pos*M+b_pos] == match) {
    131                 if(a[a_pos] != b[b_pos]) {
    132                     printf("REF - %s\n", a[a_pos].c_str());
    133                     printf("NEW + %s\n", b[b_pos].c_str());
    134                 }
    135                 //printf("R");
    136                 a_pos -= 1;
    137                 b_pos -= 1;
    138             } else if(transition[a_pos*M+b_pos] == del) {
    139                 //printf("D");
    140                 //if(a[a_pos] != b[b_pos]) {
    141                     //printf("- %s\n", a[a_pos].c_str());
    142                     printf("NEW - %s\n", b[b_pos].c_str());
    143                 //}
    144                 b_pos -= 1;
    145             } else if(transition[a_pos*M+b_pos] == insert) {
    146                 //if(a[a_pos] != b[b_pos]) {
    147                 printf("REF - %s\n", a[a_pos].c_str());
    148                 printf("NEW + %s\n", b[b_pos].c_str());
    149                 printf("NEW + %s\n", b[b_pos-1].c_str());
    150                 //}
    151                 //printf("I");
    152                 a_pos -= 1;
    153                 b_pos -= 2;
    154             } else {
    155                 printf("ERROR STATE @(%d, %d)\n", a_pos, b_pos);
    156                 exit(1);
    157             }
    158 
    159         }
    160         //printf("%d vs %d\n", N, M);
    161     } else {
    162         printf("[WARNING] XML File appears to be radically different\n");
    163     }
    164 }
    165 
    166 class PluginTest
    167 {
    168     public:
    169         struct FFTCleaner { ~FFTCleaner() { FFT_cleanup(); } } cleaner;
    170         Config config;
    171         void setUp() {
    172             config.cfg.SaveFullXml = false;
    173 
    174             synth = new SYNTH_T;
    175             synth->buffersize = 256;
    176             synth->samplerate = 48000;
    177             synth->alias();
    178 
    179             outL  = new float[1024];
    180             for(int i = 0; i < synth->buffersize; ++i)
    181                 outL[i] = 0.0f;
    182             outR = new float[1024];
    183             for(int i = 0; i < synth->buffersize; ++i)
    184                 outR[i] = 0.0f;
    185 
    186             for(int i = 0; i < 16; ++i)
    187                 master[i] = new Master(*synth, &config);
    188         }
    189 
    190         void tearDown() {
    191             for(int i = 0; i < 16; ++i)
    192                 delete master[i];
    193 
    194             delete[] outL;
    195             delete[] outR;
    196             delete synth;
    197         }
    198 
    199 
    200         void testInit() {
    201 
    202             for(int x=0; x<100; ++x)
    203                 for(int i=0; i<16; ++i)
    204                     master[i]->GetAudioOutSamples(rand()%1025,
    205                             synth->samplerate, outL, outR);
    206         }
    207 
    208         void testPanic()
    209         {
    210             master[0]->setController(0, 0x64, 0);
    211             master[0]->noteOn(0,64,64);
    212             master[0]->AudioOut(outL, outR);
    213 
    214             float sum = 0.0f;
    215             for(int i = 0; i < synth->buffersize; ++i)
    216                 sum += fabsf(outL[i]);
    217 
    218             TS_ASSERT(0.1f < sum);
    219         }
    220 
    221         string loadfile(string fname) const
    222         {
    223             std::ifstream t(fname.c_str());
    224             std::string str((std::istreambuf_iterator<char>(t)),
    225                                      std::istreambuf_iterator<char>());
    226             return str;
    227         }
    228 
    229 
    230         void testLoadSave(void)
    231         {
    232             // Do the load/save
    233             const string fname = string(SOURCE_DIR) + "/guitar-adnote.xmz";
    234             string fdata = loadfile(fname);
    235             char *result = NULL;
    236             master[0]->putalldata((char*)fdata.c_str());
    237             int res = master[0]->getalldata(&result);
    238 
    239             // Fixup, because d44dc9b corrupted guitar-adnote.xmz:
    240             // Replace "1.0f" with "1.0" and "UTF-8" with "utf-8" in `<?xml...`
    241             fdata = std::regex_replace(fdata,
    242                                        std::regex(R"(<\?xml version="1\.0f" encoding="UTF-8"\?>)"),
    243                                        R"(<?xml version="1.0" encoding="utf-8"?>)");
    244 
    245             // Fixups, because guitar-adnote.xmz was saved with MXML3
    246 #if MXML_MAJOR_VERSION >= 4
    247             // guitar-adnote has tags ending on " />" - we remove the space
    248             fdata = std::regex_replace(fdata, std::regex(" />"), "/>");
    249             // Remove trailing newline
    250             if (fdata.size() >= 1 && fdata[fdata.size() - 1] == '\n') {
    251                 fdata.pop_back();
    252             }
    253 #endif
    254 
    255             // Checks
    256             TS_ASSERT_EQUAL_INT((int)(fdata.length()+1), res);
    257             TS_ASSERT(fdata == result);
    258             if(fdata != result)
    259                 print_string_differences(fdata, result);
    260 
    261             free(result);
    262         }
    263 
    264 
    265     private:
    266         float *outR, *outL;
    267         Master *master[16];
    268 };
    269 
    270 int main()
    271 {
    272     PluginTest test;
    273     RUN_TEST(testInit);
    274     RUN_TEST(testPanic);
    275     RUN_TEST(testLoadSave);
    276     return test_summary();
    277 }