commit 3dad446ab7a6e207229b56af552dd3304a9ab11b
parent 321b23c68aa8e0c2445fdddee632693a06fd86e1
Author: Louis Burda <quent.burda@gmail.com>
Date: Fri, 25 Feb 2022 01:31:57 +0100
Refactor player interface, improve history navigation and playlist shuffle
Diffstat:
11 files changed, 597 insertions(+), 507 deletions(-)
diff --git a/.gitmodules b/.gitmodules
@@ -1,3 +1,3 @@
[submodule "lib/clist"]
- path = lib/clist
+ path = lib/liblist
url = git@sinitax.com:sinitax/clist
diff --git a/Makefile b/Makefile
@@ -1,5 +1,5 @@
CFLAGS = -I src -g $(shell pkg-config --cflags glib-2.0 dbus-1)
-CFLAGS += -I lib/clist/include
+CFLAGS += -I lib/liblist/include
LDLIBS = -lcurses -lmpdclient $(shell pkg-config --libs glib-2.0 dbus-1)
DEPFLAGS = -MT $@ -MMD -MP -MF build/$*.d
@@ -7,23 +7,28 @@ ifeq "$(PROF)" "YES"
CFLAGS += -pg
endif
+BACKEND ?= mpd
+
SRCS = $(wildcard src/*.c)
-OBJS = $(SRCS:src/%.c=build/%.o)
+OBJS = $(SRCS:src/%.c=build/%.o) build/player_$(BACKEND).o
DEPS = $(OBJS:%.o=%.d)
-LIBLIST_A = lib/clist/build/liblist.a
+LIBLIST_A = lib/liblist/build/liblist.a
-.PHONY: all tmus clean install uninstall
+.PHONY: all tmus clean cleanlibs install uninstall
all: tmus
clean:
rm -rf build
+cleanlibs:
+ rm -rf lib/liblist/build
+
build:
mkdir build
-build/%.o: src/%.c build/%.d
+build/%.o: src/%.c build/%.d | build
$(CC) -c -o $@ $(DEPFLAGS) $(CFLAGS) $<
build/%.d: | build;
@@ -31,7 +36,7 @@ build/%.d: | build;
include $(DEPS)
$(LIBLIST_A):
- make -C lib/clist build/liblist.a
+ make -C lib/liblist build/liblist.a
tmus: $(OBJS) $(LIBLIST_A)
$(CC) -o tmus $^ $(CFLAGS) $(LDLIBS)
@@ -40,5 +45,5 @@ install:
install -m 755 tmus /usr/bin
uninstall:
- rm /usr/bin/tmus
+ rm -f /usr/bin/tmus
diff --git a/lib/clist b/lib/clist
@@ -1 +0,0 @@
-Subproject commit a1eba38afd00c5c8be0a6c66684c7ace10afb3e5
diff --git a/lib/liblist b/lib/liblist
@@ -0,0 +1 @@
+Subproject commit 2f54fc46a72f532feb3f98a4cc2cae88bbd0169f
diff --git a/src/cmd.c b/src/cmd.c
@@ -80,7 +80,7 @@ cmd_add(const wchar_t *name)
tag = tag_find(name);
if (!tag) return 0;
- link = list_at(&player->playlist, track_nav.sel);
+ link = list_at(&player.playlist, track_nav.sel);
if (!link) return 0;
track = UPCAST(link, struct ref)->data;
diff --git a/src/player.c b/src/player.c
@@ -1,414 +1,2 @@
#include "player.h"
-#include "ref.h"
-
-#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;
-
-static void player_clear_msg(void);
-static int handle_mpd_status(int status);
-static void player_play_next(struct track *track);
-
-static void
-player_clear_msg(void)
-{
- free(player->msg);
- player->msg = NULL;
- player->msglvl = PLAYER_MSG_NONE;
-}
-
-static int
-handle_mpd_status(int status)
-{
- player_clear_msg();
- switch (status) {
- case MPD_ERROR_SERVER:
- case MPD_ERROR_ARGUMENT:
- if (!mpd_connection_clear_error(player->conn))
- PANIC("PLAYER: Failed to recover from argument error");
- case MPD_ERROR_SYSTEM:
- PLAYER_STATUS(PLAYER_MSG_ERR, "%s",
- mpd_connection_get_error_message(player->conn));
- return 1;
- case MPD_ERROR_CLOSED:
- PANIC("PLAYER: Connection abruptly closed");
- }
- return 0;
-}
-
-void
-player_play_next(struct track *prev)
-{
- struct link *iter;
- struct track *track;
- int index;
-
- if (list_empty(&player->playlist))
- return;
-
- iter = NULL;
- if (player->shuffle) {
- /* TODO better algorithm for random sequence */
- index = rand() % list_len(&player->playlist);
- iter = list_at(&player->playlist, index);
- ASSERT(iter != NULL);
- } else if (player->loaded) {
- for (LIST_ITER(&player->playlist, iter)) {
- track = UPCAST(iter, struct ref)->data;
- if (track == prev)
- break;
- }
- if (iter) iter = iter->next;
- }
-
- if (!iter) iter = list_at(&player->playlist, 0);
- track = UPCAST(iter, struct ref)->data;
-
- player_play_track(track);
-}
-
-void
-player_init(void)
-{
- player = malloc(sizeof(struct player));
- OOM_CHECK(player);
-
- player->conn = mpd_connection_new(NULL, 0, 0);
- OOM_CHECK(player->conn);
-
- list_init(&player->queue);
- list_init(&player->history);
-
- list_init(&player->playlist);
-
- player->track = NULL;
- player->loaded = 0;
- player->state = PLAYER_STATE_PAUSED;
-
- player->autoplay = 0;
-
- player->shuffle = 0;
-
- player->action = PLAYER_ACTION_NONE;
-
- player->seek_delay = 0;
-
- player->volume = 0;
- player->time_pos = 0;
- player->time_end = 0;
-
- player->msg = NULL;
- player->msglvl = PLAYER_MSG_INFO;
-}
-
-void
-player_deinit(void)
-{
- struct link *iter;
-
- if (!player->conn) return;
-
- refs_free(&player->queue);
- refs_free(&player->history);
-
- refs_free(&player->playlist);
-
- player_clear_msg();
-
- //mpd_run_clear(player->conn);
- mpd_connection_free(player->conn);
-}
-
-void
-player_update(void)
-{
- struct mpd_status *status;
- struct mpd_song *song;
- struct ref *ref;
- const char *tmp;
-
- status = mpd_run_status(player->conn);
- if (status == NULL)
- PANIC("MPD Fatal Error: %s",
- mpd_connection_get_error_message(player->conn));
-
- song = mpd_run_current_song(player->conn);
- if (!song) {
- /* if autoplay and another track just finished,
- * or there are tracks in queue to be played */
- if (player->track && player->autoplay
- || !list_empty(&player->queue)) {
- player->action = PLAYER_ACTION_PLAY_NEXT;
- }
- } else {
- mpd_song_free(song);
- }
-
- mpd_status_free(status);
-
- if (player->action != PLAYER_ACTION_NONE) {
- handle_mpd_status(mpd_run_clear(player->conn));
-
- ref = NULL;
- switch (player->action) {
- case PLAYER_ACTION_PLAY_PREV:
- if (list_empty(&player->history))
- break;
- ref = UPCAST(list_pop_front(&player->history),
- struct ref);
-
- /* TODO keep index instead until new track is played */
- /* TODO create slimmer player_backend interface */
-
- /* dont add current song to history */
- player->track = NULL;
-
- player_play_track(ref->data);
- ref_free(ref);
- break;
- case PLAYER_ACTION_PLAY_NEXT:
- if (!list_empty(&player->queue)) {
- ref = UPCAST(list_pop_front(&player->queue),
- struct ref);
- player_play_track(ref->data);
- ref_free(ref);
- } else {
- player_play_next(player->track);
- }
- break;
- default:
- PANIC();
- }
- player->action = PLAYER_ACTION_NONE;
- }
-
- /* TODO move prev / next handling to own functions */
-
- status = mpd_run_status(player->conn);
- if (status == NULL)
- PANIC("MPD Fatal Error: %s",
- mpd_connection_get_error_message(player->conn));
-
- song = mpd_run_current_song(player->conn);
- if (song) {
- player->loaded = true;
- player->time_pos = mpd_status_get_elapsed_time(status);
- player->time_end = mpd_song_get_duration(song);
- mpd_song_free(song);
- } else {
- player->loaded = false;
- player->track = NULL;
- player->time_pos = 0;
- player->time_end = 0;
- }
-
- switch (mpd_status_get_state(status)) {
- case MPD_STATE_PAUSE:
- player->state = PLAYER_STATE_PAUSED;
- break;
- case MPD_STATE_PLAY:
- player->state = PLAYER_STATE_PLAYING;
- break;
- case MPD_STATE_STOP:
- player->state = PLAYER_STATE_STOPPED;
- break;
- default:
- PANIC();
- }
- player->volume = mpd_status_get_volume(status);
-
- if (player->seek_delay) {
- player->seek_delay--;
- if (!player->seek_delay)
- player_play();
- }
-
-
- mpd_status_free(status);
-}
-
-void
-player_queue_clear(void)
-{
- refs_free(&player->queue);
-}
-
-void
-player_queue_append(struct track *track)
-{
- struct ref *ref;
- struct link *link;
-
- ref = ref_init(track);
- list_push_back(&player->queue, LINK(ref));
-}
-
-void
-player_queue_insert(struct track *track, size_t pos)
-{
- struct ref *ref;
- struct link *link;
-
- ref = ref_init(track);
- link = list_at(&player->queue, pos);
- link_prepend(link, LINK(ref));
-}
-
-int
-player_play_track(struct track *track)
-{
- struct link *link;
- int status;
-
- if (player->track && player->track != track) {
- list_push_front(&player->history,
- LINK(ref_init(player->track)));
- }
-
- handle_mpd_status(mpd_run_clear(player->conn));
-
- status = mpd_run_add(player->conn, track->fpath);
- if (handle_mpd_status(status))
- return PLAYER_ERR;
-
- status = mpd_run_play(player->conn);
- if (handle_mpd_status(status))
- return PLAYER_ERR;
-
- player->track = track;
-
- return PLAYER_OK;
-}
-
-int
-player_toggle_pause(void)
-{
- int status;
-
- status = mpd_run_toggle_pause(player->conn);
- if (handle_mpd_status(status))
- return PLAYER_ERR;
-
- return PLAYER_OK;
-}
-
-int
-player_pause(void)
-{
- int status;
-
- status = mpd_run_pause(player->conn, true);
- if (handle_mpd_status(status))
- return PLAYER_ERR;
-
- return PLAYER_OK;
-}
-
-int
-player_resume(void)
-{
- int status;
-
- status = mpd_run_pause(player->conn, false);
- if (handle_mpd_status(status))
- return PLAYER_ERR;
-
- return PLAYER_OK;
-}
-
-int
-player_next(void)
-{
- player->action = PLAYER_ACTION_PLAY_NEXT;
-
- return PLAYER_OK;
-}
-
-int
-player_prev(void)
-{
- player->action = PLAYER_ACTION_PLAY_PREV;
-
- return PLAYER_OK;
-}
-
-int
-player_play(void)
-{
- int status;
-
- status = mpd_run_play(player->conn);
- if (handle_mpd_status(status))
- return PLAYER_ERR;
-
- return PLAYER_OK;
-}
-
-int
-player_stop(void)
-{
- int status;
-
- status = mpd_run_stop(player->conn);
- if (handle_mpd_status(status))
- return PLAYER_ERR;
-
- return PLAYER_OK;
-}
-
-int
-player_seek(int sec)
-{
- int status;
-
- player_clear_msg();
- if (!player->loaded || player->state == PLAYER_STATE_STOPPED) {
- PLAYER_STATUS(PLAYER_MSG_INFO, "No track loaded");
- return PLAYER_ERR;
- }
-
- status = mpd_run_seek_current(player->conn, sec, false);
- if (handle_mpd_status(status))
- return PLAYER_ERR;
-
- player->seek_delay = 7;
- player_pause();
-
- return PLAYER_OK;
-}
-
-int
-player_set_volume(unsigned int vol)
-{
- int status;
-
- player_clear_msg();
- if (player->volume == -1) {
- PLAYER_STATUS(PLAYER_MSG_INFO, "Volume control not supported");
- return PLAYER_ERR;
- }
-
- status = mpd_run_set_volume(player->conn, vol);
- if (handle_mpd_status(status))
- return PLAYER_ERR;
-
- return PLAYER_OK;
-}
diff --git a/src/player.h b/src/player.h
@@ -4,19 +4,15 @@
#include "list.h"
#include "util.h"
-#include "mpd/client.h"
-
-#include <signal.h>
-
enum {
- PLAYER_OK,
- PLAYER_ERR
+ PLAYER_STATUS_OK,
+ PLAYER_STATUS_ERR
};
enum {
- PLAYER_MSG_NONE,
- PLAYER_MSG_INFO,
- PLAYER_MSG_ERR
+ PLAYER_STATUS_MSG_NONE,
+ PLAYER_STATUS_MSG_INFO,
+ PLAYER_STATUS_MSG_ERR
};
enum {
@@ -33,45 +29,41 @@ enum {
};
struct player {
- /* TODO move implementation details to source file */
- struct mpd_connection *conn;
-
- /* TODO combine with index */
- /* for navigating forward and backwards in time */
- struct list queue;
- struct list history;
+ /* played track history */
+ struct list history; /* struct ref -> struct track */
+ struct link *history_sel; /* position in history */
- /* list of track refs to choose from on prev / next */
- struct list playlist;
+ /* queued tracks */
+ struct list queue; /* struct ref -> struct track */
- /* last player track */
+ /* selected track, not (yet) part of history or queue */
struct track *track;
- /* player has a track loaded,
- * not necessarily player->track */
- int loaded;
+ /* list of tracks to choose from on prev / next */
+ struct list playlist; /* struct ref -> struct track */
+ struct link *playlist_sel; /* position in playlist */
- /* stopped, paused or playing */
- int state;
+ /* a track is loaded, not necessarily player.track */
+ bool loaded;
/* automatically select new tracks when queue empty */
- int autoplay;
+ bool autoplay;
- int shuffle;
+ /* randomize which track is chosen when queue empty */
+ bool shuffle;
- int action;
-
- /* number of frames to wait before unpausing after
- * seek to prevent pause-play cycle noises */
- int seek_delay;
+ /* stopped, paused or playing */
+ int state;
+ /* volume adjustment when possible */
int volume;
+ /* track position and duration */
unsigned int time_pos, time_end;
/* status messaging */
- char *msg;
- int msglvl;
+ char *status;
+ int status_lvl;
};
void player_init(void);
@@ -79,10 +71,6 @@ void player_deinit(void);
void player_update(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_play_track(struct track *track);
int player_toggle_pause(void);
@@ -96,5 +84,5 @@ int player_stop(void);
int player_set_volume(unsigned int vol);
-extern struct player *player;
+extern struct player player;
diff --git a/src/player_backend.c b/src/player_backend.c
@@ -1,4 +0,0 @@
-#include "player_backend.h"
-
-void foo(void);
-
diff --git a/src/player_backend.h b/src/player_backend.h
@@ -1,6 +0,0 @@
-#pragma once
-
-struct player_data {
- int time_pos, time_end;
-};
-
diff --git a/src/player_mpd.c b/src/player_mpd.c
@@ -0,0 +1,513 @@
+#include "player.h"
+#include "ref.h"
+
+#include "portaudio.h"
+#include "sndfile.h"
+#include "util.h"
+
+#include <mpd/client.h>
+#include <mpd/song.h>
+#include <mpd/status.h>
+
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#define PLAYER_STATUS(lvl, ...) do { \
+ player.status_lvl = PLAYER_STATUS_MSG_ ## lvl; \
+ if (player.status) free(player.status); \
+ player.status = aprintf(__VA_ARGS__); \
+ } while (0)
+
+struct mpd_player {
+ struct mpd_connection *conn;
+
+ /* number of frames to wait before unpausing after
+ * seek to prevent pause-play cycle noises */
+ int seek_delay;
+
+ /* action to perform on next update */
+ int action;
+};
+
+struct player player;
+struct mpd_player mpd;
+
+static void player_clear_status(void);
+
+static int mpd_handle_status(int status);
+
+static bool history_contains(struct track *track, int depth);
+
+static struct track *playlist_track_lru(int skip);
+
+static void player_play_prev(void);
+static void player_play_next(void);
+
+static void player_add_history(struct track *track);
+
+void
+player_clear_status(void)
+{
+ free(player.status);
+ player.status = NULL;
+ player.status_lvl = PLAYER_STATUS_MSG_NONE;
+}
+
+int
+mpd_handle_status(int status)
+{
+ const char *errstr;
+
+ player_clear_status();
+
+ switch (status) {
+ case MPD_ERROR_SERVER:
+ case MPD_ERROR_ARGUMENT:
+ if (!mpd_connection_clear_error(mpd.conn))
+ ERROR("PLAYER: Failed to recover from argument error");
+ case MPD_ERROR_SYSTEM:
+ errstr = mpd_connection_get_error_message(mpd.conn);
+ PLAYER_STATUS(ERR, "ERR - %s", errstr);
+ return 1;
+ case MPD_ERROR_CLOSED:
+ ERROR("PLAYER: Connection abruptly closed");
+ }
+
+ return 0;
+}
+
+bool
+history_contains(struct track *track, int depth)
+{
+ struct link *link;
+ struct ref *ref;
+
+ link = list_back(&player.history);
+ while (LIST_INNER(link) && depth-- > 0) {
+ ref = UPCAST(link, struct ref);
+ if (track == ref->data)
+ return true;
+ link = link->prev;
+ }
+
+ return false;
+}
+
+struct track *
+playlist_track_lru(int skip)
+{
+ struct track *track;
+ struct link *link;
+ struct ref *ref;
+ int len;
+
+ track = NULL;
+
+ len = list_len(&player.playlist);
+ link = list_front(&player.playlist);
+ while (skip >= 0 && LIST_INNER(link)) {
+ ref = UPCAST(link, struct ref);
+ track = ref->data;
+
+ if (!history_contains(track, len - 1))
+ skip -= 1;
+
+ if (skip <= 0) break;
+
+ link = link->next;
+ if (!LIST_INNER(link))
+ link = list_front(&player.playlist);
+ }
+
+ PLAYER_STATUS(INFO, "%i, %i", len - 1, skip);
+
+ return track;
+}
+
+void
+player_play_prev(void)
+{
+ struct link *link, *next;
+ struct track *track;
+ struct ref *ref;
+
+ if (list_empty(&player.history))
+ return;
+
+ if (!player.history_sel) {
+ next = list_back(&player.history);
+ } else if (LIST_INNER(player.history_sel->prev)) {
+ next = player.history_sel->prev;
+ } else {
+ return;
+ }
+
+ ref = UPCAST(next, struct ref);
+ player_play_track(ref->data);
+
+ player.history_sel = next;
+}
+
+void
+player_play_next(void)
+{
+ struct link *link;
+ struct track *track, *next_track;
+ struct ref *ref;
+ int index, len;
+ int status;
+
+ next_track = NULL;
+
+ link = player.history_sel;
+ if (link && LIST_INNER(link->next)) {
+ player.history_sel = link->next;
+ ref = UPCAST(link->next, struct ref);
+ next_track = ref->data;
+ } else {
+ if (!list_empty(&player.queue)) {
+ link = list_pop_front(&player.queue);
+ ref = UPCAST(link, struct ref);
+ next_track = ref->data;
+ ref_free(ref);
+ } else {
+ if (list_empty(&player.playlist))
+ return;
+
+ if (!player.history_sel) {
+ player_add_history(player.track);
+ player.track = NULL;
+ }
+ player.history_sel = NULL;
+
+ if (player.shuffle) {
+ index = rand() % list_len(&player.playlist);
+ next_track = playlist_track_lru(index + 1);
+ ASSERT(next_track != NULL);
+ } else {
+ link = player.playlist_sel;
+ if (link && LIST_INNER(link->next)) {
+ ref = UPCAST(link->next, struct ref);
+ next_track = ref->data;
+ } else {
+ status = mpd_run_clear(mpd.conn);
+ mpd_handle_status(status);
+ return;
+ }
+ }
+ }
+ }
+
+ player_play_track(next_track);
+}
+
+void
+player_add_history(struct track *track)
+{
+ struct link *link;
+ struct ref *ref;
+
+ link = list_back(&player.history);
+ if (link) {
+ ref = UPCAST(link, struct ref);
+ if (ref->data == track) return;
+ }
+
+ ref = ref_init(track);
+ list_push_back(&player.history, LINK(ref));
+}
+
+void
+player_init(void)
+{
+ mpd.conn = NULL;
+ mpd.action = PLAYER_ACTION_NONE;
+ mpd.seek_delay = 0;
+
+ list_init(&player.history);
+ player.history_sel = NULL;
+ list_init(&player.queue);
+ player.track = NULL;
+ list_init(&player.playlist);
+ player.playlist_sel = NULL;
+ player.loaded = 0;
+ player.autoplay = true;
+ player.shuffle = true;
+ player.state = PLAYER_STATE_PAUSED;
+ player.volume = 0;
+ player.time_pos = 0;
+ player.time_end = 0;
+ player.status = NULL;
+ player.status_lvl = PLAYER_STATUS_MSG_INFO;
+}
+
+void
+player_deinit(void)
+{
+ struct link *iter;
+
+ if (!mpd.conn) return;
+
+ refs_free(&player.queue);
+ refs_free(&player.history);
+ refs_free(&player.playlist);
+ if (player.status) free(player.status);
+
+ if (mpd.conn) mpd_connection_free(mpd.conn);
+}
+
+void
+player_update(void)
+{
+ struct mpd_status *status;
+ struct mpd_song *current_song;
+ struct ref *ref;
+ bool queue_empty;
+
+ if (!mpd.conn) {
+ mpd.conn = mpd_connection_new(NULL, 0, 0);
+ if (!mpd.conn) ERROR("MPD: Connect to server failed\n");
+ }
+
+ status = mpd_run_status(mpd.conn);
+ if (!status) {
+ PLAYER_STATUS(ERR, "Resetting MPD server connection");
+ mpd_connection_free(mpd.conn);
+ mpd.conn = NULL;
+ return;
+ }
+
+ current_song = mpd_run_current_song(mpd.conn);
+ if (!current_song) {
+ if (player.track && !player.history_sel) {
+ player_add_history(player.track);
+ player.track = NULL;
+ }
+
+ queue_empty = list_empty(&player.queue);
+ if (player.loaded && player.autoplay || !queue_empty)
+ mpd.action = PLAYER_ACTION_PLAY_NEXT;
+ } else {
+ mpd_song_free(current_song);
+ }
+
+ mpd_status_free(status);
+
+ if (mpd.action != PLAYER_ACTION_NONE) {
+ switch (mpd.action) {
+ case PLAYER_ACTION_PLAY_PREV:
+ player_play_prev();
+ break;
+ case PLAYER_ACTION_PLAY_NEXT:
+ player_play_next();
+ break;
+ default:
+ PANIC();
+ }
+
+ mpd.action = PLAYER_ACTION_NONE;
+ }
+
+ status = mpd_run_status(mpd.conn);
+ if (!status) {
+ PLAYER_STATUS(ERR, "Resetting MPD server connection");
+ mpd_connection_free(mpd.conn);
+ mpd.conn = NULL;
+ return;
+ }
+
+ current_song = mpd_run_current_song(mpd.conn);
+ if (current_song) {
+ player.loaded = true;
+ player.time_pos = mpd_status_get_elapsed_time(status);
+ player.time_end = mpd_song_get_duration(current_song);
+ mpd_song_free(current_song);
+ } else {
+ player.loaded = false;
+ player.time_pos = 0;
+ player.time_end = 0;
+ }
+
+ switch (mpd_status_get_state(status)) {
+ case MPD_STATE_PAUSE:
+ player.state = PLAYER_STATE_PAUSED;
+ break;
+ case MPD_STATE_PLAY:
+ player.state = PLAYER_STATE_PLAYING;
+ break;
+ case MPD_STATE_STOP:
+ player.state = PLAYER_STATE_STOPPED;
+ break;
+ default:
+ PANIC();
+ }
+
+ player.volume = mpd_status_get_volume(status);
+
+ if (mpd.seek_delay) {
+ mpd.seek_delay -= 1;
+ if (!mpd.seek_delay) player_play();
+ }
+
+ mpd_status_free(status);
+}
+
+int
+player_play_track(struct track *track)
+{
+ struct link *link;
+ struct ref *ref;
+ int status;
+
+ status = mpd_run_clear(mpd.conn);
+ mpd_handle_status(status);
+
+ status = mpd_run_add(mpd.conn, track->fpath);
+ if (mpd_handle_status(status))
+ return PLAYER_STATUS_ERR;
+
+ status = mpd_run_play(mpd.conn);
+ if (mpd_handle_status(status))
+ return PLAYER_STATUS_ERR;
+
+ if (player.track && !player.history_sel) {
+ player_add_history(player.track);
+ player.track = NULL;
+ }
+
+ player.history_sel = NULL;
+ /* re-assigning history_sel done in calling code */
+
+ player.playlist_sel = NULL;
+ for (LIST_ITER(&player.playlist, link)) {
+ ref = UPCAST(link, struct ref);
+ if (ref->data == track) {
+ player.playlist_sel = link;
+ break;
+ }
+ }
+
+ player.track = track;
+
+ return PLAYER_STATUS_OK;
+}
+
+int
+player_toggle_pause(void)
+{
+ int status;
+
+ status = mpd_run_toggle_pause(mpd.conn);
+ if (mpd_handle_status(status))
+ return PLAYER_STATUS_ERR;
+
+ return PLAYER_STATUS_OK;
+}
+
+int
+player_pause(void)
+{
+ int status;
+
+ status = mpd_run_pause(mpd.conn, true);
+ if (mpd_handle_status(status))
+ return PLAYER_STATUS_ERR;
+
+ return PLAYER_STATUS_OK;
+}
+
+int
+player_resume(void)
+{
+ int status;
+
+ status = mpd_run_pause(mpd.conn, false);
+ if (mpd_handle_status(status))
+ return PLAYER_STATUS_ERR;
+
+ return PLAYER_STATUS_OK;
+}
+
+int
+player_next(void)
+{
+ mpd.action = PLAYER_ACTION_PLAY_NEXT;
+
+ return PLAYER_STATUS_OK;
+}
+
+int
+player_prev(void)
+{
+ mpd.action = PLAYER_ACTION_PLAY_PREV;
+
+ return PLAYER_STATUS_OK;
+}
+
+int
+player_play(void)
+{
+ int status;
+
+ status = mpd_run_play(mpd.conn);
+ if (mpd_handle_status(status))
+ return PLAYER_STATUS_ERR;
+
+ return PLAYER_STATUS_OK;
+}
+
+int
+player_stop(void)
+{
+ int status;
+
+ status = mpd_run_stop(mpd.conn);
+ if (mpd_handle_status(status))
+ return PLAYER_STATUS_ERR;
+
+ return PLAYER_STATUS_OK;
+}
+
+int
+player_seek(int sec)
+{
+ int status;
+
+ player_clear_status();
+ if (!player.loaded || player.state == PLAYER_STATE_STOPPED) {
+ PLAYER_STATUS(ERR, "No track loaded");
+ return PLAYER_STATUS_ERR;
+ }
+
+ status = mpd_run_seek_current(mpd.conn, sec, false);
+ if (mpd_handle_status(status))
+ return PLAYER_STATUS_ERR;
+
+ mpd.seek_delay = 7;
+ player_pause();
+
+ return PLAYER_STATUS_OK;
+}
+
+int
+player_set_volume(unsigned int vol)
+{
+ int status;
+
+ player_clear_status();
+ if (player.volume == -1) {
+ PLAYER_STATUS(ERR, "Volume control not supported");
+ return PLAYER_STATUS_ERR;
+ }
+
+ status = mpd_run_set_volume(mpd.conn, vol);
+ if (mpd_handle_status(status))
+ return PLAYER_STATUS_ERR;
+
+ return PLAYER_STATUS_OK;
+}
+
diff --git a/src/tui.c b/src/tui.c
@@ -225,13 +225,13 @@ toggle_current_tag(void)
}
/* rebuild the full playlist */
- refs_free(&player->playlist);
+ refs_free(&player.playlist);
for (LIST_ITER(&tags_sel, link)) {
tag = UPCAST(link, struct ref)->data;
for (LIST_ITER(&tag->tracks, iter)) {
ref = ref_init(UPCAST(iter, struct ref)->data);
ASSERT(ref != NULL);
- list_push_back(&player->playlist, LINK(ref));
+ list_push_back(&player.playlist, LINK(ref));
}
}
}
@@ -375,11 +375,11 @@ track_pane_vis(struct pane *pane, int sel)
if (index < track_nav.wmin) continue;
if (index >= track_nav.wmax) break;
- if (sel && index == track_nav.sel && track == player->track)
+ if (sel && index == track_nav.sel && track == player.track)
style_on(pane->win, STYLE_ITEM_HOVER_SEL);
else if (sel && index == track_nav.sel)
style_on(pane->win, STYLE_ITEM_HOVER);
- else if (track == player->track)
+ else if (track == player.track)
style_on(pane->win, STYLE_ITEM_SEL);
else if (index == track_nav.sel)
style_on(pane->win, STYLE_PREV);
@@ -387,11 +387,11 @@ track_pane_vis(struct pane *pane, int sel)
wmove(pane->win, 1 + index - track_nav.wmin, 0);
wprintw(pane->win, "%-*.*ls", pane->w, pane->w, track->name);
- if (sel && index == track_nav.sel && track == player->track)
+ if (sel && index == track_nav.sel && track == player.track)
style_off(pane->win, STYLE_ITEM_HOVER_SEL);
else if (sel && index == track_nav.sel)
style_off(pane->win, STYLE_ITEM_HOVER);
- else if (track == player->track)
+ else if (track == player.track)
style_off(pane->win, STYLE_ITEM_SEL);
else if (index == track_nav.sel)
style_off(pane->win, STYLE_PREV);
@@ -581,46 +581,46 @@ cmd_pane_vis(struct pane *pane, int sel)
/* track name */
style_on(pane->win, STYLE_TITLE);
pane_clearln(pane, 0);
- if (player->track) {
- swprintf(linebuf, linecap, L"%ls", player->track->name);
+ if (player.loaded && player.track) {
+ swprintf(linebuf, linecap, L"%ls", player.track->name);
mvwaddwstr(pane->win, 0, 1, linebuf);
}
style_off(pane->win, STYLE_TITLE);
- if (player->loaded) {
+ if (player.loaded) {
/* status line */
line = linebuf;
line += swprintf(line, end - line, L"%c ",
- player_state_chars[player->state]);
+ player_state_chars[player.state]);
line += swprintf(line, end - line, L"%s / ",
- timestr(player->time_pos));
+ timestr(player.time_pos));
line += swprintf(line, end - line, L"%s",
- timestr(player->time_end));
+ timestr(player.time_end));
- if (player->volume >= 0) {
+ if (player.volume >= 0) {
line += swprintf(line, end - line, L" - vol: %u%%",
- player->volume);
+ player.volume);
}
- if (player->msg) {
+ if (player.status) {
line += swprintf(line, end - line, L" | [PLAYER] %s",
- player->msg);
+ player.status);
}
- if (!list_empty(&player->queue)) {
+ if (list_len(&player.queue)) {
line += swprintf(line, end - line,
L" | [QUEUE] %i tracks",
- list_len(&player->queue));
+ list_len(&player.queue));
}
ATTR_ON(pane->win, A_REVERSE);
pane_clearln(pane, 1);
mvwaddwstr(pane->win, 1, 0, linebuf);
ATTR_OFF(pane->win, A_REVERSE);
- } else if (player->msg) {
+ } else if (player.status) {
/* player message */
line = linebuf;
- line += swprintf(line, linecap, L"[PLAYER] %s", player->msg);
+ line += swprintf(line, linecap, L"[PLAYER] %s", player.status);
line += swprintf(line, end - line, L"%*.*s",
pane->w, pane->w, L" ");
@@ -629,12 +629,12 @@ cmd_pane_vis(struct pane *pane, int sel)
}
/* status bits on right of status line */
- if (player->loaded) ATTR_ON(pane->win, A_REVERSE);
+ if (player.loaded) ATTR_ON(pane->win, A_REVERSE);
mvwaddstr(pane->win, 1, pane->w - 5, "[ ]");
if (track_show_playlist) mvwaddstr(pane->win, 1, pane->w - 4, "P");
- if (player->autoplay) mvwaddstr(pane->win, 1, pane->w - 3, "A");
- if (player->shuffle) mvwaddstr(pane->win, 1, pane->w - 2, "S");
- if (player->loaded) ATTR_OFF(pane->win, A_REVERSE);
+ if (player.autoplay) mvwaddstr(pane->win, 1, pane->w - 3, "A");
+ if (player.shuffle) mvwaddstr(pane->win, 1, pane->w - 2, "S");
+ if (player.loaded) ATTR_OFF(pane->win, A_REVERSE);
if (sel || cmd_show) {
/* cmd and search input */
@@ -681,10 +681,13 @@ void
queue_hover(void)
{
struct link *link;
+ struct ref *ref;
- link = list_at(&player->playlist, track_nav.sel);
+ link = list_at(&player.playlist, track_nav.sel);
if (!link) return;
- player_queue_append(UPCAST(link, struct ref)->data);
+
+ ref = UPCAST(link, struct ref);
+ list_push_back(&player.queue, ref->data);
}
void
@@ -694,7 +697,7 @@ update_track_playlist(void)
struct tag *tag;
if (track_show_playlist) {
- tracks_vis = &player->playlist;
+ tracks_vis = &player.playlist;
} else {
link = list_at(&tags, tag_nav.sel);
if (!link) return;
@@ -719,18 +722,21 @@ main_input(wint_t c)
pane_sel = pane_after_cmd;
break;
case KEY_LEFT:
- if (!player->loaded) break;
- player_seek(MAX(player->time_pos - 10, 0));
+ if (!player.loaded) break;
+ player_seek(MAX(player.time_pos - 10, 0));
break;
case KEY_RIGHT:
- if (!player->loaded) break;
- player_seek(MIN(player->time_pos + 10, player->time_end));
+ if (!player.loaded) break;
+ player_seek(MIN(player.time_pos + 10, player.time_end));
break;
case L'y':
queue_hover();
break;
case L'o':
- player_queue_clear();
+ refs_free(&player.queue);
+ break;
+ case L'h':
+ refs_free(&player.history);
break;
case L'c':
player_toggle_pause();
@@ -753,16 +759,16 @@ main_input(wint_t c)
}
break;
case L'A':
- player->autoplay ^= 1;
+ player.autoplay ^= 1;
break;
case L'S':
- player->shuffle ^= 1;
+ player.shuffle ^= 1;
break;
case L'b':
player_seek(0);
break;
case L'x':
- if (player->state == PLAYER_STATE_PLAYING) {
+ if (player.state == PLAYER_STATE_PLAYING) {
player_stop();
} else {
player_play();
@@ -804,10 +810,10 @@ main_input(wint_t c)
completion = tag_name_gen;
break;
case L'+':
- player_set_volume(MIN(100, player->volume + 5));
+ player_set_volume(MIN(100, player.volume + 5));
break;
case L'-':
- player_set_volume(MAX(0, player->volume - 5));
+ player_set_volume(MAX(0, player.volume - 5));
break;
case L'q':
quit = 1;