1 /************************** BEGIN mspUI.h **************************/ 2 /************************************************************************ 3 FAUST Architecture File 4 Copyright (C) 2018 GRAME, Centre National de Creation Musicale 5 --------------------------------------------------------------------- 6 This Architecture section is free software; you can redistribute it 7 and/or modify it under the terms of the GNU General Public License 8 as published by the Free Software Foundation; either version 3 of 9 the License, or (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; If not, see <http://www.gnu.org/licenses/>. 18 19 EXCEPTION : As a special exception, you may create a larger work 20 that contains this FAUST architecture section and distribute 21 that work under terms of your choice, so long as this FAUST 22 architecture section is not modified. 23 ************************************************************************/ 24 // 25 // mspUI.h for static Max/MSP externals and faustgen~ 26 // 27 // Created by Martin Di Rollo on 18/04/12. 28 // Copyright (c) 2012-2019 Grame. All rights reserved. 29 // 30 31 #ifndef _mspUI_h 32 #define _mspUI_h 33 34 #include <math.h> 35 #include <string> 36 #include <map> 37 38 #include "faust/gui/UI.h" 39 #include "faust/gui/PathBuilder.h" 40 41 #define STR_SIZE 512 42 #define MULTI_SIZE 256 43 44 #ifdef WIN32 45 #include <stdio.h> 46 #define snprintf _snprintf 47 #ifndef NAN 48 static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff}; 49 #define NAN (*(const float *) __nan) 50 #endif 51 #endif 52 53 using namespace std; 54 55 struct Max_Meta1 : Meta 56 { 57 int fCount; 58 Max_Meta1Max_Meta159 Max_Meta1():fCount(0) 60 {} 61 declareMax_Meta162 void declare(const char* key, const char* value) 63 { 64 if ((strcmp("name", key) == 0) || (strcmp("author", key) == 0)) { 65 fCount++; 66 } 67 } 68 }; 69 70 struct Max_Meta2 : Meta 71 { declareMax_Meta272 void declare(const char* key, const char* value) 73 { 74 if ((strcmp("name", key) == 0) || (strcmp("author", key) == 0)) { 75 post("%s : %s", key, value); 76 } 77 } 78 }; 79 80 struct Max_Meta3 : Meta 81 { 82 string fName; 83 endWithMax_Meta384 bool endWith(const string& str, const string& suffix) 85 { 86 size_t i = str.rfind(suffix); 87 return (i != string::npos) && (i == (str.length() - suffix.length())); 88 } 89 declareMax_Meta390 void declare(const char* key, const char* value) 91 { 92 if ((strcmp("filename", key) == 0)) { 93 string val = value; 94 if (endWith(value, ".dsp")) { 95 fName = "com.grame." + val.substr(0, val.size() - 4) + "~"; 96 } else { 97 fName = "com.grame." + val + "~"; 98 } 99 } 100 } 101 }; 102 103 class mspUIObject { 104 105 protected: 106 107 string fLabel; 108 FAUSTFLOAT* fZone; 109 range(FAUSTFLOAT min,FAUSTFLOAT max,FAUSTFLOAT val)110 FAUSTFLOAT range(FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT val) {return (val < min) ? min : (val > max) ? max : val;} 111 112 public: 113 mspUIObject(const string & label,FAUSTFLOAT * zone)114 mspUIObject(const string& label, FAUSTFLOAT* zone):fLabel(label),fZone(zone) {} ~mspUIObject()115 virtual ~mspUIObject() {} 116 setValue(FAUSTFLOAT f)117 virtual void setValue(FAUSTFLOAT f) { *fZone = range(0.0, 1.0, f); } getValue()118 virtual FAUSTFLOAT getValue() { return *fZone; } 119 getInitValue()120 virtual FAUSTFLOAT getInitValue() { return FAUSTFLOAT(0); } getMinValue()121 virtual FAUSTFLOAT getMinValue() { return FAUSTFLOAT(0); } getMaxValue()122 virtual FAUSTFLOAT getMaxValue() { return FAUSTFLOAT(0); } 123 toString(char * buffer)124 virtual void toString(char* buffer) {} getName()125 virtual string getName() { return fLabel; } 126 }; 127 128 class mspCheckButton : public mspUIObject { 129 130 public: 131 mspCheckButton(const string & label,FAUSTFLOAT * zone)132 mspCheckButton(const string& label, FAUSTFLOAT* zone):mspUIObject(label,zone) {} ~mspCheckButton()133 virtual ~mspCheckButton() {} 134 toString(char * buffer)135 void toString(char* buffer) 136 { 137 snprintf(buffer, STR_SIZE, "CheckButton(float): %s", fLabel.c_str()); 138 } 139 }; 140 141 class mspButton : public mspUIObject { 142 143 public: 144 mspButton(const string & label,FAUSTFLOAT * zone)145 mspButton(const string& label, FAUSTFLOAT* zone):mspUIObject(label,zone) {} ~mspButton()146 virtual ~mspButton() {} 147 toString(char * buffer)148 void toString(char* buffer) 149 { 150 snprintf(buffer, STR_SIZE, "Button(float): %s", fLabel.c_str()); 151 } 152 }; 153 154 class mspSlider : public mspUIObject { 155 156 private: 157 158 FAUSTFLOAT fInit; 159 FAUSTFLOAT fMin; 160 FAUSTFLOAT fMax; 161 FAUSTFLOAT fStep; 162 163 public: 164 mspSlider(const string & label,FAUSTFLOAT * zone,FAUSTFLOAT init,FAUSTFLOAT min,FAUSTFLOAT max,FAUSTFLOAT step)165 mspSlider(const string& label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) 166 :mspUIObject(label,zone),fInit(init),fMin(min),fMax(max),fStep(step) {} ~mspSlider()167 virtual ~mspSlider() {} 168 toString(char * buffer)169 void toString(char* buffer) 170 { 171 stringstream str; 172 str << "Slider(float): " << fLabel << " [init=" << fInit << ":min=" << fMin << ":max=" << fMax << ":step=" << fStep << ":cur=" << *fZone << "]"; 173 string res = str.str(); 174 snprintf(buffer, STR_SIZE, "%s", res.c_str()); 175 } 176 setValue(FAUSTFLOAT f)177 void setValue(FAUSTFLOAT f) { *fZone = range(fMin, fMax, f); } 178 getInitValue()179 virtual FAUSTFLOAT getInitValue() { return fInit; } getMinValue()180 virtual FAUSTFLOAT getMinValue() { return fMin; } getMaxValue()181 virtual FAUSTFLOAT getMaxValue() { return fMax; } 182 183 }; 184 185 class mspBargraph : public mspUIObject { 186 187 private: 188 189 FAUSTFLOAT fMin; 190 FAUSTFLOAT fMax; 191 FAUSTFLOAT fCurrent; 192 193 public: 194 mspBargraph(const string & label,FAUSTFLOAT * zone,FAUSTFLOAT min,FAUSTFLOAT max)195 mspBargraph(const string& label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) 196 :mspUIObject(label,zone), fMin(min), fMax(max), fCurrent(*zone) {} ~mspBargraph()197 virtual ~mspBargraph() {} 198 toString(char * buffer)199 void toString(char* buffer) 200 { 201 stringstream str; 202 str << "Bargraph(float): " << fLabel << " [min=" << fMin << ":max=" << fMax << ":cur=" << *fZone << "]"; 203 string res = str.str(); 204 snprintf(buffer, STR_SIZE, "%s", res.c_str()); 205 } 206 207 // special version getValue(bool & new_val)208 virtual FAUSTFLOAT getValue(bool& new_val) 209 { 210 if (*fZone != fCurrent) { 211 fCurrent = *fZone; 212 new_val = true; 213 } else { 214 new_val = false; 215 } 216 return fCurrent; 217 } 218 getMinValue()219 virtual FAUSTFLOAT getMinValue() { return fMin; } getMaxValue()220 virtual FAUSTFLOAT getMaxValue() { return fMax; } 221 222 }; 223 224 class mspUI : public UI, public PathBuilder 225 { 226 private: 227 228 map<string, mspUIObject*> fInputLabelTable; // Input table using labels 229 map<string, mspUIObject*> fInputPathTable; // Input table using paths 230 map<string, mspUIObject*> fOutputLabelTable; // Table containing bargraph with labels 231 map<string, mspUIObject*> fOutputPathTable; // Table containing bargraph with paths 232 233 map<const char*, const char*> fDeclareTable; 234 235 FAUSTFLOAT* fMultiTable[MULTI_SIZE]; 236 int fMultiIndex; 237 int fMultiControl; 238 createLabel(const char * label)239 string createLabel(const char* label) 240 { 241 map<const char*, const char*>::reverse_iterator it; 242 if (fDeclareTable.size() > 0) { 243 unsigned int i = 0; 244 string res = string(label); 245 char sep = '['; 246 for (it = fDeclareTable.rbegin(); it != fDeclareTable.rend(); it++, i++) { 247 res = res + sep + (*it).first + ":" + (*it).second; 248 sep = ','; 249 } 250 res += ']'; 251 fDeclareTable.clear(); 252 return res; 253 } else { 254 return string(label); 255 } 256 } 257 addSlider(const char * label,FAUSTFLOAT * zone,FAUSTFLOAT init,FAUSTFLOAT min,FAUSTFLOAT max,FAUSTFLOAT step)258 void addSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) 259 { 260 mspUIObject* obj = new mspSlider(createLabel(label), zone, init, min, max, step); 261 fInputLabelTable[string(label)] = obj; 262 fInputPathTable[buildPath(label)] = obj; 263 fDeclareTable.clear(); 264 } 265 addBargraph(const char * label,FAUSTFLOAT * zone,FAUSTFLOAT min,FAUSTFLOAT max)266 void addBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) 267 { 268 mspUIObject* obj = new mspBargraph(createLabel(label), zone, min, max); 269 fOutputLabelTable[string(label)] = obj; 270 fOutputPathTable[buildPath(label)] = obj; 271 fDeclareTable.clear(); 272 } 273 274 public: 275 276 typedef map<string, mspUIObject*>::iterator iterator; 277 mspUI()278 mspUI() 279 { 280 for (int i = 0; i < MULTI_SIZE; i++) { 281 fMultiTable[i] = 0; 282 } 283 fMultiIndex = fMultiControl = 0; 284 } 285 ~mspUI()286 virtual ~mspUI() 287 { 288 clear(); 289 } 290 addButton(const char * label,FAUSTFLOAT * zone)291 void addButton(const char* label, FAUSTFLOAT* zone) 292 { 293 mspUIObject* obj = new mspButton(createLabel(label), zone); 294 fInputLabelTable[string(label)] = obj; 295 fInputPathTable[buildPath(label)] = obj; 296 } 297 addCheckButton(const char * label,FAUSTFLOAT * zone)298 void addCheckButton(const char* label, FAUSTFLOAT* zone) 299 { 300 mspUIObject* obj = new mspCheckButton(createLabel(label), zone); 301 fInputLabelTable[string(label)] = obj; 302 fInputPathTable[buildPath(label)] = obj; 303 } 304 addVerticalSlider(const char * label,FAUSTFLOAT * zone,FAUSTFLOAT init,FAUSTFLOAT min,FAUSTFLOAT max,FAUSTFLOAT step)305 void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) 306 { 307 addSlider(label, zone, init, min, max, step); 308 } 309 addHorizontalSlider(const char * label,FAUSTFLOAT * zone,FAUSTFLOAT init,FAUSTFLOAT min,FAUSTFLOAT max,FAUSTFLOAT step)310 void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) 311 { 312 addSlider(label, zone, init, min, max, step); 313 } 314 addNumEntry(const char * label,FAUSTFLOAT * zone,FAUSTFLOAT init,FAUSTFLOAT min,FAUSTFLOAT max,FAUSTFLOAT step)315 void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) 316 { 317 addSlider(label, zone, init, min, max, step); 318 } 319 addHorizontalBargraph(const char * label,FAUSTFLOAT * zone,FAUSTFLOAT min,FAUSTFLOAT max)320 void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) 321 { 322 addBargraph(label, zone, min, max); 323 } 324 addVerticalBargraph(const char * label,FAUSTFLOAT * zone,FAUSTFLOAT min,FAUSTFLOAT max)325 void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) 326 { 327 addBargraph(label, zone, min, max); 328 } 329 addSoundfile(const char * label,const char * filename,Soundfile ** sf_zone)330 void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) {} 331 openTabBox(const char * label)332 void openTabBox(const char* label) { pushLabel(label); fDeclareTable.clear(); } openHorizontalBox(const char * label)333 void openHorizontalBox(const char* label) { pushLabel(label); fDeclareTable.clear(); } openVerticalBox(const char * label)334 void openVerticalBox(const char* label) { pushLabel(label); fDeclareTable.clear(); } closeBox()335 void closeBox() { popLabel(); fDeclareTable.clear(); } 336 declare(FAUSTFLOAT * zone,const char * key,const char * val)337 virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val) 338 { 339 if (strcmp(key, "multi") == 0) { 340 int index = atoi(val); 341 if (index >= 0 && index < MULTI_SIZE) { 342 fMultiTable[index] = zone; 343 fMultiControl++; 344 } else { 345 post("Invalid multi index = %d", index); 346 } 347 } 348 349 fDeclareTable[key] = val; 350 } 351 setMultiValues(FAUSTFLOAT * multi,int buffer_size)352 void setMultiValues(FAUSTFLOAT* multi, int buffer_size) 353 { 354 for (int read = 0; read < buffer_size; read++) { 355 int write = (fMultiIndex + read) & (MULTI_SIZE - 1); 356 if (fMultiTable[write]) { 357 *fMultiTable[write] = multi[read]; 358 } 359 } 360 fMultiIndex += buffer_size; 361 } 362 isMulti()363 bool isMulti() { return fMultiControl > 0; } 364 isValue(const string & name)365 bool isValue(const string& name) 366 { 367 return (fInputLabelTable.count(name) || fInputPathTable.count(name)); 368 } 369 isOutputValue(const string & name)370 bool isOutputValue(const string& name) 371 { 372 return fOutputPathTable.count(name); 373 } 374 isInputValue(const string & name)375 bool isInputValue(const string& name) 376 { 377 return fInputPathTable.count(name); 378 } 379 setValue(const string & name,FAUSTFLOAT val)380 bool setValue(const string& name, FAUSTFLOAT val) 381 { 382 if (fInputLabelTable.count(name)) { 383 fInputLabelTable[name]->setValue(val); 384 return true; 385 } else if (fInputPathTable.count(name)) { 386 fInputPathTable[name]->setValue(val); 387 return true; 388 } else { 389 return false; 390 } 391 } 392 getOutputValue(const string & name,bool & new_val)393 FAUSTFLOAT getOutputValue(const string& name, bool& new_val) 394 { 395 return static_cast<mspBargraph*>(fOutputPathTable[name])->getValue(new_val); 396 } 397 begin1()398 iterator begin1() { return fInputLabelTable.begin(); } end1()399 iterator end1() { return fInputLabelTable.end(); } 400 begin2()401 iterator begin2() { return fInputPathTable.begin(); } end2()402 iterator end2() { return fInputPathTable.end(); } 403 begin3()404 iterator begin3() { return fOutputLabelTable.begin(); } end3()405 iterator end3() { return fOutputLabelTable.end(); } 406 begin4()407 iterator begin4() { return fOutputPathTable.begin(); } end4()408 iterator end4() { return fOutputPathTable.end(); } 409 inputItemsCount()410 int inputItemsCount() { return fInputLabelTable.size(); } outputItemsCount()411 int outputItemsCount() { return fOutputLabelTable.size(); } 412 clear()413 void clear() 414 { 415 for (const auto& it : fInputLabelTable) { 416 delete it.second; 417 } 418 fInputLabelTable.clear(); 419 fInputPathTable.clear(); 420 421 for (const auto& it : fOutputLabelTable) { 422 delete it.second; 423 } 424 fOutputLabelTable.clear(); 425 fOutputPathTable.clear(); 426 } 427 displayControls()428 void displayControls() 429 { 430 post("------- Range and path ----------"); 431 for (const auto& it : fInputPathTable) { 432 char param[STR_SIZE]; 433 it.second->toString(param); 434 post(param); 435 string path = "Complete path: " + it.first; 436 post(path.c_str()); 437 } 438 post("---------------------------------"); 439 } 440 checkDigit(const string & name)441 static bool checkDigit(const string& name) 442 { 443 for (int i = name.size() - 1; i >= 0; i--) { 444 if (isdigit(name[i])) { return true; } 445 } 446 return false; 447 } 448 countDigit(const string & name)449 static int countDigit(const string& name) 450 { 451 int count = 0; 452 for (int i = name.size() - 1; i >= 0; i--) { 453 if (isdigit(name[i])) { count++; } 454 } 455 return count; 456 } 457 458 }; 459 460 //============== 461 // MIDI handler 462 //============== 463 464 struct max_midi : public midi_handler { 465 466 void* m_midi_outlet = NULL; 467 m_midi_outletmax_midi468 max_midi(void* midi_outlet = NULL):m_midi_outlet(midi_outlet) 469 {} 470 sendMessagemax_midi471 void sendMessage(std::vector<unsigned char>& message) 472 { 473 assert(m_midi_outlet); 474 for (int i = 0; i < message.size(); i++) { 475 outlet_int(m_midi_outlet, message[i]); 476 } 477 } 478 479 // MIDI output API keyOnmax_midi480 MapUI* keyOn(int channel, int pitch, int velocity) 481 { 482 std::vector<unsigned char> message; 483 message.push_back(MIDI_NOTE_ON + channel); 484 message.push_back(pitch); 485 message.push_back(velocity); 486 sendMessage(message); 487 return NULL; 488 } 489 keyOffmax_midi490 void keyOff(int channel, int pitch, int velocity) 491 { 492 std::vector<unsigned char> message; 493 message.push_back(MIDI_NOTE_OFF + channel); 494 message.push_back(pitch); 495 message.push_back(velocity); 496 sendMessage(message); 497 } 498 ctrlChangemax_midi499 void ctrlChange(int channel, int ctrl, int val) 500 { 501 std::vector<unsigned char> message; 502 message.push_back(MIDI_CONTROL_CHANGE + channel); 503 message.push_back(ctrl); 504 message.push_back(val); 505 sendMessage(message); 506 } 507 chanPressmax_midi508 void chanPress(int channel, int press) 509 { 510 std::vector<unsigned char> message; 511 message.push_back(MIDI_AFTERTOUCH + channel); 512 message.push_back(press); 513 sendMessage(message); 514 } 515 progChangemax_midi516 void progChange(int channel, int pgm) 517 { 518 std::vector<unsigned char> message; 519 message.push_back(MIDI_PROGRAM_CHANGE + channel); 520 message.push_back(pgm); 521 sendMessage(message); 522 } 523 keyPressmax_midi524 void keyPress(int channel, int pitch, int press) 525 { 526 std::vector<unsigned char> message; 527 message.push_back(MIDI_POLY_AFTERTOUCH + channel); 528 message.push_back(pitch); 529 message.push_back(press); 530 sendMessage(message); 531 } 532 pitchWheelmax_midi533 void pitchWheel(int channel, int wheel) 534 { 535 std::vector<unsigned char> message; 536 message.push_back(MIDI_PITCH_BEND + channel); 537 message.push_back(wheel & 0x7F); // lsb 7bit 538 message.push_back((wheel >> 7) & 0x7F); // msb 7bit 539 sendMessage(message); 540 } 541 ctrlChange14bitsmax_midi542 void ctrlChange14bits(int channel, int ctrl, int value) {} 543 startSyncmax_midi544 void startSync(double date) 545 { 546 std::vector<unsigned char> message; 547 message.push_back(MIDI_START); 548 sendMessage(message); 549 } 550 stopSyncmax_midi551 void stopSync(double date) 552 { 553 std::vector<unsigned char> message; 554 message.push_back(MIDI_STOP); 555 sendMessage(message); 556 } 557 clockmax_midi558 void clock(double date) 559 { 560 std::vector<unsigned char> message; 561 message.push_back(MIDI_CLOCK); 562 sendMessage(message); 563 } 564 sysExmax_midi565 void sysEx(double, std::vector<unsigned char>& message) 566 { 567 sendMessage(message); 568 } 569 }; 570 571 #endif 572 /************************** END mspUI.h **************************/ 573