From d6f9d4aa95730831cdaaee62268ee9360362fc05 Mon Sep 17 00:00:00 2001 From: Claudio Matsuoka Date: Tue, 14 Feb 2012 09:16:19 -0200 Subject: [PATCH] [xmp] Add keyboard controls Signed-off-by: Claudio Matsuoka --- Makefile.in | 2 +- src/Makefile | 4 +- src/commands.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++++ src/common.h | 6 ++ src/info.c | 22 +++++-- src/main.c | 43 +++++++++++-- 6 files changed, 234 insertions(+), 14 deletions(-) create mode 100644 src/commands.c diff --git a/Makefile.in b/Makefile.in index 9617cb6..425a21a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,7 +1,7 @@ VERSION = 3.9.0 CC = @CC@ -CFLAGS = -c @CFLAGS@ @DEFS@ @CPPFLAGS@ +CFLAGS = -c -Wall @CFLAGS@ @DEFS@ @CPPFLAGS@ LD = @CC@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ diff --git a/src/Makefile b/src/Makefile index 003e08b..da57385 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,6 @@ -SRC_OBJS = sound_null.o sound_alsa.o terminal.o info.o options.o \ - getopt.o getopt1.o main.o +SRC_OBJS = sound_null.o sound_alsa.o terminal.o info.o commands.o \ + options.o getopt.o getopt1.o main.o SRC_DFILES = Makefile xmp.1 $(SRC_OBJS:.o=.c) common.h getopt.h SRC_PATH = src diff --git a/src/commands.c b/src/commands.c new file mode 100644 index 0000000..4bf2cb2 --- /dev/null +++ b/src/commands.c @@ -0,0 +1,171 @@ +#include +#include +#include "common.h" + +#ifdef __CYGWIN__ +/* + * from daniel åkerud + * date Tue, Jul 28, 2009 at 9:59 AM + * + * Under Cygwin, the read() in process_echoback blocks because VTIME = 0 + * is not handled correctly. To fix this you can either: + * + * 1. Enable "tty emulation" in Cygwin using an environment variable. + * http://www.mail-archive.com/cygwin@cygwin.com/msg99417.html + * For me this is _very_ slow and I can see the characters as they are + * typed out when running xmp. I have not investigated why this is + * happening, but there is of course a reason why this mode is not + * enabled by default. + * + * 2. Do a select() before read()ing if the platform is Cygwin. + * This makes Cygwin builds work out of the box with no fiddling around, + * but does impose a neglectible cpu overhead (for Cygwin builds only). + */ +static int stdin_ready_for_reading() +{ + fd_set fds; + struct timeval tv; + int ret; + + tv.tv_sec = 0; + tv.tv_usec = 0; + + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + + ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); + + if (ret > 0 && FD_ISSET(STDIN_FILENO, &fds)) + return 1; + + return 0; +} +#endif + +static int read_key() +{ + char key; + int ret; + + ret = 0; + +#if defined HAVE_TERMIOS_H && !defined WIN32 +#ifdef __CYGWIN__ + if (stdin_ready_for_reading()) +#endif + ret = read(0, &key, 1); +#elif defined WIN32 + if (kbhit()) { + cmd = getch(); + ret = 1; + } +#elif defined __AMIGA__ + /* Amiga CLI */ + { + BPTR in = Input(); + if (WaitForChar(in, 1)) { + Read(in, &key, 1); + ret = 1; + } + } +#else + ret = 0; +#endif + + if (ret <= 0) { + return -1; + } + + return key; +} + +/* Interactive commands */ + +/* VT100 ESC sequences: + * ESC [ A - up arrow + * ESC [ B - down arrow + * ESC [ C - right arrow + * ESC [ D - left arrow + */ +void read_command(xmp_context ctx, struct control *ctl) +{ + int cmd; + + cmd = read_key(); + if (cmd <= 0) + return; + + switch (cmd) { + case 0x1b: /* escape */ + cmd = read_key(); + if (cmd != '[') + goto cmd_quit; + cmd = read_key(); + switch (cmd) { + case 'A': + goto cmd_next_mod; + case 'B': + goto cmd_prev_mod; + case 'C': + goto cmd_next_ord; + case 'D': + goto cmd_prev_ord; + } + + break; + case 'q': /* quit */ + cmd_quit: + xmp_mod_stop(ctx); + ctl->pause = 0; + ctl->skip = -2; + break; + case 'f': /* jump to next order */ + cmd_next_ord: + xmp_ord_next(ctx); + ctl->pause = 0; + break; + case 'b': /* jump to previous order */ + cmd_prev_ord: + xmp_ord_prev(ctx); + ctl->pause = 0; + break; + case 'n': /* skip to next module */ + cmd_next_mod: + xmp_mod_stop(ctx); + ctl->pause = 0; + ctl->skip = 1; + break; + case 'p': /* skip to previous module */ + cmd_prev_mod: + xmp_mod_stop(ctx); + ctl->pause = 0; + ctl->skip = -1; + break; + case 'l': + ctl->loop ^= 1; + break; + case ' ': /* paused module */ + ctl->pause ^= 1; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + xmp_channel_mute(ctx, cmd - '1', -1); + break; + case '0': + xmp_channel_mute(ctx, 9, -1); + break; + case '!': { + int i; + for (i = 0; i < 10; i++) { + xmp_channel_mute(ctx, i, 0); + } + break; } + } +} diff --git a/src/common.h b/src/common.h index 6052091..09ce572 100644 --- a/src/common.h +++ b/src/common.h @@ -16,6 +16,12 @@ struct options { char mute[XMP_MAX_CHANNELS]; }; +struct control { + int skip; + int loop; + int pause; +}; + int set_tty(void); int reset_tty(void); diff --git a/src/info.c b/src/info.c index eadbe12..bd635b8 100644 --- a/src/info.c +++ b/src/info.c @@ -2,6 +2,8 @@ #include #include +static int max_channels = -1; + void info_mod(struct xmp_module_info *mi) { int i; @@ -24,10 +26,9 @@ void info_mod(struct xmp_module_info *mi) } -void info_frame(struct xmp_module_info *mi, int reset) +void info_frame(struct xmp_module_info *mi, int loop, int reset) { static int ord = -1, tpo = -1, bpm = -1; - static int max_channels = -1; int time; if (reset) { @@ -45,7 +46,7 @@ void info_frame(struct xmp_module_info *mi, int reset) if (mi->order != ord || mi->bpm != bpm || mi->tempo != tpo) { printf("\rTempo[%02X] BPM[%02X] Pos[%02X/%02X] " - "Pat[%02X/%02X] Row[ / ] Chn[ / ] 0:00:00.0", + "Pat[%02X/%02X] Row[ / ] Chn[ / ] 0:00:00.0", mi->tempo, mi->bpm, mi->order, mi->mod->len - 1, mi->pattern, mi->mod->pat - 1); @@ -54,15 +55,26 @@ void info_frame(struct xmp_module_info *mi, int reset) tpo = mi->tempo; } - printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" - "%02X/%02X] Chn[%02X/%02X] %3d:%02d:%02d.%d", + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + "%02X/%02X] Chn[%02X/%02X] %c %3d:%02d:%02d.%d", mi->row, mi->num_rows - 1, mi->virt_used, max_channels, + loop ? 'L' : ' ', (int)(time / (60 * 600)), (int)((time / 600) % 60), (int)((time / 10) % 60), (int)(time % 10)); fflush(stdout); } +void info_pause(struct xmp_module_info *mi, int loop) +{ + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + "%02X/%02X] Chn[%02X/%02X] %c - PAUSED -", + mi->row, mi->num_rows - 1, mi->virt_used, max_channels, + loop ? 'L' : ' '); + + fflush(stdout); +} + void info_instruments_compact(struct xmp_module_info *mi) { int i, j; diff --git a/src/main.c b/src/main.c index 8b5d1d0..2b04bed 100644 --- a/src/main.c +++ b/src/main.c @@ -47,8 +47,10 @@ int main(int argc, char **argv) xmp_context ctx; struct xmp_module_info mi; struct options options; + struct control control; int i; int first; + int skipprev; #ifndef WIN32 struct timeval tv; struct timezone tz; @@ -60,6 +62,7 @@ int main(int argc, char **argv) #endif memset(&options, 0, sizeof (struct options)); + memset(&control, 0, sizeof (struct control)); options.verbose = 1; get_options(argc, argv, &options); @@ -87,15 +90,24 @@ int main(int argc, char **argv) ctx = xmp_create_context(); + skipprev = 0; + for (first = optind; optind < argc; optind++) { printf("\nLoading %s... (%d of %d)\n", argv[optind], optind - first + 1, argc - first); if (xmp_load_module(ctx, argv[optind]) < 0) { fprintf(stderr, "%s: error loading %s\n", argv[0], - argv[i]); + argv[optind]); + if (skipprev) { + optind -= 2; + if (optind < first) { + optind += 2; + } + } continue; } + skipprev = 0; if (xmp_player_start(ctx, options.start, 44100, 0) == 0) { int new_mod = 1; @@ -121,24 +133,43 @@ int main(int argc, char **argv) /* Play module */ while (xmp_player_frame(ctx) == 0) { - + int old_loop = mi.loop_count; + xmp_player_get_info(ctx, &mi); - if (mi.loop_count > 0) +//printf("%d %d\n", old_loop, mi.loop_count); + if (!control.loop && old_loop != mi.loop_count) break; - info_frame(&mi, new_mod); + info_frame(&mi, control.loop, new_mod); sound->play(mi.buffer, mi.buffer_size); + read_command(ctx, &control); + if (control.pause) { + sound->pause(); + info_pause(&mi, control.loop); + while (control.pause) { + usleep(100000); + read_command(ctx, &control); + } + sound->resume(); + } + new_mod = 0; options.start = 0; - } xmp_player_end(ctx); } - sound->flush(); xmp_release_module(ctx); printf("\n"); + + if (control.skip == -1) { + optind -= optind > first ? 2 : 1; + skipprev = 1; + } else if (control.skip == -2) { + break; + } + control.skip = 0; } xmp_free_context(ctx);