1 // Copyright (C) 1998 by Jean-Marc Zucconi (jmz@FreeBSD.ORG)
2 // Everyone is granted permission to copy, modify and redistribute.
3 // This notice must be preserved on all copies or derivates.
4
5 #include <string.h>
6 #include <unistd.h>
7 #include <stdlib.h>
8 #include <fcntl.h>
9 #include <err.h>
10 #include <errno.h>
11 #include <stdio.h>
12 #include <sys/file.h>
13 #include <sys/cdio.h>
14 #include <math.h>
15 #include <sys/ioctl.h>
16 #include <stdarg.h>
17
18 #define DEVICE "cd0"
19
20 void open_cd (), setvol (int), do_update (), read_toc (), play_msf (int, int, int),
21 time_track (int, int, int), play_track (int), popup (int, int),
22 out (char *, ...);
23 int get_info ();
24
25 class button {
26 private:
27 int state;
28 char name[20];
29 public:
button(char * s)30 button (char *s) {state=0; strcpy (name, s);}
~button()31 ~button () {}
set(int i)32 void set (int i) {
33 if (state != i) {
34 state = i;
35 if (state == 0)
36 out (".%s configure -fg $fg", name);
37 else
38 out (".%s configure -fg $active", name);
39 }
40 }
is_set()41 int is_set () {return state;}
42 };
43
44
45 int cd_fd = -1;
46 char *device;
47 int current_track = 0, current_min = 0, current_sec = 0, current_frame = 0;
48 int last_in_toc = 0;
49 int time_btn = 0, track_btn = 0;
50
51 int linear = 0;
52
53 int animate = 1;
54 int v_animate = 0;
55
56 struct cd_toc_entry toc_buffer[100];
57
58 typedef enum {
59 PLAY, STOP, PAUSE, EJECT, PREV, NEXT, REWIND, FF,
60 TRACK, TIME, VOLUME, UPDATE, QUIT, NONE
61 } command;
62
63 #define C(x) {#x, sizeof(#x)-1, x}
64 struct {
65 char *s;
66 int l;
67 command cmd;
68 } clist[] = {
69 C(PLAY),
70 C(STOP),
71 C(PAUSE),
72 C(EJECT),
73 C(PREV),
74 C(NEXT),
75 C(REWIND),
76 C(FF),
77 C(TRACK),
78 C(TIME),
79 C(VOLUME),
80 C(UPDATE),
81 C(QUIT),
82 {0, 0, NONE}
83 };
84
85 button play_button ("play"), pause_button ("pause"), stop_button ("stop"),
86 eject_button ("eject"), prev_button ("prev"), next_button ("next"),
87 rewind_button ("rewind"), ff_button ("ff");
88
89 #define M(t) toc_buffer[t].addr.msf.minute
90 #define S(t) toc_buffer[t].addr.msf.second
91 #define F(t) toc_buffer[t].addr.msf.frame
92
main(int argc,char ** argv)93 int main (int argc, char **argv)
94 {
95 char dev[20], line[50];
96 int i;
97 char *a;
98
99 if (argv[1] && !strcmp (argv[1], "-l")) {
100 linear = 1;
101 a = argv[2];
102 } else
103 a = argv[1];
104 if (!a)
105 a = DEVICE;
106
107 device = getenv ("CDPLAYER");
108 if (!device)
109 device = a;
110
111 a = strchr (device, '/');
112 if (!a && strlen (device) < 10) {
113 sprintf (dev, "/dev/%s", device);
114 device = dev;
115 }
116 while (fgets (line, 40, stdin)) {
117 for (i = 0; clist[i].s; i++)
118 if (strncasecmp (clist[i].s, line, clist[i].l) == 0)
119 break;
120 i = clist[i].cmd;
121 if ((cd_fd == -1) && (i != UPDATE) && (i != QUIT))
122 i = NONE;
123 switch (i) {
124 case UPDATE:
125 do_update ();
126 break;
127 case PLAY:
128 if (play_button.is_set ())
129 break;
130 play_track (1);
131 play_button.set (1);
132 break;
133 case STOP:
134 if (play_button.is_set ()) {
135 play_button.set (0);
136 pause_button.set (0);
137 ioctl (cd_fd, CDIOCSTOP);
138 stop_button.set (1);
139 time_track (-1, -1, -1);
140 }
141 break;
142 case PAUSE:
143 if (play_button.is_set ())
144 if (!pause_button.is_set () && !ioctl (cd_fd, CDIOCPAUSE))
145 pause_button.set (1);
146 else if (!ioctl (cd_fd, CDIOCRESUME))
147 pause_button.set (0);
148 break;
149 case EJECT:
150 if (!eject_button.is_set ()) {
151 i = ioctl (cd_fd, CDIOCALLOW);
152 if (!i)
153 i = ioctl (cd_fd, CDIOCEJECT);
154 if (!i) {
155 eject_button.set (1);
156 play_button.set (0);
157 pause_button.set (0);
158 stop_button.set (0);
159 close (cd_fd);
160 cd_fd = -1;
161 last_in_toc = 0;
162 animate = 1;
163 v_animate = 0;
164 }
165 }
166 break;
167 case PREV:
168 if (!play_button.is_set ())
169 break;
170 get_info ();
171 if (current_sec > 2)
172 play_track (current_track);
173 else
174 play_track (current_track-1);
175 break;
176 case NEXT:
177 if (!play_button.is_set ())
178 break;
179 play_track (current_track + 1);
180 break;
181 case REWIND:
182 if (atoi (line+clist[i].l) == 1)
183 rewind_button.set (1);
184 else
185 rewind_button.set (0);
186 break;
187 case FF:
188 if (atoi (line+clist[i].l) == 1)
189 ff_button.set (1);
190 else
191 ff_button.set (0);
192 break;
193 case TRACK:
194 i = atoi (line+clist[i].l);
195 if (i == 2) {
196 char *p = strchr (line, '.') + 1;
197 i = atoi (p);
198 p = strchr (p, '.') + 1;
199 popup (i, atoi(p));
200 } else if (i < 0)
201 play_track (-i);
202 else
203 track_btn = i;
204 break;
205 case TIME:
206 time_btn = atoi (line+clist[i].l);
207 break;
208 case VOLUME:
209 setvol (atoi (line+clist[i].l));
210 break;
211 case QUIT:
212 printf ("exit\n");
213 fflush (stdout);
214 sleep (3);
215 exit (0);
216 default:
217 break;
218 }
219 printf ("after %d do_update\n",
220 play_button.is_set () && !pause_button.is_set () ? 100 : 200);
221 fflush (stdout);
222 }
223 exit (0);
224 }
225
226 void
open_cd()227 open_cd ()
228 {
229 cd_fd = open (device, O_RDONLY); /* warning: this may take a long time */
230 if (cd_fd < 0) {
231 if (errno == ENXIO)
232 return; /* open says 'Device not configured' if there is no cd in */
233 if (errno == EBUSY || EINVAL)
234 return; /* loading the CD? */
235 {FILE *f=fopen("/tmp/cdplayer.error", "w"); fprintf(f, "exiting with errno=%d\n", errno);}
236 err (1, device);
237 }
238 }
239 void
setvol(int l)240 setvol (int l)
241 {
242 struct ioc_vol v;
243
244 l = linear ? int(l*2.55) : int(55.253*log(l+1.) +.01);
245 v.vol[0] = l;
246 v.vol[1] = l;
247 v.vol[2] = 0;
248 v.vol[3] = 0;
249 ioctl (cd_fd, CDIOCSETVOL, &v);
250 }
251 int
get_info()252 get_info ()
253 {
254 struct ioc_read_subchannel s;
255 struct cd_sub_channel_info data;
256 bzero(&s, sizeof(s));
257 s.data = &data;
258 s.data_len = sizeof (data);
259 s.address_format = CD_MSF_FORMAT;
260 s.data_format = CD_CURRENT_POSITION;
261 ioctl (cd_fd, CDIOCREADSUBCHANNEL, (char *)&s);
262 current_min = data.what.position.reladdr.msf.minute;
263 current_sec = data.what.position.reladdr.msf.second;
264 current_frame = data.what.position.reladdr.msf.frame;
265 current_track = data.what.position.track_number;
266 return data.header.audio_status;
267 }
268 void
do_update()269 do_update ()
270 {
271 struct ioc_vol v;
272 int i, vol;
273
274 if (cd_fd < 0) {
275 open_cd ();
276 if (cd_fd < 0) {
277 eject_button.set (1);
278 time_track (0, 0, 0);
279 if (animate < 0) {
280 v_animate -= 10;
281 out ("set volume %d", v_animate);
282 if (v_animate == 0)
283 animate = 1;
284 } else {
285 v_animate += 10;
286 out ("set volume %d", v_animate);
287 if (v_animate == 100)
288 animate = -1;
289 }
290 return;
291 }
292 eject_button.set (0);
293 read_toc ();
294 if (get_info () == 18)
295 time_track (current_min, current_sec, current_track);
296 else
297 time_track (-1, -1, -1);
298 ioctl (cd_fd, CDIOCGETVOL, &v);
299 vol = (v.vol[0]+v.vol[1])/2;
300 vol = linear ? int(vol/2.55) : int (exp (vol/55.253) - 1);
301 out ("set volume %d", vol);
302 }
303 if (play_button.is_set () && !pause_button.is_set () &&
304 (rewind_button.is_set () || ff_button.is_set ())) {
305 int f;
306 f = (M(current_track-1) + current_min)*60*75
307 + (S(current_track-1) + current_sec)*75
308 + F(current_track-1) + current_frame
309 + (ff_button.is_set () ? 10*75 : -10*75);
310 if ((f > M(0)*60*75 + S(0)*75 + F(0)) &&
311 (f < M(last_in_toc)*60*75 + S(last_in_toc)*75 + F(last_in_toc))) {
312 int m = f/(60*75);
313 int s = (f - m*60*75)/75;
314 f -= m*60*75 + s*75;
315 play_msf (m, s, f);
316 }
317 }
318 i = get_info ();
319 switch (i) {
320 case 17:
321 stop_button.set (0);
322 pause_button.set (0);
323 play_button.set (1);
324 if (track_btn)
325 time_track (-1, -1, -1);
326 else if (time_btn) {
327 int i = (M(current_track) - M(current_track-1)) * 75*60 +
328 (S(current_track) - S(current_track-1)) *75 +
329 F(current_track) - F(current_track-1);
330
331 time_track (i/(60*75), (i-60*75*int(i/(60*75)))/75, current_track);
332 } else
333 time_track (current_min, current_sec, current_track);
334 break;
335 case 18:
336 stop_button.set (0);
337 play_button.set (1);
338 pause_button.set (1);
339 break;
340 case 19:
341 play_button.set (0);
342 stop_button.set (1);
343 time_track (-1, -1, -1);
344 break;
345 case 20:
346 case 21:
347 play_button.set (0);
348 pause_button.set (0);
349 stop_button.set (1);
350 break;
351 }
352 }
353 void
read_toc()354 read_toc ()
355 {
356 struct ioc_toc_header h;
357 struct ioc_read_toc_entry t;
358 int n;
359
360 n = ioctl (cd_fd, CDIOREADTOCHEADER, (char *)&h);
361 n = h.ending_track - h.starting_track + 1;
362 t.address_format = CD_MSF_FORMAT;
363 t.starting_track = 1;
364 t.data_len = (n+1)*sizeof(struct cd_toc_entry);
365 t.data = toc_buffer;
366 ioctl (cd_fd, CDIOREADTOCENTRYS, (char *)&t);
367 toc_buffer[n].track = 255;
368 last_in_toc = n;
369 }
370 void
time_track(int m,int s,int t)371 time_track (int m, int s, int t)
372 {
373 if (m != -1)
374 out ("set track %02d; set time %02d:%02d", t, m, s);
375 else
376 time_track (M(last_in_toc), S(last_in_toc), last_in_toc);
377 }
378 void
play_track(int start)379 play_track (int start)
380 {
381 #if 0
382 struct ioc_play_track t;
383
384 if (start == 0 || start > last_in_toc)
385 return;
386 t.start_track = start;
387 t.start_index = 1;
388 t.end_track = last_in_toc;
389 t.end_index = last_in_toc;
390 ioctl (cd_fd, CDIOCPLAYTRACKS, &t);
391 #else
392 start--;
393 play_msf (M(start), S(start), F(start));
394 #endif
395 }
396 void
play_msf(int start_m,int start_s,int start_f)397 play_msf (int start_m, int start_s, int start_f)
398 {
399 struct ioc_play_msf a;
400 int i;
401
402 a.start_m = start_m;
403 a.start_s = start_s;
404 a.start_f = start_f;
405 i = M(last_in_toc)*60*75 + S(last_in_toc)*75 + F(last_in_toc) - 1;
406 a.end_m = i/(60*75);
407 a.end_s = (i - a.end_m*60*75)/75;
408 a.end_f = i - a.end_s*75 - a.end_m*60*75;
409 i=ioctl (cd_fd, CDIOCPLAYMSF, (char *)&a);
410 }
411 void
out(char * fmt,...)412 out (char *fmt, ...)
413 {
414 va_list ap;
415
416 va_start (ap, fmt);
417 vfprintf (stdout, fmt, ap);
418 va_end (ap);
419 putchar (';');
420 }
421 void
popup(int x,int y)422 popup (int x, int y)
423 {
424 int i, m, s, d;
425
426 if (!last_in_toc)
427 return;
428 out ("if {[winfo exists .pop]} {destroy .pop}; menu .pop");
429 for (i = 0; i < last_in_toc; i++) {
430 d = (M(i+1)-M(i))*60*75 + (S(i+1)-S(i))*75 + F(i+1)-F(i);
431 m = d/(60*75);
432 s = (d - m*60*75)/75;
433 out (".pop add command -label {%2d %02d:%02d} -command \"cmd track %d\"", i+1, m, s, -i-1);
434 }
435 out ("tk_popup .pop %d %d", x, y);
436 }
437
438