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 }