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 }