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.

400 lines
9.6 KiB

  1. /*************************************************
  2. * wave.cpp *
  3. * *
  4. * Copyright (C) 1995-1999 Microsoft Inc. *
  5. * *
  6. *************************************************/
  7. // wave.cpp : implementation file
  8. //
  9. #include "stdafx.h"
  10. #include "wave.h"
  11. #include "mem.h"
  12. #ifdef _DEBUG
  13. #undef THIS_FILE
  14. static char BASED_CODE THIS_FILE[] = __FILE__;
  15. #endif
  16. /////////////////////////////////////////////////////////////////////////////
  17. // CWave
  18. IMPLEMENT_SERIAL(CWave, CObject, 0 /* Schema number*/ )
  19. // Create a simple waveform so there's something there.
  20. CWave::CWave()
  21. {
  22. m_pSamples = NULL;
  23. m_pOutDev = NULL;
  24. m_bBusy = FALSE;
  25. Create(16);
  26. }
  27. CWave::~CWave()
  28. {
  29. if (m_bBusy) {
  30. Stop();
  31. }
  32. ASSERT(!m_bBusy);
  33. if (m_pSamples) FREE(m_pSamples);
  34. }
  35. /////////////////////////////////////////////////////////////////////////////
  36. // CWave serialization
  37. void CWave::Serialize(CArchive& ar)
  38. {
  39. ar.Flush();
  40. CFile* fp = ar.GetFile();
  41. if (ar.IsStoring()) {
  42. ASSERT(0); // Save(fp);
  43. } else {
  44. Load(fp);
  45. }
  46. }
  47. ///////////////////////////////////////////////////////////////////////////
  48. // CWave notification functions
  49. void CWave::OnWaveOutDone()
  50. {
  51. }
  52. void CWave::OnWaveInData()
  53. {
  54. }
  55. /////////////////////////////////////////////////////////////////////////////
  56. // CWave commands
  57. BOOL CWave::Create(int nsamples, int samprate, int sampsize)
  58. {
  59. // validate the args
  60. if ((samprate != 11025)
  61. && (samprate != 22050)
  62. && (samprate != 44100)) {
  63. TRACE("Invalid sample rate: %d", samprate);
  64. return FALSE;
  65. }
  66. if ((sampsize != 8) && (sampsize != 16)) {
  67. TRACE("Invalid sample size: %d", sampsize);
  68. return FALSE;
  69. }
  70. // Allocate memory for the samples.
  71. int iBytes = nsamples * sampsize / 8;
  72. void* pSamples = ALLOC(iBytes);
  73. if (!pSamples) {
  74. TRACE("Out of memory for samples");
  75. return FALSE;
  76. }
  77. // Free existing memory and replace it.
  78. if (m_pSamples) FREE(m_pSamples);
  79. m_pSamples = pSamples;
  80. m_iSize = iBytes;
  81. // Fill out the format info.
  82. m_pcmfmt.wf.wFormatTag = WAVE_FORMAT_PCM;
  83. m_pcmfmt.wf.nChannels = 1; // We only do mono.
  84. m_pcmfmt.wf.nSamplesPerSec = samprate;
  85. m_pcmfmt.wf.nAvgBytesPerSec = samprate;
  86. m_pcmfmt.wf.nBlockAlign = (unsigned short) (sampsize / 8); // Number of bytes
  87. m_pcmfmt.wBitsPerSample = (unsigned short) sampsize;
  88. // Set the buffer to silence.
  89. for (int i=0; i<nsamples; i++) {
  90. SetSample(i, 0);
  91. }
  92. return TRUE;
  93. }
  94. BOOL CWave::Play(CWaveOutDevice* pWaveDevice)
  95. {
  96. if (pWaveDevice != NULL) {
  97. m_pOutDev = pWaveDevice;
  98. return pWaveDevice->Play(this);
  99. } else {
  100. m_pOutDev = &theDefaultWaveOutDevice;
  101. return theDefaultWaveOutDevice.Play(this);
  102. }
  103. }
  104. void CWave::Stop()
  105. {
  106. if (!m_bBusy) return;
  107. }
  108. BOOL CWave::Load(char* pszFileName)
  109. {
  110. CString strFile;
  111. if ((pszFileName == NULL)
  112. || (strlen(pszFileName) == 0)) {
  113. // Show an open file dialog to get the name.
  114. CFileDialog dlg (TRUE, // Open
  115. NULL, // No default extension
  116. NULL, // No initial file name
  117. OFN_FILEMUSTEXIST
  118. | OFN_HIDEREADONLY,
  119. "Wave files (*.WAV)|*.WAV|All files (*.*)|*.*||");
  120. if (dlg.DoModal() == IDOK) {
  121. strFile = dlg.GetPathName();
  122. } else {
  123. return FALSE;
  124. }
  125. } else {
  126. // Copy the supplied file path.
  127. strFile = pszFileName;
  128. }
  129. // Try to open the file for read access.
  130. CFile file;
  131. if (! file.Open(strFile,
  132. CFile::modeRead | CFile::shareDenyWrite)) {
  133. AfxMessageBox("Failed to open file");
  134. return FALSE;
  135. }
  136. BOOL bResult = Load(&file);
  137. file.Close();
  138. if (!bResult) AfxMessageBox("Failed to load file");
  139. return bResult;
  140. }
  141. BOOL CWave::Load(CFile *fp)
  142. {
  143. return Load(fp->m_hFile);
  144. }
  145. BOOL CWave::Load(UINT_PTR hFile)
  146. {
  147. HMMIO hmmio;
  148. MMIOINFO info;
  149. memset(&info, 0, sizeof(info));
  150. info.adwInfo[0] = (DWORD)hFile;
  151. hmmio = mmioOpen(NULL,
  152. &info,
  153. MMIO_READ | MMIO_ALLOCBUF);
  154. if (!hmmio) {
  155. TRACE("mmioOpen failed");
  156. return FALSE;
  157. }
  158. BOOL bResult = Load(hmmio);
  159. mmioClose(hmmio, MMIO_FHOPEN);
  160. return bResult;
  161. }
  162. BOOL CWave::Load(HMMIO hmmio)
  163. {
  164. // Check whether it's a RIFF WAVE file.
  165. MMCKINFO ckFile;
  166. ckFile.fccType = mmioFOURCC('W','A','V','E');
  167. if (mmioDescend(hmmio,
  168. &ckFile,
  169. NULL,
  170. MMIO_FINDRIFF) != 0) {
  171. TRACE("Not a RIFF or WAVE file");
  172. return FALSE;
  173. }
  174. // Find the 'fmt ' chunk.
  175. MMCKINFO ckChunk;
  176. ckChunk.ckid = mmioFOURCC('f','m','t',' ');
  177. if (mmioDescend(hmmio,
  178. &ckChunk,
  179. &ckFile,
  180. MMIO_FINDCHUNK) != 0) {
  181. TRACE("No fmt chunk in file");
  182. return FALSE;
  183. }
  184. // Allocate some memory for the fmt chunk.
  185. int iSize = ckChunk.cksize;
  186. WAVEFORMATEX* pfmt = (WAVEFORMATEX*) ALLOC(iSize);
  187. ASSERT(pfmt);
  188. if ( pfmt == NULL )
  189. {
  190. return FALSE;
  191. }
  192. // Read the fmt chunk.
  193. if (mmioRead(hmmio,
  194. (char*)pfmt,
  195. iSize) != iSize) {
  196. TRACE("Failed to read fmt chunk");
  197. FREE(pfmt);
  198. return FALSE;
  199. }
  200. // Check whether it's in PCM format.
  201. if (pfmt->wFormatTag != WAVE_FORMAT_PCM) {
  202. TRACE("Not a PCM file");
  203. FREE(pfmt);
  204. return FALSE;
  205. }
  206. // Get out of the fmt chunk.
  207. mmioAscend(hmmio, &ckChunk, 0);
  208. // Find the 'data' chunk.
  209. ckChunk.ckid = mmioFOURCC('d','a','t','a');
  210. if (mmioDescend(hmmio,
  211. &ckChunk,
  212. &ckFile,
  213. MMIO_FINDCHUNK) != 0) {
  214. TRACE("No data chunk in file");
  215. FREE(pfmt);
  216. return FALSE;
  217. }
  218. // Allocate some memory for the data chunk.
  219. iSize = ckChunk.cksize;
  220. void* pdata = ALLOC(iSize);
  221. if (!pdata) {
  222. TRACE("No mem for data");
  223. FREE(pfmt);
  224. return FALSE;
  225. }
  226. // Read the data chunk.
  227. if (mmioRead(hmmio,
  228. (char *)pdata,
  229. iSize) != iSize) {
  230. TRACE("Failed to read data chunk");
  231. FREE(pfmt);
  232. FREE(pdata);
  233. return FALSE;
  234. }
  235. // Wrap the CWave object around what we have.
  236. memcpy(&m_pcmfmt, pfmt, sizeof(m_pcmfmt));
  237. // Replace the samples.
  238. if (m_pSamples) FREE(m_pSamples);
  239. m_pSamples = pdata;
  240. m_iSize = iSize;
  241. return TRUE;
  242. }
  243. BOOL CWave::LoadResource(WORD wID)
  244. {
  245. ASSERT(wID);
  246. HINSTANCE hInst = AfxGetResourceHandle();
  247. HRSRC hrsrc = ::FindResource(hInst,
  248. MAKEINTRESOURCE(wID), "WAVE");
  249. if (!hrsrc) {
  250. TRACE("WAVE resource not found");
  251. return FALSE;
  252. }
  253. HGLOBAL hg = ::LoadResource(hInst,
  254. hrsrc);
  255. if (!hg) {
  256. TRACE("Failed to load WAVE resource");
  257. return FALSE;
  258. }
  259. char* pRes = (char*) ::LockResource(hg);
  260. ASSERT(pRes);
  261. if ( pRes == NULL )
  262. {
  263. TRACE("Failed to lock WAVE resource");
  264. return FALSE;
  265. }
  266. // Mark the resource pages as read/write so the mmioOpen
  267. // won't fail
  268. int iSize = ::SizeofResource(hInst, hrsrc);
  269. DWORD dwOldProt;
  270. BOOL b = ::VirtualProtect(pRes,
  271. iSize,
  272. PAGE_READWRITE,
  273. &dwOldProt);
  274. ASSERT(b);
  275. // Open the memory block as an HMMIO object
  276. HMMIO hmmio;
  277. MMIOINFO info;
  278. memset(&info, 0, sizeof(info));
  279. info.fccIOProc = FOURCC_MEM;
  280. info.pchBuffer = pRes;
  281. info.cchBuffer = iSize;
  282. hmmio = mmioOpen(NULL,
  283. &info,
  284. MMIO_READ);
  285. if (!hmmio) {
  286. TRACE("mmioOpen failed. Error %d\n", info.wErrorRet);
  287. return FALSE;
  288. }
  289. BOOL bResult = Load(hmmio);
  290. mmioClose(hmmio, MMIO_FHOPEN);
  291. // Note: not required to unlock or free the resource in Win32
  292. return bResult;
  293. }
  294. // Get the number of samples.
  295. int CWave::GetNumSamples()
  296. {
  297. ASSERT(m_pcmfmt.wBitsPerSample);
  298. return m_iSize * 8 / m_pcmfmt.wBitsPerSample;
  299. }
  300. // Get a sample value scaled as a 16 bit signed quantity.
  301. int CWave::GetSample(int index)
  302. {
  303. if ((index < 0) || (index >= GetNumSamples())) {
  304. TRACE("Sample index out of range");
  305. return 0;
  306. }
  307. switch (m_pcmfmt.wBitsPerSample) {
  308. case 8: {
  309. BYTE *p = (BYTE *) m_pSamples;
  310. int i = p[index]; // 0 - 255;
  311. return (i - 128) * 256;
  312. } break;
  313. case 16: {
  314. ASSERT(sizeof(short int) == 2);
  315. short int* p = (short int *) m_pSamples;
  316. return p[index];
  317. } break;
  318. default:
  319. break;
  320. }
  321. ASSERT(1); // Invalid bits per sample
  322. return 0;
  323. }
  324. // Set a sample value from a 16 bit signed quantity.
  325. void CWave::SetSample(int index, int iValue)
  326. {
  327. if ((index < 0) || (index >= GetNumSamples())) {
  328. TRACE("Sample index out of range");
  329. return;
  330. }
  331. switch (m_pcmfmt.wBitsPerSample) {
  332. case 8: {
  333. BYTE* p = (BYTE*) m_pSamples;
  334. p[index] = (unsigned char) (iValue / 256 + 128);
  335. } break;
  336. case 16: {
  337. ASSERT(sizeof(short int) == 16);
  338. short int* p = (short int*) m_pSamples;
  339. p[index] = (unsigned short) iValue;
  340. } break;
  341. default:
  342. ASSERT(1); // Invalid bits per sample
  343. break;
  344. }
  345. }