Mercurial > audlegacy
comparison Plugins/Input/cdaudio/cdaudio.c @ 61:fa848bd484d8 trunk
[svn] Move plugins to Plugins/
author | nenolod |
---|---|
date | Fri, 28 Oct 2005 22:58:11 -0700 |
parents | |
children | 86c4cd37b812 |
comparison
equal
deleted
inserted
replaced
60:1771f253e1b2 | 61:fa848bd484d8 |
---|---|
1 /* XMMS - Cross-platform multimedia player | |
2 * Copyright (C) 1998-2003 Peter Alm, Mikael Alm, Olle Hallnas, | |
3 * Thomas Nilsson and 4Front Technologies | |
4 * Copyright (C) 1999-2003 Haavard Kvaalen <havardk@xmms.org> | |
5 * | |
6 * This program is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or | |
9 * (at your option) any later version. | |
10 * | |
11 * This program is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 * GNU General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program; if not, write to the Free Software | |
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
19 */ | |
20 | |
21 #ifdef HAVE_CONFIG_H | |
22 # include "config.h" | |
23 #endif | |
24 | |
25 #include "cdaudio.h" | |
26 | |
27 #include <glib.h> | |
28 #include <glib/gi18n.h> | |
29 #include <glib/gprintf.h> | |
30 #include <string.h> | |
31 | |
32 #include <unistd.h> | |
33 #include <errno.h> | |
34 #include <fcntl.h> | |
35 #include <sys/ioctl.h> | |
36 #include <sys/stat.h> | |
37 | |
38 #include <libaudacious/configdb.h> | |
39 #include <libaudacious/util.h> | |
40 #include <libaudacious/titlestring.h> | |
41 #include "audacious/output.h" | |
42 | |
43 #ifdef CDROMSTOP | |
44 # define XMMS_STOP CDROMSTOP | |
45 #elif defined CDIOCSTOP | |
46 # define XMMS_STOP CDIOCSTOP | |
47 #else | |
48 # error "No stop ioctl" | |
49 #endif | |
50 | |
51 #ifdef CDIOCPAUSE | |
52 # define XMMS_PAUSE CDIOCPAUSE | |
53 #elif defined CDROMPAUSE | |
54 # define XMMS_PAUSE CDROMPAUSE | |
55 #else | |
56 # error "No pause ioctl" | |
57 #endif | |
58 | |
59 #ifdef CDIOCRESUME | |
60 # define XMMS_RESUME CDIOCRESUME | |
61 #elif defined CDROMRESUME | |
62 # define XMMS_RESUME CDROMRESUME | |
63 #else | |
64 # error "No resume ioctl" | |
65 #endif | |
66 | |
67 /* | |
68 * Distributions should not patch this, but instead use the | |
69 * --with-cdda-device=path and --with-cdda-dir=path configure options. | |
70 */ | |
71 | |
72 #ifndef CDDA_DEVICE | |
73 # ifdef HAVE_SYS_CDIO_H | |
74 # ifdef __FreeBSD__ | |
75 # define CDDA_DEVICE "/dev/acd0c" | |
76 # elif defined __OpenBSD__ | |
77 # define CDDA_DEVICE "/dev/cd0c" | |
78 # else | |
79 # define CDDA_DEVICE "/vol/dev/aliases/cdrom0" | |
80 # endif | |
81 # else | |
82 # define CDDA_DEVICE "/dev/cdrom" | |
83 # endif | |
84 #endif | |
85 | |
86 #ifndef CDDA_DIRECTORY | |
87 # ifdef HAVE_SYS_CDIO_H | |
88 # ifdef __FreeBSD__ | |
89 # define CDDA_DIRECTORY "/cdrom" | |
90 # elif defined __OpenBSD__ | |
91 # define CDDA_DIRECTORY "/cdrom" | |
92 # else | |
93 # define CDDA_DIRECTORY "/cdrom/cdrom0" | |
94 # endif | |
95 # else | |
96 # define CDDA_DIRECTORY "/mnt/cdrom" | |
97 # endif | |
98 #endif | |
99 | |
100 | |
101 | |
102 | |
103 static char *cdda_get_title(cdda_disc_toc_t * toc, int track); | |
104 static gboolean stop_timeout(gpointer data); | |
105 | |
106 static void cdda_init(void); | |
107 static int is_our_file(char *filename); | |
108 static GList *scan_dir(char *dir); | |
109 static void play_file(char *filename); | |
110 static void stop(void); | |
111 static void cdda_pause(short p); | |
112 static void seek(int time); | |
113 static int get_time(void); | |
114 static void get_song_info(char *filename, char **title, int *length); | |
115 static void get_volume(int *l, int *r); | |
116 static void set_volume(int l, int r); | |
117 static void cleanup(void); | |
118 void cdda_fileinfo(char *filename); | |
119 | |
120 InputPlugin cdda_ip = { | |
121 NULL, | |
122 NULL, | |
123 NULL, /* Description */ | |
124 cdda_init, | |
125 NULL, /* about */ | |
126 cdda_configure, | |
127 is_our_file, | |
128 scan_dir, | |
129 play_file, | |
130 stop, | |
131 cdda_pause, | |
132 seek, | |
133 NULL, /* set_eq */ | |
134 get_time, | |
135 get_volume, | |
136 set_volume, | |
137 cleanup, | |
138 NULL, /* obsolete */ | |
139 NULL, /* add_vis_pcm */ | |
140 NULL, /* set_info, filled in by xmms */ | |
141 NULL, /* set_info_text, filled in by xmms */ | |
142 get_song_info, | |
143 NULL, /* cdda_fileinfo, *//* file_info_box */ | |
144 NULL /* output plugin handle */ | |
145 }; | |
146 | |
147 CDDAConfig cdda_cfg; | |
148 | |
149 static struct { | |
150 struct driveinfo drive; | |
151 cdda_disc_toc_t cd_toc; | |
152 int track; | |
153 int fd; | |
154 gboolean playing; | |
155 } cdda_playing; | |
156 | |
157 static struct { | |
158 GThread *thread; | |
159 gboolean audio_error, eof; | |
160 int seek; | |
161 | |
162 } dae_data; | |
163 | |
164 static gboolean is_paused; | |
165 static int pause_time; | |
166 | |
167 struct timeout { | |
168 int id; | |
169 char *device; | |
170 }; | |
171 | |
172 static GList *timeout_list; | |
173 | |
174 /* Time to delay stop command in 1/10 second */ | |
175 #define STOP_DELAY 20 | |
176 | |
177 InputPlugin * | |
178 get_iplugin_info(void) | |
179 { | |
180 cdda_ip.description = g_strdup_printf(_("CD Audio Plugin")); | |
181 return &cdda_ip; | |
182 } | |
183 | |
184 | |
185 | |
186 #ifdef BEEP_CDROM_SOLARIS | |
187 /* | |
188 * Lowlevel cdrom access, Solaris style (Solaris, Linux) | |
189 */ | |
190 | |
191 static void | |
192 play_ioctl(struct cdda_msf *start, struct cdda_msf *end) | |
193 { | |
194 struct cdrom_msf msf; | |
195 | |
196 msf.cdmsf_min0 = start->minute; | |
197 msf.cdmsf_sec0 = start->second; | |
198 msf.cdmsf_frame0 = start->frame; | |
199 msf.cdmsf_min1 = end->minute; | |
200 msf.cdmsf_sec1 = end->second; | |
201 msf.cdmsf_frame1 = end->frame; | |
202 ioctl(cdda_playing.fd, CDROMPLAYMSF, &msf); | |
203 } | |
204 | |
205 static int | |
206 get_current_frame(void) | |
207 { | |
208 struct cdrom_subchnl subchnl; | |
209 | |
210 subchnl.cdsc_format = CDROM_MSF; | |
211 if (ioctl(cdda_playing.fd, CDROMSUBCHNL, &subchnl) < 0) | |
212 return -1; | |
213 | |
214 switch (subchnl.cdsc_audiostatus) { | |
215 case CDROM_AUDIO_COMPLETED: | |
216 case CDROM_AUDIO_ERROR: | |
217 return -1; | |
218 } | |
219 | |
220 return (LBA(subchnl.cdsc_absaddr.msf)); | |
221 } | |
222 | |
223 #if !defined(CDROMVOLREAD) | |
224 static int volume_left = 100, volume_right = 100; | |
225 #endif | |
226 | |
227 static void | |
228 drive_get_volume(int *l, int *r) | |
229 { | |
230 #if defined(CDROMVOLREAD) | |
231 struct cdrom_volctrl vol; | |
232 | |
233 if (cdda_playing.fd != -1 && !ioctl(cdda_playing.fd, CDROMVOLREAD, &vol)) { | |
234 *l = (100 * vol.channel0) / 255; | |
235 *r = (100 * vol.channel1) / 255; | |
236 } | |
237 #if 0 | |
238 else if (cdda_playing.fd != -1) | |
239 g_message("CDROMVOLREAD failed"); | |
240 #endif | |
241 #else | |
242 *l = volume_left; | |
243 *r = volume_right; | |
244 #endif | |
245 } | |
246 | |
247 static void | |
248 drive_set_volume(int l, int r) | |
249 { | |
250 struct cdrom_volctrl vol; | |
251 | |
252 if (cdda_playing.fd != -1) { | |
253 vol.channel0 = vol.channel2 = (l * 255) / 100; | |
254 vol.channel1 = vol.channel3 = (r * 255) / 100; | |
255 ioctl(cdda_playing.fd, CDROMVOLCTRL, &vol); | |
256 } | |
257 #if !defined(CDROMVOLREAD) | |
258 volume_left = l; | |
259 volume_right = r; | |
260 #endif | |
261 } | |
262 | |
263 #ifdef CDROMREADAUDIO | |
264 int | |
265 read_audio_data(int fd, int pos, int num, void *buf) | |
266 { | |
267 struct cdrom_read_audio cdra; | |
268 | |
269 #if 1 | |
270 cdra.addr.lba = pos - CDDA_MSF_OFFSET; | |
271 cdra.addr_format = CDROM_LBA; | |
272 #else | |
273 cdra.addr.msf.minute = pos / (60 * 75); | |
274 cdra.addr.msf.second = (pos / 75) % 60; | |
275 cdra.addr.msf.frame = pos % 75; | |
276 cdra.addr_format = CDROM_MSF; | |
277 #endif | |
278 | |
279 cdra.nframes = num; | |
280 cdra.buf = buf; | |
281 | |
282 if (ioctl(fd, CDROMREADAUDIO, &cdra) < 0) | |
283 return -errno; | |
284 | |
285 return cdra.nframes; | |
286 } | |
287 #endif /* CDROMREADAUDIO */ | |
288 | |
289 #if defined(CDROMCDDA) | |
290 int | |
291 read_audio_data(int fd, int pos, int num, void *buf) | |
292 { | |
293 struct cdrom_cdda cdra; | |
294 | |
295 cdra.cdda_addr = pos - CDDA_MSF_OFFSET; | |
296 cdra.cdda_length = num; | |
297 cdra.cdda_data = buf; | |
298 cdra.cdda_subcode = CDROM_DA_NO_SUBCODE; | |
299 if (ioctl(fd, CDROMCDDA, &cdra) < 0) | |
300 return -errno; | |
301 | |
302 return cdra.cdda_length; | |
303 } | |
304 #endif | |
305 | |
306 static gboolean | |
307 cdda_get_toc_lowlevel(int fd, cdda_disc_toc_t * info) | |
308 { | |
309 struct cdrom_tochdr tochdr; | |
310 struct cdrom_tocentry tocentry; | |
311 int i; | |
312 | |
313 | |
314 | |
315 if (ioctl(fd, CDROMREADTOCHDR, &tochdr)) | |
316 return FALSE; | |
317 | |
318 for (i = tochdr.cdth_trk0; i <= tochdr.cdth_trk1; i++) { | |
319 tocentry.cdte_format = CDROM_MSF; | |
320 tocentry.cdte_track = i; | |
321 if (ioctl(fd, CDROMREADTOCENTRY, &tocentry)) | |
322 return FALSE; | |
323 info->track[i].minute = tocentry.cdte_addr.msf.minute; | |
324 info->track[i].second = tocentry.cdte_addr.msf.second; | |
325 info->track[i].frame = tocentry.cdte_addr.msf.frame; | |
326 info->track[i].flags.data_track = | |
327 tocentry.cdte_ctrl == CDROM_DATA_TRACK; | |
328 | |
329 } | |
330 | |
331 /* Get the leadout track */ | |
332 tocentry.cdte_track = CDROM_LEADOUT; | |
333 tocentry.cdte_format = CDROM_MSF; | |
334 | |
335 if (ioctl(fd, CDROMREADTOCENTRY, &tocentry)) | |
336 return FALSE; | |
337 info->leadout.minute = tocentry.cdte_addr.msf.minute; | |
338 info->leadout.second = tocentry.cdte_addr.msf.second; | |
339 info->leadout.frame = tocentry.cdte_addr.msf.frame; | |
340 | |
341 info->first_track = tochdr.cdth_trk0; | |
342 info->last_track = tochdr.cdth_trk1; | |
343 | |
344 return TRUE; | |
345 } | |
346 | |
347 #endif | |
348 | |
349 #ifdef BEEP_CDROM_BSD | |
350 /* | |
351 * Lowlevel cdrom access, BSD style (FreeBSD, OpenBSD, NetBSD, Darwin) | |
352 */ | |
353 | |
354 static void | |
355 play_ioctl(struct cdda_msf *start, struct cdda_msf *end) | |
356 { | |
357 struct ioc_play_msf msf; | |
358 | |
359 msf.start_m = start->minute; | |
360 msf.start_s = start->second; | |
361 msf.start_f = start->frame; | |
362 msf.end_m = end->minute; | |
363 msf.end_s = end->second; | |
364 msf.end_f = end->frame; | |
365 ioctl(cdda_playing.fd, CDIOCPLAYMSF, &msf); | |
366 } | |
367 | |
368 static int | |
369 get_current_frame(void) | |
370 { | |
371 struct ioc_read_subchannel subchnl; | |
372 struct cd_sub_channel_info subinfo; | |
373 subchnl.address_format = CD_MSF_FORMAT; | |
374 subchnl.data_format = CD_CURRENT_POSITION; | |
375 subchnl.track = 0; | |
376 subchnl.data_len = sizeof(subinfo); | |
377 subchnl.data = &subinfo; | |
378 if (ioctl(cdda_playing.fd, CDIOCREADSUBCHANNEL, &subchnl) < 0) | |
379 return -1; | |
380 | |
381 #ifdef BEEP_CDROM_BSD_DARWIN | |
382 return ((subchnl.data->what.position.absaddr[1] * 60 | |
383 subchnl.data->what.position.absaddr[2]) * 75 + | |
384 subchnl.data->what.position.absaddr[3]); | |
385 #else | |
386 return (LBA(subchnl.data->what.position.absaddr.msf)); | |
387 #endif | |
388 } | |
389 | |
390 static void | |
391 drive_get_volume(int *l, int *r) | |
392 { | |
393 struct ioc_vol vol; | |
394 | |
395 if (cdda_playing.fd != -1) { | |
396 ioctl(cdda_playing.fd, CDIOCGETVOL, &vol); | |
397 *l = (100 * vol.vol[0]) / 255; | |
398 *r = (100 * vol.vol[1]) / 255; | |
399 } | |
400 } | |
401 | |
402 static void | |
403 drive_set_volume(int l, int r) | |
404 { | |
405 struct ioc_vol vol; | |
406 | |
407 if (cdda_playing.fd != -1) { | |
408 vol.vol[0] = vol.vol[2] = (l * 255) / 100; | |
409 vol.vol[1] = vol.vol[3] = (r * 255) / 100; | |
410 ioctl(cdda_playing.fd, CDIOCSETVOL, &vol); | |
411 } | |
412 } | |
413 | |
414 | |
415 #if defined(CDIOCREADAUDIO) | |
416 #ifdef __FreeBSD__ | |
417 int | |
418 read_audio_data(int fd, int pos, int num, void *buf) | |
419 { | |
420 struct ioc_read_audio cdra; | |
421 | |
422 cdra.address.lba = pos - CDDA_MSF_OFFSET; | |
423 cdra.address_format = CD_LBA_FORMAT; | |
424 cdra.nframes = num; | |
425 cdra.buffer = buf; | |
426 | |
427 if (ioctl(fd, CDIOCREADAUDIO, &cdra) < 0) | |
428 return -errno; | |
429 | |
430 return cdra.nframes; | |
431 } | |
432 #else | |
433 #error Please test on other <sys/cdio.h> platforms. | |
434 #endif | |
435 #endif /* CDIOCREADAUDIO */ | |
436 | |
437 #ifdef BEEP_CDROM_BSD_NETBSD /* NetBSD, OpenBSD */ | |
438 | |
439 static gboolean | |
440 cdda_get_toc_lowlevel(int fd, cdda_disc_toc_t * info) | |
441 { | |
442 struct ioc_toc_header tochdr; | |
443 struct ioc_read_toc_entry tocentry; | |
444 struct cd_toc_entry tocentrydata; | |
445 int i; | |
446 | |
447 if (ioctl(fd, CDIOREADTOCHEADER, &tochdr)) | |
448 return FALSE; | |
449 | |
450 for (i = tochdr.starting_track; i <= tochdr.ending_track; i++) { | |
451 tocentry.address_format = CD_MSF_FORMAT; | |
452 | |
453 tocentry.starting_track = i; | |
454 tocentry.data = &tocentrydata; | |
455 tocentry.data_len = sizeof(tocentrydata); | |
456 if (ioctl(fd, CDIOREADTOCENTRYS, &tocentry)) | |
457 return FALSE; | |
458 info->track[i].minute = tocentry.data->addr.msf.minute; | |
459 info->track[i].second = tocentry.data->addr.msf.second; | |
460 info->track[i].frame = tocentry.data->addr.msf.frame; | |
461 info->track[i].flags.data_track = (tocentry.data->control & 4) == 4; | |
462 } | |
463 | |
464 /* Get the leadout track */ | |
465 tocentry.address_format = CD_MSF_FORMAT; | |
466 | |
467 tocentry.starting_track = 0xAA; | |
468 tocentry.data = &tocentrydata; | |
469 tocentry.data_len = sizeof(tocentrydata); | |
470 if (ioctl(fd, CDIOREADTOCENTRYS, &tocentry)) | |
471 return FALSE; | |
472 info->leadout.minute = tocentry.data->addr.msf.minute; | |
473 info->leadout.second = tocentry.data->addr.msf.second; | |
474 info->leadout.frame = tocentry.data->addr.msf.frame; | |
475 | |
476 info->first_track = tochdr.starting_track; | |
477 info->last_track = tochdr.ending_track; | |
478 | |
479 return TRUE; | |
480 } | |
481 | |
482 #elif defined(BEEP_CDROM_BSD_DARWIN) | |
483 | |
484 static gboolean | |
485 cdda_get_toc_lowlevel(int fd, cdda_disc_toc_t * info) | |
486 { | |
487 struct ioc_toc_header tochdr; | |
488 struct ioc_read_toc_entry tocentry; | |
489 int i; | |
490 | |
491 if (ioctl(fd, CDIOREADTOCHEADER, &tochdr)) | |
492 return FALSE; | |
493 | |
494 for (i = tochdr.starting_track; i <= tochdr.ending_track; i++) { | |
495 tocentry.address_format = CD_MSF_FORMAT; | |
496 | |
497 tocentry.starting_track = i; | |
498 if (ioctl(fd, CDIOREADTOCENTRYS, &tocentry)) | |
499 return FALSE; | |
500 info->track[i].minute = tocentry.data->addr[1]; | |
501 info->track[i].second = tocentry.data->addr[2]; | |
502 info->track[i].frame = tocentry.data->addr[3]; | |
503 info->track[i].flags.data_track = (tocentry.data->control & 4) == 4; | |
504 } | |
505 | |
506 /* Get the leadout track */ | |
507 tocentry.address_format = CD_MSF_FORMAT; | |
508 | |
509 tocentry.starting_track = 0xAA; | |
510 if (ioctl(fd, CDIOREADTOCENTRYS, &tocentry)) | |
511 return FALSE; | |
512 info->leadout.minute = tocentry.data->addr[1]; | |
513 info->leadout.second = tocentry.data->addr[2]; | |
514 info->leadout.frame = tocentry.data->addr[3]; | |
515 | |
516 return TRUE; | |
517 } | |
518 | |
519 #else /* FreeBSD */ | |
520 | |
521 static gboolean | |
522 cdda_get_toc_lowlevel(int fd, cdda_disc_toc_t * info) | |
523 { | |
524 struct ioc_toc_header tochdr; | |
525 struct ioc_read_toc_single_entry tocentry; | |
526 int i; | |
527 | |
528 if (ioctl(fd, CDIOREADTOCHEADER, &tochdr)) | |
529 return FALSE; | |
530 | |
531 for (i = tochdr.starting_track; i <= tochdr.ending_track; i++) { | |
532 tocentry.address_format = CD_MSF_FORMAT; | |
533 | |
534 tocentry.track = i; | |
535 if (ioctl(fd, CDIOREADTOCENTRY, &tocentry)) | |
536 return FALSE; | |
537 info->track[i].minute = tocentry.entry.addr.msf.minute; | |
538 info->track[i].second = tocentry.entry.addr.msf.second; | |
539 info->track[i].frame = tocentry.entry.addr.msf.frame; | |
540 info->track[i].flags.data_track = (tocentry.entry.control & 4) == 4; | |
541 } | |
542 | |
543 /* Get the leadout track */ | |
544 tocentry.address_format = CD_MSF_FORMAT; | |
545 | |
546 tocentry.track = 0xAA; | |
547 if (ioctl(fd, CDIOREADTOCENTRY, &tocentry)) | |
548 return FALSE; | |
549 info->leadout.minute = tocentry.entry.addr.msf.minute; | |
550 info->leadout.second = tocentry.entry.addr.msf.second; | |
551 info->leadout.frame = tocentry.entry.addr.msf.frame; | |
552 | |
553 info->first_track = tochdr.starting_track; | |
554 info->last_track = tochdr.ending_track; | |
555 | |
556 return TRUE; | |
557 } | |
558 #endif | |
559 | |
560 #endif | |
561 | |
562 | |
563 | |
564 | |
565 | |
566 | |
567 | |
568 | |
569 | |
570 | |
571 | |
572 extern gboolean | |
573 is_mounted(const char *device_name) | |
574 { | |
575 #if defined(HAVE_MNTENT_H) || defined(HAVE_GETMNTINFO) | |
576 char devname[256]; | |
577 struct stat st; | |
578 #if defined(HAVE_MNTENT_H) | |
579 FILE *mounts; | |
580 struct mntent *mnt; | |
581 #elif defined(HAVE_GETMNTINFO) | |
582 struct statfs *fsp; | |
583 int entries; | |
584 #endif | |
585 | |
586 if (lstat(device_name, &st) < 0) | |
587 return -1; | |
588 | |
589 if (S_ISLNK(st.st_mode)) | |
590 readlink(device_name, devname, 256); | |
591 else | |
592 strncpy(devname, device_name, 256); | |
593 | |
594 #if defined(HAVE_MNTENT_H) | |
595 if ((mounts = setmntent(MOUNTED, "r")) == NULL) | |
596 return TRUE; | |
597 | |
598 while ((mnt = getmntent(mounts)) != NULL) { | |
599 if (strcmp(mnt->mnt_fsname, devname) == 0) { | |
600 endmntent(mounts); | |
601 return TRUE; | |
602 } | |
603 } | |
604 endmntent(mounts); | |
605 #elif defined(HAVE_GETMNTINFO) | |
606 entries = getmntinfo(&fsp, MNT_NOWAIT); | |
607 if (entries < 0) | |
608 return NULL; | |
609 | |
610 while (entries-- > 0) { | |
611 if (!strcmp(fsp->f_mntfromname, devname)) | |
612 return TRUE; | |
613 fsp++; | |
614 } | |
615 #endif | |
616 #endif | |
617 return FALSE; | |
618 } | |
619 | |
620 | |
621 gboolean | |
622 cdda_get_toc(cdda_disc_toc_t * info, const char *device) | |
623 { | |
624 gboolean retv = FALSE; | |
625 int fd; | |
626 | |
627 if (is_mounted(device)) | |
628 return FALSE; | |
629 | |
630 if ((fd = open(device, CDOPENFLAGS)) == -1) | |
631 return FALSE; | |
632 | |
633 memset(info, 0, sizeof(cdda_disc_toc_t)); | |
634 | |
635 retv = cdda_get_toc_lowlevel(fd, info); | |
636 close(fd); | |
637 | |
638 return retv; | |
639 } | |
640 | |
641 static void | |
642 cdda_init(void) | |
643 { | |
644 ConfigDb *db; | |
645 struct driveinfo *drive = g_malloc0(sizeof(struct driveinfo)); | |
646 int ndrives = 1, i; | |
647 | |
648 cdda_playing.fd = -1; | |
649 memset(&cdda_cfg, 0, sizeof(CDDAConfig)); | |
650 | |
651 #ifdef HAVE_OSS | |
652 drive->mixer = CDDA_MIXER_OSS; | |
653 drive->oss_mixer = SOUND_MIXER_CD; | |
654 #endif | |
655 | |
656 db = bmp_cfg_db_open(); | |
657 | |
658 /* These names are used for backwards compatibility */ | |
659 bmp_cfg_db_get_string(db, "CDDA", "device", &drive->device); | |
660 bmp_cfg_db_get_string(db, "CDDA", "directory", &drive->directory); | |
661 bmp_cfg_db_get_int(db, "CDDA", "mixer", &drive->mixer); | |
662 bmp_cfg_db_get_int(db, "CDDA", "readmode", &drive->dae); | |
663 | |
664 if (!drive->device) | |
665 drive->device = g_strdup(CDDA_DEVICE); | |
666 if (!drive->directory) | |
667 drive->directory = g_strdup(CDDA_DIRECTORY); | |
668 | |
669 cdda_cfg.drives = g_list_append(cdda_cfg.drives, drive); | |
670 | |
671 bmp_cfg_db_get_int(db, "CDDA", "num_drives", &ndrives); | |
672 for (i = 1; i < ndrives; i++) { | |
673 char label[20]; | |
674 drive = g_malloc0(sizeof(struct driveinfo)); | |
675 | |
676 sprintf(label, "device%d", i); | |
677 bmp_cfg_db_get_string(db, "CDDA", label, &drive->device); | |
678 | |
679 sprintf(label, "directory%d", i); | |
680 bmp_cfg_db_get_string(db, "CDDA", label, &drive->directory); | |
681 | |
682 sprintf(label, "mixer%d", i); | |
683 bmp_cfg_db_get_int(db, "CDDA", label, &drive->mixer); | |
684 | |
685 sprintf(label, "readmode%d", i); | |
686 bmp_cfg_db_get_int(db, "CDDA", label, &drive->dae); | |
687 | |
688 cdda_cfg.drives = g_list_append(cdda_cfg.drives, drive); | |
689 } | |
690 bmp_cfg_db_get_bool(db, "CDDA", "title_override", | |
691 &cdda_cfg.title_override); | |
692 bmp_cfg_db_get_string(db, "CDDA", "name_format", &cdda_cfg.name_format); | |
693 bmp_cfg_db_get_bool(db, "CDDA", "use_cddb", &cdda_cfg.use_cddb); | |
694 bmp_cfg_db_get_string(db, "CDDA", "cddb_server", &cdda_cfg.cddb_server); | |
695 #ifdef WITH_CDINDEX | |
696 bmp_cfg_db_get_bool(db, "CDDA", "use_cdin", &cdda_cfg.use_cdin); | |
697 #else | |
698 cdda_cfg.use_cdin = FALSE; | |
699 #endif | |
700 bmp_cfg_db_get_string(db, "CDDA", "cdin_server", &cdda_cfg.cdin_server); | |
701 bmp_cfg_db_close(db); | |
702 | |
703 if (!cdda_cfg.cdin_server) | |
704 cdda_cfg.cdin_server = g_strdup("www.cdindex.org"); | |
705 if (!cdda_cfg.cddb_server) | |
706 cdda_cfg.cddb_server = g_strdup(CDDB_DEFAULT_SERVER); | |
707 if (!cdda_cfg.name_format) | |
708 cdda_cfg.name_format = g_strdup("%p - %t"); | |
709 } | |
710 | |
711 struct driveinfo * | |
712 cdda_find_drive(char *filename) | |
713 { | |
714 GList *node; | |
715 | |
716 // FIXME: Will always return the first drive | |
717 | |
718 for (node = cdda_cfg.drives; node; node = node->next) { | |
719 struct driveinfo *d = node->data; | |
720 if (!strncmp(d->directory, filename, strlen(d->directory))) | |
721 return d; | |
722 } | |
723 | |
724 return NULL; | |
725 | |
726 } | |
727 | |
728 static void | |
729 timeout_destroy(struct timeout *entry) | |
730 { | |
731 g_free(entry->device); | |
732 g_free(entry); | |
733 timeout_list = g_list_remove(timeout_list, entry); | |
734 } | |
735 | |
736 static void | |
737 timeout_remove_for_device(char *device) | |
738 { | |
739 GList *node; | |
740 | |
741 for (node = timeout_list; node; node = node->next) { | |
742 struct timeout *t = node->data; | |
743 | |
744 if (!strcmp(t->device, device)) { | |
745 gtk_timeout_remove(t->id); | |
746 timeout_destroy(t); | |
747 return; | |
748 } | |
749 } | |
750 | |
751 } | |
752 | |
753 static void | |
754 cleanup(void) | |
755 { | |
756 while (timeout_list) { | |
757 struct timeout *t = timeout_list->data; | |
758 gtk_timeout_remove(t->id); | |
759 stop_timeout(t); | |
760 timeout_destroy(t); | |
761 } | |
762 cddb_quit(); | |
763 } | |
764 | |
765 static int | |
766 is_our_file(char *filename) | |
767 { | |
768 char *ext = ".cda"; | |
769 | |
770 if (cdda_find_drive(filename) == NULL) { | |
771 return FALSE; | |
772 } | |
773 | |
774 if (g_str_has_suffix(filename, ext)) { | |
775 return TRUE; | |
776 } | |
777 return FALSE; | |
778 } | |
779 | |
780 | |
781 static GList * | |
782 scan_dir(char *dir) | |
783 { | |
784 GList *list = NULL; | |
785 int i; | |
786 cdda_disc_toc_t toc; | |
787 struct driveinfo *drive; | |
788 | |
789 if ((drive = cdda_find_drive(dir)) == NULL) | |
790 return NULL; | |
791 | |
792 if (!cdda_get_toc(&toc, drive->device)) | |
793 return NULL; | |
794 | |
795 for (i = toc.last_track; i >= toc.first_track; i--) | |
796 if (!toc.track[i].flags.data_track) { | |
797 list = g_list_prepend(list, g_strdup_printf("Track %02d.cda", i)); | |
798 } | |
799 return list; | |
800 } | |
801 | |
802 guint | |
803 cdda_calculate_track_length(cdda_disc_toc_t * toc, int track) | |
804 { | |
805 if (track == toc->last_track) | |
806 return (LBA(toc->leadout) - LBA(toc->track[track])); | |
807 else | |
808 return (LBA(toc->track[track + 1]) - LBA(toc->track[track])); | |
809 } | |
810 | |
811 static void * | |
812 dae_play_loop(void *arg) | |
813 { | |
814 char *buffer = g_malloc(CD_FRAMESIZE_RAW * CDDA_DAE_FRAMES); | |
815 int pos = LBA(cdda_playing.cd_toc.track[cdda_playing.track]); | |
816 int end, frames; | |
817 | |
818 if (cdda_playing.track == cdda_playing.cd_toc.last_track) | |
819 end = LBA(cdda_playing.cd_toc.leadout); | |
820 else | |
821 end = LBA(cdda_playing.cd_toc.track[cdda_playing.track + 1]); | |
822 | |
823 while (cdda_playing.playing) { | |
824 int left; | |
825 char *data; | |
826 | |
827 if (dae_data.seek != -1) { | |
828 cdda_ip.output->flush(dae_data.seek * 1000); | |
829 pos = LBA(cdda_playing.cd_toc.track[cdda_playing.track]) | |
830 + dae_data.seek * 75; | |
831 dae_data.seek = -1; | |
832 dae_data.eof = FALSE; | |
833 } | |
834 frames = MIN(CDDA_DAE_FRAMES, end - pos); | |
835 if (frames == 0) | |
836 dae_data.eof = TRUE; | |
837 | |
838 if (dae_data.eof) { | |
839 xmms_usleep(30000); | |
840 continue; | |
841 } | |
842 | |
843 frames = read_audio_data(cdda_playing.fd, pos, frames, buffer); | |
844 if (frames <= 0) { | |
845 int err = -frames; | |
846 if (err == EOPNOTSUPP) | |
847 dae_data.eof = TRUE; | |
848 else { | |
849 /* | |
850 * If the read failed, skip ahead to | |
851 * avoid getting stuck on scratches | |
852 * and such. | |
853 */ | |
854 g_message("read_audio_data() failed: %s (%d)", | |
855 strerror(err), err); | |
856 pos += MIN(CDDA_DAE_FRAMES, end - pos); | |
857 } | |
858 continue; | |
859 } | |
860 left = frames * CD_FRAMESIZE_RAW; | |
861 data = buffer; | |
862 while (cdda_playing.playing && left > 0 && dae_data.seek == -1) { | |
863 int cur = MIN(512 * 2 * 2, left); | |
864 cdda_ip.add_vis_pcm(cdda_ip.output->written_time(), | |
865 FMT_S16_LE, 2, cur, data); | |
866 while (cdda_ip.output->buffer_free() < cur && | |
867 cdda_playing.playing && dae_data.seek == -1) | |
868 xmms_usleep(30000); | |
869 if (cdda_playing.playing && dae_data.seek == -1) | |
870 produce_audio(cdda_ip.output->written_time(), FMT_S16_LE, 2, cur, data, &cdda_playing.playing); | |
871 left -= cur; | |
872 data += cur; | |
873 } | |
874 pos += frames; | |
875 } | |
876 | |
877 cdda_ip.output->buffer_free(); | |
878 cdda_ip.output->buffer_free(); | |
879 g_free(buffer); | |
880 | |
881 g_thread_exit(NULL); | |
882 return NULL; | |
883 } | |
884 | |
885 static void | |
886 dae_play(void) | |
887 { | |
888 if (cdda_ip.output->open_audio(FMT_S16_LE, 44100, 2) == 0) { | |
889 dae_data.audio_error = TRUE; | |
890 cdda_playing.playing = FALSE; | |
891 return; | |
892 } | |
893 dae_data.seek = -1; | |
894 dae_data.eof = FALSE; | |
895 dae_data.audio_error = FALSE; | |
896 dae_data.thread = g_thread_create(dae_play_loop, NULL, TRUE, NULL); | |
897 } | |
898 | |
899 static void | |
900 play_file(char *filename) | |
901 { | |
902 char *tmp; | |
903 struct driveinfo *drive; | |
904 int track; | |
905 int track_len; | |
906 | |
907 // g_message(g_strdup_printf("** CD_AUDIO: trying to play file %s",filename)); | |
908 | |
909 if ((drive = cdda_find_drive(filename)) == NULL) { | |
910 // g_message("** CD_AUDIO: find drive check failed"); | |
911 return; | |
912 } | |
913 if (is_mounted(drive->device)) { | |
914 // g_message("** CD_AUDIO: drive is mounted"); | |
915 return; | |
916 } | |
917 tmp = strrchr(filename, '/'); | |
918 if (tmp) | |
919 tmp++; | |
920 else | |
921 tmp = filename; | |
922 | |
923 if (!sscanf(tmp, "Track %d.cda", &track)) { | |
924 // g_message("** CD_AUDIO: filename check failed"); | |
925 return; | |
926 } | |
927 | |
928 if (!cdda_get_toc(&cdda_playing.cd_toc, drive->device) || | |
929 cdda_playing.cd_toc.track[track].flags.data_track || | |
930 track < cdda_playing.cd_toc.first_track || | |
931 track > cdda_playing.cd_toc.last_track) { | |
932 // g_message("** CD_AUDIO: toc check failed"); | |
933 return; | |
934 } | |
935 | |
936 if ((cdda_playing.fd = open(drive->device, CDOPENFLAGS)) == -1) { | |
937 // g_message("** CD_AUDIO: device open failed"); | |
938 return; | |
939 } | |
940 track_len = cdda_calculate_track_length(&cdda_playing.cd_toc, track); | |
941 cdda_ip.set_info(cdda_get_title(&cdda_playing.cd_toc, track), | |
942 (track_len * 1000) / 75, 44100 * 2 * 2 * 8, 44100, 2); | |
943 | |
944 memcpy(&cdda_playing.drive, drive, sizeof(struct driveinfo)); | |
945 #ifndef CDDA_HAS_READAUDIO | |
946 cdda_playing.drive.dae = FALSE; | |
947 #endif | |
948 | |
949 cdda_playing.track = track; | |
950 | |
951 is_paused = FALSE; | |
952 timeout_remove_for_device(drive->device); | |
953 | |
954 cdda_playing.playing = TRUE; | |
955 if (drive->dae) | |
956 dae_play(); | |
957 else | |
958 seek(0); | |
959 } | |
960 | |
961 static char * | |
962 cdda_get_title(cdda_disc_toc_t * toc, int track) | |
963 { | |
964 G_LOCK_DEFINE_STATIC(title); | |
965 | |
966 static guint32 cached_id; | |
967 static cdinfo_t cdinfo; | |
968 TitleInput *input; | |
969 guint32 disc_id; | |
970 char *title; | |
971 | |
972 disc_id = cdda_cddb_compute_discid(toc); | |
973 | |
974 /* | |
975 * We want to avoid looking up a album from two threads simultaneously. | |
976 * This can happen since we are called both from the main-thread and | |
977 * from the playlist-thread. | |
978 */ | |
979 | |
980 G_LOCK(title); | |
981 if (!(disc_id == cached_id && cdinfo.is_valid)) { | |
982 /* | |
983 * We try to look up the disc again if the info is not | |
984 * valid. The user might have configured a new server | |
985 * in the meantime. | |
986 */ | |
987 cdda_cdinfo_flush(&cdinfo); | |
988 cached_id = disc_id; | |
989 | |
990 if (!cdda_cdinfo_read_file(disc_id, &cdinfo)) { | |
991 if (cdda_cfg.use_cddb) | |
992 cdda_cddb_get_info(toc, &cdinfo); | |
993 if (cdinfo.is_valid) | |
994 cdda_cdinfo_write_file(disc_id, &cdinfo); | |
995 } | |
996 } | |
997 XMMS_NEW_TITLEINPUT(input); | |
998 cdda_cdinfo_get(&cdinfo, track, &input->performer, &input->album_name, | |
999 &input->track_name); | |
1000 G_UNLOCK(title); | |
1001 | |
1002 input->track_number = track; | |
1003 input->file_name = input->file_path = | |
1004 g_strdup_printf(_("CD Audio Track %02u"), track); | |
1005 input->file_ext = "cda"; | |
1006 title = xmms_get_titlestring(cdda_cfg.title_override ? | |
1007 cdda_cfg.name_format : | |
1008 xmms_get_gentitle_format(), input); | |
1009 g_free(input->file_name); | |
1010 g_free(input); | |
1011 | |
1012 if (!title) | |
1013 title = g_strdup_printf(_("CD Audio Track %02u"), track); | |
1014 return title; | |
1015 } | |
1016 | |
1017 static gboolean | |
1018 stop_timeout(gpointer data) | |
1019 { | |
1020 int fd; | |
1021 struct timeout *to = data; | |
1022 | |
1023 fd = open(to->device, CDOPENFLAGS); | |
1024 if (fd != -1) { | |
1025 ioctl(fd, XMMS_STOP, 0); | |
1026 close(fd); | |
1027 } | |
1028 timeout_destroy(to); | |
1029 return FALSE; | |
1030 } | |
1031 | |
1032 static void | |
1033 stop(void) | |
1034 { | |
1035 struct timeout *to_info; | |
1036 if (cdda_playing.fd < 0) | |
1037 return; | |
1038 | |
1039 cdda_playing.playing = FALSE; | |
1040 | |
1041 if (cdda_playing.drive.dae) { | |
1042 g_thread_join(dae_data.thread); | |
1043 cdda_ip.output->close_audio(); | |
1044 } | |
1045 else | |
1046 ioctl(cdda_playing.fd, XMMS_PAUSE, 0); | |
1047 | |
1048 close(cdda_playing.fd); | |
1049 cdda_playing.fd = -1; | |
1050 | |
1051 if (!cdda_playing.drive.dae) { | |
1052 to_info = g_malloc(sizeof(*to_info)); | |
1053 to_info->device = g_strdup(cdda_playing.drive.device); | |
1054 to_info->id = gtk_timeout_add(STOP_DELAY * 100, stop_timeout, | |
1055 to_info); | |
1056 timeout_list = g_list_prepend(timeout_list, to_info); | |
1057 } | |
1058 } | |
1059 | |
1060 static void | |
1061 cdda_pause(short p) | |
1062 { | |
1063 if (cdda_playing.drive.dae) { | |
1064 cdda_ip.output->pause(p); | |
1065 return; | |
1066 } | |
1067 if (p) { | |
1068 pause_time = get_time(); | |
1069 ioctl(cdda_playing.fd, XMMS_PAUSE, 0); | |
1070 } | |
1071 else { | |
1072 ioctl(cdda_playing.fd, XMMS_RESUME, 0); | |
1073 pause_time = -1; | |
1074 } | |
1075 is_paused = p; | |
1076 } | |
1077 | |
1078 | |
1079 | |
1080 static void | |
1081 seek(int time) | |
1082 { | |
1083 struct cdda_msf *end, start; | |
1084 int track = cdda_playing.track; | |
1085 | |
1086 // g_message("** CD_AUDIO: seeking..."); | |
1087 if (cdda_playing.drive.dae) { | |
1088 dae_data.seek = time; | |
1089 while (dae_data.seek != -1) | |
1090 xmms_usleep(20000); | |
1091 return; | |
1092 } | |
1093 | |
1094 start.minute = (cdda_playing.cd_toc.track[track].minute * 60 + | |
1095 cdda_playing.cd_toc.track[track].second + time) / 60; | |
1096 start.second = (cdda_playing.cd_toc.track[track].second + time) % 60; | |
1097 start.frame = cdda_playing.cd_toc.track[track].frame; | |
1098 if (track == cdda_playing.cd_toc.last_track) | |
1099 end = &cdda_playing.cd_toc.leadout; | |
1100 else | |
1101 end = &cdda_playing.cd_toc.track[track + 1]; | |
1102 | |
1103 play_ioctl(&start, end); | |
1104 | |
1105 if (is_paused) { | |
1106 cdda_pause(TRUE); | |
1107 pause_time = time * 1000; | |
1108 } | |
1109 } | |
1110 | |
1111 static int | |
1112 get_time_analog(void) | |
1113 { | |
1114 int frame, start_frame, length; | |
1115 int track = cdda_playing.track; | |
1116 | |
1117 if (is_paused && pause_time != -1) | |
1118 return pause_time; | |
1119 | |
1120 frame = get_current_frame(); | |
1121 | |
1122 if (frame == -1) | |
1123 return -1; | |
1124 | |
1125 start_frame = LBA(cdda_playing.cd_toc.track[track]); | |
1126 length = cdda_calculate_track_length(&cdda_playing.cd_toc, track); | |
1127 | |
1128 if (frame - start_frame >= length - 20) /* 20 seems to work better */ | |
1129 return -1; | |
1130 | |
1131 return ((frame - start_frame) * 1000) / 75; | |
1132 } | |
1133 | |
1134 static int | |
1135 get_time_dae(void) | |
1136 { | |
1137 if (dae_data.audio_error) | |
1138 return -2; | |
1139 if (!cdda_playing.playing || | |
1140 (dae_data.eof && !cdda_ip.output->buffer_playing())) | |
1141 return -1; | |
1142 return cdda_ip.output->output_time(); | |
1143 } | |
1144 | |
1145 static int | |
1146 get_time(void) | |
1147 { | |
1148 if (cdda_playing.fd == -1) | |
1149 return -1; | |
1150 | |
1151 if (cdda_playing.drive.dae) | |
1152 return get_time_dae(); | |
1153 else | |
1154 return get_time_analog(); | |
1155 } | |
1156 | |
1157 static void | |
1158 get_song_info(char *filename, char **title, int *len) | |
1159 { | |
1160 cdda_disc_toc_t toc; | |
1161 int t; | |
1162 char *tmp; | |
1163 struct driveinfo *drive; | |
1164 | |
1165 *title = NULL; | |
1166 *len = -1; | |
1167 | |
1168 // g_message("** CD_AUDIO: getting song info"); | |
1169 | |
1170 if ((drive = cdda_find_drive(filename)) == NULL) | |
1171 return; | |
1172 | |
1173 tmp = strrchr(filename, '/'); | |
1174 if (tmp) | |
1175 tmp++; | |
1176 else | |
1177 tmp = filename; | |
1178 | |
1179 if (!sscanf(tmp, "Track %d.cda", &t)) | |
1180 return; | |
1181 if (!cdda_get_toc(&toc, drive->device)) | |
1182 return; | |
1183 if (t < toc.first_track || t > toc.last_track | |
1184 || toc.track[t].flags.data_track) | |
1185 return; | |
1186 | |
1187 *len = (cdda_calculate_track_length(&toc, t) * 1000) / 75; | |
1188 *title = cdda_get_title(&toc, t); | |
1189 } | |
1190 | |
1191 #ifdef HAVE_OSS | |
1192 static void | |
1193 oss_get_volume(int *l, int *r, int mixer_line) | |
1194 { | |
1195 int fd, v; | |
1196 | |
1197 fd = open(DEV_MIXER, O_RDONLY); | |
1198 if (fd != -1) { | |
1199 ioctl(fd, MIXER_READ(mixer_line), &v); | |
1200 *r = (v & 0xFF00) >> 8; | |
1201 *l = (v & 0x00FF); | |
1202 close(fd); | |
1203 } | |
1204 } | |
1205 | |
1206 static void | |
1207 oss_set_volume(int l, int r, int mixer_line) | |
1208 { | |
1209 int fd, v; | |
1210 | |
1211 fd = open(DEV_MIXER, O_RDONLY); | |
1212 if (fd != -1) { | |
1213 v = (r << 8) | l; | |
1214 ioctl(fd, MIXER_WRITE(mixer_line), &v); | |
1215 close(fd); | |
1216 } | |
1217 } | |
1218 #else | |
1219 static void | |
1220 oss_get_volume(int *l, int *r, int mixer_line) | |
1221 { | |
1222 } | |
1223 static void | |
1224 oss_set_volume(int l, int r, int mixer_line) | |
1225 { | |
1226 } | |
1227 #endif | |
1228 | |
1229 | |
1230 static void | |
1231 get_volume(int *l, int *r) | |
1232 { | |
1233 if (cdda_playing.drive.dae) | |
1234 cdda_ip.output->get_volume(l, r); | |
1235 else if (cdda_playing.drive.mixer == CDDA_MIXER_OSS) | |
1236 oss_get_volume(l, r, cdda_playing.drive.oss_mixer); | |
1237 else if (cdda_playing.drive.mixer == CDDA_MIXER_DRIVE) | |
1238 drive_get_volume(l, r); | |
1239 } | |
1240 | |
1241 static void | |
1242 set_volume(int l, int r) | |
1243 { | |
1244 if (cdda_playing.drive.dae) | |
1245 cdda_ip.output->set_volume(l, r); | |
1246 else if (cdda_playing.drive.mixer == CDDA_MIXER_OSS) | |
1247 oss_set_volume(l, r, cdda_playing.drive.oss_mixer); | |
1248 else if (cdda_playing.drive.mixer == CDDA_MIXER_DRIVE) | |
1249 drive_set_volume(l, r); | |
1250 } |