mcicda: Play back an audio CD digitally, when possible.
[wine.git] / dlls / mcicda / mcicda.c
blob384e8ebe4f60ea39ff9b8379c6c203cc5c5169a3
1 /*
2 * MCI driver for audio CD (MCICDA)
4 * Copyright 1994 Martin Ayotte
5 * Copyright 1998-99 Eric Pouech
6 * Copyright 2000 Andreas Mohr
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library 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 GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "config.h"
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <string.h>
28 #define WIN32_NO_STATUS
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "wownt32.h"
34 #include "mmddk.h"
35 #include "winioctl.h"
36 #include "ntddcdrm.h"
37 #include "winternl.h"
38 #include "wine/debug.h"
39 #include "wine/unicode.h"
40 #include "dsound.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(mcicda);
44 #define CDFRAMES_PERSEC 75
45 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
46 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
47 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
49 /* Defined by red-book standard; do not change! */
50 #define RAW_SECTOR_SIZE (2352)
52 /* Must be >= RAW_SECTOR_SIZE */
53 #define CDDA_FRAG_SIZE (32768)
54 /* Must be >= 2 */
55 #define CDDA_FRAG_COUNT (3)
57 typedef struct {
58 UINT wDevID;
59 int nUseCount; /* Incremented for each shared open */
60 BOOL fShareable; /* TRUE if first open was shareable */
61 WORD wNotifyDeviceID; /* MCI device ID with a pending notification */
62 HANDLE hCallback; /* Callback handle for pending notification */
63 DWORD dwTimeFormat;
64 HANDLE handle;
66 /* The following are used for digital playback only */
67 HANDLE hThread;
68 HANDLE stopEvent;
69 DWORD start, end;
71 IDirectSound *dsObj;
72 IDirectSoundBuffer *dsBuf;
74 CRITICAL_SECTION cs;
75 } WINE_MCICDAUDIO;
77 /*-----------------------------------------------------------------------*/
79 typedef HRESULT(WINAPI*LPDIRECTSOUNDCREATE)(LPCGUID,LPDIRECTSOUND*,LPUNKNOWN);
80 static LPDIRECTSOUNDCREATE pDirectSoundCreate;
82 static DWORD CALLBACK MCICDA_playLoop(void *ptr)
84 WINE_MCICDAUDIO *wmcda = (WINE_MCICDAUDIO*)ptr;
85 DWORD lastPos, curPos, endPos, br;
86 void *cdData;
87 DWORD lockLen, fragLen;
88 DSBCAPS caps;
89 RAW_READ_INFO rdInfo;
90 HRESULT hr = DS_OK;
92 memset(&caps, 0, sizeof(caps));
93 caps.dwSize = sizeof(caps);
94 hr = IDirectSoundBuffer_GetCaps(wmcda->dsBuf, &caps);
96 fragLen = caps.dwBufferBytes/CDDA_FRAG_COUNT;
97 curPos = lastPos = 0;
98 endPos = ~0u;
99 while (SUCCEEDED(hr) && endPos != lastPos &&
100 WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0) {
101 hr = IDirectSoundBuffer_GetCurrentPosition(wmcda->dsBuf, &curPos, NULL);
102 if ((curPos-lastPos+caps.dwBufferBytes)%caps.dwBufferBytes < fragLen) {
103 Sleep(1);
104 continue;
107 EnterCriticalSection(&wmcda->cs);
108 rdInfo.DiskOffset.QuadPart = wmcda->start<<11;
109 rdInfo.SectorCount = min(fragLen/RAW_SECTOR_SIZE, wmcda->end-wmcda->start);
110 rdInfo.TrackMode = CDDA;
112 hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, lastPos, fragLen, &cdData, &lockLen, NULL, NULL, 0);
113 if (hr == DSERR_BUFFERLOST) {
114 if(FAILED(IDirectSoundBuffer_Restore(wmcda->dsBuf)) ||
115 FAILED(IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING))) {
116 LeaveCriticalSection(&wmcda->cs);
117 break;
119 hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, lastPos, fragLen, &cdData, &lockLen, NULL, NULL, 0);
122 if (SUCCEEDED(hr)) {
123 if (rdInfo.SectorCount > 0) {
124 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RAW_READ, &rdInfo, sizeof(rdInfo), cdData, lockLen, &br, NULL))
125 WARN("CD read failed at sector %d: 0x%x\n", wmcda->start, GetLastError());
127 if (rdInfo.SectorCount*RAW_SECTOR_SIZE < lockLen) {
128 if(endPos == ~0u) endPos = lastPos;
129 memset((BYTE*)cdData + rdInfo.SectorCount*RAW_SECTOR_SIZE, 0,
130 lockLen - rdInfo.SectorCount*RAW_SECTOR_SIZE);
132 hr = IDirectSoundBuffer_Unlock(wmcda->dsBuf, cdData, lockLen, NULL, 0);
135 lastPos += fragLen;
136 lastPos %= caps.dwBufferBytes;
137 wmcda->start += rdInfo.SectorCount;
139 LeaveCriticalSection(&wmcda->cs);
141 IDirectSoundBuffer_Stop(wmcda->dsBuf);
142 SetEvent(wmcda->stopEvent);
144 ExitThread(0);
145 return 0;
150 /**************************************************************************
151 * MCICDA_drvOpen [internal]
153 static DWORD MCICDA_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
155 static HMODULE dsHandle;
156 WINE_MCICDAUDIO* wmcda;
158 if (!modp) return 0xFFFFFFFF;
160 wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO));
162 if (!wmcda)
163 return 0;
165 if (!dsHandle) {
166 dsHandle = LoadLibraryA("dsound.dll");
167 if(dsHandle)
168 pDirectSoundCreate = (LPDIRECTSOUNDCREATE)GetProcAddress(dsHandle, "DirectSoundCreate");
171 wmcda->wDevID = modp->wDeviceID;
172 mciSetDriverData(wmcda->wDevID, (DWORD_PTR)wmcda);
173 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
174 modp->wType = MCI_DEVTYPE_CD_AUDIO;
175 InitializeCriticalSection(&wmcda->cs);
176 return modp->wDeviceID;
179 /**************************************************************************
180 * MCICDA_drvClose [internal]
182 static DWORD MCICDA_drvClose(DWORD dwDevID)
184 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID);
186 if (wmcda) {
187 DeleteCriticalSection(&wmcda->cs);
188 HeapFree(GetProcessHeap(), 0, wmcda);
189 mciSetDriverData(dwDevID, 0);
191 return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
194 /**************************************************************************
195 * MCICDA_GetOpenDrv [internal]
197 static WINE_MCICDAUDIO* MCICDA_GetOpenDrv(UINT wDevID)
199 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
201 if (wmcda == NULL || wmcda->nUseCount == 0) {
202 WARN("Invalid wDevID=%u\n", wDevID);
203 return 0;
205 return wmcda;
208 /**************************************************************************
209 * MCICDA_GetStatus [internal]
211 static DWORD MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda)
213 CDROM_SUB_Q_DATA_FORMAT fmt;
214 SUB_Q_CHANNEL_DATA data;
215 DWORD br;
216 DWORD mode = MCI_MODE_NOT_READY;
218 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
219 if(wmcda->hThread != 0) {
220 DWORD status;
221 HRESULT hr;
223 hr = IDirectSoundBuffer_GetStatus(wmcda->dsBuf, &status);
224 if(SUCCEEDED(hr)) {
225 if(!(status&DSBSTATUS_PLAYING)) {
226 if(WaitForSingleObject(wmcda->stopEvent, 0) == WAIT_OBJECT_0)
227 mode = MCI_MODE_STOP;
228 else
229 mode = MCI_MODE_PAUSE;
231 else
232 mode = MCI_MODE_PLAY;
235 else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
236 &data, sizeof(data), &br, NULL)) {
237 if (GetLastError() == ERROR_NOT_READY) mode = MCI_MODE_OPEN;
238 } else {
239 switch (data.CurrentPosition.Header.AudioStatus)
241 case AUDIO_STATUS_IN_PROGRESS: mode = MCI_MODE_PLAY; break;
242 case AUDIO_STATUS_PAUSED: mode = MCI_MODE_PAUSE; break;
243 case AUDIO_STATUS_NO_STATUS:
244 case AUDIO_STATUS_PLAY_COMPLETE: mode = MCI_MODE_STOP; break;
245 case AUDIO_STATUS_PLAY_ERROR:
246 case AUDIO_STATUS_NOT_SUPPORTED:
247 default:
248 break;
251 return mode;
254 /**************************************************************************
255 * MCICDA_GetError [internal]
257 static int MCICDA_GetError(WINE_MCICDAUDIO* wmcda)
259 switch (GetLastError())
261 case ERROR_NOT_READY: return MCIERR_DEVICE_NOT_READY;
262 case ERROR_IO_DEVICE: return MCIERR_HARDWARE;
263 default:
264 FIXME("Unknown mode %u\n", GetLastError());
266 return MCIERR_DRIVER_INTERNAL;
269 /**************************************************************************
270 * MCICDA_CalcFrame [internal]
272 static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime)
274 DWORD dwFrame = 0;
275 UINT wTrack;
276 CDROM_TOC toc;
277 DWORD br;
278 BYTE* addr;
280 TRACE("(%p, %08X, %u);\n", wmcda, wmcda->dwTimeFormat, dwTime);
282 switch (wmcda->dwTimeFormat) {
283 case MCI_FORMAT_MILLISECONDS:
284 dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000;
285 TRACE("MILLISECONDS %u\n", dwFrame);
286 break;
287 case MCI_FORMAT_MSF:
288 TRACE("MSF %02u:%02u:%02u\n",
289 MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime));
290 dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime);
291 dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime);
292 dwFrame += MCI_MSF_FRAME(dwTime);
293 break;
294 case MCI_FORMAT_TMSF:
295 default: /* unknown format ! force TMSF ! ... */
296 wTrack = MCI_TMSF_TRACK(dwTime);
297 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
298 &toc, sizeof(toc), &br, NULL))
299 return 0;
300 if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack)
301 return 0;
302 TRACE("MSF %02u-%02u:%02u:%02u\n",
303 MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime),
304 MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime));
305 addr = toc.TrackData[wTrack - toc.FirstTrack].Address;
306 TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
307 wTrack, addr[1], addr[2], addr[3]);
308 dwFrame = CDFRAMES_PERMIN * (addr[1] + MCI_TMSF_MINUTE(dwTime)) +
309 CDFRAMES_PERSEC * (addr[2] + MCI_TMSF_SECOND(dwTime)) +
310 addr[3] + MCI_TMSF_FRAME(dwTime);
311 break;
313 return dwFrame;
316 /**************************************************************************
317 * MCICDA_CalcTime [internal]
319 static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet)
321 DWORD dwTime = 0;
322 UINT wTrack;
323 UINT wMinutes;
324 UINT wSeconds;
325 UINT wFrames;
326 CDROM_TOC toc;
327 DWORD br;
329 TRACE("(%p, %08X, %u);\n", wmcda, tf, dwFrame);
331 switch (tf) {
332 case MCI_FORMAT_MILLISECONDS:
333 dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1;
334 TRACE("MILLISECONDS %u\n", dwTime);
335 *lpRet = 0;
336 break;
337 case MCI_FORMAT_MSF:
338 wMinutes = dwFrame / CDFRAMES_PERMIN;
339 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
340 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
341 dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames);
342 TRACE("MSF %02u:%02u:%02u -> dwTime=%u\n",
343 wMinutes, wSeconds, wFrames, dwTime);
344 *lpRet = MCI_COLONIZED3_RETURN;
345 break;
346 case MCI_FORMAT_TMSF:
347 default: /* unknown format ! force TMSF ! ... */
348 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
349 &toc, sizeof(toc), &br, NULL))
350 return 0;
351 if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) ||
352 dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) {
353 ERR("Out of range value %u [%u,%u]\n",
354 dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack),
355 FRAME_OF_TOC(toc, toc.LastTrack + 1));
356 *lpRet = 0;
357 return 0;
359 for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) {
360 if (FRAME_OF_TOC(toc, wTrack) > dwFrame)
361 break;
363 wTrack--;
364 dwFrame -= FRAME_OF_TOC(toc, wTrack);
365 wMinutes = dwFrame / CDFRAMES_PERMIN;
366 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC;
367 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds;
368 dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames);
369 TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames);
370 *lpRet = MCI_COLONIZED4_RETURN;
371 break;
373 return dwTime;
376 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms);
377 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
379 /**************************************************************************
380 * MCICDA_Open [internal]
382 static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpOpenParms)
384 DWORD dwDeviceID;
385 DWORD ret = MCIERR_HARDWARE;
386 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID);
387 WCHAR root[7], drive = 0;
388 unsigned int count;
390 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpOpenParms);
392 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
393 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
395 dwDeviceID = lpOpenParms->wDeviceID;
397 if (wmcda->nUseCount > 0) {
398 /* The driver is already open on this channel */
399 /* If the driver was opened shareable before and this open specifies */
400 /* shareable then increment the use count */
401 if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE))
402 ++wmcda->nUseCount;
403 else
404 return MCIERR_MUST_USE_SHAREABLE;
405 } else {
406 wmcda->nUseCount = 1;
407 wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE;
409 if (dwFlags & MCI_OPEN_ELEMENT) {
410 if (dwFlags & MCI_OPEN_ELEMENT_ID) {
411 WARN("MCI_OPEN_ELEMENT_ID %p! Abort\n", lpOpenParms->lpstrElementName);
412 return MCIERR_NO_ELEMENT_ALLOWED;
414 TRACE("MCI_OPEN_ELEMENT element name: %s\n", debugstr_w(lpOpenParms->lpstrElementName));
415 if (!isalpha(lpOpenParms->lpstrElementName[0]) || lpOpenParms->lpstrElementName[1] != ':' ||
416 (lpOpenParms->lpstrElementName[2] && lpOpenParms->lpstrElementName[2] != '\\'))
418 WARN("MCI_OPEN_ELEMENT unsupported format: %s\n",
419 debugstr_w(lpOpenParms->lpstrElementName));
420 ret = MCIERR_NO_ELEMENT_ALLOWED;
421 goto the_error;
423 drive = toupper(lpOpenParms->lpstrElementName[0]);
424 root[0] = drive; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
425 if (GetDriveTypeW(root) != DRIVE_CDROM)
427 ret = MCIERR_INVALID_DEVICE_NAME;
428 goto the_error;
431 else
433 /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
434 root[0] = 'A'; root[1] = ':'; root[2] = '\\'; root[3] = '\0';
435 for (count = 0; root[0] <= 'Z'; root[0]++)
437 if (GetDriveTypeW(root) == DRIVE_CDROM && ++count >= dwDeviceID)
439 drive = root[0];
440 break;
443 if (!drive)
445 ret = MCIERR_INVALID_DEVICE_ID;
446 goto the_error;
450 wmcda->wNotifyDeviceID = dwDeviceID;
451 wmcda->dwTimeFormat = MCI_FORMAT_MSF;
453 /* now, open the handle */
454 root[0] = root[1] = '\\'; root[2] = '.'; root[3] = '\\'; root[4] = drive; root[5] = ':'; root[6] = '\0';
455 wmcda->handle = CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
456 if (wmcda->handle != 0)
457 return 0;
459 the_error:
460 --wmcda->nUseCount;
461 return ret;
464 /**************************************************************************
465 * MCICDA_Close [internal]
467 static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms)
469 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
471 TRACE("(%04X, %08X, %p);\n", wDevID, dwParam, lpParms);
473 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
475 if (--wmcda->nUseCount == 0) {
476 CloseHandle(wmcda->handle);
478 return 0;
481 /**************************************************************************
482 * MCICDA_GetDevCaps [internal]
484 static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags,
485 LPMCI_GETDEVCAPS_PARMS lpParms)
487 DWORD ret = 0;
489 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
491 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
493 if (dwFlags & MCI_GETDEVCAPS_ITEM) {
494 TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08X;\n", lpParms->dwItem);
496 switch (lpParms->dwItem) {
497 case MCI_GETDEVCAPS_CAN_RECORD:
498 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
499 ret = MCI_RESOURCE_RETURNED;
500 break;
501 case MCI_GETDEVCAPS_HAS_AUDIO:
502 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
503 ret = MCI_RESOURCE_RETURNED;
504 break;
505 case MCI_GETDEVCAPS_HAS_VIDEO:
506 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
507 ret = MCI_RESOURCE_RETURNED;
508 break;
509 case MCI_GETDEVCAPS_DEVICE_TYPE:
510 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO);
511 ret = MCI_RESOURCE_RETURNED;
512 break;
513 case MCI_GETDEVCAPS_USES_FILES:
514 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
515 ret = MCI_RESOURCE_RETURNED;
516 break;
517 case MCI_GETDEVCAPS_COMPOUND_DEVICE:
518 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
519 ret = MCI_RESOURCE_RETURNED;
520 break;
521 case MCI_GETDEVCAPS_CAN_EJECT:
522 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
523 ret = MCI_RESOURCE_RETURNED;
524 break;
525 case MCI_GETDEVCAPS_CAN_PLAY:
526 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
527 ret = MCI_RESOURCE_RETURNED;
528 break;
529 case MCI_GETDEVCAPS_CAN_SAVE:
530 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
531 ret = MCI_RESOURCE_RETURNED;
532 break;
533 default:
534 ERR("Unsupported %x devCaps item\n", lpParms->dwItem);
535 return MCIERR_UNRECOGNIZED_COMMAND;
537 } else {
538 TRACE("No GetDevCaps-Item !\n");
539 return MCIERR_UNRECOGNIZED_COMMAND;
541 TRACE("lpParms->dwReturn=%08X;\n", lpParms->dwReturn);
542 return ret;
545 static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc)
547 unsigned long serial = 0;
548 int i;
549 WORD wMagic;
550 DWORD dwStart, dwEnd;
553 * wMagic collects the wFrames from track 1
554 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
555 * frames.
556 * There it is collected for correcting the serial when there are less than
557 * 3 tracks.
559 wMagic = toc->TrackData[0].Address[3];
560 dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack);
562 for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) {
563 serial += (toc->TrackData[i].Address[1] << 16) |
564 (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3];
566 dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1);
568 if (toc->LastTrack - toc->FirstTrack + 1 < 3)
569 serial += wMagic + (dwEnd - dwStart);
571 return serial;
575 /**************************************************************************
576 * MCICDA_Info [internal]
578 static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
580 LPCWSTR str = NULL;
581 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
582 DWORD ret = 0;
583 WCHAR buffer[16];
585 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
587 if (lpParms == NULL || lpParms->lpstrReturn == NULL)
588 return MCIERR_NULL_PARAMETER_BLOCK;
589 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
591 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
593 if (dwFlags & MCI_INFO_PRODUCT) {
594 static const WCHAR wszAudioCd[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','C','D',0};
595 str = wszAudioCd;
596 } else if (dwFlags & MCI_INFO_MEDIA_UPC) {
597 ret = MCIERR_NO_IDENTITY;
598 } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) {
599 DWORD res = 0;
600 CDROM_TOC toc;
601 DWORD br;
602 static const WCHAR wszLu[] = {'%','l','u',0};
604 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
605 &toc, sizeof(toc), &br, NULL)) {
606 return MCICDA_GetError(wmcda);
609 res = CDROM_Audio_GetSerial(&toc);
610 sprintfW(buffer, wszLu, res);
611 str = buffer;
612 } else {
613 WARN("Don't know this info command (%u)\n", dwFlags);
614 ret = MCIERR_UNRECOGNIZED_COMMAND;
616 if (str) {
617 if (lpParms->dwRetSize <= strlenW(str)) {
618 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize - 1);
619 ret = MCIERR_PARAM_OVERFLOW;
620 } else {
621 strcpyW(lpParms->lpstrReturn, str);
623 } else {
624 *lpParms->lpstrReturn = 0;
626 TRACE("=> %s (%d)\n", debugstr_w(lpParms->lpstrReturn), ret);
627 return ret;
630 /**************************************************************************
631 * MCICDA_Status [internal]
633 static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
635 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
636 DWORD ret = 0;
637 CDROM_SUB_Q_DATA_FORMAT fmt;
638 SUB_Q_CHANNEL_DATA data;
639 CDROM_TOC toc;
640 DWORD br;
642 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
644 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
645 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
647 if (dwFlags & MCI_NOTIFY) {
648 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
649 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
650 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
652 if (dwFlags & MCI_STATUS_ITEM) {
653 TRACE("dwItem = %x\n", lpParms->dwItem);
654 switch (lpParms->dwItem) {
655 case MCI_STATUS_CURRENT_TRACK:
656 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
657 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
658 &data, sizeof(data), &br, NULL))
660 return MCICDA_GetError(wmcda);
662 lpParms->dwReturn = data.CurrentPosition.TrackNumber;
663 TRACE("CURRENT_TRACK=%lu\n", lpParms->dwReturn);
664 break;
665 case MCI_STATUS_LENGTH:
666 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
667 &toc, sizeof(toc), &br, NULL)) {
668 WARN("error reading TOC !\n");
669 return MCICDA_GetError(wmcda);
671 if (dwFlags & MCI_TRACK) {
672 TRACE("MCI_TRACK #%u LENGTH=??? !\n", lpParms->dwTrack);
673 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
674 return MCIERR_OUTOFRANGE;
675 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) -
676 FRAME_OF_TOC(toc, lpParms->dwTrack);
677 /* Windows returns one frame less than the total track length for the
678 last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */
679 if (lpParms->dwTrack == toc.LastTrack)
680 lpParms->dwReturn--;
681 } else {
682 /* Sum of the lengths of all of the tracks. Inherits the
683 'off by one frame' behavior from the length of the last track.
684 See above comment. */
685 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) -
686 FRAME_OF_TOC(toc, toc.FirstTrack) - 1;
688 lpParms->dwReturn = MCICDA_CalcTime(wmcda,
689 (wmcda->dwTimeFormat == MCI_FORMAT_TMSF)
690 ? MCI_FORMAT_MSF : wmcda->dwTimeFormat,
691 lpParms->dwReturn,
692 &ret);
693 TRACE("LENGTH=%lu\n", lpParms->dwReturn);
694 break;
695 case MCI_STATUS_MODE:
696 lpParms->dwReturn = MCICDA_GetStatus(wmcda);
697 TRACE("MCI_STATUS_MODE=%08lX\n", lpParms->dwReturn);
698 lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn);
699 ret = MCI_RESOURCE_RETURNED;
700 break;
701 case MCI_STATUS_MEDIA_PRESENT:
702 lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ?
703 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
704 TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N');
705 ret = MCI_RESOURCE_RETURNED;
706 break;
707 case MCI_STATUS_NUMBER_OF_TRACKS:
708 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
709 &toc, sizeof(toc), &br, NULL)) {
710 WARN("error reading TOC !\n");
711 return MCICDA_GetError(wmcda);
713 lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1;
714 TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu\n", lpParms->dwReturn);
715 if (lpParms->dwReturn == (WORD)-1)
716 return MCICDA_GetError(wmcda);
717 break;
718 case MCI_STATUS_POSITION:
719 if (dwFlags & MCI_STATUS_START) {
720 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
721 &toc, sizeof(toc), &br, NULL)) {
722 WARN("error reading TOC !\n");
723 return MCICDA_GetError(wmcda);
725 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack);
726 TRACE("get MCI_STATUS_START !\n");
727 } else if (dwFlags & MCI_TRACK) {
728 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
729 &toc, sizeof(toc), &br, NULL)) {
730 WARN("error reading TOC !\n");
731 return MCICDA_GetError(wmcda);
733 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
734 return MCIERR_OUTOFRANGE;
735 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack);
736 TRACE("get MCI_TRACK #%u !\n", lpParms->dwTrack);
737 } else {
738 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
739 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
740 &data, sizeof(data), &br, NULL)) {
741 return MCICDA_GetError(wmcda);
743 lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
745 lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret);
746 TRACE("MCI_STATUS_POSITION=%08lX\n", lpParms->dwReturn);
747 break;
748 case MCI_STATUS_READY:
749 TRACE("MCI_STATUS_READY !\n");
750 switch (MCICDA_GetStatus(wmcda))
752 case MCI_MODE_NOT_READY:
753 case MCI_MODE_OPEN:
754 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
755 break;
756 default:
757 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
758 break;
760 TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn));
761 ret = MCI_RESOURCE_RETURNED;
762 break;
763 case MCI_STATUS_TIME_FORMAT:
764 lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, MCI_FORMAT_RETURN_BASE + wmcda->dwTimeFormat);
765 TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn));
766 ret = MCI_RESOURCE_RETURNED;
767 break;
768 case 4001: /* FIXME: for bogus FullCD */
769 case MCI_CDA_STATUS_TYPE_TRACK:
770 if (!(dwFlags & MCI_TRACK))
771 ret = MCIERR_MISSING_PARAMETER;
772 else {
773 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
774 &toc, sizeof(toc), &br, NULL)) {
775 WARN("error reading TOC !\n");
776 return MCICDA_GetError(wmcda);
778 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack)
779 ret = MCIERR_OUTOFRANGE;
780 else
781 lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ?
782 MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO;
784 TRACE("MCI_CDA_STATUS_TYPE_TRACK[%d]=%ld\n", lpParms->dwTrack, lpParms->dwReturn);
785 break;
786 default:
787 FIXME("unknown command %08X !\n", lpParms->dwItem);
788 return MCIERR_UNRECOGNIZED_COMMAND;
790 } else {
791 WARN("not MCI_STATUS_ITEM !\n");
793 return ret;
796 /**************************************************************************
797 * MCICDA_SkipDataTracks [internal]
799 static DWORD MCICDA_SkipDataTracks(WINE_MCICDAUDIO* wmcda,DWORD *frame)
801 int i;
802 DWORD br;
803 CDROM_TOC toc;
804 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
805 &toc, sizeof(toc), &br, NULL)) {
806 WARN("error reading TOC !\n");
807 return MCICDA_GetError(wmcda);
809 /* Locate first track whose starting frame is bigger than frame */
810 for(i=toc.FirstTrack;i<=toc.LastTrack+1;i++)
811 if ( FRAME_OF_TOC(toc, i) > *frame ) break;
812 if (i <= toc.FirstTrack && i>toc.LastTrack+1) {
813 i = 0; /* requested address is out of range: go back to start */
814 *frame = FRAME_OF_TOC(toc,toc.FirstTrack);
816 else
817 i--;
818 /* i points to last track whose start address is not greater than frame.
819 * Now skip non-audio tracks */
820 for(;i<=toc.LastTrack+1;i++)
821 if ( ! (toc.TrackData[i-toc.FirstTrack].Control & 4) )
822 break;
823 /* The frame will be an address in the next audio track or
824 * address of lead-out. */
825 if ( FRAME_OF_TOC(toc, i) > *frame )
826 *frame = FRAME_OF_TOC(toc, i);
827 return 0;
830 /**************************************************************************
831 * MCICDA_Play [internal]
833 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
835 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
836 DWORD ret = 0, start, end;
837 DWORD br;
838 CDROM_PLAY_AUDIO_MSF play;
839 CDROM_SUB_Q_DATA_FORMAT fmt;
840 SUB_Q_CHANNEL_DATA data;
841 CDROM_TOC toc;
843 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
845 if (lpParms == NULL)
846 return MCIERR_NULL_PARAMETER_BLOCK;
848 if (wmcda == NULL)
849 return MCIERR_INVALID_DEVICE_ID;
851 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
852 &toc, sizeof(toc), &br, NULL)) {
853 WARN("error reading TOC !\n");
854 return MCICDA_GetError(wmcda);
857 if (dwFlags & MCI_FROM) {
858 start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom);
859 if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
860 return ret;
861 TRACE("MCI_FROM=%08X -> %u\n", lpParms->dwFrom, start);
862 } else {
863 fmt.Format = IOCTL_CDROM_CURRENT_POSITION;
864 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt),
865 &data, sizeof(data), &br, NULL)) {
866 return MCICDA_GetError(wmcda);
868 start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress);
869 if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) )
870 return ret;
872 if (dwFlags & MCI_TO) {
873 end = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
874 TRACE("MCI_TO=%08X -> %u\n", lpParms->dwTo, end);
875 } else {
876 end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
878 TRACE("Playing from %u to %u\n", start, end);
880 if (wmcda->hThread != 0) {
881 SetEvent(wmcda->stopEvent);
882 WaitForSingleObject(wmcda->hThread, INFINITE);
884 CloseHandle(wmcda->hThread);
885 wmcda->hThread = 0;
886 CloseHandle(wmcda->stopEvent);
887 wmcda->stopEvent = 0;
889 IDirectSoundBuffer_Stop(wmcda->dsBuf);
890 IDirectSoundBuffer_Release(wmcda->dsBuf);
891 wmcda->dsBuf = NULL;
892 IDirectSound_Release(wmcda->dsObj);
893 wmcda->dsObj = NULL;
896 if (pDirectSoundCreate) {
897 WAVEFORMATEX format;
898 DSBUFFERDESC desc;
899 DWORD lockLen;
900 void *cdData;
901 HRESULT hr;
903 hr = pDirectSoundCreate(NULL, &wmcda->dsObj, NULL);
904 if (SUCCEEDED(hr)) {
905 IDirectSound_SetCooperativeLevel(wmcda->dsObj, GetDesktopWindow(), DSSCL_PRIORITY);
907 /* The "raw" frame is relative to the start of the first track */
908 wmcda->start = start - FRAME_OF_TOC(toc, toc.FirstTrack);
909 wmcda->end = end - FRAME_OF_TOC(toc, toc.FirstTrack);
911 memset(&format, 0, sizeof(format));
912 format.wFormatTag = WAVE_FORMAT_PCM;
913 format.nChannels = 2;
914 format.nSamplesPerSec = 44100;
915 format.wBitsPerSample = 16;
916 format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
917 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
918 format.cbSize = 0;
920 memset(&desc, 0, sizeof(desc));
921 desc.dwSize = sizeof(desc);
922 desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
923 desc.dwBufferBytes = (CDDA_FRAG_SIZE - (CDDA_FRAG_SIZE%RAW_SECTOR_SIZE)) * CDDA_FRAG_COUNT;
924 desc.lpwfxFormat = &format;
926 hr = IDirectSound_CreateSoundBuffer(wmcda->dsObj, &desc, &wmcda->dsBuf, NULL);
928 if (SUCCEEDED(hr)) {
929 hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, 0, 0, &cdData, &lockLen,
930 NULL, NULL, DSBLOCK_ENTIREBUFFER);
932 if (SUCCEEDED(hr)) {
933 RAW_READ_INFO rdInfo;
934 int readok;
936 rdInfo.DiskOffset.QuadPart = wmcda->start<<11;
937 rdInfo.SectorCount = min(desc.dwBufferBytes/RAW_SECTOR_SIZE,
938 wmcda->end-wmcda->start);
939 rdInfo.TrackMode = CDDA;
941 readok = DeviceIoControl(wmcda->handle, IOCTL_CDROM_RAW_READ,
942 &rdInfo, sizeof(rdInfo), cdData, lockLen,
943 &br, NULL);
944 IDirectSoundBuffer_Unlock(wmcda->dsBuf, cdData, lockLen, NULL, 0);
946 if (readok) {
947 wmcda->start += rdInfo.SectorCount;
948 wmcda->stopEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
950 if (wmcda->stopEvent != 0)
951 wmcda->hThread = CreateThread(NULL, 0, MCICDA_playLoop, wmcda, 0, &br);
952 if (wmcda->hThread != 0) {
953 hr = IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING);
954 if (SUCCEEDED(hr))
955 return ret;
957 SetEvent(wmcda->stopEvent);
958 WaitForSingleObject(wmcda->hThread, INFINITE);
959 CloseHandle(wmcda->hThread);
960 wmcda->hThread = 0;
964 if (wmcda->stopEvent != 0) {
965 CloseHandle(wmcda->stopEvent);
966 wmcda->stopEvent = 0;
968 if (wmcda->dsBuf) {
969 IDirectSoundBuffer_Release(wmcda->dsBuf);
970 wmcda->dsBuf = NULL;
972 if (wmcda->dsObj) {
973 IDirectSound_Release(wmcda->dsObj);
974 wmcda->dsObj = NULL;
978 play.StartingM = start / CDFRAMES_PERMIN;
979 play.StartingS = (start / CDFRAMES_PERSEC) % 60;
980 play.StartingF = start % CDFRAMES_PERSEC;
981 play.EndingM = end / CDFRAMES_PERMIN;
982 play.EndingS = (end / CDFRAMES_PERSEC) % 60;
983 play.EndingF = end % CDFRAMES_PERSEC;
984 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play),
985 NULL, 0, &br, NULL)) {
986 ret = MCIERR_HARDWARE;
987 } else if (dwFlags & MCI_NOTIFY) {
988 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
990 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
991 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
994 return ret;
997 /**************************************************************************
998 * MCICDA_Stop [internal]
1000 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1002 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
1003 DWORD br;
1005 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1007 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
1009 if (wmcda->hThread != 0) {
1010 SetEvent(wmcda->stopEvent);
1011 WaitForSingleObject(wmcda->hThread, INFINITE);
1013 CloseHandle(wmcda->hThread);
1014 wmcda->hThread = 0;
1015 CloseHandle(wmcda->stopEvent);
1016 wmcda->stopEvent = 0;
1018 IDirectSoundBuffer_Release(wmcda->dsBuf);
1019 wmcda->dsBuf = NULL;
1020 IDirectSound_Release(wmcda->dsObj);
1021 wmcda->dsObj = NULL;
1023 else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL))
1024 return MCIERR_HARDWARE;
1026 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1027 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1028 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1029 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1031 return 0;
1034 /**************************************************************************
1035 * MCICDA_Pause [internal]
1037 static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1039 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
1040 DWORD br;
1042 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1044 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
1046 if (wmcda->hThread != 0) {
1047 /* Don't bother calling stop if the playLoop thread has already stopped */
1048 if(WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0 &&
1049 FAILED(IDirectSoundBuffer_Stop(wmcda->dsBuf)))
1050 return MCIERR_HARDWARE;
1052 else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL))
1053 return MCIERR_HARDWARE;
1055 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1056 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1057 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1058 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1060 return 0;
1063 /**************************************************************************
1064 * MCICDA_Resume [internal]
1066 static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1068 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
1069 DWORD br;
1071 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1073 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
1075 if (wmcda->hThread != 0) {
1076 /* Don't restart if the playLoop thread has already stopped */
1077 if(WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0 &&
1078 FAILED(IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING)))
1079 return MCIERR_HARDWARE;
1081 else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL))
1082 return MCIERR_HARDWARE;
1084 if (lpParms && (dwFlags & MCI_NOTIFY)) {
1085 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1086 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1087 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1089 return 0;
1092 /**************************************************************************
1093 * MCICDA_Seek [internal]
1095 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1097 DWORD at;
1098 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
1099 CDROM_SEEK_AUDIO_MSF seek;
1100 DWORD br, ret;
1101 CDROM_TOC toc;
1103 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1105 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
1106 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1108 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0,
1109 &toc, sizeof(toc), &br, NULL)) {
1110 WARN("error reading TOC !\n");
1111 return MCICDA_GetError(wmcda);
1113 switch (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)) {
1114 case MCI_SEEK_TO_START:
1115 TRACE("Seeking to start\n");
1116 at = FRAME_OF_TOC(toc,toc.FirstTrack);
1117 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
1118 return ret;
1119 break;
1120 case MCI_SEEK_TO_END:
1121 TRACE("Seeking to end\n");
1122 at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1;
1123 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
1124 return ret;
1125 break;
1126 case MCI_TO:
1127 TRACE("Seeking to %u\n", lpParms->dwTo);
1128 at = MCICDA_CalcFrame(wmcda, lpParms->dwTo);
1129 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) )
1130 return ret;
1131 break;
1132 default:
1133 TRACE("Unknown seek action %08lX\n",
1134 (dwFlags & ~(MCI_NOTIFY|MCI_WAIT)));
1135 return MCIERR_UNSUPPORTED_FUNCTION;
1138 if (wmcda->hThread != 0) {
1139 EnterCriticalSection(&wmcda->cs);
1140 wmcda->start = at - FRAME_OF_TOC(toc, toc.FirstTrack);
1141 /* Flush remaining data, or just let it play into the new data? */
1142 LeaveCriticalSection(&wmcda->cs);
1144 else {
1145 seek.M = at / CDFRAMES_PERMIN;
1146 seek.S = (at / CDFRAMES_PERSEC) % 60;
1147 seek.F = at % CDFRAMES_PERSEC;
1148 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek),
1149 NULL, 0, &br, NULL))
1150 return MCIERR_HARDWARE;
1153 if (dwFlags & MCI_NOTIFY) {
1154 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms->dwCallback);
1155 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1156 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1158 return 0;
1161 /**************************************************************************
1162 * MCICDA_SetDoor [internal]
1164 static DWORD MCICDA_SetDoor(UINT wDevID, BOOL open)
1166 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
1167 DWORD br;
1169 TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE");
1171 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
1173 if (!DeviceIoControl(wmcda->handle,
1174 (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA,
1175 NULL, 0, NULL, 0, &br, NULL))
1176 return MCIERR_HARDWARE;
1178 return 0;
1181 /**************************************************************************
1182 * MCICDA_Set [internal]
1184 static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1186 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID);
1188 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1190 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID;
1192 if (dwFlags & MCI_SET_DOOR_OPEN) {
1193 MCICDA_SetDoor(wDevID, TRUE);
1195 if (dwFlags & MCI_SET_DOOR_CLOSED) {
1196 MCICDA_SetDoor(wDevID, FALSE);
1199 /* only functions which require valid lpParms below this line ! */
1200 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK;
1202 TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
1203 TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
1205 if (dwFlags & MCI_SET_TIME_FORMAT) {
1206 switch (lpParms->dwTimeFormat) {
1207 case MCI_FORMAT_MILLISECONDS:
1208 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1209 break;
1210 case MCI_FORMAT_MSF:
1211 TRACE("MCI_FORMAT_MSF !\n");
1212 break;
1213 case MCI_FORMAT_TMSF:
1214 TRACE("MCI_FORMAT_TMSF !\n");
1215 break;
1216 default:
1217 WARN("bad time format !\n");
1218 return MCIERR_BAD_TIME_FORMAT;
1220 wmcda->dwTimeFormat = lpParms->dwTimeFormat;
1222 if (dwFlags & MCI_SET_VIDEO) return MCIERR_UNSUPPORTED_FUNCTION;
1223 if (dwFlags & MCI_SET_ON) return MCIERR_UNSUPPORTED_FUNCTION;
1224 if (dwFlags & MCI_SET_OFF) return MCIERR_UNSUPPORTED_FUNCTION;
1225 if (dwFlags & MCI_NOTIFY) {
1226 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
1227 lpParms->dwCallback);
1228 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1229 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1231 return 0;
1234 /**************************************************************************
1235 * DriverProc (MCICDA.@)
1237 LRESULT CALLBACK MCICDA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1238 LPARAM dwParam1, LPARAM dwParam2)
1240 switch(wMsg) {
1241 case DRV_LOAD: return 1;
1242 case DRV_FREE: return 1;
1243 case DRV_OPEN: return MCICDA_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1244 case DRV_CLOSE: return MCICDA_drvClose(dwDevID);
1245 case DRV_ENABLE: return 1;
1246 case DRV_DISABLE: return 1;
1247 case DRV_QUERYCONFIGURE: return 1;
1248 case DRV_CONFIGURE: MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1;
1249 case DRV_INSTALL: return DRVCNF_RESTART;
1250 case DRV_REMOVE: return DRVCNF_RESTART;
1253 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1255 switch (wMsg) {
1256 case MCI_OPEN_DRIVER: return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSW)dwParam2);
1257 case MCI_CLOSE_DRIVER: return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1258 case MCI_GETDEVCAPS: return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2);
1259 case MCI_INFO: return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSW)dwParam2);
1260 case MCI_STATUS: return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2);
1261 case MCI_SET: return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2);
1262 case MCI_PLAY: return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2);
1263 case MCI_STOP: return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1264 case MCI_PAUSE: return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1265 case MCI_RESUME: return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2);
1266 case MCI_SEEK: return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2);
1267 /* commands that should report an error as they are not supported in
1268 * the native version */
1269 case MCI_SET_DOOR_CLOSED:
1270 case MCI_SET_DOOR_OPEN:
1271 case MCI_LOAD:
1272 case MCI_SAVE:
1273 case MCI_FREEZE:
1274 case MCI_PUT:
1275 case MCI_REALIZE:
1276 case MCI_UNFREEZE:
1277 case MCI_UPDATE:
1278 case MCI_WHERE:
1279 case MCI_STEP:
1280 case MCI_SPIN:
1281 case MCI_ESCAPE:
1282 case MCI_COPY:
1283 case MCI_CUT:
1284 case MCI_DELETE:
1285 case MCI_PASTE:
1286 case MCI_WINDOW:
1287 TRACE("Unsupported command [0x%x]\n", wMsg);
1288 break;
1289 case MCI_OPEN:
1290 case MCI_CLOSE:
1291 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1292 break;
1293 default:
1294 TRACE("Sending msg [0x%x] to default driver proc\n", wMsg);
1295 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1297 return MCIERR_UNRECOGNIZED_COMMAND;
1300 /*-----------------------------------------------------------------------*/