1 /*
2     CmdInterpreter.cpp
3 
4     Copyright 2019 - 2021, Will Godfrey and others.
5 
6     This file is part of yoshimi, which is free software: you can
7     redistribute it and/or modify it under the terms of the GNU General
8     Public License as published by the Free Software Foundation, either
9     version 2 of the License, or (at your option) any later version.
10 
11     yoshimi 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 yoshimi.  If not, see <http://www.gnu.org/licenses/>.
18 
19 */
20 
21 #include <iostream>
22 #include <fstream>
23 #include <cstdlib>
24 #include <unistd.h>
25 #include <pwd.h>
26 #include <cstdio>
27 #include <cerrno>
28 #include <cfloat>
29 #include <sys/time.h>
30 #include <sys/types.h>
31 #include <ncurses.h>
32 #include <readline/readline.h>
33 #include <readline/history.h>
34 #include <algorithm>
35 #include <iterator>
36 #include <atomic>
37 #include <map>
38 #include <list>
39 #include <vector>
40 #include <sstream>
41 #include <cassert>
42 
43 #include "CLI/CmdInterpreter.h"
44 #include "Effects/EffectMgr.h"
45 #include "CLI/Parser.h"
46 #include "Misc/TestInvoker.h"
47 #include "Misc/TextMsgBuffer.h"
48 #include "Misc/FileMgrFuncs.h"
49 #include "Misc/NumericFuncs.h"
50 #include "Misc/FormatFuncs.h"
51 #include "Misc/CliFuncs.h"
52 
53 
54 // global variable; see SynthEngine.cpp and main.cpp
55 extern SynthEngine *firstSynth;
56 
57 // used to hold back shutdown when running sound generation for test
58 extern std::atomic <bool> waitForTest;
59 
60 
61 // these two are both zero and repesented by an enum entry
62 const unsigned char type_read = TOPLEVEL::type::Adjust;
63 
64 namespace cli {
65 
66 using std::string;
67 using std::to_string;
68 using std::vector;
69 using std::list;
70 using std::cout;
71 using std::endl;
72 
73 using file::loadText;
74 
75 using func::bitSet;
76 using func::bitTest;
77 using func::bitClear;
78 using func::bitClearHigh;
79 using func::bitFindHigh;
80 
81 using func::asString;
82 using func::string2int;
83 using func::string2int127;
84 using func::string2float;
85 
86 using cli::readControl;
87 
88 
89 /*
90  * There are two routes that 'write' commands can take.
91  * sendDirect(synth, ) and sendNormal(synth, )
92  *
93  * sendDirect(synth, ) is the older form and is now mostly used for
94  * numerical entry by test calls. It always returns zero.
95  *
96  * sendNormal(synth, ) performs 'value' range adjustment and also
97  * performs some error checks, returning a response.
98  *
99  *
100  * readControl(synth, ) provides a non-buffered way to find the
101  * value of any control. It may be temporarily blocked if
102  * there is a write command in progress.
103  *
104  * readControlText(synth, ) provides a non-buffered way to fetch
105  * some text items. It is not error checked.
106  */
107 
108 
109 // predefined OK-Reply constant
110 Reply Reply::DONE{REPLY::done_msg};
111 
112 
CmdInterpreter()113 CmdInterpreter::CmdInterpreter() :
114     currentInstance{0},
115     synth{nullptr},
116     instrumentGroup{},
117     textMsgBuffer{TextMsgBuffer::instance()},
118     testInvoker{},
119 
120     context{LEVEL::Top},
121     npart{0},
122     kitMode{PART::kitType::Off},
123     kitNumber{0},
124     inKitEditor{false},
125     voiceNumber{0},
126     insertType{0},
127     nFXtype{0},
128     nFXpreset{0},
129     nFXeqBand{0},
130     nFX{0},
131     filterSequenceSize{1},
132     filterVowelNumber{0},
133     filterNumberOfFormants{1},
134     filterFormantNumber{0},
135     chan{0},
136     axis{0},
137     mline{0}
138 { }
139 
~CmdInterpreter()140 CmdInterpreter::~CmdInterpreter()
141 { /* destructors invoked here */
142 }
143 
144 
defaults()145 void CmdInterpreter::defaults()
146 {
147     context = LEVEL::Top;
148     npart = 0;
149     kitMode = PART::kitType::Off;
150     kitNumber = 0;
151     inKitEditor = false;
152     voiceNumber = 0;
153     insertType = 0;
154     nFXtype = 0;
155     nFXpreset = 0;
156     nFXeqBand = 0;
157     nFX = 0;
158     filterVowelNumber = 0;
159     filterFormantNumber = 0;
160     chan = 0;
161     axis = 0;
162     mline = 0;
163 }
164 
165 
resetInstance(unsigned int newInstance)166 void CmdInterpreter::resetInstance(unsigned int newInstance)
167 {
168     currentInstance = newInstance;
169     synth = firstSynth->getSynthFromId(currentInstance);
170     unsigned int newID = synth->getUniqueId();
171     if (newID != currentInstance)
172     {
173         synth->getRuntime().Log("Instance " + to_string(currentInstance) + " not found. Set to " + to_string(newID), _SYS_::LogError);
174         currentInstance = newID;
175     }
176     defaults();
177 }
178 
179 
getTestInvoker()180 test::TestInvoker& CmdInterpreter::getTestInvoker()
181 {
182     if (!testInvoker)
183         testInvoker.reset(new test::TestInvoker());
184     return *testInvoker;
185 }
186 
187 
buildStatus(bool showPartDetails)188 string CmdInterpreter::buildStatus(bool showPartDetails)
189 {
190     if (bitTest(context, LEVEL::AllFX))
191     {
192         return buildAllFXStatus();
193     }
194     if (bitTest(context, LEVEL::Part))
195     {
196         return buildPartStatus(showPartDetails);
197     }
198     if (bitTest(context, LEVEL::Test))
199     {
200         return buildTestStatus();
201     }
202 
203     string result = "";
204 
205     if (bitTest(context, LEVEL::Scale))
206         result += " Scale ";
207     else if (bitTest(context, LEVEL::Bank))
208     {
209         result += " Bank " + to_string(int(readControl(synth, 0, BANK::control::selectBank, TOPLEVEL::section::bank))) + " (root " + to_string(int(readControl(synth, 0, BANK::control::selectRoot, TOPLEVEL::section::bank))) + ")";
210     }
211     else if (bitTest(context, LEVEL::Config))
212         result += " Config ";
213     else if (bitTest(context, LEVEL::Vector))
214     {
215         result += (" Vect Ch " + asString(chan + 1) + " ");
216         if (axis == 0)
217             result += "X";
218         else
219             result += "Y";
220     }
221     else if (bitTest(context, LEVEL::Learn))
222         result += (" MLearn line " + asString(mline + 1) + " ");
223 
224     return result;
225 }
226 
227 
228 
buildAllFXStatus()229 string CmdInterpreter::buildAllFXStatus()
230 {
231     assert(bitTest(context, LEVEL::AllFX));
232 
233     string result = "";
234     int section;
235     int ctl = EFFECT::sysIns::effectType;
236     if (bitTest(context, LEVEL::Part))
237     {
238         result = " p" + to_string(int(npart) + 1);
239         if (readControl(synth, 0, PART::control::enable, npart))
240             result += "+";
241         ctl = PART::control::effectType;
242         section = npart;
243     }
244     else if (bitTest(context, LEVEL::InsFX))
245     {
246         result += " Ins";
247         section = TOPLEVEL::section::insertEffects;
248     }
249     else
250     {
251         result += " Sys";
252         section = TOPLEVEL::section::systemEffects;
253     }
254     nFXtype = readControl(synth, 0, ctl, section, UNUSED, nFX);
255     result += (" eff " + asString(nFX + 1) + " " + fx_list[nFXtype].substr(0, 6));
256     nFXpreset = readControl(synth, 0, EFFECT::control::preset, section,  EFFECT::type::none + nFXtype, nFX);
257 
258     if (bitTest(context, LEVEL::InsFX) && readControl(synth, 0, EFFECT::sysIns::effectDestination, TOPLEVEL::section::systemEffects, UNUSED, nFX) == -1)
259         result += " Unrouted";
260     else if (nFXtype > 0 && nFXtype != 7)
261     {
262         result += ("-" + asString(nFXpreset + 1));
263         if (readControl(synth, 0, EFFECT::control::changed, section,  EFFECT::type::none + nFXtype, nFX))
264             result += "?";
265     }
266     return result;
267 }
268 
269 
buildPartStatus(bool showPartDetails)270 string CmdInterpreter::buildPartStatus(bool showPartDetails)
271 {
272     assert(bitTest(context, LEVEL::Part));
273 
274     int kit = UNUSED;
275     int insert = UNUSED;
276     bool justPart = false;
277     string result = " p";
278 
279     npart = readControl(synth, 0, MAIN::control::partNumber, TOPLEVEL::section::main);
280 
281     kitMode = readControl(synth, 0, PART::control::kitMode, npart);
282     if (bitFindHigh(context) == LEVEL::Part)
283     {
284         justPart = true;
285         if (kitMode == PART::kitType::Off)
286             result = " Part ";
287     }
288     result += to_string(int(npart) + 1);
289     if (readControl(synth, 0, PART::control::enable, npart))
290         result += "+";
291     if (kitMode != PART::kitType::Off)
292     {
293         kit = kitNumber;
294         insert = TOPLEVEL::insert::kitGroup;
295         result += ", ";
296         string front = "";
297         string back = " ";
298         if (!inKitEditor)
299         {
300             front = "(";
301             back = ")";
302         }
303         switch (kitMode)
304         {
305             case PART::kitType::Multi:
306                 if (justPart)
307                     result += (front + "Multi" + back);
308                 else
309                     result += "M";
310                 break;
311             case PART::kitType::Single:
312                 if (justPart)
313                     result += (front + "Single" + back);
314                 else
315                     result += "S";
316                 break;
317             case PART::kitType::CrossFade:
318                 if (justPart)
319                     result += (front + "Crossfade" + back);
320                 else
321                     result += "C";
322                 break;
323             default:
324                 break;
325         }
326         if (inKitEditor)
327         {
328             result += to_string(kitNumber + 1);
329             if (readControl(synth, 0, PART::control::enableKitLine, npart, kitNumber, UNUSED, TOPLEVEL::insert::kitGroup))
330                 result += "+";
331         }
332     }
333     else
334         kitNumber = 0;
335     if (!showPartDetails)
336         return "";
337 
338     if (bitFindHigh(context) == LEVEL::MControl)
339         return result +" Midi controllers";
340 
341     int engine = contextToEngines(context);
342     switch (engine)
343     {
344         case PART::engine::addSynth:
345             if (bitFindHigh(context) == LEVEL::AddSynth)
346                 result += ", Add";
347             else
348                 result += ", A";
349             if (readControl(synth, 0, PART::control::enableAdd, npart, kit, PART::engine::addSynth, insert))
350                 result += "+";
351             break;
352         case PART::engine::subSynth:
353             if (bitFindHigh(context) == LEVEL::SubSynth)
354                 result += ", Sub";
355             else
356                 result += ", S";
357             if (readControl(synth, 0, PART::control::enableSub, npart, kit, PART::engine::subSynth, insert))
358                 result += "+";
359             break;
360         case PART::engine::padSynth:
361             if (bitFindHigh(context) == LEVEL::PadSynth)
362                 result += ", Pad";
363             else
364                 result += ", P";
365             if (readControl(synth, 0, PART::control::enablePad, npart, kit, PART::engine::padSynth, insert))
366                 result += "+";
367             break;
368         case PART::engine::addVoice1: // intentional drop through
369         case PART::engine::addMod1:
370         {
371             result += ", A";
372             if (readControl(synth, 0, PART::control::enableAdd, npart, kit, PART::engine::addSynth, insert))
373                 result += "+";
374 
375             if (bitFindHigh(context) == LEVEL::AddVoice)
376                 result += ", Voice ";
377             else
378                 result += ", V";
379             result += to_string(voiceNumber + 1);
380             int voiceFromNumber = readControl(synth, 0, ADDVOICE::control::voiceOscillatorSource, npart, kitNumber, PART::engine::addVoice1 + voiceNumber);
381             if (voiceFromNumber > -1)
382                 result += (">" +to_string(voiceFromNumber + 1));
383             voiceFromNumber = readControl(synth, 0, ADDVOICE::control::externalOscillator, npart, kitNumber, PART::engine::addVoice1 + voiceNumber);
384             if (voiceFromNumber > -1)
385                 result += (">V" +to_string(voiceFromNumber + 1));
386             if (readControl(synth, 0, ADDVOICE::control::enableVoice, npart, kitNumber, PART::engine::addVoice1 + voiceNumber))
387                 result += "+";
388 
389             if (bitTest(context, LEVEL::AddMod))
390             {
391                 result += ", ";
392                 int tmp = readControl(synth, 0, ADDVOICE::control::modulatorType, npart, kitNumber, PART::engine::addVoice1 + voiceNumber);
393                 if (tmp > 0)
394                 {
395                     string word = addmodnameslist[tmp];
396 
397                     if (bitFindHigh(context) == LEVEL::AddMod)
398                         result += (word + " Mod ");
399                     else
400                         result += word.substr(0, 2);
401 
402                     int modulatorFromVoiceNumber = readControl(synth, 0, ADDVOICE::control::externalModulator, npart, kitNumber, PART::engine::addVoice1 + voiceNumber);
403                     if (modulatorFromVoiceNumber > -1)
404                         result += (">V" + to_string(modulatorFromVoiceNumber + 1));
405                     else
406                     {
407                         int modulatorFromNumber = readControl(synth, 0, ADDVOICE::control::modulatorOscillatorSource, npart, kitNumber, PART::engine::addVoice1 + voiceNumber);
408                         if (modulatorFromNumber > -1)
409                             result += (">" + to_string(modulatorFromNumber + 1));
410                     }
411                 }
412                 else
413                     result += "Modulator";
414             }
415             break;
416         }
417     }
418     if (bitFindHigh(context) == LEVEL::Resonance)
419     {
420         result += ", Resonance";
421         if (readControl(synth, 0, RESONANCE::control::enableResonance, npart, kitNumber, engine, TOPLEVEL::insert::resonanceGroup))
422         result += "+";
423     }
424     else if (bitTest(context, LEVEL::Oscillator))
425     {
426         int type = (int)readControl(synth, 0, OSCILLATOR::control::baseFunctionType, npart, kitNumber, engine + voiceNumber, TOPLEVEL::insert::oscillatorGroup);
427         if (type > OSCILLATOR::wave::hyperSec)
428             result += " user";
429         else
430             result += (" " + waveshape[type]);
431     }
432 
433     if (bitTest(context, LEVEL::LFO))
434     {
435         result += ", LFO ";
436         int cmd = -1;
437         switch (insertType)
438         {
439             case TOPLEVEL::insertType::amplitude:
440                 cmd = ADDVOICE::control::enableAmplitudeLFO;
441                 result += "amp";
442                 break;
443             case TOPLEVEL::insertType::frequency:
444                 cmd = ADDVOICE::control::enableFrequencyLFO;
445                 result += "freq";
446                 break;
447             case TOPLEVEL::insertType::filter:
448                 cmd = ADDVOICE::control::enableFilterLFO;
449                 result += "filt";
450                 break;
451         }
452 
453         if (engine == PART::engine::addVoice1)
454         {
455             if (readControl(synth, 0, cmd, npart, kitNumber, engine + voiceNumber))
456                 result += "+";
457         }
458         else
459             result += "+";
460     }
461     else if (bitTest(context, LEVEL::Filter))
462     {
463         int baseType = readControl(synth, 0, FILTERINSERT::control::baseType, npart, kitNumber, engine + voiceNumber, TOPLEVEL::insert::filterGroup);
464         result += ", Filter ";
465         switch (baseType)
466         {
467             case 0:
468                 result += "analog";
469                 break;
470             case 1:
471                 filterSequenceSize = readControl(synth, 0, FILTERINSERT::control::sequenceSize, npart, kitNumber, engine + voiceNumber, TOPLEVEL::insert::filterGroup);
472                 filterNumberOfFormants = readControl(synth, 0, FILTERINSERT::control::numberOfFormants, npart, kitNumber, engine + voiceNumber, TOPLEVEL::insert::filterGroup);
473                 result += "formant V";
474                 result += to_string(filterVowelNumber);
475                 result += " F";
476                 result += to_string(filterFormantNumber);
477                 break;
478             case 2:
479                 result += "state var";
480                 break;
481         }
482         if (engine == PART::engine::subSynth)
483         {
484             if (readControl(synth, 0, SUBSYNTH::control::enableFilter, npart, kitNumber, engine))
485                 result += "+";
486         }
487         else if (engine == PART::engine::addVoice1)
488         {
489             if (readControl(synth, 0, ADDVOICE::control::enableFilter, npart, kitNumber, engine + voiceNumber))
490                 result += "+";
491         }
492         else
493             result += "+";
494     }
495     else if (bitTest(context, LEVEL::Envelope))
496     {
497         result += ", Envel ";
498         int cmd = -1;
499         switch (insertType)
500         {
501             case TOPLEVEL::insertType::amplitude:
502                 if (engine == PART::engine::addMod1)
503                     cmd = ADDVOICE::control::enableModulatorAmplitudeEnvelope;
504                 else
505                     cmd = ADDVOICE::control::enableAmplitudeEnvelope;
506                 result += "amp";
507                 break;
508             case TOPLEVEL::insertType::frequency:
509                 if (engine == PART::engine::addMod1)
510                     cmd = ADDVOICE::control::enableModulatorFrequencyEnvelope;
511                 else
512                     cmd = ADDVOICE::control::enableFrequencyEnvelope;
513                 result += "freq";
514                 break;
515             case TOPLEVEL::insertType::filter:
516                 cmd = ADDVOICE::control::enableFilterEnvelope;
517                 result += "filt";
518                 break;
519             case TOPLEVEL::insertType::bandwidth:
520                 cmd = SUBSYNTH::control::enableBandwidthEnvelope;
521                 result += "band";
522                 break;
523         }
524 
525         if (readControl(synth, 0, ENVELOPEINSERT::control::enableFreeMode, npart, kitNumber, engine, TOPLEVEL::insert::envelopeGroup, insertType))
526             result += " free";
527         if (engine == PART::engine::addVoice1  || engine == PART::engine::addMod1 || (engine == PART::engine::subSynth && cmd != ADDVOICE::control::enableAmplitudeEnvelope && cmd != ADDVOICE::control::enableFilterEnvelope))
528         {
529             if (readControl(synth, 0, cmd, npart, kitNumber, engine + voiceNumber))
530                 result += "+";
531         }
532         else
533             result += "+";
534     }
535 
536     return result;
537 }
538 
539 
buildTestStatus()540 string CmdInterpreter::buildTestStatus()
541 {
542     int expose = readControl(synth, 0, CONFIG::control::exposeStatus, TOPLEVEL::section::config);
543     // render compact form when status is part of prompt
544     return getTestInvoker().showTestParams(expose == 2);
545 }
546 
547 
query(string text,bool priority)548 bool CmdInterpreter::query(string text, bool priority)
549 {
550     char *line = NULL;
551     string suffix;
552     char result;
553     char test;
554 
555     priority = !priority; // so calls make more sense
556 
557     if (priority)
558     {
559         suffix = " N/y? ";
560         test = 'n';
561     }
562     else
563     {
564         suffix = " Y/n? ";
565         test = 'y';
566     }
567     result = test;
568     text += suffix;
569     synth->getRuntime().Log(text);
570     // changed this so that all messages go to same destination.
571     //line = readline(text.c_str());
572     line = readline("");
573     if (line)
574     {
575         if (line[0] != 0)
576             result = line[0];
577         free(line);
578         line = NULL;
579     }
580     return (((result | 32) == test) ^ priority);
581 }
582 
583 
helpLoop(list<string> & msg,string * commands,int indent,bool single)584 void CmdInterpreter::helpLoop(list<string>& msg, string *commands, int indent, bool single)
585 {
586     int word = 0;
587     int spaces = 30 - indent;
588     string left = "";
589     string right = "";
590     string dent;
591     string blanks;
592 
593     while (commands[word] != "@end")
594     {
595         left = commands[word];
596         if (!single)
597             right = commands[word + 1];
598         if (left == "")
599         {
600             left = "  " + right;
601             right = "";
602         }
603         if (right > "")
604             left = left +(blanks.assign(spaces - left.length(), ' ') + right);
605         msg.push_back(dent.assign(indent, ' ') + left);
606         word += (2 - single);
607     }
608 }
609 
610 
helpList(Parser & input,unsigned int local)611 char CmdInterpreter::helpList(Parser& input, unsigned int local)
612 {
613     if (!input.matchnMove(1, "help") && !input.matchnMove(1, "?"))
614         return REPLY::todo_msg;
615 
616     int listnum = -1;
617     bool named = false;
618 
619     if (!input.isAtEnd())
620     { // 1 & 2 reserved for syseff & inseff
621         if (input.matchnMove(3, "effects"))
622             listnum = LISTS::eff;
623         else if (input.matchnMove(3, "reverb"))
624             listnum = LISTS::reverb;
625         else if (input.matchnMove(3, "echo"))
626             listnum = LISTS::echo;
627         else if (input.matchnMove(3, "chorus"))
628             listnum = LISTS::chorus;
629         else if (input.matchnMove(3, "phaser"))
630             listnum = LISTS::phaser;
631         else if (input.matchnMove(3, "alienwah"))
632             listnum = LISTS::alienwah;
633         else if (input.matchnMove(3, "distortion"))
634             listnum = LISTS::distortion;
635         else if (input.matchnMove(2, "eq"))
636             listnum = LISTS::eq;
637         else if (input.matchnMove(3, "dynfilter"))
638             listnum = LISTS::dynfilter;
639 
640         else if (input.matchnMove(1, "part"))
641             listnum = LISTS::part;
642         else if (input.matchnMove(2, "mcontrol"))
643             listnum = LISTS::mcontrol;
644         else if (input.matchnMove(3, "common"))
645             listnum = LISTS::common;
646         else if (input.matchnMove(3, "addsynth"))
647             listnum = LISTS::addsynth;
648         else if (input.matchnMove(3, "subsynth"))
649             listnum = LISTS::subsynth;
650         else if (input.matchnMove(3, "padsynth"))
651             listnum = LISTS::padsynth;
652         else if (input.matchnMove(3, "resonance"))
653             listnum = LISTS::resonance;
654         else if (input.matchnMove(3, "voice"))
655             listnum = LISTS::addvoice;
656         else if (input.matchnMove(3, "modulator"))
657             listnum = LISTS::addmod;
658         else if (input.matchnMove(3, "waveform"))
659             listnum = LISTS::waveform;
660         else if (input.matchnMove(3, "lfo"))
661             listnum = LISTS::lfo;
662         else if (input.matchnMove(3, "filter"))
663             listnum = LISTS::filter;
664         else if (input.matchnMove(3, "envelope"))
665             listnum = LISTS::envelope;
666 
667         else if (input.matchnMove(1, "vector"))
668             listnum = LISTS::vector;
669         else if (input.matchnMove(1, "scale"))
670             listnum = LISTS::scale;
671         else if (input.matchnMove(1, "load"))
672             listnum = LISTS::load;
673         else if (input.matchnMove(1, "save"))
674             listnum = LISTS::save;
675         else if (input.matchnMove(1, "list"))
676             listnum = LISTS::list;
677         else if (input.matchnMove(1, "config"))
678             listnum = LISTS::config;
679         else if (input.matchnMove(1, "bank"))
680             listnum = LISTS::bank;
681         else if (input.matchnMove(1, "mlearn"))
682             listnum = LISTS::mlearn;
683         if (listnum != -1)
684             named = true;
685     }
686     else
687     {
688         if (bitTest(local, LEVEL::AllFX))
689         {
690             switch (nFXtype)
691             {
692                 case 0:
693                     listnum = LISTS::eff;
694                     break;
695                 case 1:
696                     listnum = LISTS::reverb;
697                     break;
698                 case 2:
699                     listnum = LISTS::echo;
700                     break;
701                 case 3:
702                     listnum = LISTS::chorus;
703                     break;
704                 case 4:
705                     listnum = LISTS::phaser;
706                     break;
707                 case 5:
708                     listnum = LISTS::alienwah;
709                     break;
710                 case 6:
711                     listnum = LISTS::distortion;
712                     break;
713                 case 7:
714                     listnum = LISTS::eq;
715                     break;
716                 case 8:
717                     listnum = LISTS::dynfilter;
718                     break;
719             }
720         }
721         else if (bitTest(local, LEVEL::Envelope))
722             listnum = LISTS::envelope;
723         else if (bitTest(local, LEVEL::LFO))
724             listnum = LISTS::lfo;
725         else if (bitTest(local, LEVEL::Filter))
726             listnum = LISTS::filter;
727         else if (bitTest(local, LEVEL::Oscillator))
728             listnum = LISTS::waveform;
729         else if (bitTest(local, LEVEL::AddMod))
730             listnum = LISTS::addmod;
731         else if (bitTest(local, LEVEL::AddVoice))
732             listnum = LISTS::addvoice;
733         else if (bitTest(local, LEVEL::Resonance))
734             listnum = LISTS::resonance;
735         else if (bitTest(local, LEVEL::AddSynth))
736             listnum = LISTS::addsynth;
737         else if (bitTest(local, LEVEL::SubSynth))
738             listnum = LISTS::subsynth;
739         else if (bitTest(local, LEVEL::PadSynth))
740             listnum = LISTS::padsynth;
741         else if (bitTest(local, LEVEL::MControl))
742             listnum = LISTS::mcontrol;
743 
744         else if (bitTest(local, LEVEL::Part))
745             listnum = LISTS::part;
746         else if (bitTest(local, LEVEL::Vector))
747             listnum = LISTS::vector;
748         else if (bitTest(local, LEVEL::Scale))
749             listnum = LISTS::scale;
750         else if (bitTest(local, LEVEL::Bank))
751             listnum = LISTS::bank;
752         else if (bitTest(local, LEVEL::Config))
753             listnum = LISTS::config;
754         else if (bitTest(local, LEVEL::Learn))
755             listnum = LISTS::mlearn;
756         else if (bitTest(local, LEVEL::Test))
757             listnum = LISTS::test;
758     }
759     if (listnum == -1)
760         listnum = LISTS::all;
761     list<string>msg;
762     if (!named)
763     {
764         msg.push_back("Commands:");
765         helpLoop(msg, basics, 2);
766     }
767     switch(listnum)
768     {
769         case 0:
770             msg.push_back(" ");
771             msg.push_back("  Part [n1]   ...             - part operations");
772             msg.push_back("  VEctor [n1] ...             - vector operations");
773             msg.push_back("  SCale       ...             - scale (microtonal) operations");
774             msg.push_back("  MLearn [n1] ...             - MIDI learn operations");
775             msg.push_back("  COnfig      ...             - configuration settings");
776             msg.push_back("  BAnk        ...             - root and bank settings");
777             msg.push_back("  LIst        ...             - various available parameters");
778             msg.push_back("  LOad        ...             - load various files");
779             msg.push_back("  SAve        ...             - save various files");
780 
781             msg.push_back(" ");
782             break;
783         case LISTS::part:
784             msg.push_back("Part: [n1] = part number");
785             helpLoop(msg, partlist, 2);
786             break;
787         case LISTS::mcontrol:
788             msg.push_back("Midi Control:");
789             helpLoop(msg, mcontrollist, 2);
790             break;
791         case LISTS::common:
792             msg.push_back("Part Common:");
793             helpLoop(msg, commonlist, 2);
794             break;
795         case LISTS::addsynth:
796             msg.push_back("Part AddSynth:");
797             helpLoop(msg, addsynthlist, 2);
798             break;
799         case LISTS::subsynth:
800             msg.push_back("Part SubSynth:");
801             helpLoop(msg, subsynthlist, 2);
802             break;
803         case LISTS::padsynth:
804             msg.push_back("Part PadSynth:");
805             helpLoop(msg, padsynthlist, 2);
806             break;
807         case LISTS::resonance:
808             msg.push_back("Resonance:");
809             helpLoop(msg, resonancelist, 2);
810             break;
811         case LISTS::addvoice:
812             msg.push_back("Part AddVoice:");
813             helpLoop(msg, addvoicelist, 2);
814             break;
815         case LISTS::addmod:
816             msg.push_back("AddVoice Modulator:");
817             helpLoop(msg, addmodlist, 2);
818             break;
819         case LISTS::waveform:
820             msg.push_back("Part Waveform:");
821             helpLoop(msg, waveformlist, 2);
822             break;
823 
824         case LISTS::lfo:
825             msg.push_back("Engine LFOs:");
826             helpLoop(msg, LFOlist, 2);
827             break;
828         case LISTS::filter:
829             msg.push_back("Engine Filters:");
830             helpLoop(msg, filterlist, 2);
831             break;
832         case LISTS::envelope:
833             msg.push_back("Engine Envelopes:");
834             helpLoop(msg, envelopelist, 2);
835             break;
836 
837         case LISTS::eff:
838             msg.push_back("Effects:");
839             helpLoop(msg, fx_list, 2, true);
840             break;
841         case LISTS::reverb:
842             msg.push_back("Reverb:");
843             helpLoop(msg, reverblist, 2);
844             break;
845         case LISTS::echo:
846             msg.push_back("Echo:");
847             helpLoop(msg, echolist, 2);
848             break;
849         case LISTS::chorus:
850             msg.push_back("Chorus:");
851             helpLoop(msg, choruslist, 2);
852             break;
853         case LISTS::phaser:
854             msg.push_back("Phaser:");
855             helpLoop(msg, phaserlist, 2);
856             break;
857         case LISTS::alienwah:
858             msg.push_back("Alienwah:");
859             helpLoop(msg, alienwahlist, 2);
860             break;
861         case LISTS::distortion:
862             msg.push_back("Distortion:");
863             helpLoop(msg, distortionlist, 2);
864             break;
865         case LISTS::eq:
866             msg.push_back("EQ:");
867             helpLoop(msg, eqlist, 2);
868             break;
869         case LISTS::dynfilter:
870             msg.push_back("Dynfilter:");
871             helpLoop(msg, dynfilterlist, 2);
872             break;
873 
874         case LISTS::vector:
875             msg.push_back("Vector:");
876             helpLoop(msg, vectlist, 2);
877             break;
878         case LISTS::scale:
879             msg.push_back("Scale:");
880             helpLoop(msg, scalelist, 2);
881             break;
882         case LISTS::load:
883             msg.push_back("Load:");
884             helpLoop(msg, loadlist, 2);
885             break;
886         case LISTS::save:
887             msg.push_back("Save:");
888             helpLoop(msg, savelist, 2);
889             break;
890         case LISTS::list:
891             msg.push_back("List:");
892             helpLoop(msg, listlist, 2);
893             break;
894         case LISTS::bank:
895             msg.push_back("Bank:");
896             helpLoop(msg, banklist, 2);
897             break;
898         case LISTS::config:
899             msg.push_back("Config:");
900             helpLoop(msg, configlist, 2);
901             msg.push_back("'*' entries need to be saved and Yoshimi restarted to activate");
902             break;
903         case LISTS::mlearn:
904             msg.push_back("Mlearn:");
905             helpLoop(msg, learnlist, 2);
906             break;
907         case LISTS::test:
908             msg.push_back("Test:");
909             msg.push_back("Settings for automated testing...");
910             helpLoop(msg, testlist, 2);
911             break;
912     }
913 
914     if (listnum == LISTS::all)
915     {
916         helpLoop(msg, toplist, 2);
917         msg.push_back("'...' is a help sub-menu");
918     }
919 
920     if (synth->getRuntime().toConsole)
921         // we need this in case someone is working headless
922         cout << "\nSet CONfig REPorts [s] - set report destination (gui/stderr)" << endl;
923 
924     synth->cliOutput(msg, LINES);
925     return REPLY::exit_msg;
926 }
927 
928 
historyList(int listnum)929 void CmdInterpreter::historyList(int listnum)
930 {
931     list<string>msg;
932     int start = TOPLEVEL::XML::Instrument;
933     int end = TOPLEVEL::XML::MLearn;
934     bool found = false;
935 
936     if (listnum >= 0) // its a single list we want
937     {
938         start = listnum;
939         end = listnum;
940     }
941     for (int type = start; type <= end; ++type)
942     {
943         vector<string> listType = *synth->getHistory(type);
944         if (listType.size() > 0)
945         {
946             msg.push_back(" ");
947             switch (type)
948             {
949                 case TOPLEVEL::XML::Instrument:
950                     msg.push_back("Recent Instruments:");
951                     break;
952                 case TOPLEVEL::XML::Patch:
953                     msg.push_back("Recent Patch Sets:");
954                     break;
955                 case TOPLEVEL::XML::Scale:
956                     msg.push_back("Recent Scales:");
957                     break;
958                 case TOPLEVEL::XML::State:
959                     msg.push_back("Recent States:");
960                     break;
961                 case TOPLEVEL::XML::Vector:
962                     msg.push_back("Recent Vectors:");
963                     break;
964                 case TOPLEVEL::XML::MLearn:
965                     msg.push_back("Recent MIDI learned:");
966                     break;
967             }
968             int itemNo = 0;
969             for (vector<string>::iterator it = listType.begin(); it != listType.end(); ++it, ++ itemNo)
970                 msg.push_back(to_string(itemNo + 1) + "  " + *it);
971             found = true;
972         }
973     }
974     if (!found)
975         msg.push_back("\nNo Saved History");
976 
977     synth->cliOutput(msg, LINES);
978 }
979 
980 
historySelect(int listnum,size_t selection)981 string CmdInterpreter::historySelect(int listnum, size_t selection)
982 {
983     vector<string> listType = *synth->getHistory(listnum - 1);
984     if (listType.size() == 0)
985     {
986         synth->getRuntime().Log("No saved entries");
987         return "";
988     }
989     else if (selection >= listType.size())
990     {
991         synth->getRuntime().Log("No such entry");
992         return "";
993     }
994     return listType[selection];
995 }
996 
997 
effectsList(Parser & input,bool presets)998 int CmdInterpreter::effectsList(Parser& input, bool presets)
999 {
1000     list<string>msg;
1001 
1002     size_t presetsPos;
1003     size_t presetsLast;
1004     int presetsCount;
1005     string blanks;
1006     string left;
1007     bool all;
1008 
1009     if (bitTest(context, LEVEL::AllFX) && presets == true)
1010     {
1011          synth->getRuntime().Log("Type " + fx_list[nFXtype] + "\nPresets -" + fx_presets[nFXtype].substr(fx_presets[nFXtype].find(',') + 1));
1012          return REPLY::done_msg;
1013     }
1014     else if (presets)
1015     {
1016         synth->getRuntime().Log("No effect selected");
1017         return REPLY::done_msg;
1018     }
1019     else
1020         all = input.matchnMove(1, "all");
1021     if (!all)
1022         msg.push_back("  effect     presets");
1023     for (int i = 0; i < 9; ++ i)
1024     {
1025         presetsPos = 1;
1026         presetsLast = fx_presets [i].find(',') + 1; // skip over count
1027         presetsCount = 0;
1028         if (all)
1029         {
1030             msg.push_back("  " + fx_list[i]);
1031             msg.push_back("    presets");
1032             while (presetsPos != string::npos)
1033             {
1034                 presetsPos = fx_presets [i].find(',', presetsLast);
1035                 msg.push_back("      " + asString(presetsCount + 1) + " =" + fx_presets [i].substr(presetsLast, presetsPos - presetsLast));
1036                 presetsLast = presetsPos + 1;
1037                 ++ presetsCount;
1038             }
1039         }
1040         else
1041         {
1042             left = fx_list[i];
1043             msg.push_back("    " + left + blanks.assign(12 - left.length(), ' ') + fx_presets [i].substr(0, presetsLast - 1));
1044         }
1045     }
1046 
1047     synth->cliOutput(msg, LINES);
1048     return REPLY::done_msg;
1049 }
1050 
1051 
effects(Parser & input,unsigned char controlType)1052 int CmdInterpreter::effects(Parser& input, unsigned char controlType)
1053 {
1054     Config &Runtime = synth->getRuntime();
1055     int nFXavail;
1056     int par = nFX;
1057     int value;
1058     string dest = "";
1059     int effClass;
1060     if (bitTest(context, LEVEL::Part))
1061         effClass = npart;
1062     else if (bitTest(context, LEVEL::InsFX))
1063         effClass = TOPLEVEL::section::insertEffects;
1064     else
1065         effClass = TOPLEVEL::section::systemEffects;
1066 
1067     if (bitTest(context, LEVEL::Part))
1068     {
1069         nFXavail = NUM_PART_EFX;
1070         nFX = readControl(synth, 0, PART::control::effectNumber, npart, UNUSED, UNUSED, TOPLEVEL::insert::partEffectSelect);
1071         nFXtype = synth->part[npart]->partefx[nFX]->geteffect();
1072     }
1073     else if (bitTest(context, LEVEL::InsFX))
1074     {
1075         nFXavail = NUM_INS_EFX;
1076         nFX = readControl(synth, 0, EFFECT::sysIns::effectNumber, TOPLEVEL::section::insertEffects);
1077         nFXtype = synth->insefx[nFX]->geteffect();
1078     }
1079     else
1080     {
1081         nFXavail = NUM_SYS_EFX;
1082         nFX = readControl(synth, 0, EFFECT::sysIns::effectNumber, TOPLEVEL::section::systemEffects);
1083         nFXtype = synth->sysefx[nFX]->geteffect();
1084         int tmp = input.toggle();
1085         if (tmp >= 0)
1086             return sendNormal(synth, 0, tmp, controlType, EFFECT::sysIns::effectEnable, TOPLEVEL::section::systemEffects, UNUSED, nFX);
1087     }
1088 
1089     if (input.lineEnd(controlType))
1090     {
1091         if (bitTest(context, LEVEL::Part))
1092             dest = "Part" + to_string(int(npart + 1));
1093         else if (bitTest(context, LEVEL::InsFX))
1094             dest = "Insert";
1095         else
1096             dest = "System";
1097         Runtime.Log(dest + " effect " + asString(nFX + 1));
1098         return REPLY::done_msg;
1099     }
1100 
1101     value = string2int(input);
1102 
1103     if (value > 0)
1104     {
1105         value -= 1;
1106         input.skipChars();
1107         if (value >= nFXavail)
1108             return REPLY::range_msg;
1109 
1110         if (value != nFX)
1111         { // partially updates GUI
1112             nFX = value;
1113             if (bitTest(context, LEVEL::Part))
1114             {
1115                 sendNormal(synth, 0, nFX, TOPLEVEL::type::Write, PART::control::effectNumber, npart, UNUSED, nFX, TOPLEVEL::insert::partEffectSelect);
1116                 nFXtype = synth->part[npart]->partefx[nFX]->geteffect();
1117                 return sendNormal(synth, 0, nFXtype, TOPLEVEL::type::Write, PART::control::effectType, npart, UNUSED, nFX, TOPLEVEL::insert::partEffectSelect);
1118             }
1119             if (bitTest(context, LEVEL::InsFX))
1120             {
1121                 sendNormal(synth, 0, nFX, TOPLEVEL::type::Write, EFFECT::sysIns::effectNumber, TOPLEVEL::section::insertEffects, UNUSED, nFX);
1122 
1123                 nFXtype = synth->insefx[nFX]->geteffect();
1124                 return sendNormal(synth, 0, nFXtype, TOPLEVEL::type::Write, EFFECT::sysIns::effectType, TOPLEVEL::section::insertEffects, UNUSED, nFX);
1125             }
1126 
1127                 sendNormal(synth, 0, nFX, TOPLEVEL::type::Write, EFFECT::sysIns::effectNumber, TOPLEVEL::section::systemEffects, UNUSED, nFX);
1128 
1129                 nFXtype = synth->sysefx[nFX]->geteffect();
1130                 return sendNormal(synth, 0, nFXtype, TOPLEVEL::type::Write, EFFECT::sysIns::effectType, TOPLEVEL::section::systemEffects, UNUSED, nFX);
1131         }
1132         if (input.lineEnd(controlType))
1133         {
1134             Runtime.Log("efx number set to " + asString(nFX + 1));
1135             return REPLY::done_msg;
1136         }
1137     }
1138 
1139     bool effType = false;
1140     for (int i = 0; i < 9; ++ i)
1141     {
1142         //Runtime.Log("command " + string{input} + "  list " + fx_list[i]);
1143         if (input.matchnMove(2, fx_list[i].c_str()))
1144         {
1145             nFXtype = i;
1146             effType = true;
1147             break;
1148         }
1149     }
1150     if (effType)
1151     {
1152         //cout << "nfx " << nFX << endl;
1153         nFXpreset = 0; // always set this on type change
1154         if (bitTest(context, LEVEL::Part))
1155         {
1156             sendDirect(synth, 0, nFXtype, TOPLEVEL::type::Write, PART::control::effectType, npart, UNUSED, nFX);
1157             return REPLY::done_msg; // TODO find out why not sendNormal
1158         }
1159         else if (bitTest(context, LEVEL::InsFX))
1160             return sendNormal(synth, 0, nFXtype, TOPLEVEL::type::Write, EFFECT::sysIns::effectType, TOPLEVEL::section::insertEffects, UNUSED, nFX);
1161         else
1162             return sendNormal(synth, 0, nFXtype, TOPLEVEL::type::Write, EFFECT::sysIns::effectType, TOPLEVEL::section::systemEffects, UNUSED, nFX);
1163     }
1164 
1165     if (nFXtype > 0)
1166     {
1167         int selected = -1;
1168         int value = -1;
1169         string name = string{input}.substr(0, 3);
1170         /*
1171          * We can't do a skipChars here as we don't yet know
1172          * if 'selected' will be valid. For some controls we
1173          * need to do an on-the-spot skip, otherwise we do so
1174          * at the end when we know we have a valid result but
1175          * 'value' has not been set.
1176          * If it's not valid we don't block, but pass on to
1177          * other command tests routines.
1178          */
1179         if (controlType == type_read)
1180             value = 1; // dummy value
1181         switch (nFXtype)
1182         {
1183             case 1:
1184             {
1185                 selected = stringNumInList(name, effreverb, 3);
1186                 if (selected != 7) // EQ
1187                     nFXeqBand = 0;
1188                 if (selected == 10 && value == -1) // type
1189                 {
1190                     input.skipChars();
1191                     if (input.matchnMove(1, "random"))
1192                         value = 0;
1193                     else if (input.matchnMove(1, "freeverb"))
1194                         value = 1;
1195                     else if (input.matchnMove(1, "bandwidth"))
1196                         value = 2;
1197                     else
1198                         return REPLY::value_msg;
1199                 }
1200                 break;
1201             }
1202             case 2:
1203                 selected = stringNumInList(name, effecho, 3);
1204                 if (selected == EFFECT::control::frequency && value == -1)
1205                 {
1206                     input.skipChars();
1207                     value = freqBPMset(input, readControl(synth, 0, EFFECT::control::bpm, effClass, EFFECT::type::none + nFXtype, nFX));
1208                     if (value < 0)
1209                         return REPLY::done_msg; // error already reported
1210                 }
1211                 else if (selected == EFFECT::control::bpm && value == -1)
1212                 {
1213                     input.skipChars();
1214                     value = (input.toggle() == 1);
1215                 }
1216                 break;
1217             case 3:
1218             {
1219                 selected = stringNumInList(name, effchorus, 3);
1220                 if (selected == EFFECT::control::frequency && value == -1)
1221                 {
1222                     input.skipChars();
1223                     value = freqBPMset(input, readControl(synth, 0, EFFECT::control::bpm, effClass, EFFECT::type::none + nFXtype, nFX));
1224                     if (value < 0)
1225                         return REPLY::done_msg; // error already reported
1226                 }
1227                 else if (selected == 4 && value == -1) // filtershape
1228                 {
1229                     input.skipChars();
1230                     if (input.matchnMove(1, "sine"))
1231                         value = 0;
1232                     else if (input.matchnMove(1, "triangle"))
1233                         value = 1;
1234                     else return REPLY::value_msg;
1235                 }
1236                 else if (selected == 11) // subtract
1237                 {
1238                     input.skipChars();
1239                     value = (input.toggle() == 1);
1240                 }
1241                 else if (selected == EFFECT::control::bpm && value == -1)
1242                 {
1243                     input.skipChars();
1244                     value = (input.toggle() == 1);
1245                 }
1246                 break;
1247             }
1248             case 4:
1249             {
1250                 selected = stringNumInList(name, effphaser, 3);
1251                 if (selected == EFFECT::control::frequency && value == -1)
1252                 {
1253                     input.skipChars();
1254                     value = freqBPMset(input, readControl(synth, 0, EFFECT::control::bpm, effClass, EFFECT::type::none + nFXtype, nFX));
1255                     if (value < 0)
1256                         return REPLY::done_msg; // error already reported
1257                 }
1258                 else if (selected == 4 && value == -1) // filtershape
1259                 {
1260                     input.skipChars();
1261                     if (input.matchnMove(1, "sine"))
1262                         value = 0;
1263                     else if (input.matchnMove(1, "triangle"))
1264                         value = 1;
1265                     else return REPLY::value_msg;
1266                 }
1267                 else if (selected == 10 || selected == 12 || selected == 14) // LFO, SUB, ANA
1268                 {
1269                     input.skipChars();
1270                     value = (input.toggle() == 1);
1271                 }
1272                 else if (selected == EFFECT::control::bpm && value == -1)
1273                 {
1274                     input.skipChars();
1275                     value = (input.toggle() == 1);
1276                 }
1277                 break;
1278             }
1279             case 5:
1280             {
1281                 selected = stringNumInList(name, effalienwah, 3);
1282                 if (selected == EFFECT::control::frequency && value == -1)
1283                 {
1284                     input.skipChars();
1285                     value = freqBPMset(input, readControl(synth, 0, EFFECT::control::bpm, effClass, EFFECT::type::none + nFXtype, nFX));
1286                     if (value < 0)
1287                         return REPLY::done_msg; // error already reported
1288                 }
1289                 else if (selected == 4 && value == -1) // filtershape
1290                 {
1291                     input.skipChars();
1292                     if (input.matchnMove(1, "sine"))
1293                         value = 0;
1294                     else if (input.matchnMove(1, "triangle"))
1295                         value = 1;
1296                     else return REPLY::value_msg;
1297                 }
1298                 else if (selected == EFFECT::control::bpm && value == -1)
1299                 {
1300                     input.skipChars();
1301                     value = (input.toggle() == 1);
1302                 }
1303                 break;
1304             }
1305             case 6:
1306             {
1307                 selected = stringNumInList(name, effdistortion, 3);
1308                 if (selected == 5 && value == -1) // filtershape
1309                 {
1310                     input.skipChars();
1311                     string name = string{input}.substr(0,3);
1312                     value = stringNumInList(name, filtershapes, 3) - 1;
1313                     if (value < 0)
1314                         return REPLY::value_msg;
1315                 }
1316                 else if (selected == 6 || selected == 9 || selected == 10) // invert, stereo, prefilter
1317                 {
1318                     input.skipChars();
1319                     value = (input.toggle() == 1);
1320                 }
1321                 break;
1322             }
1323             case 7: // TODO band and type no GUI update
1324             {
1325                 selected = stringNumInList(name, effeq, 2);
1326                 if (selected == 1) // band
1327                 {
1328                     if (controlType == TOPLEVEL::type::Write)
1329                     {
1330                         input.skipChars();
1331                         value = string2int(input);
1332                         if (value < 0 || value >= MAX_EQ_BANDS)
1333                             return REPLY::range_msg;
1334                         nFXeqBand = value;
1335                     }
1336                 }
1337                 else if (selected == 2 && value == -1) // type
1338                 {
1339                     input.skipChars();
1340                     string name = string{input}.substr(0,3);
1341                     value = stringNumInList(name, eqtypes, 3);
1342                     if (value < 0)
1343                         return REPLY::value_msg;
1344                 }
1345 
1346                 if (selected > 1)
1347                 {
1348                     selected += 8;
1349                 }
1350                 break;
1351             }
1352             case 8:
1353             {
1354                 selected = stringNumInList(name, effdynamicfilter, 3);
1355                 if (selected == EFFECT::control::frequency && value == -1)
1356                 {
1357                     input.skipChars();
1358                     value = freqBPMset(input, readControl(synth, 0, EFFECT::control::bpm, effClass, EFFECT::type::none + nFXtype, nFX));
1359                     if (value < 0)
1360                         return REPLY::done_msg; // error already reported
1361                 }
1362                 else if (selected == 4 && value == -1) // filtershape
1363                 {
1364                     input.skipChars();
1365                     if (input.matchnMove(1, "sine"))
1366                         value = 0;
1367                     else if (input.matchnMove(1, "triangle"))
1368                         value = 1;
1369                     else return REPLY::value_msg;
1370                 }
1371                 else if (selected == 8) // invert
1372                 {
1373                     input.skipChars();
1374                     value = (input.toggle() == 1);
1375                 }
1376                 else if (selected == 10) // filter entry
1377                 {
1378                     bitSet(context, LEVEL::Filter);
1379                     return REPLY::done_msg;
1380                 }
1381                 else if (selected == EFFECT::control::bpm && value == -1)
1382                 {
1383                     input.skipChars();
1384                     value = (input.toggle() == 1);
1385                 }
1386             }
1387             break;
1388         }
1389         if (selected > -1)
1390         {
1391             if (value == -1)
1392             {
1393                 input.skipChars();
1394                 value = string2int(input);
1395             }
1396             //cout << "Val " << value << "  type " << controlType << "  cont " << selected << "  part " << context << "  efftype " << int(nFXtype) << "  num " << int(nFX) << endl;
1397             return sendNormal(synth, 0, value, controlType, selected, effClass, EFFECT::type::none + nFXtype, nFX);
1398         }
1399         // Continue cos it's not for us.
1400     }
1401 
1402     if (input.matchnMove(2, "send"))
1403     {
1404         bool isWrite = (controlType == TOPLEVEL::type::Write);
1405         if (input.lineEnd(controlType))
1406             return REPLY::parameter_msg;
1407 
1408         if (!bitTest(context, LEVEL::InsFX))
1409         {
1410             par = string2int(input) - 1;
1411             input.skipChars();
1412             if (input.lineEnd(controlType))
1413                 return REPLY::value_msg;
1414             value = string2int127(input);
1415         }
1416         else if (isWrite) // system effects
1417         {
1418             if (input.matchnMove(1, "master"))
1419                 value = -2;
1420             else if (input.matchnMove(1, "off"))
1421                 value = -1;
1422             else
1423             {
1424                 value = string2int(input) - 1;
1425                 if (value >= Runtime.NumAvailableParts || value < 0)
1426                     return REPLY::range_msg;
1427             }
1428         }
1429 
1430         if (!isWrite)
1431             value = 1; // dummy
1432         int control;
1433         int partno;
1434         int engine = nFX;
1435         int insert = UNUSED;
1436 
1437         if (bitTest(context, LEVEL::Part))
1438         {
1439             partno = npart;
1440             control = PART::control::partToSystemEffect1 + par;
1441             engine = UNUSED;
1442         }
1443         else if (bitTest(context, LEVEL::InsFX))
1444         {
1445             partno = TOPLEVEL::section::insertEffects;
1446             control = EFFECT::sysIns::effectDestination;
1447         }
1448         else
1449         {
1450             if (par <= nFX || par >= NUM_SYS_EFX)
1451                 return REPLY::range_msg;
1452             partno = TOPLEVEL::section::systemEffects;
1453             control = EFFECT::sysIns::toEffect1 + par - 1; // TODO this needs sorting
1454             engine = nFX;
1455             insert = TOPLEVEL::insert::systemEffectSend;
1456         }
1457         return sendNormal(synth, 0, value, controlType, control, partno, UNUSED, engine, insert);
1458     }
1459 
1460     if (input.matchnMove(3, "preset"))
1461     {
1462         /*
1463          * Using constant strings and bedding the number into the list
1464          * of presets provides a very simple way to keep track of a
1465          * moving target with minimal code and data space.
1466          * However, all of this should really be in src/Effects
1467          * not here *and* in the gui code!
1468          */
1469         int partno;
1470         nFXpreset = string2int127(input) - 1;
1471         if (bitTest(context, LEVEL::Part))
1472             partno = npart;
1473         else if (bitTest(context, LEVEL::InsFX))
1474             partno = TOPLEVEL::section::insertEffects;
1475         else
1476             partno = TOPLEVEL::section::systemEffects;
1477         return sendNormal(synth, 0, nFXpreset, controlType, EFFECT::control::preset, partno,  EFFECT::type::none + nFXtype, nFX);
1478     }
1479     return REPLY::op_msg;
1480 }
1481 
1482 
midiControllers(Parser & input,unsigned char controlType)1483 int CmdInterpreter::midiControllers(Parser& input, unsigned char controlType)
1484 {
1485     if (input.isAtEnd())
1486         return REPLY::done_msg;
1487     int value = -1;
1488     int cmd = -1;
1489     bool isWrite = (controlType == TOPLEVEL::type::Write);
1490 
1491     if (input.matchnMove(2, "volume"))
1492     {
1493         value = !(input.toggle() == 0);
1494         cmd = PART::control::volumeEnable;
1495     }
1496     if ((cmd == -1) && input.matchnMove(2, "VRange"))
1497     {
1498         value = string2int127(input);
1499         cmd = PART::control::volumeRange;
1500     }
1501     if ((cmd == -1) && input.matchnMove(2, "pan"))
1502     {
1503         value = string2int127(input);
1504         cmd = PART::control::panningWidth;
1505     }
1506     if ((cmd == -1) && input.matchnMove(2, "modwheel"))
1507     {
1508         value = (input.toggle() == 1);
1509         cmd = PART::control::exponentialModWheel;
1510     }
1511     if ((cmd == -1) && input.matchnMove(2, "mrange"))
1512     {
1513         value = string2int127(input);
1514         cmd = PART::control::modWheelDepth;
1515     }
1516     if ((cmd == -1) && input.matchnMove(2, "expression"))
1517     {
1518         value = !(input.toggle() == 0);
1519         cmd = PART::control::expressionEnable;
1520     }
1521     if ((cmd == -1) && input.matchnMove(2, "sustain"))
1522     {
1523         value = !(input.toggle() == 0);
1524         cmd = PART::control::sustainPedalEnable;
1525     }
1526     if ((cmd == -1) && input.matchnMove(2, "pwheel"))
1527     {
1528         value = string2int(input);
1529         cmd = PART::control::pitchWheelRange;
1530     }
1531     if ((cmd == -1) && input.matchnMove(2, "breath"))
1532     {
1533         value = !(input.toggle() == 0);
1534         cmd = PART::control::breathControlEnable;
1535     }
1536     if ((cmd == -1) && input.matchnMove(2, "cutoff"))
1537     {
1538         value = string2int127(input);
1539         cmd = PART::control::filterCutoffDepth;
1540     }
1541     if ((cmd == -1) && input.matchnMove(2, "q"))
1542     {
1543         value = string2int127(input);
1544         cmd = PART::control::filterQdepth;
1545     }
1546     if ((cmd == -1) && input.matchnMove(3, "bandwidth"))
1547     {
1548         value = (input.toggle() == 1);
1549         cmd = PART::control::exponentialBandwidth;
1550     }
1551     if ((cmd == -1) && input.matchnMove(3, "barange"))
1552     {
1553         value = string2int127(input);
1554         cmd = PART::control::bandwidthDepth;
1555     }
1556     if ((cmd == -1) && input.matchnMove(2, "fmamplitude"))
1557     {
1558         value = !(input.toggle() == 0);
1559         cmd = PART::control::FMamplitudeEnable;
1560     }
1561     if ((cmd == -1) && input.matchnMove(2, "rcenter"))
1562     {
1563         value = string2int127(input);
1564         cmd = PART::control::resonanceCenterFrequencyDepth;
1565     }
1566     if ((cmd == -1) && input.matchnMove(2, "rband"))
1567     {
1568         value = string2int127(input);
1569         cmd = PART::control::resonanceBandwidthDepth;
1570     }
1571 
1572     // portamento controls
1573     if (cmd == -1)
1574     {
1575         if (input.matchnMove(2, "portamento"))
1576         {
1577             value = !(input.toggle() == 0);
1578             cmd = PART::control::receivePortamento;
1579         }
1580         else if (input.matchnMove(2, "ptime"))
1581         {
1582             value = string2int127(input);
1583             cmd = PART::control::portamentoTime;
1584         }
1585         else if (input.matchnMove(2, "pdownup"))
1586         {
1587             value = string2int127(input);
1588             cmd = PART::control::portamentoTimeStretch;
1589         }
1590         else if (input.matchnMove(2, "pgate"))
1591         {
1592             value = string2int127(input);
1593             cmd = PART::control::portamentoThreshold;
1594         }
1595         else if (input.matchnMove(2, "pform"))
1596         {
1597             if (input.matchnMove(1, "start"))
1598                 value = 0;
1599             else if (input.matchnMove(1, "@end"))
1600                 value = 1;
1601             cmd = PART::control::portamentoThresholdType;
1602         }
1603         else if (input.matchnMove(2, "pproportional"))
1604         {
1605             value = (input.toggle() == 1);
1606             cmd = PART::control::enableProportionalPortamento;
1607         }
1608         else if (input.matchnMove(2, "pextent"))
1609         {
1610             value = string2int127(input);
1611             cmd = PART::control::proportionalPortamentoRate;
1612         }
1613         else if (input.matchnMove(2, "prange"))
1614         {
1615             value = string2int127(input);
1616             cmd = PART::control::proportionalPortamentoDepth;
1617         }
1618     }
1619 
1620     if ((cmd == -1) && input.matchnMove(2, "clear"))
1621     {
1622         if (isWrite)
1623             return REPLY::writeOnly_msg;
1624         value = 0;
1625         cmd = PART::control::resetAllControllers;
1626     }
1627 
1628     // midi controllers
1629     if (cmd == -1 && input.matchnMove(1, "e"))
1630     {
1631         if (input.matchnMove(1, "modulation"))
1632         {
1633             value = string2int127(input);
1634             cmd = PART::control::midiModWheel;
1635         }
1636         else if (input.matchnMove(1, "expression"))
1637         {
1638             value = string2int127(input);
1639             cmd = PART::control::midiExpression;
1640         }
1641         else if (input.matchnMove(2, "breath"))
1642         {
1643             value = string2int127(input);
1644             cmd = PART::control::midiBreath;
1645         }
1646         else if (input.matchnMove(1, "cutoff"))
1647         {
1648             value = string2int127(input);
1649             cmd = PART::control::midiFilterCutoff;
1650         }
1651         else if (input.matchnMove(1, "q"))
1652         {
1653             value = string2int127(input);
1654             cmd = PART::control::midiFilterQ;
1655         }
1656         else if (input.matchnMove(2, "bandwidth"))
1657         {
1658             value = string2int127(input);
1659             cmd = PART::control::midiBandwidth;
1660         }
1661     }
1662 
1663     if (value == -1 && controlType != TOPLEVEL::type::Write)
1664         value = 0;
1665     if (cmd > -1)
1666         return sendNormal(synth, 0, value, controlType, cmd, npart);
1667     return REPLY::available_msg;
1668 }
1669 
1670 
LFOselect(Parser & input,unsigned char controlType)1671 int CmdInterpreter::LFOselect(Parser& input, unsigned char controlType)
1672 {
1673     int cmd = -1;
1674     float value = -1;
1675     int group = -1;
1676     if (input.lineEnd(controlType))
1677         return REPLY::done_msg;
1678 
1679     int engine = contextToEngines(context);
1680     if (engine == PART::engine::addVoice1)
1681         engine += voiceNumber;
1682 
1683     if (input.matchnMove(2, "amplitude"))
1684         group = TOPLEVEL::insertType::amplitude;
1685     else if (input.matchnMove(2, "frequency"))
1686         group = TOPLEVEL::insertType::frequency;
1687     else if (input.matchnMove(2, "filter"))
1688         group = TOPLEVEL::insertType::filter;
1689     if (group > -1)
1690         insertType = group;
1691     else
1692         group = insertType;
1693     switch (group)
1694     {
1695         case TOPLEVEL::insertType::amplitude:
1696             cmd = ADDVOICE::control::enableAmplitudeLFO;
1697             break;
1698         case TOPLEVEL::insertType::frequency:
1699             cmd = ADDVOICE::control::enableFrequencyLFO;
1700             break;
1701         case TOPLEVEL::insertType::filter:
1702             cmd = ADDVOICE::control::enableFilterLFO;
1703             break;
1704     }
1705 
1706     value = input.toggle();
1707     if (value > -1)
1708     {
1709         if (engine != PART::engine::addVoice1 + voiceNumber)
1710             return REPLY::available_msg;
1711         return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, engine);
1712     }
1713     if (input.lineEnd(controlType))
1714         return REPLY::done_msg;
1715 
1716     value = -1;
1717     cmd = -1;
1718 
1719     if (input.matchnMove(1, "rate"))
1720     {
1721         cmd = LFOINSERT::control::speed;
1722         if (controlType == type_read && input.isAtEnd())
1723             value = 0;
1724 
1725         else
1726         {
1727             value = freqBPMset(input, readControl(synth, 0, LFOINSERT::bpm, npart, kitNumber, engine, TOPLEVEL::insert::LFOgroup, group), 1);
1728             if (value < 0)
1729                 return REPLY::done_msg; // error already reported
1730         }
1731     }
1732     else if (input.matchnMove(1, "intensity"))
1733         cmd = LFOINSERT::control::depth;
1734     else if (input.matchnMove(1, "start"))
1735         cmd = LFOINSERT::control::start;
1736     else if (input.matchnMove(1, "delay"))
1737         cmd = LFOINSERT::control::delay;
1738     else if (input.matchnMove(1, "expand"))
1739         cmd = LFOINSERT::control::stretch;
1740     else if (input.matchnMove(1, "continuous"))
1741     {
1742         value = (input.toggle() == 1);
1743         cmd = LFOINSERT::control::continuous;
1744     }
1745     else if (input.matchnMove(1, "bpm"))
1746     {
1747         value = (input.toggle() == 1);
1748         cmd = LFOINSERT::control::bpm;
1749     }
1750     else if (input.matchnMove(1, "type"))
1751     {
1752         if (controlType == type_read && input.isAtEnd())
1753             value = 0;
1754         else
1755         {
1756             int idx = 0;
1757             while (LFOtype [idx] != "@end")
1758             {
1759                 if (input.matchnMove(2, LFOtype[idx].c_str()))
1760                 {
1761                     value = idx;
1762                     break;
1763                 }
1764                 ++idx;
1765             }
1766             if (value == -1)
1767                 return REPLY::range_msg;
1768         }
1769         cmd = LFOINSERT::control::type;
1770     }
1771     else if (input.matchnMove(2, "ar"))
1772         cmd = LFOINSERT::control::amplitudeRandomness;
1773     else if (input.matchnMove(2, "rr"))
1774         cmd = LFOINSERT::control::frequencyRandomness;
1775 
1776     //cout << ">> base cmd " << int(cmd) << "  part " << int(npart) << "  kit " << int(kitNumber) << "  engine " << int(engine) << "  parameter " << int(group) << endl;
1777 
1778     if (value == -1)
1779         value = string2float(input);
1780     return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, engine, TOPLEVEL::insert::LFOgroup, group);
1781 }
1782 
1783 
freqBPMset(Parser & input,bool isBPM,int max)1784 float CmdInterpreter::freqBPMset(Parser& input, bool isBPM, int max)
1785 {
1786     float value;
1787     if (isBPM)
1788     {
1789         int num = string2int(input);
1790         input.skipChars();
1791         if (input.isAtEnd())
1792         {
1793             synth->getRuntime().Log("BPM mode requires two values between 1 and 16");
1794             value = -1;
1795         }
1796         int div = string2int(input);
1797         if (num > 3 && div > 3)
1798         {
1799             synth->getRuntime().Log("Cannot have both values greater than 3");
1800             value = -1;
1801         }
1802         else if (num == div)
1803             num = div = 1;
1804         value = func::BPMfractionLFOfreq(num, div) * max;
1805     }
1806     else
1807     {
1808         value = string2float(input);
1809         if (value < 0 || value > max)
1810         {
1811             synth->getRuntime().Log("Frequency requires a value between 0.0 and " + to_string(max));
1812             value = -1;
1813         }
1814     }
1815     return value;
1816 }
1817 
1818 
filterSelect(Parser & input,unsigned char controlType)1819 int CmdInterpreter::filterSelect(Parser& input, unsigned char controlType)
1820 {
1821     int cmd = -1;
1822     float value = -1;
1823     int thisPart = npart;
1824     int kit = kitNumber;
1825     int param = UNUSED;
1826     if (input.lineEnd(controlType))
1827         return REPLY::done_msg;
1828 
1829     int engine = contextToEngines(context);
1830     if (engine == PART::engine::addVoice1)
1831         engine += voiceNumber;
1832     bool isDyn = false;
1833     if (bitTest(context, LEVEL::AllFX) && nFXtype == 8)
1834     {
1835         kit = EFFECT::type::dynFilter;
1836         engine = 0;
1837         if (bitTest(context, LEVEL::InsFX))
1838         {
1839             thisPart = TOPLEVEL::section::insertEffects;
1840         }
1841         else if (!bitTest(context, LEVEL::Part))
1842         {
1843             thisPart = TOPLEVEL::section::systemEffects;
1844         }
1845         isDyn = true;
1846     }
1847 
1848     if (!isDyn && (engine == PART::engine::subSynth || engine == PART::engine::addVoice1 + voiceNumber))
1849     {
1850         value = input.toggle();
1851         if (value > -1)
1852         {
1853             if (engine == PART::engine::subSynth)
1854                 cmd = SUBSYNTH::control::enableFilter;
1855             else
1856                 cmd = ADDVOICE::control::enableFilter;
1857             readControl(synth, 0, FILTERINSERT::control::baseType, thisPart, kitNumber, engine, TOPLEVEL::insert::filterGroup);
1858 
1859             return sendNormal(synth, 0, value, controlType, cmd, thisPart, kit, engine);
1860         }
1861         value = -1; // leave it as if not set
1862     }
1863 
1864     if (input.matchnMove(2, "center"))
1865         cmd = FILTERINSERT::control::centerFrequency;
1866     else if (input.matchnMove(1, "q"))
1867         cmd = FILTERINSERT::control::Q;
1868     else if (input.matchnMove(1, "velocity"))
1869         cmd = FILTERINSERT::control::velocitySensitivity;
1870     else if (input.matchnMove(2, "slope"))
1871         cmd = FILTERINSERT::control::velocityCurve;
1872     else if (input.matchnMove(1, "gain"))
1873         cmd = FILTERINSERT::control::gain;
1874     else if (input.matchnMove(2, "tracking"))
1875         cmd = FILTERINSERT::control::frequencyTracking;
1876     else if (input.matchnMove(1, "range"))
1877     {
1878         value = (input.toggle() == 1);
1879         cmd = FILTERINSERT::control::frequencyTrackingRange;
1880     }
1881     else if (input.matchnMove(2, "category"))
1882     {
1883         if (controlType == type_read && input.isAtEnd())
1884                 value = 0; // dummy value
1885         else
1886         {
1887             if (input.matchnMove(1, "analog"))
1888                 value = 0;
1889             else if (input.matchnMove(1, "formant"))
1890             {
1891                 value = 1;
1892                 filterVowelNumber = 0;
1893                 filterFormantNumber = 0;
1894             }
1895             else if (input.matchnMove(1, "state"))
1896                 value = 2;
1897             else
1898                 return REPLY::range_msg;
1899         }
1900         cmd = FILTERINSERT::control::baseType;
1901     }
1902     else if (input.matchnMove(2, "stages"))
1903     {
1904         if (input.lineEnd(controlType))
1905             return REPLY::value_msg;
1906         value = string2int(input) - 1;
1907         cmd = FILTERINSERT::control::stages;
1908     }
1909 
1910     if (cmd == -1)
1911     {
1912         int baseType = readControl(synth, 0, FILTERINSERT::control::baseType, thisPart, kit, engine, TOPLEVEL::insert::filterGroup);
1913         //cout << "baseType " << baseType << endl;
1914         if (baseType == 1) // formant
1915         {
1916             if (input.matchnMove(1, "invert"))
1917             {
1918                 if (input.lineEnd(controlType))
1919                     return REPLY::value_msg;
1920                 value = (input.toggle() == 1);
1921                 cmd = FILTERINSERT::control::negateInput;
1922             }
1923             else if (input.matchnMove(2, "fcenter"))
1924                 cmd = FILTERINSERT::control::formantCenter;
1925             else if (input.matchnMove(2, "frange"))
1926                 cmd = FILTERINSERT::control::formantOctave;
1927             else if (input.matchnMove(1, "expand"))
1928                 cmd = FILTERINSERT::control::formantStretch;
1929             else if (input.matchnMove(1, "lucidity"))
1930                 cmd = FILTERINSERT::control::formantClearness;
1931             else if (input.matchnMove(1, "morph"))
1932                 cmd = FILTERINSERT::control::formantSlowness;
1933             else if (input.matchnMove(2, "size"))
1934             {
1935                 if (input.lineEnd(controlType))
1936                     return REPLY::value_msg;
1937                 value = string2int(input);
1938                 if (filterVowelNumber >= value)
1939                 {
1940                     filterVowelNumber = value -1; // bring back into range
1941                     filterFormantNumber = 0; // zero as size unknown
1942                 }
1943                 cmd = FILTERINSERT::control::sequenceSize;
1944             }
1945             else if (input.matchnMove(2, "count"))
1946             {
1947                 if (input.lineEnd(controlType))
1948                     return REPLY::value_msg;
1949                 value = string2int(input);
1950                 if (filterFormantNumber >= value)
1951                     filterFormantNumber = value -1; // bring back into range
1952                 cmd = FILTERINSERT::control::numberOfFormants;
1953             }
1954             else if (input.matchnMove(2, "vowel"))
1955             {
1956                 if (input.lineEnd(controlType))
1957                     return REPLY::value_msg;
1958                 value = string2int(input);
1959                 int number = string2int(input);
1960                 if (number < 0 || number >= filterSequenceSize)
1961                     return REPLY::range_msg;
1962                 filterVowelNumber = number;
1963                 filterFormantNumber = 0;
1964                 return REPLY::done_msg;
1965             }
1966             else if (input.matchnMove(1, "point"))
1967             {
1968                 if (input.lineEnd(controlType))
1969                     return REPLY::value_msg;
1970                 value = string2int(input);
1971                 if (input.lineEnd(controlType))
1972                     return REPLY::value_msg;
1973                 input.skipChars();
1974                 int position = string2int(input);
1975                 //cout << "val " << value << "  pos " << position << endl;
1976                 return sendNormal(synth, 0, value, controlType, FILTERINSERT::control::vowelPositionInSequence, thisPart, kit, engine, TOPLEVEL::insert::filterGroup, position);
1977             }
1978             else if (input.matchnMove(2, "formant"))
1979             {
1980                 if (input.lineEnd(controlType))
1981                     return REPLY::value_msg;
1982                 int number = string2int(input);
1983                 if (number < 0 || number >= filterNumberOfFormants)
1984                     return REPLY::range_msg;
1985                 filterFormantNumber = number;
1986                 return REPLY::done_msg;
1987             }
1988             else
1989             {
1990                 if (input.matchnMove(2, "ffrequency"))
1991                     cmd = FILTERINSERT::control::formantFrequency;
1992                 else if (input.matchnMove(2, "fq"))
1993                     cmd = FILTERINSERT::control::formantQ;
1994                 else if (input.matchnMove(2, "fgain"))
1995                     cmd = FILTERINSERT::control::formantAmplitude;
1996                 if (cmd == -1)
1997                     return REPLY::range_msg;
1998                 value = string2int(input);
1999                 return sendNormal(synth, 0, value, controlType, cmd, thisPart, kit, engine, TOPLEVEL::insert::filterGroup, filterFormantNumber, filterVowelNumber);
2000             }
2001         }
2002         else if (input.matchnMove(2, "type"))
2003         {
2004             if (controlType == type_read && input.isAtEnd())
2005                 value = 0;
2006             switch (baseType)
2007             {
2008                 case 0: // analog
2009                 {
2010                     if (value == -1)
2011                     {
2012                         int idx = 0;
2013                         while (filterlist [idx] != "l1")
2014                             idx += 2;
2015                         int start = idx;
2016                         while (filterlist [idx] != "hshelf")
2017                             idx += 2;
2018                         int end = idx;
2019                         idx = start;
2020                         while (idx <= end)
2021                         {
2022                             if (input.matchnMove(2, filterlist[idx].c_str()))
2023                                 break;
2024                             idx += 2;
2025                         }
2026                         if (idx > end)
2027                             return REPLY::range_msg;
2028                         value = (idx - start) / 2;
2029                     }
2030                     cmd = FILTERINSERT::control::analogType;
2031                     break;
2032                 }
2033                 case 2: // state variable
2034                 {
2035                     if (value == -1)
2036                     {
2037                         int idx = 0;
2038                         while (filterlist [idx] != "low")
2039                             idx += 2;
2040                         int start = idx;
2041                         while (filterlist [idx] != "stop")
2042                             idx += 2;
2043                         int end = idx;
2044                         idx = start;
2045                         while (idx <= end)
2046                         {
2047                             if (input.matchnMove(2, filterlist[idx].c_str()))
2048                                 break;
2049                             idx += 2;
2050                         }
2051                         if (idx > end)
2052                             return REPLY::range_msg;
2053                         value = (idx - start) / 2;
2054                     }
2055                     cmd = FILTERINSERT::control::stateVariableType;
2056                     break;
2057                 }
2058                 default:
2059                     return REPLY::available_msg;
2060                     break;
2061             }
2062         }
2063     }
2064 
2065     //cout << ">> base cmd " << int(cmd) << "  part " << int(thisPart) << "  kit " << int(kit) << "  engine " << int(engine) << "  parameter " << int(param) << endl;
2066 
2067     if (value == -1)
2068         value = string2float(input);
2069 
2070     return sendNormal(synth, 0, value, controlType, cmd, thisPart, kit, engine, TOPLEVEL::insert::filterGroup, param);
2071 }
2072 
2073 
envelopeSelect(Parser & input,unsigned char controlType)2074 int CmdInterpreter::envelopeSelect(Parser& input, unsigned char controlType)
2075 {
2076     int cmd = -1;
2077     float value = -1;
2078     int group = -1;
2079     unsigned char insert = TOPLEVEL::insert::envelopeGroup;
2080     unsigned char offset = UNUSED;
2081     if (input.lineEnd(controlType))
2082         return REPLY::done_msg;
2083 
2084     int engine = contextToEngines(context);
2085     if (engine == PART::engine::addVoice1 || engine == PART::engine::addMod1)
2086         engine += voiceNumber;
2087 
2088     if (input.matchnMove(2, "amplitute"))
2089         group = TOPLEVEL::insertType::amplitude;
2090     else if (input.matchnMove(2, "frequency"))
2091         group = TOPLEVEL::insertType::frequency;
2092     else if (input.matchnMove(2, "filter"))
2093         group = TOPLEVEL::insertType::filter;
2094     else if (input.matchnMove(2, "bandwidth"))
2095     {
2096         if (bitTest(context, LEVEL::SubSynth))
2097             group = TOPLEVEL::insertType::bandwidth;
2098         else
2099             return REPLY::available_msg;
2100     }
2101 
2102     if (group > -1)
2103         insertType = group;
2104     else
2105         group = insertType;
2106 
2107     switch (insertType)
2108     {
2109         case TOPLEVEL::insertType::amplitude:
2110             if (engine < PART::engine::addMod1)
2111                 cmd = ADDVOICE::control::enableAmplitudeEnvelope;
2112             else
2113                 cmd = ADDVOICE::control::enableModulatorAmplitudeEnvelope;
2114             break;
2115         case TOPLEVEL::insertType::frequency:
2116             if (engine < PART::engine::addMod1)
2117                 cmd = ADDVOICE::control::enableFrequencyEnvelope;
2118             else
2119                 cmd = ADDVOICE::control::enableModulatorFrequencyEnvelope;
2120             break;
2121         case TOPLEVEL::insertType::filter:
2122             cmd = ADDVOICE::control::enableFilterEnvelope;
2123             break;
2124         case TOPLEVEL::insertType::bandwidth:
2125             cmd = SUBSYNTH::control::enableBandwidthEnvelope;
2126             break;
2127     }
2128     if (input.lineEnd(controlType))
2129         return REPLY::done_msg;
2130 
2131     value = input.toggle();
2132     if (value > -1)
2133     {
2134         if (engine != PART::engine::addSynth && engine != PART::engine::padSynth)
2135             return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, engine);
2136         else
2137             return REPLY::available_msg;
2138     }
2139 
2140     if (input.matchnMove(2, "fmode"))
2141     {
2142         return sendNormal(synth, 0, (input.toggle() == 1), controlType, ENVELOPEINSERT::control::enableFreeMode, npart, kitNumber, engine, TOPLEVEL::insert::envelopeGroup, insertType);
2143     }
2144 
2145     // common controls
2146     value = -1;
2147     cmd = -1;
2148     if (input.matchnMove(2, "expand"))
2149         cmd = ENVELOPEINSERT::control::stretch;
2150     else if (input.matchnMove(1, "force"))
2151     {
2152         cmd = ENVELOPEINSERT::control::forcedRelease;
2153         value = (input.toggle() == 1);
2154     }
2155     else if (input.matchnMove(2, "linear"))
2156     {
2157         cmd = ENVELOPEINSERT::control::linearEnvelope;
2158         value = (input.toggle() == 1);
2159     }
2160 
2161     bool freeMode = readControl(synth, 0, ENVELOPEINSERT::control::enableFreeMode, npart, kitNumber, engine, TOPLEVEL::insert::envelopeGroup, insertType);
2162 
2163     if (freeMode && cmd == -1)
2164     {
2165         int pointCount = readControl(synth, 0, ENVELOPEINSERT::control::points, npart, kitNumber, engine, insert, insertType);
2166         if (input.matchnMove(1, "Points"))
2167         {
2168             value = 0; // dummy value
2169             cmd = ENVELOPEINSERT::control::points;
2170             // not using already fetched value to get normal reporting
2171         }
2172         else if (input.matchnMove(1, "Sustain"))
2173         {
2174             if (input.lineEnd(controlType))
2175                 return REPLY::value_msg;
2176             value = string2int(input);
2177             if (value == 0)
2178             {
2179                     synth->getRuntime().Log("Sustain can't be at first point");
2180                     return REPLY::done_msg;
2181             }
2182             else if (value >= (pointCount - 1))
2183             {
2184                     synth->getRuntime().Log("Sustain can't be at last point");
2185                     return REPLY::done_msg;
2186             }
2187             else if (value < 0)
2188                 return REPLY::range_msg;
2189             cmd = ENVELOPEINSERT::control::sustainPoint;
2190         }
2191         else
2192         {
2193             if (input.matchnMove(1, "insert"))
2194             {
2195                 if ((MAX_ENVELOPE_POINTS - pointCount) < 2)
2196                 {
2197                     synth->getRuntime().Log("Max points already defined");
2198                     return REPLY::done_msg;
2199                 }
2200                 if (input.lineEnd(controlType))
2201                     return REPLY::value_msg;
2202 
2203                 cmd = string2int(input); // point number
2204                 if (cmd == 0)
2205                 {
2206                     synth->getRuntime().Log("Can't add at first point");
2207                     return REPLY::done_msg;
2208                 }
2209                 if (cmd < 0 || cmd >= pointCount)
2210                     return REPLY::range_msg;
2211                 input.skipChars();
2212                 if (input.lineEnd(controlType))
2213                     return REPLY::value_msg;
2214 
2215                 offset = string2int(input); // X
2216                 input.skipChars();
2217                 if (input.lineEnd(controlType))
2218                     return REPLY::value_msg;
2219 
2220                 value = string2int(input); // Y
2221                 insert = TOPLEVEL::insert::envelopePoints;
2222 
2223             }
2224             else if (input.matchnMove(1, "delete"))
2225             {
2226                 if (pointCount <= 3)
2227                 {
2228                     synth->getRuntime().Log("Can't have less than three points");
2229                     return REPLY::done_msg;
2230                 }
2231                 if (input.lineEnd(controlType))
2232                     return REPLY::value_msg;
2233 
2234                 cmd = string2int(input); // point number
2235                 if (cmd == 0)
2236                 {
2237                     synth->getRuntime().Log("Can't delete first point");
2238                     return REPLY::done_msg;
2239                 }
2240                 if (cmd >= (pointCount - 1))
2241                 {
2242                     synth->getRuntime().Log("Can't delete last point");
2243                     return REPLY::done_msg;
2244                 }
2245                 if (cmd < 0 || cmd >= (MAX_ENVELOPE_POINTS - 1))
2246                     return REPLY::range_msg;
2247                 insert = TOPLEVEL::insert::envelopePoints;
2248             }
2249             else if (input.matchnMove(1, "change"))
2250             {
2251                 if (input.lineEnd(controlType))
2252                 return REPLY::value_msg;
2253 
2254                 cmd = string2int(input); // point number
2255                 if (cmd < 0 || cmd >= (pointCount - 1))
2256                     return REPLY::range_msg;
2257                 input.skipChars();
2258                 if (input.lineEnd(controlType))
2259                 return REPLY::value_msg;
2260 
2261                 offset = string2int(input); // X
2262                 input.skipChars();
2263                 if (input.lineEnd(controlType))
2264                 return REPLY::value_msg;
2265 
2266                 value = string2int(input); // Y
2267                 insert = TOPLEVEL::insert::envelopePointChange;
2268             }
2269         }
2270     }
2271     else if (cmd == -1)
2272     {
2273         if (input.matchnMove(1, "attack"))
2274         {
2275             if (input.matchnMove(1, "level"))
2276                 cmd = ENVELOPEINSERT::control::attackLevel;
2277             else if (input.matchnMove(1, "time"))
2278                 cmd = ENVELOPEINSERT::control::attackTime;
2279         }
2280         else if (input.matchnMove(1, "decay"))
2281         {
2282             if (input.matchnMove(1, "level"))
2283                 cmd = ENVELOPEINSERT::control::decayLevel;
2284             else if (input.matchnMove(1, "time"))
2285                 cmd = ENVELOPEINSERT::control::decayTime;
2286         }
2287         else if (input.matchnMove(1, "sustain"))
2288             cmd = ENVELOPEINSERT::control::sustainLevel;
2289         else if (input.matchnMove(1, "release"))
2290         {
2291             if (input.matchnMove(1, "level"))
2292                 cmd = ENVELOPEINSERT::control::releaseLevel;
2293             else if (input.matchnMove(1, "time"))
2294                 cmd = ENVELOPEINSERT::control::releaseTime;
2295         }
2296     }
2297 
2298     if (cmd == -1)
2299         return REPLY::op_msg;
2300 
2301     if (value == -1)
2302     {
2303         if (input.lineEnd(controlType))
2304             return REPLY::value_msg;
2305         value = string2float(input);
2306     }
2307 
2308     //cout << ">> base cmd " << int(cmd) << "  part " << int(npart) << "  kit " << int(kitNumber) << "  engine " << int(engine) << "  parameter " << int(insertType) << endl;
2309 
2310     return sendNormal(synth, 0, string2float(input), controlType, cmd, npart, kitNumber, engine, insert, insertType, offset);
2311 }
2312 
commandGroup(Parser & input)2313 int CmdInterpreter::commandGroup(Parser& input)
2314 {
2315     string line;
2316     float value = string2int(input);
2317     if (input.isAtEnd())
2318     {
2319         synth->getRuntime().Log("\nInstrument Groups");
2320         int i = 0;
2321         string entry = type_list[i];
2322         while (entry != "@end")
2323         {
2324             entry = func::stringCaps(entry, 3);
2325             line = "  " + func::stringCaps(entry, 3);
2326             synth->getRuntime().Log(line);
2327             ++ i;
2328             entry = type_list[i];
2329         }
2330         return REPLY::done_msg;
2331     }
2332     string name = string{input};
2333     value = stringNumInList(name, type_list, 2) + 1;
2334     //cout << value << endl;
2335     if (value < 1)
2336         return REPLY::range_msg;
2337     synth->getRuntime().Log("\n" + type_list[int(value - 1)] + " Instruments");
2338     list<string> msg;
2339     /*
2340     * Having two lists is messy, but the list routine clears 'msg' and
2341     * we need 'instrumentGroup' kept for later actual part loads.
2342     * Also, the search list needs embeded root, bank, and instrument IDs
2343     * but the reported one only wants the list number.
2344     */
2345     input.skipChars();
2346     bool full = (input.matchnMove(1, "location"));
2347 
2348     int count = 0;
2349     instrumentGroup.clear();
2350     do {
2351         ++ count;
2352         line = textMsgBuffer.fetch(readControl(synth, 0, BANK::control::findInstrumentName, TOPLEVEL::section::bank, UNUSED, UNUSED, UNUSED, value - 1));
2353         if (line != "@end")
2354         {
2355             instrumentGroup.push_back(line);
2356             if (!full && line.length() > 16)
2357                 line = line.substr(15); // remove root, bank, instrument IDs
2358             line = to_string(count) + "| " + line; // replace with line count
2359             msg.push_back(line);
2360         }
2361     } while (line != "@end");
2362     synth->cliOutput(msg, LINES);
2363     return REPLY::done_msg;
2364 }
2365 
2366 
commandList(Parser & input)2367 int CmdInterpreter::commandList(Parser& input)
2368 {
2369     Config &Runtime = synth->getRuntime();
2370     int ID;
2371     int tmp;
2372     list<string> msg;
2373 
2374     if (input.matchnMove(1, "instruments") || input.matchnMove(2, "programs"))
2375     {
2376         if (input.isAtEnd())
2377             ID = 128;
2378         else
2379             ID = string2int(input);
2380         synth->ListInstruments(ID, msg);
2381         synth->cliOutput(msg, LINES);
2382         return REPLY::done_msg;
2383     }
2384 
2385     if (input.matchnMove(1, "roots")) // must be before bank
2386     {
2387         synth->ListPaths(msg);
2388         synth->cliOutput(msg, LINES);
2389         return REPLY::done_msg;
2390     }
2391 
2392     if (input.matchnMove(1, "banks")
2393         || (bitFindHigh(context) == LEVEL::Bank && (input.isAtEnd() || input.isdigit())))
2394     {
2395         if (input.isAtEnd() | !input.isdigit())
2396             ID = 128;
2397         else
2398             ID = string2int(input);
2399         synth->ListBanks(ID, msg);
2400         synth->cliOutput(msg, LINES);
2401         return REPLY::done_msg;
2402     }
2403 
2404     if (input.matchnMove(1, "vectors"))
2405     {
2406         synth->ListVectors(msg);
2407         synth->cliOutput(msg, LINES);
2408         return REPLY::done_msg;
2409     }
2410 
2411     if (input.matchnMove(1, "parts"))
2412     {
2413         listCurrentParts(input, msg);
2414         synth->cliOutput(msg, LINES);
2415         return REPLY::done_msg;
2416     }
2417 
2418     if (input.matchnMove(1, "config"))
2419     {
2420         synth->ListSettings(msg);
2421         synth->cliOutput(msg, LINES);
2422         return REPLY::done_msg;
2423     }
2424 
2425     if (input.matchnMove(2, "mlearn"))
2426     {
2427         if (input.nextChar('@'))
2428         {
2429             input.skip(1);
2430             input.skipSpace();
2431             tmp = string2int(input);
2432             if (tmp > 0)
2433                 synth->midilearn.listLine(tmp - 1);
2434             else
2435                 return REPLY::value_msg;
2436         }
2437         else
2438         {
2439             synth->midilearn.listAll(msg);
2440             synth->cliOutput(msg, LINES);
2441         }
2442         return REPLY::done_msg;
2443     }
2444 
2445     if (input.matchnMove(1, "tuning"))
2446     {
2447         Runtime.Log("Tuning:\n" + synth->microtonal.tuningtotext());
2448         return REPLY::done_msg;
2449     }
2450     if (input.matchnMove(1, "keymap"))
2451     {
2452         Runtime.Log("Keymap:\n" + synth->microtonal.keymaptotext());
2453         return REPLY::done_msg;
2454     }
2455 
2456     if (input.matchnMove(1, "history"))
2457     {
2458         if (input.matchnMove(1, "instruments") || input.matchnMove(2, "program") )
2459             historyList(TOPLEVEL::XML::Instrument);
2460         else if (input.matchnMove(1, "patchsets"))
2461             historyList(TOPLEVEL::XML::Patch);
2462         else if (input.matchnMove(2, "scales"))
2463             historyList(TOPLEVEL::XML::Scale);
2464         else if (input.matchnMove(2, "states"))
2465             historyList(TOPLEVEL::XML::State);
2466         else if (input.matchnMove(1, "vectors"))
2467             historyList(TOPLEVEL::XML::Vector);
2468         else if (input.matchnMove(2, "mlearn"))
2469             historyList(TOPLEVEL::XML::MLearn);
2470         else
2471             historyList(-1);
2472         return REPLY::done_msg;
2473     }
2474 
2475     if (input.matchnMove(1, "effects") || input.matchnMove(1, "efx"))
2476         return effectsList(input);
2477     if (input.matchnMove(3, "presets"))
2478         return effectsList(input, true);
2479 
2480     msg.push_back("Lists:");
2481     helpLoop(msg, listlist, 2);
2482     if (synth->getRuntime().toConsole)
2483         // we need this in case someone is working headless
2484         cout << "\nSet CONfig REPorts [s] - set report destination (gui/stderr)" << endl;
2485     synth->cliOutput(msg, LINES);
2486     return REPLY::done_msg;
2487 }
2488 
2489 
listCurrentParts(Parser & input,list<string> & msg_buf)2490 void CmdInterpreter::listCurrentParts(Parser& input, list<string>& msg_buf)
2491 {
2492     int dest;
2493     string name = "";
2494     int avail = readControl(synth, 0, MAIN::control::availableParts, TOPLEVEL::section::main);
2495     bool full = input.matchnMove(1, "more");
2496     if (bitFindHigh(context) == LEVEL::Part)
2497     {
2498         if (!readControl(synth, 0, PART::control::kitMode, TOPLEVEL::section::part1 + npart))
2499         {
2500             if (readControl(synth, 0, PART::control::enable, TOPLEVEL::section::part1 + npart, UNUSED, PART::engine::addSynth))
2501             {
2502                 name += " AddSynth ";
2503                 if (full)
2504                 {
2505                     string found = "";
2506                     for (int voice = 0; voice < NUM_VOICES; ++voice)
2507                     {
2508                         if (readControl(synth, 0, PART::control::enableAdd, TOPLEVEL::section::part1 + npart, 0, PART::engine::addVoice1 + voice))
2509                             found += (" " + to_string(voice + 1));
2510                     }
2511                     if (found > "")
2512                         name += ("Voices" + found + " ");
2513                 }
2514             }
2515             if (readControl(synth, 0, PART::control::enable, TOPLEVEL::section::part1 + npart, UNUSED, PART::engine::subSynth))
2516                 name += " SubSynth ";
2517             if (readControl(synth, 0, PART::control::enable, TOPLEVEL::section::part1 + npart, UNUSED, PART::engine::padSynth))
2518                 name += " PadSynth ";
2519             if (name == "")
2520                 name = "no engines active!";
2521             msg_buf.push_back(name);
2522             return;
2523         }
2524         msg_buf.push_back("kit items");
2525         for (int item = 0; item < NUM_KIT_ITEMS; ++item)
2526         {
2527             name = "";
2528             if (readControl(synth, 0, PART::control::enable, TOPLEVEL::section::part1 + npart, item, UNUSED, TOPLEVEL::insert::kitGroup))
2529             {
2530                 name = "  " + to_string(item) + " ";
2531                 {
2532                 if (readControl(synth, 0, PART::control::kitItemMute, TOPLEVEL::section::part1 + npart, item, UNUSED, TOPLEVEL::insert::kitGroup))
2533                     name += "Quiet";
2534                 else
2535                 {
2536                     if (full)
2537                     {
2538                         name += "  key Min ";
2539                         int min = int(readControl(synth, 0, PART::control::minNote, TOPLEVEL::section::part1 + npart, item, UNUSED, TOPLEVEL::insert::kitGroup));
2540                         if (min < 10)
2541                             name += "  ";
2542                         else if (min < 100)
2543                             name += " ";
2544                         name += to_string(min);
2545                         name += "  Max ";
2546                         int max = int(readControl(synth, 0, PART::control::maxNote, TOPLEVEL::section::part1 + npart, item, UNUSED, TOPLEVEL::insert::kitGroup));
2547                         if (max < 10)
2548                             name += "  ";
2549                         else if (max < 100)
2550                             name += " ";
2551 
2552                         name += (to_string(max) + "  ");
2553                         string text = readControlText(synth, TOPLEVEL::action::lowPrio, PART::control::instrumentName, TOPLEVEL::section::part1 + npart, item, UNUSED, TOPLEVEL::insert::kitGroup);
2554                         if (text > "")
2555                             name += text;
2556                         msg_buf.push_back(name);
2557                         name = "    ";
2558                     }
2559                     if (readControl(synth, 0, PART::control::enable, TOPLEVEL::section::part1 + npart, item, PART::engine::addSynth, TOPLEVEL::insert::kitGroup))
2560                     {
2561                         name += "AddSynth ";
2562                         if (full)
2563                         {
2564                             string found = "";
2565                             for (int voice = 0; voice < NUM_VOICES; ++voice)
2566                             {
2567                                 if (readControl(synth, 0, PART::control::enableAdd, TOPLEVEL::section::part1 + npart, item, PART::engine::addVoice1 + voice))
2568                                 found += (" " + to_string(voice + 1));
2569                             }
2570                             if (found > "")
2571                                 name += ("Voices" + found + " ");
2572                         }
2573                     }
2574                     if (readControl(synth, 0, PART::control::enable, TOPLEVEL::section::part1 + npart, item, PART::engine::subSynth, TOPLEVEL::insert::kitGroup))
2575                         name += "SubSynth ";
2576                     if (readControl(synth, 0, PART::control::enable, TOPLEVEL::section::part1 + npart, item, PART::engine::padSynth, TOPLEVEL::insert::kitGroup))
2577                         name += "PadSynth ";
2578                     if (name == "")
2579                         name = "no engines active!";
2580                 }
2581             }
2582             if (name > "")
2583                 msg_buf.push_back(name);
2584             }
2585         }
2586         return;
2587     }
2588     msg_buf.push_back(asString(avail) + " parts available");
2589     for (int partno = 0; partno < NUM_MIDI_PARTS; ++partno)
2590     {
2591         string text = readControlText(synth, TOPLEVEL::action::lowPrio, PART::control::instrumentName, TOPLEVEL::section::part1 + partno);
2592         bool enabled = readControl(synth, 0, PART::control::enable, TOPLEVEL::section::part1 + partno);
2593         if (text != DEFAULT_NAME || enabled)
2594         {
2595             if (partno < 9)
2596                 name = " ";
2597             else
2598                 name = "";
2599             if (enabled && partno < avail)
2600                 name += "+";
2601             else
2602                 name += " ";
2603             name += to_string(partno + 1);
2604             dest = readControl(synth, 0, PART::control::audioDestination, TOPLEVEL::section::part1 + partno);
2605             if (partno >= avail)
2606                 name += " - " + text;
2607             else
2608             {
2609                 if (dest == 1)
2610                     name += " Main";
2611                 else if (dest == 2)
2612                     name += " Part";
2613                 else
2614                     name += " Both";
2615                 name += "  Chan ";
2616                 int ch = int(readControl(synth, 0, PART::control::midiChannel, TOPLEVEL::section::part1 + partno) + 1);
2617                 if (ch < 10)
2618                     name += " ";
2619                 name += to_string(ch);
2620                 if (full)
2621                 {
2622                     name += "  key Min ";
2623                     int min = int(readControl(synth, 0, PART::control::minNote, TOPLEVEL::section::part1 + partno));
2624                     if (min < 10)
2625                         name += "  ";
2626                     else if (min < 100)
2627                         name += " ";
2628                     name += to_string(min);
2629                     name += "  Max ";
2630                     int max = int(readControl(synth, 0, PART::control::maxNote, TOPLEVEL::section::part1 + partno));
2631                     if (max < 10)
2632                         name += "  ";
2633                     else if (max < 100)
2634                         name += " ";
2635                     name += to_string(max);
2636                     name += "  Shift ";
2637                     int shift = int(readControl(synth, TOPLEVEL::action::lowPrio, PART::control::keyShift, TOPLEVEL::section::part1 + partno));
2638                     if (shift >= 10)
2639                         name += " ";
2640                     else if (shift >= 0)
2641                         name += "  ";
2642                     else if (shift >= -10)
2643                         name += " ";
2644                     name += to_string(shift);
2645 
2646                 }
2647                 name +=  ("  " + text);
2648                 int mode = readControl(synth, 0, PART::control::kitMode, TOPLEVEL::section::part1 + partno);
2649                 if (mode != PART::kitType::Off)
2650                     name += " > ";
2651                 switch (mode)
2652                 {
2653                     case PART::kitType::Multi:
2654                         name += "Multi";
2655                         break;
2656                     case PART::kitType::Single:
2657                         name += "Single";
2658                         break;
2659                     case PART::kitType::CrossFade:
2660                         name += "Crossfade";
2661                         break;
2662                 }
2663             }
2664             msg_buf.push_back(name);
2665             if (full)
2666             {
2667                 name = "    Drum ";
2668                 int drum = readControl(synth, 0, PART::control::drumMode, TOPLEVEL::section::part1 + partno);
2669                 if (drum)
2670                     name += " on";
2671                 else
2672                     name += "off";
2673                 name += " Portamento ";
2674                 if (readControl(synth, 0, PART::control::portamento, TOPLEVEL::section::part1 + partno))
2675                     name += " on";
2676                 else name += "off";
2677                 int key = readControl(synth, 0, PART::control::keyMode, TOPLEVEL::section::part1 + partno);
2678                 switch (key)
2679                 {
2680                     case 0:
2681                         name += "  Polphonic";
2682                         break;
2683                     case 1:
2684                         name += "  Monophonic";
2685                         break;
2686                     case 2:
2687                         name += "  Legato";
2688                         if (drum)
2689                             name += " (drum blocked)";
2690                         break;
2691                 }
2692                 msg_buf.push_back(name);
2693             }
2694         }
2695     }
2696 }
2697 
2698 
commandMlearn(Parser & input,unsigned char controlType)2699 int CmdInterpreter::commandMlearn(Parser& input, unsigned char controlType)
2700 {
2701     Config &Runtime = synth->getRuntime();
2702     list<string> msg;
2703     bitSet(context, LEVEL::Learn);
2704 
2705     if (controlType != TOPLEVEL::type::Write)
2706     {
2707         Runtime.Log("Write only");
2708         return REPLY::done_msg; // will eventually be readable
2709     }
2710 
2711     if (input.isdigit() || input.nextChar('-')) // negative should never happen!
2712     {
2713         int lineNo = string2int(input);
2714         input.skipChars();
2715         if (lineNo <= 0)
2716             return REPLY::value_msg;
2717         else
2718             mline = lineNo -1;
2719     }
2720     int tmp = synth->midilearn.findSize();
2721     if (tmp == 0 || tmp <= mline)
2722     {
2723         if (tmp == 0)
2724             Runtime.Log("No learned lines");
2725         else
2726             Runtime.Log("Line " + to_string(mline + 1) + " Not found");
2727         mline = 0;
2728         return (REPLY::done_msg);
2729     }
2730     if (input.lineEnd(controlType))
2731         return REPLY::done_msg;
2732     {
2733         unsigned char type = 0;
2734         unsigned char control = 0;
2735         unsigned char kit = UNUSED;
2736         unsigned char engine = UNUSED;
2737         unsigned char insert = UNUSED;
2738         unsigned char parameter = UNUSED;
2739 
2740         if (input.matchnMove(2, "cc"))
2741         {
2742             if (!input.isdigit())
2743                 return REPLY::value_msg;
2744             kit = string2int(input);
2745             if (kit > 129)
2746             {
2747                 Runtime.Log("Max CC value is 129");
2748                 return REPLY::done_msg;
2749             }
2750             control = MIDILEARN::control::CCorChannel;
2751             Runtime.Log("Lines may be re-ordered");
2752         }
2753         else if (input.matchnMove(2, "channel"))
2754         {
2755             engine = string2int(input) - 1;
2756             if (engine > 16)
2757                 engine = 16;
2758             control = MIDILEARN::control::CCorChannel;
2759             Runtime.Log("Lines may be re-ordered");
2760         }
2761         else if (input.matchnMove(2, "minimum"))
2762         {
2763             insert = int((string2float(input)* 2.0f) + 0.5f);
2764             if (insert > 200)
2765                 return REPLY::value_msg;
2766             control = MIDILEARN::control::minimum;
2767         }
2768         else if (input.matchnMove(2, "maximum"))
2769         {
2770             parameter = int((string2float(input)* 2.0f) + 0.5f);
2771             if (parameter > 200)
2772                 return REPLY::value_msg;
2773             control = MIDILEARN::control::maximum;
2774         }
2775         else if (input.matchnMove(2, "mute"))
2776         {
2777             type = (input.toggle() == 1) * 4;
2778             control = MIDILEARN::control::mute;
2779         }
2780         else if (input.matchnMove(2, "limit"))
2781         {
2782             type = (input.toggle() == 1) * 2;
2783             control = MIDILEARN::control::limit;
2784         }
2785         else if (input.matchnMove(2, "block"))
2786         {
2787             type = (input.toggle() == 1);
2788             control = MIDILEARN::control::block;
2789         }
2790         else if (input.matchnMove(2, "seven"))
2791         {
2792             type = (input.toggle() == 1) * 16;
2793             control = MIDILEARN::control::sevenBit;
2794         }
2795         sendNormal(synth, 0, mline, type, control, TOPLEVEL::section::midiLearn, kit, engine, insert, parameter);
2796         return REPLY::done_msg;
2797     }
2798     return REPLY::op_msg;
2799 }
2800 
2801 
commandVector(Parser & input,unsigned char controlType)2802 int CmdInterpreter::commandVector(Parser& input, unsigned char controlType)
2803 {
2804     Config &Runtime = synth->getRuntime();
2805     list<string> msg;
2806     int tmp;
2807     bitSet(context, LEVEL::Vector);
2808     if (controlType != TOPLEVEL::type::Write)
2809     {
2810         if (synth->SingleVector(msg, chan))
2811             synth->cliOutput(msg, LINES);
2812         else
2813             Runtime.Log("No vector on channel " + asString(chan + 1));
2814         return REPLY::done_msg;
2815     }
2816     if (input.lineEnd(controlType))
2817     {
2818         if (!Runtime.vectordata.Enabled[chan])
2819             Runtime.Log("No vector on channel " + asString(chan + 1));
2820         return REPLY::done_msg;
2821     }
2822 
2823     unsigned char ch = string2int127(input);
2824     if (ch > 0)
2825     {
2826         ch -= 1;
2827         if (ch >= NUM_MIDI_CHANNELS)
2828             return REPLY::range_msg;
2829         input.skipChars();
2830         if (chan != ch)
2831         {
2832             chan = ch;
2833             axis = 0;
2834         }
2835 
2836         Runtime.Log("Vector channel set to " + asString(chan + 1));
2837     }
2838 
2839     if (input.matchWord(1, "off"))
2840     {
2841         sendDirect(synth, 0, 0,controlType,VECTOR::control::erase, TOPLEVEL::section::vector, UNUSED, UNUSED, chan);
2842         axis = 0;
2843         bitClear(context, LEVEL::Vector);
2844         return REPLY::done_msg;
2845     }
2846     if (input.matchnMove(1, "xaxis"))
2847         axis = 0;
2848     else if (input.matchnMove(1, "yaxis"))
2849     {
2850         if (!Runtime.vectordata.Enabled[chan])
2851         {
2852             Runtime.Log("Vector X must be set first");
2853             return REPLY::done_msg;
2854         }
2855         axis = 1;
2856     }
2857 
2858     if (input.lineEnd(controlType))
2859         return REPLY::done_msg;
2860 
2861     if (input.matchnMove(2, "cc"))
2862     {
2863         if (input.lineEnd(controlType))
2864             return REPLY::value_msg;
2865 
2866         tmp = string2int(input);
2867         if (axis == 0)
2868         {
2869             sendDirect(synth, 0, tmp, controlType, VECTOR::control::Xcontroller, TOPLEVEL::section::vector, UNUSED, UNUSED, chan);
2870             bitSet(context, LEVEL::Vector);
2871             return REPLY::done_msg;
2872         }
2873         if (Runtime.vectordata.Enabled[chan])
2874         {
2875             sendDirect(synth, 0, tmp, controlType, VECTOR::control::Ycontroller, TOPLEVEL::section::vector, UNUSED, UNUSED, chan);
2876             return REPLY::done_msg;
2877         }
2878     }
2879 
2880     if (!Runtime.vectordata.Enabled[chan])
2881     {
2882         Runtime.Log("Vector X CC must be set first");
2883         return REPLY::done_msg;
2884     }
2885 
2886     if (axis == 1 && (Runtime.vectordata.Yaxis[chan] > 0x7f))
2887     {
2888         Runtime.Log("Vector Y CC must be set first");
2889         return REPLY::done_msg;
2890     }
2891 
2892     if (input.matchnMove(1, "name"))
2893     {
2894         string name = "!";
2895         if (controlType == TOPLEVEL::type::Write)
2896         {
2897             name = string{input};
2898             if (name <= "!")
2899                 return REPLY::value_msg;
2900         }
2901         sendDirect(synth, TOPLEVEL::action::lowPrio, 0, controlType, VECTOR::control::name, TOPLEVEL::section::vector, UNUSED, UNUSED, chan, UNUSED, UNUSED, textMsgBuffer.push(name));
2902         return REPLY::done_msg;
2903     }
2904 
2905     if (input.matchnMove(1, "features"))
2906     {
2907         if (input.lineEnd(controlType))
2908             return REPLY::value_msg;
2909         int feat = string2int(input) - 1;
2910         if (feat < 0 || feat > 3)
2911             return REPLY::range_msg;
2912         input.skipChars();
2913         int enable = 0;
2914         if (input.toggle() == 1)
2915             enable = 1;
2916         else if (feat > 1 && input.matchnMove(1, "reverse"))
2917             enable = 2;
2918         sendDirect(synth, 0, enable, controlType, VECTOR::control::Xfeature0 + (axis * (VECTOR::control::Ycontroller - VECTOR::control::Xcontroller)) + feat , TOPLEVEL::section::vector, UNUSED, UNUSED, chan);
2919         return REPLY::done_msg;
2920     }
2921 
2922     if (input.matchnMove(2, "program") || input.matchnMove(1, "instrument"))
2923     {
2924         int hand = input.peek() | 32;
2925         input.skipChars(); // in case they type the entire word
2926         if ((axis == 0 && (hand == 'd' || hand == 'u')) || (axis == 1 && (hand == 'l' || hand == 'r')))
2927         {
2928             Runtime.Log("Bad direction for this axis");
2929             return REPLY::done_msg;
2930         }
2931         if (hand == 'l' || hand == 'd')
2932             hand = 0;
2933         else if (hand == 'r' || hand == 'u')
2934             hand = 1;
2935         else
2936             return REPLY::op_msg;
2937         tmp = string2int(input);
2938         sendDirect(synth, 0, tmp, controlType, VECTOR::control::XleftInstrument + hand + (axis * (VECTOR::control::Ycontroller - VECTOR::control::Xcontroller)), TOPLEVEL::section::vector, UNUSED, UNUSED, chan);
2939         return REPLY::done_msg;
2940     }
2941 
2942     // this disabled for now - needs a lot of work.
2943     /*if (!input.matchnMove(1, "control"))
2944         return REPLY::op_msg;
2945     if (input.isdigit())
2946     {
2947         int cmd = string2int(input);
2948         if (cmd < 2 || cmd > 4)
2949             return REPLY::range_msg;
2950         input.skipChars();
2951         if (input.lineEnd(controlType))
2952             return REPLY::value_msg;
2953         tmp = string2int(input);
2954         if (!synth->vectorInit(axis * 3 + cmd + 6, chan, tmp))
2955         {
2956             synth->vectorSet(axis * 3 + cmd + 6, chan, tmp);
2957             return REPLY::done_msg;
2958         }
2959         else
2960             return REPLY::value_msg;
2961     }*/
2962 
2963     return REPLY::op_msg;
2964 }
2965 
2966 
commandBank(Parser & input,unsigned char controlType,bool justEntered)2967 int CmdInterpreter::commandBank(Parser& input, unsigned char controlType, bool justEntered)
2968 {
2969     bitSet(context, LEVEL::Bank);
2970     int isRoot = false;
2971     if  (input.matchnMove(1, "bank"))
2972         isRoot = false; // changes nothing as we're already at bank level :)
2973     if (input.matchnMove(1, "name"))
2974     {
2975         string name = string{input};
2976         if (controlType != type_read && name <= "!")
2977             return REPLY::value_msg;
2978         int miscMsg = textMsgBuffer.push(string(input));
2979         int tmp = readControl(synth, 0, BANK::control::selectBank, TOPLEVEL::section::bank);
2980         return sendNormal(synth, TOPLEVEL::action::lowPrio, tmp, controlType, BANK::control::renameBank, TOPLEVEL::section::bank, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, miscMsg);
2981     }
2982 
2983     if (input.matchnMove(2, "instrument"))
2984     {
2985         if (input.matchnMove(1, "rename"))
2986         {
2987             if (controlType != TOPLEVEL::type::Write)
2988                 return REPLY::available_msg;
2989             if (!input.isdigit())
2990                 return REPLY::value_msg;
2991             int tmp = string2int(input) - 1; // could be up to 160
2992             if (tmp < 0 || tmp >= MAX_INSTRUMENTS_IN_BANK)
2993                 return REPLY::range_msg;
2994             input.skipChars();
2995             string name = string{input};
2996             if (name <= "!")
2997                 return REPLY::value_msg;
2998             int miscMsg = textMsgBuffer.push(name);
2999             return sendNormal(synth, TOPLEVEL::action::lowPrio, 0, controlType, BANK::control::renameInstrument, TOPLEVEL::section::bank, UNUSED, UNUSED, tmp, UNUSED, UNUSED, miscMsg);
3000         }
3001         if (input.matchnMove(1, "save"))
3002         {
3003             if (controlType != TOPLEVEL::type::Write)
3004                 return REPLY::available_msg;
3005             if (!input.isdigit())
3006                 return REPLY::value_msg;
3007             int tmp = string2int(input) - 1; // could be up to 160
3008             if (tmp < 0 || tmp >= MAX_INSTRUMENTS_IN_BANK)
3009                 return REPLY::range_msg;
3010             string line = textMsgBuffer.fetch(readControl(synth, 0, BANK::control::readInstrumentName, TOPLEVEL::section::bank, UNUSED, UNUSED, UNUSED, tmp));
3011             if (line > "!")
3012             {
3013                 if (!query("Slot " + to_string(tmp + 1) + " contains '" + line + "'. Overwrite", false))
3014                     return REPLY::done_msg;
3015             }
3016             return sendNormal(synth, TOPLEVEL::action::lowPrio, 0, controlType, BANK::control::saveInstrument, TOPLEVEL::section::bank, UNUSED, UNUSED, tmp);
3017         }
3018         return REPLY::done_msg;
3019     }
3020     if (input.matchnMove(1, "root"))
3021         isRoot = true;
3022     if (input.lineEnd(controlType))
3023         return REPLY::done_msg;
3024     if (input.isdigit() || controlType == type_read)
3025     {
3026         int tmp = string2int127(input);
3027         input.skipChars();
3028         if (isRoot)
3029         {
3030             return sendNormal(synth, TOPLEVEL::action::lowPrio, tmp, controlType, BANK::control::selectRoot, TOPLEVEL::section::bank);
3031         }
3032         return sendNormal(synth, TOPLEVEL::action::lowPrio, tmp, controlType, BANK::control::selectBank, TOPLEVEL::section::bank);
3033         if (input.lineEnd(controlType))
3034             return REPLY::done_msg;
3035     }
3036     if (input.matchnMove(2, "ID"))
3037     {
3038         int tmp = string2int127(input);
3039         if (isRoot)
3040             return sendNormal(synth, TOPLEVEL::action::lowPrio, tmp, controlType, BANK::control::changeRootId, TOPLEVEL::section::bank);
3041     }
3042     if (justEntered)
3043         return REPLY::done_msg;
3044     return REPLY::op_msg;
3045 }
3046 
3047 
commandConfig(Parser & input,unsigned char controlType)3048 int CmdInterpreter::commandConfig(Parser& input, unsigned char controlType)
3049 {
3050     float value = 0;
3051     unsigned char command = UNUSED;
3052     unsigned char action = 0;
3053     unsigned char miscmsg = UNUSED;
3054 
3055     if (input.isAtEnd())
3056         return REPLY::done_msg; // someone just came in for a look :)
3057     if (input.matchnMove(1, "oscillator"))
3058     {
3059         command = CONFIG::control::oscillatorSize;
3060         if (controlType == TOPLEVEL::type::Write && input.isAtEnd())
3061             return REPLY::value_msg;
3062         value = string2int(input);
3063     }
3064     else if (input.matchnMove(2, "buffer"))
3065     {
3066         command = CONFIG::control::bufferSize;
3067         if (controlType == TOPLEVEL::type::Write && input.isAtEnd())
3068             return REPLY::value_msg;
3069         value = string2int(input);
3070     }
3071     else if (input.matchnMove(2, "padsynth"))
3072     {
3073         command = CONFIG::control::padSynthInterpolation;
3074         value = !input.matchnMove(1, "linear");
3075     }
3076     else if (input.matchnMove(1, "virtual"))
3077     {
3078         command = CONFIG::control::virtualKeyboardLayout;
3079         if (controlType == TOPLEVEL::type::Write && input.isAtEnd())
3080             return REPLY::value_msg;
3081         value = string2int(input);
3082     }
3083     else if (input.matchnMove(1, "xml"))
3084     {
3085         command = CONFIG::control::XMLcompressionLevel;
3086         if (controlType == TOPLEVEL::type::Write && input.isAtEnd())
3087             return REPLY::value_msg;
3088         value = string2int(input);
3089     }
3090     else if (input.matchnMove(2, "reports"))
3091     {
3092         command = CONFIG::control::reportsDestination;
3093         value = !input.matchnMove(1, "stdout");
3094     }
3095     else if (input.matchnMove(2, "saved"))
3096     {
3097         command = CONFIG::control::savedInstrumentFormat;
3098         if (input.matchnMove(1, "legacy"))
3099             value = 1;
3100         else if (input.matchnMove(1, "yoshimi"))
3101             value = 2;
3102         else if (input.matchnMove(1, "both"))
3103             value = 3;
3104         else if (controlType == TOPLEVEL::type::Write)
3105             return REPLY::value_msg;
3106     }
3107     //else if (input.matchnMove(3, "engines"))
3108     //{
3109         //command = CONFIG::control::showEnginesTypes;
3110         //value = (input.toggle() != 0);
3111     //}
3112 
3113     else if (input.matchnMove(2, "state"))
3114     {
3115         command = CONFIG::control::defaultStateStart;
3116         value = (input.toggle() == 1);
3117     }
3118     else if (input.matchnMove(2, "single"))
3119     {
3120         command = CONFIG::control::enableSinglePath;
3121         value = (input.toggle() == 1);
3122     }
3123     else if (input.matchnMove(1, "hide"))
3124     {
3125         command = CONFIG::control::hideNonFatalErrors;
3126         value = (input.toggle() == 1);
3127     }
3128     else if (input.matchnMove(1, "display"))
3129     {
3130         command = CONFIG::control::showSplash;
3131         value = (input.toggle() == 1);
3132     }
3133     else if (input.matchnMove(1, "time"))
3134     {
3135         command = CONFIG::control::logInstrumentLoadTimes;
3136         value = (input.toggle() == 1);
3137     }
3138     else if (input.matchnMove(1, "include"))
3139     {
3140         command = CONFIG::control::logXMLheaders;
3141         value = (input.toggle() == 1);
3142     }
3143     else if (input.matchnMove(1, "keep"))
3144     {
3145         command = CONFIG::control::saveAllXMLdata;
3146         value = (input.toggle() == 1);
3147     }
3148     else if (input.matchnMove(1, "gui"))
3149     {
3150         command = CONFIG::control::enableGUI;
3151         if (controlType == type_read)
3152             value = 0;
3153         else
3154         {
3155             value = input.toggle();
3156             if (value == -1)
3157                 return REPLY::value_msg;
3158         }
3159     }
3160     else if (input.matchnMove(1, "cli"))
3161     {
3162         command = CONFIG::control::enableCLI;
3163         if (controlType == type_read)
3164             value = 0;
3165         else
3166         {
3167             value = input.toggle();
3168             if (value == -1)
3169                 return REPLY::value_msg;
3170         }
3171     }
3172 
3173     else if (input.matchnMove(2, "identify"))
3174     {
3175         command = CONFIG::control::enableHighlight;
3176         value = (input.toggle() == 1);
3177     }
3178 
3179     else if (input.matchnMove(3, "expose"))
3180     {
3181         value = input.toggle();
3182         if (value == -1 && input.matchnMove(2, "prompt"))
3183             value = 2;
3184         if (value == -1)
3185             return REPLY::value_msg;
3186         command = CONFIG::control::exposeStatus;
3187     }
3188 
3189     else if (input.matchnMove(1, "jack"))
3190     {
3191         if (input.matchnMove(1, "midi"))
3192         {
3193             command = CONFIG::control::jackMidiSource;
3194             action = TOPLEVEL::action::lowPrio;
3195             if (controlType != TOPLEVEL::type::Write || !input.isAtEnd())
3196             {
3197                 if (controlType == TOPLEVEL::type::Write)
3198                     miscmsg = textMsgBuffer.push(input);
3199             }
3200             else
3201                 return REPLY::value_msg;
3202         }
3203         else if (input.matchnMove(1, "server"))
3204         {
3205             command = CONFIG::control::jackServer;
3206             action = TOPLEVEL::action::lowPrio;
3207             if (controlType != TOPLEVEL::type::Write || !input.isAtEnd())
3208             {
3209                 if (controlType == TOPLEVEL::type::Write)
3210                     miscmsg = textMsgBuffer.push(input);
3211             }
3212             else
3213                 return REPLY::value_msg;
3214         }
3215         else if (input.matchnMove(1, "auto"))
3216         {
3217             command = CONFIG::control::jackAutoConnectAudio;
3218             value = (input.toggle() == 1);
3219         }
3220         else
3221             return REPLY::op_msg;
3222     }
3223 
3224     else if (input.matchnMove(2, "alsa"))
3225     {
3226         if (input.matchnMove(1, "type"))
3227         {
3228             command = CONFIG::control::alsaMidiType;
3229             if (input.matchnMove(1, "fixed"))
3230                 value = 0;
3231             else if (input.matchnMove(1, "search"))
3232                 value = 1;
3233             else if (input.matchnMove(1, "external"))
3234                 value = 2;
3235             else
3236                 return REPLY::value_msg;
3237         }
3238         else if (input.matchnMove(1, "midi"))
3239         {
3240             command = CONFIG::control::alsaMidiSource;
3241             action = TOPLEVEL::action::lowPrio;
3242             if (controlType != TOPLEVEL::type::Write || !input.isAtEnd())
3243             {
3244                 if (controlType == TOPLEVEL::type::Write)
3245                     miscmsg = textMsgBuffer.push(input);
3246             }
3247             else
3248                 return REPLY::value_msg;
3249         }
3250         else if (input.matchnMove(1, "audio"))
3251         {
3252             command = CONFIG::control::alsaAudioDevice;
3253             action = TOPLEVEL::action::lowPrio;
3254             if (controlType != TOPLEVEL::type::Write || !input.isAtEnd())
3255             {
3256                 if (controlType == TOPLEVEL::type::Write)
3257                     miscmsg = textMsgBuffer.push(input);
3258             }
3259             else
3260                 return REPLY::value_msg;
3261         }
3262         else if (input.matchnMove(1, "s"))
3263         {
3264             command = CONFIG::control::alsaSampleRate;
3265             if (controlType == TOPLEVEL::type::Write)
3266             {
3267                 if (input.lineEnd(controlType))
3268                     return REPLY::value_msg;
3269                 value = string2int(input);
3270                 if (value < 0 || value > 3)
3271                     return REPLY::range_msg;
3272             }
3273         }
3274         else
3275             return REPLY::op_msg;
3276     }
3277 
3278     else if (input.matchnMove(2, "midi"))
3279     {
3280         if (controlType != TOPLEVEL::type::Write)
3281         {
3282             return sendDirect(synth, TOPLEVEL::action::fromCLI, 0, controlType, CONFIG::control::readMIDI, TOPLEVEL::section::config);
3283         }
3284         else
3285         {
3286             value = 1;
3287             if (input.matchnMove(1, "alsa"))
3288                 command = CONFIG::control::alsaPreferredMidi;
3289             else if (input.matchnMove(1, "jack"))
3290                 command = CONFIG::control::jackPreferredMidi;
3291             else
3292                 return REPLY::value_msg;
3293         }
3294     }
3295 
3296     else if (input.matchnMove(2, "audio"))
3297     {
3298         if (controlType != TOPLEVEL::type::Write)
3299         {
3300             return sendDirect(synth, TOPLEVEL::action::fromCLI, 0, controlType, CONFIG::control::readAudio, TOPLEVEL::section::config);
3301         }
3302         else
3303         {
3304             value = 1;
3305             if (input.matchnMove(1, "alsa"))
3306                 command = CONFIG::control::alsaPreferredAudio;
3307             else if (input.matchnMove(1, "jack"))
3308                 command = CONFIG::control::jackPreferredAudio;
3309             else
3310                 return REPLY::value_msg;
3311         }
3312     }
3313 
3314     else if (input.matchnMove(2, "root"))
3315     {
3316         command = CONFIG::control::bankRootCC;
3317         value = 128; // ignored by range check
3318         if (input.lineEnd(controlType))
3319             return REPLY::value_msg;
3320         if (input.matchnMove(1, "msb"))
3321             value = 0;
3322         else if (input.matchnMove(1, "lsb"))
3323             value = 32;
3324         if (value != 128 && value == readControl(synth, 0, CONFIG::control::bankCC, TOPLEVEL::section::config))
3325         {
3326             synth->getRuntime().Log("In use for bank");
3327             return REPLY::done_msg;
3328         }
3329     }
3330     else if (input.matchnMove(2, "bank"))
3331     {
3332         command = CONFIG::control::bankCC;
3333         value = 128; // ignored by range check
3334         if (input.lineEnd(controlType))
3335             return REPLY::value_msg;
3336         if (input.matchnMove(1, "msb"))
3337             value = 0;
3338         else if (input.matchnMove(1, "lsb"))
3339             value = 32;
3340         if (value != 128 && value == readControl(synth, 0, CONFIG::control::bankRootCC, TOPLEVEL::section::config))
3341         {
3342             synth->getRuntime().Log("In use for bank root");
3343             return REPLY::done_msg;
3344         }
3345     }
3346     else if (input.matchnMove(2, "program") || input.matchnMove(2, "instrument"))
3347     {
3348         command = CONFIG::control::enableProgramChange;
3349         value = (input.toggle() == 1);
3350     }
3351     else if (input.matchnMove(3, "extend"))
3352     {
3353         command = CONFIG::control::extendedProgramChangeCC;
3354         if (controlType != TOPLEVEL::type::Write)
3355             value = 128; // ignored by range check
3356         else if (input.lineEnd(controlType))
3357             return REPLY::value_msg;
3358         else
3359         {
3360             value = string2int(input);
3361             if (value > 128)
3362                 value = 128;
3363         }
3364     }
3365     else if (input.matchnMove(1, "quiet"))
3366     {
3367         command = CONFIG::control::ignoreResetAllCCs;
3368         value = (input.toggle() == 1);
3369     }
3370     else if (input.matchnMove(1, "log"))
3371     {
3372         command = CONFIG::control::logIncomingCCs;
3373         value = (input.toggle() == 1);
3374     }
3375     else if (input.matchnMove(2, "show"))
3376     {
3377         command = CONFIG::control::showLearnEditor;
3378         value = (input.toggle() == 1);
3379     }
3380     else if (input.matchnMove(1, "nrpn"))
3381     {
3382         command = CONFIG::control::enableNRPNs;
3383         value = (input.toggle() == 1);
3384     }
3385 
3386     else if (input.matchnMove(3, "lock"))
3387     {
3388         command = CONFIG::control::historyLock;
3389         value = (input.toggle());
3390         string name = string{input}.substr(0,2);
3391         int selected = stringNumInList(name, historyGroup, 2);
3392         if (selected == -1)
3393             return REPLY::range_msg;
3394         input.skipChars();
3395         value = (input.toggle());
3396         if (controlType == TOPLEVEL::type::Write && value == -1)
3397             return REPLY::value_msg;
3398         return sendDirect(synth, TOPLEVEL::action::lowPrio, value, controlType, command, TOPLEVEL::section::config, selected);
3399     }
3400 
3401     else
3402         return  REPLY::op_msg;
3403 
3404     sendDirect(synth, action, value, controlType, command, TOPLEVEL::section::config, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, miscmsg);
3405     return REPLY::done_msg;
3406 }
3407 
3408 
commandScale(Parser & input,unsigned char controlType)3409 int CmdInterpreter::commandScale(Parser& input, unsigned char controlType)
3410 {
3411     if (input.lineEnd(controlType))
3412         return REPLY::done_msg;
3413     Config &Runtime = synth->getRuntime();
3414     float value = 0;
3415     unsigned char command = UNUSED;
3416     unsigned char action = 0;
3417     unsigned char miscmsg = UNUSED;
3418 
3419     string name;
3420 
3421     if (input.matchnMove(1, "tuning"))
3422         command = SCALES::control::tuning;
3423     else if (input.matchnMove(1, "keymap"))
3424         command = SCALES::control::keyboardMap;
3425     else if (input.matchnMove(2, "name"))
3426         command = SCALES::control::name;
3427     else if (input.matchnMove(2, "description"))
3428         command = SCALES::control::comment;
3429 
3430     if (command >= SCALES::control::tuning && command <= SCALES::control::comment)
3431     {
3432         if (controlType != TOPLEVEL::type::Write && command <= SCALES::control::importKbm)
3433         {
3434             Runtime.Log("Write only - use 'list'");
3435             return REPLY::done_msg;
3436         }
3437         if (command <= SCALES::control::keyboardMap)
3438         {
3439             if (input.matchnMove(3, "import"))
3440                 command += (SCALES::control::importKbm - SCALES::control::keyboardMap);
3441         }
3442         name = string{input};
3443         if (name == "" && controlType == TOPLEVEL::type::Write)
3444             return REPLY::value_msg;
3445         action = TOPLEVEL::action::lowPrio;
3446         miscmsg = textMsgBuffer.push(name);
3447     }
3448     else
3449     {
3450         int min = 0;
3451         int max = 127;
3452         if (input.matchnMove(2, "frequency"))
3453         {
3454             command = SCALES::control::refFrequency;
3455             min = 1;
3456             max = 20000;
3457             controlType &= ~TOPLEVEL::type::Integer; // float
3458         }
3459         else if (input.matchnMove(2, "note"))
3460             command = SCALES::control::refNote;
3461         else if (input.matchnMove(1, "invert"))
3462         {
3463             command = SCALES::control::invertScale;
3464             max = 1;
3465         }
3466         else if (input.matchnMove(2, "center"))
3467             command = SCALES::control::invertedScaleCenter;
3468         else if (input.matchnMove(2, "shift"))
3469         {
3470             command = SCALES::control::scaleShift;
3471             min = -63;
3472             max = 64;
3473         }
3474         else if (input.matchnMove(2, "scale"))
3475         {
3476             command = SCALES::control::enableMicrotonal;
3477             max = 1;
3478         }
3479         else if (input.matchnMove(2, "mapping"))
3480         {
3481             command = SCALES::control::enableKeyboardMap;
3482             max = 1;
3483         }
3484         else if (input.matchnMove(2, "first"))
3485             command = SCALES::control::lowKey;
3486         else if (input.matchnMove(2, "middle"))
3487             command = SCALES::control::middleKey;
3488         else if (input.matchnMove(1, "last"))
3489             command = SCALES::control::highKey;
3490         else if (input.matchnMove(3, "CLEar"))
3491         {
3492             input.skip(-1); // sneaky way to force a zero :)
3493             command = SCALES::control::clearAll;
3494         }
3495         else
3496             return REPLY::todo_msg;
3497 
3498         if (controlType == TOPLEVEL::type::Write)
3499         {
3500             if (input.lineEnd(controlType))
3501                 return REPLY::value_msg;
3502             if ((input.toggle() == 1))
3503                 value = 1;
3504             else//if (input.isdigit())
3505             {
3506                 value = string2float(input);
3507                 if (value < min || value > max)
3508                     return REPLY::value_msg;
3509             }
3510         }
3511     }
3512     sendDirect(synth, action, value, controlType, command, TOPLEVEL::section::scales, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, miscmsg);
3513     return REPLY::done_msg;
3514 }
3515 
3516 
modulator(Parser & input,unsigned char controlType)3517 int CmdInterpreter::modulator(Parser& input, unsigned char controlType)
3518 {
3519     if (input.lineEnd(controlType))
3520         return REPLY::done_msg;
3521 
3522 // NOTE modulator number always the same as voice.
3523 
3524     int value;
3525     int cmd = -1;
3526     string name = string{input}.substr(0,3);
3527     value = stringNumInList(name, addmodnameslist, 3);
3528     if (value != -1)
3529         cmd = ADDVOICE::control::modulatorType;
3530 
3531     if (cmd == -1)
3532     {
3533         if (readControl(synth, 0, ADDVOICE::control::modulatorType, npart, kitNumber, PART::engine::addVoice1 + voiceNumber) == 0)
3534             return REPLY::inactive_msg;
3535         if (input.matchnMove(2, "waveform"))
3536         {
3537             bitSet(context, LEVEL::Oscillator);
3538             return waveform(input, controlType);
3539         }
3540 
3541         if (input.matchnMove(2, "source"))
3542         {
3543             if (input.matchnMove(1, "local"))
3544                 value = 0;
3545             else
3546             {
3547                 int tmp = input.peek() - char('0');
3548                 if (tmp > 0)
3549                     value = tmp;
3550             }
3551             if (value == -1 || value > voiceNumber)
3552                 return REPLY::range_msg;
3553             if (value == 0)
3554                 value = 0xff;
3555             else
3556                 value -= 1;
3557             cmd = ADDVOICE::control::externalModulator;
3558         }
3559 
3560         if (input.matchnMove(3, "oscillator"))
3561         {
3562             if (input.matchnMove(1, "internal"))
3563                 value = 0;
3564             else
3565             {
3566                 int tmp = input.peek() - char('0');
3567                 if (tmp > 0)
3568                     value = tmp;
3569             }
3570             if (value == -1 || value > voiceNumber)
3571                 return REPLY::range_msg;
3572             if (value == 0)
3573                 value = 0xff;
3574             else
3575                 value -= 1;
3576             cmd = ADDVOICE::control::modulatorOscillatorSource;
3577         }
3578 
3579         else if (input.matchnMove(3, "follow"))
3580         {
3581             value = (input.toggle() == 1);
3582             cmd = ADDVOICE::control::modulatorDetuneFromBaseOsc;
3583         }
3584         else if (input.matchnMove(3, "fixed"))
3585         {
3586             value = (input.toggle() == 1);
3587             cmd = ADDVOICE::control::modulatorFrequencyAs440Hz;
3588         }
3589 
3590         else if (input.matchnMove(1, "volume"))
3591             cmd = ADDVOICE::control::modulatorAmplitude;
3592         else if (input.matchnMove(2, "velocity"))
3593             cmd = ADDVOICE::control::modulatorVelocitySense;
3594         else if (input.matchnMove(2, "damping"))
3595             cmd = ADDVOICE::control::modulatorHFdamping;
3596     }
3597 
3598     if (cmd == -1)
3599     {
3600         if (readControl(synth, 0, ADDVOICE::control::externalModulator, npart, kitNumber, PART::engine::addVoice1 + voiceNumber) != -1)
3601             return  REPLY::inactive_msg;
3602 
3603         if (input.matchnMove(2, "local"))
3604         {
3605             if (input.matchnMove(1, "internal"))
3606                 value = 0;
3607             else
3608             {
3609                 int tmp = input.peek() - char('0');
3610                 if (tmp > 0)
3611                     value = tmp;
3612             }
3613             if (value == -1 || value > voiceNumber)
3614                 return REPLY::range_msg;
3615             if (value == 0)
3616                 value = 0xff;
3617             else
3618                 value -= 1;
3619             cmd = ADDVOICE::control::modulatorOscillatorSource;
3620         }
3621         if (input.matchnMove(2, "shift"))
3622             cmd = ADDVOICE::control::modulatorOscillatorPhase;
3623     }
3624 
3625     if (cmd == -1)
3626     {
3627         if (input.matchnMove(3, "detune"))
3628         {
3629             if (input.matchnMove(1, "fine"))
3630             {
3631                 if (input.lineEnd(controlType))
3632                     return REPLY::value_msg;
3633                 value = string2int(input);
3634                 cmd = ADDVOICE::control::modulatorDetuneFrequency;
3635             }
3636             else if (input.matchnMove(1, "coarse"))
3637             {
3638                 if (input.lineEnd(controlType))
3639                     return REPLY::value_msg;
3640                 value = string2int(input);
3641                 cmd = ADDVOICE::control::modulatorCoarseDetune;
3642             }
3643             else if (input.matchnMove(1, "type"))
3644             {
3645                 if (input.lineEnd(controlType))
3646                     return REPLY::value_msg;
3647                 if (controlType == type_read)
3648                     value = 2; // dummy value
3649                 else
3650                 {
3651                     string name = string{input}.substr(0,3);
3652                     value = stringNumInList(name, detuneType, 3);
3653                 }
3654                 if (value == -1)
3655                     return REPLY::range_msg;
3656                 cmd = ADDVOICE::control::modulatorDetuneType;
3657             }
3658         }
3659         else if (input.matchnMove(3, "octave"))
3660         {
3661             if (input.lineEnd(controlType))
3662                 return REPLY::value_msg;
3663             value = string2int(input);
3664             cmd = ADDVOICE::control::modulatorOctave;
3665         }
3666     }
3667 
3668     if (cmd > -1)
3669     {
3670         if (value == -1)
3671             value = string2int(input);
3672         else if (value == 0xff)
3673             value = -1; // special case for modulator sources
3674         return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, PART::engine::addVoice1 + voiceNumber);
3675     }
3676 
3677     if (input.matchnMove(3, "envelope"))
3678     {
3679         bitSet(context, LEVEL::Envelope);
3680         return envelopeSelect(input, controlType);
3681     }
3682 
3683     return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, PART::engine::addVoice1 + voiceNumber);
3684 }
3685 
3686 
addVoice(Parser & input,unsigned char controlType)3687 int CmdInterpreter::addVoice(Parser& input, unsigned char controlType)
3688 {
3689     if (input.isdigit())
3690     {
3691         int tmp = string2int(input) - 1;
3692         if (tmp < 0 || tmp >= NUM_VOICES)
3693             return REPLY::range_msg;
3694         voiceNumber = tmp;
3695         input.skipChars();
3696     }
3697     if (input.lineEnd(controlType))
3698         return REPLY::done_msg;
3699 
3700     int enable = (input.toggle());
3701     if (enable > -1)
3702         return sendNormal(synth, 0, enable, controlType, ADDVOICE::control::enableVoice, npart, kitNumber, PART::engine::addVoice1 + voiceNumber);
3703 
3704     if (!input.lineEnd(controlType) && !readControl(synth, 0, ADDVOICE::control::enableVoice, npart, kitNumber, PART::engine::addVoice1 + voiceNumber))
3705         return REPLY::inactive_msg;
3706 
3707     if (input.matchnMove(2, "modulator"))
3708     {
3709         bitSet(context, LEVEL::AddMod);
3710         return modulator(input, controlType);
3711     }
3712     else if (input.matchnMove(2, "waveform"))
3713     {
3714         bitSet(context, LEVEL::Oscillator);
3715         return waveform(input, controlType);
3716     }
3717 
3718     int cmd = -1;
3719     int tmp = -1;
3720     if (input.matchnMove(1, "volume"))
3721         cmd = ADDVOICE::control::volume;
3722     else if (input.matchnMove(1, "pan"))
3723         cmd = ADDVOICE::control::panning;
3724     else if (input.matchnMove(2, "prandom"))
3725     {
3726         cmd = ADDVOICE::control::enableRandomPan;
3727         tmp = (input.toggle() == 1);
3728     }
3729     else if (input.matchnMove(2, "pwidth"))
3730         cmd = ADDVOICE::control::randomWidth;
3731 
3732     else if (input.matchnMove(2, "velocity"))
3733         cmd = ADDVOICE::control::velocitySense;
3734 
3735     if (cmd != -1)
3736     {
3737         if (tmp == -1)
3738         {
3739             tmp = string2int127(input);
3740             if (controlType == TOPLEVEL::type::Write && input.isAtEnd())
3741                 return REPLY::value_msg;
3742         }
3743         return sendNormal(synth, 0, tmp, controlType, cmd, npart, kitNumber, PART::engine::addVoice1 + voiceNumber);
3744     }
3745 
3746     int value = 0;
3747     if (input.matchnMove(3, "detune"))
3748     {
3749         if (input.matchnMove(1, "fine"))
3750         {
3751             if (input.lineEnd(controlType))
3752                 return REPLY::value_msg;
3753             value = string2int(input);
3754             cmd = ADDVOICE::control::detuneFrequency;
3755         }
3756         else if (input.matchnMove(1, "coarse"))
3757         {
3758             if (input.lineEnd(controlType))
3759                 return REPLY::value_msg;
3760             value = string2int(input);
3761             cmd = ADDVOICE::control::coarseDetune;
3762         }
3763         else if (input.matchnMove(1, "type"))
3764         {
3765             if (input.lineEnd(controlType))
3766                 return REPLY::value_msg;
3767             if (controlType == type_read)
3768                 value = 2; // dummy value
3769             else
3770             {
3771                 string name = string{input}.substr(0,3);
3772                 value = stringNumInList(name, detuneType, 3);
3773             }
3774             if (value == -1)
3775                 return REPLY::range_msg;
3776             cmd = ADDVOICE::control::detuneType;
3777         }
3778     }
3779     else if (input.matchnMove(3, "fixed"))
3780     {
3781         value = (input.toggle() == 1);
3782         cmd = ADDVOICE::control::baseFrequencyAs440Hz;
3783     }
3784     else if (input.matchnMove(3, "octave"))
3785     {
3786         if (input.lineEnd(controlType))
3787                 return REPLY::value_msg;
3788         value = string2int(input);
3789         cmd = ADDVOICE::control::octave;
3790     }
3791 
3792     else
3793     {
3794         int tmp_cmd = -1;
3795         if (input.matchnMove(3, "equal"))
3796             tmp_cmd = ADDVOICE::control::equalTemperVariation;
3797         else if (input.matchnMove(3, "bend"))
3798         {
3799             if (input.matchnMove(1, "adjust"))
3800                 tmp_cmd = ADDVOICE::control::pitchBendAdjustment;
3801             else if (input.matchnMove(1, "offset"))
3802                 tmp_cmd = ADDVOICE::control::pitchBendOffset;
3803         }
3804         if (tmp_cmd > -1)
3805         {
3806             if (input.lineEnd(controlType))
3807                 return REPLY::value_msg;
3808             value = string2int(input);
3809             cmd = tmp_cmd;
3810         }
3811     }
3812 
3813     if (cmd > -1)
3814         return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, PART::engine::addVoice1 + voiceNumber);
3815 
3816     if (input.matchnMove(3, "lfo"))
3817     {
3818         bitSet(context, LEVEL::LFO);
3819         return LFOselect(input, controlType);
3820     }
3821     if (input.matchnMove(3, "filter"))
3822     {
3823         bitSet(context, LEVEL::Filter);
3824         return filterSelect(input, controlType);
3825     }
3826     if (input.matchnMove(3, "envelope"))
3827     {
3828         bitSet(context, LEVEL::Envelope);
3829         return envelopeSelect(input, controlType);
3830     }
3831 
3832     value = -1;
3833     if (input.matchnMove(1, "type"))
3834     {
3835         if (input.matchnMove(1, "oscillator"))
3836             value = 0;
3837         else if (input.matchnMove(1, "white"))
3838             value = 1;
3839         else if (input.matchnMove(1, "pink"))
3840             value = 2;
3841         else if (input.matchnMove(1, "spot"))
3842             value = 3;
3843         else
3844             return REPLY::range_msg;
3845         cmd = ADDVOICE::control::soundType;
3846     }
3847     else if (input.matchnMove(3, "oscillator"))
3848     {
3849         if (input.matchnMove(1, "internal"))
3850             value = 0;
3851         else
3852         {
3853             int tmp = input.peek() - char('0');
3854             if (tmp > 0)
3855                 value = tmp;
3856         }
3857         if (value == -1 || value > voiceNumber)
3858             return REPLY::range_msg;
3859         if (value == 0)
3860             value = 0xff;
3861         else
3862             value -= 1;
3863         cmd = ADDVOICE::control::voiceOscillatorSource;
3864     }
3865     else if (input.matchnMove(3, "source"))
3866     {
3867         if (input.matchnMove(1, "local"))
3868             value = 0;
3869         else
3870         {
3871             int tmp = input.peek() - char('0');
3872             if (tmp > 0)
3873                 value = tmp;
3874         }
3875         if (value == -1 || value > voiceNumber)
3876             return REPLY::range_msg;
3877         if (value == 0)
3878             value = 0xff;
3879         else
3880             value -= 1;
3881         cmd = ADDVOICE::control::externalOscillator;
3882     }
3883     else if (input.matchnMove(1, "phase"))
3884         cmd = ADDVOICE::control::voiceOscillatorPhase;
3885     else if (input.matchnMove(1, "minus"))
3886     {
3887         value = (input.toggle() == 1);
3888         cmd = ADDVOICE::control::invertPhase;
3889     }
3890     else if (input.matchnMove(3, "delay"))
3891         cmd = ADDVOICE::control::delay;
3892     else if (input.matchnMove(1, "resonance"))
3893     {
3894         value = (input.toggle() == 1);
3895         cmd = ADDVOICE::control::enableResonance;
3896     }
3897     else if (input.matchnMove(2, "bypass"))
3898     {
3899         value = (input.toggle() == 1);
3900         cmd = ADDVOICE::control::bypassGlobalFilter;
3901     }
3902     else if (input.matchnMove(1, "unison"))
3903     {
3904         value = input.toggle();
3905         if (value > -1)
3906             cmd = ADDVOICE::control::enableUnison;
3907         else
3908         {
3909             if (input.matchnMove(1, "size"))
3910                 cmd = ADDVOICE::control::unisonSize;
3911             else if (input.matchnMove(1, "frequency"))
3912                 cmd = ADDVOICE::control::unisonFrequencySpread;
3913             else if (input.matchnMove(1, "phase"))
3914                 cmd = ADDVOICE::control::unisonPhaseRandomise;
3915             else if (input.matchnMove(1, "width"))
3916                 cmd = ADDVOICE::control::unisonStereoSpread;
3917             else if (input.matchnMove(1, "vibrato"))
3918                 cmd = ADDVOICE::control::unisonVibratoDepth;
3919             else if (input.matchnMove(1, "rate"))
3920                 cmd = ADDVOICE::control::unisonVibratoSpeed;
3921             else if (input.matchnMove(1, "invert"))
3922             {
3923                 if (controlType == type_read)
3924                     value = 1; // dummy value
3925                 else
3926                 {
3927                     value = stringNumInList(string{input}.substr(0, 1), unisonPhase, 1);
3928                     if (value == -1)
3929                         return REPLY::range_msg;
3930                 }
3931                     cmd = ADDVOICE::control::unisonPhaseInvert;
3932             }
3933         }
3934         if (cmd == -1)
3935             return REPLY::op_msg;
3936     }
3937     else
3938         return REPLY::op_msg;
3939 
3940     if (value == -1)
3941         value = string2int(input);
3942     else if (value == 0xff)
3943             value = -1; // special case for voice and oscillator sources
3944     return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, PART::engine::addVoice1 + voiceNumber);
3945 }
3946 
3947 
addSynth(Parser & input,unsigned char controlType)3948 int CmdInterpreter::addSynth(Parser& input, unsigned char controlType)
3949 {
3950     int kit = UNUSED;
3951     int insert = UNUSED;
3952     if (kitMode)
3953     {
3954         kit = kitNumber;
3955         insert = TOPLEVEL::insert::kitGroup;
3956     }
3957     int enable = (input.toggle());
3958     // This is a part command, but looks like AddSynth the the CLI user
3959     if (enable > -1)
3960         sendNormal(synth, 0, enable, controlType, PART::control::enableAdd, npart, kit, UNUSED, insert);
3961 
3962     if (input.lineEnd(controlType))
3963         return REPLY::done_msg;
3964 
3965     if (!readControl(synth, 0, PART::control::enable, npart, kit, PART::engine::addSynth, insert))
3966         return REPLY::inactive_msg;
3967 
3968     if (input.matchnMove(2, "resonance"))
3969     {
3970         bitSet(context, LEVEL::Resonance);
3971         return resonance(input, controlType);
3972     }
3973     if (input.matchnMove(3, "voice"))
3974     {
3975         bitSet(context, LEVEL::AddVoice);
3976         // starting point for envelopes etc.
3977         insertType = TOPLEVEL::insertType::amplitude;
3978         return addVoice(input, controlType);
3979     }
3980     if (input.lineEnd(controlType))
3981         return REPLY::done_msg;
3982 
3983     int cmd = -1;
3984     int tmp = -1;
3985     if (input.matchnMove(1, "volume"))
3986         cmd = ADDSYNTH::control::volume;
3987     else if (input.matchnMove(1, "pan"))
3988         cmd = ADDSYNTH::control::panning;
3989     else if (input.matchnMove(2, "prandom"))
3990     {
3991         cmd = ADDSYNTH::control::enableRandomPan;
3992         tmp = (input.toggle() == 1);
3993     }
3994     else if (input.matchnMove(2, "pwidth"))
3995         cmd = ADDSYNTH::control::randomWidth;
3996     else if (input.matchnMove(2, "velocity"))
3997         cmd = ADDSYNTH::control::velocitySense;
3998     if (cmd != -1)
3999     {
4000         if (tmp == -1)
4001         {
4002             if (controlType == TOPLEVEL::type::Write && input.isAtEnd())
4003                 return REPLY::value_msg;
4004             tmp = string2int127(input);
4005         }
4006 
4007         return sendNormal(synth, 0, tmp, controlType, cmd, npart, kitNumber, PART::engine::addSynth);
4008     }
4009 
4010     int value = 0;
4011     if (input.matchnMove(3, "detune"))
4012     {
4013         if (input.matchnMove(1, "fine"))
4014         {
4015             if (input.lineEnd(controlType))
4016                 return REPLY::value_msg;
4017             value = string2int(input);
4018             cmd = ADDSYNTH::control::detuneFrequency;
4019         }
4020         else if (input.matchnMove(1, "coarse"))
4021         {
4022             if (input.lineEnd(controlType))
4023                 return REPLY::value_msg;
4024             value = string2int(input);
4025             cmd = ADDSYNTH::control::coarseDetune;
4026         }
4027         else if (input.matchnMove(1, "type"))
4028         {
4029             if (input.lineEnd(controlType))
4030                 return REPLY::value_msg;
4031             if (controlType == type_read)
4032                 value = 2; // dummy value
4033             else
4034             {
4035                 string name = string{input}.substr(0,3);
4036                 value = stringNumInList(name, detuneType, 3);
4037             }
4038             if (value == -1)
4039                 return REPLY::range_msg;
4040             cmd = ADDSYNTH::control::detuneType;
4041         }
4042     }
4043     else if (input.matchnMove(3, "octave"))
4044     {
4045         if (input.lineEnd(controlType))
4046             return REPLY::value_msg;
4047         value = string2int(input);
4048         cmd = ADDSYNTH::control::octave;
4049     }
4050     else if (input.matchnMove(3, "stereo"))
4051     {
4052         cmd = ADDSYNTH::control::stereo;
4053         value = (input.toggle() == 1);
4054     }
4055     else
4056     {
4057         int tmp_cmd = -1;
4058         if (input.matchnMove(3, "depop"))
4059             tmp_cmd = ADDSYNTH::control::dePop;
4060         else if (input.matchnMove(2, "punch"))
4061         {
4062             if (input.matchnMove(1, "power"))
4063                 tmp_cmd = ADDSYNTH::control::punchStrength;
4064             else if (input.matchnMove(1, "duration"))
4065                 tmp_cmd = ADDSYNTH::control::punchDuration;
4066             else if (input.matchnMove(1, "stretch"))
4067                 tmp_cmd = ADDSYNTH::control::punchStretch;
4068             else if (input.matchnMove(1, "velocity"))
4069                 tmp_cmd = ADDSYNTH::control::punchVelocity;
4070         }
4071         if (tmp_cmd > -1)
4072         {
4073             if (input.lineEnd(controlType))
4074                 return REPLY::value_msg;
4075             value = string2int(input);
4076             cmd = tmp_cmd;
4077         }
4078     }
4079 
4080     if (cmd > -1)
4081         return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, PART::engine::addSynth);
4082 
4083     if (input.matchnMove(3, "lfo"))
4084     {
4085         bitSet(context, LEVEL::LFO);
4086         return LFOselect(input, controlType);
4087     }
4088     if (input.matchnMove(3, "filter"))
4089     {
4090         bitSet(context, LEVEL::Filter);
4091         return filterSelect(input, controlType);
4092     }
4093     if (input.matchnMove(3, "envelope"))
4094     {
4095         bitSet(context, LEVEL::Envelope);
4096         return envelopeSelect(input, controlType);
4097     }
4098 
4099     if (input.matchnMove(2, "bandwidth"))
4100     {
4101         if (input.lineEnd(controlType))
4102             return REPLY::value_msg;
4103         value = string2int(input);
4104         cmd = ADDSYNTH::control::relativeBandwidth;
4105     }
4106     else if (input.matchnMove(2, "group"))
4107     {
4108         if (input.lineEnd(controlType))
4109             return REPLY::value_msg;
4110         value = (input.toggle() == 1);
4111         cmd = ADDSYNTH::control::randomGroup;
4112     }
4113     if (cmd == -1)
4114         return REPLY::available_msg;
4115 
4116     return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, PART::engine::addSynth);
4117 }
4118 
4119 
subSynth(Parser & input,unsigned char controlType)4120 int CmdInterpreter::subSynth(Parser& input, unsigned char controlType)
4121 {
4122     int kit = UNUSED;
4123     int insert = UNUSED;
4124     if (kitMode)
4125     {
4126         kit = kitNumber;
4127         insert = TOPLEVEL::insert::kitGroup;
4128     }
4129     int enable = (input.toggle());
4130     // This is a part command, but looks like SubSynth the the CLI user
4131     if (enable > -1)
4132         sendNormal(synth, 0, enable, controlType, PART::control::enableSub, npart, kit, UNUSED, insert);
4133 
4134     if (input.lineEnd(controlType))
4135         return REPLY::done_msg;
4136 
4137     if (!readControl(synth, 0, PART::control::enable, npart, kit, PART::engine::subSynth, insert))
4138         return REPLY::inactive_msg;
4139 
4140     int cmd = -1;
4141     int tmp = -1;
4142     if (input.matchnMove(1, "volume"))
4143         cmd = SUBSYNTH::control::volume;
4144     else if (input.matchnMove(1, "pan"))
4145         cmd = SUBSYNTH::control::panning;
4146     else if (input.matchnMove(2, "prandom"))
4147     {
4148         cmd = SUBSYNTH::control::enableRandomPan;
4149         tmp = (input.toggle() == 1);
4150     }
4151     else if (input.matchnMove(2, "pwidth"))
4152         cmd = SUBSYNTH::control::randomWidth;
4153 
4154     else if (input.matchnMove(2, "velocity"))
4155         cmd = SUBSYNTH::control::velocitySense;
4156     if (cmd != -1)
4157     {
4158         if (tmp == -1)
4159         {
4160             tmp = string2int127(input);
4161             if (controlType == TOPLEVEL::type::Write && input.isAtEnd())
4162                 return REPLY::value_msg;
4163         }
4164         return sendNormal(synth, 0, tmp, controlType, cmd, npart, kitNumber, PART::engine::subSynth);
4165     }
4166 
4167     int value = 0;
4168     if (input.matchnMove(3, "detune"))
4169     {
4170         if (input.matchnMove(1, "fine"))
4171         {
4172             if (input.lineEnd(controlType))
4173                 return REPLY::value_msg;
4174             value = string2int(input);
4175             cmd = SUBSYNTH::control::detuneFrequency;
4176         }
4177         else if (input.matchnMove(1, "coarse"))
4178         {
4179             if (input.lineEnd(controlType))
4180                 return REPLY::value_msg;
4181             value = string2int(input);
4182             cmd = SUBSYNTH::control::coarseDetune;
4183         }
4184         else if (input.matchnMove(1, "type"))
4185         {
4186             if (input.lineEnd(controlType))
4187                 return REPLY::value_msg;
4188             if (controlType == type_read)
4189                 value = 2; // dummy value
4190             else
4191             {
4192                 string name = string{input}.substr(0,3);
4193                 value = stringNumInList(name, detuneType, 3);
4194             }
4195             if (value == -1)
4196                 return REPLY::range_msg;
4197             cmd = SUBSYNTH::control::detuneType;
4198         }
4199     }
4200     else if (input.matchnMove(3, "fixed"))
4201     {
4202         value = (input.toggle() == 1);
4203         cmd = SUBSYNTH::control::baseFrequencyAs440Hz;
4204     }
4205     else if (input.matchnMove(3, "octave"))
4206     {
4207         if (input.lineEnd(controlType))
4208             return REPLY::value_msg;
4209         value = string2int(input);
4210         cmd = SUBSYNTH::control::octave;
4211     }
4212     else if (input.matchnMove(3, "stereo"))
4213     {
4214         cmd = SUBSYNTH::control::stereo;
4215         value = (input.toggle() == 1);
4216     }
4217 
4218     else
4219     {
4220         int tmp_cmd = -1;
4221         if (input.matchnMove(3, "equal"))
4222             tmp_cmd = SUBSYNTH::control::equalTemperVariation;
4223         else if (input.matchnMove(3, "bend"))
4224         {
4225             if (input.matchnMove(1, "adjust"))
4226                 tmp_cmd = SUBSYNTH::control::pitchBendAdjustment;
4227             else if (input.matchnMove(1, "offset"))
4228                 tmp_cmd = SUBSYNTH::control::pitchBendOffset;
4229         }
4230         if (tmp_cmd > -1)
4231         {
4232             if (input.lineEnd(controlType))
4233                 return REPLY::value_msg;
4234             value = string2int(input);
4235             cmd = tmp_cmd;
4236         }
4237     }
4238 
4239     if (cmd == -1 && input.matchnMove(3, "filter"))
4240     {
4241         bitSet(context, LEVEL::Filter);
4242         return filterSelect(input, controlType);
4243     }
4244     if (cmd == -1 && input.matchnMove(3, "envelope"))
4245     {
4246         bitSet(context, LEVEL::Envelope);
4247         return envelopeSelect(input, controlType);
4248     }
4249 
4250     if (cmd > -1)
4251     {
4252         return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, PART::engine::subSynth);
4253     }
4254 
4255     value = -1;
4256     if (input.matchnMove(2, "overtone"))
4257     {
4258         if (input.matchnMove(1, "Position"))
4259         {
4260             if (controlType == type_read)
4261                 value = 1; // dummy value
4262             else
4263             {
4264                 value = stringNumInList(string{input}.substr(0, 2), subPadPosition, 2);
4265                 if (value == -1)
4266                     return REPLY::range_msg;
4267             }
4268             cmd = SUBSYNTH::control::overtonePosition;
4269         }
4270         else
4271         {
4272             if (input.matchnMove(1, "First"))
4273                 cmd = SUBSYNTH::control::overtoneParameter1;
4274             else if (input.matchnMove(1, "Second"))
4275                 cmd = SUBSYNTH::control::overtoneParameter2;
4276             else if (input.matchnMove(1, "Harmonic"))
4277                 cmd = SUBSYNTH::control::overtoneForceHarmonics;
4278             if (cmd > -1)
4279             {
4280                 if (input.lineEnd(controlType))
4281                     return REPLY::value_msg;
4282                 value = string2int(input);
4283             }
4284         }
4285     }
4286 
4287     if (cmd > -1)
4288 
4289         return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, PART::engine::subSynth);
4290 
4291     if (input.matchnMove(2, "harmonic"))
4292     {
4293         int value = -1;
4294         if (input.matchnMove(1, "stages"))
4295         {
4296             cmd = SUBSYNTH::control::filterStages;
4297             value = string2int(input);
4298         }
4299         else if (input.matchnMove(1, "mag"))
4300         {
4301             cmd = SUBSYNTH::control::magType;
4302             if (controlType == TOPLEVEL::type::Write)
4303             {
4304                 string name = string{input}.substr(0, 2);
4305                 value = stringNumInList(name, subMagType, 2);
4306             }
4307         }
4308         else if (input.matchnMove(1, "position"))
4309         {
4310             cmd = SUBSYNTH::control::startPosition;
4311             if (input.matchnMove(1, "Zero"))
4312                 value = 0;
4313             else if (input.matchnMove(1, "Random"))
4314                 value = 1;
4315             else if (input.matchnMove(1, "Maximum"))
4316                 value = 2;
4317         }
4318         if (cmd != -1)
4319         {
4320             if (value < 0 && controlType == TOPLEVEL::type::Write)
4321                 return REPLY::value_msg;
4322             return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, PART::engine::subSynth);
4323         }
4324 
4325         int control = -1;
4326         unsigned char insert = UNUSED;
4327         bool set = false;
4328         if (input.lineEnd(controlType))
4329             return REPLY::parameter_msg;
4330         control = string2int(input) - 1;
4331         input.skipChars();
4332         if (input.matchnMove(1, "amplitude"))
4333         {
4334             insert = TOPLEVEL::insert::harmonicAmplitude;
4335             set = true;
4336         }
4337         else if (input.matchnMove(1, "bandwidth"))
4338         {
4339             insert = TOPLEVEL::insert::harmonicPhaseBandwidth;
4340             set = true;
4341         }
4342         if (set)
4343         {
4344             if (input.lineEnd(controlType))
4345                 return REPLY::value_msg;
4346             return sendNormal(synth, 0, string2int(input), controlType, control, npart, kitNumber, PART::engine::subSynth, insert);
4347         }
4348     }
4349 
4350     value = -1;
4351     if (cmd == -1)
4352     {
4353         if (input.matchnMove(2, "band"))
4354         {
4355             if (input.matchnMove(1, "width"))
4356                 cmd = SUBSYNTH::control::bandwidth;
4357             else if (input.matchnMove(1, "scale"))
4358                 cmd = SUBSYNTH::control::bandwidthScale;
4359             else if (input.matchnMove(1, "envelope"))
4360             {
4361                 value = (input.toggle() == 1);
4362                 cmd = SUBSYNTH::control::enableBandwidthEnvelope;
4363             }
4364         }
4365         else if (input.matchnMove(2, "frequency"))
4366         {
4367             if (input.matchnMove(1, "envelope"))
4368             {
4369                 value = (input.toggle() == 1);
4370                 cmd = SUBSYNTH::control::enableFrequencyEnvelope;
4371             }
4372         }
4373         else if (input.matchnMove(2, "filter"))
4374         {
4375             value = (input.toggle() == 1);
4376             cmd = SUBSYNTH::control::enableFilter;
4377         }
4378 
4379     }
4380 
4381     if (cmd != -1)
4382     {
4383         //cout << "control " << int(cmd) << "  part " << int(npart) << "  kit " << int(kitNumber) << "  engine " << int(PART::engine::subSynth) << endl;
4384         if (value == -1)
4385         {
4386             if (input.lineEnd(controlType))
4387                 return REPLY::value_msg;
4388             value = string2int(input);
4389         }
4390         return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, PART::engine::subSynth);
4391     }
4392     return REPLY::available_msg;
4393 }
4394 
4395 
padSynth(Parser & input,unsigned char controlType)4396 int CmdInterpreter::padSynth(Parser& input, unsigned char controlType)
4397 {
4398     int kit = UNUSED;
4399     int insert = UNUSED;
4400     if (kitMode)
4401     {
4402         kit = kitNumber;
4403         insert = TOPLEVEL::insert::kitGroup;
4404     }
4405     int enable = (input.toggle());
4406     // This is a part command, but looks like PadSynth the the CLI user
4407     if (enable > -1)
4408         sendNormal(synth, 0, enable, controlType, PART::control::enablePad, npart, kit, UNUSED, insert);
4409 
4410     if (input.lineEnd(controlType))
4411         return REPLY::done_msg;
4412 
4413     if (!readControl(synth, 0, PART::control::enable, npart, kit, PART::engine::padSynth, insert))
4414         return REPLY::inactive_msg;
4415 
4416     if (input.matchnMove(2, "resonance"))
4417     {
4418         bitSet(context, LEVEL::Resonance);
4419         return resonance(input, controlType);
4420     }
4421     if (input.matchnMove(2, "waveform"))
4422     {
4423         bitSet(context, LEVEL::Oscillator);
4424         return waveform(input, controlType);
4425     }
4426 
4427     int cmd = -1;
4428     int tmp = -1;
4429     if (input.matchnMove(1, "volume"))
4430         cmd = PADSYNTH::control::volume;
4431     else if (input.matchnMove(1, "pan"))
4432         cmd = PADSYNTH::control::panning;
4433     else if (input.matchnMove(2, "prandom"))
4434     {
4435         cmd = SUBSYNTH::control::enableRandomPan;
4436         tmp = (input.toggle() == 1);
4437     }
4438     else if (input.matchnMove(2, "pwidth"))
4439         cmd = SUBSYNTH::control::randomWidth;
4440 
4441     else if (input.matchnMove(2, "velocity"))
4442         cmd = PADSYNTH::control::velocitySense;
4443     if (cmd != -1)
4444     {
4445         if (tmp == -1)
4446         {
4447             tmp = string2int127(input);
4448             if (controlType == TOPLEVEL::type::Write && input.isAtEnd())
4449                 return REPLY::value_msg;
4450         }
4451         return sendNormal(synth, 0, tmp, controlType, cmd, npart, kitNumber, PART::engine::padSynth);
4452     }
4453 
4454     int value = 0;
4455     if (input.matchnMove(3, "detune"))
4456     {
4457         if (input.matchnMove(1, "fine"))
4458         {
4459             if (input.lineEnd(controlType))
4460                 return REPLY::value_msg;
4461             value = string2int(input);
4462             cmd = PADSYNTH::control::detuneFrequency;
4463         }
4464         else if (input.matchnMove(1, "coarse"))
4465         {
4466             if (input.lineEnd(controlType))
4467                 return REPLY::value_msg;
4468             value = string2int(input);
4469             cmd = PADSYNTH::control::coarseDetune;
4470         }
4471         else if (input.matchnMove(1, "type"))
4472         {
4473             if (input.lineEnd(controlType))
4474                 return REPLY::value_msg;
4475             if (controlType == type_read)
4476                 value = 2; // dummy value
4477             else
4478             {
4479                 string name = string{input}.substr(0,3);
4480                 value = stringNumInList(name, detuneType, 3);
4481             }
4482             if (value == -1)
4483                 return REPLY::range_msg;
4484             cmd = PADSYNTH::control::detuneType;
4485         }
4486     }
4487     else if (input.matchnMove(3, "fixed"))
4488     {
4489         value = (input.toggle() == 1);
4490         cmd = PADSYNTH::control::baseFrequencyAs440Hz;
4491     }
4492     else if (input.matchnMove(3, "octave"))
4493     {
4494         if (input.lineEnd(controlType))
4495             return REPLY::value_msg;
4496         value = string2int(input);
4497         cmd = PADSYNTH::control::octave;
4498     }
4499     else if (input.matchnMove(3, "stereo"))
4500     {
4501         cmd = PADSYNTH::control::stereo;
4502         value = (input.toggle() == 1);
4503     }
4504 
4505     else
4506     {
4507         int tmp_cmd = -1;
4508         if (input.matchnMove(3, "equal"))
4509             tmp_cmd = PADSYNTH::control::equalTemperVariation;
4510         else if (input.matchnMove(3, "bend"))
4511         {
4512             if (input.matchnMove(1, "adjust"))
4513                 tmp_cmd = PADSYNTH::control::pitchBendAdjustment;
4514             else if (input.matchnMove(1, "offset"))
4515                 tmp_cmd = PADSYNTH::control::pitchBendOffset;
4516         }
4517         if (tmp_cmd > -1)
4518         {
4519             if (input.lineEnd(controlType))
4520                 return REPLY::value_msg;
4521             value = string2int(input);
4522             cmd = tmp_cmd;
4523         }
4524     }
4525 
4526     if (cmd > -1)
4527         return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, PART::engine::padSynth);
4528 
4529     if (input.matchnMove(3, "lfo"))
4530     {
4531         bitSet(context, LEVEL::LFO);
4532         return LFOselect(input, controlType);
4533     }
4534     if (input.matchnMove(3, "filter"))
4535     {
4536         bitSet(context, LEVEL::Filter);
4537         return filterSelect(input, controlType);
4538     }
4539     if (input.matchnMove(3, "envelope"))
4540     {
4541         bitSet(context, LEVEL::Envelope);
4542         return envelopeSelect(input, controlType);
4543     }
4544 
4545     value = -1;
4546     if (input.matchnMove(2, "overtone"))
4547     {
4548         if (input.matchnMove(1, "Position"))
4549         {
4550             if (controlType == type_read)
4551                 value = 1; // dummy value
4552             else
4553             {
4554                 value = stringNumInList(string{input}.substr(0, 2), subPadPosition, 2);
4555                 if (value == -1)
4556                     return REPLY::range_msg;
4557             }
4558             cmd = PADSYNTH::control::overtonePosition;
4559         }
4560         else
4561         {
4562             if (input.matchnMove(1, "First"))
4563                 cmd = PADSYNTH::control::overtoneParameter1;
4564             else if (input.matchnMove(1, "Second"))
4565                 cmd = PADSYNTH::control::overtoneParameter2;
4566             else if (input.matchnMove(1, "Harmonic"))
4567                 cmd = PADSYNTH::control::overtoneForceHarmonics;
4568             if (cmd > -1)
4569             {
4570                 if (input.lineEnd(controlType))
4571                     return REPLY::value_msg;
4572                 value = string2int(input);
4573             }
4574         }
4575     }
4576 
4577     else
4578     {
4579         int tmp_cmd = -1;
4580         if (input.matchnMove(3, "depop"))
4581             tmp_cmd = PADSYNTH::control::dePop;
4582         else if (input.matchnMove(2, "punch"))
4583         {
4584             if (input.matchnMove(1, "power"))
4585                 tmp_cmd = PADSYNTH::control::punchStrength;
4586             else if (input.matchnMove(1, "duration"))
4587                 tmp_cmd = PADSYNTH::control::punchDuration;
4588             else if (input.matchnMove(1, "stretch"))
4589                 tmp_cmd = PADSYNTH::control::punchStretch;
4590             else if (input.matchnMove(1, "velocity"))
4591                 tmp_cmd = PADSYNTH::control::punchVelocity;
4592         }
4593         if (tmp_cmd > -1)
4594         {
4595             if (input.lineEnd(controlType))
4596                 return REPLY::value_msg;
4597             value = string2int(input);
4598             cmd = tmp_cmd;
4599         }
4600     }
4601 
4602     if (cmd > -1)
4603         return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, PART::engine::padSynth);
4604 
4605     if (input.matchnMove(2, "xport"))
4606     {
4607         if (controlType != TOPLEVEL::type::Write)
4608             return REPLY::writeOnly_msg;
4609         if (input.isAtEnd())
4610             return REPLY::value_msg;
4611         sendDirect(synth, TOPLEVEL::action::lowPrio, 0, controlType, MAIN::control::exportPadSynthSamples, TOPLEVEL::section::main, kitNumber, 2, npart, UNUSED, UNUSED, textMsgBuffer.push(input));
4612         return REPLY::done_msg;
4613     }
4614 
4615     value = -1;
4616     if (input.matchnMove(2, "profile"))
4617     {
4618         if (input.matchnMove(1, "gauss"))
4619             value = 0;
4620         else if (input.matchnMove(1, "square"))
4621             value = 1;
4622         else if (input.matchnMove(1, "double"))
4623             value = 2;
4624         else
4625             return REPLY::value_msg;
4626 
4627         cmd = PADSYNTH::control::baseType;
4628     }
4629     else if (input.matchnMove(2, "width"))
4630     {
4631         cmd = PADSYNTH::control::baseWidth;
4632     }
4633     else if (input.matchnMove(2, "count"))
4634     {
4635         cmd = PADSYNTH::control::frequencyMultiplier;
4636     }
4637     else if (input.matchnMove(2, "expand"))
4638     {
4639         cmd = PADSYNTH::control::modulatorStretch;
4640     }
4641     else if (input.matchnMove(2, "frequency"))
4642     {
4643         cmd = PADSYNTH::control::modulatorFrequency;
4644     }
4645     else if (input.matchnMove(2, "size"))
4646     {
4647         cmd = PADSYNTH::control::size;
4648     }
4649     else if (input.matchnMove(2, "cross"))
4650     {
4651         if (input.matchnMove(1, "full"))
4652             value = 0;
4653         else if (input.matchnMove(1, "upper"))
4654             value = 1;
4655         else if (input.matchnMove(1, "lower"))
4656             value = 2;
4657         else
4658             return REPLY::value_msg;
4659 
4660         cmd = PADSYNTH::control::harmonicSidebands;
4661     }
4662     else if (input.matchnMove(2, "multiplier"))
4663     {
4664         if (input.matchnMove(1, "off"))
4665             value = 0;
4666         else if (input.matchnMove(1, "gauss"))
4667             value = 1;
4668         else if (input.matchnMove(1, "sine"))
4669             value = 2;
4670         else if (input.matchnMove(1, "double"))
4671             value = 3;
4672         else
4673             return REPLY::value_msg;
4674 
4675         cmd = PADSYNTH::control::amplitudeMultiplier;
4676     }
4677     else if (input.matchnMove(2, "mode"))
4678     {
4679         if (input.matchnMove(1, "Sum"))
4680             value = 0;
4681         else if (input.matchnMove(1, "mult"))
4682             value = 1;
4683         else if (input.matchnMove(1, "d1"))
4684             value = 2;
4685         else if (input.matchnMove(1, "d2"))
4686             value = 3;
4687         else
4688             return REPLY::value_msg;
4689 
4690         cmd = PADSYNTH::control::amplitudeMode;
4691     }
4692     else if (input.matchnMove(2, "center"))
4693     {
4694         cmd = PADSYNTH::control::spectralWidth;
4695     }
4696     else if (input.matchnMove(3, "relative"))
4697     {
4698         cmd = PADSYNTH::control::spectralAmplitude;
4699     }
4700     else if (input.matchnMove(2, "auto"))
4701     {
4702         value = (input.toggle() > 0);
4703         cmd = PADSYNTH::control::autoscale;
4704     }
4705     else if (input.matchnMove(3, "base"))
4706     {
4707         for (int i = 0; i < 9; ++ i)
4708         {
4709             if (basetypes[i] == string{input})
4710             {
4711                 value = i;
4712                 cmd = PADSYNTH::control::harmonicBase;
4713                 break;
4714             }
4715         }
4716         if (cmd == -1)
4717             return REPLY::range_msg;
4718     }
4719     else if (input.matchnMove(2, "samples"))
4720     {
4721         unsigned char sizes[] {1, 2, 4, 6, 8, 12, 24};
4722         value = string2float(input);
4723         int tmp = value * 2;
4724         for (int i = 0; i < 7; ++i)
4725         {
4726             if (tmp == sizes[i])
4727             {
4728                 value = i;
4729                 cmd = PADSYNTH::control::samplesPerOctave;
4730                 break;
4731             }
4732         }
4733         if (cmd == -1)
4734             return REPLY::range_msg;
4735     }
4736     else if (input.matchnMove(2, "range"))
4737     {
4738         cmd = PADSYNTH::control::numberOfOctaves;
4739     }
4740     else if (input.matchnMove(2, "length"))
4741     {
4742         value = bitFindHigh(string2int(input)) - 4;
4743         if (value > 6)
4744             return REPLY::range_msg;
4745         cmd = PADSYNTH::control::sampleSize;
4746     }
4747     else if (input.matchnMove(2, "bandwidth"))
4748     {
4749         cmd = PADSYNTH::control::bandwidth;
4750     }
4751     else if (input.matchnMove(2, "scale"))
4752     {
4753         if (input.matchnMove(1, "normal"))
4754             value = 0;
4755         else if (input.matchnMove(1, "equalhz"))
4756             value = 1;
4757         else if (input.matchnMove(1, "quarter"))
4758             value = 2;
4759         else if (input.matchnMove(1, "half"))
4760             value = 3;
4761         else if (input.matchnMove(1, "threequart"))
4762             value = 4;
4763         else if (input.matchnMove(1, "oneandhalf"))
4764             value = 5;
4765         else if (input.matchnMove(1, "double"))
4766             value = 6;
4767         else if (input.matchnMove(1, "inversehalf"))
4768             value = 7;
4769         else
4770             return REPLY::range_msg;
4771 
4772         cmd = PADSYNTH::control::bandwidthScale;
4773     }
4774     else if (input.matchnMove(2, "spectrum"))
4775     {
4776         if (input.matchnMove(1, "bandwidth"))
4777             value = 0;
4778         else if (input.matchnMove(1, "discrete"))
4779             value = 1;
4780         else if (input.matchnMove(1, "continuous"))
4781             value = 2;
4782         else
4783             return REPLY::range_msg;
4784 
4785         cmd = PADSYNTH::control::spectrumMode;
4786     }
4787 
4788     if (input.matchnMove(2, "apply"))
4789     {
4790         value = 1;
4791         cmd = PADSYNTH::control::applyChanges;
4792     }
4793 
4794     if (cmd > -1)
4795     {
4796         if (value == -1)
4797             value = string2int(input);
4798         return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, PART::engine::padSynth);
4799     }
4800     return REPLY::available_msg;
4801 }
4802 
4803 
resonance(Parser & input,unsigned char controlType)4804 int CmdInterpreter::resonance(Parser& input, unsigned char controlType)
4805 {
4806     int value = input.toggle();
4807     int cmd = -1;
4808     int engine = contextToEngines(context);
4809     int insert = TOPLEVEL::insert::resonanceGroup;
4810     if (value > -1)
4811     {
4812         sendNormal(synth, 0, value, controlType, RESONANCE::control::enableResonance, npart, kitNumber, engine, insert);
4813         return REPLY::done_msg;
4814     }
4815     if (input.lineEnd(controlType))
4816         return REPLY::done_msg;
4817 
4818     if (input.matchnMove(1, "random"))
4819     {
4820         if (input.matchnMove(1, "coarse"))
4821             value = 0;
4822         else if (input.matchnMove(1, "medium"))
4823             value = 1;
4824         else if (input.matchnMove(1, "fine"))
4825             value = 2;
4826         else
4827             return REPLY::value_msg;
4828         cmd = RESONANCE::control::randomType;
4829     }
4830     else if (input.matchnMove(2, "protect"))
4831     {
4832         value = (input.toggle() == 1);
4833         cmd = RESONANCE::control::protectFundamental;
4834     }
4835     else if (input.matchnMove(1, "maxdb"))
4836     {
4837         if (input.lineEnd(controlType))
4838             return REPLY::value_msg;
4839         cmd = RESONANCE::control::maxDb;
4840         value = string2int(input);
4841     }
4842     else if (input.matchnMove(2, "center"))
4843     {
4844         value = string2int(input);
4845         cmd = RESONANCE::control::centerFrequency;
4846     }
4847     else if (input.matchnMove(1, "octaves"))
4848     {
4849         value = string2int(input);
4850         cmd = RESONANCE::control::octaves;
4851     }
4852     else if (input.matchnMove(1, "interpolate"))
4853     {
4854         if (input.matchnMove(1, "linear"))
4855             value = 1;
4856         else if (input.matchnMove(1, "smooth"))
4857             value = 0;
4858         else return REPLY::value_msg;
4859         cmd = RESONANCE::control::interpolatePeaks;
4860     }
4861     else if (input.matchnMove(1, "smooth"))
4862         cmd = RESONANCE::control::smoothGraph;
4863     else if (input.matchnMove(1, "clear"))
4864         cmd = RESONANCE::control::clearGraph;
4865     else if (input.matchnMove(2, "apply"))
4866     { // this is a padsynth level control but must be callable here
4867         if (engine != PART::engine::padSynth)
4868             return REPLY::available_msg;
4869         value = 1;
4870         insert = UNUSED;
4871         cmd = PADSYNTH::control::applyChanges;
4872     }
4873     if (cmd > -1)
4874         return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, engine, insert);
4875 
4876     if (input.matchnMove(2, "points"))
4877     {
4878         insert = TOPLEVEL::insert::resonanceGraphInsert;
4879         if (input.isAtEnd()) // need to catch reading as well
4880         {
4881             if (controlType & TOPLEVEL::type::Limits)
4882                 return sendNormal(synth, 0, 0, controlType, 1, npart, kitNumber, engine, insert);
4883             else
4884             {
4885                 for (int i = 0; i < MAX_RESONANCE_POINTS; i += 8)
4886                 {
4887                     string line = asAlignedString(i + 1, 4) + ">";
4888                     for (int j = 0; j < (MAX_RESONANCE_POINTS / 32); ++ j)
4889                     {
4890                         line += asAlignedString(readControl(synth, 0, RESONANCE::control::graphPoint, npart, kitNumber, engine, insert, i + j), 4);
4891                     }
4892                     synth->getRuntime().Log(line);
4893                 }
4894             }
4895             return REPLY::done_msg;
4896         }
4897         cmd = RESONANCE::control::graphPoint;
4898 
4899         int point = string2int(input) - 1;
4900         if (point < 0 || point >= MAX_RESONANCE_POINTS)
4901             return REPLY::range_msg;
4902         input.skipChars();
4903         if (input.lineEnd(controlType))
4904             return REPLY::value_msg;
4905         value = string2int(input);
4906         return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, engine, insert, point);
4907     }
4908 
4909     return REPLY::available_msg;
4910 }
4911 
4912 
waveform(Parser & input,unsigned char controlType)4913 int CmdInterpreter::waveform(Parser& input, unsigned char controlType)
4914 {
4915     if (input.lineEnd(controlType))
4916         return REPLY::done_msg;
4917     float value = -1;
4918     int cmd = -1;
4919     int engine = contextToEngines(context);
4920     unsigned char insert = TOPLEVEL::insert::oscillatorGroup;
4921 
4922     if (controlType == type_read && input.isAtEnd())
4923         value = 0; // dummy value
4924     else
4925     {
4926         string name = string{input}.substr(0,3);
4927         value = stringNumInList(name, wavebase, 3);
4928     }
4929 
4930     if (value != -1)
4931         cmd = OSCILLATOR::control::baseFunctionType;
4932     else if (input.matchnMove(1, "harmonic"))
4933     {
4934         if (input.lineEnd(controlType))
4935             return REPLY::value_msg;
4936 
4937         if (input.matchnMove(1, "shift"))
4938             cmd = OSCILLATOR::control::harmonicShift;
4939         else if (input.matchnMove(1, "before"))
4940         {
4941             value = (input.toggle() == 1);
4942             cmd = OSCILLATOR::control::shiftBeforeWaveshapeAndFilter;
4943         }
4944         else
4945         {
4946             cmd = string2int(input) - 1;
4947             if (cmd < 0 || cmd >= MAX_AD_HARMONICS)
4948                 return REPLY::range_msg;
4949             input.skipChars();
4950 
4951             if (input.matchnMove(1, "amp"))
4952                 insert = TOPLEVEL::insert::harmonicAmplitude;
4953             else if (input.matchnMove(1, "phase"))
4954                 insert = TOPLEVEL::insert::harmonicPhaseBandwidth;
4955 
4956             if (input.lineEnd(controlType))
4957                 return REPLY::value_msg;
4958         }
4959         if (value == -1)
4960             value = string2int(input);
4961         return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, engine + voiceNumber, insert);
4962     }
4963 
4964     else if (input.matchnMove(2, "convert"))
4965     {
4966         value = 0; // dummy
4967         cmd = OSCILLATOR::control::convertToSine;
4968     }
4969 
4970     else if (input.matchnMove(2, "clear"))
4971     {
4972         value = 0; // dummy
4973         cmd = OSCILLATOR::control::clearHarmonics;
4974     }
4975 
4976     else if (input.matchnMove(2, "shape"))
4977     {
4978         if (input.matchnMove(1, "type"))
4979         {
4980             string name = string{input}.substr(0,3);
4981             value = stringNumInList(name, filtershapes, 3);
4982             if (value == -1)
4983                 return REPLY::value_msg;
4984             cmd = OSCILLATOR::control::waveshapeType;
4985         }
4986         else if (input.matchnMove(1, "par"))
4987             cmd = OSCILLATOR::control::waveshapeParameter;
4988         else return REPLY::op_msg;
4989     }
4990 
4991     else if (input.matchnMove(1, "filter"))
4992     {
4993         if (input.matchnMove(1, "type"))
4994         {
4995             if (controlType != TOPLEVEL::type::Write)
4996                 value = 0; // dummy value
4997             else
4998             {
4999                 string name = string{input}.substr(0,3);
5000                 value = stringNumInList(name, filtertype, 3);
5001                 if (value == -1)
5002                     return REPLY::value_msg;
5003             }
5004             cmd = OSCILLATOR::control::filterType;
5005         }
5006         else if (input.matchnMove(1, "par"))
5007         {
5008             switch (input.peek())
5009             {
5010                 case char('1'):
5011                     cmd = OSCILLATOR::control::filterParameter1;
5012                     break;
5013                 case char('2'):
5014                     cmd = OSCILLATOR::control::filterParameter2;
5015                     break;
5016                 default:
5017                     return REPLY::op_msg;
5018             }
5019             input.skipChars();
5020         }
5021         else if (input.matchnMove(1, "before"))
5022         {
5023             value = (input.toggle() == 1);
5024             cmd = OSCILLATOR::control::filterBeforeWaveshape;
5025         }
5026         else return REPLY::op_msg;
5027     }
5028 
5029     else if (input.matchnMove(1, "base"))
5030     {
5031         if (input.matchnMove(1, "par"))
5032             cmd = OSCILLATOR::control::baseFunctionParameter;
5033         else if (input.matchnMove(1, "convert"))
5034         {
5035             value = (input.toggle() != 0);
5036             cmd = OSCILLATOR::control::useAsBaseFunction;
5037         }
5038         else if (input.matchnMove(1, "mod"))
5039         {
5040             if (input.matchnMove(1, "type"))
5041             {
5042                 if (input.matchnMove(3, "off"))
5043                     value = 0;
5044                 else if (input.matchnMove(1, "Rev"))
5045                     value = 1;
5046                 else if (input.matchnMove(1, "Sine"))
5047                     value = 2;
5048                 else if (input.matchnMove(1, "Power"))
5049                     value = 3;
5050                 else
5051                     return REPLY::value_msg;
5052                 cmd = OSCILLATOR::control::baseModulationType;
5053             }
5054             else if (input.matchnMove(1, "par"))
5055             {
5056                 switch (input.peek())
5057                 {
5058                     case char('1'):
5059                         cmd = OSCILLATOR::control::baseModulationParameter1;
5060                         break;
5061                     case char('2'):
5062                         cmd = OSCILLATOR::control::baseModulationParameter2;
5063                         break;
5064                     case char('3'):
5065                         cmd = OSCILLATOR::control::baseModulationParameter3;
5066                         break;
5067                     default:
5068                         return REPLY::range_msg;
5069                 }
5070                 input.skipChars();
5071             }
5072             else
5073                 return REPLY::op_msg;
5074         }
5075         else
5076             return REPLY::op_msg;
5077     }
5078 
5079     else if (input.matchnMove(2, "spectrum"))
5080     {
5081         if (input.matchnMove(1, "type"))
5082         {
5083             if (input.matchnMove(3, "OFF"))
5084                 value = 0;
5085             else if (input.matchnMove(3, "Power"))
5086                 value = 1;
5087             else if (input.matchnMove(1, "Down"))
5088                 value = 2;
5089             else if (input.matchnMove(1, "Up"))
5090                 value = 3;
5091             else
5092                 return REPLY::value_msg;
5093             cmd = OSCILLATOR::control::spectrumAdjustType;
5094         }
5095         else if (input.matchnMove(1, "par"))
5096             cmd = OSCILLATOR::control::spectrumAdjustParameter;
5097         else return REPLY::op_msg;
5098     }
5099 
5100     else if (input.matchnMove(2, "adaptive"))
5101     {
5102         if (input.matchnMove(1, "type"))
5103         {
5104             string name = string{input}.substr(0,3);
5105             value = stringNumInList(name, adaptive, 3);
5106             if (value == -1)
5107                 return REPLY::value_msg;
5108             cmd = OSCILLATOR::control::adaptiveHarmonicsType;
5109         }
5110         else if (input.matchnMove(1, "base"))
5111             cmd = OSCILLATOR::control::adaptiveHarmonicsBase;
5112         else if (input.matchnMove(1, "level"))
5113             cmd = OSCILLATOR::control::adaptiveHarmonicsPower;
5114         else if (input.matchnMove(1, "par"))
5115             cmd = OSCILLATOR::control::adaptiveHarmonicsParameter;
5116         else
5117             return REPLY::op_msg;
5118     }
5119 
5120     else if (input.matchnMove(2, "apply"))
5121     { // this is a padsynth level control but must be callable here
5122         if (engine != PART::engine::padSynth)
5123             return REPLY::available_msg;
5124         value = 1;
5125         insert = UNUSED;
5126         cmd = PADSYNTH::control::applyChanges;
5127     }
5128     if (cmd == -1)
5129         return REPLY::available_msg;
5130     if (value == -1)
5131         value = string2float(input);
5132     return sendNormal(synth, 0, value, controlType, cmd, npart, kitNumber, engine + voiceNumber, insert);
5133 }
5134 
5135 
commandPart(Parser & input,unsigned char controlType)5136 int CmdInterpreter::commandPart(Parser& input, unsigned char controlType)
5137 {
5138     Config &Runtime = synth->getRuntime();
5139     int tmp = -1;
5140     if (bitTest(context, LEVEL::AllFX))
5141         return effects(input, controlType);
5142     bool partIsOn = readControl(synth, 0, PART::control::enable, npart);
5143 
5144     if (input.matchnMove(2, "bypass"))
5145     {
5146         if (!partIsOn)
5147             return REPLY::inactive_msg;
5148 
5149         int effnum = string2int(input);
5150         if (effnum < 1 || effnum > NUM_PART_EFX)
5151             return REPLY::range_msg;
5152         input.skipChars();
5153         bool value = false;
5154         if (!input.lineEnd(controlType))
5155             value = (input.toggle() == 1);
5156         return sendNormal(synth, 0, value, controlType, PART::control::effectBypass, npart, UNUSED, effnum - 1, TOPLEVEL::insert::partEffectSelect);
5157     }
5158     if (input.lineEnd(controlType))
5159         return REPLY::done_msg;
5160     if (kitMode == PART::kitType::Off)
5161         kitNumber = UNUSED; // always clear it if not kit mode
5162 
5163     // This is for actual effect definition and editing. See below for kit selection.
5164     if (!inKitEditor)
5165     {
5166         if (input.matchnMove(2, "effects") || input.matchnMove(2, "efx"))
5167         {
5168             if (!partIsOn)
5169             return REPLY::inactive_msg;
5170 
5171             context = LEVEL::Top;
5172             bitSet(context, LEVEL::AllFX);
5173             bitSet(context, LEVEL::Part);
5174             return effects(input, controlType);
5175         }
5176     }
5177 
5178     if (input.isdigit())
5179     {
5180         tmp = string2int127(input);
5181         input.skipChars();
5182         if (tmp > 0)
5183         {
5184             tmp -= 1;
5185             if (!inKitEditor)
5186             {
5187                 if (tmp >= Runtime.NumAvailableParts)
5188                 {
5189                     Runtime.Log("Part number too high");
5190                     return REPLY::done_msg;
5191                 }
5192 
5193                 //if (npart != tmp) // TODO sort this properly!
5194                 {
5195                     npart = tmp;
5196                     if (controlType == TOPLEVEL::type::Write)
5197                     {
5198                         context = LEVEL::Top;
5199                         bitSet(context, LEVEL::Part);
5200                         kitMode = PART::kitType::Off;
5201                         kitNumber = 0;
5202                         voiceNumber = 0; // must clear this too!
5203                         sendNormal(synth, 0, npart, TOPLEVEL::type::Write, MAIN::control::partNumber, TOPLEVEL::section::main);
5204                     }
5205                 }
5206                 if (input.lineEnd(controlType))
5207                     return REPLY::done_msg;
5208             }
5209             else
5210             {
5211                 if (controlType == TOPLEVEL::type::Write)
5212                 {
5213                     if (tmp >= NUM_KIT_ITEMS)
5214                         return REPLY::range_msg;
5215                     kitNumber = tmp;
5216                     voiceNumber = 0;// to avoid confusion
5217                 }
5218                 Runtime.Log("Kit item number " + to_string(kitNumber + 1));
5219                 return REPLY::done_msg;
5220             }
5221         }
5222     }
5223 
5224     int enable = input.toggle();
5225     if (enable > -1)
5226     {
5227         if (!inKitEditor) // part enable
5228         {
5229             int result = sendNormal(synth, 0, enable, controlType, PART::control::enable, npart);
5230             if (input.lineEnd(controlType))
5231                 return result;
5232         }
5233         else if (partIsOn) // kit item enable
5234         {
5235             if (enable >= 0)
5236             {
5237                 if (kitNumber == 0)
5238                 {
5239                     synth->getRuntime().Log("Kit item 1 always on.");
5240                     return REPLY::done_msg;
5241                 }
5242                 return sendNormal(synth, 0, enable, controlType, PART::control::enableKitLine, npart, kitNumber, UNUSED, TOPLEVEL::insert::kitGroup);
5243             }
5244         }
5245     }
5246     if (!partIsOn) // part must be enabled for all further operations.
5247         return REPLY::inactive_msg;
5248 
5249     if (input.matchnMove(2, "clear"))
5250     {
5251         if (controlType != TOPLEVEL::type::Write)
5252             return REPLY::writeOnly_msg;
5253         return sendNormal(synth, 0, npart, controlType, MAIN::control::defaultPart, TOPLEVEL::section::main);
5254     }
5255 
5256     if (input.matchnMove(2, "program") || input.matchnMove(1, "instrument"))
5257     {
5258         if (controlType != TOPLEVEL::type::Write)
5259         {
5260             Runtime.Log("Part name is " + synth->part[npart]->Pname);
5261             return REPLY::done_msg;
5262         }
5263 
5264         if (!input.isAtEnd()) // force part not channel number
5265         {
5266             if (input.matchnMove(1, "group"))
5267             {
5268                 if (instrumentGroup.empty())
5269                 {
5270                     Runtime.Log("No list entries, or list not seen");
5271                     return REPLY::done_msg;
5272                 }
5273                 size_t value = string2int(input);
5274                 string line;
5275                 if (value < 1 || value > instrumentGroup.size())
5276                     return REPLY::range_msg;
5277                 -- value;
5278 
5279                 list<string>:: iterator it = instrumentGroup.begin();
5280                 size_t lineNo = 0;
5281                 while (lineNo < value && it != instrumentGroup.end())
5282                 {
5283                     ++ it;
5284                     ++ lineNo;
5285                 }
5286                 if (it == instrumentGroup.end())
5287                     return REPLY::range_msg;
5288                 line = *it;
5289 
5290                 int root = string2int(line.substr(0, 3));
5291                 int bank = string2int(line.substr(5, 3));
5292                 int inst = (string2int(line.substr(10, 3))) - 1;
5293 
5294                 sendDirect(synth, 0, inst, controlType, MAIN::control::loadInstrumentFromBank, TOPLEVEL::section::main, npart, bank, root);
5295                 return REPLY::done_msg;
5296             }
5297             tmp = string2int(input) - 1;
5298             if (tmp < 0 || tmp >= MAX_INSTRUMENTS_IN_BANK)
5299                 return REPLY::range_msg;
5300             sendDirect(synth, 0, tmp, controlType, MAIN::control::loadInstrumentFromBank, TOPLEVEL::section::main, npart);
5301             return REPLY::done_msg;
5302         }
5303         else
5304             return REPLY::value_msg;
5305     }
5306 
5307     if (input.matchnMove(2, "latest"))
5308     {
5309         int result = readControl(synth, 0, BANK::control::lastSeenInBank, TOPLEVEL::section::bank);
5310         int root = result & 0xff;
5311 
5312         if (root == UNUSED)
5313         {
5314             synth->getRuntime().Log("Latest not defined");
5315             return REPLY::done_msg;
5316         }
5317         bool isSave = ((root & 0x80) != 0);
5318         root &= 0x7f;
5319 
5320         int instrument = result >> 15;
5321         int bank = (result >> 8) & 0x7f;
5322         string name = "A part was ";
5323         if (isSave)
5324             name += "sent to I ";
5325         else
5326             name += "fetched from I ";
5327         name += (to_string(instrument + 1) + ", B " + to_string(bank) + ", R " + to_string(root));
5328         synth->getRuntime().Log(name);
5329         return REPLY::done_msg;
5330     }
5331 
5332 
5333     if (!readControl(synth, 0, PART::control::enable, npart))
5334         return REPLY::inactive_msg;
5335 
5336     tmp = -1;
5337     if (input.matchnMove(3, "normal"))
5338         tmp = PART::kitType::Off;
5339     else if (input.matchnMove(2, "multi"))
5340         tmp = PART::kitType::Multi;
5341     else if (input.matchnMove(2, "single"))
5342         tmp = PART::kitType::Single;
5343     else if (input.matchnMove(2, "crossfade"))
5344         tmp = PART::kitType::CrossFade;
5345     else if (input.matchnMove(3, "kit"))
5346     {
5347         if (kitMode == PART::kitType::Off)
5348             return REPLY::inactive_msg;
5349         inKitEditor = true;
5350         return REPLY::done_msg;
5351     }
5352 
5353     if (tmp != -1)
5354     {
5355         kitNumber = 0;
5356         voiceNumber = 0; // must clear this too!
5357         kitMode = tmp;
5358         inKitEditor = (kitMode != PART::kitType::Off);
5359         return sendNormal(synth, 0, kitMode, controlType, PART::control::kitMode, npart);
5360     }
5361 
5362     if (bitTest(context, LEVEL::AllFX))
5363         return effects(input, controlType);
5364 
5365     if (input.matchnMove(3, "addsynth"))
5366     {
5367         bitSet(context, LEVEL::AddSynth);
5368         insertType = TOPLEVEL::insertType::amplitude;
5369         return addSynth(input, controlType);
5370     }
5371 
5372     if (input.matchnMove(3, "subsynth"))
5373     {
5374         bitSet(context, LEVEL::SubSynth);
5375         insertType = TOPLEVEL::insertType::amplitude;
5376         return subSynth(input, controlType);
5377     }
5378 
5379     if (input.matchnMove(3, "padsynth"))
5380     {
5381         bitSet(context, LEVEL::PadSynth);
5382         voiceNumber = 0; // TODO find out what *realy* causes this to screw up!
5383         insertType = TOPLEVEL::insertType::amplitude;
5384         return padSynth(input, controlType);
5385     }
5386 
5387     if (input.matchnMove(3, "mcontrol"))
5388     {
5389         bitSet(context, LEVEL::MControl);
5390         return midiControllers(input, controlType);
5391     }
5392 
5393     if (inKitEditor)
5394     {
5395         int value;
5396         if (input.matchnMove(2, "drum"))
5397             return sendNormal(synth, 0, (input.toggle() != 0), controlType, PART::control::drumMode, npart);
5398         if (input.matchnMove(2, "quiet"))
5399             return sendNormal(synth, 0, (input.toggle() != 0), controlType, PART::control::kitItemMute, npart, kitNumber, UNUSED, TOPLEVEL::insert::kitGroup);
5400          // This is for selection from 3 part effects. See above for definitions.
5401         if (input.matchnMove(2,"effect"))
5402         {
5403             if (controlType == TOPLEVEL::type::Write && input.isAtEnd())
5404                 return REPLY::value_msg;
5405             value = string2int(input);
5406             if (value < 0 || value > NUM_PART_EFX)
5407                 return REPLY::range_msg;
5408             return sendNormal(synth, 0, value, controlType | TOPLEVEL::type::Integer, PART::control::kitEffectNum, npart, kitNumber, UNUSED, TOPLEVEL::insert::kitGroup);
5409         }
5410         if (input.matchnMove(2,"name"))
5411         {
5412             int miscmsg = NO_MSG;
5413             if (input.lineEnd(controlType))
5414                 return REPLY::value_msg;
5415             if (controlType == TOPLEVEL::type::Write)
5416                 miscmsg = textMsgBuffer.push(input);
5417             return sendNormal(synth, TOPLEVEL::action::muteAndLoop, 0, controlType, PART::control::instrumentName, npart, kitNumber, UNUSED, TOPLEVEL::insert::kitGroup, UNUSED, UNUSED, miscmsg);
5418         }
5419     }
5420 
5421     int value = 0;
5422     int cmd = -1;
5423     if (input.matchnMove(2, "min"))
5424     {
5425         cmd = PART::control::minNote;
5426         if (controlType == TOPLEVEL::type::Write)
5427         {
5428             if (input.lineEnd(controlType))
5429                 return REPLY::value_msg;
5430             if (input.matchnMove(1, "last"))
5431                 cmd = PART::control::minToLastKey;
5432             else
5433             {
5434                 value = string2int(input);
5435                 if (value > synth->part[npart]->Pmaxkey)
5436                     return REPLY::high_msg;
5437             }
5438         }
5439 
5440     }
5441     else if (input.matchnMove(2, "max"))
5442     {
5443         cmd = PART::control::maxNote;
5444         if (controlType == TOPLEVEL::type::Write)
5445         {
5446             if (input.lineEnd(controlType))
5447                 return REPLY::value_msg;
5448             if (input.matchnMove(1, "last"))
5449                 cmd = PART::control::maxToLastKey;
5450             else
5451             {
5452                 value = string2int(input);
5453                 if (value < synth->part[npart]->Pminkey)
5454                     return REPLY::low_msg;
5455             }
5456         }
5457 
5458     }
5459     else if (input.matchnMove(2, "full"))
5460          cmd = PART::control::resetMinMaxKey;
5461 
5462     if (cmd > -1)
5463     {
5464         int insert = UNUSED;
5465         int kit = kitNumber;
5466         if (inKitEditor)
5467             insert = TOPLEVEL::insert::kitGroup;
5468         else
5469             kit = UNUSED;
5470         return sendNormal(synth, 0, value, controlType, cmd, npart, kit, UNUSED, insert);
5471     }
5472 
5473     if (input.matchnMove(2, "shift"))
5474     {
5475         if (controlType == TOPLEVEL::type::Write && input.isAtEnd())
5476             return REPLY::value_msg;
5477         int value = string2int(input);
5478         if (value < MIN_KEY_SHIFT)
5479             value = MIN_KEY_SHIFT;
5480         else if (value > MAX_KEY_SHIFT)
5481             value = MAX_KEY_SHIFT;
5482         return sendNormal(synth, TOPLEVEL::action::lowPrio, value, controlType, PART::control::keyShift, npart);
5483     }
5484 
5485     if (input.matchnMove(1, "volume"))
5486         cmd = PART::control::volume;
5487     else if (input.matchnMove(1, "pan"))
5488         cmd = PART::control::panning;
5489     else if (input.matchnMove(2, "velocity"))
5490         cmd = PART::control::velocitySense;
5491     else if (input.matchnMove(2, "LEvel"))
5492         cmd = PART::control::velocityOffset;
5493     if (cmd != -1)
5494     {
5495         int tmp = string2int127(input);
5496         if (controlType == TOPLEVEL::type::Write && input.isAtEnd())
5497             return REPLY::value_msg;
5498         return sendNormal(synth, 0, tmp, controlType, cmd, npart);
5499     }
5500 
5501     if (input.matchnMove(2, "channel"))
5502     {
5503         tmp = string2int127(input);
5504         if (controlType == TOPLEVEL::type::Write && tmp < 1)
5505             return REPLY::value_msg;
5506         tmp -= 1;
5507         return sendNormal(synth, 0, tmp, controlType, PART::control::midiChannel, npart);
5508     }
5509     if (input.matchnMove(2, "aftertouch"))
5510     {
5511         int tmp = PART::aftertouchType::modulation * 2;
5512         int cmd = PART::control::channelATset;
5513         if (input.matchnMove(1, "key"))
5514             cmd = PART::control::keyATset;
5515         else if (!input.matchnMove(1, "chan"))
5516             return REPLY::op_msg;
5517         if (input.matchnMove(1, "Off"))
5518             tmp = PART::aftertouchType::off;
5519         else
5520         {
5521             if (input.matchnMove(1, "Filter"))
5522             {
5523                 tmp = PART::aftertouchType::filterCutoff;
5524                 if (input.matchnMove(1, "Down"))
5525                     tmp |= PART::aftertouchType::filterCutoffDown;
5526             }
5527             if (input.matchnMove(1, "Peak"))
5528             {
5529                 tmp = PART::aftertouchType::filterQ;
5530                 if (input.matchnMove(1, "Down"))
5531                     tmp |= PART::aftertouchType::filterQdown;
5532             }
5533             if (input.matchnMove(1, "Bend"))
5534             {
5535                 tmp |= PART::aftertouchType::pitchBend;
5536                 if (input.matchnMove(1, "Down"))
5537                     tmp |= PART::aftertouchType::pitchBendDown;
5538             }
5539             if (input.matchnMove(1, "Volume"))
5540                 tmp |= PART::aftertouchType::volume;
5541             if (input.matchnMove(1, "Modulation"))
5542                 tmp |= PART::aftertouchType::modulation;
5543         }
5544         if (tmp == PART::aftertouchType::modulation * 2 && controlType != type_read)
5545             return REPLY::value_msg;
5546         return sendNormal(synth, 0, tmp & (PART::aftertouchType::modulation * 2 - 1), controlType, cmd, npart);
5547     }
5548     if (input.matchnMove(1, "destination"))
5549     {
5550         int dest = 0;
5551         if (controlType == TOPLEVEL::type::Write)
5552         {
5553             if (input.matchnMove(1, "main"))
5554                 dest = 1;
5555             else if (input.matchnMove(1, "part"))
5556                 dest = 2;
5557             else if (input.matchnMove(1, "both"))
5558                 dest = 3;
5559             if (dest == 0)
5560                 return REPLY::range_msg;
5561         }
5562         return sendNormal(synth, TOPLEVEL::action::muteAndLoop, dest, controlType, PART::control::audioDestination, npart);
5563     }
5564     if (input.matchnMove(1, "note"))
5565     {
5566         int value = 0;
5567         if (controlType == TOPLEVEL::type::Write)
5568         {
5569             if (input.lineEnd(controlType))
5570                 return REPLY::value_msg;
5571             value = string2int(input);
5572             if (value < 1 || value > POLIPHONY)
5573                 return REPLY::range_msg;
5574         }
5575         return sendNormal(synth, 0, value, controlType, PART::control::maxNotes, npart);
5576     }
5577 
5578     if (input.matchnMove(1, "mode"))
5579     {
5580         int value = 0;
5581         if (controlType == TOPLEVEL::type::Write)
5582         {
5583             if (input.matchnMove(1, "poly"))
5584                 value = 0;
5585             else if (input.matchnMove(1, "mono"))
5586                 value = 1;
5587             else if (input.matchnMove(1, "legato"))
5588                 value = 2;
5589             else
5590                 return REPLY::name_msg;
5591         }
5592         return sendNormal(synth, 0, value, controlType, PART::control::keyMode, npart);
5593     }
5594     if (input.matchnMove(2, "portamento"))
5595         return sendNormal(synth, 0, (input.toggle() == 1), controlType, PART::control::portamento, npart);
5596     if (input.matchnMove(1, "humanise"))
5597     {
5598         int cmd = -1;
5599         if (input.matchnMove(1, "pitch"))
5600             cmd = PART::control::humanise;
5601         else if (input.matchnMove(1, "velocity"))
5602             cmd = PART::control::humanvelocity;
5603         else
5604             return REPLY::op_msg;
5605         if (input.lineEnd(controlType))
5606             return REPLY::value_msg;
5607         return sendNormal(synth, 0, string2int(input), controlType, cmd, npart);
5608     }
5609     if (input.matchnMove(2, "name"))
5610     {
5611         string name;
5612         unsigned char miscmsg = NO_MSG;
5613         if (controlType == TOPLEVEL::type::Write)
5614         {
5615             name = string{input};
5616             if (name.size() < 3)
5617             {
5618                 Runtime.Log("Name too short");
5619                 return REPLY::done_msg;
5620             }
5621             else if (name == DEFAULT_NAME)
5622             {
5623                 Runtime.Log("Cant use name of default sound");
5624                 return REPLY::done_msg;
5625             }
5626             else
5627                 miscmsg = textMsgBuffer.push(name);
5628         }
5629         return sendNormal(synth, TOPLEVEL::action::lowPrio, 0, controlType, PART::control::instrumentName, npart, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, miscmsg);
5630     }
5631     if (input.matchnMove(3,"copyright"))
5632     {
5633         string name;
5634         if (controlType == TOPLEVEL::type::Write)
5635         {
5636             name = string{input};
5637             if (name.size() < 2)
5638                 return REPLY::value_msg;
5639         }
5640         unsigned char miscmsg = textMsgBuffer.push(name);
5641         return sendNormal(synth, TOPLEVEL::action::lowPrio, 0, controlType, PART::control::instrumentCopyright, npart, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, miscmsg);
5642     }
5643     if (input.matchnMove(3,"info"))
5644     {
5645         string name;
5646         if (controlType == TOPLEVEL::type::Write)
5647         {
5648             name = string{input};
5649             if (name.size() < 2)
5650                 return REPLY::value_msg;
5651         }
5652         unsigned char miscmsg = textMsgBuffer.push(name);
5653         return sendNormal(synth, TOPLEVEL::action::lowPrio, 0, controlType, PART::control::instrumentComments, npart, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, miscmsg);
5654     }
5655     if (input.matchnMove(3,"type"))
5656     {
5657         if (input.lineEnd(controlType))
5658             return REPLY::value_msg;
5659         int pos = 0;
5660         if (controlType == TOPLEVEL::type::Write)
5661         {
5662             string name = type_list[pos];
5663             while (name != "@end" && !input.matchnMove(3, name.c_str()))
5664             {
5665                 ++ pos;
5666                 name = type_list[pos];
5667             }
5668             if (name == "@end")
5669                 pos = 0; // undefined
5670         }
5671         return sendNormal(synth, TOPLEVEL::action::lowPrio, pos, controlType, PART::control::instrumentType, npart);
5672     }
5673     return REPLY::op_msg;
5674 }
5675 
5676 
5677 /* Special operations used by the Yoshimi-testsuite for automated acceptance tests */
commandTest(Parser & input,unsigned char controlType)5678 int CmdInterpreter::commandTest(Parser& input, unsigned char controlType)
5679 {
5680     bitSet(context, LEVEL::Test);
5681     if (input.matchnMove(2, "test"))
5682     {
5683         // just consume; we are already in the test context
5684     }
5685 
5686     string response;
5687     if (getTestInvoker().handleParameterChange(input, controlType, response, synth->buffersize))
5688         synth->getRuntime().Log(response);
5689 
5690     // proceed to launch the test invocation and then cause termination of Yoshimi
5691     if (controlType == TOPLEVEL::type::Write && input.matchnMove(3, "execute"))
5692     {
5693         size_t wait_at_least_one_cycle = ceil(1.1 * (synth->buffersize_f / synth->samplerate_f) * 1000*1000);
5694 
5695         sendNormal(synth, 0, 0, TOPLEVEL::type::Write,MAIN::control::stopSound, TOPLEVEL::section::main);
5696         do usleep(wait_at_least_one_cycle); // with buffersize 128 and 48kHz -> one buffer lasts ~ 2.6ms
5697         while (synth->audioOut != _SYS_::mute::Idle);
5698 
5699         // NOTE: the following initiates a shutdown
5700         waitForTest = true;
5701         synth->getRuntime().runSynth = false;
5702         usleep(wait_at_least_one_cycle);
5703 
5704         // Launch computation for automated acceptance test
5705         getTestInvoker().performSoundCalculation(*synth);
5706         return REPLY::exit_msg;
5707     }
5708     else if (!input.isAtEnd())
5709         return REPLY::op_msg; //"Which Operation?"
5710 
5711     return REPLY::done_msg;
5712 }
5713 
5714 
commandReadnSet(Parser & input,unsigned char controlType)5715 int CmdInterpreter::commandReadnSet(Parser& input, unsigned char controlType)
5716 {
5717     Config &Runtime = synth->getRuntime();
5718     string name;
5719 
5720 
5721     /*CommandBlock getData;
5722     getData.data.value = 0;
5723     getData.data.part = TOPLEVEL::section::copyPaste;
5724     getData.data.kit = 0;
5725     getData.data.engine = 135;
5726     getData.data.insert = UNUSED;
5727     cout << synth->unifiedpresets.findSectionName(&getData) << endl;*/
5728 
5729 
5730     if (input.matchnMove(2, "yoshimi"))
5731     {
5732         if (controlType != TOPLEVEL::type::Write)
5733         {
5734             //Runtime.Log("Instance " + asString(currentInstance), _SYS_::LogError);
5735             Runtime.Log("Instance " + to_string(synth->getUniqueId()));
5736             return REPLY::done_msg;
5737         }
5738         if (input.lineEnd(controlType))
5739             return REPLY::value_msg;
5740 
5741         resetInstance(string2int(input));
5742         return REPLY::done_msg;
5743     }
5744 
5745     if (input.matchnMove(4, "tone"))
5746     {
5747 
5748         if (controlType != TOPLEVEL::type::Write)
5749             return REPLY::available_msg;
5750         if (input.lineEnd(controlType))
5751             return REPLY::value_msg;
5752 
5753         int chan = string2int(input) - 1;
5754         input.skipChars();
5755         if (chan < 0 || chan > 15)
5756             return REPLY::range_msg;
5757 
5758         int pitch = string2int(input);
5759         input.skipChars();
5760         if (pitch < 0 || pitch > 127)
5761             return REPLY::range_msg;
5762 
5763         int velocity = string2int(input);
5764         int control;
5765         if (velocity > 0 && velocity <= 127)
5766             control = MIDI::noteOn;
5767         else
5768             control = MIDI::noteOff;
5769 
5770         sendDirect(synth, 0, velocity, controlType, control, TOPLEVEL::midiIn, chan, pitch);
5771         return REPLY::done_msg;
5772     }
5773 
5774     if (input.matchnMove(4, "seed"))
5775     {
5776         if (controlType != TOPLEVEL::type::Write)
5777             return REPLY::available_msg;
5778         int seed = string2int(input);
5779         if (seed < 0)
5780             seed = 0;
5781         else if (seed > 0xffffff)
5782             seed = 0xffffff;
5783         sendDirect(synth, 0, seed, controlType | TOPLEVEL::type::Integer, MAIN::control::reseed, TOPLEVEL::main);
5784         return REPLY::done_msg;
5785     }
5786 
5787     switch (bitFindHigh(context))
5788     {
5789         case LEVEL::Config:
5790             return commandConfig(input, controlType);
5791             break;
5792         case LEVEL::Bank:
5793             return commandBank(input, controlType);
5794             break;
5795         case LEVEL::Scale:
5796             return commandScale(input, controlType);
5797             break;
5798         case LEVEL::Envelope:
5799             return envelopeSelect(input, controlType);
5800             break;
5801         case LEVEL::Filter:
5802             return filterSelect(input, controlType);
5803             break;
5804         case LEVEL::LFO:
5805             return LFOselect(input, controlType);
5806             break;
5807         case LEVEL::Resonance:
5808             return resonance(input, controlType);
5809             break;
5810         case LEVEL::Oscillator:
5811             return waveform(input, controlType);
5812             break;
5813         case LEVEL::AddMod:
5814             return modulator(input, controlType);
5815             break;
5816         case LEVEL::AddVoice:
5817             return addVoice(input, controlType);
5818             break;
5819         case LEVEL::AddSynth:
5820             return addSynth(input, controlType);
5821             break;
5822         case LEVEL::SubSynth:
5823             return subSynth(input, controlType);
5824             break;
5825         case LEVEL::PadSynth:
5826             return padSynth(input, controlType);
5827             break;
5828         case LEVEL::MControl:
5829             return midiControllers(input, controlType);
5830             break;
5831         case LEVEL::Part:
5832             return commandPart(input, controlType);
5833             break;
5834         case LEVEL::Vector:
5835             return commandVector(input, controlType);
5836             break;
5837         case LEVEL::Learn:
5838             return commandMlearn(input, controlType);
5839             break;
5840         case LEVEL::Test:
5841             return commandTest(input, controlType);
5842             break;
5843     }
5844 
5845     if (input.matchnMove(3, "mono"))
5846     {
5847         return sendNormal(synth, 0, (input.toggle() == 1), controlType, MAIN::control::mono, TOPLEVEL::section::main);
5848     }
5849 
5850     if (input.matchnMove(2, "config"))
5851     {
5852         context = LEVEL::Top;
5853         bitSet(context, LEVEL::Config);
5854         return commandConfig(input, controlType);
5855     }
5856 
5857     if (input.matchnMove(1, "bank"))
5858     {
5859         context = LEVEL::Top;
5860         bitSet(context, LEVEL::Bank);
5861         return commandBank(input, controlType, true);
5862     }
5863 
5864     if (input.matchnMove(1, "scale"))
5865     {
5866         context = LEVEL::Top;
5867         bitSet(context, LEVEL::Scale);
5868         return commandScale(input, controlType);
5869     }
5870 
5871     if (input.matchnMove(1, "part"))
5872     {
5873         nFX = 0; // just to be sure
5874         // TODO get correct part number
5875         /*if (controlType != TOPLEVEL::type::Write && input.isAtEnd())
5876         {
5877             if (synth->partonoffRead(npart))
5878                 name = " enabled";
5879             else
5880                 name = " disabled";
5881             Runtime.Log("Current part " + asString(npart + 1) + name, _SYS_::LogError);
5882             return REPLY::done_msg;
5883         }*/
5884         context = LEVEL::Top;
5885         bitSet(context, LEVEL::Part);
5886         nFXtype = synth->part[npart]->partefx[nFX]->geteffect();
5887         return commandPart(input, controlType);
5888     }
5889 
5890     if (input.matchnMove(2, "vector"))
5891     {
5892         context = LEVEL::Top;
5893         return commandVector(input, controlType);
5894     }
5895 
5896     if (input.matchnMove(2, "mlearn"))
5897     {
5898         context = LEVEL::Top;
5899         return commandMlearn(input, controlType);
5900     }
5901 
5902     if (input.matchnMove(3, "test"))
5903     {
5904         context = LEVEL::Top;
5905         return commandTest(input, controlType);
5906     }
5907 
5908     if ((context == LEVEL::Top || bitTest(context, LEVEL::InsFX)) && input.matchnMove(3, "system"))
5909     {
5910         bitSet(context,LEVEL::AllFX);
5911         bitClear(context, LEVEL::InsFX);
5912         nFX = 0; // just to be sure
5913         input.matchnMove(2, "effects"); // clear it if given
5914         input.matchnMove(2, "efx");
5915         nFXtype = synth->sysefx[nFX]->geteffect();
5916         return effects(input, controlType);
5917     }
5918     if ((context == LEVEL::Top || bitTest(context, LEVEL::AllFX)) && !bitTest(context, LEVEL::Part) && input.matchnMove(3, "insert"))
5919     {
5920         bitSet(context,LEVEL::AllFX);
5921         bitSet(context,LEVEL::InsFX);
5922         nFX = 0; // just to be sure
5923         input.matchnMove(2, "effects"); // clear it if given
5924         input.matchnMove(2, "efx");
5925         nFXtype = synth->insefx[nFX]->geteffect();
5926         return effects(input, controlType);
5927     }
5928     if (bitTest(context, LEVEL::AllFX))
5929         return effects(input, controlType);
5930 
5931     if (input.matchnMove(1, "volume"))
5932     {
5933         if (input.lineEnd(controlType))
5934             return REPLY::value_msg;
5935         return sendNormal(synth, 0, string2int127(input), controlType, MAIN::control::volume, TOPLEVEL::section::main);
5936     }
5937     if (input.matchnMove(2, "detune"))
5938     {
5939         if (input.lineEnd(controlType))
5940             return REPLY::value_msg;
5941         return sendNormal(synth, TOPLEVEL::action::lowPrio, string2int127(input), controlType, MAIN::control::detune, TOPLEVEL::section::main);
5942     }
5943 
5944     if (input.matchnMove(2, "shift"))
5945     {
5946         if (input.lineEnd(controlType))
5947             return REPLY::value_msg;
5948         int value = string2int(input);
5949         return sendNormal(synth, TOPLEVEL::action::lowPrio, value, controlType, MAIN::control::keyShift, TOPLEVEL::section::main);
5950     }
5951 
5952     if (input.matchnMove(2, "solo"))
5953     {
5954         int value = MIDI::SoloType::Disabled;
5955 
5956         if (input.matchnMove(2, "cc"))
5957         {
5958             if (controlType == TOPLEVEL::type::Write)
5959             {
5960                 if (input.lineEnd(controlType))
5961                     return REPLY::value_msg;
5962                 value = string2int127(input);
5963                 string otherCC = Runtime.masterCCtest(value);
5964                 if (otherCC > "")
5965                 {
5966                     Runtime.Log("In use for " + otherCC);
5967                     return REPLY::done_msg;
5968                 }
5969             }
5970             return sendNormal(synth, 0, value, controlType, MAIN::control::soloCC, TOPLEVEL::section::main);
5971         }
5972 
5973         else if (input.matchnMove(2, "row"))
5974             value = MIDI::SoloType::Row;
5975         else if (input.matchnMove(2, "column"))
5976             value = MIDI::SoloType::Column;
5977         else if (input.matchnMove(2, "loop"))
5978             value = MIDI::SoloType::Loop;
5979         else if (input.matchnMove(2, "twoway"))
5980             value = MIDI::SoloType::TwoWay;
5981         else if (input.matchnMove(2, "channel"))
5982             value = MIDI::SoloType::Channel;
5983         return sendNormal(synth, 0, value, controlType, MAIN::control::soloType, TOPLEVEL::section::main);
5984     }
5985     if (input.matchnMove(2, "available")) // 16, 32, 64
5986     {
5987         if (input.lineEnd(controlType))
5988             return REPLY::value_msg;
5989         int value = string2int(input);
5990         if (controlType == TOPLEVEL::type::Write && value != 16 && value != 32 && value != 64)
5991             return REPLY::range_msg;
5992         return sendNormal(synth, 0, value, controlType, MAIN::control::availableParts, TOPLEVEL::section::main);
5993     }
5994     if (input.matchnMove(3, "panning"))
5995     {
5996         int value = MAIN::panningType::normal;
5997         if (input.matchnMove(1, "cut"))
5998             value = MAIN::panningType::cut;
5999         else if (input.matchnMove(1, "boost"))
6000             value = MAIN::panningType::boost;
6001         else if (!input.matchnMove(1, "default") && controlType == TOPLEVEL::type::Write)
6002             return REPLY::range_msg;
6003 
6004         return sendNormal(synth, 0, value, controlType, MAIN::control::panLawType, TOPLEVEL::section::main);
6005     }
6006     if (input.matchnMove(2, "clear"))
6007     {
6008         if (input.lineEnd(controlType))
6009             return REPLY::value_msg;
6010 
6011         int value = string2int(input) -1;
6012         if (value < 0)
6013             return REPLY::range_msg;
6014 
6015         if (!readControl(synth, 0, PART::control::enable, value))
6016             return REPLY::inactive_msg;
6017 
6018         return sendNormal(synth, 0, value, controlType, MAIN::control::defaultPart, TOPLEVEL::section::main);
6019     }
6020     return REPLY::op_msg;
6021 }
6022 
6023 
processSrcriptFile(const string & filename,bool toplevel)6024 Reply CmdInterpreter::processSrcriptFile(const string& filename, bool toplevel)
6025 {
6026     if (filename <= "!")
6027         return Reply::what("Exec");
6028 
6029     Config &Runtime = synth->getRuntime();
6030     stringstream reader(loadText(filename));
6031     if (reader.eof())
6032     {
6033         Runtime.Log("Can't read file " + filename);
6034         return Reply::DONE;
6035     }
6036 
6037     cli::Parser scriptParser;
6038     if (toplevel)
6039         context = LEVEL::Top; // start from top level
6040 
6041     string line;
6042     int lineNo = 0;
6043     const char DELIM_NEWLINE ='\n';
6044     while (std::getline(reader, line, DELIM_NEWLINE))
6045     {
6046         //cout << "line >" << line << "<" << endl;
6047         scriptParser.initWithExternalBuffer(line);
6048         if (scriptParser.isTooLarge())
6049         {
6050             Runtime.Log("*** Error: line " + to_string(lineNo) + " too long");
6051             return Reply(REPLY::failed_msg);
6052         }
6053         ++ lineNo;
6054         if (line.empty())
6055             continue; // skip empty line but count it.
6056 
6057         scriptParser.skipSpace();
6058         if (scriptParser.peek() == '#' || iscntrl((unsigned char) scriptParser.peek()))
6059         {   // skip comment lines
6060             continue;
6061         }
6062         if (scriptParser.matchnMove(3, "run"))
6063         {
6064             Runtime.Log("*** Error: scripts are not recursive @ line " + to_string(lineNo) + " ***");
6065             return Reply(REPLY::failed_msg);
6066         }
6067         if (scriptParser.matchnMove(4, "wait"))
6068         {
6069             int mSec = string2int(scriptParser);
6070             if (mSec < 2)
6071                 mSec = 2;
6072             else if (mSec > 30000)
6073                 mSec = 30000;
6074             mSec -= 1; //allow for internal time
6075             Runtime.Log("Waiting " + to_string(mSec) + "mS");
6076             if (mSec > 1000)
6077             {
6078                 sleep (mSec / 1000);
6079                 mSec = mSec % 1000;
6080             }
6081             usleep(mSec * 1000);
6082         }
6083         else
6084         {
6085             usleep(2000); // the loop is too fast otherwise!
6086             Reply reply = cmdIfaceProcessCommand(scriptParser);
6087             if (reply.code > REPLY::done_msg)
6088             {
6089                 Runtime.Log("*** Error: " + replies[reply.code] + " @ line " + to_string(lineNo) + ": " + line + " ***");
6090                 return Reply(REPLY::failed_msg);
6091             }
6092         }
6093     }
6094     return Reply::DONE;
6095 }
6096 
6097 
cmdIfaceProcessCommand(Parser & input)6098 Reply CmdInterpreter::cmdIfaceProcessCommand(Parser& input)
6099 {
6100     input.trim();
6101 
6102     unsigned int newID = synth->getUniqueId();
6103     if (newID != currentInstance)
6104     {
6105         currentInstance = newID;
6106         defaults();
6107     }
6108     Config &Runtime = synth->getRuntime();
6109 
6110     buildStatus(false);
6111 
6112 #ifdef REPORT_NOTES_ON_OFF
6113     if (input.matchnMove(3, "report")) // note test
6114     {
6115         cout << "note on sent " << Runtime.noteOnSent << endl;
6116         cout << "note on seen " << Runtime.noteOnSeen << endl;
6117         cout << "note off sent " << Runtime.noteOffSent << endl;
6118         cout << "note off seen " << Runtime.noteOffSeen << endl;
6119         cout << "notes hanging sent " << Runtime.noteOnSent - Runtime.noteOffSent << endl;
6120         cout << "notes hanging seen " << Runtime.noteOnSeen - Runtime.noteOffSeen << endl;
6121         return Reply::DONE;
6122     }
6123 #endif
6124     if (input.matchnMove(5, "filer"))
6125     {
6126         string result;
6127         file::dir2string(result, "/home/will", ".xiz");
6128         cout << result << endl;
6129         return Reply::DONE;
6130     }
6131 
6132     if (input.matchnMove(2, "exit"))
6133     {
6134         if (input.matchnMove(2, "force"))
6135         {
6136             sendDirect(synth, 0, 0, TOPLEVEL::type::Write, TOPLEVEL::control::forceExit, UNUSED);
6137             return Reply::DONE;
6138         }
6139         bool echo = (synth->getRuntime().toConsole);
6140         if (currentInstance > 0)
6141         {
6142             if (echo)
6143                 cout << "Can only exit from instance 0" << endl;
6144             Runtime.Log("Can only exit from instance 0", _SYS_::LogError);
6145             return Reply::DONE;
6146         }
6147         string message;
6148         if (Runtime.configChanged)
6149         {
6150             if (echo)
6151                 cout << "System config has been changed. Still exit N/y?" << endl;
6152             message = "System config has been changed. Still exit";
6153         }
6154         else
6155         {
6156             if (echo)
6157                 cout << "All data will be lost. Still exit N/y?" << endl;
6158             message = "All data will be lost. Still exit";
6159         }
6160         if (query(message, false))
6161         {
6162             // this seems backwards but it *always* saves.
6163             // seeing configChanged makes it reload the old config first.
6164             Runtime.runSynth = false;
6165             return Reply{REPLY::exit_msg};
6166         }
6167         return Reply::DONE;
6168     }
6169 
6170     if (input.nextChar('/'))
6171     {
6172         input.skip(1);
6173         input.skipSpace();
6174         defaults();
6175         if (input.isAtEnd())
6176             return Reply::DONE;
6177     }
6178 
6179     if (input.matchnMove(3, "reset"))
6180     {
6181         int control = MAIN::control::masterReset;
6182         if (input.matchnMove(3, "all"))
6183             control = MAIN::control::masterResetAndMlearn;
6184         if (query("Restore to basic settings", false))
6185         {
6186             sendDirect(synth, TOPLEVEL::action::muteAndLoop, 0, TOPLEVEL::type::Write, control, TOPLEVEL::section::main);
6187             defaults();
6188         }
6189         return Reply::DONE;
6190     }
6191 
6192     if (input.startsWith(".."))
6193     {
6194         input.skip(2);
6195         input.skipSpace();
6196         if (bitFindHigh(context) == LEVEL::Filter)
6197         {
6198             filterVowelNumber = 0;
6199             filterFormantNumber = 0;
6200         }
6201 
6202         /*
6203          * kit mode is a pseudo context level so the code
6204          * below emulates normal 'back' actions
6205          */
6206         if (bitFindHigh(context) == LEVEL::Part && kitMode != PART::kitType::Off)
6207         {
6208             int newPart = npart;
6209             input.markPoint();
6210             defaults();
6211             npart = newPart;
6212             bitSet(context, LEVEL::Part);
6213             if (input.matchnMove(1, "set"))
6214             {
6215                 if (!input.isdigit())
6216                     input.reset_to_mark();
6217                 else
6218                 {
6219                     int tmp = string2int(input);
6220                     if (tmp < 1 || tmp > Runtime.NumAvailableParts)
6221                         return REPLY::range_msg;
6222 
6223                     npart = tmp -1;
6224                     return Reply::DONE;
6225                 }
6226             }
6227             else
6228                 return Reply::DONE;
6229         }
6230 
6231         if (bitFindHigh(context) == LEVEL::AllFX || bitFindHigh(context) == LEVEL::InsFX)
6232             defaults();
6233         else if (bitFindHigh(context) == LEVEL::Part)
6234         {
6235             int temPart = npart;
6236             if (bitTest(context, LEVEL::AllFX) || bitTest(context, LEVEL::InsFX))
6237             {
6238                 defaults();
6239                 bitSet(context, LEVEL::Part); // restore part level
6240             }
6241             else
6242                 defaults();
6243             npart = temPart;
6244         }
6245         else
6246         {
6247             bitClearHigh(context);
6248         }
6249         if (input.isAtEnd())
6250             return Reply::DONE;
6251     }
6252 
6253     if (helpList(input, context))
6254         return Reply::DONE;
6255 
6256     if (input.matchnMove(2, "stop"))
6257         return Reply{sendNormal(synth, 0, 0, TOPLEVEL::type::Write,MAIN::control::stopSound, TOPLEVEL::section::main)};
6258     if (input.matchnMove(1, "list"))
6259     {
6260         if (input.matchnMove(1, "group"))
6261             return Reply{commandGroup(input)};
6262         return Reply{commandList(input)};
6263     }
6264 
6265     if (input.matchnMove(4, "runlocal"))
6266         return processSrcriptFile(input, false);
6267     if (input.matchnMove(3, "run"))
6268         return processSrcriptFile(input);
6269 
6270     if (input.matchnMove(1, "set"))
6271     {
6272         if (!input.isAtEnd())
6273             return Reply{commandReadnSet(input, TOPLEVEL::type::Write)};
6274         else
6275             return Reply::what("set");
6276     }
6277     // special shortcut when in Test context: 'execute' directly launches test
6278     if (bitFindHigh(context) == LEVEL::Test  && input.matchWord(3, "EXEcute"))
6279         return Reply{commandReadnSet(input, TOPLEVEL::type::Write)};
6280 
6281     if (input.matchnMove(1, "read") || input.matchnMove(1, "get"))
6282     {
6283         /*
6284          * we no longer test for line end as some contexts can return
6285          * useful information with a simple read.
6286          */
6287         return Reply{commandReadnSet(input, type_read)};
6288     }
6289 
6290     if (input.matchnMove(3, "minimum"))
6291     {
6292         if (!input.isAtEnd())
6293             return Reply{commandReadnSet(input, TOPLEVEL::type::Minimum | TOPLEVEL::type::Limits)};
6294         else
6295         {
6296             return Reply::what("minimum");
6297         }
6298     }
6299 
6300     if (input.matchnMove(3, "maximum"))
6301     {
6302         if (!input.isAtEnd())
6303             return Reply{commandReadnSet(input, TOPLEVEL::type::Maximum | TOPLEVEL::type::Limits)};
6304         else
6305         {
6306             return Reply::what("maximum");
6307         }
6308     }
6309 
6310     if (input.matchnMove(3, "default"))
6311     {
6312         if (!input.isAtEnd())
6313             return Reply{commandReadnSet(input, TOPLEVEL::type::Default | TOPLEVEL::type::Limits)};
6314         else
6315         {
6316             return Reply::what("default");
6317         }
6318     }
6319 
6320     if (input.matchnMove(2, "mlearn"))
6321     {
6322         if (!input.isAtEnd())
6323             return Reply{commandReadnSet(input, TOPLEVEL::type::LearnRequest)};
6324         else
6325         {
6326             return Reply::what("mlearn");
6327         }
6328     }
6329 
6330     if (input.matchnMove(3, "add"))
6331     {
6332         if (input.matchnMove(1, "root"))
6333         {
6334             return sendNormal(synth, TOPLEVEL::action::lowPrio, 0, TOPLEVEL::type::Write, BANK::control::addNamedRoot, TOPLEVEL::section::bank, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, textMsgBuffer.push(input));
6335         }
6336         if (input.matchnMove(1, "bank"))
6337         {
6338             int root = readControl(synth, 0, BANK::control::selectRoot, TOPLEVEL::section::bank);
6339 
6340             return sendNormal(synth, TOPLEVEL::action::lowPrio, 0, TOPLEVEL::type::Write, BANK::control::createBank, TOPLEVEL::section::bank, UNUSED, root, UNUSED, UNUSED, UNUSED, textMsgBuffer.push(input));
6341         }
6342         if (input.matchnMove(2, "yoshimi"))
6343         {
6344             if (currentInstance !=0)
6345             {
6346                 Runtime.Log("Only instance 0 can start others");
6347                 return Reply::DONE;
6348             }
6349             int forceId = string2int(input);
6350             if (forceId < 1 || forceId >= 32)
6351                 forceId = 0;
6352             sendDirect(synth, TOPLEVEL::action::lowPrio, forceId, TOPLEVEL::type::Write, MAIN::control::startInstance, TOPLEVEL::section::main);
6353             return Reply::DONE;
6354         }
6355         else
6356         {
6357             return Reply::what("add");
6358         }
6359     }
6360 
6361     if (input.matchWord(3, "import") || input.matchWord(3, "export") )
6362     { // need the double test to find which then move along line
6363         int type = 0;
6364         string replyMsg;
6365         if (input.matchnMove(3, "import"))
6366         {
6367             type = MAIN::control::importBank;
6368             replyMsg = "import";
6369         }
6370         else if (input.matchnMove(3, "export"))
6371         {
6372             type = MAIN::control::exportBank;
6373             replyMsg = "export";
6374         }
6375 
6376         int root = UNUSED;
6377         if (input.matchnMove(1, "root"))
6378         {
6379             if (input.isdigit())
6380             {
6381                 root = string2int(input);
6382                 input.skipChars();
6383             }
6384             else
6385                 root = 200; // force invalid root error
6386         }
6387         int value = string2int(input);
6388         input.skipChars();
6389         string name(input);
6390         if (root < 0 || (root > 127 && root != UNUSED) || value < 0 || value > 127 || name <="!")
6391             return Reply{REPLY::what_msg};
6392         else
6393         {
6394             sendDirect(synth, TOPLEVEL::action::lowPrio, value, TOPLEVEL::type::Write, type, TOPLEVEL::section::main, root, UNUSED, UNUSED, UNUSED, UNUSED, textMsgBuffer.push(name));
6395             return Reply{REPLY::done_msg, replyMsg};
6396         }
6397     }
6398 
6399     if (input.matchnMove(3, "remove"))
6400     {
6401         if  (input.matchnMove(1, "root"))
6402         {
6403             if (input.isdigit())
6404             {
6405                 int rootID = string2int(input);
6406                 if (rootID >= MAX_BANK_ROOT_DIRS)
6407                     return Reply{REPLY::range_msg};
6408                 else
6409                 {
6410                     sendDirect(synth, TOPLEVEL::action::lowPrio, 0, TOPLEVEL::type::Write,BANK::deselectRoot, TOPLEVEL::section::bank, rootID);
6411                     return Reply::DONE;
6412                 }
6413             }
6414             else
6415                 return Reply{REPLY::value_msg};
6416         }
6417         if (input.matchnMove(1, "bank"))
6418         {
6419             if (!input.isdigit())
6420                 return Reply{REPLY::value_msg};
6421 
6422             int bankID = string2int(input);
6423             if (bankID >= MAX_BANKS_IN_ROOT)
6424                 return Reply{REPLY::range_msg};
6425 
6426             int rootID = readControl(synth, 0, BANK::control::selectRoot, TOPLEVEL::section::bank);
6427             input.skipChars();
6428             if (!input.isAtEnd())
6429             {
6430                 if (input.matchnMove(1, "root"))
6431                 {
6432                     if (!input.isdigit())
6433                         return Reply{REPLY::value_msg};
6434                     rootID = string2int(input);
6435                     if (rootID >= MAX_BANK_ROOT_DIRS)
6436                         return Reply{REPLY::range_msg};
6437                 }
6438             }
6439             int tmp = int(readControl(synth, TOPLEVEL::action::lowPrio, BANK::control::findBankSize, TOPLEVEL::section::bank, bankID, rootID));
6440             if (tmp == UNUSED)
6441             {
6442                 Runtime.Log("No bank at this location");
6443                 return Reply::DONE;
6444             }
6445             else if (tmp)
6446             {
6447                 Runtime.Log("Bank " + to_string(bankID) + " has " + asString(tmp) + " Instruments");
6448                 if (!query("Delete bank and all of these", false))
6449                 {
6450                     Runtime.Log("Aborted");
6451                     return Reply::DONE;
6452                 }
6453             }
6454             sendDirect(synth, TOPLEVEL::action::lowPrio, bankID, TOPLEVEL::type::Write, MAIN::control::deleteBank, TOPLEVEL::section::main, rootID);
6455             return Reply::DONE;
6456         }
6457         if (input.matchnMove(2, "yoshimi"))
6458         {
6459             if (input.isAtEnd())
6460             {
6461                 return Reply::what("remove");
6462             }
6463             else
6464             {
6465                 unsigned int to_close = string2int(input);
6466                 if (to_close == 0)
6467                     Runtime.Log("Use 'Exit' to close main instance");
6468                 else if (to_close == currentInstance)
6469                     Runtime.Log("Instance can't close itself");
6470                 else
6471                 {
6472                     sendDirect(synth, TOPLEVEL::action::lowPrio, to_close, TOPLEVEL::type::Write, MAIN::control::stopInstance, TOPLEVEL::section::main);
6473                 }
6474                 return Reply::DONE;
6475             }
6476         }
6477         if (input.matchnMove(2, "mlearn"))
6478         {
6479             if (input.matchnMove(3, "all"))
6480             {
6481                 sendNormal(synth, 0, 0, 0, MIDILEARN::control::clearAll, TOPLEVEL::section::midiLearn);
6482                 return Reply::DONE;
6483             }
6484             else if (input.nextChar('@'))
6485             {
6486                 input.skip(1);
6487                 input.skipSpace();
6488                 int tmp = string2int(input);
6489                 if (tmp == 0)
6490                     return Reply{REPLY::value_msg};
6491                 sendNormal(synth, 0, tmp - 1, 0, MIDILEARN::control::deleteLine, TOPLEVEL::section::midiLearn);
6492                 return Reply::DONE;
6493             }
6494         }
6495         if (input.matchnMove(2, "instrument") || input.matchnMove(2, "program"))
6496         {
6497             int tmp = string2int(input);
6498             if (tmp <= 0 || tmp > MAX_INSTRUMENTS_IN_BANK)
6499                     return Reply{REPLY::range_msg};
6500             if (query("Permanently remove instrument " + to_string(tmp) + " from bank", false))
6501                 sendDirect(synth, TOPLEVEL::action::lowPrio, tmp - 1, TOPLEVEL::type::Write, BANK::control::deleteInstrument, TOPLEVEL::section::bank);
6502             return Reply::DONE;
6503         }
6504         return Reply::what("remove");
6505     }
6506 
6507     else if (input.matchnMove(2, "load"))
6508     {
6509         if (input.matchnMove(2, "mlearn"))
6510         {
6511             if (input.nextChar('@'))
6512             {
6513                 input.skip(1);
6514                 int tmp = string2int(input);
6515                 if (tmp == 0)
6516                     return Reply{REPLY::value_msg};
6517                 sendNormal(synth, 0, tmp - 1, TOPLEVEL::type::Write, MIDILEARN::control::loadFromRecent, TOPLEVEL::section::midiLearn);
6518                 return Reply::DONE;
6519             }
6520             if (input.isAtEnd())
6521                 return Reply{REPLY::name_msg};
6522             sendNormal(synth, 0, 0, TOPLEVEL::type::Write, MIDILEARN::control::loadList, TOPLEVEL::section::midiLearn, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, textMsgBuffer.push(string{input}));
6523             return Reply::DONE;
6524         }
6525         if (input.matchnMove(2, "vector"))
6526         {
6527             string loadChan;
6528             unsigned char ch;
6529             if (input.matchnMove(1, "channel"))
6530             {
6531                 ch = string2int127(input);
6532                 if (ch > 0)
6533                 {
6534                     ch -= 1;
6535                     input.skipChars();
6536                 }
6537                 else
6538                     ch = chan;
6539                 loadChan = "channel " + asString(ch + 1);
6540             }
6541             else
6542             {
6543                 ch = UNUSED;
6544                 loadChan = "source channel";
6545             }
6546             if (ch != UNUSED && ch >= NUM_MIDI_CHANNELS)
6547                 return Reply{REPLY::range_msg};
6548             if (input.isAtEnd())
6549                 return Reply{REPLY::name_msg};
6550             string name;
6551             if (input.nextChar('@'))
6552             {
6553                 input.skip(1);
6554                 input.skipSpace();
6555                 int tmp = string2int(input);
6556                 if (tmp <= 0)
6557                     return Reply{REPLY::value_msg};
6558                 name = historySelect(5, tmp - 1);
6559                 if (name == "")
6560                     return Reply::DONE;
6561             }
6562             else
6563             {
6564                 name = string{input};
6565                 if (name == "")
6566                     return Reply{REPLY::name_msg};
6567             }
6568             sendDirect(synth, TOPLEVEL::action::muteAndLoop, 0, TOPLEVEL::type::Write, MAIN::control::loadNamedVector, TOPLEVEL::section::main, UNUSED, UNUSED, ch, UNUSED, UNUSED, textMsgBuffer.push(name));
6569             return Reply::DONE;
6570         }
6571         if (input.matchnMove(2, "state"))
6572         {
6573             if (input.isAtEnd())
6574                 return Reply{REPLY::name_msg};
6575             string name;
6576             if (input.nextChar('@'))
6577             {
6578                 input.skip(1);
6579                 input.skipSpace();
6580                 int tmp = string2int(input);
6581                 if (tmp <= 0)
6582                     return Reply{REPLY::value_msg};
6583                 name = historySelect(4, tmp - 1);
6584                 if (name == "")
6585                     return Reply::DONE;
6586             }
6587             else
6588             {
6589                 name = string{input};
6590                 if (name == "")
6591                         return Reply{REPLY::name_msg};
6592             }
6593             sendDirect(synth, TOPLEVEL::action::muteAndLoop, 0, TOPLEVEL::type::Write, MAIN::control::loadNamedState, TOPLEVEL::section::main, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, textMsgBuffer.push(name));
6594             return Reply::DONE;
6595         }
6596         if (input.matchnMove(2, "scale"))
6597         {
6598             if (input.isAtEnd())
6599                 return Reply{REPLY::name_msg};
6600             string name;
6601             if (input.nextChar('@'))
6602             {
6603                 input.skip(1);
6604                 input.skipSpace();
6605                 int tmp = string2int(input);
6606                 if (tmp <= 0)
6607                     return Reply{REPLY::value_msg};
6608                 name = historySelect(3, tmp - 1);
6609                 if (name == "")
6610                     return Reply::DONE;
6611             }
6612             else
6613             {
6614                 name = string{input};
6615                 if (name == "")
6616                     return Reply{REPLY::name_msg};
6617             }
6618             sendDirect(synth, TOPLEVEL::action::lowPrio, 0, TOPLEVEL::type::Write, MAIN::control::loadNamedScale, TOPLEVEL::section::main, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, textMsgBuffer.push(name));
6619             return Reply::DONE;
6620         }
6621         if (input.matchnMove(1, "patchset"))
6622         {
6623             if (input.isAtEnd())
6624                 return Reply{REPLY::name_msg};
6625             string name;
6626             if (input.nextChar('@'))
6627             {
6628                 input.skip(1);
6629                 input.skipSpace();
6630                 int tmp = string2int(input);
6631                 if (tmp <= 0)
6632                     return Reply{REPLY::value_msg};
6633                 name = historySelect(2, tmp - 1);
6634                 if (name == "")
6635                     return Reply::DONE;
6636             }
6637             else
6638             {
6639                 name = string{input};
6640                 if (name == "")
6641                     return Reply{REPLY::name_msg};
6642             }
6643             sendDirect(synth, TOPLEVEL::action::muteAndLoop, 0, TOPLEVEL::type::Write, MAIN::control::loadNamedPatchset, TOPLEVEL::section::main, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, textMsgBuffer.push(name));
6644             return Reply::DONE;
6645         }
6646         if (input.matchnMove(1, "instrument"))
6647         {
6648             if (input.isAtEnd())
6649                 return Reply{REPLY::name_msg};
6650             string name;
6651             if (input.nextChar('@'))
6652             {
6653                 input.skip(1);
6654                 input.skipSpace();
6655                 int tmp = string2int(input);
6656                 if (tmp <= 0)
6657                     return Reply{REPLY::value_msg};
6658                 name = historySelect(1, tmp - 1);
6659                 if (name == "")
6660                     return Reply::DONE;
6661             }
6662             else
6663             {
6664                 name = string{input};
6665                 if (name == "")
6666                     return Reply{REPLY::name_msg};
6667             }
6668 
6669             sendDirect(synth, 0, 0, TOPLEVEL::type::Write, MAIN::control::loadInstrumentByName, TOPLEVEL::section::main, npart, UNUSED, UNUSED, UNUSED, UNUSED, textMsgBuffer.push(name));
6670             return Reply::DONE;
6671         }
6672         if  (input.matchnMove(1, "default"))
6673         {
6674             if (bitFindHigh(context) == LEVEL::Part)
6675                 return sendNormal(synth, TOPLEVEL::action::lowPrio, 0, TOPLEVEL::type::Write, PART::control::defaultInstrumentCopyright, TOPLEVEL::section::part1 + npart, UNUSED, UNUSED, UNUSED, 0);
6676             else
6677             {
6678                 synth->getRuntime().Log("Only available at part level");
6679                 return Reply::DONE;
6680             }
6681         }
6682         return Reply::what("load");
6683     }
6684 
6685     if (input.matchnMove(2, "save"))
6686     {
6687         if (input.matchnMove(2, "mlearn"))
6688         {
6689             if (input.isAtEnd())
6690                 return Reply{REPLY::name_msg};
6691 
6692             sendNormal(synth, 0, 0, TOPLEVEL::type::Write, MIDILEARN::control::saveList, TOPLEVEL::section::midiLearn, 0, 0, 0, 0, UNUSED, textMsgBuffer.push(string{input}));
6693             return Reply::DONE;
6694         }
6695         if (input.matchnMove(2, "vector"))
6696         {
6697             int tmp = chan;
6698             if (input.matchnMove(1, "channel"))
6699             {
6700                 tmp = string2int127(input) - 1;
6701                 input.skipChars();
6702             }
6703             if (tmp >= NUM_MIDI_CHANNELS || tmp < 0)
6704                 return Reply{REPLY::range_msg};
6705             if (input.isAtEnd())
6706                 return Reply{REPLY::name_msg};
6707             chan = tmp;
6708             sendDirect(synth, TOPLEVEL::action::lowPrio, 0, TOPLEVEL::type::Write, MAIN::control::saveNamedVector, TOPLEVEL::section::main, UNUSED, UNUSED, chan, UNUSED, UNUSED, textMsgBuffer.push(string{input}));
6709             return Reply::DONE;
6710         }
6711         if (input.matchnMove(2, "state"))
6712         {
6713             if (input.isAtEnd())
6714                 return Reply{REPLY::value_msg};
6715             sendDirect(synth, TOPLEVEL::action::lowPrio, 0, TOPLEVEL::type::Write, MAIN::control::saveNamedState, TOPLEVEL::section::main, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, textMsgBuffer.push(string{input}));
6716             return Reply::DONE;
6717         }
6718         if (input.matchnMove(1, "config"))
6719         {
6720             sendDirect(synth, TOPLEVEL::action::lowPrio, 0, TOPLEVEL::type::Write, CONFIG::control::saveCurrentConfig, TOPLEVEL::section::config, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, textMsgBuffer.push("DUMMY"));
6721             return Reply::DONE;
6722         }
6723 
6724         if (input.matchnMove(2, "scale"))
6725         {
6726             if (input.isAtEnd())
6727                 return Reply{REPLY::name_msg};
6728             sendDirect(synth, TOPLEVEL::action::lowPrio, 0, TOPLEVEL::type::Write, MAIN::control::saveNamedScale, TOPLEVEL::section::main, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, textMsgBuffer.push(string{input}));
6729             return Reply::DONE;
6730         }
6731         else if (input.matchnMove(1, "patchset"))
6732         {
6733             if (input.isAtEnd())
6734                 return Reply{REPLY::name_msg};
6735             sendDirect(synth, TOPLEVEL::action::lowPrio, 0, TOPLEVEL::type::Write, MAIN::control::saveNamedPatchset, TOPLEVEL::section::main, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, textMsgBuffer.push(string{input}));
6736             return Reply::DONE;
6737         }
6738         if (input.matchnMove(1, "instrument"))
6739         {
6740             if (!readControl(synth, 0, PART::control::enable, npart))
6741             return REPLY::inactive_msg;
6742 
6743             if (readControlText(synth, TOPLEVEL::action::lowPrio, PART::control::instrumentName, TOPLEVEL::section::part1 + npart) == DEFAULT_NAME)
6744             {
6745                 Runtime.Log("Nothing to save!");
6746                 return Reply::DONE;
6747             }
6748             if (input.isAtEnd())
6749                 return Reply{REPLY::name_msg};
6750             sendDirect(synth, TOPLEVEL::action::lowPrio, npart, TOPLEVEL::type::Write, MAIN::control::saveNamedInstrument, TOPLEVEL::section::main, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, textMsgBuffer.push(string{input}));
6751             return Reply::DONE;
6752         }
6753         if  (input.matchnMove(1, "default"))
6754             return sendNormal(synth, TOPLEVEL::action::lowPrio, 0, TOPLEVEL::type::Write, PART::control::defaultInstrumentCopyright, TOPLEVEL::section::part1 + npart, UNUSED, UNUSED, UNUSED, 1);
6755         return Reply::what("save");
6756     }
6757 
6758     if (input.matchnMove(2, "zread"))
6759     {
6760         /*
6761          * This is a very specific test for reading values and is intended to measure
6762          * the time these calls take. For that reason the return echos to the CLI and
6763          * GUI are suppressed, and all results are sent to the CLI only.
6764          *
6765          * It is only the selection time we are measuring, and that the correct
6766          * value is returned.
6767          *
6768          * The limit to the number of repeats is INT max. Using high repeat numbers
6769          * reduces the effect of the processing overhead outside the call loop itself.
6770          */
6771 
6772         cout << "here zread" << endl;
6773 
6774         // repeats, control, part, kit, engine, insert, parameter, miscmsg
6775         float result;
6776         unsigned char control, part;
6777         unsigned char kit = UNUSED;
6778         unsigned char engine = UNUSED;
6779         unsigned char insert = UNUSED;
6780         unsigned char parameter = UNUSED;
6781         unsigned char miscmsg = UNUSED;
6782         int repeats;
6783         if (input.isAtEnd())
6784             return REPLY::value_msg;
6785         repeats = string2int(input);
6786         if (repeats < 1)
6787             repeats = 1;
6788         input.skipChars();
6789         if (input.isAtEnd())
6790             return REPLY::value_msg;
6791         control = string2int(input);
6792         input.skipChars();
6793         if (input.isAtEnd())
6794             return REPLY::value_msg;
6795         part = string2int(input);
6796         input.skipChars();
6797         if (!input.isAtEnd())
6798         {
6799             kit = string2int(input);
6800             input.skipChars();
6801             if (!input.isAtEnd())
6802             {
6803                 engine = string2int(input);
6804                 input.skipChars();
6805                 if (!input.isAtEnd())
6806                 {
6807                     insert = string2int(input);
6808                     input.skipChars();
6809                     if (!input.isAtEnd())
6810                     {
6811                         parameter = string2int(input);
6812                         input.skipChars();
6813                         if (!input.isAtEnd())
6814                             miscmsg = string2int(input);
6815                     }
6816                 }
6817             }
6818         }
6819 
6820         CommandBlock putData;
6821         putData.data.value = 0;
6822         putData.data.control = control;
6823         putData.data.part = part;
6824         putData.data.kit = kit;
6825         putData.data.engine = engine;
6826         putData.data.insert = insert;
6827         putData.data.parameter = parameter;
6828         putData.data.miscmsg = miscmsg;
6829         putData.data.type = 0;
6830         putData.data.source = 0;
6831         struct timeval tv1, tv2;
6832         gettimeofday(&tv1, NULL);
6833         for (int i = 0; i < repeats; ++ i)
6834             result = synth->interchange.readAllData(&putData);
6835         gettimeofday(&tv2, NULL);
6836 
6837         if (tv1.tv_usec > tv2.tv_usec)
6838         {
6839             tv2.tv_sec--;
6840             tv2.tv_usec += 1000000;
6841             }
6842         float actual = (tv2.tv_sec - tv1.tv_sec) *1000000 + (tv2.tv_usec - tv1.tv_usec);
6843         cout << "result " << result << endl;
6844         cout << "Loops " << repeats << "  Total time " << actual << "uS" << "  average call time " << actual/repeats * 1000.0f << "nS" << endl;
6845         return REPLY::done_msg;
6846     }
6847 
6848     // legacyCLIaccess goes here
6849 
6850     return REPLY::unrecognised_msg;
6851 }
6852 
6853 
6854 }//(End)namespace cli
6855