mplay

Controllable music player
git clone https://git.sinitax.com/sinitax/mplay
Log | Files | Refs | sfeed.txt

commit 016d89d50b91a1865f04288b33c8f2d4779a5513
parent 6df0caaa1fc7bf68b2e96947da52c068d507b898
Author: Louis Burda <quent.burda@gmail.com>
Date:   Sat,  9 Mar 2024 20:27:59 +0100

Rework to select mplay impl by extension

Diffstat:
D.gitmodules | 3---
DLICENSE | 21---------------------
MMakefile | 3+--
Dlib/minimp3 | 1-
Mmplay.c | 738+++++++------------------------------------------------------------------------
Amplay.h | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 169 insertions(+), 707 deletions(-)

diff --git a/.gitmodules b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "lib/minimp3"] - path = lib/minimp3 - url = git@github.com:lieff/minimp3.git diff --git a/LICENSE b/LICENSE @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 Louis Burda - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/Makefile b/Makefile @@ -1,8 +1,7 @@ PREFIX ?= /usr/local BINDIR ?= /bin -CFLAGS = -I lib/minimp3/ -Wunused-variable -Wunused-function -Wconversion -LDFLAGS = -lpulse -lpulse-simple +CFLAGS = -Wunused-variable -Wunused-function -Wconversion ifeq ($(DEBUG),1) CFLAGS += -Og -g diff --git a/lib/minimp3 b/lib/minimp3 @@ -1 +0,0 @@ -Subproject commit afb604c06bc8beb145fecd42c0ceb5bda8795144 diff --git a/mplay.c b/mplay.c @@ -1,703 +1,81 @@ -#define MINIMP3_IMPLEMENTATION -#include "minimp3.h" - -#include <pulse/sample.h> -#include <pulse/introspect.h> -#include <pulse/operation.h> -#include <pulse/stream.h> -#include <pulse/thread-mainloop.h> -#include <pulse/volume.h> -#include <pulse/pulseaudio.h> -#include <pulse/def.h> - -#include <pthread.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <termios.h> #include <unistd.h> -#include <err.h> -#include <stdint.h> -#include <stdbool.h> +#include <dirent.h> + +#include <stdarg.h> #include <stdio.h> +#include <string.h> #include <stdlib.h> -enum { - KEY_NONE = 0, - KEY_BRK = 3, - KEY_EOF = 0x100, - KEY_LEFT, - KEY_RIGHT, - KEY_UP, - KEY_DOWN, -}; - -struct audiofile { - uint8_t *data; - size_t len; -}; - -struct mp3d { - mp3dec_t dec; - uint8_t *pos; - ssize_t left; - - mp3d_sample_t *samples; - size_t sample_cnt; - size_t sample_cap; - - size_t sample_next; - - bool seek; - - int rate; - int channels; - - bool init; - bool pause; -}; - -static pa_sample_spec pa_spec = { - .format = PA_SAMPLE_S16LE, - .rate = 44100, - .channels = 2 -}; - -static pa_buffer_attr pa_buf = { - .fragsize = UINT32_MAX, - .maxlength = UINT32_MAX, - .tlength = UINT32_MAX, - .prebuf = UINT32_MAX, - .minreq = UINT32_MAX, -}; - -static pa_stream_flags_t pa_stream_flags = PA_STREAM_START_CORKED - | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY; - -static pa_threaded_mainloop *pa_mloop; -static pa_context *pa_ctx; -static pa_stream *pa_strm; -static pa_sink_input_info pa_strm_sink; -static bool pa_strm_sink_update; - -static struct audiofile audiofile = { 0 }; - -static struct mp3d mp3d = { 0 }; - -static pthread_t input_worker_thread; - -static struct termios term_orig; -static struct termios term_raw; -static bool term_set = false; - -static bool cmd_input_mode = false; -static bool use_stdio = true; -static bool debug = false; - -static uint8_t * -map_file(const char *path, size_t *len) -{ - struct stat attr; - uint8_t *buf; - int fd; - - fd = open(path, O_RDONLY); - if (fd < 0) err(1, "open %s", path); - - if (fstat(fd, &attr)) - err(1, "fstat %s", path); - - if ((attr.st_mode & S_IFMT) == S_IFDIR) - errx(1, "not a file: %s", path); - - buf = mmap(NULL, (size_t) attr.st_size, PROT_READ, MAP_SHARED, fd, 0); - if (!buf) err(1, "mmap %s", path); - - *len = (size_t) attr.st_size; - - close(fd); - - return buf; -} - -static size_t -decode_next_frame(mp3d_sample_t *samples, size_t *size) -{ - static const size_t max_cnt = MINIMP3_MAX_SAMPLES_PER_FRAME; - mp3dec_frame_info_t info; - size_t cnt; - - if (size) *size = 0; - - if (mp3d.sample_next < mp3d.sample_cnt) { - cnt = MIN(max_cnt, mp3d.sample_cnt - mp3d.sample_next); - *size = cnt * sizeof(mp3d_sample_t); - } else { - if (mp3d.sample_cnt + max_cnt > mp3d.sample_cap) { - mp3d.sample_cap = MAX(mp3d.sample_cap * 2, - mp3d.sample_cnt + max_cnt); - mp3d.samples = realloc(mp3d.samples, - mp3d.sample_cap * sizeof(mp3d_sample_t)); - if (!mp3d.samples) err(1, "realloc"); - } - - cnt = (size_t) mp3dec_decode_frame(&mp3d.dec, mp3d.pos, - (int) mp3d.left, &mp3d.samples[mp3d.sample_cnt], &info); - cnt *= (size_t) info.channels; - mp3d.sample_cnt += cnt; - - if (size) *size = (size_t) info.frame_bytes; - - if (!info.frame_bytes) - return 0; - - if (!mp3d.init) { - mp3d.rate = info.hz; - mp3d.channels = info.channels; - } - - mp3d.pos += info.frame_bytes; - mp3d.left -= info.frame_bytes; - } - - if (samples != NULL) - memcpy(samples, &mp3d.samples[mp3d.sample_next], - cnt * sizeof(mp3d_sample_t)); - mp3d.sample_next += cnt; - - return cnt; -} - -static void -decoder_init(void) -{ - mp3d_sample_t samples[MINIMP3_MAX_SAMPLES_PER_FRAME]; - size_t size; - - mp3dec_init(&mp3d.dec); - mp3d.pos = audiofile.data; - mp3d.left = (ssize_t) audiofile.len; - - mp3d.sample_cnt = 0; - mp3d.sample_next = 0; - mp3d.sample_cap = MINIMP3_MAX_SAMPLES_PER_FRAME; - mp3d.samples = malloc(mp3d.sample_cap * sizeof(mp3d_sample_t)); - if (!mp3d.samples) err(1, "malloc"); - - /* decode channel specs */ - decode_next_frame(samples, &size); - if (!size) errx(1, "Invalid mp3"); - - mp3d.seek = false; - mp3d.pause = false; - mp3d.init = true; -} - -static void -decoder_seek(size_t sample_pos) -{ - size_t size, cnt; - - while (mp3d.sample_next < sample_pos) { - cnt = decode_next_frame(NULL, &size); - if (!size) { - printf("+EXIT: End of file (seek)\n"); - exit(0); - } - } - mp3d.sample_next = sample_pos; -} - -static void -pa_stream_drain_callback(pa_stream *stream, int success, void *data) -{ - printf("+EXIT: End of file\n"); - exit(0); -} - -static void -pa_stream_write_callback(pa_stream *stream, size_t requested, void *data) -{ - mp3d_sample_t samples[MINIMP3_MAX_SAMPLES_PER_FRAME]; - pa_operation *op; - ssize_t remaining; - size_t cnt, size; - - remaining = (ssize_t) requested; - while (remaining > 0) { - cnt = decode_next_frame(samples, &size); - if (!size) { - op = pa_stream_drain(pa_strm, - pa_stream_drain_callback, NULL); - pa_operation_unref(op); - return; - } - if (!cnt) continue; - - pa_stream_write(stream, samples, - cnt * sizeof(mp3d_sample_t), NULL, 0, - mp3d.seek ? PA_SEEK_RELATIVE_ON_READ: PA_SEEK_RELATIVE); - - mp3d.seek = false; - remaining -= (ssize_t) (cnt * sizeof(mp3d_sample_t)); - } -} - -static void -pa_stream_underflow_callback(struct pa_stream *stream, void *data) -{ - printf("+UNDERFLOW\n"); -} - -static void -pa_stream_overflow_callback(struct pa_stream *stream, void *data) -{ - printf("+OVERFLOW\n"); -} - -static void -update_sink_input_info_callback(struct pa_context *ctx, - const pa_sink_input_info *info, int eol, void *data) -{ - if (eol) return; - memcpy(&pa_strm_sink, info, sizeof(pa_sink_input_info)); - pa_strm_sink_update = true; - pa_threaded_mainloop_signal(pa_mloop, true); -} - -static void -update_sink_input_info(void) -{ - pa_operation *op; - - pa_strm_sink_update = false; - op = pa_context_get_sink_input_info(pa_ctx, - pa_stream_get_index(pa_strm), - update_sink_input_info_callback, NULL); - if (!op) errx(1, "pa_context_get_sink_input_info failed"); - - while (!pa_strm_sink_update) { - pa_threaded_mainloop_unlock(pa_mloop); - pa_threaded_mainloop_wait(pa_mloop); - pa_threaded_mainloop_lock(pa_mloop); - } - - pa_threaded_mainloop_accept(pa_mloop); - - pa_operation_unref(op); -} - -static size_t -stream_samples_buffered(void) -{ - static size_t last_read_index = 0, last_write_index = 0; - const pa_timing_info *info; - size_t buffered; - - info = pa_stream_get_timing_info(pa_strm); - if (!info || info->write_index_corrupt || info->read_index_corrupt) - return 0; - - if (info->read_index != last_read_index) - last_write_index = (size_t) MAX(0, info->write_index); - - buffered = (last_write_index - last_read_index) - / sizeof(mp3d_sample_t); - - last_read_index = (size_t) MAX(0, info->read_index); - - return buffered; -} - -static double -user_vol(void) -{ - pa_volume_t vol; - - update_sink_input_info(); - if (!pa_strm_sink.has_volume) - return -1; - - vol = pa_cvolume_avg(&pa_strm_sink.volume); - - return (double) vol * 100.F / (double) PA_VOLUME_NORM; -} - -static double -user_time(void) -{ - size_t sample_pos; - - sample_pos = MAX(0, mp3d.sample_next - stream_samples_buffered()); - - return (double) sample_pos * 1.F / (double) (mp3d.rate * mp3d.channels); -} - -static void -cmd_setvol(double vol) -{ - pa_operation *op; - - update_sink_input_info(); - pa_cvolume_set(&pa_strm_sink.volume, 2, - (pa_volume_t) (vol * PA_VOLUME_NORM / 100.F)); - if (pa_cvolume_avg(&pa_strm_sink.volume) > 2 * PA_VOLUME_NORM) - pa_cvolume_set(&pa_strm_sink.volume, 2, 2 * PA_VOLUME_NORM); - - op = pa_context_set_sink_input_volume(pa_ctx, - pa_stream_get_index(pa_strm), - &pa_strm_sink.volume, NULL, NULL); - pa_operation_unref(op); - - printf("+VOLUME: %02.2f\n", (double) pa_cvolume_avg(&pa_strm_sink.volume) - * 100.f / (double) PA_VOLUME_NORM); -} - -static void -cmd_seek(double time_sec) -{ - size_t sample_pos; - pa_operation *op; - - if (time_sec < 0) time_sec = 0; - sample_pos = (size_t) (time_sec * mp3d.channels * mp3d.rate); - decoder_seek(sample_pos); - - op = pa_stream_flush(pa_strm, NULL, NULL); - pa_operation_unref(op); - mp3d.seek = true; - - printf("+SEEK: %02.2f\n", user_time()); -} - -static void -cmd_pause_toggle(void) -{ - pa_operation *op; - - mp3d.pause ^= 1; - op = pa_stream_cork(pa_strm, mp3d.pause, NULL, NULL); - pa_operation_unref(op); - - printf("+PAUSE: %i\n", mp3d.pause); -} - -static void -cmd_status(void) -{ - update_sink_input_info(); - - printf("+STATUS: vol:%02.2f pos:%02.2f pause:%i\n", - user_vol(), user_time(), mp3d.pause); -} - -static int -getkey_esc(void) -{ - int c; - - c = getchar(); - switch (c) { - case 'A': - return KEY_UP; - case 'B': - return KEY_DOWN; - case 'C': - return KEY_RIGHT; - case 'D': - return KEY_LEFT; - case EOF: - return KEY_EOF; - default: - return KEY_NONE; - } -} - -static int -getkey(void) +void +die(const char *fmt, ...) { - int c; + va_list ap; - c = getchar(); - switch (c) { - case 0x1b: - c = getchar(); - switch (c) { - case '[': - return getkey_esc(); - default: - return KEY_NONE; - } - default: - return c > 0 ? (c & 0xff) : c; - } -} - -static bool -cmd_input(void) -{ - char linebuf[256]; - char *end, *tok; - double input; - - if (!fgets(linebuf, sizeof(linebuf), stdin)) - return false; - - tok = strchr(linebuf, '\n'); - if (tok) *tok = '\0'; - - pa_threaded_mainloop_lock(pa_mloop); - - if (!strncmp(linebuf, "seek ", 5)) { - input = strtof(linebuf + 5, &end); - if (!end || end && *end) { - printf("Invalid input\n"); - goto exit; - } - cmd_seek(input); - } else if (!strncmp(linebuf, "vol ", 4)) { - input = strtof(linebuf + 4, &end); - if (!end || end && *end) { - printf("Invalid input\n"); - goto exit; - } - cmd_setvol(input); - } else if (!strcmp(linebuf, "status")) { - cmd_status(); - } else if (!strcmp(linebuf, "pause")) { - cmd_pause_toggle(); - } else if (!strcmp(linebuf, "key")) { - cmd_input_mode = false; - if (term_set) tcsetattr(0, TCSANOW, &term_raw); - printf("+CMDINPUT: 0\n"); + fputs("mplay: ", stderr); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + if (*fmt && fmt[strlen(fmt)-1] == ':') { + perror(NULL); } else { - printf("+INVALID: Invalid command\n"); + fputc('\n', stderr); } - -exit: - pa_threaded_mainloop_unlock(pa_mloop); - return true; -} - -static bool -key_input(void) -{ - int key; - - while (!(key = getkey())); - if (key <= 0 || key == 'q' || key == KEY_EOF) - return false; - - pa_threaded_mainloop_lock(pa_mloop); - - switch (key) { - case KEY_UP: - case '+': - cmd_setvol(user_vol() + 5); - break; - case KEY_DOWN: - case '-': - cmd_setvol(user_vol() - 5); - break; - case KEY_LEFT: - cmd_seek(user_time() - 5); - break; - case KEY_RIGHT: - cmd_seek(user_time() + 5); - break; - case 'c': - cmd_pause_toggle(); - break; - case 's': - cmd_status(); - break; - case 'i': - cmd_input_mode = true; - printf("+CMDINPUT: 1\n"); - if (term_set) tcsetattr(0, TCSANOW, &term_orig); - break; - default: - printf("+INVALID: Invalid command '%x'\n", key); - break; - } - - pa_threaded_mainloop_unlock(pa_mloop); - - return true; + exit(1); } -static void * -input_worker(void *arg) +int +main(int argc, char **argv) { - setvbuf(stdin, NULL, _IONBF, 0); - setvbuf(stdout, NULL, _IONBF, 0); - - if (!tcgetattr(0, &term_orig)) - term_set = true; + char *path, *bin; + char *tok, *sep, *end; + char **arg, *fmt; + struct dirent *ent; + DIR *dir; - term_raw = term_orig; - term_raw.c_iflag |= BRKINT; - term_raw.c_iflag &= ~(0U | IGNBRK); - term_raw.c_lflag &= ~(0U | ICANON); - term_raw.c_lflag &= ~(0U | ECHO); + if (argc < 2) die("mplay [-f FMT] ... FILE[.FMT]"); - if (term_set && !cmd_input_mode) { - if (tcsetattr(0, TCSANOW, &term_raw)) - err(1, "tcsetattr"); - } - - printf("+READY\n"); + path = strdup(getenv("PATH")); + if (!path) die("PATH not set"); - while (true) { - if (cmd_input_mode) { - if (!cmd_input()) - break; + fmt = NULL; + for (arg = argv + 1; *arg; arg++) { + if (!strcmp(*arg, "-f")) { + fmt = *++arg; } else { - if (!key_input()) - break; + break; } } + arg[-1] = argv[0]; - return NULL; -} - -static void -pulse_context_init(void) -{ - pa_mainloop_api *pa_mloop_api; - int ret; - - pa_mloop_api = pa_threaded_mainloop_get_api(pa_mloop); - if (!pa_mloop_api) errx(1, "pa_threaded_mainloop_get_api"); - - pa_ctx = pa_context_new(pa_mloop_api, "mplay"); - if (!pa_ctx) errx(1, "pa_context_new"); - - ret = pa_context_connect(pa_ctx, NULL, 0, NULL); - if (ret) errx(1, "pa_context_connect: %s", - pa_strerror(pa_context_errno(pa_ctx))); - - while (pa_context_get_state(pa_ctx) != PA_CONTEXT_READY) { - pa_threaded_mainloop_unlock(pa_mloop); - pa_threaded_mainloop_wait(pa_mloop); - pa_threaded_mainloop_lock(pa_mloop); - } -} - -static void -pulse_stream_init(void) -{ - pa_channel_map pa_chmap; - int ret; - - pa_channel_map_init_stereo(&pa_chmap); - - pa_strm = pa_stream_new(pa_ctx, "mplay", &pa_spec, &pa_chmap); - if (!pa_strm) errx(1, "pa_stream_new: %s", - pa_strerror(pa_context_errno(pa_ctx))); - - pa_stream_set_write_callback(pa_strm, pa_stream_write_callback, NULL); - - if (debug) { - pa_stream_set_underflow_callback(pa_strm, - pa_stream_underflow_callback, NULL); - pa_stream_set_overflow_callback(pa_strm, - pa_stream_overflow_callback, NULL); - } - - ret = pa_stream_connect_playback(pa_strm, NULL, - &pa_buf, pa_stream_flags, NULL, NULL); - if (ret) errx(1, "pa_stream_connect_playback failed"); - - while (pa_stream_get_state(pa_strm) != PA_STREAM_READY) { - pa_threaded_mainloop_unlock(pa_mloop); - pa_threaded_mainloop_wait(pa_mloop); - pa_threaded_mainloop_lock(pa_mloop); + if (fmt == NULL) { + fmt = strrchr(argv[argc - 1], '.'); + if (!fmt) die("no file extension"); + fmt += 1; } -} - -static void -sigint_handler(int sig) -{ - exit(0); -} - -static void -cleanup(void) -{ - if (term_set) tcsetattr(0, TCSANOW, &term_orig); - - pa_stream_disconnect(pa_strm); - pa_stream_unref(pa_strm); - pa_context_disconnect(pa_ctx); - pa_context_unref(pa_ctx); - - munmap(audiofile.data, audiofile.len); -} - -static void -usage(void) -{ - fprintf(stderr, "Usage: mplay [-h] [-i] FILE\n"); - exit(1); -} - -int -main(int argc, const char **argv) -{ - pthread_mutex_t lock; - const char **arg; - const char *file; - - file = NULL; - for (arg = argv + 1; *arg; arg++) { - if (!strcmp(*arg, "-i")) { - cmd_input_mode = true; - } else if (!strcmp(*arg, "-h")) { - use_stdio = false; - } else if (!strcmp(*arg, "-d")) { - debug = true; - } else { - if (file) usage(); - file = *arg; + tok = path; + do { + sep = strchr(tok, ':'); + end = sep ? sep : tok + strlen(tok); + *end = '\0'; + dir = opendir(tok); + if (!dir) continue; + while ((ent = readdir(dir))) { + if (!strncmp(ent->d_name, "mplay.", 6) + && !strcmp(ent->d_name + 6, fmt)) { + bin = malloc(strlen(tok) + strlen(ent->d_name) + 2); + if (!bin) die("malloc:"); + strcpy(bin, tok); + strcat(bin, "/"); + strcat(bin, ent->d_name); + closedir(dir); + execv(bin, arg); + die("execv %s:", bin); + } } - } - - if (!file) usage(); - - if (strlen(file) < 4 || strcmp(file + strlen(file) - 4, ".mp3")) - errx(1, "not a mp3 file"); - - audiofile.data = map_file(file, &audiofile.len); - decoder_init(); - - pa_spec.channels = (uint8_t) mp3d.channels; - pa_spec.rate = (uint32_t) mp3d.rate; - - pa_mloop = pa_threaded_mainloop_new(); - if (!pa_mloop) errx(1, "pa_threaded_mainloop_new"); - - pa_threaded_mainloop_start(pa_mloop); - - pa_threaded_mainloop_lock(pa_mloop); - pulse_context_init(); - pulse_stream_init(); - pa_stream_cork(pa_strm, 0, NULL, NULL); - pa_threaded_mainloop_unlock(pa_mloop); - - atexit(cleanup); - signal(SIGINT, sigint_handler); - - if (use_stdio) { - if (pthread_create(&input_worker_thread, NULL, input_worker, NULL)) - err(1, "pthread_create"); - pthread_join(input_worker_thread, NULL); - } else { - pthread_mutex_init(&lock, NULL); - pthread_mutex_lock(&lock); - pthread_mutex_lock(&lock); - } + closedir(dir); + tok = sep + 1; + } while (sep); - return 0; + die("no handler"); } diff --git a/mplay.h b/mplay.h @@ -0,0 +1,110 @@ +#pragma once + +#include <stdio.h> + +#define MPLAY_ACTION_KEY_VERSION '?' +#define MPLAY_ACTION_KEY_STATUS 'S' +#define MPLAY_ACTION_KEY_VOL 'v' +#define MPLAY_ACTION_KEY_VOL_UP '+' +#define MPLAY_ACTION_KEY_VOL_DOWN '-' +#define MPLAY_ACTION_KEY_SEEK 's' +#define MPLAY_ACTION_KEY_SEEK_FWD '>' +#define MPLAY_ACTION_KEY_SEEK_BWD '<' +#define MPLAY_ACTION_KEY_PLAYPAUSE 'p' + +enum mplay_key { + MPLAY_KEY_INV, + MPLAY_KEY_EOF, + MPLAY_KEY_UP, + MPLAY_KEY_DOWN, + MPLAY_KEY_LEFT, + MPLAY_KEY_RIGHT, +}; + +enum mplay_action { + MPLAY_ACTION_NONE, + MPLAY_ACTION_VERSION, + MPLAY_ACTION_STATUS, + MPLAY_ACTION_SEEK, + MPLAY_ACTION_SEEK_FWD, + MPLAY_ACTION_SEEK_BWD, + MPLAY_ACTION_VOL, + MPLAY_ACTION_VOL_UP, + MPLAY_ACTION_VOL_DOWN, + MPLAY_ACTION_PLAYPAUSE, + MPLAY_ACTION_EXIT, +}; + +static int +mplay__getkey_esc(void) +{ + int c; + + c = getchar(); + switch (c) { + case 'A': + return MPLAY_KEY_UP; + case 'B': + return MPLAY_KEY_DOWN; + case 'C': + return MPLAY_KEY_RIGHT; + case 'D': + return MPLAY_KEY_LEFT; + case EOF: + return MPLAY_KEY_EOF; + default: + return MPLAY_KEY_INV; + } +} + +static int +mplay_getkey(void) +{ + int c; + + c = getchar(); + switch (c) { + case 0x1b: + c = getchar(); + switch (c) { + case '[': + return mplay__getkey_esc(); + default: + return MPLAY_KEY_INV; + } + default: + return c > 0 ? (c & 0xff) : c; + } +} + +static int +mplay_action(int key) +{ + switch (key) { + case MPLAY_ACTION_KEY_VERSION: + return MPLAY_ACTION_VERSION; + case MPLAY_ACTION_KEY_VOL: + return MPLAY_ACTION_VOL; + case MPLAY_KEY_UP: + case MPLAY_ACTION_KEY_VOL_UP: + return MPLAY_ACTION_VOL_UP; + case MPLAY_KEY_DOWN: + case MPLAY_ACTION_KEY_VOL_DOWN: + return MPLAY_ACTION_VOL_DOWN; + case MPLAY_ACTION_KEY_SEEK: + return MPLAY_ACTION_SEEK; + case MPLAY_KEY_LEFT: + case MPLAY_ACTION_KEY_SEEK_BWD: + return MPLAY_ACTION_SEEK_BWD; + case MPLAY_KEY_RIGHT: + case MPLAY_ACTION_KEY_SEEK_FWD: + return MPLAY_ACTION_SEEK_FWD; + case MPLAY_ACTION_KEY_PLAYPAUSE: + return MPLAY_ACTION_PLAYPAUSE; + case MPLAY_ACTION_KEY_STATUS: + return MPLAY_ACTION_STATUS; + default: + return MPLAY_ACTION_NONE; + } +} +