1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: snd_audiocd_win32.cpp 3866 2008-11-17 18:58:49Z dj_jl $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 
26 // HEADER FILES ------------------------------------------------------------
27 
28 #include "winlocal.h"
29 #include <mmsystem.h>
30 
31 #include "gamedefs.h"
32 #include "snd_local.h"
33 
34 // MACROS ------------------------------------------------------------------
35 
36 // TYPES -------------------------------------------------------------------
37 
38 class VWin32CDAudioDevice : public VCDAudioDevice, public VWinMessageHandler
39 {
40 public:
41 	DWORD		CDDevice;
42 
43 	void Init();
44 	void Update();
45 	void Shutdown();
46 	void GetInfo();
47 	void Play(int, bool);
48 	void Pause();
49 	void Resume();
50 	void Stop();
51 	void OpenDoor();
52 	void CloseDoor();
53 
54 	LONG OnMessage(HWND, UINT, WPARAM, LPARAM);
55 };
56 
57 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
58 
59 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
60 
61 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
62 
63 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
64 
65 // PUBLIC DATA DEFINITIONS -------------------------------------------------
66 
67 IMPLEMENT_CD_AUDIO_DEVICE(VWin32CDAudioDevice, CDDRV_Default, "Default",
68 	"Windows CD audio device", NULL);
69 
70 // PRIVATE DATA DEFINITIONS ------------------------------------------------
71 
72 // CODE --------------------------------------------------------------------
73 
74 //==========================================================================
75 //
76 //	VWin32CDAudioDevice::Init
77 //
78 //==========================================================================
79 
Init()80 void VWin32CDAudioDevice::Init()
81 {
82 	guard(VWin32CDAudioDevice::Init);
83 	MCI_OPEN_PARMS		open;
84 	MCI_SET_PARMS		set;
85 	DWORD				result;
86 	int					i;
87 
88 	if (GArgs.CheckParm("-nosound") || GArgs.CheckParm("-nocdaudio"))
89 	{
90 		return;
91 	}
92 
93 	open.dwCallback       = (DWORD)hwnd;
94 	open.lpstrDeviceType  = "cdaudio";
95 	result = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE, (DWORD)&open);
96 	if (result)
97 	{
98 		GCon->Log(NAME_Init, "CDAudio_Init: MCI_OPEN failed");
99 		return;
100 	}
101 	CDDevice = open.wDeviceID;
102 
103 	// Set the time format to track/minute/second/frame (TMSF).
104 	set.dwTimeFormat = MCI_FORMAT_TMSF;
105 	result = mciSendCommand(CDDevice, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)&set);
106 	if (result)
107 	{
108 		GCon->Log(NAME_Init, "MCI_SET_TIME_FORMAT failed");
109 		mciSendCommand(CDDevice, MCI_CLOSE, 0, (DWORD)NULL);
110 		return;
111 	}
112 
113 	GCDMsgHandler = this;
114 	for (i = 0; i < 100; i++)
115 		Remap[i] = i;
116 	Initialised = true;
117 	Enabled = true;
118 
119 	GetInfo();
120 	if (!CDValid)
121 	{
122 		GCon->Log(NAME_Init, "CDAudio_Init: No CD in player.");
123 	}
124 
125 	GCon->Log(NAME_Init, "CD Audio Initialised");
126 	unguard;
127 }
128 
129 //==========================================================================
130 //
131 //  VWin32CDAudioDevice::OnMessage
132 //
133 //==========================================================================
134 
OnMessage(HWND,UINT,WPARAM wParam,LPARAM lParam)135 LONG VWin32CDAudioDevice::OnMessage(HWND, UINT, WPARAM wParam, LPARAM lParam)
136 {
137 	guard(VWin32CDAudioDevice::OnMessage);
138 	if ((DWORD)lParam != CDDevice)
139 		return 1;
140 
141 	switch (wParam)
142 	{
143 	case MCI_NOTIFY_SUCCESSFUL:
144 		if (Playing)
145 		{
146 			Playing = false;
147 			if (PlayLooping)
148 				Play(PlayTrack, true);
149 		}
150 		break;
151 
152 	case MCI_NOTIFY_ABORTED:
153 	case MCI_NOTIFY_SUPERSEDED:
154 		break;
155 
156 	case MCI_NOTIFY_FAILURE:
157 		GCon->Log(NAME_Dev, "MCI_NOTIFY_FAILURE");
158 		Stop();
159 		CDValid = false;
160 		break;
161 
162 	default:
163 		GCon->Logf(NAME_Dev, "Unexpected MM_MCINOTIFY type (%d)", wParam);
164 		return 1;
165 	}
166 
167 	return 0;
168 	unguard;
169 }
170 
171 //==========================================================================
172 //
173 //	VWin32CDAudioDevice::Update
174 //
175 //==========================================================================
176 
Update()177 void VWin32CDAudioDevice::Update()
178 {
179 }
180 
181 //==========================================================================
182 //
183 //	VWin32CDAudioDevice::Shutdown
184 //
185 //==========================================================================
186 
Shutdown()187 void VWin32CDAudioDevice::Shutdown()
188 {
189 	guard(VWin32CDAudioDevice::Shutdown);
190 	if (!Initialised)
191 	{
192 		return;
193 	}
194 
195 	Stop();
196 	if (mciSendCommand(CDDevice, MCI_CLOSE, MCI_WAIT, (DWORD)NULL))
197 		GCon->Log(NAME_Dev, "CD_Shutdown: MCI_CLOSE failed");
198 	GCDMsgHandler = NULL;
199 	Initialised = false;
200 	unguard;
201 }
202 
203 //==========================================================================
204 //
205 //	VWin32CDAudioDevice::GetInfo
206 //
207 //==========================================================================
208 
GetInfo()209 void VWin32CDAudioDevice::GetInfo()
210 {
211 	guard(VWin32CDAudioDevice::GetInfo);
212 	DWORD				result;
213 	MCI_STATUS_PARMS	parms;
214 
215 	CDValid = false;
216 
217 	parms.dwCallback = (DWORD)hwnd;
218 	parms.dwItem = MCI_STATUS_READY;
219 	result = mciSendCommand(CDDevice, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD)&parms);
220 	if (result)
221 	{
222 		GCon->Log(NAME_Dev, "CDAudio: drive ready test - get status failed");
223 		return;
224 	}
225 	if (!parms.dwReturn)
226 	{
227 		GCon->Log(NAME_Dev, "CDAudio: drive not ready");
228 		return;
229 	}
230 
231 	parms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
232 	result = mciSendCommand(CDDevice, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD)&parms);
233 	if (result)
234 	{
235 		GCon->Log(NAME_Dev, "CDAudio: get tracks - status failed");
236 		return;
237 	}
238 	if (parms.dwReturn < 1)
239 	{
240 		GCon->Log(NAME_Dev, "CDAudio: no music tracks");
241 		return;
242 	}
243 
244 	CDValid = true;
245 	MaxTrack = parms.dwReturn;
246 	unguard;
247 }
248 
249 //==========================================================================
250 //
251 //	VWin32CDAudioDevice::Play
252 //
253 //==========================================================================
254 
Play(int track,bool looping)255 void VWin32CDAudioDevice::Play(int track, bool looping)
256 {
257 	guard(VWin32CDAudioDevice::Play);
258 	MCI_STATUS_PARMS	status;
259 	MCI_PLAY_PARMS		play;
260 	DWORD				result;
261 
262 	if (!CDValid)
263 	{
264 		GetInfo();
265 		if (!CDValid)
266 			return;
267 	}
268 
269 	track = Remap[track];
270 
271 	if (track < 1 || track > MaxTrack)
272 	{
273 		GCon->Logf(NAME_Dev, "CDAudio: Bad track number %d.", track);
274 		return;
275 	}
276 
277 	// don't try to play a non-audio track
278 	status.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
279 	status.dwTrack = track;
280 	result = mciSendCommand(CDDevice, MCI_STATUS, MCI_STATUS_ITEM |
281 		MCI_TRACK | MCI_WAIT, (DWORD)&status);
282 	if (result)
283 	{
284 		GCon->Log(NAME_Dev, "MCI_STATUS failed");
285 		return;
286 	}
287 	if (status.dwReturn != MCI_CDA_TRACK_AUDIO)
288 	{
289 		GCon->Logf("CDAudio: track %d is not audio", track);
290 		return;
291 	}
292 
293 	// get the length of the track to be played
294 	status.dwItem = MCI_STATUS_LENGTH;
295 	status.dwTrack = track;
296 	result = mciSendCommand(CDDevice, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD)&status);
297 	if (result)
298 	{
299 		GCon->Log(NAME_Dev, "MCI_STATUS failed");
300 		return;
301 	}
302 
303 	if (Playing)
304 	{
305 		if (PlayTrack == track)
306 			return;
307 		Stop();
308 	}
309 
310 	play.dwCallback = (DWORD)hwnd;
311 	play.dwFrom = MCI_MAKE_TMSF(track, 0, 0, 0);
312 	play.dwTo = (status.dwReturn << 8) | track;
313 
314 	result = mciSendCommand(CDDevice, MCI_PLAY, MCI_NOTIFY | MCI_FROM | MCI_TO, (DWORD)&play);
315 
316 	if (result)
317 	{
318 		GCon->Log(NAME_Dev, "MCI_PLAY failed");
319 		return;
320 	}
321 
322 	PlayLooping = looping;
323 	PlayTrack = track;
324 	Playing = true;
325 	unguard;
326 }
327 
328 //==========================================================================
329 //
330 //	VWin32CDAudioDevice::Pause
331 //
332 //==========================================================================
333 
Pause()334 void VWin32CDAudioDevice::Pause()
335 {
336 	guard(VWin32CDAudioDevice::Pause);
337 	MCI_GENERIC_PARMS	parms;
338 	DWORD				result;
339 
340 	if (!Playing)
341 		return;
342 
343 	parms.dwCallback = (DWORD)hwnd;
344 
345 	result = mciSendCommand(CDDevice, MCI_PAUSE, 0, (DWORD)&parms);
346 
347 	if (result)
348 		GCon->Log(NAME_Dev, "MCI_PAUSE failed");
349 
350 	WasPlaying = Playing;
351 	Playing = false;
352 	unguard;
353 }
354 
355 //==========================================================================
356 //
357 //	VWin32CDAudioDevice::Resume
358 //
359 //==========================================================================
360 
Resume()361 void VWin32CDAudioDevice::Resume()
362 {
363 	guard(VWin32CDAudioDevice::Resume);
364 	MCI_GENERIC_PARMS	parms;
365 	DWORD				result;
366 
367 	if (!WasPlaying)
368 		return;
369 
370 	parms.dwCallback = (DWORD)hwnd;
371 	result = mciSendCommand(CDDevice, MCI_RESUME, 0, (DWORD)&parms);
372 
373 	if (result)
374 	{
375 		GCon->Log(NAME_Dev, "MCI_RESUME failed");
376 		return;
377 	}
378 
379 	Playing = true;
380 	unguard;
381 }
382 
383 //==========================================================================
384 //
385 //	VWin32CDAudioDevice::Stop
386 //
387 //==========================================================================
388 
Stop()389 void VWin32CDAudioDevice::Stop()
390 {
391 	guard(VWin32CDAudioDevice::Stop);
392 	DWORD				result;
393 
394 	if (!Playing)
395 		return;
396 
397 	result = mciSendCommand(CDDevice, MCI_STOP, 0, (DWORD)NULL);
398 
399 	if (result)
400 		GCon->Log(NAME_Dev, "MCI_STOP failed");
401 
402 	WasPlaying = false;
403 	Playing = false;
404 	unguard;
405 }
406 
407 //==========================================================================
408 //
409 //	VWin32CDAudioDevice::OpenDoor
410 //
411 //==========================================================================
412 
OpenDoor()413 void VWin32CDAudioDevice::OpenDoor()
414 {
415 	guard(VWin32CDAudioDevice::OpenDoor);
416 	MCI_SET_PARMS		parms;
417 	DWORD				result;
418 
419 	parms.dwCallback = (DWORD)hwnd;
420 
421 	result = mciSendCommand(CDDevice, MCI_SET, MCI_SET_DOOR_OPEN, (DWORD)&parms);
422 
423 	if (result)
424 		GCon->Log(NAME_Dev, "MCI_SET_DOOR_OPEN failed");
425 	unguard;
426 }
427 
428 //==========================================================================
429 //
430 //	VWin32CDAudioDevice::CloseDoor
431 //
432 //==========================================================================
433 
CloseDoor()434 void VWin32CDAudioDevice::CloseDoor()
435 {
436 	guard(VWin32CDAudioDevice::CloseDoor);
437 	MCI_SET_PARMS		parms;
438 	DWORD				result;
439 
440 	parms.dwCallback = (DWORD)hwnd;
441 
442 	result = mciSendCommand(CDDevice, MCI_SET, MCI_SET_DOOR_CLOSED, (DWORD)&parms);
443 
444 	if (result)
445 		GCon->Log(NAME_Dev, "MCI_SET_DOOR_CLOSED failed");
446 	unguard;
447 }
448