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 <arpa/inet.h>
34 #define CD_MSF_OFFSET 150
44 // Audio command values
56 // Track selection values (what to do when I've played the requested track)
57 // Note: Track selection is not perfect - so use tsNone if you want to avoid trouble.
58 // Basically, if we receive a CDROM_AUDIO_COMPLETED status, then we have to decide what to do.
59 // If we think the last play command was ours (Next/Prev/FFwd/Rewd don't count), then we do something,
60 // depending on the current track selection mode.
61 // Failures: Sometimes we may think we sent the last play command when we did not (if we didn't see play stop in
63 // If another application is polling the status, it may receive the CDROM_AUDIO_COMPLETED we are looking
64 // for, and we will not, so will think play was stopped manually.
65 // Similarly, we may read the CDROM_AUDIO_COMPLETED status when we don't want it, such that the other
66 // application never sees it.
67 // Verdict: Linux audio cdrom handling is broken.
68 // Update: New define _CDCTL_SENSITIVE_EOT may help in cases where CDROM_AUDIO_COMPLETED is not being returned
69 // correctly. It may, however, interfere with other running cd players.
71 // Update: I think this works like a dream now. Even with many cd players sharing a cdrom. Let me know if not!!
73 #define tsNone 0 // Just stop
74 #define tsRepeat 1 // Play it again
75 #define tsNext 2 // Play next track (stop at end of CD)
76 #define tsRepeatCD 3 // Play next track (start from first track if end is reached)
77 #define tsRandom 4 // Play a track at random
83 device
=(char *)malloc(sizeof(char)*(strlen(dname
)+1));
89 if(cdfdopen
=(cdfd
=open(device
,O_RDONLY
| O_NONBLOCK
))!=-1){
110 void doAudioCommand(int cmd
){
112 int newtrk
=status_track
;
116 #ifdef _CDCTL_SOFT_STOP
117 ioctl(cdfd
,CDIOCSTART
);
119 #ifndef _CDCTL_SOFT_STOP
120 ioctl(cdfd
,CDIOCSTOP
);
126 status_state
=ssPlaying
;
127 select(status_track
);
131 ioctl(cdfd
,CDIOCPAUSE
);
134 ioctl(cdfd
,CDIOCRESUME
);
144 if(newtrk
>cd_tracks
-1)
149 if(status_pos
>cd_trklist
[status_track
].track_start
+_CDCTL_SKIP_SIZE
){
150 status_pos
-=_CDCTL_SKIP_SIZE
;
155 if(status_pos
<cd_trklist
[status_track
].track_start
+cd_trklist
[status_track
].track_len
-_CDCTL_SKIP_SIZE
){
156 status_pos
+=_CDCTL_SKIP_SIZE
;
161 if(ioctl(cdfd
,CDIOCEJECT
))
164 status_state
=ssTrayOpen
;
167 ioctl(cdfd
,CDIOCCLOSE
);
176 struct ioc_read_subchannel sc
;
177 struct cd_sub_channel_info csci
;
178 sc
.address_format
=CD_MSF_FORMAT
;
181 sc
.data_len
=sizeof(csci
);
182 sc
.data_format
=CD_CURRENT_POSITION
;
183 if(ioctl(cdfd
, CDIOCREADSUBCHANNEL
, &sc
)){
184 if(status_state
!=ssNoCD
)
185 status_state
=ssTrayOpen
;
191 if(status_state
==ssNoCD
|| status_state
==ssTrayOpen
)
194 switch(csci
.header
.audio_status
){
195 case CD_AS_PLAY_IN_PROGRESS
:
196 if(status_state
==ssStopped
)
198 status_state
=ssPlaying
;
200 case CD_AS_PLAY_PAUSED
:
201 if(status_state
==ssStopped
)
203 status_state
=ssPaused
;
205 case CD_AS_PLAY_COMPLETED
:
207 status_state
=ssPlaying
;
213 status_state
=ssStopped
;
217 #ifdef _CDCTL_SENSITIVE_EOT
219 start
= cd_trklist
[status_track
].track_start
;
220 stop
= start
+ cd_trklist
[status_track
].track_len
- _CDCTL_SENSITIVITY
;
221 now
= ((csci
.what
.position
.absaddr
.msf
.minute
) * 60 + csci
.what
.position
.absaddr
.msf
.second
) * 75 + csci
.what
.position
.absaddr
.msf
.frame
- CD_MSF_OFFSET
;
222 if(now
>0 && (now
<start
|| now
>=stop
)){
223 status_state
=ssPlaying
;
229 status_state
=ssStopped
;
234 status_state
=ssStopped
;
237 if(cd_trklist
[status_track
].track_data
)
242 void setVolume(int l
, int r
){
249 ioctl(cdfd
,CDIOCSETVOL
,&vol
);
256 ioctl(cdfd
,CDIOCGETVOL
,&vol
);
257 status_volumel
=vol
.vol
[0];
258 status_volumer
=vol
.vol
[1];
262 return status_volumel
;
265 return status_volumer
;
267 void setTrackSelection(int ts
){
270 int getTrackSelection(){
282 int getTrackStart(int trk
){
283 return cd_trklist
[trk
-1].track_start
;
285 int getTrackLen(int trk
){
286 return cd_trklist
[trk
-1].track_len
;
288 bool getTrackData(int trk
){
289 return cd_trklist
[trk
-1].track_data
;
291 int getStatusState(){
294 int getStatusTrack(){
295 return status_track
+1;
297 int getStatusPosAbs(){
298 return status_pos
-cd_trklist
[0].track_start
;
300 int getStatusPosRel(){
301 return status_pos
-cd_trklist
[status_track
].track_start
;
307 struct ioc_toc_header hdr
;
308 ioctl(cdfd
, CDIOREADTOCHEADER
, &hdr
);
309 cd_tracks
=hdr
.ending_track
;
310 cd_trklist
=(struct CDTrack
*)malloc(cd_tracks
*sizeof(struct CDTrack
));
311 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
312 struct ioc_read_toc_entry te
;
314 te
.data_len
= (cd_tracks
+ 1) * sizeof(struct cd_toc_entry
);
315 te
.data
= (struct cd_toc_entry
*)malloc(te
.data_len
);
316 te
.address_format
= CD_LBA_FORMAT
;
317 te
.starting_track
= 0;
318 ioctl(cdfd
, CDIOREADTOCENTRYS
, &te
);
319 for(int i
= 0; i
< cd_tracks
; i
++) {
320 cd_trklist
[i
].track_data
= te
.data
[i
].control
& 4 ? true : false;
321 cd_trklist
[i
].track_start
= ntohl(te
.data
[i
].addr
.lba
);
322 cd_trklist
[i
].track_len
= ntohl(te
.data
[i
+ 1].addr
.lba
)
323 - cd_trklist
[i
].track_start
;
325 cd_len
= ntohl(te
.data
[cd_tracks
].addr
.lba
);
328 struct cdrom_tocentry te
;
331 for(int i
=0;i
<=cd_tracks
;i
++){
333 te
.cdte_track
=CDROM_LEADOUT
;
336 te
.cdte_format
=CDROM_MSF
; // I think it is ok to read this as LBA, but for a quiet life...
337 ioctl(cdfd
, CDROMREADTOCENTRY
, &te
);
338 int this_addr
=((te
.cdte_addr
.msf
.minute
* 60) + te
.cdte_addr
.msf
.second
) * 75 + te
.cdte_addr
.msf
.frame
- CD_MSF_OFFSET
;
340 cd_trklist
[i
-1].track_len
= this_addr
- prev_addr
- 1;
343 cd_trklist
[i
].track_data
= te
.cdte_ctrl
& CDROM_DATA_TRACK
? true : false;
344 cd_trklist
[i
].track_start
= this_addr
;
351 void trackinfo(struct cd_sub_channel_info
*subchnl
){
352 int currenttrack
= status_track
;
354 if(status_state
==ssPlaying
|| status_state
==ssPaused
){
355 status_pos
=((subchnl
->what
.position
.absaddr
.msf
.minute
) * 60 + subchnl
->what
.position
.absaddr
.msf
.second
) * 75 + subchnl
->what
.position
.absaddr
.msf
.frame
- CD_MSF_OFFSET
;
356 for(status_track
=0;status_track
<cd_tracks
;status_track
++){
357 if(status_pos
<cd_trklist
[status_track
].track_start
+cd_trklist
[status_track
].track_len
) {
358 if (status_track
!= currenttrack
) {
359 status_track
= currenttrack
;
367 struct ioc_play_msf pmsf
;
368 int abs0
=status_pos
+ CD_MSF_OFFSET
;
369 int abs1
=cd_trklist
[status_track
].track_start
+ cd_trklist
[status_track
].track_len
- 1 + CD_MSF_OFFSET
;
370 pmsf
.start_m
=abs0
/(75*60);
371 pmsf
.end_m
=abs1
/(75*60);
372 pmsf
.start_s
=(abs0
%(75*60))/75;
373 pmsf
.end_s
=(abs1
%(75*60))/75;
374 pmsf
.start_f
=abs0
%75;
377 #ifdef _CDCTL_STOP_BEFORE_PLAY
378 ioctl(cdfd
,CDIOCSTOP
);
380 #ifdef _CDCTL_START_BEFORE_PLAY
381 ioctl(cdfd
,CDIOCSTART
);
384 ioctl(cdfd
,CDIOCPLAYMSF
,&pmsf
);
386 void select(int trk
){
388 status_pos
=cd_trklist
[status_track
].track_start
;
389 if(status_state
==ssPlaying
){
390 if(cd_trklist
[status_track
].track_data
){
392 #ifdef _CDCTL_HARD_STOP
393 ioctl(cdfd
,CDIOCSTOP
);
395 #ifndef _CDCTL_HARD_STOP
396 ioctl(cdfd
,CDIOCSTART
);
406 int newtrk
=status_track
;
418 if(newtrk
>=cd_tracks
){
425 if(newtrk
>=cd_tracks
)
429 newtrk
+=(int)((cd_tracks
-1)*(float)rand()/RAND_MAX
+1);
430 if(newtrk
>=cd_tracks
)
434 } while(cd_trklist
[newtrk
].track_data
);
452 struct CDTrack
*cd_trklist
;