1 /*
2  * Copyright (c) 2010, 2011 Ryan Flannery <ryan.flannery@gmail.com>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include "player.h"
18 
19 /* gloabls */
20 player_backend_t player;
21 player_info_t player_info;
22 
23 
24 /* callbacks */
callback_playnext()25 static void callback_playnext() { player_skip_song(1); }
26 
27 static void
callback_fatal(char * fmt,...)28 callback_fatal(char *fmt, ...)
29 {
30    va_list ap;
31 
32    ui_destroy();
33 
34    fprintf(stderr,"The player-backend '%s' has experienced a fatal error:\n",
35          player.name);
36 
37    va_start(ap, fmt);
38    vfprintf(stderr, fmt, ap);
39    va_end(ap);
40 
41    fflush(stderr);
42 
43    VSIG_QUIT = 1;
44    exit(1);
45 }
46 
47 
48 /* definition of backends */
49 const player_backend_t PlayerBackends[] = {
50    {
51       BACKEND_MPLAYER, "mplayer", false, NULL,
52       mplayer_start,
53       mplayer_finish,
54       mplayer_sigchld,
55       mplayer_play,
56       mplayer_stop,
57       mplayer_pause,
58       mplayer_seek,
59       mplayer_volume_step,
60       mplayer_get_position,
61       mplayer_get_volume,
62       mplayer_is_playing,
63       mplayer_is_paused,
64       mplayer_set_callback_playnext,
65       mplayer_set_callback_notice,
66       mplayer_set_callback_error,
67       mplayer_set_callback_fatal,
68       mplayer_monitor
69    },
70    { 0, "", false, NULL, NULL, NULL, NULL,
71       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
72 };
73 const size_t PlayerBackendsSize = sizeof(PlayerBackends) / sizeof(player_backend_t);
74 
75 
76 /* setup/destroy functions */
77 void
player_init(const char * backend)78 player_init(const char *backend)
79 {
80    bool   found;
81    size_t i;
82 
83    player_info.mode  = MODE_LINEAR;
84    player_info.queue = NULL;
85    player_info.qidx  = -1;
86 
87    player_info.rseed = time(0);
88    srand(player_info.rseed);
89 
90    /* find the player backend */
91    found = false;
92    for (i = 0; i < PlayerBackendsSize && !found; i++) {
93       if (!strcasecmp(backend, PlayerBackends[i].name)) {
94          player = PlayerBackends[i];
95          found = true;
96       }
97    }
98 
99    if (!found) {
100       ui_destroy();
101       errx(1, "backend '%s' not supported", backend);
102    }
103 
104    if (player.dynamic) {
105       ui_destroy();
106       errx(1, "dynamically loaded backends not yet supported");
107    }
108 
109    player.set_callback_playnext(callback_playnext);
110    player.set_callback_notice(paint_message);
111    player.set_callback_error(paint_error);
112    player.set_callback_fatal(callback_fatal);
113    player.start();
114 }
115 
116 void
player_destroy()117 player_destroy()
118 {
119    player.finish();
120 }
121 
122 void
player_set_queue(playlist * queue,int pos)123 player_set_queue(playlist *queue, int pos)
124 {
125    player_info.queue = queue;
126    player_info.qidx  = pos;
127 }
128 
129 void
player_play()130 player_play()
131 {
132    if (player_info.queue == NULL)
133       errx(1, "player_play: bad queue/qidx");
134 
135    if (player_info.qidx < 0 || player_info.qidx > player_info.queue->nfiles)
136       errx(1, "player_play: qidx %i out-of-range", player_info.qidx);
137 
138    player.play(player_info.queue->files[player_info.qidx]->filename);
139 
140 }
141 
142 void
player_stop()143 player_stop()
144 {
145    player.stop();
146 }
147 
148 void
player_pause()149 player_pause()
150 {
151    if (!player.playing())
152       return;
153 
154    player.pause();
155 }
156 
157 void
player_seek(int seconds)158 player_seek(int seconds)
159 {
160    if (!player.playing())
161       return;
162 
163    player.seek(seconds);
164 }
165 
166 /* TODO merge this with the player_play_prev_song into player_skip_song */
167 void
player_play_next_song(int skip)168 player_play_next_song(int skip)
169 {
170    if (!player.playing())
171       return;
172 
173    switch (player_info.mode) {
174    case MODE_LINEAR:
175       player_info.qidx += skip;
176       if (player_info.qidx >= player_info.queue->nfiles) {
177          player_info.qidx = 0;
178          player_stop();
179       } else
180          player_play();
181 
182       break;
183 
184    case MODE_LOOP:
185       player_info.qidx += skip;
186       if (player_info.qidx >= player_info.queue->nfiles)
187          player_info.qidx %= player_info.queue->nfiles;
188 
189       player_play();
190       break;
191 
192    case MODE_RANDOM:
193       player_info.qidx = rand() % player_info.queue->nfiles;
194       player_play();
195       break;
196    }
197 }
198 
199 /* TODO (see comment for player_play_next_song) */
200 void
player_play_prev_song(int skip)201 player_play_prev_song(int skip)
202 {
203    if (!player.playing())
204       return;
205 
206    switch (player_info.mode) {
207    case MODE_LINEAR:
208       player_info.qidx -= skip;
209       if (player_info.qidx < 0) {
210          player_info.qidx = 0;
211          player_stop();
212       } else
213          player_play();
214 
215       break;
216 
217    case MODE_LOOP:
218       skip %= player_info.queue->nfiles;
219       if (skip <= player_info.qidx)
220          player_info.qidx -= skip;
221       else
222          player_info.qidx = player_info.queue->nfiles - (skip + player_info.qidx);
223 
224       player_play();
225       break;
226 
227    case MODE_RANDOM:
228       player_info.qidx = rand() % player_info.queue->nfiles;
229       player_play();
230       break;
231    }
232 }
233 
234 /* TODO merge with above... wtf didn't i do it that way!? */
235 void
player_skip_song(int num)236 player_skip_song(int num)
237 {
238    if (num >= 0)
239       player_play_next_song(num);
240    else
241       player_play_prev_song(num * -1);
242 }
243 
244 void
player_volume_step(float percent)245 player_volume_step(float percent)
246 {
247    if (!player.playing())
248       return;
249 
250    player.volume_step(percent);
251 }
252 
253 void
player_monitor(void)254 player_monitor(void)
255 {
256    player.monitor();
257 }
258 
259