1 /*
2 CliFuncs.h
3
4 Copyright 2019, Will Godfrey.
5
6 Copyright 2021, Rainer Hans Liffers
7
8 This file is part of yoshimi, which is free software: you can
9 redistribute it and/or modify it under the terms of the GNU General
10 Public License as published by the Free Software Foundation, either
11 version 2 of the License, or (at your option) any later version.
12
13 yoshimi is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with yoshimi. If not, see <http://www.gnu.org/licenses/>.
20
21 */
22
23 #ifndef CLIFUNCS_H
24 #define CLIFUNCS_H
25
26 #include <cmath>
27 #include <string>
28 #include <cstring>
29
30 #include <readline/readline.h>
31 #include <cassert>
32
33 #include "CLI/Parser.h"
34 #include "Interface/TextLists.h"
35 #include "Misc/SynthEngine.h"
36 #include "Misc/TextMsgBuffer.h"
37 #include "Misc/NumericFuncs.h"
38 #include "Misc/FormatFuncs.h"
39
40
41 namespace cli {
42
43 using func::bitTest;
44 using func::bitFindHigh;
45
46 using func::asString;
47
48 using std::string;
49
50
contextToEngines(int context)51 inline int contextToEngines(int context)
52 {
53 int engine = UNUSED;
54 if (bitTest(context, LEVEL::SubSynth))
55 engine = PART::engine::subSynth;
56 else if (bitTest(context, LEVEL::PadSynth))
57 engine = PART::engine::padSynth;
58 else if (bitTest(context, LEVEL::AddMod))
59 engine = PART::engine::addMod1;
60 else if (bitTest(context, LEVEL::AddVoice))
61 engine = PART::engine::addVoice1;
62 else if (bitTest(context, LEVEL::AddSynth))
63 engine = PART::engine::addSynth;
64 return engine;
65 }
66
67
68 inline float readControl(SynthEngine *synth,
69 unsigned char action, unsigned char control, unsigned char part,
70 unsigned char kit = UNUSED,
71 unsigned char engine = UNUSED,
72 unsigned char insert = UNUSED,
73 unsigned char parameter = UNUSED,
74 unsigned char offset = UNUSED,
75 unsigned char miscmsg = NO_MSG)
76 {
77 float value;
78 CommandBlock putData;
79
80 putData.data.value = 0;
81 putData.data.type = 0;
82 putData.data.source = action;
83 putData.data.control = control;
84 putData.data.part = part;
85 putData.data.kit = kit;
86 putData.data.engine = engine;
87 putData.data.insert = insert;
88 putData.data.parameter = parameter;
89 putData.data.offset = offset;
90 putData.data.miscmsg = miscmsg;
91 value = synth->interchange.readAllData(&putData);
92 //if (putData.data.type & TOPLEVEL::type::Error)
93 //return 0xfffff;
94 //std::cout << "err" << std::endl;
95 return value;
96 }
97
98
99 inline string readControlText(SynthEngine *synth,
100 unsigned char action, unsigned char control, unsigned char part,
101 unsigned char kit = UNUSED,
102 unsigned char engine = UNUSED,
103 unsigned char insert = UNUSED,
104 unsigned char parameter = UNUSED,
105 unsigned char offset = UNUSED)
106 {
107 float value;
108 CommandBlock putData;
109
110 putData.data.value = 0;
111 putData.data.type = 0;
112 putData.data.source = action;
113 putData.data.control = control;
114 putData.data.part = part;
115 putData.data.kit = kit;
116 putData.data.engine = engine;
117 putData.data.insert = insert;
118 putData.data.parameter = parameter;
119 putData.data.offset = offset;
120 putData.data.miscmsg = UNUSED;
121 value = synth->interchange.readAllData(&putData);
122 return TextMsgBuffer::instance().fetch(value);
123 }
124
125
readLimits(SynthEngine * synth,float value,unsigned char type,unsigned char control,unsigned char part,unsigned char kit,unsigned char engine,unsigned char insert,unsigned char parameter,unsigned char miscmsg)126 inline void readLimits(SynthEngine *synth,
127 float value, unsigned char type, unsigned char control, unsigned char part,
128 unsigned char kit, unsigned char engine, unsigned char insert,
129 unsigned char parameter, unsigned char miscmsg)
130 {
131 CommandBlock putData;
132
133 putData.data.value = value;
134 putData.data.type = type;
135 putData.data.control = control;
136 putData.data.part = part;
137 putData.data.kit = kit;
138 putData.data.engine = engine;
139 putData.data.insert = insert;
140 putData.data.parameter = parameter;
141 putData.data.miscmsg = miscmsg;
142
143 value = synth->interchange.readAllData(&putData);
144 string name;
145 switch (type & 3)
146 {
147 case TOPLEVEL::type::Minimum:
148 name = "Min ";
149 break;
150 case TOPLEVEL::type::Maximum:
151 name = "Max ";
152 break;
153 default:
154 name = "Default ";
155 break;
156 }
157 type = putData.data.type;
158 if ((type & TOPLEVEL::type::Integer) == 0)
159 name += std::to_string(value);
160 else if (value < 0)
161 name += std::to_string(int(value - 0.5f));
162 else
163 name += std::to_string(int(value + 0.5f));
164 if (type & TOPLEVEL::type::Error)
165 name += " - error";
166 else if (type & TOPLEVEL::type::Learnable)
167 name += " - learnable";
168 synth->getRuntime().Log(name);
169 }
170
171
172 inline int sendNormal(SynthEngine *synth,
173 unsigned char action, float value, unsigned char type, unsigned char control, unsigned char part,
174 unsigned char kit = UNUSED,
175 unsigned char engine = UNUSED,
176 unsigned char insert = UNUSED,
177 unsigned char parameter = UNUSED,
178 unsigned char offset = UNUSED,
179 unsigned char miscmsg = NO_MSG)
180 {
181 if ((type & TOPLEVEL::type::Limits) && part != TOPLEVEL::section::midiLearn)
182 {
183 readLimits(synth, value, type, control, part, kit, engine, insert, parameter, miscmsg);
184 return REPLY::done_msg;
185 }
186 action |= TOPLEVEL::action::fromCLI;
187
188 CommandBlock putData;
189
190 putData.data.value = value;
191 putData.data.type = type;
192 putData.data.control = control;
193 putData.data.part = part;
194 putData.data.kit = kit;
195 putData.data.engine = engine;
196 putData.data.insert = insert;
197 putData.data.parameter = parameter;
198 putData.data.offset = offset;
199 putData.data.miscmsg = miscmsg;
200
201 /*
202 * MIDI learn settings are synced by the audio thread
203 * but not passed on to any of the normal controls.
204 * The type field is used for a different purpose.
205 */
206
207 if (part != TOPLEVEL::section::midiLearn)
208 {
209 putData.data.type |= TOPLEVEL::type::Limits;
210 float newValue = synth->interchange.readAllData(&putData);
211 if (type & TOPLEVEL::type::LearnRequest)
212 {
213 if ((putData.data.type & TOPLEVEL::type::Learnable) == 0)
214 {
215 synth->getRuntime().Log("Can't learn this control");
216 return REPLY::failed_msg;
217 }
218 }
219 else
220 {
221 if (putData.data.type & TOPLEVEL::type::Error)
222 return REPLY::available_msg;
223 if (newValue != value && (type & TOPLEVEL::type::Write))
224 { // checking the original type not the reported one
225 putData.data.value = newValue;
226 synth->getRuntime().Log("Range adjusted");
227 }
228 }
229 action |= TOPLEVEL::action::fromCLI;
230 }
231 putData.data.source = action;
232 putData.data.type = type;
233 if (synth->interchange.fromCLI.write(putData.bytes))
234 {
235 synth->getRuntime().finishedCLI = false;
236 }
237 else
238 {
239 synth->getRuntime().Log("Unable to write to fromCLI buffer");
240 return REPLY::failed_msg;
241 }
242 return REPLY::done_msg;
243 }
244
245
246 inline int sendDirect(SynthEngine *synth,
247 unsigned char action, float value, unsigned char type, unsigned char control, unsigned char part,
248 unsigned char kit = UNUSED,
249 unsigned char engine = UNUSED,
250 unsigned char insert = UNUSED,
251 unsigned char parameter = UNUSED,
252 unsigned char offset = UNUSED,
253 unsigned char miscmsg = NO_MSG,
254 unsigned char request = UNUSED)
255 {
256 if (action == TOPLEVEL::action::fromMIDI && part != TOPLEVEL::section::midiLearn)
257 request = type & TOPLEVEL::type::Default;
258 CommandBlock putData;
259
260 putData.data.value = value;
261 putData.data.control = control;
262 putData.data.part = part;
263 putData.data.kit = kit;
264 putData.data.engine = engine;
265 putData.data.insert = insert;
266 putData.data.parameter = parameter;
267 putData.data.offset = offset;
268 putData.data.miscmsg = miscmsg;
269
270 if (type == TOPLEVEL::type::Default)
271 {
272 putData.data.type = TOPLEVEL::type::Limits;
273 synth->interchange.readAllData(&putData);
274 if ((putData.data.type & TOPLEVEL::type::Learnable) == 0)
275 {
276 synth->getRuntime().Log("Can't learn this control");
277 return 0;
278 }
279 }
280
281 if (part != TOPLEVEL::section::midiLearn)
282 action |= TOPLEVEL::action::fromCLI;
283 /*
284 * MIDI learn is synced by the audio thread but
285 * not passed on to any of the normal controls.
286 * The type field is used for a different purpose.
287 */
288 putData.data.source = action | TOPLEVEL::action::fromCLI;
289 putData.data.type = type;
290 if (request < TOPLEVEL::type::Limits)
291 {
292 putData.data.type = request | TOPLEVEL::type::Limits;
293 value = synth->interchange.readAllData(&putData);
294 string name;
295 switch (request)
296 {
297 case TOPLEVEL::type::Minimum:
298 name = "Min ";
299 break;
300 case TOPLEVEL::type::Maximum:
301 name = "Max ";
302 break;
303 default:
304 name = "Default ";
305 break;
306 }
307 type = putData.data.type;
308 if ((type & TOPLEVEL::type::Integer) == 0)
309 name += std::to_string(value);
310 else if (value < 0)
311 name += std::to_string(int(value - 0.5f));
312 else
313 name += std::to_string(int(value + 0.5f));
314 if (type & TOPLEVEL::type::Error)
315 name += " - error";
316 else if (type & TOPLEVEL::type::Learnable)
317 name += " - learnable";
318 synth->getRuntime().Log(name);
319 return 0;
320 }
321
322 if (part == TOPLEVEL::section::main && (type & TOPLEVEL::type::Write) == 0 && control >= MAIN::control::readPartPeak && control <= MAIN::control::readMainLRrms)
323 {
324 string name;
325 switch (control)
326 {
327 case MAIN::control::readPartPeak:
328 name = "part " + std::to_string(int(kit));
329 if (engine == 0)
330 name += "L ";
331 else
332 name += "R ";
333 name += "peak ";
334 break;
335 case MAIN::control::readMainLRpeak:
336 name = "main ";
337 if (kit == 0)
338 name += "L ";
339 else
340 name += "R ";
341 name += "peak ";
342 break;
343 case MAIN::control::readMainLRrms:
344 name = "main ";
345 if (kit == 0)
346 name += "L ";
347 else
348 name += "R ";
349 name += "RMS ";
350 break;
351 }
352 value = synth->interchange.readAllData(&putData);
353 synth->getRuntime().Log(name + std::to_string(value));
354 return 0;
355 }
356
357 if (part == TOPLEVEL::section::config && putData.data.miscmsg != UNUSED && (control == CONFIG::control::bankRootCC || control == CONFIG::control::bankCC || control == CONFIG::control::extendedProgramChangeCC))
358 {
359 synth->getRuntime().Log("In use by " + TextMsgBuffer::instance().fetch(putData.data.miscmsg) );
360 return 0;
361 }
362
363 if (parameter != UNUSED && (parameter & TOPLEVEL::action::lowPrio))
364 action |= (parameter & TOPLEVEL::action::muteAndLoop); // transfer low prio and loopback
365 putData.data.source = action;
366
367 if (synth->interchange.fromCLI.write(putData.bytes))
368 {
369 synth->getRuntime().finishedCLI = false;
370 }
371 else
372 synth->getRuntime().Log("Unable to write to fromCLI buffer");
373 return 0; // no function for this yet
374 }
375
376
377 }//(End)namespace cli
378 #endif /*CLIFUNCS_H*/
379