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