1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: i_cdmus_fbsd.c 538 2009-09-23 23:24:07Z smite-meister $
5 //
6 // Copyright (C) 1998-2000 by DooM Legacy Team.
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. See the
16 // GNU General Public License for more details.
17 //
18 //
19 // $Log: i_cdmus_fbsd.c,v $
20 // Revision 1.1 2001/05/16 22:33:35 bock
21 // Initial FreeBSD support.
22 //
23 //
24 // DESCRIPTION:
25 // cd music interface for FreeBSD
26 //
27 //-----------------------------------------------------------------------------
28
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <sys/ioctl.h>
32 #include <sys/file.h>
33 #include <sys/types.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <time.h>
37 #include <errno.h>
38
39 #include <sys/cdio.h>
40 #include "doomtype.h"
41 #include "i_sound.h"
42 #include "command.h"
43 #include "m_argv.h"
44
45 #define MAX_CD_TRACKS 256
46
47 static boolean cdValid = false;
48 static boolean playing = false;
49 static boolean wasPlaying = false;
50 static boolean initialized = false;
51 static boolean enabled = false;
52 static boolean playLooping = false;
53 static byte playTrack;
54 static byte maxTrack;
55 static byte cdRemap[MAX_CD_TRACKS];
56 static int cdvolume = -1;
57
58 CV_PossibleValue_t cd_volume_cons_t[]={{0,"MIN"},{31,"MAX"},{0,NULL}};
59
60 consvar_t cd_volume = {"cd_volume","31",CV_SAVE, cd_volume_cons_t};
61 consvar_t cdUpdate = {"cd_update","1",CV_SAVE};
62 consvar_t cv_jigglecdvol = {"jigglecdvolume", "0", CV_SAVE};
63
64 static int cdfile = -1;
65 static char cd_dev[64] = "/dev/cdrom";
66
67
CDAudio_GetAudioDiskInfo(void)68 static int CDAudio_GetAudioDiskInfo(void)
69 {
70 struct ioc_toc_header tochdr;
71
72 cdValid = false;
73
74 if ( ioctl(cdfile, CDIOREADTOCHEADER, &tochdr) == -1 ) {
75 CONS_Printf("ioctl cdromreadtochdr failed\n");
76 ioctl( cdfile, CDIOCALLOW);
77 return -1;
78 }
79 ioctl( cdfile, CDIOCALLOW);
80
81 if (tochdr.starting_track < 1) {
82 CONS_Printf("CDAudio_GetAudioDiskInfo: no music tracks\n");
83 return -1;
84 }
85
86 cdValid = true;
87 maxTrack = tochdr.ending_track;
88
89 return 0;
90 }
91
I_EjectCD(void)92 static void I_EjectCD(void)
93 {
94 if (cdfile == -1 || !enabled)
95 return; // no cd init'd
96
97 I_StopCD();
98
99 if ( ioctl(cdfile, CDIOCEJECT) == -1 )
100 CONS_Printf("ioctl cdromeject failed\n");
101 ioctl(cdfile, CDIOCALLOW);
102 }
103
Command_Cd_f(void)104 static void Command_Cd_f (void)
105 {
106 char *command;
107 int ret;
108 int n;
109
110 if (!initialized)
111 return;
112
113 if (COM_Argc() < 2) {
114 CONS_Printf ("cd [on] [off] [remap] [reset] [open]\n"
115 " [info] [play <track>] [resume]\n"
116 " [stop] [pause] [loop <track>]\n");
117 return;
118 }
119
120 command = COM_Argv (1);
121
122 if (!strncmp(command, "on", 2)) {
123 enabled = true;
124 return;
125 }
126
127 if (!strncmp(command, "off", 3)) {
128 I_StopCD();
129 enabled = false;
130 return;
131 }
132
133 if (!strncmp(command, "remap", 5)) {
134 ret = COM_Argc() - 2;
135 if (ret <= 0) {
136 for (n = 1; n < MAX_CD_TRACKS; n++)
137 if (cdRemap[n] != n)
138 CONS_Printf(" %u -> %u\n", n, cdRemap[n]);
139 return;
140 }
141 for (n = 1; n <= ret; n++)
142 cdRemap[n] = atoi(COM_Argv (n+1));
143 return;
144 }
145
146 if (!strncmp(command, "reset", 5)) {
147 enabled = true;
148 I_StopCD();
149
150 for (n = 0; n < MAX_CD_TRACKS; n++)
151 cdRemap[n] = n;
152 CDAudio_GetAudioDiskInfo();
153 return;
154 }
155
156 if (!cdValid) {
157 CDAudio_GetAudioDiskInfo();
158 if (!cdValid) {
159 CONS_Printf("No CD in player.\n");
160 return;
161 }
162 }
163
164 if (!strncmp(command, "open", 4)) {
165 I_EjectCD();
166 cdValid = false;
167 return;
168 }
169
170 if (!strncmp(command, "info", 4)) {
171 CONS_Printf("%u tracks\n", maxTrack);
172 if (playing)
173 CONS_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
174 else if (wasPlaying)
175 CONS_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack);
176 CONS_Printf("Volume is %d\n", cdvolume);
177 return;
178 }
179
180 if (!strncmp(command, "play", 4)) {
181 I_PlayCD((byte)atoi(COM_Argv (2)), false);
182 return;
183 }
184
185 if (!strncmp(command, "loop", 4)) {
186 I_PlayCD((byte)atoi(COM_Argv (2)), true);
187 return;
188 }
189
190 if (!strncmp(command, "stop", 4)) {
191 I_StopCD();
192 return;
193 }
194
195 if (!strncmp(command, "pause", 5)) {
196 I_PauseCD();
197 return;
198 }
199
200 if (!strncmp(command, "resume", 6)) {
201 I_ResumeCD();
202 return;
203 }
204
205 CONS_Printf("Invalid command \"cd %s\"\n", COM_Argv (1));
206 }
207
I_StopCD(void)208 void I_StopCD(void)
209 {
210 if (cdfile == -1 || !enabled)
211 return;
212
213 if (!(playing || wasPlaying))
214 return;
215
216 if ( ioctl(cdfile, CDIOCSTOP, 0) == -1 )
217 CONS_Printf("ioctl cdromstop failed (%d)\n", errno);
218 ioctl(cdfile, CDIOCALLOW);
219
220 wasPlaying = false;
221 playing = false;
222 }
223
I_PauseCD(void)224 void I_PauseCD (void)
225 {
226 if (cdfile == -1 || !enabled)
227 return;
228
229 if (!playing)
230 return;
231
232 if ( ioctl(cdfile, CDIOCPAUSE) == -1 )
233 CONS_Printf("ioctl cdrompause failed (%d)\n", errno);
234 ioctl(cdfile, CDIOCALLOW);
235
236 wasPlaying = playing;
237 playing = false;
238 }
239
240 // continue after a pause
I_ResumeCD(void)241 void I_ResumeCD (void)
242 {
243 if (cdfile == -1 || !enabled)
244 return;
245
246 if (!cdValid)
247 return;
248
249 if (!wasPlaying)
250 return;
251
252 if ( ioctl(cdfile, CDIOCRESUME) == -1 )
253 CONS_Printf("ioctl cdromresume failed\n");
254
255 playing = true;
256 wasPlaying = false;
257
258 if(cv_jigglecdvol.value)
259 {
260 I_SetVolumeCD(31-cd_volume.value);
261 I_SetVolumeCD(cd_volume.value);
262 }
263
264 return;
265 }
266
267
I_ShutdownCD(void)268 void I_ShutdownCD (void)
269 {
270 if (!initialized)
271 return;
272 I_StopCD();
273 close(cdfile);
274 cdfile = -1;
275
276 initialized = false;
277 enabled = false;
278 }
279
I_InitCD(void)280 void I_InitCD (void)
281 {
282 int i;
283
284 // Don't start music on a dedicated server
285 if (M_CheckParm("-dedicated"))
286 return ;
287
288 // Has been checked in d_main.c, but doesn't hurt here
289 if (M_CheckParm ("-nocd"))
290 return ;
291
292 // New commandline switch -cddev
293 if ((i = M_CheckParm("-cddev")) != 0 && M_IsNextParm()) {
294 strncpy(cd_dev, M_GetNextParm() , sizeof(cd_dev));
295 cd_dev[sizeof(cd_dev) - 1] = 0;
296 }
297
298 if ((cdfile = open(cd_dev, O_RDONLY)) == -1) {
299 int myerrno = errno;
300 CONS_Printf("I_InitCD: open of \"%s\" failed (%i)\n", cd_dev, myerrno);
301 if(EACCES == myerrno) // permission denied -> very common problem with IDE drives
302 CONS_Printf("-------------------------------------\n"
303 "Permission denied to open device %s\n"
304 "Set read permission or run as root\n"
305 "if in doubt *READ THE DOCS*\n"
306 "-------------------------------------\n", cd_dev); // Shall we add a line about this in the README?
307 cdfile = -1;
308 return ;
309 }
310 for (i = 0; i < MAX_CD_TRACKS; i++)
311 cdRemap[i] = i;
312
313 initialized = true;
314 enabled = true;
315
316 if (CDAudio_GetAudioDiskInfo()) {
317 CONS_Printf("I_InitCD: No CD in player.\n");
318 cdValid = false;
319 }
320
321 if(cv_jigglecdvol.value)
322 {
323 I_SetVolumeCD(31-cd_volume.value);
324 I_SetVolumeCD(cd_volume.value);
325 }
326
327 COM_AddCommand ("cd", Command_Cd_f);
328
329 CONS_Printf("CD Audio Initialized\n");
330
331 return ;
332 }
333
334
335
336 // loop/go to next track when track is finished (if cd_update var is true)
337 // update the volume when it has changed (from console/menu)
338 // FIXME: Why do we have Setvolume then ???
339 // TODO: check for cd change and restart music ?
340 //
I_UpdateCD(void)341 void I_UpdateCD (void)
342 {
343 struct ioc_read_subchannel subchnl;
344 struct cd_sub_channel_info data;
345 static time_t lastchk;
346 boolean was_valid;
347
348 if (!enabled)
349 return;
350
351 I_SetVolumeCD(cd_volume.value);
352
353 // FIXME: Do we have a "hicup" here every 2 secs?
354 if (playing && lastchk < time(NULL)) {
355 lastchk = time(NULL) + 2; //two seconds between chks
356 subchnl.address_format = CD_MSF_FORMAT;
357 subchnl.data_format = CD_CURRENT_POSITION;
358 subchnl.track = 0;
359 subchnl.data_len = sizeof(data);
360 subchnl.data = &data;
361 if (ioctl(cdfile, CDIOCREADSUBCHANNEL, &subchnl) == -1 ) {
362 CONS_Printf("ioctl cdromsubchnl failed\n");
363 ioctl(cdfile, CDIOCALLOW);
364 playing = false;
365 return;
366 }
367 if (data.header.audio_status != CD_AS_PLAY_IN_PROGRESS &&
368 data.header.audio_status != CD_AS_PLAY_PAUSED) {
369 playing = false;
370 if (playLooping)
371 I_PlayCD(playTrack, true);
372 }
373 }
374 }
375
376
377 // play the cd
I_PlayCD(int track,boolean looping)378 void I_PlayCD (int track, boolean looping)
379 {
380 struct ioc_read_toc_single_entry entry;
381 struct ioc_play_track ti;
382
383 if (cdfile == -1 || !enabled)
384 return;
385
386 if (!cdValid)
387 {
388 CDAudio_GetAudioDiskInfo();
389 if (!cdValid)
390 return;
391 }
392
393 track = cdRemap[track];
394
395 if (track < 1 || track > maxTrack)
396 {
397 CONS_Printf("I_PlayCD: Bad track number %u.\n", track);
398 return;
399 }
400
401 // don't try to play a non-audio track
402 entry.track = track;
403 entry.address_format = CD_MSF_FORMAT;
404 if ( ioctl(cdfile, CDIOREADTOCENTRY, &entry) == -1 )
405 {
406 CONS_Printf("ioctl cdromreadtocentry failed\n");
407 return;
408 }
409 ioctl( cdfile, CDIOCALLOW);
410 if (entry.entry.control == 0x04)
411 {
412 CONS_Printf("I_PlayCD: track %i is not audio\n", track);
413 return;
414 }
415
416 if(cv_jigglecdvol.value)
417 {
418 I_SetVolumeCD(31-cd_volume.value);
419 I_SetVolumeCD(cd_volume.value);
420 }
421
422 if (playing)
423 {
424 if (playTrack == track)
425 return;
426 I_StopCD();
427 }
428
429 ti.start_track = track;
430 ti.end_track = track;
431 ti.start_index = 1;
432 ti.end_index = 99;
433
434 if (ioctl(cdfile, CDIOCPLAYTRACKS, &ti) == -1)
435 {
436 CONS_Printf("ioctl cdiocplaytracks failed\n");
437 }
438 ioctl(cdfile, CDIOCALLOW);
439
440 if(cv_jigglecdvol.value)
441 {
442 I_SetVolumeCD(31-cd_volume.value);
443 I_SetVolumeCD(cd_volume.value);
444 }
445
446 playLooping = looping;
447 playTrack = track;
448 playing = true;
449 }
450
451
452 // volume : logical cd audio volume 0-31 (hardware is 0-255)
I_SetVolumeCD(int volume)453 int I_SetVolumeCD (int volume)
454 {
455 struct ioc_vol volctrl;
456
457 if(volume < 0 || volume > 31)
458 CONS_Printf("cdvolume should be between 0-31\n");
459
460 // volume control for CD music
461 if (volume != cdvolume){
462 volctrl.vol[0] = volume/31.0 * 255.0;
463 volctrl.vol[1] = volctrl.vol[0];
464 volctrl.vol[2] = 0;
465 volctrl.vol[3] = 0;
466
467 if(ioctl(cdfile, CDIOCSETVOL, &volctrl) == -1){
468 CONS_Printf("ioctl cdromvolctrl failed\n");
469 }
470
471 cdvolume = volume;
472 }
473
474 return 0;
475 }
476