44 #ifdef HAVE_LIBREADLINE
45 #include <readline/readline.h>
46 #include <readline/history.h>
54 #define COMMAND_LENGTH 10
57 log_handler(
const gchar *log_domain, GLogLevelFlags log_level,
const gchar *message, gpointer notused)
59 if (strcmp(log_domain,
"smfsh") == 0)
60 fprintf(stderr,
"%s\n", message);
62 fprintf(stderr,
"%s: %s\n", log_domain, message);
72 if (file_name == NULL) {
74 g_critical(
"Please specify file name.");
80 file_name = strdup(file_name);
83 selected_track = NULL;
84 selected_event = NULL;
97 g_critical(
"Couldn't load '%s'.", file_name);
101 g_critical(
"Cannot initialize smf_t.");
108 g_message(
"File '%s' loaded.", file_name);
110 g_message(
"%s.", decoded);
125 if (file_name == NULL) {
127 g_critical(
"Please specify file name.");
133 file_name = strdup(file_name);
142 g_critical(
"Couldn't save '%s'", file_name);
146 g_message(
"File '%s' saved.", file_name);
159 if (new_ppqn == NULL) {
160 g_message(
"Pulses Per Quarter Note (aka Division) is %d.", smf->
ppqn);
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.");
169 g_critical(
"Invalid PPQN, valid values are greater than zero.");
174 g_message(
"smf_set_ppqn failed.");
178 g_message(
"Pulses Per Quarter Note changed to %d.", smf->
ppqn);
190 if (new_format == NULL) {
191 g_message(
"Format is %d.", smf->
format);
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.");
199 if (tmp < 0 || tmp > 2) {
200 g_critical(
"Invalid format value, valid values are in range 0 - 2, inclusive.");
205 g_critical(
"smf_set_format failed.");
209 g_message(
"Forma changed to %d.", smf->
format);
221 g_message(
"There are no tracks.");
233 if (selected_track == NULL) {
234 g_message(
"No track currently selected and no track number given.");
241 num = strtol(arg, &end, 10);
242 if (end - arg != strlen(arg)) {
243 g_critical(
"Invalid track number, garbage characters after the number.");
249 g_critical(
"Invalid track number specified; valid choices are 1 - %d.", smf->
number_of_tracks);
251 g_critical(
"There are no tracks.");
266 if (selected_track == NULL)
267 g_message(
"No track currently selected.");
269 g_message(
"Currently selected is track number %d, containing %d events.",
273 g_message(
"There are no tracks.");
282 if (selected_track == NULL) {
283 g_critical(
"smf_get_track_by_number() failed, track not selected.");
287 selected_event = NULL;
289 g_message(
"Track number %d selected; it contains %d events.",
300 if (selected_track == NULL) {
301 g_critical(
"smf_track_new() failed, track not created.");
307 selected_event = NULL;
309 g_message(
"Created new track; track number %d selected.", selected_track->
track_number);
322 if (selected_track != NULL && num == selected_track->
track_number) {
323 selected_track = NULL;
324 selected_event = NULL;
329 g_message(
"Track %d removed.", num);
334 #define BUFFER_SIZE 1024
340 char *decoded, *type;
349 if (decoded == NULL) {
351 if (decoded == NULL) {
352 g_critical(
"show_event: malloc failed.");
356 off += snprintf(decoded + off,
BUFFER_SIZE - off,
"Unknown event:");
358 for (i = 0; i <
event->midi_buffer_length && i < 5; i++)
362 g_message(
"%d: %s: %s, %f seconds, %d pulses, %d delta pulses", event->
event_number, type, decoded,
375 if (selected_track == NULL) {
376 g_critical(
"No track selected - please use 'track <number>' command first.");
381 g_message(
"Selected track is empty.");
385 g_message(
"List of events in track %d follows:", selected_track->
track_number);
403 if (selected_track == NULL) {
404 g_critical(
"You need to select track first (using 'track <number>').");
409 if (selected_event == NULL) {
410 g_message(
"No event currently selected and no event number given.");
417 num = strtol(arg, &end, 10);
418 if (end - arg != strlen(arg)) {
419 g_critical(
"Invalid event number, garbage characters after the number.");
425 g_critical(
"Invalid event number specified; valid choices are 1 - %d.", selected_track->
number_of_events);
427 g_critical(
"There are no events in currently selected track.");
441 if (selected_event == NULL) {
442 g_message(
"No event currently selected.");
453 if (selected_event == NULL) {
454 g_critical(
"smf_get_event_by_number() failed, event not selected.");
458 g_message(
"Event number %d selected.", selected_event->
event_number);
468 int i, value, midi_buffer_length;
470 unsigned char *midi_buffer = NULL;
473 if ((strlen(str) % 2) != 0) {
474 g_critical(
"Hex value should have even number of characters, you know.");
478 midi_buffer_length = strlen(str) / 2;
479 midi_buffer = malloc(midi_buffer_length);
480 if (midi_buffer == NULL) {
481 g_critical(
"malloc() failed.");
485 for (i = 0; i < midi_buffer_length; i++) {
487 buf[1] = str[i * 2 + 1];
489 value = strtoll(buf, &end, 16);
491 if (end - buf != 2) {
492 g_critical(
"Garbage characters detected after hex.");
496 midi_buffer[i] = value;
499 *buffer = midi_buffer;
500 *length = midi_buffer_length;
505 if (midi_buffer != NULL)
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.");
521 int midi_buffer_length;
523 unsigned char *midi_buffer;
524 char *time, *endtime;
526 if (selected_track == NULL) {
527 g_critical(
"Please select a track first, using 'track <number>' command.");
538 str = strchr(str,
' ');
544 seconds = strtod(time, &endtime);
545 if (endtime - time != strlen(time)) {
546 g_critical(
"Time is supposed to be a number, without trailing characters.");
556 if (
decode_hex(str, &midi_buffer, &midi_buffer_length)) {
562 if (selected_event == NULL) {
563 g_critical(
"smf_event_new() failed, event not created.");
571 g_critical(
"Event is invalid from the MIDI specification point of view, not created.");
573 selected_event = NULL;
579 g_message(
"Event created.");
587 double seconds, type;
588 char *time, *typestr, *end;
590 if (selected_track == NULL) {
591 g_critical(
"Please select a track first, using 'track <number>' command.");
596 g_critical(
"Usage: text <time-in-seconds> <event-type> <text-itself>");
602 str = strchr(str,
' ');
608 seconds = strtod(time, &end);
609 if (end - time != strlen(time)) {
610 g_critical(
"Time is supposed to be a number, without trailing characters.");
616 g_critical(
"Usage: text <time-in-seconds> <event-type> <text-itself>");
622 str = strchr(str,
' ');
628 type = strtod(typestr, &end);
629 if (end - typestr != strlen(typestr)) {
630 g_critical(
"Type is supposed to be a number, without trailing characters.");
634 if (type < 1 || type > 9) {
635 g_critical(
"Valid values for type are 1 - 9, inclusive.");
641 g_critical(
"Usage: text <time-in-seconds> <event-type> <text-itself>");
646 if (selected_event == NULL) {
647 g_critical(
"smf_event_new_textual() failed, event not created.");
655 g_message(
"Event created.");
667 if (selected_track == NULL) {
668 g_critical(
"Please select a track first, using 'track <number>' command.");
673 g_critical(
"Please specify the time, in seconds.");
677 seconds = strtod(time, &end);
678 if (end - time != strlen(time)) {
679 g_critical(
"Time is supposed to be a number, without trailing characters.");
684 g_critical(
"smf_track_add_eot() failed.");
688 g_message(
"Event created.");
701 if (selected_event != NULL && num == selected_event->
event_number)
702 selected_event = NULL;
706 g_message(
"Event #%d removed.", num);
722 g_message(
"Tempo #%d: Starts at %d pulses, %f seconds, setting %d microseconds per quarter note, %.2f BPM.",
725 g_message(
"Time signature: %d/%d, %d clocks per click, %d 32nd notes per quarter note.",
751 g_debug(
"Good bye.");
759 int (*
function)(
char *command);
763 {
"load",
cmd_load,
"Load named file."},
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."},
775 {
"text",
cmd_text,
"Add textual event and select it."},
780 {
"rm",
cmd_eventrm,
"Remove currently selected event."},
782 {
"length",
cmd_length,
"Show length of the song."},
784 {
"exit",
cmd_exit,
"Exit to shell."},
792 int i, padding_length;
796 g_message(
"Available commands:");
800 if (tmp->
help == NULL)
804 assert(padding_length >= 0);
805 for (i = 0; i < padding_length; i++)
809 g_message(
"%s:%s%s", tmp->
name, padding, tmp->
help);
826 for (src = str, dest = str; src < dest + len; src++) {
827 if (*src ==
'\n' || *src ==
'\0') {
847 if (isspace(dest[len - 1]))
848 dest[len - 1] =
'\0';
857 #ifdef HAVE_LIBREADLINE
858 buf = readline(
"smfsh> ");
862 g_critical(
"Malloc failed.");
866 fprintf(stdout,
"smfsh> ");
869 buf = fgets(buf, 1024, stdin);
873 fprintf(stdout,
"exit\n");
874 return (strdup(
"exit"));
884 #ifdef HAVE_LIBREADLINE
894 char *command, *args;
898 args = strchr(line,
' ');
905 if (strcmp(tmp->
name, command) == 0)
909 g_warning(
"No such command: '%s'. Type 'help' to see available commands.", command);
918 char *command_line, *command, *next_command;
923 next_command = strchr(command,
';');
924 if (next_command != NULL) {
925 *next_command =
'\0';
930 if (strlen(command) > 0) {
933 g_warning(
"Command finished with error.");
936 command = next_command;
943 #ifdef HAVE_LIBREADLINE
946 smfsh_command_generator(
const char *text,
int state)
954 while (command->
name != NULL) {
958 if (strncmp(tmp, text, strlen(text)) == 0)
959 return (strdup(tmp));
966 smfsh_completion(
const char *text,
int start,
int end)
972 for (i = 0; i <
start; i++) {
973 if (!isspace(rl_line_buffer[i]))
978 return (rl_completion_matches(text, smfsh_command_generator));
986 fprintf(stderr,
"usage: smfsh [-V | file]\n");
996 while ((ch =
getopt(argc, argv,
"V")) != -1) {
1015 g_critical(
"Cannot initialize smf_t.");
1024 #ifdef HAVE_LIBREADLINE
1025 rl_readline_name =
"smfsh";
1026 rl_attempted_completion_function = smfsh_completion;
void smf_rewind(smf_t *smf)
double smf_get_length_seconds(const smf_t *smf)
int32_t delta_time_pulses
int smf_event_is_valid(const smf_event_t *event) WARN_UNUSED_RESULT
int getopt(int nargc, char *const *nargv, const char *ostr)
int smf_set_ppqn(smf_t *smf, uint16_t ppqn)
size_t midi_buffer_length
static int parse_track_number(const char *arg)
static int cmd_save(char *file_name)
void smf_add_track(smf_t *smf, smf_track_t *track)
LIBPBD_API Transmitter error
smf_event_t * selected_event
int smf_event_is_metadata(const smf_event_t *event) WARN_UNUSED_RESULT
static int cmd_trackrm(char *arg)
smf_event_t * smf_track_get_event_by_number(const smf_track_t *track, size_t event_number)
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > start
void smf_event_delete(smf_event_t *event)
char * smf_event_decode(const smf_event_t *event) WARN_UNUSED_RESULT
static void read_and_execute_command(void)
smf_tempo_t * smf_get_tempo_by_number(const smf_t *smf, size_t number) WARN_UNUSED_RESULT
static int cmd_eventrm(char *number)
int(* function)(char *command)
static int cmd_events(char *notused)
smf_track_t * selected_track
static int cmd_track(char *arg)
static struct command_struct commands[]
static int cmd_help(char *notused)
void smf_delete(smf_t *smf)
static void strip_unneeded_whitespace(char *str, int len)
const char * smf_get_version(void)
smf_track_t * smf_track_new(void)
static void log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer notused)
static int cmd_exit(char *notused)
static void eventadd_usage(void)
static int cmd_tracks(char *notused)
smf_event_t * smf_event_new(void)
static int cmd_format(char *new_format)
static char * read_command(void)
static int parse_event_number(const char *arg)
smf_event_t * smf_track_get_next_event(smf_track_t *track)
smf_track_t * smf_get_track_by_number(const smf_t *smf, int track_number)
static int cmd_trackadd(char *notused)
static int cmd_version(char *notused)
static int decode_hex(char *str, unsigned char **buffer, int *length)
static int cmd_event(char *arg)
int microseconds_per_quarter_note
char * smf_decode(const smf_t *smf) WARN_UNUSED_RESULT
smf_event_t * smf_event_new_textual(int type, const char *text)
static int execute_command(char *line)
smf_t * smf_load(FILE *) WARN_UNUSED_RESULT
static int cmd_length(char *notused)
void smf_track_add_event_seconds(smf_track_t *track, smf_event_t *event, double seconds)
void smf_track_delete(smf_track_t *track)
int smf_track_add_eot_seconds(smf_track_t *track, double seconds)
static int cmd_eventaddeot(char *time)
static int cmd_ppqn(char *new_ppqn)
static int cmd_eventadd(char *str)
int smf_save(smf_t *smf, FILE *file) WARN_UNUSED_RESULT
int smf_set_format(smf_t *smf, int format)
static int cmd_text(char *str)
size_t smf_get_length_pulses(const smf_t *smf)
int main(int argc, char *argv[])
static int show_event(smf_event_t *event)
static int cmd_tempo(char *notused)
LIBARDOUR_API PBD::PropertyDescriptor< framecnt_t > length
static int cmd_load(char *file_name)