redbooksound.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. //
  2. // redbooksound.cpp
  3. //
  4. // SoundEngine support for redbook audio.
  5. //
  6. #include "pch.h"
  7. #include "soundbase.h"
  8. #include "redbooksound.h"
  9. #include "ds3dutil.h"
  10. using std::list;
  11. namespace SoundEngine {
  12. //
  13. // A class dedicated to controling the volume of the CD player(s) on a system
  14. //
  15. class CDVolume
  16. {
  17. public:
  18. enum { c_nMaxChannels = 8 };
  19. private:
  20. struct MixerLineData
  21. {
  22. HMIXEROBJ hmixer;
  23. DWORD cChannels;
  24. MIXERCONTROL mixercontrol;
  25. MIXERCONTROLDETAILS_UNSIGNED vmixercontrolsOld[c_nMaxChannels];
  26. };
  27. public:
  28. list<MixerLineData> m_listMixerLines;
  29. CDVolume()
  30. {
  31. UINT uMaxDevices = mixerGetNumDevs();
  32. // find all of the CD line controls and store their ID and starting volume
  33. for (UINT uDeviceID = 0; uDeviceID < uMaxDevices; ++uDeviceID)
  34. {
  35. HMIXEROBJ hmixer;
  36. //
  37. // open the mixer in question
  38. //
  39. if (MMSYSERR_NOERROR != mixerOpen((LPHMIXER)&hmixer, uDeviceID, NULL, NULL, MIXER_OBJECTF_MIXER))
  40. {
  41. debugf("Failed to open mixer %d\n", uDeviceID);
  42. continue;
  43. }
  44. //
  45. // look for a mixer line attached to a CD
  46. //
  47. MIXERLINE mixerline;
  48. mixerline.cbStruct = sizeof(mixerline);
  49. mixerline.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
  50. if (MMSYSERR_NOERROR != mixerGetLineInfo(hmixer, &mixerline, MIXER_GETLINEINFOF_COMPONENTTYPE)
  51. || mixerline.cControls == 0)
  52. {
  53. debugf("Failed to find CD line on mixer %d\n", uDeviceID);
  54. mixerClose((HMIXER)hmixer);
  55. continue;
  56. }
  57. //
  58. // look for a volume control for that mixer line
  59. //
  60. MIXERLINECONTROLS mixerlinecontrols;
  61. MixerLineData mixerlinedata;
  62. mixerlinecontrols.cbStruct = sizeof(mixerlinecontrols);
  63. mixerlinecontrols.dwLineID = mixerline.dwLineID;
  64. mixerlinecontrols.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
  65. mixerlinecontrols.cControls = 1;
  66. mixerlinecontrols.cbmxctrl = sizeof(MIXERCONTROL);
  67. mixerlinecontrols.pamxctrl = &(mixerlinedata.mixercontrol);
  68. mixerlinedata.hmixer = hmixer;
  69. mixerlinedata.cChannels = mixerline.cChannels;
  70. if (MMSYSERR_NOERROR !=
  71. mixerGetLineControls(hmixer, &mixerlinecontrols, MIXER_GETLINECONTROLSF_ONEBYTYPE))
  72. {
  73. debugf("Failed to find CD volume fader on mixer %d\n", uDeviceID);
  74. mixerClose((HMIXER)hmixer);
  75. continue;
  76. }
  77. // don't try to use more than 8 channels (not likely to be a problem)
  78. if (mixerlinedata.cChannels > c_nMaxChannels)
  79. mixerlinedata.cChannels = 1;
  80. //
  81. // Get the initial volume settings (so we can restore them when we are done)
  82. //
  83. MIXERCONTROLDETAILS mixercontroldetails;
  84. mixercontroldetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
  85. mixercontroldetails.dwControlID = mixerlinedata.mixercontrol.dwControlID;
  86. mixercontroldetails.cChannels = mixerlinedata.cChannels;
  87. mixercontroldetails.cMultipleItems = 0;
  88. mixercontroldetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
  89. mixercontroldetails.paDetails = &(mixerlinedata.vmixercontrolsOld);
  90. if (MMSYSERR_NOERROR !=
  91. mixerGetControlDetails(hmixer, &mixercontroldetails, MIXER_GETCONTROLDETAILSF_VALUE))
  92. {
  93. debugf("Failed to get previous volume levels for mixer %d\n", uDeviceID);
  94. mixerClose((HMIXER)hmixer);
  95. continue;
  96. }
  97. // add this to the list of volume controls
  98. m_listMixerLines.push_back(mixerlinedata);
  99. }
  100. }
  101. ~CDVolume()
  102. {
  103. // restore the volume settings for all of the CD players
  104. while (!m_listMixerLines.empty())
  105. {
  106. MixerLineData& mixerlinedata = m_listMixerLines.back();
  107. MIXERCONTROLDETAILS mixercontroldetails;
  108. mixercontroldetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
  109. mixercontroldetails.dwControlID = mixerlinedata.mixercontrol.dwControlID;
  110. mixercontroldetails.cChannels = mixerlinedata.cChannels;
  111. mixercontroldetails.cMultipleItems = 0;
  112. mixercontroldetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
  113. mixercontroldetails.paDetails = &(mixerlinedata.vmixercontrolsOld);
  114. ZVerify(mixerSetControlDetails(mixerlinedata.hmixer, &mixercontroldetails, MIXER_SETCONTROLDETAILSF_VALUE)
  115. == MMSYSERR_NOERROR);
  116. ZVerify(mixerClose((HMIXER)mixerlinedata.hmixer) == MMSYSERR_NOERROR);
  117. m_listMixerLines.pop_back();
  118. }
  119. }
  120. HRESULT SetGain(float fGain)
  121. {
  122. if (fGain > 0.0f)
  123. {
  124. return E_INVALIDARG;
  125. }
  126. const float fMinGain = -40;
  127. float fClippedGain = max(fMinGain, fGain);
  128. // set the volume on every CD player (since we can't map to the right one)
  129. // restore the volume settings for all of the CD players
  130. std::list<MixerLineData>::iterator mixerline;
  131. for (mixerline = m_listMixerLines.begin(); mixerline != m_listMixerLines.end(); ++mixerline)
  132. {
  133. MixerLineData& mixerlinedata = *mixerline;
  134. // translate the gain to a linear volume setting.
  135. MIXERCONTROLDETAILS_UNSIGNED volume;
  136. volume.dwValue = (DWORD)(mixerlinedata.mixercontrol.Bounds.dwMinimum
  137. + (mixerlinedata.mixercontrol.Bounds.dwMaximum - mixerlinedata.mixercontrol.Bounds.dwMinimum)
  138. * (1 - fClippedGain/fMinGain));
  139. // set the volume for this control
  140. MIXERCONTROLDETAILS mixercontroldetails;
  141. mixercontroldetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
  142. mixercontroldetails.dwControlID = mixerlinedata.mixercontrol.dwControlID;
  143. mixercontroldetails.cChannels = 1;
  144. mixercontroldetails.cMultipleItems = 0;
  145. mixercontroldetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
  146. mixercontroldetails.paDetails = &volume;
  147. ZVerify(mixerSetControlDetails(mixerlinedata.hmixer, &mixercontroldetails, MIXER_SETCONTROLDETAILSF_VALUE)
  148. == MMSYSERR_NOERROR);
  149. }
  150. return S_OK;
  151. }
  152. };
  153. //
  154. // An implementation of a generic disk player wrapper that wraps a CD player
  155. //
  156. class DiskPlayerImpl : public IDiskPlayer, private WorkerThread
  157. {
  158. UINT m_idDevice;
  159. // the device name
  160. ZString m_strElementName;
  161. // a critical section controling access to the queued track and current track
  162. CriticalSection m_csTrack;
  163. enum { trackEmpty = -1, trackStop = 0 };
  164. // the next track to play (trackEmpty if the queue is empty, or trackStop
  165. // if a stop is queued).
  166. volatile int m_nQueuedTrack;
  167. // the current track of the CD player (or trackStop if the cd player is stopped).
  168. volatile int m_nCurrentTrack;
  169. CDVolume m_cdvolume;
  170. public:
  171. DiskPlayerImpl() :
  172. m_nQueuedTrack(trackEmpty),
  173. m_nCurrentTrack(trackStop)
  174. {
  175. };
  176. ~DiskPlayerImpl()
  177. {
  178. StopThread();
  179. }
  180. HRESULT Init(const ZString& strDevice)
  181. {
  182. DWORD dwError;
  183. // try to open the device
  184. MCI_OPEN_PARMS mciOpenParms;
  185. DWORD dwFlags;
  186. mciOpenParms.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_CD_AUDIO;
  187. dwFlags = MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID;
  188. // translate the device name into an element name, if appropriate
  189. if (!strDevice.IsEmpty())
  190. {
  191. m_strElementName = TranslateElementName(strDevice);
  192. mciOpenParms.lpstrElementName = m_strElementName;
  193. dwFlags |= MCI_OPEN_ELEMENT;
  194. }
  195. // try opening it to make sure it exists
  196. dwError = mciSendCommand(NULL, MCI_OPEN, dwFlags, (UINT_PTR)&mciOpenParms);
  197. if (dwError)
  198. {
  199. char cbError[256];
  200. mciGetErrorString(dwError, cbError, 256);
  201. debugf("Open failed for CD Audio device '%': %s\n", (const char*)strDevice, cbError);
  202. return E_FAIL;
  203. }
  204. mciSendCommand(mciOpenParms.wDeviceID, MCI_CLOSE, 0, NULL);
  205. // start the background (io) thread
  206. StartThread(THREAD_PRIORITY_NORMAL, 200);
  207. return S_OK;
  208. }
  209. void ThreadInit()
  210. {
  211. DWORD dwError;
  212. // try to open the device
  213. MCI_OPEN_PARMS mciOpenParms;
  214. DWORD dwFlags;
  215. ZString strElementName;
  216. mciOpenParms.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_CD_AUDIO;
  217. dwFlags = MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID;
  218. if (!m_strElementName.IsEmpty())
  219. {
  220. mciOpenParms.lpstrElementName = m_strElementName;
  221. dwFlags |= MCI_OPEN_ELEMENT;
  222. }
  223. dwError = mciSendCommand(NULL, MCI_OPEN, dwFlags, (UINT_PTR)&mciOpenParms);
  224. if (dwError)
  225. {
  226. char cbError[256];
  227. mciGetErrorString(dwError, cbError, 256);
  228. debugf("Open failed for CD Audio device '%': %s\n", (const char*)m_strElementName, cbError);
  229. }
  230. m_idDevice = mciOpenParms.wDeviceID;
  231. // Set the time format to track/minute/second/frame (TMSF).
  232. MCI_SET_PARMS mciSetParms;
  233. mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF;
  234. dwError = mciSendCommand(m_idDevice, MCI_SET, MCI_SET_TIME_FORMAT, (UINT_PTR)&mciSetParms);
  235. if (dwError)
  236. {
  237. char cbError[256];
  238. mciGetErrorString(dwError, cbError, 256);
  239. debugf("Set format failed for CD Audio device: %s\n", cbError);
  240. }
  241. }
  242. // called for background thread; changes state (since that can involve
  243. // slow blocking calls) and updates the state (since the state is read
  244. // frequently and querying the device is a bit slow)
  245. bool ThreadIteration()
  246. {
  247. // fetch the current requested state and copy it to the current state
  248. int nRequestedTrack;
  249. int nOldTrack;
  250. {
  251. CriticalSectionLock lock(m_csTrack);
  252. nOldTrack = m_nCurrentTrack;
  253. nRequestedTrack = m_nQueuedTrack;
  254. if (nRequestedTrack != trackEmpty)
  255. m_nCurrentTrack = nRequestedTrack;
  256. m_nQueuedTrack = trackEmpty;
  257. }
  258. // if there is a new state requested, make that change
  259. if (nRequestedTrack != trackEmpty)
  260. {
  261. if (nRequestedTrack == trackStop)
  262. StopImpl();
  263. else
  264. PlayImpl(nRequestedTrack);
  265. }
  266. // otherwise, just update the current state
  267. else
  268. {
  269. if ((nOldTrack != trackStop) && (IsPlayingImpl() != S_OK))
  270. {
  271. CriticalSectionLock lock(m_csTrack);
  272. m_nCurrentTrack = trackStop;
  273. }
  274. }
  275. return true;
  276. }
  277. void ThreadCleanup()
  278. {
  279. StopImpl();
  280. mciSendCommand(m_idDevice, MCI_CLOSE, 0, NULL);
  281. }
  282. ZString TranslateElementName(const ZString& strDevice)
  283. {
  284. if (strDevice.Find(':') != -1)
  285. {
  286. return strDevice;
  287. }
  288. else
  289. {
  290. // get a list of all of the drives on the system
  291. char cTemp;
  292. int nDrivesStringLength = GetLogicalDriveStrings(1, &cTemp);
  293. if (nDrivesStringLength == 0)
  294. {
  295. ZError("Error getting drives list\n");
  296. return strDevice;
  297. }
  298. char* cbDrives = (char*)_alloca(nDrivesStringLength);
  299. nDrivesStringLength = GetLogicalDriveStrings(nDrivesStringLength, cbDrives);
  300. if (nDrivesStringLength == 0)
  301. {
  302. ZError("Error getting drives list\n");
  303. return strDevice;
  304. }
  305. // search through the list of drives looking for a CD-ROM who's volume
  306. // label matches strDevice
  307. while (cbDrives[0] != '\0')
  308. {
  309. const int c_nVolumeNameLength = 1024;
  310. char cbVolumeName[c_nVolumeNameLength];
  311. if (GetDriveType(cbDrives) == DRIVE_CDROM
  312. && GetVolumeInformation(cbDrives, cbVolumeName,
  313. c_nVolumeNameLength, NULL, NULL, NULL, NULL, 0))
  314. {
  315. if (_stricmp(strDevice, cbVolumeName) == 0)
  316. {
  317. return cbDrives;
  318. }
  319. }
  320. cbDrives += strlen(cbDrives) + 1;
  321. }
  322. return strDevice;
  323. }
  324. }
  325. // plays one track
  326. virtual HRESULT Play(int nTrack)
  327. {
  328. CriticalSectionLock lock(m_csTrack);
  329. if (nTrack <= 0)
  330. {
  331. return E_INVALIDARG;
  332. }
  333. m_nQueuedTrack = nTrack;
  334. return S_OK;
  335. }
  336. // stops the CD player
  337. virtual HRESULT Stop()
  338. {
  339. CriticalSectionLock lock(m_csTrack);
  340. m_nQueuedTrack = trackStop;
  341. return S_OK;
  342. }
  343. // returns S_OK if the CD player is playing, S_FALSE otherwise
  344. virtual HRESULT IsPlaying()
  345. {
  346. CriticalSectionLock lock(m_csTrack);
  347. int nTrack;
  348. if (m_nQueuedTrack != trackEmpty)
  349. {
  350. nTrack = m_nQueuedTrack;
  351. }
  352. else
  353. {
  354. nTrack = m_nCurrentTrack;
  355. }
  356. return (nTrack == trackStop) ? S_FALSE : S_OK;
  357. }
  358. // returns the current track of the CD player
  359. virtual HRESULT GetCurrentTrack(int& nTrack)
  360. {
  361. CriticalSectionLock lock(m_csTrack);
  362. if (m_nQueuedTrack != trackEmpty)
  363. {
  364. nTrack = m_nQueuedTrack;
  365. }
  366. else
  367. {
  368. nTrack = m_nCurrentTrack;
  369. }
  370. return (nTrack != trackStop) ? S_OK : E_FAIL;
  371. }
  372. // sets the gain on the CD player, from 0 to -100 dB
  373. virtual HRESULT SetGain(float fGain)
  374. {
  375. return m_cdvolume.SetGain(fGain);
  376. };
  377. // plays one track (blocking)
  378. HRESULT PlayImpl(int nTrack)
  379. {
  380. MCI_PLAY_PARMS mciPlayParms;
  381. mciPlayParms.dwFrom = MCI_MAKE_TMSF(nTrack, 0, 0, 0);
  382. mciPlayParms.dwTo = MCI_MAKE_TMSF(nTrack + 1, 0, 0, 0);
  383. DWORD dwError = mciSendCommand(m_idDevice, MCI_PLAY, MCI_FROM | MCI_TO, (UINT_PTR)&mciPlayParms);
  384. // if the track is out of range, retry without the stop point in case
  385. // this is the last track on the CD. (review: we could store it, but
  386. // this case handles switching the CD)
  387. if (dwError == MCIERR_OUTOFRANGE)
  388. dwError = mciSendCommand(m_idDevice, MCI_PLAY, MCI_FROM, (UINT_PTR)&mciPlayParms);
  389. // this is the highest track on the CD
  390. if (dwError)
  391. {
  392. char cbError[256];
  393. mciGetErrorString(dwError, cbError, 256);
  394. debugf("Play track %d failed for CD Audio device: %s\n", nTrack, cbError);
  395. return E_FAIL;
  396. }
  397. return S_OK;
  398. };
  399. // stops the CD player (blocking)
  400. HRESULT StopImpl()
  401. {
  402. DWORD dwError = mciSendCommand(m_idDevice, MCI_STOP, 0, NULL);
  403. if (dwError)
  404. {
  405. char cbError[256];
  406. mciGetErrorString(dwError, cbError, 256);
  407. debugf("Stop failed for CD Audio device: %s\n", cbError);
  408. return E_FAIL;
  409. }
  410. return S_OK;
  411. };
  412. // returns S_OK if the CD player is playing, S_FALSE otherwise (blocking)
  413. HRESULT IsPlayingImpl()
  414. {
  415. MCI_STATUS_PARMS mciStatusParams;
  416. mciStatusParams.dwItem = MCI_STATUS_MODE;
  417. DWORD dwError = mciSendCommand(m_idDevice, MCI_STATUS, MCI_STATUS_ITEM, (UINT_PTR)&mciStatusParams);
  418. if (dwError)
  419. {
  420. char cbError[256];
  421. mciGetErrorString(dwError, cbError, 256);
  422. debugf("Status:Mode failed for CD Audio device: %s\n", cbError);
  423. return E_FAIL;
  424. }
  425. return (mciStatusParams.dwReturn == MCI_MODE_PLAY) ? S_OK : S_FALSE;
  426. };
  427. // returns the current track of the CD player (blocking)
  428. HRESULT GetCurrentTrackImpl(int& nTrack)
  429. {
  430. MCI_STATUS_PARMS mciStatusParams;
  431. mciStatusParams.dwItem = MCI_STATUS_CURRENT_TRACK;
  432. DWORD dwError = mciSendCommand(m_idDevice, MCI_STATUS, MCI_STATUS_ITEM, (UINT_PTR)&mciStatusParams);
  433. if (dwError)
  434. {
  435. char cbError[256];
  436. mciGetErrorString(dwError, cbError, 256);
  437. debugf("Status:Track failed for CD Audio device: %s\n", cbError);
  438. return E_FAIL;
  439. }
  440. nTrack = mciStatusParams.dwReturn;
  441. return S_OK;
  442. };
  443. };
  444. // Creates a new disk player object (CD, minidisk, etc.). strDevice can be
  445. // empty (choose any device), a path ("E:\"), or a volume label.
  446. HRESULT CreateDiskPlayer(TRef<IDiskPlayer>& pdiskplayer, const ZString& strDevice)
  447. {
  448. TRef<DiskPlayerImpl> pdiskplayerimpl = new DiskPlayerImpl();
  449. HRESULT hr = pdiskplayerimpl->Init(strDevice);
  450. if (SUCCEEDED(hr))
  451. pdiskplayer = pdiskplayerimpl;
  452. return hr;
  453. };
  454. class DummyDiskPlayer : public IDiskPlayer
  455. {
  456. public:
  457. // plays one track
  458. virtual HRESULT Play(int nTrack)
  459. {
  460. return S_OK;
  461. };
  462. // stops the CD player
  463. virtual HRESULT Stop()
  464. {
  465. return S_OK;
  466. };
  467. // returns S_OK if the CD player is playing
  468. virtual HRESULT IsPlaying()
  469. {
  470. return S_FALSE;
  471. };
  472. // returns the current track of the CD player
  473. virtual HRESULT GetCurrentTrack(int& nTrack)
  474. {
  475. nTrack = 1;
  476. return S_OK;
  477. };
  478. // sets the gain on the CD player, from 0 to -100 dB
  479. virtual HRESULT SetGain(float fGain)
  480. {
  481. return S_OK;
  482. };
  483. };
  484. // Creates a new disk player object that only has stubs for each of the calls
  485. HRESULT CreateDummyDiskPlayer(TRef<IDiskPlayer>& pdiskplayer)
  486. {
  487. pdiskplayer = new DummyDiskPlayer();
  488. return S_OK;
  489. }
  490. // a template for redbook audio
  491. class RedbookSoundTemplate : public ISoundTemplate
  492. {
  493. private:
  494. TRef<IDiskPlayer> m_pdiskplayer;
  495. int m_nTrack;
  496. //
  497. // Playback controls
  498. //
  499. class RedbookSoundInstance : public ISoundInstance
  500. {
  501. TRef<IDiskPlayer> m_pdiskplayer;
  502. int m_nTrack;
  503. public:
  504. RedbookSoundInstance(TRef<IDiskPlayer> pdiskplayer, int nTrack) :
  505. m_pdiskplayer(pdiskplayer),
  506. m_nTrack(nTrack)
  507. {
  508. pdiskplayer->Play(nTrack);
  509. }
  510. // Stops the sound. If bForceNow is true the sound will stop ASAP,
  511. // possibly popping. If it is false some sounds may play a trail-off
  512. // sound or fade away.
  513. virtual HRESULT Stop(bool bForceNow = false)
  514. {
  515. HRESULT hr = IsPlaying();
  516. if (hr == S_OK)
  517. {
  518. return m_pdiskplayer->Stop();
  519. }
  520. else if (SUCCEEDED(hr))
  521. return S_OK;
  522. else
  523. return hr;
  524. }
  525. // returns S_OK if the sound is currently playing, S_FALSE otherwise.
  526. virtual HRESULT IsPlaying()
  527. {
  528. HRESULT hr = m_pdiskplayer->IsPlaying();
  529. if (hr == S_OK)
  530. {
  531. // it's playing, but is it playing our track or something else?
  532. int nTrack;
  533. hr = m_pdiskplayer->GetCurrentTrack(nTrack);
  534. if (FAILED(hr))
  535. return hr;
  536. return (nTrack == m_nTrack) ? S_OK : S_FALSE;
  537. }
  538. else
  539. return hr;
  540. }
  541. // Gets an event which fires when the sound finishes playing (for any
  542. // reason)
  543. virtual IEventSource* GetFinishEventSource()
  544. {
  545. ZError("NYI");
  546. return NULL;
  547. }
  548. // Gets an interface for tweaking the sound, if supported, NULL otherwise.
  549. virtual TRef<ISoundTweakable> GetISoundTweakable()
  550. {
  551. return NULL;
  552. }
  553. virtual TRef<ISoundTweakable3D> GetISoundTweakable3D()
  554. {
  555. return NULL;
  556. }
  557. };
  558. public:
  559. // tries to initialize the object with the given file.
  560. HRESULT Init(TRef<IDiskPlayer> pdiskplayer, int nTrack)
  561. {
  562. if (!pdiskplayer)
  563. {
  564. ZAssert(false);
  565. return E_POINTER;
  566. }
  567. if (nTrack <= 0)
  568. {
  569. ZAssert(false);
  570. return E_INVALIDARG;
  571. }
  572. m_pdiskplayer = pdiskplayer;
  573. m_nTrack = nTrack;
  574. return S_OK;
  575. };
  576. // Creates a new instance of the given sound
  577. virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
  578. ISoundBufferSource* pbufferSource, ISoundPositionSource* psource = NULL)
  579. {
  580. if (!pbufferSource)
  581. {
  582. ZAssert(false);
  583. return E_POINTER;
  584. }
  585. if (psource)
  586. {
  587. ZAssert(false);
  588. return E_NOTIMPL;
  589. }
  590. psoundNew = new RedbookSoundInstance(m_pdiskplayer, m_nTrack);
  591. return S_OK;
  592. }
  593. };
  594. // creates a sound template representing a redbook audio track
  595. HRESULT CreateRedbookSoundTemplate(TRef<ISoundTemplate>& pstDest, TRef<IDiskPlayer> pdiskplayer, int nTrack)
  596. {
  597. TRef<RedbookSoundTemplate> ptemplate = new RedbookSoundTemplate();
  598. HRESULT hr = ptemplate->Init(pdiskplayer, nTrack);
  599. if (ZSucceeded(hr))
  600. pstDest = ptemplate;
  601. return hr;
  602. }
  603. };