Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2786 lines
96 KiB

  1. // Copyright (c) 1998-1999 Microsoft Corporation
  2. // audpath.cpp : Implementation of CAudioPath
  3. #include <windows.h>
  4. #include <mmsystem.h>
  5. #include <dsoundp.h>
  6. #include "dmsegobj.h"
  7. #include "dmgraph.h"
  8. #include "dmperf.h"
  9. #include "dmusici.h"
  10. #include "..\shared\Validate.h"
  11. #include "audpath.h"
  12. #include "debug.h"
  13. #include "..\shared\dmusiccp.h"
  14. #define ASSERT assert
  15. CBufferNode::CBufferNode()
  16. {
  17. m_lActivateCount = 0;
  18. m_cRef = 1;
  19. m_pBuffer = NULL;
  20. m_pManager = NULL;
  21. }
  22. CBufferNode::~CBufferNode()
  23. {
  24. FinalDeactivate();
  25. if (m_pManager)
  26. {
  27. m_pManager->Remove(this);
  28. }
  29. }
  30. ULONG CBufferNode::AddRef()
  31. {
  32. return InterlockedIncrement(&m_cRef);
  33. }
  34. ULONG CBufferNode::Release()
  35. {
  36. if (!InterlockedDecrement(&m_cRef))
  37. {
  38. delete this;
  39. return 0;
  40. }
  41. return m_cRef;
  42. }
  43. HRESULT CBufferNode::Activate(BOOL fActivate)
  44. {
  45. HRESULT hr = S_OK;
  46. if (m_pBuffer)
  47. {
  48. if (fActivate)
  49. {
  50. if (!m_lActivateCount)
  51. {
  52. // Don't bother starting if the primary buffer.
  53. if (SUCCEEDED(hr) && !(m_BufferHeader.dwFlags & DMUS_BUFFERF_PRIMARY))
  54. {
  55. TraceI(2,"Play buffer %lx\n",m_pBuffer);
  56. hr = m_pBuffer->Play(0,0,DSBPLAY_LOOPING);
  57. #ifdef DBG
  58. if (FAILED(hr))
  59. {
  60. Trace(1,"Error: Activate on audiopath failed because buffer failed Play command.\n");
  61. }
  62. #endif
  63. }
  64. }
  65. if (hr == S_OK)
  66. {
  67. m_lActivateCount++;
  68. }
  69. TraceI(3,"Incrementing %lx to %ld\n",m_pBuffer,m_lActivateCount);
  70. }
  71. else
  72. {
  73. if (m_lActivateCount > 0)
  74. {
  75. m_lActivateCount--;
  76. if (!m_lActivateCount)
  77. {
  78. // Don't bother stopping the primary buffer.
  79. if (!(m_BufferHeader.dwFlags & DMUS_BUFFERF_PRIMARY))
  80. {
  81. TraceI(2,"Stop buffer %lx\n",m_pBuffer);
  82. hr = m_pBuffer->Stop();
  83. }
  84. }
  85. TraceI(3,"Decrementing %lx to %ld\n",m_pBuffer,m_lActivateCount);
  86. if (hr != S_OK)
  87. {
  88. m_lActivateCount++;
  89. }
  90. }
  91. }
  92. }
  93. return hr;
  94. }
  95. void CBufferNode::FinalDeactivate()
  96. {
  97. if (m_lActivateCount)
  98. {
  99. Activate(FALSE);
  100. }
  101. if (m_pBuffer)
  102. {
  103. m_pBuffer->Release();
  104. m_pBuffer = NULL;
  105. }
  106. }
  107. CBufferManager::CBufferManager()
  108. {
  109. m_pFirstBuffer = NULL;
  110. m_pSinkConnect = NULL;
  111. m_pSynthSink = NULL;
  112. m_pPerf = NULL;
  113. }
  114. CBufferManager::~CBufferManager()
  115. {
  116. Clear();
  117. if (m_pSinkConnect)
  118. {
  119. m_pSinkConnect->Release();
  120. }
  121. if (m_pSynthSink)
  122. {
  123. m_pSynthSink->Release();
  124. }
  125. }
  126. HRESULT CBufferManager::Init(CPerformance *pPerf, DMUS_AUDIOPARAMS *pAudioParams)
  127. {
  128. HRESULT hr = S_OK;
  129. m_pPerf = pPerf;
  130. m_AudioParams = *pAudioParams;
  131. CBufferNode *pNode = new CBufferNode;
  132. if (pNode)
  133. {
  134. pNode->m_BufferHeader.guidBufferID = GUID_Buffer_Primary;
  135. pNode->m_BufferHeader.dwFlags = DMUS_BUFFERF_SHARED | DMUS_BUFFERF_DEFINED | DMUS_BUFFERF_PRIMARY;
  136. pNode->m_pManager = this;
  137. AddHead(pNode);
  138. // Create the primary buffer. This will be used for requests to access the listener.
  139. DSBUFFERDESC dsbdesc;
  140. memset(&dsbdesc, 0, sizeof(dsbdesc));
  141. dsbdesc.dwSize = sizeof(dsbdesc);
  142. dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRL3D;
  143. // create primary buffer
  144. if (SUCCEEDED(hr = pPerf->m_pDirectSound->CreateSoundBuffer(&dsbdesc, &pNode->m_pBuffer, NULL)))
  145. {
  146. WAVEFORMATEX wfPrimary;
  147. memset(&wfPrimary, 0, sizeof(wfPrimary));
  148. if (SUCCEEDED(hr = pNode->m_pBuffer->GetFormat(&wfPrimary, sizeof(wfPrimary), NULL)))
  149. {
  150. assert(wfPrimary.wFormatTag == WAVE_FORMAT_PCM);
  151. BOOL fUpgrade = FALSE;
  152. if (wfPrimary.nSamplesPerSec < m_AudioParams.dwSampleRate)
  153. {
  154. wfPrimary.nSamplesPerSec = m_AudioParams.dwSampleRate;
  155. fUpgrade = TRUE;
  156. }
  157. if (wfPrimary.wBitsPerSample < 16)
  158. {
  159. wfPrimary.wBitsPerSample = 16;
  160. fUpgrade = TRUE;
  161. }
  162. if (fUpgrade)
  163. {
  164. wfPrimary.nBlockAlign = wfPrimary.nChannels * (wfPrimary.wBitsPerSample / 8);
  165. wfPrimary.nAvgBytesPerSec = wfPrimary.nSamplesPerSec * wfPrimary.nBlockAlign;
  166. // the existing format is of lesser quality than we desire, so let's upgrade it
  167. if (FAILED(hr = pNode->m_pBuffer->SetFormat( &wfPrimary )))
  168. {
  169. if (hr == DSERR_PRIOLEVELNEEDED)
  170. {
  171. // okay, so maybe the app doen't want us changing primary buffer
  172. Trace(0, "SynthSink - SetFormat on primary buffer failed, lacking priority\n");
  173. }
  174. else
  175. {
  176. Trace(0, "SynthSink - Activation failed, couldn't set primary buffer format\n");
  177. }
  178. }
  179. }
  180. }
  181. }
  182. }
  183. return hr;
  184. }
  185. HRESULT CBufferManager::InitSink(/*WAVEFORMATEX *pSinkFormat*/)
  186. {
  187. HRESULT hr = S_OK;
  188. // Only init the sink if it's needed. If the audiopaths will be using buffers,
  189. // we need the sink. If not, bypass setting it up.
  190. if (!m_pSinkConnect && (m_pPerf->m_AudioParams.dwFeatures & DMUS_AUDIOF_BUFFERS))
  191. {
  192. static WAVEFORMATEX sDefaultFormat = { WAVE_FORMAT_PCM,1,22050,22050*2,2,16,0 };
  193. sDefaultFormat.nSamplesPerSec = m_AudioParams.dwSampleRate;
  194. sDefaultFormat.nAvgBytesPerSec = m_AudioParams.dwSampleRate * 2;
  195. sDefaultFormat.cbSize = 0;
  196. sDefaultFormat.nBlockAlign = 2;
  197. sDefaultFormat.nChannels = 1;
  198. sDefaultFormat.wBitsPerSample = 16;
  199. sDefaultFormat.wFormatTag = WAVE_FORMAT_PCM;
  200. IDirectSoundPrivate* pDSPrivate;
  201. hr = m_pPerf->m_pDirectSound->QueryInterface(IID_IDirectSoundPrivate, (void**)&pDSPrivate);
  202. if (SUCCEEDED(hr))
  203. {
  204. hr = pDSPrivate->AllocSink(&sDefaultFormat, &m_pSinkConnect);
  205. pDSPrivate->Release();
  206. }
  207. if (SUCCEEDED(hr))
  208. {
  209. IReferenceClock *pClock = NULL;
  210. hr = m_pPerf->m_pDirectMusic->GetMasterClock(NULL, &pClock);
  211. if (SUCCEEDED(hr))
  212. {
  213. hr = m_pSinkConnect->SetMasterClock(pClock);
  214. pClock->Release();
  215. }
  216. }
  217. if (SUCCEEDED(hr))
  218. {
  219. hr = m_pSinkConnect->QueryInterface(IID_IDirectSoundSynthSink,(void **) &m_pSynthSink);
  220. if (SUCCEEDED(hr))
  221. {
  222. hr = m_pSynthSink->Activate(TRUE);
  223. }
  224. }
  225. if (SUCCEEDED(hr))
  226. {
  227. // Create a dummy buffer that gets activated at the start and stays active until
  228. // the sink closes down. This is an unfortunate patch to the "clock-hopping" bug.
  229. // Once we come up with a better solution for providing timing from the sink, this
  230. // can go away.
  231. CBufferConfig Config(BUFFER_MONO);
  232. hr = CreateBuffer(&Config,&m_pFirstBuffer);
  233. if (SUCCEEDED(hr))
  234. {
  235. hr = m_pFirstBuffer->Activate(TRUE);
  236. }
  237. }
  238. }
  239. return hr;
  240. }
  241. void CBufferManager::FinalDeactivate()
  242. {
  243. // Kill dummy buffer.
  244. if (m_pFirstBuffer)
  245. {
  246. m_pFirstBuffer->Activate(FALSE);
  247. delete m_pFirstBuffer;
  248. m_pFirstBuffer = NULL;
  249. }
  250. CBufferNode *pNode = GetHead();
  251. for (;pNode;pNode = pNode->GetNext())
  252. {
  253. pNode->FinalDeactivate();
  254. }
  255. if (m_pSynthSink)
  256. {
  257. m_pSynthSink->Activate(FALSE);
  258. m_pSynthSink->Release();
  259. m_pSynthSink = NULL;
  260. }
  261. if (m_pSinkConnect)
  262. {
  263. m_pSinkConnect->SetMasterClock(NULL);
  264. m_pSinkConnect->Release();
  265. m_pSinkConnect = NULL;
  266. }
  267. }
  268. void CBufferManager::Clear()
  269. {
  270. CBufferNode *pNode;
  271. FinalDeactivate();
  272. while (pNode = GetHead())
  273. {
  274. delete pNode;
  275. }
  276. }
  277. CBufferNode *CBufferManager::GetBufferNode(REFGUID guidBufferID)
  278. {
  279. CBufferNode *pNode = GetHead();
  280. for (;pNode;pNode = pNode->GetNext())
  281. {
  282. if (pNode->m_BufferHeader.guidBufferID == guidBufferID)
  283. {
  284. pNode->AddRef();
  285. break;
  286. }
  287. }
  288. return pNode;
  289. }
  290. HRESULT CBufferManager::CreateBuffer(CBufferConfig *pConfig, CBufferConfig ** ppNew)
  291. {
  292. HRESULT hr = S_OK;
  293. if (m_AudioParams.dwFeatures & DMUS_AUDIOF_BUFFERS)
  294. {
  295. ASSERT(m_pSinkConnect);
  296. CBufferConfig *pNew = new CBufferConfig(pConfig->m_dwStandardBufferID);
  297. if (pNew)
  298. {
  299. *ppNew = pNew;
  300. pNew->m_BufferHeader = pConfig->m_BufferHeader;
  301. if (pConfig->m_BufferHeader.dwFlags & DMUS_BUFFERF_SHARED)
  302. {
  303. // Check to see if there already is a buffer in the list with this GUID.
  304. CBufferNode *pNode = GetHead();
  305. for (;pNode;pNode = pNode->GetNext())
  306. {
  307. if (pNode->m_BufferHeader.dwFlags & DMUS_BUFFERF_SHARED)
  308. {
  309. if (pNode->m_BufferHeader.guidBufferID == pConfig->m_BufferHeader.guidBufferID)
  310. {
  311. pNew->m_pBufferNode = pNode;
  312. pNode->AddRef();
  313. TraceI(2,"Found shared Buffer %lx\n",pNode->m_pBuffer);
  314. return S_OK;
  315. }
  316. }
  317. }
  318. }
  319. // Either the buffer does not already exist, or it is not to be shared, so
  320. // create a new buffer node and requested buffer.
  321. CBufferNode *pNode = new CBufferNode;
  322. if (pNode)
  323. {
  324. pNode->m_BufferHeader = pConfig->m_BufferHeader;
  325. // Predefined buffer type?
  326. if (pConfig->m_BufferHeader.dwFlags & DMUS_BUFFERF_DEFINED)
  327. {
  328. // Must be a standard type. Create by hand.
  329. CBufferNode *pSendNode = NULL;
  330. DSBUFFERDESC BufferDesc;
  331. WAVEFORMATEX WaveFormat;
  332. DWORD dwFunctionIDs[2];
  333. CLSID clsidDMO;
  334. BOOL fDMO = FALSE;
  335. DWORD dwNumFunctionIDs;
  336. memset(&BufferDesc, 0, sizeof(BufferDesc));
  337. BufferDesc.dwSize = sizeof(BufferDesc);
  338. BufferDesc.lpwfxFormat = &WaveFormat;
  339. BufferDesc.dwBufferBytes = 0;
  340. BufferDesc.dwFlags = 0;
  341. memset(&WaveFormat,0,sizeof(WaveFormat));
  342. WaveFormat.nChannels = 2;
  343. switch (pConfig->m_dwStandardBufferID)
  344. {
  345. case BUFFER_REVERB :
  346. dwFunctionIDs[0] = DSBUSID_REVERB_SEND;
  347. dwNumFunctionIDs = 1;
  348. WaveFormat.nChannels = 2;
  349. BufferDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN | DSBCAPS_LOCDEFER;
  350. clsidDMO = GUID_DSFX_WAVES_REVERB;
  351. fDMO = TRUE;
  352. break;
  353. case BUFFER_ENVREVERB :
  354. dwNumFunctionIDs = 0;
  355. WaveFormat.nChannels = 2;
  356. BufferDesc.dwFlags = DSBCAPS_MIXIN | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME;
  357. clsidDMO = GUID_DSFX_STANDARD_I3DL2REVERB;
  358. fDMO = TRUE;
  359. if (!(m_AudioParams.dwFeatures & DMUS_AUDIOF_BUFFERS))
  360. {
  361. hr = DMUS_E_AUDIOPATH_NOBUFFER;
  362. Trace(1,"Audiopath Creation error: Requested Environmental reverb buffer when DMUS_AUDIOF_ENVIRON not enabled via InitAudio.\n");
  363. }
  364. break;
  365. // Following removed for DX8, should be reintroduced for Whistler and DX8.1...
  366. /* case BUFFER_3D :
  367. clsidDMO = GUID_DSFX_STANDARD_I3DL2SOURCE;
  368. fDMO = TRUE;
  369. pSendNode = GetBufferNode(GUID_Buffer_EnvReverb);
  370. if (!pSendNode)
  371. {
  372. Trace(1,"Error: Failed creating 3D audiopath because the environmental reverb audiopath has not been created.\n");
  373. hr = DMUS_E_AUDIOPATH_NOGLOBALFXBUFFER;
  374. break;
  375. }*/
  376. case BUFFER_3D_DRY :
  377. dwFunctionIDs[0] = DSBUSID_DYNAMIC_0;
  378. dwNumFunctionIDs = 1;
  379. WaveFormat.nChannels = 1;
  380. BufferDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY |
  381. DSBCAPS_CTRL3D | DSBCAPS_MUTE3DATMAXDISTANCE | DSBCAPS_LOCDEFER;
  382. BufferDesc.guid3DAlgorithm = DS3DALG_NO_VIRTUALIZATION;
  383. break;
  384. case BUFFER_MONO :
  385. dwFunctionIDs[0] = DSBUSID_LEFT;
  386. dwNumFunctionIDs = 1;
  387. WaveFormat.nChannels = 1;
  388. BufferDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN | DSBCAPS_LOCDEFER;
  389. break;
  390. case BUFFER_STEREO :
  391. BufferDesc.dwFlags = DSBCAPS_CTRLFREQUENCY;
  392. case BUFFER_MUSIC :
  393. dwFunctionIDs[0] = DSBUSID_LEFT;
  394. dwFunctionIDs[1] = DSBUSID_RIGHT;
  395. dwNumFunctionIDs = 2;
  396. WaveFormat.nChannels = 2;
  397. BufferDesc.dwFlags |= DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_LOCDEFER;
  398. break;
  399. default:
  400. hr = E_INVALIDARG;
  401. }
  402. if (SUCCEEDED(hr))
  403. {
  404. WaveFormat.nBlockAlign = WaveFormat.nChannels * 2;
  405. WaveFormat.nSamplesPerSec = m_AudioParams.dwSampleRate;
  406. WaveFormat.nAvgBytesPerSec = WaveFormat.nChannels * WaveFormat.nSamplesPerSec * 2;
  407. WaveFormat.wBitsPerSample = 16;
  408. WaveFormat.wFormatTag = WAVE_FORMAT_PCM;
  409. // Ensure that the mixin flag and number of bus ids are in sync (you can't combine mixin with buses.)
  410. if ((pConfig->m_BufferHeader.dwFlags & DMUS_BUFFERF_MIXIN) || (dwNumFunctionIDs == 0))
  411. {
  412. dwNumFunctionIDs = 0;
  413. BufferDesc.dwFlags |= DSBCAPS_MIXIN;
  414. pConfig->m_BufferHeader.dwFlags |= DMUS_BUFFERF_MIXIN;
  415. BufferDesc.dwFlags &= ~DSBCAPS_LOCDEFER;
  416. }
  417. // Always allow effects to be created on the buffer, regardless of whether we need them now.
  418. BufferDesc.dwFlags |= DSBCAPS_CTRLFX;
  419. hr = m_pSinkConnect->CreateSoundBuffer(&BufferDesc, &dwFunctionIDs[0], dwNumFunctionIDs,
  420. pConfig->m_BufferHeader.guidBufferID, &pNode->m_pBuffer);
  421. #ifdef DBG
  422. if (FAILED(hr))
  423. {
  424. if (pConfig->m_BufferHeader.dwFlags & DMUS_BUFFERF_MIXIN)
  425. {
  426. Trace(1,"Error: Failed creating global (MIXIN) buffer for standard path.\n");
  427. }
  428. else
  429. {
  430. Trace(1,"Error: Failed creating buffer for standard path.\n");
  431. }
  432. }
  433. #endif
  434. }
  435. if (SUCCEEDED(hr))
  436. {
  437. if (fDMO)
  438. {
  439. IDirectSoundBuffer8 *pBuff8;
  440. if (SUCCEEDED(pNode->m_pBuffer->QueryInterface(IID_IDirectSoundBuffer8,(void **)&pBuff8)))
  441. {
  442. DWORD dwResult;
  443. DSEFFECTDESC FXDesc;
  444. FXDesc.dwSize = sizeof(DSEFFECTDESC);
  445. FXDesc.dwFlags = 0;
  446. FXDesc.guidDSFXClass = clsidDMO;
  447. FXDesc.dwReserved1 = 0;
  448. FXDesc.dwReserved2 = 0;
  449. if (pSendNode)
  450. {
  451. FXDesc.dwReserved1 = DWORD_PTR(pSendNode->GetBuffer());
  452. }
  453. hr = pBuff8->SetFX(1, &FXDesc, &dwResult);
  454. if (FXDesc.dwReserved1)
  455. {
  456. ((IDirectSoundBuffer*)FXDesc.dwReserved1)->Release();
  457. }
  458. pBuff8->Release();
  459. }
  460. }
  461. }
  462. if (pSendNode)
  463. {
  464. pSendNode->Release();
  465. }
  466. }
  467. else
  468. {
  469. pConfig->m_BufferHeader.dwFlags &= ~DMUS_BUFFERF_MIXIN;
  470. hr = m_pSinkConnect->CreateSoundBufferFromConfig(pConfig->m_pBufferConfig,&pNode->m_pBuffer);
  471. if (SUCCEEDED(hr))
  472. {
  473. // We need to know if this is a mixin buffer so we can identify it later.
  474. DWORD dwBusIDs[32];
  475. DWORD dwFuncIDs[32];
  476. DWORD dwCount = 32;
  477. dwFuncIDs[0] = 0;
  478. if (SUCCEEDED(m_pSinkConnect->GetSoundBufferBusIDs(pNode->m_pBuffer,dwBusIDs,dwFuncIDs,&dwCount)))
  479. {
  480. if (dwFuncIDs[0] == 0xFFFFFFFF)
  481. {
  482. pConfig->m_BufferHeader.dwFlags |= DMUS_BUFFERF_MIXIN;
  483. }
  484. }
  485. }
  486. else
  487. {
  488. Trace(1,"Error: Failed creation of buffer defined in audio path configuration file.\n");
  489. }
  490. }
  491. if (SUCCEEDED(hr))
  492. {
  493. TraceI(2,"Created Buffer %lx\n",pNode->m_pBuffer);
  494. pNew->m_pBufferNode = pNode;
  495. pNode->m_pManager = this;
  496. AddHead(pNode);
  497. }
  498. else
  499. {
  500. delete pNode;
  501. }
  502. }
  503. else
  504. {
  505. hr = E_OUTOFMEMORY;
  506. }
  507. }
  508. if (FAILED(hr) && pNew)
  509. {
  510. delete pNew;
  511. *ppNew = NULL;
  512. }
  513. }
  514. else
  515. {
  516. hr = DMUS_E_AUDIOPATH_NOBUFFER;
  517. Trace(0,"Audiopath Creation error: Requested buffer when DMUS_AUDIOF_BUFFERS not enabled via InitAudio.\n");
  518. }
  519. return hr;
  520. }
  521. CBufferConnect::CBufferConnect()
  522. {
  523. m_ppBufferNodes = NULL;
  524. m_pguidBufferIDs = NULL;
  525. m_ConnectHeader.dwBufferCount = 0;
  526. }
  527. HRESULT CBufferConnect::Load(CRiffParser *pParser)
  528. {
  529. HRESULT hr = pParser->Read(&m_ConnectHeader, sizeof(DMUS_IO_PCHANNELTOBUFFER_HEADER));
  530. if (SUCCEEDED(hr))
  531. {
  532. DWORD dwBufferCount = m_ConnectHeader.dwBufferCount;
  533. m_pguidBufferIDs = new GUID[dwBufferCount];
  534. if (m_pguidBufferIDs )
  535. {
  536. hr = pParser->Read(m_pguidBufferIDs,sizeof(GUID)*dwBufferCount);
  537. }
  538. else
  539. {
  540. hr = E_OUTOFMEMORY;
  541. }
  542. }
  543. return hr;
  544. }
  545. CBufferConnect *CBufferConnect::CreateRunTimeVersion(CPortConfig *pParent)
  546. {
  547. CBufferConnect *pCopy = new CBufferConnect;
  548. if (pCopy && m_pguidBufferIDs)
  549. {
  550. pCopy->m_ConnectHeader = m_ConnectHeader;
  551. pCopy->m_ppBufferNodes = new CBufferNode *[m_ConnectHeader.dwBufferCount];
  552. if (pCopy->m_ppBufferNodes)
  553. {
  554. for (DWORD dwIndex = 0; dwIndex < m_ConnectHeader.dwBufferCount; dwIndex++)
  555. {
  556. pCopy->m_ppBufferNodes[dwIndex] = pParent->GetBufferNode(m_pguidBufferIDs[dwIndex]);
  557. }
  558. }
  559. else
  560. {
  561. delete pCopy;
  562. pCopy = NULL;
  563. }
  564. }
  565. return pCopy;
  566. }
  567. CBufferConnect::~CBufferConnect()
  568. {
  569. if (m_ppBufferNodes)
  570. {
  571. for (DWORD dwIndex = 0; dwIndex < m_ConnectHeader.dwBufferCount; dwIndex++)
  572. {
  573. if (m_ppBufferNodes[dwIndex]) m_ppBufferNodes[dwIndex]->Release();
  574. }
  575. delete [] m_ppBufferNodes;
  576. }
  577. if (m_pguidBufferIDs)
  578. {
  579. delete [] m_pguidBufferIDs;
  580. }
  581. }
  582. HRESULT CBufferConnectList::CreateRunTimeVersion(CBufferConnectList *pDestination, CPortConfig *pParent)
  583. {
  584. CBufferConnect *pScan = GetHead();
  585. CBufferConnect *pCopy;
  586. for (;pScan;pScan = pScan->GetNext())
  587. {
  588. pCopy = pScan->CreateRunTimeVersion(pParent);
  589. if (pCopy)
  590. {
  591. pDestination->AddTail(pCopy);
  592. }
  593. else
  594. {
  595. return E_OUTOFMEMORY;
  596. }
  597. }
  598. return S_OK;
  599. }
  600. void CBufferConnectList::Clear()
  601. {
  602. CBufferConnect *pBuffer;
  603. while (pBuffer = RemoveHead())
  604. {
  605. delete pBuffer;
  606. }
  607. }
  608. HRESULT CBufferConfig::Activate(BOOL fActivate)
  609. {
  610. if (m_pBufferNode)
  611. {
  612. return m_pBufferNode->Activate(fActivate);
  613. }
  614. return S_OK;
  615. }
  616. CBufferConfig::CBufferConfig(DWORD dwType)
  617. {
  618. m_pBufferNode = NULL;
  619. m_BufferHeader.guidBufferID = GUID_NULL;
  620. m_BufferHeader.dwFlags = DMUS_BUFFERF_DEFINED;
  621. m_dwStandardBufferID = dwType;
  622. switch (dwType)
  623. {
  624. case BUFFER_REVERB :
  625. m_BufferHeader.guidBufferID = GUID_Buffer_Reverb;
  626. m_BufferHeader.dwFlags = DMUS_BUFFERF_SHARED | DMUS_BUFFERF_DEFINED;
  627. break;
  628. case BUFFER_ENVREVERB :
  629. m_BufferHeader.guidBufferID = GUID_Buffer_EnvReverb;
  630. m_BufferHeader.dwFlags = DMUS_BUFFERF_SHARED | DMUS_BUFFERF_DEFINED | DMUS_BUFFERF_MIXIN;
  631. break;
  632. // Following removed for DX8, should be reintroduced for Whistler and DX8.1...
  633. // case BUFFER_3D :
  634. // m_BufferHeader.guidBufferID = GUID_Buffer_3D;
  635. // break;
  636. case BUFFER_3D_DRY :
  637. m_BufferHeader.guidBufferID = GUID_Buffer_3D_Dry;
  638. break;
  639. case BUFFER_MONO :
  640. m_BufferHeader.guidBufferID = GUID_Buffer_Mono;
  641. break;
  642. case BUFFER_STEREO :
  643. m_BufferHeader.guidBufferID = GUID_Buffer_Stereo;
  644. break;
  645. case BUFFER_MUSIC :
  646. m_BufferHeader.guidBufferID = GUID_Buffer_Stereo;
  647. m_BufferHeader.dwFlags = DMUS_BUFFERF_SHARED | DMUS_BUFFERF_DEFINED;
  648. break;
  649. default:
  650. m_BufferHeader.dwFlags = 0;
  651. break;
  652. }
  653. m_pBufferConfig = NULL;
  654. }
  655. CBufferConfig::~CBufferConfig()
  656. {
  657. if (m_pBufferNode)
  658. m_pBufferNode->Release();
  659. if (m_pBufferConfig)
  660. m_pBufferConfig->Release();
  661. }
  662. void CBufferConfig::DecideType()
  663. {
  664. if (m_BufferHeader.guidBufferID == GUID_Buffer_Reverb)
  665. {
  666. m_dwStandardBufferID = BUFFER_REVERB;
  667. }
  668. else if (m_BufferHeader.guidBufferID == GUID_Buffer_EnvReverb)
  669. {
  670. m_dwStandardBufferID = BUFFER_ENVREVERB;
  671. }
  672. // Following removed for DX8, should be reintroduced for Whistler and DX8.1...
  673. /* else if (m_BufferHeader.guidBufferID == GUID_Buffer_3D)
  674. {
  675. m_dwStandardBufferID = BUFFER_3D;
  676. }*/
  677. else if (m_BufferHeader.guidBufferID == GUID_Buffer_3D_Dry)
  678. {
  679. m_dwStandardBufferID = BUFFER_3D_DRY;
  680. }
  681. else if (m_BufferHeader.guidBufferID == GUID_Buffer_Mono)
  682. {
  683. m_dwStandardBufferID = BUFFER_MONO;
  684. }
  685. else if (m_BufferHeader.guidBufferID == GUID_Buffer_Stereo)
  686. {
  687. m_dwStandardBufferID = BUFFER_STEREO;
  688. }
  689. }
  690. HRESULT CBufferConfig::Load(IStream *pStream)
  691. {
  692. IPersistStream *pPersist;
  693. HRESULT hr = CoCreateInstance( CLSID_DirectSoundBufferConfig,
  694. NULL, CLSCTX_INPROC, IID_IPersistStream,
  695. (void**)&pPersist );
  696. if (SUCCEEDED(hr))
  697. {
  698. hr = pPersist->Load(pStream);
  699. if (SUCCEEDED(hr))
  700. {
  701. m_pBufferConfig = pPersist;
  702. IDirectMusicObject *pObject;
  703. hr = pPersist->QueryInterface(IID_IDirectMusicObject,(void **) &pObject);
  704. if (SUCCEEDED(hr))
  705. {
  706. DMUS_OBJECTDESC Desc;
  707. Desc.dwSize = sizeof(Desc);
  708. pObject->GetDescriptor(&Desc);
  709. if (Desc.dwValidData & DMUS_OBJ_OBJECT)
  710. {
  711. m_BufferHeader.guidBufferID = Desc.guidObject;
  712. }
  713. else
  714. {
  715. Trace(1,"Error: Unable to load Buffer Configuration in AudioPath Config - Missing buffer GUID in file.\n");
  716. hr = E_FAIL;
  717. }
  718. pObject->Release();
  719. }
  720. }
  721. else
  722. {
  723. pPersist->Release();
  724. }
  725. }
  726. return hr;
  727. }
  728. void CBufferConfigList::Clear()
  729. {
  730. CBufferConfig *pBuffer;
  731. while (pBuffer = RemoveHead())
  732. {
  733. delete pBuffer;
  734. }
  735. }
  736. CBufferNode * CBufferConfigList::GetBufferNode(REFGUID guidBufferID)
  737. {
  738. CBufferNode *pBuff = NULL;
  739. CBufferConfig *pBuffer;
  740. for (pBuffer = GetHead();pBuffer;pBuffer = pBuffer->GetNext())
  741. {
  742. if (pBuffer->m_BufferHeader.guidBufferID == guidBufferID)
  743. {
  744. if (pBuffer->m_pBufferNode)
  745. {
  746. pBuffer->m_pBufferNode->AddRef();
  747. pBuff = pBuffer->m_pBufferNode;
  748. }
  749. break;
  750. }
  751. }
  752. return pBuff;
  753. }
  754. HRESULT CBufferConfigList::Activate(BOOL fActivate)
  755. {
  756. HRESULT hr = S_OK;
  757. CBufferConfig *pBuffer;
  758. for (pBuffer = GetHead();pBuffer;pBuffer = pBuffer->GetNext())
  759. {
  760. hr = pBuffer->Activate(fActivate);
  761. if (FAILED(hr))
  762. {
  763. CBufferConfig *pUndo;
  764. for (pUndo = GetHead();pUndo && (pUndo != pBuffer);pUndo = pUndo->GetNext())
  765. {
  766. pUndo->Activate(!fActivate);
  767. }
  768. break;
  769. }
  770. }
  771. return hr;
  772. }
  773. HRESULT CBufferConfigList::CreateRunTimeVersion(CBufferConfigList *pCopy, CBufferManager *pManager)
  774. /* To create a runtime version, we scan through all bufferconfigs and, for each one, we call the
  775. buffer manager to create a new one, managed by CBufferNode. In the case where the buffer already exists, it just
  776. addrefs the CBufferNode and returns that.
  777. */
  778. {
  779. HRESULT hr = S_OK;
  780. CBufferConfig *pBuffer;
  781. for (pBuffer = GetHead();pBuffer;pBuffer = pBuffer->GetNext())
  782. {
  783. CBufferConfig *pNew = NULL;
  784. hr = pManager->CreateBuffer(pBuffer,&pNew);
  785. if (SUCCEEDED(hr))
  786. {
  787. pCopy->AddTail(pNew);
  788. }
  789. else
  790. {
  791. break;
  792. }
  793. }
  794. return hr;
  795. }
  796. CPortConfig::CPortConfig()
  797. {
  798. m_fAlreadyHere = FALSE;
  799. m_pPort = NULL;
  800. m_dwPortID = 0;
  801. m_pParent = NULL;
  802. m_PortHeader.guidPort = GUID_Synth_Default; // Default synth, as specified by DMUS_AUDPARAMS.
  803. m_PortHeader.dwFlags = DMUS_PORTCONFIGF_DRUMSON10;
  804. m_PortHeader.dwPChannelBase = 0;
  805. m_PortHeader.dwPChannelCount = 32;
  806. m_PortParams.dwChannelGroups = 2;
  807. m_PortParams.dwSize = sizeof(DMUS_PORTPARAMS8);
  808. m_PortParams.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS | DMUS_PORTPARAMS_FEATURES;
  809. m_PortParams.dwFeatures = DMUS_PORT_FEATURE_AUDIOPATH | DMUS_PORT_FEATURE_STREAMING;
  810. }
  811. CPortConfig::~CPortConfig()
  812. {
  813. if (m_pPort) m_pPort->Release();
  814. m_BufferConnectList.Clear();
  815. m_BufferConfigList.Clear();
  816. }
  817. HRESULT CPortConfig::Activate(BOOL fActivate)
  818. {
  819. HRESULT hr = m_BufferConfigList.Activate(fActivate);
  820. if (SUCCEEDED(hr) && fActivate && m_pPort)
  821. {
  822. HRESULT hrTemp = m_pPort->Activate(TRUE);
  823. if (FAILED(hrTemp))
  824. {
  825. hr = hrTemp;
  826. }
  827. }
  828. return hr;
  829. }
  830. HRESULT CPortConfig::CreateRunTimeVersion(CPortConfig ** ppCopy, CAudioPath *pParent, CBufferManager *pManager)
  831. {
  832. HRESULT hr = E_OUTOFMEMORY;
  833. CPortConfig *pCopy = new CPortConfig();
  834. if (pCopy)
  835. {
  836. pCopy->m_pParent = pParent;
  837. pCopy->m_PortHeader = m_PortHeader;
  838. pCopy->m_PortParams = m_PortParams;
  839. hr = m_BufferConfigList.CreateRunTimeVersion(&pCopy->m_BufferConfigList,pManager);
  840. if (SUCCEEDED(hr))
  841. {
  842. hr = m_BufferConnectList.CreateRunTimeVersion(&pCopy->m_BufferConnectList,pCopy);
  843. }
  844. }
  845. *ppCopy = pCopy;
  846. return hr;
  847. }
  848. CBufferNode * CPortConfig::GetBufferNode(REFGUID guidBuffer)
  849. {
  850. CBufferNode *pBuff = NULL;
  851. pBuff = m_BufferConfigList.GetBufferNode(guidBuffer);
  852. if (!pBuff && !m_fAlreadyHere)
  853. {
  854. m_fAlreadyHere = TRUE;
  855. pBuff = m_pParent->GetBufferNode(guidBuffer);
  856. m_fAlreadyHere = FALSE;
  857. }
  858. return pBuff;
  859. }
  860. HRESULT CPortConfig::Load(CRiffParser *pParser)
  861. {
  862. RIFFIO ckNext;
  863. HRESULT hr = S_OK;
  864. DWORD dwLoadedBoth = 0;
  865. pParser->EnterList(&ckNext);
  866. while (pParser->NextChunk(&hr))
  867. {
  868. switch(ckNext.ckid)
  869. {
  870. case DMUS_FOURCC_PORTCONFIG_ITEM:
  871. hr = pParser->Read(&m_PortHeader, sizeof(DMUS_IO_PORTCONFIG_HEADER));
  872. dwLoadedBoth |= 1;
  873. break;
  874. case DMUS_FOURCC_PORTPARAMS_ITEM:
  875. hr = pParser->Read(&m_PortParams, sizeof(DMUS_PORTPARAMS8));
  876. dwLoadedBoth |= 2;
  877. break;
  878. case FOURCC_LIST:
  879. case FOURCC_RIFF:
  880. switch(ckNext.fccType)
  881. {
  882. RIFFIO ckChild;
  883. case DMUS_FOURCC_PCHANNELS_LIST:
  884. pParser->EnterList(&ckChild);
  885. while (pParser->NextChunk(&hr))
  886. {
  887. switch( ckChild.ckid )
  888. {
  889. case DMUS_FOURCC_PCHANNELS_ITEM:
  890. {
  891. CBufferConnect *pBufferConnect = new CBufferConnect;
  892. if (pBufferConnect)
  893. {
  894. hr = pBufferConnect->Load(pParser);
  895. if (SUCCEEDED(hr))
  896. {
  897. m_BufferConnectList.AddTail(pBufferConnect);
  898. }
  899. }
  900. else
  901. {
  902. hr = E_OUTOFMEMORY;
  903. }
  904. }
  905. break;
  906. }
  907. }
  908. pParser->LeaveList();
  909. break;
  910. case DMUS_FOURCC_DSBUFFER_LIST:
  911. {
  912. CBufferConfig *pSource = new CBufferConfig(0);
  913. if (pSource)
  914. {
  915. pParser->EnterList(&ckChild);
  916. while (pParser->NextChunk(&hr))
  917. {
  918. switch( ckChild.ckid )
  919. {
  920. case DMUS_FOURCC_DSBUFFATTR_ITEM:
  921. hr = pParser->Read(&pSource->m_BufferHeader,
  922. sizeof(DMUS_IO_BUFFER_ATTRIBUTES_HEADER));
  923. pSource->DecideType();
  924. break;
  925. case FOURCC_LIST:
  926. case FOURCC_RIFF:
  927. if ( ckChild.fccType == DMUS_FOURCC_DSBC_FORM)
  928. {
  929. pParser->SeekBack();
  930. hr = pSource->Load(pParser->GetStream());
  931. pParser->SeekForward();
  932. if (FAILED(hr))
  933. {
  934. Trace(1,"AudioPath Configuration failed loading buffer\n");
  935. }
  936. }
  937. }
  938. }
  939. pParser->LeaveList();
  940. if (SUCCEEDED(hr))
  941. {
  942. m_BufferConfigList.AddTail(pSource);
  943. }
  944. else
  945. {
  946. delete pSource;
  947. }
  948. }
  949. else
  950. {
  951. hr = E_OUTOFMEMORY;
  952. }
  953. }
  954. break;
  955. case DMUS_FOURCC_DSBC_FORM:
  956. {
  957. CBufferConfig *pSource = new CBufferConfig(0);
  958. if (pSource)
  959. {
  960. pParser->SeekBack();
  961. hr = pSource->Load(pParser->GetStream());
  962. pParser->SeekForward();
  963. if (SUCCEEDED(hr))
  964. {
  965. m_BufferConfigList.AddTail(pSource);
  966. }
  967. else
  968. {
  969. Trace(1,"AudioPath Configuration failed loading buffer\n");
  970. delete pSource;
  971. }
  972. }
  973. else
  974. {
  975. hr = E_OUTOFMEMORY;
  976. }
  977. }
  978. break;
  979. default:
  980. break;
  981. }
  982. break;
  983. default:
  984. break;
  985. }
  986. }
  987. if (dwLoadedBoth != 3)
  988. {
  989. hr = DMUS_E_CHUNKNOTFOUND;
  990. Trace(1,"Error: Failure loading port configuration chunk in Audio Path Configuration.\n");
  991. }
  992. // Make sure the channel groups in the portparams is large enough to handle the requested
  993. // channels in portheader.
  994. m_PortParams.dwChannelGroups = (m_PortHeader.dwPChannelCount + 15) / 16;
  995. m_PortParams.dwValidParams |= DMUS_PORTPARAMS_CHANNELGROUPS;
  996. pParser->LeaveList();
  997. return hr;
  998. }
  999. HRESULT CPortConfigList::Activate(BOOL fActivate)
  1000. {
  1001. HRESULT hr = S_OK;
  1002. CPortConfig *pPort;
  1003. for (pPort = GetHead();pPort;pPort = pPort->GetNext())
  1004. {
  1005. hr = pPort->Activate(fActivate);
  1006. if (FAILED(hr))
  1007. {
  1008. CPortConfig *pUndo;
  1009. for (pUndo = GetHead();pUndo && (pUndo != pPort);pUndo = pUndo->GetNext())
  1010. {
  1011. pUndo->Activate(!fActivate);
  1012. }
  1013. break;
  1014. }
  1015. }
  1016. return hr;
  1017. }
  1018. HRESULT CPortConfigList::CreateRunTimeVersion(CPortConfigList *pDestination,CAudioPath *pParent,CBufferManager *pManager)
  1019. {
  1020. HRESULT hr = S_OK;
  1021. CPortConfig *pScan = GetHead();
  1022. CPortConfig *pCopy;
  1023. for (;pScan;pScan = pScan->GetNext())
  1024. {
  1025. hr = pScan->CreateRunTimeVersion(&pCopy,pParent,pManager);
  1026. if (pCopy)
  1027. {
  1028. pDestination->AddTail(pCopy);
  1029. }
  1030. else
  1031. {
  1032. break;
  1033. }
  1034. }
  1035. return hr;
  1036. }
  1037. BOOL CPortConfigList::UsesPort(IDirectMusicPort *pPort)
  1038. {
  1039. CPortConfig *pScan = GetHead();
  1040. for (;pScan;pScan = pScan->GetNext())
  1041. {
  1042. if (pScan->m_pPort == pPort) return TRUE;
  1043. }
  1044. return FALSE;
  1045. }
  1046. void CPortConfigList::Clear()
  1047. {
  1048. CPortConfig *pPort;
  1049. while (pPort = RemoveHead())
  1050. {
  1051. delete pPort;
  1052. }
  1053. }
  1054. CAudioPath::CAudioPath()
  1055. {
  1056. TraceI(2,"Creating AudioPath %lx\n",this);
  1057. InitializeCriticalSection(&m_CriticalSection);
  1058. m_fDeactivating = FALSE;
  1059. m_bLastVol = 127;
  1060. m_cRef = 0;
  1061. m_fActive = FALSE;
  1062. m_pdwVChannels = NULL;
  1063. m_pdwPChannels = NULL;
  1064. m_dwChannelCount = 0;
  1065. m_pPerformance = NULL;
  1066. m_pGraph = NULL;
  1067. m_pConfig = NULL;
  1068. m_pUnkDispatch = NULL;
  1069. }
  1070. CAudioPath::~CAudioPath()
  1071. {
  1072. EnterCriticalSection(&m_CriticalSection);
  1073. if (m_pUnkDispatch)
  1074. {
  1075. m_pUnkDispatch->Release(); // free IDispatch implementation we may have borrowed
  1076. }
  1077. LeaveCriticalSection(&m_CriticalSection);
  1078. Deactivate();
  1079. DeleteCriticalSection(&m_CriticalSection);
  1080. }
  1081. STDMETHODIMP_(ULONG) CAudioPath::AddRef()
  1082. {
  1083. return InterlockedIncrement(&m_cRef);
  1084. }
  1085. STDMETHODIMP_(ULONG) CAudioPath::Release()
  1086. {
  1087. if (!InterlockedDecrement(&m_cRef))
  1088. {
  1089. delete this;
  1090. return 0;
  1091. }
  1092. return m_cRef;
  1093. }
  1094. STDMETHODIMP CAudioPath::QueryInterface(
  1095. const IID &iid,
  1096. void **ppv)
  1097. {
  1098. V_INAME(CAudioPath::QueryInterface);
  1099. V_PTRPTR_WRITE(ppv);
  1100. V_REFGUID(iid);
  1101. *ppv = NULL;
  1102. if (iid == IID_IUnknown || iid == IID_IDirectMusicAudioPath)
  1103. {
  1104. *ppv = static_cast<IDirectMusicAudioPath*>(this);
  1105. } else
  1106. if (iid == IID_CAudioPath)
  1107. {
  1108. *ppv = static_cast<CAudioPath*>(this);
  1109. } else
  1110. if (iid == IID_IDirectMusicGraph)
  1111. {
  1112. *ppv = static_cast<IDirectMusicGraph*>(this);
  1113. }
  1114. else if (iid == IID_IDispatch)
  1115. {
  1116. // A helper scripting object implements IDispatch, which we expose via COM aggregation.
  1117. if (!m_pUnkDispatch)
  1118. {
  1119. // Create the helper object
  1120. ::CoCreateInstance(
  1121. CLSID_AutDirectMusicAudioPath,
  1122. static_cast<IDirectMusicAudioPath*>(this),
  1123. CLSCTX_INPROC_SERVER,
  1124. IID_IUnknown,
  1125. reinterpret_cast<void**>(&m_pUnkDispatch));
  1126. }
  1127. if (m_pUnkDispatch)
  1128. {
  1129. return m_pUnkDispatch->QueryInterface(IID_IDispatch, ppv);
  1130. }
  1131. }
  1132. if (*ppv == NULL)
  1133. {
  1134. Trace(4,"Warning: Request to query unknown interface on AudioPath object\n");
  1135. return E_NOINTERFACE;
  1136. }
  1137. reinterpret_cast<IUnknown*>(this)->AddRef();
  1138. return S_OK;
  1139. }
  1140. static BYTE VolumeToMidi(long lVolume)
  1141. {
  1142. static long lDBToMIDI[97] = { // Array used to convert db to MIDI.
  1143. 127, 119, 113, 106, 100, 95, 89, 84, 80, 75,
  1144. 71, 67, 63, 60, 56, 53, 50, 47, 45, 42,
  1145. 40, 37, 35, 33, 31, 30, 28, 26, 25, 23,
  1146. 22, 21, 20, 19, 17, 16, 15, 15, 14, 13,
  1147. 12, 11, 11, 10, 10, 9, 8, 8, 8, 7,
  1148. 7, 6, 6, 6, 5, 5, 5, 4, 4, 4,
  1149. 4, 3, 3, 3, 3, 3, 2, 2, 2, 2,
  1150. 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
  1151. 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
  1152. 0, 0, 0, 0, 0, 0, 0
  1153. };
  1154. if (lVolume < -9600) lVolume = -9600;
  1155. if (lVolume > 0) lVolume = 0;
  1156. lVolume = -lVolume;
  1157. long lFraction = lVolume % 100;
  1158. lVolume = lVolume / 100;
  1159. long lResult = lDBToMIDI[lVolume];
  1160. lResult += ((lDBToMIDI[lVolume + 1] - lResult) * lFraction) / 100;
  1161. return (BYTE) lResult;
  1162. }
  1163. STDMETHODIMP CAudioPath::SetVolume(long lVolume,DWORD dwDuration)
  1164. {
  1165. if (lVolume < DSBVOLUME_MIN || lVolume > DSBVOLUME_MAX)
  1166. {
  1167. return E_INVALIDARG;
  1168. }
  1169. HRESULT hr = E_FAIL; // This should never happen, since the audiopath is created by the performance.
  1170. BYTE bMIDIVol = VolumeToMidi(lVolume);
  1171. DMUS_CURVE_PMSG *pCurve;
  1172. EnterCriticalSection(&m_CriticalSection);
  1173. if (m_pPerformance)
  1174. {
  1175. hr = m_pPerformance->AllocPMsg(sizeof(DMUS_CURVE_PMSG),(DMUS_PMSG **)&pCurve);
  1176. if (SUCCEEDED(hr))
  1177. {
  1178. REFERENCE_TIME rtTimeNow = 0;
  1179. m_pPerformance->GetLatencyTime(&rtTimeNow);
  1180. pCurve->rtTime = rtTimeNow;
  1181. pCurve->dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME | DMUS_PMSGF_DX8;
  1182. pCurve->dwPChannel = DMUS_PCHANNEL_BROADCAST_AUDIOPATH;
  1183. // dwVirtualTrackID: this isn't a track so leave as 0
  1184. pCurve->dwType = DMUS_PMSGT_CURVE;
  1185. pCurve->dwGroupID = -1; // this isn't a track so just say all groups
  1186. // curve PMsg fields
  1187. pCurve->mtDuration = dwDuration; // setting the DMUS_PMSGF_LOCKTOREFTIME is interpreted by the performance that mtDuration is milliseconds
  1188. // mtResetDuration: no reset so leave as 0
  1189. pCurve->nStartValue = m_bLastVol;
  1190. m_bLastVol = bMIDIVol;
  1191. // nStartValue: will be ignored
  1192. pCurve->nEndValue = bMIDIVol;
  1193. // nResetValue: no reset so leave as 0
  1194. pCurve->bType = DMUS_CURVET_CCCURVE;
  1195. pCurve->bCurveShape = dwDuration ? DMUS_CURVES_LINEAR : DMUS_CURVES_INSTANT;
  1196. pCurve->bCCData = 7; // MIDI volume controller number
  1197. pCurve->bFlags = DMUS_CURVE_START_FROM_CURRENT;
  1198. // wParamType: leave as zero since this isn't a NRPN/RPN curve
  1199. pCurve->wMergeIndex = 0xFFFF; // �� special merge index so this won't get stepped on. is a big number OK? define a constant for this value?
  1200. // send it
  1201. StampPMsg((DMUS_PMSG *)pCurve);
  1202. hr = m_pPerformance->SendPMsg((DMUS_PMSG*)pCurve);
  1203. }
  1204. }
  1205. else
  1206. {
  1207. hr = DMUS_E_NOT_INIT;
  1208. }
  1209. LeaveCriticalSection(&m_CriticalSection);
  1210. return hr;
  1211. }
  1212. STDMETHODIMP CAudioPath::GetObjectInPath( DWORD dwPChannel,DWORD dwStage,
  1213. DWORD dwBuffer, REFGUID guidObject,
  1214. DWORD dwIndex, REFGUID iidInterface, void ** ppObject)
  1215. {
  1216. V_INAME(IDirectMusicAudioPath::GetObjectInPath);
  1217. V_PTRPTR_WRITE(ppObject);
  1218. *ppObject = NULL;
  1219. if (dwBuffer && ((dwStage < DMUS_PATH_BUFFER) || (dwStage >= DMUS_PATH_PRIMARY_BUFFER)))
  1220. {
  1221. return DMUS_E_NOT_FOUND;
  1222. }
  1223. HRESULT hr = DMUS_E_NOT_FOUND;
  1224. CPortConfig *pPortConfig;
  1225. EnterCriticalSection(&m_CriticalSection);
  1226. switch (dwStage)
  1227. {
  1228. case DMUS_PATH_AUDIOPATH:
  1229. if ((dwIndex == 0) && (dwPChannel == 0))
  1230. {
  1231. hr = QueryInterface(iidInterface,ppObject);
  1232. }
  1233. break;
  1234. case DMUS_PATH_AUDIOPATH_GRAPH:
  1235. if ((dwIndex == 0) && (dwPChannel == 0))
  1236. {
  1237. if (!m_pGraph)
  1238. {
  1239. m_pGraph = new CGraph;
  1240. }
  1241. if (m_pGraph)
  1242. {
  1243. hr = m_pGraph->QueryInterface(iidInterface,ppObject);
  1244. }
  1245. else
  1246. {
  1247. hr = E_OUTOFMEMORY;
  1248. }
  1249. }
  1250. break;
  1251. case DMUS_PATH_AUDIOPATH_TOOL:
  1252. if (m_pGraph)
  1253. {
  1254. hr = m_pGraph->GetObjectInPath(dwPChannel,guidObject,dwIndex,iidInterface,ppObject);
  1255. }
  1256. break;
  1257. case DMUS_PATH_PERFORMANCE:
  1258. if (m_pPerformance && (dwIndex == 0) && (dwPChannel == 0))
  1259. {
  1260. hr = m_pPerformance->QueryInterface(iidInterface,ppObject);
  1261. }
  1262. break;
  1263. case DMUS_PATH_PERFORMANCE_GRAPH:
  1264. if (m_pPerformance && (dwIndex == 0) && (dwPChannel == 0))
  1265. {
  1266. IDirectMusicGraph *pGraph;
  1267. if (SUCCEEDED(hr = m_pPerformance->GetGraphInternal(&pGraph)))
  1268. {
  1269. hr = pGraph->QueryInterface(iidInterface,ppObject);
  1270. pGraph->Release();
  1271. }
  1272. }
  1273. break;
  1274. case DMUS_PATH_PERFORMANCE_TOOL:
  1275. if (m_pPerformance)
  1276. {
  1277. IDirectMusicGraph *pGraph;
  1278. if (SUCCEEDED(hr = m_pPerformance->GetGraphInternal(&pGraph)))
  1279. {
  1280. CGraph *pCGraph = (CGraph *) pGraph;
  1281. // Convert from audiopath channel to performance channel.
  1282. ConvertPChannel( dwPChannel,&dwPChannel);
  1283. hr = pCGraph->GetObjectInPath(dwPChannel,guidObject,dwIndex,iidInterface,ppObject);
  1284. pGraph->Release();
  1285. }
  1286. }
  1287. break;
  1288. case DMUS_PATH_PORT:
  1289. pPortConfig = m_PortConfigList.GetHead();
  1290. for (;pPortConfig;pPortConfig = pPortConfig->GetNext())
  1291. {
  1292. // First, see if this matches the port guid.
  1293. if ((pPortConfig->m_PortHeader.guidPort == guidObject) || (guidObject == GUID_All_Objects))
  1294. {
  1295. // Then, see if we have a pchannel match.
  1296. if ((dwPChannel == DMUS_PCHANNEL_ALL) ||
  1297. ((pPortConfig->m_PortHeader.dwPChannelBase <= dwPChannel) &&
  1298. ((pPortConfig->m_PortHeader.dwPChannelBase + pPortConfig->m_PortHeader.dwPChannelCount) > dwPChannel)))
  1299. {
  1300. // If everything matches, there is always the chance that we have multiple instances of
  1301. // this in the list and we are actually looking at a second or third pointer to the same port.
  1302. // So, scan through the list again, making the exact same matches. If this port is found in
  1303. // an earlier instance, fail the match.
  1304. BOOL fSuccess = true;
  1305. CPortConfig *pScan = m_PortConfigList.GetHead();
  1306. for (;pScan;pScan = pScan->GetNext())
  1307. {
  1308. // First, see if this matches the port guid.
  1309. if ((pScan->m_PortHeader.guidPort == guidObject) || (guidObject == GUID_All_Objects))
  1310. {
  1311. // Then, see if we have a pchannel match.
  1312. if ((dwPChannel == DMUS_PCHANNEL_ALL) ||
  1313. ((pScan->m_PortHeader.dwPChannelBase <= dwPChannel) &&
  1314. ((pScan->m_PortHeader.dwPChannelBase + pScan->m_PortHeader.dwPChannelCount) > dwPChannel)))
  1315. {
  1316. // If this is the same as the outer loop, we have arrived.
  1317. if (pScan == pPortConfig)
  1318. {
  1319. break;
  1320. }
  1321. else
  1322. {
  1323. // Else, if this points to the same port, we have failed.
  1324. if (pScan->m_pPort == pPortConfig->m_pPort)
  1325. {
  1326. fSuccess = false;
  1327. break;
  1328. }
  1329. }
  1330. }
  1331. }
  1332. }
  1333. if (fSuccess)
  1334. {
  1335. if (dwIndex)
  1336. {
  1337. dwIndex--;
  1338. }
  1339. else
  1340. {
  1341. IDirectMusicPort *pPort;
  1342. if (SUCCEEDED(m_pPerformance->GetPort(pPortConfig->m_dwPortID,&pPort)))
  1343. {
  1344. hr = pPort->QueryInterface(iidInterface,ppObject);
  1345. pPort->Release();
  1346. }
  1347. break;
  1348. }
  1349. }
  1350. }
  1351. }
  1352. }
  1353. break;
  1354. case DMUS_PATH_SINK:
  1355. if (m_pPerformance && (dwIndex == 0) && (dwPChannel == 0) &&
  1356. m_pPerformance->m_BufferManager.m_pSinkConnect)
  1357. {
  1358. hr = m_pPerformance->m_BufferManager.m_pSinkConnect->QueryInterface(iidInterface,ppObject);
  1359. }
  1360. break;
  1361. case DMUS_PATH_BUFFER:
  1362. case DMUS_PATH_BUFFER_DMO:
  1363. {
  1364. CBufferConnect *pConnect = NULL;
  1365. BOOL fAllChannels = (dwPChannel == DMUS_PCHANNEL_ALL);
  1366. pPortConfig = m_PortConfigList.GetHead();
  1367. for (;pPortConfig && FAILED(hr);pPortConfig = pPortConfig->GetNext())
  1368. {
  1369. if (fAllChannels || ((pPortConfig->m_PortHeader.dwPChannelBase <= dwPChannel) &&
  1370. ((pPortConfig->m_PortHeader.dwPChannelCount +
  1371. pPortConfig->m_PortHeader.dwPChannelBase) > dwPChannel)))
  1372. {
  1373. pConnect = pPortConfig->m_BufferConnectList.GetHead();
  1374. for (;pConnect;)
  1375. {
  1376. if (fAllChannels || ((pConnect->m_ConnectHeader.dwPChannelBase <= dwPChannel) &&
  1377. ((pConnect->m_ConnectHeader.dwPChannelCount +
  1378. pConnect->m_ConnectHeader.dwPChannelBase) > dwPChannel)))
  1379. {
  1380. // Found the buffer connect. Which buffer will be determined
  1381. // by dwBuffer. If dwBuffer is greater than the count of buffer,
  1382. // decrement and move on to the next pConnect.
  1383. if (pConnect->m_ConnectHeader.dwBufferCount > dwBuffer)
  1384. {
  1385. if (pConnect->m_ppBufferNodes[dwBuffer])
  1386. {
  1387. IDirectSoundBuffer *pBuffer = pConnect->m_ppBufferNodes[dwBuffer]->GetBuffer();
  1388. if (pBuffer)
  1389. {
  1390. if (dwStage == DMUS_PATH_BUFFER)
  1391. {
  1392. if (dwIndex == 0)
  1393. {
  1394. hr = pBuffer->QueryInterface(iidInterface,ppObject);
  1395. }
  1396. }
  1397. else
  1398. {
  1399. IDirectSoundBuffer8 *pBuffer8;
  1400. hr = pBuffer->QueryInterface(IID_IDirectSoundBuffer8,(void **) &pBuffer8);
  1401. if (SUCCEEDED(hr))
  1402. {
  1403. hr = pBuffer8->GetObjectInPath(guidObject,dwIndex,iidInterface,ppObject);
  1404. pBuffer8->Release();
  1405. }
  1406. }
  1407. pBuffer->Release();
  1408. }
  1409. }
  1410. pConnect = NULL;
  1411. }
  1412. else
  1413. {
  1414. dwBuffer -= pConnect->m_ConnectHeader.dwBufferCount;
  1415. }
  1416. }
  1417. if (pConnect)
  1418. {
  1419. pConnect = pConnect->GetNext();
  1420. }
  1421. }
  1422. }
  1423. }
  1424. }
  1425. break;
  1426. case DMUS_PATH_MIXIN_BUFFER :
  1427. case DMUS_PATH_MIXIN_BUFFER_DMO :
  1428. if (dwPChannel == 0)
  1429. {
  1430. CBufferConfig *pConfig = m_BufferConfigList.GetHead();
  1431. for (;pConfig; pConfig = pConfig->GetNext())
  1432. {
  1433. if (pConfig->m_BufferHeader.dwFlags & DMUS_BUFFERF_MIXIN)
  1434. {
  1435. if (!dwBuffer)
  1436. {
  1437. IDirectSoundBuffer *pBuffer = pConfig->m_pBufferNode->GetBuffer();
  1438. if (pBuffer)
  1439. {
  1440. if (dwStage == DMUS_PATH_MIXIN_BUFFER)
  1441. {
  1442. if (dwIndex == 0)
  1443. {
  1444. hr = pBuffer->QueryInterface(iidInterface,ppObject);
  1445. }
  1446. }
  1447. else
  1448. {
  1449. IDirectSoundBuffer8 *pBuffer8;
  1450. hr = pBuffer->QueryInterface(IID_IDirectSoundBuffer8,(void **) &pBuffer8);
  1451. if (SUCCEEDED(hr))
  1452. {
  1453. hr = pBuffer8->GetObjectInPath(guidObject,dwIndex,iidInterface,ppObject);
  1454. pBuffer8->Release();
  1455. }
  1456. }
  1457. pBuffer->Release();
  1458. }
  1459. break;
  1460. }
  1461. dwBuffer--;
  1462. }
  1463. }
  1464. }
  1465. break;
  1466. case DMUS_PATH_PRIMARY_BUFFER :
  1467. if ((dwIndex == 0) && (dwPChannel == 0))
  1468. {
  1469. CBufferNode *pNode = m_pPerformance->m_BufferManager.GetBufferNode(GUID_Buffer_Primary);
  1470. if (pNode)
  1471. {
  1472. IDirectSoundBuffer *pBuffer = pNode->GetBuffer();
  1473. if (pBuffer)
  1474. {
  1475. hr = pBuffer->QueryInterface(iidInterface,ppObject);
  1476. pBuffer->Release();
  1477. }
  1478. pNode->Release();
  1479. }
  1480. }
  1481. break;
  1482. default:
  1483. hr = E_INVALIDARG;
  1484. #ifdef DBG
  1485. Trace(1,"Error: Audiopath does not support stage 0x%lx\n",dwStage);
  1486. #endif
  1487. }
  1488. LeaveCriticalSection(&m_CriticalSection);
  1489. #ifdef DBG
  1490. if (hr == DMUS_E_NOT_FOUND)
  1491. {
  1492. Trace(3,"Warning: Requested AudioPath object not found\n");
  1493. }
  1494. #endif
  1495. return hr;
  1496. }
  1497. HRESULT STDMETHODCALLTYPE CAudioPath::Activate(BOOL fActivate)
  1498. {
  1499. TraceI(2,"Audiopath %lx Activate: %ld\n",this,fActivate);
  1500. if (fActivate == m_fActive)
  1501. {
  1502. #ifdef DBG
  1503. if (fActivate) Trace(2,"Warning: Attempt to activate already active audiopath.\n");
  1504. else Trace(2,"Warning: Attempt to deactivate already inactive audiopath.\n");
  1505. #endif
  1506. return S_FALSE;
  1507. }
  1508. m_fActive = fActivate;
  1509. if (!fActivate && !m_fDeactivating)
  1510. {
  1511. EnterCriticalSection(&m_CriticalSection);
  1512. CPerformance *pPerf = m_pPerformance;
  1513. LeaveCriticalSection(&m_CriticalSection);
  1514. if (pPerf)
  1515. {
  1516. // Kill anything currently playing on the audiopath.
  1517. pPerf->StopEx(static_cast<IDirectMusicAudioPath*>(this),0,0);
  1518. }
  1519. }
  1520. EnterCriticalSection(&m_CriticalSection);
  1521. HRESULT hr = m_BufferConfigList.Activate(fActivate);
  1522. if (SUCCEEDED(hr))
  1523. {
  1524. hr = m_PortConfigList.Activate(fActivate);
  1525. if (FAILED(hr))
  1526. {
  1527. m_BufferConfigList.Activate(!fActivate);
  1528. }
  1529. }
  1530. LeaveCriticalSection(&m_CriticalSection);
  1531. if (FAILED(hr))
  1532. {
  1533. m_fActive = !fActivate;
  1534. }
  1535. return hr;
  1536. }
  1537. HRESULT STDMETHODCALLTYPE CAudioPath::ConvertPChannel( DWORD dwPChannelIn,DWORD *pdwPChannelOut)
  1538. {
  1539. V_INAME(IDirectMusicAudioPath::ConvertPChannel);
  1540. V_PTR_WRITE(pdwPChannelOut,DWORD);
  1541. // If any special PMsg address (for example, broadcast), leave as is.
  1542. if (dwPChannelIn >= DMUS_PCHANNEL_KILL_ME)
  1543. {
  1544. *pdwPChannelOut = dwPChannelIn;
  1545. return S_OK;
  1546. }
  1547. DWORD dwScan = 0;
  1548. for (;dwScan < m_dwChannelCount;dwScan++)
  1549. {
  1550. if (m_pdwVChannels[dwScan] == dwPChannelIn)
  1551. {
  1552. *pdwPChannelOut = m_pdwPChannels[dwScan];
  1553. return S_OK;
  1554. }
  1555. }
  1556. Trace(1,"Error: Audiopath failed request to convert out of range PChannel %ld\n",dwPChannelIn);
  1557. return DMUS_E_NOT_FOUND;
  1558. }
  1559. HRESULT STDMETHODCALLTYPE CAudioPath::Shutdown()
  1560. {
  1561. return E_NOTIMPL;
  1562. }
  1563. HRESULT STDMETHODCALLTYPE CAudioPath::InsertTool(
  1564. IDirectMusicTool *pTool,
  1565. DWORD *pdwPChannels,
  1566. DWORD cPChannels,
  1567. LONG lIndex)
  1568. {
  1569. return E_NOTIMPL;
  1570. }
  1571. HRESULT STDMETHODCALLTYPE CAudioPath::GetTool(
  1572. DWORD dwIndex,
  1573. IDirectMusicTool** ppTool)
  1574. {
  1575. return E_NOTIMPL;
  1576. }
  1577. HRESULT STDMETHODCALLTYPE CAudioPath::RemoveTool(
  1578. IDirectMusicTool* pTool)
  1579. {
  1580. return E_NOTIMPL;
  1581. }
  1582. HRESULT STDMETHODCALLTYPE CAudioPath::StampPMsg(
  1583. /* [in */ DMUS_PMSG* pPMsg)
  1584. {
  1585. V_INAME(IDirectMusicAudioPath::StampPMsg);
  1586. V_BUFPTR_WRITE(pPMsg,sizeof(DMUS_PMSG));
  1587. HRESULT hr = E_FAIL;
  1588. if (!m_fActive)
  1589. {
  1590. // Only kill notes and wave messages, since they are the only PMsgs that make sound.
  1591. if ((pPMsg->dwType == DMUS_PMSGT_NOTE) || (pPMsg->dwType == DMUS_PMSGT_WAVE))
  1592. {
  1593. pPMsg->dwPChannel = DMUS_PCHANNEL_KILL_ME;
  1594. Trace(1,"Error: Attempting to play on an inactive AudioPath, PMsg being ignored.\n");
  1595. return DMUS_E_AUDIOPATH_INACTIVE;
  1596. }
  1597. }
  1598. EnterCriticalSection(&m_CriticalSection);
  1599. if (!m_pPerformance)
  1600. {
  1601. LeaveCriticalSection(&m_CriticalSection);
  1602. return DMUS_E_NOT_INIT;
  1603. }
  1604. // First, check if the audio path has its own graph.
  1605. if (m_pGraph)
  1606. {
  1607. // Could return DMUS_S_LAST_TOOL, indicating end of graph.
  1608. // If so, we'll treat that as a failure and drop on through to the next graph...
  1609. if( S_OK == ( hr = m_pGraph->StampPMsg( pPMsg )))
  1610. {
  1611. if( pPMsg->pGraph != this ) // Make sure this is set to point to the segstate embedded graph so it will come here again.
  1612. {
  1613. if( pPMsg->pGraph )
  1614. {
  1615. pPMsg->pGraph->Release();
  1616. pPMsg->pGraph = NULL;
  1617. }
  1618. pPMsg->pGraph = this;
  1619. AddRef();
  1620. }
  1621. }
  1622. }
  1623. // If done with the graph, send to the performance. Also, check for the special case of
  1624. // DMUS_PCHANNEL_BROADCAST_AUDIOPATH. If so, duplicate the pMsg
  1625. // and send all the copies with the appropriate pchannel values.
  1626. // Otherwise, convert the vchannel to the matching pchannel (this is the
  1627. // point where the pchannel mapping occurs.)
  1628. if( FAILED(hr) || (hr == DMUS_S_LAST_TOOL))
  1629. {
  1630. if (pPMsg->dwPChannel == DMUS_PCHANNEL_BROADCAST_AUDIOPATH)
  1631. {
  1632. DWORD dwIndex;
  1633. for (dwIndex = 1;dwIndex < m_dwChannelCount;dwIndex++)
  1634. {
  1635. DWORD dwNewChannel = m_pdwPChannels[dwIndex];
  1636. // Don't broadcast any broadcast messages!
  1637. // And, if this is a transpose on the drum channel, don't send it.
  1638. if ((dwNewChannel < DMUS_PCHANNEL_BROADCAST_GROUPS) &&
  1639. ((pPMsg->dwType != DMUS_PMSGT_TRANSPOSE) || ((dwNewChannel & 0xF) != 9)))
  1640. {
  1641. DMUS_PMSG *pNewMsg;
  1642. if (SUCCEEDED(m_pPerformance->ClonePMsg(pPMsg,&pNewMsg)))
  1643. {
  1644. pNewMsg->dwPChannel = dwNewChannel;
  1645. m_pPerformance->StampPMsg(pNewMsg);
  1646. m_pPerformance->SendPMsg(pNewMsg);
  1647. }
  1648. }
  1649. }
  1650. // Now, set the pchannel for this one. First check that there are any
  1651. // pchannels. If none, mark the PMsg to be deleted by the SendPMsg routine.
  1652. // Also, mark it this way if the PMsg is a broadcast PMsg.
  1653. pPMsg->dwPChannel = DMUS_PCHANNEL_KILL_ME;
  1654. if (m_dwChannelCount)
  1655. {
  1656. if (m_pdwPChannels[0] < DMUS_PCHANNEL_BROADCAST_GROUPS)
  1657. {
  1658. pPMsg->dwPChannel = m_pdwPChannels[0];
  1659. }
  1660. }
  1661. }
  1662. else
  1663. {
  1664. DWORD dwScan = 0;
  1665. for (;dwScan < m_dwChannelCount;dwScan++)
  1666. {
  1667. if (m_pdwVChannels[dwScan] == pPMsg->dwPChannel)
  1668. {
  1669. pPMsg->dwPChannel = m_pdwPChannels[dwScan];
  1670. break;
  1671. }
  1672. }
  1673. // If a map was not found, kill the message.
  1674. // But, ignore for notifications, since they really don't care about pchannel.
  1675. // And, ignore for performance broadcast PMsgs.
  1676. if ((dwScan == m_dwChannelCount) &&
  1677. (pPMsg->dwType != DMUS_PMSGT_NOTIFICATION) &&
  1678. (pPMsg->dwPChannel < DMUS_PCHANNEL_BROADCAST_GROUPS))
  1679. {
  1680. pPMsg->dwPChannel = DMUS_PCHANNEL_KILL_ME;
  1681. }
  1682. }
  1683. hr = m_pPerformance->StampPMsg(pPMsg);
  1684. }
  1685. LeaveCriticalSection(&m_CriticalSection);
  1686. return hr;
  1687. }
  1688. CGraph *CAudioPath::GetGraph()
  1689. {
  1690. CGraph *pGraph;
  1691. EnterCriticalSection(&m_CriticalSection);
  1692. // Return the graph, and AddRef if it exists.
  1693. if (pGraph = m_pGraph)
  1694. {
  1695. m_pGraph->AddRef();
  1696. }
  1697. LeaveCriticalSection(&m_CriticalSection);
  1698. return pGraph;
  1699. }
  1700. void CAudioPath::Deactivate()
  1701. {
  1702. m_fDeactivating = TRUE;
  1703. Activate(FALSE);
  1704. EnterCriticalSection(&m_CriticalSection);
  1705. m_fActive = FALSE;
  1706. m_PortConfigList.Clear();
  1707. m_BufferConfigList.Clear();
  1708. if (m_pGraph)
  1709. {
  1710. m_pGraph->Release();
  1711. m_pGraph = NULL;
  1712. }
  1713. if (m_pConfig)
  1714. {
  1715. m_pConfig->Release();
  1716. m_pConfig = NULL;
  1717. }
  1718. if (m_pPerformance)
  1719. {
  1720. if (m_pdwVChannels && m_pdwPChannels)
  1721. {
  1722. DWORD dwIndex;
  1723. for (dwIndex = 0;dwIndex <m_dwChannelCount;dwIndex++)
  1724. {
  1725. m_pPerformance->ReleasePChannel(m_pdwPChannels[dwIndex]);
  1726. }
  1727. delete [] m_pdwVChannels;
  1728. delete [] m_pdwPChannels;
  1729. }
  1730. m_pPerformance->m_AudioPathList.Remove(this);
  1731. m_pPerformance->RemoveUnusedPorts();
  1732. m_pPerformance->Release();
  1733. m_pPerformance = NULL;
  1734. }
  1735. m_fDeactivating = FALSE;
  1736. LeaveCriticalSection(&m_CriticalSection);
  1737. }
  1738. void CAudioPath::SetGraph(CGraph *pGraph)
  1739. {
  1740. EnterCriticalSection(&m_CriticalSection);
  1741. // Is this a change?
  1742. if (!m_pGraph)
  1743. {
  1744. pGraph->Clone((IDirectMusicGraph **) &m_pGraph);
  1745. }
  1746. LeaveCriticalSection(&m_CriticalSection);
  1747. }
  1748. CBufferNode * CAudioPath::GetBufferNode(REFGUID guidBuffer)
  1749. {
  1750. CBufferNode *pBuff = NULL;
  1751. EnterCriticalSection(&m_CriticalSection);
  1752. pBuff = m_BufferConfigList.GetBufferNode(guidBuffer);
  1753. if (!pBuff)
  1754. {
  1755. CPortConfig *pConfig = m_PortConfigList.GetHead();
  1756. for (;pConfig;pConfig = pConfig->GetNext())
  1757. {
  1758. pBuff = pConfig->GetBufferNode(guidBuffer);
  1759. if (pBuff)
  1760. {
  1761. break;
  1762. }
  1763. }
  1764. }
  1765. LeaveCriticalSection(&m_CriticalSection);
  1766. if (!pBuff)
  1767. {
  1768. pBuff = m_pPerformance->m_BufferManager.GetBufferNode(guidBuffer);
  1769. }
  1770. return pBuff;
  1771. }
  1772. HRESULT CAudioPath::Init(IUnknown *pSourceConfig,CPerformance *pPerf)
  1773. {
  1774. HRESULT hr = E_INVALIDARG;
  1775. EnterCriticalSection(&m_CriticalSection);
  1776. m_pPerformance = pPerf;
  1777. pPerf->m_AudioPathList.AddHead(this);
  1778. pPerf->AddRef();
  1779. if (pPerf && pSourceConfig)
  1780. {
  1781. if (SUCCEEDED(hr = pSourceConfig->QueryInterface(IID_CAudioPathConfig,(void **) &m_pConfig)))
  1782. {
  1783. if (m_pConfig->m_pGraph)
  1784. {
  1785. SetGraph(m_pConfig->m_pGraph);
  1786. }
  1787. // The very first audio path has to create the sink.
  1788. hr = pPerf->m_BufferManager.InitSink();
  1789. if (SUCCEEDED(hr))
  1790. {
  1791. // First, install any global buffers that are required.
  1792. hr = m_pConfig->m_BufferConfigList.CreateRunTimeVersion(&m_BufferConfigList,&pPerf->m_BufferManager);
  1793. if (SUCCEEDED(hr))
  1794. {
  1795. // Then, install the ports and buffers.
  1796. hr = m_pConfig->m_PortConfigList.CreateRunTimeVersion(&m_PortConfigList,this,&pPerf->m_BufferManager);
  1797. if (SUCCEEDED(hr))
  1798. {
  1799. hr = ConnectToPorts(pPerf,pPerf->m_AudioParams.dwSampleRate);
  1800. }
  1801. }
  1802. }
  1803. }
  1804. }
  1805. LeaveCriticalSection(&m_CriticalSection);
  1806. return hr;
  1807. }
  1808. HRESULT CAudioPath::ConnectToPorts(CPerformance *pPerf,DWORD dwSampleRate)
  1809. /* This must be called from within a critical section.
  1810. */
  1811. {
  1812. HRESULT hr = S_OK;
  1813. // Scan through the list of portconfigs and hook them up with active ports
  1814. // in the performance. If a port is not available, create the port.
  1815. CPortConfig *pConfig = m_PortConfigList.GetHead();
  1816. DWORD dwChannelCount = 0; // Used to add up total PChannels needed.
  1817. for (;pConfig && SUCCEEDED(hr);pConfig = pConfig->GetNext())
  1818. {
  1819. // Given the configuration, either find a port with a matching id, or create one.
  1820. hr = pPerf->GetPathPort(pConfig);
  1821. dwChannelCount += pConfig->m_PortHeader.dwPChannelCount;
  1822. }
  1823. if (SUCCEEDED(hr))
  1824. {
  1825. // Now, allocate the VChannels needed for each portconfig.
  1826. m_pdwVChannels = new DWORD[dwChannelCount];
  1827. if (m_pdwVChannels)
  1828. {
  1829. m_pdwPChannels = new DWORD[dwChannelCount];
  1830. if (!m_pdwPChannels)
  1831. {
  1832. delete [] m_pdwVChannels;
  1833. m_pdwVChannels = NULL;
  1834. hr = E_OUTOFMEMORY;
  1835. }
  1836. }
  1837. else
  1838. {
  1839. hr = E_OUTOFMEMORY;
  1840. }
  1841. }
  1842. if (SUCCEEDED(hr))
  1843. {
  1844. // Scan through the port configs and allocate the pchannels, copying the assignments
  1845. // into virtual channel assignment arrays.
  1846. pConfig = m_PortConfigList.GetHead();
  1847. DWORD dwIndex = 0;
  1848. for (;pConfig;pConfig = pConfig->GetNext())
  1849. {
  1850. // If this port uses buffers, then connect them up.
  1851. if (((pConfig->m_PortParams.dwValidParams & DMUS_PORTPARAMS_FEATURES) &&
  1852. (pConfig->m_PortParams.dwFeatures & DMUS_PORT_FEATURE_AUDIOPATH)))
  1853. {
  1854. CBufferConnect *pConnect = pConfig->m_BufferConnectList.GetHead();
  1855. for (;pConnect && SUCCEEDED(hr);pConnect = pConnect->GetNext())
  1856. {
  1857. // For each connect block, there should be an array of buffers
  1858. // to connect the range of PChannels to.
  1859. // For each PChannel, get a virtual pchannel and then assign
  1860. // it to the bus ids that belong to the buffers.
  1861. if (pConnect->m_ppBufferNodes)
  1862. {
  1863. DWORD dwCount = 0;
  1864. DWORD dwBusIDs[32];
  1865. DWORD *pdwBusIDBase = &dwBusIDs[0];
  1866. DWORD dwTotalRead = 0;
  1867. DWORD dwAmountLeft = 32;
  1868. for (;(dwCount < pConnect->m_ConnectHeader.dwBufferCount) && dwAmountLeft; dwCount++)
  1869. {
  1870. if (pConnect->m_ppBufferNodes[dwCount] && !(pConnect->m_ppBufferNodes[dwCount]->m_BufferHeader.dwFlags & DMUS_BUFFERF_PRIMARY))
  1871. {
  1872. IDirectSoundBuffer *pBuffer = pConnect->m_ppBufferNodes[dwCount]->GetBuffer();
  1873. if (pBuffer)
  1874. {
  1875. dwTotalRead = dwAmountLeft;
  1876. hr = m_pPerformance->m_BufferManager.m_pSinkConnect->GetSoundBufferBusIDs(pBuffer,pdwBusIDBase,NULL,&dwTotalRead);
  1877. pBuffer->Release();
  1878. if (FAILED(hr)) break;
  1879. pdwBusIDBase += dwTotalRead; // Increment pointer by how many was read.
  1880. dwAmountLeft -= dwTotalRead;
  1881. }
  1882. }
  1883. }
  1884. if (SUCCEEDED(hr))
  1885. {
  1886. dwTotalRead = 32 - dwAmountLeft;
  1887. // Now, allocate the pchannels and assign them to buses.
  1888. IDirectMusicPortP* pPortP = NULL;
  1889. if (SUCCEEDED(pConfig->m_pPort->QueryInterface(IID_IDirectMusicPortP, (void**)&pPortP)))
  1890. {
  1891. for (dwCount = 0;dwCount < pConnect->m_ConnectHeader.dwPChannelCount; dwCount++)
  1892. {
  1893. DWORD dwDrumFlags = 0;
  1894. m_pdwVChannels[dwIndex] = pConnect->m_ConnectHeader.dwPChannelBase + dwCount;
  1895. if (pConfig->m_PortHeader.dwFlags & DMUS_PORTCONFIGF_DRUMSON10)
  1896. {
  1897. dwDrumFlags = 1;
  1898. if (((pConnect->m_ConnectHeader.dwPChannelBase + dwCount) & 0xF) == 9)
  1899. {
  1900. // This is a drum on channel 10.
  1901. dwDrumFlags |= 2;
  1902. }
  1903. }
  1904. // Now, allocate a virtual pchannel for this and get back the equivalent group and midi channel.
  1905. DWORD dwGroup;
  1906. DWORD dwMChannel;
  1907. hr = pPerf->AllocVChannel(pConfig->m_dwPortID,dwDrumFlags,&m_pdwPChannels[dwIndex],&dwGroup,&dwMChannel);
  1908. if (dwTotalRead && SUCCEEDED(hr))
  1909. {
  1910. hr = pPortP->AssignChannelToBuses(dwGroup,dwMChannel,dwBusIDs,dwTotalRead);
  1911. }
  1912. dwIndex++;
  1913. }
  1914. pPortP->Release();
  1915. }
  1916. }
  1917. }
  1918. }
  1919. }
  1920. else
  1921. {
  1922. DWORD dwCount;
  1923. for (dwCount = 0;SUCCEEDED(hr) && (dwCount < pConfig->m_PortHeader.dwPChannelCount); dwCount++)
  1924. {
  1925. DWORD dwDrumFlags = 0;
  1926. m_pdwVChannels[dwIndex] = pConfig->m_PortHeader.dwPChannelBase + dwCount;
  1927. if (pConfig->m_PortHeader.dwFlags & DMUS_PORTCONFIGF_DRUMSON10)
  1928. {
  1929. dwDrumFlags = 1;
  1930. if (((pConfig->m_PortHeader.dwPChannelBase + dwCount) & 0xF) == 9)
  1931. {
  1932. // This is a drum on channel 10.
  1933. dwDrumFlags |= 2;
  1934. }
  1935. }
  1936. // Now, allocate a virtual pchannel for this.
  1937. DWORD dwGroup; // These won't be used since we won't be assigning pchannels on the port to buffers.
  1938. DWORD dwMChannel;
  1939. hr = pPerf->AllocVChannel(pConfig->m_dwPortID,dwDrumFlags,&m_pdwPChannels[dwIndex],&dwGroup,&dwMChannel);
  1940. /* Trace(0,"%ld: Mapping %ld to %ld (Port %ld, Group %ld, Channel %ld)\n",
  1941. dwIndex,m_pdwVChannels[dwIndex],m_pdwPChannels[dwIndex],
  1942. pConfig->m_dwPortID,dwGroup,dwMChannel);*/
  1943. dwIndex++;
  1944. }
  1945. }
  1946. }
  1947. }
  1948. m_dwChannelCount = dwChannelCount;
  1949. return hr;
  1950. }
  1951. void CAudioPathList::Clear()
  1952. {
  1953. CAudioPath *pPath;
  1954. while (pPath = GetHead())
  1955. {
  1956. pPath->Deactivate(); // This should also remove it from the list.
  1957. assert(pPath != GetHead()); // Make sure this is always the case!
  1958. }
  1959. }
  1960. CBufferNode * CAudioPathList::GetBufferNode(REFGUID guidBufferID)
  1961. {
  1962. CBufferNode *pBuff = NULL;
  1963. CAudioPath *pPath;
  1964. for (pPath = GetHead();pPath;pPath = pPath->GetNext())
  1965. {
  1966. pBuff = pPath->GetBufferNode(guidBufferID);
  1967. if (pBuff)
  1968. {
  1969. break;
  1970. }
  1971. }
  1972. return pBuff;
  1973. }
  1974. BOOL CAudioPathList::UsesPort(IDirectMusicPort *pPort)
  1975. {
  1976. CAudioPath *pPath = GetHead();
  1977. for (;pPath;pPath = pPath->GetNext())
  1978. {
  1979. if (pPath->UsesPort(pPort))
  1980. {
  1981. return TRUE;
  1982. }
  1983. }
  1984. return FALSE;
  1985. }
  1986. CAudioPathConfig::CAudioPathConfig()
  1987. {
  1988. m_pGraph = NULL;
  1989. m_fPartialLoad = 0;
  1990. m_cRef = 1;
  1991. memset(&m_guidObject,0,sizeof(m_guidObject));
  1992. m_dwValidData = DMUS_OBJ_CLASS; // upon creation, only this data is valid
  1993. memset(&m_guidObject,0,sizeof(m_guidObject));
  1994. memset(&m_ftDate, 0,sizeof(m_ftDate));
  1995. memset(&m_vVersion, 0,sizeof(m_vVersion));
  1996. m_pUnkDispatch = NULL;
  1997. InitializeCriticalSection(&m_CriticalSection);
  1998. InterlockedIncrement(&g_cComponent);
  1999. }
  2000. CAudioPathConfig::~CAudioPathConfig()
  2001. {
  2002. if (m_pGraph)
  2003. {
  2004. m_pGraph->Release();
  2005. }
  2006. if (m_pUnkDispatch)
  2007. {
  2008. m_pUnkDispatch->Release(); // free IDispatch implementation we may have borrowed
  2009. }
  2010. m_PortConfigList.Clear();
  2011. m_BufferConfigList.Clear();
  2012. DeleteCriticalSection(&m_CriticalSection);
  2013. InterlockedDecrement(&g_cComponent);
  2014. }
  2015. CAudioPathConfig *CAudioPathConfig::CreateStandardConfig(DWORD dwType,DWORD dwPChannelCount,DWORD dwSampleRate)
  2016. {
  2017. CAudioPathConfig *pConfig = new CAudioPathConfig;
  2018. if (pConfig)
  2019. {
  2020. DWORD dwGlobalType = 0; // Global mixin buffer.
  2021. DWORD dwTypes[3]; // What types of buffers to create.
  2022. DWORD dwTotal = 0; // How many buffers.
  2023. GUID guidBufferIDs[3]; // IDs of buffers that should be connected to.
  2024. DWORD dwConnections = 0; // How many buffer connections.
  2025. BOOL fCreatePort = TRUE;
  2026. switch (dwType)
  2027. {
  2028. case DMUS_APATH_SHARED_STEREOPLUSREVERB:
  2029. dwTypes[0] = BUFFER_MUSIC;
  2030. dwTypes[1] = BUFFER_REVERB;
  2031. guidBufferIDs[0] = GUID_Buffer_Stereo;
  2032. guidBufferIDs[1] = GUID_Buffer_Reverb;
  2033. dwConnections = 2;
  2034. dwTotal = 2;
  2035. break;
  2036. // Following removed for DX8, should be reintroduced for Whistler and DX8.1...
  2037. /* case DMUS_APATH_DYNAMIC_ENV3D:
  2038. dwGlobalType = BUFFER_ENVREVERB;
  2039. dwTypes[0] = BUFFER_3D;
  2040. guidBufferIDs[0] = GUID_Buffer_3D;
  2041. dwConnections = 1;
  2042. dwTotal = 1;
  2043. break;*/
  2044. case DMUS_APATH_DYNAMIC_3D:
  2045. dwTypes[0] = BUFFER_3D_DRY;
  2046. guidBufferIDs[0] = GUID_Buffer_3D_Dry;
  2047. dwConnections = 1;
  2048. dwTotal = 1;
  2049. break;
  2050. case DMUS_APATH_DYNAMIC_MONO:
  2051. dwTypes[0] = BUFFER_MONO;
  2052. guidBufferIDs[0] = GUID_Buffer_Mono;
  2053. dwConnections = 1;
  2054. dwTotal = 1;
  2055. break;
  2056. case DMUS_APATH_DYNAMIC_STEREO:
  2057. dwTypes[0] = BUFFER_STEREO;
  2058. guidBufferIDs[0] = GUID_Buffer_Stereo;
  2059. dwConnections = 1;
  2060. dwTotal = 1;
  2061. break;
  2062. }
  2063. if (dwGlobalType)
  2064. {
  2065. CBufferConfig *pBuffer = new CBufferConfig(dwGlobalType);
  2066. if (pBuffer)
  2067. {
  2068. // This buffer configuration just has an id to identify which standard
  2069. // buffer, instead of a pointer to a DSoundBufferConfig object,
  2070. // which is what you'd see in the file io case.
  2071. pConfig->m_BufferConfigList.AddHead(pBuffer);
  2072. }
  2073. else
  2074. {
  2075. delete pConfig;
  2076. return NULL;
  2077. }
  2078. }
  2079. if (fCreatePort)
  2080. {
  2081. CPortConfig *pPort = new CPortConfig();
  2082. if (pPort)
  2083. {
  2084. pConfig->m_PortConfigList.AddHead(pPort);
  2085. for (DWORD dwIndex = 0; dwIndex < dwTotal; dwIndex++)
  2086. {
  2087. CBufferConfig *pBuffer = new CBufferConfig(dwTypes[dwIndex]);
  2088. if (pBuffer)
  2089. {
  2090. // This buffer configuration just has an id to identify which standard
  2091. // buffer, instead of a pointer to a DSoundBufferConfig object,
  2092. // which is what you'd see in the file io case.
  2093. pPort->m_BufferConfigList.AddHead(pBuffer);
  2094. }
  2095. else
  2096. {
  2097. delete pConfig;
  2098. return NULL;
  2099. }
  2100. }
  2101. // If there are connections to buffers, create the connection structure.
  2102. if (dwConnections)
  2103. {
  2104. CBufferConnect *pConnect = new CBufferConnect;
  2105. if (pConnect)
  2106. {
  2107. pPort->m_BufferConnectList.AddHead(pConnect);
  2108. pConnect->m_ConnectHeader.dwBufferCount = dwConnections;
  2109. pConnect->m_ConnectHeader.dwFlags = 0;
  2110. pConnect->m_ConnectHeader.dwPChannelBase = 0;
  2111. pConnect->m_ConnectHeader.dwPChannelCount = dwPChannelCount;
  2112. pConnect->m_pguidBufferIDs = new GUID[dwConnections];
  2113. if (pConnect->m_pguidBufferIDs)
  2114. {
  2115. for (DWORD dwIndex = 0; dwIndex < dwConnections; dwIndex++)
  2116. {
  2117. pConnect->m_pguidBufferIDs[dwIndex] = guidBufferIDs[dwIndex];
  2118. }
  2119. }
  2120. else
  2121. {
  2122. delete pConfig;
  2123. return NULL;
  2124. }
  2125. }
  2126. }
  2127. pPort->m_PortHeader.dwPChannelCount = dwPChannelCount;
  2128. pPort->m_PortParams.dwChannelGroups = (dwPChannelCount + 15) / 16;
  2129. }
  2130. else
  2131. {
  2132. delete pConfig;
  2133. pConfig = NULL;
  2134. }
  2135. }
  2136. }
  2137. return pConfig;
  2138. }
  2139. STDMETHODIMP CAudioPathConfig::QueryInterface(
  2140. const IID &iid,
  2141. void **ppv)
  2142. {
  2143. V_INAME(CAudioPathConfig::QueryInterface);
  2144. V_PTRPTR_WRITE(ppv);
  2145. V_REFGUID(iid);
  2146. *ppv = NULL;
  2147. if ((iid == IID_IUnknown ) || (iid == IID_IDirectMusicObject))
  2148. {
  2149. *ppv = static_cast<IDirectMusicObject*>(this);
  2150. }
  2151. else if (iid == IID_IPersistStream)
  2152. {
  2153. *ppv = static_cast<IPersistStream*>(this);
  2154. }
  2155. else if (iid == IID_CAudioPathConfig)
  2156. {
  2157. *ppv = static_cast<CAudioPathConfig*>(this);
  2158. }
  2159. else if (iid == IID_IDispatch)
  2160. {
  2161. // A helper scripting object implements IDispatch, which we expose via COM aggregation.
  2162. if (!m_pUnkDispatch)
  2163. {
  2164. // Create the helper object
  2165. ::CoCreateInstance(
  2166. CLSID_AutDirectMusicAudioPathConfig,
  2167. static_cast<IDirectMusicObject*>(this),
  2168. CLSCTX_INPROC_SERVER,
  2169. IID_IUnknown,
  2170. reinterpret_cast<void**>(&m_pUnkDispatch));
  2171. }
  2172. if (m_pUnkDispatch)
  2173. {
  2174. return m_pUnkDispatch->QueryInterface(IID_IDispatch, ppv);
  2175. }
  2176. }
  2177. if (*ppv == NULL)
  2178. {
  2179. Trace(4,"Warning: Request to query unknown interface on AudioPathConfig object\n");
  2180. return E_NOINTERFACE;
  2181. }
  2182. AddRef();
  2183. return S_OK;
  2184. }
  2185. STDMETHODIMP_(ULONG) CAudioPathConfig::AddRef()
  2186. {
  2187. return InterlockedIncrement(&m_cRef);
  2188. }
  2189. STDMETHODIMP_(ULONG) CAudioPathConfig::Release()
  2190. {
  2191. if (!InterlockedDecrement(&m_cRef))
  2192. {
  2193. delete this;
  2194. return 0;
  2195. }
  2196. return m_cRef;
  2197. }
  2198. STDMETHODIMP CAudioPathConfig::GetDescriptor(LPDMUS_OBJECTDESC pDesc)
  2199. {
  2200. // Argument validation
  2201. V_INAME(CAudioPathConfig::GetDescriptor);
  2202. V_STRUCTPTR_WRITE(pDesc, DMUS_OBJECTDESC);
  2203. pDesc->guidClass = CLSID_DirectMusicAudioPathConfig;
  2204. pDesc->guidObject = m_guidObject;
  2205. pDesc->ftDate = m_ftDate;
  2206. pDesc->vVersion = m_vVersion;
  2207. memcpy( pDesc->wszName, m_wszName, sizeof(m_wszName) );
  2208. memcpy( pDesc->wszCategory, m_wszCategory, sizeof(m_wszCategory) );
  2209. memcpy( pDesc->wszFileName, m_wszFileName, sizeof(m_wszFileName) );
  2210. pDesc->dwValidData = ( m_dwValidData | DMUS_OBJ_CLASS );
  2211. return S_OK;
  2212. }
  2213. STDMETHODIMP CAudioPathConfig::SetDescriptor(LPDMUS_OBJECTDESC pDesc)
  2214. {
  2215. // Argument validation
  2216. V_INAME(CAudioPathConfig::SetDescriptor);
  2217. V_STRUCTPTR_READ(pDesc, DMUS_OBJECTDESC);
  2218. HRESULT hr = E_INVALIDARG;
  2219. DWORD dw = 0;
  2220. if( pDesc->dwSize >= sizeof(DMUS_OBJECTDESC) )
  2221. {
  2222. if( pDesc->dwValidData & DMUS_OBJ_OBJECT )
  2223. {
  2224. m_guidObject = pDesc->guidObject;
  2225. dw |= DMUS_OBJ_OBJECT;
  2226. }
  2227. if( pDesc->dwValidData & DMUS_OBJ_NAME )
  2228. {
  2229. memcpy( m_wszName, pDesc->wszName, sizeof(WCHAR)*DMUS_MAX_NAME );
  2230. dw |= DMUS_OBJ_NAME;
  2231. }
  2232. if( pDesc->dwValidData & DMUS_OBJ_CATEGORY )
  2233. {
  2234. memcpy( m_wszCategory, pDesc->wszCategory, sizeof(WCHAR)*DMUS_MAX_CATEGORY );
  2235. dw |= DMUS_OBJ_CATEGORY;
  2236. }
  2237. if( ( pDesc->dwValidData & DMUS_OBJ_FILENAME ) ||
  2238. ( pDesc->dwValidData & DMUS_OBJ_FULLPATH ) )
  2239. {
  2240. memcpy( m_wszFileName, pDesc->wszFileName, sizeof(WCHAR)*DMUS_MAX_FILENAME );
  2241. dw |= (pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH));
  2242. }
  2243. if( pDesc->dwValidData & DMUS_OBJ_VERSION )
  2244. {
  2245. m_vVersion = pDesc->vVersion;
  2246. dw |= DMUS_OBJ_VERSION;
  2247. }
  2248. if( pDesc->dwValidData & DMUS_OBJ_DATE )
  2249. {
  2250. m_ftDate = pDesc->ftDate;
  2251. dw |= DMUS_OBJ_DATE;
  2252. }
  2253. m_dwValidData |= dw;
  2254. if( pDesc->dwValidData & (~dw) )
  2255. {
  2256. Trace(2,"Warning: AudioPathConfig::SetDescriptor was not able to handle all passed fields, dwValidData bits %lx.\n",pDesc->dwValidData & (~dw));
  2257. hr = S_FALSE; // there were extra fields we didn't parse;
  2258. pDesc->dwValidData = dw;
  2259. }
  2260. else
  2261. {
  2262. hr = S_OK;
  2263. }
  2264. }
  2265. else
  2266. {
  2267. Trace(1,"Error: Descriptor size is larger than AudioPathConfig::SetDescriptor can handle\n");
  2268. }
  2269. return hr;
  2270. }
  2271. STDMETHODIMP CAudioPathConfig::ParseDescriptor(LPSTREAM pIStream, LPDMUS_OBJECTDESC pDesc)
  2272. {
  2273. V_INAME(CAudioPathConfig::ParseDescriptor);
  2274. V_INTERFACE(pIStream);
  2275. V_STRUCTPTR_WRITE(pDesc, DMUS_OBJECTDESC);
  2276. CRiffParser Parser(pIStream);
  2277. RIFFIO ckMain;
  2278. RIFFIO ckNext;
  2279. RIFFIO ckUNFO;
  2280. HRESULT hr = S_OK;
  2281. Parser.EnterList(&ckMain);
  2282. if (Parser.NextChunk(&hr) && (ckMain.fccType == DMUS_FOURCC_AUDIOPATH_FORM))
  2283. {
  2284. pDesc->dwValidData = DMUS_OBJ_CLASS;
  2285. pDesc->guidClass = CLSID_DirectMusicAudioPathConfig;
  2286. Parser.EnterList(&ckNext);
  2287. while(Parser.NextChunk(&hr))
  2288. {
  2289. switch(ckNext.ckid)
  2290. {
  2291. case DMUS_FOURCC_GUID_CHUNK:
  2292. hr = Parser.Read( &pDesc->guidObject, sizeof(GUID) );
  2293. if( SUCCEEDED(hr) )
  2294. {
  2295. pDesc->dwValidData |= DMUS_OBJ_OBJECT;
  2296. }
  2297. break;
  2298. case DMUS_FOURCC_VERSION_CHUNK:
  2299. hr = Parser.Read( &pDesc->vVersion, sizeof(DMUS_VERSION) );
  2300. if( SUCCEEDED(hr) )
  2301. {
  2302. pDesc->dwValidData |= DMUS_OBJ_VERSION;
  2303. }
  2304. break;
  2305. case DMUS_FOURCC_CATEGORY_CHUNK:
  2306. hr = Parser.Read( &pDesc->wszCategory, sizeof(pDesc->wszCategory) );
  2307. if( SUCCEEDED(hr) )
  2308. {
  2309. pDesc->dwValidData |= DMUS_OBJ_CATEGORY;
  2310. }
  2311. break;
  2312. case DMUS_FOURCC_DATE_CHUNK:
  2313. hr = Parser.Read( &pDesc->ftDate, sizeof(FILETIME) );
  2314. if( SUCCEEDED(hr))
  2315. {
  2316. pDesc->dwValidData |= DMUS_OBJ_DATE;
  2317. }
  2318. break;
  2319. case FOURCC_LIST:
  2320. switch(ckNext.fccType)
  2321. {
  2322. case DMUS_FOURCC_UNFO_LIST:
  2323. Parser.EnterList(&ckUNFO);
  2324. while (Parser.NextChunk(&hr))
  2325. {
  2326. switch( ckUNFO.ckid )
  2327. {
  2328. case DMUS_FOURCC_UNAM_CHUNK:
  2329. {
  2330. hr = Parser.Read(&pDesc->wszName, sizeof(pDesc->wszName));
  2331. if(SUCCEEDED(hr) )
  2332. {
  2333. pDesc->dwValidData |= DMUS_OBJ_NAME;
  2334. }
  2335. break;
  2336. }
  2337. default:
  2338. break;
  2339. }
  2340. }
  2341. Parser.LeaveList();
  2342. break;
  2343. }
  2344. break;
  2345. default:
  2346. break;
  2347. }
  2348. }
  2349. Parser.LeaveList();
  2350. }
  2351. else
  2352. {
  2353. Trace(1,"Error: Failed parsing - file is not AudioPathConfig format.\n");
  2354. hr = DMUS_E_CHUNKNOTFOUND;
  2355. }
  2356. return hr;
  2357. }
  2358. /////////////////////////////////////////////////////////////////////////////
  2359. // IPersist
  2360. HRESULT CAudioPathConfig::GetClassID( CLSID* pClassID )
  2361. {
  2362. V_INAME(CAudioPathConfig::GetClassID);
  2363. V_PTR_WRITE(pClassID, CLSID);
  2364. *pClassID = CLSID_DirectMusicAudioPathConfig;
  2365. return S_OK;
  2366. }
  2367. /////////////////////////////////////////////////////////////////////////////
  2368. // IPersistStream functions
  2369. HRESULT CAudioPathConfig::IsDirty()
  2370. {
  2371. return S_FALSE;
  2372. }
  2373. HRESULT CAudioPathConfig::Load( IStream* pIStream )
  2374. {
  2375. V_INAME(IPersistStream::Load);
  2376. V_INTERFACE(pIStream);
  2377. CRiffParser Parser(pIStream);
  2378. RIFFIO ckMain;
  2379. HRESULT hr = S_OK;
  2380. Parser.EnterList(&ckMain);
  2381. if (Parser.NextChunk(&hr) && (ckMain.fccType == DMUS_FOURCC_AUDIOPATH_FORM))
  2382. {
  2383. EnterCriticalSection(&m_CriticalSection);
  2384. // Clear out any data that was previously loaded.
  2385. if (m_pGraph)
  2386. {
  2387. m_pGraph->Release();
  2388. }
  2389. m_PortConfigList.Clear();
  2390. m_BufferConfigList.Clear();
  2391. hr = Load(&Parser);
  2392. LeaveCriticalSection(&m_CriticalSection);
  2393. }
  2394. else
  2395. {
  2396. Trace(1,"Error: Failed parsing - file is not AudioPathConfig format.\n");
  2397. hr = DMUS_E_DESCEND_CHUNK_FAIL;
  2398. }
  2399. return hr;
  2400. }
  2401. HRESULT CAudioPathConfig::Load(CRiffParser *pParser)
  2402. {
  2403. RIFFIO ckNext;
  2404. HRESULT hr = S_OK;
  2405. EnterCriticalSection(&m_CriticalSection);
  2406. pParser->EnterList(&ckNext);
  2407. while(pParser->NextChunk(&hr))
  2408. {
  2409. switch(ckNext.ckid)
  2410. {
  2411. case DMUS_FOURCC_GUID_CHUNK:
  2412. hr = pParser->Read( &m_guidObject, sizeof(GUID) );
  2413. if( SUCCEEDED(hr) )
  2414. {
  2415. m_dwValidData |= DMUS_OBJ_OBJECT;
  2416. }
  2417. break;
  2418. case DMUS_FOURCC_VERSION_CHUNK:
  2419. hr = pParser->Read( &m_vVersion, sizeof(DMUS_VERSION) );
  2420. if( SUCCEEDED(hr) )
  2421. {
  2422. m_dwValidData |= DMUS_OBJ_VERSION;
  2423. }
  2424. break;
  2425. case DMUS_FOURCC_CATEGORY_CHUNK:
  2426. hr = pParser->Read( &m_wszCategory, sizeof(WCHAR)*DMUS_MAX_CATEGORY );
  2427. if( SUCCEEDED(hr) )
  2428. {
  2429. m_dwValidData |= DMUS_OBJ_CATEGORY;
  2430. }
  2431. break;
  2432. case DMUS_FOURCC_DATE_CHUNK:
  2433. hr = pParser->Read( &m_ftDate, sizeof(FILETIME) );
  2434. if( SUCCEEDED(hr))
  2435. {
  2436. m_dwValidData |= DMUS_OBJ_DATE;
  2437. }
  2438. break;
  2439. case FOURCC_LIST:
  2440. case FOURCC_RIFF:
  2441. switch(ckNext.fccType)
  2442. {
  2443. RIFFIO ckChild;
  2444. case DMUS_FOURCC_UNFO_LIST:
  2445. pParser->EnterList(&ckChild);
  2446. while (pParser->NextChunk(&hr))
  2447. {
  2448. switch( ckChild.ckid )
  2449. {
  2450. case DMUS_FOURCC_UNAM_CHUNK:
  2451. {
  2452. hr = pParser->Read(&m_wszName, sizeof(m_wszName));
  2453. if(SUCCEEDED(hr) )
  2454. {
  2455. m_dwValidData |= DMUS_OBJ_NAME;
  2456. }
  2457. break;
  2458. }
  2459. default:
  2460. break;
  2461. }
  2462. }
  2463. pParser->LeaveList();
  2464. break;
  2465. case DMUS_FOURCC_PORTCONFIGS_LIST:
  2466. pParser->EnterList(&ckChild);
  2467. while (pParser->NextChunk(&hr))
  2468. {
  2469. switch( ckChild.ckid )
  2470. {
  2471. case FOURCC_LIST:
  2472. if (ckChild.fccType == DMUS_FOURCC_PORTCONFIG_LIST)
  2473. {
  2474. CPortConfig *pConfig = new CPortConfig();
  2475. if (pConfig)
  2476. {
  2477. hr = pConfig->Load(pParser);
  2478. if (SUCCEEDED(hr))
  2479. {
  2480. m_PortConfigList.AddTail(pConfig);
  2481. }
  2482. }
  2483. else
  2484. {
  2485. hr = E_OUTOFMEMORY;
  2486. }
  2487. }
  2488. break;
  2489. }
  2490. }
  2491. pParser->LeaveList();
  2492. break;
  2493. case DMUS_FOURCC_DSBUFFER_LIST:
  2494. {
  2495. CBufferConfig *pSource = new CBufferConfig(0);
  2496. if (pSource)
  2497. {
  2498. pParser->EnterList(&ckChild);
  2499. while (pParser->NextChunk(&hr))
  2500. {
  2501. switch( ckChild.ckid )
  2502. {
  2503. case DMUS_FOURCC_DSBUFFATTR_ITEM:
  2504. hr = pParser->Read(&pSource->m_BufferHeader,
  2505. sizeof(DMUS_IO_BUFFER_ATTRIBUTES_HEADER));
  2506. pSource->DecideType();
  2507. break;
  2508. case FOURCC_LIST:
  2509. case FOURCC_RIFF:
  2510. if ( ckChild.fccType == DMUS_FOURCC_DSBC_FORM)
  2511. {
  2512. pParser->SeekBack();
  2513. hr = pSource->Load(pParser->GetStream());
  2514. pParser->SeekForward();
  2515. }
  2516. }
  2517. }
  2518. if (SUCCEEDED(hr))
  2519. {
  2520. m_BufferConfigList.AddTail(pSource);
  2521. }
  2522. else
  2523. {
  2524. delete pSource;
  2525. Trace(1,"Error: AudioPath Configuration failed loading buffer\n");
  2526. }
  2527. pParser->LeaveList();
  2528. }
  2529. else
  2530. {
  2531. hr = E_OUTOFMEMORY;
  2532. }
  2533. }
  2534. break;
  2535. case DMUS_FOURCC_DSBC_FORM:
  2536. {
  2537. CBufferConfig *pSource = new CBufferConfig(0);
  2538. if (pSource)
  2539. {
  2540. pParser->SeekBack();
  2541. hr = pSource->Load(pParser->GetStream());
  2542. pParser->SeekForward();
  2543. if (SUCCEEDED(hr))
  2544. {
  2545. m_BufferConfigList.AddTail(pSource);
  2546. }
  2547. else
  2548. {
  2549. Trace(1,"Error: AudioPath Configuration failed loading buffer\n");
  2550. delete pSource;
  2551. }
  2552. }
  2553. else
  2554. {
  2555. hr = E_OUTOFMEMORY;
  2556. }
  2557. }
  2558. break;
  2559. case DMUS_FOURCC_TOOLGRAPH_FORM:
  2560. {
  2561. CGraph *pGraph = new CGraph;
  2562. if (pGraph)
  2563. {
  2564. hr = pGraph->Load(pParser);
  2565. if(m_pGraph)
  2566. {
  2567. m_pGraph->Release();
  2568. }
  2569. m_pGraph = pGraph;
  2570. }
  2571. else
  2572. {
  2573. hr = E_OUTOFMEMORY;
  2574. }
  2575. }
  2576. break;
  2577. }
  2578. break;
  2579. }
  2580. }
  2581. pParser->LeaveList();
  2582. LeaveCriticalSection(&m_CriticalSection);
  2583. return hr;
  2584. }
  2585. HRESULT CAudioPathConfig::Save( IStream* pIStream, BOOL fClearDirty )
  2586. {
  2587. return E_NOTIMPL;
  2588. }
  2589. HRESULT CAudioPathConfig::GetSizeMax( ULARGE_INTEGER FAR* pcbSize )
  2590. {
  2591. return E_NOTIMPL;
  2592. }