1 /*
2 * Copyright (C) 1997-2001 Id Software, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * 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 along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59
17 * 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 #if defined(CDAUDIO)
24
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <sys/ioctl.h>
29 #include <sys/file.h>
30 #include <sys/types.h>
31 #include <fcntl.h>
32 #include <string.h>
33 #include <time.h>
34 #include <errno.h>
35 #include <linux/cdrom.h>
36
37 #include "../client/client.h"
38
39 static qboolean cdValid = false;
40 static qboolean playing = false;
41 static qboolean wasPlaying = false;
42 static qboolean initialized = false;
43 static qboolean enabled = true;
44 static qboolean playLooping = false;
45 static float cdvolume;
46 static byte remap[100];
47 static byte playTrack;
48 static byte maxTrack;
49
50 static int cdfile = -1;
51
52 /* static char cd_dev[64] = "/dev/cdrom"; */
53
54 cvar_t *cd_volume;
55 cvar_t *cd_nocd;
56 cvar_t *cd_dev;
57
58 void CDAudio_Pause(void);
59
60 static void
CDAudio_Eject(void)61 CDAudio_Eject(void)
62 {
63 if (cdfile == -1 || !enabled)
64 return; /* no cd init'd */
65 if (ioctl(cdfile, CDROMEJECT) == -1)
66 Com_DPrintf("ioctl cdromeject failed\n");
67 }
68
69
70 static void
CDAudio_CloseDoor(void)71 CDAudio_CloseDoor(void)
72 {
73 if (cdfile == -1 || !enabled)
74 return; /* no cd init'd */
75
76 if (ioctl(cdfile, CDROMCLOSETRAY) == -1)
77 Com_DPrintf("ioctl cdromclosetray failed\n");
78 }
79
80 static int
CDAudio_GetAudioDiskInfo(void)81 CDAudio_GetAudioDiskInfo(void)
82 {
83 struct cdrom_tochdr tochdr;
84
85 cdValid = false;
86 if (ioctl(cdfile, CDROMREADTOCHDR, &tochdr) == -1) {
87 Com_DPrintf("ioctl cdromreadtochdr failed\n");
88 return -1;
89 }
90 if (tochdr.cdth_trk0 < 1) {
91 Com_DPrintf("CDAudio: no music tracks\n");
92 return -1;
93 }
94 cdValid = true;
95 maxTrack = tochdr.cdth_trk1;
96
97 return 0;
98 }
99
100
101 void
CDAudio_Play(int track,qboolean looping)102 CDAudio_Play(int track, qboolean looping)
103 {
104 struct cdrom_tocentry entry;
105 struct cdrom_ti ti;
106
107 if (cdfile == -1 || !enabled)
108 return;
109
110 if (!cdValid) {
111 CDAudio_GetAudioDiskInfo();
112 if (!cdValid)
113 return;
114 }
115 track = remap[track];
116
117 if (track < 1 || track > maxTrack) {
118 Com_DPrintf("CDAudio: Bad track number %u.\n", track);
119 return;
120 }
121 /* don't try to play a non-audio track */
122 entry.cdte_track = track;
123 entry.cdte_format = CDROM_LBA;
124 if (ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1) {
125 Com_DPrintf("ioctl cdromreadtocentry failed\n");
126 return;
127 }
128 if (entry.cdte_ctrl == CDROM_DATA_TRACK) {
129 Com_Printf("CDAudio: track %i is not audio\n", track);
130 return;
131 }
132 if (playing) {
133 if (playTrack == track)
134 return;
135 CDAudio_Stop();
136 }
137 ti.cdti_trk0 = track;
138 ti.cdti_trk1 = track;
139 ti.cdti_ind0 = 0;
140 ti.cdti_ind1 = 0;
141
142 if (ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1) {
143 Com_DPrintf("ioctl cdromplaytrkind failed\n");
144 return;
145 }
146 if (ioctl(cdfile, CDROMRESUME) == -1)
147 Com_DPrintf("ioctl cdromresume failed\n");
148
149 playLooping = looping;
150 playTrack = track;
151 playing = true;
152
153 if (cd_volume->value == 0.0)
154 CDAudio_Pause();
155 }
156
157 void
CDAudio_RandomPlay(void)158 CDAudio_RandomPlay(void)
159 {
160 int track, i = 0, free_tracks = 0, remap_track;
161 float f;
162 byte *track_bools;
163 struct cdrom_tocentry entry;
164 struct cdrom_ti ti;
165
166 if (cdfile == -1 || !enabled)
167 return;
168
169 track_bools = (byte *) Q_malloc(maxTrack * sizeof(byte));
170
171 if (track_bools == 0)
172 return;
173
174 /* create array of available audio tracknumbers */
175
176 for (; i < maxTrack; i++) {
177 entry.cdte_track = remap[i];
178 entry.cdte_format = CDROM_LBA;
179 if (ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1) {
180 track_bools[i] = 0;
181 } else
182 track_bools[i] = (entry.cdte_ctrl != CDROM_DATA_TRACK);
183
184 free_tracks += track_bools[i];
185 }
186
187 if (!free_tracks) {
188 Com_DPrintf("CDAudio_RandomPlay: Unable to find and play a random audio track, insert an audio cd please");
189 goto free_end;
190 }
191 /* choose random audio track */
192 do {
193 do {
194 f = ((float)rand()) / ((float)RAND_MAX + 1.0);
195 track = (int)(maxTrack * f);
196 }
197 while (!track_bools[track]);
198
199 remap_track = remap[track];
200
201 if (playing) {
202 if (playTrack == remap_track) {
203 goto free_end;
204 }
205 CDAudio_Stop();
206 }
207 ti.cdti_trk0 = remap_track;
208 ti.cdti_trk1 = remap_track;
209 ti.cdti_ind0 = 0;
210 ti.cdti_ind1 = 0;
211
212 if (ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1) {
213 track_bools[track] = 0;
214 free_tracks--;
215 } else {
216 playLooping = true;
217 playTrack = remap_track;
218 playing = true;
219 break;
220 }
221 }
222 while (free_tracks > 0);
223
224 free_end:
225 Q_free((void *)track_bools);
226 }
227
228 void
CDAudio_Stop(void)229 CDAudio_Stop(void)
230 {
231 if (cdfile == -1 || !enabled)
232 return;
233
234 if (!playing)
235 return;
236
237 if (ioctl(cdfile, CDROMSTOP) == -1)
238 Com_DPrintf("ioctl cdromstop failed (%d)\n", errno);
239
240 wasPlaying = false;
241 playing = false;
242 }
243
244 void
CDAudio_Pause(void)245 CDAudio_Pause(void)
246 {
247 if (cdfile == -1 || !enabled)
248 return;
249
250 if (!playing)
251 return;
252 if (ioctl(cdfile, CDROMPAUSE) == -1)
253 Com_DPrintf("ioctl cdrompause failed\n");
254
255 wasPlaying = playing;
256 playing = false;
257 }
258
259
260 void
CDAudio_Resume(void)261 CDAudio_Resume(void)
262 {
263 if (cdfile == -1 || !enabled)
264 return;
265
266 if (!cdValid)
267 return;
268
269 if (!wasPlaying)
270 return;
271
272 if (ioctl(cdfile, CDROMRESUME) == -1)
273 Com_DPrintf("ioctl cdromresume failed\n");
274 playing = true;
275 }
276
277 static void
CD_f(void)278 CD_f(void)
279 {
280 char *command;
281 int ret;
282 int n;
283
284 if (Cmd_Argc() < 2)
285 return;
286
287 command = Cmd_Argv(1);
288
289 if (Q_strcasecmp(command, "on") == 0) {
290 enabled = true;
291 return;
292 }
293 if (Q_strcasecmp(command, "off") == 0) {
294 if (playing)
295 CDAudio_Stop();
296 enabled = false;
297 return;
298 }
299 if (Q_strcasecmp(command, "reset") == 0) {
300 enabled = true;
301 if (playing)
302 CDAudio_Stop();
303 for (n = 0; n < 100; n++)
304 remap[n] = n;
305 CDAudio_GetAudioDiskInfo();
306 return;
307 }
308 if (Q_strcasecmp(command, "remap") == 0) {
309 ret = Cmd_Argc() - 2;
310 if (ret <= 0) {
311 for (n = 1; n < 100; n++)
312 if (remap[n] != n)
313 Com_Printf(" %u -> %u\n", n, remap[n]);
314 return;
315 }
316 for (n = 1; n <= ret; n++)
317 remap[n] = atoi(Cmd_Argv(n + 1));
318 return;
319 }
320 if (Q_strcasecmp(command, "close") == 0) {
321 CDAudio_CloseDoor();
322 return;
323 }
324 if (!cdValid) {
325 CDAudio_GetAudioDiskInfo();
326 if (!cdValid) {
327 Com_Printf("No CD in player.\n");
328 return;
329 }
330 }
331 if (Q_strcasecmp(command, "play") == 0) {
332 CDAudio_Play((byte) atoi(Cmd_Argv(2)), false);
333 return;
334 }
335 if (Q_strcasecmp(command, "loop") == 0) {
336 CDAudio_Play((byte) atoi(Cmd_Argv(2)), true);
337 return;
338 }
339 if (Q_strcasecmp(command, "stop") == 0) {
340 CDAudio_Stop();
341 return;
342 }
343 if (Q_strcasecmp(command, "pause") == 0) {
344 CDAudio_Pause();
345 return;
346 }
347 if (Q_strcasecmp(command, "resume") == 0) {
348 CDAudio_Resume();
349 return;
350 }
351 if (Q_strcasecmp(command, "eject") == 0) {
352 if (playing)
353 CDAudio_Stop();
354 CDAudio_Eject();
355 cdValid = false;
356 return;
357 }
358 if (!Q_strcasecmp(command, "random")) {
359 CDAudio_RandomPlay();
360 return;
361 }
362 if (Q_strcasecmp(command, "info") == 0) {
363 Com_Printf("%u tracks\n", maxTrack);
364 if (playing)
365 Com_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
366 else if (wasPlaying)
367 Com_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack);
368 Com_Printf("Volume is %f\n", cdvolume);
369 return;
370 }
371 }
372
373 void
CDAudio_Update(void)374 CDAudio_Update(void)
375 {
376 struct cdrom_subchnl subchnl;
377 static time_t lastchk;
378
379 if (cdfile == -1 || !enabled)
380 return;
381
382 if (cd_volume && cd_volume->value != cdvolume) {
383 if (cdvolume) {
384 Cvar_SetValue("cd_volume", 0.0);
385 cdvolume = cd_volume->value;
386 CDAudio_Pause();
387 } else {
388 Cvar_SetValue("cd_volume", 1.0);
389 cdvolume = cd_volume->value;
390 CDAudio_Resume();
391 }
392 }
393 if (playing && lastchk < time(NULL)) {
394 lastchk = time(NULL) + 2; /* two seconds between chks */
395 subchnl.cdsc_format = CDROM_MSF;
396 if (ioctl(cdfile, CDROMSUBCHNL, &subchnl) == -1) {
397 Com_DPrintf("ioctl cdromsubchnl failed\n");
398 playing = false;
399 return;
400 }
401 if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY &&
402 subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED) {
403 playing = false;
404 if (playLooping)
405 CDAudio_Play(playTrack, true);
406 }
407 }
408 }
409
410 int
CDAudio_Init(void)411 CDAudio_Init(void)
412 {
413 int i;
414 cvar_t *cv;
415 extern uid_t saved_euid;
416
417 if (initialized)
418 return 0;
419
420 cv = Cvar_Get("nocdaudio", "0", CVAR_NOSET);
421 if (cv->value)
422 return -1;
423
424 cd_nocd = Cvar_Get("cd_nocd", "0", CVAR_ARCHIVE);
425 if (cd_nocd->value)
426 return -1;
427
428 cd_volume = Cvar_Get("cd_volume", "1", CVAR_ARCHIVE);
429
430 cd_dev = Cvar_Get("cd_dev", "/dev/cdrom", CVAR_ARCHIVE);
431
432 seteuid(saved_euid);
433
434 cdfile = open(cd_dev->string, O_RDONLY | O_NONBLOCK | O_EXCL);
435
436 seteuid(getuid());
437
438 if (cdfile == -1) {
439 Com_Printf("CDAudio_Init: open of \"%s\" failed (%i)\n", cd_dev->string, errno);
440
441 cdfile = -1;
442 return -1;
443 }
444 for (i = 0; i < 100; i++)
445 remap[i] = i;
446 initialized = true;
447 enabled = true;
448
449 if (CDAudio_GetAudioDiskInfo()) {
450 Com_Printf("CDAudio_Init: No CD in player.\n");
451 cdValid = false;
452 }
453 Cmd_AddCommand("cd", CD_f);
454
455 Com_Printf("CD Audio Initialized\n");
456
457 return 0;
458 }
459
460 void
CDAudio_Activate(qboolean active)461 CDAudio_Activate(qboolean active)
462 {
463 if (active)
464 CDAudio_Resume();
465 else
466 CDAudio_Pause();
467 }
468
469 void
CDAudio_Shutdown(void)470 CDAudio_Shutdown(void)
471 {
472 if (!initialized)
473 return;
474 CDAudio_Stop();
475 close(cdfile);
476 cdfile = -1;
477 initialized = false;
478 }
479
480 #endif /* CD_AUDIO */
481
482