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 device->mDeviceName = new char[dStrlen(str) + 1]; 88 dStrcpy(device->mDeviceName, str); 89 90 RedBook::installDevice(device); 91 } 92 str += dStrlen(str) + 1; 93 } 94 95 delete [] buf; 96} 97 98void handleRedBookCallback(U32 code, U32 deviceId) 99{ 100 if(code != MCI_NOTIFY_SUCCESSFUL) 101 return; 102 103 Win32RedBookDevice * device = dynamic_cast<Win32RedBookDevice*>(RedBook::getCurrentDevice()); 104 if(!device) 105 return; 106 107 if(device->getDeviceId() != deviceId) 108 return; 109 110 // only installed callback on play (no callback if play is aborted) 111 RedBook::handleCallback(RedBook::PlayFinished); 112} 113 114//------------------------------------------------------------------------------ 115// Class: Win32RedBookDevice 116//------------------------------------------------------------------------------ 117Win32RedBookDevice::Win32RedBookDevice() 118{ 119 mVolumeInitialized = false; 120} 121 122Win32RedBookDevice::~Win32RedBookDevice() 123{ 124 close(); 125} 126 127U32 Win32RedBookDevice::getDeviceId() 128{ 129 return(mDeviceId); 130} 131 132bool Win32RedBookDevice::open() 133{ 134 if(mAcquired) 135 { 136 setLastError("Device is already open."); 137 return(false); 138 } 139 140 U32 error; 141 142 // open the device 143 MCI_OPEN_PARMS openParms; 144#ifdef UNICODE 145 openParms.lpstrDeviceType = (LPCWSTR)MCI_DEVTYPE_CD_AUDIO; 146 147 UTF16 buf[512]; 148 convertUTF8toUTF16((UTF8 *)mDeviceName, buf); 149 openParms.lpstrElementName = buf; 150#else 151 openParms.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO; 152 openParms.lpstrElementName = mDeviceName; 153#endif 154 155 error = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, (DWORD_PTR)(LPMCI_OPEN_PARMS)&openParms); 156 if(error) 157 { 158 // attempt to open as a shared device 159 error = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID|MCI_OPEN_SHAREABLE, (DWORD_PTR)(LPMCI_OPEN_PARMS)&openParms); 160 if(error) 161 { 162 setLastError(error); 163 return(false); 164 } 165 } 166 167 // set time mode to milliseconds 168 MCI_SET_PARMS setParms; 169 setParms.dwTimeFormat = MCI_FORMAT_MILLISECONDS; 170 171 error = mciSendCommand(openParms.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPMCI_SET_PARMS)&setParms); 172 if(error) 173 { 174 setLastError(error); 175 return(false); 176 } 177 178 // 179 mDeviceId = openParms.wDeviceID; 180 mAcquired = true; 181 182 openVolume(); 183 setLastError(""); 184 return(true); 185} 186 187bool Win32RedBookDevice::close() 188{ 189 if(!mAcquired) 190 { 191 setLastError("Device has not been acquired"); 192 return(false); 193 } 194 195 stop(); 196 197 U32 error; 198 199 MCI_GENERIC_PARMS closeParms; 200 error = mciSendCommand(mDeviceId, MCI_CLOSE, 0, (DWORD_PTR)(LPMCI_GENERIC_PARMS)&closeParms); 201 if(error) 202 { 203 setLastError(error); 204 return(false); 205 } 206 207 mAcquired = false; 208 closeVolume(); 209 setLastError(""); 210 return(true); 211} 212 213bool Win32RedBookDevice::play(U32 track) 214{ 215 if(!mAcquired) 216 { 217 setLastError("Device has not been acquired"); 218 return(false); 219 } 220 221 U32 numTracks; 222 if(!getTrackCount(&numTracks)) 223 return(false); 224 225 if(track >= numTracks) 226 { 227 setLastError("Track index is out of range"); 228 return(false); 229 } 230 231 MCI_STATUS_PARMS statusParms; 232 233 // get track start time 234 statusParms.dwItem = MCI_STATUS_POSITION; 235 statusParms.dwTrack = track + 1; 236 237 U32 error; 238 error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK|MCI_WAIT, 239 (DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms); 240 241 if(error) 242 { 243 setLastError(error); 244 return(false); 245 } 246 247 MCI_PLAY_PARMS playParms; 248 playParms.dwFrom = statusParms.dwReturn; 249 250 // get track end time 251 statusParms.dwItem = MCI_STATUS_LENGTH; 252 error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK|MCI_WAIT, 253 (DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms); 254 255 if(error) 256 { 257 setLastError(error); 258 return(false); 259 } 260 261 playParms.dwTo = playParms.dwFrom + statusParms.dwReturn; 262 263 // play the track 264 playParms.dwCallback = MAKELONG(getWin32WindowHandle(), 0); 265 error = mciSendCommand(mDeviceId, MCI_PLAY, MCI_FROM|MCI_TO|MCI_NOTIFY, 266 (DWORD_PTR)(LPMCI_PLAY_PARMS)&playParms); 267 268 if(error) 269 { 270 setLastError(error); 271 return(false); 272 } 273 274 setLastError(""); 275 return(true); 276} 277 278bool Win32RedBookDevice::stop() 279{ 280 if(!mAcquired) 281 { 282 setLastError("Device has not been acquired"); 283 return(false); 284 } 285 286 MCI_GENERIC_PARMS genParms; 287 288 U32 error = mciSendCommand(mDeviceId, MCI_STOP, 0, (DWORD_PTR)(LPMCI_GENERIC_PARMS)&genParms); 289 if(error) 290 { 291 setLastError(error); 292 return(false); 293 } 294 295 setLastError(""); 296 return(true); 297} 298 299bool Win32RedBookDevice::getTrackCount(U32 * numTracks) 300{ 301 if(!mAcquired) 302 { 303 setLastError("Device has not been acquired"); 304 return(false); 305 } 306 307 MCI_STATUS_PARMS statusParms; 308 309 statusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; 310 U32 error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms); 311 if(error) 312 { 313 setLastError(error); 314 return(false); 315 } 316 317 *numTracks = statusParms.dwReturn; 318 return(true); 319} 320 321bool Win32RedBookDevice::getVolume(F32 * volume) 322{ 323 if(!mAcquired) 324 { 325 setLastError("Device has not been acquired"); 326 return(false); 327 } 328 329 if(!mVolumeInitialized) 330 { 331 setLastError("Volume failed to initialize"); 332 return(false); 333 } 334 335 U32 vol = 0; 336 if(mUsingMixer) 337 { 338 mixerGetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_GETCONTROLDETAILSF_VALUE); 339 vol = mMixerVolumeValue.dwValue; 340 } 341 else 342 auxGetVolume(mAuxVolumeDeviceId, (unsigned long *)&vol); 343 344 vol &= 0xffff; 345 *volume = F32(vol) / 65535.f; 346 347 setLastError(""); 348 return(true); 349} 350 351bool Win32RedBookDevice::setVolume(F32 volume) 352{ 353 if(!mAcquired) 354 { 355 setLastError("Device has not been acquired"); 356 return(false); 357 } 358 359 if(!mVolumeInitialized) 360 { 361 setLastError("Volume failed to initialize"); 362 return(false); 363 } 364 365 // move into a U32 - left/right U16 volumes 366 U32 vol = U32(volume * 65536.f); 367 if(vol > 0xffff) 368 vol = 0xffff; 369 370 if(mUsingMixer) 371 { 372 mMixerVolumeValue.dwValue = vol; 373 mixerSetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_SETCONTROLDETAILSF_VALUE); 374 } 375 else 376 { 377 vol |= vol << 16; 378 auxSetVolume(mAuxVolumeDeviceId, vol); 379 } 380 381 setLastError(""); 382 return(true); 383} 384 385//------------------------------------------------------------------------------ 386 387void Win32RedBookDevice::openVolume() 388{ 389 setLastError(""); 390 391 // first attempt to get the volume control through the mixer API 392 S32 i; 393 for(i = mixerGetNumDevs() - 1; i >= 0; i--) 394 { 395 // open the mixer 396 if(mixerOpen((HMIXER*)&mVolumeDeviceId, i, 0, 0, 0) == MMSYSERR_NOERROR) 397 { 398 MIXERLINE lineInfo; 399 memset(&lineInfo, 0, sizeof(lineInfo)); 400 lineInfo.cbStruct = sizeof(lineInfo); 401 lineInfo.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC; 402 403 // get the cdaudio line 404 if(mixerGetLineInfo(mVolumeDeviceId, &lineInfo, MIXER_GETLINEINFOF_COMPONENTTYPE) == MMSYSERR_NOERROR) 405 { 406 MIXERLINECONTROLS lineControls; 407 MIXERCONTROL volumeControl; 408 409 memset(&lineControls, 0, sizeof(lineControls)); 410 lineControls.cbStruct = sizeof(lineControls); 411 lineControls.dwLineID = lineInfo.dwLineID; 412 lineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; 413 lineControls.cControls = 1; 414 lineControls.cbmxctrl = sizeof(volumeControl); 415 lineControls.pamxctrl = &volumeControl; 416 417 memset(&volumeControl, 0, sizeof(volumeControl)); 418 volumeControl.cbStruct = sizeof(volumeControl); 419 420 // get the volume control 421 if(mixerGetLineControls(mVolumeDeviceId, &lineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE) == MMSYSERR_NOERROR) 422 { 423 memset(&mMixerVolumeDetails, 0, sizeof(mMixerVolumeDetails)); 424 mMixerVolumeDetails.cbStruct = sizeof(mMixerVolumeDetails); 425 mMixerVolumeDetails.dwControlID = volumeControl.dwControlID; 426 mMixerVolumeDetails.cChannels = 1; 427 mMixerVolumeDetails.cbDetails = sizeof(mMixerVolumeValue); 428 mMixerVolumeDetails.paDetails = &mMixerVolumeValue; 429 430 memset(&mMixerVolumeValue, 0, sizeof(mMixerVolumeValue)); 431 432 // query the current value 433 if(mixerGetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_GETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR) 434 { 435 mUsingMixer = true; 436 mVolumeInitialized = true; 437 mOriginalVolume = mMixerVolumeValue.dwValue; 438 return; 439 } 440 } 441 } 442 } 443 444 mixerClose((HMIXER)mVolumeDeviceId); 445 } 446 447 // try aux 448 for(i = auxGetNumDevs() - 1; i >= 0; i--) 449 { 450 AUXCAPS caps; 451 auxGetDevCaps(i, &caps, sizeof(AUXCAPS)); 452 if((caps.wTechnology == AUXCAPS_CDAUDIO) && (caps.dwSupport & AUXCAPS_VOLUME)) 453 { 454 mAuxVolumeDeviceId = i; 455 mVolumeInitialized = true; 456 mUsingMixer = false; 457 auxGetVolume(i, (unsigned long *)&mOriginalVolume); 458 return; 459 } 460 } 461 462 setLastError("Volume failed to initialize"); 463} 464 465void Win32RedBookDevice::closeVolume() 466{ 467 setLastError(""); 468 if(!mVolumeInitialized) 469 return; 470 471 if(mUsingMixer) 472 { 473 mMixerVolumeValue.dwValue = mOriginalVolume; 474 mixerSetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_SETCONTROLDETAILSF_VALUE); 475 mixerClose((HMIXER)mVolumeDeviceId); 476 } 477 else 478 auxSetVolume(mAuxVolumeDeviceId, mOriginalVolume); 479 480 mVolumeInitialized = false; 481} 482 483//------------------------------------------------------------------------------ 484 485void Win32RedBookDevice::setLastError(const char * error) 486{ 487 RedBook::setLastError(error); 488} 489 490void Win32RedBookDevice::setLastError(U32 errorId) 491{ 492 char buffer[256]; 493 if(!mciGetErrorStringA(errorId, buffer, sizeof(buffer) - 1)) 494 setLastError("Failed to get MCI error string!"); 495 else 496 setLastError(buffer); 497} 498 499
