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
|
|
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 }
|