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