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