wmail: fixed use-after-free.
[dockapps.git] / wmcdplay / cdctl.h
blob3bdf75af34a850b818a9ba387cec6d9a079728d4
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 #ifdef __linux__
33 #include <linux/cdrom.h>
34 #endif
35 #ifdef __GNU__
36 #include <sys/cdrom.h>
37 #endif
39 // CD status values
40 #define ssData 0
41 #define ssStopped 1
42 #define ssPlaying 2
43 #define ssPaused 3
44 #define ssNoCD 4
45 #define ssTrayOpen 5
47 // Audio command values
48 #define acStop 0
49 #define acPlay 1
50 #define acPause 2
51 #define acResume 3
52 #define acPrev 4
53 #define acNext 5
54 #define acRewd 6
55 #define acFFwd 7
56 #define acEject 8
57 #define acClose 9
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
65 // in between).
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
82 class CDCtl
84 public:
85 CDCtl(char *dname){
86 device=(char *)malloc(sizeof(char)*(strlen(dname)+1));
87 strcpy(device,dname);
88 srand(getpid());
89 tracksel=tsRandom;
90 tskOurPlay=false;
92 if((cdfdopen = (cdfd = open(device,O_RDONLY | O_NONBLOCK))) != -1) {
93 status_state=ssNoCD;
94 status_track=0;
95 status_pos=0;
96 cd_trklist=NULL;
97 doStatus();
98 readVolume();
101 ~CDCtl(){
102 if(cdfdopen){
103 close(cdfd);
104 if(device!=NULL)
105 free(device);
106 if(cd_trklist!=NULL)
107 free(cd_trklist);
110 bool openOK(){
111 return cdfdopen;
113 void doAudioCommand(int cmd){
114 if(cdfdopen){
115 int newtrk=status_track;
116 switch(cmd){
117 case acStop:
119 #ifdef _CDCTL_SOFT_STOP
120 ioctl(cdfd,CDROMSTART);
121 #endif
122 #ifndef _CDCTL_SOFT_STOP
123 ioctl(cdfd,CDROMSTOP);
124 #endif
125 tskOurPlay=false;
127 break;
128 case acPlay:
129 status_state=ssPlaying;
130 select(status_track);
131 tskOurPlay=true;
132 break;
133 case acPause:
134 ioctl(cdfd,CDROMPAUSE);
135 break;
136 case acResume:
137 ioctl(cdfd,CDROMRESUME);
138 break;
139 case acPrev:
140 newtrk--;
141 if(newtrk<0)
142 newtrk=cd_tracks-1;
143 select(newtrk);
144 break;
145 case acNext:
146 newtrk++;
147 if(newtrk>cd_tracks-1)
148 newtrk=0;
149 select(newtrk);
150 break;
151 case acRewd:
152 if(status_pos>cd_trklist[status_track].track_start+_CDCTL_SKIP_SIZE){
153 status_pos-=_CDCTL_SKIP_SIZE;
154 play();
156 break;
157 case acFFwd:
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;
160 play();
162 break;
163 case acEject:
164 if(ioctl(cdfd,CDROMEJECT))
165 status_state=ssNoCD;
166 else
167 status_state=ssTrayOpen;
168 break;
169 case acClose:
170 ioctl(cdfd,CDROMCLOSETRAY);
171 status_state=ssNoCD;
172 break;
174 doStatus();
177 void doStatus(){
178 if(cdfdopen){
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;
184 status_track=0;
185 status_pos=0;
186 tskOurPlay=false;
188 else{
189 if(status_state==ssNoCD || status_state==ssTrayOpen)
190 readTOC();
191 int start,now,stop;
192 switch(sc.cdsc_audiostatus){
193 case CDROM_AUDIO_PLAY:
194 if(status_state==ssStopped)
195 tskOurPlay=false;
196 status_state=ssPlaying;
197 break;
198 case CDROM_AUDIO_PAUSED:
199 if(status_state==ssStopped)
200 tskOurPlay=false;
201 status_state=ssPaused;
202 break;
203 case CDROM_AUDIO_COMPLETED:
204 if(tskOurPlay){
205 status_state=ssPlaying;
206 selecttrack();
207 doStatus();
208 return;
210 else
211 status_state=ssStopped;
212 break;
213 default:
215 #ifdef _CDCTL_SENSITIVE_EOT
216 if(tskOurPlay){
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;
222 selecttrack();
223 doStatus();
224 return;
226 else
227 status_state=ssStopped;
229 else
230 #endif
232 status_state=ssStopped;
234 trackinfo(&sc);
235 if(cd_trklist[status_track].track_data)
236 status_state=ssData;
240 void setVolume(int l, int r){
241 if(cdfdopen){
242 struct cdrom_volctrl vol;
243 vol.channel0=l;
244 vol.channel1=r;
245 ioctl(cdfd,CDROMVOLCTRL,&vol);
246 readVolume();
249 void readVolume(){
250 if(cdfdopen){
251 struct cdrom_volctrl vol;
252 ioctl(cdfd,CDROMVOLREAD,&vol);
253 status_volumel=vol.channel0;
254 status_volumer=vol.channel1;
257 int getVolumeL(){
258 return status_volumel;
260 int getVolumeR(){
261 return status_volumer;
263 void setTrackSelection(int ts){
264 tracksel=ts;
266 int getTrackSelection(){
267 return tracksel;
269 char *getDevName(){
270 return device;
272 int getCDTracks(){
273 return cd_tracks;
275 int getCDLen(){
276 return cd_len;
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(){
288 return status_state;
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;
299 private:
300 void readTOC(){
301 if(cd_trklist!=NULL)
302 free(cd_trklist);
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;
308 int prev_addr=0;
309 for(int i=0;i<=cd_tracks;i++){
310 if(i==cd_tracks)
311 te.cdte_track=CDROM_LEADOUT;
312 else
313 te.cdte_track=i+1;
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;
317 if(i>0)
318 cd_trklist[i-1].track_len = this_addr - prev_addr - 1;
319 prev_addr=this_addr;
320 if(i<cd_tracks){
321 cd_trklist[i].track_data = te.cdte_ctrl & CDROM_DATA_TRACK ? true : false;
322 cd_trklist[i].track_start = this_addr;
324 else
325 cd_len = 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)
333 break;
337 void play(){
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);
350 #endif
351 #ifdef _CDCTL_START_BEFORE_PLAY
352 ioctl(cdfd,CDROMSTART);
353 #endif
355 ioctl(cdfd,CDROMPLAYMSF,&pmsf);
357 void select(int trk){
358 status_track=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);
365 #endif
366 #ifndef _CDCTL_HARD_STOP
367 ioctl(cdfd,CDROMSTART);
368 #endif
369 tskOurPlay=false;
372 else
373 play();
376 void selecttrack(){
377 int newtrk=status_track;
379 switch(tracksel){
380 case tsNone:
381 tskOurPlay=false;
382 return;
383 break;
384 case tsRepeat:
385 // do nothing
386 break;
387 case tsNext:
388 newtrk++;
389 if(newtrk>=cd_tracks){
390 tskOurPlay=false;
391 return;
393 break;
394 case tsRepeatCD:
395 newtrk++;
396 if(newtrk>=cd_tracks)
397 newtrk=0;
398 break;
399 case tsRandom:
400 newtrk+=(int)((cd_tracks-1)*(float)rand()/RAND_MAX+1);
401 if(newtrk>=cd_tracks)
402 newtrk-=cd_tracks;
403 break;
405 } while(cd_trklist[newtrk].track_data);
406 select(newtrk);
407 play();
409 int cdfd;
410 int cdfdopen;
411 char *device;
412 int tracksel;
413 bool tskOurPlay;
415 struct CDTrack{
416 int track_start;
417 int track_len;
418 bool track_data;
421 int cd_tracks;
422 int cd_len;
423 struct CDTrack *cd_trklist;
424 int status_state;
425 int status_track;
426 int status_pos;
427 int status_volumel;
428 int status_volumer;