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