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 | --- |
D | LICENSE | | | 21 | --------------------- |
M | Makefile | | | 3 | +-- |
D | lib/minimp3 | | | 1 | - |
M | mplay.c | | | 738 | +++++++------------------------------------------------------------------------ |
A | mplay.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;
+ }
+}
+