1 /************************** BEGIN soulpatch-dsp.h **************************/
2 /************************************************************************
3  FAUST Architecture File
4  Copyright (C) 2019 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 #ifndef __soulpatch_dsp__
26 #define __soulpatch_dsp__
27 
28 #include <iostream>
29 #include <sstream>
30 #include <fstream>
31 #include <vector>
32 #include <string>
33 #include <string.h>
34 #include <map>
35 #include <algorithm>
36 
37 #include <soul/soul_patch.h>
38 #include <soul/patch/helper_classes/soul_patch_Utilities.h>
39 
40 #include "faust/dsp/dsp.h"
41 #include "faust/midi/midi.h"
42 #include "faust/gui/UI.h"
43 #include "faust/gui/JSONUIDecoder.h"
44 #include "faust/dsp/libfaust.h"
45 
46 class soul_dsp_factory;
47 
48 /**
49 * Faust wrapped SOUL DSP
50 */
51 
52 class soulpatch_dsp : public dsp {
53 
54     private:
55 
56         /*
57         struct ConsolePrinter : public soul::patch::RefCountHelper<soul::patch::ConsoleMessageHandler, ConsolePrinter>
58         {
59             // Called when a message is sent to the console by the program.
60             void handleConsoleMessage(uint64_t sampleCount, const char* endpointName, const char* message)
61             {
62                 std::cout << sampleCount << " " << endpointName << ": " << message << std::endl;
63             }
64         };
65         */
66 
startWith(const std::string & str,const std::string & prefix)67         bool startWith(const std::string& str, const std::string& prefix)
68         {
69             return (str.substr(0, prefix.size()) == prefix);
70         }
71 
unquote(const std::string & str)72         std::string unquote(const std::string& str)
73         {
74             return (str[0] == '"') ? str.substr(1, str.size() - 2) : str;
75         }
76 
countTotalBusChannels(soul::patch::Span<soul::patch::Bus> buses)77         int countTotalBusChannels(soul::patch::Span<soul::patch::Bus> buses)
78         {
79             int res = 0;
80             for (const auto& i : buses) {
81                 res += i.numChannels;
82             }
83             return res;
84         }
85 
checkParam(soul::patch::Span<const char * > names,const std::string & name)86         bool checkParam(soul::patch::Span<const char*> names, const std::string& name)
87         {
88             for (int i = 0; i < names.size(); i++) {
89                 if (names[i] == name) return true;
90             }
91             return false;
92         }
93 
94         struct ZoneParam {
95 
96             FAUSTFLOAT fZone;
97             soul::patch::Parameter::Ptr fParam;
98 
ZoneParamZoneParam99             ZoneParam(soul::patch::Parameter::Ptr param):fParam(param)
100             {}
101 
reflectZoneZoneParam102             void reflectZone() { fParam->setValue(fZone); }
modifyZoneZoneParam103             void modifyZone() { fZone = fParam->getValue(); }
104 
105         };
106 
107         soul_dsp_factory* fFactory;
108 
109         std::vector<ZoneParam*> fInputsControl;
110         std::vector<ZoneParam*> fOutputsControl;
111 
112         soul::patch::PatchPlayer* fPlayer;
113         soul::patch::PatchPlayerConfiguration fConfig;
114         soul::patch::PatchPlayer::RenderContext fRenderContext;
115 
116         // Event handlers used to call additional functions
117         soul::patch::Parameter::Ptr fClassInit;
118         soul::patch::Parameter::Ptr fInstanceConstants;
119         soul::patch::Parameter::Ptr fInstanceResetUserInterface;
120         soul::patch::Parameter::Ptr fInstanceClear;
121         JSONUITemplatedDecoder* fDecoder;
122 
123         // MIDI handling
124         midi_handler* fMIDIHander;
125         std::vector<soul::MIDIEvent> fMIDIInputMessages;
126         std::vector<soul::MIDIEvent> fMIDIOutputMessages;
127 
128     public:
129 
130         // Implemented later on
131         soulpatch_dsp(soul_dsp_factory* factory, std::string& error_msg);
132 
~soulpatch_dsp()133         virtual ~soulpatch_dsp()
134         {
135             for (const auto& i : fInputsControl) {
136                 delete i;
137             }
138             for (const auto& i : fOutputsControl) {
139                 delete i;
140             }
141             delete fDecoder;
142         }
143 
setMidiHandler(midi_handler * handler)144         void setMidiHandler(midi_handler* handler) { fMIDIHander = handler; }
getMidiHandler()145         midi_handler* getMidiHandler() { return fMIDIHander; }
146 
getNumInputs()147         virtual int getNumInputs()
148         {
149             return fRenderContext.numInputChannels;
150         }
151 
getNumOutputs()152         virtual int getNumOutputs()
153         {
154             return fRenderContext.numOutputChannels;
155         }
156 
buildUserInterface(UI * ui_interface)157         virtual void buildUserInterface(UI* ui_interface)
158         {
159             if (fDecoder) {
160 
161                 soul::patch::Span<soul::patch::Parameter::Ptr> params = fPlayer->getParameters();
162                 for (int i = 0; i < params.size(); i++) {
163                     std::string label = params[i]->ID->getCharPointer();
164                     if (label.find("Hslider") != std::string::npos
165                         || label.find("Vslider") != std::string::npos
166                         || label.find("Entry") != std::string::npos
167                         || label.find("Button") != std::string::npos
168                         || label.find("Checkbox") != std::string::npos) {
169                         // Additional event handlers are added before the control handlers, so the control index start at 5
170                         fDecoder->setReflectZoneFun(i-5, [=](double value) { params[i]->setValue(value); });
171                     } else if (label.find("Hbargraph") != std::string::npos
172                         || label.find("Vbargraph") != std::string::npos) {
173                         // Additional event handlers are added before the control handlers, so the control index start at 5
174                         fDecoder->setModifyZoneFun(i-5, [=]() { std::cout << params[i]->getValue() << std::endl; return params[i]->getValue(); });
175                     }
176                 }
177                 fDecoder->buildUserInterface(ui_interface);
178 
179             } else {
180 
181                 ui_interface->openVerticalBox("SOUL");
182                 {
183                     ui_interface->openVerticalBox("Inputs");
184 
185                     soul::patch::Span<soul::patch::Parameter::Ptr> params = fPlayer->getParameters();
186                     for (int i = 0; i < params.size(); i++) {
187 
188                         std::string label = params[i]->ID->getCharPointer();
189                         ZoneParam* param = new ZoneParam(params[i]);
190                         FAUSTFLOAT* zone = &param->fZone;
191                         fInputsControl.push_back(param);
192 
193                         // Possibly add 'Faust compatible' metadata
194                         soul::patch::Span<const char*> names = params[i]->getPropertyNames();
195                         for (int j = 0; j < names.size(); j++) {
196                             std::string key = names[j];
197                             if (key == "unit") {
198                                 ui_interface->declare(zone, key.c_str(), params[i]->getProperty(names[j])->getCharPointer());
199                             }
200                         }
201 
202                         if (checkParam(params[i]->getPropertyNames(), "boolean")) {
203                             ui_interface->addButton(params[i]->name->getCharPointer(), zone);
204                         } else if (!checkParam(params[i]->getPropertyNames(), "hidden")) {
205                             ui_interface->addHorizontalSlider(params[i]->name->getCharPointer(), zone,
206                                                               params[i]->initialValue, params[i]->minValue,
207                                                               params[i]->maxValue, params[i]->step);
208                         }
209                     }
210 
211                     ui_interface->closeBox();
212                 }
213                 {
214                     ui_interface->openVerticalBox("Outputs");
215                     /*
216                     soul::patch::Span<soul::patch::Parameter::Ptr> params = fPlayer->getParameters();
217                     for (int i = 0; i < params.size(); i++) {
218 
219                         std::string label = params[i]->ID->getCharPointer();
220                         ZoneParam* param = new ZoneParam(params[i]);
221                         FAUSTFLOAT* zone = &param->fZone;
222                         fOutputsControl.push_back(param);
223 
224                         // Possibly add 'Faust compatible' metadata
225                         soul::patch::Span<const char*> names = params[i]->getPropertyNames();
226                         for (int j = 0; j < names.size(); j++) {
227                             std::string key = names[j];
228                             if (key == "unit") {
229                                 ui_interface->declare(zone, key.c_str(), params[i]->getProperty(names[j])->getCharPointer());
230                             }
231                         }
232                         if (!(checkParam(params[i]->getPropertyNames(), "hidden"))) {
233                             ui_interface->addVerticalBargraph(params[i]->name->getCharPointer(), zone, params[i]->minValue, params[i]->maxValue);
234                         }
235                     }
236                     */
237                     ui_interface->closeBox();
238                 }
239                 ui_interface->closeBox();
240             }
241         }
242 
getSampleRate()243         virtual int getSampleRate()
244         {
245             return int(fConfig.sampleRate);
246         }
247 
classInit(int sample_rate)248         void classInit(int sample_rate)
249         {
250             if (fClassInit) fClassInit->setValue(0);
251         }
252 
253         // Implemented later on
254         virtual void init(int sample_rate);
255 
instanceInit(int sample_rate)256         virtual void instanceInit(int sample_rate)
257         {
258             instanceConstants(sample_rate);
259             instanceResetUserInterface();
260             instanceClear();
261         }
262 
instanceConstants(int sample_rate)263         virtual void instanceConstants(int sample_rate)
264         {
265             if (fInstanceConstants) fInstanceConstants->setValue(0);
266         }
267 
instanceResetUserInterface()268         virtual void instanceResetUserInterface()
269         {
270             if (fInstanceResetUserInterface) fInstanceResetUserInterface->setValue(0);
271         }
272 
instanceClear()273         virtual void instanceClear()
274         {
275             if (fInstanceClear) fInstanceClear->setValue(0);
276         }
277 
278         // Implemented later on
279         virtual soulpatch_dsp* clone();
280 
metadata(Meta * m)281         virtual void metadata(Meta* m) {}
282 
compute(int count,FAUSTFLOAT ** inputs,FAUSTFLOAT ** outputs)283         virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs)
284         {
285             // Update inputs control
286             if (fDecoder) {
287                 for (const auto& i : fDecoder->getInputControls()) {
288                     i->reflectZone();
289                 }
290             } else {
291                 for (const auto& i : fInputsControl) {
292                     i->reflectZone();
293                 }
294             }
295 
296             // MIDI input handling
297             if (fMIDIHander) {
298                 fRenderContext.numMIDIMessagesIn = fMIDIHander->recvMessages(reinterpret_cast<std::vector<MIDIMessage>*>(&fMIDIInputMessages));
299                 if (fRenderContext.numMIDIMessagesIn > 1024) {
300                     std::cerr << "MIDI input overflow\n";
301                 }
302                 fRenderContext.incomingMIDI = std::addressof(fMIDIInputMessages[0]);
303                 fRenderContext.outgoingMIDI = std::addressof(fMIDIOutputMessages[0]);
304                 fRenderContext.maximumMIDIMessagesOut = (uint32_t)fMIDIOutputMessages.size();
305                 fRenderContext.numMIDIMessagesOut = 0;
306             } else {
307                 fRenderContext.incomingMIDI = nullptr;
308                 fRenderContext.incomingMIDI = nullptr;
309                 fRenderContext.numMIDIMessagesIn = 0;
310             }
311 
312             // Setup audio buffers
313             fRenderContext.inputChannels = (const FAUSTFLOAT**)inputs;
314             fRenderContext.outputChannels = outputs;
315             fRenderContext.numFrames = count;
316 
317             // DSP compute
318             fPlayer->render(fRenderContext);
319 
320             // MIDI output handling
321             if (fMIDIHander && fRenderContext.numMIDIMessagesOut != 0) {
322                 if (fRenderContext.numMIDIMessagesOut > fRenderContext.maximumMIDIMessagesOut) {
323                     std::cerr << "MIDI output overflow\n";
324                 }
325                 int numMessagesOut = std::min(fRenderContext.numMIDIMessagesOut, fRenderContext.maximumMIDIMessagesOut);
326                 fMIDIHander->sendMessages(reinterpret_cast<std::vector<MIDIMessage>*>(&fMIDIOutputMessages), numMessagesOut);
327             }
328 
329             // Update outputs control
330             if (fDecoder) {
331                 for (const auto& i : fDecoder->getOutputControls()) {
332                     i->modifyZone();
333                 }
334             } else {
335                 for (const auto& i : fOutputsControl) {
336                     i->modifyZone();
337                 }
338             }
339         }
340 
compute(double,int count,FAUSTFLOAT ** inputs,FAUSTFLOAT ** outputs)341         virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); }
342 
343 };
344 
345 /**
346  * Faust wrapped SOUL patches factory
347  */
348 
349 class soul_dsp_factory : public dsp_factory {
350 
351     protected:
352 
353         friend soulpatch_dsp;
354 
355         struct FaustSOULFile : public soul::patch::VirtualFile {
356 
getNameFaustSOULFile357             virtual soul::patch::String* getName() { return {}; }
358 
getAbsolutePathFaustSOULFile359             virtual soul::patch::String* getAbsolutePath() { return {}; }
360 
getParentFaustSOULFile361             virtual soul::patch::VirtualFile* getParent() { return {}; }
362 
getChildFileFaustSOULFile363             virtual soul::patch::VirtualFile* getChildFile (const char* subPath) { return {}; }
364 
getSizeFaustSOULFile365             virtual int64_t getSize() { return 0; }
366 
getLastModificationTimeFaustSOULFile367             virtual int64_t getLastModificationTime() { return 0; }
368 
readFaustSOULFile369             virtual int64_t read (uint64_t startPositionInFile, void* targetBuffer, uint64_t bytesToRead) { return 0; }
370 
371         };
372 
373         struct FaustSourceFilePreprocessor : public soul::patch::SourceFilePreprocessor {
374 
preprocessSourceFileFaustSourceFilePreprocessor375             virtual soul::patch::VirtualFile* preprocessSourceFile (soul::patch::VirtualFile& inputFile) override
376             {
377                 return {};
378             }
379 
380         };
381 
382         std::string fPath;
383         soul::patch::PatchInstance::Ptr fPatch;
384         soul::patch::Description* fDescription;
385         FaustSourceFilePreprocessor::Ptr fProcessor;
386 
387     public:
388 
soul_dsp_factory(const std::string & path,std::string & error_msg)389         soul_dsp_factory(const std::string& path, std::string& error_msg)
390         {
391             fPath = path;
392             std::string filename = "/usr/local/lib/" + std::string(soul::patch::SOULPatchLibrary::getLibraryFileName());
393             soul::patch::SOULPatchLibrary library(filename.c_str());
394 
395             if (!library.loadedSuccessfully()) {
396                 error_msg = "cannot load SOUL_PatchLoader.dylib\n";
397                 throw std::bad_alloc();
398             }
399 
400             fPatch = library.createPatchFromFileBundle(fPath.c_str());
401             if (!fPatch) {
402                 error_msg = "cannot load SOUL patch : " + path + "\n";
403                 throw std::bad_alloc();
404             }
405 
406             fDescription = fPatch->getDescription();
407             /*
408             std::cout << "name \"" << fDescription.name->getCharPointer() << "\"" << std::endl;
409             std::cout << "description \"" << fDescription.description->getCharPointer() << "\"" << std::endl;
410             std::cout << "category \"" << fDescription.category->getCharPointer() << "\"" << std::endl;
411             std::cout << "manufacturer \"" << fDescription.manufacturer->getCharPointer() << "\"" << std::endl;
412             std::cout << "URL \"" << fDescription.URL->getCharPointer() << "\"" << std::endl;
413             std::cout << "isInstrument " << fDescription.isInstrument << "\"" << std::endl;
414             */
415         }
416 
~soul_dsp_factory()417         virtual ~soul_dsp_factory() {}
418 
getName()419         virtual std::string getName() { return fDescription->name; }
getSHAKey()420         virtual std::string getSHAKey() { return ""; }
getDSPCode()421         virtual std::string getDSPCode() { return ""; }
getCompileOptions()422         virtual std::string getCompileOptions() { return ""; }
getLibraryList()423         virtual std::vector<std::string> getLibraryList() { return {}; }
getIncludePathnames()424         virtual std::vector<std::string> getIncludePathnames() { return {}; }
425 
createDSPInstance()426         virtual soulpatch_dsp* createDSPInstance()
427         {
428             std::string error_msg;
429             return new soulpatch_dsp(this, error_msg);
430         }
431 
setMemoryManager(dsp_memory_manager * manager)432         virtual void setMemoryManager(dsp_memory_manager* manager) {}
getMemoryManager()433         virtual dsp_memory_manager* getMemoryManager() { return nullptr; };
434 
435 };
436 
soulpatch_dsp(soul_dsp_factory * factory,std::string & error_msg)437 soulpatch_dsp::soulpatch_dsp(soul_dsp_factory* factory, std::string& error_msg)
438 {
439     fFactory = factory;
440     fDecoder = nullptr;
441     fMIDIHander = nullptr;
442     memset(&fRenderContext, 0, sizeof(fRenderContext));
443 
444     fConfig.sampleRate = 44100;
445     fConfig.maxFramesPerBlock = 4096;
446     fPlayer = fFactory->fPatch->compileNewPlayer(fConfig, nullptr, fFactory->fProcessor.get(), nullptr);
447     if (!fPlayer->isPlayable()) {
448         soul::patch::Span<soul::patch::CompilationMessage> errors = fPlayer->getCompileMessages();
449         error_msg = "getCompileError";
450         for (int i = 0; i < errors.size(); i++) {
451             error_msg += " ";
452             error_msg += errors[i].getFullMessage();
453         }
454         error_msg += "\n";
455         throw std::bad_alloc();
456     }
457 
458     // Setup fixed parts of the render context
459     fRenderContext.numInputChannels = countTotalBusChannels(fPlayer->getInputBuses());
460     fRenderContext.numOutputChannels = countTotalBusChannels(fPlayer->getOutputBuses());
461 }
462 
init(int sample_rate)463 void soulpatch_dsp::init(int sample_rate)
464 {
465     fConfig.sampleRate = double(sample_rate);
466     fPlayer = fFactory->fPatch->compileNewPlayer(fConfig, nullptr, fFactory->fProcessor.get(), nullptr);
467     fMIDIInputMessages.resize(1024);
468     fMIDIOutputMessages.resize(1024);
469 
470     // FAUST soul code has additional functions
471     soul::patch::Span<soul::patch::Parameter::Ptr> params = fPlayer->getParameters();
472     for (int i = 0; i < params.size(); i++) {
473         std::string label = params[i]->ID->getCharPointer();
474         if (label == "eventbuildUserInterface") {
475             soul::patch::Span<const char*> names = params[i]->getPropertyNames();
476             // JSON is the first key
477             std::string json = unquote(params[i]->getProperty(names[0])->getCharPointer());
478             json.erase(std::remove(json.begin(), json.end(), '\\'), json.end());
479             fDecoder = createJSONUIDecoder(json);
480         } else if (label == "eventclassInit") {
481             fClassInit = params[i];
482         } else if (label == "eventinstanceConstants") {
483             fInstanceConstants = params[i];
484         } else if (label == "eventinstanceResetUserInterface") {
485             fInstanceResetUserInterface = params[i];
486         } else if (label == "eventinstanceClear") {
487             fInstanceClear = params[i];
488         }
489     }
490 
491     fPlayer->reset();
492     classInit(sample_rate);
493     instanceInit(sample_rate);
494 }
495 
496 // External API
497 
clone()498 soulpatch_dsp* soulpatch_dsp::clone()
499 {
500     return fFactory->createDSPInstance();
501 }
502 
getSOULDSPFactoryFromSHAKey(const std::string & sha_key)503 soul_dsp_factory* getSOULDSPFactoryFromSHAKey(const std::string& sha_key)
504 {
505     return nullptr;
506 }
507 
createSOULDSPFactoryFromFile(const std::string & filename,int argc,const char * argv[],std::string & error_msg)508 soul_dsp_factory* createSOULDSPFactoryFromFile(const std::string& filename,
509                                                int argc, const char* argv[],
510                                                std::string& error_msg)
511 {
512     try {
513         soul_dsp_factory* factory = new soul_dsp_factory(filename, error_msg);
514         // Check that SOUL patch compilation works
515         soulpatch_dsp dummy(factory, error_msg);
516         return factory;
517     } catch (...) {
518         return nullptr;
519     }
520 }
521 
createSOULDSPFactoryFromString(const std::string & name_app,const std::string & dsp_content,int argc,const char * argv[],std::string & error_msg)522 soul_dsp_factory* createSOULDSPFactoryFromString(const std::string& name_app,
523                                                 const std::string& dsp_content,
524                                                 int argc, const char* argv[],
525                                                 std::string& error_msg)
526 {
527     return nullptr;
528 }
529 
deleteSOULDSPFactory(soul_dsp_factory * factory)530 bool deleteSOULDSPFactory(soul_dsp_factory* factory)
531 {
532     return false;
533 }
534 
535 /**
536  * Faust/SOUL hybrid file parser
537  */
538 
539 class faust_soul_parser  {
540 
541     private:
542 
extractFaustBlocks(std::istream & in,std::stringstream & res_file)543         std::map <std::string, std::string> extractFaustBlocks(std::istream& in, std::stringstream& res_file)
544         {
545             std::string line;
546             std::stringstream faust_block;
547             bool is_faust_block = false;
548             int brackets = 0;
549             std::map <std::string, std::string> faust_blocks;     // name, code
550             std::map <std::string, std::string>::iterator cur_faust_block;
551 
552             while (getline(in, line)) {
553 
554                 std::stringstream line_reader(line);
555                 std::string token1, token2, token3;
556 
557                 line_reader >> token1;
558                 line_reader >> token2;
559                 line_reader >> token3;
560 
561                 if (is_faust_block) {
562                     // End of block
563                     if ((token1 == "}") && (--brackets == 0)) {
564                         is_faust_block = false;
565                         cur_faust_block->second = faust_block.str();
566                         faust_block.str("");
567                         // Start of block (or could be on the previous line)
568                     } else if (token1 == "{") {
569                         brackets++;
570                         continue;
571                     } else {
572                         faust_block << line << "\n";
573                     }
574                     continue;
575                 } else {
576                     is_faust_block = (token1 == "faust" && token2 != "");
577                     if (is_faust_block) {
578                         if (token3 == "{") brackets++;
579                         faust_blocks[token2] = "";
580                         cur_faust_block = faust_blocks.find(token2);
581                     }
582                 }
583 
584                 // Keep the lines of SOUL file
585                 if (!is_faust_block) res_file << line << std::endl;
586             }
587 
588             return faust_blocks;
589         }
590 
generateSOULBlock(const std::string & name,const std::string & code,int argc,const char * argv[])591         std::string generateSOULBlock(const std::string& name, const std::string& code, int argc, const char* argv[])
592         {
593             int argc1 = 0;
594             const char* argv1[64];
595             argv1[argc1++] = "-lang";
596             //argv1[argc1++] = "soul";
597             argv1[argc1++] = "soul-hybrid";
598             argv1[argc1++] = "-cn";
599             argv1[argc1++] = name.c_str();
600             argv1[argc1++] = "-o";
601             argv1[argc1++] = "/var/tmp/exp.soul";
602             for (int i = 0; i < argc; i++) {
603                 argv1[argc1++] = argv[i];
604             }
605             argv1[argc1] = nullptr;  // NULL terminated argv
606 
607             std::string error_msg;
608             bool res = generateAuxFilesFromString(name, code, argc1, argv1, error_msg);
609 
610             if (res) {
611                 std::ifstream soul_file("/var/tmp/exp.soul");
612                 std::string soul_string((std::istreambuf_iterator<char>(soul_file)), std::istreambuf_iterator<char>());
613                 return soul_string;
614             } else {
615                 std::cerr << "ERROR : generateAuxFilesFromFile " << error_msg;
616                 return "";
617             }
618         }
619 
620     public:
621 
faust_soul_parser()622         faust_soul_parser()
623         {}
624 
~faust_soul_parser()625         ~faust_soul_parser()
626         {}
627 
parseSOULFile(const std::string & input,const std::string & output,int argc,const char * argv[])628         bool parseSOULFile(const std::string& input, const std::string& output, int argc, const char* argv[])
629         {
630             std::ifstream reader(input.c_str());
631             if (reader.is_open()) {
632 
633                 // Open SOUL output file
634                 std::ofstream output_file(output);
635 
636                 // Extract the Faust blocks and returns the input file without them
637                 std::stringstream soul_file;
638                 std::map<std::string, std::string> faust_blocks = extractFaustBlocks(reader, soul_file);
639 
640                 // Write all Faust blocks translated to SOUL
641                 for (const auto& it : faust_blocks) {
642                     std::string block = generateSOULBlock(it.first, it.second, argc, argv);
643                     if (block == "") return false;
644                     output_file << block;
645                 }
646 
647                 // Write the SOUL part
648                 output_file << soul_file.str();
649                 output_file.close();
650 
651                 return true;
652             } else {
653                 return false;
654             }
655         }
656 
generateSOULFile(const std::string & input,const std::string & output,int argc,const char * argv[])657         bool generateSOULFile(const std::string& input, const std::string& output, int argc, const char* argv[])
658         {
659             int argc1 = 0;
660             const char* argv1[64];
661             argv1[argc1++] = "-lang";
662             argv1[argc1++] = "soul";
663             argv1[argc1++] = "-o";
664             argv1[argc1++] = output.c_str();
665             for (int i = 0; i < argc; i++) {
666                 argv1[argc1++] = argv[i];
667             }
668             argv1[argc1] = nullptr;  // NULL terminated argv
669 
670             std::string error_msg;
671             bool res = generateAuxFilesFromFile(input, argc1, argv1, error_msg);
672             if (!res) {
673                 std::cerr << "ERROR : generateAuxFilesFromFile " << error_msg;
674             }
675             return res;
676         }
677 
createSOULPatch(const std::string & soul_file)678         void createSOULPatch(const std::string& soul_file)
679         {
680             // Generate "soulpatch" file
681             std::string soulpatch_file = soul_file + "patch";
682             std::ofstream patch_file(soulpatch_file);
683             patch_file << "{" << std::endl;
684                 patch_file << "\t\"soulPatchV1\":" << std::endl;
685                 patch_file << "\t{" << std::endl;
686                 patch_file << "\t\t\"ID\": \"grame.soul.hybrid\"," << std::endl;
687                 patch_file << "\t\t\"version\": \"1.0\"," << std::endl;
688                 patch_file << "\t\t\"name\": \"hybrid\"," << std::endl;
689                 patch_file << "\t\t\"description\": \"SOUL example\"," << std::endl;
690                 patch_file << "\t\t\"category\": \"synth\"," << std::endl;
691                 patch_file << "\t\t\"manufacturer\": \"GRAME\"," << std::endl;
692                 patch_file << "\t\t\"website\": \"https://faust.grame.fr\"," << std::endl;
693                 patch_file << "\t\t\"isInstrument\": true," << std::endl;
694                 patch_file << "\t\t\"source\": "; patch_file << "\"" << soul_file << "\"" << std::endl;
695                 patch_file << "\t}" << std::endl;
696             patch_file << "}";
697             patch_file.close();
698         }
699 
700 };
701 
702 #endif
703 /************************** END soulpatch-dsp.h **************************/
704