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