Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenmcicda.c
Go to the documentation of this file.
00001 /* 00002 * MCI driver for audio CD (MCICDA) 00003 * 00004 * Copyright 1994 Martin Ayotte 00005 * Copyright 1998-99 Eric Pouech 00006 * Copyright 2000 Andreas Mohr 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with this library; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00021 */ 00022 00023 #include "config.h" 00024 #include <stdarg.h> 00025 #include <stdio.h> 00026 #include <string.h> 00027 00028 #define WIN32_NO_STATUS 00029 #include "windef.h" 00030 #include "winbase.h" 00031 #include "wingdi.h" 00032 #include "winuser.h" 00033 #include "wownt32.h" 00034 #include "mmddk.h" 00035 #include "winioctl.h" 00036 #include "ntddcdrm.h" 00037 #include "winternl.h" 00038 #include "wine/debug.h" 00039 #include "wine/unicode.h" 00040 #include "dsound.h" 00041 00042 WINE_DEFAULT_DEBUG_CHANNEL(mcicda); 00043 00044 #define CDFRAMES_PERSEC 75 00045 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60) 00046 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3]) 00047 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address) 00048 00049 /* Defined by red-book standard; do not change! */ 00050 #define RAW_SECTOR_SIZE (2352) 00051 00052 /* Must be >= RAW_SECTOR_SIZE */ 00053 #define CDDA_FRAG_SIZE (32768) 00054 /* Must be >= 2 */ 00055 #define CDDA_FRAG_COUNT (3) 00056 00057 typedef struct { 00058 UINT wDevID; 00059 int nUseCount; /* Incremented for each shared open */ 00060 BOOL fShareable; /* TRUE if first open was shareable */ 00061 MCIDEVICEID wNotifyDeviceID; /* MCI device ID with a pending notification */ 00062 HANDLE hCallback; /* Callback handle for pending notification */ 00063 DWORD dwTimeFormat; 00064 HANDLE handle; 00065 00066 /* The following are used for digital playback only */ 00067 HANDLE hThread; 00068 HANDLE stopEvent; 00069 DWORD start, end; 00070 00071 IDirectSound *dsObj; 00072 IDirectSoundBuffer *dsBuf; 00073 00074 CRITICAL_SECTION cs; 00075 } WINE_MCICDAUDIO; 00076 00077 /*-----------------------------------------------------------------------*/ 00078 00079 typedef HRESULT(WINAPI*LPDIRECTSOUNDCREATE)(LPCGUID,LPDIRECTSOUND*,LPUNKNOWN); 00080 static LPDIRECTSOUNDCREATE pDirectSoundCreate; 00081 00082 static DWORD CALLBACK MCICDA_playLoop(void *ptr) 00083 { 00084 WINE_MCICDAUDIO *wmcda = (WINE_MCICDAUDIO*)ptr; 00085 DWORD lastPos, curPos, endPos, br; 00086 void *cdData; 00087 DWORD lockLen, fragLen; 00088 DSBCAPS caps; 00089 RAW_READ_INFO rdInfo; 00090 HRESULT hr = DS_OK; 00091 00092 memset(&caps, 0, sizeof(caps)); 00093 caps.dwSize = sizeof(caps); 00094 hr = IDirectSoundBuffer_GetCaps(wmcda->dsBuf, &caps); 00095 00096 fragLen = caps.dwBufferBytes/CDDA_FRAG_COUNT; 00097 curPos = lastPos = 0; 00098 endPos = ~0u; 00099 while (SUCCEEDED(hr) && endPos != lastPos && 00100 WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0) { 00101 hr = IDirectSoundBuffer_GetCurrentPosition(wmcda->dsBuf, &curPos, NULL); 00102 if ((curPos-lastPos+caps.dwBufferBytes)%caps.dwBufferBytes < fragLen) { 00103 Sleep(1); 00104 continue; 00105 } 00106 00107 EnterCriticalSection(&wmcda->cs); 00108 rdInfo.DiskOffset.QuadPart = wmcda->start<<11; 00109 rdInfo.SectorCount = min(fragLen/RAW_SECTOR_SIZE, wmcda->end-wmcda->start); 00110 rdInfo.TrackMode = CDDA; 00111 00112 hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, lastPos, fragLen, &cdData, &lockLen, NULL, NULL, 0); 00113 if (hr == DSERR_BUFFERLOST) { 00114 if(FAILED(IDirectSoundBuffer_Restore(wmcda->dsBuf)) || 00115 FAILED(IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING))) { 00116 LeaveCriticalSection(&wmcda->cs); 00117 break; 00118 } 00119 hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, lastPos, fragLen, &cdData, &lockLen, NULL, NULL, 0); 00120 } 00121 00122 if (SUCCEEDED(hr)) { 00123 if (rdInfo.SectorCount > 0) { 00124 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RAW_READ, &rdInfo, sizeof(rdInfo), cdData, lockLen, &br, NULL)) 00125 WARN("CD read failed at sector %d: 0x%x\n", wmcda->start, GetLastError()); 00126 } 00127 if (rdInfo.SectorCount*RAW_SECTOR_SIZE < lockLen) { 00128 if(endPos == ~0u) endPos = lastPos; 00129 memset((BYTE*)cdData + rdInfo.SectorCount*RAW_SECTOR_SIZE, 0, 00130 lockLen - rdInfo.SectorCount*RAW_SECTOR_SIZE); 00131 } 00132 hr = IDirectSoundBuffer_Unlock(wmcda->dsBuf, cdData, lockLen, NULL, 0); 00133 } 00134 00135 lastPos += fragLen; 00136 lastPos %= caps.dwBufferBytes; 00137 wmcda->start += rdInfo.SectorCount; 00138 00139 LeaveCriticalSection(&wmcda->cs); 00140 } 00141 IDirectSoundBuffer_Stop(wmcda->dsBuf); 00142 SetEvent(wmcda->stopEvent); 00143 00144 /* A design bug in native: the independent CD player called by the 00145 * MCI has no means to signal end of playing, therefore the MCI 00146 * notification is left hanging. MCI_NOTIFY_SUPERSEDED will be 00147 * signaled by the next command that has MCI_NOTIFY set (or 00148 * MCI_NOTIFY_ABORTED for MCI_PLAY). */ 00149 00150 return 0; 00151 } 00152 00153 00154 00155 /************************************************************************** 00156 * MCICDA_drvOpen [internal] 00157 */ 00158 static DWORD MCICDA_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp) 00159 { 00160 static HMODULE dsHandle; 00161 WINE_MCICDAUDIO* wmcda; 00162 00163 if (!modp) return 0xFFFFFFFF; 00164 /* FIXME: MCIERR_CANNOT_LOAD_DRIVER if there's no drive of type CD-ROM */ 00165 00166 wmcda = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCICDAUDIO)); 00167 00168 if (!wmcda) 00169 return 0; 00170 00171 if (!dsHandle) { 00172 dsHandle = LoadLibraryA("dsound.dll"); 00173 if(dsHandle) 00174 pDirectSoundCreate = (LPDIRECTSOUNDCREATE)GetProcAddress(dsHandle, "DirectSoundCreate"); 00175 } 00176 00177 wmcda->wDevID = modp->wDeviceID; 00178 mciSetDriverData(wmcda->wDevID, (DWORD_PTR)wmcda); 00179 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE; 00180 modp->wType = MCI_DEVTYPE_CD_AUDIO; 00181 InitializeCriticalSection(&wmcda->cs); 00182 wmcda->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINE_MCICDAUDIO.cs"); 00183 return modp->wDeviceID; 00184 } 00185 00186 /************************************************************************** 00187 * MCICDA_drvClose [internal] 00188 */ 00189 static DWORD MCICDA_drvClose(DWORD dwDevID) 00190 { 00191 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(dwDevID); 00192 00193 if (wmcda) { 00194 wmcda->cs.DebugInfo->Spare[0] = 0; 00195 DeleteCriticalSection(&wmcda->cs); 00196 HeapFree(GetProcessHeap(), 0, wmcda); 00197 mciSetDriverData(dwDevID, 0); 00198 } 00199 return (dwDevID == 0xFFFFFFFF) ? 1 : 0; 00200 } 00201 00202 /************************************************************************** 00203 * MCICDA_GetOpenDrv [internal] 00204 */ 00205 static WINE_MCICDAUDIO* MCICDA_GetOpenDrv(UINT wDevID) 00206 { 00207 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID); 00208 00209 if (wmcda == NULL || wmcda->nUseCount == 0) { 00210 WARN("Invalid wDevID=%u\n", wDevID); 00211 return 0; 00212 } 00213 return wmcda; 00214 } 00215 00216 /************************************************************************** 00217 * MCICDA_mciNotify [internal] 00218 * 00219 * Notifications in MCI work like a 1-element queue. 00220 * Each new notification request supersedes the previous one. 00221 */ 00222 static void MCICDA_Notify(DWORD_PTR hWndCallBack, WINE_MCICDAUDIO* wmcda, UINT wStatus) 00223 { 00224 MCIDEVICEID wDevID = wmcda->wNotifyDeviceID; 00225 HANDLE old = InterlockedExchangePointer(&wmcda->hCallback, NULL); 00226 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED); 00227 mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus); 00228 } 00229 00230 /************************************************************************** 00231 * MCICDA_GetStatus [internal] 00232 */ 00233 static DWORD MCICDA_GetStatus(WINE_MCICDAUDIO* wmcda) 00234 { 00235 CDROM_SUB_Q_DATA_FORMAT fmt; 00236 SUB_Q_CHANNEL_DATA data; 00237 DWORD br; 00238 DWORD mode = MCI_MODE_NOT_READY; 00239 00240 fmt.Format = IOCTL_CDROM_CURRENT_POSITION; 00241 if(wmcda->hThread != 0) { 00242 DWORD status; 00243 HRESULT hr; 00244 00245 hr = IDirectSoundBuffer_GetStatus(wmcda->dsBuf, &status); 00246 if(SUCCEEDED(hr)) { 00247 if(!(status&DSBSTATUS_PLAYING)) { 00248 if(WaitForSingleObject(wmcda->stopEvent, 0) == WAIT_OBJECT_0) 00249 mode = MCI_MODE_STOP; 00250 else 00251 mode = MCI_MODE_PAUSE; 00252 } 00253 else 00254 mode = MCI_MODE_PLAY; 00255 } 00256 } 00257 else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt), 00258 &data, sizeof(data), &br, NULL)) { 00259 if (GetLastError() == ERROR_NOT_READY) mode = MCI_MODE_OPEN; 00260 } else { 00261 switch (data.CurrentPosition.Header.AudioStatus) 00262 { 00263 case AUDIO_STATUS_IN_PROGRESS: mode = MCI_MODE_PLAY; break; 00264 case AUDIO_STATUS_PAUSED: mode = MCI_MODE_PAUSE; break; 00265 case AUDIO_STATUS_NO_STATUS: 00266 case AUDIO_STATUS_PLAY_COMPLETE: mode = MCI_MODE_STOP; break; 00267 case AUDIO_STATUS_PLAY_ERROR: 00268 case AUDIO_STATUS_NOT_SUPPORTED: 00269 default: 00270 break; 00271 } 00272 } 00273 return mode; 00274 } 00275 00276 /************************************************************************** 00277 * MCICDA_GetError [internal] 00278 */ 00279 static int MCICDA_GetError(WINE_MCICDAUDIO* wmcda) 00280 { 00281 switch (GetLastError()) 00282 { 00283 case ERROR_NOT_READY: return MCIERR_DEVICE_NOT_READY; 00284 case ERROR_NOT_SUPPORTED: 00285 case ERROR_IO_DEVICE: return MCIERR_HARDWARE; 00286 default: 00287 FIXME("Unknown mode %u\n", GetLastError()); 00288 } 00289 return MCIERR_DRIVER_INTERNAL; 00290 } 00291 00292 /************************************************************************** 00293 * MCICDA_CalcFrame [internal] 00294 */ 00295 static DWORD MCICDA_CalcFrame(WINE_MCICDAUDIO* wmcda, DWORD dwTime) 00296 { 00297 DWORD dwFrame = 0; 00298 UINT wTrack; 00299 CDROM_TOC toc; 00300 DWORD br; 00301 BYTE* addr; 00302 00303 TRACE("(%p, %08X, %u);\n", wmcda, wmcda->dwTimeFormat, dwTime); 00304 00305 switch (wmcda->dwTimeFormat) { 00306 case MCI_FORMAT_MILLISECONDS: 00307 dwFrame = ((dwTime - 1) * CDFRAMES_PERSEC + 500) / 1000; 00308 TRACE("MILLISECONDS %u\n", dwFrame); 00309 break; 00310 case MCI_FORMAT_MSF: 00311 TRACE("MSF %02u:%02u:%02u\n", 00312 MCI_MSF_MINUTE(dwTime), MCI_MSF_SECOND(dwTime), MCI_MSF_FRAME(dwTime)); 00313 dwFrame += CDFRAMES_PERMIN * MCI_MSF_MINUTE(dwTime); 00314 dwFrame += CDFRAMES_PERSEC * MCI_MSF_SECOND(dwTime); 00315 dwFrame += MCI_MSF_FRAME(dwTime); 00316 break; 00317 case MCI_FORMAT_TMSF: 00318 default: /* unknown format ! force TMSF ! ... */ 00319 wTrack = MCI_TMSF_TRACK(dwTime); 00320 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 00321 &toc, sizeof(toc), &br, NULL)) 00322 return 0; 00323 if (wTrack < toc.FirstTrack || wTrack > toc.LastTrack) 00324 return 0; 00325 TRACE("MSF %02u-%02u:%02u:%02u\n", 00326 MCI_TMSF_TRACK(dwTime), MCI_TMSF_MINUTE(dwTime), 00327 MCI_TMSF_SECOND(dwTime), MCI_TMSF_FRAME(dwTime)); 00328 addr = toc.TrackData[wTrack - toc.FirstTrack].Address; 00329 TRACE("TMSF trackpos[%u]=%d:%d:%d\n", 00330 wTrack, addr[1], addr[2], addr[3]); 00331 dwFrame = CDFRAMES_PERMIN * (addr[1] + MCI_TMSF_MINUTE(dwTime)) + 00332 CDFRAMES_PERSEC * (addr[2] + MCI_TMSF_SECOND(dwTime)) + 00333 addr[3] + MCI_TMSF_FRAME(dwTime); 00334 break; 00335 } 00336 return dwFrame; 00337 } 00338 00339 /************************************************************************** 00340 * MCICDA_CalcTime [internal] 00341 */ 00342 static DWORD MCICDA_CalcTime(WINE_MCICDAUDIO* wmcda, DWORD tf, DWORD dwFrame, LPDWORD lpRet) 00343 { 00344 DWORD dwTime = 0; 00345 UINT wTrack; 00346 UINT wMinutes; 00347 UINT wSeconds; 00348 UINT wFrames; 00349 CDROM_TOC toc; 00350 DWORD br; 00351 00352 TRACE("(%p, %08X, %u);\n", wmcda, tf, dwFrame); 00353 00354 switch (tf) { 00355 case MCI_FORMAT_MILLISECONDS: 00356 dwTime = (dwFrame * 1000) / CDFRAMES_PERSEC + 1; 00357 TRACE("MILLISECONDS %u\n", dwTime); 00358 *lpRet = 0; 00359 break; 00360 case MCI_FORMAT_MSF: 00361 wMinutes = dwFrame / CDFRAMES_PERMIN; 00362 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC; 00363 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds; 00364 dwTime = MCI_MAKE_MSF(wMinutes, wSeconds, wFrames); 00365 TRACE("MSF %02u:%02u:%02u -> dwTime=%u\n", 00366 wMinutes, wSeconds, wFrames, dwTime); 00367 *lpRet = MCI_COLONIZED3_RETURN; 00368 break; 00369 case MCI_FORMAT_TMSF: 00370 default: /* unknown format ! force TMSF ! ... */ 00371 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 00372 &toc, sizeof(toc), &br, NULL)) 00373 return 0; 00374 if (dwFrame < FRAME_OF_TOC(toc, toc.FirstTrack) || 00375 dwFrame > FRAME_OF_TOC(toc, toc.LastTrack + 1)) { 00376 ERR("Out of range value %u [%u,%u]\n", 00377 dwFrame, FRAME_OF_TOC(toc, toc.FirstTrack), 00378 FRAME_OF_TOC(toc, toc.LastTrack + 1)); 00379 *lpRet = 0; 00380 return 0; 00381 } 00382 for (wTrack = toc.FirstTrack; wTrack <= toc.LastTrack; wTrack++) { 00383 if (FRAME_OF_TOC(toc, wTrack) > dwFrame) 00384 break; 00385 } 00386 wTrack--; 00387 dwFrame -= FRAME_OF_TOC(toc, wTrack); 00388 wMinutes = dwFrame / CDFRAMES_PERMIN; 00389 wSeconds = (dwFrame - CDFRAMES_PERMIN * wMinutes) / CDFRAMES_PERSEC; 00390 wFrames = dwFrame - CDFRAMES_PERMIN * wMinutes - CDFRAMES_PERSEC * wSeconds; 00391 dwTime = MCI_MAKE_TMSF(wTrack, wMinutes, wSeconds, wFrames); 00392 TRACE("%02u-%02u:%02u:%02u\n", wTrack, wMinutes, wSeconds, wFrames); 00393 *lpRet = MCI_COLONIZED4_RETURN; 00394 break; 00395 } 00396 return dwTime; 00397 } 00398 00399 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms); 00400 00401 /************************************************************************** 00402 * MCICDA_Open [internal] 00403 */ 00404 static DWORD MCICDA_Open(UINT wDevID, DWORD dwFlags, LPMCI_OPEN_PARMSW lpOpenParms) 00405 { 00406 MCIDEVICEID dwDeviceID; 00407 DWORD ret; 00408 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID); 00409 WCHAR root[7], drive = 0; 00410 00411 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpOpenParms); 00412 00413 if (lpOpenParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 00414 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 00415 00416 dwDeviceID = lpOpenParms->wDeviceID; 00417 00418 if (wmcda->nUseCount > 0) { 00419 /* The driver is already open on this channel */ 00420 /* If the driver was opened shareable before and this open specifies */ 00421 /* shareable then increment the use count */ 00422 if (wmcda->fShareable && (dwFlags & MCI_OPEN_SHAREABLE)) 00423 ++wmcda->nUseCount; 00424 else 00425 return MCIERR_MUST_USE_SHAREABLE; 00426 } else { 00427 wmcda->nUseCount = 1; 00428 wmcda->fShareable = dwFlags & MCI_OPEN_SHAREABLE; 00429 } 00430 if (dwFlags & MCI_OPEN_ELEMENT) { 00431 if (dwFlags & MCI_OPEN_ELEMENT_ID) { 00432 WARN("MCI_OPEN_ELEMENT_ID %p! Abort\n", lpOpenParms->lpstrElementName); 00433 ret = MCIERR_FLAGS_NOT_COMPATIBLE; 00434 goto the_error; 00435 } 00436 TRACE("MCI_OPEN_ELEMENT element name: %s\n", debugstr_w(lpOpenParms->lpstrElementName)); 00437 /* Only the first letter counts since w2k 00438 * Win9x-NT accept only d: and w98SE accepts d:\foobar as well. 00439 * Play d:\Track03.cda plays from the first track, not #3. */ 00440 if (!isalpha(lpOpenParms->lpstrElementName[0])) 00441 { 00442 ret = MCIERR_INVALID_FILE; 00443 goto the_error; 00444 } 00445 drive = toupper(lpOpenParms->lpstrElementName[0]); 00446 root[0] = drive; root[1] = ':'; root[2] = '\\'; root[3] = '\0'; 00447 if (GetDriveTypeW(root) != DRIVE_CDROM) 00448 { 00449 ret = MCIERR_INVALID_FILE; 00450 goto the_error; 00451 } 00452 } 00453 else 00454 { 00455 root[0] = 'A'; root[1] = ':'; root[2] = '\\'; root[3] = '\0'; 00456 for ( ; root[0] <= 'Z'; root[0]++) 00457 { 00458 if (GetDriveTypeW(root) == DRIVE_CDROM) 00459 { 00460 drive = root[0]; 00461 break; 00462 } 00463 } 00464 if (!drive) 00465 { 00466 ret = MCIERR_CANNOT_LOAD_DRIVER; /* drvOpen should return this */ 00467 goto the_error; 00468 } 00469 } 00470 00471 wmcda->wNotifyDeviceID = dwDeviceID; 00472 wmcda->dwTimeFormat = MCI_FORMAT_MSF; 00473 00474 /* now, open the handle */ 00475 root[0] = root[1] = '\\'; root[2] = '.'; root[3] = '\\'; root[4] = drive; root[5] = ':'; root[6] = '\0'; 00476 wmcda->handle = CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); 00477 if (wmcda->handle == INVALID_HANDLE_VALUE) 00478 { 00479 ret = MCIERR_MUST_USE_SHAREABLE; 00480 goto the_error; 00481 } 00482 00483 if (dwFlags & MCI_NOTIFY) { 00484 mciDriverNotify(HWND_32(LOWORD(lpOpenParms->dwCallback)), 00485 dwDeviceID, MCI_NOTIFY_SUCCESSFUL); 00486 } 00487 return 0; 00488 00489 the_error: 00490 --wmcda->nUseCount; 00491 return ret; 00492 } 00493 00494 /************************************************************************** 00495 * MCICDA_Close [internal] 00496 */ 00497 static DWORD MCICDA_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms) 00498 { 00499 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 00500 00501 TRACE("(%04X, %08X, %p);\n", wDevID, dwParam, lpParms); 00502 00503 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 00504 00505 MCICDA_Stop(wDevID, MCI_WAIT, NULL); 00506 00507 if (--wmcda->nUseCount == 0) { 00508 CloseHandle(wmcda->handle); 00509 } 00510 if ((dwParam & MCI_NOTIFY) && lpParms) 00511 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 00512 return 0; 00513 } 00514 00515 /************************************************************************** 00516 * MCICDA_GetDevCaps [internal] 00517 */ 00518 static DWORD MCICDA_GetDevCaps(UINT wDevID, DWORD dwFlags, 00519 LPMCI_GETDEVCAPS_PARMS lpParms) 00520 { 00521 WINE_MCICDAUDIO* wmcda = (WINE_MCICDAUDIO*)mciGetDriverData(wDevID); 00522 DWORD ret = 0; 00523 00524 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 00525 00526 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 00527 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 00528 00529 if (dwFlags & MCI_GETDEVCAPS_ITEM) { 00530 TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08X;\n", lpParms->dwItem); 00531 00532 switch (lpParms->dwItem) { 00533 case MCI_GETDEVCAPS_CAN_RECORD: 00534 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 00535 ret = MCI_RESOURCE_RETURNED; 00536 break; 00537 case MCI_GETDEVCAPS_HAS_AUDIO: 00538 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 00539 ret = MCI_RESOURCE_RETURNED; 00540 break; 00541 case MCI_GETDEVCAPS_HAS_VIDEO: 00542 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 00543 ret = MCI_RESOURCE_RETURNED; 00544 break; 00545 case MCI_GETDEVCAPS_DEVICE_TYPE: 00546 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO); 00547 ret = MCI_RESOURCE_RETURNED; 00548 break; 00549 case MCI_GETDEVCAPS_USES_FILES: 00550 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 00551 ret = MCI_RESOURCE_RETURNED; 00552 break; 00553 case MCI_GETDEVCAPS_COMPOUND_DEVICE: 00554 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 00555 ret = MCI_RESOURCE_RETURNED; 00556 break; 00557 case MCI_GETDEVCAPS_CAN_EJECT: 00558 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 00559 ret = MCI_RESOURCE_RETURNED; 00560 break; 00561 case MCI_GETDEVCAPS_CAN_PLAY: 00562 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 00563 ret = MCI_RESOURCE_RETURNED; 00564 break; 00565 case MCI_GETDEVCAPS_CAN_SAVE: 00566 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 00567 ret = MCI_RESOURCE_RETURNED; 00568 break; 00569 default: 00570 WARN("Unsupported %x devCaps item\n", lpParms->dwItem); 00571 return MCIERR_UNSUPPORTED_FUNCTION; 00572 } 00573 } else { 00574 TRACE("No GetDevCaps-Item !\n"); 00575 return MCIERR_MISSING_PARAMETER; 00576 } 00577 TRACE("lpParms->dwReturn=%08X;\n", lpParms->dwReturn); 00578 if (dwFlags & MCI_NOTIFY) { 00579 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 00580 } 00581 return ret; 00582 } 00583 00584 static DWORD CDROM_Audio_GetSerial(CDROM_TOC* toc) 00585 { 00586 DWORD serial = 0; 00587 int i; 00588 WORD wMagic; 00589 DWORD dwStart, dwEnd; 00590 00591 /* 00592 * wMagic collects the wFrames from track 1 00593 * dwStart, dwEnd collect the beginning and end of the disc respectively, in 00594 * frames. 00595 * There it is collected for correcting the serial when there are less than 00596 * 3 tracks. 00597 */ 00598 wMagic = toc->TrackData[0].Address[3]; 00599 dwStart = FRAME_OF_TOC(*toc, toc->FirstTrack); 00600 00601 for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) { 00602 serial += (toc->TrackData[i].Address[1] << 16) | 00603 (toc->TrackData[i].Address[2] << 8) | toc->TrackData[i].Address[3]; 00604 } 00605 dwEnd = FRAME_OF_TOC(*toc, toc->LastTrack + 1); 00606 00607 if (toc->LastTrack - toc->FirstTrack + 1 < 3) 00608 serial += wMagic + (dwEnd - dwStart); 00609 00610 return serial; 00611 } 00612 00613 00614 /************************************************************************** 00615 * MCICDA_Info [internal] 00616 */ 00617 static DWORD MCICDA_Info(UINT wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms) 00618 { 00619 LPCWSTR str = NULL; 00620 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 00621 DWORD ret = 0; 00622 WCHAR buffer[16]; 00623 00624 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 00625 00626 if (lpParms == NULL || lpParms->lpstrReturn == NULL) 00627 return MCIERR_NULL_PARAMETER_BLOCK; 00628 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 00629 00630 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize); 00631 00632 if (dwFlags & MCI_INFO_PRODUCT) { 00633 static const WCHAR wszAudioCd[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','C','D',0}; 00634 str = wszAudioCd; 00635 } else if (dwFlags & MCI_INFO_MEDIA_UPC) { 00636 ret = MCIERR_NO_IDENTITY; 00637 } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) { 00638 DWORD res = 0; 00639 CDROM_TOC toc; 00640 DWORD br; 00641 static const WCHAR wszLu[] = {'%','l','u',0}; 00642 00643 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 00644 &toc, sizeof(toc), &br, NULL)) { 00645 return MCICDA_GetError(wmcda); 00646 } 00647 00648 res = CDROM_Audio_GetSerial(&toc); 00649 sprintfW(buffer, wszLu, res); 00650 str = buffer; 00651 } else { 00652 WARN("Don't know this info command (%u)\n", dwFlags); 00653 ret = MCIERR_MISSING_PARAMETER; 00654 } 00655 if (!ret) { 00656 TRACE("=> %s\n", debugstr_w(str)); 00657 if (lpParms->dwRetSize) { 00658 WCHAR zero = 0; 00659 /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize 00660 * to the number of characters written, excluding \0. */ 00661 lstrcpynW(lpParms->lpstrReturn, str ? str : &zero, lpParms->dwRetSize); 00662 } else ret = MCIERR_PARAM_OVERFLOW; 00663 } 00664 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY)) 00665 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 00666 return ret; 00667 } 00668 00669 /************************************************************************** 00670 * MCICDA_Status [internal] 00671 */ 00672 static DWORD MCICDA_Status(UINT wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms) 00673 { 00674 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 00675 DWORD ret = 0; 00676 CDROM_SUB_Q_DATA_FORMAT fmt; 00677 SUB_Q_CHANNEL_DATA data; 00678 CDROM_TOC toc; 00679 DWORD br; 00680 00681 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 00682 00683 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 00684 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 00685 00686 if (dwFlags & MCI_STATUS_ITEM) { 00687 TRACE("dwItem = %x\n", lpParms->dwItem); 00688 switch (lpParms->dwItem) { 00689 case MCI_STATUS_CURRENT_TRACK: 00690 fmt.Format = IOCTL_CDROM_CURRENT_POSITION; 00691 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt), 00692 &data, sizeof(data), &br, NULL)) 00693 { 00694 return MCICDA_GetError(wmcda); 00695 /* alt. data.CurrentPosition.TrackNumber = 1; -- what native yields */ 00696 } 00697 lpParms->dwReturn = data.CurrentPosition.TrackNumber; 00698 TRACE("CURRENT_TRACK=%lu\n", lpParms->dwReturn); 00699 break; 00700 case MCI_STATUS_LENGTH: 00701 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 00702 &toc, sizeof(toc), &br, NULL)) { 00703 WARN("error reading TOC !\n"); 00704 return MCICDA_GetError(wmcda); 00705 } 00706 if (dwFlags & MCI_TRACK) { 00707 TRACE("MCI_TRACK #%u LENGTH=??? !\n", lpParms->dwTrack); 00708 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack) 00709 return MCIERR_OUTOFRANGE; 00710 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack + 1) - 00711 FRAME_OF_TOC(toc, lpParms->dwTrack); 00712 /* Windows returns one frame less than the total track length for the 00713 last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */ 00714 if (lpParms->dwTrack == toc.LastTrack) 00715 lpParms->dwReturn--; 00716 } else { 00717 /* Sum of the lengths of all of the tracks. Inherits the 00718 'off by one frame' behavior from the length of the last track. 00719 See above comment. */ 00720 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 00721 FRAME_OF_TOC(toc, toc.FirstTrack) - 1; 00722 } 00723 lpParms->dwReturn = MCICDA_CalcTime(wmcda, 00724 (wmcda->dwTimeFormat == MCI_FORMAT_TMSF) 00725 ? MCI_FORMAT_MSF : wmcda->dwTimeFormat, 00726 lpParms->dwReturn, 00727 &ret); 00728 TRACE("LENGTH=%lu\n", lpParms->dwReturn); 00729 break; 00730 case MCI_STATUS_MODE: 00731 lpParms->dwReturn = MCICDA_GetStatus(wmcda); 00732 TRACE("MCI_STATUS_MODE=%08lX\n", lpParms->dwReturn); 00733 lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn, lpParms->dwReturn); 00734 ret = MCI_RESOURCE_RETURNED; 00735 break; 00736 case MCI_STATUS_MEDIA_PRESENT: 00737 lpParms->dwReturn = (MCICDA_GetStatus(wmcda) == MCI_MODE_OPEN) ? 00738 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE); 00739 TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms->dwReturn) ? 'Y' : 'N'); 00740 ret = MCI_RESOURCE_RETURNED; 00741 break; 00742 case MCI_STATUS_NUMBER_OF_TRACKS: 00743 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 00744 &toc, sizeof(toc), &br, NULL)) { 00745 WARN("error reading TOC !\n"); 00746 return MCICDA_GetError(wmcda); 00747 } 00748 lpParms->dwReturn = toc.LastTrack - toc.FirstTrack + 1; 00749 TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu\n", lpParms->dwReturn); 00750 if (lpParms->dwReturn == (WORD)-1) 00751 return MCICDA_GetError(wmcda); 00752 break; 00753 case MCI_STATUS_POSITION: 00754 switch (dwFlags & (MCI_STATUS_START | MCI_TRACK)) { 00755 case MCI_STATUS_START: 00756 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 00757 &toc, sizeof(toc), &br, NULL)) { 00758 WARN("error reading TOC !\n"); 00759 return MCICDA_GetError(wmcda); 00760 } 00761 lpParms->dwReturn = FRAME_OF_TOC(toc, toc.FirstTrack); 00762 TRACE("get MCI_STATUS_START !\n"); 00763 break; 00764 case MCI_TRACK: 00765 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 00766 &toc, sizeof(toc), &br, NULL)) { 00767 WARN("error reading TOC !\n"); 00768 return MCICDA_GetError(wmcda); 00769 } 00770 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack) 00771 return MCIERR_OUTOFRANGE; 00772 lpParms->dwReturn = FRAME_OF_TOC(toc, lpParms->dwTrack); 00773 TRACE("get MCI_TRACK #%u !\n", lpParms->dwTrack); 00774 break; 00775 case 0: 00776 fmt.Format = IOCTL_CDROM_CURRENT_POSITION; 00777 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt), 00778 &data, sizeof(data), &br, NULL)) { 00779 return MCICDA_GetError(wmcda); 00780 } 00781 lpParms->dwReturn = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress); 00782 break; 00783 default: 00784 return MCIERR_FLAGS_NOT_COMPATIBLE; 00785 } 00786 lpParms->dwReturn = MCICDA_CalcTime(wmcda, wmcda->dwTimeFormat, lpParms->dwReturn, &ret); 00787 TRACE("MCI_STATUS_POSITION=%08lX\n", lpParms->dwReturn); 00788 break; 00789 case MCI_STATUS_READY: 00790 TRACE("MCI_STATUS_READY !\n"); 00791 switch (MCICDA_GetStatus(wmcda)) 00792 { 00793 case MCI_MODE_NOT_READY: 00794 case MCI_MODE_OPEN: 00795 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 00796 break; 00797 default: 00798 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 00799 break; 00800 } 00801 TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms->dwReturn)); 00802 ret = MCI_RESOURCE_RETURNED; 00803 break; 00804 case MCI_STATUS_TIME_FORMAT: 00805 lpParms->dwReturn = MAKEMCIRESOURCE(wmcda->dwTimeFormat, MCI_FORMAT_RETURN_BASE + wmcda->dwTimeFormat); 00806 TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms->dwReturn)); 00807 ret = MCI_RESOURCE_RETURNED; 00808 break; 00809 case 4001: /* FIXME: for bogus FullCD */ 00810 case MCI_CDA_STATUS_TYPE_TRACK: 00811 if (!(dwFlags & MCI_TRACK)) 00812 ret = MCIERR_MISSING_PARAMETER; 00813 else { 00814 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 00815 &toc, sizeof(toc), &br, NULL)) { 00816 WARN("error reading TOC !\n"); 00817 return MCICDA_GetError(wmcda); 00818 } 00819 if (lpParms->dwTrack < toc.FirstTrack || lpParms->dwTrack > toc.LastTrack) 00820 ret = MCIERR_OUTOFRANGE; 00821 else 00822 lpParms->dwReturn = (toc.TrackData[lpParms->dwTrack - toc.FirstTrack].Control & 0x04) ? 00823 MCI_CDA_TRACK_OTHER : MCI_CDA_TRACK_AUDIO; 00824 /* FIXME: MAKEMCIRESOURCE "audio" | "other", localised */ 00825 } 00826 TRACE("MCI_CDA_STATUS_TYPE_TRACK[%d]=%ld\n", lpParms->dwTrack, lpParms->dwReturn); 00827 break; 00828 default: 00829 FIXME("unknown command %08X !\n", lpParms->dwItem); 00830 return MCIERR_UNSUPPORTED_FUNCTION; 00831 } 00832 } else return MCIERR_MISSING_PARAMETER; 00833 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0) 00834 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 00835 return ret; 00836 } 00837 00838 /************************************************************************** 00839 * MCICDA_SkipDataTracks [internal] 00840 */ 00841 static DWORD MCICDA_SkipDataTracks(WINE_MCICDAUDIO* wmcda,DWORD *frame) 00842 { 00843 int i; 00844 DWORD br; 00845 CDROM_TOC toc; 00846 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 00847 &toc, sizeof(toc), &br, NULL)) { 00848 WARN("error reading TOC !\n"); 00849 return MCICDA_GetError(wmcda); 00850 } 00851 if (*frame < FRAME_OF_TOC(toc,toc.FirstTrack) || 00852 *frame >= FRAME_OF_TOC(toc,toc.LastTrack+1)) /* lead-out */ 00853 return MCIERR_OUTOFRANGE; 00854 for(i=toc.LastTrack+1;i>toc.FirstTrack;i--) 00855 if ( FRAME_OF_TOC(toc, i) <= *frame ) break; 00856 /* i points to last track whose start address is not greater than frame. 00857 * Now skip non-audio tracks */ 00858 for(;i<=toc.LastTrack;i++) 00859 if ( ! (toc.TrackData[i-toc.FirstTrack].Control & 4) ) 00860 break; 00861 /* The frame will be an address in the next audio track or 00862 * address of lead-out. */ 00863 if ( FRAME_OF_TOC(toc, i) > *frame ) 00864 *frame = FRAME_OF_TOC(toc, i); 00865 /* Lead-out is an invalid seek position (on Linux as well). */ 00866 if (*frame == FRAME_OF_TOC(toc,toc.LastTrack+1)) 00867 (*frame)--; 00868 return 0; 00869 } 00870 00871 /************************************************************************** 00872 * MCICDA_Play [internal] 00873 */ 00874 static DWORD MCICDA_Play(UINT wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms) 00875 { 00876 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 00877 DWORD ret = 0, start, end; 00878 HANDLE oldcb; 00879 DWORD br; 00880 CDROM_PLAY_AUDIO_MSF play; 00881 CDROM_SUB_Q_DATA_FORMAT fmt; 00882 SUB_Q_CHANNEL_DATA data; 00883 CDROM_TOC toc; 00884 00885 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 00886 00887 if (lpParms == NULL) 00888 return MCIERR_NULL_PARAMETER_BLOCK; 00889 00890 if (wmcda == NULL) 00891 return MCIERR_INVALID_DEVICE_ID; 00892 00893 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 00894 &toc, sizeof(toc), &br, NULL)) { 00895 WARN("error reading TOC !\n"); 00896 return MCICDA_GetError(wmcda); 00897 } 00898 00899 if (dwFlags & MCI_FROM) { 00900 start = MCICDA_CalcFrame(wmcda, lpParms->dwFrom); 00901 if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) ) 00902 return ret; 00903 TRACE("MCI_FROM=%08X -> %u\n", lpParms->dwFrom, start); 00904 } else { 00905 fmt.Format = IOCTL_CDROM_CURRENT_POSITION; 00906 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_Q_CHANNEL, &fmt, sizeof(fmt), 00907 &data, sizeof(data), &br, NULL)) { 00908 return MCICDA_GetError(wmcda); 00909 } 00910 start = FRAME_OF_ADDR(data.CurrentPosition.AbsoluteAddress); 00911 if ( (ret=MCICDA_SkipDataTracks(wmcda, &start)) ) 00912 return ret; 00913 } 00914 if (dwFlags & MCI_TO) { 00915 end = MCICDA_CalcFrame(wmcda, lpParms->dwTo); 00916 if ( (ret=MCICDA_SkipDataTracks(wmcda, &end)) ) 00917 return ret; 00918 TRACE("MCI_TO=%08X -> %u\n", lpParms->dwTo, end); 00919 } else { 00920 end = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1; 00921 } 00922 if (end < start) return MCIERR_OUTOFRANGE; 00923 TRACE("Playing from %u to %u\n", start, end); 00924 00925 oldcb = InterlockedExchangePointer(&wmcda->hCallback, 00926 (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL); 00927 if (oldcb) mciDriverNotify(oldcb, wmcda->wNotifyDeviceID, MCI_NOTIFY_ABORTED); 00928 00929 if (start == end || start == FRAME_OF_TOC(toc,toc.LastTrack+1)-1) { 00930 if (dwFlags & MCI_NOTIFY) { 00931 oldcb = InterlockedExchangePointer(&wmcda->hCallback, NULL); 00932 if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_SUCCESSFUL); 00933 } 00934 return MMSYSERR_NOERROR; 00935 } 00936 00937 if (wmcda->hThread != 0) { 00938 SetEvent(wmcda->stopEvent); 00939 WaitForSingleObject(wmcda->hThread, INFINITE); 00940 00941 CloseHandle(wmcda->hThread); 00942 wmcda->hThread = 0; 00943 CloseHandle(wmcda->stopEvent); 00944 wmcda->stopEvent = 0; 00945 00946 IDirectSoundBuffer_Stop(wmcda->dsBuf); 00947 IDirectSoundBuffer_Release(wmcda->dsBuf); 00948 wmcda->dsBuf = NULL; 00949 IDirectSound_Release(wmcda->dsObj); 00950 wmcda->dsObj = NULL; 00951 } 00952 00953 if (pDirectSoundCreate) { 00954 WAVEFORMATEX format; 00955 DSBUFFERDESC desc; 00956 DWORD lockLen; 00957 void *cdData; 00958 HRESULT hr; 00959 00960 hr = pDirectSoundCreate(NULL, &wmcda->dsObj, NULL); 00961 if (SUCCEEDED(hr)) { 00962 IDirectSound_SetCooperativeLevel(wmcda->dsObj, GetDesktopWindow(), DSSCL_PRIORITY); 00963 00964 /* The "raw" frame is relative to the start of the first track */ 00965 wmcda->start = start - FRAME_OF_TOC(toc, toc.FirstTrack); 00966 wmcda->end = end - FRAME_OF_TOC(toc, toc.FirstTrack); 00967 00968 memset(&format, 0, sizeof(format)); 00969 format.wFormatTag = WAVE_FORMAT_PCM; 00970 format.nChannels = 2; 00971 format.nSamplesPerSec = 44100; 00972 format.wBitsPerSample = 16; 00973 format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; 00974 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; 00975 format.cbSize = 0; 00976 00977 memset(&desc, 0, sizeof(desc)); 00978 desc.dwSize = sizeof(desc); 00979 desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; 00980 desc.dwBufferBytes = (CDDA_FRAG_SIZE - (CDDA_FRAG_SIZE%RAW_SECTOR_SIZE)) * CDDA_FRAG_COUNT; 00981 desc.lpwfxFormat = &format; 00982 00983 hr = IDirectSound_CreateSoundBuffer(wmcda->dsObj, &desc, &wmcda->dsBuf, NULL); 00984 } 00985 if (SUCCEEDED(hr)) { 00986 hr = IDirectSoundBuffer_Lock(wmcda->dsBuf, 0, 0, &cdData, &lockLen, 00987 NULL, NULL, DSBLOCK_ENTIREBUFFER); 00988 } 00989 if (SUCCEEDED(hr)) { 00990 RAW_READ_INFO rdInfo; 00991 int readok; 00992 00993 rdInfo.DiskOffset.QuadPart = wmcda->start<<11; 00994 rdInfo.SectorCount = min(desc.dwBufferBytes/RAW_SECTOR_SIZE, 00995 wmcda->end-wmcda->start); 00996 rdInfo.TrackMode = CDDA; 00997 00998 readok = DeviceIoControl(wmcda->handle, IOCTL_CDROM_RAW_READ, 00999 &rdInfo, sizeof(rdInfo), cdData, lockLen, 01000 &br, NULL); 01001 IDirectSoundBuffer_Unlock(wmcda->dsBuf, cdData, lockLen, NULL, 0); 01002 01003 if (readok) { 01004 wmcda->start += rdInfo.SectorCount; 01005 wmcda->stopEvent = CreateEventA(NULL, TRUE, FALSE, NULL); 01006 } 01007 if (wmcda->stopEvent != 0) 01008 wmcda->hThread = CreateThread(NULL, 0, MCICDA_playLoop, wmcda, 0, &br); 01009 if (wmcda->hThread != 0) { 01010 hr = IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING); 01011 if (SUCCEEDED(hr)) { 01012 /* FIXME: implement MCI_WAIT and send notification only in that case */ 01013 if (0) { 01014 oldcb = InterlockedExchangePointer(&wmcda->hCallback, NULL); 01015 if (oldcb) mciDriverNotify(oldcb, wmcda->wNotifyDeviceID, 01016 FAILED(hr) ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL); 01017 } 01018 return ret; 01019 } 01020 01021 SetEvent(wmcda->stopEvent); 01022 WaitForSingleObject(wmcda->hThread, INFINITE); 01023 CloseHandle(wmcda->hThread); 01024 wmcda->hThread = 0; 01025 } 01026 } 01027 01028 if (wmcda->stopEvent != 0) { 01029 CloseHandle(wmcda->stopEvent); 01030 wmcda->stopEvent = 0; 01031 } 01032 if (wmcda->dsBuf) { 01033 IDirectSoundBuffer_Release(wmcda->dsBuf); 01034 wmcda->dsBuf = NULL; 01035 } 01036 if (wmcda->dsObj) { 01037 IDirectSound_Release(wmcda->dsObj); 01038 wmcda->dsObj = NULL; 01039 } 01040 } 01041 01042 play.StartingM = start / CDFRAMES_PERMIN; 01043 play.StartingS = (start / CDFRAMES_PERSEC) % 60; 01044 play.StartingF = start % CDFRAMES_PERSEC; 01045 play.EndingM = end / CDFRAMES_PERMIN; 01046 play.EndingS = (end / CDFRAMES_PERSEC) % 60; 01047 play.EndingF = end % CDFRAMES_PERSEC; 01048 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PLAY_AUDIO_MSF, &play, sizeof(play), 01049 NULL, 0, &br, NULL)) { 01050 wmcda->hCallback = NULL; 01051 ret = MCIERR_HARDWARE; 01052 } 01053 /* The independent CD player has no means to signal MCI_NOTIFY when it's done. 01054 * Native sends a notification with MCI_WAIT only. */ 01055 return ret; 01056 } 01057 01058 /************************************************************************** 01059 * MCICDA_Stop [internal] 01060 */ 01061 static DWORD MCICDA_Stop(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 01062 { 01063 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 01064 HANDLE oldcb; 01065 DWORD br; 01066 01067 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 01068 01069 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 01070 01071 oldcb = InterlockedExchangePointer(&wmcda->hCallback, NULL); 01072 if (oldcb) mciDriverNotify(oldcb, wmcda->wNotifyDeviceID, MCI_NOTIFY_ABORTED); 01073 01074 if (wmcda->hThread != 0) { 01075 SetEvent(wmcda->stopEvent); 01076 WaitForSingleObject(wmcda->hThread, INFINITE); 01077 01078 CloseHandle(wmcda->hThread); 01079 wmcda->hThread = 0; 01080 CloseHandle(wmcda->stopEvent); 01081 wmcda->stopEvent = 0; 01082 01083 IDirectSoundBuffer_Release(wmcda->dsBuf); 01084 wmcda->dsBuf = NULL; 01085 IDirectSound_Release(wmcda->dsObj); 01086 wmcda->dsObj = NULL; 01087 } 01088 else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_STOP_AUDIO, NULL, 0, NULL, 0, &br, NULL)) 01089 return MCIERR_HARDWARE; 01090 01091 if ((dwFlags & MCI_NOTIFY) && lpParms) 01092 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 01093 return 0; 01094 } 01095 01096 /************************************************************************** 01097 * MCICDA_Pause [internal] 01098 */ 01099 static DWORD MCICDA_Pause(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 01100 { 01101 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 01102 HANDLE oldcb; 01103 DWORD br; 01104 01105 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 01106 01107 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 01108 01109 oldcb = InterlockedExchangePointer(&wmcda->hCallback, NULL); 01110 if (oldcb) mciDriverNotify(oldcb, wmcda->wNotifyDeviceID, MCI_NOTIFY_ABORTED); 01111 01112 if (wmcda->hThread != 0) { 01113 /* Don't bother calling stop if the playLoop thread has already stopped */ 01114 if(WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0 && 01115 FAILED(IDirectSoundBuffer_Stop(wmcda->dsBuf))) 01116 return MCIERR_HARDWARE; 01117 } 01118 else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_PAUSE_AUDIO, NULL, 0, NULL, 0, &br, NULL)) 01119 return MCIERR_HARDWARE; 01120 01121 if ((dwFlags & MCI_NOTIFY) && lpParms) 01122 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 01123 return 0; 01124 } 01125 01126 /************************************************************************** 01127 * MCICDA_Resume [internal] 01128 */ 01129 static DWORD MCICDA_Resume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 01130 { 01131 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 01132 DWORD br; 01133 01134 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 01135 01136 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 01137 01138 if (wmcda->hThread != 0) { 01139 /* Don't restart if the playLoop thread has already stopped */ 01140 if(WaitForSingleObject(wmcda->stopEvent, 0) != WAIT_OBJECT_0 && 01141 FAILED(IDirectSoundBuffer_Play(wmcda->dsBuf, 0, 0, DSBPLAY_LOOPING))) 01142 return MCIERR_HARDWARE; 01143 } 01144 else if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_RESUME_AUDIO, NULL, 0, NULL, 0, &br, NULL)) 01145 return MCIERR_HARDWARE; 01146 01147 if ((dwFlags & MCI_NOTIFY) && lpParms) 01148 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 01149 return 0; 01150 } 01151 01152 /************************************************************************** 01153 * MCICDA_Seek [internal] 01154 */ 01155 static DWORD MCICDA_Seek(UINT wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms) 01156 { 01157 DWORD at; 01158 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 01159 CDROM_SEEK_AUDIO_MSF seek; 01160 DWORD br, position, ret; 01161 CDROM_TOC toc; 01162 01163 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 01164 01165 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 01166 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 01167 01168 position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO); 01169 if (!position) return MCIERR_MISSING_PARAMETER; 01170 if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE; 01171 01172 /* Stop sends MCI_NOTIFY_ABORTED when needed. 01173 * Tests show that native first sends ABORTED and reads the TOC, 01174 * then only checks the position flags, then stops and seeks. */ 01175 MCICDA_Stop(wDevID, MCI_WAIT, 0); 01176 01177 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_READ_TOC, NULL, 0, 01178 &toc, sizeof(toc), &br, NULL)) { 01179 WARN("error reading TOC !\n"); 01180 return MCICDA_GetError(wmcda); 01181 } 01182 switch (position) { 01183 case MCI_SEEK_TO_START: 01184 TRACE("Seeking to start\n"); 01185 at = FRAME_OF_TOC(toc,toc.FirstTrack); 01186 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) ) 01187 return ret; 01188 break; 01189 case MCI_SEEK_TO_END: 01190 TRACE("Seeking to end\n"); 01191 /* End is prior to lead-out 01192 * yet Win9X seeks to even one frame less than that. */ 01193 at = FRAME_OF_TOC(toc, toc.LastTrack + 1) - 1; 01194 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) ) 01195 return ret; 01196 break; 01197 case MCI_TO: 01198 TRACE("Seeking to %u\n", lpParms->dwTo); 01199 at = MCICDA_CalcFrame(wmcda, lpParms->dwTo); 01200 if ( (ret=MCICDA_SkipDataTracks(wmcda, &at)) ) 01201 return ret; 01202 break; 01203 default: 01204 return MCIERR_FLAGS_NOT_COMPATIBLE; 01205 } 01206 01207 { 01208 seek.M = at / CDFRAMES_PERMIN; 01209 seek.S = (at / CDFRAMES_PERSEC) % 60; 01210 seek.F = at % CDFRAMES_PERSEC; 01211 if (!DeviceIoControl(wmcda->handle, IOCTL_CDROM_SEEK_AUDIO_MSF, &seek, sizeof(seek), 01212 NULL, 0, &br, NULL)) 01213 return MCIERR_HARDWARE; 01214 } 01215 01216 if (dwFlags & MCI_NOTIFY) 01217 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 01218 return 0; 01219 } 01220 01221 /************************************************************************** 01222 * MCICDA_SetDoor [internal] 01223 */ 01224 static DWORD MCICDA_SetDoor(UINT wDevID, BOOL open) 01225 { 01226 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 01227 DWORD br; 01228 01229 TRACE("(%04x, %s) !\n", wDevID, (open) ? "OPEN" : "CLOSE"); 01230 01231 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 01232 01233 if (!DeviceIoControl(wmcda->handle, 01234 (open) ? IOCTL_STORAGE_EJECT_MEDIA : IOCTL_STORAGE_LOAD_MEDIA, 01235 NULL, 0, NULL, 0, &br, NULL)) 01236 return MCIERR_HARDWARE; 01237 01238 return 0; 01239 } 01240 01241 /************************************************************************** 01242 * MCICDA_Set [internal] 01243 */ 01244 static DWORD MCICDA_Set(UINT wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms) 01245 { 01246 WINE_MCICDAUDIO* wmcda = MCICDA_GetOpenDrv(wDevID); 01247 01248 TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms); 01249 01250 if (wmcda == NULL) return MCIERR_INVALID_DEVICE_ID; 01251 01252 if (dwFlags & MCI_SET_DOOR_OPEN) { 01253 MCICDA_SetDoor(wDevID, TRUE); 01254 } 01255 if (dwFlags & MCI_SET_DOOR_CLOSED) { 01256 MCICDA_SetDoor(wDevID, FALSE); 01257 } 01258 01259 /* only functions which require valid lpParms below this line ! */ 01260 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 01261 /* 01262 TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat); 01263 */ 01264 if (dwFlags & MCI_SET_TIME_FORMAT) { 01265 switch (lpParms->dwTimeFormat) { 01266 case MCI_FORMAT_MILLISECONDS: 01267 TRACE("MCI_FORMAT_MILLISECONDS !\n"); 01268 break; 01269 case MCI_FORMAT_MSF: 01270 TRACE("MCI_FORMAT_MSF !\n"); 01271 break; 01272 case MCI_FORMAT_TMSF: 01273 TRACE("MCI_FORMAT_TMSF !\n"); 01274 break; 01275 default: 01276 return MCIERR_BAD_TIME_FORMAT; 01277 } 01278 wmcda->dwTimeFormat = lpParms->dwTimeFormat; 01279 } 01280 if (dwFlags & MCI_SET_AUDIO) /* one xp machine ignored it */ 01281 TRACE("SET_AUDIO %X %x\n", dwFlags, lpParms->dwAudio); 01282 01283 if (dwFlags & MCI_NOTIFY) 01284 MCICDA_Notify(lpParms->dwCallback, wmcda, MCI_NOTIFY_SUCCESSFUL); 01285 return 0; 01286 } 01287 01288 /************************************************************************** 01289 * DriverProc (MCICDA.@) 01290 */ 01291 LRESULT CALLBACK MCICDA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg, 01292 LPARAM dwParam1, LPARAM dwParam2) 01293 { 01294 switch(wMsg) { 01295 case DRV_LOAD: return 1; 01296 case DRV_FREE: return 1; 01297 case DRV_OPEN: return MCICDA_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2); 01298 case DRV_CLOSE: return MCICDA_drvClose(dwDevID); 01299 case DRV_ENABLE: return 1; 01300 case DRV_DISABLE: return 1; 01301 case DRV_QUERYCONFIGURE: return 1; 01302 case DRV_CONFIGURE: MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK); return 1; 01303 case DRV_INSTALL: return DRVCNF_RESTART; 01304 case DRV_REMOVE: return DRVCNF_RESTART; 01305 } 01306 01307 if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION; 01308 01309 switch (wMsg) { 01310 case MCI_OPEN_DRIVER: return MCICDA_Open(dwDevID, dwParam1, (LPMCI_OPEN_PARMSW)dwParam2); 01311 case MCI_CLOSE_DRIVER: return MCICDA_Close(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); 01312 case MCI_GETDEVCAPS: return MCICDA_GetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2); 01313 case MCI_INFO: return MCICDA_Info(dwDevID, dwParam1, (LPMCI_INFO_PARMSW)dwParam2); 01314 case MCI_STATUS: return MCICDA_Status(dwDevID, dwParam1, (LPMCI_STATUS_PARMS)dwParam2); 01315 case MCI_SET: return MCICDA_Set(dwDevID, dwParam1, (LPMCI_SET_PARMS)dwParam2); 01316 case MCI_PLAY: return MCICDA_Play(dwDevID, dwParam1, (LPMCI_PLAY_PARMS)dwParam2); 01317 case MCI_STOP: return MCICDA_Stop(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); 01318 case MCI_PAUSE: return MCICDA_Pause(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); 01319 case MCI_RESUME: return MCICDA_Resume(dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); 01320 case MCI_SEEK: return MCICDA_Seek(dwDevID, dwParam1, (LPMCI_SEEK_PARMS)dwParam2); 01321 /* commands that should report an error as they are not supported in 01322 * the native version */ 01323 case MCI_RECORD: 01324 case MCI_LOAD: 01325 case MCI_SAVE: 01326 return MCIERR_UNSUPPORTED_FUNCTION; 01327 case MCI_BREAK: 01328 case MCI_FREEZE: 01329 case MCI_PUT: 01330 case MCI_REALIZE: 01331 case MCI_UNFREEZE: 01332 case MCI_UPDATE: 01333 case MCI_WHERE: 01334 case MCI_STEP: 01335 case MCI_SPIN: 01336 case MCI_ESCAPE: 01337 case MCI_COPY: 01338 case MCI_CUT: 01339 case MCI_DELETE: 01340 case MCI_PASTE: 01341 case MCI_WINDOW: 01342 TRACE("Unsupported command [0x%x]\n", wMsg); 01343 break; 01344 case MCI_OPEN: 01345 case MCI_CLOSE: 01346 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n"); 01347 break; 01348 default: 01349 TRACE("Sending msg [0x%x] to default driver proc\n", wMsg); 01350 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2); 01351 } 01352 return MCIERR_UNRECOGNIZED_COMMAND; 01353 } 01354 01355 /*-----------------------------------------------------------------------*/ Generated on Fri May 25 2012 04:22:48 for ReactOS by
1.7.6.1
|