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