1 // cdctl.h - CDCtl class provides easy control of cd audio functions
2 // Copyright (C) 1998 Sam Hawker <shawkie@geocities.com>
3 // This software comes with ABSOLUTELY NO WARRANTY
4 // This software is free software, and you are welcome to redistribute it
5 // under certain conditions
6 // See the README file for a more complete notice.
8 // Although cdctl.h is an integral part of wmcdplay, it may also be distributed seperately.
10 // Change this define to alter the size of forward and backward skips (in frames)
11 // Yes, I know this should really be a method of CDCtl
12 #define _CDCTL_SKIP_SIZE 1125
14 // Try defining some of these. They may improve performance or reliability
15 // (or just plain make it work)
16 // #define _CDCTL_STOP_BEFORE_PLAY
17 // #define _CDCTL_START_BEFORE_PLAY
18 // #define _CDCTL_SOFT_STOP
20 // Define this if it stops after each track
21 #define _CDCTL_SENSITIVE_EOT
22 // If it still stops for a while between tracks, increase this (0-75 is a sensible range)
23 #define _CDCTL_SENSITIVITY 0
27 #include <sys/ioctl.h>
28 #include <sys/types.h>
33 #include <linux/cdrom.h>
36 #include <sys/cdrom.h>
47 // Audio command values
59 // Track selection values (what to do when I've played the requested track)
60 // Note: Track selection is not perfect - so use tsNone if you want to avoid trouble.
61 // Basically, if we receive a CDROM_AUDIO_COMPLETED status, then we have to decide what to do.
62 // If we think the last play command was ours (Next/Prev/FFwd/Rewd don't count), then we do something,
63 // depending on the current track selection mode.
64 // Failures: Sometimes we may think we sent the last play command when we did not (if we didn't see play stop in
66 // If another application is polling the status, it may receive the CDROM_AUDIO_COMPLETED we are looking
67 // for, and we will not, so will think play was stopped manually.
68 // Similarly, we may read the CDROM_AUDIO_COMPLETED status when we don't want it, such that the other
69 // application never sees it.
70 // Verdict: Linux audio cdrom handling is broken.
71 // Update: New define _CDCTL_SENSITIVE_EOT may help in cases where CDROM_AUDIO_COMPLETED is not being returned
72 // correctly. It may, however, interfere with other running cd players.
74 // Update: I think this works like a dream now. Even with many cd players sharing a cdrom. Let me know if not!!
76 #define tsNone 0 // Just stop
77 #define tsRepeat 1 // Play it again
78 #define tsNext 2 // Play next track (stop at end of CD)
79 #define tsRepeatCD 3 // Play next track (start from first track if end is reached)
80 #define tsRandom 4 // Play a track at random
86 device
=(char *)malloc(sizeof(char)*(strlen(dname
)+1));
92 if((cdfdopen
= (cdfd
= open(device
,O_RDONLY
| O_NONBLOCK
))) != -1) {
113 void doAudioCommand(int cmd
){
115 int newtrk
=status_track
;
119 #ifdef _CDCTL_SOFT_STOP
120 ioctl(cdfd
,CDROMSTART
);
122 #ifndef _CDCTL_SOFT_STOP
123 ioctl(cdfd
,CDROMSTOP
);
129 status_state
=ssPlaying
;
130 select(status_track
);
134 ioctl(cdfd
,CDROMPAUSE
);
137 ioctl(cdfd
,CDROMRESUME
);
147 if(newtrk
>cd_tracks
-1)
152 if(status_pos
>cd_trklist
[status_track
].track_start
+_CDCTL_SKIP_SIZE
){
153 status_pos
-=_CDCTL_SKIP_SIZE
;
158 if(status_pos
<cd_trklist
[status_track
].track_start
+cd_trklist
[status_track
].track_len
-_CDCTL_SKIP_SIZE
){
159 status_pos
+=_CDCTL_SKIP_SIZE
;
164 if(ioctl(cdfd
,CDROMEJECT
))
167 status_state
=ssTrayOpen
;
170 ioctl(cdfd
,CDROMCLOSETRAY
);
179 struct cdrom_subchnl sc
;
180 sc
.cdsc_format
=CDROM_MSF
;
181 if(ioctl(cdfd
, CDROMSUBCHNL
, &sc
)){
182 if(status_state
!=ssNoCD
)
183 status_state
=ssTrayOpen
;
189 if(status_state
==ssNoCD
|| status_state
==ssTrayOpen
)
192 switch(sc
.cdsc_audiostatus
){
193 case CDROM_AUDIO_PLAY
:
194 if(status_state
==ssStopped
)
196 status_state
=ssPlaying
;
198 case CDROM_AUDIO_PAUSED
:
199 if(status_state
==ssStopped
)
201 status_state
=ssPaused
;
203 case CDROM_AUDIO_COMPLETED
:
205 status_state
=ssPlaying
;
211 status_state
=ssStopped
;
215 #ifdef _CDCTL_SENSITIVE_EOT
217 start
= cd_trklist
[status_track
].track_start
;
218 stop
= start
+ cd_trklist
[status_track
].track_len
- _CDCTL_SENSITIVITY
;
219 now
= ((sc
.cdsc_absaddr
.msf
.minute
) * 60 + sc
.cdsc_absaddr
.msf
.second
) * 75 + sc
.cdsc_absaddr
.msf
.frame
- CD_MSF_OFFSET
;
220 if(now
>0 && (now
<start
|| now
>=stop
)){
221 status_state
=ssPlaying
;
227 status_state
=ssStopped
;
232 status_state
=ssStopped
;
235 if(cd_trklist
[status_track
].track_data
)
240 void setVolume(int l
, int r
){
242 struct cdrom_volctrl vol
;
245 ioctl(cdfd
,CDROMVOLCTRL
,&vol
);
251 struct cdrom_volctrl vol
;
252 ioctl(cdfd
,CDROMVOLREAD
,&vol
);
253 status_volumel
=vol
.channel0
;
254 status_volumer
=vol
.channel1
;
258 return status_volumel
;
261 return status_volumer
;
263 void setTrackSelection(int ts
){
266 int getTrackSelection(){
278 int getTrackStart(int trk
){
279 return cd_trklist
[trk
-1].track_start
;
281 int getTrackLen(int trk
){
282 return cd_trklist
[trk
-1].track_len
;
284 bool getTrackData(int trk
){
285 return cd_trklist
[trk
-1].track_data
;
287 int getStatusState(){
290 int getStatusTrack(){
291 return status_track
+1;
293 int getStatusPosAbs(){
294 return status_pos
-cd_trklist
[0].track_start
;
296 int getStatusPosRel(){
297 return status_pos
-cd_trklist
[status_track
].track_start
;
303 struct cdrom_tochdr hdr
;
304 ioctl(cdfd
, CDROMREADTOCHDR
, &hdr
);
305 cd_tracks
=hdr
.cdth_trk1
;
306 cd_trklist
=(struct CDTrack
*)malloc(cd_tracks
*sizeof(struct CDTrack
));
307 struct cdrom_tocentry te
;
309 for(int i
=0;i
<=cd_tracks
;i
++){
311 te
.cdte_track
=CDROM_LEADOUT
;
314 te
.cdte_format
=CDROM_MSF
; // I think it is ok to read this as LBA, but for a quiet life...
315 ioctl(cdfd
, CDROMREADTOCENTRY
, &te
);
316 int this_addr
=((te
.cdte_addr
.msf
.minute
* 60) + te
.cdte_addr
.msf
.second
) * 75 + te
.cdte_addr
.msf
.frame
- CD_MSF_OFFSET
;
318 cd_trklist
[i
-1].track_len
= this_addr
- prev_addr
- 1;
321 cd_trklist
[i
].track_data
= te
.cdte_ctrl
& CDROM_DATA_TRACK
? true : false;
322 cd_trklist
[i
].track_start
= this_addr
;
328 void trackinfo(struct cdrom_subchnl
*subchnl
){
329 if(status_state
==ssPlaying
|| status_state
==ssPaused
){
330 status_pos
=((subchnl
->cdsc_absaddr
.msf
.minute
) * 60 + subchnl
->cdsc_absaddr
.msf
.second
) * 75 + subchnl
->cdsc_absaddr
.msf
.frame
- CD_MSF_OFFSET
;
331 for(status_track
=0;status_track
<cd_tracks
;status_track
++){
332 if(status_pos
<cd_trklist
[status_track
].track_start
+cd_trklist
[status_track
].track_len
)
338 struct cdrom_msf pmsf
;
339 int abs0
=status_pos
+ CD_MSF_OFFSET
;
340 int abs1
=cd_trklist
[status_track
].track_start
+ cd_trklist
[status_track
].track_len
- 1 + CD_MSF_OFFSET
;
341 pmsf
.cdmsf_min0
=abs0
/(75*60);
342 pmsf
.cdmsf_min1
=abs1
/(75*60);
343 pmsf
.cdmsf_sec0
=(abs0
%(75*60))/75;
344 pmsf
.cdmsf_sec1
=(abs1
%(75*60))/75;
345 pmsf
.cdmsf_frame0
=abs0
%75;
346 pmsf
.cdmsf_frame1
=abs1
%75;
348 #ifdef _CDCTL_STOP_BEFORE_PLAY
349 ioctl(cdfd
,CDROMSTOP
);
351 #ifdef _CDCTL_START_BEFORE_PLAY
352 ioctl(cdfd
,CDROMSTART
);
355 ioctl(cdfd
,CDROMPLAYMSF
,&pmsf
);
357 void select(int trk
){
359 status_pos
=cd_trklist
[status_track
].track_start
;
360 if(status_state
==ssPlaying
){
361 if(cd_trklist
[status_track
].track_data
){
363 #ifdef _CDCTL_HARD_STOP
364 ioctl(cdfd
,CDROMSTOP
);
366 #ifndef _CDCTL_HARD_STOP
367 ioctl(cdfd
,CDROMSTART
);
377 int newtrk
=status_track
;
389 if(newtrk
>=cd_tracks
){
396 if(newtrk
>=cd_tracks
)
400 newtrk
+=(int)((cd_tracks
-1)*(float)rand()/RAND_MAX
+1);
401 if(newtrk
>=cd_tracks
)
405 } while(cd_trklist
[newtrk
].track_data
);
423 struct CDTrack
*cd_trklist
;