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