1 /* 2 * MCI driver for audio CD (MCICDA) 3 * 4 * Copyright 1994 Martin Ayotte 5 * Copyright 1998-99 Eric Pouech 6 * Copyright 2000 Andreas Mohr 7 * 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. 12 * 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. 17 * 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 21 */ 22 23 #include "config.h" 24 #include <stdarg.h> 25 #include <stdio.h> 26 #include <string.h> 27 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 "wine/winternl.h" 38 #include "wine/debug.h" 39 #include "wine/unicode.h" 40 #include "dsound.h" 41 42 WINE_DEFAULT_DEBUG_CHANNEL(mcicda); 43 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) 48 49 /* Defined by red-book standard; do not change! */ 50 #define RAW_SECTOR_SIZE (2352) 51 52 /* Must be >= RAW_SECTOR_SIZE */ 53 #define CDDA_FRAG_SIZE (32768) 54 /* Must be >= 2 */ 55 #define CDDA_FRAG_COUNT (3) 56 57 typedef struct { 58 UINT wDevID; 59 int nUseCount; /* Incremented for each shared open */ 60 BOOL fShareable; /* TRUE if first open was shareable */ 61 MCIDEVICEID wNotifyDeviceID; /* MCI device ID with a pending notification */ 62 HANDLE hCallback; /* Callback handle for pending notification */ 63 DWORD dwTimeFormat; 64 HANDLE handle; 65 66 /* The following are used for digital playback only */ 67 HANDLE hThread; 68 HANDLE stopEvent; 69 DWORD start, end; 70 71 IDirectSound *dsObj; 72 IDirectSoundBuffer *dsBuf; 73 74 CRITICAL_SECTION cs; 75 } WINE_MCICDAUDIO; 76 77 /*-----------------------------------------------------------------------*/ 78 79 typedef HRESULT(WINAPI*LPDIRECTSOUNDCREATE)(LPCGUID,LPDIRECTSOUND*,LPUNKNOWN); 80 static LPDIRECTSOUNDCREATE pDirectSoundCreate; 81 82 static BOOL device_io(HANDLE dev, DWORD code, void *inbuffer, DWORD insize, void *outbuffer, DWORD outsize, DWORD *retsize, OVERLAPPED *overlapped) 83 { 84 const char *str; 85 BOOL ret = DeviceIoControl(dev, code, inbuffer, insize, outbuffer, outsize, retsize, overlapped); 86 87 #define XX(x) case (x): str = #x; break 88 switch (code) 89 { 90 XX(IOCTL_CDROM_RAW_READ); 91 XX(IOCTL_CDROM_READ_TOC); 92 XX(IOCTL_CDROM_READ_Q_CHANNEL); 93 XX(IOCTL_CDROM_SEEK_AUDIO_MSF); 94 XX(IOCTL_CDROM_PLAY_AUDIO_MSF); 95 XX(IOCTL_CDROM_STOP_AUDIO); 96 XX(IOCTL_CDROM_PAUSE_AUDIO); 97 XX(IOCTL_CDROM_RESUME_AUDIO); 98 XX(IOCTL_STORAGE_EJECT_MEDIA); 99 XX(IOCTL_STORAGE_LOAD_MEDIA); 100 default: str = wine_dbg_sprintf("UNKNOWN (0x%x)", code); 101 }; 102 #undef XX 103 TRACE("Device %p, Code %s -> Return %d, Bytes %u\n", dev, str, ret, *retsize); 104 return ret; 105 } 106 107 static DWORD CALLBACK MCICDA_playLoop(void *ptr) 108 { 109 WINE_MCICDAUDIO *wmcda = (WINE_MCICDAUDIO*)ptr; 110 DWORD lastPos, curPos, endPos, br; 111 void *cdData; 112 DWORD lockLen, fragLen; 113 DSBCAPS caps; 114 RAW_READ_INFO rdInfo; 115 HRESULT hr = DS_OK; 116 117 memset(&caps, 0, sizeof(caps)); 118 caps.dwSize = sizeof(caps); 119 hr = IDirectSoundBuffer_GetCaps(wmcda->dsBuf, &caps); 120 121 fragLen = caps.dwBufferBytes/CDDA_FRAG_COUNT; 122 curPos = lastPos = 0; 123 endPos = ~0u; 124 while (SUCCEEDED(hr) && endPos != lastPos && 125 WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0) { 126 hr = IDirectSoundBuffer_GetCurrentPosition(wmcda->dsBuf, &curPos, NULL); 127 if ((curPos-lastPos+caps.dwBufferBytes)%caps.dwBufferBytes < fragLen) { 128 Sleep(1); 129 continue; 130 } 131 132 EnterCriticalSection(&wmcda->cs); 133 rdInfo.DiskOffset.QuadPart = wmcda->start<<11; 134 rdInfo.SectorCount = min(fragLen/RAW_SECTOR_SIZE, wmcda->end-wmcda->start); 135 rdInfo.TrackMode = CDDA; 136 137 hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, lastPos, fragLen, &cdData, &lockLen, NULL, NULL, 0); 138 if (hr == DSERR_BUFFERLOST) { 139 if(FAILED(IDirectSoundBuffer_Restore(wmcda->dsBuf)) || 140 FAILED(IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING))) { 141 LeaveCriticalSection(&wmcda->cs); 142 break; 143 } 144 hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, lastPos, fragLen, &cdData, &lockLen, NULL, NULL, 0); 145 } 146 147 if (SUCCEEDED(hr)) { 148 if (rdInfo.SectorCount > 0) { 149 if (!device_io(wmcda->handle, IOCTL_CDROM_RAW_READ, &rdInfo, sizeof(rdInfo), cdData, lockLen, &br, NULL)) 150 WARN("CD read failed at sector %d: 0x%x\n", wmcda->start, GetLastError()); 151 } 152 if (rdInfo.SectorCount*RAW_SECTOR_SIZE < lockLen) { 153 if(endPos == ~0u) endPos = lastPos; 154 memset((BYTE*)cdData + rdInfo.SectorCount*RAW_SECTOR_SIZE, 0, 155 lockLen - rdInfo.SectorCount*RAW_SECTOR_SIZE); 156 } 157 hr = IDirectSoundBuffer_Unlock(wmcda->dsBuf, cdData, lockLen, NULL, 0); 158 } 159 160 lastPos += fragLen; 161 lastPos %= caps.dwBufferBytes; 162 wmcda->start += rdInfo.SectorCount; 163 164 LeaveCriticalSection(&wmcda->cs); 165 } 166 IDirectSoundBuffer_Stop(wmcda->dsBuf); 167 SetEvent(wmcda->stopEvent); 168 169 /* A design bug in native: the independent CD player called by the 170 * MCI has no means to signal end of playing, therefore the MCI 171 * notification is left hanging. MCI_NOTIFY_SUPERSEDED will be 172 * signaled by the next command that has MCI_NOTIFY set (or 173 * MCI_NOTIFY_ABORTED for MCI_PLAY). */ 174 175 return 0; 176 } 177 178 179 180 /************************************************************************** 181 * MCICDA_drvOpen [internal] 182 */ 183 static DWORD MCICDA_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp) 184 { 185 static HMODULE dsHandle; 186 WINE_MCICDAUDIO* wmcda; 187 188 if (!modp) return 0xFFFFFFFF; 189 /* FIXME: MCIERR_CANNOT_LOAD_DRIVER if there's no drive of type CD-ROM */ 190 191 wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO)); 192 193 if (!wmcda) 194 return 0; 195 196 if (!dsHandle) { 197 dsHandle = LoadLibraryA("dsound.dll"); 198 if(dsHandle) 199 pDirectSoundCreate = (LPDIRECTSOUNDCREATE)GetProcAddress(dsHandle, "DirectSoundCreate"); 200 } 201 202 wmcda->wDevID = modp->wDeviceID; 203 mciSetDriverData(wmcda->wDevID, (DWORD_PTR)wmcda); 204 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE; 205 modp->wType = MCI_DEVTYPE_CD_AUDIO; 206 InitializeCriticalSection(&wmcda->cs); 207 wmcda->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINE_MCICDAUDIO.cs"); 208 return modp->wDeviceID; 209 } 210 211 /************************************************************************** 212 * MCICDA_drvClose [internal] 213 */ 214 static DWORD MCICDA_drvClose(DWORD dwDevID) 215 { 216 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID); 217 218 if (wmcda) { 219 wmcda->cs.DebugInfo->Spare[0] = 0; 220 DeleteCriticalSection(&wmcda->cs); 221 HeapFree(GetProcessHeap(), 0, wmcda); 222 mciSetDriverData(dwDevID, 0); 223 } 224 return (dwDevID == 0xFFFFFFFF) ? 1 : 0; 225 } 226 227 /************************************************************************** 228 * MCICDA_GetOpenDrv [internal] 229 */ 230 static WINE_MCICDAUDIO* MCICDA_GetOpenDrv(UINT wDevID) 231 { 232 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID); 233 234 if (wmcda == NULL || wmcda->nUseCount == 0) { 235 WARN("Invalid wDevID=%u\n", wDevID); 236 return 0; 237 } 238 return wmcda; 239 } 240 241 /************************************************************************** 242 * MCICDA_mciNotify [internal] 243 * 244 * Notifications in MCI work like a 1-element queue. 245 * Each new notification request supersedes the previous one. 246 */ 247 static void MCICDA_Notify(DWORD_PTR hWndCallBack, WINE_MCICDAUDIO* wmcda, UINT wStatus) 248 { 249 MCIDEVICEID wDevID = wmcda->wNotifyDeviceID; 250 HANDLE old = InterlockedExchangePointer(&wmcda->hCallback, NULL); 251 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED); 252 mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus); 253 } 254 255 /************************************************************************** 256 * MCICDA_ReadTOC [internal] 257 */ 258 static BOOL MCICDA_ReadTOC(WINE_MCICDAUDIO* wmcda, CDROM_TOC *toc, DWORD *br) 259 { 260 if (!device_io(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, toc, sizeof(*toc), br, NULL)) { 261 WARN("error reading TOC !\n"); 262 return FALSE; 263 } 264 return TRUE; 265 } 266 267 /************************************************************************** 268 * MCICDA_GetStatus [internal] 269 */ 270 static DWORD MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda) 271 { 272 CDROM_SUB_Q_DATA_FORMAT fmt; 273 SUB_Q_CHANNEL_DATA data; 274 DWORD br; 275 DWORD mode = MCI_MODE_NOT_READY; 276 277 fmt.Format = IOCTL_CDROM_CURRENT_POSITION; 278 if(wmcda->hThread != 0) { 279 DWORD status; 280 HRESULT hr; 281 282 hr = IDirectSoundBuffer_GetStatus(wmcda->dsBuf, &status); 283 if(SUCCEEDED(hr)) { 284 if(!(status&DSBSTATUS_PLAYING)) { 285 if(WaitForSingleObject(wmcda->stopEvent, 0) == WAIT_OBJECT_0) 286 mode = MCI_MODE_STOP; 287 else 288 mode = MCI_MODE_PAUSE; 289 } 290 else 291 mode = MCI_MODE_PLAY; 292 } 293 } 294 else if (!device_io(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt), 295 &data, sizeof(data), &br, NULL)) { 296 if (GetLastError() == ERROR_NOT_READY) mode = MCI_MODE_OPEN; 297 } else { 298 switch (data.CurrentPosition.Header.AudioStatus) 299 { 300 case AUDIO_STATUS_IN_PROGRESS: mode = MCI_MODE_PLAY; break; 301 case AUDIO_STATUS_PAUSED: mode = MCI_MODE_PAUSE; break; 302 case AUDIO_STATUS_NO_STATUS: 303 case AUDIO_STATUS_PLAY_COMPLETE: mode = MCI_MODE_STOP; break; 304 case AUDIO_STATUS_PLAY_ERROR: 305 case AUDIO_STATUS_NOT_SUPPORTED: 306 default: 307 break; 308 } 309 } 310 return mode; 311 } 312 313 /************************************************************************** 314 * MCICDA_GetError [internal] 315 */ 316 static int MCICDA_GetError(WINE_MCICDAUDIO* wmcda) 317 { 318 switch (GetLastError()) 319 { 320 case ERROR_NOT_READY: return MCIERR_DEVICE_NOT_READY; 321 case ERROR_NOT_SUPPORTED: 322 case ERROR_IO_DEVICE: return MCIERR_HARDWARE; 323 default: 324 FIXME("Unknown mode %u\n", GetLastError()); 325 } 326 return MCIERR_DRIVER_INTERNAL; 327 } 328 329 /************************************************************************** 330 * MCICDA_CalcFrame [internal] 331 */ 332 static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime) 333 { 334 DWORD dwFrame = 0; 335 UINT wTrack; 336 CDROM_TOC toc; 337 DWORD br; 338 BYTE* addr; 339 340 TRACE("(%p, %08X, %u);\n", wmcda, wmcda->dwTimeFormat, dwTime); 341 342 switch (wmcda->dwTimeFormat) { 343 case MCI_FORMAT_MILLISECONDS: 344 dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000; 345 TRACE("MILLISECONDS %u\n", dwFrame); 346 break; 347 case MCI_FORMAT_MSF: 348 TRACE("MSF %02u:%02u:%02u\n", 349 MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime)); 350 dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime); 351 dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime); 352 dwFrame += MCI_MSF_FRAME(dwTime); 353 break; 354 case MCI_FORMAT_TMSF: 355 default: /* unknown format ! force TMSF ! ... */ 356 wTrack = MCI_TMSF_TRACK(dwTime); 357 if (!device_io(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 358 &toc, sizeof(toc), &br, NULL)) 359 return 0; 360 if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack) 361 return 0; 362 TRACE("MSF %02u-%02u:%02u:%02u\n", 363 MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime), 364 MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime)); 365 addr = toc.TrackData[wTrack - toc.FirstTrack].Address; 366 TRACE("TMSF trackpos[%u]=%d:%d:%d\n", 367 wTrack, addr[1], addr[2], addr[3]); 368 dwFrame = CDFRAMES_PERMIN * (addr[1] + MCI_TMSF_MINUTE(dwTime)) + 369 CDFRAMES_PERSEC * (addr[2] + MCI_TMSF_SECOND(dwTime)) + 370 addr[3] + MCI_TMSF_FRAME(dwTime); 371 break; 372 } 373 return dwFrame; 374 } 375 376 /************************************************************************** 377 * MCICDA_CalcTime [internal] 378 */ 379 static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet) 380 { 381 DWORD dwTime = 0; 382 UINT wTrack; 383 UINT wMinutes; 384 UINT wSeconds; 385 UINT wFrames; 386 CDROM_TOC toc; 387 DWORD br; 388 389 TRACE("(%p, %08X, %u);\n", wmcda, tf, dwFrame); 390 391 switch (tf) { 392 case MCI_FORMAT_MILLISECONDS: 393 dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1; 394 TRACE("MILLISECONDS %u\n", dwTime); 395 *lpRet = 0; 396 break; 397 case MCI_FORMAT_MSF: 398 wMinutes = dwFrame / CDFRAMES_PERMIN; 399 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC; 400 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds; 401 dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames); 402 TRACE("MSF %02u:%02u:%02u -> dwTime=%u\n", 403 wMinutes, wSeconds, wFrames, dwTime); 404 *lpRet = MCI_COLONIZED3_RETURN; 405 break; 406 case MCI_FORMAT_TMSF: 407 default: /* unknown format ! force TMSF ! ... */ 408 if (!device_io(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 409 &toc, sizeof(toc), &br, NULL)) 410 return 0; 411 if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) || 412 dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) { 413 ERR("Out of range value %u [%u,%u]\n", 414 dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack), 415 FRAME_OF_TOC(toc, toc.LastTrack + 1)); 416 *lpRet = 0; 417 return 0; 418 } 419 for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) { 420 if (FRAME_OF_TOC(toc, wTrack) > dwFrame) 421 break; 422 } 423 wTrack--; 424 dwFrame -= FRAME_OF_TOC(toc, wTrack); 425 wMinutes = dwFrame / CDFRAMES_PERMIN; 426 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC; 427 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds; 428 dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames); 429 TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames); 430 *lpRet = MCI_COLONIZED4_RETURN; 431 break; 432 } 433 return dwTime; 434 } 435 436 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms); 437 438 /************************************************************************** 439 * MCICDA_Open [internal] 440 */ 441 static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpOpenParms) 442 { 443 MCIDEVICEID dwDeviceID; 444 DWORD ret; 445 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID); 446 WCHAR root[7], drive = 0; 447 448 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpOpenParms); 449 450 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 451 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 452 453 dwDeviceID = lpOpenParms->wDeviceID; 454 455 if (wmcda->nUseCount > 0) { 456 /* The driver is already open on this channel */ 457 /* If the driver was opened shareable before and this open specifies */ 458 /* shareable then increment the use count */ 459 if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE)) 460 ++wmcda->nUseCount; 461 else 462 return MCIERR_MUST_USE_SHAREABLE; 463 } else { 464 wmcda->nUseCount = 1; 465 wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE; 466 } 467 if (dwFlags & MCI_OPEN_ELEMENT) { 468 if (dwFlags & MCI_OPEN_ELEMENT_ID) { 469 WARN("MCI_OPEN_ELEMENT_ID %p! Abort\n", lpOpenParms->lpstrElementName); 470 ret = MCIERR_FLAGS_NOT_COMPATIBLE; 471 goto the_error; 472 } 473 TRACE("MCI_OPEN_ELEMENT element name: %s\n", debugstr_w(lpOpenParms->lpstrElementName)); 474 /* Only the first letter counts since w2k 475 * Win9x-NT accept only d: and w98SE accepts d:\foobar as well. 476 * Play d:\Track03.cda plays from the first track, not #3. */ 477 if (!isalpha(lpOpenParms->lpstrElementName[0])) 478 { 479 ret = MCIERR_INVALID_FILE; 480 goto the_error; 481 } 482 drive = toupper(lpOpenParms->lpstrElementName[0]); 483 root[0] = drive; root[1] = ':'; root[2] = '\\'; root[3] = '\0'; 484 if (GetDriveTypeW(root) != DRIVE_CDROM) 485 { 486 ret = MCIERR_INVALID_FILE; 487 goto the_error; 488 } 489 } 490 else 491 { 492 root[0] = 'A'; root[1] = ':'; root[2] = '\\'; root[3] = '\0'; 493 for ( ; root[0] <= 'Z'; root[0]++) 494 { 495 if (GetDriveTypeW(root) == DRIVE_CDROM) 496 { 497 drive = root[0]; 498 break; 499 } 500 } 501 if (!drive) 502 { 503 ret = MCIERR_CANNOT_LOAD_DRIVER; /* drvOpen should return this */ 504 goto the_error; 505 } 506 } 507 508 wmcda->wNotifyDeviceID = dwDeviceID; 509 wmcda->dwTimeFormat = MCI_FORMAT_MSF; 510 511 /* now, open the handle */ 512 root[0] = root[1] = '\\'; root[2] = '.'; root[3] = '\\'; root[4] = drive; root[5] = ':'; root[6] = '\0'; 513 wmcda->handle = CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); 514 if (wmcda->handle == INVALID_HANDLE_VALUE) 515 { 516 ret = MCIERR_MUST_USE_SHAREABLE; 517 goto the_error; 518 } 519 520 if (dwFlags & MCI_NOTIFY) { 521 mciDriverNotify(HWND_32(LOWORD(lpOpenParms->dwCallback)), 522 dwDeviceID, MCI_NOTIFY_SUCCESSFUL); 523 } 524 return 0; 525 526 the_error: 527 --wmcda->nUseCount; 528 return ret; 529 } 530 531 /************************************************************************** 532 * MCICDA_Close [internal] 533 */ 534 static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms) 535 { 536 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 537 538 TRACE("(%04X, %08X, %p);\n", wDevID, dwParam, lpParms); 539 540 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 541 542 MCICDA_Stop(wDevID, MCI_WAIT, NULL); 543 544 if (--wmcda->nUseCount == 0) { 545 CloseHandle(wmcda->handle); 546 } 547 if ((dwParam & MCI_NOTIFY) && lpParms) 548 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 549 return 0; 550 } 551 552 /************************************************************************** 553 * MCICDA_GetDevCaps [internal] 554 */ 555 static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags, 556 LPMCI_GETDEVCAPS_PARMS lpParms) 557 { 558 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID); 559 DWORD ret = 0; 560 561 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 562 563 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 564 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 565 566 if (dwFlags & MCI_GETDEVCAPS_ITEM) { 567 TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08X;\n", lpParms->dwItem); 568 569 switch (lpParms->dwItem) { 570 case MCI_GETDEVCAPS_CAN_RECORD: 571 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 572 ret = MCI_RESOURCE_RETURNED; 573 break; 574 case MCI_GETDEVCAPS_HAS_AUDIO: 575 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 576 ret = MCI_RESOURCE_RETURNED; 577 break; 578 case MCI_GETDEVCAPS_HAS_VIDEO: 579 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 580 ret = MCI_RESOURCE_RETURNED; 581 break; 582 case MCI_GETDEVCAPS_DEVICE_TYPE: 583 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO); 584 ret = MCI_RESOURCE_RETURNED; 585 break; 586 case MCI_GETDEVCAPS_USES_FILES: 587 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 588 ret = MCI_RESOURCE_RETURNED; 589 break; 590 case MCI_GETDEVCAPS_COMPOUND_DEVICE: 591 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 592 ret = MCI_RESOURCE_RETURNED; 593 break; 594 case MCI_GETDEVCAPS_CAN_EJECT: 595 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 596 ret = MCI_RESOURCE_RETURNED; 597 break; 598 case MCI_GETDEVCAPS_CAN_PLAY: 599 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 600 ret = MCI_RESOURCE_RETURNED; 601 break; 602 case MCI_GETDEVCAPS_CAN_SAVE: 603 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 604 ret = MCI_RESOURCE_RETURNED; 605 break; 606 default: 607 WARN("Unsupported %x devCaps item\n", lpParms->dwItem); 608 return MCIERR_UNSUPPORTED_FUNCTION; 609 } 610 } else { 611 TRACE("No GetDevCaps-Item !\n"); 612 return MCIERR_MISSING_PARAMETER; 613 } 614 TRACE("lpParms->dwReturn=%08X;\n", lpParms->dwReturn); 615 if (dwFlags & MCI_NOTIFY) { 616 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 617 } 618 return ret; 619 } 620 621 static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc) 622 { 623 DWORD serial = 0; 624 int i; 625 WORD wMagic; 626 DWORD dwStart, dwEnd; 627 628 /* 629 * wMagic collects the wFrames from track 1 630 * dwStart, dwEnd collect the beginning and end of the disc respectively, in 631 * frames. 632 * There it is collected for correcting the serial when there are less than 633 * 3 tracks. 634 */ 635 wMagic = toc->TrackData[0].Address[3]; 636 dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack); 637 638 for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) { 639 serial += (toc->TrackData[i].Address[1] << 16) | 640 (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3]; 641 } 642 dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1); 643 644 if (toc->LastTrack - toc->FirstTrack + 1 < 3) 645 serial += wMagic + (dwEnd - dwStart); 646 647 return serial; 648 } 649 650 651 /************************************************************************** 652 * MCICDA_Info [internal] 653 */ 654 static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms) 655 { 656 LPCWSTR str = NULL; 657 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 658 DWORD ret = 0; 659 WCHAR buffer[16]; 660 661 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 662 663 if (lpParms == NULL || lpParms->lpstrReturn == NULL) 664 return MCIERR_NULL_PARAMETER_BLOCK; 665 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 666 667 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize); 668 669 if (dwFlags & MCI_INFO_PRODUCT) { 670 static const WCHAR wszAudioCd[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','C','D',0}; 671 str = wszAudioCd; 672 } else if (dwFlags & MCI_INFO_MEDIA_UPC) { 673 ret = MCIERR_NO_IDENTITY; 674 } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) { 675 DWORD res = 0; 676 CDROM_TOC toc; 677 DWORD br; 678 static const WCHAR wszLu[] = {'%','l','u',0}; 679 680 if (!device_io(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 681 &toc, sizeof(toc), &br, NULL)) { 682 return MCICDA_GetError(wmcda); 683 } 684 685 res = CDROM_Audio_GetSerial(&toc); 686 sprintfW(buffer, wszLu, res); 687 str = buffer; 688 } else { 689 WARN("Don't know this info command (%u)\n", dwFlags); 690 ret = MCIERR_MISSING_PARAMETER; 691 } 692 if (!ret) { 693 TRACE("=> %s\n", debugstr_w(str)); 694 if (lpParms->dwRetSize) { 695 /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize 696 * to the number of characters written, excluding \0. */ 697 lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize); 698 } else ret = MCIERR_PARAM_OVERFLOW; 699 } 700 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY)) 701 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 702 return ret; 703 } 704 705 /************************************************************************** 706 * MCICDA_Status [internal] 707 */ 708 static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms) 709 { 710 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 711 DWORD ret = 0; 712 CDROM_SUB_Q_DATA_FORMAT fmt; 713 SUB_Q_CHANNEL_DATA data; 714 CDROM_TOC toc; 715 DWORD br; 716 717 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 718 719 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 720 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 721 722 if (dwFlags & MCI_STATUS_ITEM) { 723 TRACE("dwItem = %x\n", lpParms->dwItem); 724 switch (lpParms->dwItem) { 725 case MCI_STATUS_CURRENT_TRACK: 726 fmt.Format = IOCTL_CDROM_CURRENT_POSITION; 727 if (!device_io(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt), 728 &data, sizeof(data), &br, NULL)) 729 { 730 return MCICDA_GetError(wmcda); 731 /* alt. data.CurrentPosition.TrackNumber = 1; -- what native yields */ 732 } 733 lpParms->dwReturn = data.CurrentPosition.TrackNumber; 734 TRACE("CURRENT_TRACK=%lu\n", lpParms->dwReturn); 735 break; 736 case MCI_STATUS_LENGTH: 737 if (!MCICDA_ReadTOC(wmcda, &toc, &br)) 738 return MCICDA_GetError(wmcda); 739 740 if (dwFlags & MCI_TRACK) { 741 TRACE("MCI_TRACK #%u LENGTH=??? !\n", lpParms->dwTrack); 742 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack) 743 return MCIERR_OUTOFRANGE; 744 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) - 745 FRAME_OF_TOC(toc, lpParms->dwTrack); 746 /* Windows returns one frame less than the total track length for the 747 last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */ 748 if (lpParms->dwTrack == toc.LastTrack) 749 lpParms->dwReturn--; 750 } else { 751 /* Sum of the lengths of all of the tracks. Inherits the 752 'off by one frame' behavior from the length of the last track. 753 See above comment. */ 754 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 755 FRAME_OF_TOC(toc, toc.FirstTrack) - 1; 756 } 757 lpParms->dwReturn = MCICDA_CalcTime(wmcda, 758 (wmcda->dwTimeFormat == MCI_FORMAT_TMSF) 759 ? MCI_FORMAT_MSF : wmcda->dwTimeFormat, 760 lpParms->dwReturn, 761 &ret); 762 TRACE("LENGTH=%lu\n", lpParms->dwReturn); 763 break; 764 case MCI_STATUS_MODE: 765 lpParms->dwReturn = MCICDA_GetStatus(wmcda); 766 TRACE("MCI_STATUS_MODE=%08lX\n", lpParms->dwReturn); 767 lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn); 768 ret = MCI_RESOURCE_RETURNED; 769 break; 770 case MCI_STATUS_MEDIA_PRESENT: 771 lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ? 772 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE); 773 TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N'); 774 ret = MCI_RESOURCE_RETURNED; 775 break; 776 case MCI_STATUS_NUMBER_OF_TRACKS: 777 if (!MCICDA_ReadTOC(wmcda, &toc, &br)) 778 return MCICDA_GetError(wmcda); 779 780 lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1; 781 TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu\n", lpParms->dwReturn); 782 if (lpParms->dwReturn == (WORD)-1) 783 return MCICDA_GetError(wmcda); 784 break; 785 case MCI_STATUS_POSITION: 786 switch (dwFlags & (MCI_STATUS_START | MCI_TRACK)) { 787 case MCI_STATUS_START: 788 if (!MCICDA_ReadTOC(wmcda, &toc, &br)) 789 return MCICDA_GetError(wmcda); 790 791 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack); 792 TRACE("get MCI_STATUS_START !\n"); 793 break; 794 case MCI_TRACK: 795 if (!MCICDA_ReadTOC(wmcda, &toc, &br)) 796 return MCICDA_GetError(wmcda); 797 798 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack) 799 return MCIERR_OUTOFRANGE; 800 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack); 801 TRACE("get MCI_TRACK #%u !\n", lpParms->dwTrack); 802 break; 803 case 0: 804 fmt.Format = IOCTL_CDROM_CURRENT_POSITION; 805 if (!device_io(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt), 806 &data, sizeof(data), &br, NULL)) { 807 return MCICDA_GetError(wmcda); 808 } 809 lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress); 810 break; 811 default: 812 return MCIERR_FLAGS_NOT_COMPATIBLE; 813 } 814 lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret); 815 TRACE("MCI_STATUS_POSITION=%08lX\n", lpParms->dwReturn); 816 break; 817 case MCI_STATUS_READY: 818 TRACE("MCI_STATUS_READY !\n"); 819 switch (MCICDA_GetStatus(wmcda)) 820 { 821 case MCI_MODE_NOT_READY: 822 case MCI_MODE_OPEN: 823 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 824 break; 825 default: 826 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 827 break; 828 } 829 TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn)); 830 ret = MCI_RESOURCE_RETURNED; 831 break; 832 case MCI_STATUS_TIME_FORMAT: 833 lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, MCI_FORMAT_RETURN_BASE + wmcda->dwTimeFormat); 834 TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn)); 835 ret = MCI_RESOURCE_RETURNED; 836 break; 837 case 4001: /* FIXME: for bogus FullCD */ 838 case MCI_CDA_STATUS_TYPE_TRACK: 839 if (!(dwFlags & MCI_TRACK)) 840 ret = MCIERR_MISSING_PARAMETER; 841 else { 842 if (!MCICDA_ReadTOC(wmcda, &toc, &br)) 843 return MCICDA_GetError(wmcda); 844 845 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack) 846 ret = MCIERR_OUTOFRANGE; 847 else 848 lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ? 849 MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO; 850 /* FIXME: MAKEMCIRESOURCE "audio" | "other", localised */ 851 } 852 TRACE("MCI_CDA_STATUS_TYPE_TRACK[%d]=%ld\n", lpParms->dwTrack, lpParms->dwReturn); 853 break; 854 default: 855 FIXME("unknown command %08X !\n", lpParms->dwItem); 856 return MCIERR_UNSUPPORTED_FUNCTION; 857 } 858 } else return MCIERR_MISSING_PARAMETER; 859 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0) 860 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 861 return ret; 862 } 863 864 /************************************************************************** 865 * MCICDA_SkipDataTracks [internal] 866 */ 867 static DWORD MCICDA_SkipDataTracks(WINE_MCICDAUDIO* wmcda,DWORD *frame) 868 { 869 int i; 870 DWORD br; 871 CDROM_TOC toc; 872 if (!MCICDA_ReadTOC(wmcda, &toc, &br)) 873 return MCICDA_GetError(wmcda); 874 875 if (*frame < FRAME_OF_TOC(toc,toc.FirstTrack) || 876 *frame >= FRAME_OF_TOC(toc,toc.LastTrack+1)) /* lead-out */ 877 return MCIERR_OUTOFRANGE; 878 for(i=toc.LastTrack+1;i>toc.FirstTrack;i--) 879 if ( FRAME_OF_TOC(toc, i) <= *frame ) break; 880 /* i points to last track whose start address is not greater than frame. 881 * Now skip non-audio tracks */ 882 for(;i<=toc.LastTrack;i++) 883 if ( ! (toc.TrackData[i-toc.FirstTrack].Control & 4) ) 884 break; 885 /* The frame will be an address in the next audio track or 886 * address of lead-out. */ 887 if ( FRAME_OF_TOC(toc, i) > *frame ) 888 *frame = FRAME_OF_TOC(toc, i); 889 /* Lead-out is an invalid seek position (on Linux as well). */ 890 if (*frame == FRAME_OF_TOC(toc,toc.LastTrack+1)) 891 (*frame)--; 892 return 0; 893 } 894 895 /************************************************************************** 896 * MCICDA_Play [internal] 897 */ 898 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms) 899 { 900 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 901 DWORD ret = 0, start, end; 902 HANDLE oldcb; 903 DWORD br; 904 CDROM_PLAY_AUDIO_MSF play; 905 CDROM_SUB_Q_DATA_FORMAT fmt; 906 SUB_Q_CHANNEL_DATA data; 907 CDROM_TOC toc; 908 909 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 910 911 if (lpParms == NULL) 912 return MCIERR_NULL_PARAMETER_BLOCK; 913 914 if (wmcda == NULL) 915 return MCIERR_INVALID_DEVICE_ID; 916 917 if (!MCICDA_ReadTOC(wmcda, &toc, &br)) 918 return MCICDA_GetError(wmcda); 919 920 if (dwFlags & MCI_FROM) { 921 start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom); 922 if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) ) 923 return ret; 924 TRACE("MCI_FROM=%08X -> %u\n", lpParms->dwFrom, start); 925 } else { 926 fmt.Format = IOCTL_CDROM_CURRENT_POSITION; 927 if (!device_io(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt), 928 &data, sizeof(data), &br, NULL)) { 929 return MCICDA_GetError(wmcda); 930 } 931 start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress); 932 if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) ) 933 return ret; 934 } 935 if (dwFlags & MCI_TO) { 936 end = MCICDA_CalcFrame(wmcda, lpParms->dwTo); 937 if ( (ret=MCICDA_SkipDataTracks(wmcda, &end)) ) 938 return ret; 939 TRACE("MCI_TO=%08X -> %u\n", lpParms->dwTo, end); 940 } else { 941 end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1; 942 } 943 if (end < start) return MCIERR_OUTOFRANGE; 944 TRACE("Playing from %u to %u\n", start, end); 945 946 oldcb = InterlockedExchangePointer(&wmcda->hCallback, 947 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL); 948 if (oldcb) mciDriverNotify(oldcb, wmcda->wNotifyDeviceID, MCI_NOTIFY_ABORTED); 949 950 if (start == end || start == FRAME_OF_TOC(toc,toc.LastTrack+1)-1) { 951 if (dwFlags & MCI_NOTIFY) { 952 oldcb = InterlockedExchangePointer(&wmcda->hCallback, NULL); 953 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_SUCCESSFUL); 954 } 955 return MMSYSERR_NOERROR; 956 } 957 958 if (wmcda->hThread != 0) { 959 SetEvent(wmcda->stopEvent); 960 WaitForSingleObject(wmcda->hThread, INFINITE); 961 962 CloseHandle(wmcda->hThread); 963 wmcda->hThread = 0; 964 CloseHandle(wmcda->stopEvent); 965 wmcda->stopEvent = 0; 966 967 IDirectSoundBuffer_Stop(wmcda->dsBuf); 968 IDirectSoundBuffer_Release(wmcda->dsBuf); 969 wmcda->dsBuf = NULL; 970 IDirectSound_Release(wmcda->dsObj); 971 wmcda->dsObj = NULL; 972 } 973 974 if (pDirectSoundCreate) { 975 WAVEFORMATEX format; 976 DSBUFFERDESC desc; 977 DWORD lockLen; 978 void *cdData; 979 HRESULT hr; 980 981 hr = pDirectSoundCreate(NULL, &wmcda->dsObj, NULL); 982 if (SUCCEEDED(hr)) { 983 IDirectSound_SetCooperativeLevel(wmcda->dsObj, GetDesktopWindow(), DSSCL_PRIORITY); 984 985 /* The "raw" frame is relative to the start of the first track */ 986 wmcda->start = start - FRAME_OF_TOC(toc, toc.FirstTrack); 987 wmcda->end = end - FRAME_OF_TOC(toc, toc.FirstTrack); 988 989 memset(&format, 0, sizeof(format)); 990 format.wFormatTag = WAVE_FORMAT_PCM; 991 format.nChannels = 2; 992 format.nSamplesPerSec = 44100; 993 format.wBitsPerSample = 16; 994 format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; 995 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; 996 format.cbSize = 0; 997 998 memset(&desc, 0, sizeof(desc)); 999 desc.dwSize = sizeof(desc); 1000 desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; 1001 desc.dwBufferBytes = (CDDA_FRAG_SIZE - (CDDA_FRAG_SIZE%RAW_SECTOR_SIZE)) * CDDA_FRAG_COUNT; 1002 desc.lpwfxFormat = &format; 1003 1004 hr = IDirectSound_CreateSoundBuffer(wmcda->dsObj, &desc, &wmcda->dsBuf, NULL); 1005 } 1006 if (SUCCEEDED(hr)) { 1007 hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, 0, 0, &cdData, &lockLen, 1008 NULL, NULL, DSBLOCK_ENTIREBUFFER); 1009 } 1010 if (SUCCEEDED(hr)) { 1011 RAW_READ_INFO rdInfo; 1012 int readok; 1013 1014 rdInfo.DiskOffset.QuadPart = wmcda->start<<11; 1015 rdInfo.SectorCount = min(desc.dwBufferBytes/RAW_SECTOR_SIZE, 1016 wmcda->end-wmcda->start); 1017 rdInfo.TrackMode = CDDA; 1018 1019 readok = device_io(wmcda->handle, IOCTL_CDROM_RAW_READ, 1020 &rdInfo, sizeof(rdInfo), cdData, lockLen, 1021 &br, NULL); 1022 IDirectSoundBuffer_Unlock(wmcda->dsBuf, cdData, lockLen, NULL, 0); 1023 1024 if (readok) { 1025 wmcda->start += rdInfo.SectorCount; 1026 wmcda->stopEvent = CreateEventA(NULL, TRUE, FALSE, NULL); 1027 } 1028 if (wmcda->stopEvent != 0) 1029 wmcda->hThread = CreateThread(NULL, 0, MCICDA_playLoop, wmcda, 0, &br); 1030 if (wmcda->hThread != 0) { 1031 hr = IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING); 1032 if (SUCCEEDED(hr)) { 1033 /* FIXME: implement MCI_WAIT and send notification only in that case */ 1034 if (0) { 1035 oldcb = InterlockedExchangePointer(&wmcda->hCallback, NULL); 1036 if (oldcb) mciDriverNotify(oldcb, wmcda->wNotifyDeviceID, 1037 FAILED(hr) ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL); 1038 } 1039 return ret; 1040 } 1041 1042 SetEvent(wmcda->stopEvent); 1043 WaitForSingleObject(wmcda->hThread, INFINITE); 1044 CloseHandle(wmcda->hThread); 1045 wmcda->hThread = 0; 1046 } 1047 } 1048 1049 if (wmcda->stopEvent != 0) { 1050 CloseHandle(wmcda->stopEvent); 1051 wmcda->stopEvent = 0; 1052 } 1053 if (wmcda->dsBuf) { 1054 IDirectSoundBuffer_Release(wmcda->dsBuf); 1055 wmcda->dsBuf = NULL; 1056 } 1057 if (wmcda->dsObj) { 1058 IDirectSound_Release(wmcda->dsObj); 1059 wmcda->dsObj = NULL; 1060 } 1061 } 1062 1063 play.StartingM = start / CDFRAMES_PERMIN; 1064 play.StartingS = (start / CDFRAMES_PERSEC) % 60; 1065 play.StartingF = start % CDFRAMES_PERSEC; 1066 play.EndingM = end / CDFRAMES_PERMIN; 1067 play.EndingS = (end / CDFRAMES_PERSEC) % 60; 1068 play.EndingF = end % CDFRAMES_PERSEC; 1069 if (!device_io(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play), 1070 NULL, 0, &br, NULL)) { 1071 wmcda->hCallback = NULL; 1072 ret = MCIERR_HARDWARE; 1073 } 1074 /* The independent CD player has no means to signal MCI_NOTIFY when it's done. 1075 * Native sends a notification with MCI_WAIT only. */ 1076 return ret; 1077 } 1078 1079 /************************************************************************** 1080 * MCICDA_Stop [internal] 1081 */ 1082 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 1083 { 1084 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 1085 HANDLE oldcb; 1086 DWORD br; 1087 1088 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 1089 1090 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 1091 1092 oldcb = InterlockedExchangePointer(&wmcda->hCallback, NULL); 1093 if (oldcb) mciDriverNotify(oldcb, wmcda->wNotifyDeviceID, MCI_NOTIFY_ABORTED); 1094 1095 if (wmcda->hThread != 0) { 1096 SetEvent(wmcda->stopEvent); 1097 WaitForSingleObject(wmcda->hThread, INFINITE); 1098 1099 CloseHandle(wmcda->hThread); 1100 wmcda->hThread = 0; 1101 CloseHandle(wmcda->stopEvent); 1102 wmcda->stopEvent = 0; 1103 1104 IDirectSoundBuffer_Release(wmcda->dsBuf); 1105 wmcda->dsBuf = NULL; 1106 IDirectSound_Release(wmcda->dsObj); 1107 wmcda->dsObj = NULL; 1108 } 1109 else if (!device_io(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL)) 1110 return MCIERR_HARDWARE; 1111 1112 if ((dwFlags & MCI_NOTIFY) && lpParms) 1113 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 1114 return 0; 1115 } 1116 1117 /************************************************************************** 1118 * MCICDA_Pause [internal] 1119 */ 1120 static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 1121 { 1122 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 1123 HANDLE oldcb; 1124 DWORD br; 1125 1126 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 1127 1128 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 1129 1130 oldcb = InterlockedExchangePointer(&wmcda->hCallback, NULL); 1131 if (oldcb) mciDriverNotify(oldcb, wmcda->wNotifyDeviceID, MCI_NOTIFY_ABORTED); 1132 1133 if (wmcda->hThread != 0) { 1134 /* Don't bother calling stop if the playLoop thread has already stopped */ 1135 if(WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0 && 1136 FAILED(IDirectSoundBuffer_Stop(wmcda->dsBuf))) 1137 return MCIERR_HARDWARE; 1138 } 1139 else if (!device_io(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL)) 1140 return MCIERR_HARDWARE; 1141 1142 if ((dwFlags & MCI_NOTIFY) && lpParms) 1143 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 1144 return 0; 1145 } 1146 1147 /************************************************************************** 1148 * MCICDA_Resume [internal] 1149 */ 1150 static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 1151 { 1152 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 1153 DWORD br; 1154 1155 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 1156 1157 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 1158 1159 if (wmcda->hThread != 0) { 1160 /* Don't restart if the playLoop thread has already stopped */ 1161 if(WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0 && 1162 FAILED(IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING))) 1163 return MCIERR_HARDWARE; 1164 } 1165 else if (!device_io(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL)) 1166 return MCIERR_HARDWARE; 1167 1168 if ((dwFlags & MCI_NOTIFY) && lpParms) 1169 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 1170 return 0; 1171 } 1172 1173 /************************************************************************** 1174 * MCICDA_Seek [internal] 1175 */ 1176 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms) 1177 { 1178 DWORD at; 1179 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 1180 CDROM_SEEK_AUDIO_MSF seek; 1181 DWORD br, position, ret; 1182 CDROM_TOC toc; 1183 1184 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 1185 1186 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 1187 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 1188 1189 position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO); 1190 if (!position) return MCIERR_MISSING_PARAMETER; 1191 if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE; 1192 1193 /* Stop sends MCI_NOTIFY_ABORTED when needed. 1194 * Tests show that native first sends ABORTED and reads the TOC, 1195 * then only checks the position flags, then stops and seeks. */ 1196 MCICDA_Stop(wDevID, MCI_WAIT, 0); 1197 1198 if (!MCICDA_ReadTOC(wmcda, &toc, &br)) 1199 return MCICDA_GetError(wmcda); 1200 1201 switch (position) { 1202 case MCI_SEEK_TO_START: 1203 TRACE("Seeking to start\n"); 1204 at = FRAME_OF_TOC(toc,toc.FirstTrack); 1205 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) ) 1206 return ret; 1207 break; 1208 case MCI_SEEK_TO_END: 1209 TRACE("Seeking to end\n"); 1210 /* End is prior to lead-out 1211 * yet Win9X seeks to even one frame less than that. */ 1212 at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1; 1213 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) ) 1214 return ret; 1215 break; 1216 case MCI_TO: 1217 TRACE("Seeking to %u\n", lpParms->dwTo); 1218 at = MCICDA_CalcFrame(wmcda, lpParms->dwTo); 1219 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) ) 1220 return ret; 1221 break; 1222 default: 1223 return MCIERR_FLAGS_NOT_COMPATIBLE; 1224 } 1225 1226 { 1227 seek.M = at / CDFRAMES_PERMIN; 1228 seek.S = (at / CDFRAMES_PERSEC) % 60; 1229 seek.F = at % CDFRAMES_PERSEC; 1230 if (!device_io(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek), 1231 NULL, 0, &br, NULL)) 1232 return MCIERR_HARDWARE; 1233 } 1234 1235 if (dwFlags & MCI_NOTIFY) 1236 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 1237 return 0; 1238 } 1239 1240 /************************************************************************** 1241 * MCICDA_SetDoor [internal] 1242 */ 1243 static DWORD MCICDA_SetDoor(UINT wDevID, BOOL open) 1244 { 1245 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 1246 DWORD br; 1247 1248 TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE"); 1249 1250 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 1251 1252 if (!device_io(wmcda->handle, 1253 (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA, 1254 NULL, 0, NULL, 0, &br, NULL)) 1255 return MCIERR_HARDWARE; 1256 1257 return 0; 1258 } 1259 1260 /************************************************************************** 1261 * MCICDA_Set [internal] 1262 */ 1263 static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms) 1264 { 1265 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 1266 1267 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 1268 1269 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 1270 1271 if (dwFlags & MCI_SET_DOOR_OPEN) { 1272 MCICDA_SetDoor(wDevID, TRUE); 1273 } 1274 if (dwFlags & MCI_SET_DOOR_CLOSED) { 1275 MCICDA_SetDoor(wDevID, FALSE); 1276 } 1277 1278 /* only functions which require valid lpParms below this line ! */ 1279 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 1280 /* 1281 TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat); 1282 */ 1283 if (dwFlags & MCI_SET_TIME_FORMAT) { 1284 switch (lpParms->dwTimeFormat) { 1285 case MCI_FORMAT_MILLISECONDS: 1286 TRACE("MCI_FORMAT_MILLISECONDS !\n"); 1287 break; 1288 case MCI_FORMAT_MSF: 1289 TRACE("MCI_FORMAT_MSF !\n"); 1290 break; 1291 case MCI_FORMAT_TMSF: 1292 TRACE("MCI_FORMAT_TMSF !\n"); 1293 break; 1294 default: 1295 return MCIERR_BAD_TIME_FORMAT; 1296 } 1297 wmcda->dwTimeFormat = lpParms->dwTimeFormat; 1298 } 1299 if (dwFlags & MCI_SET_AUDIO) /* one xp machine ignored it */ 1300 TRACE("SET_AUDIO %X %x\n", dwFlags, lpParms->dwAudio); 1301 1302 if (dwFlags & MCI_NOTIFY) 1303 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 1304 return 0; 1305 } 1306 1307 /************************************************************************** 1308 * DriverProc (MCICDA.@) 1309 */ 1310 LRESULT CALLBACK MCICDA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg, 1311 LPARAM dwParam1, LPARAM dwParam2) 1312 { 1313 switch(wMsg) { 1314 case DRV_LOAD: return 1; 1315 case DRV_FREE: return 1; 1316 case DRV_OPEN: return MCICDA_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2); 1317 case DRV_CLOSE: return MCICDA_drvClose(dwDevID); 1318 case DRV_ENABLE: return 1; 1319 case DRV_DISABLE: return 1; 1320 case DRV_QUERYCONFIGURE: return 1; 1321 case DRV_CONFIGURE: MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1; 1322 case DRV_INSTALL: return DRVCNF_RESTART; 1323 case DRV_REMOVE: return DRVCNF_RESTART; 1324 } 1325 1326 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION; 1327 1328 switch (wMsg) { 1329 case MCI_OPEN_DRIVER: return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSW)dwParam2); 1330 case MCI_CLOSE_DRIVER: return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); 1331 case MCI_GETDEVCAPS: return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2); 1332 case MCI_INFO: return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSW)dwParam2); 1333 case MCI_STATUS: return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2); 1334 case MCI_SET: return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2); 1335 case MCI_PLAY: return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2); 1336 case MCI_STOP: return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); 1337 case MCI_PAUSE: return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); 1338 case MCI_RESUME: return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); 1339 case MCI_SEEK: return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2); 1340 /* commands that should report an error as they are not supported in 1341 * the native version */ 1342 case MCI_RECORD: 1343 case MCI_LOAD: 1344 case MCI_SAVE: 1345 return MCIERR_UNSUPPORTED_FUNCTION; 1346 case MCI_BREAK: 1347 case MCI_FREEZE: 1348 case MCI_PUT: 1349 case MCI_REALIZE: 1350 case MCI_UNFREEZE: 1351 case MCI_UPDATE: 1352 case MCI_WHERE: 1353 case MCI_STEP: 1354 case MCI_SPIN: 1355 case MCI_ESCAPE: 1356 case MCI_COPY: 1357 case MCI_CUT: 1358 case MCI_DELETE: 1359 case MCI_PASTE: 1360 case MCI_WINDOW: 1361 TRACE("Unsupported command [0x%x]\n", wMsg); 1362 break; 1363 case MCI_OPEN: 1364 case MCI_CLOSE: 1365 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n"); 1366 break; 1367 default: 1368 TRACE("Sending msg [0x%x] to default driver proc\n", wMsg); 1369 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2); 1370 } 1371 return MCIERR_UNRECOGNIZED_COMMAND; 1372 } 1373 1374 /*-----------------------------------------------------------------------*/ 1375