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.

1489 lines
39 KiB

  1. //==========================================================================;
  2. //
  3. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  4. // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  5. // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  6. // PURPOSE.
  7. //
  8. // Copyright (c) 1992 - 1998 Microsoft Corporation. All Rights Reserved.
  9. //
  10. //--------------------------------------------------------------------------;
  11. #include <streams.h>
  12. #include <tchar.h>
  13. #include <commctrl.h>
  14. #include <initguid.h>
  15. #include <stdio.h>
  16. #include <wxdebug.h>
  17. #include <ks.h>
  18. #include <ksmedia.h>
  19. #include "scope.h"
  20. #include "resource.h"
  21. //
  22. //
  23. // What this sample illustrates
  24. //
  25. // An audio oscilloscope that shows the waveform graphically as the audio is
  26. // received by the filter. The filter is a renderer that can put where ever
  27. // the normal runtime renderer goes. We have a single input pin that accepts
  28. // a number of difference audio formats and renders the data as appropriate.
  29. //
  30. //
  31. // Summary
  32. //
  33. // This is an audio oscilloscope renderer - we are basicly an audio renderer
  34. // When we are created we also create a class to look after the scope window
  35. // whose constructor creates a worker thread, when it is destroyed it will
  36. // also terminate the worker thread. On that worker thread a window is looked
  37. // after that shows the audio waveform for data sent to us. The data is kept
  38. // in a circular buffer that loops when sufficient data has been received. We
  39. // support a number of different audio formats such as 8bit mode and stereo.
  40. //
  41. //
  42. // Demonstration Instructions
  43. //
  44. // (To really sure of this demonstration the machine must have a sound card)
  45. // Start GRAPHEDT available in the ActiveMovie SDK tools. Drag and drop any
  46. // MPEG, AVI or MOV file into the tool and it will be rendered. Then go to
  47. // the filters in the graph and find the filter (box) titled "Audio Renderer"
  48. // This is the filter we will be replacing with this oscilloscope renderer.
  49. // Then click on the box and hit DELETE. After that go to the Graph menu and
  50. // select "Insert Filters", from the dialog box that pops up find and select
  51. // "Oscilloscope", then dismiss the dialog. Back in the graph layout find the
  52. // output pin of the filter that was connected to the input of the audio
  53. // renderer you just deleted, right click and select "Render". You should
  54. // see it being connected to the input pin of the oscilloscope you inserted
  55. //
  56. // Click Run on GRAPHEDT and you'll see a waveform for the audio soundtrack...
  57. //
  58. //
  59. // Files
  60. //
  61. // icon1.ico The icon for the oscilloscope window
  62. // makefile How we build it...
  63. // resource.h Microsoft Visual C++ generated file
  64. // scope.cpp The main filter and window implementations
  65. // scope.def What APIs the DLL imports and exports
  66. // scope.h Window and filter class definitions
  67. // scope.mak Visual C++ generated makefile
  68. // scope.rc Dialog box template for our window
  69. // scope.reg What goes in the registry to make us work
  70. //
  71. //
  72. // Base classes we use
  73. //
  74. // CBaseInputPin A generic input pin we use for the filter
  75. // CCritSec A wrapper class around a critical section
  76. // CBaseFilter The generic ActiveMovie filter object
  77. //
  78. //
  79. // Setup data
  80. const AMOVIESETUP_MEDIATYPE sudPinTypes =
  81. {
  82. &KSDATAFORMAT_TYPE_VBI, // Major type
  83. &KSDATAFORMAT_SUBTYPE_RAW8 // Minor type
  84. };
  85. const AMOVIESETUP_PIN sudPins =
  86. {
  87. L"Input", // Pin string name
  88. FALSE, // Is it rendered
  89. FALSE, // Is it an output
  90. FALSE, // Allowed zero pins
  91. FALSE, // Allowed many
  92. &CLSID_NULL, // Connects to filter
  93. L"Output", // Connects to pin
  94. 1, // Number of pins types
  95. &sudPinTypes } ; // Pin information
  96. const AMOVIESETUP_FILTER sudScope =
  97. {
  98. &CLSID_VBISCOPE, // Filter CLSID
  99. L"VBI Scope", // String name
  100. MERIT_DO_NOT_USE, // Filter merit
  101. 1, // Number pins
  102. &sudPins // Pin details
  103. };
  104. // List of class IDs and creator functions for class factory
  105. CFactoryTemplate g_Templates [] = {
  106. { L"VBI Scope"
  107. , &CLSID_VBISCOPE
  108. , CScopeFilter::CreateInstance
  109. , NULL
  110. , &sudScope }
  111. };
  112. int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
  113. //
  114. // CreateInstance
  115. //
  116. // This goes in the factory template table to create new instances
  117. //
  118. CUnknown * WINAPI CScopeFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
  119. {
  120. return new CScopeFilter(pUnk, phr);
  121. } // CreateInstance
  122. //
  123. // Constructor
  124. //
  125. // Create the filter, scope window, and input pin
  126. //
  127. #pragma warning(disable:4355)
  128. //
  129. CScopeFilter::CScopeFilter(LPUNKNOWN pUnk,HRESULT *phr) :
  130. CBaseFilter(NAME("VBIScope"), pUnk, (CCritSec *) this, CLSID_VBISCOPE),
  131. m_Window(NAME("VBIScope"), this, phr)
  132. {
  133. // Create the single input pin
  134. m_pInputPin = new CScopeInputPin(this,phr,L"Scope Input Pin");
  135. if (m_pInputPin == NULL) {
  136. *phr = E_OUTOFMEMORY;
  137. }
  138. } // (Constructor)
  139. //
  140. // Destructor
  141. //
  142. CScopeFilter::~CScopeFilter()
  143. {
  144. // Delete the contained interfaces
  145. ASSERT(m_pInputPin);
  146. delete m_pInputPin;
  147. m_pInputPin = NULL;
  148. } // (Destructor)
  149. //
  150. // GetPinCount
  151. //
  152. // Return the number of input pins we support
  153. //
  154. int CScopeFilter::GetPinCount()
  155. {
  156. return 1;
  157. } // GetPinCount
  158. //
  159. // GetPin
  160. //
  161. // Return our single input pin - not addrefed
  162. //
  163. CBasePin *CScopeFilter::GetPin(int n)
  164. {
  165. // We only support one input pin and it is numbered zero
  166. ASSERT(n == 0);
  167. if (n != 0) {
  168. return NULL;
  169. }
  170. return m_pInputPin;
  171. } // GetPin
  172. //
  173. // JoinFilterGraph
  174. //
  175. // Show our window when we join a filter graph
  176. // - and hide it when we are annexed from it
  177. //
  178. STDMETHODIMP CScopeFilter::JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName)
  179. {
  180. HRESULT hr = CBaseFilter::JoinFilterGraph(pGraph, pName);
  181. if (FAILED(hr)) {
  182. return hr;
  183. }
  184. // Hide or show the scope as appropriate
  185. if (pGraph == NULL) {
  186. m_Window.InactivateWindow();
  187. } else {
  188. m_Window.ActivateWindow();
  189. }
  190. return hr;
  191. } // JoinFilterGraph
  192. //
  193. // Stop
  194. //
  195. // Switch the filter into stopped mode.
  196. //
  197. STDMETHODIMP CScopeFilter::Stop()
  198. {
  199. CAutoLock lock(this);
  200. if (m_State != State_Stopped) {
  201. // Pause the device if we were running
  202. if (m_State == State_Running) {
  203. HRESULT hr = Pause();
  204. if (FAILED(hr)) {
  205. return hr;
  206. }
  207. }
  208. DbgLog((LOG_TRACE,1,TEXT("Stopping....")));
  209. // Base class changes state and tells pin to go to inactive
  210. // the pin Inactive method will decommit our allocator which
  211. // we need to do before closing the device
  212. HRESULT hr = CBaseFilter::Stop();
  213. if (FAILED(hr)) {
  214. return hr;
  215. }
  216. }
  217. return NOERROR;
  218. } // Stop
  219. //
  220. // Pause
  221. //
  222. // Override Pause to stop the window streaming
  223. //
  224. STDMETHODIMP CScopeFilter::Pause()
  225. {
  226. CAutoLock lock(this);
  227. // Check we can PAUSE given our current state
  228. if (m_State == State_Running) {
  229. m_Window.StopStreaming();
  230. }
  231. // tell the pin to go inactive and change state
  232. return CBaseFilter::Pause();
  233. } // Pause
  234. //
  235. // Run
  236. //
  237. // Overriden to start the window streaming
  238. //
  239. STDMETHODIMP CScopeFilter::Run(REFERENCE_TIME tStart)
  240. {
  241. CAutoLock lock(this);
  242. HRESULT hr = NOERROR;
  243. FILTER_STATE fsOld = m_State;
  244. // This will call Pause if currently stopped
  245. hr = CBaseFilter::Run(tStart);
  246. if (FAILED(hr)) {
  247. return hr;
  248. }
  249. m_Window.ActivateWindow();
  250. if (fsOld != State_Running) {
  251. m_Window.StartStreaming();
  252. }
  253. return NOERROR;
  254. } // Run
  255. //
  256. // Constructor
  257. //
  258. CScopeInputPin::CScopeInputPin(CScopeFilter *pFilter,
  259. HRESULT *phr,
  260. LPCWSTR pPinName) :
  261. CBaseInputPin(NAME("Scope Input Pin"), pFilter, pFilter, phr, pPinName)
  262. {
  263. m_pFilter = pFilter;
  264. } // (Constructor)
  265. //
  266. // Destructor does nothing
  267. //
  268. CScopeInputPin::~CScopeInputPin()
  269. {
  270. } // (Destructor)
  271. //
  272. // BreakConnect
  273. //
  274. // This is called when a connection or an attempted connection is terminated
  275. // and allows us to reset the connection media type to be invalid so that
  276. // we can always use that to determine whether we are connected or not. We
  277. // leave the format block alone as it will be reallocated if we get another
  278. // connection or alternatively be deleted if the filter is finally released
  279. //
  280. HRESULT CScopeInputPin::BreakConnect()
  281. {
  282. // Check we have a valid connection
  283. if (m_mt.IsValid() == FALSE) {
  284. return E_FAIL;
  285. }
  286. m_pFilter->Stop();
  287. // Reset the CLSIDs of the connected media type
  288. m_mt.SetType(&GUID_NULL);
  289. m_mt.SetSubtype(&GUID_NULL);
  290. return CBaseInputPin::BreakConnect();
  291. } // BreakConnect
  292. //
  293. // CheckMediaType
  294. //
  295. // Check that we can support a given proposed type
  296. //
  297. HRESULT CScopeInputPin::CheckMediaType(const CMediaType *pmt)
  298. {
  299. PKS_VBIINFOHEADER pIH = (PKS_VBIINFOHEADER) pmt->Format();
  300. if (pIH == NULL)
  301. return E_INVALIDARG;
  302. if (pmt->majortype != KSDATAFORMAT_TYPE_VBI) {
  303. return E_INVALIDARG;
  304. }
  305. if (pmt->subtype != KSDATAFORMAT_SUBTYPE_RAW8) {
  306. return E_INVALIDARG;
  307. }
  308. if (pmt->formattype != KSDATAFORMAT_SPECIFIER_VBI) {
  309. return E_INVALIDARG;
  310. }
  311. return NOERROR;
  312. } // CheckMediaType
  313. //
  314. // SetMediaType
  315. //
  316. // Actually set the format of the input pin
  317. //
  318. HRESULT CScopeInputPin::SetMediaType(const CMediaType *pmt)
  319. {
  320. CAutoLock lock(m_pFilter);
  321. // Pass the call up to my base class
  322. HRESULT hr = CBaseInputPin::SetMediaType(pmt);
  323. if (SUCCEEDED(hr)) {
  324. PKS_VBIINFOHEADER pIH = (PKS_VBIINFOHEADER) pmt->Format();
  325. // Save a copy of the VBI info header
  326. m_pFilter->m_Window.m_VBIIH = *pIH;
  327. ASSERT (pIH->BufferSize == ((pIH->EndLine - pIH->StartLine + 1) *
  328. pIH->StrideInBytes));
  329. m_pFilter->m_Window.m_nSamplesPerLine = pIH->SamplesPerLine;
  330. m_pFilter->m_Window.m_MaxValue = 256;
  331. m_pFilter->m_Window.m_DurationPerSample = 1.0 / pIH->SamplingFrequency;
  332. m_pFilter->m_Window.m_DurationOfLine = (double) pIH->SamplesPerLine *
  333. m_pFilter->m_Window.m_DurationPerSample;
  334. if (!m_pFilter->m_Window.AllocWaveBuffers ())
  335. return E_FAIL;
  336. // Reset the horizontal scroll bar
  337. m_pFilter->m_Window.SetHorizScrollRange(m_pFilter->m_Window.m_hwndDlg);
  338. // Reset the horizontal scroll bar
  339. m_pFilter->m_Window.SetControlRanges(m_pFilter->m_Window.m_hwndDlg);
  340. }
  341. return hr;
  342. } // SetMediaType
  343. //
  344. // Active
  345. //
  346. // Implements the remaining IMemInputPin virtual methods
  347. //
  348. HRESULT CScopeInputPin::Active(void)
  349. {
  350. return NOERROR;
  351. } // Active
  352. //
  353. // Inactive
  354. //
  355. // Called when the filter is stopped
  356. //
  357. HRESULT CScopeInputPin::Inactive(void)
  358. {
  359. return NOERROR;
  360. } // Inactive
  361. //
  362. // Receive
  363. //
  364. // Here's the next block of data from the stream
  365. //
  366. HRESULT CScopeInputPin::Receive(IMediaSample * pSample)
  367. {
  368. // Lock this with the filter-wide lock
  369. CAutoLock lock(m_pFilter);
  370. // If we're stopped, then reject this call
  371. // (the filter graph may be in mid-change)
  372. if (m_pFilter->m_State == State_Stopped) {
  373. return E_FAIL;
  374. }
  375. // Check all is well with the base class
  376. HRESULT hr = CBaseInputPin::Receive(pSample);
  377. if (FAILED(hr)) {
  378. return hr;
  379. }
  380. // Send the sample to the video window object for rendering
  381. return m_pFilter->m_Window.Receive(pSample);
  382. } // Receive
  383. //
  384. // CScopeWindow Constructor
  385. //
  386. CScopeWindow::CScopeWindow(TCHAR *pName, CScopeFilter *pRenderer,HRESULT *phr) :
  387. m_hInstance(g_hInst),
  388. m_pRenderer(pRenderer),
  389. m_hThread(INVALID_HANDLE_VALUE),
  390. m_ThreadID(0),
  391. m_hwndDlg(NULL),
  392. m_hwnd(NULL),
  393. m_pPoints1(NULL),
  394. m_pPoints2(NULL),
  395. m_nPoints(0),
  396. m_bStreaming(FALSE),
  397. m_bActivated(FALSE),
  398. m_LastMediaSampleSize(0)
  399. {
  400. // Create a thread to look after the window
  401. ASSERT(m_pRenderer);
  402. m_hThread = CreateThread(NULL, // Security attributes
  403. (DWORD) 0, // Initial stack size
  404. WindowMessageLoop, // Thread start address
  405. (LPVOID) this, // Thread parameter
  406. (DWORD) 0, // Creation flags
  407. &m_ThreadID); // Thread identifier
  408. // If we couldn't create a thread the whole thing's off
  409. ASSERT(m_hThread);
  410. if (m_hThread == NULL) {
  411. *phr = E_FAIL;
  412. return;
  413. }
  414. // Wait until the window has been initialised
  415. m_SyncWorker.Wait();
  416. } // (Constructor)
  417. //
  418. // Destructor
  419. //
  420. CScopeWindow::~CScopeWindow()
  421. {
  422. // Ensure we stop streaming and release any samples
  423. StopStreaming();
  424. InactivateWindow();
  425. // Tell the thread to destroy the window
  426. SendMessage(m_hwndDlg, WM_GOODBYE, (WPARAM)0, (LPARAM)0);
  427. // Make sure it has finished
  428. ASSERT(m_hThread != NULL);
  429. WaitForSingleObject(m_hThread,INFINITE);
  430. CloseHandle(m_hThread);
  431. if (m_pPoints1 != NULL) delete [] m_pPoints1;
  432. if (m_pPoints2 != NULL) delete [] m_pPoints2;
  433. } // (Destructor)
  434. //
  435. // ResetStreamingTimes
  436. //
  437. // This resets the latest sample stream times
  438. //
  439. HRESULT CScopeWindow::ResetStreamingTimes()
  440. {
  441. m_StartSample = 0;
  442. m_EndSample = 0;
  443. return NOERROR;
  444. } // ResetStreamingTimes
  445. //
  446. // StartStreaming
  447. //
  448. // This is called when we start running state
  449. //
  450. HRESULT CScopeWindow::StartStreaming()
  451. {
  452. CAutoLock cAutoLock(this);
  453. // Are we already streaming
  454. if (m_bStreaming == TRUE) {
  455. return NOERROR;
  456. }
  457. m_bStreaming = TRUE;
  458. m_CurrentFrame = 0;
  459. m_LastFrame = 0;
  460. m_DroppedFrames = 0;
  461. return NOERROR;
  462. } // StartStreaming
  463. //
  464. // StopStreaming
  465. //
  466. // This is called when we stop streaming
  467. //
  468. HRESULT CScopeWindow::StopStreaming()
  469. {
  470. CAutoLock cAutoLock(this);
  471. // Have we been stopped already
  472. if (m_bStreaming == FALSE) {
  473. return NOERROR;
  474. }
  475. m_bStreaming = FALSE;
  476. return NOERROR;
  477. } // StopStreaming
  478. //
  479. // InactivateWindow
  480. //
  481. // Called at the end to put the window in an inactive state
  482. //
  483. HRESULT CScopeWindow::InactivateWindow()
  484. {
  485. // Has the window been activated
  486. if (m_bActivated == FALSE) {
  487. return S_FALSE;
  488. }
  489. // Now hide the scope window
  490. ShowWindow(m_hwndDlg,SW_HIDE);
  491. m_bActivated = FALSE;
  492. return NOERROR;
  493. } // InactivateWindow
  494. //
  495. // ActivateWindow
  496. //
  497. // Show the scope window
  498. //
  499. HRESULT CScopeWindow::ActivateWindow()
  500. {
  501. // Has the window been activated
  502. if (m_bActivated == TRUE) {
  503. return S_FALSE;
  504. }
  505. m_bActivated = TRUE;
  506. ASSERT(m_bStreaming == FALSE);
  507. ShowWindow(m_hwndDlg,SW_SHOWNORMAL);
  508. return NOERROR;
  509. } // ActivateWindow
  510. //
  511. // OnClose
  512. //
  513. // This function handles the WM_CLOSE message
  514. //
  515. BOOL CScopeWindow::OnClose()
  516. {
  517. InactivateWindow();
  518. return TRUE;
  519. } // OnClose
  520. typedef struct TBEntry_tag {
  521. double TBDuration;
  522. TCHAR TBText[16];
  523. } TBEntry;
  524. TBEntry Timebases[] =
  525. {
  526. 1e-6, TEXT ("100 nS/Div"),
  527. 2e-6, TEXT ("200 nS/Div"),
  528. 5e-6, TEXT ("500 nS/Div"),
  529. 10e-6, TEXT ("1 uS/Div"),
  530. 20e-6, TEXT ("2 uS/Div"),
  531. 50e-6, TEXT ("5 uS/Div"),
  532. 60e-6, TEXT ("6 uS/Div"),
  533. 100e-6, TEXT ("10 uS/Div"),
  534. };
  535. #define N_TIMEBASES (sizeof(Timebases) / sizeof (Timebases[0]))
  536. #define TIMEBASE_DEFAULT_INDEX 5
  537. //
  538. // SetControlRanges
  539. //
  540. // Set the scroll ranges for all of the vertical trackbars
  541. //
  542. void CScopeWindow::SetControlRanges(HWND hDlg)
  543. {
  544. SendMessage(m_hwndTopLine, TBM_SETRANGE, TRUE,
  545. MAKELONG(m_VBIIH.StartLine, m_VBIIH.EndLine) );
  546. SendMessage(m_hwndTopLine, TBM_SETPOS, TRUE, (LPARAM) m_TopLine);
  547. SetDlgItemInt (hDlg, IDC_TOP_LINE_TEXT, m_TopLine, TRUE);
  548. SendMessage(m_hwndBottomLine, TBM_SETRANGE, TRUE,
  549. MAKELONG(m_VBIIH.StartLine, m_VBIIH.EndLine) );
  550. SendMessage(m_hwndBottomLine, TBM_SETPOS, TRUE, (LPARAM) m_BottomLine);
  551. SetDlgItemInt(hDlg, IDC_BOTTOM_LINE_TEXT, m_BottomLine, TRUE);
  552. SendMessage(m_hwndTimebase, TBM_SETRANGE, TRUE, MAKELONG(0, N_TIMEBASES - 1) );
  553. SendMessage(m_hwndTimebase, TBM_SETPOS, TRUE, (LPARAM) m_nTimebase);
  554. SetDlgItemText (hDlg, IDC_TIMEBASE_TEXT, Timebases[m_nTimebase].TBText);
  555. } // SetControlRanges
  556. //
  557. // StringFromTVStandard
  558. //
  559. TCHAR * StringFromTVStandard(long TVStd)
  560. {
  561. TCHAR * ptc;
  562. switch (TVStd) {
  563. case 0: ptc = TEXT("None"); break;
  564. case AnalogVideo_NTSC_M: ptc = TEXT("NTSC_M"); break;
  565. case AnalogVideo_NTSC_M_J: ptc = TEXT("NTSC_M_J"); break;
  566. case AnalogVideo_NTSC_433: ptc = TEXT("NTSC_433"); break;
  567. case AnalogVideo_PAL_B: ptc = TEXT("PAL_B"); break;
  568. case AnalogVideo_PAL_D: ptc = TEXT("PAL_D"); break;
  569. case AnalogVideo_PAL_G: ptc = TEXT("PAL_G"); break;
  570. case AnalogVideo_PAL_H: ptc = TEXT("PAL_H"); break;
  571. case AnalogVideo_PAL_I: ptc = TEXT("PAL_I"); break;
  572. case AnalogVideo_PAL_M: ptc = TEXT("PAL_M"); break;
  573. case AnalogVideo_PAL_N: ptc = TEXT("PAL_N"); break;
  574. case AnalogVideo_PAL_60: ptc = TEXT("PAL_60"); break;
  575. case AnalogVideo_SECAM_B: ptc = TEXT("SECAM_B"); break;
  576. case AnalogVideo_SECAM_D: ptc = TEXT("SECAM_D"); break;
  577. case AnalogVideo_SECAM_G: ptc = TEXT("SECAM_G"); break;
  578. case AnalogVideo_SECAM_H: ptc = TEXT("SECAM_H"); break;
  579. case AnalogVideo_SECAM_K: ptc = TEXT("SECAM_K"); break;
  580. case AnalogVideo_SECAM_K1: ptc = TEXT("SECAM_K1"); break;
  581. case AnalogVideo_SECAM_L: ptc = TEXT("SECAM_L"); break;
  582. case AnalogVideo_SECAM_L1: ptc = TEXT("SECAM_L1"); break;
  583. default:
  584. ptc = TEXT("[undefined]");
  585. break;
  586. }
  587. return ptc;
  588. }
  589. //
  590. // SetHorizScrollRange
  591. //
  592. // The horizontal scrollbar handles scrolling through the buffer
  593. //
  594. void CScopeWindow::SetHorizScrollRange(HWND hDlg)
  595. {
  596. SendMessage(m_hwndTBScroll, TBM_SETRANGE, TRUE, MAKELONG(0, (m_nPoints - 1)));
  597. SendMessage(m_hwndTBScroll, TBM_SETPOS, TRUE, (LPARAM) 0);
  598. m_TBScroll = 0;
  599. // TODO: Show info about each sample
  600. TCHAR szFormat[160];
  601. _stprintf (szFormat, TEXT("BpL: %d\nStr: %d\n%f\n"), m_VBIIH.SamplesPerLine,
  602. m_VBIIH.StrideInBytes,
  603. (float) m_VBIIH.SamplingFrequency / 1e6);
  604. TCHAR * pStd = StringFromTVStandard (m_VBIIH.VideoStandard);
  605. _tcscat (szFormat, pStd ? pStd : TEXT("[undefined]") );
  606. SetDlgItemText (hDlg, IDC_FORMAT, szFormat);
  607. } // SetHorizScrollRange
  608. //
  609. // ProcessHorizScrollCommands
  610. //
  611. // Called when we get a horizontal scroll bar message
  612. //
  613. void CScopeWindow::ProcessHorizScrollCommands(HWND hDlg, WPARAM wParam, LPARAM lParam)
  614. {
  615. int pos;
  616. int command = LOWORD (wParam);
  617. if (command != TB_ENDTRACK &&
  618. command != TB_THUMBTRACK &&
  619. command != TB_LINEDOWN &&
  620. command != TB_LINEUP &&
  621. command != TB_PAGEUP &&
  622. command != TB_PAGEDOWN)
  623. return;
  624. ASSERT (IsWindow ((HWND) lParam));
  625. pos = (int) SendMessage((HWND) lParam, TBM_GETPOS, 0, 0L);
  626. if ((HWND) lParam == m_hwndTBScroll) {
  627. m_TBScroll = pos;
  628. }
  629. OnPaint();
  630. } // ProcessHorizScrollCommands
  631. //
  632. // ProcessVertScrollCommands
  633. //
  634. // Called when we get a vertical scroll bar message
  635. //
  636. void CScopeWindow::ProcessVertScrollCommands(HWND hDlg, WPARAM wParam, LPARAM lParam)
  637. {
  638. int pos;
  639. int command = LOWORD (wParam);
  640. if (command != TB_ENDTRACK &&
  641. command != TB_THUMBTRACK &&
  642. command != TB_LINEDOWN &&
  643. command != TB_LINEUP &&
  644. command != TB_PAGEUP &&
  645. command != TB_PAGEDOWN)
  646. return;
  647. ASSERT (IsWindow ((HWND) lParam));
  648. pos = (int) SendMessage((HWND) lParam, TBM_GETPOS, 0, 0L);
  649. if ((HWND) lParam == m_hwndTopLine) {
  650. m_TopLine = pos;
  651. SetDlgItemInt (hDlg, IDC_TOP_LINE_TEXT, m_TopLine, TRUE);
  652. } else if ((HWND) lParam == m_hwndBottomLine) {
  653. m_BottomLine = pos;
  654. SetDlgItemInt (hDlg, IDC_BOTTOM_LINE_TEXT, m_BottomLine, TRUE);
  655. } else if ((HWND) lParam == m_hwndTimebase) {
  656. m_nTimebase = pos ;
  657. SetDlgItemText (hDlg, IDC_TIMEBASE_TEXT, Timebases[m_nTimebase].TBText);
  658. }
  659. OnPaint();
  660. } // ProcessVertScrollCommands
  661. //
  662. // InitialiseWindow
  663. //
  664. // This is called by the worker window thread after it has created the main
  665. // window and it wants to initialise the rest of the owner objects window
  666. // variables such as the device contexts. We execute this function with the
  667. // critical section still locked.
  668. //
  669. HRESULT CScopeWindow::InitialiseWindow(HWND hDlg)
  670. {
  671. HRESULT hr = NO_ERROR;
  672. RECT rR;
  673. // Initialise the window variables
  674. m_hwnd = GetDlgItem (hDlg, IDC_SCOPEWINDOW);
  675. // Quick sanity check
  676. ASSERT(m_hwnd != NULL);
  677. m_nTimebase = TIMEBASE_DEFAULT_INDEX;
  678. m_fFreeze = 0;
  679. // Default is show CC on the bottom trace
  680. m_TopLine = 20;
  681. m_BottomLine = 21;
  682. m_TBScroll = 0;
  683. m_TopF1 = TRUE;
  684. m_TopF2 = TRUE;
  685. m_BottomF1 = TRUE;
  686. m_BottomF2 = FALSE;
  687. GetWindowRect (m_hwnd, &rR);
  688. m_Width = rR.right - rR.left;
  689. m_Height = rR.bottom - rR.top;
  690. m_hwndTopLine = GetDlgItem (hDlg, IDC_TOP_LINE);
  691. m_hwndTopLineText = GetDlgItem (hDlg, IDC_TOP_LINE_TEXT);
  692. m_hwndBottomLine = GetDlgItem (hDlg, IDC_BOTTOM_LINE);
  693. m_hwndBottomLineText = GetDlgItem (hDlg, IDC_BOTTOM_LINE_TEXT);
  694. m_hwndTimebase = GetDlgItem (hDlg, IDC_TIMEBASE);
  695. m_hwndFreeze = GetDlgItem (hDlg, IDC_FREEZE);
  696. m_hwndTBStart = GetDlgItem (hDlg, IDC_TS_START);
  697. m_hwndTBEnd = GetDlgItem (hDlg, IDC_TS_LAST);
  698. m_hwndFrameCount = GetDlgItem (hDlg, IDC_FRAMES);
  699. m_hwndTBScroll = GetDlgItem (hDlg, IDC_TB_SCROLL);
  700. SetControlRanges(hDlg);
  701. SetHorizScrollRange(hDlg);
  702. CheckDlgButton(hDlg, IDC_TOP_F1, m_TopF1);
  703. CheckDlgButton(hDlg, IDC_TOP_F2, m_TopF2);
  704. CheckDlgButton(hDlg, IDC_BOTTOM_F1, m_BottomF1);
  705. CheckDlgButton(hDlg, IDC_BOTTOM_F2, m_BottomF2);
  706. CheckDlgButton(hDlg, IDC_FREEZE, m_fFreeze);
  707. m_hPen1 = CreatePen (PS_SOLID, 0, RGB (0, 0xff, 0));
  708. m_hPen2 = CreatePen (PS_SOLID, 0, RGB (0, 0xff, 0));
  709. m_hPenTicks = CreatePen (PS_SOLID, 0, RGB (0x80, 0x80, 0x80));
  710. m_hBrushBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
  711. if ( !m_hPen1 || !m_hPen2 || !m_hPenTicks || !m_hBrushBackground )
  712. hr = E_FAIL;
  713. HDC hdc = GetDC (NULL);
  714. if ( hdc )
  715. {
  716. m_hBitmap = CreateCompatibleBitmap (hdc, m_Width, m_Height);
  717. ReleaseDC (NULL, hdc);
  718. }
  719. else
  720. {
  721. m_hBitmap = NULL;
  722. hr = E_FAIL;
  723. }
  724. return hr;
  725. } // InitialiseWindow
  726. //
  727. // UninitialiseWindow
  728. //
  729. // This is called by the worker window thread when it receives a WM_GOODBYE
  730. // message from the window object destructor to delete all the resources we
  731. // allocated during initialisation
  732. //
  733. HRESULT CScopeWindow::UninitialiseWindow()
  734. {
  735. // Reset the window variables
  736. DeleteObject (m_hPen1);
  737. DeleteObject (m_hPen2);
  738. DeleteObject (m_hPenTicks);
  739. DeleteObject (m_hBitmap);
  740. m_hwnd = NULL;
  741. return NOERROR;
  742. } // UninitialiseWindow
  743. //
  744. // ScopeDlgProc
  745. //
  746. // The Scope window is actually a dialog box, and this is its window proc.
  747. // The only thing tricky about this is that the "this" pointer to the
  748. // CScopeWindow is passed during the WM_INITDIALOG message and is stored
  749. // in the window user data. This lets us access methods in the class
  750. // from within the dialog.
  751. //
  752. INT_PTR CALLBACK ScopeDlgProc(HWND hDlg, // Handle of dialog box
  753. UINT uMsg, // Message identifier
  754. WPARAM wParam, // First message parameter
  755. LPARAM lParam) // Second message parameter
  756. {
  757. CScopeWindow *pScopeWindow; // Pointer to the owning object
  758. // Get the window long that holds our owner pointer
  759. pScopeWindow = (CScopeWindow *) GetWindowLongPtr(hDlg, GWLP_USERDATA);
  760. switch (uMsg) {
  761. case WM_INITDIALOG:
  762. pScopeWindow = (CScopeWindow *) lParam;
  763. SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)pScopeWindow);
  764. return TRUE;
  765. case WM_COMMAND:
  766. switch (wParam) {
  767. case IDOK:
  768. case IDCANCEL:
  769. EndDialog (hDlg, 0);
  770. return TRUE;
  771. case IDC_FREEZE:
  772. pScopeWindow->m_fFreeze =
  773. (BOOL) IsDlgButtonChecked(hDlg,IDC_FREEZE);
  774. pScopeWindow->DrawWaveform();
  775. break;
  776. case IDC_TOP_F1:
  777. pScopeWindow->m_TopF1 =
  778. (BOOL) IsDlgButtonChecked(hDlg,IDC_TOP_F1);
  779. break;
  780. case IDC_TOP_F2:
  781. pScopeWindow->m_TopF2 =
  782. (BOOL) IsDlgButtonChecked(hDlg,IDC_TOP_F2);
  783. break;
  784. case IDC_BOTTOM_F1:
  785. pScopeWindow->m_BottomF1 =
  786. (BOOL) IsDlgButtonChecked(hDlg,IDC_BOTTOM_F1);
  787. break;
  788. case IDC_BOTTOM_F2:
  789. pScopeWindow->m_BottomF2 =
  790. (BOOL) IsDlgButtonChecked(hDlg,IDC_BOTTOM_F2);
  791. break;
  792. default:
  793. break;
  794. }
  795. case WM_VSCROLL:
  796. pScopeWindow->ProcessVertScrollCommands(hDlg, wParam, lParam);
  797. break;
  798. case WM_HSCROLL:
  799. pScopeWindow->ProcessHorizScrollCommands(hDlg, wParam, lParam);
  800. break;
  801. case WM_PAINT:
  802. ASSERT(pScopeWindow != NULL);
  803. pScopeWindow->OnPaint();
  804. break;
  805. // We stop WM_CLOSE messages going any further by intercepting them
  806. // and then setting an abort signal flag in the owning renderer so
  807. // that it knows the user wants to quit. The renderer can then
  808. // go about deleting it's interfaces and the window helper object
  809. // which will eventually cause a WM_DESTROY message to arrive. To
  810. // make it look as though the window has been immediately closed
  811. // we hide it and then wait for the renderer to catch us up
  812. case WM_CLOSE:
  813. ASSERT(pScopeWindow != NULL);
  814. pScopeWindow->OnClose();
  815. return (LRESULT) 0;
  816. // We receive a WM_GOODBYE window message (synchronously) from the
  817. // window object destructor in which case we do actually destroy
  818. // the window and complete the process in the WM_DESTROY message
  819. case WM_GOODBYE:
  820. ASSERT(pScopeWindow != NULL);
  821. pScopeWindow->UninitialiseWindow();
  822. PostQuitMessage(FALSE);
  823. EndDialog (hDlg, 0);
  824. return (LRESULT) 0;
  825. default:
  826. break;
  827. }
  828. return (LRESULT) 0;
  829. } // ScopeDlgProc
  830. //
  831. // MessageLoop
  832. //
  833. // This is the standard windows message loop for our worker thread. It sits
  834. // in a normal processing loop dispatching messages until it receives a quit
  835. // message, which may be generated through the owning object's destructor
  836. //
  837. HRESULT CScopeWindow::MessageLoop()
  838. {
  839. MSG Message; // Windows message structure
  840. DWORD dwResult; // Wait return code value
  841. HANDLE hWait[] = { (HANDLE) m_RenderEvent };
  842. // Enter the modified message loop
  843. while (TRUE) {
  844. // We use this to wait for two different kinds of events, the first
  845. // are the normal windows messages, the other is an event that will
  846. // be signaled when a sample is ready
  847. dwResult = MsgWaitForMultipleObjects((DWORD) 1, // Number events
  848. hWait, // Event handle
  849. FALSE, // Wait for either
  850. INFINITE, // No timeout
  851. QS_ALLINPUT); // All messages
  852. // Has a sample become ready to render
  853. if (dwResult == WAIT_OBJECT_0) {
  854. DrawWaveform();
  855. }
  856. // Process the thread's window message
  857. while (PeekMessage(&Message,NULL,(UINT) 0,(UINT) 0,PM_REMOVE)) {
  858. // Check for the WM_QUIT message
  859. if (Message.message == WM_QUIT) {
  860. return NOERROR;
  861. }
  862. // Send the message to the window procedure
  863. TranslateMessage(&Message);
  864. DispatchMessage(&Message);
  865. }
  866. }
  867. } // MessageLoop
  868. //
  869. // WindowMessageLoop
  870. //
  871. // This creates a window and processes it's messages on a separate thread
  872. //
  873. DWORD __stdcall CScopeWindow::WindowMessageLoop(LPVOID lpvThreadParm)
  874. {
  875. CScopeWindow *pScopeWindow; // The owner renderer object
  876. // Cast the thread parameter to be our owner object
  877. pScopeWindow = (CScopeWindow *) lpvThreadParm;
  878. pScopeWindow->m_hwndDlg =
  879. CreateDialogParam(
  880. pScopeWindow->m_hInstance, // Handle of app instance
  881. MAKEINTRESOURCE (IDD_SCOPEDIALOG), // Dialog box template
  882. NULL, // Handle of owner window
  883. ScopeDlgProc, // Address of dialog procedure
  884. (LPARAM) pScopeWindow // Initialization value
  885. );
  886. if (pScopeWindow->m_hwndDlg )
  887. {
  888. // Initialise the window, then signal the constructor that it can
  889. // continue and then unlock the object's critical section and
  890. // process messages
  891. if ( SUCCEEDED(pScopeWindow->InitialiseWindow(pScopeWindow->m_hwndDlg)) )
  892. {
  893. pScopeWindow->m_SyncWorker.Set();
  894. pScopeWindow->MessageLoop();
  895. }
  896. }
  897. ExitThread(TRUE);
  898. return TRUE;
  899. } // WindowMessageLoop
  900. //
  901. // OnPaint
  902. //
  903. // WM_PAINT message
  904. //
  905. BOOL CScopeWindow::OnPaint()
  906. {
  907. DrawWaveform();
  908. return TRUE;
  909. } // OnPaint
  910. //
  911. // ClearWindow
  912. //
  913. // Clear the scope to black and draw the center tickmarks
  914. //
  915. void CScopeWindow::ClearWindow(HDC hdc)
  916. {
  917. int y = m_Height / 2;
  918. SetMapMode (hdc, MM_TEXT);
  919. SetWindowOrgEx (hdc, 0, 0, NULL);
  920. SetViewportOrgEx (hdc, 0, 0, NULL);
  921. // Paint the entire window black
  922. PatBlt(hdc, // Handle of device context
  923. (INT) 0, // x-coord of upper-left corner
  924. (INT) 0, // y-coord of upper-left corner
  925. m_Width, // Width of rectangle to be filled
  926. m_Height, // Height of rectangle to be filled
  927. BLACKNESS); // Raster operation code
  928. // Draw the horizontal line
  929. HPEN hPenOld = (HPEN) SelectObject (hdc, m_hPenTicks);
  930. MoveToEx (hdc, 0, y, NULL);
  931. LineTo (hdc, m_Width, y);
  932. // Draw the tickmarks
  933. float inc = (float) m_Width / 10;
  934. int pos, j;
  935. int TickPoint;
  936. for (j = 0; j <= 10; j++) {
  937. if (j == 0 || j == 5 || j == 10)
  938. TickPoint = m_Height / 15;
  939. else
  940. TickPoint = m_Height / 30;
  941. pos = (int) (j * inc);
  942. MoveToEx (hdc, pos, y + TickPoint, NULL);
  943. LineTo (hdc, pos, y - TickPoint);
  944. }
  945. SelectObject (hdc, hPenOld);
  946. } // ClearWindow
  947. //
  948. // DrawPartialWaveform
  949. //
  950. // Draw a part of the VBIScope waveform - IndexStart and IndexEnd
  951. // are pointers into the m_pPoints array (in LOGICAL COORDINATES)
  952. // while ViewpointStart and ViewpointEnd are in SCREEN COORDINATES
  953. //
  954. void CScopeWindow::DrawPartialWaveform(HDC hdc,
  955. int IndexStart,
  956. int IndexEnd,
  957. int ViewportStart,
  958. int ViewportEnd)
  959. {
  960. int nPoints = IndexEnd - IndexStart;
  961. int nViewportWidth = ViewportEnd - ViewportStart;
  962. HPEN OldPen;
  963. ASSERT (IndexStart + nPoints < m_nPoints);
  964. // Origin at lower left, x increases up, y increases to right
  965. SetMapMode (hdc, MM_ANISOTROPIC);
  966. // Draw the first trace
  967. if (m_TopF1 || m_TopF2) {
  968. SetWindowOrgEx (hdc, IndexStart, 0, NULL);
  969. SetWindowExtEx (hdc, nPoints, (int) m_MaxValue, NULL);
  970. SetViewportExtEx (hdc, nViewportWidth, -m_Height / 2, NULL);
  971. SetViewportOrgEx (hdc, ViewportStart, m_Height / 2, NULL);
  972. OldPen = (HPEN) SelectObject (hdc, m_hPen1);
  973. Polyline (hdc, m_pPoints1 + IndexStart, nPoints + 1);
  974. SelectObject (hdc, OldPen);
  975. }
  976. // Draw the second trace
  977. if (m_BottomF1 || m_BottomF2) {
  978. SetWindowOrgEx (hdc, IndexStart, 0, NULL);
  979. SetWindowExtEx (hdc, nPoints, (int) m_MaxValue, NULL);
  980. SetViewportExtEx (hdc, nViewportWidth, -m_Height / 2, NULL);
  981. SetViewportOrgEx (hdc, ViewportStart, m_Height , NULL);
  982. OldPen = (HPEN) SelectObject (hdc, m_hPen2);
  983. Polyline (hdc, m_pPoints2 + IndexStart, nPoints + 1);
  984. SelectObject (hdc, OldPen);
  985. }
  986. } // DrawPartialWaveform
  987. //
  988. // DrawWaveform
  989. //
  990. // Draw the full VBIScope waveform
  991. //
  992. void CScopeWindow::DrawWaveform(void)
  993. {
  994. CAutoLock lock(m_pRenderer);
  995. TCHAR szT[40];
  996. if (m_pPoints1 == NULL)
  997. return;
  998. double ActualLineStartTime = m_VBIIH.ActualLineStartTime / 100.0; // in microseconds.
  999. double StartTime = m_TBScroll * m_DurationPerSample;
  1000. double StopTime = StartTime + Timebases [m_nTimebase].TBDuration;
  1001. double TotalLineTime = m_nPoints * m_DurationPerSample;
  1002. int PointsToDisplay;
  1003. int ActualPointsToDisplay;
  1004. ActualPointsToDisplay = PointsToDisplay = (int) (m_nPoints *
  1005. (Timebases [m_nTimebase].TBDuration / TotalLineTime));
  1006. if (m_TBScroll + PointsToDisplay >= m_nPoints - 1) {
  1007. ActualPointsToDisplay = m_nPoints - m_TBScroll - 1;
  1008. }
  1009. HDC hdc = GetWindowDC (m_hwnd); // WindowDC has clipping region
  1010. if ( hdc )
  1011. {
  1012. HDC hdcT = CreateCompatibleDC (hdc);
  1013. if ( hdcT )
  1014. {
  1015. HBITMAP hBitmapOld = (HBITMAP) SelectObject (hdcT, m_hBitmap);
  1016. ClearWindow (hdcT);
  1017. DrawPartialWaveform(hdcT,
  1018. m_TBScroll, m_TBScroll + ActualPointsToDisplay,// Index start, Index end
  1019. 0, (int) (m_Width * (float) ActualPointsToDisplay / PointsToDisplay)); // Window start, Window end
  1020. SetMapMode (hdcT, MM_TEXT);
  1021. SetWindowOrgEx (hdcT, 0, 0, NULL);
  1022. SetViewportOrgEx (hdcT, 0, 0, NULL);
  1023. BitBlt(hdc, // Handle of destination device context
  1024. 0, // x-coordinate of upper-left corner
  1025. 0, // y-coordinate of upper-left corner
  1026. m_Width, // Wwidth of destination rectangle
  1027. m_Height, // Height of destination rectangle
  1028. hdcT, // Handle of source device context
  1029. 0, // x-coordinate of source rectangle
  1030. 0, // y-coordinate of source rectangle
  1031. SRCCOPY); // Raster operation code
  1032. SelectObject (hdcT, hBitmapOld);
  1033. DeleteDC (hdcT);
  1034. }
  1035. GdiFlush();
  1036. ReleaseDC (m_hwnd, hdc);
  1037. }
  1038. // Frame count
  1039. wsprintf (szT, TEXT("%ld"), m_CurrentFrame);
  1040. SetDlgItemText (m_hwndDlg, IDC_FRAMES, szT);
  1041. // Dropped
  1042. wsprintf (szT, TEXT("%ld"), m_DroppedFrames);
  1043. SetDlgItemText (m_hwndDlg, IDC_DROPPED, szT);
  1044. // Show the start time of the display in microseconds
  1045. _stprintf (szT, TEXT("%7.4lfus"), ActualLineStartTime + StartTime * 1e6);
  1046. SetDlgItemText (m_hwndDlg, IDC_TS_START, szT);
  1047. // And the stop time
  1048. _stprintf (szT, TEXT("%7.4lfus"), ActualLineStartTime + StopTime * 1e6);
  1049. SetDlgItemText (m_hwndDlg, IDC_TS_LAST, szT);
  1050. } // DrawWaveform
  1051. //
  1052. // AllocWaveBuffers
  1053. //
  1054. // Allocate two buffers for two video lines
  1055. // This is only called when the format changes
  1056. // Return TRUE if allocations succeed
  1057. //
  1058. BOOL CScopeWindow::AllocWaveBuffers()
  1059. {
  1060. int j;
  1061. if (m_pPoints1) delete [] m_pPoints1;
  1062. if (m_pPoints2) delete [] m_pPoints2;
  1063. m_pPoints1 = NULL;
  1064. m_pPoints2 = NULL;
  1065. m_nPoints = 0;
  1066. m_nPoints = m_nSamplesPerLine = m_VBIIH.SamplesPerLine;
  1067. if (m_pPoints1 = new POINT [m_nPoints]) {
  1068. for (j = 0; j < m_nPoints; j++)
  1069. m_pPoints1[j].x = j;
  1070. }
  1071. if (m_pPoints2 = new POINT [m_nPoints]) {
  1072. for (j = 0; j < m_nPoints; j++)
  1073. m_pPoints2[j].x = j;
  1074. }
  1075. // Return TRUE if allocations succeeded
  1076. ASSERT ((m_pPoints1 != NULL) && (m_pPoints2 != NULL));
  1077. return ((m_pPoints1 != NULL) && (m_pPoints2 != NULL));
  1078. } // AllocWaveBuffers
  1079. //
  1080. // CopyWaveform
  1081. //
  1082. // Copy the current MediaSample into a POINT array so we can use GDI
  1083. // to paint the waveform.
  1084. //
  1085. void CScopeWindow::CopyWaveform(IMediaSample *pMediaSample)
  1086. {
  1087. BYTE *pWave; // Pointer to VBI data
  1088. ULONG nBytes;
  1089. ULONG j;
  1090. BYTE *pb;
  1091. HRESULT hr;
  1092. REFERENCE_TIME tDummy;
  1093. IMediaSample2 *Sample2;
  1094. AM_SAMPLE2_PROPERTIES SampleProperties;
  1095. pMediaSample->GetPointer(&pWave);
  1096. ASSERT(pWave != NULL);
  1097. nBytes = pMediaSample->GetActualDataLength();
  1098. ASSERT (nBytes == (m_VBIIH.EndLine - m_VBIIH.StartLine + 1) *
  1099. m_VBIIH.StrideInBytes);
  1100. hr = pMediaSample->GetMediaTime (&m_CurrentFrame, &tDummy);
  1101. m_DroppedFrames += (m_CurrentFrame - (m_LastFrame + 1));
  1102. m_LastFrame = m_CurrentFrame;
  1103. // All this to just get the field polarity flags...
  1104. if (SUCCEEDED( pMediaSample->QueryInterface(
  1105. __uuidof(IMediaSample2),
  1106. reinterpret_cast<PVOID*>(&Sample2) ) )) {
  1107. hr = Sample2->GetProperties(
  1108. sizeof( SampleProperties ),
  1109. reinterpret_cast<PBYTE> (&SampleProperties) );
  1110. Sample2->Release();
  1111. m_FrameFlags = SampleProperties.dwTypeSpecificFlags;
  1112. }
  1113. m_IsF1 = (m_FrameFlags & KS_VIDEO_FLAG_FIELD1);
  1114. // Copy data for the top line
  1115. if ((m_IsF1 && m_TopF1) || (!m_IsF1 && m_TopF2)) {
  1116. pb = pWave + ((m_TopLine - m_VBIIH.StartLine) *
  1117. m_VBIIH.StrideInBytes);
  1118. for (j = 0; j < m_VBIIH.SamplesPerLine; j++) {
  1119. m_pPoints1[j].y = (int)*pb++;
  1120. }
  1121. }
  1122. // Copy data for the bottom line
  1123. if ((m_IsF1 && m_BottomF1) || (!m_IsF1 && m_BottomF2)) {
  1124. pb = pWave + ((m_BottomLine - m_VBIIH.StartLine) *
  1125. m_VBIIH.StrideInBytes);
  1126. for (j = 0; j < m_VBIIH.SamplesPerLine; j++) {
  1127. m_pPoints2[j].y = (int)*pb++;
  1128. }
  1129. }
  1130. } // CopyWaveform
  1131. //
  1132. // Receive
  1133. //
  1134. // Called when the input pin receives another sample.
  1135. // Copy the waveform to our circular 1 second buffer
  1136. //
  1137. HRESULT CScopeWindow::Receive(IMediaSample *pSample)
  1138. {
  1139. CAutoLock cAutoLock(this);
  1140. ASSERT(pSample != NULL);
  1141. // Has our UI been frozen
  1142. if (m_fFreeze) {
  1143. return NOERROR;
  1144. }
  1145. REFERENCE_TIME tStart, tStop;
  1146. pSample->GetTime (&tStart,&tStop);
  1147. m_StartSample = tStart;
  1148. m_EndSample = tStop;
  1149. // Ignore samples of the wrong size!!!
  1150. if ((m_LastMediaSampleSize = pSample->GetActualDataLength()) != (int) m_VBIIH.BufferSize) {
  1151. // ASSERT (FALSE);
  1152. return NOERROR;
  1153. }
  1154. if (m_bStreaming == TRUE) {
  1155. CopyWaveform (pSample); // Copy data to our circular buffer
  1156. SetEvent(m_RenderEvent); // Set an event to display the
  1157. }
  1158. return NOERROR;
  1159. } // Receive
  1160. //
  1161. // DllRegisterServer
  1162. //
  1163. // Handles DLL registry
  1164. //
  1165. STDAPI DllRegisterServer()
  1166. {
  1167. return AMovieDllRegisterServer2( TRUE );
  1168. } // DllRegisterServer
  1169. //
  1170. // DllUnregisterServer
  1171. //
  1172. STDAPI DllUnregisterServer()
  1173. {
  1174. return AMovieDllRegisterServer2( FALSE );
  1175. } // DllUnregisterServer