winRedbook.cpp
Engine/source/platformWin32/winRedbook.cpp
Classes:
class
Public Functions
handleRedBookCallback(U32 code, U32 deviceId)
Detailed Description
Public Functions
handleRedBookCallback(U32 code, U32 deviceId)
installRedBookDevices()
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platformWin32/platformWin32.h" 25#include "platform/platformRedBook.h" 26#include "core/strings/unicode.h" 27#include "core/strings/stringFunctions.h" 28 29class Win32RedBookDevice : public RedBookDevice 30{ 31 private: 32 typedef RedBookDevice Parent; 33 34 U32 mDeviceId; 35 36 void setLastError(const char *); 37 void setLastError(U32); 38 39 MIXERCONTROLDETAILS mMixerVolumeDetails; 40 MIXERCONTROLDETAILS_UNSIGNED mMixerVolumeValue; 41 42 union { 43 HMIXEROBJ mVolumeDeviceId; 44 UINT mAuxVolumeDeviceId; 45 }; 46 47 U32 mOriginalVolume; 48 bool mVolumeInitialized; 49 50 bool mUsingMixer; 51 52 void openVolume(); 53 void closeVolume(); 54 55 public: 56 Win32RedBookDevice(); 57 ~Win32RedBookDevice(); 58 59 U32 getDeviceId(); 60 61 bool open(); 62 bool close(); 63 bool play(U32); 64 bool stop(); 65 bool getTrackCount(U32 *); 66 bool getVolume(F32 *); 67 bool setVolume(F32); 68}; 69 70//------------------------------------------------------------------------------ 71// Win32 specific 72//------------------------------------------------------------------------------ 73void installRedBookDevices() 74{ 75 U32 bufSize = ::GetLogicalDriveStrings(0,0); 76 77 char * buf = new char[bufSize]; 78 79 ::GetLogicalDriveStringsA(bufSize, buf); 80 81 char * str = buf; 82 while(*str) 83 { 84 if(::GetDriveTypeA(str) == DRIVE_CDROM) 85 { 86 Win32RedBookDevice * device = new Win32RedBookDevice; 87 dsize_t deviceNameLen = dStrlen(str) + 1; 88 device->mDeviceName = new char[deviceNameLen]; 89 dStrcpy(device->mDeviceName, str, deviceNameLen); 90 91 RedBook::installDevice(device); 92 } 93 str += dStrlen(str) + 1; 94 } 95 96 delete [] buf; 97} 98 99void handleRedBookCallback(U32 code, U32 deviceId) 100{ 101 if(code != MCI_NOTIFY_SUCCESSFUL) 102 return; 103 104 Win32RedBookDevice * device = dynamic_cast<Win32RedBookDevice*>(RedBook::getCurrentDevice()); 105 if(!device) 106 return; 107 108 if(device->getDeviceId() != deviceId) 109 return; 110 111 // only installed callback on play (no callback if play is aborted) 112 RedBook::handleCallback(RedBook::PlayFinished); 113} 114 115//------------------------------------------------------------------------------ 116// Class: Win32RedBookDevice 117//------------------------------------------------------------------------------ 118Win32RedBookDevice::Win32RedBookDevice() 119{ 120 mVolumeInitialized = false; 121} 122 123Win32RedBookDevice::~Win32RedBookDevice() 124{ 125 close(); 126} 127 128U32 Win32RedBookDevice::getDeviceId() 129{ 130 return(mDeviceId); 131} 132 133bool Win32RedBookDevice::open() 134{ 135 if(mAcquired) 136 { 137 setLastError("Device is already open."); 138 return(false); 139 } 140 141 U32 error; 142 143 // open the device 144 MCI_OPEN_PARMS openParms; 145#ifdef UNICODE 146 openParms.lpstrDeviceType = (LPCWSTR)MCI_DEVTYPE_CD_AUDIO; 147 148 UTF16 buf[512]; 149 convertUTF8toUTF16((UTF8 *)mDeviceName, buf); 150 openParms.lpstrElementName = buf; 151#else 152 openParms.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO; 153 openParms.lpstrElementName = mDeviceName; 154#endif 155 156 error = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, (DWORD_PTR)(LPMCI_OPEN_PARMS)&openParms); 157 if(error) 158 { 159 // attempt to open as a shared device 160 error = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID|MCI_OPEN_SHAREABLE, (DWORD_PTR)(LPMCI_OPEN_PARMS)&openParms); 161 if(error) 162 { 163 setLastError(error); 164 return(false); 165 } 166 } 167 168 // set time mode to milliseconds 169 MCI_SET_PARMS setParms; 170 setParms.dwTimeFormat = MCI_FORMAT_MILLISECONDS; 171 172 error = mciSendCommand(openParms.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPMCI_SET_PARMS)&setParms); 173 if(error) 174 { 175 setLastError(error); 176 return(false); 177 } 178 179 // 180 mDeviceId = openParms.wDeviceID; 181 mAcquired = true; 182 183 openVolume(); 184 setLastError(""); 185 return(true); 186} 187 188bool Win32RedBookDevice::close() 189{ 190 if(!mAcquired) 191 { 192 setLastError("Device has not been acquired"); 193 return(false); 194 } 195 196 stop(); 197 198 U32 error; 199 200 MCI_GENERIC_PARMS closeParms; 201 error = mciSendCommand(mDeviceId, MCI_CLOSE, 0, (DWORD_PTR)(LPMCI_GENERIC_PARMS)&closeParms); 202 if(error) 203 { 204 setLastError(error); 205 return(false); 206 } 207 208 mAcquired = false; 209 closeVolume(); 210 setLastError(""); 211 return(true); 212} 213 214bool Win32RedBookDevice::play(U32 track) 215{ 216 if(!mAcquired) 217 { 218 setLastError("Device has not been acquired"); 219 return(false); 220 } 221 222 U32 numTracks; 223 if(!getTrackCount(&numTracks)) 224 return(false); 225 226 if(track >= numTracks) 227 { 228 setLastError("Track index is out of range"); 229 return(false); 230 } 231 232 MCI_STATUS_PARMS statusParms; 233 234 // get track start time 235 statusParms.dwItem = MCI_STATUS_POSITION; 236 statusParms.dwTrack = track + 1; 237 238 U32 error; 239 error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK|MCI_WAIT, 240 (DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms); 241 242 if(error) 243 { 244 setLastError(error); 245 return(false); 246 } 247 248 MCI_PLAY_PARMS playParms; 249 playParms.dwFrom = statusParms.dwReturn; 250 251 // get track end time 252 statusParms.dwItem = MCI_STATUS_LENGTH; 253 error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK|MCI_WAIT, 254 (DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms); 255 256 if(error) 257 { 258 setLastError(error); 259 return(false); 260 } 261 262 playParms.dwTo = playParms.dwFrom + statusParms.dwReturn; 263 264 // play the track 265 playParms.dwCallback = MAKELONG(getWin32WindowHandle(), 0); 266 error = mciSendCommand(mDeviceId, MCI_PLAY, MCI_FROM|MCI_TO|MCI_NOTIFY, 267 (DWORD_PTR)(LPMCI_PLAY_PARMS)&playParms); 268 269 if(error) 270 { 271 setLastError(error); 272 return(false); 273 } 274 275 setLastError(""); 276 return(true); 277} 278 279bool Win32RedBookDevice::stop() 280{ 281 if(!mAcquired) 282 { 283 setLastError("Device has not been acquired"); 284 return(false); 285 } 286 287 MCI_GENERIC_PARMS genParms; 288 289 U32 error = mciSendCommand(mDeviceId, MCI_STOP, 0, (DWORD_PTR)(LPMCI_GENERIC_PARMS)&genParms); 290 if(error) 291 { 292 setLastError(error); 293 return(false); 294 } 295 296 setLastError(""); 297 return(true); 298} 299 300bool Win32RedBookDevice::getTrackCount(U32 * numTracks) 301{ 302 if(!mAcquired) 303 { 304 setLastError("Device has not been acquired"); 305 return(false); 306 } 307 308 MCI_STATUS_PARMS statusParms; 309 310 statusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; 311 U32 error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms); 312 if(error) 313 { 314 setLastError(error); 315 return(false); 316 } 317 318 *numTracks = statusParms.dwReturn; 319 return(true); 320} 321 322bool Win32RedBookDevice::getVolume(F32 * volume) 323{ 324 if(!mAcquired) 325 { 326 setLastError("Device has not been acquired"); 327 return(false); 328 } 329 330 if(!mVolumeInitialized) 331 { 332 setLastError("Volume failed to initialize"); 333 return(false); 334 } 335 336 U32 vol = 0; 337 if(mUsingMixer) 338 { 339 mixerGetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_GETCONTROLDETAILSF_VALUE); 340 vol = mMixerVolumeValue.dwValue; 341 } 342 else 343 auxGetVolume(mAuxVolumeDeviceId, (unsigned long *)&vol); 344 345 vol &= 0xffff; 346 *volume = F32(vol) / 65535.f; 347 348 setLastError(""); 349 return(true); 350} 351 352bool Win32RedBookDevice::setVolume(F32 volume) 353{ 354 if(!mAcquired) 355 { 356 setLastError("Device has not been acquired"); 357 return(false); 358 } 359 360 if(!mVolumeInitialized) 361 { 362 setLastError("Volume failed to initialize"); 363 return(false); 364 } 365 366 // move into a U32 - left/right U16 volumes 367 U32 vol = U32(volume * 65536.f); 368 if(vol > 0xffff) 369 vol = 0xffff; 370 371 if(mUsingMixer) 372 { 373 mMixerVolumeValue.dwValue = vol; 374 mixerSetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_SETCONTROLDETAILSF_VALUE); 375 } 376 else 377 { 378 vol |= vol << 16; 379 auxSetVolume(mAuxVolumeDeviceId, vol); 380 } 381 382 setLastError(""); 383 return(true); 384} 385 386//------------------------------------------------------------------------------ 387 388void Win32RedBookDevice::openVolume() 389{ 390 setLastError(""); 391 392 // first attempt to get the volume control through the mixer API 393 S32 i; 394 for(i = mixerGetNumDevs() - 1; i >= 0; i--) 395 { 396 // open the mixer 397 if(mixerOpen((HMIXER*)&mVolumeDeviceId, i, 0, 0, 0) == MMSYSERR_NOERROR) 398 { 399 MIXERLINE lineInfo; 400 memset(&lineInfo, 0, sizeof(lineInfo)); 401 lineInfo.cbStruct = sizeof(lineInfo); 402 lineInfo.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC; 403 404 // get the cdaudio line 405 if(mixerGetLineInfo(mVolumeDeviceId, &lineInfo, MIXER_GETLINEINFOF_COMPONENTTYPE) == MMSYSERR_NOERROR) 406 { 407 MIXERLINECONTROLS lineControls; 408 MIXERCONTROL volumeControl; 409 410 memset(&lineControls, 0, sizeof(lineControls)); 411 lineControls.cbStruct = sizeof(lineControls); 412 lineControls.dwLineID = lineInfo.dwLineID; 413 lineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; 414 lineControls.cControls = 1; 415 lineControls.cbmxctrl = sizeof(volumeControl); 416 lineControls.pamxctrl = &volumeControl; 417 418 memset(&volumeControl, 0, sizeof(volumeControl)); 419 volumeControl.cbStruct = sizeof(volumeControl); 420 421 // get the volume control 422 if(mixerGetLineControls(mVolumeDeviceId, &lineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE) == MMSYSERR_NOERROR) 423 { 424 memset(&mMixerVolumeDetails, 0, sizeof(mMixerVolumeDetails)); 425 mMixerVolumeDetails.cbStruct = sizeof(mMixerVolumeDetails); 426 mMixerVolumeDetails.dwControlID = volumeControl.dwControlID; 427 mMixerVolumeDetails.cChannels = 1; 428 mMixerVolumeDetails.cbDetails = sizeof(mMixerVolumeValue); 429 mMixerVolumeDetails.paDetails = &mMixerVolumeValue; 430 431 memset(&mMixerVolumeValue, 0, sizeof(mMixerVolumeValue)); 432 433 // query the current value 434 if(mixerGetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_GETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR) 435 { 436 mUsingMixer = true; 437 mVolumeInitialized = true; 438 mOriginalVolume = mMixerVolumeValue.dwValue; 439 return; 440 } 441 } 442 } 443 } 444 445 mixerClose((HMIXER)mVolumeDeviceId); 446 } 447 448 // try aux 449 for(i = auxGetNumDevs() - 1; i >= 0; i--) 450 { 451 AUXCAPS caps; 452 auxGetDevCaps(i, &caps, sizeof(AUXCAPS)); 453 if((caps.wTechnology == AUXCAPS_CDAUDIO) && (caps.dwSupport & AUXCAPS_VOLUME)) 454 { 455 mAuxVolumeDeviceId = i; 456 mVolumeInitialized = true; 457 mUsingMixer = false; 458 auxGetVolume(i, (unsigned long *)&mOriginalVolume); 459 return; 460 } 461 } 462 463 setLastError("Volume failed to initialize"); 464} 465 466void Win32RedBookDevice::closeVolume() 467{ 468 setLastError(""); 469 if(!mVolumeInitialized) 470 return; 471 472 if(mUsingMixer) 473 { 474 mMixerVolumeValue.dwValue = mOriginalVolume; 475 mixerSetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_SETCONTROLDETAILSF_VALUE); 476 mixerClose((HMIXER)mVolumeDeviceId); 477 } 478 else 479 auxSetVolume(mAuxVolumeDeviceId, mOriginalVolume); 480 481 mVolumeInitialized = false; 482} 483 484//------------------------------------------------------------------------------ 485 486void Win32RedBookDevice::setLastError(const char * error) 487{ 488 RedBook::setLastError(error); 489} 490 491void Win32RedBookDevice::setLastError(U32 errorId) 492{ 493 char buffer[256]; 494 if(!mciGetErrorStringA(errorId, buffer, sizeof(buffer) - 1)) 495 setLastError("Failed to get MCI error string!"); 496 else 497 setLastError(buffer); 498} 499 500