1 // omc-learn.c
2 // LiVES (lives-exe)
3 // (c) G. Finch 2008 - 2018 <salsaman+lives@gmail.com>
4 // Released under the GPL 3 or later
5 // see file ../COPYING for licensing details
6
7 #ifdef ENABLE_OSC
8
9 #include "main.h"
10 #include "paramwindow.h"
11 #include "effects.h"
12 #include "interface.h"
13 #include "callbacks.h"
14
15 #include "omc-learn.h"
16
17 #ifdef OMC_JS_IMPL
18 #include <linux/joystick.h>
19 #endif
20
21 #include <errno.h>
22
23 // learn and match with an external control
24 // generally, external data is passed in as a type and a string (a sequence ascii encoded ints separated by spaces)
25 // the string will have a fixed sig(nature) which is matched against learned nodes
26 //
27 // the number of fixed values depends on the origin of the data; for example for a MIDI controller
28 // it is 2 (controller + controller number)
29 // the rest of the string is variables. These are either mapped in order to the parameters of the macro or can be filtered against
30
31 // these types/strings are matched against OMC macros -
32 // the macros have slots for parameters which are filled in order from variables in the input
33
34 // TODO !! - greedy matching should done - i.e. if an input sequence matches more than one macro,
35 // each of those macros will be triggered
36 // for now, only first match is acted on
37
38 // some events are filtered out, for example MIDI_NOTE_OFF, joystick button release; this needs to be done automatically
39
40 // TODO: we need end up with a table (struct *) like:
41 // int supertype;
42 // int ntypes;
43 // int *nfixed;
44 // int **min;
45 // int **max;
46 // boolean *uses_index;
47 // char **ignore;
48
49 // where min/max are not known we will need to calibrate
50
51 static OSCbuf obuf;
52 static char byarr[OSC_BUF_SIZE];
53 static lives_omc_macro_t omc_macros[N_OMC_MACROS];
54 static LiVESSList *omc_node_list;
55 static boolean omc_macros_inited = FALSE;
56
57 static void init_omc_macros(void);
58
59 //////////////////////////////////////////////////////////////
get_omc_macro(int idx)60 const lives_omc_macro_t *get_omc_macro(int idx) {
61 if (!omc_macros_inited) {
62 init_omc_macros();
63 omc_macros_inited = TRUE;
64 OSC_initBuffer(&obuf, OSC_BUF_SIZE, byarr);
65 }
66
67 if (idx >= N_OMC_MACROS || !omc_macros[idx].msg) return NULL;
68
69 return &omc_macros[idx];
70 }
71
72
has_devicemap(int target)73 boolean has_devicemap(int target) {
74 if (target != -1) {
75 lives_omc_match_node_t *mnode;
76 LiVESSList *slist = omc_node_list;
77 while (slist) {
78 mnode = (lives_omc_match_node_t *)slist->data;
79 if (mnode->macro == target) return TRUE;
80 slist = slist->next;
81 }
82 return FALSE;
83 }
84 return (omc_node_list != NULL);
85 }
86
87
omc_match_node_free(lives_omc_match_node_t * mnode)88 static void omc_match_node_free(lives_omc_match_node_t *mnode) {
89 if (mnode->nvars > 0) {
90 lives_free(mnode->offs0); lives_free(mnode->scale); lives_free(mnode->offs1);
91 lives_free(mnode->min); lives_free(mnode->max);
92 lives_free(mnode->matchp); lives_free(mnode->matchi);
93 }
94
95 if (mnode->map) lives_free(mnode->map);
96 if (mnode->fvali) lives_free(mnode->fvali);
97 if (mnode->fvald) lives_free(mnode->fvald);
98
99 lives_free(mnode->srch);
100
101 lives_free(mnode);
102 }
103
104
remove_all_nodes(boolean every,omclearn_w * omclw)105 static void remove_all_nodes(boolean every, omclearn_w *omclw) {
106 lives_omc_match_node_t *mnode;
107 LiVESSList *slist_last = NULL, *slist_next;
108 LiVESSList *slist = omc_node_list;
109
110 while (slist) {
111 slist_next = slist->next;
112
113 mnode = (lives_omc_match_node_t *)slist->data;
114
115 if (every || mnode->macro == UNMATCHED) {
116 if (slist_last) slist_last->next = slist->next;
117 else omc_node_list = slist->next;
118 omc_match_node_free(mnode);
119 } else slist_last = slist;
120 slist = slist_next;
121 }
122
123 lives_widget_set_sensitive(omclw->clear_button, FALSE);
124 if (!slist) lives_widget_set_sensitive(omclw->del_all_button, FALSE);
125 mainw->midi_channel_lock = FALSE;
126 }
127
128
js_index(const char * string)129 LIVES_INLINE int js_index(const char *string) {
130 // js index, or midi channel number
131 char **array = lives_strsplit(string, " ", -1);
132 int res = atoi(array[1]);
133 lives_strfreev(array);
134 return res;
135 }
136
137
midi_msg_type(const char * string)138 static int midi_msg_type(const char *string) {
139 int type = atoi(string);
140
141 if ((type & 0XF0) == 0X90) return OMC_MIDI_NOTE; // data: note, velocity
142 if ((type & 0XF0) == 0x80) return OMC_MIDI_NOTE_OFF; // data: note, velocity
143 if ((type & 0XF0) == 0xB0) return OMC_MIDI_CONTROLLER; // data: controller number, data
144 if ((type & 0XF0) == 0xC0) return OMC_MIDI_PGM_CHANGE; // data: program number
145 if ((type & 0XF0) == 0xE0) return OMC_MIDI_PITCH_BEND; // data: lsb, msb
146
147 // other types are currently ignored:
148
149 // 0XA0 is polyphonic aftertouch, has note and pressure
150
151 // 0xD0 is channel aftertouch, 1 byte pressure
152
153 // 0XF0 - 0xFF is sysex
154
155 return 0;
156 }
157
158
get_nfixed(int type,const char * string)159 static int get_nfixed(int type, const char *string) {
160 int nfixed = 0;
161
162 switch (type) {
163 case OMC_JS_BUTTON:
164 nfixed = 3; // type, index, value
165 break;
166 case OMC_JS_AXIS:
167 nfixed = 2; // type, index
168 break;
169 #ifdef OMC_MIDI_IMPL
170 case OMC_MIDI:
171 type = midi_msg_type(string);
172 return get_nfixed(type, NULL);
173 case OMC_MIDI_CONTROLLER:
174 if (prefs->midi_rcv_channel > MIDI_OMNI) nfixed = 2; // type, cnum
175 else nfixed = 3; // type, channel, cnum
176 break;
177 case OMC_MIDI_NOTE:
178 case OMC_MIDI_NOTE_OFF:
179 case OMC_MIDI_PITCH_BEND:
180 case OMC_MIDI_PGM_CHANGE:
181 if (prefs->midi_rcv_channel > MIDI_OMNI) nfixed = 1; // type
182 else nfixed = 2; // type, channel
183 break;
184 #endif
185 }
186 return nfixed;
187 }
188
189
midi_index(const char * string)190 LIVES_INLINE int midi_index(const char *string) {
191 // midi controller number
192 char **array;
193 int res;
194 int nfixed = get_nfixed(OMC_MIDI_CONTROLLER, NULL);
195
196 if (get_token_count(string, ' ') < nfixed) return -1;
197
198 array = lives_strsplit(string, " ", -1);
199 res = atoi(array[nfixed - 1]);
200 lives_strfreev(array);
201 return res;
202 }
203
204 #ifdef OMC_JS_IMPL
205
206 static int js_fd;
207
208
209 #ifndef IS_MINGW
get_js_filename(void)210 const char *get_js_filename(void) {
211 char *js_fname;
212
213 // OPEN DEVICE FILE
214 // first try to open /dev/input/js
215 js_fname = "/dev/input/js";
216 js_fd = open(js_fname, O_RDONLY | O_NONBLOCK);
217 if (js_fd < 0) {
218 // if it doesn't open, try to open /dev/input/js0
219 js_fname = "/dev/input/js0";
220 js_fd = open(js_fname, O_RDONLY | O_NONBLOCK);
221 if (js_fd < 0) {
222 js_fname = "/dev/js0";
223 js_fd = open(js_fname, O_RDONLY | O_NONBLOCK);
224 // if no device is found
225 if (js_fd < 0) {
226 return NULL;
227 }
228 }
229 }
230 return js_fname;
231 }
232 #endif
233
234
js_open(void)235 boolean js_open(void) {
236 if (!(prefs->omc_dev_opts & OMC_DEV_JS)) return TRUE;
237
238 if (strlen(prefs->omc_js_fname)) {
239 js_fd = open(prefs->omc_js_fname, O_RDONLY | O_NONBLOCK);
240 if (js_fd < 0) return FALSE;
241 } else {
242 const char *tmp = get_js_filename();
243 if (tmp) {
244 lives_snprintf(prefs->omc_js_fname, 256, "%s", tmp);
245 }
246 }
247 if (!strlen(prefs->omc_js_fname)) return FALSE;
248
249 mainw->ext_cntl[EXT_CNTL_JS] = TRUE;
250 d_print(_("Responding to joystick events from %s\n"), prefs->omc_js_fname);
251
252 return TRUE;
253 }
254
255
js_close(void)256 void js_close(void) {
257 if (mainw->ext_cntl[EXT_CNTL_JS]) {
258 close(js_fd);
259 mainw->ext_cntl[EXT_CNTL_JS] = FALSE;
260 }
261 }
262
263
js_mangle(void)264 char *js_mangle(void) {
265 // get js event and process it
266 struct js_event jse;
267 size_t bytes;
268 char *ret;
269 int type = 0;
270
271 bytes = read(js_fd, &jse, sizeof(jse));
272
273 if (bytes != sizeof(jse)) return NULL;
274
275 jse.type &= ~JS_EVENT_INIT; /* ignore synthetic events */
276 if (jse.type == JS_EVENT_AXIS) {
277 type = OMC_JS_AXIS;
278 if (jse.value == 0) return NULL;
279 } else if (jse.type == JS_EVENT_BUTTON) {
280 if (jse.value == 0) return NULL;
281 type = OMC_JS_BUTTON;
282 }
283
284 ret = lives_strdup_printf("%d %d %d", type, jse.number, jse.value);
285
286 return ret;
287 }
288
289 #endif // OMC_JS
290
js_msg_type(const char * string)291 LIVES_INLINE int js_msg_type(const char *string) {
292 return atoi(string);
293 }
294
295
296 #ifdef OMC_MIDI_IMPL
297
298 static int midi_fd;
299
300 #ifndef IS_MINGW
301
get_midi_filename(void)302 const char *get_midi_filename(void) {
303 char *midi_fname;
304
305 // OPEN DEVICE FILE
306 midi_fname = "/dev/midi";
307 midi_fd = open(midi_fname, O_RDONLY | O_NONBLOCK);
308 if (midi_fd < 0) {
309 midi_fname = "/dev/midi0";
310 midi_fd = open(midi_fname, O_RDONLY | O_NONBLOCK);
311 if (midi_fd < 0) {
312 midi_fname = "/dev/midi1";
313 midi_fd = open(midi_fname, O_RDONLY | O_NONBLOCK);
314 if (midi_fd < 0) {
315 return NULL;
316 }
317 }
318 }
319 return midi_fname;
320 }
321
322 #endif
323
324
midi_open(void)325 boolean midi_open(void) {
326 if (!(prefs->omc_dev_opts & OMC_DEV_MIDI)) return TRUE;
327
328 #ifdef ALSA_MIDI
329 if (prefs->use_alsa_midi) {
330 d_print(_("Creating ALSA MIDI port(s)..."));
331 mainw->alsa_midi_dummy = -1;
332
333 // Open an ALSA MIDI port
334 if (snd_seq_open(&mainw->seq_handle, "default", SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK) < 0) {
335 d_print_failed();
336 return FALSE;
337 }
338
339 d_print("\n");
340
341 snd_seq_set_client_name(mainw->seq_handle, "LiVES");
342 d_print(_("MIDI IN port..."));
343 if ((mainw->alsa_midi_port = snd_seq_create_simple_port(mainw->seq_handle, "LiVES",
344 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
345 SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_PORT | SND_SEQ_PORT_TYPE_SOFTWARE)) < 0) {
346 snd_seq_close(mainw->seq_handle);
347 mainw->seq_handle = NULL;
348 d_print_failed();
349 return FALSE;
350 }
351 if (prefs->alsa_midi_dummy) {
352 d_print_done();
353 d_print(_("dummy MIDI OUT port..."));
354 // create dummy MIDI out if asked to. Some clients use the name for reference.
355 if ((mainw->alsa_midi_dummy = snd_seq_create_simple_port(mainw->seq_handle,
356 "LiVES", // some external clients read this name,
357 //but will actually send to the WRITE port with same name
358 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, // need both
359 SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_PORT | SND_SEQ_PORT_TYPE_SOFTWARE)) < 0) {
360 snd_seq_delete_simple_port(mainw->seq_handle, mainw->alsa_midi_port);
361 snd_seq_close(mainw->seq_handle);
362 mainw->seq_handle = NULL;
363 d_print_failed();
364 return FALSE;
365 }
366 }
367 d_print_done();
368 } else {
369 #endif
370
371 #ifndef IS_MINGW
372 if (strlen(prefs->omc_midi_fname)) {
373 midi_fd = open(prefs->omc_midi_fname, O_RDONLY | O_NONBLOCK);
374 if (midi_fd < 0) return FALSE;
375 } else {
376 const char *tmp = get_midi_filename();
377 if (tmp) {
378 lives_snprintf(prefs->omc_midi_fname, 256, "%s", tmp);
379 }
380 }
381 if (!strlen(prefs->omc_midi_fname)) return FALSE;
382
383 d_print(_("Responding to MIDI events from %s\n"), prefs->omc_midi_fname);
384 #endif
385
386 #ifdef ALSA_MIDI
387 }
388 #endif
389
390 mainw->ext_cntl[EXT_CNTL_MIDI] = TRUE;
391
392 return TRUE;
393 }
394
395
midi_close(void)396 void midi_close(void) {
397 if (mainw->ext_cntl[EXT_CNTL_MIDI]) {
398 #ifdef ALSA_MIDI
399 if (mainw->seq_handle) {
400 // close
401 snd_seq_delete_simple_port(mainw->seq_handle, mainw->alsa_midi_port);
402 if (mainw->alsa_midi_dummy >= 0) snd_seq_delete_simple_port(mainw->seq_handle, mainw->alsa_midi_dummy);
403 snd_seq_close(mainw->seq_handle);
404 mainw->seq_handle = NULL;
405 } else {
406 #endif
407 close(midi_fd);
408
409 #ifdef ALSA_MIDI
410 }
411 #endif
412 mainw->ext_cntl[EXT_CNTL_MIDI] = FALSE;
413 }
414 }
415
416
get_midi_len(int msgtype)417 static int get_midi_len(int msgtype) {
418 switch (msgtype) {
419 case OMC_MIDI_CONTROLLER:
420 case OMC_MIDI_NOTE:
421 case OMC_MIDI_PITCH_BEND:
422 case OMC_MIDI_NOTE_OFF:
423 return 3;
424 case OMC_MIDI_PGM_CHANGE:
425 return 2;
426 }
427 return 0;
428 }
429
430
midi_mangle(void)431 char *midi_mangle(void) {
432 // get MIDI event and process it
433 char *string = NULL;
434
435 ssize_t bytes, tot = 0, allowed = prefs->midi_rpt;
436 unsigned char midbuf[4], xbuf[4];
437 int target = 1, mtype = 0, channel;
438 boolean got_target = FALSE;
439
440 #ifdef ALSA_MIDI
441 int npfd = 0;
442 struct pollfd *pfd = NULL;
443 snd_seq_event_t *ev;
444 int typeNumber;
445 boolean hasmore = FALSE;
446
447 if (mainw->seq_handle) {
448 if (snd_seq_event_input_pending(mainw->seq_handle, 0) == 0) {
449 // returns number of poll descriptors
450 npfd = snd_seq_poll_descriptors_count(mainw->seq_handle, POLLIN);
451
452 if (npfd < 1) return NULL;
453
454 pfd = (struct pollfd *)lives_malloc(npfd * sizeof(struct pollfd));
455
456 // fill our poll descriptors
457 snd_seq_poll_descriptors(mainw->seq_handle, pfd, npfd, POLLIN);
458 } else hasmore = TRUE; // events remaining from the last call to this function
459
460 if (hasmore || poll(pfd, npfd, 0) > 0) {
461 do {
462 if (snd_seq_event_input(mainw->seq_handle, &ev) < 0) {
463 break; // an error occurred reading from the port
464 }
465
466 switch (ev->type) {
467 case SND_SEQ_EVENT_CONTROLLER:
468 if (prefs->midi_rcv_channel != MIDI_OMNI && ev->data.control.channel != prefs->midi_rcv_channel) break;
469 typeNumber = 176;
470 if (prefs->midi_rcv_channel == MIDI_OMNI)
471 string = lives_strdup_printf("%d %d %u %d", typeNumber + ev->data.control.channel, ev->data.control.channel,
472 ev->data.control.param,
473 ev->data.control.value);
474 else
475 string = lives_strdup_printf("%d %u %d", typeNumber, ev->data.control.param,
476 ev->data.control.value);
477
478 break;
479 case SND_SEQ_EVENT_PITCHBEND:
480 if (prefs->midi_rcv_channel != MIDI_OMNI && ev->data.control.channel != prefs->midi_rcv_channel) break;
481 typeNumber = 224;
482 if (prefs->midi_rcv_channel == MIDI_OMNI)
483 string = lives_strdup_printf("%d %d %d", typeNumber + ev->data.control.channel, ev->data.control.channel,
484 ev->data.control.value);
485 else
486 string = lives_strdup_printf("%d %d", typeNumber, ev->data.control.value);
487 break;
488
489 case SND_SEQ_EVENT_NOTEON:
490 if (prefs->midi_rcv_channel != MIDI_OMNI && ev->data.note.channel != prefs->midi_rcv_channel) break;
491 typeNumber = 144;
492 if (prefs->midi_rcv_channel == MIDI_OMNI)
493 string = lives_strdup_printf("%d %d %d %d", typeNumber + ev->data.note.channel,
494 ev->data.note.channel, ev->data.note.note,
495 ev->data.note.velocity);
496 else
497 string = lives_strdup_printf("%d %d %d", typeNumber, ev->data.note.note,
498 ev->data.note.velocity);
499
500 break;
501 case SND_SEQ_EVENT_NOTEOFF:
502 if (prefs->midi_rcv_channel != MIDI_OMNI && ev->data.note.channel != prefs->midi_rcv_channel) break;
503 typeNumber = 128;
504 if (prefs->midi_rcv_channel == MIDI_OMNI)
505 string = lives_strdup_printf("%d %d %d %d", typeNumber + ev->data.note.channel,
506 ev->data.note.channel, ev->data.note.note,
507 ev->data.note.off_velocity);
508 else
509 string = lives_strdup_printf("%d %d %d", typeNumber, ev->data.note.note,
510 ev->data.note.off_velocity);
511
512 break;
513 case SND_SEQ_EVENT_PGMCHANGE:
514 if (prefs->midi_rcv_channel != MIDI_OMNI && ev->data.note.channel != prefs->midi_rcv_channel) break;
515 typeNumber = 192;
516 if (prefs->midi_rcv_channel == MIDI_OMNI)
517 string = lives_strdup_printf("%d %d %d", typeNumber + ev->data.note.channel,
518 ev->data.note.channel, ev->data.control.value);
519 else
520 string = lives_strdup_printf("%d %d", typeNumber, ev->data.control.value);
521
522 break;
523 }
524 snd_seq_free_event(ev);
525 } while (snd_seq_event_input_pending(mainw->seq_handle, 0) > 0 && !string);
526 }
527
528 if (pfd) lives_free(pfd);
529 } else {
530 #endif
531 if (midi_fd == -1) return NULL;
532
533 while (tot < target) {
534 bytes = read(midi_fd, xbuf, target - tot);
535
536 if (bytes < 1) {
537 if (--allowed < 0) return NULL;
538 continue;
539 }
540
541 if (!got_target) {
542 char *str = lives_strdup_printf("%d", xbuf[0]);
543 target = get_midi_len((mtype = midi_msg_type(str)));
544 lives_free(str);
545 }
546
547 //g_print("midi pip %d %02X , tg=%d\n",bytes,xbuf[0],target);
548
549 lives_memcpy(midbuf + tot, xbuf, bytes);
550
551 tot += bytes;
552 }
553
554 if (mtype == 0) return NULL;
555
556 channel = (midbuf[0] & 0x0F); // MIDI channel
557
558 if (prefs->midi_rcv_channel != MIDI_OMNI && channel != prefs->midi_rcv_channel) return NULL; // wrong channel, ignore it
559
560 if (prefs->midi_rcv_channel == MIDI_OMNI) {
561 // omni mode
562 if (target == 2) string = lives_strdup_printf("%u %u %u", midbuf[0], channel, midbuf[1]);
563 else if (target == 3) string = lives_strdup_printf("%u %u %u %u", midbuf[0], channel, midbuf[1], midbuf[2]);
564 else string = lives_strdup_printf("%u %u %u %u %u", midbuf[0], channel, midbuf[1], midbuf[2], midbuf[3]);
565 } else {
566 midbuf[0] &= 0xF0;
567 if (target == 2) string = lives_strdup_printf("%u %u", midbuf[0], midbuf[1]);
568 else if (target == 3) string = lives_strdup_printf("%u %u %u", midbuf[0], midbuf[1], midbuf[2]);
569 else string = lives_strdup_printf("%u %u %u %u", midbuf[0], midbuf[1], midbuf[2], midbuf[3]);
570 }
571 #ifdef ALSA_MIDI
572 }
573 #endif
574
575 //g_print("got %s\n",string);
576
577 return string;
578 }
579
580 #endif //OMC_MIDI_IMPL
581
582
cut_string_elems(const char * string,int nelems)583 LIVES_INLINE char *cut_string_elems(const char *string, int nelems) {
584 // remove elements after nelems
585
586 char *retval = lives_strdup(string);
587 register int i;
588 size_t slen = strlen(string);
589
590 if (nelems < 0) return retval;
591
592 for (i = 0; i < slen; i++) {
593 if (!strncmp((string + i), " ", 1)) {
594 if (--nelems == 0) {
595 lives_memset(retval + i, 0, 1);
596 return retval;
597 }
598 }
599 }
600 return retval;
601 }
602
603
omc_learn_get_pname(int type,int idx)604 static char *omc_learn_get_pname(int type, int idx) {
605 switch (type) {
606 case OMC_MIDI_CONTROLLER:
607 case OMC_MIDI_PGM_CHANGE:
608 return (_("data"));
609 case OMC_MIDI_NOTE:
610 case OMC_MIDI_NOTE_OFF:
611 if (idx == 1) return (_("velocity"));
612 return (_("note"));
613 case OMC_JS_AXIS:
614 case OMC_MIDI_PITCH_BEND:
615 return (_("value"));
616 default:
617 return (_("state"));
618 }
619 }
620
621
omc_learn_get_pvalue(int type,int idx,const char * string)622 static int omc_learn_get_pvalue(int type, int idx, const char *string) {
623 char **array = lives_strsplit(string, " ", -1);
624 int res;
625 int nfixed = get_nfixed(type, NULL);
626
627 res = atoi(array[nfixed + idx]);
628 lives_strfreev(array);
629 return res;
630 }
631
632
cell1_edited_callback(LiVESCellRenderer * spinbutton,const char * path_string,const char * new_text,livespointer user_data)633 static void cell1_edited_callback(LiVESCellRenderer *spinbutton, const char *path_string, const char *new_text,
634 livespointer user_data) {
635 lives_omc_match_node_t *mnode = (lives_omc_match_node_t *)user_data;
636
637 lives_omc_macro_t omacro = omc_macros[mnode->macro];
638
639 int vali;
640 double vald;
641
642 LiVESTreeIter iter;
643
644 int row;
645
646 int *indices;
647
648 LiVESTreePath *tpath = lives_tree_path_new_from_string(path_string);
649
650 if (lives_tree_path_get_depth(tpath) != 2) {
651 lives_tree_path_free(tpath);
652 return;
653 }
654
655 indices = lives_tree_path_get_indices(tpath);
656 row = indices[1];
657
658 lives_tree_model_get_iter(LIVES_TREE_MODEL(mnode->gtkstore2), &iter, tpath);
659
660 lives_tree_path_free(tpath);
661
662 if (row > (omacro.nparams - mnode->nvars)) {
663 // text, so dont alter
664 return;
665 }
666
667 switch (omacro.ptypes[row]) {
668 case OMC_PARAM_INT:
669 vali = atoi(new_text);
670 mnode->fvali[row] = vali;
671 break;
672 case OMC_PARAM_DOUBLE:
673 vald = lives_strtod(new_text, NULL);
674 mnode->fvald[row] = vald;
675 break;
676 }
677
678 lives_tree_store_set(mnode->gtkstore2, &iter, VALUE2_COLUMN, new_text, -1);
679 }
680
681
682 #if GTK_CHECK_VERSION(3, 0, 0)
rowexpand(LiVESWidget * tv,LiVESTreeIter * iter,LiVESTreePath * path,livespointer ud)683 static void rowexpand(LiVESWidget *tv, LiVESTreeIter *iter, LiVESTreePath *path, livespointer ud) {
684 lives_widget_queue_resize(tv);
685 }
686 #endif
687
688
omc_macro_row_add_params(lives_omc_match_node_t * mnode,int row,omclearn_w * omclw)689 static void omc_macro_row_add_params(lives_omc_match_node_t *mnode, int row, omclearn_w *omclw) {
690 lives_omc_macro_t macro = omc_macros[mnode->macro];
691
692 LiVESCellRenderer *renderer;
693 LiVESTreeViewColumn *column;
694
695 LiVESTreeIter iter1, iter2;
696
697 LiVESAdjustment *adj;
698
699 char *strval = NULL, *vname;
700 char *oldval = NULL, *final = NULL;
701
702 int mfrom;
703 int i;
704
705 mnode->gtkstore2 = lives_tree_store_new(OMC_NUM2_COLUMNS, LIVES_COL_TYPE_STRING, LIVES_COL_TYPE_STRING,
706 LIVES_COL_TYPE_OBJECT);
707
708 if (macro.nparams == 0) return;
709
710 lives_tree_store_append(mnode->gtkstore2, &iter1, NULL); /* Acquire an iterator */
711 lives_tree_store_set(mnode->gtkstore2, &iter1, TITLE2_COLUMN, (_("Params.")), -1);
712
713 for (i = 0; i < macro.nparams; i++) {
714 lives_tree_store_append(mnode->gtkstore2, &iter2, &iter1); /* Acquire a child iterator */
715
716 if (oldval) {
717 lives_free(oldval);
718 oldval = NULL;
719 }
720
721 if (final) {
722 lives_free(final);
723 final = NULL;
724 }
725
726 adj = NULL;
727
728 if ((mfrom = mnode->map[i]) != -1) strval = (_("variable"));
729 else {
730 switch (macro.ptypes[i]) {
731 case OMC_PARAM_INT:
732 strval = lives_strdup_printf("%d", mnode->fvali[i]);
733 adj = lives_adjustment_new(mnode->fvali[i], macro.mini[i], macro.maxi[i], 1., 1., 0.);
734 break;
735 case OMC_PARAM_DOUBLE:
736 strval = lives_strdup_printf("%.*f", OMC_FP_FIX, mnode->fvald[i]);
737 adj = lives_adjustment_new(mnode->fvald[i], macro.mind[i], macro.maxd[i], 1., 1., 0.);
738 break;
739 }
740 }
741
742 vname = macro.pname[i];
743
744 lives_tree_store_set(mnode->gtkstore2, &iter2, TITLE2_COLUMN, vname, VALUE2_COLUMN, strval, ADJUSTMENT, adj, -1);
745 }
746
747 lives_free(strval);
748
749 mnode->treev2 = lives_tree_view_new_with_model(LIVES_TREE_MODEL(mnode->gtkstore2));
750
751 if (palette->style & STYLE_1) {
752 lives_widget_set_base_color(mnode->treev2, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
753 lives_widget_set_text_color(mnode->treev2, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
754 }
755
756 renderer = lives_cell_renderer_text_new();
757 column = lives_tree_view_column_new_with_attributes(NULL,
758 renderer, LIVES_TREE_VIEW_COLUMN_TEXT, TITLE2_COLUMN, NULL);
759
760 lives_tree_view_append_column(LIVES_TREE_VIEW(mnode->treev2), column);
761
762 renderer = lives_cell_renderer_spin_new();
763
764 if (renderer) {
765 #ifdef GUI_GTK
766 g_object_set(renderer, "width-chars", 7, "mode", GTK_CELL_RENDERER_MODE_EDITABLE,
767 "editable", TRUE, "xalign", 1.0, NULL);
768
769 #endif
770
771 lives_signal_connect(renderer, LIVES_WIDGET_EDITED_SIGNAL, LIVES_GUI_CALLBACK(cell1_edited_callback), mnode);
772
773 // renderer = lives_cell_renderer_text_new ();
774 column = lives_tree_view_column_new_with_attributes(_("value"),
775 renderer, LIVES_TREE_VIEW_COLUMN_TEXT, VALUE2_COLUMN,
776 "adjustment", ADJUSTMENT, NULL);
777
778 lives_tree_view_append_column(LIVES_TREE_VIEW(mnode->treev2), column);
779 }
780
781 #if GTK_CHECK_VERSION(3, 0, 0)
782 lives_signal_connect(LIVES_GUI_OBJECT(mnode->treev2), LIVES_WIDGET_ROW_EXPANDED_SIGNAL,
783 LIVES_GUI_CALLBACK(rowexpand), NULL);
784 #endif
785
786 lives_table_attach(LIVES_TABLE(omclw->table), mnode->treev2, 3, 4, row, row + 1,
787 (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
788 (LiVESAttachOptions)(LIVES_EXPAND), 0, 0);
789 }
790
791
omc_learn_link_params(lives_omc_match_node_t * mnode)792 static void omc_learn_link_params(lives_omc_match_node_t *mnode) {
793 lives_omc_macro_t omc_macro = omc_macros[mnode->macro];
794 int mps = omc_macro.nparams - 1;
795 int lps = mnode->nvars - 1;
796 int i;
797
798 if (mnode->map) lives_free(mnode->map);
799 if (mnode->fvali) lives_free(mnode->fvali);
800 if (mnode->fvald) lives_free(mnode->fvald);
801
802 mnode->map = (int *)lives_malloc(omc_macro.nparams * sizint);
803 mnode->fvali = (int *)lives_malloc(omc_macro.nparams * sizint);
804 mnode->fvald = (double *)lives_malloc(omc_macro.nparams * sizdbl);
805
806 if (lps > mps) lps = mps;
807
808 if (lps >= 0) {
809 for (i = mps; i >= 0; i--) {
810 if (mnode->matchp[lps]) lps++; // variable is filtered for
811 }
812 }
813
814 for (i = mps; i >= 0; i--) {
815 if (lps < 0 || lps >= mnode->nvars) {
816 //g_print("fixed !\n");
817 mnode->map[i] = -1;
818 if (omc_macro.ptypes[i] == OMC_PARAM_INT) mnode->fvali[i] = omc_macro.vali[i];
819 else mnode->fvald[i] = omc_macro.vald[i];
820 } else {
821 // g_print("varied !\n");
822 if (!mnode->matchp[lps]) mnode->map[i] = lps;
823 else i++;
824 }
825 lps--;
826 }
827 }
828
829
on_omc_combo_entry_changed(LiVESCombo * combo,livespointer ptr)830 static void on_omc_combo_entry_changed(LiVESCombo *combo, livespointer ptr) {
831 lives_omc_match_node_t *mnode = (lives_omc_match_node_t *)ptr;
832 const char *macro_text;
833 int i, row = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(combo), "row"));
834 omclearn_w *omclw = (omclearn_w *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(combo), "omclw");
835
836 macro_text = lives_combo_get_active_text(LIVES_COMBO(combo));
837
838 if (mnode->treev2) {
839 // remove old mapping
840 lives_widget_destroy(mnode->treev2);
841 mnode->treev2 = NULL;
842
843 mnode->macro = -1;
844
845 lives_free(mnode->map);
846 lives_free(mnode->fvali);
847 lives_free(mnode->fvald);
848
849 mnode->map = mnode->fvali = NULL;
850 mnode->fvald = NULL;
851 }
852
853 if (!strcmp(macro_text, mainw->string_constants[LIVES_STRING_CONSTANT_NONE])) {
854 return;
855 }
856
857 for (i = 0; i < N_OMC_MACROS; i++) {
858 if (!strcmp(macro_text, omc_macros[i].macro_text)) break;
859 }
860
861 mnode->macro = i;
862 omc_learn_link_params(mnode);
863 omc_macro_row_add_params(mnode, row, omclw);
864 }
865
866
cell_toggled_callback(LiVESCellRenderer * toggle,const char * path_string,livespointer user_data)867 static void cell_toggled_callback(LiVESCellRenderer *toggle, const char *path_string, livespointer user_data) {
868 lives_omc_match_node_t *mnode = (lives_omc_match_node_t *)user_data;
869 int row;
870
871 char *txt;
872
873 int *indices;
874
875 LiVESTreePath *tpath = lives_tree_path_new_from_string(path_string);
876
877 LiVESTreeIter iter;
878
879 if (lives_tree_path_get_depth(tpath) != 2) {
880 lives_tree_path_free(tpath);
881 return;
882 }
883
884 indices = lives_tree_path_get_indices(tpath);
885 row = indices[1];
886
887 lives_tree_model_get_iter(LIVES_TREE_MODEL(mnode->gtkstore), &iter, tpath);
888
889 lives_tree_path_free(tpath);
890
891 lives_tree_model_get(LIVES_TREE_MODEL(mnode->gtkstore), &iter, VALUE_COLUMN, &txt, -1);
892
893 if (!strcmp(txt, "-")) {
894 lives_free(txt);
895 return;
896 }
897
898 lives_free(txt);
899
900 mnode->matchp[row] = !(mnode->matchp[row]);
901
902 lives_tree_store_set(mnode->gtkstore, &iter, FILTER_COLUMN, mnode->matchp[row], -1);
903
904 omc_learn_link_params(mnode);
905 }
906
907
cell_edited_callback(LiVESCellRenderer * spinbutton,const char * path_string,const char * new_text,livespointer user_data)908 static void cell_edited_callback(LiVESCellRenderer *spinbutton, const char *path_string, const char *new_text,
909 livespointer user_data) {
910 lives_omc_match_node_t *mnode = (lives_omc_match_node_t *)user_data;
911
912 int col = LIVES_POINTER_TO_INT(lives_widget_object_get_data(LIVES_WIDGET_OBJECT(spinbutton), "colnum"));
913
914 int vali;
915 double vald;
916
917 LiVESTreeIter iter;
918
919 int row;
920
921 int *indices;
922
923 LiVESTreePath *tpath = lives_tree_path_new_from_string(path_string);
924
925 if (lives_tree_path_get_depth(tpath) != 2) {
926 lives_tree_path_free(tpath);
927 return;
928 }
929
930 indices = lives_tree_path_get_indices(tpath);
931 row = indices[1];
932
933 lives_tree_model_get_iter(LIVES_TREE_MODEL(mnode->gtkstore), &iter, tpath);
934
935 lives_tree_path_free(tpath);
936
937 switch (col) {
938 case OFFS1_COLUMN:
939 vali = atoi(new_text);
940 mnode->offs0[row] = vali;
941 break;
942 case OFFS2_COLUMN:
943 vali = atoi(new_text);
944 mnode->offs1[row] = vali;
945 break;
946 case SCALE_COLUMN:
947 vald = lives_strtod(new_text, NULL);
948 mnode->scale[row] = vald;
949 break;
950 }
951
952 lives_tree_store_set(mnode->gtkstore, &iter, col, new_text, -1);
953 }
954
955
create_omc_macro_combo(lives_omc_match_node_t * mnode,int row,omclearn_w * omclw)956 static LiVESWidget *create_omc_macro_combo(lives_omc_match_node_t *mnode, int row, omclearn_w *omclw) {
957 LiVESWidget *combo = lives_standard_combo_new(NULL, NULL, NULL, NULL);
958
959 for (int i = 0; i < N_OMC_MACROS; i++) {
960 if (!omc_macros[i].msg) break;
961 lives_combo_append_text(LIVES_COMBO(combo), omc_macros[i].macro_text);
962 }
963
964 if (mnode->macro != -1) {
965 lives_combo_set_active_index(LIVES_COMBO(combo), mnode->macro);
966 }
967
968 lives_signal_connect_after(LIVES_WIDGET_OBJECT(combo), LIVES_WIDGET_CHANGED_SIGNAL,
969 LIVES_GUI_CALLBACK(on_omc_combo_entry_changed), mnode);
970
971 lives_widget_object_set_data(LIVES_WIDGET_OBJECT(combo), "row", LIVES_INT_TO_POINTER(row));
972 lives_widget_object_set_data(LIVES_WIDGET_OBJECT(combo), "omclw", (livespointer)omclw);
973
974 return combo;
975 }
976
977
get_chan_string(const char * string)978 static char *get_chan_string(const char *string) {
979 char *chstr;
980 if (prefs->midi_rcv_channel == MIDI_OMNI) {
981 int chan = js_index(string);
982 // TRANSLATORS: ch is abbreviation for MIDI "channel"
983 chstr = lives_strdup_printf(_(" ch %d"), chan);
984 } else chstr = lives_strdup("");
985 return chstr;
986 }
987
988
omc_learner_add_row(int type,int detail,lives_omc_match_node_t * mnode,const char * string,omclearn_w * omclw)989 static void omc_learner_add_row(int type, int detail, lives_omc_match_node_t *mnode, const char *string, omclearn_w *omclw) {
990 LiVESWidget *label, *combo;
991 LiVESWidgetObject *spinadj;
992
993 LiVESCellRenderer *renderer;
994 LiVESTreeViewColumn *column;
995
996 LiVESTreeIter iter1, iter2;
997
998 char *strval, *strval2, *strval3, *strval4, *vname, *valstr;
999 char *oldval = NULL, *final = NULL;
1000 char *labelt = NULL;
1001 char *chstr = NULL;
1002
1003 int val;
1004
1005 omclw->tbl_rows++;
1006 lives_table_resize(LIVES_TABLE(omclw->table), omclw->tbl_rows, 4);
1007
1008 mnode->gtkstore = lives_tree_store_new(OMC_NUM_COLUMNS, LIVES_COL_TYPE_STRING, LIVES_COL_TYPE_STRING, LIVES_COL_TYPE_BOOLEAN,
1009 LIVES_COL_TYPE_STRING,
1010 LIVES_COL_TYPE_STRING, LIVES_COL_TYPE_STRING, LIVES_COL_TYPE_STRING);
1011
1012 lives_tree_store_append(mnode->gtkstore, &iter1, NULL); /* Acquire an iterator */
1013 lives_tree_store_set(mnode->gtkstore, &iter1, TITLE_COLUMN, (_("Vars.")), -1);
1014
1015 for (int i = 0; i < mnode->nvars; i++) {
1016 lives_tree_store_append(mnode->gtkstore, &iter2, &iter1); /* Acquire a child iterator */
1017
1018 if (oldval) {
1019 lives_free(oldval);
1020 oldval = NULL;
1021 }
1022
1023 if (final) {
1024 lives_free(final);
1025 final = NULL;
1026 }
1027
1028 strval = lives_strdup_printf("%d - %d", mnode->min[i], mnode->max[i]);
1029 strval2 = lives_strdup_printf("%d", mnode->offs0[i]);
1030 strval3 = lives_strdup_printf("%.*f", OMC_FP_FIX, mnode->scale[i]);
1031 strval4 = lives_strdup_printf("%d", mnode->offs1[i]);
1032
1033 if (type > 0) {
1034 vname = omc_learn_get_pname(type, i);
1035 val = omc_learn_get_pvalue(type, i, string);
1036
1037 valstr = lives_strdup_printf("%d", val);
1038 if (!mnode->matchp[i]) {
1039 mnode->matchi[i] = val;
1040 }
1041 } else {
1042 vname = omc_learn_get_pname(-type, i);
1043 if (mnode->matchp[i]) valstr = lives_strdup_printf("%d", mnode->matchi[i]);
1044 else valstr = lives_strdup("-");
1045 }
1046
1047 lives_tree_store_set(mnode->gtkstore, &iter2, TITLE_COLUMN, vname, VALUE_COLUMN, valstr, FILTER_COLUMN, mnode->matchp[i],
1048 RANGE_COLUMN, strval, OFFS1_COLUMN, strval2, SCALE_COLUMN, strval3, OFFS2_COLUMN, strval4, -1);
1049
1050 lives_free(strval); lives_free(strval2); lives_free(strval3);
1051 lives_free(strval4); lives_free(valstr); lives_free(vname);
1052 }
1053
1054 mnode->treev1 = lives_tree_view_new_with_model(LIVES_TREE_MODEL(mnode->gtkstore));
1055
1056 if (type < 0) type = -type;
1057
1058 switch (type) {
1059 case OMC_MIDI_NOTE:
1060 chstr = get_chan_string(string);
1061 labelt = lives_strdup_printf(_("MIDI%s note on"), chstr);
1062 break;
1063 case OMC_MIDI_NOTE_OFF:
1064 chstr = get_chan_string(string);
1065 labelt = lives_strdup_printf(_("MIDI%s note off"), chstr);
1066 break;
1067 case OMC_MIDI_CONTROLLER:
1068 chstr = get_chan_string(string);
1069 labelt = lives_strdup_printf(_("MIDI%s controller %d"), chstr, detail);
1070 break;
1071 case OMC_MIDI_PITCH_BEND:
1072 chstr = get_chan_string(string);
1073 labelt = lives_strdup_printf(_("MIDI%s pitch bend"), chstr);
1074 break;
1075 case OMC_MIDI_PGM_CHANGE:
1076 chstr = get_chan_string(string);
1077 labelt = lives_strdup_printf(_("MIDI%s pgm change"), chstr);
1078 break;
1079 case OMC_JS_BUTTON:
1080 labelt = lives_strdup_printf(_("Joystick button %d"), detail);
1081 break;
1082 case OMC_JS_AXIS:
1083 labelt = lives_strdup_printf(_("Joystick axis %d"), detail);
1084 break;
1085 }
1086
1087 if (chstr) lives_free(chstr);
1088
1089 label = lives_standard_label_new(labelt);
1090
1091 if (labelt) lives_free(labelt);
1092
1093 #if !GTK_CHECK_VERSION(3, 0, 0)
1094 if (palette->style & STYLE_1) {
1095 lives_widget_set_fg_color(label, LIVES_WIDGET_STATE_NORMAL, &palette->black);
1096 }
1097 #endif
1098
1099 omclw->tbl_currow++;
1100
1101 lives_table_attach(LIVES_TABLE(omclw->table), label, 0, 1, omclw->tbl_currow, omclw->tbl_currow + 1,
1102 (LiVESAttachOptions)(0), (LiVESAttachOptions)(0), 0, 0);
1103
1104 // properties
1105 if (palette->style & STYLE_1) {
1106 lives_widget_set_base_color(mnode->treev1, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars);
1107 lives_widget_set_text_color(mnode->treev1, LIVES_WIDGET_STATE_NORMAL, &palette->menu_and_bars_fore);
1108 }
1109
1110 renderer = lives_cell_renderer_text_new();
1111 column = lives_tree_view_column_new_with_attributes(NULL,
1112 renderer, LIVES_TREE_VIEW_COLUMN_TEXT, TITLE_COLUMN, NULL);
1113
1114 lives_tree_view_append_column(LIVES_TREE_VIEW(mnode->treev1), column);
1115
1116 renderer = lives_cell_renderer_text_new();
1117 column = lives_tree_view_column_new_with_attributes(_("value"),
1118 renderer, LIVES_TREE_VIEW_COLUMN_TEXT, VALUE_COLUMN, NULL);
1119
1120 lives_tree_view_append_column(LIVES_TREE_VIEW(mnode->treev1), column);
1121
1122 renderer = lives_cell_renderer_toggle_new();
1123 column = lives_tree_view_column_new_with_attributes(_("x"),
1124 renderer, "active", FILTER_COLUMN, NULL);
1125
1126 lives_tree_view_append_column(LIVES_TREE_VIEW(mnode->treev1), column);
1127
1128 lives_signal_connect(renderer, LIVES_WIDGET_TOGGLED_SIGNAL, LIVES_GUI_CALLBACK(cell_toggled_callback), mnode);
1129
1130 renderer = lives_cell_renderer_text_new();
1131 column = lives_tree_view_column_new_with_attributes(_("range"),
1132 renderer, LIVES_TREE_VIEW_COLUMN_TEXT, RANGE_COLUMN, NULL);
1133
1134 lives_tree_view_append_column(LIVES_TREE_VIEW(mnode->treev1), column);
1135
1136 renderer = lives_cell_renderer_spin_new();
1137
1138 if (renderer) {
1139 lives_widget_object_set_data(LIVES_WIDGET_OBJECT(renderer), "colnum", LIVES_UINT_TO_POINTER(OFFS1_COLUMN));
1140
1141 spinadj = (LiVESWidgetObject *)lives_adjustment_new(0., -100000., 100000., 1., 10., 0);
1142
1143 #ifdef GUI_GTK
1144 g_object_set(renderer, "width-chars", 7, "mode", GTK_CELL_RENDERER_MODE_EDITABLE,
1145 "editable", TRUE, "xalign", 1.0, "adjustment", spinadj, NULL);
1146 #endif
1147
1148 lives_signal_connect(renderer, LIVES_WIDGET_EDITED_SIGNAL, LIVES_GUI_CALLBACK(cell_edited_callback), mnode);
1149
1150 column = lives_tree_view_column_new_with_attributes(_("+ offset1"),
1151 renderer, LIVES_TREE_VIEW_COLUMN_TEXT, OFFS1_COLUMN, NULL);
1152
1153 lives_tree_view_append_column(LIVES_TREE_VIEW(mnode->treev1), column);
1154 }
1155
1156 renderer = lives_cell_renderer_spin_new();
1157
1158 if (renderer) {
1159 spinadj = (LiVESWidgetObject *)lives_adjustment_new(1., -100000., 100000., 1., 10., 0);
1160
1161 #ifdef GUI_GTK
1162 g_object_set(renderer, "width-chars", 12, "mode", GTK_CELL_RENDERER_MODE_EDITABLE,
1163 "editable", TRUE, "xalign", 1.0, "adjustment", spinadj,
1164 "digits", OMC_FP_FIX, NULL);
1165 #endif
1166
1167 lives_widget_object_set_data(LIVES_WIDGET_OBJECT(renderer), "colnum", LIVES_UINT_TO_POINTER(SCALE_COLUMN));
1168 lives_signal_connect(renderer, LIVES_WIDGET_EDITED_SIGNAL, LIVES_GUI_CALLBACK(cell_edited_callback), mnode);
1169
1170 column = lives_tree_view_column_new_with_attributes(_("* scale"),
1171 renderer, LIVES_TREE_VIEW_COLUMN_TEXT, SCALE_COLUMN, NULL);
1172 lives_tree_view_append_column(LIVES_TREE_VIEW(mnode->treev1), column);
1173 }
1174
1175 renderer = lives_cell_renderer_spin_new();
1176
1177 if (renderer) {
1178 spinadj = (LiVESWidgetObject *)lives_adjustment_new(0., -100000., 100000., 1., 10., 0);
1179
1180 #ifdef GUI_GTK
1181 g_object_set(renderer, "width-chars", 7, "mode", GTK_CELL_RENDERER_MODE_EDITABLE,
1182 "editable", TRUE, "xalign", 1.0, "adjustment", spinadj, NULL);
1183 #endif
1184
1185 lives_widget_object_set_data(LIVES_WIDGET_OBJECT(renderer), "colnum", LIVES_UINT_TO_POINTER(OFFS2_COLUMN));
1186 lives_signal_connect(renderer, LIVES_WIDGET_EDITED_SIGNAL, LIVES_GUI_CALLBACK(cell_edited_callback), mnode);
1187
1188 column = lives_tree_view_column_new_with_attributes(_("+ offset2"),
1189 renderer, LIVES_TREE_VIEW_COLUMN_TEXT, OFFS2_COLUMN, NULL);
1190 lives_tree_view_append_column(LIVES_TREE_VIEW(mnode->treev1), column);
1191 }
1192
1193 #if LIVES_TABLE_IS_GRID
1194 lives_widget_set_size_request(mnode->treev1, -1, TREE_ROW_HEIGHT);
1195 #endif
1196
1197 lives_table_attach(LIVES_TABLE(omclw->table), mnode->treev1, 1, 2, omclw->tbl_currow, omclw->tbl_currow + 1,
1198 (LiVESAttachOptions)(LIVES_FILL | LIVES_EXPAND),
1199 (LiVESAttachOptions)(LIVES_EXPAND), 0, 0);
1200
1201 #if GTK_CHECK_VERSION(3, 0, 0)
1202 lives_signal_connect(LIVES_GUI_OBJECT(mnode->treev1), LIVES_WIDGET_ROW_EXPANDED_SIGNAL,
1203 LIVES_GUI_CALLBACK(rowexpand), NULL);
1204 #endif
1205
1206 combo = create_omc_macro_combo(mnode, omclw->tbl_currow, omclw);
1207
1208 lives_table_attach(LIVES_TABLE(omclw->table), combo, 2, 3, omclw->tbl_currow, omclw->tbl_currow + 1,
1209 (LiVESAttachOptions) 0, (LiVESAttachOptions)(0), 0, 0);
1210
1211 if (mnode->macro == UNMATCHED) lives_widget_set_sensitive(omclw->clear_button, TRUE);
1212 lives_widget_set_sensitive(omclw->del_all_button, TRUE);
1213 }
1214
1215
killit(LiVESWidget * widget,livespointer user_data)1216 static void killit(LiVESWidget *widget, livespointer user_data) {
1217 lives_widget_destroy(widget);
1218 }
1219
1220
show_existing(omclearn_w * omclw)1221 static void show_existing(omclearn_w *omclw) {
1222 LiVESSList *slist = omc_node_list;
1223 lives_omc_match_node_t *mnode;
1224 int type, supertype;
1225 char **array, *srch;
1226 int idx;
1227
1228 while (slist) {
1229 mnode = (lives_omc_match_node_t *)slist->data;
1230
1231 srch = lives_strdup(mnode->srch);
1232 array = lives_strsplit(srch, " ", -1);
1233
1234 supertype = atoi(array[0]);
1235 #ifdef OMC_MIDI_IMPL
1236 if (supertype == OMC_MIDI) {
1237 size_t blen;
1238 char *tmp;
1239
1240 type = midi_msg_type(array[1]);
1241 if (get_token_count(srch, ' ') > (prefs->midi_rcv_channel == -1 ? 3 : 2))
1242 idx = atoi(array[prefs->midi_rcv_channel == -1 ? 3 : 2]);
1243 else idx = -1;
1244 srch = lives_strdup(mnode->srch);
1245 if (prefs->midi_rcv_channel == MIDI_OMNI) {
1246 // remove the channel if it is in the string
1247 tmp = cut_string_elems(srch, 1);
1248 blen = strlen(tmp);
1249 tmp = lives_strdup(srch + blen + 1);
1250 lives_free(srch);
1251 srch = tmp;
1252 }
1253 } else {
1254 #endif
1255 type = supertype;
1256 idx = atoi(array[1]);
1257 #ifdef OMC_MIDI_IMPL
1258 }
1259 #endif
1260 lives_strfreev(array);
1261
1262 omc_learner_add_row(-type, idx, mnode, srch, omclw);
1263 lives_free(srch);
1264
1265 omc_macro_row_add_params(mnode, omclw->tbl_currow, omclw);
1266
1267 slist = slist->next;
1268 }
1269 }
1270
1271
clear_unmatched(LiVESButton * button,livespointer user_data)1272 static void clear_unmatched(LiVESButton *button, livespointer user_data) {
1273 omclearn_w *omclw = (omclearn_w *)user_data;
1274
1275 // destroy everything in table
1276
1277 lives_container_foreach(LIVES_CONTAINER(omclw->table), killit, NULL);
1278
1279 omclw->tbl_currow = -1;
1280
1281 remove_all_nodes(FALSE, omclw);
1282
1283 show_existing(omclw);
1284 }
1285
1286
del_all(LiVESButton * button,livespointer user_data)1287 static void del_all(LiVESButton *button, livespointer user_data) {
1288 omclearn_w *omclw = (omclearn_w *)user_data;
1289
1290 // need to use the full version here to override the default transient window
1291 if (!do_warning_dialog(_("\nClick OK to delete all entries\n"))) return;
1292
1293 // destroy everything in table
1294 lives_container_foreach(LIVES_CONTAINER(omclw->table), killit, NULL);
1295
1296 remove_all_nodes(TRUE, omclw);
1297
1298 lives_widget_set_sensitive(mainw->midi_save, FALSE);
1299 }
1300
1301
close_learner_dialog(LiVESButton * button,livespointer user_data)1302 static void close_learner_dialog(LiVESButton *button, livespointer user_data) {
1303 mainw->cancelled = CANCEL_USER;
1304 if (has_devicemap(-1)) lives_widget_set_sensitive(mainw->midi_save, TRUE);
1305 }
1306
1307
create_omclearn_dialog(void)1308 static omclearn_w *create_omclearn_dialog(void) {
1309 LiVESWidget *ok_button;
1310 LiVESWidget *scrolledwindow;
1311 int winsize_h, winsize_v;
1312
1313 omclearn_w *omclw = (omclearn_w *)lives_malloc(sizeof(omclearn_w));
1314
1315 omclw->tbl_rows = 4;
1316 omclw->tbl_currow = -1;
1317
1318 winsize_h = GUI_SCREEN_WIDTH - SCR_WIDTH_SAFETY;
1319 winsize_v = GUI_SCREEN_HEIGHT - SCR_HEIGHT_SAFETY;
1320
1321 omclw->dialog = lives_standard_dialog_new(_("OMC Learner"), FALSE, winsize_h, winsize_v);
1322 lives_signal_handlers_disconnect_by_func(omclw->dialog, LIVES_GUI_CALLBACK(return_true), NULL);
1323
1324 omclw->top_vbox = lives_dialog_get_content_area(LIVES_DIALOG(omclw->dialog));
1325
1326 omclw->table = lives_table_new(omclw->tbl_rows, 4, FALSE);
1327
1328 lives_table_set_col_spacings(LIVES_TABLE(omclw->table), widget_opts.packing_width * 2);
1329
1330 scrolledwindow = lives_standard_scrolled_window_new(winsize_h, winsize_v - SCR_HEIGHT_SAFETY, omclw->table);
1331
1332 lives_box_pack_start(LIVES_BOX(omclw->top_vbox), scrolledwindow, TRUE, TRUE, 0);
1333
1334 omclw->clear_button = lives_dialog_add_button_from_stock(LIVES_DIALOG(omclw->dialog), LIVES_STOCK_CLEAR, _("Clear _unmatched"),
1335 LIVES_RESPONSE_NONE);
1336
1337 lives_signal_connect(LIVES_GUI_OBJECT(omclw->clear_button), LIVES_WIDGET_CLICKED_SIGNAL,
1338 LIVES_GUI_CALLBACK(clear_unmatched), (livespointer)omclw);
1339
1340 lives_widget_set_sensitive(omclw->clear_button, FALSE);
1341
1342 omclw->del_all_button = lives_dialog_add_button_from_stock(LIVES_DIALOG(omclw->dialog), LIVES_STOCK_DELETE, _("_Delete all"),
1343 LIVES_RESPONSE_NONE);
1344
1345 lives_signal_connect(LIVES_GUI_OBJECT(omclw->del_all_button), LIVES_WIDGET_CLICKED_SIGNAL,
1346 LIVES_GUI_CALLBACK(del_all), (livespointer)omclw);
1347
1348 lives_widget_set_sensitive(omclw->del_all_button, FALSE);
1349
1350 ok_button = lives_dialog_add_button_from_stock(LIVES_DIALOG(omclw->dialog), LIVES_STOCK_CLOSE, _("_Close Window"),
1351 LIVES_RESPONSE_OK);
1352
1353 lives_button_grab_default_special(ok_button);
1354
1355 lives_signal_connect(LIVES_GUI_OBJECT(ok_button), LIVES_WIDGET_CLICKED_SIGNAL,
1356 LIVES_GUI_CALLBACK(close_learner_dialog), NULL);
1357
1358 if (prefs->gui_monitor != 0) {
1359 lives_window_center(LIVES_WINDOW(omclw->dialog));
1360 }
1361
1362 if (prefs->open_maximised) {
1363 lives_window_unmaximize(LIVES_WINDOW(omclw->dialog));
1364 lives_window_maximize(LIVES_WINDOW(omclw->dialog));
1365 }
1366
1367 if (prefs->show_gui)
1368 lives_widget_show_all(omclw->dialog);
1369
1370 return omclw;
1371 }
1372
1373
init_omc_macros(void)1374 static void init_omc_macros(void) {
1375 int i;
1376
1377 for (i = 0; i < N_OMC_MACROS; i++) {
1378 omc_macros[i].macro_text = NULL;
1379 omc_macros[i].info_text = NULL;
1380 omc_macros[i].msg = NULL;
1381 omc_macros[i].nparams = 0;
1382 omc_macros[i].pname = NULL;
1383 }
1384
1385 omc_macros[START_PLAYBACK].msg = lives_strdup("/video/play");
1386 omc_macros[START_PLAYBACK].macro_text = (_("Start video playback"));
1387
1388 omc_macros[STOP_PLAYBACK].msg = lives_strdup("/video/stop");
1389 omc_macros[STOP_PLAYBACK].macro_text = (_("Stop video playback"));
1390
1391 omc_macros[CLIP_SELECT].msg = lives_strdup("/clip/foreground/select");
1392 omc_macros[CLIP_SELECT].macro_text = (_("Clip select <clipnum>"));
1393 omc_macros[CLIP_SELECT].info_text = (_("Switch foreground clip to the nth valid clip"));
1394 omc_macros[CLIP_SELECT].nparams = 1;
1395
1396 omc_macros[PLAY_FORWARDS].msg = lives_strdup("/video/play/forwards");
1397 omc_macros[PLAY_FORWARDS].macro_text = (_("Play forwards"));
1398 omc_macros[PLAY_FORWARDS].info_text = (_("Play video in a forwards direction"));
1399
1400 omc_macros[PLAY_BACKWARDS].msg = lives_strdup("/video/play/backwards");
1401 omc_macros[PLAY_BACKWARDS].macro_text = (_("Play backwards"));
1402 omc_macros[PLAY_BACKWARDS].info_text = (_("Play video in a backwards direction"));
1403
1404 omc_macros[REVERSE_PLAYBACK].msg = lives_strdup("/video/play/reverse");
1405 omc_macros[REVERSE_PLAYBACK].macro_text = (_("Reverse playback direction"));
1406 omc_macros[REVERSE_PLAYBACK].info_text = (_("Reverse direction of video playback"));
1407
1408 omc_macros[PLAY_FASTER].msg = lives_strdup("/video/play/faster");
1409 omc_macros[PLAY_FASTER].macro_text = (_("Play video faster"));
1410 omc_macros[PLAY_FASTER].info_text = (_("Play video at a slightly faster rate"));
1411
1412 omc_macros[PLAY_SLOWER].msg = lives_strdup("/video/play/slower");
1413 omc_macros[PLAY_SLOWER].macro_text = (_("Play video slower"));
1414 omc_macros[PLAY_SLOWER].info_text = (_("Play video at a slightly slower rate"));
1415
1416 omc_macros[TOGGLE_FREEZE].msg = lives_strdup("/video/freeze/toggle");
1417 omc_macros[TOGGLE_FREEZE].macro_text = (_("Toggle video freeze"));
1418 omc_macros[TOGGLE_FREEZE].info_text = (_("Freeze video, or if already frozen, unfreeze it"));
1419
1420 omc_macros[SET_FRAMERATE].msg = lives_strdup("/video/fps/set");
1421 omc_macros[SET_FRAMERATE].macro_text = (_("Set video framerate to <fps>"));
1422 omc_macros[SET_FRAMERATE].info_text = (_("Set the framerate of foreground clip to <(float) fps>"));
1423 omc_macros[SET_FRAMERATE].nparams = 1;
1424
1425 omc_macros[START_RECORDING].msg = lives_strdup("/record/enable");
1426 omc_macros[START_RECORDING].macro_text = (_("Start recording"));
1427
1428 omc_macros[STOP_RECORDING].msg = lives_strdup("/record/disable");
1429 omc_macros[STOP_RECORDING].macro_text = (_("Stop recording"));
1430
1431 omc_macros[TOGGLE_RECORDING].msg = lives_strdup("/record/toggle");
1432 omc_macros[TOGGLE_RECORDING].macro_text = (_("Toggle recording state"));
1433
1434 omc_macros[SWAP_FOREGROUND_BACKGROUND].msg = lives_strdup("/clip/foreground/background/swap");
1435 omc_macros[SWAP_FOREGROUND_BACKGROUND].macro_text = (_("Swap foreground and background clips"));
1436
1437 omc_macros[RESET_EFFECT_KEYS].msg = lives_strdup("/effect_key/reset");
1438 omc_macros[RESET_EFFECT_KEYS].macro_text = (_("Reset effect keys"));
1439 omc_macros[RESET_EFFECT_KEYS].info_text = (_("Switch all effects off."));
1440
1441 omc_macros[ENABLE_EFFECT_KEY].msg = lives_strdup("/effect_key/enable");
1442 omc_macros[ENABLE_EFFECT_KEY].macro_text = (_("Enable effect key <key>"));
1443 omc_macros[ENABLE_EFFECT_KEY].nparams = 1;
1444
1445 omc_macros[DISABLE_EFFECT_KEY].msg = lives_strdup("/effect_key/disable");
1446 omc_macros[DISABLE_EFFECT_KEY].macro_text = (_("Disable effect key <key>"));
1447 omc_macros[DISABLE_EFFECT_KEY].nparams = 1;
1448
1449 omc_macros[TOGGLE_EFFECT_KEY].msg = lives_strdup("/effect_key/toggle");
1450 omc_macros[TOGGLE_EFFECT_KEY].macro_text = (_("Toggle effect key <key>"));
1451 omc_macros[TOGGLE_EFFECT_KEY].nparams = 1;
1452
1453 omc_macros[SET_PARAMETER_VALUE].msg = lives_strdup("/effect_key/nparameter/value/set");
1454 omc_macros[SET_PARAMETER_VALUE].macro_text = (_("Set parameter value <key> <pnum> = <value>"));
1455 omc_macros[SET_PARAMETER_VALUE].info_text = (_("Set <value> of pth (numerical) parameter for effect key <key>."));
1456 omc_macros[SET_PARAMETER_VALUE].nparams = 3;
1457
1458 omc_macros[NEXT_CLIP_SELECT].msg = lives_strdup("/clip/select/next");
1459 omc_macros[NEXT_CLIP_SELECT].macro_text = (_("Switch foreground to next clip"));
1460
1461 omc_macros[PREV_CLIP_SELECT].msg = lives_strdup("/clip/select/previous");
1462 omc_macros[PREV_CLIP_SELECT].macro_text = (_("Switch foreground to previous clip"));
1463
1464 omc_macros[SET_FPS_RATIO].msg = lives_strdup("/video/fps/ratio/set");
1465 omc_macros[SET_FPS_RATIO].macro_text = (_("Set video framerate to ratio <fps__ratio>"));
1466 omc_macros[SET_FPS_RATIO].info_text = (_("Set the framerate ratio of the foreground clip to <(float) fps__ratio>"));
1467 omc_macros[SET_FPS_RATIO].nparams = 1;
1468
1469 omc_macros[RETRIGGER_CLIP].msg = lives_strdup("/clip/foreground/retrigger");
1470 omc_macros[RETRIGGER_CLIP].macro_text = (_("Retrigger clip <clipnum>"));
1471 omc_macros[RETRIGGER_CLIP].info_text = lives_strdup(
1472 _("Switch foreground clip to the nth valid clip, and reset the frame number"));
1473 omc_macros[RETRIGGER_CLIP].nparams = 1;
1474
1475 omc_macros[NEXT_MODE_CYCLE].msg = lives_strdup("/effect_key/mode/next");
1476 omc_macros[NEXT_MODE_CYCLE].macro_text = (_("Cycle to next mode for effect key <key>"));
1477 omc_macros[NEXT_MODE_CYCLE].nparams = 1;
1478
1479 omc_macros[PREV_MODE_CYCLE].msg = lives_strdup("/effect_key/mode/previous");
1480 omc_macros[PREV_MODE_CYCLE].macro_text = (_("Cycle to previous mode for effect key <key>"));
1481 omc_macros[PREV_MODE_CYCLE].nparams = 1;
1482
1483 omc_macros[SET_VPP_PARAMETER_VALUE].msg = lives_strdup("/video/play/parameter/value/set");
1484 omc_macros[SET_VPP_PARAMETER_VALUE].macro_text = (_("Set playback plugin parameter value <pnum> = <value>"));
1485 omc_macros[SET_VPP_PARAMETER_VALUE].info_text = (_("Set <value> of pth parameter for the playback plugin."));
1486 omc_macros[SET_VPP_PARAMETER_VALUE].nparams = 2;
1487
1488 omc_macros[OSC_NOTIFY].msg = lives_strdup("internal"); // handled internally
1489 omc_macros[OSC_NOTIFY].macro_text = (_("Send OSC notification message"));
1490 omc_macros[OSC_NOTIFY].info_text = lives_strdup(
1491 _("Send LIVES_OSC_NOTIFY_USER1 notification to all listeners, with variable <value>."));
1492 omc_macros[OSC_NOTIFY].nparams = 2;
1493
1494 for (i = 0; i < N_OMC_MACROS; i++) {
1495 if (omc_macros[i].msg) {
1496 if (omc_macros[i].nparams > 0) {
1497 omc_macros[i].ptypes = (int *)lives_malloc(omc_macros[i].nparams * sizint);
1498 omc_macros[i].mini = (int *)lives_malloc(omc_macros[i].nparams * sizint);
1499 omc_macros[i].maxi = (int *)lives_malloc(omc_macros[i].nparams * sizint);
1500 omc_macros[i].vali = (int *)lives_malloc(omc_macros[i].nparams * sizint);
1501
1502 omc_macros[i].mind = (double *)lives_malloc(omc_macros[i].nparams * sizdbl);
1503 omc_macros[i].maxd = (double *)lives_malloc(omc_macros[i].nparams * sizdbl);
1504 omc_macros[i].vald = (double *)lives_malloc(omc_macros[i].nparams * sizdbl);
1505 omc_macros[i].pname = (char **)lives_malloc(omc_macros[i].nparams * sizeof(char *));
1506
1507 }
1508 }
1509 }
1510
1511 // clip select
1512 omc_macros[CLIP_SELECT].ptypes[0] = OMC_PARAM_INT;
1513 omc_macros[CLIP_SELECT].mini[0] = omc_macros[CLIP_SELECT].vali[0] = 1;
1514 omc_macros[CLIP_SELECT].maxi[0] = MAX_FILES;
1515 // TRANSLATORS: short form of "clip number"
1516 omc_macros[CLIP_SELECT].pname[0] = (_("clipnum"));
1517
1518 // set fps (will be handled to avoid 0.)
1519 omc_macros[SET_FRAMERATE].ptypes[0] = OMC_PARAM_DOUBLE;
1520 omc_macros[SET_FRAMERATE].mind[0] = -FPS_MAX;
1521 omc_macros[SET_FRAMERATE].vald[0] = prefs->default_fps;
1522 omc_macros[SET_FRAMERATE].maxd[0] = FPS_MAX;
1523 // TRANSLATORS: short form of "frames per second"
1524 omc_macros[SET_FRAMERATE].pname[0] = (_("fps"));
1525
1526 // effect_key enable,disable, toggle
1527 omc_macros[ENABLE_EFFECT_KEY].ptypes[0] = OMC_PARAM_INT;
1528 omc_macros[ENABLE_EFFECT_KEY].mini[0] = 1;
1529 omc_macros[ENABLE_EFFECT_KEY].vali[0] = 1;
1530 omc_macros[ENABLE_EFFECT_KEY].maxi[0] = prefs->rte_keys_virtual;
1531 // TRANSLATORS: as in keyboard key
1532 omc_macros[ENABLE_EFFECT_KEY].pname[0] = (_("key"));
1533
1534 omc_macros[DISABLE_EFFECT_KEY].ptypes[0] = OMC_PARAM_INT;
1535 omc_macros[DISABLE_EFFECT_KEY].mini[0] = 1;
1536 omc_macros[DISABLE_EFFECT_KEY].vali[0] = 1;
1537 omc_macros[DISABLE_EFFECT_KEY].maxi[0] = prefs->rte_keys_virtual;
1538 // TRANSLATORS: as in keyboard key
1539 omc_macros[DISABLE_EFFECT_KEY].pname[0] = (_("key"));
1540
1541 omc_macros[TOGGLE_EFFECT_KEY].ptypes[0] = OMC_PARAM_INT;
1542 omc_macros[TOGGLE_EFFECT_KEY].mini[0] = 1;
1543 omc_macros[TOGGLE_EFFECT_KEY].vali[0] = 1;
1544 omc_macros[TOGGLE_EFFECT_KEY].maxi[0] = prefs->rte_keys_virtual;
1545 // TRANSLATORS: as in keyboard key
1546 omc_macros[TOGGLE_EFFECT_KEY].pname[0] = (_("key"));
1547
1548 // key
1549 omc_macros[SET_PARAMETER_VALUE].ptypes[0] = OMC_PARAM_INT;
1550 omc_macros[SET_PARAMETER_VALUE].mini[0] = 1;
1551 omc_macros[SET_PARAMETER_VALUE].vali[0] = 1;
1552 omc_macros[SET_PARAMETER_VALUE].maxi[0] = prefs->rte_keys_virtual;
1553 // TRANSLATORS: as in keyboard key
1554 omc_macros[SET_PARAMETER_VALUE].pname[0] = (_("key"));
1555
1556 // param (this will be matched with numeric params)
1557 omc_macros[SET_PARAMETER_VALUE].ptypes[1] = OMC_PARAM_INT;
1558 omc_macros[SET_PARAMETER_VALUE].mini[1] = 0;
1559 omc_macros[SET_PARAMETER_VALUE].maxi[1] = 65536;
1560 omc_macros[SET_PARAMETER_VALUE].vali[1] = 0;
1561 // TRANSLATORS: short form of "parameter number"
1562 omc_macros[SET_PARAMETER_VALUE].pname[1] = (_("pnum"));
1563
1564 // value (this will get special handling)
1565 // type conversion and auto offset/scaling will be done
1566 omc_macros[SET_PARAMETER_VALUE].ptypes[2] = OMC_PARAM_SPECIAL;
1567 omc_macros[SET_PARAMETER_VALUE].mind[2] = 0.;
1568 omc_macros[SET_PARAMETER_VALUE].maxd[2] = 0.;
1569 omc_macros[SET_PARAMETER_VALUE].vald[2] = 0.;
1570 omc_macros[SET_PARAMETER_VALUE].pname[2] = (_("value"));
1571
1572 // set ratio fps (will be handled to avoid 0.)
1573 omc_macros[SET_FPS_RATIO].ptypes[0] = OMC_PARAM_DOUBLE;
1574 omc_macros[SET_FPS_RATIO].mind[0] = -10.;
1575 omc_macros[SET_FPS_RATIO].vald[0] = 1.;
1576 omc_macros[SET_FPS_RATIO].maxd[0] = 10.;
1577 // TRANSLATORS: short form of "frames per second"
1578 omc_macros[SET_FPS_RATIO].pname[0] = (_("fps__ratio"));
1579
1580 // clip retrigger
1581 omc_macros[RETRIGGER_CLIP].ptypes[0] = OMC_PARAM_INT;
1582 omc_macros[RETRIGGER_CLIP].mini[0] = omc_macros[RETRIGGER_CLIP].vali[0] = 1;
1583 omc_macros[RETRIGGER_CLIP].maxi[0] = MAX_FILES;
1584 // TRANSLATORS: short form of "clip number"
1585 omc_macros[RETRIGGER_CLIP].pname[0] = (_("clipnum"));
1586
1587 // key
1588 omc_macros[NEXT_MODE_CYCLE].ptypes[0] = OMC_PARAM_INT;
1589 omc_macros[NEXT_MODE_CYCLE].mini[0] = 1;
1590 omc_macros[NEXT_MODE_CYCLE].vali[0] = 1;
1591 omc_macros[NEXT_MODE_CYCLE].maxi[0] = prefs->rte_keys_virtual;
1592 // TRANSLATORS: as in keyboard key
1593 omc_macros[NEXT_MODE_CYCLE].pname[0] = (_("key"));
1594
1595 // key
1596 omc_macros[PREV_MODE_CYCLE].ptypes[0] = OMC_PARAM_INT;
1597 omc_macros[PREV_MODE_CYCLE].mini[0] = 1;
1598 omc_macros[PREV_MODE_CYCLE].vali[0] = 1;
1599 omc_macros[PREV_MODE_CYCLE].maxi[0] = prefs->rte_keys_virtual;
1600 // TRANSLATORS: as in keyboard key
1601 omc_macros[PREV_MODE_CYCLE].pname[0] = (_("key"));
1602
1603 // param
1604 omc_macros[SET_VPP_PARAMETER_VALUE].ptypes[0] = OMC_PARAM_INT;
1605 omc_macros[SET_VPP_PARAMETER_VALUE].mini[0] = 0;
1606 omc_macros[SET_VPP_PARAMETER_VALUE].maxi[0] = 128;
1607 omc_macros[SET_VPP_PARAMETER_VALUE].vali[0] = 0;
1608 // TRANSLATORS: short form of "parameter number"
1609 omc_macros[SET_VPP_PARAMETER_VALUE].pname[0] = (_("pnum"));
1610
1611 // value (this will get special handling)
1612 // type conversion and auto offset/scaling will be done
1613 omc_macros[SET_VPP_PARAMETER_VALUE].ptypes[1] = OMC_PARAM_SPECIAL;
1614 omc_macros[SET_VPP_PARAMETER_VALUE].mind[1] = 0.;
1615 omc_macros[SET_VPP_PARAMETER_VALUE].maxd[1] = 0.;
1616 omc_macros[SET_VPP_PARAMETER_VALUE].vald[1] = 0.;
1617 omc_macros[SET_VPP_PARAMETER_VALUE].pname[1] = (_("value"));
1618
1619 // variables for LIVES_OSC_NOTIFY_USER1
1620 omc_macros[OSC_NOTIFY].ptypes[0] = OMC_PARAM_INT;
1621 omc_macros[OSC_NOTIFY].mini[0] = 0;
1622 omc_macros[OSC_NOTIFY].vali[0] = 0;
1623 omc_macros[OSC_NOTIFY].maxi[0] = 100000;
1624 omc_macros[OSC_NOTIFY].pname[0] = (_("discrimination"));
1625
1626 omc_macros[OSC_NOTIFY].ptypes[1] = OMC_PARAM_DOUBLE;
1627 omc_macros[OSC_NOTIFY].mini[1] = -1000000.;
1628 omc_macros[OSC_NOTIFY].vali[1] = 0.;
1629 omc_macros[OSC_NOTIFY].maxi[1] = 1000000.;
1630 omc_macros[OSC_NOTIFY].pname[1] = (_("data"));
1631 }
1632
1633
match_filtered_params(lives_omc_match_node_t * mnode,const char * sig,int nfixed)1634 static boolean match_filtered_params(lives_omc_match_node_t *mnode, const char *sig, int nfixed) {
1635 int i;
1636 char **array = lives_strsplit(sig, " ", -1);
1637
1638 for (i = 0; i < mnode->nvars; i++) {
1639 if (mnode->matchp[i]) {
1640 if (mnode->matchi[i] != atoi(array[nfixed + i])) {
1641 //g_print("data mismatch %d %d %d\n",mnode->matchi[i],atoi(array[nfixed+i]),nfixed);
1642 lives_strfreev(array);
1643 return FALSE;
1644 }
1645 }
1646 }
1647 //g_print("data match\n");
1648 lives_strfreev(array);
1649 return TRUE;
1650 }
1651
1652
omc_match_sig(int type,int index,const char * sig)1653 static lives_omc_match_node_t *omc_match_sig(int type, int index, const char *sig) {
1654 LiVESSList *nlist = omc_node_list;
1655 char *srch, *cnodex;
1656 lives_omc_match_node_t *cnode;
1657 int nfixed;
1658
1659 if (type == OMC_MIDI) {
1660 if (index == -1) srch = lives_strdup_printf("%d %s ", type, sig);
1661 else srch = lives_strdup_printf("%d %d %s ", type, index, sig);
1662 } else srch = lives_strdup_printf("%s ", sig);
1663
1664 nfixed = get_nfixed(type, sig);
1665
1666 while (nlist) {
1667 cnode = (lives_omc_match_node_t *)nlist->data;
1668 cnodex = lives_strdup_printf("%s ", cnode->srch);
1669 //g_print("cf %s and %s\n",cnode->srch,srch);
1670 if (!strncmp(cnodex, srch, strlen(cnodex))) {
1671 // got a possible match
1672 // now check the data
1673 if (match_filtered_params(cnode, sig, nfixed)) {
1674 lives_free(srch);
1675 lives_free(cnodex);
1676 return cnode;
1677 }
1678 }
1679 nlist = nlist->next;
1680 lives_free(cnodex);
1681 }
1682 lives_free(srch);
1683 return NULL;
1684 }
1685
1686
1687 /* not used yet */
1688 /*static char *omclearn_request_min(int type) {
1689 char *msg=NULL;
1690
1691 switch (type) {
1692 case OMC_JS_AXIS:
1693 msg=(_("\n\nNow move the stick to the opposite position and click OK\n\n"));
1694 break;
1695 case OMC_MIDI_CONTROLLER:
1696 msg=(_("\n\nPlease set the control to its minimum value and click OK\n\n"));
1697 break;
1698 case OMC_MIDI_NOTE:
1699 msg=(_("\n\nPlease release the note\n\n"));
1700 break;
1701 }
1702
1703 do_error_dialog(msg);
1704 if (msg!=NULL) lives_free(msg);
1705
1706 return NULL;
1707 }*/
1708
1709 /*
1710 LIVES_INLINE int omclearn_get_fixed_elems(const char *string1, const char *string2) {
1711 // count how many (non-space) elements match
1712 // e.g "a b c" and "a b d" returns 2
1713
1714 // neither string may end in a space
1715
1716 register int i;
1717
1718 int match = 0;
1719 int stlen = MIN(strlen(string1), strlen(string2));
1720
1721 for (i = 0; i < stlen; i++) {
1722 if (strcmp((string1 + i), (string2 + i))) return match;
1723 if (!strcmp((string1 + i), " ")) match++;
1724 }
1725
1726 return match + 1;
1727 }
1728 */
1729
get_nth_elem(const char * string,int idx)1730 LIVES_INLINE int get_nth_elem(const char *string, int idx) {
1731 char **array = lives_strsplit(string, " ", -1);
1732 int retval = atoi(array[idx]);
1733 lives_strfreev(array);
1734 return retval;
1735 }
1736
1737
lives_omc_match_node_new(int str_type,int index,const char * string,int nfixed)1738 static lives_omc_match_node_t *lives_omc_match_node_new(int str_type, int index, const char *string, int nfixed) {
1739 int i;
1740 char *tmp;
1741 char *srch_str;
1742 lives_omc_match_node_t *mnode = (lives_omc_match_node_t *)lives_malloc(sizeof(lives_omc_match_node_t));
1743
1744 if (str_type == OMC_MIDI) {
1745 mainw->midi_channel_lock = TRUE;
1746 if (index > -1) srch_str = lives_strdup_printf("%d %d %s", str_type, index, (tmp = cut_string_elems(string,
1747 nfixed < 0 ? -1 : nfixed)));
1748 else srch_str = lives_strdup_printf("%d %s", str_type, (tmp = cut_string_elems(string, nfixed < 0 ? -1 : nfixed)));
1749 lives_free(tmp);
1750 } else {
1751 srch_str = lives_strdup_printf("%s", (tmp = cut_string_elems(string, nfixed < 0 ? -1 : nfixed)));
1752 lives_free(tmp);
1753 }
1754
1755 //g_print("srch_str was %d %d .%s. %d\n", str_type, index, srch_str, nfixed);
1756
1757 mnode->srch = srch_str;
1758 mnode->macro = -1;
1759
1760 if (nfixed < 0) mnode->nvars = -(nfixed + 1);
1761 else mnode->nvars = get_token_count(string, ' ') - nfixed;
1762
1763 if (mnode->nvars > 0) {
1764 mnode->offs0 = (int *)lives_malloc(mnode->nvars * sizint);
1765 mnode->scale = (double *)lives_malloc(mnode->nvars * sizdbl);
1766 mnode->offs1 = (int *)lives_malloc(mnode->nvars * sizint);
1767 mnode->min = (int *)lives_malloc(mnode->nvars * sizint);
1768 mnode->max = (int *)lives_malloc(mnode->nvars * sizint);
1769 mnode->matchp = (boolean *)lives_malloc(mnode->nvars * sizeof(boolean));
1770 mnode->matchi = (int *)lives_malloc(mnode->nvars * sizint);
1771 }
1772
1773 for (i = 0; i < mnode->nvars; i++) {
1774 mnode->offs0[i] = mnode->offs1[i] = 0;
1775 mnode->scale[i] = 1.;
1776 mnode->matchp[i] = FALSE;
1777 }
1778
1779 mnode->map = mnode->fvali = NULL;
1780 mnode->fvald = NULL;
1781
1782 mnode->treev1 = mnode->treev2 = NULL;
1783 mnode->gtkstore = mnode->gtkstore2 = NULL;
1784
1785 return mnode;
1786 }
1787
1788
omclearn_get_values(const char * string,int nfixed)1789 static int *omclearn_get_values(const char *string, int nfixed) {
1790 register int i, j;
1791 size_t slen, tslen;
1792 int *retvals, count = 0, nvars;
1793
1794 slen = strlen(string);
1795
1796 nvars = get_token_count(string, ' ') - nfixed;
1797
1798 retvals = (int *)lives_malloc(nvars * sizint);
1799
1800 for (i = 0; i < slen; i++) {
1801 if (!strncmp((string + i), " ", 1)) {
1802 if (--nfixed <= 0) {
1803 char *tmp = lives_strdup(string + i + 1);
1804 tslen = strlen(tmp);
1805 for (j = 0; j < tslen; j++) {
1806 if (!strncmp((tmp + j), " ", 1)) {
1807 lives_memset(tmp + j, 0, 1);
1808 retvals[count++] = atoi(tmp);
1809 lives_free(tmp);
1810 break;
1811 }
1812 }
1813 if (j == tslen) {
1814 retvals[count++] = atoi(tmp);
1815 lives_free(tmp);
1816 return retvals;
1817 }
1818 i += j;
1819 }
1820 }
1821 }
1822
1823 // should never reach here
1824 return NULL;
1825 }
1826
1827
omclearn_match_control(lives_omc_match_node_t * mnode,int str_type,int index,const char * string,int nfixed,omclearn_w * omclw)1828 void omclearn_match_control(lives_omc_match_node_t *mnode, int str_type, int index, const char *string, int nfixed,
1829 omclearn_w *omclw) {
1830 if (nfixed == -1) {
1831 // already there : allow user to update
1832 return;
1833 }
1834
1835 if (index == -1) {
1836 index = get_nth_elem(string, 1);
1837 }
1838
1839 // add descriptive text on left
1840 // add combo box on right
1841
1842 omc_learner_add_row(str_type, index, mnode, string, omclw);
1843 }
1844
1845
omc_learn(const char * string,int str_type,int idx,omclearn_w * omclw)1846 lives_omc_match_node_t *omc_learn(const char *string, int str_type, int idx, omclearn_w *omclw) {
1847 // here we come with a string, which must be a sequence of integers
1848 // separated by single spaces
1849
1850 // the str_type is one of JS_AXIS, JS_BUTTON, MIDI_CONTROLLER, MIDI_KEY, etc.
1851
1852 // idx is -1, except for JS_BUTTON and JS_AXIS where it can be used
1853
1854 // the string is first transformed into
1855 // signifier and value
1856
1857 // next, we check if signifier is already matched to a macro
1858
1859 // if not we allow the user to match it to any macro that has n or fewer parameters,
1860 // where n is the number of variables in string
1861
1862 lives_omc_match_node_t *mnode;
1863
1864 int nfixed = get_nfixed(str_type, string);
1865
1866 switch (str_type) {
1867 case OMC_MIDI_CONTROLLER:
1868 // display controller and allow it to be matched
1869 // then request min
1870
1871 mnode = omc_match_sig(OMC_MIDI, idx, string);
1872 //g_print("autoscale !\n");
1873
1874 if (!mnode || mnode->macro == UNMATCHED) {
1875 mnode = lives_omc_match_node_new(OMC_MIDI, idx, string, nfixed);
1876 mnode->max[0] = 127;
1877 mnode->min[0] = 0;
1878 idx = midi_index(string);
1879 omclearn_match_control(mnode, str_type, idx, string, nfixed, omclw);
1880 return mnode;
1881 }
1882 break;
1883 case OMC_MIDI_PGM_CHANGE:
1884 // display controller and allow it to be matched
1885
1886 mnode = omc_match_sig(OMC_MIDI, idx, string);
1887 //g_print("autoscale !\n");
1888
1889 if (!mnode || mnode->macro == UNMATCHED) {
1890 mnode = lives_omc_match_node_new(OMC_MIDI, idx, string, nfixed);
1891 mnode->max[0] = 127;
1892 mnode->min[0] = 0;
1893 idx = midi_index(string);
1894 omclearn_match_control(mnode, str_type, idx, string, nfixed, omclw);
1895 return mnode;
1896 }
1897 break;
1898 case OMC_MIDI_PITCH_BEND:
1899 // display controller and allow it to be matched
1900 // then request min
1901
1902 mnode = omc_match_sig(OMC_MIDI, idx, string);
1903 //g_print("autoscale !\n");
1904
1905 if (!mnode || mnode->macro == UNMATCHED) {
1906 mnode = lives_omc_match_node_new(OMC_MIDI, idx, string, nfixed);
1907 mnode->max[0] = 8192;
1908 mnode->min[0] = -8192;
1909 omclearn_match_control(mnode, str_type, idx, string, nfixed, omclw);
1910 return mnode;
1911 }
1912 break;
1913 case OMC_MIDI_NOTE:
1914 case OMC_MIDI_NOTE_OFF:
1915 // display note and allow it to be matched
1916 mnode = omc_match_sig(OMC_MIDI, idx, string);
1917
1918 if (!mnode || mnode->macro == UNMATCHED) {
1919 mnode = lives_omc_match_node_new(OMC_MIDI, idx, string, nfixed);
1920
1921 mnode->max[0] = 127;
1922 mnode->min[0] = 0;
1923
1924 mnode->max[1] = 127;
1925 mnode->min[1] = 0;
1926
1927 omclearn_match_control(mnode, str_type, idx, string, nfixed, omclw);
1928
1929 return mnode;
1930 }
1931 break;
1932 case OMC_JS_AXIS:
1933 // display axis and allow it to be matched
1934 // then request min
1935
1936 mnode = omc_match_sig(str_type, idx, string);
1937
1938 if (!mnode || mnode->macro == UNMATCHED) {
1939 mnode = lives_omc_match_node_new(str_type, idx, string, nfixed);
1940
1941 mnode->min[0] = -128;
1942 mnode->max[0] = 128;
1943
1944 omclearn_match_control(mnode, str_type, idx, string, nfixed, omclw);
1945 return mnode;
1946 }
1947 break;
1948 case OMC_JS_BUTTON:
1949 // display note and allow it to be matched
1950 mnode = omc_match_sig(str_type, idx, string);
1951
1952 if (!mnode || mnode->macro == UNMATCHED) {
1953 mnode = lives_omc_match_node_new(str_type, idx, string, nfixed);
1954 omclearn_match_control(mnode, str_type, idx, string, nfixed, omclw);
1955 return mnode;
1956 }
1957 break;
1958 default:
1959 // hmmm....
1960
1961 break;
1962 }
1963 return NULL;
1964 }
1965
1966
1967 // here we process a string which is formed of (supertype) (type) [(idx)] [(values)]
1968 // eg "val_for_js js_button idx_1 1" => "2 3 1
1969
1970 // in learn mode we store the string + its meaning
1971
1972 // in playback mode, we match the string with our database, and then convert/append the variables
1973
omc_process_string(int supertype,const char * string,boolean learn,omclearn_w * omclw)1974 boolean omc_process_string(int supertype, const char *string, boolean learn, omclearn_w *omclw) {
1975 // only need to set omclw if learn is TRUE
1976
1977 // returns TRUE if we learn new, or if we carry out an action
1978 // retruns FALSE otherwise
1979
1980 boolean ret = FALSE;
1981 int type = -1, idx = -1;
1982 lives_omc_match_node_t *mnode;
1983
1984 if (!string) return FALSE;
1985
1986 if (!omc_macros_inited) {
1987 init_omc_macros();
1988 omc_macros_inited = TRUE;
1989 OSC_initBuffer(&obuf, OSC_BUF_SIZE, byarr);
1990 }
1991
1992 switch (supertype) {
1993 case OMC_INTERNAL:
1994 supertype = type = js_msg_type(string);
1995 idx = js_index(string);
1996 break;
1997 case OMC_JS:
1998 #ifdef OMC_JS_IMPL
1999 supertype = type = js_msg_type(string);
2000 idx = js_index(string);
2001 #endif
2002 break;
2003 #ifdef OMC_MIDI_IMPL
2004 case OMC_MIDI:
2005 type = midi_msg_type(string);
2006 idx = -1;
2007 #endif
2008 }
2009 if (type > -1) {
2010 if (learn) {
2011 // pass to learner
2012 mnode = omc_learn(string, type, idx, omclw);
2013 if (mnode) {
2014 ret = TRUE;
2015 omc_node_list = lives_slist_append(omc_node_list, mnode);
2016 }
2017 } else {
2018 OSCbuf *oscbuf = omc_learner_decode(supertype, idx, string);
2019 // if not playing, the only commands we allow are:
2020 // /video/play
2021 // /clip/foreground/retrigger
2022 // and enabling a generator
2023
2024 // basically only messages which will trigger start of playback
2025
2026 // further checks are performed when enabling/toggling an effect to see whether it is a generator
2027
2028 if (oscbuf && !OSC_isBufferEmpty(oscbuf)) {
2029 if (!LIVES_IS_PLAYING
2030 && strcmp(oscbuf->buffer, "/video/play")
2031 && strcmp(oscbuf->buffer, "/clip/foreground/retrigger")
2032 && strcmp(oscbuf->buffer, "/effect_key/enable")
2033 && strcmp(oscbuf->buffer, "/effect_key/toggle")
2034 ) return FALSE;
2035
2036 lives_osc_act(oscbuf);
2037 ret = TRUE;
2038 }
2039 }
2040 }
2041 return ret;
2042 }
2043
2044
on_midi_learn_activate(LiVESMenuItem * menuitem,livespointer user_data)2045 void on_midi_learn_activate(LiVESMenuItem *menuitem, livespointer user_data) {
2046 omclearn_w *omclw = create_omclearn_dialog();
2047 char *string = NULL;
2048
2049 if (!omc_macros_inited) {
2050 init_omc_macros();
2051 omc_macros_inited = TRUE;
2052 OSC_initBuffer(&obuf, OSC_BUF_SIZE, byarr);
2053 }
2054
2055 #ifdef OMC_MIDI_IMPL
2056 if (!mainw->ext_cntl[EXT_CNTL_MIDI]) midi_open();
2057 #endif
2058
2059 #ifdef OMC_JS_IMPL
2060 if (!mainw->ext_cntl[EXT_CNTL_JS]) js_open();
2061 #endif
2062
2063 mainw->cancelled = CANCEL_NONE;
2064
2065 show_existing(omclw);
2066
2067 // read controls and notes
2068 while (mainw->cancelled == CANCEL_NONE) {
2069 // read from devices
2070
2071 #ifdef OMC_JS_IMPL
2072 if (mainw->ext_cntl[EXT_CNTL_JS]) string = js_mangle();
2073 if (string) {
2074 omc_process_string(OMC_JS, string, TRUE, omclw);
2075 lives_free(string);
2076 string = NULL;
2077 } else {
2078 #endif
2079
2080 #ifdef OMC_MIDI_IMPL
2081 if (mainw->ext_cntl[EXT_CNTL_MIDI]) string = midi_mangle();
2082 //#define TEST_OMC_LEARN
2083 #ifdef TEST_OMC_LEARN
2084 string = lives_strdup("176 10 0 1");
2085 #endif
2086 if (string) {
2087 omc_process_string(OMC_MIDI, string, TRUE, omclw);
2088 lives_free(string);
2089 string = NULL;
2090 }
2091 #endif
2092
2093 #ifdef OMC_JS_IMPL
2094 }
2095 #endif
2096
2097 lives_usleep(prefs->sleep_time);
2098
2099 lives_widget_context_update();
2100 }
2101
2102 remove_all_nodes(FALSE, omclw);
2103
2104 lives_widget_destroy(omclw->dialog);
2105
2106 mainw->cancelled = CANCEL_NONE;
2107
2108 lives_free(omclw);
2109 }
2110
2111
write_fx_tag(const char * string,int nfixed,lives_omc_match_node_t * mnode,lives_omc_macro_t * omacro,char * typetags)2112 static void write_fx_tag(const char *string, int nfixed, lives_omc_match_node_t *mnode, lives_omc_macro_t *omacro,
2113 char *typetags) {
2114 // get typetag for a filter parameter
2115
2116 int i, j, k;
2117 int *vals = omclearn_get_values(string, nfixed);
2118 int oval0 = 1, oval1 = 0;
2119
2120 for (i = 0; i < omacro->nparams; i++) {
2121 // get fixed val or map from
2122 j = mnode->map[i];
2123
2124 if (j > -1) {
2125 if (i == 2) {
2126 // auto scale for fx param
2127 int ntmpls = 0, ptype, flags;
2128 int mode = rte_key_getmode(oval0);
2129 weed_plant_t *filter;
2130 weed_plant_t **ptmpls;
2131 weed_plant_t *ptmpl;
2132
2133 if (mode == -1) return;
2134
2135 filter = rte_keymode_get_filter(oval0, mode);
2136 ptmpls = weed_filter_get_in_paramtmpls(filter, &ntmpls);
2137
2138 for (k = 0; k < ntmpls; k++) {
2139 ptmpl = ptmpls[k];
2140 if (weed_plant_has_leaf(ptmpl, WEED_LEAF_HOST_INTERNAL_CONNECTION)) continue;
2141 ptype = weed_paramtmpl_get_type(ptmpl);
2142 flags = weed_paramtmpl_get_flags(ptmpl);
2143 if (flags & WEED_PARAMETER_VARIABLE_SIZE) flags ^= WEED_PARAMETER_VARIABLE_SIZE;
2144 if ((ptype == WEED_PARAM_INTEGER || ptype == WEED_PARAM_FLOAT) && flags == 0 &&
2145 weed_leaf_num_elements(ptmpl, WEED_LEAF_DEFAULT) == 1) {
2146 if (oval1 == 0) {
2147 if (ptype == WEED_PARAM_INTEGER) {
2148 // **int
2149 lives_strappend(typetags, OSC_MAX_TYPETAGS, "i");
2150 } else {
2151 // float
2152 lives_strappend(typetags, OSC_MAX_TYPETAGS, "f");
2153 }
2154 }
2155 oval1--;
2156 }
2157 }
2158 lives_free(ptmpls);
2159 } else {
2160 // playback plugin params
2161 if (omacro->ptypes[i] == OMC_PARAM_INT) {
2162 int oval = myround((double)(vals[j] + mnode->offs0[j]) * mnode->scale[j]) + mnode->offs1[j];
2163 if (i == 0) oval0 = oval;
2164 if (i == 1) oval1 = oval;
2165 }
2166 }
2167 } else {
2168 if (omacro->ptypes[i] == OMC_PARAM_INT) {
2169 if (i == 0) oval0 = mnode->fvali[i];
2170 if (i == 1) oval1 = mnode->fvali[i];
2171 }
2172 }
2173 }
2174 lives_free(vals);
2175 }
2176
2177
omc_learner_decode(int type,int idx,const char * string)2178 OSCbuf *omc_learner_decode(int type, int idx, const char *string) {
2179 lives_omc_match_node_t *mnode = NULL;
2180 lives_omc_macro_t omacro;
2181 int *vals = NULL;
2182 double oval = 0.;
2183 int macro, nfixed = 0;
2184 int oval0 = 1, oval1 = 0;
2185 int ntmpls = 0, ptype, flags;
2186 int i, j, k;
2187
2188 char typetags[OSC_MAX_TYPETAGS];
2189
2190 if (type == OMC_INTERNAL) {
2191 if (idx < 0 || idx >= N_OMC_MACROS || !omc_macros[idx].msg) return NULL;
2192 macro = idx;
2193 } else {
2194 mnode = omc_match_sig(type, idx, string);
2195
2196 if (!mnode) return NULL;
2197
2198 macro = mnode->macro;
2199
2200 if (macro == UNMATCHED) return NULL;
2201 }
2202
2203 omacro = omc_macros[macro];
2204
2205 if (!omacro.msg) return NULL;
2206
2207 if (type != OMC_INTERNAL) nfixed = get_token_count(string, ' ') - mnode->nvars;
2208
2209 OSC_resetBuffer(&obuf);
2210
2211 if (macro != OSC_NOTIFY) {
2212 lives_snprintf(typetags, OSC_MAX_TYPETAGS, ",");
2213
2214 // TODO ***: OMC_INTERNAL...we want to set param number token[2] with value token[3]
2215 // get typetags
2216 for (i = 0; i < omacro.nparams; i++) {
2217 if (omacro.ptypes[i] == OMC_PARAM_SPECIAL) {
2218 write_fx_tag(string, nfixed, mnode, &omacro, typetags);
2219 } else {
2220 if (omacro.ptypes[i] == OMC_PARAM_INT) lives_strappend(typetags, OSC_MAX_TYPETAGS, "i");
2221 else lives_strappend(typetags, OSC_MAX_TYPETAGS, "f");
2222 }
2223 }
2224 OSC_writeAddressAndTypes(&obuf, omacro.msg, typetags);
2225 }
2226
2227 if (omacro.nparams > 0) {
2228 if (type != OMC_INTERNAL) vals = omclearn_get_values(string, nfixed);
2229
2230 for (i = 0; i < omacro.nparams; i++) {
2231 // get fixed val or map from
2232 if (type != OMC_INTERNAL) j = mnode->map[i];
2233 else j = -1; // TODO *****, get from token[2]
2234 if (j > -1) {
2235 if (macro == SET_VPP_PARAMETER_VALUE && i == 1 && mainw->vpp && mainw->vpp->play_params &&
2236 oval0 < mainw->vpp->num_play_params) {
2237 // auto scale for playback plugin params
2238
2239 weed_plant_t *ptmpl = weed_get_plantptr_value((weed_plant_t *)pp_get_param(mainw->vpp->play_params, oval0),
2240 WEED_LEAF_TEMPLATE, NULL);
2241 ptype = weed_paramtmpl_get_type(ptmpl);
2242 if ((ptype == WEED_PARAM_INTEGER || ptype == WEED_PARAM_FLOAT) &&
2243 weed_leaf_num_elements(ptmpl, WEED_LEAF_DEFAULT) == 1) {
2244 if (ptype == WEED_PARAM_INTEGER) {
2245 int omin = mnode->min[j];
2246 int omax = mnode->max[j];
2247 int mini = weed_get_int_value(ptmpl, WEED_LEAF_MIN, NULL);
2248 int maxi = weed_get_int_value(ptmpl, WEED_LEAF_MAX, NULL);
2249 oval0 = (int)((double)(vals[j] - omin) / (double)(omax - omin) * (double)(maxi - mini)) + mini;
2250 OSC_writeIntArg(&obuf, oval0);
2251 } else {
2252 // float
2253 int omin = mnode->min[j];
2254 int omax = mnode->max[j];
2255 double minf = weed_get_double_value(ptmpl, WEED_LEAF_MIN, NULL);
2256 double maxf = weed_get_double_value(ptmpl, WEED_LEAF_MAX, NULL);
2257 oval = (double)(vals[j] - omin) / (double)(omax - omin) * (maxf - minf) + minf;
2258 OSC_writeFloatArg(&obuf, (float)oval);
2259 } // end float
2260 }
2261 } else {
2262 if (macro == SET_PARAMETER_VALUE && i == 2) {
2263 // auto scale for fx param
2264 int mode = rte_key_getmode(oval0);
2265 weed_plant_t *filter;
2266 weed_plant_t **ptmpls;
2267 weed_plant_t *ptmpl;
2268
2269 if (mode == -1) return NULL;
2270
2271 filter = rte_keymode_get_filter(oval0, mode);
2272
2273 ptmpls = weed_filter_get_in_paramtmpls(filter, &ntmpls);
2274 for (k = 0; k < ntmpls; k++) {
2275 ptmpl = ptmpls[k];
2276 if (weed_plant_has_leaf(ptmpl, WEED_LEAF_HOST_INTERNAL_CONNECTION)) continue;
2277 ptype = weed_paramtmpl_get_type(ptmpl);
2278 flags = weed_paramtmpl_get_flags(ptmpl);
2279 if ((ptype == WEED_PARAM_INTEGER || ptype == WEED_PARAM_FLOAT) && flags == 0 &&
2280 weed_leaf_num_elements(ptmpl, WEED_LEAF_DEFAULT) == 1) {
2281 if (oval1 == 0) {
2282 if (ptype == WEED_PARAM_INTEGER) {
2283 int omin = mnode->min[j];
2284 int omax = mnode->max[j];
2285 int mini = weed_get_int_value(ptmpl, WEED_LEAF_MIN, NULL);
2286 int maxi = weed_get_int_value(ptmpl, WEED_LEAF_MAX, NULL);
2287 int oval = (int)((double)(vals[j] - omin) / (double)(omax - omin) * (double)(maxi - mini)) + mini;
2288 OSC_writeIntArg(&obuf, oval);
2289 } else {
2290 // float
2291 int omin = mnode->min[j];
2292 int omax = mnode->max[j];
2293 double minf = weed_get_double_value(ptmpl, WEED_LEAF_MIN, NULL);
2294 double maxf = weed_get_double_value(ptmpl, WEED_LEAF_MAX, NULL);
2295 oval = (double)(vals[j] - omin) / (double)(omax - omin) * (maxf - minf) + minf;
2296 OSC_writeFloatArg(&obuf, (float)oval);
2297 } // end float
2298 }
2299 oval1--;
2300 }
2301 }
2302 lives_free(ptmpls);
2303 } else {
2304 if (omacro.ptypes[i] == OMC_PARAM_INT) {
2305 int oval;
2306 if (type != OMC_INTERNAL) oval = myround((double)(vals[j] + mnode->offs0[j]) * mnode->scale[j]) + mnode->offs1[j];
2307 else oval = 0; // TODO ****
2308 if (i == 0) oval0 = (int)oval;
2309 if (i == 1) oval1 = (int)oval;
2310 if (macro != OSC_NOTIFY) {
2311 OSC_writeIntArg(&obuf, oval);
2312 }
2313 } else {
2314 double oval;
2315 if (type != OMC_INTERNAL) oval = (double)(vals[j] + mnode->offs0[j]) * mnode->scale[j] + (double)mnode->offs1[j];
2316 else oval = 0.; //
2317 if (macro != OSC_NOTIFY) OSC_writeFloatArg(&obuf, oval);
2318 }
2319 }
2320 }
2321 } else { // use default vals
2322 if (omacro.ptypes[i] == OMC_PARAM_INT) {
2323 if (macro != OSC_NOTIFY) OSC_writeIntArg(&obuf, mnode->fvali[i]);
2324 if (type != OMC_INTERNAL) {
2325 if (i == 0) oval0 = mnode->fvali[i];
2326 if (i == 1) oval1 = mnode->fvali[i];
2327 } else {
2328 if (i == 0) oval0 = omacro.vali[i];
2329 if (i == 1) oval1 = omacro.vali[i];
2330 }
2331 } else {
2332 if (type != OMC_INTERNAL) {
2333 oval = mnode->fvald[i];
2334 } else {
2335 oval = omacro.vald[i];
2336 }
2337 if (macro != OSC_NOTIFY) OSC_writeFloatArg(&obuf, (float)oval);
2338 }
2339 }
2340 }
2341 if (vals) lives_free(vals);
2342 }
2343
2344 if (macro == OSC_NOTIFY) {
2345 char *tmp; // send OSC notificion USER1
2346 if (prefs->show_dev_opts)
2347 g_print("sending noti\n");
2348 lives_notify(LIVES_OSC_NOTIFY_USER1, (tmp = lives_strdup_printf("%d %f", oval0, oval)));
2349 lives_free(tmp);
2350 }
2351
2352 return &obuf;
2353 }
2354
2355
2356 /////////////////////////////////////
2357
2358 /** Save device mapping to an external file
2359 */
2360
on_devicemap_save_activate(LiVESMenuItem * menuitem,livespointer user_data)2361 void on_devicemap_save_activate(LiVESMenuItem *menuitem, livespointer user_data) {
2362 LiVESSList *slist = omc_node_list;
2363
2364 size_t srchlen;
2365
2366 lives_omc_match_node_t *mnode;
2367 lives_omc_macro_t omacro;
2368
2369 char *save_file;
2370 char *devmapdir = lives_build_path(prefs->config_datadir, LIVES_DEVICEMAP_DIR, NULL);
2371
2372 LiVESResponseType retval;
2373
2374 int nnodes;
2375 int fd;
2376
2377 int i;
2378
2379 uint8_t omnimidi;
2380
2381 save_file = choose_file(devmapdir, NULL, NULL, LIVES_FILE_CHOOSER_ACTION_SAVE, NULL, NULL);
2382 lives_free(devmapdir);
2383
2384 if (!save_file) return;
2385 if (!*save_file) {
2386 lives_free(save_file);
2387 return;
2388 }
2389
2390 d_print(_("Saving device mapping to file %s..."), save_file);
2391
2392 do {
2393 retval = 0;
2394 if ((fd = open(save_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) {
2395 retval = do_write_failed_error_s_with_retry(save_file, lives_strerror(errno));
2396 if (retval == LIVES_RESPONSE_CANCEL) {
2397 lives_free(save_file);
2398 d_print_failed();
2399 return;
2400 }
2401 } else {
2402 THREADVAR(write_failed) = FALSE;
2403
2404 lives_write(fd, OMC_FILE_VSTRING, strlen(OMC_FILE_VSTRING), TRUE);
2405
2406 if (prefs->midi_rcv_channel == MIDI_OMNI) omnimidi = 1;
2407 else omnimidi = 0;
2408
2409 lives_write(fd, &omnimidi, 1, TRUE);
2410
2411 nnodes = lives_slist_length(omc_node_list);
2412 lives_write_le(fd, &nnodes, 4, TRUE);
2413
2414 while (slist) {
2415 if (THREADVAR(write_failed)) break;
2416 mnode = (lives_omc_match_node_t *)slist->data;
2417 srchlen = strlen(mnode->srch);
2418
2419 lives_write_le(fd, &srchlen, 4, TRUE);
2420 lives_write(fd, mnode->srch, srchlen, TRUE);
2421
2422 lives_write_le(fd, &mnode->macro, 4, TRUE);
2423 lives_write_le(fd, &mnode->nvars, 4, TRUE);
2424
2425 for (i = 0; i < mnode->nvars; i++) {
2426 if (THREADVAR(write_failed)) break;
2427 lives_write_le(fd, &mnode->offs0[i], 4, TRUE);
2428 lives_write_le(fd, &mnode->scale[i], 8, TRUE);
2429 lives_write_le(fd, &mnode->offs1[i], 4, TRUE);
2430
2431 lives_write_le(fd, &mnode->min[i], 4, TRUE);
2432 lives_write_le(fd, &mnode->max[i], 4, TRUE);
2433
2434 lives_write_le(fd, &mnode->matchp[i], 4, TRUE);
2435 lives_write_le(fd, &mnode->matchi[i], 4, TRUE);
2436 }
2437
2438 omacro = omc_macros[mnode->macro];
2439
2440 for (i = 0; i < omacro.nparams; i++) {
2441 if (THREADVAR(write_failed)) break;
2442 lives_write_le(fd, &mnode->map[i], 4, TRUE);
2443 lives_write_le(fd, &mnode->fvali[i], 4, TRUE);
2444 lives_write_le(fd, &mnode->fvald[i], 8, TRUE);
2445 }
2446 slist = slist->next;
2447 }
2448
2449 close(fd);
2450
2451 if (THREADVAR(write_failed)) {
2452 retval = do_write_failed_error_s_with_retry(save_file, NULL);
2453 if (retval == LIVES_RESPONSE_CANCEL) d_print_file_error_failed();
2454 }
2455 }
2456 } while (retval == LIVES_RESPONSE_RETRY);
2457
2458 if (retval != LIVES_RESPONSE_CANCEL) d_print_done();
2459
2460 lives_free(save_file);
2461 }
2462
2463
omc_node_list_free(LiVESSList * slist)2464 static void omc_node_list_free(LiVESSList *slist) {
2465 while (slist) {
2466 omc_match_node_free((lives_omc_match_node_t *)slist->data);
2467 slist = slist->next;
2468 }
2469 lives_slist_free(slist);
2470 slist = NULL;
2471 }
2472
2473
do_devicemap_load_error(const char * fname)2474 static void do_devicemap_load_error(const char *fname) {
2475 char *msg = lives_strdup_printf(_("\n\nError parsing file\n%s\n"), fname);
2476 do_error_dialog(msg);
2477 lives_free(msg);
2478 }
2479
2480
do_devicemap_version_error(const char * fname)2481 static void do_devicemap_version_error(const char *fname) {
2482 char *msg = lives_strdup_printf(_("\n\nInvalid version in file\n%s\n"), fname);
2483 do_error_dialog(msg);
2484 lives_free(msg);
2485 }
2486
2487
on_devicemap_load_activate(LiVESMenuItem * menuitem,livespointer user_data)2488 void on_devicemap_load_activate(LiVESMenuItem *menuitem, livespointer user_data) {
2489 lives_omc_match_node_t *mnode;
2490 lives_omc_macro_t omacro;
2491
2492 ssize_t bytes;
2493
2494 char tstring[512];
2495
2496 char *load_file = NULL;
2497 char *srch;
2498
2499 uint8_t omnimidi = 1;
2500
2501 uint32_t srchlen, nnodes, macro, nvars, supertype;
2502 int idx = -1;
2503 int fd;
2504 int new_midi_rcv_channel = prefs->midi_rcv_channel;
2505
2506 register int i, j;
2507
2508 #ifdef OMC_MIDI_IMPL
2509 size_t blen;
2510 char *tmp;
2511 #endif
2512
2513 char *devmapdir = lives_build_path(prefs->config_datadir, LIVES_DEVICEMAP_DIR, NULL);
2514
2515 if (!user_data) load_file = choose_file(devmapdir, NULL, NULL, LIVES_FILE_CHOOSER_ACTION_OPEN, NULL, NULL);
2516 else load_file = lives_strdup((char *)user_data);
2517 lives_free(devmapdir);
2518
2519 if (!load_file) return;
2520 if (!*load_file) {
2521 lives_free(load_file);
2522 return;
2523 }
2524
2525 d_print(_("Loading device mapping from file %s..."), load_file);
2526
2527 if ((fd = open(load_file, O_RDONLY)) < 0) {
2528 if (!mainw->go_away) {
2529 char *msg = lives_strdup_printf(_("\n\nUnable to open file\n%s\nError code %d\n"), load_file, errno);
2530 do_error_dialog(msg);
2531 lives_free(msg);
2532 }
2533 lives_free(load_file);
2534 d_print_failed();
2535 return;
2536 }
2537
2538 if (!omc_macros_inited) {
2539 init_omc_macros();
2540 omc_macros_inited = TRUE;
2541 OSC_initBuffer(&obuf, OSC_BUF_SIZE, byarr);
2542 }
2543
2544 bytes = read(fd, tstring, strlen(OMC_FILE_VSTRING));
2545 if (bytes < strlen(OMC_FILE_VSTRING)) {
2546 goto load_failed;
2547 }
2548
2549 if (strncmp(tstring, OMC_FILE_VSTRING, strlen(OMC_FILE_VSTRING))) {
2550 if (strncmp(tstring, OMC_FILE_VSTRING_1_0, strlen(OMC_FILE_VSTRING_1_0))) {
2551 d_print_failed();
2552 if (!mainw->go_away) do_devicemap_version_error(load_file);
2553 lives_free(load_file);
2554 close(fd);
2555 return;
2556 }
2557 } else {
2558 bytes = lives_read(fd, &omnimidi, 1, TRUE);
2559 if (bytes < 1) {
2560 goto load_failed;
2561 }
2562 }
2563
2564 bytes = lives_read_le(fd, &nnodes, 4, TRUE);
2565 if (bytes < 4) {
2566 goto load_failed;
2567 }
2568
2569 if (omc_node_list) {
2570 omc_node_list_free(omc_node_list);
2571 omc_node_list = NULL;
2572 }
2573
2574 for (i = 0; i < nnodes; i++) {
2575 bytes = lives_read_le(fd, &srchlen, 4, TRUE);
2576 if (bytes < 4) {
2577 goto load_failed;
2578 }
2579
2580 srch = (char *)lives_malloc(srchlen + 1);
2581
2582 bytes = read(fd, srch, srchlen);
2583 if (bytes < srchlen) {
2584 goto load_failed2;
2585 }
2586
2587 lives_memset(srch + srchlen, 0, 1);
2588
2589 bytes = lives_read_le(fd, ¯o, 4, TRUE);
2590 if (bytes < sizint) {
2591 goto load_failed2;
2592 }
2593
2594 bytes = lives_read_le(fd, &nvars, 4, TRUE);
2595 if (bytes < 4) {
2596 goto load_failed2;
2597 }
2598
2599 supertype = atoi(srch);
2600
2601 switch (supertype) {
2602 #ifdef OMC_JS_IMPL
2603 case OMC_JS:
2604 supertype = js_msg_type(srch);
2605 case OMC_JS_BUTTON:
2606 case OMC_JS_AXIS:
2607 idx = js_index(srch);
2608 break;
2609 #endif
2610 #ifdef OMC_MIDI_IMPL
2611 case OMC_MIDI:
2612 if (omnimidi && prefs->midi_rcv_channel > MIDI_OMNI) {
2613 new_midi_rcv_channel = MIDI_OMNI;
2614 } else if (!omnimidi && prefs->midi_rcv_channel == MIDI_OMNI) {
2615 new_midi_rcv_channel = 0;
2616 }
2617 idx = -1;
2618
2619 // cut first value (supertype) as we will be added back in match_node_new
2620 tmp = cut_string_elems(srch, 1);
2621 blen = strlen(tmp);
2622 tmp = lives_strdup(srch + blen + 1);
2623 lives_free(srch);
2624 srch = tmp;
2625
2626 break;
2627 #endif
2628 default:
2629 return;
2630 }
2631
2632 mnode = lives_omc_match_node_new(supertype, idx, srch, -(nvars + 1));
2633 lives_free(srch);
2634
2635 mnode->macro = macro;
2636
2637 for (j = 0; j < nvars; j++) {
2638 bytes = lives_read_le(fd, &mnode->offs0[j], 4, TRUE);
2639 if (bytes < 4) {
2640 goto load_failed;
2641 }
2642 bytes = lives_read_le(fd, &mnode->scale[j], 8, TRUE);
2643 if (bytes < 8) {
2644 goto load_failed;
2645 }
2646 bytes = lives_read_le(fd, &mnode->offs1[j], 4, TRUE);
2647 if (bytes < 4) {
2648 goto load_failed;
2649 }
2650 bytes = lives_read_le(fd, &mnode->min[j], 4, TRUE);
2651 if (bytes < 4) {
2652 goto load_failed;
2653 }
2654 bytes = lives_read_le(fd, &mnode->max[j], 4, TRUE);
2655 if (bytes < 4) {
2656 goto load_failed;
2657 }
2658 bytes = lives_read_le(fd, &mnode->matchp[j], 4, TRUE);
2659 if (bytes < 4) {
2660 goto load_failed;
2661 }
2662 bytes = lives_read_le(fd, &mnode->matchi[j], 4, TRUE);
2663 if (bytes < 4) {
2664 goto load_failed;
2665 }
2666 }
2667
2668 omacro = omc_macros[macro];
2669
2670 mnode->map = (int *)lives_malloc(omacro.nparams * sizint);
2671 mnode->fvali = (int *)lives_malloc(omacro.nparams * sizint);
2672 mnode->fvald = (double *)lives_malloc(omacro.nparams * sizdbl);
2673
2674 for (j = 0; j < omacro.nparams; j++) {
2675 bytes = lives_read_le(fd, &mnode->map[j], 4, TRUE);
2676 if (bytes < 4) {
2677 goto load_failed;
2678 }
2679 bytes = lives_read_le(fd, &mnode->fvali[j], 4, TRUE);
2680 if (bytes < 4) {
2681 goto load_failed;
2682 }
2683 bytes = read(fd, &mnode->fvald[j], 8);
2684 if (bytes < 8) {
2685 goto load_failed;
2686 }
2687 }
2688 omc_node_list = lives_slist_append(omc_node_list, (livespointer)mnode);
2689 }
2690
2691 close(fd);
2692 d_print_done();
2693
2694 #ifdef OMC_MIDI_IMPL
2695 if (!mainw->ext_cntl[EXT_CNTL_MIDI]) midi_open();
2696 #endif
2697
2698 #ifdef OMC_JS_IMPL
2699 if (!mainw->ext_cntl[EXT_CNTL_JS]) js_open();
2700 #endif
2701
2702 if (has_devicemap(-1)) lives_widget_set_sensitive(mainw->midi_save, TRUE);
2703
2704 if (new_midi_rcv_channel != prefs->midi_rcv_channel) {
2705 char *dpr;
2706 if (new_midi_rcv_channel == MIDI_OMNI) dpr = (_("MIDI receive channel was set to ALL CHANNELS\n"));
2707 else dpr = lives_strdup_printf(_("MIDI receive channel was set to channel %d\n"), new_midi_rcv_channel);
2708 prefs->midi_rcv_channel = new_midi_rcv_channel;
2709 d_print(dpr);
2710 lives_free(dpr);
2711 do_warning_dialog(
2712 _("The MIDI receive channel setting was updated by the device map.\n"
2713 "Please review the setting in Preferences and adjust it if necessary.\n"));
2714 }
2715 return;
2716
2717 load_failed2:
2718 lives_free(srch);
2719 load_failed:
2720 d_print_failed();
2721 if (!mainw->go_away) do_devicemap_load_error(load_file);
2722 lives_free(load_file);
2723 close(fd);
2724 }
2725
2726 #endif
2727