2
0

winRedbook.cpp 13 KB

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