Source code of Windows XP (NT5)
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.

482 lines
15 KiB

  1. // Echo.cpp : Implementation of CEchoTool
  2. //
  3. // Copyright (C) 1999 Microsoft Corporation. All Rights Reserved
  4. //
  5. #include "dmusicc.h"
  6. #include "dmusici.h"
  7. #include "debug.h"
  8. #include "echo.h"
  9. #include "toolhelp.h"
  10. CEchoTool::CEchoTool()
  11. {
  12. ParamInfo Params[DMUS_ECHO_PARAMCOUNT] =
  13. {
  14. { DMUS_ECHO_REPEAT, MPT_INT,MP_CAPS_ALL,0,100,2,
  15. L"Repeats",L"Repeat",NULL }, // Repeat - twice by default
  16. { DMUS_ECHO_DECAY, MPT_INT,MP_CAPS_ALL,0,100,12,
  17. L"dB",L"Decay",NULL }, // Decay - 12 db by default
  18. { DMUS_ECHO_TIMEUNIT, MPT_ENUM,MP_CAPS_ALL, DMUS_TIME_UNIT_MS,DMUS_TIME_UNIT_1,DMUS_TIME_UNIT_GRID,
  19. L"",L"Delay Units",L"Milliseconds,Music Clicks,Grid,Beat,Bar,64th note triplets,64th notes,32nd note triplets,32nd notes,16th note triplets,16th notes,8th note triplets,8th notes,Quarter note triplets,Quarter notes,Half note triplets,Half notes,Whole note triplets,Whole notes" },
  20. { DMUS_ECHO_DELAY, MPT_INT,MP_CAPS_ALL,1,1000,1,
  21. L"",L"Delay",NULL}, // Delay - 1 grid note by default
  22. { DMUS_ECHO_GROUPOFFSET, MPT_INT,MP_CAPS_ALL,0,100,0,
  23. L"Channel Groups",L"Channel Offset", NULL }, // Group offset - none by default
  24. { DMUS_ECHO_TYPE, MPT_ENUM,MP_CAPS_ALL, DMUS_ECHOT_FALLING,DMUS_ECHOT_RISING_CLIP,DMUS_ECHOT_FALLING_CLIP,
  25. L"",L"Type",L"Falling,Falling & Truncated,Rising,Rising & Truncated"} // Type - falling by default
  26. };
  27. InitParams(DMUS_ECHO_PARAMCOUNT,Params);
  28. m_fMusicTime = TRUE; // override default setting.
  29. }
  30. STDMETHODIMP_(ULONG) CEchoTool::AddRef()
  31. {
  32. return InterlockedIncrement(&m_cRef);
  33. }
  34. STDMETHODIMP_(ULONG) CEchoTool::Release()
  35. {
  36. if( 0 == InterlockedDecrement(&m_cRef) )
  37. {
  38. delete this;
  39. return 0;
  40. }
  41. return m_cRef;
  42. }
  43. STDMETHODIMP CEchoTool::QueryInterface(const IID &iid, void **ppv)
  44. {
  45. if (iid == IID_IUnknown || iid == IID_IDirectMusicTool || iid == IID_IDirectMusicTool8)
  46. {
  47. *ppv = static_cast<IDirectMusicTool8*>(this);
  48. }
  49. else if(iid == IID_IPersistStream)
  50. {
  51. *ppv = static_cast<IPersistStream*>(this);
  52. }
  53. else if(iid == IID_IDirectMusicEchoTool)
  54. {
  55. *ppv = static_cast<IDirectMusicEchoTool*>(this);
  56. }
  57. else if(iid == IID_IMediaParams)
  58. {
  59. *ppv = static_cast<IMediaParams*>(this);
  60. }
  61. else if(iid == IID_IMediaParamInfo)
  62. {
  63. *ppv = static_cast<IMediaParamInfo*>(this);
  64. }
  65. else if(iid == IID_ISpecifyPropertyPages)
  66. {
  67. *ppv = static_cast<ISpecifyPropertyPages*>(this);
  68. }
  69. else
  70. {
  71. *ppv = NULL;
  72. return E_NOINTERFACE;
  73. }
  74. AddRef();
  75. return S_OK;
  76. }
  77. //////////////////////////////////////////////////////////////////////
  78. // IPersistStream
  79. STDMETHODIMP CEchoTool::GetClassID(CLSID* pClassID)
  80. {
  81. if (pClassID)
  82. {
  83. *pClassID = CLSID_DirectMusicEchoTool;
  84. return S_OK;
  85. }
  86. return E_POINTER;
  87. }
  88. //////////////////////////////////////////////////////////////////////
  89. // IPersistStream Methods:
  90. STDMETHODIMP CEchoTool::IsDirty()
  91. {
  92. if (m_fDirty) return S_OK;
  93. else return S_FALSE;
  94. }
  95. STDMETHODIMP CEchoTool::Load(IStream* pStream)
  96. {
  97. EnterCriticalSection(&m_CrSec);
  98. DWORD dwChunkID;
  99. DWORD dwSize;
  100. HRESULT hr = pStream->Read(&dwChunkID, sizeof(dwChunkID), NULL);
  101. hr = pStream->Read(&dwSize, sizeof(dwSize), NULL);
  102. if(SUCCEEDED(hr) && (dwChunkID == FOURCC_ECHO_CHUNK))
  103. {
  104. DMUS_IO_ECHO_HEADER Header;
  105. memset(&Header,0,sizeof(Header));
  106. hr = pStream->Read(&Header, min(sizeof(Header),dwSize), NULL);
  107. if (SUCCEEDED(hr))
  108. {
  109. SetParam(DMUS_ECHO_REPEAT,(float) Header.dwRepeat);
  110. SetParam(DMUS_ECHO_DECAY,(float) Header.dwDecay);
  111. SetParam(DMUS_ECHO_TIMEUNIT,(float) Header.dwTimeUnit);
  112. SetParam(DMUS_ECHO_DELAY,(float) Header.dwDelay);
  113. SetParam(DMUS_ECHO_GROUPOFFSET,(float) Header.dwGroupOffset);
  114. SetParam(DMUS_ECHO_TYPE,(float) Header.dwType);
  115. }
  116. }
  117. m_fDirty = FALSE;
  118. LeaveCriticalSection(&m_CrSec);
  119. return hr;
  120. }
  121. STDMETHODIMP CEchoTool::Save(IStream* pStream, BOOL fClearDirty)
  122. {
  123. EnterCriticalSection(&m_CrSec);
  124. DWORD dwChunkID = FOURCC_ECHO_CHUNK;
  125. DWORD dwSize = sizeof(DMUS_IO_ECHO_HEADER);
  126. HRESULT hr = pStream->Write(&dwChunkID, sizeof(dwChunkID), NULL);
  127. if (SUCCEEDED(hr))
  128. {
  129. hr = pStream->Write(&dwSize, sizeof(dwSize), NULL);
  130. }
  131. if (SUCCEEDED(hr))
  132. {
  133. DMUS_IO_ECHO_HEADER Header;
  134. GetParamInt(DMUS_ECHO_REPEAT,MAX_REF_TIME,(long *) &Header.dwRepeat);
  135. GetParamInt(DMUS_ECHO_DECAY,MAX_REF_TIME,(long *) &Header.dwDecay);
  136. GetParamInt(DMUS_ECHO_TIMEUNIT,MAX_REF_TIME,(long *) &Header.dwTimeUnit);
  137. GetParamInt(DMUS_ECHO_DELAY,MAX_REF_TIME,(long *) &Header.dwDelay);
  138. GetParamInt(DMUS_ECHO_GROUPOFFSET,MAX_REF_TIME,(long *) &Header.dwGroupOffset);
  139. GetParamInt(DMUS_ECHO_TYPE,MAX_REF_TIME,(long *) &Header.dwType);
  140. hr = pStream->Write(&Header, sizeof(Header),NULL);
  141. }
  142. if (fClearDirty) m_fDirty = FALSE;
  143. LeaveCriticalSection(&m_CrSec);
  144. return hr;
  145. }
  146. STDMETHODIMP CEchoTool::GetSizeMax(ULARGE_INTEGER* pcbSize)
  147. {
  148. if (pcbSize == NULL)
  149. {
  150. return E_POINTER;
  151. }
  152. pcbSize->QuadPart = sizeof(DMUS_IO_ECHO_HEADER) + 8; // Data plus RIFF header.
  153. return S_OK;
  154. }
  155. STDMETHODIMP CEchoTool::GetPages(CAUUID * pPages)
  156. {
  157. pPages->cElems = 1;
  158. pPages->pElems = (GUID *) CoTaskMemAlloc(sizeof(GUID));
  159. if (pPages->pElems == NULL)
  160. return E_OUTOFMEMORY;
  161. *(pPages->pElems) = CLSID_EchoPage;
  162. return NOERROR;
  163. }
  164. static long glResTypes[DMUS_TIME_UNIT_COUNT] =
  165. { 1, // DMUS_TIME_UNIT_MS
  166. 1, // DMUS_TIME_UNIT_MTIME
  167. 384, // DMUS_TIME_UNIT_GRID
  168. 768, // DMUS_TIME_UNIT_BEAT
  169. 3072, // DMUS_TIME_UNIT_BAR
  170. 32, // DMUS_TIME_UNIT_64T
  171. 48, // DMUS_TIME_UNIT_64
  172. 64, // DMUS_TIME_UNIT_32T
  173. 96, // DMUS_TIME_UNIT_32
  174. 128, // DMUS_TIME_UNIT_16T
  175. 192, // DMUS_TIME_UNIT_16
  176. 256, // DMUS_TIME_UNIT_8T
  177. 384, // DMUS_TIME_UNIT_8
  178. 512, // DMUS_TIME_UNIT_4T
  179. 768, // DMUS_TIME_UNIT_4
  180. 1024, // DMUS_TIME_UNIT_2T
  181. 1536, // DMUS_TIME_UNIT_2
  182. 2048, // DMUS_TIME_UNIT_1T
  183. 3072 // DMUS_TIME_UNIT_1
  184. };
  185. /////////////////////////////////////////////////////////////////
  186. // IDirectMusicTool
  187. STDMETHODIMP CEchoTool::ProcessPMsg( IDirectMusicPerformance* pPerf,
  188. DMUS_PMSG* pPMsg )
  189. {
  190. // returning S_FREE frees the message. If StampPMsg()
  191. // fails, there is no destination for this message so
  192. // free it.
  193. if(NULL == pPMsg->pGraph )
  194. {
  195. return DMUS_S_FREE;
  196. }
  197. // We need to know the time format so we can call GetParamInt() to read control parameters.
  198. REFERENCE_TIME rtTime;
  199. if (m_fMusicTime) rtTime = pPMsg->mtTime;
  200. else rtTime = pPMsg->rtTime;
  201. // We need to know if there's an offset, because that determines which kinds of messages we process.
  202. long lGroupOffset;
  203. GetParamInt(DMUS_ECHO_GROUPOFFSET,rtTime,&lGroupOffset);
  204. lGroupOffset *= 16; // Convert to pchannels.
  205. if( pPMsg->dwType == DMUS_PMSGT_NOTE )
  206. {
  207. DMUS_NOTE_PMSG *pNote = (DMUS_NOTE_PMSG *) pPMsg;
  208. IDirectMusicPerformance8 *pPerf8; // We'll need the DX8 interface to access ClonePMsg.
  209. if (SUCCEEDED(pPerf->QueryInterface(IID_IDirectMusicPerformance8,(void **)&pPerf8)))
  210. {
  211. long lRepeats, lDecay, lTimeUnit, lDelay, lType;
  212. GetParamInt(DMUS_ECHO_REPEAT,rtTime,&lRepeats);
  213. GetParamInt(DMUS_ECHO_DELAY,rtTime,&lDelay);
  214. GetParamInt(DMUS_ECHO_TIMEUNIT,rtTime,&lTimeUnit);
  215. GetParamInt(DMUS_ECHO_DECAY,rtTime,&lDecay);
  216. GetParamInt(DMUS_ECHO_TYPE,rtTime,&lType);
  217. long lStartVolume;
  218. if (lTimeUnit > DMUS_TIME_UNIT_BAR)
  219. {
  220. lDelay *= glResTypes[lTimeUnit];
  221. }
  222. else if (lTimeUnit >= DMUS_TIME_UNIT_GRID)
  223. {
  224. DMUS_TIMESIGNATURE TimeSig;
  225. if (SUCCEEDED(pPerf8->GetParamEx(GUID_TimeSignature,pNote->dwVirtualTrackID,pNote->dwGroupID,DMUS_SEG_ANYTRACK,pNote->mtTime,NULL,&TimeSig)))
  226. {
  227. DWORD dwBeat = (4 * 768) / TimeSig.bBeat;
  228. if (lTimeUnit == DMUS_TIME_UNIT_BEAT)
  229. {
  230. lDelay *= dwBeat;
  231. }
  232. else if (lTimeUnit == DMUS_TIME_UNIT_GRID)
  233. {
  234. lDelay *= (dwBeat / TimeSig.wGridsPerBeat);
  235. }
  236. else
  237. {
  238. lDelay *= (dwBeat * TimeSig.bBeatsPerMeasure);
  239. }
  240. }
  241. }
  242. lDecay *= 100; // We'll do our math in 1/100ths of a dB.
  243. if (lType & DMUS_ECHOT_RISING)
  244. {
  245. lStartVolume = MidiToVolume(pNote->bVelocity) - (lRepeats * lDecay);
  246. lDecay = -lDecay;
  247. }
  248. else
  249. {
  250. lStartVolume = MidiToVolume(pNote->bVelocity);
  251. }
  252. long lCount;
  253. for (lCount = 0; lCount <= lRepeats; lCount++)
  254. {
  255. DMUS_NOTE_PMSG *pCopy = NULL;
  256. if (lCount > 0)
  257. {
  258. pNote->dwSize = sizeof (DMUS_NOTE_PMSG);
  259. pPerf8->ClonePMsg((DMUS_PMSG *) pNote,(DMUS_PMSG **)&pCopy);
  260. }
  261. else
  262. {
  263. pCopy = pNote;
  264. }
  265. if (pCopy)
  266. {
  267. pCopy->bVelocity = VolumeToMidi(lStartVolume - (lCount * lDecay));
  268. pCopy->dwPChannel += (lCount * lGroupOffset);
  269. if (lTimeUnit != DMUS_TIME_UNIT_MS)
  270. {
  271. pCopy->mtTime += (lCount * lDelay);
  272. pCopy->dwFlags &= ~DMUS_PMSGF_REFTIME;
  273. }
  274. else
  275. {
  276. pCopy->rtTime += (lCount * lDelay * 10000); // Convert from ms to rt.
  277. pCopy->dwFlags &= ~DMUS_PMSGF_MUSICTIME;
  278. }
  279. if (lType & DMUS_ECHOT_FALLING_CLIP)
  280. {
  281. if (pCopy->mtDuration <= lDecay)
  282. {
  283. pCopy->mtDuration = lDecay - 1;
  284. }
  285. }
  286. if (lCount) // Don't send the original note. We need it for clone and
  287. // it will be requeued on DMUS_S_REQUEUE anyway.
  288. {
  289. if (SUCCEEDED(pCopy->pGraph->StampPMsg((DMUS_PMSG *)pCopy)))
  290. {
  291. pPerf->SendPMsg((DMUS_PMSG *)pCopy);
  292. }
  293. else
  294. {
  295. pPerf->FreePMsg((DMUS_PMSG *)pCopy);
  296. }
  297. }
  298. }
  299. }
  300. pPerf8->Release();
  301. }
  302. }
  303. else if (lGroupOffset > 0)
  304. {
  305. IDirectMusicPerformance8 *pPerf8; // We'll need the DX8 interface to access ClonePMsg.
  306. if (SUCCEEDED(pPerf->QueryInterface(IID_IDirectMusicPerformance8,(void **)&pPerf8)))
  307. {
  308. // If the echoes are being sent to other pchannels, duplicate all other events
  309. // so they go down those pchannels too.
  310. long lRepeats, lTimeUnit, lDelay;
  311. GetParamInt(DMUS_ECHO_REPEAT,rtTime,&lRepeats);
  312. GetParamInt(DMUS_ECHO_DELAY,rtTime,&lDelay);
  313. GetParamInt(DMUS_ECHO_TIMEUNIT,rtTime,&lTimeUnit);
  314. if (lTimeUnit > DMUS_TIME_UNIT_MS)
  315. {
  316. lDelay *= glResTypes[lTimeUnit];
  317. }
  318. long lCount;
  319. for (lCount = 0; lCount <= lRepeats; lCount++)
  320. {
  321. DMUS_PMSG *pCopy = NULL;
  322. if (lCount > 0)
  323. {
  324. pPerf8->ClonePMsg(pPMsg,&pCopy);
  325. }
  326. else
  327. {
  328. pCopy = pPMsg;
  329. }
  330. if (pCopy)
  331. {
  332. pCopy->dwPChannel += (lCount * lGroupOffset);
  333. if (lTimeUnit != DMUS_TIME_UNIT_MS)
  334. {
  335. pCopy->mtTime += (lCount * lDelay);
  336. pCopy->dwFlags &= ~DMUS_PMSGF_REFTIME;
  337. }
  338. else
  339. {
  340. pCopy->rtTime += (lCount * lDelay * 10000); // Convert from ms to rt.
  341. pCopy->dwFlags &= ~DMUS_PMSGF_MUSICTIME;
  342. }
  343. if (lCount) // Don't send the original note. We need it for clone and
  344. // it will be requeued on DMUS_S_REQUEUE anyway.
  345. {
  346. if (SUCCEEDED(pCopy->pGraph->StampPMsg((DMUS_PMSG *)pCopy)))
  347. {
  348. pPerf->SendPMsg((DMUS_PMSG *)pCopy);
  349. }
  350. else
  351. {
  352. pPerf->FreePMsg((DMUS_PMSG *)pCopy);
  353. }
  354. }
  355. }
  356. }
  357. pPerf8->Release();
  358. }
  359. }
  360. if (FAILED(pPMsg->pGraph->StampPMsg(pPMsg)))
  361. {
  362. return DMUS_S_FREE;
  363. }
  364. return DMUS_S_REQUEUE;
  365. }
  366. STDMETHODIMP CEchoTool::Clone( IDirectMusicTool ** ppTool)
  367. {
  368. CEchoTool *pNew = new CEchoTool;
  369. if (pNew)
  370. {
  371. HRESULT hr = pNew->CopyParamsFromSource(this);
  372. if (SUCCEEDED(hr))
  373. {
  374. *ppTool = (IDirectMusicTool *) pNew;
  375. }
  376. else
  377. {
  378. delete pNew;
  379. }
  380. return hr;
  381. }
  382. else
  383. {
  384. return E_OUTOFMEMORY;
  385. }
  386. }
  387. STDMETHODIMP CEchoTool::SetRepeat(DWORD dwRepeat)
  388. {
  389. return SetParam(DMUS_ECHO_REPEAT,(float) dwRepeat);
  390. }
  391. STDMETHODIMP CEchoTool::SetDecay(DWORD dwDecay)
  392. {
  393. return SetParam(DMUS_ECHO_DECAY,(float) dwDecay);
  394. }
  395. STDMETHODIMP CEchoTool::SetTimeUnit(DWORD dwTimeUnit)
  396. {
  397. return SetParam(DMUS_ECHO_TIMEUNIT,(float) dwTimeUnit);
  398. }
  399. STDMETHODIMP CEchoTool::SetDelay(DWORD dwDelay)
  400. {
  401. return SetParam(DMUS_ECHO_DELAY,(float) dwDelay);
  402. }
  403. STDMETHODIMP CEchoTool::SetGroupOffset(DWORD dwGroupOffset)
  404. {
  405. return SetParam(DMUS_ECHO_GROUPOFFSET,(float) dwGroupOffset);
  406. }
  407. STDMETHODIMP CEchoTool::SetType(DWORD dwType)
  408. {
  409. return SetParam(DMUS_ECHO_TYPE,(float) dwType);
  410. }
  411. STDMETHODIMP CEchoTool::GetRepeat(DWORD * pdwRepeat)
  412. {
  413. return GetParamInt(DMUS_ECHO_REPEAT,MAX_REF_TIME,(long *) pdwRepeat);
  414. }
  415. STDMETHODIMP CEchoTool::GetDecay(DWORD * pdwDecay)
  416. {
  417. return GetParamInt(DMUS_ECHO_DECAY,MAX_REF_TIME,(long *) pdwDecay);
  418. }
  419. STDMETHODIMP CEchoTool::GetTimeUnit(DWORD * pdwTimeUnit)
  420. {
  421. return GetParamInt(DMUS_ECHO_TIMEUNIT,MAX_REF_TIME,(long *) pdwTimeUnit);
  422. }
  423. STDMETHODIMP CEchoTool::GetDelay(DWORD * pdwDelay)
  424. {
  425. return GetParamInt(DMUS_ECHO_DELAY,MAX_REF_TIME,(long *) pdwDelay);
  426. }
  427. STDMETHODIMP CEchoTool::GetGroupOffset(DWORD * pdwGroupOffset)
  428. {
  429. return GetParamInt(DMUS_ECHO_GROUPOFFSET,MAX_REF_TIME,(long *) pdwGroupOffset);
  430. }
  431. STDMETHODIMP CEchoTool::GetType(DWORD * pdwType)
  432. {
  433. return GetParamInt(DMUS_ECHO_TYPE,MAX_REF_TIME,(long *) pdwType);
  434. }