LiVES  3.2.0
omc-learn.c
Go to the documentation of this file.
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 
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 
73 boolean has_devicemap(int target) {
74  if (target != -1) {
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 
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 
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 
124  if (!slist) lives_widget_set_sensitive(omclw->del_all_button, FALSE);
126 }
127 
128 
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 
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 
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 
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
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 
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 
250  d_print(_("Responding to joystick events from %s\n"), prefs->omc_js_fname);
251 
252  return TRUE;
253 }
254 
255 
256 void js_close(void) {
257  if (mainw->ext_cntl[EXT_CNTL_JS]) {
258  close(js_fd);
260  }
261 }
262 
263 
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 
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 
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 
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 
391 
392  return TRUE;
393 }
394 
395 
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
413  }
414 }
415 
416 
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 
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;
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;
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;
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;
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;
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 
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 
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 
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 
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)
683 static void rowexpand(LiVESWidget *tv, LiVESTreeIter *iter, LiVESTreePath *path, livespointer ud) {
685 }
686 #endif
687 
688 
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();
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 ();
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 
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 
830 static void on_omc_combo_entry_changed(LiVESCombo *combo, livespointer 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
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 
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 
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 
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 
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 
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();
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();
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();
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();
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
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 
1213 }
1214 
1215 
1216 static void killit(LiVESWidget *widget, livespointer user_data) {
1217  lives_widget_destroy(widget);
1218 }
1219 
1220 
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 
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 
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 
1299 }
1300 
1301 
1302 static void close_learner_dialog(LiVESButton *button, livespointer user_data) {
1305 }
1306 
1307 
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 
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 
1349 
1350  ok_button = lives_dialog_add_button_from_stock(LIVES_DIALOG(omclw->dialog), LIVES_STOCK_CLOSE, _("_Close Window"),
1351  LIVES_RESPONSE_OK);
1352 
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 
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
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 
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 
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 
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 
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;
1743 
1744  if (str_type == OMC_MIDI) {
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 
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 
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 
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 
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 
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
2057 #endif
2058 
2059 #ifdef OMC_JS_IMPL
2060  if (!mainw->ext_cntl[EXT_CNTL_JS]) js_open();
2061 #endif
2062 
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 
2100  }
2101 
2102  remove_all_nodes(FALSE, omclw);
2103 
2104  lives_widget_destroy(omclw->dialog);
2105 
2107 
2108  lives_free(omclw);
2109 }
2110 
2111 
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 
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 
2357 
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) {
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) {
2398  d_print_failed();
2399  return;
2400  }
2401  } else {
2402  THREADVAR(write_failed) = FALSE;
2403 
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)) {
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 
2461 }
2462 
2463 
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 
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 
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 
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, &macro, 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
2696 #endif
2697 
2698 #ifdef OMC_JS_IMPL
2699  if (!mainw->ext_cntl[EXT_CNTL_JS]) js_open();
2700 #endif
2701 
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);
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
widget_opts_t::packing_width
int packing_width
horizontal pixels between widgets
Definition: widget-helper.h:1410
OMC_FP_FIX
#define OMC_FP_FIX
floating point precision
Definition: omc-learn.h:19
lives_window_center
boolean lives_window_center(LiVESWindow *window)
Definition: widget-helper.c:11251
LIVES_IS_PLAYING
#define LIVES_IS_PLAYING
Definition: main.h:840
lives_signal_connect
ulong lives_signal_connect(LiVESWidget *, const char *signal_name, ulong funcptr, livespointer data)
lives_omc_macro_t::msg
char * msg
OSC message.
Definition: omc-learn.h:50
OMC_MIDI
#define OMC_MIDI
Definition: omc-learn.h:132
lives_dialog_add_button_from_stock
LiVESWidget * lives_dialog_add_button_from_stock(LiVESDialog *dialog, const char *stock_id, const char *label, int response_id)
Definition: widget-helper.c:9892
VALUE2_COLUMN
@ VALUE2_COLUMN
Definition: omc-learn.h:121
lives_omc_match_node_t::matchi
int * matchi
match value
Definition: omc-learn.h:78
pp_get_param
const weed_plant_t * pp_get_param(weed_plant_t **pparams, int idx)
Definition: plugins.c:1042
lives_free
#define lives_free
Definition: machinestate.h:52
SET_VPP_PARAMETER_VALUE
#define SET_VPP_PARAMETER_VALUE
Definition: mainwindow.h:670
ADJUSTMENT
@ ADJUSTMENT
Definition: omc-learn.h:122
OMC_MIDI_NOTE_OFF
#define OMC_MIDI_NOTE_OFF
Definition: omc-learn.h:134
lives_malloc
#define lives_malloc
Definition: machinestate.h:46
choose_file
char * choose_file(const char *dir, const char *fname, char **const filt, LiVESFileChooserAction act, const char *title, LiVESWidget *extra_widget)
Definition: interface.c:4080
lives_widget_destroy
LIVES_GLOBAL_INLINE boolean lives_widget_destroy(LiVESWidget *widget)
Definition: widget-helper.c:1553
SCR_HEIGHT_SAFETY
#define SCR_HEIGHT_SAFETY
Definition: mainwindow.h:90
rte_keymode_get_filter
weed_plant_t * rte_keymode_get_filter(int key, int mode)
returns filter_class bound to key/mode (or NULL)
Definition: effects-weed.c:9465
lives_tree_path_get_indices
WIDGET_HELPER_GLOBAL_INLINE int * lives_tree_path_get_indices(LiVESTreePath *tpath)
Definition: widget-helper.c:5799
lives_read_le
ssize_t lives_read_le(int fd, void *buf, ssize_t count, boolean allow_less)
Definition: utils.c:486
OMC_FILE_VSTRING
#define OMC_FILE_VSTRING
Definition: omc-learn.h:149
lives_omc_macro_t::macro_text
char * macro_text
macro text
Definition: omc-learn.h:51
lives_omc_match_node_t::srch
char * srch
string to match
Definition: omc-learn.h:66
_prefs::midi_rcv_channel
int midi_rcv_channel
Definition: preferences.h:334
omclearn_w::clear_button
LiVESWidget * clear_button
Definition: omc-learn.h:99
lives_window_maximize
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_maximize(LiVESWindow *window)
Definition: widget-helper.c:2889
lives_table_set_col_spacings
WIDGET_HELPER_GLOBAL_INLINE boolean lives_table_set_col_spacings(LiVESTable *table, uint32_t spacing)
Definition: widget-helper.c:6972
lives_dialog_get_content_area
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_dialog_get_content_area(LiVESDialog *dialog)
Definition: widget-helper.c:2479
PLAY_BACKWARDS
#define PLAY_BACKWARDS
Definition: mainwindow.h:649
_prefs::show_gui
boolean show_gui
Definition: preferences.h:290
_prefs::omc_midi_fname
char omc_midi_fname[PATH_MAX]
utf8
Definition: preferences.h:321
VALUE_COLUMN
@ VALUE_COLUMN
Definition: events.c:5554
STOP_PLAYBACK
#define STOP_PLAYBACK
Definition: mainwindow.h:646
FILTER_COLUMN
@ FILTER_COLUMN
Definition: omc-learn.h:111
mainwindow::vpp
_vid_playback_plugin * vpp
video plugin
Definition: mainwindow.h:1572
_palette::style
int style
Definition: mainwindow.h:297
OMC_DEV_MIDI
#define OMC_DEV_MIDI
Definition: omc-learn.h:10
effects.h
lives_table_attach
WIDGET_HELPER_GLOBAL_INLINE boolean lives_table_attach(LiVESTable *table, LiVESWidget *child, uint32_t left, uint32_t right, uint32_t top, uint32_t bottom, LiVESAttachOptions xoptions, LiVESAttachOptions yoptions, uint32_t xpad, uint32_t ypad)
Definition: widget-helper.c:7035
STOP_RECORDING
#define STOP_RECORDING
Definition: mainwindow.h:656
lives_omc_macro_t::nparams
int nparams
Definition: omc-learn.h:55
lives_omc_macro_t
Definition: omc-learn.h:49
lives_standard_dialog_new
LiVESWidget * lives_standard_dialog_new(const char *title, boolean add_std_buttons, int width, int height)
Definition: widget-helper.c:9971
prefs
_prefs * prefs
Definition: preferences.h:847
OMC_PARAM_SPECIAL
#define OMC_PARAM_SPECIAL
can be int or double, depending on effect type
Definition: omc-learn.h:47
omc_process_string
boolean omc_process_string(int supertype, const char *string, boolean learn, omclearn_w *omclw)
process a string (i.e. convert to an OSC message and pass to OSC subsys) only need to set omclw if le...
RETRIGGER_CLIP
#define RETRIGGER_CLIP
Definition: mainwindow.h:667
lives_omc_macro_t::mind
double * mind
Definition: omc-learn.h:62
omclearn_w::table
LiVESWidget * table
Definition: omc-learn.h:104
omclearn_w
Definition: omc-learn.h:97
LIVES_OSC_NOTIFY_USER1
#define LIVES_OSC_NOTIFY_USER1
user defined notification (e.g. external sync)
Definition: osc_notify.h:58
_palette::menu_and_bars_fore
LiVESWidgetColor menu_and_bars_fore
Definition: mainwindow.h:328
lives_omc_match_node_t::offs0
int * offs0
offs to add to params before scale (pre-bias)
Definition: omc-learn.h:70
lives_button_grab_default_special
boolean lives_button_grab_default_special(LiVESWidget *button)
Definition: widget-helper.c:7587
PLAY_FASTER
#define PLAY_FASTER
Definition: mainwindow.h:651
_palette::menu_and_bars
LiVESWidgetColor menu_and_bars
Definition: mainwindow.h:327
get_token_count
size_t get_token_count(const char *string, int delim)
Definition: utils.c:5430
sizdbl
ssize_t sizdbl
Definition: main.c:102
CANCEL_USER
@ CANCEL_USER
user pressed stop
Definition: main.h:704
omc_learner_decode
OSCbuf * omc_learner_decode(int type, int index, const char *string)
decode learnt behaviours
PLAY_FORWARDS
#define PLAY_FORWARDS
Definition: mainwindow.h:648
_prefs::rte_keys_virtual
short rte_keys_virtual
Definition: preferences.h:223
_prefs::gui_monitor
int gui_monitor
Definition: preferences.h:305
_prefs::midi_rpt
int midi_rpt
Definition: preferences.h:316
_prefs::omc_dev_opts
uint32_t omc_dev_opts
Definition: preferences.h:318
do_error_dialog
LIVES_GLOBAL_INLINE LiVESResponseType do_error_dialog(const char *text)
Definition: dialogs.c:749
EXT_CNTL_JS
@ EXT_CNTL_JS
Definition: mainwindow.h:219
lives_tree_model_get
WIDGET_HELPER_GLOBAL_INLINE boolean lives_tree_model_get(LiVESTreeModel *tmod, LiVESTreeIter *titer,...)
Definition: widget-helper.c:5708
SET_FPS_RATIO
#define SET_FPS_RATIO
Definition: mainwindow.h:666
lives_tree_model_get_iter
WIDGET_HELPER_GLOBAL_INLINE boolean lives_tree_model_get_iter(LiVESTreeModel *tmod, LiVESTreeIter *titer, LiVESTreePath *tpath)
Definition: widget-helper.c:5721
tv
struct timeval tv
Definition: main.h:1136
TRUE
#define TRUE
Definition: videoplugin.h:59
lives_omc_match_node_t::treev1
LiVESWidget * treev1
Definition: omc-learn.h:88
lives_omc_macro_t::info_text
char * info_text
descriptive text
Definition: omc-learn.h:52
sizint
ssize_t sizint
type sizes
Definition: main.c:102
mainwindow::string_constants
char * string_constants[NUM_LIVES_STRING_CONSTANTS]
Definition: mainwindow.h:1539
lives_memset
#define lives_memset
Definition: machinestate.h:61
_prefs::default_fps
double default_fps
Definition: preferences.h:173
THREADVAR
#define THREADVAR(var)
Definition: machinestate.h:531
lives_table_new
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_table_new(uint32_t rows, uint32_t cols, boolean homogeneous)
Definition: widget-helper.c:6931
omclearn_w::tbl_currow
int tbl_currow
Definition: omc-learn.h:103
lives_omc_match_node_t::min
int * min
min values of input params
Definition: omc-learn.h:74
lives_cell_renderer_text_new
WIDGET_HELPER_GLOBAL_INLINE LiVESCellRenderer * lives_cell_renderer_text_new(void)
Definition: widget-helper.c:5334
lives_standard_combo_new
LiVESWidget * lives_standard_combo_new(const char *labeltext, LiVESList *list, LiVESBox *box, const char *tooltip)
Definition: widget-helper.c:9544
OMC_JS_AXIS
#define OMC_JS_AXIS
Definition: omc-learn.h:129
PREV_CLIP_SELECT
#define PREV_CLIP_SELECT
Definition: mainwindow.h:665
mainwindow::cancelled
volatile lives_cancel_t cancelled
Definition: mainwindow.h:798
lives_tree_store_new
WIDGET_HELPER_GLOBAL_INLINE LiVESTreeStore * lives_tree_store_new(int ncols,...)
Definition: widget-helper.c:5808
OMC_NUM2_COLUMNS
@ OMC_NUM2_COLUMNS
Definition: omc-learn.h:123
TITLE_COLUMN
@ TITLE_COLUMN
Definition: events.c:5552
lives_omc_match_node_t::macro
int macro
action number this is linked to (or -1) (see mainwindow.h)
Definition: omc-learn.h:67
START_RECORDING
#define START_RECORDING
Definition: mainwindow.h:655
callbacks.h
lives_omc_match_node_t::fvali
int * fvali
defaults, mapping to fixed ints
Definition: omc-learn.h:83
PREV_MODE_CYCLE
#define PREV_MODE_CYCLE
Definition: mainwindow.h:669
d_print
void d_print(const char *fmt,...)
Definition: utils.c:2542
_vid_playback_plugin::play_params
weed_plant_t ** play_params
Definition: plugins.h:194
omclearn_w::del_all_button
LiVESWidget * del_all_button
Definition: omc-learn.h:100
_prefs::open_maximised
boolean open_maximised
Definition: preferences.h:28
lives_table_resize
WIDGET_HELPER_GLOBAL_INLINE boolean lives_table_resize(LiVESTable *table, uint32_t rows, uint32_t cols)
Definition: widget-helper.c:7011
lives_tree_store_set
WIDGET_HELPER_GLOBAL_INLINE boolean lives_tree_store_set(LiVESTreeStore *tstore, LiVESTreeIter *titer,...)
Definition: widget-helper.c:5851
lives_adjustment_new
WIDGET_HELPER_GLOBAL_INLINE LiVESAdjustment * lives_adjustment_new(double value, double lower, double upper, double step_increment, double page_increment, double page_size)
Definition: widget-helper.c:3196
lives_tree_path_free
WIDGET_HELPER_GLOBAL_INLINE boolean lives_tree_path_free(LiVESTreePath *tpath)
Definition: widget-helper.c:5772
mainwindow::midi_channel_lock
boolean midi_channel_lock
Definition: mainwindow.h:1587
lives_omc_match_node_t::gtkstore
LiVESTreeStore * gtkstore
Definition: omc-learn.h:91
SET_FRAMERATE
#define SET_FRAMERATE
Definition: mainwindow.h:654
_prefs::omc_js_fname
char omc_js_fname[PATH_MAX]
utf8
Definition: preferences.h:320
d_print_file_error_failed
void d_print_file_error_failed(void)
Definition: utils.c:2625
LIVES_DEVICEMAP_DIR
#define LIVES_DEVICEMAP_DIR
Definition: mainwindow.h:615
OFFS2_COLUMN
@ OFFS2_COLUMN
Definition: omc-learn.h:115
OMC_DEV_JS
#define OMC_DEV_JS
Definition: omc-learn.h:11
lives_standard_label_new
LiVESWidget * lives_standard_label_new(const char *text)
Definition: widget-helper.c:8601
lives_combo_set_active_index
WIDGET_HELPER_GLOBAL_INLINE boolean lives_combo_set_active_index(LiVESCombo *combo, int index)
Definition: widget-helper.c:3883
weed_filter_get_in_paramtmpls
WEED_GLOBAL_INLINE weed_plant_t ** weed_filter_get_in_paramtmpls(weed_plant_t *filter, int *ntmpls)
Definition: weed-effects-utils.c:257
weed_paramtmpl_get_type
WEED_GLOBAL_INLINE int weed_paramtmpl_get_type(weed_plant_t *paramtmpl)
Definition: weed-effects-utils.c:312
interface.h
lives_cell_renderer_toggle_new
WIDGET_HELPER_GLOBAL_INLINE LiVESCellRenderer * lives_cell_renderer_toggle_new(void)
Definition: widget-helper.c:5354
weed_paramtmpl_get_flags
WEED_GLOBAL_INLINE int weed_paramtmpl_get_flags(weed_plant_t *paramtmpl)
Definition: weed-effects-utils.c:300
TOGGLE_FREEZE
#define TOGGLE_FREEZE
Definition: mainwindow.h:653
d_print_failed
void d_print_failed(void)
Definition: utils.c:2615
lives_omc_match_node_t::nvars
int nvars
number of input params
Definition: omc-learn.h:69
DISABLE_EFFECT_KEY
#define DISABLE_EFFECT_KEY
Definition: mainwindow.h:661
has_devicemap
boolean has_devicemap(int has_this_macro)
lives_omc_macro_t::vali
int * vali
Definition: omc-learn.h:60
lives_write_le
ssize_t lives_write_le(int fd, livesconstpointer buf, ssize_t count, boolean allow_fail)
CANCEL_NONE
@ CANCEL_NONE
no cancel
Definition: main.h:701
lives_cell_renderer_spin_new
WIDGET_HELPER_GLOBAL_INLINE LiVESCellRenderer * lives_cell_renderer_spin_new(void)
Definition: widget-helper.c:5343
OSC_MAX_TYPETAGS
#define OSC_MAX_TYPETAGS
Definition: omc-learn.h:160
do_warning_dialog
LIVES_GLOBAL_INLINE boolean do_warning_dialog(const char *text)
Definition: dialogs.c:564
palette
_palette * palette
interface colour settings
Definition: main.c:101
save_file
void save_file(int clip, int start, int end, const char *filename)
Definition: saveplay.c:1260
lives_signal_handlers_disconnect_by_func
WIDGET_HELPER_GLOBAL_INLINE boolean lives_signal_handlers_disconnect_by_func(livespointer instance, LiVESGuiCallback func, livespointer data)
Definition: widget-helper.c:1399
lives_tree_view_append_column
WIDGET_HELPER_GLOBAL_INLINE int lives_tree_view_append_column(LiVESTreeView *tview, LiVESTreeViewColumn *tvcol)
Definition: widget-helper.c:5909
ENABLE_EFFECT_KEY
#define ENABLE_EFFECT_KEY
Definition: mainwindow.h:660
OSC_NOTIFY
#define OSC_NOTIFY
Definition: mainwindow.h:671
OMC_INTERNAL
#define OMC_INTERNAL
Definition: omc-learn.h:126
OMC_MIDI_PGM_CHANGE
#define OMC_MIDI_PGM_CHANGE
Definition: omc-learn.h:137
lives_strdup_printf
#define lives_strdup_printf(fmt,...)
Definition: support.c:27
RESET_EFFECT_KEYS
#define RESET_EFFECT_KEYS
Definition: mainwindow.h:659
GUI_SCREEN_HEIGHT
#define GUI_SCREEN_HEIGHT
Definition: mainwindow.h:100
get_midi_filename
const char * get_midi_filename(void)
NEXT_CLIP_SELECT
#define NEXT_CLIP_SELECT
Definition: mainwindow.h:664
paramwindow.h
lives_tree_view_column_new_with_attributes
WIDGET_HELPER_GLOBAL_INLINE LiVESTreeViewColumn * lives_tree_view_column_new_with_attributes(const char *title, LiVESCellRenderer *crend,...)
Definition: widget-helper.c:5940
lives_omc_match_node_t::offs1
int * offs1
offs to add to params after scale (post bias)
Definition: omc-learn.h:72
OMC_MIDI_NOTE
#define OMC_MIDI_NOTE
Definition: omc-learn.h:133
SWAP_FOREGROUND_BACKGROUND
#define SWAP_FOREGROUND_BACKGROUND
Definition: mainwindow.h:658
midi_close
void midi_close(void)
lives_tree_store_append
WIDGET_HELPER_GLOBAL_INLINE boolean lives_tree_store_append(LiVESTreeStore *tstore, LiVESTreeIter *titer, LiVESTreeIter *parent)
Definition: widget-helper.c:5831
WEED_LEAF_HOST_INTERNAL_CONNECTION
#define WEED_LEAF_HOST_INTERNAL_CONNECTION
Definition: effects-weed.h:102
lives_read
ssize_t lives_read(int fd, void *buf, ssize_t count, boolean allow_less)
Definition: utils.c:460
mainwindow::midi_save
LiVESWidget * midi_save
Definition: mainwindow.h:1188
OMC_JS_BUTTON
#define OMC_JS_BUTTON
Definition: omc-learn.h:130
_palette::black
LiVESWidgetColor black
Definition: mainwindow.h:307
lives_memcpy
#define lives_memcpy
Definition: machinestate.h:55
UNMATCHED
#define UNMATCHED
Definition: mainwindow.h:644
lives_widget_set_sensitive
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_sensitive(LiVESWidget *widget, boolean state)
Definition: widget-helper.c:1477
lives_strappend
LIVES_GLOBAL_INLINE int lives_strappend(const char *string, int len, const char *xnew)
Definition: machinestate.c:1436
lives_combo_append_text
WIDGET_HELPER_GLOBAL_INLINE boolean lives_combo_append_text(LiVESCombo *combo, const char *text)
Definition: widget-helper.c:3802
myround
#define myround(n)
Definition: main.h:300
SCALE_COLUMN
@ SCALE_COLUMN
Definition: omc-learn.h:114
OMC_MIDI_CONTROLLER
#define OMC_MIDI_CONTROLLER
Definition: omc-learn.h:135
mainwindow::go_away
boolean go_away
Definition: mainwindow.h:1614
lives_omc_match_node_t::map
int * map
mapping macro parameters to variables in the input (whether we use a default or a variable)
Definition: omc-learn.h:81
lives_omc_macro_t::mini
int * mini
Definition: omc-learn.h:60
lives_widget_queue_resize
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_queue_resize(LiVESWidget *widget)
Definition: widget-helper.c:1605
TOGGLE_RECORDING
#define TOGGLE_RECORDING
Definition: mainwindow.h:657
do_write_failed_error_s_with_retry
LiVESResponseType do_write_failed_error_s_with_retry(const char *fname, const char *errtext)
Definition: dialogs.c:4058
lives_tree_path_get_depth
WIDGET_HELPER_GLOBAL_INLINE int lives_tree_path_get_depth(LiVESTreePath *tpath)
Definition: widget-helper.c:5790
lives_omc_match_node_t::gtkstore2
LiVESTreeStore * gtkstore2
Definition: omc-learn.h:92
lives_tree_view_new_with_model
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_tree_view_new_with_model(LiVESTreeModel *tmod)
Definition: widget-helper.c:5864
main.h
OMC_PARAM_INT
#define OMC_PARAM_INT
Definition: omc-learn.h:45
lives_omc_match_node_t::matchp
boolean * matchp
do we additionally need to match this param val in the input ?
Definition: omc-learn.h:77
lives_window_unmaximize
WIDGET_HELPER_GLOBAL_INLINE boolean lives_window_unmaximize(LiVESWindow *window)
Definition: widget-helper.c:2898
MAX_FILES
#define MAX_FILES
max files is actually 1 more than this, since file 0 is the clipboard
Definition: main.h:184
lives_combo_get_active_text
WIDGET_HELPER_GLOBAL_INLINE const char * lives_combo_get_active_text(LiVESCombo *combo)
Definition: widget-helper.c:3874
get_omc_macro
const lives_omc_macro_t * get_omc_macro(int idx)
_prefs::sleep_time
int sleep_time
Definition: preferences.h:176
lives_notify
void lives_notify(int msgnumber, const char *msgstring)
Definition: callbacks.c:49
sig
#define sig(a)
Definition: main.h:268
mainw
mainwindow * mainw
Definition: main.c:103
LIVES_STRING_CONSTANT_NONE
@ LIVES_STRING_CONSTANT_NONE
Definition: mainwindow.h:371
on_midi_learn_activate
void on_midi_learn_activate(LiVESMenuItem *, livespointer)
start learning MIDI inputs
EXT_CNTL_MIDI
@ EXT_CNTL_MIDI
Definition: mainwindow.h:220
OMC_FILE_VSTRING_1_0
#define OMC_FILE_VSTRING_1_0
Definition: omc-learn.h:150
lives_standard_scrolled_window_new
LiVESWidget * lives_standard_scrolled_window_new(int width, int height, LiVESWidget *child)
Definition: widget-helper.c:10272
TOGGLE_EFFECT_KEY
#define TOGGLE_EFFECT_KEY
Definition: mainwindow.h:662
lives_omc_macro_t::ptypes
int * ptypes
Definition: omc-learn.h:59
midi_mangle
char * midi_mangle(void)
lives_widget_show_all
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_show_all(LiVESWidget *widget)
Definition: widget-helper.c:1523
TREE_ROW_HEIGHT
#define TREE_ROW_HEIGHT
(unexpanded) height of rows in treeviews
Definition: mainwindow.h:96
RANGE_COLUMN
@ RANGE_COLUMN
Definition: omc-learn.h:112
lives_container_foreach
WIDGET_HELPER_GLOBAL_INLINE boolean lives_container_foreach(LiVESContainer *cont, LiVESWidgetCallback callback, livespointer cb_data)
Definition: widget-helper.c:4957
lives_omc_macro_t::pname
char ** pname
Definition: omc-learn.h:57
widget_opts
widget_opts_t widget_opts
Definition: widget-helper.h:1442
TITLE2_COLUMN
@ TITLE2_COLUMN
Definition: omc-learn.h:120
lives_omc_macro_t::maxd
double * maxd
Definition: omc-learn.h:62
midi_open
boolean midi_open(void)
omc-learn.h
lives_write
ssize_t lives_write(int fd, livesconstpointer buf, ssize_t count, boolean allow_fail)
GUI_SCREEN_WIDTH
#define GUI_SCREEN_WIDTH
Definition: mainwindow.h:99
MIDI_OMNI
#define MIDI_OMNI
Definition: omc-learn.h:28
OMC_JS
#define OMC_JS
Definition: omc-learn.h:128
OMC_MIDI_PITCH_BEND
#define OMC_MIDI_PITCH_BEND
Definition: omc-learn.h:136
SET_PARAMETER_VALUE
#define SET_PARAMETER_VALUE
Definition: mainwindow.h:663
FPS_MAX
#define FPS_MAX
maximum fps we will allow (double)
Definition: main.h:218
OSC_BUF_SIZE
#define OSC_BUF_SIZE
Definition: omc-learn.h:159
lives_omc_match_node_t::scale
double * scale
scale for params (unbiased scale)
Definition: omc-learn.h:71
lives_omc_macro_t::maxi
int * maxi
Definition: omc-learn.h:60
d_print_done
void d_print_done(void)
Definition: utils.c:2620
omclearn_w::tbl_rows
int tbl_rows
Definition: omc-learn.h:102
rte_key_getmode
int rte_key_getmode(int key)
returns current active mode for a key (or -1)
Definition: effects-weed.c:9424
omclearn_w::dialog
LiVESWidget * dialog
Definition: omc-learn.h:98
lives_widget_set_fg_color
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_fg_color(LiVESWidget *widget, LiVESWidgetState state, const LiVESWidgetColor *color)
Definition: widget-helper.c:2079
START_PLAYBACK
#define START_PLAYBACK
Definition: mainwindow.h:645
on_devicemap_save_activate
void on_devicemap_save_activate(LiVESMenuItem *, livespointer)
lives_omc_match_node_t::fvald
double * fvald
defaults, mapping to fixed doubles
Definition: omc-learn.h:84
NEXT_MODE_CYCLE
#define NEXT_MODE_CYCLE
Definition: mainwindow.h:668
N_OMC_MACROS
#define N_OMC_MACROS
max number of macros
Definition: omc-learn.h:16
lives_tree_path_new_from_string
WIDGET_HELPER_GLOBAL_INLINE LiVESTreePath * lives_tree_path_new_from_string(const char *path)
Definition: widget-helper.c:5781
_prefs::config_datadir
char config_datadir[PATH_MAX]
kept in locale encoding (general config files) (default ~/.local/share/lives)
Definition: preferences.h:64
lives_omc_match_node_t
Definition: omc-learn.h:65
CLIP_SELECT
#define CLIP_SELECT
Definition: mainwindow.h:647
FALSE
#define FALSE
Definition: videoplugin.h:60
lives_widget_set_size_request
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_size_request(LiVESWidget *widget, int width, int height)
Definition: widget-helper.c:1614
lives_omc_match_node_t::treev2
LiVESWidget * treev2
Definition: omc-learn.h:89
OMC_NUM_COLUMNS
@ OMC_NUM_COLUMNS
Definition: omc-learn.h:116
lives_widget_set_text_color
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_text_color(LiVESWidget *widget, LiVESWidgetState state, const LiVESWidgetColor *color)
Definition: widget-helper.c:2101
on_devicemap_load_activate
void on_devicemap_load_activate(LiVESMenuItem *, livespointer)
_
#define _(String)
Definition: support.h:44
STYLE_1
#define STYLE_1
turn on theming if set
Definition: mainwindow.h:299
omclearn_w::top_vbox
LiVESWidget * top_vbox
Definition: omc-learn.h:105
OMC_PARAM_DOUBLE
#define OMC_PARAM_DOUBLE
Definition: omc-learn.h:46
OFFS1_COLUMN
@ OFFS1_COLUMN
Definition: omc-learn.h:113
lives_omc_macro_t::vald
double * vald
Definition: omc-learn.h:62
LIVES_INLINE
#define LIVES_INLINE
Definition: main.h:238
lives_omc_match_node_t::max
int * max
max values of input params
Definition: omc-learn.h:75
lives_widget_set_base_color
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_base_color(LiVESWidget *widget, LiVESWidgetState state, const LiVESWidgetColor *color)
Definition: widget-helper.c:2126
lives_widget_context_update
boolean lives_widget_context_update(void)
Definition: widget-helper.c:11878
SCR_WIDTH_SAFETY
#define SCR_WIDTH_SAFETY
sepwin/screen size safety margins in pixels
Definition: mainwindow.h:89
PLAY_SLOWER
#define PLAY_SLOWER
Definition: mainwindow.h:652
REVERSE_PLAYBACK
#define REVERSE_PLAYBACK
Definition: mainwindow.h:650
mainwindow::ext_cntl
boolean ext_cntl[MAX_EXT_CNTL]
external control inputs
Definition: mainwindow.h:1579
_prefs::show_dev_opts
boolean show_dev_opts
Definition: preferences.h:463
lives_box_pack_start
WIDGET_HELPER_GLOBAL_INLINE boolean lives_box_pack_start(LiVESBox *box, LiVESWidget *child, boolean expand, boolean fill, uint32_t padding)
Definition: widget-helper.c:3281