1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20 // Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
21 // rights reserved.
22
23 #include <windows.h>
24 #include "../client/client.h"
25
26 extern HWND cl_hwnd;
27
28 static qboolean cdValid = false;
29 static qboolean playing = false;
30 static qboolean wasPlaying = false;
31 static qboolean initialized = false;
32 static qboolean enabled = false;
33 static qboolean playLooping = false;
34 static byte remap[100];
35 static byte cdrom;
36 static byte playTrack;
37 static byte maxTrack;
38
39 cvar_t *cd_nocd;
40 cvar_t *cd_loopcount;
41 cvar_t *cd_looptrack;
42
43 UINT wDeviceID;
44 int loopcounter;
45
46
47 void CDAudio_Pause(void);
48
CDAudio_Eject(void)49 static void CDAudio_Eject(void)
50 {
51 DWORD dwReturn;
52
53 if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, (DWORD)NULL))
54 Com_DPrintf("MCI_SET_DOOR_OPEN failed (%i)\n", dwReturn);
55 }
56
57
CDAudio_CloseDoor(void)58 static void CDAudio_CloseDoor(void)
59 {
60 DWORD dwReturn;
61
62 if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, (DWORD)NULL))
63 Com_DPrintf("MCI_SET_DOOR_CLOSED failed (%i)\n", dwReturn);
64 }
65
66
CDAudio_GetAudioDiskInfo(void)67 static int CDAudio_GetAudioDiskInfo(void)
68 {
69 DWORD dwReturn;
70 MCI_STATUS_PARMS mciStatusParms;
71
72
73 cdValid = false;
74
75 mciStatusParms.dwItem = MCI_STATUS_READY;
76 dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms);
77 if (dwReturn)
78 {
79 Com_DPrintf("CDAudio: drive ready test - get status failed\n");
80 return -1;
81 }
82 if (!mciStatusParms.dwReturn)
83 {
84 Com_DPrintf("CDAudio: drive not ready\n");
85 return -1;
86 }
87
88 mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
89 dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms);
90 if (dwReturn)
91 {
92 Com_DPrintf("CDAudio: get tracks - status failed\n");
93 return -1;
94 }
95 if (mciStatusParms.dwReturn < 1)
96 {
97 Com_DPrintf("CDAudio: no music tracks\n");
98 return -1;
99 }
100
101 cdValid = true;
102 maxTrack = mciStatusParms.dwReturn;
103
104 return 0;
105 }
106
CDAudio_Play2(int track,qboolean looping)107 void CDAudio_Play2(int track, qboolean looping)
108 {
109 DWORD dwReturn;
110 MCI_PLAY_PARMS mciPlayParms;
111 MCI_STATUS_PARMS mciStatusParms;
112
113 if (!enabled)
114 return;
115
116 if (!cdValid)
117 {
118 CDAudio_GetAudioDiskInfo();
119 if (!cdValid)
120 return;
121 }
122
123 track = remap[track];
124
125 if (track < 1 || track > maxTrack)
126 {
127 CDAudio_Stop();
128 return;
129 }
130
131 // don't try to play a non-audio track
132 mciStatusParms.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
133 mciStatusParms.dwTrack = track;
134 dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms);
135 if (dwReturn)
136 {
137 Com_DPrintf("MCI_STATUS failed (%i)\n", dwReturn);
138 return;
139 }
140 if (mciStatusParms.dwReturn != MCI_CDA_TRACK_AUDIO)
141 {
142 Com_Printf("CDAudio: track %i is not audio\n", track);
143 return;
144 }
145
146 // get the length of the track to be played
147 mciStatusParms.dwItem = MCI_STATUS_LENGTH;
148 mciStatusParms.dwTrack = track;
149 dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms);
150 if (dwReturn)
151 {
152 Com_DPrintf("MCI_STATUS failed (%i)\n", dwReturn);
153 return;
154 }
155
156 if (playing)
157 {
158 if (playTrack == track)
159 return;
160 CDAudio_Stop();
161 }
162
163 mciPlayParms.dwFrom = MCI_MAKE_TMSF(track, 0, 0, 0);
164 mciPlayParms.dwTo = (mciStatusParms.dwReturn << 8) | track;
165 mciPlayParms.dwCallback = (DWORD)cl_hwnd;
166 dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_FROM | MCI_TO, (DWORD)(LPVOID) &mciPlayParms);
167 if (dwReturn)
168 {
169 Com_DPrintf("CDAudio: MCI_PLAY failed (%i)\n", dwReturn);
170 return;
171 }
172
173 playLooping = looping;
174 playTrack = track;
175 playing = true;
176
177 if ( Cvar_VariableValue( "cd_nocd" ) )
178 CDAudio_Pause ();
179 }
180
181
CDAudio_Play(int track,qboolean looping)182 void CDAudio_Play(int track, qboolean looping)
183 {
184 // set a loop counter so that this track will change to the
185 // looptrack later
186 loopcounter = 0;
187 CDAudio_Play2(track, looping);
188 }
189
CDAudio_RandomPlay(void)190 void CDAudio_RandomPlay(void)
191 {
192 int track, i = 0, free_tracks = 0, remap_track;
193 float f;
194 byte* track_bools;;
195 DWORD dwReturn;
196 MCI_PLAY_PARMS mciPlayParms;
197 MCI_STATUS_PARMS mciStatusParms;
198
199 if (!enabled)
200 return;
201
202 //create array of available audio tracknumbers
203
204 track_bools = (byte*)malloc(maxTrack * sizeof(byte));
205
206 if (track_bools == 0)
207 return;
208
209 for (; i < maxTrack; i++)
210 {
211 // don't try to play a non-audio track
212 mciStatusParms.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
213 mciStatusParms.dwTrack = remap[i];
214 dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms);
215 if (dwReturn)
216 {
217 track_bools[i] = 0;
218 }
219 else
220 track_bools[i] = (mciStatusParms.dwReturn == MCI_CDA_TRACK_AUDIO);
221
222 free_tracks += track_bools[i];
223 }
224
225 if (!free_tracks)
226 {
227 Com_DPrintf("CDAudio_RandomPlay: Unable to find and play a random audio track, insert an audio cd please");
228 goto free_end;
229 }
230
231 //choose random audio track
232 do
233 {
234 do
235 {
236 f = ((float)rand()) / ((float)RAND_MAX + 1.0);
237 track = (int)(maxTrack * f);
238 }
239 while(!track_bools[track]);
240
241 remap_track = remap[track];
242
243 // get the length of the track to be played
244 mciStatusParms.dwItem = MCI_STATUS_LENGTH;
245 mciStatusParms.dwTrack = remap_track;
246 dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms);
247 if (dwReturn)
248 {
249 Com_DPrintf("MCI_STATUS failed (%i)\n", dwReturn);
250 goto free_end;
251 }
252
253 if (playing)
254 {
255 if (playTrack == remap_track)
256 {
257 goto free_end;
258 }
259 CDAudio_Stop();
260 }
261
262 mciPlayParms.dwFrom = MCI_MAKE_TMSF(remap_track, 0, 0, 0);
263 mciPlayParms.dwTo = (mciStatusParms.dwReturn << 8) | remap_track;
264 mciPlayParms.dwCallback = (DWORD)cl_hwnd;
265 dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_FROM | MCI_TO, (DWORD)(LPVOID) &mciPlayParms);
266 if (dwReturn)
267 {
268 track_bools[track] = 0;
269 free_tracks--;
270 }
271 else
272 {
273 playLooping = true;
274 playTrack = remap_track;
275 playing = true;
276 break;
277 }
278 while (free_tracks > 0);
279
280 free_end:
281 free((void*)track_bools);
282 }
283
284 void CDAudio_Stop(void)
285 {
286 DWORD dwReturn;
287
288 if (!enabled)
289 return;
290
291 if (!playing)
292 return;
293
294 if (dwReturn = mciSendCommand(wDeviceID, MCI_STOP, 0, (DWORD)NULL))
295 Com_DPrintf("MCI_STOP failed (%i)", dwReturn);
296
297 wasPlaying = false;
298 playing = false;
299 }
300
301
302 void CDAudio_Pause(void)
303 {
304 DWORD dwReturn;
305 MCI_GENERIC_PARMS mciGenericParms;
306
307 if (!enabled)
308 return;
309
310 if (!playing)
311 return;
312
313 mciGenericParms.dwCallback = (DWORD)cl_hwnd;
314 if (dwReturn = mciSendCommand(wDeviceID, MCI_PAUSE, 0, (DWORD)(LPVOID) &mciGenericParms))
315 Com_DPrintf("MCI_PAUSE failed (%i)", dwReturn);
316
317 wasPlaying = playing;
318 playing = false;
319 }
320
321
322 void CDAudio_Resume(void)
323 {
324 DWORD dwReturn;
325 MCI_PLAY_PARMS mciPlayParms;
326
327 if (!enabled)
328 return;
329
330 if (!cdValid)
331 return;
332
333 if (!wasPlaying)
334 return;
335
336 mciPlayParms.dwFrom = MCI_MAKE_TMSF(playTrack, 0, 0, 0);
337 mciPlayParms.dwTo = MCI_MAKE_TMSF(playTrack + 1, 0, 0, 0);
338 mciPlayParms.dwCallback = (DWORD)cl_hwnd;
339 dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_TO | MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms);
340 if (dwReturn)
341 {
342 Com_DPrintf("CDAudio: MCI_PLAY failed (%i)\n", dwReturn);
343 return;
344 }
345 playing = true;
346 }
347
348
349 static void CD_f (void)
350 {
351 char *command;
352 int ret;
353 int n;
354
355 if (Cmd_Argc() < 2)
356 return;
357
358 command = Cmd_Argv (1);
359
360 if (Q_strcasecmp(command, "on") == 0)
361 {
362 enabled = true;
363 return;
364 }
365
366 if (Q_strcasecmp(command, "off") == 0)
367 {
368 if (playing)
369 CDAudio_Stop();
370 enabled = false;
371 return;
372 }
373
374 if (Q_strcasecmp(command, "reset") == 0)
375 {
376 enabled = true;
377 if (playing)
378 CDAudio_Stop();
379 for (n = 0; n < 100; n++)
380 remap[n] = n;
381 CDAudio_GetAudioDiskInfo();
382 return;
383 }
384
385 if (Q_strcasecmp(command, "remap") == 0)
386 {
387 ret = Cmd_Argc() - 2;
388 if (ret <= 0)
389 {
390 for (n = 1; n < 100; n++)
391 if (remap[n] != n)
392 Com_Printf(" %u -> %u\n", n, remap[n]);
393 return;
394 }
395 for (n = 1; n <= ret; n++)
396 remap[n] = atoi(Cmd_Argv (n+1));
397 return;
398 }
399
400 if (Q_strcasecmp(command, "close") == 0)
401 {
402 CDAudio_CloseDoor();
403 return;
404 }
405
406 if (!cdValid)
407 {
408 CDAudio_GetAudioDiskInfo();
409 if (!cdValid)
410 {
411 Com_Printf("No CD in player.\n");
412 return;
413 }
414 }
415
416 if (Q_strcasecmp(command, "play") == 0)
417 {
418 CDAudio_Play(atoi(Cmd_Argv (2)), false);
419 return;
420 }
421
422 if (Q_strcasecmp(command, "loop") == 0)
423 {
424 CDAudio_Play(atoi(Cmd_Argv (2)), true);
425 return;
426 }
427
428 if (Q_strcasecmp(command, "stop") == 0)
429 {
430 CDAudio_Stop();
431 return;
432 }
433
434 if (Q_strcasecmp(command, "pause") == 0)
435 {
436 CDAudio_Pause();
437 return;
438 }
439
440 if (Q_strcasecmp(command, "resume") == 0)
441 {
442 CDAudio_Resume();
443 return;
444 }
445
446 if (Q_strcasecmp(command, "eject") == 0)
447 {
448 if (playing)
449 CDAudio_Stop();
450 CDAudio_Eject();
451 cdValid = false;
452 return;
453 }
454
455 if (Q_strcasecmp(command, "info") == 0)
456 {
457 Com_Printf("%u tracks\n", maxTrack);
458 if (playing)
459 Com_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
460 else if (wasPlaying)
461 Com_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack);
462 return;
463 }
464 }
465
466
467 LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
468 {
469 if (lParam != wDeviceID)
470 return 1;
471
472 switch (wParam)
473 {
474 case MCI_NOTIFY_SUCCESSFUL:
475 if (playing)
476 {
477 playing = false;
478 if (playLooping)
479 {
480 // if the track has played the given number of times,
481 // go to the ambient track
482 if (++loopcounter >= cd_loopcount->value)
483 CDAudio_Play2(cd_looptrack->value, true);
484 else
485 CDAudio_Play2(playTrack, true);
486 }
487 }
488 break;
489
490 case MCI_NOTIFY_ABORTED:
491 case MCI_NOTIFY_SUPERSEDED:
492 break;
493
494 case MCI_NOTIFY_FAILURE:
495 Com_DPrintf("MCI_NOTIFY_FAILURE\n");
496 CDAudio_Stop ();
497 cdValid = false;
498 break;
499
500 default:
501 Com_DPrintf("Unexpected MM_MCINOTIFY type (%i)\n", wParam);
502 return 1;
503 }
504
505 return 0;
506 }
507
508
509 void CDAudio_Update(void)
510 {
511 if ( cd_nocd->value != !enabled )
512 {
513 if ( cd_nocd->value )
514 {
515 CDAudio_Stop();
516 enabled = false;
517 }
518 else
519 {
520 enabled = true;
521 CDAudio_Resume ();
522 }
523 }
524 }
525
526
527 int CDAudio_Init(void)
528 {
529 DWORD dwReturn;
530 MCI_OPEN_PARMS mciOpenParms;
531 MCI_SET_PARMS mciSetParms;
532 int n;
533
534 if (initialized)
535 return;
536
537 cd_nocd = Cvar_Get ("cd_nocd", "0", CVAR_ARCHIVE );
538 cd_loopcount = Cvar_Get ("cd_loopcount", "4", 0);
539 cd_looptrack = Cvar_Get ("cd_looptrack", "11", 0);
540 if ( cd_nocd->value)
541 return -1;
542
543 mciOpenParms.lpstrDeviceType = "cdaudio";
544 if (dwReturn = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE, (DWORD) (LPVOID) &mciOpenParms))
545 {
546 Com_Printf("CDAudio_Init: MCI_OPEN failed (%i)\n", dwReturn);
547 return -1;
548 }
549 wDeviceID = mciOpenParms.wDeviceID;
550
551 // Set the time format to track/minute/second/frame (TMSF).
552 mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF;
553 if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID) &mciSetParms))
554 {
555 Com_Printf("MCI_SET_TIME_FORMAT failed (%i)\n", dwReturn);
556 mciSendCommand(wDeviceID, MCI_CLOSE, 0, (DWORD)NULL);
557 return -1;
558 }
559
560 for (n = 0; n < 100; n++)
561 remap[n] = n;
562 initialized = true;
563 enabled = true;
564
565 if (CDAudio_GetAudioDiskInfo())
566 {
567 // Com_Printf("CDAudio_Init: No CD in player.\n");
568 cdValid = false;
569 enabled = false;
570 }
571
572 Cmd_AddCommand ("cd", CD_f);
573
574 Com_Printf("CD Audio Initialized\n");
575
576 return 0;
577 }
578
579
580 void CDAudio_Shutdown(void)
581 {
582 if (!initialized)
583 return;
584 CDAudio_Stop();
585 if (mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)NULL))
586 Com_DPrintf("CDAudio_Shutdown: MCI_CLOSE failed\n");
587 initialized = false;
588 }
589
590
591 /*
592 ===========
593 CDAudio_Activate
594
595 Called when the main window gains or loses focus.
596 The window have been destroyed and recreated
597 between a deactivate and an activate.
598 ===========
599 */
600 void CDAudio_Activate (qboolean active)
601 {
602 if (active)
603 CDAudio_Resume ();
604 else
605 CDAudio_Pause ();
606 }
607