|
|
/*******************************Module*Header*********************************\
* Module Name: mcicda.c * * Media Control Architecture Redbook CD Audio Driver * * Created: 4/25/90 * Author: DLL (DavidLe) * * History: * DavidLe - Based on MCI Pioneer Videodisc Driver * MikeRo 12/90 - 1/91 * RobinSp 10th March 1992 - Move to Windows NT * * Copyright (c) 1990-1999 Microsoft Corporation * \****************************************************************************/ #include <windows.h>
#include <mmsystem.h>
#include <mmddk.h>
#include "mcicda.h"
#include "cda.h"
#include "cdio.h"
#define CHECK_MSF
#define MCICDA_BAD_TIME 0xFFFFFFFF
HANDLE hInstance;
UINT_PTR wTimerID; int nWaitingDrives;
DRIVEDATA DriveTable[MCIRBOOK_MAX_DRIVES];
// MBR This
void CALLBACK TimerProc ( HWND hwnd, UINT uMessage, UINT uTimer, DWORD dwParam) { DID i; int wStatus;
for (i = 0; i < MCIRBOOK_MAX_DRIVES; ++i) {
EnterCrit( CdInfo[i].DeviceCritSec );
if (DriveTable[i].bActiveTimer) {
// MBR can other conditions beside successful completion of the
// play cause the != DISC_PLAYING?
if ((wStatus = CDA_drive_status (i)) != DISC_PLAYING) {
if (--nWaitingDrives <= 0) KillTimer (NULL, uTimer); DriveTable[i].dwPlayTo = MCICDA_BAD_TIME; DriveTable[i].bActiveTimer = FALSE;
switch (wStatus) { case DISC_PLAYING: case DISC_PAUSED: case DISC_READY: wStatus = MCI_NOTIFY_SUCCESSFUL; break; default: wStatus = MCI_NOTIFY_FAILURE; break; } mciDriverNotify (DriveTable[i].hCallback, DriveTable[i].wDeviceID, wStatus); } }
LeaveCrit( CdInfo[i].DeviceCritSec ); } }
/*****************************************************************************
@doc INTERNAL MCICDA
@api UINT | notify | This function handles the notify for all mci commands.
@parm DID | didDrive | Drive identifier
@parm WORD | wDeviceID | Calling device ID
@parm BOOL | wStartTimer | A boolean indicating that a timer is to be started
@parm UINT | wFlag | The flag to be passed by mciDriverNotify
@parm LPMCI_GENERIC_PARMS | lpParms | For direct callback
*****************************************************************************/ UINT notify ( DID didDrive, MCIDEVICEID wDeviceID, BOOL wStartTimer, UINT wFlag, LPMCI_GENERIC_PARMS lpParms) {
if (DriveTable[didDrive].bActiveTimer) { mciDriverNotify (DriveTable[didDrive].hCallback, wDeviceID, MCI_NOTIFY_SUPERSEDED); if (--nWaitingDrives <= 0) KillTimer (NULL, wTimerID); DriveTable[didDrive].bActiveTimer = FALSE; }
if (!wStartTimer) mciDriverNotify ((HWND)lpParms->dwCallback, wDeviceID, wFlag); else { if (!DriveTable[didDrive].bActiveTimer && nWaitingDrives++ == 0) {
// MBR every 1/10 of a sec. Should this be a parameter?
wTimerID = SetTimer (NULL, 1, 100, (TIMERPROC)TimerProc); if (wTimerID == 0) return MCICDAERR_NO_TIMERS; }
DriveTable[didDrive].wDeviceID = wDeviceID; DriveTable[didDrive].bActiveTimer = TRUE; DriveTable[didDrive].hCallback = (HANDLE)lpParms->dwCallback; } return 0; }
/*****************************************************************************
@doc INTERNAL MCICDA
@api void | abort_notify |
@parm PINSTDATA | pInst | application instance data
@rdesc
@comm
*****************************************************************************/ void abort_notify (PINSTDATA pInst) { DID didDrive = pInst->uDevice; if (DriveTable[didDrive].bActiveTimer) { mciDriverNotify (DriveTable[didDrive].hCallback, pInst->uMCIDeviceID, MCI_NOTIFY_ABORTED); // Kill timer if appropriate
if (--nWaitingDrives == 0) KillTimer (NULL, wTimerID); DriveTable[didDrive].dwPlayTo = MCICDA_BAD_TIME; DriveTable[didDrive].bActiveTimer = FALSE; } }
/*
Return TRUE if the drive is in a playable state */
UINT disc_ready (DID didDrive) { // The disk is ready if we can read its TOC (note the
// kernel driver works out if the TOC really needs reading
if (CDA_disc_ready(didDrive)) {
if (CDA_num_tracks(didDrive)) { return TRUE; } else { CDA_reset_drive(didDrive); return FALSE; }
} else return FALSE; }
/*
* @func redbook | flip3 | Put minute/second/frame values in different order * * @parm redbook | rbIn | Current position as track|minute|second|frame * * @rdesc (redbook)0|frame|second|minute */
redbook flip3 (redbook rbIn) { return MAKERED(MCI_MSF_MINUTE(rbIn), MCI_MSF_SECOND(rbIn), MCI_MSF_FRAME(rbIn)); }
/*
* @func redbook | flip4 | Put track/minute/second/frame values in different order * * @parm redbook | rbIn | Current position as track|minute|second|frame * * @rdesc (redbook)frame|second|minute|track */
redbook flip4 (redbook rbIn) { redbook rbOut;
LPSTR lpOut = (LPSTR)&rbOut, lpIn = (LPSTR)&rbIn;
lpOut[0] = lpIn[3]; lpOut[1] = lpIn[2]; lpOut[2] = lpIn[1]; lpOut[3] = lpIn[0];
return rbOut; }
// MBR Return the absolute redbook time of track sTrack, rbTime into track
/*****************************************************************************
@doc INTERNAL MCICDA
@api redbook | track_time | Return the absolute redbook time of track sTrack, rbTime into track
@parm DID | didDrive |
@parm int | sTrack |
@parm redbook | rbTime |
@rdesc
@comm *****************************************************************************/ redbook track_time (DID didDrive, int sTrack, redbook rbTime) { redbook rbTemp;
rbTemp = CDA_track_start (didDrive, sTrack); if (rbTemp == INVALID_TRACK) return rbTemp; return redadd (rbTime, rbTemp); }
redbook miltored(DWORD dwMill) { unsigned char m, s, f; long r1, r2;
r1 = dwMill % 60000;
m = (unsigned char) ((dwMill - r1) / 60000);
r2 = r1 % 1000;
s = (unsigned char) ((r1 - r2) / 1000);
f = (unsigned char) ((r2 * 75) / 1000);
return MAKERED(m, s, f); }
DWORD redtomil(redbook rbRed) { // Adding an extra one ms to prevent rounding errors at start
return (DWORD)REDMINUTE(rbRed) * 60000 + (DWORD)REDSECOND(rbRed) * 1000 + ((DWORD)REDFRAME(rbRed) * 1000) / 75 + 1; }
#ifdef AUDIOPHILE
DWORD NEAR PASCAL mcSeek( PINSTDATA pInst, DWORD dwFlags, LPMCI_SEEK_PARMS lpSeek );
DWORD NEAR PASCAL GetAudioPhileInfo(LPCTSTR lpCDAFileName) { OFSTRUCT of; RIFFCDA cda; HFILE hf;
//
// open the file and read the CDA info.
//
if ((hf = _lopen (lpCDAFileName)) == HFILE_ERROR) return 0; _lread(hf, &cda, sizeof(cda)); _lclose(hf);
if (cda.dwRIFF != RIFF_RIFF || cda.dwCDDA != RIFF_CDDA) { return 0; } return MCI_MAKE_TMSF(cda.wTrack,0,0,0); } #endif
DWORD mcOpen ( PINSTDATA pInst, DWORD dwFlags, LPMCI_OPEN_PARMS lpOpen) { DID didDrive = (DID)pInst->uDevice; DID didOld = (DID)pInst->uDevice; UCHAR Volume; DWORD dwTempVol; int nUseCount;
/* Instance Initialization */ pInst->dwTimeFormat = MCI_FORMAT_MSF;
/* If an ELEMENT_ID is specified, this could be a drive letter */ if (dwFlags & (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID)) { if ((dwFlags & (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID)) == (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID)) { dprintf2(("mcOpen, (%08lX), Flags not compatible", (DWORD)didDrive)); return MCIERR_FLAGS_NOT_COMPATIBLE; }
//
// Find the device corresponding to this name
//
if (COMMAND_SUCCESSFUL != CDA_get_drive(lpOpen->lpstrElementName, &didDrive)) { dprintf2(("mcOpen, (%08lX), Failed to get corresponding device", (DWORD)didDrive)); return MCIERR_INVALID_FILE; }
dprintf2(("mcOpen, changing from drive (%08lx) to drive (%08lX)", (DWORD)(pInst->uDevice), (DWORD)didDrive)); pInst->uDevice = didDrive; }
/* Device Initialization */ nUseCount = DriveTable[didDrive].nUseCount; if (nUseCount > 0) { // This drive is already open as another MCI device
if (dwFlags & MCI_OPEN_SHAREABLE && DriveTable[didDrive].bShareable) { // Shareable was specified so just increment the use count
nUseCount++; dprintf2(("mcOpen, drive (%08lx), Incrementing UseCount, now = %ld", (DWORD)didDrive, (DWORD)nUseCount)); } else { dprintf2(("mcOpen, drive (%08lx), tryed to share without specifing MCI_OPEN_SHAREABLE", (DWORD)didDrive)); return MCIERR_MUST_USE_SHAREABLE; } } else { nUseCount = 1; }
if (!CDA_open(didDrive)) { dprintf2(("mcOpen, drive (%08lx), failed to open, UseCount = %ld", (DWORD)didDrive, (DWORD)nUseCount)); return MCIERR_DEVICE_OPEN; }
//
// Don't call disc_ready here because it will read the table of
// contents and on some drivers this will terminate any play
// unnecessarily
//
if (CDA_drive_status (didDrive) == DISC_PLAYING) DriveTable[didDrive].bDiscPlayed = TRUE; else DriveTable[didDrive].bDiscPlayed = FALSE; DriveTable[didDrive].bActiveTimer = FALSE; DriveTable[didDrive].dwPlayTo = MCICDA_BAD_TIME; DriveTable[didDrive].bShareable = (dwFlags & MCI_OPEN_SHAREABLE) != 0; DriveTable[didDrive].nUseCount = nUseCount;
dprintf2(("mcOpen, drive (%08lx), Setting UseCount = %ld", (DWORD)didDrive, (DWORD)nUseCount));
//dstewart: fix for when vol in registry is > 8 bits
dwTempVol = CDAudio_GetUnitVolume(didDrive); if (dwTempVol > 0xFF) { dwTempVol = 0xFF; } Volume = (UCHAR)dwTempVol;
CDA_set_audio_volume_all (didDrive, Volume);
#ifdef AUDIOPHILE
/*
* AudioPhile track information handler. * * The new CDROM file system for Windows 4.0 produces files that describe * CDAudio tracks. If a user wants to play a track, she should be able * to double click on the track. So, we add open element support here * and add an mplayer association s.t. the file may be read and the disc * played back. We need to reject the Phile if a CDROM of this ID can't * be found. A message box should be displayed if the disc is incorrect. * Repercussions of this feature are that we need to simulate a disc in * a data structure. */
if (dwFlags & (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID)) { MCI_SEEK_PARMS Seek;
pInst->dwTimeFormat = MCI_FORMAT_TMSF;
Seek.dwTo = GetAudioPhileInfo(lpOpen->lpstrElementName); if (Seek.dwTo != 0L) mcSeek(pInst, MCI_TO, (LPMCI_SEEK_PARMS)&Seek); } #endif
return 0; }
#define MSF_BITS ((redbook) 0x00FFFFFF)
/*****************************************************************************
@doc INTERNAL MCICDA
@api redbook | convert_time | Take a DWORD time value and convert from current time format into redbook.
@parm PINSTDATA | pInst | Pointer to application instance data
@parm DWORD | dwTimeIn |
@rdesc Return MCICDA_BAD_TIME if out of range.
@comm *****************************************************************************/ redbook convert_time( PINSTDATA pInst, DWORD dwTimeIn ) { DID didDrive = (DID)pInst->uDevice; redbook rbTime; short nTrack;
switch (pInst->dwTimeFormat) { case MCI_FORMAT_MILLISECONDS: rbTime = miltored (dwTimeIn); return rbTime;
case MCI_FORMAT_MSF: dprintf3(("Time IN: %lu",dwTimeIn)); rbTime = flip3 (dwTimeIn); dprintf3(("Time OUT: %d:%d:%d:%d", REDTRACK(rbTime), REDMINUTE(rbTime),REDSECOND(rbTime), REDFRAME(rbTime)));
break;
case MCI_FORMAT_TMSF: nTrack = (short)(dwTimeIn & 0xFF); if (nTrack > CDA_num_tracks( didDrive)) return MCICDA_BAD_TIME; rbTime = track_time (didDrive, nTrack, flip3 (dwTimeIn >> 8)); if (rbTime == INVALID_TRACK) return MCICDA_BAD_TIME; break; }
#ifdef CHECK_MSF
if ((REDFRAME(rbTime)>74) || (REDMINUTE(rbTime)>99) || (REDSECOND(rbTime)>59)) return MCICDA_BAD_TIME; #endif
return rbTime; }
/*****************************************************************************
@doc INTERNAL MCICDA
@api DWORD | seek | Process the MCI_SEEK command
@parm PINSTDATA | pInst | Pointer to application instance data
@parm DWORD | dwFlags |
@parm LPMCI_SEEK_PARMS | lpSeek |
@rdesc
@comm *****************************************************************************/ DWORD mcSeek( PINSTDATA pInst, DWORD dwFlags, LPMCI_SEEK_PARMS lpSeek ) { DID didDrive = pInst->uDevice;
redbook rbTime = 0; LPSTR lpTime = (LPSTR) &rbTime; redbook rbStart; redbook rbEnd; BOOL fForceAudio;
dprintf3(("Seek, drive %d TO %8x", didDrive, lpSeek->dwTo)); abort_notify (pInst);
if ( !disc_ready (didDrive)) return MCIERR_HARDWARE;
if ((rbStart = CDA_track_start( didDrive, 1)) == INVALID_TRACK) return MCIERR_HARDWARE;
rbStart &= MSF_BITS;
if ((rbEnd = CDA_disc_end( didDrive)) == INVALID_TRACK) return MCIERR_HARDWARE;
rbEnd &= MSF_BITS;
// Check only one positioning command is given.
// First isolate the bits we want
// Then subtract 1. This removes the least significant bit, and puts
// ones in any lower bit positions. Leaves other bits untouched.
// If any bits are left on, more than one of TO, START or END was given
// Note: if NO flags are given this ends up ANDING 0 with -1 == 0
// which is OK.
#define SEEK_BITS (dwFlags & (MCI_TO | MCI_SEEK_TO_START | MCI_SEEK_TO_END))
#define CHECK_FLAGS (((SEEK_BITS)-1) & (SEEK_BITS))
if (CHECK_FLAGS) { return MCIERR_FLAGS_NOT_COMPATIBLE; }
if (dwFlags & MCI_TO) { // When the above test is reviewed and proven to pick out
// incompatible flags delete these lines.
// Note: we detect more incompatible cases than Win 16 - this
// is deliberate and fixes a Win 16 bug. CurtisP has seen this code.
//if (dwFlags & (MCI_SEEK_TO_START | MCI_SEEK_TO_END))
// return MCIERR_FLAGS_NOT_COMPATIBLE;
if ((rbTime = convert_time (pInst, lpSeek->dwTo)) == MCICDA_BAD_TIME) return MCIERR_OUTOFRANGE;
// if seek pos is before valid audio return an error
if ( rbTime < rbStart) return MCIERR_OUTOFRANGE;
// similarly, if seek pos is past end of disk return an error
else if (rbTime > rbEnd) return MCIERR_OUTOFRANGE;
fForceAudio = FALSE;
} else if (dwFlags & MCI_SEEK_TO_START) {
rbTime = rbStart; fForceAudio = TRUE; // We want the first audio track
} else if (dwFlags & MCI_SEEK_TO_END) {
rbTime = rbEnd; fForceAudio = TRUE; // We want the last audio track
} else { return MCIERR_MISSING_PARAMETER; }
// send seek command to driver
if (CDA_seek_audio (didDrive, rbTime, fForceAudio) != COMMAND_SUCCESSFUL) return MCIERR_HARDWARE; if (CDA_pause_audio (didDrive) != COMMAND_SUCCESSFUL) return MCIERR_HARDWARE;
DriveTable[didDrive].bDiscPlayed = TRUE;
return 0; }
/*****************************************************************************
@doc INTERNAL MCICDA
@api BOOL | wait |
@parm DWORD | dwFlags |
@parm PINSTDATA | pInst | Pointer to application instance data
@rdesc Return TRUE if BREAK was pressed
@comm If the wait flag is set then wait until the device is no longer playing *****************************************************************************/ BOOL wait ( DWORD dwFlags, PINSTDATA pInst ) { DID didDrive = pInst->uDevice; MCIDEVICEID wDeviceID = pInst->uMCIDeviceID;
if (dwFlags & MCI_WAIT) { //Note: jyg This is interesting. I've noticed that some drives do give
// sporadic errors. Thus this retry stuff. 5X is enough to
// determine true failure.
int status, retry=0; retry: while ((status = CDA_drive_status (didDrive)) == DISC_PLAYING) {
LeaveCrit( CdInfo[didDrive].DeviceCritSec );
if (mciDriverYield (wDeviceID) != 0) { EnterCrit( CdInfo[didDrive].DeviceCritSec ); return TRUE; }
Sleep(50); EnterCrit( CdInfo[didDrive].DeviceCritSec ); }
if (status == DISC_NOT_READY && retry++ < 5) goto retry; } return FALSE; }
/*****************************************************************************
@doc INTERNAL MCICDA
@api DWORD | play | Process the MCI_PLAY command
@parm PINSTDATA | pInst | Pointer to application instance data
@parm DWORD | dwFlags |
@parm LPMCI_PLAY_PARMS | lpPlay |
@parm BOOL FAR * | bBreak |
@rdesc
@comm *****************************************************************************/ DWORD mcPlay( PINSTDATA pInst, DWORD dwFlags, LPMCI_PLAY_PARMS lpPlay, BOOL FAR * bBreak ) { DID didDrive = pInst->uDevice;
redbook rbFrom, rbTo; redbook dStart, dEnd; BOOL bAbort = FALSE;
if (!disc_ready (didDrive)) // MBR could return more specific error
return MCIERR_HARDWARE;
// do we have both from and to parameters?
// If so then do a "seek" instead
if ((dwFlags & (MCI_FROM | MCI_TO)) == (MCI_FROM | MCI_TO)) if (lpPlay->dwTo == lpPlay->dwFrom) // Convert a 'play x to x' into 'seek to x'
{ MCI_SEEK_PARMS Seek;
Seek.dwTo = lpPlay->dwFrom; Seek.dwCallback = lpPlay->dwCallback; return mcSeek(pInst, dwFlags, (LPMCI_SEEK_PARMS)&Seek); }
// mask is to ignore track number in the upper byte
// which appears at some times
dStart = CDA_track_start( didDrive, 1) & MSF_BITS; dEnd = CDA_disc_end( didDrive) & MSF_BITS;
if (dwFlags & MCI_TO) { if ((rbTo = convert_time (pInst, lpPlay->dwTo)) == MCICDA_BAD_TIME) return MCIERR_OUTOFRANGE; } else rbTo = dEnd;
if (dwFlags & MCI_FROM) { if ((rbFrom = convert_time (pInst, lpPlay->dwFrom)) == MCICDA_BAD_TIME) return MCIERR_OUTOFRANGE;
} else // no FROM
{ // If the disk has never played the current position is indeterminate so
// we must start from the beginning
if (!DriveTable[didDrive].bDiscPlayed) { // Initial position is at the beginning of track 1
rbFrom = track_time (didDrive, (int)1, (redbook)0); if (rbFrom == INVALID_TRACK) return MCIERR_HARDWARE; } else if ((!(dwFlags & MCI_TO) || rbTo == DriveTable[didDrive].dwPlayTo) && CDA_drive_status (didDrive) == DISC_PLAYING) // Disc is playing and no (or redundent) "to" position was
// specified so do nothng
goto exit_fn; else { CDA_time_info (didDrive, NULL, &rbFrom); // Current position in track 0 means play starting from track 1
if (REDTRACK(rbFrom) == 0) { rbFrom = track_time (didDrive, (int)1, (redbook)0); if (rbFrom == INVALID_TRACK) return MCIERR_HARDWARE; } rbFrom &= MSF_BITS; // Some drives (SONY) will return an illegal position
if (rbFrom < dStart) rbFrom = dStart; } }
rbFrom &= MSF_BITS; rbTo &= MSF_BITS;
if (dwFlags & MCI_TO) { if (rbFrom > rbTo || rbTo > dEnd) return MCIERR_OUTOFRANGE; } else { rbTo = dEnd; }
// if From is before audio start return an error
if ( rbFrom < dStart) return MCIERR_OUTOFRANGE;
if (dwFlags & MCI_FROM) { // Try a seek - don't care if it works (!)
CDA_seek_audio(didDrive, rbFrom, TRUE); }
// send play command to driver
if (CDA_play_audio(didDrive, rbFrom, rbTo) != COMMAND_SUCCESSFUL) return MCIERR_HARDWARE; // values should be vaild so err is hard
DriveTable[didDrive].bDiscPlayed = TRUE;
exit_fn:; // Abort if either from or (a new) to position is specified
if (dwFlags & MCI_FROM || rbTo != DriveTable[didDrive].dwPlayTo) abort_notify (pInst);
*bBreak = wait(dwFlags, pInst);
DriveTable[didDrive].dwPlayTo = rbTo;
return 0; }
/*****************************************************************************
@doc INTERNAL MCICDA
@api DWORD | mcGetDevCaps | Process the MCI_GETDEVCAPS command
@parm PINSTDATA | pInst | Pointer to application data instance
@parm DWORD | dwFlags |
@parm LPMCI_GETDEVCAPS_PARMS | lpCaps |
@rdesc
@comm *****************************************************************************/ DWORD mcGetDevCaps( PINSTDATA pInst, DWORD dwFlags, LPMCI_GETDEVCAPS_PARMS lpCaps ) { DWORD dwReturn = 0;
if (!(dwFlags & MCI_GETDEVCAPS_ITEM)) return MCIERR_MISSING_PARAMETER;
switch (lpCaps->dwItem) { case MCI_GETDEVCAPS_CAN_RECORD: case MCI_GETDEVCAPS_CAN_SAVE: case MCI_GETDEVCAPS_HAS_VIDEO: case MCI_GETDEVCAPS_USES_FILES: case MCI_GETDEVCAPS_COMPOUND_DEVICE: lpCaps->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); dwReturn = MCI_RESOURCE_RETURNED; break; case MCI_GETDEVCAPS_HAS_AUDIO: case MCI_GETDEVCAPS_CAN_EJECT: // mbr - bogus...
case MCI_GETDEVCAPS_CAN_PLAY: lpCaps->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); dwReturn = MCI_RESOURCE_RETURNED; break; case MCI_GETDEVCAPS_DEVICE_TYPE: lpCaps->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_CD_AUDIO, MCI_DEVTYPE_CD_AUDIO); dwReturn = MCI_RESOURCE_RETURNED; break; default: dwReturn = MCIERR_UNSUPPORTED_FUNCTION; break; }
return dwReturn; }
/*****************************************************************************
@doc INTERNAL MCICDA
@api DWORD | mcStatus | Process the MCI_STATUS command
@parm PINSTDATA | pInst | Pointer to application instance data
@parm DWORD | dwFlags |
@parm LPMCI_STATUS_PARMS | lpStatus |
@rdesc
@comm *****************************************************************************/ DWORD mcStatus ( PINSTDATA pInst, DWORD dwFlags, LPMCI_STATUS_PARMS lpStatus) { DID didDrive = (DID)pInst->uDevice; DWORD dwReturn = 0;
if (!(dwFlags & MCI_STATUS_ITEM)) return MCIERR_MISSING_PARAMETER;
switch (lpStatus->dwItem) { int n;
case MCI_STATUS_MEDIA_PRESENT: if (CDA_traystate(didDrive) != TRAY_OPEN && CDA_disc_ready(didDrive)) lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); else lpStatus->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); dwReturn = MCI_RESOURCE_RETURNED; break; case MCI_STATUS_READY: switch (CDA_drive_status (didDrive)) { case DISC_PLAYING: case DISC_PAUSED: case DISC_READY: lpStatus->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); break; default: lpStatus->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); break; } dwReturn = MCI_RESOURCE_RETURNED; break; case MCI_STATUS_MODE: { switch (CDA_drive_status (didDrive)) { case DISC_PLAYING: n = MCI_MODE_PLAY; break; case DISC_PAUSED: n = MCI_MODE_STOP; // HACK HACK!
break; case DISC_READY: n = MCI_MODE_STOP; break; default: if (CDA_traystate (didDrive) == TRAY_OPEN) n = MCI_MODE_OPEN; else n = MCI_MODE_NOT_READY; break; } lpStatus->dwReturn = (DWORD)MAKEMCIRESOURCE(n, n); dwReturn = MCI_RESOURCE_RETURNED; break; } case MCI_STATUS_TIME_FORMAT: n = (WORD)pInst->dwTimeFormat; lpStatus->dwReturn = (DWORD)MAKEMCIRESOURCE(n,n + MCI_FORMAT_RETURN_BASE); dwReturn = MCI_RESOURCE_RETURNED; break; case MCI_STATUS_POSITION: { redbook tracktime, disctime;
if (dwFlags & MCI_TRACK) { int n;
if (dwFlags & MCI_STATUS_START) return MCIERR_FLAGS_NOT_COMPATIBLE;
if (!disc_ready(didDrive)) return MCIERR_HARDWARE; if ((n = CDA_num_tracks (didDrive)) == 0) return MCIERR_HARDWARE; if (!lpStatus->dwTrack || lpStatus->dwTrack > (DWORD)n) return MCIERR_OUTOFRANGE; lpStatus->dwReturn = CDA_track_start (didDrive, (short)lpStatus->dwTrack); switch (pInst->dwTimeFormat) { case MCI_FORMAT_MILLISECONDS: lpStatus->dwReturn = redtomil ((redbook)lpStatus->dwReturn); dwReturn = 0; break; case MCI_FORMAT_TMSF: lpStatus->dwReturn = lpStatus->dwTrack; dwReturn = MCI_COLONIZED4_RETURN; break; case MCI_FORMAT_MSF: lpStatus->dwReturn = flip3 ((redbook)lpStatus->dwReturn); dwReturn = MCI_COLONIZED3_RETURN; break; } } else if (dwFlags & MCI_STATUS_START) { if (!disc_ready(didDrive)) return MCIERR_HARDWARE; if ((n = CDA_num_tracks (didDrive)) == 0) return MCIERR_HARDWARE; lpStatus->dwReturn = CDA_track_start (didDrive, 1); switch (pInst->dwTimeFormat) { case MCI_FORMAT_MILLISECONDS: lpStatus->dwReturn = redtomil ((redbook)lpStatus->dwReturn); dwReturn = 0; break; case MCI_FORMAT_TMSF: lpStatus->dwReturn = 1; dwReturn = MCI_COLONIZED4_RETURN; break; case MCI_FORMAT_MSF: lpStatus->dwReturn = flip3 ((redbook)lpStatus->dwReturn); dwReturn = MCI_COLONIZED3_RETURN; break; } } else { if (!DriveTable[didDrive].bDiscPlayed) { tracktime = REDTH(0, 1); if (!disc_ready(didDrive)) return MCIERR_HARDWARE; disctime = CDA_track_start( didDrive, 1); } else if (CDA_time_info(didDrive, &tracktime, &disctime) != COMMAND_SUCCESSFUL) return MCIERR_HARDWARE;
if (REDTRACK(tracktime) == 0) { tracktime = (redbook)0; disctime = (redbook)0; } switch (pInst->dwTimeFormat) { case MCI_FORMAT_MILLISECONDS: lpStatus->dwReturn = redtomil (disctime); dwReturn = 0; break; case MCI_FORMAT_MSF: lpStatus->dwReturn = flip3(disctime); dwReturn = MCI_COLONIZED3_RETURN; break; case MCI_FORMAT_TMSF: lpStatus->dwReturn = flip4 (tracktime); dwReturn = MCI_COLONIZED4_RETURN; break; } } break; } case MCI_STATUS_LENGTH: if (!disc_ready(didDrive)) return MCIERR_HARDWARE;
if (dwFlags & MCI_TRACK) { lpStatus->dwReturn = CDA_track_length (didDrive, (short)lpStatus->dwTrack); if (lpStatus->dwReturn == INVALID_TRACK) return MCIERR_OUTOFRANGE; switch (pInst->dwTimeFormat) { case MCI_FORMAT_MILLISECONDS: lpStatus->dwReturn = redtomil ((redbook)lpStatus->dwReturn); dwReturn = 0; break; case MCI_FORMAT_MSF: case MCI_FORMAT_TMSF: lpStatus->dwReturn = flip3((redbook)lpStatus->dwReturn); dwReturn = MCI_COLONIZED3_RETURN; break; } } else { // Subtract one to match SEEK_TO_END
lpStatus->dwReturn = CDA_disc_length (didDrive); switch (pInst->dwTimeFormat) { case MCI_FORMAT_MILLISECONDS: lpStatus->dwReturn = redtomil ((redbook)lpStatus->dwReturn); dwReturn = 0; break; case MCI_FORMAT_MSF: case MCI_FORMAT_TMSF: lpStatus->dwReturn = flip3((redbook)lpStatus->dwReturn); dwReturn = MCI_COLONIZED3_RETURN; break; } } break; case MCI_STATUS_NUMBER_OF_TRACKS: if (!disc_ready(didDrive)) return MCIERR_HARDWARE;
lpStatus->dwReturn = (DWORD)CDA_num_tracks (didDrive); dwReturn = 0; break; case MCI_STATUS_CURRENT_TRACK: { redbook tracktime;
if (!DriveTable[didDrive].bDiscPlayed) lpStatus->dwReturn = 1; else { if (CDA_time_info(didDrive, &tracktime, NULL) != COMMAND_SUCCESSFUL) return MCIERR_HARDWARE;
lpStatus->dwReturn = REDTRACK (tracktime); } break; } case MCI_CDA_STATUS_TYPE_TRACK: if (!disc_ready(didDrive)) return MCIERR_HARDWARE;
if (dwFlags & MCI_TRACK) { DWORD dwTmp;
dwTmp = CDA_track_type (didDrive, (int)lpStatus->dwTrack);
switch (dwTmp) { case INVALID_TRACK: return MCIERR_OUTOFRANGE;
case MCI_CDA_TRACK_AUDIO: lpStatus->dwReturn = (DWORD)MAKEMCIRESOURCE(dwTmp, MCI_CDA_AUDIO_S); break;
case MCI_CDA_TRACK_OTHER: lpStatus->dwReturn = (DWORD)MAKEMCIRESOURCE(dwTmp, MCI_CDA_OTHER_S); break; } dwReturn = MCI_RESOURCE_RETURNED | MCI_RESOURCE_DRIVER; } break;
case MCI_STATUS_TRACK_POS: { // Note: This code is a major hack that does an end-run around
// past the normal MCI functionality. The only reason it
// is here is because the new functionality replaces 3 MCI
// calls in CDPLAYER to get the position,track, and status
// with this one call.
// This means what used to take ~15 IOCTL's to accomplish
// now takes ~1 IOCTL. Since CDPLAYER generates one of
// these messages every 1/2 second for updating it's timer
// display. This is a major reduction in system traffic
// for SCSI and IDE CD-Roms drivers.
DWORD status; DWORD mciStatus; redbook tracktime, disctime; int rc; STATUSTRACKPOS stp; PSTATUSTRACKPOS pSTP;
if (!DriveTable[didDrive].bDiscPlayed) { tracktime = REDTH(0, 1); if (!disc_ready(didDrive)) { dprintf(("mcStatus (%08LX), MCI_STATUS_TRACK_POS, Disc Not Ready", (DWORD)didDrive)); return MCIERR_HARDWARE; } disctime = CDA_track_start( didDrive, 1);
status = CDA_drive_status (didDrive); switch (status) { case DISC_PLAYING: mciStatus = MCI_MODE_PLAY; break; case DISC_PAUSED: mciStatus = MCI_MODE_STOP; // HACK HACK!
break; case DISC_READY: mciStatus = MCI_MODE_STOP; break; default: if (CDA_traystate (didDrive) == TRAY_OPEN) mciStatus = MCI_MODE_OPEN; else mciStatus = MCI_MODE_NOT_READY; break; } } else { rc = CDA_status_track_pos (didDrive, &status, &tracktime, &disctime); if (rc != COMMAND_SUCCESSFUL) { dprintf(("mcStatus (%08LX), MCI_STATUS_TRACK_POS, CDA_status_track_pos failed", (DWORD)didDrive)); return MCIERR_HARDWARE; }
if (REDTRACK(tracktime) == 0) { tracktime = (redbook)0; disctime = (redbook)0; }
switch (status) { case DISC_PLAYING: mciStatus = MCI_MODE_PLAY; break; case DISC_PAUSED: mciStatus = MCI_MODE_STOP; // HACK HACK!
break; case DISC_READY: mciStatus = MCI_MODE_STOP; break; case DISC_NOT_IN_CDROM: mciStatus = MCI_MODE_OPEN; break; default: mciStatus = MCI_MODE_NOT_READY; break; } }
stp.dwStatus = mciStatus; stp.dwTrack = REDTRACK (tracktime); switch (pInst->dwTimeFormat) { case MCI_FORMAT_MILLISECONDS: stp.dwDiscTime = redtomil ((redbook)disctime); dwReturn = 0; break; case MCI_FORMAT_MSF: stp.dwDiscTime = flip3(disctime); dwReturn = MCI_COLONIZED3_RETURN; break; case MCI_FORMAT_TMSF: stp.dwDiscTime = flip4 (tracktime); dwReturn = MCI_COLONIZED4_RETURN; break; }
pSTP = (PSTATUSTRACKPOS)lpStatus->dwReturn; if (pSTP == NULL) return MCIERR_MISSING_PARAMETER;
pSTP->dwStatus = stp.dwStatus; pSTP->dwTrack = stp.dwTrack; pSTP->dwDiscTime = stp.dwDiscTime; break; }
default: dwReturn = MCIERR_UNSUPPORTED_FUNCTION; break; }
return dwReturn; }
/*****************************************************************************
@doc INTERNAL MCICDA
@api DWORD | mcClose | Process the MCI_CLOSE command
@parm PINSTDATA | pInst | Pointer to application data instance
@rdesc
@comm *****************************************************************************/ DWORD mcClose( PINSTDATA pInst) { DID didDrive = pInst->uDevice; MCIDEVICEID wDeviceID = pInst->uMCIDeviceID; int nUseCount;
if (!pInst) { dprintf2(("mcClose, passed in NULL pointer")); }
if (DriveTable[didDrive].nUseCount == 0) { dprintf2(("mcClose (%08lX), nUseCount already ZERO!!!", (DWORD)didDrive)); } else if (--DriveTable[didDrive].nUseCount == 0) { dprintf2(("mcClose, Actually closing device (%08lX)", (DWORD)didDrive)); CDA_close(didDrive); CDA_terminate_audio (); } else { dprintf2(("mcClose, Enter, device (%08lx), decremented useCount = %ld", (DWORD)didDrive, DriveTable[didDrive].nUseCount)); // Note: Having this here prevents a mis-count problem
CDA_close(didDrive); }
// Abort any notify if the use count is 0 or if the notify is for the device
// being closed
if ((DriveTable[didDrive].nUseCount == 0) || (wDeviceID == DriveTable[didDrive].wDeviceID)) abort_notify (pInst);
mciSetDriverData(pInst->uMCIDeviceID, 0L); LocalFree((HLOCAL)pInst);
dprintf2(("mcClose, Exit, device (%08lx), useCount = %ld", (DWORD)didDrive, DriveTable[didDrive].nUseCount)); return 0; }
/*****************************************************************************
@doc INTERNAL MCICDA
@api DWORD | mcStop | Process the MCI_STOP command
@parm PINSTDATA | pInst | Pointer to application data instance
@parm DWORD | dwFlags |
@rdesc
*****************************************************************************/ DWORD mcStop( PINSTDATA pInst, DWORD dwFlags, LPMCI_GENERIC_PARMS lpGeneric) { DID didDrive = pInst->uDevice;
if (!disc_ready (didDrive)) return MCIERR_HARDWARE;
abort_notify (pInst);
if (CDA_stop_audio(didDrive) != COMMAND_SUCCESSFUL) return MCIERR_HARDWARE;
return 0; }
/*****************************************************************************
@doc INTERNAL MCICDA
@api DWORD | mcPause | Process the MCI_PAUSE command
@parm PINSTDATA | pInst | Pointer to application data instance
@parm DWORD | dwFlags |
@rdesc
*****************************************************************************/
DWORD mcPause( PINSTDATA pInst, DWORD dwFlags, LPMCI_GENERIC_PARMS lpGeneric) { DID didDrive = pInst->uDevice;
if (!disc_ready (didDrive)) return MCIERR_HARDWARE;
abort_notify (pInst);
if (CDA_pause_audio(didDrive) != COMMAND_SUCCESSFUL) return MCIERR_HARDWARE;
return 0; }
/*****************************************************************************
@doc INTERNAL MCICDA
@api DWORD | mcResume | Process the MCI_PAUSE command
@parm PINSTDATA | pInst | Pointer to application data instance
@parm DWORD | dwFlags |
@rdesc
*****************************************************************************/
DWORD mcResume( PINSTDATA pInst, DWORD dwFlags, LPMCI_GENERIC_PARMS lpGeneric) { DID didDrive = pInst->uDevice;
if (!disc_ready (didDrive)) return MCIERR_HARDWARE;
abort_notify (pInst);
if (CDA_resume_audio(didDrive) != COMMAND_SUCCESSFUL) return MCIERR_HARDWARE;
return 0; }
// MBR cda.c!SendDriverReq masks off the actual error bits and just
// leaves the upper bit set - this is ok for now. There exists
// no seperate "command is known but not supported" error at
// the driver level, so if the driver returns "unrecognized
// command", we return "unsupported function".
#define ERRQ(X) (((X)==0) ? MCIERR_UNSUPPORTED_FUNCTION : 0)
/*****************************************************************************
@doc INTERNAL MCICDA
@api DWORD | mcSet | Process the MCI_SET command
@parm DWORD | dwFlags |
@parm LPMCI_SET_PARMS | lpSet |
@rdesc
@comm *****************************************************************************/ DWORD mcSet( PINSTDATA pInst, DWORD dwFlags, LPMCI_SET_PARMS lpSet ) { DID didDrive = pInst->uDevice; UINT wErr = 0;
dwFlags &= ~(MCI_NOTIFY | MCI_WAIT);
if (!dwFlags) return MCIERR_MISSING_PARAMETER; if (dwFlags & MCI_SET_TIME_FORMAT) { DWORD wFormat = lpSet->dwTimeFormat;
switch (wFormat) { case MCI_FORMAT_MILLISECONDS: case MCI_FORMAT_MSF: case MCI_FORMAT_TMSF: pInst->dwTimeFormat = wFormat; break; default: wErr = MCIERR_BAD_TIME_FORMAT; break; } }
if (!wErr && (dwFlags & MCI_SET_DOOR_OPEN)) { abort_notify (pInst); CDA_stop_audio (didDrive); CDA_eject(didDrive);
DriveTable[didDrive].bDiscPlayed = FALSE; }
if (!wErr && (dwFlags & MCI_SET_AUDIO)) { UCHAR wVolume; if (dwFlags & MCI_SET_ON && dwFlags & MCI_SET_OFF) return MCIERR_FLAGS_NOT_COMPATIBLE;
if (dwFlags & MCI_SET_ON) wVolume = 255; else if (dwFlags & MCI_SET_OFF) wVolume = 0; else return MCIERR_MISSING_PARAMETER;
switch (lpSet->dwAudio) { case MCI_SET_AUDIO_ALL: if (CDA_set_audio_volume_all (didDrive, wVolume) != COMMAND_SUCCESSFUL) wErr = MCIERR_HARDWARE; break; case MCI_SET_AUDIO_LEFT: if (CDA_set_audio_volume (didDrive, 0, wVolume) != COMMAND_SUCCESSFUL) wErr = MCIERR_HARDWARE; break; case MCI_SET_AUDIO_RIGHT: if (CDA_set_audio_volume (didDrive, 1, wVolume) != COMMAND_SUCCESSFUL) wErr = MCIERR_HARDWARE; break; } }
if (!wErr && dwFlags & MCI_SET_DOOR_CLOSED) CDA_closetray (didDrive);
return wErr; }
/*****************************************************************************
@doc INTERNAL MCICDA
@api DWORD | mcInfo | Process the MCI_INFO command
@parm PINSTDATA | pInst | Pointer to application instance data
@parm DWORD | dwFlags |
@parm LPMCI_INFO_PARMS | lpInfo |
@rdesc
@comm *****************************************************************************/ DWORD mcInfo ( PINSTDATA pInst, DWORD dwFlags, LPMCI_INFO_PARMS lpInfo ) { DID didDrive = pInst->uDevice; DWORD wReturnBufferLength;
wReturnBufferLength = LOWORD(lpInfo->dwRetSize);
if (!lpInfo->lpstrReturn || !wReturnBufferLength) return MCIERR_PARAM_OVERFLOW;
if (dwFlags & MCI_INFO_PRODUCT) { *(lpInfo->lpstrReturn) = '\0'; lpInfo->dwRetSize = (DWORD)LoadString(hInstance, IDS_PRODUCTNAME, lpInfo->lpstrReturn, (int)wReturnBufferLength); return 0; } else if (dwFlags & MCI_INFO_MEDIA_UPC) { unsigned char upc[16]; int i;
if (!disc_ready(didDrive)) return MCIERR_HARDWARE;
if (CDA_disc_upc(didDrive, lpInfo->lpstrReturn) != COMMAND_SUCCESSFUL) return MCIERR_NO_IDENTITY;
return 0; } else if (dwFlags & MCI_INFO_MEDIA_IDENTITY) { DWORD dwId; if (!disc_ready(didDrive)) return MCIERR_HARDWARE;
dwId = CDA_disc_id(didDrive); if (dwId == (DWORD)-1L) return MCIERR_HARDWARE; wsprintf(lpInfo->lpstrReturn,TEXT("%lu"),dwId); return 0; } else return MCIERR_MISSING_PARAMETER; }
/*
* @doc INTERNAL MCIRBOOK * * @api DWORD | mciDriverEntry | Single entry point for MCI drivers * * @parm MCIDEVICEID | wDeviceID | The MCI device ID * * @parm UINT | message | The requested action to be performed. * * @parm LPARAM | lParam1 | Data for this message. Defined seperately for * each message * * @parm LPARAM | lParam2 | Data for this message. Defined seperately for * each message * * @rdesc Defined seperately for each message. * */ DWORD CD_MCI_Handler (MCIDEVICEID wDeviceID, UINT message, DWORD_PTR lParam1, DWORD_PTR lParam2) { DID didDrive; LPMCI_GENERIC_PARMS lpGeneric = (LPMCI_GENERIC_PARMS)lParam2; BOOL bDelayed = FALSE; DWORD dwErr = 0, wNotifyErr; DWORD dwPlayTo = MCICDA_BAD_TIME; WORD wNotifyStatus = MCI_NOTIFY_SUCCESSFUL; PINSTDATA pInst;
pInst = (PINSTDATA)mciGetDriverData(wDeviceID); didDrive = (DID)pInst->uDevice;
EnterCrit( CdInfo[didDrive].DeviceCritSec );
switch (message) { case MCI_OPEN_DRIVER: dwErr = mcOpen (pInst, (DWORD)lParam1, (LPMCI_OPEN_PARMS)lParam2); break;
case MCI_CLOSE_DRIVER: dwErr = mcClose (pInst); break;
case MCI_PLAY: { BOOL bBreak = FALSE; dwErr = mcPlay (pInst, (DWORD)lParam1, (LPMCI_PLAY_PARMS)lParam2, &bBreak);
if (dwErr == 0 && (DWORD)lParam1 & MCI_WAIT && (DWORD)lParam1 & MCI_NOTIFY) { switch (CDA_drive_status (didDrive)) { case DISC_PLAYING: case DISC_PAUSED: case DISC_READY: break; default: wNotifyStatus = MCI_NOTIFY_FAILURE; break; } }
// If MCI_WAIT is not set or if the wait loop was broken out of then delay
if (!((DWORD)lParam1 & MCI_WAIT) || bBreak) bDelayed = TRUE; break; }
case MCI_SEEK: dwErr = mcSeek (pInst, (DWORD)lParam1, (LPMCI_SEEK_PARMS)lParam2); break;
case MCI_STOP: dwErr = mcStop ( pInst, (DWORD)lParam1, (LPMCI_GENERIC_PARMS)lParam2); break;
case MCI_PAUSE: dwErr = mcPause ( pInst, (DWORD)lParam1, (LPMCI_GENERIC_PARMS)lParam2); break;
case MCI_GETDEVCAPS: dwErr = mcGetDevCaps (pInst, (DWORD)lParam1, (LPMCI_GETDEVCAPS_PARMS)lParam2); break;
case MCI_STATUS: dwErr = mcStatus (pInst, (DWORD)lParam1, (LPMCI_STATUS_PARMS)lParam2); break;
case MCI_SET: dwErr = mcSet (pInst, (DWORD)lParam1, (LPMCI_SET_PARMS)lParam2); break;
case MCI_INFO: dwErr = mcInfo (pInst, (DWORD)lParam1, (LPMCI_INFO_PARMS)lParam2); break;
case MCI_RECORD: case MCI_LOAD: case MCI_SAVE: LeaveCrit( CdInfo[didDrive].DeviceCritSec ); return MCIERR_UNSUPPORTED_FUNCTION;
case MCI_RESUME: dwErr = mcResume ( pInst, (DWORD)lParam1, (LPMCI_GENERIC_PARMS)lParam2); break;
default: LeaveCrit( CdInfo[didDrive].DeviceCritSec ); return MCIERR_UNRECOGNIZED_COMMAND; } /* switch */
/* it is possible that the instance information has disappeared if
* CLOSE NOTIFY is requested. Therefore NOTIFY should never take * instance data. */
if ((DWORD)lParam1 & MCI_NOTIFY && LOWORD (dwErr) == 0) if ((wNotifyErr = notify (didDrive, wDeviceID, bDelayed, wNotifyStatus, (LPMCI_GENERIC_PARMS)lParam2)) != 0) { LeaveCrit( CdInfo[didDrive].DeviceCritSec ); return wNotifyErr; }
LeaveCrit( CdInfo[didDrive].DeviceCritSec );
return dwErr; }
|