wmcdplay: Centralize version number.
[dockapps.git] / wmcdplay / cdctl_freebsd.h
blob0be07bcb4cd70ac7c926fe35248946e01600dd08
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
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/ioctl.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <sys/cdio.h>
33 #include <arpa/inet.h>
34 #define CD_MSF_OFFSET 150
36 // CD status values
37 #define ssData 0
38 #define ssStopped 1
39 #define ssPlaying 2
40 #define ssPaused 3
41 #define ssNoCD 4
42 #define ssTrayOpen 5
44 // Audio command values
45 #define acStop 0
46 #define acPlay 1
47 #define acPause 2
48 #define acResume 3
49 #define acPrev 4
50 #define acNext 5
51 #define acRewd 6
52 #define acFFwd 7
53 #define acEject 8
54 #define acClose 9
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
62 // in between).
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
79 class CDCtl
81 public:
82 CDCtl(char *dname){
83 device=(char *)malloc(sizeof(char)*(strlen(dname)+1));
84 strcpy(device,dname);
85 srand(getpid());
86 tracksel=tsRandom;
87 tskOurPlay=false;
89 if(cdfdopen=(cdfd=open(device,O_RDONLY | O_NONBLOCK))!=-1){
90 status_state=ssNoCD;
91 status_track=0;
92 status_pos=0;
93 cd_trklist=NULL;
94 doStatus();
95 readVolume();
98 ~CDCtl(){
99 if(cdfdopen){
100 close(cdfd);
101 if(device!=NULL)
102 free(device);
103 if(cd_trklist!=NULL)
104 free(cd_trklist);
107 bool openOK(){
108 return cdfdopen;
110 void doAudioCommand(int cmd){
111 if(cdfdopen){
112 int newtrk=status_track;
113 switch(cmd){
114 case acStop:
116 #ifdef _CDCTL_SOFT_STOP
117 ioctl(cdfd,CDIOCSTART);
118 #endif
119 #ifndef _CDCTL_SOFT_STOP
120 ioctl(cdfd,CDIOCSTOP);
121 #endif
122 tskOurPlay=false;
124 break;
125 case acPlay:
126 status_state=ssPlaying;
127 select(status_track);
128 tskOurPlay=true;
129 break;
130 case acPause:
131 ioctl(cdfd,CDIOCPAUSE);
132 break;
133 case acResume:
134 ioctl(cdfd,CDIOCRESUME);
135 break;
136 case acPrev:
137 newtrk--;
138 if(newtrk<0)
139 newtrk=cd_tracks-1;
140 select(newtrk);
141 break;
142 case acNext:
143 newtrk++;
144 if(newtrk>cd_tracks-1)
145 newtrk=0;
146 select(newtrk);
147 break;
148 case acRewd:
149 if(status_pos>cd_trklist[status_track].track_start+_CDCTL_SKIP_SIZE){
150 status_pos-=_CDCTL_SKIP_SIZE;
151 play();
153 break;
154 case acFFwd:
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;
157 play();
159 break;
160 case acEject:
161 if(ioctl(cdfd,CDIOCEJECT))
162 status_state=ssNoCD;
163 else
164 status_state=ssTrayOpen;
165 break;
166 case acClose:
167 ioctl(cdfd,CDIOCCLOSE);
168 status_state=ssNoCD;
169 break;
171 doStatus();
174 void doStatus(){
175 if(cdfdopen){
176 struct ioc_read_subchannel sc;
177 struct cd_sub_channel_info csci;
178 sc.address_format=CD_MSF_FORMAT;
179 sc.track = 0;
180 sc.data=&csci;
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;
186 status_track=0;
187 status_pos=0;
188 tskOurPlay=false;
190 else{
191 if(status_state==ssNoCD || status_state==ssTrayOpen)
192 readTOC();
193 int start,now,stop;
194 switch(csci.header.audio_status){
195 case CD_AS_PLAY_IN_PROGRESS:
196 if(status_state==ssStopped)
197 tskOurPlay=false;
198 status_state=ssPlaying;
199 break;
200 case CD_AS_PLAY_PAUSED:
201 if(status_state==ssStopped)
202 tskOurPlay=false;
203 status_state=ssPaused;
204 break;
205 case CD_AS_PLAY_COMPLETED:
206 if(tskOurPlay){
207 status_state=ssPlaying;
208 selecttrack();
209 doStatus();
210 return;
212 else
213 status_state=ssStopped;
214 break;
215 default:
217 #ifdef _CDCTL_SENSITIVE_EOT
218 if(tskOurPlay){
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;
224 selecttrack();
225 doStatus();
226 return;
228 else
229 status_state=ssStopped;
231 else
232 #endif
234 status_state=ssStopped;
236 trackinfo(&csci);
237 if(cd_trklist[status_track].track_data)
238 status_state=ssData;
242 void setVolume(int l, int r){
243 if(cdfdopen){
244 struct ioc_vol vol;
245 vol.vol[0]=l;
246 vol.vol[1]=r;
247 vol.vol[2]=0;
248 vol.vol[3]=0;
249 ioctl(cdfd,CDIOCSETVOL,&vol);
250 readVolume();
253 void readVolume(){
254 if(cdfdopen){
255 struct ioc_vol vol;
256 ioctl(cdfd,CDIOCGETVOL,&vol);
257 status_volumel=vol.vol[0];
258 status_volumer=vol.vol[1];
261 int getVolumeL(){
262 return status_volumel;
264 int getVolumeR(){
265 return status_volumer;
267 void setTrackSelection(int ts){
268 tracksel=ts;
270 int getTrackSelection(){
271 return tracksel;
273 char *getDevName(){
274 return device;
276 int getCDTracks(){
277 return cd_tracks;
279 int getCDLen(){
280 return cd_len;
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(){
292 return status_state;
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;
303 private:
304 void readTOC(){
305 if(cd_trklist!=NULL)
306 free(cd_trklist);
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);
326 free(te.data);
327 #else
328 struct cdrom_tocentry te;
329 int prev_addr=0;
331 for(int i=0;i<=cd_tracks;i++){
332 if(i==cd_tracks)
333 te.cdte_track=CDROM_LEADOUT;
334 else
335 te.cdte_track=i+1;
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;
339 if(i>0)
340 cd_trklist[i-1].track_len = this_addr - prev_addr - 1;
341 prev_addr=this_addr;
342 if(i<cd_tracks){
343 cd_trklist[i].track_data = te.cdte_ctrl & CDROM_DATA_TRACK ? true : false;
344 cd_trklist[i].track_start = this_addr;
346 else
347 cd_len = this_addr;
349 #endif
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;
361 break;
366 void play(){
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;
375 pmsf.end_f=abs1%75;
377 #ifdef _CDCTL_STOP_BEFORE_PLAY
378 ioctl(cdfd,CDIOCSTOP);
379 #endif
380 #ifdef _CDCTL_START_BEFORE_PLAY
381 ioctl(cdfd,CDIOCSTART);
382 #endif
384 ioctl(cdfd,CDIOCPLAYMSF,&pmsf);
386 void select(int trk){
387 status_track=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);
394 #endif
395 #ifndef _CDCTL_HARD_STOP
396 ioctl(cdfd,CDIOCSTART);
397 #endif
398 tskOurPlay=false;
401 else
402 play();
405 void selecttrack(){
406 int newtrk=status_track;
408 switch(tracksel){
409 case tsNone:
410 tskOurPlay=false;
411 return;
412 break;
413 case tsRepeat:
414 // do nothing
415 break;
416 case tsNext:
417 newtrk++;
418 if(newtrk>=cd_tracks){
419 tskOurPlay=false;
420 return;
422 break;
423 case tsRepeatCD:
424 newtrk++;
425 if(newtrk>=cd_tracks)
426 newtrk=0;
427 break;
428 case tsRandom:
429 newtrk+=(int)((cd_tracks-1)*(float)rand()/RAND_MAX+1);
430 if(newtrk>=cd_tracks)
431 newtrk-=cd_tracks;
432 break;
434 } while(cd_trklist[newtrk].track_data);
435 select(newtrk);
436 play();
438 int cdfd;
439 int cdfdopen;
440 char *device;
441 int tracksel;
442 bool tskOurPlay;
444 struct CDTrack{
445 int track_start;
446 int track_len;
447 bool track_data;
450 int cd_tracks;
451 int cd_len;
452 struct CDTrack *cd_trklist;
453 int status_state;
454 int status_track;
455 int status_pos;
456 int status_volumel;
457 int status_volumer;