1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 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 "quakedef.h"
25
26extern	HWND	mainwindow;
27extern	cvar_t	bgmvolume;
28
29static qboolean cdValid = false;
30static qboolean	playing = false;
31static qboolean	wasPlaying = false;
32static qboolean	initialized = false;
33static qboolean	enabled = false;
34static qboolean playLooping = false;
35static float	cdvolume;
36static byte 	remap[100];
37static byte		cdrom;
38static byte		playTrack;
39static byte		maxTrack;
40
41UINT	wDeviceID;
42
43
44static void CDAudio_Eject(void)
45{
46	DWORD	dwReturn;
47
48    if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, (DWORD)NULL))
49		Con_DPrintf("MCI_SET_DOOR_OPEN failed (%i)\n", dwReturn);
50}
51
52
53static void CDAudio_CloseDoor(void)
54{
55	DWORD	dwReturn;
56
57    if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, (DWORD)NULL))
58		Con_DPrintf("MCI_SET_DOOR_CLOSED failed (%i)\n", dwReturn);
59}
60
61
62static int CDAudio_GetAudioDiskInfo(void)
63{
64	DWORD				dwReturn;
65	MCI_STATUS_PARMS	mciStatusParms;
66
67
68	cdValid = false;
69
70	mciStatusParms.dwItem = MCI_STATUS_READY;
71    dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms);
72	if (dwReturn)
73	{
74		Con_DPrintf("CDAudio: drive ready test - get status failed\n");
75		return -1;
76	}
77	if (!mciStatusParms.dwReturn)
78	{
79		Con_DPrintf("CDAudio: drive not ready\n");
80		return -1;
81	}
82
83	mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
84    dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms);
85	if (dwReturn)
86	{
87		Con_DPrintf("CDAudio: get tracks - status failed\n");
88		return -1;
89	}
90	if (mciStatusParms.dwReturn < 1)
91	{
92		Con_DPrintf("CDAudio: no music tracks\n");
93		return -1;
94	}
95
96	cdValid = true;
97	maxTrack = mciStatusParms.dwReturn;
98
99	return 0;
100}
101
102
103void CDAudio_Play(byte track, qboolean looping)
104{
105	DWORD				dwReturn;
106    MCI_PLAY_PARMS		mciPlayParms;
107	MCI_STATUS_PARMS	mciStatusParms;
108
109	if (!enabled)
110		return;
111
112	if (!cdValid)
113	{
114		CDAudio_GetAudioDiskInfo();
115		if (!cdValid)
116			return;
117	}
118
119	track = remap[track];
120
121	if (track < 1 || track > maxTrack)
122	{
123		Con_DPrintf("CDAudio: Bad track number %u.\n", track);
124		return;
125	}
126
127	// don't try to play a non-audio track
128	mciStatusParms.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
129	mciStatusParms.dwTrack = track;
130    dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms);
131	if (dwReturn)
132	{
133		Con_DPrintf("MCI_STATUS failed (%i)\n", dwReturn);
134		return;
135	}
136	if (mciStatusParms.dwReturn != MCI_CDA_TRACK_AUDIO)
137	{
138		Con_Printf("CDAudio: track %i is not audio\n", track);
139		return;
140	}
141
142	// get the length of the track to be played
143	mciStatusParms.dwItem = MCI_STATUS_LENGTH;
144	mciStatusParms.dwTrack = track;
145    dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms);
146	if (dwReturn)
147	{
148		Con_DPrintf("MCI_STATUS failed (%i)\n", dwReturn);
149		return;
150	}
151
152	if (playing)
153	{
154		if (playTrack == track)
155			return;
156		CDAudio_Stop();
157	}
158
159    mciPlayParms.dwFrom = MCI_MAKE_TMSF(track, 0, 0, 0);
160	mciPlayParms.dwTo = (mciStatusParms.dwReturn << 8) | track;
161    mciPlayParms.dwCallback = (DWORD)mainwindow;
162    dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_FROM | MCI_TO, (DWORD)(LPVOID) &mciPlayParms);
163	if (dwReturn)
164	{
165		Con_DPrintf("CDAudio: MCI_PLAY failed (%i)\n", dwReturn);
166		return;
167	}
168
169	playLooping = looping;
170	playTrack = track;
171	playing = true;
172
173	if (cdvolume == 0.0)
174		CDAudio_Pause ();
175}
176
177
178void CDAudio_Stop(void)
179{
180	DWORD	dwReturn;
181
182	if (!enabled)
183		return;
184
185	if (!playing)
186		return;
187
188    if (dwReturn = mciSendCommand(wDeviceID, MCI_STOP, 0, (DWORD)NULL))
189		Con_DPrintf("MCI_STOP failed (%i)", dwReturn);
190
191	wasPlaying = false;
192	playing = false;
193}
194
195
196void CDAudio_Pause(void)
197{
198	DWORD				dwReturn;
199	MCI_GENERIC_PARMS	mciGenericParms;
200
201	if (!enabled)
202		return;
203
204	if (!playing)
205		return;
206
207	mciGenericParms.dwCallback = (DWORD)mainwindow;
208    if (dwReturn = mciSendCommand(wDeviceID, MCI_PAUSE, 0, (DWORD)(LPVOID) &mciGenericParms))
209		Con_DPrintf("MCI_PAUSE failed (%i)", dwReturn);
210
211	wasPlaying = playing;
212	playing = false;
213}
214
215
216void CDAudio_Resume(void)
217{
218	DWORD			dwReturn;
219    MCI_PLAY_PARMS	mciPlayParms;
220
221	if (!enabled)
222		return;
223
224	if (!cdValid)
225		return;
226
227	if (!wasPlaying)
228		return;
229
230    mciPlayParms.dwFrom = MCI_MAKE_TMSF(playTrack, 0, 0, 0);
231    mciPlayParms.dwTo = MCI_MAKE_TMSF(playTrack + 1, 0, 0, 0);
232    mciPlayParms.dwCallback = (DWORD)mainwindow;
233    dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_TO | MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms);
234	if (dwReturn)
235	{
236		Con_DPrintf("CDAudio: MCI_PLAY failed (%i)\n", dwReturn);
237		return;
238	}
239	playing = true;
240}
241
242
243static void CD_f (void)
244{
245	char	*command;
246	int		ret;
247	int		n;
248	int		startAddress;
249
250	if (Cmd_Argc() < 2)
251		return;
252
253	command = Cmd_Argv (1);
254
255	if (Q_strcasecmp(command, "on") == 0)
256	{
257		enabled = true;
258		return;
259	}
260
261	if (Q_strcasecmp(command, "off") == 0)
262	{
263		if (playing)
264			CDAudio_Stop();
265		enabled = false;
266		return;
267	}
268
269	if (Q_strcasecmp(command, "reset") == 0)
270	{
271		enabled = true;
272		if (playing)
273			CDAudio_Stop();
274		for (n = 0; n < 100; n++)
275			remap[n] = n;
276		CDAudio_GetAudioDiskInfo();
277		return;
278	}
279
280	if (Q_strcasecmp(command, "remap") == 0)
281	{
282		ret = Cmd_Argc() - 2;
283		if (ret <= 0)
284		{
285			for (n = 1; n < 100; n++)
286				if (remap[n] != n)
287					Con_Printf("  %u -> %u\n", n, remap[n]);
288			return;
289		}
290		for (n = 1; n <= ret; n++)
291			remap[n] = Q_atoi(Cmd_Argv (n+1));
292		return;
293	}
294
295	if (Q_strcasecmp(command, "close") == 0)
296	{
297		CDAudio_CloseDoor();
298		return;
299	}
300
301	if (!cdValid)
302	{
303		CDAudio_GetAudioDiskInfo();
304		if (!cdValid)
305		{
306			Con_Printf("No CD in player.\n");
307			return;
308		}
309	}
310
311	if (Q_strcasecmp(command, "play") == 0)
312	{
313		CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), false);
314		return;
315	}
316
317	if (Q_strcasecmp(command, "loop") == 0)
318	{
319		CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), true);
320		return;
321	}
322
323	if (Q_strcasecmp(command, "stop") == 0)
324	{
325		CDAudio_Stop();
326		return;
327	}
328
329	if (Q_strcasecmp(command, "pause") == 0)
330	{
331		CDAudio_Pause();
332		return;
333	}
334
335	if (Q_strcasecmp(command, "resume") == 0)
336	{
337		CDAudio_Resume();
338		return;
339	}
340
341	if (Q_strcasecmp(command, "eject") == 0)
342	{
343		if (playing)
344			CDAudio_Stop();
345		CDAudio_Eject();
346		cdValid = false;
347		return;
348	}
349
350	if (Q_strcasecmp(command, "info") == 0)
351	{
352		Con_Printf("%u tracks\n", maxTrack);
353		if (playing)
354			Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
355		else if (wasPlaying)
356			Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack);
357		Con_Printf("Volume is %f\n", cdvolume);
358		return;
359	}
360}
361
362
363LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
364{
365	if (lParam != wDeviceID)
366		return 1;
367
368	switch (wParam)
369	{
370		case MCI_NOTIFY_SUCCESSFUL:
371			if (playing)
372			{
373				playing = false;
374				if (playLooping)
375					CDAudio_Play(playTrack, true);
376			}
377			break;
378
379		case MCI_NOTIFY_ABORTED:
380		case MCI_NOTIFY_SUPERSEDED:
381			break;
382
383		case MCI_NOTIFY_FAILURE:
384			Con_DPrintf("MCI_NOTIFY_FAILURE\n");
385			CDAudio_Stop ();
386			cdValid = false;
387			break;
388
389		default:
390			Con_DPrintf("Unexpected MM_MCINOTIFY type (%i)\n", wParam);
391			return 1;
392	}
393
394	return 0;
395}
396
397
398void CDAudio_Update(void)
399{
400	if (!enabled)
401		return;
402
403	if (bgmvolume.value != cdvolume)
404	{
405		if (cdvolume)
406		{
407			Cvar_SetValue ("bgmvolume", 0.0);
408			cdvolume = bgmvolume.value;
409			CDAudio_Pause ();
410		}
411		else
412		{
413			Cvar_SetValue ("bgmvolume", 1.0);
414			cdvolume = bgmvolume.value;
415			CDAudio_Resume ();
416		}
417	}
418}
419
420
421int CDAudio_Init(void)
422{
423	DWORD	dwReturn;
424	MCI_OPEN_PARMS	mciOpenParms;
425    MCI_SET_PARMS	mciSetParms;
426	int				n;
427
428	if (cls.state == ca_dedicated)
429		return -1;
430
431	if (COM_CheckParm("-nocdaudio"))
432		return -1;
433
434	mciOpenParms.lpstrDeviceType = "cdaudio";
435	if (dwReturn = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE, (DWORD) (LPVOID) &mciOpenParms))
436	{
437		Con_Printf("CDAudio_Init: MCI_OPEN failed (%i)\n", dwReturn);
438		return -1;
439	}
440	wDeviceID = mciOpenParms.wDeviceID;
441
442    // Set the time format to track/minute/second/frame (TMSF).
443    mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF;
444    if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID) &mciSetParms))
445    {
446		Con_Printf("MCI_SET_TIME_FORMAT failed (%i)\n", dwReturn);
447        mciSendCommand(wDeviceID, MCI_CLOSE, 0, (DWORD)NULL);
448		return -1;
449    }
450
451	for (n = 0; n < 100; n++)
452		remap[n] = n;
453	initialized = true;
454	enabled = true;
455
456	if (CDAudio_GetAudioDiskInfo())
457	{
458		Con_Printf("CDAudio_Init: No CD in player.\n");
459		cdValid = false;
460	}
461
462	Cmd_AddCommand ("cd", CD_f);
463
464	Con_Printf("CD Audio Initialized\n");
465
466	return 0;
467}
468
469
470void CDAudio_Shutdown(void)
471{
472	if (!initialized)
473		return;
474	CDAudio_Stop();
475	if (mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)NULL))
476		Con_DPrintf("CDAudio_Shutdown: MCI_CLOSE failed\n");
477}
478