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
36 #define __NTDDSTOR_H /* ROS HACK */
39 #include "wine/debug.h"
40 #include "wine/unicode.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(mcicda
);
45 #define CDFRAMES_PERSEC 75
46 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
47 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
48 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
50 /* Defined by red-book standard; do not change! */
51 #define RAW_SECTOR_SIZE (2352)
53 /* Must be >= RAW_SECTOR_SIZE */
54 #define CDDA_FRAG_SIZE (32768)
56 #define CDDA_FRAG_COUNT (3)
60 int nUseCount
; /* Incremented for each shared open */
61 BOOL fShareable
; /* TRUE if first open was shareable */
62 WORD wNotifyDeviceID
; /* MCI device ID with a pending notification */
63 HANDLE hCallback
; /* Callback handle for pending notification */
67 /* The following are used for digital playback only */
73 IDirectSoundBuffer
*dsBuf
;
78 /*-----------------------------------------------------------------------*/
80 typedef HRESULT(WINAPI
*LPDIRECTSOUNDCREATE
)(LPCGUID
,LPDIRECTSOUND
*,LPUNKNOWN
);
81 static LPDIRECTSOUNDCREATE pDirectSoundCreate
;
83 static DWORD CALLBACK
MCICDA_playLoop(void *ptr
)
85 WINE_MCICDAUDIO
*wmcda
= (WINE_MCICDAUDIO
*)ptr
;
86 DWORD lastPos
, curPos
, endPos
, br
;
88 DWORD lockLen
, fragLen
;
93 memset(&caps
, 0, sizeof(caps
));
94 caps
.dwSize
= sizeof(caps
);
95 hr
= IDirectSoundBuffer_GetCaps(wmcda
->dsBuf
, &caps
);
97 fragLen
= caps
.dwBufferBytes
/CDDA_FRAG_COUNT
;
100 while (SUCCEEDED(hr
) && endPos
!= lastPos
&&
101 WaitForSingleObject(wmcda
->stopEvent
, 0) != WAIT_OBJECT_0
) {
102 hr
= IDirectSoundBuffer_GetCurrentPosition(wmcda
->dsBuf
, &curPos
, NULL
);
103 if ((curPos
-lastPos
+caps
.dwBufferBytes
)%caps
.dwBufferBytes
< fragLen
) {
108 EnterCriticalSection(&wmcda
->cs
);
109 rdInfo
.DiskOffset
.QuadPart
= wmcda
->start
<<11;
110 rdInfo
.SectorCount
= min(fragLen
/RAW_SECTOR_SIZE
, wmcda
->end
-wmcda
->start
);
111 rdInfo
.TrackMode
= CDDA
;
113 hr
= IDirectSoundBuffer_Lock(wmcda
->dsBuf
, lastPos
, fragLen
, &cdData
, &lockLen
, NULL
, NULL
, 0);
114 if (hr
== DSERR_BUFFERLOST
) {
115 if(FAILED(IDirectSoundBuffer_Restore(wmcda
->dsBuf
)) ||
116 FAILED(IDirectSoundBuffer_Play(wmcda
->dsBuf
, 0, 0, DSBPLAY_LOOPING
))) {
117 LeaveCriticalSection(&wmcda
->cs
);
120 hr
= IDirectSoundBuffer_Lock(wmcda
->dsBuf
, lastPos
, fragLen
, &cdData
, &lockLen
, NULL
, NULL
, 0);
124 if (rdInfo
.SectorCount
> 0) {
125 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_RAW_READ
, &rdInfo
, sizeof(rdInfo
), cdData
, lockLen
, &br
, NULL
))
126 WARN("CD read failed at sector %d: 0x%x\n", wmcda
->start
, GetLastError());
128 if (rdInfo
.SectorCount
*RAW_SECTOR_SIZE
< lockLen
) {
129 if(endPos
== ~0u) endPos
= lastPos
;
130 memset((BYTE
*)cdData
+ rdInfo
.SectorCount
*RAW_SECTOR_SIZE
, 0,
131 lockLen
- rdInfo
.SectorCount
*RAW_SECTOR_SIZE
);
133 hr
= IDirectSoundBuffer_Unlock(wmcda
->dsBuf
, cdData
, lockLen
, NULL
, 0);
137 lastPos
%= caps
.dwBufferBytes
;
138 wmcda
->start
+= rdInfo
.SectorCount
;
140 LeaveCriticalSection(&wmcda
->cs
);
142 IDirectSoundBuffer_Stop(wmcda
->dsBuf
);
143 SetEvent(wmcda
->stopEvent
);
145 EnterCriticalSection(&wmcda
->cs
);
146 if (wmcda
->hCallback
) {
147 mciDriverNotify(wmcda
->hCallback
, wmcda
->wNotifyDeviceID
,
148 FAILED(hr
) ? MCI_NOTIFY_FAILURE
:
149 ((endPos
!=lastPos
) ? MCI_NOTIFY_ABORTED
:
150 MCI_NOTIFY_SUCCESSFUL
));
151 wmcda
->hCallback
= NULL
;
153 LeaveCriticalSection(&wmcda
->cs
);
161 /**************************************************************************
162 * MCICDA_drvOpen [internal]
164 static DWORD
MCICDA_drvOpen(LPCWSTR str
, LPMCI_OPEN_DRIVER_PARMSW modp
)
166 static HMODULE dsHandle
;
167 WINE_MCICDAUDIO
* wmcda
;
169 if (!modp
) return 0xFFFFFFFF;
171 wmcda
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WINE_MCICDAUDIO
));
177 dsHandle
= LoadLibraryA("dsound.dll");
179 pDirectSoundCreate
= (LPDIRECTSOUNDCREATE
)GetProcAddress(dsHandle
, "DirectSoundCreate");
182 wmcda
->wDevID
= modp
->wDeviceID
;
183 mciSetDriverData(wmcda
->wDevID
, (DWORD_PTR
)wmcda
);
184 modp
->wCustomCommandTable
= MCI_NO_COMMAND_TABLE
;
185 modp
->wType
= MCI_DEVTYPE_CD_AUDIO
;
186 InitializeCriticalSection(&wmcda
->cs
);
187 return modp
->wDeviceID
;
190 /**************************************************************************
191 * MCICDA_drvClose [internal]
193 static DWORD
MCICDA_drvClose(DWORD dwDevID
)
195 WINE_MCICDAUDIO
* wmcda
= (WINE_MCICDAUDIO
*)mciGetDriverData(dwDevID
);
198 DeleteCriticalSection(&wmcda
->cs
);
199 HeapFree(GetProcessHeap(), 0, wmcda
);
200 mciSetDriverData(dwDevID
, 0);
202 return (dwDevID
== 0xFFFFFFFF) ? 1 : 0;
205 /**************************************************************************
206 * MCICDA_GetOpenDrv [internal]
208 static WINE_MCICDAUDIO
* MCICDA_GetOpenDrv(UINT wDevID
)
210 WINE_MCICDAUDIO
* wmcda
= (WINE_MCICDAUDIO
*)mciGetDriverData(wDevID
);
212 if (wmcda
== NULL
|| wmcda
->nUseCount
== 0) {
213 WARN("Invalid wDevID=%u\n", wDevID
);
219 /**************************************************************************
220 * MCICDA_GetStatus [internal]
222 static DWORD
MCICDA_GetStatus(WINE_MCICDAUDIO
* wmcda
)
224 CDROM_SUB_Q_DATA_FORMAT fmt
;
225 SUB_Q_CHANNEL_DATA data
;
227 DWORD mode
= MCI_MODE_NOT_READY
;
229 fmt
.Format
= IOCTL_CDROM_CURRENT_POSITION
;
230 if(wmcda
->hThread
!= 0) {
234 hr
= IDirectSoundBuffer_GetStatus(wmcda
->dsBuf
, &status
);
236 if(!(status
&DSBSTATUS_PLAYING
)) {
237 if(WaitForSingleObject(wmcda
->stopEvent
, 0) == WAIT_OBJECT_0
)
238 mode
= MCI_MODE_STOP
;
240 mode
= MCI_MODE_PAUSE
;
243 mode
= MCI_MODE_PLAY
;
246 else if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_Q_CHANNEL
, &fmt
, sizeof(fmt
),
247 &data
, sizeof(data
), &br
, NULL
)) {
248 if (GetLastError() == ERROR_NOT_READY
) mode
= MCI_MODE_OPEN
;
250 switch (data
.CurrentPosition
.Header
.AudioStatus
)
252 case AUDIO_STATUS_IN_PROGRESS
: mode
= MCI_MODE_PLAY
; break;
253 case AUDIO_STATUS_PAUSED
: mode
= MCI_MODE_PAUSE
; break;
254 case AUDIO_STATUS_NO_STATUS
:
255 case AUDIO_STATUS_PLAY_COMPLETE
: mode
= MCI_MODE_STOP
; break;
256 case AUDIO_STATUS_PLAY_ERROR
:
257 case AUDIO_STATUS_NOT_SUPPORTED
:
265 /**************************************************************************
266 * MCICDA_GetError [internal]
268 static int MCICDA_GetError(WINE_MCICDAUDIO
* wmcda
)
270 switch (GetLastError())
272 case ERROR_NOT_READY
: return MCIERR_DEVICE_NOT_READY
;
273 case ERROR_IO_DEVICE
: return MCIERR_HARDWARE
;
275 FIXME("Unknown mode %u\n", GetLastError());
277 return MCIERR_DRIVER_INTERNAL
;
280 /**************************************************************************
281 * MCICDA_CalcFrame [internal]
283 static DWORD
MCICDA_CalcFrame(WINE_MCICDAUDIO
* wmcda
, DWORD dwTime
)
291 TRACE("(%p, %08X, %u);\n", wmcda
, wmcda
->dwTimeFormat
, dwTime
);
293 switch (wmcda
->dwTimeFormat
) {
294 case MCI_FORMAT_MILLISECONDS
:
295 dwFrame
= ((dwTime
- 1) * CDFRAMES_PERSEC
+ 500) / 1000;
296 TRACE("MILLISECONDS %u\n", dwFrame
);
299 TRACE("MSF %02u:%02u:%02u\n",
300 MCI_MSF_MINUTE(dwTime
), MCI_MSF_SECOND(dwTime
), MCI_MSF_FRAME(dwTime
));
301 dwFrame
+= CDFRAMES_PERMIN
* MCI_MSF_MINUTE(dwTime
);
302 dwFrame
+= CDFRAMES_PERSEC
* MCI_MSF_SECOND(dwTime
);
303 dwFrame
+= MCI_MSF_FRAME(dwTime
);
305 case MCI_FORMAT_TMSF
:
306 default: /* unknown format ! force TMSF ! ... */
307 wTrack
= MCI_TMSF_TRACK(dwTime
);
308 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
309 &toc
, sizeof(toc
), &br
, NULL
))
311 if (wTrack
< toc
.FirstTrack
|| wTrack
> toc
.LastTrack
)
313 TRACE("MSF %02u-%02u:%02u:%02u\n",
314 MCI_TMSF_TRACK(dwTime
), MCI_TMSF_MINUTE(dwTime
),
315 MCI_TMSF_SECOND(dwTime
), MCI_TMSF_FRAME(dwTime
));
316 addr
= toc
.TrackData
[wTrack
- toc
.FirstTrack
].Address
;
317 TRACE("TMSF trackpos[%u]=%d:%d:%d\n",
318 wTrack
, addr
[1], addr
[2], addr
[3]);
319 dwFrame
= CDFRAMES_PERMIN
* (addr
[1] + MCI_TMSF_MINUTE(dwTime
)) +
320 CDFRAMES_PERSEC
* (addr
[2] + MCI_TMSF_SECOND(dwTime
)) +
321 addr
[3] + MCI_TMSF_FRAME(dwTime
);
327 /**************************************************************************
328 * MCICDA_CalcTime [internal]
330 static DWORD
MCICDA_CalcTime(WINE_MCICDAUDIO
* wmcda
, DWORD tf
, DWORD dwFrame
, LPDWORD lpRet
)
340 TRACE("(%p, %08X, %u);\n", wmcda
, tf
, dwFrame
);
343 case MCI_FORMAT_MILLISECONDS
:
344 dwTime
= (dwFrame
* 1000) / CDFRAMES_PERSEC
+ 1;
345 TRACE("MILLISECONDS %u\n", dwTime
);
349 wMinutes
= dwFrame
/ CDFRAMES_PERMIN
;
350 wSeconds
= (dwFrame
- CDFRAMES_PERMIN
* wMinutes
) / CDFRAMES_PERSEC
;
351 wFrames
= dwFrame
- CDFRAMES_PERMIN
* wMinutes
- CDFRAMES_PERSEC
* wSeconds
;
352 dwTime
= MCI_MAKE_MSF(wMinutes
, wSeconds
, wFrames
);
353 TRACE("MSF %02u:%02u:%02u -> dwTime=%u\n",
354 wMinutes
, wSeconds
, wFrames
, dwTime
);
355 *lpRet
= MCI_COLONIZED3_RETURN
;
357 case MCI_FORMAT_TMSF
:
358 default: /* unknown format ! force TMSF ! ... */
359 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
360 &toc
, sizeof(toc
), &br
, NULL
))
362 if (dwFrame
< FRAME_OF_TOC(toc
, toc
.FirstTrack
) ||
363 dwFrame
> FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1)) {
364 ERR("Out of range value %u [%u,%u]\n",
365 dwFrame
, FRAME_OF_TOC(toc
, toc
.FirstTrack
),
366 FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1));
370 for (wTrack
= toc
.FirstTrack
; wTrack
<= toc
.LastTrack
; wTrack
++) {
371 if (FRAME_OF_TOC(toc
, wTrack
) > dwFrame
)
375 dwFrame
-= FRAME_OF_TOC(toc
, wTrack
);
376 wMinutes
= dwFrame
/ CDFRAMES_PERMIN
;
377 wSeconds
= (dwFrame
- CDFRAMES_PERMIN
* wMinutes
) / CDFRAMES_PERSEC
;
378 wFrames
= dwFrame
- CDFRAMES_PERMIN
* wMinutes
- CDFRAMES_PERSEC
* wSeconds
;
379 dwTime
= MCI_MAKE_TMSF(wTrack
, wMinutes
, wSeconds
, wFrames
);
380 TRACE("%02u-%02u:%02u:%02u\n", wTrack
, wMinutes
, wSeconds
, wFrames
);
381 *lpRet
= MCI_COLONIZED4_RETURN
;
387 static DWORD
MCICDA_Seek(UINT wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
);
388 static DWORD
MCICDA_Stop(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
);
390 /**************************************************************************
391 * MCICDA_Open [internal]
393 static DWORD
MCICDA_Open(UINT wDevID
, DWORD dwFlags
, LPMCI_OPEN_PARMSW lpOpenParms
)
396 DWORD ret
= MCIERR_HARDWARE
;
397 WINE_MCICDAUDIO
* wmcda
= (WINE_MCICDAUDIO
*)mciGetDriverData(wDevID
);
398 WCHAR root
[7], drive
= 0;
401 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpOpenParms
);
403 if (lpOpenParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
404 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
406 dwDeviceID
= lpOpenParms
->wDeviceID
;
408 if (wmcda
->nUseCount
> 0) {
409 /* The driver is already open on this channel */
410 /* If the driver was opened shareable before and this open specifies */
411 /* shareable then increment the use count */
412 if (wmcda
->fShareable
&& (dwFlags
& MCI_OPEN_SHAREABLE
))
415 return MCIERR_MUST_USE_SHAREABLE
;
417 wmcda
->nUseCount
= 1;
418 wmcda
->fShareable
= dwFlags
& MCI_OPEN_SHAREABLE
;
420 if (dwFlags
& MCI_OPEN_ELEMENT
) {
421 if (dwFlags
& MCI_OPEN_ELEMENT_ID
) {
422 WARN("MCI_OPEN_ELEMENT_ID %p! Abort\n", lpOpenParms
->lpstrElementName
);
423 ret
= MCIERR_NO_ELEMENT_ALLOWED
;
426 TRACE("MCI_OPEN_ELEMENT element name: %s\n", debugstr_w(lpOpenParms
->lpstrElementName
));
427 if (!isalpha(lpOpenParms
->lpstrElementName
[0]) || lpOpenParms
->lpstrElementName
[1] != ':' ||
428 (lpOpenParms
->lpstrElementName
[2] && lpOpenParms
->lpstrElementName
[2] != '\\'))
430 WARN("MCI_OPEN_ELEMENT unsupported format: %s\n",
431 debugstr_w(lpOpenParms
->lpstrElementName
));
432 ret
= MCIERR_NO_ELEMENT_ALLOWED
;
435 drive
= toupper(lpOpenParms
->lpstrElementName
[0]);
436 root
[0] = drive
; root
[1] = ':'; root
[2] = '\\'; root
[3] = '\0';
437 if (GetDriveTypeW(root
) != DRIVE_CDROM
)
439 ret
= MCIERR_INVALID_DEVICE_NAME
;
445 /* drive letter isn't passed... get the dwDeviceID'th cdrom in the system */
446 root
[0] = 'A'; root
[1] = ':'; root
[2] = '\\'; root
[3] = '\0';
447 for (count
= 0; root
[0] <= 'Z'; root
[0]++)
449 if (GetDriveTypeW(root
) == DRIVE_CDROM
&& ++count
>= dwDeviceID
)
457 ret
= MCIERR_INVALID_DEVICE_ID
;
462 wmcda
->wNotifyDeviceID
= dwDeviceID
;
463 wmcda
->dwTimeFormat
= MCI_FORMAT_MSF
;
465 /* now, open the handle */
466 root
[0] = root
[1] = '\\'; root
[2] = '.'; root
[3] = '\\'; root
[4] = drive
; root
[5] = ':'; root
[6] = '\0';
467 wmcda
->handle
= CreateFileW(root
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
468 if (wmcda
->handle
== INVALID_HANDLE_VALUE
)
471 if (dwFlags
& MCI_NOTIFY
) {
472 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpOpenParms
->dwCallback
);
473 mciDriverNotify(HWND_32(LOWORD(lpOpenParms
->dwCallback
)),
474 wmcda
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
483 /**************************************************************************
484 * MCICDA_Close [internal]
486 static DWORD
MCICDA_Close(UINT wDevID
, DWORD dwParam
, LPMCI_GENERIC_PARMS lpParms
)
488 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
490 TRACE("(%04X, %08X, %p);\n", wDevID
, dwParam
, lpParms
);
492 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
494 MCICDA_Stop(wDevID
, MCI_WAIT
, NULL
);
496 if (--wmcda
->nUseCount
== 0) {
497 CloseHandle(wmcda
->handle
);
502 /**************************************************************************
503 * MCICDA_GetDevCaps [internal]
505 static DWORD
MCICDA_GetDevCaps(UINT wDevID
, DWORD dwFlags
,
506 LPMCI_GETDEVCAPS_PARMS lpParms
)
510 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
512 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
514 if (dwFlags
& MCI_GETDEVCAPS_ITEM
) {
515 TRACE("MCI_GETDEVCAPS_ITEM dwItem=%08X;\n", lpParms
->dwItem
);
517 switch (lpParms
->dwItem
) {
518 case MCI_GETDEVCAPS_CAN_RECORD
:
519 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
520 ret
= MCI_RESOURCE_RETURNED
;
522 case MCI_GETDEVCAPS_HAS_AUDIO
:
523 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
524 ret
= MCI_RESOURCE_RETURNED
;
526 case MCI_GETDEVCAPS_HAS_VIDEO
:
527 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
528 ret
= MCI_RESOURCE_RETURNED
;
530 case MCI_GETDEVCAPS_DEVICE_TYPE
:
531 lpParms
->dwReturn
= MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO
, MCI_DEVTYPE_CD_AUDIO
);
532 ret
= MCI_RESOURCE_RETURNED
;
534 case MCI_GETDEVCAPS_USES_FILES
:
535 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
536 ret
= MCI_RESOURCE_RETURNED
;
538 case MCI_GETDEVCAPS_COMPOUND_DEVICE
:
539 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
540 ret
= MCI_RESOURCE_RETURNED
;
542 case MCI_GETDEVCAPS_CAN_EJECT
:
543 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
544 ret
= MCI_RESOURCE_RETURNED
;
546 case MCI_GETDEVCAPS_CAN_PLAY
:
547 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
548 ret
= MCI_RESOURCE_RETURNED
;
550 case MCI_GETDEVCAPS_CAN_SAVE
:
551 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
552 ret
= MCI_RESOURCE_RETURNED
;
555 ERR("Unsupported %x devCaps item\n", lpParms
->dwItem
);
556 return MCIERR_UNRECOGNIZED_COMMAND
;
559 TRACE("No GetDevCaps-Item !\n");
560 return MCIERR_UNRECOGNIZED_COMMAND
;
562 TRACE("lpParms->dwReturn=%08X;\n", lpParms
->dwReturn
);
566 static DWORD
CDROM_Audio_GetSerial(CDROM_TOC
* toc
)
571 DWORD dwStart
, dwEnd
;
574 * wMagic collects the wFrames from track 1
575 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
577 * There it is collected for correcting the serial when there are less than
580 wMagic
= toc
->TrackData
[0].Address
[3];
581 dwStart
= FRAME_OF_TOC(*toc
, toc
->FirstTrack
);
583 for (i
= 0; i
<= toc
->LastTrack
- toc
->FirstTrack
; i
++) {
584 serial
+= (toc
->TrackData
[i
].Address
[1] << 16) |
585 (toc
->TrackData
[i
].Address
[2] << 8) | toc
->TrackData
[i
].Address
[3];
587 dwEnd
= FRAME_OF_TOC(*toc
, toc
->LastTrack
+ 1);
589 if (toc
->LastTrack
- toc
->FirstTrack
+ 1 < 3)
590 serial
+= wMagic
+ (dwEnd
- dwStart
);
596 /**************************************************************************
597 * MCICDA_Info [internal]
599 static DWORD
MCICDA_Info(UINT wDevID
, DWORD dwFlags
, LPMCI_INFO_PARMSW lpParms
)
602 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
606 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
608 if (lpParms
== NULL
|| lpParms
->lpstrReturn
== NULL
)
609 return MCIERR_NULL_PARAMETER_BLOCK
;
610 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
612 TRACE("buf=%p, len=%u\n", lpParms
->lpstrReturn
, lpParms
->dwRetSize
);
614 if (dwFlags
& MCI_INFO_PRODUCT
) {
615 static const WCHAR wszAudioCd
[] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','C','D',0};
617 } else if (dwFlags
& MCI_INFO_MEDIA_UPC
) {
618 ret
= MCIERR_NO_IDENTITY
;
619 } else if (dwFlags
& MCI_INFO_MEDIA_IDENTITY
) {
623 static const WCHAR wszLu
[] = {'%','l','u',0};
625 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
626 &toc
, sizeof(toc
), &br
, NULL
)) {
627 return MCICDA_GetError(wmcda
);
630 res
= CDROM_Audio_GetSerial(&toc
);
631 sprintfW(buffer
, wszLu
, res
);
634 WARN("Don't know this info command (%u)\n", dwFlags
);
635 ret
= MCIERR_UNRECOGNIZED_COMMAND
;
638 if (lpParms
->dwRetSize
<= strlenW(str
)) {
639 lstrcpynW(lpParms
->lpstrReturn
, str
, lpParms
->dwRetSize
- 1);
640 ret
= MCIERR_PARAM_OVERFLOW
;
642 strcpyW(lpParms
->lpstrReturn
, str
);
645 *lpParms
->lpstrReturn
= 0;
647 TRACE("=> %s (%d)\n", debugstr_w(lpParms
->lpstrReturn
), ret
);
651 /**************************************************************************
652 * MCICDA_Status [internal]
654 static DWORD
MCICDA_Status(UINT wDevID
, DWORD dwFlags
, LPMCI_STATUS_PARMS lpParms
)
656 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
658 CDROM_SUB_Q_DATA_FORMAT fmt
;
659 SUB_Q_CHANNEL_DATA data
;
663 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
665 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
666 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
668 if (dwFlags
& MCI_NOTIFY
) {
669 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
670 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
671 wmcda
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
673 if (dwFlags
& MCI_STATUS_ITEM
) {
674 TRACE("dwItem = %x\n", lpParms
->dwItem
);
675 switch (lpParms
->dwItem
) {
676 case MCI_STATUS_CURRENT_TRACK
:
677 fmt
.Format
= IOCTL_CDROM_CURRENT_POSITION
;
678 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_Q_CHANNEL
, &fmt
, sizeof(fmt
),
679 &data
, sizeof(data
), &br
, NULL
))
681 return MCICDA_GetError(wmcda
);
683 lpParms
->dwReturn
= data
.CurrentPosition
.TrackNumber
;
684 TRACE("CURRENT_TRACK=%lu\n", lpParms
->dwReturn
);
686 case MCI_STATUS_LENGTH
:
687 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
688 &toc
, sizeof(toc
), &br
, NULL
)) {
689 WARN("error reading TOC !\n");
690 return MCICDA_GetError(wmcda
);
692 if (dwFlags
& MCI_TRACK
) {
693 TRACE("MCI_TRACK #%u LENGTH=??? !\n", lpParms
->dwTrack
);
694 if (lpParms
->dwTrack
< toc
.FirstTrack
|| lpParms
->dwTrack
> toc
.LastTrack
)
695 return MCIERR_OUTOFRANGE
;
696 lpParms
->dwReturn
= FRAME_OF_TOC(toc
, lpParms
->dwTrack
+ 1) -
697 FRAME_OF_TOC(toc
, lpParms
->dwTrack
);
698 /* Windows returns one frame less than the total track length for the
699 last track on the CD. See CDDB HOWTO. Verified on Win95OSR2. */
700 if (lpParms
->dwTrack
== toc
.LastTrack
)
703 /* Sum of the lengths of all of the tracks. Inherits the
704 'off by one frame' behavior from the length of the last track.
705 See above comment. */
706 lpParms
->dwReturn
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1) -
707 FRAME_OF_TOC(toc
, toc
.FirstTrack
) - 1;
709 lpParms
->dwReturn
= MCICDA_CalcTime(wmcda
,
710 (wmcda
->dwTimeFormat
== MCI_FORMAT_TMSF
)
711 ? MCI_FORMAT_MSF
: wmcda
->dwTimeFormat
,
714 TRACE("LENGTH=%lu\n", lpParms
->dwReturn
);
716 case MCI_STATUS_MODE
:
717 lpParms
->dwReturn
= MCICDA_GetStatus(wmcda
);
718 TRACE("MCI_STATUS_MODE=%08lX\n", lpParms
->dwReturn
);
719 lpParms
->dwReturn
= MAKEMCIRESOURCE(lpParms
->dwReturn
, lpParms
->dwReturn
);
720 ret
= MCI_RESOURCE_RETURNED
;
722 case MCI_STATUS_MEDIA_PRESENT
:
723 lpParms
->dwReturn
= (MCICDA_GetStatus(wmcda
) == MCI_MODE_OPEN
) ?
724 MAKEMCIRESOURCE(FALSE
, MCI_FALSE
) : MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
725 TRACE("MCI_STATUS_MEDIA_PRESENT =%c!\n", LOWORD(lpParms
->dwReturn
) ? 'Y' : 'N');
726 ret
= MCI_RESOURCE_RETURNED
;
728 case MCI_STATUS_NUMBER_OF_TRACKS
:
729 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
730 &toc
, sizeof(toc
), &br
, NULL
)) {
731 WARN("error reading TOC !\n");
732 return MCICDA_GetError(wmcda
);
734 lpParms
->dwReturn
= toc
.LastTrack
- toc
.FirstTrack
+ 1;
735 TRACE("MCI_STATUS_NUMBER_OF_TRACKS = %lu\n", lpParms
->dwReturn
);
736 if (lpParms
->dwReturn
== (WORD
)-1)
737 return MCICDA_GetError(wmcda
);
739 case MCI_STATUS_POSITION
:
740 if (dwFlags
& MCI_STATUS_START
) {
741 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
742 &toc
, sizeof(toc
), &br
, NULL
)) {
743 WARN("error reading TOC !\n");
744 return MCICDA_GetError(wmcda
);
746 lpParms
->dwReturn
= FRAME_OF_TOC(toc
, toc
.FirstTrack
);
747 TRACE("get MCI_STATUS_START !\n");
748 } else if (dwFlags
& MCI_TRACK
) {
749 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
750 &toc
, sizeof(toc
), &br
, NULL
)) {
751 WARN("error reading TOC !\n");
752 return MCICDA_GetError(wmcda
);
754 if (lpParms
->dwTrack
< toc
.FirstTrack
|| lpParms
->dwTrack
> toc
.LastTrack
)
755 return MCIERR_OUTOFRANGE
;
756 lpParms
->dwReturn
= FRAME_OF_TOC(toc
, lpParms
->dwTrack
);
757 TRACE("get MCI_TRACK #%u !\n", lpParms
->dwTrack
);
759 fmt
.Format
= IOCTL_CDROM_CURRENT_POSITION
;
760 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_Q_CHANNEL
, &fmt
, sizeof(fmt
),
761 &data
, sizeof(data
), &br
, NULL
)) {
762 return MCICDA_GetError(wmcda
);
764 lpParms
->dwReturn
= FRAME_OF_ADDR(data
.CurrentPosition
.AbsoluteAddress
);
766 lpParms
->dwReturn
= MCICDA_CalcTime(wmcda
, wmcda
->dwTimeFormat
, lpParms
->dwReturn
, &ret
);
767 TRACE("MCI_STATUS_POSITION=%08lX\n", lpParms
->dwReturn
);
769 case MCI_STATUS_READY
:
770 TRACE("MCI_STATUS_READY !\n");
771 switch (MCICDA_GetStatus(wmcda
))
773 case MCI_MODE_NOT_READY
:
775 lpParms
->dwReturn
= MAKEMCIRESOURCE(FALSE
, MCI_FALSE
);
778 lpParms
->dwReturn
= MAKEMCIRESOURCE(TRUE
, MCI_TRUE
);
781 TRACE("MCI_STATUS_READY=%u!\n", LOWORD(lpParms
->dwReturn
));
782 ret
= MCI_RESOURCE_RETURNED
;
784 case MCI_STATUS_TIME_FORMAT
:
785 lpParms
->dwReturn
= MAKEMCIRESOURCE(wmcda
->dwTimeFormat
, MCI_FORMAT_RETURN_BASE
+ wmcda
->dwTimeFormat
);
786 TRACE("MCI_STATUS_TIME_FORMAT=%08x!\n", LOWORD(lpParms
->dwReturn
));
787 ret
= MCI_RESOURCE_RETURNED
;
789 case 4001: /* FIXME: for bogus FullCD */
790 case MCI_CDA_STATUS_TYPE_TRACK
:
791 if (!(dwFlags
& MCI_TRACK
))
792 ret
= MCIERR_MISSING_PARAMETER
;
794 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
795 &toc
, sizeof(toc
), &br
, NULL
)) {
796 WARN("error reading TOC !\n");
797 return MCICDA_GetError(wmcda
);
799 if (lpParms
->dwTrack
< toc
.FirstTrack
|| lpParms
->dwTrack
> toc
.LastTrack
)
800 ret
= MCIERR_OUTOFRANGE
;
802 lpParms
->dwReturn
= (toc
.TrackData
[lpParms
->dwTrack
- toc
.FirstTrack
].Control
& 0x04) ?
803 MCI_CDA_TRACK_OTHER
: MCI_CDA_TRACK_AUDIO
;
805 TRACE("MCI_CDA_STATUS_TYPE_TRACK[%d]=%ld\n", lpParms
->dwTrack
, lpParms
->dwReturn
);
808 FIXME("unknown command %08X !\n", lpParms
->dwItem
);
809 return MCIERR_UNRECOGNIZED_COMMAND
;
812 WARN("not MCI_STATUS_ITEM !\n");
817 /**************************************************************************
818 * MCICDA_SkipDataTracks [internal]
820 static DWORD
MCICDA_SkipDataTracks(WINE_MCICDAUDIO
* wmcda
,DWORD
*frame
)
825 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
826 &toc
, sizeof(toc
), &br
, NULL
)) {
827 WARN("error reading TOC !\n");
828 return MCICDA_GetError(wmcda
);
830 /* Locate first track whose starting frame is bigger than frame */
831 for(i
=toc
.FirstTrack
;i
<=toc
.LastTrack
+1;i
++)
832 if ( FRAME_OF_TOC(toc
, i
) > *frame
) break;
833 if (i
<= toc
.FirstTrack
&& i
>toc
.LastTrack
+1) {
834 i
= 0; /* requested address is out of range: go back to start */
835 *frame
= FRAME_OF_TOC(toc
,toc
.FirstTrack
);
839 /* i points to last track whose start address is not greater than frame.
840 * Now skip non-audio tracks */
841 for(;i
<=toc
.LastTrack
+1;i
++)
842 if ( ! (toc
.TrackData
[i
-toc
.FirstTrack
].Control
& 4) )
844 /* The frame will be an address in the next audio track or
845 * address of lead-out. */
846 if ( FRAME_OF_TOC(toc
, i
) > *frame
)
847 *frame
= FRAME_OF_TOC(toc
, i
);
851 /**************************************************************************
852 * MCICDA_Play [internal]
854 static DWORD
MCICDA_Play(UINT wDevID
, DWORD dwFlags
, LPMCI_PLAY_PARMS lpParms
)
856 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
857 DWORD ret
= 0, start
, end
;
859 CDROM_PLAY_AUDIO_MSF play
;
860 CDROM_SUB_Q_DATA_FORMAT fmt
;
861 SUB_Q_CHANNEL_DATA data
;
864 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
867 return MCIERR_NULL_PARAMETER_BLOCK
;
870 return MCIERR_INVALID_DEVICE_ID
;
872 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
873 &toc
, sizeof(toc
), &br
, NULL
)) {
874 WARN("error reading TOC !\n");
875 return MCICDA_GetError(wmcda
);
878 if (dwFlags
& MCI_FROM
) {
879 start
= MCICDA_CalcFrame(wmcda
, lpParms
->dwFrom
);
880 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &start
)) )
882 TRACE("MCI_FROM=%08X -> %u\n", lpParms
->dwFrom
, start
);
884 fmt
.Format
= IOCTL_CDROM_CURRENT_POSITION
;
885 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_Q_CHANNEL
, &fmt
, sizeof(fmt
),
886 &data
, sizeof(data
), &br
, NULL
)) {
887 return MCICDA_GetError(wmcda
);
889 start
= FRAME_OF_ADDR(data
.CurrentPosition
.AbsoluteAddress
);
890 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &start
)) )
893 if (dwFlags
& MCI_TO
) {
894 end
= MCICDA_CalcFrame(wmcda
, lpParms
->dwTo
);
895 TRACE("MCI_TO=%08X -> %u\n", lpParms
->dwTo
, end
);
897 end
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1) - 1;
899 TRACE("Playing from %u to %u\n", start
, end
);
901 if (wmcda
->hThread
!= 0) {
902 SetEvent(wmcda
->stopEvent
);
903 WaitForSingleObject(wmcda
->hThread
, INFINITE
);
905 CloseHandle(wmcda
->hThread
);
907 CloseHandle(wmcda
->stopEvent
);
908 wmcda
->stopEvent
= 0;
910 IDirectSoundBuffer_Stop(wmcda
->dsBuf
);
911 IDirectSoundBuffer_Release(wmcda
->dsBuf
);
913 IDirectSound_Release(wmcda
->dsObj
);
916 else if(wmcda
->hCallback
) {
917 mciDriverNotify(wmcda
->hCallback
, wmcda
->wNotifyDeviceID
,
919 wmcda
->hCallback
= NULL
;
922 if ((dwFlags
&MCI_NOTIFY
))
923 wmcda
->hCallback
= HWND_32(LOWORD(lpParms
->dwCallback
));
925 if (pDirectSoundCreate
) {
932 hr
= pDirectSoundCreate(NULL
, &wmcda
->dsObj
, NULL
);
934 IDirectSound_SetCooperativeLevel(wmcda
->dsObj
, GetDesktopWindow(), DSSCL_PRIORITY
);
936 /* The "raw" frame is relative to the start of the first track */
937 wmcda
->start
= start
- FRAME_OF_TOC(toc
, toc
.FirstTrack
);
938 wmcda
->end
= end
- FRAME_OF_TOC(toc
, toc
.FirstTrack
);
940 memset(&format
, 0, sizeof(format
));
941 format
.wFormatTag
= WAVE_FORMAT_PCM
;
942 format
.nChannels
= 2;
943 format
.nSamplesPerSec
= 44100;
944 format
.wBitsPerSample
= 16;
945 format
.nBlockAlign
= format
.nChannels
* format
.wBitsPerSample
/ 8;
946 format
.nAvgBytesPerSec
= format
.nSamplesPerSec
* format
.nBlockAlign
;
949 memset(&desc
, 0, sizeof(desc
));
950 desc
.dwSize
= sizeof(desc
);
951 desc
.dwFlags
= DSBCAPS_GETCURRENTPOSITION2
| DSBCAPS_GLOBALFOCUS
;
952 desc
.dwBufferBytes
= (CDDA_FRAG_SIZE
- (CDDA_FRAG_SIZE
%RAW_SECTOR_SIZE
)) * CDDA_FRAG_COUNT
;
953 desc
.lpwfxFormat
= &format
;
955 hr
= IDirectSound_CreateSoundBuffer(wmcda
->dsObj
, &desc
, &wmcda
->dsBuf
, NULL
);
958 hr
= IDirectSoundBuffer_Lock(wmcda
->dsBuf
, 0, 0, &cdData
, &lockLen
,
959 NULL
, NULL
, DSBLOCK_ENTIREBUFFER
);
962 RAW_READ_INFO rdInfo
;
965 rdInfo
.DiskOffset
.QuadPart
= wmcda
->start
<<11;
966 rdInfo
.SectorCount
= min(desc
.dwBufferBytes
/RAW_SECTOR_SIZE
,
967 wmcda
->end
-wmcda
->start
);
968 rdInfo
.TrackMode
= CDDA
;
970 readok
= DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_RAW_READ
,
971 &rdInfo
, sizeof(rdInfo
), cdData
, lockLen
,
973 IDirectSoundBuffer_Unlock(wmcda
->dsBuf
, cdData
, lockLen
, NULL
, 0);
976 wmcda
->start
+= rdInfo
.SectorCount
;
977 wmcda
->stopEvent
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
979 if (wmcda
->stopEvent
!= 0)
980 wmcda
->hThread
= CreateThread(NULL
, 0, MCICDA_playLoop
, wmcda
, 0, &br
);
981 if (wmcda
->hThread
!= 0) {
982 hr
= IDirectSoundBuffer_Play(wmcda
->dsBuf
, 0, 0, DSBPLAY_LOOPING
);
986 SetEvent(wmcda
->stopEvent
);
987 WaitForSingleObject(wmcda
->hThread
, INFINITE
);
988 CloseHandle(wmcda
->hThread
);
993 if (wmcda
->stopEvent
!= 0) {
994 CloseHandle(wmcda
->stopEvent
);
995 wmcda
->stopEvent
= 0;
998 IDirectSoundBuffer_Release(wmcda
->dsBuf
);
1002 IDirectSound_Release(wmcda
->dsObj
);
1003 wmcda
->dsObj
= NULL
;
1007 play
.StartingM
= start
/ CDFRAMES_PERMIN
;
1008 play
.StartingS
= (start
/ CDFRAMES_PERSEC
) % 60;
1009 play
.StartingF
= start
% CDFRAMES_PERSEC
;
1010 play
.EndingM
= end
/ CDFRAMES_PERMIN
;
1011 play
.EndingS
= (end
/ CDFRAMES_PERSEC
) % 60;
1012 play
.EndingF
= end
% CDFRAMES_PERSEC
;
1013 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_PLAY_AUDIO_MSF
, &play
, sizeof(play
),
1014 NULL
, 0, &br
, NULL
)) {
1015 wmcda
->hCallback
= NULL
;
1016 ret
= MCIERR_HARDWARE
;
1017 } else if (dwFlags
& MCI_NOTIFY
) {
1018 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
1020 mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1021 wmcda->wNotifyDeviceID, MCI_NOTIFY_SUCCESSFUL);
1027 /**************************************************************************
1028 * MCICDA_Stop [internal]
1030 static DWORD
MCICDA_Stop(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1032 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
1035 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1037 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1039 if (wmcda
->hThread
!= 0) {
1040 SetEvent(wmcda
->stopEvent
);
1041 WaitForSingleObject(wmcda
->hThread
, INFINITE
);
1043 CloseHandle(wmcda
->hThread
);
1045 CloseHandle(wmcda
->stopEvent
);
1046 wmcda
->stopEvent
= 0;
1048 IDirectSoundBuffer_Release(wmcda
->dsBuf
);
1049 wmcda
->dsBuf
= NULL
;
1050 IDirectSound_Release(wmcda
->dsObj
);
1051 wmcda
->dsObj
= NULL
;
1053 else if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_STOP_AUDIO
, NULL
, 0, NULL
, 0, &br
, NULL
))
1054 return MCIERR_HARDWARE
;
1056 if (wmcda
->hCallback
) {
1057 mciDriverNotify(wmcda
->hCallback
, wmcda
->wNotifyDeviceID
, MCI_NOTIFY_ABORTED
);
1058 wmcda
->hCallback
= NULL
;
1061 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
1062 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
1063 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
1064 wmcda
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
1069 /**************************************************************************
1070 * MCICDA_Pause [internal]
1072 static DWORD
MCICDA_Pause(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1074 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
1077 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1079 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1081 if (wmcda
->hThread
!= 0) {
1082 /* Don't bother calling stop if the playLoop thread has already stopped */
1083 if(WaitForSingleObject(wmcda
->stopEvent
, 0) != WAIT_OBJECT_0
&&
1084 FAILED(IDirectSoundBuffer_Stop(wmcda
->dsBuf
)))
1085 return MCIERR_HARDWARE
;
1087 else if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_PAUSE_AUDIO
, NULL
, 0, NULL
, 0, &br
, NULL
))
1088 return MCIERR_HARDWARE
;
1090 EnterCriticalSection(&wmcda
->cs
);
1091 if (wmcda
->hCallback
) {
1092 mciDriverNotify(wmcda
->hCallback
, wmcda
->wNotifyDeviceID
, MCI_NOTIFY_SUPERSEDED
);
1093 wmcda
->hCallback
= NULL
;
1095 LeaveCriticalSection(&wmcda
->cs
);
1097 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
1098 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
1099 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
1100 wmcda
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
1105 /**************************************************************************
1106 * MCICDA_Resume [internal]
1108 static DWORD
MCICDA_Resume(UINT wDevID
, DWORD dwFlags
, LPMCI_GENERIC_PARMS lpParms
)
1110 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
1113 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1115 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1117 if (wmcda
->hThread
!= 0) {
1118 /* Don't restart if the playLoop thread has already stopped */
1119 if(WaitForSingleObject(wmcda
->stopEvent
, 0) != WAIT_OBJECT_0
&&
1120 FAILED(IDirectSoundBuffer_Play(wmcda
->dsBuf
, 0, 0, DSBPLAY_LOOPING
)))
1121 return MCIERR_HARDWARE
;
1123 else if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_RESUME_AUDIO
, NULL
, 0, NULL
, 0, &br
, NULL
))
1124 return MCIERR_HARDWARE
;
1126 if (lpParms
&& (dwFlags
& MCI_NOTIFY
)) {
1127 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
1128 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
1129 wmcda
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
1134 /**************************************************************************
1135 * MCICDA_Seek [internal]
1137 static DWORD
MCICDA_Seek(UINT wDevID
, DWORD dwFlags
, LPMCI_SEEK_PARMS lpParms
)
1140 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
1141 CDROM_SEEK_AUDIO_MSF seek
;
1145 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1147 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1148 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1150 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
1151 &toc
, sizeof(toc
), &br
, NULL
)) {
1152 WARN("error reading TOC !\n");
1153 return MCICDA_GetError(wmcda
);
1155 switch (dwFlags
& ~(MCI_NOTIFY
|MCI_WAIT
)) {
1156 case MCI_SEEK_TO_START
:
1157 TRACE("Seeking to start\n");
1158 at
= FRAME_OF_TOC(toc
,toc
.FirstTrack
);
1159 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &at
)) )
1162 case MCI_SEEK_TO_END
:
1163 TRACE("Seeking to end\n");
1164 at
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1) - 1;
1165 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &at
)) )
1169 TRACE("Seeking to %u\n", lpParms
->dwTo
);
1170 at
= MCICDA_CalcFrame(wmcda
, lpParms
->dwTo
);
1171 if ( (ret
=MCICDA_SkipDataTracks(wmcda
, &at
)) )
1175 TRACE("Unknown seek action %08lX\n",
1176 (dwFlags
& ~(MCI_NOTIFY
|MCI_WAIT
)));
1177 return MCIERR_UNSUPPORTED_FUNCTION
;
1180 if (wmcda
->hThread
!= 0) {
1181 EnterCriticalSection(&wmcda
->cs
);
1182 wmcda
->start
= at
- FRAME_OF_TOC(toc
, toc
.FirstTrack
);
1183 /* Flush remaining data, or just let it play into the new data? */
1184 LeaveCriticalSection(&wmcda
->cs
);
1187 seek
.M
= at
/ CDFRAMES_PERMIN
;
1188 seek
.S
= (at
/ CDFRAMES_PERSEC
) % 60;
1189 seek
.F
= at
% CDFRAMES_PERSEC
;
1190 if (!DeviceIoControl(wmcda
->handle
, IOCTL_CDROM_SEEK_AUDIO_MSF
, &seek
, sizeof(seek
),
1191 NULL
, 0, &br
, NULL
))
1192 return MCIERR_HARDWARE
;
1195 if (dwFlags
& MCI_NOTIFY
) {
1196 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n", lpParms
->dwCallback
);
1197 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
1198 wmcda
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
1203 /**************************************************************************
1204 * MCICDA_SetDoor [internal]
1206 static DWORD
MCICDA_SetDoor(UINT wDevID
, BOOL open
)
1208 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
1211 TRACE("(%04x, %s) !\n", wDevID
, (open
) ? "OPEN" : "CLOSE");
1213 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1215 if (!DeviceIoControl(wmcda
->handle
,
1216 (open
) ? IOCTL_STORAGE_EJECT_MEDIA
: IOCTL_STORAGE_LOAD_MEDIA
,
1217 NULL
, 0, NULL
, 0, &br
, NULL
))
1218 return MCIERR_HARDWARE
;
1223 /**************************************************************************
1224 * MCICDA_Set [internal]
1226 static DWORD
MCICDA_Set(UINT wDevID
, DWORD dwFlags
, LPMCI_SET_PARMS lpParms
)
1228 WINE_MCICDAUDIO
* wmcda
= MCICDA_GetOpenDrv(wDevID
);
1230 TRACE("(%04X, %08X, %p);\n", wDevID
, dwFlags
, lpParms
);
1232 if (wmcda
== NULL
) return MCIERR_INVALID_DEVICE_ID
;
1234 if (dwFlags
& MCI_SET_DOOR_OPEN
) {
1235 MCICDA_SetDoor(wDevID
, TRUE
);
1237 if (dwFlags
& MCI_SET_DOOR_CLOSED
) {
1238 MCICDA_SetDoor(wDevID
, FALSE
);
1241 /* only functions which require valid lpParms below this line ! */
1242 if (lpParms
== NULL
) return MCIERR_NULL_PARAMETER_BLOCK
;
1244 TRACE("dwTimeFormat=%08lX\n", lpParms->dwTimeFormat);
1245 TRACE("dwAudio=%08lX\n", lpParms->dwAudio);
1247 if (dwFlags
& MCI_SET_TIME_FORMAT
) {
1248 switch (lpParms
->dwTimeFormat
) {
1249 case MCI_FORMAT_MILLISECONDS
:
1250 TRACE("MCI_FORMAT_MILLISECONDS !\n");
1252 case MCI_FORMAT_MSF
:
1253 TRACE("MCI_FORMAT_MSF !\n");
1255 case MCI_FORMAT_TMSF
:
1256 TRACE("MCI_FORMAT_TMSF !\n");
1259 WARN("bad time format !\n");
1260 return MCIERR_BAD_TIME_FORMAT
;
1262 wmcda
->dwTimeFormat
= lpParms
->dwTimeFormat
;
1264 if (dwFlags
& MCI_SET_VIDEO
) return MCIERR_UNSUPPORTED_FUNCTION
;
1265 if (dwFlags
& MCI_SET_ON
) return MCIERR_UNSUPPORTED_FUNCTION
;
1266 if (dwFlags
& MCI_SET_OFF
) return MCIERR_UNSUPPORTED_FUNCTION
;
1267 if (dwFlags
& MCI_NOTIFY
) {
1268 TRACE("MCI_NOTIFY_SUCCESSFUL %08lX !\n",
1269 lpParms
->dwCallback
);
1270 mciDriverNotify(HWND_32(LOWORD(lpParms
->dwCallback
)),
1271 wmcda
->wNotifyDeviceID
, MCI_NOTIFY_SUCCESSFUL
);
1276 /**************************************************************************
1277 * DriverProc (MCICDA.@)
1279 LRESULT CALLBACK
MCICDA_DriverProc(DWORD_PTR dwDevID
, HDRVR hDriv
, UINT wMsg
,
1280 LPARAM dwParam1
, LPARAM dwParam2
)
1283 case DRV_LOAD
: return 1;
1284 case DRV_FREE
: return 1;
1285 case DRV_OPEN
: return MCICDA_drvOpen((LPCWSTR
)dwParam1
, (LPMCI_OPEN_DRIVER_PARMSW
)dwParam2
);
1286 case DRV_CLOSE
: return MCICDA_drvClose(dwDevID
);
1287 case DRV_ENABLE
: return 1;
1288 case DRV_DISABLE
: return 1;
1289 case DRV_QUERYCONFIGURE
: return 1;
1290 case DRV_CONFIGURE
: MessageBoxA(0, "MCI audio CD driver !", "Wine Driver", MB_OK
); return 1;
1291 case DRV_INSTALL
: return DRVCNF_RESTART
;
1292 case DRV_REMOVE
: return DRVCNF_RESTART
;
1295 if (dwDevID
== 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION
;
1298 case MCI_OPEN_DRIVER
: return MCICDA_Open(dwDevID
, dwParam1
, (LPMCI_OPEN_PARMSW
)dwParam2
);
1299 case MCI_CLOSE_DRIVER
: return MCICDA_Close(dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
)dwParam2
);
1300 case MCI_GETDEVCAPS
: return MCICDA_GetDevCaps(dwDevID
, dwParam1
, (LPMCI_GETDEVCAPS_PARMS
)dwParam2
);
1301 case MCI_INFO
: return MCICDA_Info(dwDevID
, dwParam1
, (LPMCI_INFO_PARMSW
)dwParam2
);
1302 case MCI_STATUS
: return MCICDA_Status(dwDevID
, dwParam1
, (LPMCI_STATUS_PARMS
)dwParam2
);
1303 case MCI_SET
: return MCICDA_Set(dwDevID
, dwParam1
, (LPMCI_SET_PARMS
)dwParam2
);
1304 case MCI_PLAY
: return MCICDA_Play(dwDevID
, dwParam1
, (LPMCI_PLAY_PARMS
)dwParam2
);
1305 case MCI_STOP
: return MCICDA_Stop(dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
)dwParam2
);
1306 case MCI_PAUSE
: return MCICDA_Pause(dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
)dwParam2
);
1307 case MCI_RESUME
: return MCICDA_Resume(dwDevID
, dwParam1
, (LPMCI_GENERIC_PARMS
)dwParam2
);
1308 case MCI_SEEK
: return MCICDA_Seek(dwDevID
, dwParam1
, (LPMCI_SEEK_PARMS
)dwParam2
);
1309 /* commands that should report an error as they are not supported in
1310 * the native version */
1311 case MCI_SET_DOOR_CLOSED
:
1312 case MCI_SET_DOOR_OPEN
:
1329 TRACE("Unsupported command [0x%x]\n", wMsg
);
1333 ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1336 TRACE("Sending msg [0x%x] to default driver proc\n", wMsg
);
1337 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1339 return MCIERR_UNRECOGNIZED_COMMAND
;
1342 /*-----------------------------------------------------------------------*/