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.

963 lines
27 KiB

  1. #include <objbase.h>
  2. #include <windowsx.h>
  3. #include <mmsystem.h>
  4. #include <dsoundp.h>
  5. #include "debug.h"
  6. #include "dmusicc.h"
  7. #include "dmusici.h"
  8. #include "validate.h"
  9. #include "riff.h"
  10. #include "dswave.h"
  11. #include "waveutil.h"
  12. #include "riff.h"
  13. #include <regstr.h>
  14. #include <share.h>
  15. // CWaveViewPort(IStream *pStream); // Constructor receives stream.
  16. // ~CWaveViewPort(); // Destructor releases memory, streams, etc.
  17. //
  18. // STDMETHODIMP Init();
  19. // STDMETHODIMP GetFormat(LPWAVEFORMATEX pWaveFormatEx, LPDWORD pdwWaveFormatExSize);
  20. // STDMETHODIMP Seek(DWORD dwSample);
  21. // STDMETHODIMP Read(LPVOID *ppvBuffer, DWORD cpvBuffer, LPDWORD pcb);
  22. CWaveViewPort::CWaveViewPort() : m_dwDecompressedStart(0), m_dwDecompStartOffset(0), m_dwDecompStartOffsetPCM(0), m_dwDecompStartDelta(0)
  23. {
  24. V_INAME(CWaveViewPort::CWaveViewPort);
  25. InterlockedIncrement(&g_cComponent);
  26. InitializeCriticalSection(&m_CriticalSection);
  27. // Note: on pre-Blackcomb OS's, this call can raise an exception; if it
  28. // ever pops in stress, we can add an exception handler and retry loop.
  29. m_cRef = 1;
  30. // General stuff...
  31. m_pStream = NULL;
  32. m_cSamples = 0L;
  33. m_cbStream = 0L;
  34. m_dwStart = 0L;
  35. // Viewport info...
  36. m_pwfxTarget = NULL;
  37. ZeroMemory(&m_ash, sizeof(ACMSTREAMHEADER));
  38. m_hStream = NULL;
  39. m_pDst = NULL;
  40. m_pRaw = NULL;
  41. m_fdwOptions = 0L;
  42. m_dwPreCacheFilePos = 0;
  43. m_dwFirstPCMSample = 0;
  44. m_dwPCMSampleOut = 0;
  45. return;
  46. }
  47. CWaveViewPort::~CWaveViewPort()
  48. {
  49. V_INAME(CWaveViewPort::~CWaveViewPort);
  50. if (m_pStream) m_pStream->Release();
  51. if (m_hStream)
  52. {
  53. acmStreamUnprepareHeader(m_hStream, &m_ash, 0L);
  54. acmStreamClose(m_hStream, 0);
  55. }
  56. if (NULL != m_pwfxTarget)
  57. {
  58. GlobalFreePtr(m_pwfxTarget);
  59. }
  60. if (NULL != m_ash.pbDst)
  61. {
  62. GlobalFreePtr(m_ash.pbDst);
  63. }
  64. if (NULL != m_ash.pbSrc)
  65. {
  66. GlobalFreePtr(m_ash.pbSrc);
  67. }
  68. DeleteCriticalSection(&m_CriticalSection);
  69. InterlockedDecrement(&g_cComponent);
  70. return;
  71. }
  72. STDMETHODIMP CWaveViewPort::QueryInterface
  73. (
  74. const IID &iid,
  75. void **ppv
  76. )
  77. {
  78. V_INAME(CWaveViewPort::QueryInterface);
  79. if (iid == IID_IUnknown || iid == IID_IDirectSoundSource)
  80. {
  81. *ppv = static_cast<IDirectSoundSource*>(this);
  82. }
  83. else
  84. {
  85. *ppv = NULL;
  86. Trace(4,"Warning: Request to query unknown interface on Viewport\n");
  87. return E_NOINTERFACE;
  88. }
  89. reinterpret_cast<IUnknown*>(this)->AddRef();
  90. return S_OK;
  91. }
  92. STDMETHODIMP_(ULONG) CWaveViewPort::AddRef()
  93. {
  94. V_INAME(CWaveViewPort::AddRef);
  95. return InterlockedIncrement(&m_cRef);
  96. }
  97. STDMETHODIMP_(ULONG) CWaveViewPort::Release()
  98. {
  99. V_INAME(CWaveViewPort::Release);
  100. if (!InterlockedDecrement(&m_cRef))
  101. {
  102. delete this;
  103. return 0;
  104. }
  105. return m_cRef;
  106. }
  107. STDMETHODIMP CWaveViewPort::SetSink
  108. (
  109. IDirectSoundConnect *pSinkConnect
  110. )
  111. {
  112. V_INAME(CWaveViewPort::Init);
  113. return S_OK;
  114. }
  115. STDMETHODIMP CWaveViewPort::GetFormat
  116. (
  117. LPWAVEFORMATEX pwfx,
  118. DWORD dwSizeAllocated,
  119. LPDWORD pdwSizeWritten
  120. )
  121. {
  122. DWORD cbSize;
  123. V_INAME(CWaveViewPort::GetFormat);
  124. if (!pwfx && !pdwSizeWritten)
  125. {
  126. Trace(1, "ERROR: GetFormat (Viewport): Must request either the format or the required size");
  127. return E_INVALIDARG;
  128. }
  129. if (!m_pwfxTarget)
  130. {
  131. return DSERR_BADFORMAT;
  132. }
  133. // Note: Assuming that the wave object fills the cbSize field even
  134. // on PCM formats...
  135. if (WAVE_FORMAT_PCM == m_pwfxTarget->wFormatTag)
  136. {
  137. cbSize = sizeof(PCMWAVEFORMAT);
  138. }
  139. else
  140. {
  141. cbSize = sizeof(WAVEFORMATEX) + m_pwfxTarget->cbSize;
  142. }
  143. if (pdwSizeWritten)
  144. {
  145. V_PTR_WRITE(pdwSizeWritten, DWORD);
  146. *pdwSizeWritten = cbSize;
  147. }
  148. if (pwfx)
  149. {
  150. V_BUFPTR_WRITE(pwfx, dwSizeAllocated);
  151. if (dwSizeAllocated < cbSize)
  152. {
  153. return DSERR_INVALIDPARAM;
  154. }
  155. else
  156. {
  157. CopyMemory(pwfx, m_pwfxTarget, cbSize);
  158. // Set the cbSize field in destination if we have room
  159. if (WAVE_FORMAT_PCM == m_pwfxTarget->wFormatTag && dwSizeAllocated >= sizeof(WAVEFORMATEX))
  160. {
  161. pwfx->cbSize = 0;
  162. }
  163. }
  164. }
  165. return S_OK;
  166. }
  167. STDMETHODIMP CWaveViewPort::Seek
  168. (
  169. ULONGLONG ullPosition
  170. )
  171. {
  172. LARGE_INTEGER li;
  173. HRESULT hr;
  174. MMRESULT mmr;
  175. DWORD cbSize;
  176. V_INAME(CWaveViewPort::Seek);
  177. m_fdwOptions &= ~DSOUND_WVP_STREAMEND; // rsw clear this on seek: no longer at stream end
  178. if (m_fdwOptions & DSOUND_WVP_NOCONVERT)
  179. {
  180. if ((DWORD) ullPosition >= m_cbStream)
  181. {
  182. // Seek past end of stream
  183. //
  184. m_fdwOptions |= DSOUND_WVP_STREAMEND;
  185. m_dwOffset = m_cbStream;
  186. return S_OK;
  187. }
  188. m_dwOffset = (DWORD) ullPosition; // rsw initialize offset to Seek position
  189. if (0 != (ullPosition % m_pwfxTarget->nBlockAlign))
  190. {
  191. // Seek position not block aligned?
  192. Trace(1, "ERROR: Seek (wave): Seek position is not block-aligned.\n");
  193. return (DMUS_E_BADWAVE);
  194. }
  195. li.HighPart = 0;
  196. li.LowPart = ((DWORD)ullPosition) + m_dwStartPos;
  197. hr = m_pStream->Seek(li, STREAM_SEEK_SET, NULL);
  198. if (FAILED(hr))
  199. {
  200. Trace(1, "ERROR: Seek (Viewport): Seeking the vieport's stream failed.\n");
  201. return (DMUS_E_BADWAVE);
  202. }
  203. }
  204. else
  205. {
  206. // Estimating source stream position...
  207. //
  208. // Should we create lookup table?!
  209. cbSize = (DWORD)ullPosition;
  210. if (cbSize)
  211. {
  212. mmr = acmStreamSize(m_hStream, cbSize, &cbSize, ACM_STREAMSIZEF_DESTINATION);
  213. if (MMSYSERR_NOERROR != mmr)
  214. {
  215. Trace(1, "ERROR: Seek (viewport): Could not convert target stream size to source format.\n");
  216. return (DMUS_E_BADWAVE);
  217. }
  218. }
  219. if (cbSize >= m_cbStream)
  220. {
  221. // Seek past end of stream
  222. //
  223. m_fdwOptions |= DSOUND_WVP_STREAMEND;
  224. m_dwOffset = m_cbStream;
  225. return S_OK;
  226. }
  227. // If this is a seek to the precache end we know where to start reading from
  228. if((m_fdwOptions & DSOUND_WAVEF_ONESHOT) == 0)
  229. {
  230. // Go back to the block that was read for the end of the precached data
  231. if(cbSize != 0 && m_dwPCMSampleOut == ullPosition)
  232. {
  233. m_dwOffset = m_dwPreCacheFilePos;
  234. li.HighPart = 0;
  235. li.LowPart = m_dwOffset + m_dwStartPos;
  236. }
  237. else
  238. {
  239. m_dwOffset = cbSize; // rsw initialize offset to Seek position
  240. li.HighPart = 0;
  241. li.LowPart = cbSize + m_dwStartPos;
  242. }
  243. hr = m_pStream->Seek(li, STREAM_SEEK_SET, NULL);
  244. if (FAILED(hr))
  245. {
  246. Trace(1, "ERROR: Seek (viewport): Seeking the viewport's stream failed.\n");
  247. return (DMUS_E_BADWAVE);
  248. }
  249. // Since we're restarting, re-initialize.
  250. m_fdwOptions &= (~DSOUND_WVP_CONVERTMASK);
  251. m_fdwOptions |= DSOUND_WVP_CONVERTSTATE_01;
  252. m_ash.cbSrcLength = (DWORD)m_ash.dwSrcUser;
  253. m_ash.cbSrcLengthUsed = m_ash.cbSrcLength;
  254. m_ash.dwDstUser = 0L;
  255. m_ash.cbDstLengthUsed = 0L;
  256. }
  257. /////////////////////////////////////////////////////////////////////////////////////
  258. // If we're starting the wave, re-seek the stream (one-shots always need to re-seek).
  259. // NOTE: The following assumes that compressed waves always seek from the beginning,
  260. // since the value returned by acmStreamSize is pretty unreliable.
  261. /////////////////////////////////////////////////////////////////////////////////////
  262. else if ( cbSize == 0 || (m_fdwOptions & DSOUND_WAVEF_ONESHOT) )
  263. {
  264. m_dwOffset = cbSize; // rsw initialize offset to Seek position
  265. li.HighPart = 0;
  266. li.LowPart = cbSize + m_dwStartPos;
  267. hr = m_pStream->Seek(li, STREAM_SEEK_SET, NULL);
  268. if (FAILED(hr))
  269. {
  270. Trace(1, "ERROR: Seek (viewport): Seeking the viewport's stream failed.\n");
  271. return (DMUS_E_BADWAVE);
  272. }
  273. // Since we're restarting, re-initialize.
  274. m_fdwOptions &= (~DSOUND_WVP_CONVERTMASK);
  275. m_fdwOptions |= DSOUND_WVP_CONVERTSTATE_01;
  276. m_ash.cbSrcLength = (DWORD)m_ash.dwSrcUser;
  277. m_ash.cbSrcLengthUsed = m_ash.cbSrcLength;
  278. m_ash.dwDstUser = 0L;
  279. m_ash.cbDstLengthUsed = 0L;
  280. }
  281. }
  282. TraceI(5, "Seek (Viewport): Succeeded.\n");
  283. return S_OK;
  284. }
  285. static inline HRESULT MMRESULTToHRESULT(
  286. MMRESULT mmr)
  287. {
  288. switch (mmr)
  289. {
  290. case MMSYSERR_NOERROR:
  291. return S_OK;
  292. case MMSYSERR_ALLOCATED:
  293. return DSERR_ALLOCATED;
  294. case MMSYSERR_NOMEM:
  295. return E_OUTOFMEMORY;
  296. }
  297. return E_FAIL;
  298. }
  299. HRESULT CWaveViewPort::acmRead
  300. (
  301. void
  302. )
  303. {
  304. DWORD cbSize;
  305. DWORD dwOffset;
  306. DWORD fdwConvert = 0;
  307. MMRESULT mmr;
  308. HRESULT hr;
  309. V_INAME(CWaveViewPort::acmRead);
  310. for (m_ash.cbDstLengthUsed = 0; 0 == m_ash.cbDstLengthUsed; )
  311. {
  312. // Did we use up the entire buffer?
  313. if (m_ash.cbSrcLengthUsed == m_ash.cbSrcLength)
  314. {
  315. // Yep!
  316. dwOffset = 0L;
  317. cbSize = (DWORD)m_ash.dwSrcUser;
  318. }
  319. else
  320. {
  321. // Nope!
  322. dwOffset = m_ash.cbSrcLength - m_ash.cbSrcLengthUsed;
  323. cbSize = (DWORD)m_ash.dwSrcUser - dwOffset;
  324. // Moving the remaining data from the end of buffer to the beginning
  325. MoveMemory(
  326. m_ash.pbSrc, // Base address
  327. &(m_ash.pbSrc[m_ash.cbSrcLengthUsed]), // Address of unused bytes
  328. dwOffset); // Number of unused bytes
  329. }
  330. // Are we at the end of the stream?
  331. cbSize = min(cbSize, m_cbStream - m_dwOffset);
  332. if (0 == cbSize)
  333. {
  334. if (dwOffset)
  335. {
  336. m_ash.cbSrcLength = dwOffset;
  337. }
  338. }
  339. else
  340. {
  341. hr = m_pStream->Read(&(m_ash.pbSrc[dwOffset]), cbSize, &cbSize);
  342. if (FAILED(hr))
  343. {
  344. Trace(1, "ERROR: Read (Viewport): Attempt to read source stream returned 0x%08lx\n", hr);
  345. //>>>>>>>>>>>>>>>>>>>>
  346. m_fdwOptions &= (~DSOUND_WVP_CONVERTMASK);
  347. m_fdwOptions |= DSOUND_WVP_STREAMEND;
  348. return(DMUS_E_CANNOTREAD);
  349. //>>>>>>>>>>>>>>>>>>>>
  350. }
  351. m_dwOffset += cbSize;
  352. m_ash.cbSrcLength = cbSize + dwOffset;
  353. }
  354. switch (m_fdwOptions & DSOUND_WVP_CONVERTMASK)
  355. {
  356. case DSOUND_WVP_CONVERTSTATE_01:
  357. fdwConvert = ACM_STREAMCONVERTF_BLOCKALIGN;
  358. break;
  359. case DSOUND_WVP_CONVERTSTATE_02:
  360. fdwConvert = ACM_STREAMCONVERTF_BLOCKALIGN | ACM_STREAMCONVERTF_END;
  361. break;
  362. case DSOUND_WVP_CONVERTSTATE_03:
  363. fdwConvert = ACM_STREAMCONVERTF_END;
  364. break;
  365. default:
  366. TraceI(3, "CWaveViewPort::acmRead: Default case?!\n");
  367. break;
  368. }
  369. mmr = acmStreamConvert(m_hStream, &m_ash, fdwConvert);
  370. if (MMSYSERR_NOERROR != mmr)
  371. {
  372. Trace(1, "ERROR: Read (Viewport): Attempt to convert wave to PCM failed.\n");
  373. return (MMRESULTToHRESULT(mmr));
  374. }
  375. if (0 != m_ash.cbDstLengthUsed)
  376. {
  377. m_ash.dwDstUser = 0L;
  378. return (S_OK);
  379. }
  380. // No data returned?
  381. switch (m_fdwOptions & DSOUND_WVP_CONVERTMASK)
  382. {
  383. case DSOUND_WVP_CONVERTSTATE_01:
  384. if (0 == cbSize)
  385. {
  386. // We're at the end of the stream..
  387. m_fdwOptions &= (~DSOUND_WVP_CONVERTMASK);
  388. m_fdwOptions |= DSOUND_WVP_CONVERTSTATE_02;
  389. TraceI(5, "CWaveViewPort::acmRead: Moving to stage 2\n");
  390. }
  391. // Otherwise, continue converting data as normal.
  392. break;
  393. case DSOUND_WVP_CONVERTSTATE_02:
  394. // We have hit the last partial block!
  395. m_fdwOptions &= (~DSOUND_WVP_CONVERTMASK);
  396. m_fdwOptions |= DSOUND_WVP_CONVERTSTATE_03;
  397. TraceI(5, "CWaveViewPort::acmRead: Moving to stage 3\n");
  398. break;
  399. case DSOUND_WVP_CONVERTSTATE_03:
  400. // No more data after end flag, NO MORE DATA!!
  401. m_fdwOptions &= (~DSOUND_WVP_CONVERTMASK);
  402. m_fdwOptions |= DSOUND_WVP_STREAMEND;
  403. Trace(2, "WARNING: Read (Viewport): End of source stream.\n");
  404. return (DMUS_E_BADWAVE);
  405. default:
  406. TraceI(3, "CWaveViewPort::acmRead: Default case?!\n");
  407. break;
  408. }
  409. }
  410. TraceI(3, "CWaveViewPort::acmRead: We should never get here!\n");
  411. return (S_OK);
  412. }
  413. //////////////////////////////////////////////////////////////////////////////
  414. //
  415. // ppvBuffer[] contains cpvBuffer pointers-to-samples, each to be filled with
  416. // *pcb bytes of data. On output *pcb will contain the number of bytes (per
  417. // buffer) actually read.
  418. //
  419. // pdwBusIds and pdwFuncIds are used to specify the bus and functionality
  420. // of each buffer, but these are ignored by the wave object.
  421. //
  422. STDMETHODIMP CWaveViewPort::Read
  423. (
  424. LPVOID *ppvBuffer,
  425. LPDWORD pdwBusIds,
  426. LPDWORD pdwFuncIds,
  427. LPLONG plPitchShifts,
  428. DWORD cpvBuffer,
  429. ULONGLONG *pcb
  430. )
  431. {
  432. HRESULT hr = S_OK;
  433. DWORD cbRead;
  434. DWORD dwOffset;
  435. DWORD cbSize;
  436. V_INAME(CWaveViewPort::Read);
  437. V_BUFPTR_READ(ppvBuffer, (cpvBuffer * sizeof(LPVOID)));
  438. V_BUFPTR_READ_OPT(pdwBusIds, (cpvBuffer * sizeof(LPDWORD)));
  439. V_BUFPTR_READ_OPT(pdwFuncIds, (cpvBuffer * sizeof(LPDWORD)));
  440. for (cbRead = cpvBuffer, cbSize = (DWORD)*pcb; cbRead; cbRead--)
  441. {
  442. V_BUFPTR_WRITE(ppvBuffer[cbRead - 1], cbSize);
  443. }
  444. if (m_fdwOptions & DSOUND_WVP_STREAMEND)
  445. {
  446. *pcb = 0;
  447. Trace(2, "WARNING: Read (Viewport): Attempt to read at end of stream.\n");
  448. return (S_FALSE);
  449. }
  450. LPVOID *ppvWriteBuffers = ppvBuffer;
  451. DWORD dwWriteBufferCount = cpvBuffer;
  452. if (m_fdwOptions & DSOUND_WVP_NOCONVERT)
  453. {
  454. // Total number of bytes to read... size of each buffer * number of buffers
  455. cbRead = ((DWORD)*pcb) * dwWriteBufferCount;
  456. dwOffset = 0;
  457. TraceI(5, "CWaveViewPort::Read - No conversion [%d bytes]\n", cbRead);
  458. do
  459. {
  460. // Calculate read size... It's going to be the size of:
  461. // 1. Remaining bytes to read.
  462. // 2. Size of the buffer.
  463. // 3. Remaining bytes in the stream.
  464. // Whichever happens to be the smallest.
  465. cbSize = min(cbRead, m_ash.cbDstLength);
  466. cbSize = min(cbSize, m_cbStream - m_dwOffset);
  467. TraceI(5, "CWaveViewPort::Read - Trying to read %d bytes\n", cbSize);
  468. DWORD _cbSize = cbSize; cbSize = 0; // Read may not set cbSize to zero
  469. hr = m_pStream->Read(m_ash.pbDst, _cbSize, &cbSize);
  470. TraceI(5, "CWaveViewPort::Read - Read %d bytes\n", cbSize);
  471. if (FAILED(hr))
  472. {
  473. Trace(2, "WARNING: Read (Viewport): Attempt to read returned 0x%08lx.\n", hr);
  474. break;
  475. }
  476. dwOffset = DeinterleaveBuffers(
  477. m_pwfxTarget,
  478. m_ash.pbDst,
  479. (LPBYTE *)ppvWriteBuffers,
  480. dwWriteBufferCount,
  481. cbSize,
  482. dwOffset);
  483. cbRead -= cbSize;
  484. m_dwOffset += cbSize;
  485. if (m_dwOffset >= m_cbStream)
  486. {
  487. m_fdwOptions |= DSOUND_WVP_STREAMEND;
  488. break;
  489. }
  490. }
  491. while (0 != cbRead);
  492. if (SUCCEEDED(hr))
  493. {
  494. *pcb = dwOffset;
  495. }
  496. }
  497. else
  498. {
  499. // If this is the read for the precache then we should remember the fileposition,
  500. // start sample for the decompressed block and the last sample passed back so we
  501. // can accurately pick up from there when refilling buffers
  502. // We use the LPLONG plPitchShifts in the read method as a boolean
  503. // this is a HACK!! We need to change this...
  504. // *plPitchShifts == 2 is to remember the precache offset
  505. // *plPitchShifts == 1 is to read from there
  506. bool fRememberPreCache = false;
  507. if(plPitchShifts != NULL && *plPitchShifts == 2 && (m_fdwOptions & DSOUND_WAVEF_ONESHOT) == 0)
  508. {
  509. fRememberPreCache = true;
  510. }
  511. bool bRemoveSilence = false;
  512. cbRead = ((DWORD)*pcb) * dwWriteBufferCount;
  513. dwOffset = 0;
  514. TraceI(5, "CWaveViewPort::Read - Conversion needed\n");
  515. do
  516. {
  517. if(m_dwDecompressedStart > 0 && m_dwOffset <= m_dwDecompStartOffset)
  518. {
  519. bRemoveSilence = true;
  520. }
  521. // Is there any remnant data in destination buffer?
  522. if (m_ash.dwDstUser >= m_ash.cbDstLengthUsed)
  523. {
  524. if(fRememberPreCache)
  525. {
  526. // Go back on block
  527. m_dwPreCacheFilePos = m_dwOffset - m_ash.cbSrcLength;
  528. m_dwFirstPCMSample = dwOffset * dwWriteBufferCount;
  529. }
  530. if(plPitchShifts != NULL && *plPitchShifts == 1)
  531. {
  532. // Seek to the right place first
  533. Seek(m_dwPCMSampleOut);
  534. // Read one block since we're starting one block behind
  535. hr = acmRead();
  536. if(FAILED(hr))
  537. {
  538. break;
  539. }
  540. }
  541. hr = acmRead();
  542. }
  543. if (FAILED(hr))
  544. {
  545. // acmRead spews when it fails; no need to do it again here
  546. break;
  547. }
  548. DWORD dwDstOffset = (ULONG)m_ash.dwDstUser;
  549. if(bRemoveSilence)
  550. {
  551. // We have partial data to throw away
  552. if(m_dwDecompStartOffset <= m_dwOffset)
  553. {
  554. if(dwDstOffset > 0)
  555. {
  556. dwDstOffset += m_dwDecompStartDelta;
  557. }
  558. else
  559. {
  560. // This is the first decompressed block so we go straight to the value we know
  561. dwDstOffset += m_dwDecompStartOffsetPCM;
  562. }
  563. m_ash.dwDstUser = dwDstOffset;
  564. bRemoveSilence = false;
  565. }
  566. else
  567. {
  568. // This is all throw away data
  569. bRemoveSilence = false;
  570. cbSize = min(cbRead, m_ash.cbDstLengthUsed - dwDstOffset);
  571. m_ash.dwDstUser += cbSize;
  572. continue;
  573. }
  574. }
  575. // We use the LPLONG plPitchShifts in the read method as a boolean
  576. // this is a HACK!! We need to change this...
  577. if(plPitchShifts && *plPitchShifts == 1)
  578. {
  579. dwDstOffset = m_dwPCMSampleOut - m_dwFirstPCMSample;
  580. m_ash.dwDstUser = dwDstOffset;
  581. plPitchShifts = 0;
  582. }
  583. cbSize = min(cbRead, m_ash.cbDstLengthUsed - dwDstOffset);
  584. dwOffset = DeinterleaveBuffers(
  585. m_pwfxTarget,
  586. &(m_ash.pbDst[dwDstOffset]),
  587. (LPBYTE *)ppvWriteBuffers,
  588. dwWriteBufferCount,
  589. cbSize,
  590. dwOffset);
  591. cbRead -= cbSize;
  592. m_ash.dwDstUser += cbSize;
  593. if ((m_fdwOptions & DSOUND_WVP_STREAMEND) &&
  594. (m_ash.dwDstUser >= m_ash.cbDstLengthUsed))
  595. {
  596. break;
  597. }
  598. }
  599. while(0 != cbRead);
  600. if(fRememberPreCache)
  601. {
  602. m_dwPCMSampleOut = dwOffset * dwWriteBufferCount;
  603. }
  604. if (SUCCEEDED(hr))
  605. {
  606. *pcb = dwOffset;
  607. }
  608. }
  609. TraceI(5, "CWaveViewPort::Read returning %x (%d bytes)\n", hr, dwOffset);
  610. return hr;
  611. }
  612. STDMETHODIMP CWaveViewPort::GetSize
  613. (
  614. ULONGLONG *pcb
  615. )
  616. {
  617. V_INAME(CWaveViewPort::GetSize);
  618. V_PTR_WRITE(pcb, ULONGLONG);
  619. TraceI(5, "CWaveViewPort::GetSize [%d samples]\n", m_cSamples);
  620. HRESULT hr = S_OK;
  621. if (m_fdwOptions & DSOUND_WVP_NOCONVERT)
  622. {
  623. // No conversion. This is trivial
  624. *pcb = (SAMPLE_TIME)(m_cbStream);
  625. }
  626. else if (!m_pwfxTarget)
  627. {
  628. hr = DSERR_UNINITIALIZED;
  629. }
  630. else
  631. {
  632. // Conversion required; let's hope target format is PCM
  633. if (WAVE_FORMAT_PCM == m_pwfxTarget->wFormatTag)
  634. {
  635. // Cool. This is simply the number of samples X the block align
  636. *pcb = (SAMPLE_TIME)((m_cSamples - m_dwDecompressedStart) * m_pwfxTarget->nBlockAlign);
  637. }
  638. else
  639. {
  640. Trace(1, "ERROR: GetSize (Viewport): Conversion required and target is not PCM.\n");
  641. hr = DSERR_BADFORMAT;
  642. }
  643. }
  644. return (hr);
  645. }
  646. HRESULT CWaveViewPort::Create
  647. (
  648. PCREATEVIEWPORT pCreate
  649. )
  650. {
  651. DWORD cbSize;
  652. MMRESULT mmr;
  653. HRESULT hr;
  654. LARGE_INTEGER li;
  655. LPWAVEFORMATEX pwfxSrc = pCreate->pwfxSource;
  656. LPWAVEFORMATEX pwfxDst = pCreate->pwfxTarget;
  657. V_INAME(CWaveViewPort::Create);
  658. TraceI(5, "CWaveViewPort::Create [%d samples]\n", pCreate->cSamples);
  659. EnterCriticalSection(&m_CriticalSection);
  660. // Clone source stream...
  661. hr = pCreate->pStream->Clone(&m_pStream);
  662. if (FAILED(hr))
  663. {
  664. LeaveCriticalSection(&m_CriticalSection);
  665. return (hr);
  666. }
  667. // Misc assignments
  668. m_cSamples = pCreate->cSamples;
  669. m_cbStream = pCreate->cbStream;
  670. m_dwOffset = 0L;
  671. m_fdwOptions = pCreate->fdwOptions;
  672. m_dwDecompressedStart = pCreate->dwDecompressedStart;
  673. m_dwDecompStartOffset = 0L;
  674. m_dwDecompStartOffsetPCM = 0L;
  675. m_dwDecompStartDelta = 0L;
  676. TraceI(5, "CWaveViewPort:: %d samples\n", m_cSamples);
  677. // Allocate destination format
  678. cbSize = SIZEOFFORMATEX(pwfxDst);
  679. m_pwfxTarget = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, cbSize);
  680. if (NULL == m_pwfxTarget)
  681. {
  682. LeaveCriticalSection(&m_CriticalSection);
  683. TraceI(1, "OUT OF MEMORY: CWaveViewPort::Create - size: %d \n", cbSize);
  684. return (E_OUTOFMEMORY);
  685. }
  686. // We don't own the buffer for pwfxDst, so we can't touch its cbSize.
  687. // We have to set the size manually on PCM, we KNOW the buffer is
  688. // large enough.
  689. CopyFormat(m_pwfxTarget, pwfxDst);
  690. if (WAVE_FORMAT_PCM == m_pwfxTarget->wFormatTag)
  691. {
  692. m_pwfxTarget->cbSize = 0;
  693. }
  694. // Calculating (block-aligned) size of destination buffer...
  695. cbSize = (pwfxDst->nAvgBytesPerSec * CONVERTLENGTH) / 1000;
  696. cbSize = BLOCKALIGN(cbSize, pwfxDst->nBlockAlign);
  697. m_ash.pbDst = (LPBYTE)GlobalAllocPtr(GHND, cbSize);
  698. if (NULL == m_ash.pbDst)
  699. {
  700. LeaveCriticalSection(&m_CriticalSection);
  701. TraceI(1, "OUT OF MEMORY: CWaveViewPort::Create 01\n");
  702. return (E_OUTOFMEMORY);
  703. }
  704. m_ash.cbDstLength = cbSize;
  705. // Getting stream starting offset...
  706. li.HighPart = 0;
  707. li.LowPart = 0;
  708. hr = m_pStream->Seek(li, STREAM_SEEK_CUR, (ULARGE_INTEGER *)(&li));
  709. m_dwStartPos = li.LowPart;
  710. // Do we need to use the ACM?
  711. if (FormatCmp(pwfxSrc, pwfxDst))
  712. {
  713. // Formats compare!! All we need to do is to copy the data straight
  714. // from the source stream. Way Cool!!
  715. TraceI(5, "Source and Destination formats are similar!\n");
  716. m_fdwOptions |= DSOUND_WVP_NOCONVERT;
  717. }
  718. else
  719. {
  720. // Source and destination formats are different...
  721. TraceI(5, "CWaveViewPort:Create: Formats are different... Use ACM!\n");
  722. m_fdwOptions |= DSOUND_WVP_CONVERTSTATE_01;
  723. mmr = acmStreamOpen(&m_hStream, NULL, pwfxSrc, pwfxDst, NULL, 0, 0, 0);
  724. if (MMSYSERR_NOERROR != mmr)
  725. {
  726. Trace(1, "ERROR: Create (Viewport): Attempt to open a conversion stream failed.\n");
  727. LeaveCriticalSection(&m_CriticalSection);
  728. return MMRESULTToHRESULT(mmr);
  729. }
  730. mmr = acmStreamSize(m_hStream, cbSize, &cbSize, ACM_STREAMSIZEF_DESTINATION);
  731. if (MMSYSERR_NOERROR != mmr)
  732. {
  733. Trace(1, "ERROR: Create(Viewport): Could not convert target stream size to source format.\n");
  734. LeaveCriticalSection(&m_CriticalSection);
  735. return MMRESULTToHRESULT(mmr);
  736. }
  737. m_ash.cbSrcLength = cbSize;
  738. m_ash.pbSrc = (LPBYTE)GlobalAllocPtr(GHND, cbSize);
  739. if (NULL == m_ash.pbSrc)
  740. {
  741. TraceI(1, "OUT OF MEMORY: CWaveViewPort:Create: GlobalAlloc failed.\n");
  742. LeaveCriticalSection(&m_CriticalSection);
  743. return E_OUTOFMEMORY;
  744. }
  745. // Also get the position for the actual start for the decompressed data
  746. if(m_dwDecompressedStart > 0)
  747. {
  748. m_dwDecompStartOffsetPCM = m_dwDecompressedStart * (pwfxDst->wBitsPerSample / 8) * pwfxDst->nChannels;
  749. mmr = acmStreamSize(m_hStream, m_dwDecompStartOffsetPCM, &m_dwDecompStartOffset, ACM_STREAMSIZEF_DESTINATION);
  750. DWORD dwDelta = 0;
  751. mmr = acmStreamSize(m_hStream, m_dwDecompStartOffset, &dwDelta, ACM_STREAMSIZEF_SOURCE);
  752. m_dwDecompStartDelta = m_dwDecompStartOffsetPCM - dwDelta;
  753. m_dwDecompStartOffset += m_dwStartPos;
  754. }
  755. // For the source buffer, it is the full buffer size.
  756. m_ash.dwSrcUser = m_ash.cbSrcLength;
  757. m_ash.cbSrcLengthUsed = m_ash.cbSrcLength;
  758. // For the destination buffer, it is the offset into the buffer
  759. // where the data can be found.
  760. m_ash.dwDstUser = 0L;
  761. m_ash.cbDstLengthUsed = 0L;
  762. m_ash.cbStruct = sizeof(ACMSTREAMHEADER);
  763. mmr= acmStreamPrepareHeader(m_hStream, &m_ash, 0L);
  764. if (MMSYSERR_NOERROR != mmr)
  765. {
  766. Trace(1, "ERROR: Create (Viewport): Attempt to prepare header for conversion stream failed.\n");
  767. LeaveCriticalSection(&m_CriticalSection);
  768. return MMRESULTToHRESULT(mmr);
  769. }
  770. }
  771. LeaveCriticalSection(&m_CriticalSection);
  772. return S_OK;
  773. }