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