1 /************************************************************************
2 FAUST Architecture File
3 Copyright (C) 2012-2020 GRAME, Centre National de Creation Musicale
4 ---------------------------------------------------------------------
5 This Architecture section is free software; you can redistribute it
6 and/or modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 3 of
8 the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; If not, see <http://www.gnu.org/licenses/>.
17
18 EXCEPTION : As a special exception, you may create a larger work
19 that contains this FAUST architecture section and distribute
20 that work under terms of your choice, so long as this FAUST
21 architecture section is not modified.
22 ************************************************************************
23 ************************************************************************/
24
25 #ifdef WIN32
26 #pragma warning (disable: 4244 4800 4267)
27 #define _CRT_SECURE_NO_WARNINGS
28 #else
29 #include <Carbon/Carbon.h>
30 #endif
31
32 #include "faustgen~.h"
33 #include "faust/dsp/libfaust.h"
34
35 #define LLVM_DSP
36 #include "faust/dsp/poly-dsp.h"
37
38 // Globals
39 int faustgen_factory::gFaustCounter = 0;
40 map<string, faustgen_factory*> faustgen_factory::gFactoryMap;
41 t_jrgba faustgen::gDefaultColor = {-1., -1., -1., -1.};
42
43 std::list<GUI*> GUI::fGuiList;
44 ztimedmap GUI::gTimedZoneMap;
45
46 //===================
47 // Faust DSP Factory
48 //===================
49
50 struct MyMeta : public Meta, public std::map<std::string, std::string>
51 {
declareMyMeta52 void declare(const char* key, const char* value)
53 {
54 (*this)[key] = value;
55 }
getMyMeta56 const std::string get(const char* key, const char* def)
57 {
58 return (this->find(key) != this->end()) ? (*this)[key] : def;
59 }
60 };
61
getCodeSize()62 static const char* getCodeSize()
63 {
64 int tmp;
65 return (sizeof(&tmp) == 8) ? "64 bits" : "32 bits";
66 }
67
68 #ifdef __APPLE__
69
70 #include <CoreFoundation/CoreFoundation.h>
71 #include <IOKit/IOKitLib.h>
72
getTarget()73 static string getTarget()
74 {
75 int tmp;
76 return (sizeof(&tmp) == 8) ? "" : "i386-apple-darwin10.6.0";
77 }
78
79 // Returns the serial number as a CFString.
80 // It is the caller's responsibility to release the returned CFString when done with it.
getSerialNumber()81 static string getSerialNumber()
82 {
83 io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
84
85 if (platformExpert) {
86 CFTypeRef serialNumberAsCFString =
87 IORegistryEntryCreateCFProperty(platformExpert,
88 CFSTR(kIOPlatformSerialNumberKey),
89 kCFAllocatorDefault, 0);
90 if (serialNumberAsCFString) {
91 char serial_name[256];
92 CFStringGetCString((CFStringRef)serialNumberAsCFString, serial_name, 256, kCFStringEncodingMacRoman);
93 string res = string(serial_name) + string(getCodeSize());
94 CFRelease(serialNumberAsCFString);
95 return res;
96 }
97 IOObjectRelease(platformExpert);
98 }
99 return "";
100 }
101
102 #else
103
getTarget()104 static string getTarget() { return ""; }
105
getComputerName(char * computer_name,DWORD * computer_name_lg)106 static int getComputerName(char* computer_name, DWORD* computer_name_lg)
107 {
108 HKEY hKey;
109 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
110 "SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ComputerName",
111 0, KEY_QUERY_VALUE, &hKey ) != ERROR_SUCCESS)
112 return FALSE;
113 if (RegQueryValueEx(hKey, "ComputerName", NULL, NULL,
114 (LPBYTE) computer_name,
115 (LPDWORD) computer_name_lg) != ERROR_SUCCESS) {
116 RegCloseKey(hKey);
117 return FALSE;
118 }
119 RegCloseKey(hKey);
120 return TRUE;
121 }
122
getSerialNumber()123 static string getSerialNumber()
124 {
125 char serial_name[256] = "Default Windows name";
126 DWORD name_lg = 256;
127 if (getComputerName(serial_name, &name_lg) == TRUE) {
128 return string(serial_name) + string(getCodeSize());
129 } else {
130 return "Windows";
131 }
132 }
133
134 #endif
135
getFolderFromFilename(const string & fullpath)136 static string getFolderFromFilename(const string& fullpath)
137 {
138 size_t first = fullpath.find_first_of(SEPARATOR);
139 size_t last = fullpath.find_last_of(SEPARATOR);
140 return (first != string::npos && last != string::npos) ? fullpath.substr(first, last - first) : "";
141 }
142
getFolderFromPath(const string & fullpath)143 static string getFolderFromPath(const string& fullpath)
144 {
145 size_t first = fullpath.find_first_of(SEPARATOR);
146 return (first != string::npos) ? fullpath.substr(first, fullpath.size() - first) : "";
147 }
148
149 struct Max_Meta : public Meta
150 {
declareMax_Meta151 void declare(const char* key, const char* value)
152 {
153 if ((strcmp("name", key) == 0) || (strcmp("author", key) == 0)) {
154 post("%s : %s", key, value);
155 }
156 }
157 };
158
faustgen_factory(const string & name)159 faustgen_factory::faustgen_factory(const string& name)
160 {
161 m_siginlets = 0;
162 m_sigoutlets = 0;
163
164 fDefaultPath = path_getdefault();
165 fName = name;
166 fDSPfactory = NULL;
167 fBitCodeSize = 0;
168 fBitCode = NULL;
169 fSourceCodeSize = 0;
170 fSourceCode = NULL;
171 gFaustCounter++;
172 fFaustNumber = gFaustCounter;
173 fOptLevel = LLVM_OPTIMIZATION;
174 fPolyphonic = false;
175 fSoundUI = NULL;
176
177 #ifdef __APPLE__
178 // OSX only : access to the fautgen~ bundle
179 CFBundleRef faustgen_bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.grame.faustgen-")); // - character added since SDK 7.3.3
180 CFURLRef faustgen_ref = CFBundleCopyBundleURL(faustgen_bundle);
181 UInt8 bundle_path[512];
182 Boolean res = CFURLGetFileSystemRepresentation(faustgen_ref, true, bundle_path, 512);
183 assert(res);
184
185 // Built the complete resource path
186 fLibraryPath.insert(string((const char*)bundle_path) + string(FAUST_LIBRARY_PATH));
187
188 // Draw path in temporary folder
189 fDrawPath = string(FAUST_DRAW_PATH);
190 #endif
191
192 #ifdef WIN32
193 HMODULE handle = LoadLibrary("faustgen~.mxe64");
194 if (handle) {
195 // Get faustgen~.mxe64 path
196 char name[512];
197 GetModuleFileName(handle, name, 512);
198 string str_name = string(name);
199 str_name = str_name.substr(0, str_name.find_last_of("\\"));
200 // Built the complete resource path
201 fLibraryPath.insert(string(str_name) + string(FAUST_LIBRARY_PATH));
202 // Draw path in temporary folder
203 TCHAR lpTempPathBuffer[MAX_PATH];
204 // Gets the temp path env string (no guarantee it's a valid path).
205 DWORD dwRetVal = GetTempPath(MAX_PATH, lpTempPathBuffer);
206 if (dwRetVal > MAX_PATH || (dwRetVal == 0)) {
207 post("GetTempPath failed...");
208 // Try our value instead...
209 fDrawPath = string(str_name) + string(FAUST_DRAW_PATH);
210 } else {
211 fDrawPath = string(lpTempPathBuffer);
212 }
213 FreeLibrary(handle);
214 } else {
215 post("Error : cannot locate faustgen~.mxe64...");
216 fDrawPath = "";
217 }
218 #endif
219 }
220
~faustgen_factory()221 faustgen_factory::~faustgen_factory()
222 {
223 free_dsp_factory();
224 free_sourcecode();
225 free_bitcode();
226
227 remove_svg();
228
229 delete fSoundUI;
230 fSoundUI = NULL;
231 }
232
free_sourcecode()233 void faustgen_factory::free_sourcecode()
234 {
235 if (fSourceCode) {
236 sysmem_freehandle(fSourceCode);
237 fSourceCodeSize = 0;
238 fSourceCode = NULL;
239 }
240 }
241
free_bitcode()242 void faustgen_factory::free_bitcode()
243 {
244 if (fBitCode) {
245 sysmem_freehandle(fBitCode);
246 fBitCodeSize = 0;
247 fBitCode = NULL;
248 }
249 }
250
free_dsp_factory()251 void faustgen_factory::free_dsp_factory()
252 {
253 lock_audio();
254 lock_ui();
255 {
256 // Free all instances
257 for (auto& it : fInstances) {
258 it->free_dsp();
259 }
260
261 //deleteDSPFactory(fDSPfactory);
262 fDSPfactory = NULL;
263 }
264 unlock_ui();
265 unlock_audio();
266 }
267
create_factory_from_bitcode()268 llvm_dsp_factory* faustgen_factory::create_factory_from_bitcode()
269 {
270 // Alternate model using machine code
271 string error_msg;
272 llvm_dsp_factory* factory = readDSPFactoryFromMachine(*fBitCode, getTarget(), error_msg);
273 if (factory) {
274 // Reset fSoundUI with the new factory getIncludePathnames
275 delete fSoundUI;
276 fSoundUI = new SoundUI(factory->getIncludePathnames(), -1, nullptr, true);
277 /*
278 std::vector<std::string> sound_directories = factory->getIncludePathnames();
279 for (int i = 0; i < sound_directories.size(); i++) {
280 post("sound_directories %d %s", i, sound_directories[i].c_str());
281 }
282 */
283 } else {
284 post("%s", error_msg.c_str());
285 }
286 return factory;
287 }
288
create_factory_from_sourcecode()289 llvm_dsp_factory* faustgen_factory::create_factory_from_sourcecode()
290 {
291 char name_app[64];
292 sprintf(name_app, "faustgen-%d", fFaustNumber);
293
294 // To be sure we get a correct SVG diagram...
295 remove_svg();
296
297 default_compile_options();
298 print_compile_options();
299
300 // Prepare compile options
301 string error_msg;
302 const char* argv[64];
303
304 assert(fCompileOptions.size() < 64);
305 StringVectorIt it;
306 int i = 0;
307 for (it = fCompileOptions.begin(); it != fCompileOptions.end(); it++) {
308 argv[i++] = (char*)(*it).c_str();
309 }
310 argv[fCompileOptions.size()] = 0; // NULL terminated argv
311
312 // Generate SVG file
313 if (!generateAuxFilesFromString(name_app, *fSourceCode, fCompileOptions.size(), argv, error_msg)) {
314 post("Generate SVG error : %s", error_msg.c_str());
315 }
316
317 llvm_dsp_factory* factory = createDSPFactoryFromString(name_app, *fSourceCode, fCompileOptions.size(), argv, getTarget(), error_msg, fOptLevel);
318
319 if (factory) {
320 // Reset fSoundUI with the new factory getIncludePathnames
321 delete fSoundUI;
322 fSoundUI = new SoundUI(factory->getIncludePathnames(), -1, nullptr, true);
323 /*
324 std::vector<std::string> sound_directories = factory->getIncludePathnames();
325 for (int i = 0; i < sound_directories.size(); i++) {
326 post("sound_directories %d %s", i, sound_directories[i].c_str());
327 }
328 */
329 return factory;
330 } else {
331 // Update all instances
332 for (auto& it : fInstances) {
333 it->hilight_on();
334 }
335 if (fInstances.begin() != fInstances.end()) {
336 (*fInstances.begin())->hilight_error(error_msg);
337 }
338 post("Invalid Faust code or compile options : %s", error_msg.c_str());
339 return 0;
340 }
341 }
342
create_dsp_instance(int nvoices)343 ::dsp* faustgen_factory::create_dsp_instance(int nvoices)
344 {
345 ::dsp* mono = fDSPfactory->createDSPInstance();
346
347 // Check 'nvoices' metadata
348 if (nvoices == 0) {
349 MyMeta meta;
350 mono->metadata(&meta);
351 std::string numVoices = meta.get("nvoices", "0");
352 nvoices = atoi(numVoices.c_str());
353 if (nvoices < 0) nvoices = 0;
354 }
355
356 if (nvoices > 0) {
357 fPolyphonic = true;
358 return new mydsp_poly(mono, nvoices, true);
359 } else {
360 fPolyphonic = false;
361 return mono;
362 }
363 }
364
create_dsp_aux()365 ::dsp* faustgen_factory::create_dsp_aux()
366 {
367 ::dsp* dsp = 0;
368 Max_Meta meta;
369 string error;
370
371 // Factory already allocated
372 if (fDSPfactory) {
373 dsp = create_dsp_instance();
374 post("Factory already allocated, %i input(s), %i output(s)", dsp->getNumInputs(), dsp->getNumOutputs());
375 goto end;
376 }
377
378 // Tries to create from bitcode
379 if (fBitCodeSize > 0) {
380 fDSPfactory = create_factory_from_bitcode();
381 if (fDSPfactory) {
382 dsp = create_dsp_instance();
383 dsp->metadata(&meta);
384 post("Compilation from bitcode succeeded, %i input(s), %i output(s)", dsp->getNumInputs(), dsp->getNumOutputs());
385 goto end;
386 } else {
387 post("Compilation from bitcode failed...");
388 }
389 }
390
391 // Otherwise tries to create from source code
392 if (fSourceCodeSize > 0) {
393 fDSPfactory = create_factory_from_sourcecode();
394 if (fDSPfactory) {
395 dsp = create_dsp_instance();
396 dsp->metadata(&meta);
397 post("Compilation from source code succeeded, %i input(s), %i output(s)", dsp->getNumInputs(), dsp->getNumOutputs());
398 goto end;
399 } else {
400 post("Compilation from source code failed...");
401 }
402 }
403
404 // Otherwise creates default DSP keeping the same input/output number
405 fDSPfactory = createDSPFactoryFromString("default", DEFAULT_CODE, 0, 0, getTarget(), error, 0);
406 dsp = create_dsp_instance();
407 post("Allocation of default DSP succeeded, %i input(s), %i output(s)", dsp->getNumInputs(), dsp->getNumOutputs());
408
409 end:
410 assert(dsp);
411 m_siginlets = dsp->getNumInputs();
412 m_sigoutlets = dsp->getNumOutputs();
413
414 // Prepare JSON
415 make_json(dsp);
416 return dsp;
417 }
418
make_json(::dsp * dsp)419 void faustgen_factory::make_json(::dsp* dsp)
420 {
421 // Prepare JSON
422 JSONUI builder(m_siginlets, m_sigoutlets);
423 dsp->buildUserInterface(&builder);
424 dsp->metadata(&builder);
425 fJSON = builder.JSON();
426 }
427
add_library_path(const string & library_path)428 void faustgen_factory::add_library_path(const string& library_path)
429 {
430 fLibraryPath.insert(library_path);
431 }
432
add_compile_option(const string & key,const string & value)433 void faustgen_factory::add_compile_option(const string& key, const string& value)
434 {
435 if ((key != "") && (value != "")) {
436 fCompileOptions.push_back(key);
437 fCompileOptions.push_back(value);
438 }
439 }
440
add_compile_option(const string & value)441 void faustgen_factory::add_compile_option(const string& value)
442 {
443 if (value != "") {
444 fCompileOptions.push_back(value);
445 }
446 }
447
print_compile_options()448 void faustgen_factory::print_compile_options()
449 {
450 if (fCompileOptions.size() > 0) {
451 post("-----------------------------");
452 StringVectorIt it;
453 for (it = fCompileOptions.begin(); it != fCompileOptions.end(); it++) {
454 post("Compile option = %s", (*it).c_str());
455 }
456 post("-----------------------------");
457 }
458 }
459
default_compile_options()460 void faustgen_factory::default_compile_options()
461 {
462 // Clear and set default value
463 fCompileOptions.clear();
464
465 // By default when double
466 if (sizeof(FAUSTFLOAT) == 8) {
467 add_compile_option("-double");
468 }
469
470 // Add -svg to current compile options
471 add_compile_option("-svg");
472
473 // All library paths
474 StringSetIt it1;
475 for (it1 = fLibraryPath.begin(); it1 != fLibraryPath.end(); it1++) {
476 add_compile_option("-I", *it1);
477 }
478
479 // Draw path
480 add_compile_option("-O", fDrawPath);
481
482 // All options set in the 'compileoptions' message
483 StringVectorIt it;
484 for (it = fOptions.begin(); it != fOptions.end(); it++) {
485 // '-opt v' : parsed for LLVM optimization level
486 if (*it == "-opt") {
487 it++;
488 fOptLevel = atoi((*it).c_str());
489 } else {
490 add_compile_option(*it);
491 }
492 }
493
494 // Vector mode by default
495 /*
496 add_compile_option("-vec");
497 add_compile_option("-lv");
498 add_compile_option("1");
499 */
500 /*
501 Seems not necessary...
502 fCompileOptions.push_back("-vs");
503 stringstream num;
504 num << sys_getblksize();
505 add_compile_option(num.str());
506 */
507 }
508
getfromdictionary(t_dictionary * d)509 void faustgen_factory::getfromdictionary(t_dictionary* d)
510 {
511 // Read machine serial number
512 const char* serial_number;
513 t_max_err err = dictionary_getstring(d, gensym("serial_number"), &serial_number);
514 if (err != MAX_ERR_NONE || strcmp(serial_number, getSerialNumber().c_str()) != 0) {
515 post("Patch compiled on another machine or another CPU architecture, so ignore bitcode, force recompilation and use default compileoptions");
516 goto read_sourcecode;
517 }
518
519 // Read sourcecode "version" key
520 const char* faustgen_version;
521 err = dictionary_getstring(d, gensym("version"), &faustgen_version);
522
523 if (err != MAX_ERR_NONE) {
524 post("Cannot read \"version\" key, so ignore bitcode, force recompilation and use default compileoptions");
525 goto read_sourcecode;
526 } else if (strcmp(faustgen_version, FAUSTGEN_VERSION) != 0) {
527 post("Older version of faustgen~ (%s versus %s), so ignore bitcode, force recompilation and use default compileoptions", FAUSTGEN_VERSION, faustgen_version);
528 goto read_sourcecode;
529 }
530
531 // Read bitcode size key
532 err = dictionary_getlong(d, gensym("machinecode_size"), (t_atom_long*)&fBitCodeSize);
533 if (err != MAX_ERR_NONE) {
534 fBitCodeSize = 0;
535 goto read_sourcecode;
536 }
537
538 // If OK read bitcode
539 fBitCode = sysmem_newhandleclear(fBitCodeSize + 1); // We need to use a size larger by one for the null terminator
540 const char* bitcode;
541 err = dictionary_getstring(d, gensym("machinecode"), &bitcode); // The retrieved pointer references the string in the dictionary, it is not a copy.
542 sysmem_copyptr(bitcode, *fBitCode, fBitCodeSize);
543 if (err != MAX_ERR_NONE) {
544 fBitCodeSize = 0;
545 }
546
547 read_sourcecode:
548 // Load fLibraryPath
549 int i = 0;
550 const char* read_library_path;
551 char library_path[32];
552
553 loop:
554 snprintf(library_path, 32, "library_path%d", i++);
555 err = dictionary_getstring(d, gensym(library_path), &read_library_path);
556 if (err == MAX_ERR_NONE) {
557 fLibraryPath.insert(read_library_path);
558 goto loop;
559 }
560
561 // Read sourcecode size key
562 err = dictionary_getlong(d, gensym("sourcecode_size"), (t_atom_long*)&fSourceCodeSize);
563 if (err != MAX_ERR_NONE) {
564 goto default_sourcecode;
565 }
566
567 // If OK read sourcecode
568 fSourceCode = sysmem_newhandleclear(fSourceCodeSize + 1); // We need to use a size larger by one for the null terminator
569 const char* sourcecode;
570 err = dictionary_getstring(d, gensym("sourcecode"), &sourcecode); // The retrieved pointer references the string in the dictionary, it is not a copy.
571 sysmem_copyptr(sourcecode, *fSourceCode, fSourceCodeSize);
572 if (err == MAX_ERR_NONE) {
573 return;
574 }
575
576 default_sourcecode:
577 // Otherwise tries to create from default source code
578 fSourceCodeSize = strlen(DEFAULT_SOURCE_CODE);
579 fSourceCode = sysmem_newhandleclear(fSourceCodeSize + 1);
580 sysmem_copyptr(DEFAULT_SOURCE_CODE, *fSourceCode, fSourceCodeSize);
581 }
582
583 // Called when saving the Max patcher
584 // This function saves the necessary data inside the JSON file (Faust sourcecode)
appendtodictionary(t_dictionary * d)585 void faustgen_factory::appendtodictionary(t_dictionary* d)
586 {
587 post("Saving object version, library_path, sourcecode and bitcode...");
588
589 // Save machine serial number
590 dictionary_appendstring(d, gensym("serial_number"), getSerialNumber().c_str());
591
592 // Save faustgen~ version
593 dictionary_appendstring(d, gensym("version"), FAUSTGEN_VERSION);
594
595 // Save fLibraryPath
596 StringSetIt it;
597 int i = 0;
598 for (it = fLibraryPath.begin(); it != fLibraryPath.end(); it++) {
599 char library_path[32];
600 snprintf(library_path, 32, "library_path%d", i++);
601 dictionary_appendstring(d, gensym(library_path), (*it).c_str());
602 }
603
604 // Save source code
605 if (fSourceCodeSize) {
606 dictionary_appendlong(d, gensym("sourcecode_size"), fSourceCodeSize);
607 dictionary_appendstring(d, gensym("sourcecode"), *fSourceCode);
608 }
609
610 // Save bitcode
611 if (fDSPfactory) {
612 // Alternate model using machine code
613 string machinecode = writeDSPFactoryToMachine(fDSPfactory, getTarget());
614 dictionary_appendlong(d, gensym("machinecode_size"), machinecode.size());
615 dictionary_appendstring(d, gensym("machinecode"), machinecode.c_str());
616 }
617 }
618
assist(void * b,long msg,long a,char * dst)619 void faustgen::assist(void* b, long msg, long a, char* dst)
620 {
621 if (msg == ASSIST_INLET) {
622 if (a == 0) {
623 if (fDSP->getNumInputs() == 0) {
624 sprintf(dst, "(messages)");
625 } else {
626 sprintf(dst, "(messages/signal) : Audio Input %ld", (a+1));
627 }
628 } else if (a < fDSP->getNumInputs()) {
629 sprintf(dst, "(signal) : Audio Input %ld", (a+1));
630 }
631 } else if (msg == ASSIST_OUTLET) {
632 if (a < fDSP->getNumOutputs()) {
633 sprintf(dst, "(signal) : Audio Output %ld", (a+1));
634 } else if (a == fDSP->getNumOutputs()) {
635 sprintf(dst, "(list) : [path, cur|init, min, max]*");
636 } else {
637 sprintf(dst, "(int) : raw MIDI bytes*");
638 }
639 }
640 }
641
try_open_svg()642 bool faustgen_factory::try_open_svg()
643 {
644 // Open the svg diagram file inside a web browser
645 char command[512];
646 #ifdef WIN32
647 sprintf(command, "type \"file:///%sfaustgen-%d-svg/process.svg\"", fDrawPath.c_str(), fFaustNumber);
648 #else
649 sprintf(command, "open \"file://%sfaustgen-%d-svg/process.svg\"", fDrawPath.c_str(), fFaustNumber);
650 #endif
651 return (system(command) == 0);
652 }
653
open_svg()654 void faustgen_factory::open_svg()
655 {
656 // Open the svg diagram file inside a web browser
657 char command[512];
658 #ifdef WIN32
659 sprintf(command, "start \"\" \"file:///%sfaustgen-%d-svg/process.svg\"", fDrawPath.c_str(), fFaustNumber);
660 #else
661 sprintf(command, "open \"file://%sfaustgen-%d-svg/process.svg\"", fDrawPath.c_str(), fFaustNumber);
662 #endif
663 system(command);
664 }
665
remove_svg()666 void faustgen_factory::remove_svg()
667 {
668 // Possibly done by "compileoptions" or display_svg
669 char command[512];
670 #ifdef WIN32
671 sprintf(command, "rmdir /S/Q \"%sfaustgen-%d-svg\"", fDrawPath.c_str(), fFaustNumber);
672 #else
673 sprintf(command, "rm -r \"%sfaustgen-%d-svg\"", fDrawPath.c_str(), fFaustNumber);
674 #endif
675 system(command);
676 }
677
display_svg()678 void faustgen_factory::display_svg()
679 {
680 // Try to open SVG svg diagram file inside a web browser
681 if (!try_open_svg()) {
682
683 post("SVG diagram not available, recompile to produce it");
684
685 // Force recompilation to produce it
686 llvm_dsp_factory* factory = create_factory_from_sourcecode();
687 //deleteDSPFactory(factory);
688
689 // Open the SVG diagram file inside a web browser
690 open_svg();
691 }
692 }
693
open_file(const char * file)694 bool faustgen_factory::open_file(const char* file)
695 {
696 char command[512];
697 #ifdef WIN32
698 sprintf(command, "start \"\" \"%s%s\"", (*fLibraryPath.begin()).c_str(), file);
699 #else
700 sprintf(command, "open \"%s%s\"", (*fLibraryPath.begin()).c_str(), file);
701 #endif
702 post(command);
703 return (system(command) == 0);
704 }
705
open_file(const char * appl,const char * file)706 bool faustgen_factory::open_file(const char* appl, const char* file)
707 {
708 char command[512];
709 #ifdef WIN32
710 sprintf(command, "start \"\" %s \"%s%s\"", appl, (*fLibraryPath.begin()).c_str(), file);
711 #else
712 sprintf(command, "open -a %s \"%s%s\"", appl, (*fLibraryPath.begin()).c_str(), file);
713 #endif
714 return (system(command) == 0);
715 }
716
display_documentation()717 void faustgen_factory::display_documentation()
718 {
719 // Open the Web documentation
720 char command[512];
721 #ifdef WIN32
722 sprintf(command, "start \"\" \"https://faustdoc.grame.fr/manual/introduction\"");
723 #else
724 sprintf(command, "open \"https://faustdoc.grame.fr/manual/introduction\"");
725 #endif
726 system(command);
727 }
728
display_libraries_aux(const char * lib)729 void faustgen_factory::display_libraries_aux(const char* lib)
730 {
731 const char* appl;
732 int i = 0;
733
734 while ((appl = TEXT_APPL_LIST[i++]) && (strcmp(appl, "") != 0)) {
735 if (open_file(appl, lib)) {
736 break;
737 }
738 }
739 }
740
display_libraries()741 void faustgen_factory::display_libraries()
742 {
743 // Open the libraries
744 #ifdef WIN32
745 open_file(FAUST_PDF_LIBRARY);
746 open_file("all.lib");
747 open_file("aanl.lib");
748 open_file("analyzers.lib");
749 open_file("basics.lib");
750 open_file("compressors.lib");
751 open_file("delays.lib");
752 open_file("demos.lib");
753 open_file("double.lib");
754 open_file("dx7.lib");
755 open_file("envelopes.lib");
756 open_file("filters.lib");
757 open_file("fds.lib");
758 open_file("hoa.lib");
759 open_file("instruments.lib");
760 open_file("interpolators.lib");
761 open_file("maths.lib");
762 open_file("maxmsp.lib");
763 open_file("mi.lib");
764 open_file("misceffects.lib");
765 open_file("noises.lib");
766 open_file("oscillators.lib");
767 open_file("phaflangers.lib");
768 open_file("physmodels.lib");
769 open_file("platform.lib");
770 open_file("quantizers.lib");
771 open_file("reducemaps.lib");
772 open_file("reverbs.lib");
773 open_file("routes.lib");
774 open_file("runtime.lib");
775 open_file("sf.lib");
776 open_file("signals.lib");
777 open_file("soundfiles.lib");
778 open_file("spats.lib");
779 open_file("stdfaust.lib");
780 open_file("synths.lib");
781 open_file("tonestacks.lib");
782 open_file("tubes.lib");
783 open_file("vaeffects.lib");
784 open_file("version.lib");
785 open_file("wdmodels.lib");
786 open_file("webaudio.lib");
787 #else
788 display_libraries_aux("all.lib");
789 display_libraries_aux("aanl.lib");
790 display_libraries_aux("analyzers.lib");
791 display_libraries_aux("basics.lib");
792 display_libraries_aux("compressors.lib");
793 display_libraries_aux("delays.lib");
794 display_libraries_aux("demos.lib");
795 display_libraries_aux("double.lib");
796 display_libraries_aux("dx7.lib");
797 display_libraries_aux("envelopes.lib");
798 display_libraries_aux("filters.lib");
799 display_libraries_aux("fds.lib");
800 display_libraries_aux("hoa.lib");
801 display_libraries_aux("instruments.lib");
802 display_libraries_aux("interpolators.lib");
803 display_libraries_aux("maths.lib");
804 display_libraries_aux("maxmsp.lib");
805 display_libraries_aux("mi.lib");
806 display_libraries_aux("misceffects.lib");
807 display_libraries_aux("noises.lib");
808 display_libraries_aux("oscillators.lib");
809 display_libraries_aux("phaflangers.lib");
810 display_libraries_aux("physmodels.lib");
811 display_libraries_aux("platform.lib");
812 display_libraries_aux("quantizers.lib");
813 display_libraries_aux("reducemaps.lib");
814 display_libraries_aux("reverbs.lib");
815 display_libraries_aux("routes.lib");
816 display_libraries_aux("runtime.lib");
817 display_libraries_aux("sf.lib");
818 display_libraries_aux("signals.lib");
819 display_libraries_aux("soundfiles.lib");
820 display_libraries_aux("spats.lib");
821 display_libraries_aux("stdfaust.lib");
822 display_libraries_aux("synths.lib");
823 display_libraries_aux("tonestacks.lib");
824 display_libraries_aux("tubes.lib");
825 display_libraries_aux("vaeffects.lib");
826 display_libraries_aux("version.lib");
827 display_libraries_aux("wdmodels.lib");
828 display_libraries_aux("webaudio.lib");
829 #endif
830 }
831
update_sourcecode(int size,char * source_code)832 void faustgen_factory::update_sourcecode(int size, char* source_code)
833 {
834 // Recompile only if text has been changed
835 if (strcmp(source_code, *fSourceCode) != 0) {
836
837 // Update all instances
838 for (auto& it : fInstances) {
839 it->hilight_off();
840 }
841
842 // Delete the existing Faust module
843 free_dsp_factory();
844
845 // Free the memory allocated for fSourceCode
846 free_sourcecode();
847
848 // Free the memory allocated for fBitCode
849 free_bitcode();
850
851 // Allocate the right memory for fSourceCode
852 fSourceCode = sysmem_newhandleclear(size + 1);
853 sysmem_copyptr(source_code, *fSourceCode, size);
854 fSourceCodeSize = size;
855
856 // Update all instances
857 for (auto& it : fInstances) {
858 it->update_sourcecode();
859 }
860
861 } else {
862 post("DSP code has not been changed...");
863 }
864 }
865
librarypath(long inlet,t_symbol * s)866 void faustgen_factory::librarypath(long inlet, t_symbol* s)
867 {
868 if (s != gensym("")) {
869 add_library_path(getFolderFromPath(s->s_name));
870 }
871 }
872
read(long inlet,t_symbol * s)873 void faustgen_factory::read(long inlet, t_symbol* s)
874 {
875 char filename[MAX_FILENAME_CHARS];
876 short path = 0;
877 long type = 'TEXT';
878 t_max_err err;
879 t_filehandle fh;
880
881 // No filename, so open load dialog
882 if (s == gensym("")) {
883 filename[0] = 0;
884 if (open_dialog(filename, &path, (t_fourcc*)&type, (t_fourcc*)&type, 1)) {
885 post("Faust DSP file not found");
886 return;
887 }
888 // Otherwise locate the file
889 } else {
890 strncpy_zero(filename, s->s_name, MAX_FILENAME_CHARS);
891 // Set default path with saved value
892 path_setdefault(fDefaultPath, 0);
893 if (locatefile_extended(filename, &path, (t_fourcc*)&type, (t_fourcc*)&type, 1)) {
894 post("Faust DSP file '%s' not found", filename);
895 return;
896 }
897 }
898
899 // File found, open it and recompile DSP
900 err = path_opensysfile(filename, path, &fh, READ_PERM);
901 if (err) {
902 post("Faust DSP file '%s' cannot be opened", filename);
903 return;
904 }
905
906 // Delete the existing Faust module
907 free_dsp_factory();
908
909 // Free the memory allocated for fBitCode
910 free_bitcode();
911
912 err = sysfile_readtextfile(fh, fSourceCode, 0, (t_sysfile_text_flags)(TEXT_LB_UNIX | TEXT_NULL_TERMINATE));
913 if (err) {
914 post("Faust DSP file '%s' cannot be read", filename);
915 }
916
917 sysfile_close(fh);
918 fSourceCodeSize = sysmem_handlesize(fSourceCode);
919
920 // Add DSP file enclosing folder pathname in the '-I' list
921 char full_path[MAX_FILENAME_CHARS];
922 if ((err = path_topathname(path, filename, full_path)) == 0) {
923 add_library_path(getFolderFromFilename(full_path));
924 } else {
925 post("path_topathname err = %d", err);
926 }
927
928 // Update all instances
929 for (auto& it : fInstances) {
930 it->update_sourcecode();
931 }
932 }
933
write(long inlet,t_symbol * s)934 void faustgen_factory::write(long inlet, t_symbol* s)
935 {
936 char filename[MAX_FILENAME_CHARS];
937 short path = 0;
938 long type = 'TEXT';
939 t_max_err err;
940 t_filehandle fh;
941
942 // No filename, so open save dialog
943 if (s == gensym("")) {
944 filename[0] = 0;
945 if (saveas_dialog(filename, &path, NULL)) {
946 post("Faust DSP file not found");
947 return;
948 } else {
949 err = path_createsysfile(filename, path, type, &fh);
950 if (err) {
951 post("Faust DSP file '%s' cannot be created", filename);
952 return;
953 }
954 }
955 // Otherwise locate or create the file
956 } else {
957 strncpy_zero(filename, s->s_name, MAX_FILENAME_CHARS);
958 // Set default path with saved value
959 path_setdefault(fDefaultPath, 0);
960 if (locatefile_extended(filename, &path, (t_fourcc*)&type, (t_fourcc*)&type, 1)) {
961 post("Faust DSP file '%s' not found, so tries to create it", filename);
962 err = path_createsysfile(filename, path, type, &fh);
963 if (err) {
964 post("Faust DSP file '%s' cannot be created", filename);
965 return;
966 }
967 } else {
968 err = path_opensysfile(filename, path, &fh, WRITE_PERM);
969 if (err) {
970 post("Faust DSP file '%s' cannot be opened", filename);
971 return;
972 }
973 }
974 }
975
976 err = sysfile_writetextfile(fh, fSourceCode, (t_sysfile_text_flags)(TEXT_LB_UNIX | TEXT_NULL_TERMINATE));
977 if (err) {
978 post("Faust DSP file '%s' cannot be written", filename);
979 }
980 sysfile_close(fh);
981 }
982
compileoptions(long inlet,t_symbol * s,long argc,t_atom * argv)983 void faustgen_factory::compileoptions(long inlet, t_symbol* s, long argc, t_atom* argv)
984 {
985 post("Compiler options modified for faustgen");
986
987 if (argc == 0) {
988 post("No argument entered, no additional compilation option will be used");
989 }
990
991 // Clear options
992 fOptions.clear();
993 int i;
994 t_atom* ap;
995
996 // Increment ap each time to get to the next atom
997 for (i = 0, ap = argv; i < argc; i++, ap++) {
998 switch (atom_gettype(ap)) {
999
1000 case A_LONG: {
1001 stringstream num;
1002 num << atom_getlong(ap);
1003 string res = num.str();
1004 fOptions.push_back(res.c_str());
1005 break;
1006 }
1007
1008 case A_FLOAT:
1009 post("Invalid compiler option argument - float");
1010 break;
1011
1012 case A_SYM:
1013 // Add options to default ones
1014 fOptions.push_back(atom_getsym(ap)->s_name);
1015 break;
1016
1017 default:
1018 post("Invalid compiler option argument - unknown");
1019 break;
1020 }
1021 }
1022
1023 /*
1024 if (optimize) {
1025 post("Start looking for optimal compilation options...");
1026 #ifdef __APPLE__
1027 double best;
1028 dsp_optimizer optimizer(string(*fSourceCode), (*fLibraryPath.begin()).c_str(), getTarget(), sys_getblksize());
1029 fOptions = optimizer.findOptimizedParameters(best);
1030 #endif
1031 post("Optimal compilation options found");
1032 }
1033 */
1034
1035 // Delete the existing Faust module
1036 free_dsp_factory();
1037
1038 // Free the memory allocated for fBitCode
1039 free_bitcode();
1040
1041 // Update all instances
1042 for (auto& it : fInstances) {
1043 it->update_sourcecode();
1044 }
1045 }
1046
1047 //====================
1048 // Faust DSP Instance
1049 //====================
1050
allocate_factory(const string & effect_name)1051 bool faustgen::allocate_factory(const string& effect_name)
1052 {
1053 bool res = false;
1054
1055 if (faustgen_factory::gFactoryMap.find(effect_name) != faustgen_factory::gFactoryMap.end()) {
1056 fDSPfactory = faustgen_factory::gFactoryMap[effect_name];
1057 } else {
1058 fDSPfactory = new faustgen_factory(effect_name);
1059 faustgen_factory::gFactoryMap[effect_name] = fDSPfactory;
1060 res = true;
1061 }
1062
1063 fDSPfactory->add_instance(this);
1064 return res;
1065 }
1066
faustgen(t_symbol * sym,long ac,t_atom * argv)1067 faustgen::faustgen(t_symbol* sym, long ac, t_atom* argv)
1068 {
1069 m_siginlets = 0;
1070 m_sigoutlets = 0;
1071
1072 fDSP = NULL;
1073 fDSPUI = NULL;
1074 fMidiUI = NULL;
1075 fOSCUI = NULL;
1076 fSavedUI = NULL;
1077 fDSPfactory = NULL;
1078 fEditor = NULL;
1079 fMute = false;
1080
1081 int i;
1082 t_atom* ap;
1083 bool res = false;
1084
1085 fMidiHandler.startMidi();
1086
1087 // Allocate factory with a given "name"
1088 for (i = 0, ap = argv; i < ac; i++, ap++) {
1089 if (atom_gettype(ap) == A_SYM) {
1090 res = allocate_factory(atom_getsym(ap)->s_name);
1091 break;
1092 }
1093 }
1094
1095 // Empty (= no name) faustgen~ will be internally separated as groups with different names
1096 if (!fDSPfactory) {
1097 string effect_name;
1098 stringstream num;
1099 num << faustgen_factory::gFaustCounter;
1100 effect_name = "faustgen_factory-" + num.str();
1101 res = allocate_factory(effect_name);
1102 }
1103
1104 t_object* box;
1105 object_obex_lookup((t_object*)&m_ob, gensym("#B"), &box);
1106 if (gDefaultColor.red == -1.) {
1107 jbox_get_color(box, &gDefaultColor);
1108 }
1109
1110 // Needed to script objects
1111 char name[64];
1112 sprintf(name, "faustgen-%lld", (long long)this);
1113 jbox_set_varname(box, gensym(name));
1114
1115 // Fetch the data inside the max patcher using the dictionary
1116 t_dictionary* d = 0;
1117 if ((d = (t_dictionary*)gensym("#D")->s_thing) && res) {
1118 fDSPfactory->getfromdictionary(d);
1119 }
1120
1121 create_dsp(true);
1122 }
1123
1124 // Called upon deleting the object inside the patcher
~faustgen()1125 faustgen::~faustgen()
1126 {
1127 free_dsp();
1128
1129 if (fEditor) {
1130 object_free(fEditor);
1131 fEditor = NULL;
1132 }
1133
1134 fDSPfactory->remove_instance(this);
1135 fMidiHandler.stopMidi();
1136 }
1137
free_dsp()1138 void faustgen::free_dsp()
1139 {
1140 // Save controller state
1141 fSavedUI->save();
1142
1143 delete fMidiUI;
1144 fMidiUI = NULL;
1145
1146 delete fOSCUI;
1147 fOSCUI = NULL;
1148
1149 delete fSavedUI;
1150 fSavedUI = NULL;
1151
1152 delete fDSPUI;
1153 fDSPUI = NULL;
1154
1155 delete fDSP;
1156 fDSP = NULL;
1157 }
1158
json_reader(const char * jsontext)1159 t_dictionary* faustgen::json_reader(const char* jsontext)
1160 {
1161 t_dictionary *d = NULL;
1162 t_max_err err;
1163 t_atom result[1];
1164 t_object *jsonreader = (t_object*)object_new(_sym_nobox, _sym_jsonreader);
1165
1166 err = (t_max_err)object_method(jsonreader, _sym_parse, jsontext, result);
1167 if (!err) {
1168 t_object *ro = (t_object*)atom_getobj(result);
1169 if (ro) {
1170 if (object_classname_compare(ro, _sym_dictionary)) {
1171 d = (t_dictionary*)ro;
1172 } else {
1173 object_free(ro);
1174 }
1175 }
1176 }
1177 object_free(jsonreader);
1178 return d;
1179 }
1180
1181 // Called upon sending the object a message inside the max patcher
1182 // Allows to set a value to the Faust module's parameter, or a list of values
anything(long inlet,t_symbol * s,long ac,t_atom * av)1183 void faustgen::anything(long inlet, t_symbol* s, long ac, t_atom* av)
1184 {
1185 fDSPfactory->lock_ui();
1186 {
1187 bool res = false;
1188 string name = string((s)->s_name);
1189
1190 // If no argument is there, consider it as a toggle message for a button
1191 if (ac == 0 && fDSPUI->isValue(name)) {
1192
1193 FAUSTFLOAT off = FAUSTFLOAT(0.0);
1194 FAUSTFLOAT on = FAUSTFLOAT(1.0);
1195 fDSPUI->setValue(name, off);
1196 fDSPUI->setValue(name, on);
1197
1198 av[0].a_type = A_FLOAT;
1199 av[0].a_w.w_float = off;
1200 anything(inlet, s, 1, av);
1201
1202 goto unlock;
1203
1204 } else if (mspUI::checkDigit(name)) { // List of values
1205
1206 int pos, ndigit = 0;
1207 for (pos = name.size() - 1; pos >= 0; pos--) {
1208 if (isdigit(name[pos]) || name[pos] == ' ') {
1209 ndigit++;
1210 } else {
1211 break;
1212 }
1213 }
1214 pos++;
1215
1216 string prefix = name.substr(0, pos);
1217 string num_base = name.substr(pos);
1218 int num = atoi(num_base.c_str());
1219
1220 int i;
1221 t_atom* ap;
1222
1223 // Increment ap each time to get to the next atom
1224 for (i = 0, ap = av; i < ac; i++, ap++) {
1225 FAUSTFLOAT value;
1226 switch (atom_gettype(ap)) {
1227 case A_LONG:
1228 value = FAUSTFLOAT(ap[0].a_w.w_long);
1229 break;
1230 case A_FLOAT:
1231 value = FAUSTFLOAT(ap[0].a_w.w_float);
1232 break;
1233 default:
1234 post("Invalid argument in parameter setting");
1235 goto unlock;
1236 }
1237
1238 string num_val = to_string(num + i);
1239 stringstream param_name; param_name << prefix;
1240 for (int i = 0; i < ndigit - mspUI::countDigit(num_val); i++) {
1241 param_name << ' ';
1242 }
1243 param_name << num_val;
1244
1245 // Try special naming scheme for list of parameters
1246 res = fDSPUI->setValue(param_name.str(), value);
1247
1248 // Otherwise try standard name
1249 if (!res) {
1250 res = fDSPUI->setValue(name, value);
1251 }
1252 if (!res) {
1253 post("Unknown parameter : %s", (s)->s_name);
1254 }
1255 }
1256
1257 } else {
1258 // Standard parameter name
1259 FAUSTFLOAT value = (av[0].a_type == A_LONG) ? FAUSTFLOAT(av[0].a_w.w_long) : FAUSTFLOAT(av[0].a_w.w_float);
1260 res = fDSPUI->setValue(name, value);
1261 if (!res) {
1262 post("Unknown parameter : %s", (s)->s_name);
1263 }
1264 }
1265 }
1266
1267 unlock:
1268 fDSPfactory->unlock_ui();
1269 }
1270
compileoptions(long inlet,t_symbol * s,long argc,t_atom * argv)1271 void faustgen::compileoptions(long inlet, t_symbol* s, long argc, t_atom* argv)
1272 {
1273 fDSPfactory->compileoptions(inlet, s, argc, argv);
1274 }
1275
read(long inlet,t_symbol * s)1276 void faustgen::read(long inlet, t_symbol* s)
1277 {
1278 fDSPfactory->read(inlet, s);
1279 }
1280
write(long inlet,t_symbol * s)1281 void faustgen::write(long inlet, t_symbol* s)
1282 {
1283 fDSPfactory->write(inlet, s);
1284 }
1285
polyphony(long inlet,t_symbol * s,long ac,t_atom * av)1286 void faustgen::polyphony(long inlet, t_symbol* s, long ac, t_atom* av)
1287 {
1288 fDSPfactory->lock_audio();
1289 fDSPfactory->lock_ui();
1290 {
1291 free_dsp();
1292 fDSP = fDSPfactory->create_dsp_instance(av[0].a_w.w_long);
1293 assert(fDSP);
1294
1295 // Init all controller (UI, MIDI, Soundfile)
1296 init_controllers();
1297
1298 // Prepare JSON
1299 fDSPfactory->make_json(fDSP);
1300
1301 // Send JSON to JS script
1302 create_jsui();
1303
1304 // Initialize at the system's sampling rate
1305 fDSP->init(sys_getsr());
1306 }
1307 fDSPfactory->unlock_audio();
1308 fDSPfactory->unlock_ui();
1309 }
1310
1311 // Reset controllers to init value and send [path, init, min, max]
init(long inlet,t_symbol * s,long ac,t_atom * av)1312 void faustgen::init(long inlet, t_symbol* s, long ac, t_atom* av)
1313 {
1314 // Reset internal state
1315 fSavedUI->reset();
1316
1317 // Input controllers
1318 for (mspUI::iterator it = fDSPUI->begin2(); it != fDSPUI->end2(); it++) {
1319 t_atom myList[4];
1320 atom_setsym(&myList[0], gensym((*it).first.c_str()));
1321 atom_setfloat(&myList[1], (*it).second->getInitValue()); // init value
1322 atom_setfloat(&myList[2], (*it).second->getMinValue());
1323 atom_setfloat(&myList[3], (*it).second->getMaxValue());
1324 outlet_list(m_control_outlet, 0, 4, myList);
1325 }
1326 // Output controllers
1327 for (mspUI::iterator it = fDSPUI->begin4(); it != fDSPUI->end4(); it++) {
1328 t_atom myList[4];
1329 atom_setsym(&myList[0], gensym((*it).first.c_str()));
1330 atom_setfloat(&myList[1], (*it).second->getInitValue()); // init value
1331 atom_setfloat(&myList[2], (*it).second->getMinValue());
1332 atom_setfloat(&myList[3], (*it).second->getMaxValue());
1333 outlet_list(m_control_outlet, 0, 4, myList);
1334 }
1335 }
1336
dump_inputs()1337 void faustgen::dump_inputs()
1338 {
1339 // Input controllers
1340 for (mspUI::iterator it = fDSPUI->begin2(); it != fDSPUI->end2(); it++) {
1341 t_atom myList[4];
1342 atom_setsym(&myList[0], gensym((*it).first.c_str()));
1343 atom_setfloat(&myList[1], (*it).second->getValue()); // cur value
1344 atom_setfloat(&myList[2], (*it).second->getMinValue());
1345 atom_setfloat(&myList[3], (*it).second->getMaxValue());
1346 outlet_list(m_control_outlet, 0, 4, myList);
1347 }
1348 }
dump_outputs()1349 void faustgen::dump_outputs()
1350 {
1351 // Output controllers
1352 for (mspUI::iterator it = fDSPUI->begin4(); it != fDSPUI->end4(); it++) {
1353 t_atom myList[4];
1354 atom_setsym(&myList[0], gensym((*it).first.c_str()));
1355 atom_setfloat(&myList[1], (*it).second->getValue()); // cur value
1356 atom_setfloat(&myList[2], (*it).second->getMinValue());
1357 atom_setfloat(&myList[3], (*it).second->getMaxValue());
1358 outlet_list(m_control_outlet, 0, 4, myList);
1359 }
1360 }
1361
1362 // Dump controllers as list of [path, cur, min, max]
dump(long inlet,t_symbol * s,long ac,t_atom * av)1363 void faustgen::dump(long inlet, t_symbol* s, long ac, t_atom* av)
1364 {
1365 dump_inputs();
1366 dump_outputs();
1367 }
1368
1369 // osc 'IP inport outport xmit bundle'
osc(long inlet,t_symbol * s,long ac,t_atom * av)1370 void faustgen::osc(long inlet, t_symbol* s, long ac, t_atom* av)
1371 {
1372 if (ac == 5) {
1373 fDSPfactory->lock_audio();
1374 fDSPfactory->lock_ui();
1375 {
1376 delete fOSCUI;
1377
1378 const char* argv1[32];
1379 int argc1 = 0;
1380
1381 argv1[argc1++] = "Faust";
1382
1383 argv1[argc1++] = "-desthost";
1384 argv1[argc1++] = atom_getsym(&av[0])->s_name;
1385
1386 char inport[32];
1387 snprintf(inport, 32, "%ld", long(av[1].a_w.w_long));
1388 argv1[argc1++] = "-port";
1389 argv1[argc1++] = inport;
1390
1391 char outport[32];
1392 snprintf(outport, 32, "%ld", long(av[2].a_w.w_long));
1393 argv1[argc1++] = "-outport";
1394 argv1[argc1++] = outport;
1395
1396 char xmit[32];
1397 snprintf(xmit, 32, "%ld", long(av[3].a_w.w_long));
1398 argv1[argc1++] = "-xmit";
1399 argv1[argc1++] = xmit;
1400
1401 char bundle[32];
1402 snprintf(bundle, 32, "%ld", long(av[4].a_w.w_long));
1403 argv1[argc1++] = "-bundle";
1404 argv1[argc1++] = bundle;
1405
1406 fOSCUI = new OSCUI("Faust", argc1, (char**)argv1);
1407 fDSP->buildUserInterface(fOSCUI);
1408 fOSCUI->run();
1409
1410 post(fOSCUI->getInfos().c_str());
1411 }
1412 fDSPfactory->unlock_ui();
1413 fDSPfactory->unlock_audio();
1414 } else {
1415 post("Should be : osc 'IP inport outport xmit(0|1|2) bundle(0|1)'");
1416 }
1417 }
1418
midievent(long inlet,t_symbol * s,long ac,t_atom * av)1419 void faustgen::midievent(long inlet, t_symbol* s, long ac, t_atom* av)
1420 {
1421 if (ac > 0) {
1422 int type = (int)av[0].a_w.w_long & 0xf0;
1423 int channel = (int)av[0].a_w.w_long & 0x0f;
1424
1425 if (ac == 1) {
1426 fMidiHandler.handleSync(0.0, av[0].a_w.w_long);
1427 } else if (ac == 2) {
1428 fMidiHandler.handleData1(0.0, type, channel, av[1].a_w.w_long);
1429 } else if (ac == 3) {
1430 fMidiHandler.handleData2(0.0, type, channel, av[1].a_w.w_long, av[2].a_w.w_long);
1431 }
1432 }
1433 }
1434
librarypath(long inlet,t_symbol * s)1435 void faustgen::librarypath(long inlet, t_symbol* s)
1436 {
1437 fDSPfactory->librarypath(inlet, s);
1438 }
1439
1440 // Called when saving the Max patcher, this function saves the necessary data inside the json file (faust sourcecode)
appendtodictionary(t_dictionary * d)1441 void faustgen::appendtodictionary(t_dictionary* d)
1442 {
1443 fDSPfactory->appendtodictionary(d);
1444 }
1445
getfromdictionary(t_dictionary * d)1446 void faustgen::getfromdictionary(t_dictionary* d)
1447 {
1448 fDSPfactory->getfromdictionary(d);
1449 }
1450
1451 // Called when the user double-clicks on the faustgen object inside the Max patcher
dblclick(long inlet)1452 void faustgen::dblclick(long inlet)
1453 {
1454 // Create a popup menu inside the Max patcher
1455 t_jpopupmenu* popup = jpopupmenu_create();
1456 jpopupmenu_additem(popup, 1, "Edit DSP code", NULL, 0, 0, NULL);
1457 jpopupmenu_additem(popup, 2, "View DSP parameters", NULL, 0, 0, NULL);
1458 jpopupmenu_additem(popup, 3, "View compile options", NULL, 0, 0, NULL);
1459 jpopupmenu_additem(popup, 4, "View SVG diagram", NULL, 0, 0, NULL);
1460 jpopupmenu_additem(popup, 5, "View Web documentation", NULL, 0, 0, NULL);
1461 jpopupmenu_additem(popup, 6, "View libraries", NULL, 0, 0, NULL);
1462
1463 // Get mouse position
1464 int x,y;
1465 jmouse_getposition_global(&x, &y);
1466 t_pt coordinate;
1467 coordinate.x = x;
1468 coordinate.y = y;
1469
1470 int choice = jpopupmenu_popup(popup, coordinate, 0);
1471
1472 switch (choice) {
1473
1474 case 1:
1475 // Open the text editor to allow the user to input Faust sourcecode
1476 display_dsp_source();
1477 break;
1478
1479 case 2:
1480 // Display inside the max window the current values of the module's parameters, as well as their ranges
1481 display_dsp_params();
1482 break;
1483
1484 case 3:
1485 // Display compiler options
1486 fDSPfactory->print_compile_options();
1487 break;
1488
1489 case 4:
1490 // Open the SVG diagram file inside a web browser
1491 display_svg();
1492 break;
1493
1494 case 5:
1495 // Open the documentation
1496 display_documentation();
1497 break;
1498
1499 case 6:
1500 // Open the libraries
1501 display_libraries();
1502 break;
1503
1504 default:
1505 break;
1506 }
1507
1508 // Destroy the popup menu once this is done
1509 jpopupmenu_destroy(popup);
1510 }
1511
1512 // Called when closing the text editor, calls for the creation of a new Faust module with the updated sourcecode
edclose(long inlet,char ** source_code,long size)1513 void faustgen::edclose(long inlet, char** source_code, long size)
1514 {
1515 // Edclose "may" be called with an already deallocated object (like closing the patcher with a still opened editor)
1516 if (fDSP && fEditor) {
1517 fDSPfactory->update_sourcecode(size, *source_code);
1518 fEditor = NULL;
1519 }
1520 }
1521
update_sourcecode()1522 void faustgen::update_sourcecode()
1523 {
1524 // Create a new DSP instance
1525 create_dsp(false);
1526
1527 // faustgen~ state is modified...
1528 set_dirty();
1529
1530 // Send a bang
1531 outlet_bang(m_control_outlet);
1532 }
1533
1534 // Process the signal data with the Faust module
perform(int vs,t_sample ** inputs,long numins,t_sample ** outputs,long numouts)1535 inline void faustgen::perform(int vs, t_sample** inputs, long numins, t_sample** outputs, long numouts)
1536 {
1537 if (!fMute && fDSPfactory->try_lock_audio()) {
1538 // Has to be tested again when the lock has been taken...
1539 if (fDSP) {
1540 fDSP->compute(vs, static_cast<FAUSTFLOAT**>(inputs), static_cast<FAUSTFLOAT**>(outputs));
1541 if (fOSCUI) fOSCUI->endBundle();
1542 //update_outputs();
1543 // Use the right outlet to output messages
1544 dump_outputs();
1545 // Done for fMidiUI and fOSCUI
1546 GUI::updateAllGuis();
1547 }
1548 fDSPfactory->unlock_audio();
1549 } else {
1550 // Write null buffers to outs
1551 for (int i = 0; i < numouts; i++) {
1552 memset(outputs[i], 0, sizeof(t_sample) * vs);
1553 }
1554 }
1555 }
1556
init(double samplerate)1557 inline void faustgen::init(double samplerate)
1558 {
1559 fDSP->init(samplerate);
1560 }
1561
1562 // Display source code
display_dsp_source()1563 void faustgen::display_dsp_source()
1564 {
1565 if (fEditor) {
1566 // Editor already open, set it to to foreground
1567 object_attr_setchar(fEditor, gensym("visible"), 1);
1568 } else {
1569 // Create a text editor object
1570 fEditor = (t_object*)object_new(CLASS_NOBOX, gensym("jed"), this, 0);
1571
1572 // Set the text inside the text editor to be fSourceCode
1573 object_method(fEditor, gensym("settext"), fDSPfactory->get_sourcecode(), gensym("utf-8"));
1574 object_attr_setchar(fEditor, gensym("scratch"), 1);
1575 char name[256];
1576 snprintf(name, 256, "DSP code : %s", fDSPfactory->get_name().c_str());
1577 object_attr_setsym(fEditor, gensym("title"), gensym(name));
1578 }
1579 }
1580
1581 // Display the Faust module's parameters along with their standard values
display_dsp_params()1582 void faustgen::display_dsp_params()
1583 {
1584 fDSPUI->displayControls();
1585 post("JSON : %s", fDSPfactory->get_json());
1586 }
1587
display_svg()1588 void faustgen::display_svg()
1589 {
1590 fDSPfactory->display_svg();
1591 }
1592
display_documentation()1593 void faustgen::display_documentation()
1594 {
1595 fDSPfactory->display_documentation();
1596 }
1597
display_libraries()1598 void faustgen::display_libraries()
1599 {
1600 fDSPfactory->display_libraries();
1601 }
1602
hilight_on()1603 void faustgen::hilight_on()
1604 {
1605 t_jrgba color;
1606 jrgba_set(&color, 1.0, 0.0, 0.0, 1.0);
1607 t_object* box;
1608 object_obex_lookup((t_object*)&m_ob, gensym("#B"), &box);
1609 jbox_set_color(box, &color);
1610 }
1611
hilight_off()1612 void faustgen::hilight_off()
1613 {
1614 t_object* box;
1615 object_obex_lookup((t_object*)&m_ob, gensym("#B"), &box);
1616 jbox_set_color(box, &gDefaultColor);
1617 }
1618
hilight_error(const string & error)1619 void faustgen::hilight_error(const string& error)
1620 {
1621 object_error_obtrusive((t_object*)&m_ob, (char*)error.c_str());
1622 }
1623
init_controllers()1624 void faustgen::init_controllers()
1625 {
1626 // Initialize User Interface (here connnection with controls)
1627 if (!fDSPUI) {
1628 fDSPUI = new mspUI();
1629 fDSP->buildUserInterface(fDSPUI);
1630 }
1631
1632 // MIDI handling
1633 if (!fMidiUI) {
1634 fMidiUI = new MidiUI(&fMidiHandler);
1635 fDSP->buildUserInterface(fMidiUI);
1636 }
1637
1638 // State handling
1639 if (!fSavedUI) {
1640 fSavedUI = new SaveLabelUI();
1641 }
1642
1643 // Soundfile handling
1644 if (fDSPfactory->fSoundUI) {
1645 fDSP->buildUserInterface(fDSPfactory->fSoundUI);
1646 }
1647 }
1648
create_dsp(bool init)1649 void faustgen::create_dsp(bool init)
1650 {
1651 fDSPfactory->lock_audio();
1652 fDSPfactory->lock_ui();
1653 {
1654 fDSP = fDSPfactory->create_dsp_aux();
1655 assert(fDSP);
1656
1657 // Init all controllers (UI, MIDI, Soundfile)
1658 init_controllers();
1659
1660 // Initialize at the system's sampling rate
1661 fDSP->init(sys_getsr());
1662
1663 // Setup MAX audio IO
1664 bool dspstate = false;
1665
1666 if ((m_siginlets != fDSP->getNumInputs()) || (m_sigoutlets != fDSP->getNumOutputs())) {
1667 // Number of ins/outs have changed... possibly stop IO
1668 dspstate = sys_getdspobjdspstate((t_object*)&m_ob);
1669 if (dspstate) {
1670 dsp_status("stop");
1671 }
1672 }
1673
1674 setupIO(&faustgen::perform, &faustgen::init, fDSP->getNumInputs(), fDSP->getNumOutputs(), init);
1675
1676 // Setup m_midi_outlet MIDI output handler
1677 fMidiHandler.m_midi_outlet = m_midi_outlet;
1678
1679 // Load old controller state
1680 fDSP->buildUserInterface(fSavedUI);
1681
1682 // Possibly restart IO
1683 if (dspstate) {
1684 dsp_status("start");
1685 }
1686
1687 // Send JSON to JS script
1688 create_jsui();
1689 }
1690 fDSPfactory->unlock_ui();
1691 fDSPfactory->unlock_audio();
1692 }
1693
set_dirty()1694 void faustgen::set_dirty()
1695 {
1696 t_object* mypatcher;
1697 object_obex_lookup(&m_ob, gensym("#P"), &mypatcher);
1698 jpatcher_set_dirty(mypatcher, 1);
1699 }
1700
check_dac()1701 t_pxobject* faustgen::check_dac()
1702 {
1703 t_object *patcher, *box, *obj;
1704 object_obex_lookup(this, gensym("#P"), &patcher);
1705
1706 for (box = jpatcher_get_firstobject(patcher); box; box = jbox_get_nextobject(box)) {
1707 obj = jbox_get_object(box);
1708 if (obj) {
1709 if ((object_classname(obj) == gensym("dac~"))
1710 || (object_classname(obj) == gensym("ezdac~"))
1711 || (object_classname(obj) == gensym("ezadc~"))
1712 || (object_classname(obj) == gensym("adc~"))) {
1713 return (t_pxobject*)box;
1714 }
1715 }
1716 }
1717
1718 return NULL;
1719 }
1720
create_jsui()1721 void faustgen::create_jsui()
1722 {
1723 t_object *patcher, *box, *obj;
1724 object_obex_lookup(this, gensym("#P"), &patcher);
1725
1726 for (box = jpatcher_get_firstobject(patcher); box; box = jbox_get_nextobject(box)) {
1727 obj = jbox_get_object(box);
1728 // Notify JSON
1729 if (obj && strcmp(object_classname(obj)->s_name, "js") == 0) {
1730 t_atom json;
1731 atom_setsym(&json, gensym(fDSPfactory->get_json()));
1732 object_method_typed(obj, gensym("anything"), 1, &json, 0);
1733 }
1734 }
1735
1736 // Keep all outputs to be notified in update_outputs
1737 fOutputTable.clear();
1738 for (box = jpatcher_get_firstobject(patcher); box; box = jbox_get_nextobject(box)) {
1739 obj = jbox_get_object(box);
1740 t_symbol* scriptingname = jbox_get_varname(obj); // scripting name
1741 // Keep control outputs
1742 if (scriptingname && fDSPUI->isOutputValue(scriptingname->s_name)) {
1743 fOutputTable[scriptingname->s_name].push_back(obj);
1744 }
1745 }
1746 }
1747
update_outputs()1748 void faustgen::update_outputs()
1749 {
1750 for (auto& it1 : fOutputTable) {
1751 bool new_val = false;
1752 FAUSTFLOAT value = fDSPUI->getOutputValue(it1.first, new_val);
1753 if (new_val) {
1754 t_atom at_value;
1755 atom_setfloat(&at_value, value);
1756 for (auto& it2 : it1.second) {
1757 object_method_typed(it2, gensym("float"), 1, &at_value, 0);
1758 }
1759 }
1760 }
1761 }
1762
dsp_status(const char * mess)1763 void faustgen::dsp_status(const char* mess)
1764 {
1765 t_pxobject* dac = NULL;
1766
1767 if ((dac = check_dac())) {
1768 t_atom msg[1];
1769 atom_setsym(msg, gensym(mess));
1770 object_method_typed(dac, gensym("message"), 1, msg, 0);
1771 } else { // Global
1772 object_method(gensym("dsp")->s_thing, gensym(mess));
1773 }
1774 }
1775
mute(long inlet,long mute)1776 void faustgen::mute(long inlet, long mute)
1777 {
1778 fMute = mute;
1779 }
1780
ext_main(void * r)1781 extern "C" void ext_main(void* r)
1782 {
1783 #ifdef WIN32
1784 static bool done = false;
1785 if (done) return;
1786 done = true;
1787 #endif
1788
1789 // Creates an instance of Faustgen
1790 t_class * mclass = faustgen::makeMaxClass("faustgen~");
1791 post("faustgen~ v%s (sample = 64 bits code = %s)", FAUSTGEN_VERSION, getCodeSize());
1792 post("LLVM powered Faust embedded compiler v%s", getCLibFaustVersion());
1793 post("Copyright (c) 2012-2021 Grame");
1794
1795 // Start 'libfaust' in multi-thread safe mode
1796 startMTDSPFactories();
1797
1798 // Process all messages coming to the object using a custom method
1799 REGISTER_METHOD_GIMME(faustgen, anything);
1800
1801 // Process the "compileoptions" message, to add optional compilation possibilities
1802 REGISTER_METHOD_GIMME(faustgen, compileoptions);
1803
1804 // Process the "midievent" message
1805 REGISTER_METHOD_GIMME(faustgen, midievent);
1806
1807 // Process the "polyphony" message
1808 REGISTER_METHOD_GIMME(faustgen, polyphony);
1809
1810 // Process the "init" message
1811 REGISTER_METHOD_GIMME(faustgen, init);
1812
1813 // Process the "dump" message
1814 REGISTER_METHOD_GIMME(faustgen, dump);
1815
1816 // Process the "osc" message
1817 REGISTER_METHOD_GIMME(faustgen, osc);
1818
1819 // Register inside Max the necessary methods
1820 REGISTER_METHOD_DEFSYM(faustgen, read);
1821 REGISTER_METHOD_DEFSYM(faustgen, write);
1822 REGISTER_METHOD_DEFSYM(faustgen, librarypath);
1823 REGISTER_METHOD_LONG(faustgen, mute);
1824 REGISTER_METHOD_CANT(faustgen, dblclick);
1825 REGISTER_METHOD_ASSIST(faustgen, assist);
1826 REGISTER_METHOD_EDCLOSE(faustgen, edclose);
1827 REGISTER_METHOD_JSAVE(faustgen, appendtodictionary);
1828 }
1829
1830