ardour
smfsh.c
Go to the documentation of this file.
1 /*-
2  * Copyright (c) 2007, 2008 Edward Tomasz NapieraƂa <trasz@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  *
14  * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE
15  * AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18  * THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
20  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
21  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <sysexits.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <assert.h>
41 #include "smf.h"
42 #include "config.h"
43 
44 #ifdef HAVE_LIBREADLINE
45 #include <readline/readline.h>
46 #include <readline/history.h>
47 #endif
48 
51 smf_t *smf = NULL;
52 char *last_file_name = NULL;
53 
54 #define COMMAND_LENGTH 10
55 
56 static void
57 log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer notused)
58 {
59  if (strcmp(log_domain, "smfsh") == 0)
60  fprintf(stderr, "%s\n", message);
61  else
62  fprintf(stderr, "%s: %s\n", log_domain, message);
63 }
64 
65 static int cmd_track(char *arg);
66 
67 static int
68 cmd_load(char *file_name)
69 {
70  char *decoded;
71 
72  if (file_name == NULL) {
73  if (last_file_name == NULL) {
74  g_critical("Please specify file name.");
75  return (-1);
76  }
77 
78  file_name = strdup(last_file_name);
79  } else {
80  file_name = strdup(file_name);
81  }
82 
83  selected_track = NULL;
84  selected_event = NULL;
85 
86  if (smf != NULL) {
87  smf_delete(smf);
88  smf = NULL;
89  }
90 
91  if (last_file_name != NULL)
92  free(last_file_name);
93  last_file_name = strdup(file_name);
94 
95  smf = smf_load(file_name);
96  if (smf == NULL) {
97  g_critical("Couldn't load '%s'.", file_name);
98 
99  smf = smf_new();
100  if (smf == NULL) {
101  g_critical("Cannot initialize smf_t.");
102  return (-1);
103  }
104 
105  return (-2);
106  }
107 
108  g_message("File '%s' loaded.", file_name);
109  decoded = smf_decode(smf);
110  g_message("%s.", decoded);
111  free(decoded);
112 
113  cmd_track("1");
114 
115  free(file_name);
116 
117  return (0);
118 }
119 
120 static int
121 cmd_save(char *file_name)
122 {
123  int ret;
124 
125  if (file_name == NULL) {
126  if (last_file_name == NULL) {
127  g_critical("Please specify file name.");
128  return (-1);
129  }
130 
131  file_name = strdup(last_file_name);
132  } else {
133  file_name = strdup(file_name);
134  }
135 
136  if (last_file_name != NULL)
137  free(last_file_name);
138  last_file_name = strdup(file_name);
139 
140  ret = smf_save(smf, file_name);
141  if (ret) {
142  g_critical("Couldn't save '%s'", file_name);
143  return (-1);
144  }
145 
146  g_message("File '%s' saved.", file_name);
147 
148  free(file_name);
149 
150  return (0);
151 }
152 
153 static int
154 cmd_ppqn(char *new_ppqn)
155 {
156  int tmp;
157  char *end;
158 
159  if (new_ppqn == NULL) {
160  g_message("Pulses Per Quarter Note (aka Division) is %d.", smf->ppqn);
161  } else {
162  tmp = strtol(new_ppqn, &end, 10);
163  if (end - new_ppqn != strlen(new_ppqn)) {
164  g_critical("Invalid PPQN, garbage characters after the number.");
165  return (-1);
166  }
167 
168  if (tmp <= 0) {
169  g_critical("Invalid PPQN, valid values are greater than zero.");
170  return (-2);
171  }
172 
173  if (smf_set_ppqn(smf, tmp)) {
174  g_message("smf_set_ppqn failed.");
175  return (-3);
176  }
177 
178  g_message("Pulses Per Quarter Note changed to %d.", smf->ppqn);
179  }
180 
181  return (0);
182 }
183 
184 static int
185 cmd_format(char *new_format)
186 {
187  int tmp;
188  char *end;
189 
190  if (new_format == NULL) {
191  g_message("Format is %d.", smf->format);
192  } else {
193  tmp = strtol(new_format, &end, 10);
194  if (end - new_format != strlen(new_format)) {
195  g_critical("Invalid format value, garbage characters after the number.");
196  return (-1);
197  }
198 
199  if (tmp < 0 || tmp > 2) {
200  g_critical("Invalid format value, valid values are in range 0 - 2, inclusive.");
201  return (-2);
202  }
203 
204  if (smf_set_format(smf, tmp)) {
205  g_critical("smf_set_format failed.");
206  return (-3);
207  }
208 
209  g_message("Forma changed to %d.", smf->format);
210  }
211 
212  return (0);
213 }
214 
215 static int
216 cmd_tracks(char *notused)
217 {
218  if (smf->number_of_tracks > 0)
219  g_message("There are %d tracks, numbered from 1 to %d.", smf->number_of_tracks, smf->number_of_tracks);
220  else
221  g_message("There are no tracks.");
222 
223  return (0);
224 }
225 
226 static int
227 parse_track_number(const char *arg)
228 {
229  int num;
230  char *end;
231 
232  if (arg == NULL) {
233  if (selected_track == NULL) {
234  g_message("No track currently selected and no track number given.");
235  return (-1);
236  } else {
237  return (selected_track->track_number);
238  }
239  }
240 
241  num = strtol(arg, &end, 10);
242  if (end - arg != strlen(arg)) {
243  g_critical("Invalid track number, garbage characters after the number.");
244  return (-1);
245  }
246 
247  if (num < 1 || num > smf->number_of_tracks) {
248  if (smf->number_of_tracks > 0) {
249  g_critical("Invalid track number specified; valid choices are 1 - %d.", smf->number_of_tracks);
250  } else {
251  g_critical("There are no tracks.");
252  }
253 
254  return (-1);
255  }
256 
257  return (num);
258 }
259 
260 static int
261 cmd_track(char *arg)
262 {
263  int num;
264 
265  if (arg == NULL) {
266  if (selected_track == NULL)
267  g_message("No track currently selected.");
268  else
269  g_message("Currently selected is track number %d, containing %d events.",
270  selected_track->track_number, selected_track->number_of_events);
271  } else {
272  if (smf->number_of_tracks == 0) {
273  g_message("There are no tracks.");
274  return (-1);
275  }
276 
277  num = parse_track_number(arg);
278  if (num < 0)
279  return (-1);
280 
281  selected_track = smf_get_track_by_number(smf, num);
282  if (selected_track == NULL) {
283  g_critical("smf_get_track_by_number() failed, track not selected.");
284  return (-3);
285  }
286 
287  selected_event = NULL;
288 
289  g_message("Track number %d selected; it contains %d events.",
290  selected_track->track_number, selected_track->number_of_events);
291  }
292 
293  return (0);
294 }
295 
296 static int
297 cmd_trackadd(char *notused)
298 {
299  selected_track = smf_track_new();
300  if (selected_track == NULL) {
301  g_critical("smf_track_new() failed, track not created.");
302  return (-1);
303  }
304 
305  smf_add_track(smf, selected_track);
306 
307  selected_event = NULL;
308 
309  g_message("Created new track; track number %d selected.", selected_track->track_number);
310 
311  return (0);
312 }
313 
314 static int
315 cmd_trackrm(char *arg)
316 {
317  int num = parse_track_number(arg);
318 
319  if (num < 0)
320  return (-1);
321 
322  if (selected_track != NULL && num == selected_track->track_number) {
323  selected_track = NULL;
324  selected_event = NULL;
325  }
326 
328 
329  g_message("Track %d removed.", num);
330 
331  return (0);
332 }
333 
334 #define BUFFER_SIZE 1024
335 
336 static int
338 {
339  int off = 0, i;
340  char *decoded, *type;
341 
342  if (smf_event_is_metadata(event))
343  type = "Metadata";
344  else
345  type = "Event";
346 
347  decoded = smf_event_decode(event);
348 
349  if (decoded == NULL) {
350  decoded = malloc(BUFFER_SIZE);
351  if (decoded == NULL) {
352  g_critical("show_event: malloc failed.");
353  return (-1);
354  }
355 
356  off += snprintf(decoded + off, BUFFER_SIZE - off, "Unknown event:");
357 
358  for (i = 0; i < event->midi_buffer_length && i < 5; i++)
359  off += snprintf(decoded + off, BUFFER_SIZE - off, " 0x%x", event->midi_buffer[i]);
360  }
361 
362  g_message("%d: %s: %s, %f seconds, %d pulses, %d delta pulses", event->event_number, type, decoded,
363  event->time_seconds, event->time_pulses, event->delta_time_pulses);
364 
365  free(decoded);
366 
367  return (0);
368 }
369 
370 static int
371 cmd_events(char *notused)
372 {
373  smf_event_t *event;
374 
375  if (selected_track == NULL) {
376  g_critical("No track selected - please use 'track <number>' command first.");
377  return (-1);
378  }
379 
380  if (selected_track->number_of_events == 0) {
381  g_message("Selected track is empty.");
382  return (0);
383  }
384 
385  g_message("List of events in track %d follows:", selected_track->track_number);
386 
387  smf_rewind(smf);
388 
389  while ((event = smf_track_get_next_event(selected_track)) != NULL)
390  show_event(event);
391 
392  smf_rewind(smf);
393 
394  return (0);
395 }
396 
397 static int
398 parse_event_number(const char *arg)
399 {
400  int num;
401  char *end;
402 
403  if (selected_track == NULL) {
404  g_critical("You need to select track first (using 'track <number>').");
405  return (-1);
406  }
407 
408  if (arg == NULL) {
409  if (selected_event == NULL) {
410  g_message("No event currently selected and no event number given.");
411  return (-1);
412  } else {
413  return (selected_event->event_number);
414  }
415  }
416 
417  num = strtol(arg, &end, 10);
418  if (end - arg != strlen(arg)) {
419  g_critical("Invalid event number, garbage characters after the number.");
420  return (-1);
421  }
422 
423  if (num < 1 || num > selected_track->number_of_events) {
424  if (selected_track->number_of_events > 0)
425  g_critical("Invalid event number specified; valid choices are 1 - %d.", selected_track->number_of_events);
426  else
427  g_critical("There are no events in currently selected track.");
428 
429  return (-1);
430  }
431 
432  return (num);
433 }
434 
435 static int
436 cmd_event(char *arg)
437 {
438  int num;
439 
440  if (arg == NULL) {
441  if (selected_event == NULL) {
442  g_message("No event currently selected.");
443  } else {
444  g_message("Currently selected is event %d, track %d.", selected_event->event_number, selected_track->track_number);
445  show_event(selected_event);
446  }
447  } else {
448  num = parse_event_number(arg);
449  if (num < 0)
450  return (-1);
451 
452  selected_event = smf_track_get_event_by_number(selected_track, num);
453  if (selected_event == NULL) {
454  g_critical("smf_get_event_by_number() failed, event not selected.");
455  return (-2);
456  }
457 
458  g_message("Event number %d selected.", selected_event->event_number);
459  show_event(selected_event);
460  }
461 
462  return (0);
463 }
464 
465 static int
466 decode_hex(char *str, unsigned char **buffer, int *length)
467 {
468  int i, value, midi_buffer_length;
469  char buf[3];
470  unsigned char *midi_buffer = NULL;
471  char *end = NULL;
472 
473  if ((strlen(str) % 2) != 0) {
474  g_critical("Hex value should have even number of characters, you know.");
475  goto error;
476  }
477 
478  midi_buffer_length = strlen(str) / 2;
479  midi_buffer = malloc(midi_buffer_length);
480  if (midi_buffer == NULL) {
481  g_critical("malloc() failed.");
482  goto error;
483  }
484 
485  for (i = 0; i < midi_buffer_length; i++) {
486  buf[0] = str[i * 2];
487  buf[1] = str[i * 2 + 1];
488  buf[2] = '\0';
489  value = strtoll(buf, &end, 16);
490 
491  if (end - buf != 2) {
492  g_critical("Garbage characters detected after hex.");
493  goto error;
494  }
495 
496  midi_buffer[i] = value;
497  }
498 
499  *buffer = midi_buffer;
500  *length = midi_buffer_length;
501 
502  return (0);
503 
504 error:
505  if (midi_buffer != NULL)
506  free(midi_buffer);
507 
508  return (-1);
509 }
510 
511 static void
513 {
514  g_message("Usage: add <time-in-seconds> <midi-in-hex> - for example, 'add 1 903C7F' will add");
515  g_message("Note On event, note C4, velocity 127, channel 1, one second from the start of song, channel 1.");
516 }
517 
518 static int
519 cmd_eventadd(char *str)
520 {
521  int midi_buffer_length;
522  double seconds;
523  unsigned char *midi_buffer;
524  char *time, *endtime;
525 
526  if (selected_track == NULL) {
527  g_critical("Please select a track first, using 'track <number>' command.");
528  return (-1);
529  }
530 
531  if (str == NULL) {
532  eventadd_usage();
533  return (-2);
534  }
535 
536  /* Extract the time. Don't use strsep(3), it doesn't work on SunOS. */
537  time = str;
538  str = strchr(str, ' ');
539  if (str != NULL) {
540  *str = '\0';
541  str++;
542  }
543 
544  seconds = strtod(time, &endtime);
545  if (endtime - time != strlen(time)) {
546  g_critical("Time is supposed to be a number, without trailing characters.");
547  return (-3);
548  }
549 
550  /* Called with one parameter? */
551  if (str == NULL) {
552  eventadd_usage();
553  return (-4);
554  }
555 
556  if (decode_hex(str, &midi_buffer, &midi_buffer_length)) {
557  eventadd_usage();
558  return (-5);
559  }
560 
561  selected_event = smf_event_new();
562  if (selected_event == NULL) {
563  g_critical("smf_event_new() failed, event not created.");
564  return (-6);
565  }
566 
567  selected_event->midi_buffer = midi_buffer;
568  selected_event->midi_buffer_length = midi_buffer_length;
569 
570  if (smf_event_is_valid(selected_event) == 0) {
571  g_critical("Event is invalid from the MIDI specification point of view, not created.");
572  smf_event_delete(selected_event);
573  selected_event = NULL;
574  return (-7);
575  }
576 
577  smf_track_add_event_seconds(selected_track, selected_event, seconds);
578 
579  g_message("Event created.");
580 
581  return (0);
582 }
583 
584 static int
585 cmd_text(char *str)
586 {
587  double seconds, type;
588  char *time, *typestr, *end;
589 
590  if (selected_track == NULL) {
591  g_critical("Please select a track first, using 'track <number>' command.");
592  return (-1);
593  }
594 
595  if (str == NULL) {
596  g_critical("Usage: text <time-in-seconds> <event-type> <text-itself>");
597  return (-2);
598  }
599 
600  /* Extract the time. Don't use strsep(3), it doesn't work on SunOS. */
601  time = str;
602  str = strchr(str, ' ');
603  if (str != NULL) {
604  *str = '\0';
605  str++;
606  }
607 
608  seconds = strtod(time, &end);
609  if (end - time != strlen(time)) {
610  g_critical("Time is supposed to be a number, without trailing characters.");
611  return (-3);
612  }
613 
614  /* Called with one parameter? */
615  if (str == NULL) {
616  g_critical("Usage: text <time-in-seconds> <event-type> <text-itself>");
617  return (-4);
618  }
619 
620  /* Extract the event type. */
621  typestr = str;
622  str = strchr(str, ' ');
623  if (str != NULL) {
624  *str = '\0';
625  str++;
626  }
627 
628  type = strtod(typestr, &end);
629  if (end - typestr != strlen(typestr)) {
630  g_critical("Type is supposed to be a number, without trailing characters.");
631  return (-4);
632  }
633 
634  if (type < 1 || type > 9) {
635  g_critical("Valid values for type are 1 - 9, inclusive.");
636  return (-5);
637  }
638 
639  /* Called with one parameter? */
640  if (str == NULL) {
641  g_critical("Usage: text <time-in-seconds> <event-type> <text-itself>");
642  return (-4);
643  }
644 
645  selected_event = smf_event_new_textual(type, str);
646  if (selected_event == NULL) {
647  g_critical("smf_event_new_textual() failed, event not created.");
648  return (-6);
649  }
650 
651  assert(smf_event_is_valid(selected_event));
652 
653  smf_track_add_event_seconds(selected_track, selected_event, seconds);
654 
655  g_message("Event created.");
656 
657  return (0);
658 }
659 
660 
661 static int
662 cmd_eventaddeot(char *time)
663 {
664  double seconds;
665  char *end;
666 
667  if (selected_track == NULL) {
668  g_critical("Please select a track first, using 'track <number>' command.");
669  return (-1);
670  }
671 
672  if (time == NULL) {
673  g_critical("Please specify the time, in seconds.");
674  return (-2);
675  }
676 
677  seconds = strtod(time, &end);
678  if (end - time != strlen(time)) {
679  g_critical("Time is supposed to be a number, without trailing characters.");
680  return (-3);
681  }
682 
683  if (smf_track_add_eot_seconds(selected_track, seconds)) {
684  g_critical("smf_track_add_eot() failed.");
685  return (-4);
686  }
687 
688  g_message("Event created.");
689 
690  return (0);
691 }
692 
693 static int
694 cmd_eventrm(char *number)
695 {
696  int num = parse_event_number(number);
697 
698  if (num < 0)
699  return (-1);
700 
701  if (selected_event != NULL && num == selected_event->event_number)
702  selected_event = NULL;
703 
704  smf_event_delete(smf_track_get_event_by_number(selected_track, num));
705 
706  g_message("Event #%d removed.", num);
707 
708  return (0);
709 }
710 
711 static int
712 cmd_tempo(char *notused)
713 {
714  int i;
715  smf_tempo_t *tempo;
716 
717  for (i = 0;; i++) {
718  tempo = smf_get_tempo_by_number(smf, i);
719  if (tempo == NULL)
720  break;
721 
722  g_message("Tempo #%d: Starts at %d pulses, %f seconds, setting %d microseconds per quarter note, %.2f BPM.",
723  i, tempo->time_pulses, tempo->time_seconds, tempo->microseconds_per_quarter_note,
724  60000000.0 / (double)tempo->microseconds_per_quarter_note);
725  g_message("Time signature: %d/%d, %d clocks per click, %d 32nd notes per quarter note.",
726  tempo->numerator, tempo->denominator, tempo->clocks_per_click, tempo->notes_per_note);
727  }
728 
729  return (0);
730 }
731 
732 static int
733 cmd_length(char *notused)
734 {
735  g_message("Length: %d pulses, %f seconds.", smf_get_length_pulses(smf), smf_get_length_seconds(smf));
736 
737  return (0);
738 }
739 
740 static int
741 cmd_version(char *notused)
742 {
743  g_message("libsmf version %s.", smf_get_version());
744 
745  return (0);
746 }
747 
748 static int
749 cmd_exit(char *notused)
750 {
751  g_debug("Good bye.");
752  exit(0);
753 }
754 
755 static int cmd_help(char *notused);
756 
757 static struct command_struct {
758  char *name;
759  int (*function)(char *command);
760  char *help;
761 } commands[] = {{"help", cmd_help, "Show this help."},
762  {"?", cmd_help, NULL},
763  {"load", cmd_load, "Load named file."},
764  {"open", cmd_load},
765  {"save", cmd_save, "Save to named file."},
766  {"ppqn", cmd_ppqn, "Show ppqn (aka division), or set ppqn if used with parameter."},
767  {"format", cmd_format, "Show format, or set format if used with parameter."},
768  {"tracks", cmd_tracks, "Show number of tracks."},
769  {"track", cmd_track, "Show number of currently selected track, or select a track."},
770  {"trackadd", cmd_trackadd, "Add a track and select it."},
771  {"trackrm", cmd_trackrm, "Remove currently selected track."},
772  {"events", cmd_events, "Show events in the currently selected track."},
773  {"event", cmd_event, "Show number of currently selected event, or select an event."},
774  {"add", cmd_eventadd, "Add an event and select it."},
775  {"text", cmd_text, "Add textual event and select it."},
776  {"eventadd", cmd_eventadd, NULL},
777  {"eot", cmd_eventaddeot, "Add an End Of Track event."},
778  {"eventaddeot", cmd_eventaddeot, NULL},
779  {"eventrm", cmd_eventrm, NULL},
780  {"rm", cmd_eventrm, "Remove currently selected event."},
781  {"tempo", cmd_tempo, "Show tempo map."},
782  {"length", cmd_length, "Show length of the song."},
783  {"version", cmd_version, "Show libsmf version."},
784  {"exit", cmd_exit, "Exit to shell."},
785  {"quit", cmd_exit, NULL},
786  {"bye", cmd_exit, NULL},
787  {NULL, NULL, NULL}};
788 
789 static int
790 cmd_help(char *notused)
791 {
792  int i, padding_length;
793  char padding[COMMAND_LENGTH + 1];
794  struct command_struct *tmp;
795 
796  g_message("Available commands:");
797 
798  for (tmp = commands; tmp->name != NULL; tmp++) {
799  /* Skip commands with no help string. */
800  if (tmp->help == NULL)
801  continue;
802 
803  padding_length = COMMAND_LENGTH - strlen(tmp->name);
804  assert(padding_length >= 0);
805  for (i = 0; i < padding_length; i++)
806  padding[i] = ' ';
807  padding[i] = '\0';
808 
809  g_message("%s:%s%s", tmp->name, padding, tmp->help);
810  }
811 
812  return (0);
813 }
814 
820 static void
821 strip_unneeded_whitespace(char *str, int len)
822 {
823  char *src, *dest;
824  int skip_white = 1;
825 
826  for (src = str, dest = str; src < dest + len; src++) {
827  if (*src == '\n' || *src == '\0') {
828  *dest = '\0';
829  break;
830  }
831 
832  if (isspace(*src)) {
833  if (skip_white)
834  continue;
835 
836  skip_white = 1;
837  } else {
838  skip_white = 0;
839  }
840 
841  *dest = *src;
842  dest++;
843  }
844 
845  /* Remove trailing whitespace. */
846  len = strlen(dest);
847  if (isspace(dest[len - 1]))
848  dest[len - 1] = '\0';
849 }
850 
851 static char *
853 {
854  char *buf;
855  int len;
856 
857 #ifdef HAVE_LIBREADLINE
858  buf = readline("smfsh> ");
859 #else
860  buf = malloc(1024);
861  if (buf == NULL) {
862  g_critical("Malloc failed.");
863  return (NULL);
864  }
865 
866  fprintf(stdout, "smfsh> ");
867  fflush(stdout);
868 
869  buf = fgets(buf, 1024, stdin);
870 #endif
871 
872  if (buf == NULL) {
873  fprintf(stdout, "exit\n");
874  return (strdup("exit"));
875  }
876 
877  strip_unneeded_whitespace(buf, 1024);
878 
879  len = strlen(buf);
880 
881  if (len == 0)
882  return (read_command());
883 
884 #ifdef HAVE_LIBREADLINE
885  add_history(buf);
886 #endif
887 
888  return (buf);
889 }
890 
891 static int
892 execute_command(char *line)
893 {
894  char *command, *args;
895  struct command_struct *tmp;
896 
897  command = line;
898  args = strchr(line, ' ');
899  if (args != NULL) {
900  *args = '\0';
901  args++;
902  }
903 
904  for (tmp = commands; tmp->name != NULL; tmp++) {
905  if (strcmp(tmp->name, command) == 0)
906  return ((tmp->function)(args));
907  }
908 
909  g_warning("No such command: '%s'. Type 'help' to see available commands.", command);
910 
911  return (-1);
912 }
913 
914 static void
916 {
917  int ret;
918  char *command_line, *command, *next_command;
919 
920  command = command_line = read_command();
921 
922  do {
923  next_command = strchr(command, ';');
924  if (next_command != NULL) {
925  *next_command = '\0';
926  next_command++;
927  }
928 
929  strip_unneeded_whitespace(command, 1024);
930  if (strlen(command) > 0) {
931  ret = execute_command(command);
932  if (ret)
933  g_warning("Command finished with error.");
934  }
935 
936  command = next_command;
937 
938  } while (command);
939 
940  free(command_line);
941 }
942 
943 #ifdef HAVE_LIBREADLINE
944 
945 static char *
946 smfsh_command_generator(const char *text, int state)
947 {
948  static struct command_struct *command = commands;
949  char *tmp;
950 
951  if (state == 0)
952  command = commands;
953 
954  while (command->name != NULL) {
955  tmp = command->name;
956  command++;
957 
958  if (strncmp(tmp, text, strlen(text)) == 0)
959  return (strdup(tmp));
960  }
961 
962  return (NULL);
963 }
964 
965 static char **
966 smfsh_completion(const char *text, int start, int end)
967 {
968  int i;
969 
970  /* Return NULL if "text" is not the first word in the input line. */
971  if (start != 0) {
972  for (i = 0; i < start; i++) {
973  if (!isspace(rl_line_buffer[i]))
974  return (NULL);
975  }
976  }
977 
978  return (rl_completion_matches(text, smfsh_command_generator));
979 }
980 
981 #endif
982 
983 static void
984 usage(void)
985 {
986  fprintf(stderr, "usage: smfsh [-V | file]\n");
987 
988  exit(EX_USAGE);
989 }
990 
991 int
992 main(int argc, char *argv[])
993 {
994  int ch;
995 
996  while ((ch = getopt(argc, argv, "V")) != -1) {
997  switch (ch) {
998  case 'V':
999  cmd_version(NULL);
1000  exit(EX_OK);
1001 
1002  case '?':
1003  default:
1004  usage();
1005  }
1006  }
1007 
1008  if (argc > 2)
1009  usage();
1010 
1011  g_log_set_default_handler(log_handler, NULL);
1012 
1013  smf = smf_new();
1014  if (smf == NULL) {
1015  g_critical("Cannot initialize smf_t.");
1016  return (-1);
1017  }
1018 
1019  if (argc == 2)
1020  cmd_load(argv[1]);
1021  else
1022  cmd_trackadd(NULL);
1023 
1024 #ifdef HAVE_LIBREADLINE
1025  rl_readline_name = "smfsh";
1026  rl_attempted_completion_function = smfsh_completion;
1027 #endif
1028 
1029  for (;;)
1031 
1032  return (0);
1033 }
1034 
void smf_rewind(smf_t *smf)
Definition: smf.c:908
char * help
Definition: smfsh.c:760
double smf_get_length_seconds(const smf_t *smf)
Definition: smf.c:1079
int clocks_per_click
Definition: smf.h:269
int32_t delta_time_pulses
Definition: smf.h:309
double time_seconds
Definition: smf.h:265
int smf_event_is_valid(const smf_event_t *event) WARN_UNUSED_RESULT
Definition: smf_load.c:770
int format
Definition: smf.h:235
int getopt(int nargc, char *const *nargv, const char *ostr)
Definition: getopt.c:59
int numerator
Definition: smf.h:267
int smf_set_ppqn(smf_t *smf, uint16_t ppqn)
Definition: smf.c:682
#define BUFFER_SIZE
Definition: smfsh.c:334
size_t midi_buffer_length
Definition: smf.h:324
size_t event_number
Definition: smf.h:305
static int parse_track_number(const char *arg)
Definition: smfsh.c:227
static int cmd_save(char *file_name)
Definition: smfsh.c:121
void smf_add_track(smf_t *smf, smf_track_t *track)
Definition: smf.c:156
LIBPBD_API Transmitter error
smf_event_t * selected_event
Definition: smfsh.c:50
static void usage(void)
Definition: smfsh.c:984
int smf_event_is_metadata(const smf_event_t *event) WARN_UNUSED_RESULT
Definition: smf_decode.c:57
int denominator
Definition: smf.h:268
static int cmd_trackrm(char *arg)
Definition: smfsh.c:315
int track_number
Definition: smf.h:279
int notes_per_note
Definition: smf.h:270
smf_event_t * smf_track_get_event_by_number(const smf_track_t *track, size_t event_number)
Definition: smf.c:778
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > start
Definition: region.cc:63
void smf_event_delete(smf_event_t *event)
Definition: smf.c:371
char * smf_event_decode(const smf_event_t *event) WARN_UNUSED_RESULT
Definition: smf_decode.c:519
static void read_and_execute_command(void)
Definition: smfsh.c:915
char * last_file_name
Definition: smfsh.c:52
smf_tempo_t * smf_get_tempo_by_number(const smf_t *smf, size_t number) WARN_UNUSED_RESULT
Definition: smf_tempo.c:257
static int cmd_eventrm(char *number)
Definition: smfsh.c:694
int(* function)(char *command)
Definition: smfsh.c:759
#define COMMAND_LENGTH
Definition: smfsh.c:54
uint16_t ppqn
Definition: smf.h:239
static int cmd_events(char *notused)
Definition: smfsh.c:371
smf_track_t * selected_track
Definition: smfsh.c:49
static int cmd_track(char *arg)
Definition: smfsh.c:261
static struct command_struct commands[]
static int cmd_help(char *notused)
Definition: smfsh.c:790
void smf_delete(smf_t *smf)
Definition: smf.c:88
static void strip_unneeded_whitespace(char *str, int len)
Definition: smfsh.c:821
const char * smf_get_version(void)
Definition: smf.c:1120
smf_track_t * smf_track_new(void)
Definition: smf.c:110
size_t time_pulses
Definition: smf.h:312
static void log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer notused)
Definition: smfsh.c:57
static int cmd_exit(char *notused)
Definition: smfsh.c:749
int number_of_tracks
Definition: smf.h:242
static void eventadd_usage(void)
Definition: smfsh.c:512
static int cmd_tracks(char *notused)
Definition: smfsh.c:216
smf_event_t * smf_event_new(void)
Definition: smf.c:225
static int cmd_format(char *new_format)
Definition: smfsh.c:185
size_t number_of_events
Definition: smf.h:280
size_t time_pulses
Definition: smf.h:264
static char * read_command(void)
Definition: smfsh.c:852
static int parse_event_number(const char *arg)
Definition: smfsh.c:398
smf_event_t * smf_track_get_next_event(smf_track_t *track)
Definition: smf.c:698
smf_track_t * smf_get_track_by_number(const smf_t *smf, int track_number)
Definition: smf.c:757
static int cmd_trackadd(char *notused)
Definition: smfsh.c:297
static int cmd_version(char *notused)
Definition: smfsh.c:741
static int decode_hex(char *str, unsigned char **buffer, int *length)
Definition: smfsh.c:466
double time_seconds
Definition: smf.h:315
static int cmd_event(char *arg)
Definition: smfsh.c:436
int microseconds_per_quarter_note
Definition: smf.h:266
char * smf_decode(const smf_t *smf) WARN_UNUSED_RESULT
Definition: smf_decode.c:605
smf_event_t * smf_event_new_textual(int type, const char *text)
Definition: smf_save.c:193
static int execute_command(char *line)
Definition: smfsh.c:892
smf_t * smf_load(FILE *) WARN_UNUSED_RESULT
Definition: smf_load.c:930
static int cmd_length(char *notused)
Definition: smfsh.c:733
void smf_track_add_event_seconds(smf_track_t *track, smf_event_t *event, double seconds)
Definition: smf_tempo.c:434
void smf_track_delete(smf_track_t *track)
Definition: smf.c:131
int smf_track_add_eot_seconds(smf_track_t *track, double seconds)
Definition: smf.c:565
static int cmd_eventaddeot(char *time)
Definition: smfsh.c:662
static int cmd_ppqn(char *new_ppqn)
Definition: smfsh.c:154
static int cmd_eventadd(char *str)
Definition: smfsh.c:519
smf_t * smf_new(void)
Definition: smf.c:55
int smf_save(smf_t *smf, FILE *file) WARN_UNUSED_RESULT
Definition: smf_save.c:623
int smf_set_format(smf_t *smf, int format)
Definition: smf.c:660
static int cmd_text(char *str)
Definition: smfsh.c:585
size_t smf_get_length_pulses(const smf_t *smf)
Definition: smf.c:1051
uint8_t * midi_buffer
Definition: smf.h:321
int main(int argc, char *argv[])
Definition: smfsh.c:992
char * name
Definition: smfsh.c:758
static int show_event(smf_event_t *event)
Definition: smfsh.c:337
static int cmd_tempo(char *notused)
Definition: smfsh.c:712
smf_t * smf
Definition: smfsh.c:51
LIBARDOUR_API PBD::PropertyDescriptor< framecnt_t > length
Definition: region.cc:64
static int cmd_load(char *file_name)
Definition: smfsh.c:68