480 lines
12 KiB
C++
Executable File
480 lines
12 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "platformWin32/platformWin32.h"
|
|
#include "platform/platformRedBook.h"
|
|
#include "core/unicode.h"
|
|
|
|
class Win32RedBookDevice : public RedBookDevice
|
|
{
|
|
private:
|
|
typedef RedBookDevice Parent;
|
|
|
|
U32 mDeviceId;
|
|
|
|
void setLastError(const char *);
|
|
void setLastError(U32);
|
|
|
|
MIXERCONTROLDETAILS mMixerVolumeDetails;
|
|
MIXERCONTROLDETAILS_UNSIGNED mMixerVolumeValue;
|
|
|
|
union {
|
|
HMIXEROBJ mVolumeDeviceId;
|
|
UINT mAuxVolumeDeviceId;
|
|
};
|
|
|
|
U32 mOriginalVolume;
|
|
bool mVolumeInitialized;
|
|
|
|
bool mUsingMixer;
|
|
|
|
void openVolume();
|
|
void closeVolume();
|
|
|
|
public:
|
|
Win32RedBookDevice();
|
|
~Win32RedBookDevice();
|
|
|
|
U32 getDeviceId();
|
|
|
|
bool open();
|
|
bool close();
|
|
bool play(U32);
|
|
bool stop();
|
|
bool getTrackCount(U32 *);
|
|
bool getVolume(F32 *);
|
|
bool setVolume(F32);
|
|
};
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Win32 specific
|
|
//------------------------------------------------------------------------------
|
|
void installRedBookDevices()
|
|
{
|
|
U32 bufSize = ::GetLogicalDriveStrings(0,0);
|
|
|
|
char * buf = new char[bufSize];
|
|
|
|
::GetLogicalDriveStringsA(bufSize, buf);
|
|
|
|
char * str = buf;
|
|
while(*str)
|
|
{
|
|
if(::GetDriveTypeA(str) == DRIVE_CDROM)
|
|
{
|
|
Win32RedBookDevice * device = new Win32RedBookDevice;
|
|
device->mDeviceName = new char[dStrlen(str) + 1];
|
|
dStrcpy(device->mDeviceName, str);
|
|
|
|
RedBook::installDevice(device);
|
|
}
|
|
str += dStrlen(str) + 1;
|
|
}
|
|
|
|
delete [] buf;
|
|
}
|
|
|
|
void handleRedBookCallback(U32 code, U32 deviceId)
|
|
{
|
|
if(code != MCI_NOTIFY_SUCCESSFUL)
|
|
return;
|
|
|
|
Win32RedBookDevice * device = dynamic_cast<Win32RedBookDevice*>(RedBook::getCurrentDevice());
|
|
if(!device)
|
|
return;
|
|
|
|
if(device->getDeviceId() != deviceId)
|
|
return;
|
|
|
|
// only installed callback on play (no callback if play is aborted)
|
|
RedBook::handleCallback(RedBook::PlayFinished);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Class: Win32RedBookDevice
|
|
//------------------------------------------------------------------------------
|
|
Win32RedBookDevice::Win32RedBookDevice()
|
|
{
|
|
mVolumeInitialized = false;
|
|
}
|
|
|
|
Win32RedBookDevice::~Win32RedBookDevice()
|
|
{
|
|
close();
|
|
}
|
|
|
|
U32 Win32RedBookDevice::getDeviceId()
|
|
{
|
|
return(mDeviceId);
|
|
}
|
|
|
|
bool Win32RedBookDevice::open()
|
|
{
|
|
if(mAcquired)
|
|
{
|
|
setLastError("Device is already open.");
|
|
return(false);
|
|
}
|
|
|
|
U32 error;
|
|
|
|
// open the device
|
|
MCI_OPEN_PARMS openParms;
|
|
#ifdef UNICODE
|
|
openParms.lpstrDeviceType = (LPCWSTR)MCI_DEVTYPE_CD_AUDIO;
|
|
|
|
UTF16 buf[512];
|
|
convertUTF8toUTF16((UTF8 *)mDeviceName, buf, sizeof(buf));
|
|
openParms.lpstrElementName = buf;
|
|
#else
|
|
openParms.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO;
|
|
openParms.lpstrElementName = mDeviceName;
|
|
#endif
|
|
|
|
error = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, (DWORD_PTR)(LPMCI_OPEN_PARMS)&openParms);
|
|
if(error)
|
|
{
|
|
// attempt to open as a shared device
|
|
error = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID|MCI_OPEN_SHAREABLE, (DWORD_PTR)(LPMCI_OPEN_PARMS)&openParms);
|
|
if(error)
|
|
{
|
|
setLastError(error);
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
// set time mode to milliseconds
|
|
MCI_SET_PARMS setParms;
|
|
setParms.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
|
|
|
|
error = mciSendCommand(openParms.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPMCI_SET_PARMS)&setParms);
|
|
if(error)
|
|
{
|
|
setLastError(error);
|
|
return(false);
|
|
}
|
|
|
|
//
|
|
mDeviceId = openParms.wDeviceID;
|
|
mAcquired = true;
|
|
|
|
openVolume();
|
|
setLastError("");
|
|
return(true);
|
|
}
|
|
|
|
bool Win32RedBookDevice::close()
|
|
{
|
|
if(!mAcquired)
|
|
{
|
|
setLastError("Device has not been acquired");
|
|
return(false);
|
|
}
|
|
|
|
stop();
|
|
|
|
U32 error;
|
|
|
|
MCI_GENERIC_PARMS closeParms;
|
|
error = mciSendCommand(mDeviceId, MCI_CLOSE, 0, (DWORD_PTR)(LPMCI_GENERIC_PARMS)&closeParms);
|
|
if(error)
|
|
{
|
|
setLastError(error);
|
|
return(false);
|
|
}
|
|
|
|
mAcquired = false;
|
|
closeVolume();
|
|
setLastError("");
|
|
return(true);
|
|
}
|
|
|
|
bool Win32RedBookDevice::play(U32 track)
|
|
{
|
|
if(!mAcquired)
|
|
{
|
|
setLastError("Device has not been acquired");
|
|
return(false);
|
|
}
|
|
|
|
U32 numTracks;
|
|
if(!getTrackCount(&numTracks))
|
|
return(false);
|
|
|
|
if(track >= numTracks)
|
|
{
|
|
setLastError("Track index is out of range");
|
|
return(false);
|
|
}
|
|
|
|
MCI_STATUS_PARMS statusParms;
|
|
|
|
// get track start time
|
|
statusParms.dwItem = MCI_STATUS_POSITION;
|
|
statusParms.dwTrack = track + 1;
|
|
|
|
U32 error;
|
|
error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK|MCI_WAIT,
|
|
(DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms);
|
|
|
|
if(error)
|
|
{
|
|
setLastError(error);
|
|
return(false);
|
|
}
|
|
|
|
MCI_PLAY_PARMS playParms;
|
|
playParms.dwFrom = statusParms.dwReturn;
|
|
|
|
// get track end time
|
|
statusParms.dwItem = MCI_STATUS_LENGTH;
|
|
error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK|MCI_WAIT,
|
|
(DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms);
|
|
|
|
if(error)
|
|
{
|
|
setLastError(error);
|
|
return(false);
|
|
}
|
|
|
|
playParms.dwTo = playParms.dwFrom + statusParms.dwReturn;
|
|
|
|
// play the track
|
|
playParms.dwCallback = MAKELONG(winState.appWindow, 0);
|
|
error = mciSendCommand(mDeviceId, MCI_PLAY, MCI_FROM|MCI_TO|MCI_NOTIFY,
|
|
(DWORD_PTR)(LPMCI_PLAY_PARMS)&playParms);
|
|
|
|
if(error)
|
|
{
|
|
setLastError(error);
|
|
return(false);
|
|
}
|
|
|
|
setLastError("");
|
|
return(true);
|
|
}
|
|
|
|
bool Win32RedBookDevice::stop()
|
|
{
|
|
if(!mAcquired)
|
|
{
|
|
setLastError("Device has not been acquired");
|
|
return(false);
|
|
}
|
|
|
|
MCI_GENERIC_PARMS genParms;
|
|
|
|
U32 error = mciSendCommand(mDeviceId, MCI_STOP, 0, (DWORD_PTR)(LPMCI_GENERIC_PARMS)&genParms);
|
|
if(error)
|
|
{
|
|
setLastError(error);
|
|
return(false);
|
|
}
|
|
|
|
setLastError("");
|
|
return(true);
|
|
}
|
|
|
|
bool Win32RedBookDevice::getTrackCount(U32 * numTracks)
|
|
{
|
|
if(!mAcquired)
|
|
{
|
|
setLastError("Device has not been acquired");
|
|
return(false);
|
|
}
|
|
|
|
MCI_STATUS_PARMS statusParms;
|
|
|
|
statusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
|
|
U32 error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms);
|
|
if(error)
|
|
{
|
|
setLastError(error);
|
|
return(false);
|
|
}
|
|
|
|
*numTracks = statusParms.dwReturn;
|
|
return(true);
|
|
}
|
|
|
|
bool Win32RedBookDevice::getVolume(F32 * volume)
|
|
{
|
|
if(!mAcquired)
|
|
{
|
|
setLastError("Device has not been acquired");
|
|
return(false);
|
|
}
|
|
|
|
if(!mVolumeInitialized)
|
|
{
|
|
setLastError("Volume failed to initialize");
|
|
return(false);
|
|
}
|
|
|
|
U32 vol = 0;
|
|
if(mUsingMixer)
|
|
{
|
|
mixerGetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_GETCONTROLDETAILSF_VALUE);
|
|
vol = mMixerVolumeValue.dwValue;
|
|
}
|
|
else
|
|
auxGetVolume(mAuxVolumeDeviceId, (unsigned long *)&vol);
|
|
|
|
vol &= 0xffff;
|
|
*volume = F32(vol) / 65535.f;
|
|
|
|
setLastError("");
|
|
return(true);
|
|
}
|
|
|
|
bool Win32RedBookDevice::setVolume(F32 volume)
|
|
{
|
|
if(!mAcquired)
|
|
{
|
|
setLastError("Device has not been acquired");
|
|
return(false);
|
|
}
|
|
|
|
if(!mVolumeInitialized)
|
|
{
|
|
setLastError("Volume failed to initialize");
|
|
return(false);
|
|
}
|
|
|
|
// move into a U32 - left/right U16 volumes
|
|
U32 vol = U32(volume * 65536.f);
|
|
if(vol > 0xffff)
|
|
vol = 0xffff;
|
|
|
|
if(mUsingMixer)
|
|
{
|
|
mMixerVolumeValue.dwValue = vol;
|
|
mixerSetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_SETCONTROLDETAILSF_VALUE);
|
|
}
|
|
else
|
|
{
|
|
vol |= vol << 16;
|
|
auxSetVolume(mAuxVolumeDeviceId, vol);
|
|
}
|
|
|
|
setLastError("");
|
|
return(true);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Win32RedBookDevice::openVolume()
|
|
{
|
|
setLastError("");
|
|
|
|
// first attempt to get the volume control through the mixer API
|
|
S32 i;
|
|
for(i = mixerGetNumDevs() - 1; i >= 0; i--)
|
|
{
|
|
// open the mixer
|
|
if(mixerOpen((HMIXER*)&mVolumeDeviceId, i, 0, 0, 0) == MMSYSERR_NOERROR)
|
|
{
|
|
MIXERLINE lineInfo;
|
|
memset(&lineInfo, 0, sizeof(lineInfo));
|
|
lineInfo.cbStruct = sizeof(lineInfo);
|
|
lineInfo.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
|
|
|
|
// get the cdaudio line
|
|
if(mixerGetLineInfo(mVolumeDeviceId, &lineInfo, MIXER_GETLINEINFOF_COMPONENTTYPE) == MMSYSERR_NOERROR)
|
|
{
|
|
MIXERLINECONTROLS lineControls;
|
|
MIXERCONTROL volumeControl;
|
|
|
|
memset(&lineControls, 0, sizeof(lineControls));
|
|
lineControls.cbStruct = sizeof(lineControls);
|
|
lineControls.dwLineID = lineInfo.dwLineID;
|
|
lineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
|
|
lineControls.cControls = 1;
|
|
lineControls.cbmxctrl = sizeof(volumeControl);
|
|
lineControls.pamxctrl = &volumeControl;
|
|
|
|
memset(&volumeControl, 0, sizeof(volumeControl));
|
|
volumeControl.cbStruct = sizeof(volumeControl);
|
|
|
|
// get the volume control
|
|
if(mixerGetLineControls(mVolumeDeviceId, &lineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE) == MMSYSERR_NOERROR)
|
|
{
|
|
memset(&mMixerVolumeDetails, 0, sizeof(mMixerVolumeDetails));
|
|
mMixerVolumeDetails.cbStruct = sizeof(mMixerVolumeDetails);
|
|
mMixerVolumeDetails.dwControlID = volumeControl.dwControlID;
|
|
mMixerVolumeDetails.cChannels = 1;
|
|
mMixerVolumeDetails.cbDetails = sizeof(mMixerVolumeValue);
|
|
mMixerVolumeDetails.paDetails = &mMixerVolumeValue;
|
|
|
|
memset(&mMixerVolumeValue, 0, sizeof(mMixerVolumeValue));
|
|
|
|
// query the current value
|
|
if(mixerGetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_GETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR)
|
|
{
|
|
mUsingMixer = true;
|
|
mVolumeInitialized = true;
|
|
mOriginalVolume = mMixerVolumeValue.dwValue;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mixerClose((HMIXER)mVolumeDeviceId);
|
|
}
|
|
|
|
// try aux
|
|
for(i = auxGetNumDevs() - 1; i >= 0; i--)
|
|
{
|
|
AUXCAPS caps;
|
|
auxGetDevCaps(i, &caps, sizeof(AUXCAPS));
|
|
if((caps.wTechnology == AUXCAPS_CDAUDIO) && (caps.dwSupport & AUXCAPS_VOLUME))
|
|
{
|
|
mAuxVolumeDeviceId = i;
|
|
mVolumeInitialized = true;
|
|
mUsingMixer = false;
|
|
auxGetVolume(i, (unsigned long *)&mOriginalVolume);
|
|
return;
|
|
}
|
|
}
|
|
|
|
setLastError("Volume failed to initialize");
|
|
}
|
|
|
|
void Win32RedBookDevice::closeVolume()
|
|
{
|
|
setLastError("");
|
|
if(!mVolumeInitialized)
|
|
return;
|
|
|
|
if(mUsingMixer)
|
|
{
|
|
mMixerVolumeValue.dwValue = mOriginalVolume;
|
|
mixerSetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_SETCONTROLDETAILSF_VALUE);
|
|
mixerClose((HMIXER)mVolumeDeviceId);
|
|
}
|
|
else
|
|
auxSetVolume(mAuxVolumeDeviceId, mOriginalVolume);
|
|
|
|
mVolumeInitialized = false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Win32RedBookDevice::setLastError(const char * error)
|
|
{
|
|
RedBook::setLastError(error);
|
|
}
|
|
|
|
void Win32RedBookDevice::setLastError(U32 errorId)
|
|
{
|
|
char buffer[256];
|
|
if(!mciGetErrorStringA(errorId, buffer, sizeof(buffer) - 1))
|
|
setLastError("Failed to get MCI error string!");
|
|
else
|
|
setLastError(buffer);
|
|
}
|
|
|