1 /*
2 cd_linux.c
3
4 Linux CD Audio support
5
6 Copyright (C) 1996-1997 Id Software, Inc.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to:
21
22 Free Software Foundation, Inc.
23 59 Temple Place - Suite 330
24 Boston, MA 02111-1307, USA
25
26 */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #ifdef HAVE_STRING_H
32 # include <string.h>
33 #endif
34 #ifdef HAVE_STRINGS_H
35 # include <strings.h>
36 #endif
37 #ifdef HAVE_UNISTD_H
38 # include <unistd.h>
39 #endif
40 #ifdef HAVE_SYS_IOCTL_H
41 # include <sys/ioctl.h>
42 #endif
43
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <stdlib.h>
47 #include <time.h>
48 #include <linux/cdrom.h>
49
50 #include "QF/cdaudio.h"
51 #include "QF/cmd.h"
52 #include "QF/cvar.h"
53 #include "QF/qargs.h"
54 #include "QF/sound.h"
55 #include "QF/sys.h"
56
57 #include "QF/plugin/general.h"
58 #include "QF/plugin/cd.h"
59
60 #include "compat.h"
61
62 static plugin_t plugin_info;
63 static plugin_data_t plugin_info_data;
64 static plugin_funcs_t plugin_info_funcs;
65 static general_data_t plugin_info_general_data;
66 static general_funcs_t plugin_info_general_funcs;
67 static cd_funcs_t plugin_info_cd_funcs;
68
69 static qboolean cdValid = false;
70 static qboolean playing = false;
71 static qboolean wasPlaying = false;
72 static qboolean mus_enabled = false;
73 static qboolean playLooping = false;
74 static float cdvolume;
75 static byte remap[100];
76 static byte playTrack;
77 static byte maxTrack;
78 static int cdfile = -1;
79
80 static cvar_t *mus_cddevice;
81 static cvar_t *bgmvolume;
82
83
84 static void
I_CDAudio_CloseDoor(void)85 I_CDAudio_CloseDoor (void)
86 {
87 if (cdfile == -1 || !mus_enabled)
88 return; // no cd init'd
89
90 if (ioctl (cdfile, CDROMCLOSETRAY) == -1)
91 Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromclosetray failed\n");
92 }
93
94 static void
I_CDAudio_Eject(void)95 I_CDAudio_Eject (void)
96 {
97 if (cdfile == -1 || !mus_enabled)
98 return; // no cd init'd
99
100 if (ioctl (cdfile, CDROMEJECT) == -1)
101 Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromeject failed\n");
102 }
103
104 static int
I_CDAudio_GetAudioDiskInfo(void)105 I_CDAudio_GetAudioDiskInfo (void)
106 {
107 struct cdrom_tochdr tochdr;
108
109 cdValid = false;
110
111 if (ioctl (cdfile, CDROMREADTOCHDR, &tochdr) == -1) {
112 Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromreadtochdr failed\n");
113 return -1;
114 }
115
116 if (tochdr.cdth_trk0 < 1) {
117 Sys_MaskPrintf (SYS_SND, "CDAudio: no music tracks\n");
118 return -1;
119 }
120
121 cdValid = true;
122 maxTrack = tochdr.cdth_trk1;
123
124 return 0;
125 }
126
127 static void
I_CDAudio_Pause(void)128 I_CDAudio_Pause (void)
129 {
130 if (cdfile == -1 || !mus_enabled)
131 return;
132
133 if (!playing)
134 return;
135
136 if (ioctl (cdfile, CDROMPAUSE) == -1)
137 Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdrompause failed\n");
138
139 wasPlaying = playing;
140 playing = false;
141 }
142
143 static void
I_CDAudio_Stop(void)144 I_CDAudio_Stop (void)
145 {
146 if (cdfile == -1 || !mus_enabled)
147 return;
148
149 if (!playing)
150 return;
151
152 if (ioctl (cdfile, CDROMSTOP) == -1)
153 Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromstop failed (%d)\n",
154 errno);
155
156 wasPlaying = false;
157 playing = false;
158 }
159
160 static void
I_CDAudio_Play(int track,qboolean looping)161 I_CDAudio_Play (int track, qboolean looping)
162 {
163 struct cdrom_tocentry entry0;
164 struct cdrom_tocentry entry1;
165 struct cdrom_msf msf;
166
167 if (cdfile == -1 || !mus_enabled)
168 return;
169
170 if (!cdValid) {
171 I_CDAudio_GetAudioDiskInfo ();
172 if (!cdValid)
173 return;
174 }
175
176 if (track < 0 || track >= (int) sizeof (remap)) {
177 Sys_Printf ("CDAudio: invalid track number\n");
178 return;
179 }
180
181 track = remap[track];
182
183 if (track < 1 || track > maxTrack) {
184 I_CDAudio_Stop ();
185 return;
186 }
187 // don't try to play a non-audio track
188 entry0.cdte_track = track;
189 entry0.cdte_format = CDROM_MSF;
190 if (ioctl (cdfile, CDROMREADTOCENTRY, &entry0) == -1) {
191 Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromreadtocentry failed\n");
192 return;
193 }
194 entry1.cdte_track = track + 1;
195 entry1.cdte_format = CDROM_MSF;
196 if (entry1.cdte_track > maxTrack) {
197 entry1.cdte_track = CDROM_LEADOUT;
198 }
199 if (ioctl (cdfile, CDROMREADTOCENTRY, &entry1) == -1) {
200 Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromreadtocentry failed\n");
201 return;
202 }
203 if (entry0.cdte_ctrl == CDROM_DATA_TRACK) {
204 Sys_Printf ("track %i is not audio\n", track);
205 return;
206 }
207
208 if (playing) {
209 if (playTrack == track)
210 return;
211 I_CDAudio_Stop ();
212 }
213
214 msf.cdmsf_min0 = entry0.cdte_addr.msf.minute;
215 msf.cdmsf_sec0 = entry0.cdte_addr.msf.second;
216 msf.cdmsf_frame0 = entry0.cdte_addr.msf.frame;
217
218 msf.cdmsf_min1 = entry1.cdte_addr.msf.minute;
219 msf.cdmsf_sec1 = entry1.cdte_addr.msf.second;
220 msf.cdmsf_frame1 = entry1.cdte_addr.msf.frame;
221
222 Sys_MaskPrintf (SYS_SND, "%2d:%02d:%02d %2d:%02d:%02d\n",
223 msf.cdmsf_min0,
224 msf.cdmsf_sec0,
225 msf.cdmsf_frame0,
226 msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1);
227
228 if (ioctl (cdfile, CDROMPLAYMSF, &msf) == -1) {
229 Sys_MaskPrintf (SYS_SND,
230 "CDAudio: ioctl cdromplaytrkind failed (%s)\n",
231 strerror (errno));
232 return;
233 }
234
235 playLooping = looping;
236 playTrack = track;
237 playing = true;
238
239 if (cdvolume == 0.0)
240 I_CDAudio_Pause ();
241 }
242
243 static void
I_CDAudio_Resume(void)244 I_CDAudio_Resume (void)
245 {
246 if (cdfile == -1 || !mus_enabled)
247 return;
248
249 if (!cdValid)
250 return;
251
252 if (!wasPlaying)
253 return;
254
255 if (ioctl (cdfile, CDROMRESUME) == -1)
256 Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromresume failed\n");
257 playing = true;
258 }
259
260 static void
I_CDAudio_Shutdown(void)261 I_CDAudio_Shutdown (void)
262 {
263 if (cdfile != -1) {
264 I_CDAudio_Stop ();
265 close (cdfile);
266 cdfile = -1;
267 }
268 mus_enabled = false;
269 }
270
271 static void
I_CD_f(void)272 I_CD_f (void)
273 {
274 const char *command;
275 int ret, n;
276
277 if (Cmd_Argc () < 2)
278 return;
279
280 command = Cmd_Argv (1);
281
282 if (strequal (command, "on")) {
283 mus_enabled = true;
284 return;
285 }
286
287 if (strequal (command, "off")) {
288 if (playing)
289 I_CDAudio_Stop ();
290 mus_enabled = false;
291 return;
292 }
293
294 if (strequal (command, "reset")) {
295 mus_enabled = true;
296 if (playing)
297 I_CDAudio_Stop ();
298 for (n = 0; n < 100; n++)
299 remap[n] = n;
300 I_CDAudio_GetAudioDiskInfo ();
301 return;
302 }
303
304 if (strequal (command, "remap")) {
305 ret = Cmd_Argc () - 2;
306 if (ret <= 0) {
307 for (n = 1; n < 100; n++)
308 if (remap[n] != n)
309 Sys_Printf (" %u -> %u\n", n, remap[n]);
310 return;
311 }
312 for (n = 1; n <= ret; n++)
313 remap[n] = atoi (Cmd_Argv (n + 1));
314 return;
315 }
316
317 if (strequal (command, "close")) {
318 I_CDAudio_CloseDoor ();
319 return;
320 }
321
322 if (!cdValid) {
323 I_CDAudio_GetAudioDiskInfo ();
324 if (!cdValid) {
325 Sys_Printf ("No CD in player.\n");
326 return;
327 }
328 }
329
330 if (strequal (command, "play")) {
331 I_CDAudio_Play (atoi (Cmd_Argv (2)), false);
332 return;
333 }
334
335 if (strequal (command, "loop")) {
336 I_CDAudio_Play (atoi (Cmd_Argv (2)), true);
337 return;
338 }
339
340 if (strequal (command, "stop")) {
341 I_CDAudio_Stop ();
342 return;
343 }
344
345 if (strequal (command, "pause")) {
346 I_CDAudio_Pause ();
347 return;
348 }
349
350 if (strequal (command, "resume")) {
351 I_CDAudio_Resume ();
352 return;
353 }
354
355 if (strequal (command, "eject")) {
356 if (playing)
357 I_CDAudio_Stop ();
358 I_CDAudio_Eject ();
359 cdValid = false;
360 return;
361 }
362
363 if (strequal (command, "info")) {
364 Sys_Printf ("%u tracks\n", maxTrack);
365 if (playing)
366 Sys_Printf ("Currently %s track %u\n",
367 playLooping ? "looping" : "playing", playTrack);
368 else if (wasPlaying)
369 Sys_Printf ("Paused %s track %u\n",
370 playLooping ? "looping" : "playing", playTrack);
371 Sys_Printf ("Volume is %g\n", cdvolume);
372 return;
373 }
374 }
375
376 static void
I_CDAudio_Update(void)377 I_CDAudio_Update (void)
378 {
379 struct cdrom_subchnl subchnl;
380 static time_t lastchk;
381
382 if (!mus_enabled)
383 return;
384
385 if (bgmvolume->value != cdvolume) {
386 if (cdvolume) {
387 Cvar_SetValue (bgmvolume, 0.0);
388 cdvolume = bgmvolume->value;
389 I_CDAudio_Pause ();
390 } else {
391 Cvar_SetValue (bgmvolume, 1.0);
392 cdvolume = bgmvolume->value;
393 I_CDAudio_Resume ();
394 }
395 }
396
397 if (playing && lastchk < time (NULL)) {
398 lastchk = time (NULL) + 2; // two seconds between chks
399 subchnl.cdsc_format = CDROM_MSF;
400 if (ioctl (cdfile, CDROMSUBCHNL, &subchnl) == -1) {
401 Sys_MaskPrintf (SYS_SND, "CDAudio: ioctl cdromsubchnl failed\n");
402 playing = false;
403 return;
404 }
405 if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY &&
406 subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED) {
407 playing = false;
408 if (playLooping)
409 I_CDAudio_Play (playTrack, true);
410 }
411 }
412 }
413
414 static void
Mus_CDChange(cvar_t * mus_cdaudio)415 Mus_CDChange (cvar_t *mus_cdaudio)
416 {
417 int i;
418
419 I_CDAudio_Shutdown ();
420 if (strequal (mus_cdaudio->string, "none")) {
421 return;
422 }
423
424 cdfile = open (mus_cdaudio->string, O_RDONLY | O_NONBLOCK);
425 if (cdfile == -1) {
426 Sys_MaskPrintf (SYS_SND,
427 "Mus_CDInit: open device \"%s\" failed (error %i)\n",
428 mus_cdaudio->string, errno);
429 return;
430 }
431
432 if (I_CDAudio_GetAudioDiskInfo ()) {
433 Sys_Printf ("CDAudio_Init: No CD in player.\n");
434 cdValid = false;
435 }
436
437 for (i = 0; i < 100; i++)
438 remap[i] = i;
439
440 mus_enabled = true;
441 }
442
443 static void
I_CDAudio_Init(void)444 I_CDAudio_Init (void)
445 {
446 mus_cddevice = Cvar_Get ("mus_cddevice", "/dev/cdrom", CVAR_NONE,
447 Mus_CDChange, "device to use for CD music");
448 bgmvolume = Cvar_Get ("bgmvolume", "1", CVAR_ARCHIVE, NULL,
449 "Volume of CD music");
450 }
451
452 static general_funcs_t plugin_info_general_funcs = {
453 I_CDAudio_Init,
454 I_CDAudio_Shutdown,
455 };
456
457 static cd_funcs_t plugin_info_cd_funcs = {
458 I_CD_f,
459 I_CDAudio_Pause,
460 I_CDAudio_Play,
461 I_CDAudio_Resume,
462 I_CDAudio_Update,
463 };
464
465 static plugin_funcs_t plugin_info_funcs = {
466 &plugin_info_general_funcs,
467 0,
468 &plugin_info_cd_funcs,
469 0,
470 0,
471 0,
472 };
473
474 static plugin_data_t plugin_info_data = {
475 &plugin_info_general_data,
476 0,
477 0,
478 0,
479 0,
480 0,
481 };
482
483 static plugin_t plugin_info = {
484 qfp_cd,
485 0,
486 QFPLUGIN_VERSION,
487 "0.1",
488 "Linux CD Audio output\n",
489 "Copyright (C) 2001 contributors of the QuakeForge project\n"
490 "Please see the file \"AUTHORS\" for a list of contributors\n",
491 &plugin_info_funcs,
492 &plugin_info_data,
493 };
494
PLUGIN_INFO(cd,linux)495 PLUGIN_INFO (cd, linux)
496 {
497 return &plugin_info;
498 }
499