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.

448 lines
14 KiB

  1. /* (C) Copyright Microsoft Corporation 1991. All Rights Reserved */
  2. /* wavedisp.c
  3. *
  4. * Implements waveform display control ("td_wavedisplay").
  5. *
  6. * This is NOT a general-purpose control (see the globals below).
  7. *
  8. * The waveform is represented as a string of characters in a font that
  9. * consists of vertical lines that correspond to different amplitudes.
  10. * Actually there is no font, it's just done with patBlts
  11. *
  12. * WARNING: this control cheats: it stores information in globals, so you
  13. * couldn't put it in a DLL (or use two of them in the same app)
  14. * without changing it.
  15. */
  16. /* Revision History.
  17. * 4/2/92 LaurieGr (AKA LKG) Ported to WIN32 / WIN16 common code
  18. * 25/6/92LaurieGr enhanced, Also had to reformat to 80 cols because
  19. * NT has only crappy fonts again.
  20. * 21/Feb/94 LaurieGr merged Daytona and Motown versions
  21. */
  22. #include "nocrap.h"
  23. #include <windows.h>
  24. #include <windowsx.h>
  25. #include <mmsystem.h>
  26. #include <mmreg.h>
  27. #include <math.h>
  28. #include "SoundRec.h"
  29. /* constants */
  30. #define MAX_TRIGGER_SEARCH 200 // limit search for trigger pt.
  31. #define MIN_TRIGGER_SAMPLE (128 - 8) // look for silent part
  32. #define MAX_TRIGGER_SAMPLE (128 + 8) // look for silent part
  33. #define MIN_TRIG16_SAMPLE (-1024) // look for silent part
  34. #define MAX_TRIG16_SAMPLE (1024) // look for silent part
  35. /* globals */
  36. static NPBYTE gpbWaveDisplay; // text string in WaveLine font
  37. // initially has samples in it
  38. // enough room for 4 bytes/sample
  39. static RECT grcWaveDisplay; // wave display rectangle
  40. static HBITMAP ghbmWaveDisplay; // mono bitmap.
  41. static HDC ghdcWaveDisplay; // memory DC for bitmap.
  42. // static iXScale = 1; // samples per pel across screen
  43. /* UpdateWaveDisplayString()
  44. *
  45. * Copy samples from just before the current position in the sample buffer
  46. * to the wave display string. The code tries to find a good "trigger point"
  47. * in the waveform so that the waveform will be aligned at the beginning of
  48. * a wave.
  49. *
  50. * The current position is in gpWaveSamples at glWavePosition which is
  51. * measured in samples (not bytes).
  52. *
  53. * The wave display string will contain numbers in the range -16..15
  54. *
  55. * for 8 bit: x' = abs(x-128)/8
  56. * for 16 bit: x' = abs(x)/2048
  57. *
  58. * When the display is "in motion" (i.e. actually playing or recording
  59. * we try to keep the display somewhat static by looking for a trigger
  60. * point (like an oscilloscope would) and display that part of the wave
  61. * that has just either played or been recorded.
  62. *
  63. */
  64. static void NEAR PASCAL
  65. UpdateWaveDisplayString(void)
  66. {
  67. // piSrc and pbSrc are init NULL to kill a compiler diagnostic.
  68. // The compiler cannot follow the logic and thinks that they may be
  69. // used before being set. (It's wrong. Hint: look at cbSrc and cbTrigger)
  70. BYTE * pbSrc = NULL; // pointer into <gpWaveSamples> to 8 bits
  71. short * piSrc = NULL; // pointer into <gpWaveSamples> to 16 bits
  72. // (use one or other according to wave format)
  73. int cbSrc; // number of samples that can be copied
  74. BYTE * pbDst; // pointer into <gpbWaveDisplay>
  75. int cbDst; // size of <gpWaveDisplay>
  76. int cbTrigger; // limit the search for a "trigger"
  77. BYTE b;
  78. int i;
  79. int cnt;
  80. WORD nSkipChannels;
  81. BOOL fStereoIn;
  82. BOOL fEightIn;
  83. cbDst = grcWaveDisplay.right - grcWaveDisplay.left; // rectangle size
  84. pbDst = gpbWaveDisplay;
  85. // Note: IsWaveFormatPCM() is called before this function is ever called, therefore
  86. // we can always rely on the fact that gpWaveFormat->wwFormatTag == WAVE_FORMAT_PCM.
  87. // This also implies, as mentioned in the docs on WAVEFORMATEX, that the
  88. // gpWaveFormat->wBitsPerSample should be equal to 8 or 16.
  89. // Note: We average the first two channels if they exist, any additional channels are
  90. // ignored.
  91. fStereoIn = gpWaveFormat->nChannels != 1;
  92. fEightIn = ((LPWAVEFORMATEX)gpWaveFormat)->wBitsPerSample == 8;
  93. nSkipChannels = max (0, gpWaveFormat->nChannels - 2);
  94. /* search for a "trigger point" if we are recording or playing */
  95. if ((ghWaveOut != NULL) || (ghWaveIn != NULL))
  96. { // we are in motion - align the *right* hand side of the window.
  97. cbTrigger = MAX_TRIGGER_SEARCH;
  98. if (gpWaveSamples == NULL)
  99. {
  100. /* no document at all is open */
  101. cbSrc = 0;
  102. }
  103. else
  104. {
  105. long lStartOffsetSrc, lEndOffsetSrc;
  106. /* align the *right* side of wave display to the current
  107. * position in the wave buffer, so that during recording
  108. * we see only samples that have just been recorded
  109. */
  110. lStartOffsetSrc = glWavePosition - (cbDst + cbTrigger);
  111. lEndOffsetSrc = glWavePosition;
  112. if (lStartOffsetSrc < 0)
  113. lEndOffsetSrc -= lStartOffsetSrc, lStartOffsetSrc = 0L;
  114. if (lEndOffsetSrc > glWaveSamplesValid)
  115. lEndOffsetSrc = glWaveSamplesValid;
  116. // Bombay Bug 1360: lStartOffsetSrc > lEndOffsetSrc causes GP Fault
  117. // if glWaveSamplesValid < lStartOffsetSrc we have a problem.
  118. if (lStartOffsetSrc > lEndOffsetSrc)
  119. {
  120. lStartOffsetSrc = lEndOffsetSrc - (cbDst + cbTrigger);
  121. if (lStartOffsetSrc < 0)
  122. lStartOffsetSrc = 0L;
  123. }
  124. cbSrc = (int)wfSamplesToBytes(gpWaveFormat, lEndOffsetSrc - lStartOffsetSrc);
  125. /* copy samples from buffer into local one */
  126. memmove( gpbWaveDisplay
  127. , gpWaveSamples + wfSamplesToBytes(gpWaveFormat, lStartOffsetSrc)
  128. , cbSrc
  129. );
  130. pbSrc = (BYTE *) gpbWaveDisplay;
  131. piSrc = (short *) gpbWaveDisplay;
  132. }
  133. if (cbTrigger > 0) {
  134. cbTrigger = min(cbSrc, cbTrigger); // don't look beyond buffer end
  135. /* search for a silent part in waveform */
  136. if (fEightIn)
  137. {
  138. while (cbTrigger > 0)
  139. {
  140. b = *pbSrc;
  141. if ((b > MIN_TRIGGER_SAMPLE) && (b < MAX_TRIGGER_SAMPLE))
  142. break;
  143. cbSrc--, pbSrc++, cbTrigger--;
  144. if (fStereoIn)
  145. pbSrc+=(nSkipChannels+1);
  146. }
  147. }
  148. else
  149. { // not EightIn
  150. while (cbTrigger > 0)
  151. {
  152. i = *piSrc;
  153. if ((i > MIN_TRIG16_SAMPLE) && (i < MAX_TRIG16_SAMPLE))
  154. break;
  155. cbSrc--, piSrc++, cbTrigger--;
  156. if (fStereoIn)
  157. piSrc+=(nSkipChannels+1);
  158. }
  159. }
  160. /* search for a non-silent part in waveform (this is the "trigger") */
  161. if (fEightIn)
  162. {
  163. while (cbTrigger > 0)
  164. {
  165. b = *pbSrc;
  166. if ((b <= MIN_TRIGGER_SAMPLE) || (b >= MAX_TRIGGER_SAMPLE))
  167. break;
  168. cbSrc--, pbSrc++, cbTrigger--;
  169. if (fStereoIn)
  170. pbSrc+=(nSkipChannels+1);
  171. }
  172. }
  173. else
  174. { // not EightIn
  175. while (cbTrigger > 0)
  176. {
  177. i = *piSrc;
  178. if ((i <= MIN_TRIG16_SAMPLE) || (i >= MAX_TRIG16_SAMPLE))
  179. break;
  180. cbSrc--, piSrc++, cbTrigger--;
  181. if (fStereoIn)
  182. piSrc+=(nSkipChannels+1);
  183. }
  184. }
  185. }
  186. }
  187. else // it's not playing or recording - static display
  188. {
  189. long lStartOffsetSrc, lEndOffsetSrc;
  190. /* align the *left* side of wave display to the current
  191. * position in the wave buffer
  192. */
  193. lStartOffsetSrc = glWavePosition;
  194. lEndOffsetSrc = glWavePosition + cbDst;
  195. if (lEndOffsetSrc > glWaveSamplesValid)
  196. lEndOffsetSrc = glWaveSamplesValid;
  197. cbSrc = (int)wfSamplesToBytes( gpWaveFormat
  198. , lEndOffsetSrc - lStartOffsetSrc
  199. );
  200. //
  201. // copy samples from buffer into local one
  202. //
  203. memmove( gpbWaveDisplay
  204. , gpWaveSamples
  205. + wfSamplesToBytes(gpWaveFormat, lStartOffsetSrc)
  206. , cbSrc
  207. );
  208. pbSrc = (BYTE *) gpbWaveDisplay;
  209. piSrc = (short *) gpbWaveDisplay;
  210. }
  211. cnt = min(cbSrc, cbDst);
  212. cbDst -= cnt;
  213. /* map cnt number of samples from pbSrc to string characters at pbDst
  214. ** fEightIn => 8 byte samples, else 16
  215. ** fStereoIn => Average left and right channels
  216. **
  217. ** pbSrc and pbDst both point into the same buffer addressed by
  218. ** gpbWaveDisplay, pbSrc >= pbDst. We process left to right, so OK.
  219. */
  220. if (fEightIn)
  221. {
  222. BYTE *pbDC = pbDst;
  223. int dccnt = cnt;
  224. DWORD dwSum = 0L;
  225. while (cnt-- > 0)
  226. {
  227. b = *pbSrc++;
  228. if (fStereoIn)
  229. {
  230. // Average left and right channels.
  231. b /= 2;
  232. b += (*pbSrc++ / 2);
  233. // Skip channels past Stereo
  234. pbSrc+=nSkipChannels;
  235. }
  236. dwSum += *pbDst++ = (BYTE)(b/8 + 112); // 128 + (b-128)/8
  237. }
  238. /* Eliminate DC offsets by subtracting the average offset
  239. * over all samples.
  240. */
  241. if (dwSum)
  242. {
  243. dwSum /= (DWORD)dccnt;
  244. dwSum -= 128;
  245. while (dwSum && dccnt-- > 0)
  246. *pbDC++ -= (BYTE)dwSum;
  247. }
  248. }
  249. else
  250. {
  251. BYTE *pbDC = pbDst;
  252. int dccnt = cnt;
  253. LONG lSum = 0L;
  254. while (cnt-- > 0)
  255. {
  256. i = *piSrc++;
  257. if (fStereoIn)
  258. {
  259. // Average left and right channels.
  260. i /= 2;
  261. i += (*piSrc++ / 2);
  262. // Skip channels past Stereo
  263. piSrc+=nSkipChannels;
  264. }
  265. lSum += *pbDst++ = (BYTE)(i/2048 + 128);
  266. }
  267. /* Eliminate DC offsets by subtracting the average offset
  268. * over all samples.
  269. */
  270. if (lSum)
  271. {
  272. lSum /= dccnt;
  273. lSum -= 128;
  274. while (lSum && dccnt-- > 0)
  275. *pbDC++ -= (BYTE)lSum;
  276. }
  277. }
  278. /* if necessary, pad the strings with whatever character represents
  279. * the "silence level". This is 128, the midpoint level.
  280. */
  281. while (cbDst-- > 0)
  282. *pbDst++ = 128;
  283. }
  284. /* WaveDisplayWndProc()
  285. *
  286. * This is the window procedure for the "WaveDisplay" control.
  287. */
  288. INT_PTR CALLBACK
  289. WaveDisplayWndProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
  290. {
  291. PAINTSTRUCT ps;
  292. RECT rc;
  293. int i;
  294. int n;
  295. int dx;
  296. int dy;
  297. switch (wMsg)
  298. {
  299. case WM_CREATE:
  300. /* make the window a bit bigger so that it lines up with
  301. * the frames of the shadow-frames beside it
  302. */
  303. /* allocate <gpbWaveDisplay> */
  304. GetClientRect(hwnd, &grcWaveDisplay);
  305. InflateRect(&grcWaveDisplay, -1, -1); // account for border
  306. gpbWaveDisplay = (NPBYTE)GlobalAllocPtr(GHND,
  307. (grcWaveDisplay.right+MAX_TRIGGER_SEARCH) * 4);
  308. // 4 is the maximum bytes per sample allowed
  309. if (gpbWaveDisplay == NULL)
  310. return -1; // out of memory
  311. ghdcWaveDisplay = CreateCompatibleDC(NULL);
  312. if (ghdcWaveDisplay == NULL)
  313. return -1; // out of memory
  314. ghbmWaveDisplay = CreateBitmap(
  315. grcWaveDisplay.right-grcWaveDisplay.left,
  316. grcWaveDisplay.bottom-grcWaveDisplay.top,
  317. 1,1,NULL);
  318. if (ghbmWaveDisplay == NULL)
  319. return -1; // out of memory
  320. SelectObject(ghdcWaveDisplay, ghbmWaveDisplay);
  321. break;
  322. case WM_DESTROY:
  323. /* free <gpbWaveDisplay> */
  324. if (gpbWaveDisplay != NULL)
  325. {
  326. GlobalFreePtr(gpbWaveDisplay);
  327. gpbWaveDisplay = NULL;
  328. }
  329. if (ghbmWaveDisplay != NULL)
  330. {
  331. DeleteDC(ghdcWaveDisplay);
  332. DeleteObject(ghbmWaveDisplay);
  333. ghdcWaveDisplay = NULL;
  334. ghbmWaveDisplay = NULL;
  335. }
  336. break;
  337. case WM_ERASEBKGND:
  338. /* draw the border and fill */
  339. GetClientRect(hwnd, &rc);
  340. DrawShadowFrame((HDC)wParam, &rc);
  341. return 0L;
  342. case WM_PAINT:
  343. BeginPaint(hwnd, &ps);
  344. if (!IsWaveFormatPCM(gpWaveFormat))
  345. {
  346. FillRect(ps.hdc, &grcWaveDisplay, ghbrPanel);
  347. }
  348. else if (gpbWaveDisplay != NULL)
  349. {
  350. /* update <gpbWaveDisplay> */
  351. UpdateWaveDisplayString();
  352. dx = grcWaveDisplay.right-grcWaveDisplay.left;
  353. dy = grcWaveDisplay.bottom-grcWaveDisplay.top;
  354. //
  355. // update the bitmap.
  356. //
  357. PatBlt(ghdcWaveDisplay,0,0,dx,dy,BLACKNESS);
  358. PatBlt(ghdcWaveDisplay,0,dy/2,dx,1,WHITENESS);
  359. for (i=0; i<dx; i++)
  360. {
  361. n = (BYTE)gpbWaveDisplay[i]; // n.b. must get it UNSIGNED
  362. n = n-128; // -16..15
  363. if (n > 0)
  364. PatBlt(ghdcWaveDisplay,
  365. i, dy/2-n,
  366. 1, n*2+1, WHITENESS);
  367. if (n < -1)
  368. {
  369. n++; // neg peak == pos peak
  370. PatBlt(ghdcWaveDisplay,
  371. i, dy/2+n,
  372. 1, -(n*2)+1, WHITENESS);
  373. }
  374. }
  375. /* draw the waveform */
  376. SetTextColor(ps.hdc, RGB_BGWAVEDISP);
  377. SetBkColor(ps.hdc, RGB_FGWAVEDISP);
  378. BitBlt(ps.hdc, grcWaveDisplay.left, grcWaveDisplay.top,
  379. dx,dy, ghdcWaveDisplay, 0, 0, SRCCOPY);
  380. }
  381. EndPaint(hwnd, &ps);
  382. return 0L;
  383. }
  384. return DefWindowProc(hwnd, wMsg, wParam, lParam);
  385. }