2 * MCI driver for audio CD (MCICDA)
4 * Copyright 1994 Martin Ayotte
5 * Copyright 1998-99 Eric Pouech
6 * Copyright 2000 Andreas Mohr
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #define WIN32_NO_STATUS
38 #include "wine/debug.h"
39 #include "wine/unicode.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(mcicda
);
44 #define CDFRAMES_PERSEC 75
45 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
46 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
47 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
49 /* Defined by red-book standard; do not change! */
50 #define RAW_SECTOR_SIZE (2352)
52 /* Must be >= RAW_SECTOR_SIZE */
53 #define CDDA_FRAG_SIZE (32768)
55 #define CDDA_FRAG_COUNT (3)
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 */
66 /* The following are used for digital playback only */
72 IDirectSoundBuffer
*dsBuf
;
77 /*-----------------------------------------------------------------------*/
79 typedef HRESULT(WINAPI
*LPDIRECTSOUNDCREATE
)(LPCGUID
,LPDIRECTSOUND
*,LPUNKNOWN
);
80 static LPDIRECTSOUNDCREATE pDirectSoundCreate
;
82 static BOOL
device_io(HANDLE dev
, DWORD code
, void *inbuffer
, DWORD insize
, void *outbuffer
, DWORD outsize
, DWORD
*retsize
, OVERLAPPED
*overlapped
)
85 BOOL ret
= DeviceIoControl(dev
, code
, inbuffer
, insize
, outbuffer
, outsize
, retsize
, overlapped
);
87 #define XX(x) case (x): str = #x; break
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
);
103 TRACE("Device %p, Code %s -> Return %d, Bytes %u\n", dev
, str
, ret
, *retsize
);
107 static DWORD CALLBACK
MCICDA_playLoop(void *ptr
)
109 WINE_MCICDAUDIO
*wmcda
= (WINE_MCICDAUDIO
*)ptr
;
110 DWORD lastPos
, curPos
, endPos
, br
;
112 DWORD lockLen
, fragLen
;
114 RAW_READ_INFO rdInfo
;
117 memset(&caps
, 0, sizeof(caps
));
118 caps
.dwSize
= sizeof(caps
);
119 hr
= IDirectSoundBuffer_GetCaps(wmcda
->dsBuf
, &caps
);
121 fragLen
= caps
.dwBufferBytes
/CDDA_FRAG_COUNT
;
122 curPos
= lastPos
= 0;
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
) {
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
;
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
);
144 hr
= IDirectSoundBuffer_Lock(wmcda
->dsBuf
, lastPos
, fragLen
, &cdData
, &lockLen
, NULL
, NULL
, 0);
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());
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
);
157 hr
= IDirectSoundBuffer_Unlock(wmcda
->dsBuf
, cdData
, lockLen
, NULL
, 0);
161 lastPos
%= caps
.dwBufferBytes
;
162 wmcda
->start
+= rdInfo
.SectorCount
;
164 LeaveCriticalSection(&wmcda
->cs
);
166 IDirectSoundBuffer_Stop(wmcda
->dsBuf
);
167 SetEvent(wmcda
->stopEvent
);
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). */
180 /**************************************************************************
181 * MCICDA_drvOpen [internal]
183 static DWORD
MCICDA_drvOpen(LPCWSTR str
, LPMCI_OPEN_DRIVER_PARMSW modp
)
185 static HMODULE dsHandle
;
186 WINE_MCICDAUDIO
* wmcda
;
188 if (!modp
) return 0xFFFFFFFF;
189 /* FIXME: MCIERR_CANNOT_LOAD_DRIVER if there's no drive of type CD-ROM */
191 wmcda
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WINE_MCICDAUDIO
));
197 dsHandle
= LoadLibraryA("dsound.dll");
199 pDirectSoundCreate
= (LPDIRECTSOUNDCREATE
)GetProcAddress(dsHandle
, "DirectSoundCreate");
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
;
211 /**************************************************************************
212 * MCICDA_drvClose [internal]
214 static DWORD
MCICDA_drvClose(DWORD dwDevID
)
216 WINE_MCICDAUDIO
* wmcda
= (WINE_MCICDAUDIO
*)mciGetDriverData(dwDevID
);
219 wmcda
->cs
.DebugInfo
->Spare
[0] = 0;
220 DeleteCriticalSection(&wmcda
->cs
);
221 HeapFree(GetProcessHeap(), 0, wmcda
);
222 mciSetDriverData(dwDevID
, 0);
224 return (dwDevID
== 0xFFFFFFFF) ? 1 : 0;
227 /**************************************************************************
228 * MCICDA_GetOpenDrv [internal]
230 static WINE_MCICDAUDIO
* MCICDA_GetOpenDrv(UINT wDevID
)
232 WINE_MCICDAUDIO
* wmcda
= (WINE_MCICDAUDIO
*)mciGetDriverData(wDevID
);
234 if (wmcda
== NULL
|| wmcda
->nUseCount
== 0) {
235 WARN("Invalid wDevID=%u\n", wDevID
);
241 /**************************************************************************
242 * MCICDA_mciNotify [internal]
244 * Notifications in MCI work like a 1-element queue.
245 * Each new notification request supersedes the previous one.
247 static void MCICDA_Notify(DWORD_PTR hWndCallBack
, WINE_MCICDAUDIO
* wmcda
, UINT wStatus
)
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
);
255 /**************************************************************************
256 * MCICDA_GetStatus [internal]
258 static DWORD
MCICDA_GetStatus(WINE_MCICDAUDIO
* wmcda
)
260 CDROM_SUB_Q_DATA_FORMAT fmt
;
261 SUB_Q_CHANNEL_DATA data
;
263 DWORD mode
= MCI_MODE_NOT_READY
;
265 fmt
.Format
= IOCTL_CDROM_CURRENT_POSITION
;
266 if(wmcda
->hThread
!= 0) {
270 hr
= IDirectSoundBuffer_GetStatus(wmcda
->dsBuf
, &status
);
272 if(!(status
&DSBSTATUS_PLAYING
)) {
273 if(WaitForSingleObject(wmcda
->stopEvent
, 0) == WAIT_OBJECT_0
)
274 mode
= MCI_MODE_STOP
;
276 mode
= MCI_MODE_PAUSE
;
279 mode
= MCI_MODE_PLAY
;
282 else if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_Q_CHANNEL
, &fmt
, sizeof(fmt
),
283 &data
, sizeof(data
), &br
, NULL
)) {
284 if (GetLastError() == ERROR_NOT_READY
) mode
= MCI_MODE_OPEN
;
286 switch (data
.CurrentPosition
.Header
.AudioStatus
)
288 case AUDIO_STATUS_IN_PROGRESS
: mode
= MCI_MODE_PLAY
; break;
289 case AUDIO_STATUS_PAUSED
: mode
= MCI_MODE_PAUSE
; break;
290 case AUDIO_STATUS_NO_STATUS
:
291 case AUDIO_STATUS_PLAY_COMPLETE
: mode
= MCI_MODE_STOP
; break;
292 case AUDIO_STATUS_PLAY_ERROR
:
293 case AUDIO_STATUS_NOT_SUPPORTED
:
301 /**************************************************************************
302 * MCICDA_GetError [internal]
304 static int MCICDA_GetError(WINE_MCICDAUDIO
* wmcda
)
306 switch (GetLastError())
308 case ERROR_NOT_READY
: return MCIERR_DEVICE_NOT_READY
;
309 case ERROR_NOT_SUPPORTED
:
310 case ERROR_IO_DEVICE
: return MCIERR_HARDWARE
;
312 FIXME("Unknown mode %u\n", GetLastError());
314 return MCIERR_DRIVER_INTERNAL
;
317 /**************************************************************************
318 * MCICDA_CalcFrame [internal]
320 static DWORD
MCICDA_CalcFrame(WINE_MCICDAUDIO
* wmcda
, DWORD dwTime
)
328 TRACE("(%p, %08X, %u);\n", wmcda
, wmcda
->dwTimeFormat
, dwTime
);
330 switch (wmcda
->dwTimeFormat
) {
331 case MCI_FORMAT_MILLISECONDS
:
332 dwFrame
= ((dwTime
- 1) * CDFRAMES_PERSEC
+ 500) / 1000;
333 TRACE("MILLISECONDS %u\n", dwFrame
);
336 TRACE("MSF %02u:%02u:%02u\n",
337 MCI_MSF_MINUTE(dwTime
), MCI_MSF_SECOND(dwTime
), MCI_MSF_FRAME(dwTime
));
338 dwFrame
+= CDFRAMES_PERMIN
* MCI_MSF_MINUTE(dwTime
);
339 dwFrame
+= CDFRAMES_PERSEC
* MCI_MSF_SECOND(dwTime
);
340 dwFrame
+= MCI_MSF_FRAME(dwTime
);
342 case MCI_FORMAT_TMSF
:
343 default: /* unknown format ! force TMSF ! ... */
344 wTrack
= MCI_TMSF_TRACK(dwTime
);
345 if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
346 &toc
, sizeof(toc
), &br
, NULL
))
348 if (wTrack
< toc
.FirstTrack
|| wTrack
> toc
.LastTrack
)
350 TRACE("MSF %02u-%02u:%02u:%02u\n",
351 MCI_TMSF_TRACK(dwTime
), MCI_TMSF_MINUTE(dwTime
),
352 MCI_TMSF_SECOND(dwTime
), MCI_TMSF_FRAME(dwTime
));
353 addr
= toc
.TrackData
[wTrack
- toc
.FirstTrack
].Address
;
354 TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
355 wTrack
, addr
[1], addr
[2], addr
[3]);
356 dwFrame
= CDFRAMES_PERMIN
* (addr
[1] + MCI_TMSF_MINUTE(dwTime
)) +
357 CDFRAMES_PERSEC
* (addr
[2] + MCI_TMSF_SECOND(dwTime
)) +
358 addr
[3] + MCI_TMSF_FRAME(dwTime
);
364 /**************************************************************************
365 * MCICDA_CalcTime [internal]
367 static DWORD
MCICDA_CalcTime(WINE_MCICDAUDIO
* wmcda
, DWORD tf
, DWORD dwFrame
, LPDWORD lpRet
)
377 TRACE("(%p, %08X, %u);\n", wmcda
, tf
, dwFrame
);
380 case MCI_FORMAT_MILLISECONDS
:
381 dwTime
= (dwFrame
* 1000) / CDFRAMES_PERSEC
+ 1;
382 TRACE("MILLISECONDS %u\n", dwTime
);
386 wMinutes
= dwFrame
/ CDFRAMES_PERMIN
;
387 wSeconds
= (dwFrame
- CDFRAMES_PERMIN
* wMinutes
) / CDFRAMES_PERSEC
;
388 wFrames
= dwFrame
- CDFRAMES_PERMIN
* wMinutes
- CDFRAMES_PERSEC
* wSeconds
;
389 dwTime
= MCI_MAKE_MSF(wMinutes
, wSeconds
, wFrames
);
390 TRACE("MSF %02u:%02u:%02u -> dwTime=%u\n",
391 wMinutes
, wSeconds
, wFrames
, dwTime
);
392 *lpRet
= MCI_COLONIZED3_RETURN
;
394 case MCI_FORMAT_TMSF
:
395 default: /* unknown format ! force TMSF ! ... */
396 if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
397 &toc
, sizeof(toc
), &br
, NULL
))
399 if (dwFrame
< FRAME_OF_TOC(toc
, toc
.FirstTrack
) ||
400 dwFrame
> FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1)) {
401 ERR("Out of range value %u [%u,%u]\n",
402 dwFrame
, FRAME_OF_TOC(toc
, toc
.FirstTrack
),
403 FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1));
407 for (wTrack
= toc
.FirstTrack
; wTrack
<= toc
.LastTrack
; wTrack
++) {
408 if (FRAME_OF_TOC(toc
, wTrack
) > dwFrame
)
412 dwFrame
-= FRAME_OF_TOC(toc
, wTrack
);
413 wMinutes
= dwFrame
/ CDFRAMES_PERMIN
;
414 wSeconds
= (dwFrame
- CDFRAMES_PERMIN
* wMinutes
) / CDFRAMES_PERSEC
;
415 wFrames
= dwFrame
- CDFRAMES_PERMIN
* wMinutes
- CDFRAMES_PERSEC
* wSeconds
;
416 dwTime
= MCI_MAKE_TMSF(wTrack
, wMinutes
, wSeconds
, wFrames
);
417 TRACE("%02u-%02u:%02u:%02u\n", wTrack
, wMinutes
, wSeconds
, wFrames
);
418 *lpRet
= MCI_COLONIZED4_RETURN
;
424 static DWORD
MCICDA_Stop(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
);
426 /**************************************************************************
427 * MCICDA_Open [internal]
429 static DWORD
MCICDA_Open(UINT wDevID
, DWORD dwFlags
, LPMCI_OPEN_PARMSW lpOpenParms
)
431 MCIDEVICEID dwDeviceID
;
433 WINE_MCICDAUDIO
* wmcda
= (WINE_MCICDAUDIO
*)mciGetDriverData(wDevID
);
434 WCHAR root
[7], drive
= 0;
436 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpOpenParms
);
438 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
439 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
441 dwDeviceID
= lpOpenParms
->wDeviceID
;
443 if (wmcda
->nUseCount
> 0) {
444 /* The driver is already open on this channel */
445 /* If the driver was opened shareable before and this open specifies */
446 /* shareable then increment the use count */
447 if (wmcda
->fShareable
&& (dwFlags
& MCI_OPEN_SHAREABLE
))
450 return MCIERR_MUST_USE_SHAREABLE
;
452 wmcda
->nUseCount
= 1;
453 wmcda
->fShareable
= dwFlags
& MCI_OPEN_SHAREABLE
;
455 if (dwFlags
& MCI_OPEN_ELEMENT
) {
456 if (dwFlags
& MCI_OPEN_ELEMENT_ID
) {
457 WARN("MCI_OPEN_ELEMENT_ID %p! Abort\n", lpOpenParms
->lpstrElementName
);
458 ret
= MCIERR_FLAGS_NOT_COMPATIBLE
;
461 TRACE("MCI_OPEN_ELEMENT element name: %s\n", debugstr_w(lpOpenParms
->lpstrElementName
));
462 /* Only the first letter counts since w2k
463 * Win9x-NT accept only d: and w98SE accepts d:\foobar as well.
464 * Play d:\Track03.cda plays from the first track, not #3. */
465 if (!isalpha(lpOpenParms
->lpstrElementName
[0]))
467 ret
= MCIERR_INVALID_FILE
;
470 drive
= toupper(lpOpenParms
->lpstrElementName
[0]);
471 root
[0] = drive
; root
[1] = ':'; root
[2] = '\\'; root
[3] = '\0';
472 if (GetDriveTypeW(root
) != DRIVE_CDROM
)
474 ret
= MCIERR_INVALID_FILE
;
480 root
[0] = 'A'; root
[1] = ':'; root
[2] = '\\'; root
[3] = '\0';
481 for ( ; root
[0] <= 'Z'; root
[0]++)
483 if (GetDriveTypeW(root
) == DRIVE_CDROM
)
491 ret
= MCIERR_CANNOT_LOAD_DRIVER
; /* drvOpen should return this */
496 wmcda
->wNotifyDeviceID
= dwDeviceID
;
497 wmcda
->dwTimeFormat
= MCI_FORMAT_MSF
;
499 /* now, open the handle */
500 root
[0] = root
[1] = '\\'; root
[2] = '.'; root
[3] = '\\'; root
[4] = drive
; root
[5] = ':'; root
[6] = '\0';
501 wmcda
->handle
= CreateFileW(root
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
502 if (wmcda
->handle
== INVALID_HANDLE_VALUE
)
504 ret
= MCIERR_MUST_USE_SHAREABLE
;
508 if (dwFlags
& MCI_NOTIFY
) {
509 mciDriverNotify(HWND_32(LOWORD(lpOpenParms
->dwCallback
)),
510 dwDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
519 /**************************************************************************
520 * MCICDA_Close [internal]
522 static DWORD
MCICDA_Close(UINT wDevID
, DWORD dwParam
, LPMCI_GENERIC_PARMS lpParms
)
524 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
526 TRACE("(%04X, %08X, %p);\n", wDevID
, dwParam
, lpParms
);
528 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
530 MCICDA_Stop(wDevID
, MCI_WAIT
, NULL
);
532 if (--wmcda
->nUseCount
== 0) {
533 CloseHandle(wmcda
->handle
);
535 if ((dwParam
& MCI_NOTIFY
) && lpParms
)
536 MCICDA_Notify(lpParms
->dwCallback
, wmcda
, MCI_NOTIFY_SUCCESSFUL
);
540 /**************************************************************************
541 * MCICDA_GetDevCaps [internal]
543 static DWORD
MCICDA_GetDevCaps(UINT wDevID
, DWORD dwFlags
,
544 LPMCI_GETDEVCAPS_PARMS lpParms
)
546 WINE_MCICDAUDIO
* wmcda
= (WINE_MCICDAUDIO
*)mciGetDriverData(wDevID
);
549 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
551 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
552 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
554 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
555 TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08X;\n", lpParms
->dwItem
);
557 switch (lpParms
->dwItem
) {
558 case MCI_GETDEVCAPS_CAN_RECORD
:
559 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
560 ret
= MCI_RESOURCE_RETURNED
;
562 case MCI_GETDEVCAPS_HAS_AUDIO
:
563 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
564 ret
= MCI_RESOURCE_RETURNED
;
566 case MCI_GETDEVCAPS_HAS_VIDEO
:
567 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
568 ret
= MCI_RESOURCE_RETURNED
;
570 case MCI_GETDEVCAPS_DEVICE_TYPE
:
571 lpParms
->dwReturn
= MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO
, MCI_DEVTYPE_CD_AUDIO
);
572 ret
= MCI_RESOURCE_RETURNED
;
574 case MCI_GETDEVCAPS_USES_FILES
:
575 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
576 ret
= MCI_RESOURCE_RETURNED
;
578 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
579 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
580 ret
= MCI_RESOURCE_RETURNED
;
582 case MCI_GETDEVCAPS_CAN_EJECT
:
583 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
584 ret
= MCI_RESOURCE_RETURNED
;
586 case MCI_GETDEVCAPS_CAN_PLAY
:
587 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
588 ret
= MCI_RESOURCE_RETURNED
;
590 case MCI_GETDEVCAPS_CAN_SAVE
:
591 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
592 ret
= MCI_RESOURCE_RETURNED
;
595 WARN("Unsupported %x devCaps item\n", lpParms
->dwItem
);
596 return MCIERR_UNSUPPORTED_FUNCTION
;
599 TRACE("No GetDevCaps-Item !\n");
600 return MCIERR_MISSING_PARAMETER
;
602 TRACE("lpParms->dwReturn=%08X;\n", lpParms
->dwReturn
);
603 if (dwFlags
& MCI_NOTIFY
) {
604 MCICDA_Notify(lpParms
->dwCallback
, wmcda
, MCI_NOTIFY_SUCCESSFUL
);
609 static DWORD
CDROM_Audio_GetSerial(CDROM_TOC
* toc
)
614 DWORD dwStart
, dwEnd
;
617 * wMagic collects the wFrames from track 1
618 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
620 * There it is collected for correcting the serial when there are less than
623 wMagic
= toc
->TrackData
[0].Address
[3];
624 dwStart
= FRAME_OF_TOC(*toc
, toc
->FirstTrack
);
626 for (i
= 0; i
<= toc
->LastTrack
- toc
->FirstTrack
; i
++) {
627 serial
+= (toc
->TrackData
[i
].Address
[1] << 16) |
628 (toc
->TrackData
[i
].Address
[2] << 8) | toc
->TrackData
[i
].Address
[3];
630 dwEnd
= FRAME_OF_TOC(*toc
, toc
->LastTrack
+ 1);
632 if (toc
->LastTrack
- toc
->FirstTrack
+ 1 < 3)
633 serial
+= wMagic
+ (dwEnd
- dwStart
);
639 /**************************************************************************
640 * MCICDA_Info [internal]
642 static DWORD
MCICDA_Info(UINT wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMSW lpParms
)
645 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
649 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
651 if (lpParms
== NULL
|| lpParms
->lpstrReturn
== NULL
)
652 return MCIERR_NULL_PARAMETER_BLOCK
;
653 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
655 TRACE("buf=%p, len=%u\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
657 if (dwFlags
& MCI_INFO_PRODUCT
) {
658 static const WCHAR wszAudioCd
[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','C','D',0};
660 } else if (dwFlags
& MCI_INFO_MEDIA_UPC
) {
661 ret
= MCIERR_NO_IDENTITY
;
662 } else if (dwFlags
& MCI_INFO_MEDIA_IDENTITY
) {
666 static const WCHAR wszLu
[] = {'%','l','u',0};
668 if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
669 &toc
, sizeof(toc
), &br
, NULL
)) {
670 return MCICDA_GetError(wmcda
);
673 res
= CDROM_Audio_GetSerial(&toc
);
674 sprintfW(buffer
, wszLu
, res
);
677 WARN("Don't know this info command (%u)\n", dwFlags
);
678 ret
= MCIERR_MISSING_PARAMETER
;
681 TRACE("=> %s\n", debugstr_w(str
));
682 if (lpParms
->dwRetSize
) {
683 /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize
684 * to the number of characters written, excluding \0. */
685 lstrcpynW(lpParms
->lpstrReturn
, str
, lpParms
->dwRetSize
);
686 } else ret
= MCIERR_PARAM_OVERFLOW
;
688 if (MMSYSERR_NOERROR
==ret
&& (dwFlags
& MCI_NOTIFY
))
689 MCICDA_Notify(lpParms
->dwCallback
, wmcda
, MCI_NOTIFY_SUCCESSFUL
);
693 /**************************************************************************
694 * MCICDA_Status [internal]
696 static DWORD
MCICDA_Status(UINT wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
698 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
700 CDROM_SUB_Q_DATA_FORMAT fmt
;
701 SUB_Q_CHANNEL_DATA data
;
705 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
707 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
708 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
710 if (dwFlags
& MCI_STATUS_ITEM
) {
711 TRACE("dwItem = %x\n", lpParms
->dwItem
);
712 switch (lpParms
->dwItem
) {
713 case MCI_STATUS_CURRENT_TRACK
:
714 fmt
.Format
= IOCTL_CDROM_CURRENT_POSITION
;
715 if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_Q_CHANNEL
, &fmt
, sizeof(fmt
),
716 &data
, sizeof(data
), &br
, NULL
))
718 return MCICDA_GetError(wmcda
);
719 /* alt. data.CurrentPosition.TrackNumber = 1; -- what native yields */
721 lpParms
->dwReturn
= data
.CurrentPosition
.TrackNumber
;
722 TRACE("CURRENT_TRACK=%lu\n", lpParms
->dwReturn
);
724 case MCI_STATUS_LENGTH
:
725 if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
726 &toc
, sizeof(toc
), &br
, NULL
)) {
727 WARN("error reading TOC !\n");
728 return MCICDA_GetError(wmcda
);
730 if (dwFlags
& MCI_TRACK
) {
731 TRACE("MCI_TRACK #%u LENGTH=??? !\n", lpParms
->dwTrack
);
732 if (lpParms
->dwTrack
< toc
.FirstTrack
|| lpParms
->dwTrack
> toc
.LastTrack
)
733 return MCIERR_OUTOFRANGE
;
734 lpParms
->dwReturn
= FRAME_OF_TOC(toc
, lpParms
->dwTrack
+ 1) -
735 FRAME_OF_TOC(toc
, lpParms
->dwTrack
);
736 /* Windows returns one frame less than the total track length for the
737 last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */
738 if (lpParms
->dwTrack
== toc
.LastTrack
)
741 /* Sum of the lengths of all of the tracks. Inherits the
742 'off by one frame' behavior from the length of the last track.
743 See above comment. */
744 lpParms
->dwReturn
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1) -
745 FRAME_OF_TOC(toc
, toc
.FirstTrack
) - 1;
747 lpParms
->dwReturn
= MCICDA_CalcTime(wmcda
,
748 (wmcda
->dwTimeFormat
== MCI_FORMAT_TMSF
)
749 ? MCI_FORMAT_MSF
: wmcda
->dwTimeFormat
,
752 TRACE("LENGTH=%lu\n", lpParms
->dwReturn
);
754 case MCI_STATUS_MODE
:
755 lpParms
->dwReturn
= MCICDA_GetStatus(wmcda
);
756 TRACE("MCI_STATUS_MODE=%08lX\n", lpParms
->dwReturn
);
757 lpParms
->dwReturn
= MAKEMCIRESOURCE(lpParms
->dwReturn
, lpParms
->dwReturn
);
758 ret
= MCI_RESOURCE_RETURNED
;
760 case MCI_STATUS_MEDIA_PRESENT
:
761 lpParms
->dwReturn
= (MCICDA_GetStatus(wmcda
) == MCI_MODE_OPEN
) ?
762 MAKEMCIRESOURCE(FALSE
, MCI_FALSE
) : MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
763 TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms
->dwReturn
) ? 'Y' : 'N');
764 ret
= MCI_RESOURCE_RETURNED
;
766 case MCI_STATUS_NUMBER_OF_TRACKS
:
767 if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
768 &toc
, sizeof(toc
), &br
, NULL
)) {
769 WARN("error reading TOC !\n");
770 return MCICDA_GetError(wmcda
);
772 lpParms
->dwReturn
= toc
.LastTrack
- toc
.FirstTrack
+ 1;
773 TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu\n", lpParms
->dwReturn
);
774 if (lpParms
->dwReturn
== (WORD
)-1)
775 return MCICDA_GetError(wmcda
);
777 case MCI_STATUS_POSITION
:
778 switch (dwFlags
& (MCI_STATUS_START
| MCI_TRACK
)) {
779 case MCI_STATUS_START
:
780 if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
781 &toc
, sizeof(toc
), &br
, NULL
)) {
782 WARN("error reading TOC !\n");
783 return MCICDA_GetError(wmcda
);
785 lpParms
->dwReturn
= FRAME_OF_TOC(toc
, toc
.FirstTrack
);
786 TRACE("get MCI_STATUS_START !\n");
789 if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
790 &toc
, sizeof(toc
), &br
, NULL
)) {
791 WARN("error reading TOC !\n");
792 return MCICDA_GetError(wmcda
);
794 if (lpParms
->dwTrack
< toc
.FirstTrack
|| lpParms
->dwTrack
> toc
.LastTrack
)
795 return MCIERR_OUTOFRANGE
;
796 lpParms
->dwReturn
= FRAME_OF_TOC(toc
, lpParms
->dwTrack
);
797 TRACE("get MCI_TRACK #%u !\n", lpParms
->dwTrack
);
800 fmt
.Format
= IOCTL_CDROM_CURRENT_POSITION
;
801 if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_Q_CHANNEL
, &fmt
, sizeof(fmt
),
802 &data
, sizeof(data
), &br
, NULL
)) {
803 return MCICDA_GetError(wmcda
);
805 lpParms
->dwReturn
= FRAME_OF_ADDR(data
.CurrentPosition
.AbsoluteAddress
);
808 return MCIERR_FLAGS_NOT_COMPATIBLE
;
810 lpParms
->dwReturn
= MCICDA_CalcTime(wmcda
, wmcda
->dwTimeFormat
, lpParms
->dwReturn
, &ret
);
811 TRACE("MCI_STATUS_POSITION=%08lX\n", lpParms
->dwReturn
);
813 case MCI_STATUS_READY
:
814 TRACE("MCI_STATUS_READY !\n");
815 switch (MCICDA_GetStatus(wmcda
))
817 case MCI_MODE_NOT_READY
:
819 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
822 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
825 TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms
->dwReturn
));
826 ret
= MCI_RESOURCE_RETURNED
;
828 case MCI_STATUS_TIME_FORMAT
:
829 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmcda
->dwTimeFormat
, MCI_FORMAT_RETURN_BASE
+ wmcda
->dwTimeFormat
);
830 TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms
->dwReturn
));
831 ret
= MCI_RESOURCE_RETURNED
;
833 case 4001: /* FIXME: for bogus FullCD */
834 case MCI_CDA_STATUS_TYPE_TRACK
:
835 if (!(dwFlags
& MCI_TRACK
))
836 ret
= MCIERR_MISSING_PARAMETER
;
838 if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
839 &toc
, sizeof(toc
), &br
, NULL
)) {
840 WARN("error reading TOC !\n");
841 return MCICDA_GetError(wmcda
);
843 if (lpParms
->dwTrack
< toc
.FirstTrack
|| lpParms
->dwTrack
> toc
.LastTrack
)
844 ret
= MCIERR_OUTOFRANGE
;
846 lpParms
->dwReturn
= (toc
.TrackData
[lpParms
->dwTrack
- toc
.FirstTrack
].Control
& 0x04) ?
847 MCI_CDA_TRACK_OTHER
: MCI_CDA_TRACK_AUDIO
;
848 /* FIXME: MAKEMCIRESOURCE "audio" | "other", localised */
850 TRACE("MCI_CDA_STATUS_TYPE_TRACK[%d]=%ld\n", lpParms
->dwTrack
, lpParms
->dwReturn
);
853 FIXME("unknown command %08X !\n", lpParms
->dwItem
);
854 return MCIERR_UNSUPPORTED_FUNCTION
;
856 } else return MCIERR_MISSING_PARAMETER
;
857 if ((dwFlags
& MCI_NOTIFY
) && HRESULT_CODE(ret
)==0)
858 MCICDA_Notify(lpParms
->dwCallback
, wmcda
, MCI_NOTIFY_SUCCESSFUL
);
862 /**************************************************************************
863 * MCICDA_SkipDataTracks [internal]
865 static DWORD
MCICDA_SkipDataTracks(WINE_MCICDAUDIO
* wmcda
,DWORD
*frame
)
870 if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
871 &toc
, sizeof(toc
), &br
, NULL
)) {
872 WARN("error reading TOC !\n");
873 return MCICDA_GetError(wmcda
);
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) )
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))
895 /**************************************************************************
896 * MCICDA_Play [internal]
898 static DWORD
MCICDA_Play(UINT wDevID
, DWORD dwFlags
, LPMCI_PLAY_PARMS lpParms
)
900 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
901 DWORD ret
= 0, start
, end
;
904 CDROM_PLAY_AUDIO_MSF play
;
905 CDROM_SUB_Q_DATA_FORMAT fmt
;
906 SUB_Q_CHANNEL_DATA data
;
909 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
912 return MCIERR_NULL_PARAMETER_BLOCK
;
915 return MCIERR_INVALID_DEVICE_ID
;
917 if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
918 &toc
, sizeof(toc
), &br
, NULL
)) {
919 WARN("error reading TOC !\n");
920 return MCICDA_GetError(wmcda
);
923 if (dwFlags
& MCI_FROM
) {
924 start
= MCICDA_CalcFrame(wmcda
, lpParms
->dwFrom
);
925 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &start
)) )
927 TRACE("MCI_FROM=%08X -> %u\n", lpParms
->dwFrom
, start
);
929 fmt
.Format
= IOCTL_CDROM_CURRENT_POSITION
;
930 if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_Q_CHANNEL
, &fmt
, sizeof(fmt
),
931 &data
, sizeof(data
), &br
, NULL
)) {
932 return MCICDA_GetError(wmcda
);
934 start
= FRAME_OF_ADDR(data
.CurrentPosition
.AbsoluteAddress
);
935 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &start
)) )
938 if (dwFlags
& MCI_TO
) {
939 end
= MCICDA_CalcFrame(wmcda
, lpParms
->dwTo
);
940 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &end
)) )
942 TRACE("MCI_TO=%08X -> %u\n", lpParms
->dwTo
, end
);
944 end
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1) - 1;
946 if (end
< start
) return MCIERR_OUTOFRANGE
;
947 TRACE("Playing from %u to %u\n", start
, end
);
949 oldcb
= InterlockedExchangePointer(&wmcda
->hCallback
,
950 (dwFlags
& MCI_NOTIFY
) ? HWND_32(LOWORD(lpParms
->dwCallback
)) : NULL
);
951 if (oldcb
) mciDriverNotify(oldcb
, wmcda
->wNotifyDeviceID
, MCI_NOTIFY_ABORTED
);
953 if (start
== end
|| start
== FRAME_OF_TOC(toc
,toc
.LastTrack
+1)-1) {
954 if (dwFlags
& MCI_NOTIFY
) {
955 oldcb
= InterlockedExchangePointer(&wmcda
->hCallback
, NULL
);
956 if (oldcb
) mciDriverNotify(oldcb
, wDevID
, MCI_NOTIFY_SUCCESSFUL
);
958 return MMSYSERR_NOERROR
;
961 if (wmcda
->hThread
!= 0) {
962 SetEvent(wmcda
->stopEvent
);
963 WaitForSingleObject(wmcda
->hThread
, INFINITE
);
965 CloseHandle(wmcda
->hThread
);
967 CloseHandle(wmcda
->stopEvent
);
968 wmcda
->stopEvent
= 0;
970 IDirectSoundBuffer_Stop(wmcda
->dsBuf
);
971 IDirectSoundBuffer_Release(wmcda
->dsBuf
);
973 IDirectSound_Release(wmcda
->dsObj
);
977 if (pDirectSoundCreate
) {
984 hr
= pDirectSoundCreate(NULL
, &wmcda
->dsObj
, NULL
);
986 IDirectSound_SetCooperativeLevel(wmcda
->dsObj
, GetDesktopWindow(), DSSCL_PRIORITY
);
988 /* The "raw" frame is relative to the start of the first track */
989 wmcda
->start
= start
- FRAME_OF_TOC(toc
, toc
.FirstTrack
);
990 wmcda
->end
= end
- FRAME_OF_TOC(toc
, toc
.FirstTrack
);
992 memset(&format
, 0, sizeof(format
));
993 format
.wFormatTag
= WAVE_FORMAT_PCM
;
994 format
.nChannels
= 2;
995 format
.nSamplesPerSec
= 44100;
996 format
.wBitsPerSample
= 16;
997 format
.nBlockAlign
= format
.nChannels
* format
.wBitsPerSample
/ 8;
998 format
.nAvgBytesPerSec
= format
.nSamplesPerSec
* format
.nBlockAlign
;
1001 memset(&desc
, 0, sizeof(desc
));
1002 desc
.dwSize
= sizeof(desc
);
1003 desc
.dwFlags
= DSBCAPS_GETCURRENTPOSITION2
| DSBCAPS_GLOBALFOCUS
;
1004 desc
.dwBufferBytes
= (CDDA_FRAG_SIZE
- (CDDA_FRAG_SIZE
%RAW_SECTOR_SIZE
)) * CDDA_FRAG_COUNT
;
1005 desc
.lpwfxFormat
= &format
;
1007 hr
= IDirectSound_CreateSoundBuffer(wmcda
->dsObj
, &desc
, &wmcda
->dsBuf
, NULL
);
1009 if (SUCCEEDED(hr
)) {
1010 hr
= IDirectSoundBuffer_Lock(wmcda
->dsBuf
, 0, 0, &cdData
, &lockLen
,
1011 NULL
, NULL
, DSBLOCK_ENTIREBUFFER
);
1013 if (SUCCEEDED(hr
)) {
1014 RAW_READ_INFO rdInfo
;
1017 rdInfo
.DiskOffset
.QuadPart
= wmcda
->start
<<11;
1018 rdInfo
.SectorCount
= min(desc
.dwBufferBytes
/RAW_SECTOR_SIZE
,
1019 wmcda
->end
-wmcda
->start
);
1020 rdInfo
.TrackMode
= CDDA
;
1022 readok
= device_io(wmcda
->handle
, IOCTL_CDROM_RAW_READ
,
1023 &rdInfo
, sizeof(rdInfo
), cdData
, lockLen
,
1025 IDirectSoundBuffer_Unlock(wmcda
->dsBuf
, cdData
, lockLen
, NULL
, 0);
1028 wmcda
->start
+= rdInfo
.SectorCount
;
1029 wmcda
->stopEvent
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
1031 if (wmcda
->stopEvent
!= 0)
1032 wmcda
->hThread
= CreateThread(NULL
, 0, MCICDA_playLoop
, wmcda
, 0, &br
);
1033 if (wmcda
->hThread
!= 0) {
1034 hr
= IDirectSoundBuffer_Play(wmcda
->dsBuf
, 0, 0, DSBPLAY_LOOPING
);
1035 if (SUCCEEDED(hr
)) {
1036 /* FIXME: implement MCI_WAIT and send notification only in that case */
1038 oldcb
= InterlockedExchangePointer(&wmcda
->hCallback
, NULL
);
1039 if (oldcb
) mciDriverNotify(oldcb
, wmcda
->wNotifyDeviceID
,
1040 FAILED(hr
) ? MCI_NOTIFY_FAILURE
: MCI_NOTIFY_SUCCESSFUL
);
1045 SetEvent(wmcda
->stopEvent
);
1046 WaitForSingleObject(wmcda
->hThread
, INFINITE
);
1047 CloseHandle(wmcda
->hThread
);
1052 if (wmcda
->stopEvent
!= 0) {
1053 CloseHandle(wmcda
->stopEvent
);
1054 wmcda
->stopEvent
= 0;
1057 IDirectSoundBuffer_Release(wmcda
->dsBuf
);
1058 wmcda
->dsBuf
= NULL
;
1061 IDirectSound_Release(wmcda
->dsObj
);
1062 wmcda
->dsObj
= NULL
;
1066 play
.StartingM
= start
/ CDFRAMES_PERMIN
;
1067 play
.StartingS
= (start
/ CDFRAMES_PERSEC
) % 60;
1068 play
.StartingF
= start
% CDFRAMES_PERSEC
;
1069 play
.EndingM
= end
/ CDFRAMES_PERMIN
;
1070 play
.EndingS
= (end
/ CDFRAMES_PERSEC
) % 60;
1071 play
.EndingF
= end
% CDFRAMES_PERSEC
;
1072 if (!device_io(wmcda
->handle
, IOCTL_CDROM_PLAY_AUDIO_MSF
, &play
, sizeof(play
),
1073 NULL
, 0, &br
, NULL
)) {
1074 wmcda
->hCallback
= NULL
;
1075 ret
= MCIERR_HARDWARE
;
1077 /* The independent CD player has no means to signal MCI_NOTIFY when it's done.
1078 * Native sends a notification with MCI_WAIT only. */
1082 /**************************************************************************
1083 * MCICDA_Stop [internal]
1085 static DWORD
MCICDA_Stop(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1087 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
1091 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1093 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1095 oldcb
= InterlockedExchangePointer(&wmcda
->hCallback
, NULL
);
1096 if (oldcb
) mciDriverNotify(oldcb
, wmcda
->wNotifyDeviceID
, MCI_NOTIFY_ABORTED
);
1098 if (wmcda
->hThread
!= 0) {
1099 SetEvent(wmcda
->stopEvent
);
1100 WaitForSingleObject(wmcda
->hThread
, INFINITE
);
1102 CloseHandle(wmcda
->hThread
);
1104 CloseHandle(wmcda
->stopEvent
);
1105 wmcda
->stopEvent
= 0;
1107 IDirectSoundBuffer_Release(wmcda
->dsBuf
);
1108 wmcda
->dsBuf
= NULL
;
1109 IDirectSound_Release(wmcda
->dsObj
);
1110 wmcda
->dsObj
= NULL
;
1112 else if (!device_io(wmcda
->handle
, IOCTL_CDROM_STOP_AUDIO
, NULL
, 0, NULL
, 0, &br
, NULL
))
1113 return MCIERR_HARDWARE
;
1115 if ((dwFlags
& MCI_NOTIFY
) && lpParms
)
1116 MCICDA_Notify(lpParms
->dwCallback
, wmcda
, MCI_NOTIFY_SUCCESSFUL
);
1120 /**************************************************************************
1121 * MCICDA_Pause [internal]
1123 static DWORD
MCICDA_Pause(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1125 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
1129 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1131 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1133 oldcb
= InterlockedExchangePointer(&wmcda
->hCallback
, NULL
);
1134 if (oldcb
) mciDriverNotify(oldcb
, wmcda
->wNotifyDeviceID
, MCI_NOTIFY_ABORTED
);
1136 if (wmcda
->hThread
!= 0) {
1137 /* Don't bother calling stop if the playLoop thread has already stopped */
1138 if(WaitForSingleObject(wmcda
->stopEvent
, 0) != WAIT_OBJECT_0
&&
1139 FAILED(IDirectSoundBuffer_Stop(wmcda
->dsBuf
)))
1140 return MCIERR_HARDWARE
;
1142 else if (!device_io(wmcda
->handle
, IOCTL_CDROM_PAUSE_AUDIO
, NULL
, 0, NULL
, 0, &br
, NULL
))
1143 return MCIERR_HARDWARE
;
1145 if ((dwFlags
& MCI_NOTIFY
) && lpParms
)
1146 MCICDA_Notify(lpParms
->dwCallback
, wmcda
, MCI_NOTIFY_SUCCESSFUL
);
1150 /**************************************************************************
1151 * MCICDA_Resume [internal]
1153 static DWORD
MCICDA_Resume(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1155 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
1158 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1160 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1162 if (wmcda
->hThread
!= 0) {
1163 /* Don't restart if the playLoop thread has already stopped */
1164 if(WaitForSingleObject(wmcda
->stopEvent
, 0) != WAIT_OBJECT_0
&&
1165 FAILED(IDirectSoundBuffer_Play(wmcda
->dsBuf
, 0, 0, DSBPLAY_LOOPING
)))
1166 return MCIERR_HARDWARE
;
1168 else if (!device_io(wmcda
->handle
, IOCTL_CDROM_RESUME_AUDIO
, NULL
, 0, NULL
, 0, &br
, NULL
))
1169 return MCIERR_HARDWARE
;
1171 if ((dwFlags
& MCI_NOTIFY
) && lpParms
)
1172 MCICDA_Notify(lpParms
->dwCallback
, wmcda
, MCI_NOTIFY_SUCCESSFUL
);
1176 /**************************************************************************
1177 * MCICDA_Seek [internal]
1179 static DWORD
MCICDA_Seek(UINT wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
1182 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
1183 CDROM_SEEK_AUDIO_MSF seek
;
1184 DWORD br
, position
, ret
;
1187 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1189 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1190 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1192 position
= dwFlags
& (MCI_SEEK_TO_START
|MCI_SEEK_TO_END
|MCI_TO
);
1193 if (!position
) return MCIERR_MISSING_PARAMETER
;
1194 if (position
&(position
-1)) return MCIERR_FLAGS_NOT_COMPATIBLE
;
1196 /* Stop sends MCI_NOTIFY_ABORTED when needed.
1197 * Tests show that native first sends ABORTED and reads the TOC,
1198 * then only checks the position flags, then stops and seeks. */
1199 MCICDA_Stop(wDevID
, MCI_WAIT
, 0);
1201 if (!device_io(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
1202 &toc
, sizeof(toc
), &br
, NULL
)) {
1203 WARN("error reading TOC !\n");
1204 return MCICDA_GetError(wmcda
);
1207 case MCI_SEEK_TO_START
:
1208 TRACE("Seeking to start\n");
1209 at
= FRAME_OF_TOC(toc
,toc
.FirstTrack
);
1210 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &at
)) )
1213 case MCI_SEEK_TO_END
:
1214 TRACE("Seeking to end\n");
1215 /* End is prior to lead-out
1216 * yet Win9X seeks to even one frame less than that. */
1217 at
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1) - 1;
1218 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &at
)) )
1222 TRACE("Seeking to %u\n", lpParms
->dwTo
);
1223 at
= MCICDA_CalcFrame(wmcda
, lpParms
->dwTo
);
1224 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &at
)) )
1228 return MCIERR_FLAGS_NOT_COMPATIBLE
;
1232 seek
.M
= at
/ CDFRAMES_PERMIN
;
1233 seek
.S
= (at
/ CDFRAMES_PERSEC
) % 60;
1234 seek
.F
= at
% CDFRAMES_PERSEC
;
1235 if (!device_io(wmcda
->handle
, IOCTL_CDROM_SEEK_AUDIO_MSF
, &seek
, sizeof(seek
),
1236 NULL
, 0, &br
, NULL
))
1237 return MCIERR_HARDWARE
;
1240 if (dwFlags
& MCI_NOTIFY
)
1241 MCICDA_Notify(lpParms
->dwCallback
, wmcda
, MCI_NOTIFY_SUCCESSFUL
);
1245 /**************************************************************************
1246 * MCICDA_SetDoor [internal]
1248 static DWORD
MCICDA_SetDoor(UINT wDevID
, BOOL open
)
1250 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
1253 TRACE("(%04x, %s) !\n", wDevID
, (open
) ? "OPEN" : "CLOSE");
1255 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1257 if (!device_io(wmcda
->handle
,
1258 (open
) ? IOCTL_STORAGE_EJECT_MEDIA
: IOCTL_STORAGE_LOAD_MEDIA
,
1259 NULL
, 0, NULL
, 0, &br
, NULL
))
1260 return MCIERR_HARDWARE
;
1265 /**************************************************************************
1266 * MCICDA_Set [internal]
1268 static DWORD
MCICDA_Set(UINT wDevID
, DWORD dwFlags
, LPMCI_SET_PARMS lpParms
)
1270 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
1272 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1274 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1276 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
1277 MCICDA_SetDoor(wDevID
, TRUE
);
1279 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
1280 MCICDA_SetDoor(wDevID
, FALSE
);
1283 /* only functions which require valid lpParms below this line ! */
1284 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1286 TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
1288 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
1289 switch (lpParms
->dwTimeFormat
) {
1290 case MCI_FORMAT_MILLISECONDS
:
1291 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1293 case MCI_FORMAT_MSF
:
1294 TRACE("MCI_FORMAT_MSF !\n");
1296 case MCI_FORMAT_TMSF
:
1297 TRACE("MCI_FORMAT_TMSF !\n");
1300 return MCIERR_BAD_TIME_FORMAT
;
1302 wmcda
->dwTimeFormat
= lpParms
->dwTimeFormat
;
1304 if (dwFlags
& MCI_SET_AUDIO
) /* one xp machine ignored it */
1305 TRACE("SET_AUDIO %X %x\n", dwFlags
, lpParms
->dwAudio
);
1307 if (dwFlags
& MCI_NOTIFY
)
1308 MCICDA_Notify(lpParms
->dwCallback
, wmcda
, MCI_NOTIFY_SUCCESSFUL
);
1312 /**************************************************************************
1313 * DriverProc (MCICDA.@)
1315 LRESULT CALLBACK
MCICDA_DriverProc(DWORD_PTR dwDevID
, HDRVR hDriv
, UINT wMsg
,
1316 LPARAM dwParam1
, LPARAM dwParam2
)
1319 case DRV_LOAD
: return 1;
1320 case DRV_FREE
: return 1;
1321 case DRV_OPEN
: return MCICDA_drvOpen((LPCWSTR
)dwParam1
, (LPMCI_OPEN_DRIVER_PARMSW
)dwParam2
);
1322 case DRV_CLOSE
: return MCICDA_drvClose(dwDevID
);
1323 case DRV_ENABLE
: return 1;
1324 case DRV_DISABLE
: return 1;
1325 case DRV_QUERYCONFIGURE
: return 1;
1326 case DRV_CONFIGURE
: MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK
); return 1;
1327 case DRV_INSTALL
: return DRVCNF_RESTART
;
1328 case DRV_REMOVE
: return DRVCNF_RESTART
;
1331 if (dwDevID
== 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION
;
1334 case MCI_OPEN_DRIVER
: return MCICDA_Open(dwDevID
, dwParam1
, (LPMCI_OPEN_PARMSW
)dwParam2
);
1335 case MCI_CLOSE_DRIVER
: return MCICDA_Close(dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
)dwParam2
);
1336 case MCI_GETDEVCAPS
: return MCICDA_GetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
)dwParam2
);
1337 case MCI_INFO
: return MCICDA_Info(dwDevID
, dwParam1
, (LPMCI_INFO_PARMSW
)dwParam2
);
1338 case MCI_STATUS
: return MCICDA_Status(dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
)dwParam2
);
1339 case MCI_SET
: return MCICDA_Set(dwDevID
, dwParam1
, (LPMCI_SET_PARMS
)dwParam2
);
1340 case MCI_PLAY
: return MCICDA_Play(dwDevID
, dwParam1
, (LPMCI_PLAY_PARMS
)dwParam2
);
1341 case MCI_STOP
: return MCICDA_Stop(dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
)dwParam2
);
1342 case MCI_PAUSE
: return MCICDA_Pause(dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
)dwParam2
);
1343 case MCI_RESUME
: return MCICDA_Resume(dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
)dwParam2
);
1344 case MCI_SEEK
: return MCICDA_Seek(dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
)dwParam2
);
1345 /* commands that should report an error as they are not supported in
1346 * the native version */
1350 return MCIERR_UNSUPPORTED_FUNCTION
;
1366 TRACE("Unsupported command [0x%x]\n", wMsg
);
1370 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1373 TRACE("Sending msg [0x%x] to default driver proc\n", wMsg
);
1374 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1376 return MCIERR_UNRECOGNIZED_COMMAND
;
1379 /*-----------------------------------------------------------------------*/