wmclockmon: update change-log
[dockapps.git] / ascd / libworkman / cdrom.c
blob8eea47e2dfd56ff7b0844c069715ee96d06f42e7
1 /*
2 * $Id: cdrom.c,v 1.10 1999/05/05 16:34:19 dirk Exp $
4 * This file is part of WorkMan, the civilized CD player library
5 * (c) 1991-1997 by Steven Grimm (original author)
6 * (c) by Dirk Försterling (current 'author' = maintainer)
7 * The maintainer can be contacted by his e-mail address:
8 * milliByte@DeathsDoor.com
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the Free
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * Interface between most of WorkMan and the low-level CD-ROM library
26 * routines defined in plat_*.c and drv_*.c. The goal is to have no
27 * platform- or drive-dependent code here.
30 static char cdrom_id[] = "$Id: cdrom.c,v 1.10 1999/05/05 16:34:19 dirk Exp $";
32 #include <errno.h>
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 /* #include <sys/time.h> */
40 #include "include/wm_config.h"
41 #include "include/wm_struct.h"
42 #include "include/wm_cddb.h"
43 #include "include/wm_cdrom.h"
44 #include "include/wm_database.h"
45 #include "include/wm_platform.h"
46 #include "include/wm_helpers.h"
47 #include "include/wm_cdinfo.h"
49 #ifdef CAN_CLOSE
50 #include <fcntl.h>
51 #endif
53 #define WM_MSG_CLASS WM_MSG_CLASS_CDROM
55 /* extern struct wm_drive generic_proto, toshiba_proto, sony_proto; */
56 /* toshiba33_proto; <=== Somehow, this got lost */
59 * The supported drive types are listed here. NULL means match anything.
60 * The first match in the list is used, and substring matches are done (so
61 * put long names before their shorter prefixes.)
63 struct drivelist {
64 char *ven;
65 char *mod;
66 char *rev;
67 struct wm_drive *proto;
68 } drives[] = {
69 { "TOSHIBA", "XM-3501", NULL, &toshiba_proto },
70 { "TOSHIBA", "XM-3401", NULL, &toshiba_proto },
71 { "TOSHIBA", "XM-3301", NULL, &toshiba_proto },
72 { "SONY", "CDU-8012", NULL, &sony_proto },
73 { "SONY", "CDU 561", NULL, &sony_proto },
74 { WM_STR_GENVENDOR, WM_STR_GENMODEL, WM_STR_GENREV, &generic_proto },
75 { NULL, NULL, NULL, &generic_proto }
79 * Solaris 2.2 will remove the device out from under us. Getting an ENOENT
80 * is therefore sometimes not a problem.
82 int intermittent_dev = 0;
85 * Do we want to keep the CD device open after quitting by default?
87 int keep_open = 0;
90 #if defined DEFAULT_CD_DEVICE
91 char *cd_device = DEFAULT_CD_DEVICE;
92 #else
93 char *cd_device = NULL;
94 #endif
97 int wm_cd_cur_balance = 10;
99 struct wm_drive drive = { -1, "", "", "", NULL, NULL };
101 char _wm_drive_vendor[32] = "Generic";
102 char _wm_drive_model[32] = "drive type";
103 char _wm_drive_revision[32] = "";
106 * Give information about the drive we found during wmcd_open()
109 char *wm_drive_vendor( void )
111 char *s = NULL;
113 wm_strmcpy( &s, _wm_drive_vendor );
114 return s;
117 char *wm_drive_model( void )
119 char *s = NULL;
121 wm_strmcpy( &s, _wm_drive_model );
122 return s;
125 char *wm_drive_revision( void )
127 char *s = NULL;
129 wm_strmcpy( &s, _wm_drive_revision );
130 return s;
133 void wm_drive_settype( char *vendor, char *model, char *revision )
135 sprintf( _wm_drive_vendor, "%s", vendor );
136 sprintf( _wm_drive_model, "%s", model );
137 sprintf( _wm_drive_revision, "%s", revision );
141 * Figure out which prototype drive structure we should be using based
142 * on the vendor, model, and revision of the current drive.
144 struct wm_drive *
145 find_drive_struct(char *vendor, char *model, char *rev)
147 struct drivelist *d;
149 for (d = drives; d; d++)
151 if( ( (d->ven != NULL) && strncmp(d->ven, vendor, strlen(d->ven)) ) ||
152 ( (d->mod != NULL) && strncmp(d->mod, model, strlen(d->mod)) ) ||
153 ( (d->rev != NULL) && strncmp(d->rev, rev, strlen(d->rev)) ) )
154 continue;
156 if (d->proto->vendor[0] == '\0')
157 strcpy(d->proto->vendor, vendor);
158 if (d->proto->model[0] == '\0')
159 strcpy(d->proto->model, model);
161 return (d->proto);
164 return (NULL); /* this means the list is badly terminated. */
165 } /* find_drive_struct() */
168 * read_toc()
170 * Read the table of contents from the CD. Return a pointer to a wm_cdinfo
171 * struct containing the relevant information (minus artist/cdname/etc.)
172 * This is a static struct. Returns NULL if there was an error.
174 * XXX allocates one trackinfo too many.
176 struct wm_cdinfo *
177 read_toc()
179 struct wm_playlist *l;
180 int i, pos;
182 if ((drive.get_trackcount)(&drive, &thiscd.ntracks) < 0)
184 perror("trackcount");
185 return (NULL);
188 thiscd.artist[0] = thiscd.cdname[0] = '\0';
189 thiscd.whichdb = thiscd.otherrc = thiscd.otherdb = thiscd.user = NULL;
190 thiscd.length = 0;
191 thiscd.autoplay = thiscd.playmode = thiscd.volume = 0;
193 /* Free up any left-over playlists. */
194 if (thiscd.lists != NULL)
196 for (l = thiscd.lists; l->name != NULL; l++)
198 free(l->name);
199 free(l->list);
201 free(thiscd.lists);
202 thiscd.lists = NULL;
205 if (thiscd.trk != NULL)
206 free(thiscd.trk);
208 thiscd.trk = malloc((thiscd.ntracks + 1) * sizeof(struct wm_trackinfo));
209 if (thiscd.trk == NULL)
211 perror("malloc");
212 return (NULL);
215 for (i = 0; i < thiscd.ntracks; i++)
217 if ((drive.get_trackinfo)(&drive, i + 1, &thiscd.trk[i].data,
218 &thiscd.trk[i].start) < 0)
220 perror("CD track info read");
221 return (NULL);
224 thiscd.trk[i].avoid = thiscd.trk[i].data;
225 thiscd.trk[i].length = thiscd.trk[i].start / 75;
227 thiscd.trk[i].songname = thiscd.trk[i].otherrc =
228 thiscd.trk[i].otherdb = NULL;
229 thiscd.trk[i].contd = 0;
230 thiscd.trk[i].volume = 0;
231 thiscd.trk[i].track = i + 1;
232 thiscd.trk[i].section = 0;
235 if ((drive.get_cdlen)(&drive, &thiscd.trk[i].start) < 0)
237 perror("CD length read");
238 return (NULL);
240 thiscd.trk[i].length = thiscd.trk[i].start / 75;
242 /* Now compute actual track lengths. */
243 pos = thiscd.trk[0].length;
245 for (i = 0; i < thiscd.ntracks; i++)
247 thiscd.trk[i].length = thiscd.trk[i+1].length - pos;
248 pos = thiscd.trk[i+1].length;
249 if (thiscd.trk[i].data)
250 thiscd.trk[i].length = (thiscd.trk[i + 1].start -
251 thiscd.trk[i].start) * 2;
252 if (thiscd.trk[i].avoid)
253 wm_strmcpy(&thiscd.trk[i].songname, "DATA TRACK");
256 thiscd.length = thiscd.trk[thiscd.ntracks].length;
257 thiscd.cddbid = cddb_discid(drive);
259 return (&thiscd);
263 * wm_cd_status()
265 * Return values:
267 * 0 No CD in drive.
268 * 1 CD in drive.
269 * 2 CD has just been inserted (TOC has been read)
271 * Updates cur_track, cur_pos_rel, cur_pos_abs and other variables.
274 wm_cd_status( void )
276 static enum wm_cd_modes oldmode = WM_CDM_UNKNOWN;
277 enum wm_cd_modes mode;
278 int status, trackno = cur_track;
279 int ret = WM_CDS_DISC_READY;
281 /* Open the drive. This returns 1 if the device isn't ready. */
283 status = wmcd_open(&drive);
285 if (status < 0)
286 return (status);
287 if (status > 0)
288 return (WM_CDS_NO_DISC);
290 /* If the user hit the stop button, don't pass PLAYING as oldmode.
291 * Likewise, if we've just started playing, don't remember that
292 * we were stopped before (or the state machine in get_drive_status
293 * can get confused.)
295 if( (cur_cdmode == WM_CDM_STOPPED) || (cur_cdmode == WM_CDM_PLAYING) )
296 oldmode = cur_cdmode;
298 if( (drive.get_drive_status)(&drive, oldmode, &mode, &cur_frame,
299 &trackno, &cur_index) < 0)
301 perror("CD get drive status");
302 return (-1);
304 oldmode = mode;
306 if (mode == WM_CDM_EJECTED || mode == WM_CDM_UNKNOWN)
308 cur_cdmode = WM_CDM_EJECTED;
309 cur_track = -1;
310 cur_cdlen = cur_tracklen = 1;
311 cur_pos_abs = cur_pos_rel = cur_frame = 0;
313 if (exit_on_eject)
314 exit(0);
316 return (WM_CDS_NO_DISC);
319 /* If there wasn't a CD before and there is now, learn about it. */
320 if (cur_cdmode == WM_CDM_EJECTED)
322 cur_pos_rel = cur_pos_abs = 0;
325 status = wmcd_reopen( &drive );
327 if ((cd = read_toc()) == NULL)
329 if (exit_on_eject)
331 exit(-1);
332 } else {
333 return (-1);
337 cur_nsections = 0;
338 cur_ntracks = cd->ntracks;
339 cur_cdlen = cd->length;
340 load();
341 cur_artist = cd->artist;
342 cur_cdname = cd->cdname;
343 cur_cdmode = WM_CDM_STOPPED;
344 ret = WM_CDS_JUST_INSERTED;
347 switch (mode) {
348 case WM_CDM_PLAYING:
349 case WM_CDM_PAUSED:
350 cur_pos_abs = cur_frame / 75;
352 /* Only look up the current track number if necessary. */
353 if (cur_track < 1 || cur_frame < cd->trk[cur_track-1].start ||
354 cur_frame >= (cur_track >= cur_ntracks ?
355 (cur_cdlen + 1) * 75 :
356 cd->trk[cur_track].start))
358 cur_track = 0;
359 while (cur_track < cur_ntracks && cur_frame >=
360 cd->trk[cur_track].start)
361 cur_track++;
363 if (cur_track >= 1 && trackno > cd->trk[cur_track-1].track)
364 cur_track++;
365 /* Fall through */
367 case WM_CDM_UNKNOWN:
368 if (mode == WM_CDM_UNKNOWN)
370 mode = WM_CDM_STOPPED;
371 cur_lasttrack = cur_firsttrack = -1;
373 /* Fall through */
375 case WM_CDM_STOPPED:
376 if (cur_track >= 1 && cur_track <= cur_ntracks)
378 cur_trackname = cd->trk[cur_track-1].songname;
379 cur_avoid = cd->trk[cur_track-1].avoid;
380 cur_contd = cd->trk[cur_track-1].contd;
381 cur_pos_rel = (cur_frame -
382 cd->trk[cur_track-1].start) / 75;
383 if (cur_pos_rel < 0)
384 cur_pos_rel = -cur_pos_rel;
387 if( (playlist != NULL) && playlist[0].start & (cur_listno > 0))
389 cur_pos_abs -= cd->trk[playlist[cur_listno-1].
390 start - 1].start / 75;
391 cur_pos_abs += playlist[cur_listno-1].starttime;
393 if (cur_pos_abs < 0)
394 cur_pos_abs = cur_frame = 0;
396 if (cur_track < 1)
397 cur_tracklen = cd->length;
398 else
399 cur_tracklen = cd->trk[cur_track-1].length;
400 /* Fall through */
402 case WM_CDM_TRACK_DONE:
403 cur_cdmode = mode;
404 break;
405 case WM_CDM_FORWARD:
406 case WM_CDM_EJECTED:
407 break;
410 return (ret);
413 #undef CLIF_VOL
414 #ifdef CLIF_VOL
416 * cd_volume(vol, bal, max)
418 * Set the volume levels. "vol" and "bal" are the volume and balance knob
419 * settings, respectively. "max" is the maximum value of the volume knob
420 * (the balance knob is assumed to always go from 0 to 20.)
422 void
423 cd_volume(vol, bal, max)
424 int vol, bal, max;
426 int left, right, scale;
429 * Set "left" and "right" to volume-slider values accounting for the
430 * balance setting.
432 /* printf("Vol = %d, Bal = %d, Max = %d\n", vol, bal, max);
435 vol = (vol * 100 + max - 16) / max;
436 scale = (vol + 5) / 10;
438 if (bal < 9)
440 right = vol - scale * (10 - bal);
441 #ifdef SYMETRIC_BALANCE
442 left = vol + scale * (10 - bal);
443 #else
444 left = vol;
445 #endif
447 else if (bal > 11)
449 #ifdef SYMETRIC_BALANCE
450 right = vol + scale * (bal - 10);
451 #else
452 right = vol;
453 #endif
454 left = vol - scale * (bal - 10);
456 else
457 left = right = vol;
460 * some plat_*.c is missing the limitation
462 left = left < 0 ? 0 : left > 100 ? 100 : left;
463 right = right < 0 ? 0 : right > 100 ? 100 : right;
464 /* printf("Left = %d, Right = %d\n", left, right);
466 (void) (drive.set_volume)(&drive, left, right);
467 } /* cd_volume() */
469 #else
472 * cd_volume(vol, bal, max)
474 * Set the volume levels. "vol" and "bal" are the volume and balance knob
475 * settings, respectively. "max" is the maximum value of the volume knob
476 * (the balance knob is assumed to always go from 0 to 20.)
478 void
479 cd_volume( int vol, int bal, int max )
481 int left, right;
484 * Set "left" and "right" to volume-slider values accounting for the
485 * balance setting.
487 * XXX - the maximum volume setting is assumed to be in the 20-30 range.
489 if (bal < 9)
490 right = vol - (9 - bal) * 2;
491 else
492 right = vol;
493 if (bal > 11)
494 left = vol - (bal - 11) * 2;
495 else
496 left = vol;
498 left = (left * 100 + max - 1) / max;
499 right = (right * 100 + max - 1) / max;
500 if (left > 100)
501 left = 100;
502 if (right > 100)
503 right = 100;
505 (void) (drive.set_volume)(&drive, left, right);
506 } /* cd_volume() */
508 #endif /* CLIF_VOL */
512 * wm_cd_pause()
514 * Pause the CD, if it's in play mode. If it's already paused, go back to
515 * play mode.
517 void
518 wm_cd_pause( void )
520 static int paused_pos;
522 if (cur_cdmode == WM_CDM_EJECTED) /* do nothing if there's no CD! */
523 return;
525 switch (cur_cdmode) {
526 case WM_CDM_PLAYING: /* playing */
527 cur_cdmode = WM_CDM_PAUSED;
528 (drive.pause)(&drive);
529 paused_pos = cur_pos_rel;
530 break;
532 case WM_CDM_PAUSED: /* paused */
533 cur_cdmode = WM_CDM_PLAYING;
534 /* (drive.resume)(&drive); */
535 if ((drive.resume)(&drive))
536 wm_cd_play(cur_track, paused_pos,
537 playlist[cur_listno-1].end);
538 default: /* */
539 break;
541 } /* wm_cd_pause() */
544 * wm_cd_stop()
546 * Stop the CD if it's not already stopped.
548 void
549 wm_cd_stop( void )
551 if (cur_cdmode == WM_CDM_EJECTED)
552 return;
554 if (cur_cdmode != WM_CDM_STOPPED)
556 cur_lasttrack = cur_firsttrack = -1;
557 cur_cdmode = WM_CDM_STOPPED;
558 (drive.stop)(&drive);
559 cur_track = 1;
561 } /* wm_cd_stop() */
564 * wm_cd_play_chunk(start, end)
566 * Play the CD from one position to another (both in frames.)
568 void
569 wm_cd_play_chunk( int start, int end, int realstart )
571 if (cur_cdmode == WM_CDM_EJECTED || cd == NULL)
572 return;
574 end--;
575 if (start >= end)
576 start = end-1;
578 (drive.play)(&drive, start, end, realstart);
582 * wm_cd_play(starttrack, pos, endtrack)
584 * Start playing the CD or jump to a new position. "pos" is in seconds,
585 * relative to start of track.
587 void
588 wm_cd_play( int start, int pos, int end )
590 if (cur_cdmode == WM_CDM_EJECTED || cd == NULL)
591 return;
593 cur_firsttrack = start;
594 start--;
595 end--;
596 cur_lasttrack = end;
598 wm_cd_play_chunk(cd->trk[start].start + pos * 75, end >= cur_ntracks ?
599 cur_cdlen * 75 : cd->trk[end].start - 1,
600 cd->trk[start].start);
602 /* So we don't update the display with the old frame number */
603 wm_cd_status();
604 cur_frame = cd->trk[start].start + pos * 75;
605 cur_track = cur_firsttrack;
606 cur_cdmode = WM_CDM_PLAYING;
610 * Set the offset into the current track and play. -1 means end of track
611 * (i.e., go to next track.)
613 void
614 wm_cd_play_from_pos( int pos )
616 if (pos == -1)
617 if (cd)
618 pos = cd->trk[cur_track - 1].length - 1;
619 if (cur_cdmode == WM_CDM_PLAYING)
620 wm_cd_play(cur_track, pos, playlist[cur_listno-1].end);
621 } /* wm_cd_play_from_pos() */
624 * Eject the current CD, if there is one, and set the mode to 5.
626 * Returns 0 on success, 1 if the CD couldn't be ejected, or 2 if the
627 * CD contains a mounted filesystem.
630 wm_cd_eject( void )
632 int status;
634 status = (drive.eject)(&drive);
635 if (status < 0)
637 if (status == -3)
639 return (2);
640 } else {
641 return (1);
645 if (exit_on_eject)
646 exit(0);
648 cur_track = -1;
649 cur_cdlen = cur_tracklen = 1;
650 cur_pos_abs = cur_pos_rel = cur_frame = 0;
651 cur_cdmode = WM_CDM_EJECTED;
653 return (0);
656 int wm_cd_closetray(void)
658 return((drive.closetray)(&drive) ? 0 : wm_cd_status()==2 ? 1 : 0);
659 } /* wm_cd_closetray() */
662 * find_trkind(track, index, start)
664 * Start playing at a particular track and index, optionally using a particular
665 * frame as a starting position. Returns a frame number near the start of the
666 * index mark if successful, 0 if the track/index didn't exist.
668 * This is made significantly more tedious (though probably easier to port)
669 * by the fact that CDROMPLAYTRKIND doesn't work as advertised. The routine
670 * does a binary search of the track, terminating when the interval gets to
671 * around 10 frames or when the next track is encountered, at which point
672 * it's a fair bet the index in question doesn't exist.
675 find_trkind( int track, int index, int start )
677 int top = 0, bottom, current, interval, ret = 0, i;
679 if( cur_cdmode == WM_CDM_EJECTED || cd == NULL )
680 return ( 0 ); /* WARNING: was nothing */
682 for (i = 0; i < cur_ntracks; i++)
683 if (cd->trk[i].track == track)
684 break;
685 bottom = cd->trk[i].start;
687 for (; i < cur_ntracks; i++)
688 if (cd->trk[i].track > track)
689 break;
691 top = i == cur_ntracks ? (cd->length - 1) * 75 : cd->trk[i].start;
693 if (start > bottom && start < top)
694 bottom = start;
696 current = (top + bottom) / 2;
697 interval = (top - bottom) / 4;
699 do {
700 wm_cd_play_chunk(current, current + 75, current);
702 if (wm_cd_status() != 1)
703 return (0);
704 while (cur_frame < current)
705 if (wm_cd_status() != 1 || cur_cdmode != WM_CDM_PLAYING)
706 return (0);
707 else
708 wm_susleep(1);
710 if (cd->trk[cur_track - 1].track > track)
711 break;
713 if (cur_index >= index)
715 ret = current;
716 current -= interval;
718 else
719 current += interval;
720 interval /= 2;
721 } while (interval > 2);
723 return (ret);
724 } /* find_trkind() */
727 * Read the initial volume from the drive, if available. Set cur_balance to
728 * the balance level (0-20, 10=centered) and return the proper setting for
729 * the volume knob.
731 * "max" is the maximum value of the volume knob.
734 wm_cd_read_initial_volume( int max )
736 int left, right;
738 if ((drive.get_volume)(&drive, &left, &right) < 0 || left == -1)
739 return (max);
741 left = (left * max + 99) / 100;
742 right = (right * max + 99) / 100;
744 if (left < right)
746 wm_cd_cur_balance = (right - left) / 2 + 11;
747 if (wm_cd_cur_balance > 20)
748 wm_cd_cur_balance = 20;
750 return (right);
752 else if (left == right)
754 wm_cd_cur_balance = 10;
755 return (left);
757 else
759 wm_cd_cur_balance = (right - left) / 2 + 9;
760 if (wm_cd_cur_balance < 0)
761 wm_cd_cur_balance = 0;
763 return (left);
765 } /* wm_cd_read_initial_volume() */
768 * Prototype wm_drive structure, with generic functions. The generic functions
769 * will be replaced with drive-specific functions as appropriate once the drive
770 * type has been sensed.
772 struct wm_drive generic_proto = {
773 -1, /* fd */
774 "Generic\0", /* vendor */
775 "drive type\0 ", /* model */
776 "\0", /* revision */
777 NULL, /* aux */
778 NULL, /* daux */
780 gen_init, /* functions... */
781 gen_get_trackcount,
782 gen_get_cdlen,
783 gen_get_trackinfo,
784 gen_get_drive_status,
785 gen_get_volume,
786 gen_set_volume,
787 gen_pause,
788 gen_resume,
789 gen_stop,
790 gen_play,
791 gen_eject,
792 gen_closetray