1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: i_cdmus.c 1474 2019-10-15 12:34:14Z wesleyjohnson $
5 //
6 // Copyright (C) 1998-2016 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.c,v $
20 // Revision 1.3  2001/05/16 22:33:35  bock
21 // Initial FreeBSD support.
22 //
23 // Revision 1.2  2000/09/10 10:56:00  metzgermeister
24 // Revision 1.1  2000/08/21 21:17:32  metzgermeister
25 // Initial import to CVS
26 //
27 // DESCRIPTION:
28 //      cd music interface
29 //
30 //-----------------------------------------------------------------------------
31 
32 // Because of WINVER redefine, put before any include that might define WINVER
33 #include "doomincl.h"
34 
35 #include <SDL.h>
36 
37 #include "i_sound.h"
38 #include "command.h"
39 #include "m_argv.h"
40 
41 // [WDJ] SDL cannot control CDROM volume.
42 // This is only present in the case someone wants to experiment, do not
43 // turn it on for releases.
44 // #define CDROM_VOLUME_CONTROL
45 
46 // [WDJ] SDL_cdrom.h is not present on some MAC SDL installs.
47 // This detects if SDL.h includes SDL/SDL_cdrom.h
48 // [WDJ] SDL2 does not even have CDROM API.
49 #if defined(SDL_AUDIO_TRACK) && ! defined(USE_SDL2)
50 
51 // Remap index is level map 1..34, or (episode-1)*9+map 1..36
52 #define MAX_MAPPING   40
53 #define MAX_CD_TRACKS 255
54 
55 static SDL_CD *cdrom = NULL;  // if non-NULL, the CD-ROM system is initialized
56 
57 static boolean cd_enabled = false; // do we want cd music? changed using the cd console command
58 static boolean play_looping = false;
59 static unsigned int playTrack; // track being played, 1..n
60 static byte  cdRemap[MAX_MAPPING];
61 
62 static int open_cdrom(void);
63 static void cd_volume_onchange(void);
64 
65 #ifdef CDROM_VOLUME_CONTROL
66 CV_PossibleValue_t cd_volume_cons_t[]={{0,"MIN"},{31,"MAX"},{0,NULL}};
67 consvar_t cd_volume = {"cd_volume", "31", CV_SAVE | CV_CALL, cd_volume_cons_t, cd_volume_onchange};
68 #else
69 // [WDJ] SDL cannot control CDROM volume.
70 CV_PossibleValue_t cd_volume_cons_t[]={{0,"MIN"},{1,"MAX"},{0,NULL}};
71 consvar_t cd_volume = {"cd_volume", "1", CV_SAVE | CV_CALL, cd_volume_cons_t, cd_volume_onchange};
72 #endif
73 
cd_volume_onchange(void)74 static void cd_volume_onchange(void)
75 {
76 
77   if (!cdrom)
78     return;
79 
80 #ifdef CDROM_VOLUME_CONTROL
81   // Experiment here.
82 #else
83   // HACK: SDL does not support setting the CD volume.
84   // Use pause instead and toggle between full and no music.
85   if (cd_volume.value > 0 && cdrom->status == CD_PAUSED)
86     I_ResumeCD();
87   else if (cd_volume.value == 0 && cdrom->status == CD_PLAYING)
88     I_PauseCD();
89 #endif
90 }
91 
92 
93 static Uint32  lastchk = 0;   // SDL time
94 
95 /**************************************************************************
96  *
97  * function: CDAudio_GetAudioDiskInfo
98  *
99  * description:
100  * update the SDL_CD status info behind the cdrom pointer
101  * returns true if there's a cd in the drive and it's ok
102  *
103  **************************************************************************/
CDAudio_GetAudioDiskInfo(void)104 static boolean CDAudio_GetAudioDiskInfo(void)
105 {
106   // use CD_INDRIVE() for cdValid
107   CDstatus cdStatus = SDL_CDStatus(cdrom);
108 
109   if (cdStatus == CD_ERROR)
110   {
111       CONS_Printf("CD Error: %s\n", SDL_GetError());
112       return false;
113   }
114 
115   if (!CD_INDRIVE(cdStatus))
116   {
117       if( verbose )
118           CONS_Printf("No CD in drive\n");
119       return false;
120   }
121 
122   return true;
123 }
124 
125 /**************************************************************************
126  *
127  * function: StopCD
128  *
129  * description:
130  *
131  *
132  **************************************************************************/
I_StopCD(void)133 void I_StopCD(void)
134 {
135   if (!cdrom)
136     return;
137 
138   lastchk = 0;
139 
140   if (SDL_CDStop(cdrom))
141   {
142       CONS_Printf("CD stop failed\n");
143   }
144 }
145 
146 /**************************************************************************
147  *
148  * function: I_EjectCD
149  *
150  * description:
151  *
152  *
153  **************************************************************************/
I_EjectCD(void)154 static void I_EjectCD(void)
155 {
156   if (!cdrom)
157     return; // no cd init'd
158 
159   I_StopCD();
160 
161   if (SDL_CDEject(cdrom))
162   {
163       GenPrintf(EMSG_warn, "CD eject failed\n");
164   }
165 }
166 
167 /**************************************************************************
168  *
169  * function: command_CD_f
170  *
171  * description:
172  * handles all CD commands from the console
173  *
174  **************************************************************************/
command_CD_f(void)175 static void command_CD_f (void)
176 {
177     char	*command;
178     int		ret;
179     int		n;
180 
181     if (COM_Argc() < 2) {
182 	CONS_Printf ("cd [on] [off] [remap] [reset] [open]\n"
183 		     "   [info] [play <track>] [resume]\n"
184 		     "   [stop] [pause] [loop <track>]\n");
185 	return;
186     }
187 
188     command = COM_Argv (1);
189 
190     if (!strncmp(command, "on", 2)) {
191 	cd_enabled = true;
192 	return;
193     }
194 
195     if (!strncmp(command, "off", 3)) {
196 	I_StopCD();
197 	cd_enabled = false;
198 	return;
199     }
200 
201     if (!strncmp(command, "remap", 5)) {
202 	ret = COM_Argc() - 2;
203 	if (ret <= 0) {
204 	    // list the mapping
205 	    for (n = 0; n < MAX_MAPPING; n++)
206 	    {
207 		if (cdRemap[n] != (n+1))
208 		    CONS_Printf("  %d -> %d\n", n, cdRemap[n]);
209 	    }
210 	    return;
211 	}
212 
213 	// set a mapping
214 	for (n = 0; n < ret; n++)
215 	    cdRemap[n] = atoi(COM_Argv(n+1));
216 	return;
217     }
218 
219     if (!strncmp(command, "reset", 5)) {
220 	cd_enabled = true;
221 	I_StopCD();
222 
223 	for (n = 0; n < MAX_MAPPING; n++)
224 	    cdRemap[n] = (n+1);
225 
226 	CDAudio_GetAudioDiskInfo();
227 	return;
228     }
229 
230     if (!cdrom)
231        open_cdrom();
232 
233     if (!strncmp(command, "info", 4))
234     {
235         if( !cdrom )
236         {
237 	    CONS_Printf("No CDROM");
238 	    return;
239 	}
240         SDL_CDStatus( cdrom );  // update status
241         CONS_Printf("CDROM: %s\n", SDL_CDName(0) );
242         CONS_Printf("%d tracks\n", cdrom->numtracks);
243 	if (cdrom->status == CD_PLAYING)
244     	    CONS_Printf("Currently %s track %d\n", play_looping ? "looping" : "playing", playTrack);
245 	else if (cdrom->status == CD_PAUSED)
246 	    CONS_Printf("Paused %s track %d\n", play_looping ? "looping" : "playing", playTrack);
247 	else
248             CONS_Printf("Not playing\n");
249 #ifdef CDROM_VOLUME_CONTROL
250 	CONS_Printf("Volume is %d\n", cd_volume.value);
251 #endif
252 	return;
253     }
254 
255     // from this point on, make sure the cd is ok
256     SDL_CDStatus( cdrom );  // update status
257     if ( !(CD_INDRIVE(cdrom->status)) ) {
258       if (!CDAudio_GetAudioDiskInfo()) // check if situation has changed
259         return;
260     }
261 
262     if (!strncmp(command, "open", 4)) {
263 	I_EjectCD();
264 	return;
265     }
266 
267     if (!strncmp(command, "play", 4)) {
268 	I_PlayCD(atoi(COM_Argv (2)), false);
269 	return;
270     }
271 
272     if (!strncmp(command, "loop", 4)) {
273 	I_PlayCD(atoi(COM_Argv (2)), true);
274 	return;
275     }
276 
277     if (!strncmp(command, "stop", 4)) {
278 	I_StopCD();
279 	return;
280     }
281 
282     if (!strncmp(command, "pause", 5)) {
283 	I_PauseCD();
284 	return;
285     }
286 
287     if (!strncmp(command, "resume", 6)) {
288 	I_ResumeCD();
289 	return;
290     }
291 
292     CONS_Printf("Invalid command \"cd %s\"\n", COM_Argv (1));
293 }
294 
295 
296 /**************************************************************************
297  *
298  * function: PauseCD
299  *
300  * description:
301  *
302  *
303  **************************************************************************/
I_PauseCD(void)304 void I_PauseCD (void)
305 {
306   if (!cdrom || !cd_enabled)
307     return;
308 
309   lastchk = 0;
310 
311   if (SDL_CDPause(cdrom))
312   {
313       CONS_Printf("CD pause failed\n");
314   }
315 }
316 
317 /**************************************************************************
318  *
319  * function: ResumeCD
320  *
321  * description:
322  *
323  *
324  **************************************************************************/
325 // continue after a pause
I_ResumeCD(void)326 void I_ResumeCD (void)
327 {
328   if (!cdrom || !cd_enabled)
329     return;
330 
331   // only enable play check if play looping
332   lastchk = ( play_looping )? 2 : 0;
333 
334   if (SDL_CDResume(cdrom))
335   {
336       CONS_Printf("CD resume failed\n");
337   }
338 }
339 
340 
341 /**************************************************************************
342  *
343  * function: ShutdownCD
344  *
345  * description:
346  *
347  *
348  **************************************************************************/
I_ShutdownCD(void)349 void I_ShutdownCD (void)
350 {
351   if (!cdrom)
352     return;
353 
354   I_StopCD();
355 
356   SDL_CDClose(cdrom);
357   cdrom = NULL;
358 }
359 
360 /**************************************************************************
361  *
362  * function: InitCD
363  *
364  * description:
365  * Initialize the first CD drive SDL detects and add console command 'cd'
366  *
367  **************************************************************************/
I_InitCD(void)368 void I_InitCD (void)
369 {
370   cdrom = NULL;
371 
372   // Initialize SDL cdrom subsystem
373   if (SDL_InitSubSystem(SDL_INIT_CDROM) < 0)
374   {
375       CONS_Printf(" Couldn't initialize SDL CD-ROM subsystem: %s\n", SDL_GetError());
376       return;
377   }
378 
379   if (SDL_CDNumDrives() < 1)
380   {
381       CONS_Printf(" No CD-ROM drives found.\n");
382       return;
383   }
384 
385   COM_AddCommand ("cd", command_CD_f, CC_command);
386 }
387 
388 
389 static
open_cdrom(void)390 int open_cdrom(void)
391 {
392   int i;
393 
394   if( cdrom )  return 1;   // already open
395 
396   // Open a drive
397   const char *cdName = SDL_CDName(0);
398   cdrom = SDL_CDOpen(0);
399 
400   if (!cdrom)
401   {
402       CONS_Printf("Error CD-ROM drive %s: %s\n",
403 		  ( cdName? cdName : "NULL" ), SDL_GetError());
404       return -1;
405   }
406   CONS_Printf("CD-ROM drive %s initialized.\n", cdName);
407 
408   // init track mapping
409   for (i = 0; i < MAX_MAPPING; i++)
410     cdRemap[i] = (i+1);
411 
412   cd_enabled = true;
413 
414   if (CDAudio_GetAudioDiskInfo())
415     CONS_Printf(" %d tracks found.\n", cdrom->numtracks);
416 
417   return 2;
418 }
419 
420 
421 //
422 /**************************************************************************
423  *
424  * function: UpdateCD
425  *
426  * description:
427  * checks the cd status and restarts play if looping
428  *
429  **************************************************************************/
I_UpdateCD(void)430 void I_UpdateCD (void)
431 {
432   if (!cdrom || !cd_enabled || !play_looping )
433     return;
434 
435   if( lastchk < 3 )
436   {
437       if( lastchk == 2 )
438       {
439 	  // Est. time to play,  + 4 secs.
440 	  SDL_CDStatus( cdrom );
441 	  int pt = cdrom->track[playTrack-1].length/CD_FPS;  // play time, secs
442 	  if( verbose )
443 	     CONS_Printf( "Play time %i secs\n", pt );
444           lastchk = SDL_GetTicks() + ( pt + 4 ) * 1000;
445 
446       }
447       return;
448   }
449 
450   if( lastchk < SDL_GetTicks() )
451   {
452       lastchk = SDL_GetTicks() + 4000; // 4 seconds between chks
453 
454       // [WDJ] Checking the CD status blocks for 1/4 second,
455       // which is very visible during play.
456       // So this is done only once per track played.
457 
458       // check the status
459 //      if (!CDAudio_GetAudioDiskInfo())
460 //	return; // no valid cd in drive
461       SDL_CDStatus( cdrom );  // update status
462       if (cdrom->status == CD_STOPPED && play_looping)
463 	I_PlayCD(playTrack|0x100, true);
464   }
465 }
466 
467 
468 
469 /**************************************************************************
470  *
471  * function: PlayCD
472  *
473  * description:
474  * play the requested track and set the looping flag
475  * pauses the CD if volume is 0
476  *
477  **************************************************************************/
478 //  track : 1 .. n
I_PlayCD(unsigned int track,boolean looping)479 void I_PlayCD (unsigned int track, boolean looping)
480 {
481   unsigned int  s_track;  // sdl track 0..n-1
482 
483   if( ! cdrom ) {  // to allow insert of disk after starting DoomLegacy
484       if( open_cdrom() < 0 )   return;
485   }
486 
487   if (!cdrom || !cd_enabled)
488     return;
489 
490   // Avoid checking status when possible.
491   if ( !(CD_INDRIVE(cdrom->status)) )
492   {
493       if (!CDAudio_GetAudioDiskInfo()) // check if situation has changed
494       {
495 	    CONS_Printf("No CD in drive.\n");
496 	    return;
497       }
498   }
499 
500   if( track & 0x100 )
501   {
502       // looping bypasses remap
503       track = track & 0x3f;
504   }
505   else
506   {
507       if( track < 1 )   track = 1;
508       if( track > MAX_MAPPING-1 )  track = MAX_MAPPING-1;
509       // cdRemap index is 0 .. n-1
510       track = cdRemap[track-1];
511       if( track == 0 )  return;
512       // SDL tracks are 0 .. n-1
513   }
514   s_track = track - 1;
515 
516   if (track > cdrom->numtracks)
517   {
518       CONS_Printf("I_PlayCD: Bad track number %d.\n", track);
519       return;
520   }
521 
522   // don't try to play a non-audio track
523   if (cdrom->track[s_track].type == SDL_DATA_TRACK)
524   {
525       CONS_Printf("I_PlayCD: track %d is not audio.\n", track);
526       return;
527   }
528 
529   if (cdrom->status == CD_PLAYING)
530   {
531       if (playTrack == track)
532 	return; // already playing it
533 
534       I_StopCD();
535   }
536 
537   if (SDL_CDPlayTracks(cdrom, s_track, 0, 1, 0))
538   {
539       CONS_Printf("Error playing track %d: %s\n", track, SDL_GetError());
540       return;
541   }
542 
543   play_looping = looping;
544   // only enable play check if play looping
545   lastchk = ( play_looping )? 2 : 0;
546   playTrack = track;
547 
548   if (cd_volume.value == 0)
549     I_PauseCD(); // cd "volume" hack
550 }
551 
552 
553 #else
554 // [WDJ] SDL2 does not have cdrom API.
555 // [WDJ] some MAC do not have SDL_cdrom.h
556 // This will stop errors for now.
557 
558 CV_PossibleValue_t cd_volume_cons_t[]={{0,"MIN"},{31,"MAX"},{0,NULL}};
559 consvar_t cd_volume = {"cd_volume", "31", CV_SAVE | CV_CALL, cd_volume_cons_t, NULL};
560 
I_StopCD(void)561 void I_StopCD(void)
562 {
563 }
564 
I_PauseCD(void)565 void I_PauseCD (void)
566 {
567 }
568 
I_ResumeCD(void)569 void I_ResumeCD (void)
570 {
571 }
572 
I_ShutdownCD(void)573 void I_ShutdownCD (void)
574 {
575 }
576 
I_InitCD(void)577 void I_InitCD (void)
578 {
579 }
580 
I_UpdateCD(void)581 void I_UpdateCD (void)
582 {
583 }
584 
I_PlayCD(unsigned int track,boolean looping)585 void I_PlayCD (unsigned int track, boolean looping)
586 {
587 }
588 
589 #endif
590