tmus

TUI Music Player
git clone https://git.sinitax.com/sinitax/tmus
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

player_mpd.c (6097B)


      1#include "player.h"
      2
      3#include "data.h"
      4#include "list.h"
      5#include "util.h"
      6#include "log.h"
      7
      8#include <mpd/client.h>
      9#include <mpd/song.h>
     10#include <mpd/status.h>
     11
     12#include <signal.h>
     13#include <stdbool.h>
     14#include <stdlib.h>
     15#include <string.h>
     16#include <sys/mman.h>
     17#include <unistd.h>
     18
     19struct mpd_player {
     20	struct mpd_connection *conn;
     21
     22	/* number of frames to wait before unpausing after
     23	 * seek to prevent pause-play cycle noises */
     24	int seek_delay;
     25
     26	/* action to perform on next update */
     27	int action;
     28};
     29
     30struct player player;
     31struct mpd_player mpd;
     32
     33static bool mpd_handle_status(int status);
     34static char *mpd_loaded_track_name(struct mpd_song *song);
     35
     36bool
     37mpd_handle_status(int status)
     38{
     39	const char *errstr;
     40
     41	switch (status) {
     42	case MPD_ERROR_SERVER:
     43	case MPD_ERROR_ARGUMENT:
     44		if (!mpd_connection_clear_error(mpd.conn))
     45			ERRORX(SYSTEM, "Player failed to recover");
     46	case MPD_ERROR_SYSTEM:
     47		errstr = mpd_connection_get_error_message(mpd.conn);
     48		PLAYER_STATUS("MPD ERR - %s", errstr);
     49		return false;
     50	case MPD_ERROR_CLOSED:
     51		ERRORX(SYSTEM, "Player connection abruptly closed");
     52	}
     53
     54	return true;
     55}
     56
     57char *
     58mpd_loaded_track_name(struct mpd_song *song)
     59{
     60	const char *path, *sep;
     61
     62	path = mpd_song_get_uri(song);
     63
     64	sep = strrchr(path, '/');
     65	if (!sep) return astrdup(path);
     66
     67	return astrdup(sep + 1);
     68}
     69
     70void
     71player_init(void)
     72{
     73	mpd.conn = NULL;
     74	mpd.seek_delay = 0;
     75
     76	list_init(&player.playlist);
     77	list_init(&player.history);
     78	list_init(&player.queue);
     79
     80	player.track = NULL;
     81	player.track_name = NULL;
     82
     83	player.loaded = 0;
     84	player.autoplay = true;
     85	player.shuffle = true;
     86
     87	player.state = PLAYER_STATE_PAUSED;
     88	player.volume = 50;
     89
     90	player.time_pos = 0;
     91	player.time_end = 0;
     92}
     93
     94void
     95player_deinit(void)
     96{
     97	list_clear(&player.playlist);
     98	list_clear(&player.queue);
     99	list_clear(&player.history);
    100
    101	free(player.status);
    102	free(player.track_name);
    103
    104	if (mpd.conn) mpd_connection_free(mpd.conn);
    105}
    106
    107void
    108player_update(void)
    109{
    110	struct mpd_status *status;
    111	struct mpd_song *current_song;
    112	bool queue_empty;
    113
    114	if (!mpd.conn) {
    115		mpd.conn = mpd_connection_new(NULL, 0, 0);
    116		if (!mpd.conn) ERRX("MPD connection failed");
    117	}
    118
    119	status = mpd_run_status(mpd.conn);
    120	if (!status) {
    121		PLAYER_STATUS("MPD connection reset: %s",
    122			mpd_connection_get_error_message(mpd.conn));
    123		mpd_connection_free(mpd.conn);
    124		mpd.conn = NULL;
    125		return;
    126	}
    127
    128	current_song = mpd_run_current_song(mpd.conn);
    129	if (!current_song) {
    130		if (player.track)
    131			player_add_history(player.track);
    132
    133		queue_empty = list_empty(&player.queue);
    134		if (player.loaded && player.autoplay || !queue_empty)
    135			player_next();
    136	} else {
    137		mpd_song_free(current_song);
    138	}
    139
    140	mpd_status_free(status);
    141
    142	/* in case autoplay loaded new track,
    143	 * get status and track name again.. */
    144	status = mpd_run_status(mpd.conn);
    145	if (!status) {
    146		PLAYER_STATUS("MPD connection reset: %s",
    147			mpd_connection_get_error_message(mpd.conn));
    148		mpd_connection_free(mpd.conn);
    149		mpd.conn = NULL;
    150		return;
    151	}
    152
    153	current_song = mpd_run_current_song(mpd.conn);
    154	if (current_song) {
    155		free(player.track_name);
    156		player.track_name = mpd_loaded_track_name(current_song);
    157		player.loaded = true;
    158		player.time_pos = mpd_status_get_elapsed_time(status);
    159		player.time_end = mpd_song_get_duration(current_song);
    160		mpd_song_free(current_song);
    161	} else {
    162		free(player.track_name);
    163		player.track_name = NULL;
    164		player.loaded = false;
    165		player.time_pos = 0;
    166		player.time_end = 0;
    167	}
    168
    169	switch (mpd_status_get_state(status)) {
    170	case MPD_STATE_PAUSE:
    171		player.state = PLAYER_STATE_PAUSED;
    172		break;
    173	case MPD_STATE_PLAY:
    174		player.state = PLAYER_STATE_PLAYING;
    175		break;
    176	case MPD_STATE_STOP:
    177		player.state = PLAYER_STATE_STOPPED;
    178		break;
    179	default:
    180		ASSERT(0);
    181	}
    182
    183	player.volume = mpd_status_get_volume(status);
    184
    185	if (mpd.seek_delay) {
    186		mpd.seek_delay -= 1;
    187		if (!mpd.seek_delay) player_play();
    188	}
    189
    190	mpd_status_free(status);
    191}
    192
    193int
    194player_play_track(struct track *track, bool new)
    195{
    196	int status;
    197
    198	ASSERT(track != NULL);
    199
    200	status = mpd_run_clear(mpd.conn);
    201	if (!mpd_handle_status(status))
    202		return PLAYER_ERR;
    203
    204	status = mpd_run_add(mpd.conn, track->fpath);
    205	if (!mpd_handle_status(status))
    206		return PLAYER_ERR;
    207
    208	status = mpd_run_play(mpd.conn);
    209	if (!mpd_handle_status(status))
    210		return PLAYER_ERR;
    211
    212	/* add last track to history */
    213	if (player.track && !link_inuse(&player.track->link_hs))
    214		player_add_history(player.track);
    215
    216	/* new invocations result in updated history pos */
    217	if (new) link_pop(&track->link_hs);
    218
    219	player.track = track;
    220
    221	return PLAYER_OK;
    222}
    223
    224int
    225player_clear_track(void)
    226{
    227	int status;
    228
    229	player.track = NULL;
    230	status = mpd_run_clear(mpd.conn);
    231
    232	if (!mpd_handle_status(status))
    233		return PLAYER_ERR;
    234
    235	return PLAYER_OK;
    236}
    237
    238int
    239player_toggle_pause(void)
    240{
    241	int status;
    242
    243	status = mpd_run_toggle_pause(mpd.conn);
    244	if (!mpd_handle_status(status))
    245		return PLAYER_ERR;
    246
    247	return PLAYER_OK;
    248}
    249
    250int
    251player_pause(void)
    252{
    253	int status;
    254
    255	status = mpd_run_pause(mpd.conn, true);
    256	if (!mpd_handle_status(status))
    257		return PLAYER_ERR;
    258
    259	return PLAYER_OK;
    260}
    261
    262int
    263player_resume(void)
    264{
    265	int status;
    266
    267	status = mpd_run_pause(mpd.conn, false);
    268	if (!mpd_handle_status(status))
    269		return PLAYER_ERR;
    270
    271	return PLAYER_OK;
    272}
    273
    274int
    275player_play(void)
    276{
    277	int status;
    278
    279	status = mpd_run_play(mpd.conn);
    280	if (!mpd_handle_status(status))
    281		return PLAYER_ERR;
    282
    283	return PLAYER_OK;
    284}
    285
    286int
    287player_stop(void)
    288{
    289	int status;
    290
    291	status = mpd_run_stop(mpd.conn);
    292	if (!mpd_handle_status(status))
    293		return PLAYER_ERR;
    294
    295	return PLAYER_OK;
    296}
    297
    298int
    299player_seek(int sec)
    300{
    301	int status;
    302
    303	if (!player.loaded || player.state == PLAYER_STATE_STOPPED) {
    304		PLAYER_STATUS("No track loaded");
    305		return PLAYER_ERR;
    306	}
    307
    308	status = mpd_run_seek_current(mpd.conn, sec, false);
    309	if (!mpd_handle_status(status))
    310		return PLAYER_ERR;
    311
    312	mpd.seek_delay = 7;
    313	player_pause();
    314
    315	return PLAYER_OK;
    316}
    317
    318int
    319player_set_volume(unsigned int vol)
    320{
    321	int status;
    322
    323	if (player.volume == -1) {
    324		PLAYER_STATUS("Volume control not supported");
    325		return PLAYER_ERR;
    326	}
    327
    328	status = mpd_run_set_volume(mpd.conn, vol);
    329	if (!mpd_handle_status(status))
    330		return PLAYER_ERR;
    331	player.volume = vol;
    332
    333	return PLAYER_OK;
    334}
    335