//----------------------------------------------------------------------------- // 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(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); }