tmus

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

player_mplay.c (6808B)


      1#include "player.h"
      2
      3#include "tui.h"
      4#include "data.h"
      5#include "list.h"
      6#include "util.h"
      7#include "log.h"
      8
      9#include "mplay.h"
     10
     11#include <sys/wait.h>
     12#include <sys/mman.h>
     13#include <unistd.h>
     14#include <errno.h>
     15#include <signal.h>
     16#include <stdbool.h>
     17#include <stdio.h>
     18#include <stdlib.h>
     19#include <string.h>
     20
     21#define MPLAY_STATUS(line) do { \
     22		free(user_status); \
     23		user_status = aprintf("Player: %s", \
     24			line ? line : "no response"); \
     25		user_status_uptime = 20; \
     26	} while (0)
     27
     28struct mplay_player {
     29	FILE *stdin;
     30	FILE *stdout;
     31	pid_t pid;
     32	uint64_t update_ms;
     33};
     34
     35struct player player;
     36struct mplay_player mplay;
     37
     38static void sigpipe_handler(int sig);
     39
     40static void mplay_kill(void);
     41static bool mplay_run(struct track *track);
     42static char *mplay_readline(void);
     43static char *mplay_info_arg(char *line, const char *prefix);
     44
     45void
     46sigpipe_handler(int sig)
     47{
     48	mplay_kill();
     49}
     50
     51bool
     52mplay_run(struct track *track)
     53{
     54	int output[2];
     55	int input[2];
     56	char *path;
     57	char *line;
     58
     59	ASSERT(!player.loaded);
     60
     61	if (pipe(input) == -1)
     62		ERROR(SYSTEM, "pipe");
     63
     64	if (pipe(output) == -1)
     65		ERROR(SYSTEM, "pipe");
     66
     67	mplay.pid = fork();
     68	if (mplay.pid < 0) ERROR(SYSTEM, "fork");
     69
     70	if (mplay.pid != 0) {
     71		close(output[1]);
     72		mplay.stdout = fdopen(output[0], "r");
     73		if (!mplay.stdout) ERROR(SYSTEM, "fdopen");
     74		setvbuf(mplay.stdout, NULL, _IONBF, 0);
     75
     76		close(input[0]);
     77		mplay.stdin = fdopen(input[1], "w");
     78		if (!mplay.stdin) ERROR(SYSTEM, "fdopen");
     79		setvbuf(mplay.stdin, NULL, _IONBF, 0);
     80	} else {
     81		dup2(input[0], 0);
     82		dup2(output[1], 1);
     83		dup2(output[1], 2);
     84		close(input[0]);
     85		close(input[1]);
     86		close(output[0]);
     87		close(output[1]);
     88		path = aprintf("%s/%s/%s", datadir,
     89			track->tag->name, track->name);
     90		execlp("mplay", "mplay", path, NULL);
     91		abort();
     92	}
     93
     94	player.loaded = true;
     95
     96	line = mplay_readline();
     97	if (!line || !mplay_info_arg(line, MPLAY_INFO_STR_READY)) {
     98		mplay_kill();
     99		MPLAY_STATUS(line);
    100		return false;
    101	}
    102
    103	return true;
    104}
    105
    106void
    107mplay_kill(void)
    108{
    109	if (!player.loaded) return;
    110	kill(mplay.pid, SIGKILL);
    111	waitpid(mplay.pid, NULL, 0);
    112	player.loaded = false;
    113}
    114
    115char *
    116mplay_readline(void)
    117{
    118	static char linebuf[256];
    119	char *tok;
    120
    121	/* TODO: add timeout */
    122	if (!mplay.stdout || !fgets(linebuf, sizeof(linebuf), mplay.stdout)) {
    123		mplay_kill(); /* dont clear track yet */
    124		return NULL;
    125	}
    126
    127	tok = strchr(linebuf, '\n');
    128	if (tok) *tok = '\0';
    129
    130	return linebuf;
    131}
    132
    133char *
    134mplay_info_arg(char *line, const char *prefix)
    135{
    136	if (strncmp(line, "mplay!", 6))
    137		return NULL;
    138	line += 6;
    139
    140	size_t prefixlen = strlen(prefix);
    141	if (strncmp(line, prefix, prefixlen))
    142		return NULL;
    143	line += prefixlen;
    144
    145	if (line[0] == ':')
    146		return line+1;
    147	else if (!line[0])
    148		return line;
    149	return NULL;
    150}
    151
    152void
    153player_init(void)
    154{
    155	list_init(&player.playlist);
    156	list_init(&player.history);
    157	list_init(&player.queue);
    158
    159	player.track = NULL;
    160	player.track_name = NULL;
    161
    162	player.loaded = false;
    163	player.autoplay = true;
    164	player.shuffle = true;
    165
    166	player.state = PLAYER_STATE_PAUSED;
    167	player.volume = 50;
    168
    169	player.time_pos = 0;
    170	player.time_end = 0;
    171
    172	signal(SIGPIPE, sigpipe_handler);
    173}
    174
    175void
    176player_deinit(void)
    177{
    178	list_clear(&player.playlist);
    179	list_clear(&player.queue);
    180	list_clear(&player.history);
    181
    182	free(player.track_name);
    183
    184	mplay_kill();
    185}
    186
    187void
    188player_update(void)
    189{
    190	bool queue_empty;
    191	char *tok, *line;
    192	char *arg;
    193
    194	if (!player.loaded) {
    195		queue_empty = list_empty(&player.queue);
    196		if (player.track && player.autoplay || !queue_empty) {
    197			player_next();
    198		} else if (player.track) {
    199			player_clear_track();
    200		}
    201	}
    202
    203	if (!player.loaded) return;
    204
    205	if (current_ms() >= mplay.update_ms + 330) {
    206		mplay.update_ms = current_ms();
    207		fputc(MPLAY_ACTION_KEY_STATUS, mplay.stdin);
    208		line = mplay_readline();
    209		if (!line || !(arg = mplay_info_arg(line, MPLAY_INFO_STR_STATUS))) {
    210			if (line && (arg = mplay_info_arg(line, MPLAY_INFO_STR_EXIT))) {
    211				player_next();
    212				return;
    213			}
    214			mplay_kill();
    215			MPLAY_STATUS(line);
    216			return;
    217		}
    218
    219		tok = arg;
    220		while (1) {
    221			if (!strncmp(tok, "volume=", 7)) {
    222				player.volume = atoi(tok + 7);
    223			} else if (!strncmp(tok, "pause=", 6)) {
    224				player.state = atoi(tok + 6)
    225					? PLAYER_STATE_PAUSED : PLAYER_STATE_PLAYING;
    226			} else if (!strncmp(tok, "pos=", 4)) {
    227				player.time_pos = atoi(tok + 4);
    228			} else if (!strncmp(tok, "end=", 4)) {
    229				player.time_end = atoi(tok + 4);
    230			}
    231			tok = strchr(tok, ',');
    232			if (!tok) break;
    233			tok += 1;
    234		}
    235	}
    236}
    237
    238int
    239player_play_track(struct track *track, bool new)
    240{
    241	ASSERT(track != NULL);
    242
    243	player_clear_track();
    244	player.track = track;
    245
    246	if (!mplay_run(track))
    247		return PLAYER_ERR;
    248
    249	/* new invocations are removed from history */
    250	if (new) list_link_pop(&track->link_hs);
    251
    252	player.time_pos = 0;
    253	player.time_end = 0;
    254
    255	return PLAYER_OK;
    256}
    257
    258int
    259player_clear_track(void)
    260{
    261	if (player.track)
    262		player_add_history(player.track);
    263
    264	player.track = NULL;
    265	mplay_kill();
    266
    267	return PLAYER_OK;
    268}
    269
    270int
    271player_toggle_pause(void)
    272{
    273	char *line, *arg;
    274
    275	if (!player.loaded) return PLAYER_ERR;
    276
    277	fputc(MPLAY_ACTION_KEY_PAUSE, mplay.stdin);
    278	line = mplay_readline();
    279	if (!line || !(arg = mplay_info_arg(line, MPLAY_INFO_STR_PAUSE))) {
    280		mplay_kill();
    281		MPLAY_STATUS(line);
    282		return PLAYER_ERR;
    283	}
    284
    285	if (atoi(arg))
    286		player.state = PLAYER_STATE_PAUSED;
    287	else
    288		player.state = PLAYER_STATE_PLAYING;
    289
    290	return PLAYER_OK;
    291}
    292
    293int
    294player_pause(void)
    295{
    296	if (!player.loaded) return PLAYER_ERR;
    297
    298	if (player.state != PLAYER_STATE_PAUSED)
    299		player_toggle_pause();
    300
    301	return PLAYER_OK;
    302}
    303
    304int
    305player_resume(void)
    306{
    307	if (!player.loaded) return PLAYER_ERR;
    308
    309	if (player.state != PLAYER_STATE_PLAYING)
    310		player_toggle_pause();
    311
    312	return PLAYER_OK;
    313}
    314
    315int
    316player_play(void)
    317{
    318	return PLAYER_OK;
    319}
    320
    321int
    322player_stop(void)
    323{
    324	if (!player.loaded) return PLAYER_ERR;
    325
    326	player_clear_track();
    327
    328	return PLAYER_OK;
    329}
    330
    331int
    332player_seek(int sec)
    333{
    334	char *line, *arg;
    335
    336	if (!player.loaded) return PLAYER_ERR;
    337
    338	putc(MPLAY_ACTION_KEY_SEEK, mplay.stdin);
    339	fprintf(mplay.stdin, "%i\n", sec);
    340	line = mplay_readline();
    341	if (!line || !(arg = mplay_info_arg(line, MPLAY_INFO_STR_SEEK))) {
    342		if (line && (arg = mplay_info_arg(line, MPLAY_INFO_STR_EXIT))) {
    343			return player_next();
    344		}
    345		mplay_kill();
    346		MPLAY_STATUS(line);
    347		return PLAYER_ERR;
    348	}
    349
    350	player.time_pos = atoi(arg);
    351	player.time_end = MAX(player.time_pos, player.time_end);
    352
    353	return PLAYER_OK;
    354}
    355
    356int
    357player_set_volume(unsigned int vol)
    358{
    359	char *line, *arg;
    360
    361	if (!player.loaded) return PLAYER_ERR;
    362
    363	if (player.volume == -1) {
    364		PLAYER_STATUS("volume control not supported");
    365		return PLAYER_ERR;
    366	}
    367
    368	putc(MPLAY_ACTION_KEY_VOLUME, mplay.stdin);
    369	fprintf(mplay.stdin, "%i\n", vol);
    370	line = mplay_readline();
    371	if (!line || !(arg = mplay_info_arg(line, MPLAY_INFO_STR_VOLUME))) {
    372		mplay_kill();
    373		MPLAY_STATUS(line);
    374		return PLAYER_ERR;
    375	}
    376
    377	player.volume = atoi(arg);
    378
    379	return PLAYER_OK;
    380}
    381