1670 lines
41 KiB
C
1670 lines
41 KiB
C
|
/*******************************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;
|
||
|
}
|