commit 1bd07952245e3fc8ed95af0c1eff45938098b40b
parent 9fe644f0d99375ffd3011d8828f7dbd0fb103af0
Author: Louis Burda <quent.burda@gmail.com>
Date: Sun, 12 Dec 2021 23:06:54 +0100
Added playback via libmpdclient
Diffstat:
M | .gitignore | | | 2 | +- |
M | Makefile | | | 16 | +++++++++++----- |
A | README | | | 9 | +++++++++ |
M | history.c | | | 7 | +++++-- |
M | main.c | | | 120 | +++++++++++++++++++++++++++++++++++++++++++++++-------------------------------- |
M | player.c | | | 219 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------- |
M | player.h | | | 64 | ++++++++++++++++++++++++++++++++++++---------------------------- |
A | tmus | | | 0 | |
M | util.c | | | 77 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------- |
M | util.h | | | 5 | ++++- |
10 files changed, 361 insertions(+), 158 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,4 +1,4 @@
vgcore*
-main
+tmus
env.sh
*.o
diff --git a/Makefile b/Makefile
@@ -1,15 +1,21 @@
CFLAGS = -I . -g
-LDLIBS = -lcurses -lreadline -lportaudio -lsndfile
+LDLIBS = -lcurses -lreadline -lmpdclient
-.PHONY: all main
+.PHONY: all tmus clean install uninstall
-all: main
+all: tmus
clean:
- rm main
+ rm tmus
%.o: %.c %.h
$(CC) -c -o $@ $< $(CFLAGS) $(LDLIBS)
-main: main.c util.o history.o link.o player.o tag.o track.o
+tmus: main.c util.o history.o link.o player.o tag.o track.o
$(CC) -o $@ $^ $(CFLAGS) $(LDLIBS)
+
+install:
+ install -m 755 tmus /usr/bin
+
+uninstall:
+ rm /usr/bin/tmus
diff --git a/README b/README
@@ -0,0 +1,9 @@
+tmus
+====
+
+mpd backend
+-----------
+
+To load songs by path, mpd requires the client to connect via
+a unix socket. Set the environment variable MPD_HOST accordingly.
+
diff --git a/history.c b/history.c
@@ -39,12 +39,15 @@ history_free(struct history *history)
struct link *iter, *next;
struct inputln *ln;
- for (iter = &history->list; iter; iter = next) {
+ for (iter = history->list.next; iter; iter = next) {
next = iter->next;
ln = UPCAST(iter, struct inputln);
free(ln);
}
- free(history->cmd);
+ history->list = LIST_HEAD;
+ free(history->query);
+ history->query = NULL;
+ history->cmd = NULL;
}
struct inputln *
diff --git a/main.c b/main.c
@@ -8,8 +8,7 @@
#include "track.h"
#include "player.h"
-#include "sndfile.h"
-#include "portaudio.h"
+#include "mpd/player.h"
#include "curses.h"
#include <dirent.h>
@@ -71,7 +70,6 @@ struct cmd {
cmd_handler func;
};
-
int style_attrs[STYLE_COUNT] = { 0 };
const char *datadir;
@@ -89,10 +87,8 @@ struct pane *const panes[] = {
struct link tags;
-struct track *track_sel;
struct link tracks;
struct link playlist;
-int track_paused;
completion_generator completion;
struct history search_history, command_history;
@@ -104,7 +100,6 @@ struct link tags_sel;
int track_index;
-
void init(void);
void cleanup(void);
@@ -142,12 +137,10 @@ void main_vis(void);
int usercmd_save(const char *args);
-
const struct cmd cmds[] = {
{ "save", usercmd_save },
};
-
void
init(void)
{
@@ -155,7 +148,7 @@ init(void)
win_ratio = 0.3f;
initscr();
- cbreak();
+ raw();
noecho();
halfdelay(1);
intrflush(stdscr, FALSE);
@@ -188,26 +181,21 @@ init(void)
tag_index = 0;
tags_sel = LIST_HEAD;
- // player = player_thread();
+ player_init();
data_load();
+ signal(SIGINT, exit);
+
atexit(cleanup);
}
void
cleanup(void)
{
- int status;
-
- // TODO stop player
-
data_save();
- kill(player->pid, SIGTERM);
- waitpid(player->pid, &status, 0);
-
- // TODO free player
+ player_free();
history_free(&search_history);
history_free(&command_history);
@@ -292,7 +280,7 @@ data_save(void)
void
tracks_save(struct tag *tag)
{
-
+
}
void
@@ -488,23 +476,14 @@ track_input(wint_t c)
track_index = MAX(0, track_index - 1);
return 1;
case KEY_DOWN:
- track_index = MIN(list_len(&tracks) - 1,
- track_index + 1);
+ track_index = MIN(list_len(&tracks) - 1, track_index + 1);
return 1;
case KEY_ENTER:
link = link_iter(tracks.next, track_index);
ASSERT(link != NULL);
track = UPCAST(link, struct track);
- if (track != track_sel) {
- track_sel = track;
- track_paused = 0;
- } else {
- track_paused ^= 1;
- }
- // if (!track_paused)
- // track_play();
- // else
- // track_pause();
+ player->track = track;
+ player_play_track(track);
return 1;
}
@@ -525,21 +504,21 @@ track_vis(struct pane *pane, int sel)
for (iter = tracks.next; iter; iter = iter->next) {
track = UPCAST(iter, struct track);
- if (sel && index == track_index && track == track_sel)
+ if (sel && index == track_index && track == player->track)
style_on(pane->win, STYLE_ITEM_HOVER_SEL);
else if (sel && index == track_index)
style_on(pane->win, STYLE_ITEM_HOVER);
- else if (track == track_sel)
+ else if (track == player->track)
style_on(pane->win, STYLE_ITEM_SEL);
wmove(pane->win, 1 + index, 0);
wprintw(pane->win, "%-*.*s", pane->w, pane->w, track->name);
- if (sel && index == track_index && track == track_sel)
+ if (sel && index == track_index && track == player->track)
style_off(pane->win, STYLE_ITEM_HOVER_SEL);
else if (sel && index == track_index)
style_off(pane->win, STYLE_ITEM_HOVER);
- else if (track == track_sel)
+ else if (track == player->track)
style_off(pane->win, STYLE_ITEM_SEL);
index++;
@@ -559,9 +538,11 @@ cmd_input(wint_t c)
switch (c) {
case KEY_ESC:
- if (history->cmd == history->query)
- return 0;
- history->cmd = history->query;
+ if (history->cmd == history->query) {
+ pane_sel = NULL; /* TODO: save last and switch back */
+ } else {
+ history->cmd = history->query;
+ }
break;
case KEY_LEFT:
inputln_left(history->cmd);
@@ -601,25 +582,43 @@ void
cmd_vis(struct pane *pane, int sel)
{
struct inputln *cmd;
+ char state_char;
+ char *line;
werase(pane->win);
- if (track_sel)
- pane_title(pane, track_sel->name, sel);
- else
- pane_title(pane, "", sel);
+ wmove(pane->win, 0, 0);
+ style_on(pane->win, STYLE_TITLE);
+ wprintw(pane->win, " %-*.*s\n", pane->w - 1, pane->w - 1,
+ player->track ? player->track->name : "");
+ style_off(pane->win, STYLE_TITLE);
+
+ if (player->time_pos) {
+ state_char = player->state == PLAYER_STATE_PLAYING ? '>' : '|';
+ line = appendstrf(NULL, "%c ", state_char);
+ line = appendstrf(line, "%s / ", timestr(player->time_pos));
+ line = appendstrf(line, "%s", timestr(player->time_end));
+ if (player->volume >= 0) {
+ line = appendstrf(line, " - vol: %u%", player->volume);
+ }
+ if (player->msg) {
+ line = appendstrf(line, " | [PLAYER] %s", player->msg);
+ }
- static int i = 0;
- wmove(pane->win, 1, 0);
- wprintw(pane->win, "%i", i++);
+ wmove(pane->win, 1, 0);
+ ATTR_ON(pane->win, A_REVERSE);
+ wprintw(pane->win, "%-*.*s\n", pane->w, pane->w, line);
+ ATTR_OFF(pane->win, A_REVERSE);
+
+ free(line);
+ }
if (sel || cmd_show) {
wmove(pane->win, 2, 0);
waddch(pane->win, cmd_mode == IMODE_SEARCH ? '/' : ':');
cmd = history->cmd;
wprintw(pane->win, "%-*.*ls", pane->w - 1, pane->w - 1, cmd->buf);
- // TODO: if query != cmd, highlight query substr
- if (sel) {
+ if (sel) { /* cursor */
ATTR_ON(pane->win, A_REVERSE);
wmove(pane->win, 2, 1 + cmd->cur);
waddch(pane->win, cmd->cur < cmd->len ? cmd->buf[cmd->cur] : ' ');
@@ -647,14 +646,37 @@ main_input(wint_t c)
case KEY_RIGHT:
pane_sel = &pane_right;
break;
+ case 't':
+ player_toggle_pause();
+ break;
+ case 'n':
+ case '>':
+ player_next();
+ break;
+ case 'p':
+ case '<':
+ player_prev();
+ break;
+ case 'b':
+ player_seek(0);
+ break;
case ':':
cmd_mode = IMODE_EXECUTE;
pane_sel = &pane_bot;
break;
+ case '+':
+ player_set_volume(MIN(100, player->volume + 5));
+ break;
+ case '-':
+ player_set_volume(MAX(0, player->volume - 5));
+ break;
case '/':
cmd_mode = IMODE_SEARCH;
pane_sel = &pane_bot;
break;
+ case 'q':
+ quit = 1;
+ break;
}
}
@@ -693,6 +715,8 @@ main(int argc, const char **argv)
c = KEY_RESIZE;
do {
+ player_update();
+
if (c == KEY_RESIZE) {
resize();
} else if (c != ERR) {
@@ -714,7 +738,5 @@ main(int argc, const char **argv)
get_wch(&c);
} while (!quit);
-
- cleanup();
}
diff --git a/player.c b/player.c
@@ -2,112 +2,221 @@
#include "portaudio.h"
#include "sndfile.h"
+#include "util.h"
+#include <mpd/song.h>
+#include <mpd/status.h>
+#include <stdbool.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
+#define PLAYER_STATUS(lvl, ...) do { \
+ player->msglvl = lvl; \
+ if (player->msg) free(player->msg); \
+ player->msg = aprintf(__VA_ARGS__); \
+ } while (0)
+
static struct player player_static;
struct player *player;
-struct player *
-player_thread()
+void
+player_init(void)
{
- struct player *player;
- pid_t pid;
-
- player = mmap(&player_static, sizeof(struct player),
- PROT_READ | PROT_WRITE, MAP_SHARED, -1, 0);
- player->action = PLAYER_NONE;
- player->resp = PLAYER_NOTSET;
- player->alive = 1;
-
- pid = fork();
- if (!pid) {
- player_main();
- player->alive = 0;
- exit(0);
- }
+ player = malloc(sizeof(struct player));
+ ASSERT(player != NULL);
+
+ player->conn = mpd_connection_new(NULL, 0, 0);
+ ASSERT(player->conn != NULL);
+
+ player->queue = LIST_HEAD;
+ player->track = NULL;
+ player->state = PLAYER_STATE_PAUSED;
+
+ player->volume = 0;
+ player->time_pos = 0;
+ player->time_end = 0;
+
+ player->msg = NULL;
+ player->msglvl = PLAYER_MSG_INFO;
- player->pid = pid;
+ mpd_run_stop(player->conn);
+ mpd_run_clear(player->conn);
+}
- return player;
+void
+player_free(void)
+{
+ if (!player->conn) return;
+ mpd_run_stop(player->conn);
+ mpd_run_clear(player->conn);
+ mpd_connection_free(player->conn);
}
void
-player_main(void)
+player_update(void)
{
- PaStream *stream;
- int status;
-
- if (Pa_Initialize() != paNoError)
- return;
-
- while (player->action != PLAYER_EXIT) {
- player->resp = PLAYER_NOTSET;
- switch (player->action) {
- case PLAYER_PLAY:
- player->resp = player_play();
- break;
- case PLAYER_PAUSE:
- player->resp = player_pause();
- break;
- case PLAYER_SKIP:
- player->resp = player_skip();
- break;
- case PLAYER_PREV:
- player->resp = player_prev();
- break;
- }
- Pa_Sleep(100);
+ struct mpd_status *status;
+ struct mpd_song *song;
+ const char *tmp;
+
+ status = mpd_run_status(player->conn);
+ ASSERT(status != NULL);
+
+ player->state = mpd_status_get_state(status) == MPD_STATE_PLAY
+ ? PLAYER_STATE_PLAYING : PLAYER_STATE_PAUSED;
+ player->volume = mpd_status_get_volume(status);
+
+ song = mpd_run_current_song(player->conn);
+ if (song) {
+ player->time_pos = mpd_status_get_elapsed_time(status);
+ player->time_end = mpd_song_get_duration(song);
+ mpd_song_free(song);
+ } else {
+ player->time_pos = 0;
+ player->time_end = 0;
}
- Pa_Terminate();
+ mpd_status_free(status);
}
-int
-player_alive(void)
+void
+player_queue_clear(void)
{
- return player->alive && !kill(player->pid, 0);
+ struct link *iter, *next;;
+
+ for (iter = &player->queue; iter; ) {
+ next = iter->next;
+ free(UPCAST(iter, struct track_ref));
+ iter = next;
+ }
+
+ player->queue = LIST_HEAD;
}
void
-player_loadfile(const char *file)
+player_queue_append(struct track *track)
{
- ASSERT(player_alive());
- player_action(PLAYER_STOP);
+ player_queue_insert(track, list_len(&player->queue));
}
void
-player_action(int action)
+player_queue_insert(struct track *track, size_t pos)
+{
+ struct track_ref *new;
+ struct link *iter;
+ int i;
+
+ new = malloc(sizeof(struct track_ref));
+ new->track = track;
+ new->link = LINK_EMPTY;
+
+ iter = &player->queue;
+ for (i = 0; i < pos && iter->next; i++)
+ iter = iter->next;
+
+ link_append(iter, &new->link);
+}
+
+int
+player_play_track(struct track *track)
{
- ASSERT(player_alive());
- player->action = action;
+ player_clear_msg();
+ player->track = track;
+ mpd_run_stop(player->conn);
+
+ if (!mpd_run_add(player->conn, player->track->fpath)
+ || !mpd_run_play(player->conn)) {
+ PLAYER_STATUS(PLAYER_MSG_ERR, "Playback failed");
+ return PLAYER_ERR;
+ }
+
+ return PLAYER_OK;
}
int
-player_play(void)
+player_toggle_pause(void)
{
+ if (!mpd_run_toggle_pause(player->conn)) {
+ PLAYER_STATUS(PLAYER_MSG_ERR, "Pause toggle failed");
+ return PLAYER_ERR;
+ }
+
return PLAYER_OK;
}
int
player_pause(void)
{
+ if (!mpd_run_pause(player->conn, true)) {
+ PLAYER_STATUS(PLAYER_MSG_ERR, "Pausing track failed");
+ return PLAYER_ERR;
+ }
+
+ return PLAYER_OK;
+}
+
+int
+player_resume(void)
+{
+ if (!mpd_run_pause(player->conn, false)) {
+ PLAYER_STATUS(PLAYER_MSG_ERR, "Resuming track failed");
+ return PLAYER_ERR;
+ }
+
return PLAYER_OK;
}
int
-player_skip(void)
+player_next(void)
{
+ if (!mpd_run_next(player->conn)) {
+ PLAYER_STATUS(PLAYER_MSG_ERR, "Playing next track failed");
+ return PLAYER_ERR;
+ }
+
return PLAYER_OK;
}
int
player_prev(void)
{
+ if (!mpd_run_previous(player->conn)) {
+ PLAYER_STATUS(PLAYER_MSG_ERR, "Playing prev track failed");
+ return PLAYER_ERR;
+ }
+
return PLAYER_OK;
}
+int
+player_seek(int sec)
+{
+ /* TODO */
+ return PLAYER_OK;
+}
+int
+player_set_volume(unsigned int vol)
+{
+ if (player->volume == -1) {
+ PLAYER_STATUS(PLAYER_MSG_INFO, "Setting volume not supported");
+ return PLAYER_ERR;
+ }
+
+ if (!mpd_run_set_volume(player->conn, vol)) {
+ PLAYER_STATUS(PLAYER_MSG_ERR, "Setting volume failed");
+ return PLAYER_ERR;
+ }
+ return PLAYER_OK;
+}
+
+void
+player_clear_msg(void)
+{
+ free(player->msg);
+ player->msg = NULL;
+ player->msglvl = PLAYER_MSG_NONE;
+}
diff --git a/player.h b/player.h
@@ -1,54 +1,62 @@
#pragma once
+#include "track.h"
#include "util.h"
-#include "sndfile.h"
+#include "mpd/client.h"
#include <signal.h>
enum {
- PLAYER_NONE,
- PLAYER_PAUSE,
- PLAYER_PLAY,
- PLAYER_SKIP,
- PLAYER_PREV,
- PLAYER_STOP,
- PLAYER_LOAD,
- PLAYER_EXIT
+ PLAYER_OK,
+ PLAYER_ERR
};
enum {
- PLAYER_NOTSET,
- PLAYER_OK,
- PLAYER_FAIL
+ PLAYER_MSG_NONE,
+ PLAYER_MSG_INFO,
+ PLAYER_MSG_ERR
+};
+
+enum {
+ PLAYER_STATE_PAUSED,
+ PLAYER_STATE_PLAYING
};
struct player {
- int action, resp;
+ struct mpd_connection *conn;
- int reload;
- char *filepath;
- SNDFILE *file;
- SF_INFO info;
+ struct link queue;
+ struct track *track;
+ int state;
- int sample_index;
+ int volume;
+ unsigned int time_pos, time_end;
- int alive;
- pid_t pid;
+ char *msg;
+ int msglvl;
};
-struct player *player_thread(void);
+void player_init(void);
+void player_free(void);
+void player_update(void);
-void player_main(void);
+void player_queue_clear(void);
+void player_queue_append(struct track *track);
+void player_queue_insert(struct track *track, size_t pos);
-int player_alive(void);
-
-void player_loadfile(const char *path);
-void player_action(int action);
+int player_play_track(struct track *track);
+int player_toggle_pause(void);
int player_pause(void);
-int player_play(void);
+int player_resume(void);
int player_prev(void);
-int player_skip(void);
+int player_next(void);
+int player_seek(int sec);
+
+int player_set_volume(unsigned int vol);
+
+void player_clear_msg(void);
extern struct player *player;
+
diff --git a/tmus b/tmus
Binary files differ.
diff --git a/util.c b/util.c
@@ -47,6 +47,52 @@ assert(int cond, const char *file, int line, const char *condstr)
}
char *
+aprintf(const char *fmtstr, ...)
+{
+ va_list ap, cpy;
+ size_t size;
+ char *str;
+
+ va_copy(cpy, ap);
+
+ va_start(ap, fmtstr);
+ size = vsnprintf(NULL, 0, fmtstr, ap);
+ va_end(ap);
+
+ str = malloc(size + 1);
+ ASSERT(str != NULL);
+
+ va_start(cpy, fmtstr);
+ vsnprintf(str, size + 1, fmtstr, cpy);
+ va_end(cpy);
+
+ return str;
+}
+
+char *
+appendstrf(char *alloc, const char *fmtstr, ...)
+{
+ va_list ap, cpy;
+ size_t size, prevlen;
+
+ va_copy(cpy, ap);
+
+ va_start(ap, fmtstr);
+ size = vsnprintf(NULL, 0, fmtstr, ap);
+ va_end(ap);
+
+ prevlen = alloc ? strlen(alloc) : 0;
+ alloc = realloc(alloc, prevlen + size + 1);
+ ASSERT(alloc != NULL);
+
+ va_start(cpy, fmtstr);
+ vsnprintf(alloc + prevlen, size + 1, fmtstr, cpy);
+ va_end(cpy);
+
+ return alloc;
+}
+
+char *
sanitized(const char *instr)
{
const char *p;
@@ -65,25 +111,22 @@ sanitized(const char *instr)
return clean;
}
-char *
-aprintf(const char *fmtstr, ...)
+const char *
+timestr(unsigned int secs)
{
- va_list ap, cpy;
- size_t size;
- char *str;
+ static char buf[16];
+ unsigned int mins, hours;
- va_copy(cpy, ap);
-
- va_start(ap, fmtstr);
- size = vsnprintf(NULL, 0, fmtstr, ap);
- va_end(ap);
-
- str = malloc(size + 1);
- ASSERT(str != NULL);
+ hours = secs / 3600;
+ mins = secs / 60 % 60;
+ secs = secs % 60;
- va_start(cpy, fmtstr);
- vsnprintf(str, size + 1, fmtstr, cpy);
- va_end(cpy);
+ if (hours) {
+ snprintf(buf, sizeof(buf), "%02u:%02u:%02u", hours, mins, secs);
+ } else {
+ snprintf(buf, sizeof(buf), "%02u:%02u", mins, secs);
+ }
- return str;
+ return buf;
}
+
diff --git a/util.h b/util.h
@@ -11,6 +11,9 @@
int strnwidth(const char *s, int n);
void assert(int cond, const char *file, int line, const char *condstr);
+char *aprintf(const char *fmtstr, ...);
+char *appendstrf(char *alloc, const char *fmtstr, ...);
+
char *sanitized(const char *instr);
-char *aprintf(const char *fmtstr, ...);
+const char *timestr(unsigned int seconds);