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 = ¶m->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 = ¶m->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