#include "player.h" #include "ref.h" #include "portaudio.h" #include "sndfile.h" #include "util.h" #include #include #include #include #include #include #include #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)); ASSERT(player != NULL); player->conn = mpd_connection_new(NULL, 0, 0); ASSERT(player->conn != NULL); 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) { fprintf(stderr, "MPD Fatal Error: %s\n", mpd_connection_get_error_message(player->conn)); exit(1); } 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) { fprintf(stderr, "MPD Fatal Error: %s\n", mpd_connection_get_error_message(player->conn)); exit(1); } 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; }