1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20 // Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
21 // rights reserved.
22
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <sys/ioctl.h>
27 #include <sys/file.h>
28 #include <sys/types.h>
29 #include <fcntl.h>
30 #include <string.h>
31 #include <time.h>
32 #include <errno.h>
33
34 #if defined(__FreeBSD__)
35 #include <sys/cdio.h>
36 #else
37 #include <linux/cdrom.h>
38 #endif
39
40 #include "../client/client.h"
41
42 static qboolean cdValid = false;
43 static qboolean playing = false;
44 static qboolean wasPlaying = false;
45 static qboolean initialized = false;
46 static qboolean enabled = true;
47 static qboolean playLooping = false;
48 static float cdvolume;
49 static byte remap[100];
50 static byte playTrack;
51 static byte maxTrack;
52
53 static int cdfile = -1;
54
55 //static char cd_dev[64] = "/dev/cdrom";
56
57 cvar_t *cd_volume;
58 cvar_t *cd_nocd;
59 cvar_t *cd_dev;
60
61 void CDAudio_Pause(void);
62
CDAudio_Eject(void)63 static void CDAudio_Eject(void)
64 {
65 if (cdfile == -1 || !enabled)
66 return; // no cd init'd
67
68 #if defined(__FreeBSD__)
69 if ( ioctl(cdfile, CDIOCEJECT) == -1 )
70 Com_DPrintf("ioctl cdioeject failed\n");
71 #else
72 if ( ioctl(cdfile, CDROMEJECT) == -1 )
73 Com_DPrintf("ioctl cdromeject failed\n");
74 #endif
75 }
76
77
CDAudio_CloseDoor(void)78 static void CDAudio_CloseDoor(void)
79 {
80 if (cdfile == -1 || !enabled)
81 return; // no cd init'd
82
83 #if defined(__FreeBSD__)
84 if ( ioctl(cdfile, CDIOCCLOSE) == -1 )
85 Com_DPrintf("ioctl cdiocclose failed\n");
86 #else
87 if ( ioctl(cdfile, CDROMCLOSETRAY) == -1 )
88 Com_DPrintf("ioctl cdromclosetray failed\n");
89 #endif
90 }
91
CDAudio_GetAudioDiskInfo(void)92 static int CDAudio_GetAudioDiskInfo(void)
93 {
94 #if defined(__FreeBSD__)
95 struct ioc_toc_header tochdr;
96 #endif
97 #ifdef __linux__
98 struct cdrom_tochdr tochdr;
99 #endif
100
101 cdValid = false;
102
103 #if defined(__FreeBSD__)
104 if ( ioctl(cdfile, CDIOREADTOCHEADER, &tochdr) == -1 )
105 {
106 Com_DPrintf("ioctl cdioreadtocheader failed\n");
107 #endif
108 #ifdef __linux__
109 if ( ioctl(cdfile, CDROMREADTOCHDR, &tochdr) == -1 )
110 {
111 Com_DPrintf("ioctl cdromreadtochdr failed\n");
112 #endif
113 return -1;
114 }
115
116 #if defined(__FreeBSD__)
117 if (tochdr.starting_track < 1)
118 #endif
119 #ifdef __linux__
120 if (tochdr.cdth_trk0 < 1)
121 #endif
122 {
123 Com_DPrintf("CDAudio: no music tracks\n");
124 return -1;
125 }
126
127 cdValid = true;
128 #if defined(__FreeBSD__)
129 maxTrack = tochdr.ending_track;
130 #endif
131 #ifdef __linux__
132 maxTrack = tochdr.cdth_trk1;
133 #endif
134
135 return 0;
136 }
137
138
139 void CDAudio_Play(int track, qboolean looping)
140 {
141 #if defined(__FreeBSD__)
142 struct ioc_read_toc_entry entry;
143 struct cd_toc_entry toc_buffer;
144 struct ioc_play_track ti;
145 #endif
146 #if defined(__linux__)
147 struct cdrom_tocentry entry;
148 struct cdrom_ti ti;
149 #endif
150
151 if (cdfile == -1 || !enabled)
152 return;
153
154 if (!cdValid)
155 {
156 CDAudio_GetAudioDiskInfo();
157 if (!cdValid)
158 return;
159 }
160
161 track = remap[track];
162
163 if (track < 1 || track > maxTrack)
164 {
165 Com_DPrintf("CDAudio: Bad track number %u.\n", track);
166 return;
167 }
168
169 #if defined(__FreeBSD__)
170 #define CDROM_DATA_TRACK 4
171 bzero((char *)&toc_buffer, sizeof(toc_buffer));
172 entry.data_len = sizeof(toc_buffer);
173 entry.data = &toc_buffer;
174 // don't try to play a non-audio track
175 entry.starting_track = track;
176 entry.address_format = CD_MSF_FORMAT;
177 if ( ioctl(cdfile, CDIOREADTOCENTRYS, &entry) == -1 )
178 {
179 Com_DPrintf("ioctl cdromreadtocentry failed\n");
180 return;
181 }
182 if (toc_buffer.control == CDROM_DATA_TRACK)
183 #endif
184 #if defined(__linux__)
185 // don't try to play a non-audio track
186 entry.cdte_track = track;
187 entry.cdte_format = CDROM_LBA;
188 if ( ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1 )
189 {
190 Com_DPrintf("ioctl cdromreadtocentry failed\n");
191 return;
192 }
193 if (entry.cdte_ctrl == CDROM_DATA_TRACK)
194 #endif
195 {
196 Com_Printf("CDAudio: track %i is not audio\n", track);
197 return;
198 }
199
200
201 if (playing)
202 {
203 if (playTrack == track)
204 return;
205 CDAudio_Stop();
206 }
207
208 #if defined(__FreeBSD__)
209 ti.start_track = track;
210 ti.end_track = track;
211 ti.start_index = 1;
212 ti.end_index = 99;
213 #endif
214 #if defined(__linux__)
215 ti.cdti_trk0 = track;
216 ti.cdti_trk1 = track;
217 ti.cdti_ind0 = 0;
218 ti.cdti_ind1 = 0;
219 #endif
220
221 #if defined(__FreeBSD__)
222 if ( ioctl(cdfile, CDIOCPLAYTRACKS, &ti) == -1 )
223 #endif
224 #if defined(__linux__)
225 if ( ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1 )
226 #endif
227 {
228 Com_DPrintf("ioctl cdromplaytrkind failed\n");
229 return;
230 }
231
232 #if defined(__FreeBSD__)
233 if ( ioctl(cdfile, CDIOCRESUME) == -1 )
234 #endif
235 #if defined(__linux__)
236 if ( ioctl(cdfile, CDROMRESUME) == -1 )
237 #endif
238 Com_DPrintf("ioctl cdromresume failed\n");
239
240 playLooping = looping;
241 playTrack = track;
242 playing = true;
243
244 if (cd_volume->value == 0.0)
245 CDAudio_Pause ();
246 }
247
248 void CDAudio_RandomPlay(void)
249 {
250 int track, i = 0, free_tracks = 0, remap_track;
251 float f;
252 byte* track_bools;
253 #if defined(__FreeBSD__)
254 struct ioc_read_toc_entry entry;
255 struct cd_toc_entry toc_buffer;
256 struct ioc_play_track ti;
257 #endif
258 #if defined(__linux__)
259 struct cdrom_tocentry entry;
260 struct cdrom_ti ti;
261 #endif
262
263 if (cdfile == -1 || !enabled)
264 return;
265
266 track_bools = (byte*)malloc(maxTrack * sizeof(byte));
267
268 if (track_bools == 0)
269 return;
270
271 //create array of available audio tracknumbers
272
273 for (; i < maxTrack; i++)
274 {
275 #if defined(__FreeBSD__)
276 #define CDROM_DATA_TRACK 4
277 bzero((char *)&toc_buffer, sizeof(toc_buffer));
278 entry.data_len = sizeof(toc_buffer);
279 entry.data = &toc_buffer;
280
281 entry.starting_track = remap[i];
282 entry.address_format = CD_LBA_FORMAT;
283 if ( ioctl(cdfile, CDIOREADTOCENTRYS, &entry) == -1 )
284 {
285 track_bools[i] = 0;
286 }
287 else
288 track_bools[i] = (entry.data->control != CDROM_DATA_TRACK);
289 #endif
290 #if defined(__linux__)
291 entry.cdte_track = remap[i];
292 entry.cdte_format = CDROM_LBA;
293 if ( ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1 )
294 {
295 track_bools[i] = 0;
296 }
297 else
298 track_bools[i] = (entry.cdte_ctrl != CDROM_DATA_TRACK);
299 #endif
300
301 free_tracks += track_bools[i];
302 }
303
304 if (!free_tracks)
305 {
306 Com_DPrintf("CDAudio_RandomPlay: Unable to find and play a random audio track, insert an audio cd please");
307 goto free_end;
308 }
309
310 //choose random audio track
311 do
312 {
313 do
314 {
315 f = ((float)rand()) / ((float)RAND_MAX + 1.0);
316 track = (int)(maxTrack * f);
317 }
318 while(!track_bools[track]);
319
320 remap_track = remap[track];
321
322 if (playing)
323 {
324 if (playTrack == remap_track)
325 {
326 goto free_end;
327 }
328 CDAudio_Stop();
329 }
330
331 #if defined(__FreeBSD__)
332 #define CDROMPLAYTRKIND 0x5304
333
334 ti.start_track = remap_track;
335 ti.end_track = remap_track;
336 ti.start_index = 0;
337 ti.end_index = 0;
338 #endif
339 #if defined(__linux__)
340 ti.cdti_trk0 = remap_track;
341 ti.cdti_trk1 = remap_track;
342 ti.cdti_ind0 = 0;
343 ti.cdti_ind1 = 0;
344 #endif
345
346 if ( ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1 )
347 {
348 track_bools[track] = 0;
349 free_tracks--;
350 }
351 else
352 {
353 playLooping = true;
354 playTrack = remap_track;
355 playing = true;
356 break;
357 }
358 }
359 while (free_tracks > 0);
360
361 free_end:
362 free((void*)track_bools);
363 }
364
365 void CDAudio_Stop(void)
366 {
367 if (cdfile == -1 || !enabled)
368 return;
369
370 if (!playing)
371 return;
372
373 #if defined(__FreeBSD__)
374 if ( ioctl(cdfile, CDIOCSTOP) == -1 )
375 Com_DPrintf("ioctl cdiocstop failed (%d)\n", errno);
376 #endif
377 #if defined(__linux__)
378 if ( ioctl(cdfile, CDROMSTOP) == -1 )
379 Com_DPrintf("ioctl cdromstop failed (%d)\n", errno);
380 #endif
381
382 wasPlaying = false;
383 playing = false;
384 }
385
386 void CDAudio_Pause(void)
387 {
388 if (cdfile == -1 || !enabled)
389 return;
390
391 if (!playing)
392 return;
393
394 #if defined(__FreeBSD__)
395 if ( ioctl(cdfile, CDIOCPAUSE) == -1 )
396 Com_DPrintf("ioctl cdiocpause failed\n");
397 #endif
398 #if defined(__linux__)
399 if ( ioctl(cdfile, CDROMPAUSE) == -1 )
400 Com_DPrintf("ioctl cdrompause failed\n");
401 #endif
402
403 wasPlaying = playing;
404 playing = false;
405 }
406
407
408 void CDAudio_Resume(void)
409 {
410 if (cdfile == -1 || !enabled)
411 return;
412
413 if (!cdValid)
414 return;
415
416 if (!wasPlaying)
417 return;
418
419 #if defined(__FreeBSD__)
420 if ( ioctl(cdfile, CDIOCRESUME) == -1 )
421 Com_DPrintf("ioctl cdiocresume failed\n");
422 #endif
423 #if defined(__linux__)
424 if ( ioctl(cdfile, CDROMRESUME) == -1 )
425 Com_DPrintf("ioctl cdromresume failed\n");
426 #endif
427 playing = true;
428 }
429
430 static void CD_f (void)
431 {
432 char *command;
433 int ret;
434 int n;
435
436 if (Cmd_Argc() < 2)
437 return;
438
439 command = Cmd_Argv (1);
440
441 if (Q_strcasecmp(command, "on") == 0)
442 {
443 enabled = true;
444 return;
445 }
446
447 if (Q_strcasecmp(command, "off") == 0)
448 {
449 if (playing)
450 CDAudio_Stop();
451 enabled = false;
452 return;
453 }
454
455 if (Q_strcasecmp(command, "reset") == 0)
456 {
457 enabled = true;
458 if (playing)
459 CDAudio_Stop();
460 for (n = 0; n < 100; n++)
461 remap[n] = n;
462 CDAudio_GetAudioDiskInfo();
463 return;
464 }
465
466 if (Q_strcasecmp(command, "remap") == 0)
467 {
468 ret = Cmd_Argc() - 2;
469 if (ret <= 0)
470 {
471 for (n = 1; n < 100; n++)
472 if (remap[n] != n)
473 Com_Printf(" %u -> %u\n", n, remap[n]);
474 return;
475 }
476 for (n = 1; n <= ret; n++)
477 remap[n] = atoi(Cmd_Argv (n+1));
478 return;
479 }
480
481 if (Q_strcasecmp(command, "close") == 0)
482 {
483 CDAudio_CloseDoor();
484 return;
485 }
486
487 if (!cdValid)
488 {
489 CDAudio_GetAudioDiskInfo();
490 if (!cdValid)
491 {
492 Com_Printf("No CD in player.\n");
493 return;
494 }
495 }
496
497 if (Q_strcasecmp(command, "play") == 0)
498 {
499 CDAudio_Play((byte)atoi(Cmd_Argv (2)), false);
500 return;
501 }
502
503 if (Q_strcasecmp(command, "loop") == 0)
504 {
505 CDAudio_Play((byte)atoi(Cmd_Argv (2)), true);
506 return;
507 }
508
509 if (Q_strcasecmp(command, "stop") == 0)
510 {
511 CDAudio_Stop();
512 return;
513 }
514
515 if (Q_strcasecmp(command, "pause") == 0)
516 {
517 CDAudio_Pause();
518 return;
519 }
520
521 if (Q_strcasecmp(command, "resume") == 0)
522 {
523 CDAudio_Resume();
524 return;
525 }
526
527 if (Q_strcasecmp(command, "eject") == 0)
528 {
529 if (playing)
530 CDAudio_Stop();
531 CDAudio_Eject();
532 cdValid = false;
533 return;
534 }
535
536 if (Q_strcasecmp(command, "info") == 0)
537 {
538 Com_Printf("%u tracks\n", maxTrack);
539 if (playing)
540 Com_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
541 else if (wasPlaying)
542 Com_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack);
543 Com_Printf("Volume is %f\n", cdvolume);
544 return;
545 }
546 }
547
548 void CDAudio_Update(void)
549 {
550 #if defined(__FreeBSD__)
551 struct ioc_read_subchannel subchnl;
552 struct cd_sub_channel_info data;
553 #endif
554 #if defined(__linux__)
555 struct cdrom_subchnl subchnl;
556 #endif
557 static time_t lastchk;
558
559 if (cdfile == -1 || !enabled)
560 return;
561
562 if (cd_volume && cd_volume->value != cdvolume)
563 {
564 if (cdvolume)
565 {
566 Cvar_SetValue ("cd_volume", 0.0);
567 cdvolume = cd_volume->value;
568 CDAudio_Pause ();
569 }
570 else
571 {
572 Cvar_SetValue ("cd_volume", 1.0);
573 cdvolume = cd_volume->value;
574 CDAudio_Resume ();
575 }
576 }
577
578 if (playing && lastchk < time(NULL)) {
579 lastchk = time(NULL) + 2; //two seconds between chks
580 #if defined(__FreeBSD__)
581 subchnl.address_format = CD_MSF_FORMAT;
582 subchnl.data_format = CD_CURRENT_POSITION;
583 subchnl.data_len = sizeof(data);
584 subchnl.track = playTrack;
585 subchnl.data = &data;
586 if (ioctl(cdfile, CDIOCREADSUBCHANNEL, &subchnl) == -1 ) {
587 Com_DPrintf("ioctl cdiocreadsubchannel failed\n");
588 playing = false;
589 return;
590 }
591 if (subchnl.data->header.audio_status != CD_AS_PLAY_IN_PROGRESS &&
592 subchnl.data->header.audio_status != CD_AS_PLAY_PAUSED) {
593 playing = false;
594 if (playLooping)
595 CDAudio_Play(playTrack, true);
596 }
597 #endif
598 #if defined(__linux__)
599 subchnl.cdsc_format = CDROM_MSF;
600 if (ioctl(cdfile, CDROMSUBCHNL, &subchnl) == -1 ) {
601 Com_DPrintf("ioctl cdromsubchnl failed\n");
602 playing = false;
603 return;
604 }
605 if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY &&
606 subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED) {
607 playing = false;
608 if (playLooping)
609 CDAudio_Play(playTrack, true);
610 }
611 #endif
612 }
613 }
614
615 int CDAudio_Init(void)
616 {
617 int i;
618 cvar_t *cv;
619 extern uid_t saved_euid;
620
621 if (initialized)
622 return 0;
623
624 cv = Cvar_Get ("nocdaudio", "0", CVAR_NOSET);
625 if (cv->value)
626 return -1;
627
628 cd_nocd = Cvar_Get ("cd_nocd", "0", CVAR_ARCHIVE );
629 if ( cd_nocd->value)
630 return -1;
631
632 cd_volume = Cvar_Get ("cd_volume", "1", CVAR_ARCHIVE);
633
634 cd_dev = Cvar_Get("cd_dev", "/dev/cdrom", CVAR_ARCHIVE);
635
636 seteuid(saved_euid);
637
638 cdfile = open(cd_dev->string, O_RDONLY | O_NONBLOCK | O_EXCL);
639
640 seteuid(getuid());
641
642 if (cdfile == -1) {
643 Com_Printf("CDAudio_Init: open of \"%s\" failed (%i)\n", cd_dev->string, errno);
644
645 cdfile = -1;
646 return -1;
647 }
648
649 for (i = 0; i < 100; i++)
650 remap[i] = i;
651 initialized = true;
652 enabled = true;
653
654 if (CDAudio_GetAudioDiskInfo())
655 {
656 Com_Printf("CDAudio_Init: No CD in player.\n");
657 cdValid = false;
658 }
659
660 Cmd_AddCommand ("cd", CD_f);
661
662 Com_Printf("CD Audio Initialized\n");
663
664 return 0;
665 }
666
667 void CDAudio_Activate (qboolean active)
668 {
669 if (active)
670 CDAudio_Resume ();
671 else
672 CDAudio_Pause ();
673 }
674
675 void CDAudio_Shutdown(void)
676 {
677 if (!initialized)
678 return;
679 CDAudio_Stop();
680 close(cdfile);
681 cdfile = -1;
682 initialized = false;
683 }
684