smflite

annotate smfsh/smfsh.c @ 1:8e535ca4bb86

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