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 $";
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"
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.)
67 struct wm_drive
*proto
;
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?
90 #if defined DEFAULT_CD_DEVICE
91 char *cd_device
= DEFAULT_CD_DEVICE
;
93 char *cd_device
= NULL
;
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 )
113 wm_strmcpy( &s
, _wm_drive_vendor
);
117 char *wm_drive_model( void )
121 wm_strmcpy( &s
, _wm_drive_model
);
125 char *wm_drive_revision( void )
129 wm_strmcpy( &s
, _wm_drive_revision
);
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.
145 find_drive_struct(char *vendor
, char *model
, char *rev
)
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
)) ) )
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
);
164 return (NULL
); /* this means the list is badly terminated. */
165 } /* find_drive_struct() */
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.
179 struct wm_playlist
*l
;
182 if ((drive
.get_trackcount
)(&drive
, &thiscd
.ntracks
) < 0)
184 perror("trackcount");
188 thiscd
.artist
[0] = thiscd
.cdname
[0] = '\0';
189 thiscd
.whichdb
= thiscd
.otherrc
= thiscd
.otherdb
= thiscd
.user
= NULL
;
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
++)
205 if (thiscd
.trk
!= NULL
)
208 thiscd
.trk
= malloc((thiscd
.ntracks
+ 1) * sizeof(struct wm_trackinfo
));
209 if (thiscd
.trk
== 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");
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");
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
);
269 * 2 CD has just been inserted (TOC has been read)
271 * Updates cur_track, cur_pos_rel, cur_pos_abs and other variables.
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
);
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
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");
306 if (mode
== WM_CDM_EJECTED
|| mode
== WM_CDM_UNKNOWN
)
308 cur_cdmode
= WM_CDM_EJECTED
;
310 cur_cdlen
= cur_tracklen
= 1;
311 cur_pos_abs
= cur_pos_rel
= cur_frame
= 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
)
338 cur_ntracks
= cd
->ntracks
;
339 cur_cdlen
= cd
->length
;
341 cur_artist
= cd
->artist
;
342 cur_cdname
= cd
->cdname
;
343 cur_cdmode
= WM_CDM_STOPPED
;
344 ret
= WM_CDS_JUST_INSERTED
;
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
))
359 while (cur_track
< cur_ntracks
&& cur_frame
>=
360 cd
->trk
[cur_track
].start
)
363 if (cur_track
>= 1 && trackno
> cd
->trk
[cur_track
-1].track
)
368 if (mode
== WM_CDM_UNKNOWN
)
370 mode
= WM_CDM_STOPPED
;
371 cur_lasttrack
= cur_firsttrack
= -1;
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;
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
;
394 cur_pos_abs
= cur_frame
= 0;
397 cur_tracklen
= cd
->length
;
399 cur_tracklen
= cd
->trk
[cur_track
-1].length
;
402 case WM_CDM_TRACK_DONE
:
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.)
423 cd_volume(vol
, bal
, max
)
426 int left
, right
, scale
;
429 * Set "left" and "right" to volume-slider values accounting for the
432 /* printf("Vol = %d, Bal = %d, Max = %d\n", vol, bal, max);
435 vol
= (vol
* 100 + max
- 16) / max
;
436 scale
= (vol
+ 5) / 10;
440 right
= vol
- scale
* (10 - bal
);
441 #ifdef SYMETRIC_BALANCE
442 left
= vol
+ scale
* (10 - bal
);
449 #ifdef SYMETRIC_BALANCE
450 right
= vol
+ scale
* (bal
- 10);
454 left
= vol
- scale
* (bal
- 10);
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
);
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.)
479 cd_volume( int vol
, int bal
, int max
)
484 * Set "left" and "right" to volume-slider values accounting for the
487 * XXX - the maximum volume setting is assumed to be in the 20-30 range.
490 right
= vol
- (9 - bal
) * 2;
494 left
= vol
- (bal
- 11) * 2;
498 left
= (left
* 100 + max
- 1) / max
;
499 right
= (right
* 100 + max
- 1) / max
;
505 (void) (drive
.set_volume
)(&drive
, left
, right
);
508 #endif /* CLIF_VOL */
514 * Pause the CD, if it's in play mode. If it's already paused, go back to
520 static int paused_pos
;
522 if (cur_cdmode
== WM_CDM_EJECTED
) /* do nothing if there's no CD! */
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
;
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
);
541 } /* wm_cd_pause() */
546 * Stop the CD if it's not already stopped.
551 if (cur_cdmode
== WM_CDM_EJECTED
)
554 if (cur_cdmode
!= WM_CDM_STOPPED
)
556 cur_lasttrack
= cur_firsttrack
= -1;
557 cur_cdmode
= WM_CDM_STOPPED
;
558 (drive
.stop
)(&drive
);
564 * wm_cd_play_chunk(start, end)
566 * Play the CD from one position to another (both in frames.)
569 wm_cd_play_chunk( int start
, int end
, int realstart
)
571 if (cur_cdmode
== WM_CDM_EJECTED
|| cd
== NULL
)
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.
588 wm_cd_play( int start
, int pos
, int end
)
590 if (cur_cdmode
== WM_CDM_EJECTED
|| cd
== NULL
)
593 cur_firsttrack
= start
;
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 */
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.)
614 wm_cd_play_from_pos( int pos
)
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.
634 status
= (drive
.eject
)(&drive
);
649 cur_cdlen
= cur_tracklen
= 1;
650 cur_pos_abs
= cur_pos_rel
= cur_frame
= 0;
651 cur_cdmode
= WM_CDM_EJECTED
;
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
)
685 bottom
= cd
->trk
[i
].start
;
687 for (; i
< cur_ntracks
; i
++)
688 if (cd
->trk
[i
].track
> track
)
691 top
= i
== cur_ntracks
? (cd
->length
- 1) * 75 : cd
->trk
[i
].start
;
693 if (start
> bottom
&& start
< top
)
696 current
= (top
+ bottom
) / 2;
697 interval
= (top
- bottom
) / 4;
700 wm_cd_play_chunk(current
, current
+ 75, current
);
702 if (wm_cd_status() != 1)
704 while (cur_frame
< current
)
705 if (wm_cd_status() != 1 || cur_cdmode
!= WM_CDM_PLAYING
)
710 if (cd
->trk
[cur_track
- 1].track
> track
)
713 if (cur_index
>= index
)
721 } while (interval
> 2);
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
731 * "max" is the maximum value of the volume knob.
734 wm_cd_read_initial_volume( int max
)
738 if ((drive
.get_volume
)(&drive
, &left
, &right
) < 0 || left
== -1)
741 left
= (left
* max
+ 99) / 100;
742 right
= (right
* max
+ 99) / 100;
746 wm_cd_cur_balance
= (right
- left
) / 2 + 11;
747 if (wm_cd_cur_balance
> 20)
748 wm_cd_cur_balance
= 20;
752 else if (left
== right
)
754 wm_cd_cur_balance
= 10;
759 wm_cd_cur_balance
= (right
- left
) / 2 + 9;
760 if (wm_cd_cur_balance
< 0)
761 wm_cd_cur_balance
= 0;
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
= {
774 "Generic\0", /* vendor */
775 "drive type\0 ", /* model */
780 gen_init
, /* functions... */
784 gen_get_drive_status
,