home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / mac / SiteBldr / AMOVIE / SDK / _SETUP / COMMON.Z / scope.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-17  |  41.9 KB  |  1,448 lines

  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 - 1996  Microsoft Corporation.  All Rights Reserved.
  9. //
  10. //--------------------------------------------------------------------------;
  11. //
  12. //  Oscilloscope
  13.  
  14. #include <streams.h>
  15. #include <commctrl.h>
  16. #include <mmsystem.h>
  17. #include <initguid.h>
  18. #include <wxdebug.h>
  19. #include "scope.h"
  20. #include "resource.h"
  21.  
  22. // setup data
  23.  
  24. AMOVIESETUP_MEDIATYPE sudPinTypes =     { &MEDIATYPE_Audio                 // clsMajorType
  25.                                            , &MEDIASUBTYPE_NULL }  ;       // clsMinorType
  26.  
  27. AMOVIESETUP_PIN sudPins   =    { L"Input"            // strName
  28.                                , FALSE               // bRendered
  29.                                , FALSE               // bOutput
  30.                                , FALSE               // bZero
  31.                                , FALSE               // bMany
  32.                                , &CLSID_NULL         // clsConnectsToFilter
  33.                                , L"Output"           // strConnectsToPin
  34.                                , 1                   // nTypes
  35.                                , &sudPinTypes } ;    // lpTypes
  36.  
  37.  
  38. AMOVIESETUP_FILTER sudScope     = { &CLSID_Scope                    // clsID
  39.                                   , L"Oscilloscope filter"          // strName
  40.                                   , MERIT_DO_NOT_USE                // dwMerit
  41.                                   , 1                               // nPins
  42.                                   , &sudPins };                     // lpPin
  43.  
  44. /* List of class IDs and creator functions for class factory */
  45.  
  46. CFactoryTemplate g_Templates []  = {
  47.     {L"Oscilloscope filter", &CLSID_Scope, CScopeFilter::CreateInstance }
  48. };
  49.  
  50. int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
  51.  
  52.  
  53. /* This goes in the factory template table to create new instances */
  54.  
  55. CUnknown *CScopeFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
  56. {
  57.     return new CScopeFilter(pUnk, phr);
  58. }
  59.  
  60.  
  61. //////////////////////////////////////////////////////////////////////////
  62. // Filter
  63. //////////////////////////////////////////////////////////////////////////
  64.  
  65. /* Constructor */
  66. // Create the filter, scope window, and input pin
  67.  
  68. #pragma warning(disable:4355)
  69.  
  70. CScopeFilter::CScopeFilter(
  71.     LPUNKNOWN pUnk,
  72.     HRESULT *phr)
  73.     : CBaseFilter(NAME("Oscilloscope"), pUnk, (CCritSec *) this, CLSID_Scope, phr),
  74.     m_Window(NAME("Oscilloscope"), this, phr)
  75. {
  76.     m_fStopping = FALSE;
  77.  
  78.     /* Create the single input pin */
  79.     m_pInputPin = new CScopeInputPin(
  80.                             this,                   // Owning filter
  81.                             phr,                    // Result code
  82.                             L"Scope Input Pin");     // Pin name
  83.     ASSERT(m_pInputPin);
  84.  
  85.     /* Initialise ... */
  86.  
  87. }
  88.  
  89. //
  90. // GetSetupData
  91. //
  92. LPAMOVIESETUP_FILTER CScopeFilter::GetSetupData()
  93. {
  94.   return &sudScope;
  95. }
  96.  
  97.  
  98. /* Destructor */
  99. CScopeFilter::~CScopeFilter()
  100. {
  101.     /* Delete the contained interfaces */
  102.  
  103.     ASSERT(m_pInputPin);
  104.  
  105.     delete m_pInputPin;
  106. }
  107.  
  108.  
  109. /* Return the number of input pins we support */
  110. int CScopeFilter::GetPinCount()
  111. {
  112.     return 1;
  113. }
  114.  
  115. /* Return our single input pin - not addrefed */
  116. CBasePin *CScopeFilter::GetPin(int n)
  117. {
  118.     /* We only support one input pin and it is numbered zero */
  119.  
  120.     ASSERT(n == 0);
  121.     if (n != 0) {
  122.         return NULL;
  123.     }
  124.  
  125.     return m_pInputPin;
  126. }
  127.  
  128. // Show our window when we join a filter graph, and hide it when we are no longer part of a graph
  129. STDMETHODIMP CScopeFilter::JoinFilterGraph(IFilterGraph * pGraph, LPCWSTR pName)
  130. {
  131.     HRESULT hr =  CBaseFilter::JoinFilterGraph(pGraph, pName);
  132.  
  133.     if (FAILED(hr)) {
  134.         return hr;
  135.     }
  136.     if (pGraph == NULL) {
  137.         m_Window.InactivateWindow();
  138.     }
  139.     else {
  140.         m_Window.ActivateWindow ();
  141.     }
  142.     return hr;
  143. }
  144.  
  145. // switch the filter into stopped mode.
  146. STDMETHODIMP CScopeFilter::Stop()
  147. {
  148.     CAutoLock lock(this);
  149.  
  150.     if (m_State != State_Stopped) {
  151.  
  152.         // pause the device if we were running
  153.         if (m_State == State_Running) {
  154.             HRESULT hr = Pause();
  155.             if (FAILED(hr)) {
  156.                 return hr;
  157.             }
  158.         }
  159.  
  160.         DbgLog((LOG_TRACE,1,TEXT("Stopping....")));
  161.  
  162.         // base class changes state and tells pin to go to inactive
  163.         // the pin Inactive method will decommit our allocator, which we
  164.         // need to do before closing the device.
  165.  
  166.         // do the state change
  167.         HRESULT hr =  CBaseFilter::Stop();
  168.         if (FAILED(hr)) {
  169.             return hr;
  170.         }
  171.     }
  172.     return NOERROR;
  173. }
  174.  
  175. STDMETHODIMP CScopeFilter::Pause()
  176. {
  177.     CAutoLock lock(this);
  178.  
  179.     HRESULT hr = NOERROR;
  180.  
  181.     /* Check we can PAUSE given our current state */
  182.  
  183.     if (m_State == State_Running) {
  184.         DbgLog((LOG_TRACE,1,TEXT("Running->Paused")));
  185.  
  186.         m_Window.StopStreaming();
  187.     } else {
  188.         if (m_State == State_Stopped) {
  189.             DbgLog((LOG_TRACE,1,TEXT("Stopped->Paused")));
  190.         }
  191.     }
  192.  
  193.     // tell the pin to go inactive and change state
  194.     return CBaseFilter::Pause();
  195. }
  196.  
  197. STDMETHODIMP CScopeFilter::Run(REFERENCE_TIME tStart)
  198. {
  199.     CAutoLock lock(this);
  200.     HRESULT hr = NOERROR;
  201.     FILTER_STATE fsOld = m_State;
  202.  
  203.     // this will call Pause if currently stopped
  204.     hr = CBaseFilter::Run(tStart);
  205.     if (FAILED(hr)) {
  206.         return hr;
  207.     }
  208.  
  209.     if (fsOld != State_Running) {
  210.  
  211.         DbgLog((LOG_TRACE,1,TEXT("Paused->Running")));
  212.  
  213.         m_Window.StartStreaming();
  214.     }
  215.  
  216.     return NOERROR;
  217. }
  218.  
  219.  
  220.  
  221. //////////////////////////////////////////////////////////////////////////
  222. // Pin
  223. //////////////////////////////////////////////////////////////////////////
  224.  
  225. /* Constructor */
  226.  
  227. CScopeInputPin::CScopeInputPin(
  228.     CScopeFilter *pFilter,
  229.     HRESULT *phr,
  230.     LPCWSTR pPinName)
  231.     : CBaseInputPin(NAME("Scope Input Pin"), pFilter, pFilter, phr, pPinName)
  232. {
  233.     m_pFilter = pFilter;
  234. }
  235.  
  236. CScopeInputPin::~CScopeInputPin()
  237. {
  238. }
  239.  
  240. /* This is called when a connection or an attempted connection is terminated
  241.    and allows us to reset the connection media type to be invalid so that
  242.    we can always use that to determine whether we are connected or not. We
  243.    leave the format block alone as it will be reallocated if we get another
  244.    connection or alternatively be deleted if the filter is finally released */
  245.  
  246. HRESULT CScopeInputPin::BreakConnect()
  247. {
  248.     /* Check we have a valid connection */
  249.  
  250.     if (m_mt.IsValid() == FALSE) {
  251.         return E_FAIL;
  252.     }
  253.  
  254.     m_pFilter->Stop();
  255.  
  256.     /* Reset the CLSIDs of the connected media type */
  257.  
  258.     m_mt.SetType(&GUID_NULL);
  259.     m_mt.SetSubtype(&GUID_NULL);
  260.  
  261.     return CBaseInputPin::BreakConnect();
  262. }
  263.  
  264.  
  265. /* Check that we can support a given proposed type */
  266. HRESULT CScopeInputPin::CheckMediaType(const CMediaType *pmt)
  267. {
  268.     WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmt->Format();
  269.  
  270.     if (pwfx == NULL)
  271.         return E_INVALIDARG;
  272.  
  273.     #ifdef DEBUG
  274.  
  275.     DbgLog((LOG_TRACE,1,TEXT("Format length %d"),pmt->FormatLength()));
  276.  
  277.     const int iGUID_STRING = 128;
  278.     OLECHAR szGUIDName[iGUID_STRING];
  279.  
  280.     /* Dump the GUID types */
  281.  
  282.     DbgLog((LOG_TRACE,1,TEXT("Checking input media type....")));
  283.  
  284.     if (pmt->majortype == MEDIATYPE_Audio) {
  285.         DbgLog((LOG_TRACE,1,TEXT("Major type MEDIATYPE_Audio")));
  286.     } else {
  287.         StringFromGUID2(pmt->majortype,szGUIDName,iGUID_STRING);
  288.         DbgLog((LOG_TRACE,1,TEXT("Major type %ls"),szGUIDName));
  289.     }
  290.     StringFromGUID2(pmt->subtype,szGUIDName,iGUID_STRING);
  291.     DbgLog((LOG_TRACE,2,TEXT("Subtype %ls"),szGUIDName));
  292.  
  293.     #endif
  294.  
  295.     // reject non-PCM Audio type
  296.     if (pmt->majortype != MEDIATYPE_Audio) {
  297.         return E_INVALIDARG;
  298.     }
  299.  
  300.     if (pmt->FormatLength() < sizeof(PCMWAVEFORMAT)) {
  301.         return E_INVALIDARG;
  302.     }
  303.  
  304.     if (pwfx->wFormatTag != WAVE_FORMAT_PCM) {
  305.         return E_INVALIDARG;
  306.     }
  307.  
  308.     return NOERROR;
  309. }
  310.  
  311. // Actually set the format of the input pin
  312. HRESULT CScopeInputPin::SetMediaType(const CMediaType *pmt)
  313. {
  314.     CAutoLock lock(m_pFilter);
  315.  
  316.     HRESULT hr;        // return code from base class calls
  317.  
  318.     // Pass the call up to my base class
  319.     hr = CBaseInputPin::SetMediaType(pmt);
  320.     if (SUCCEEDED(hr)) {
  321.         WAVEFORMATEX *pwf = (WAVEFORMATEX *) pmt->Format();
  322.  
  323.         m_pFilter->m_Window.m_nChannels = pwf->nChannels;
  324.         m_pFilter->m_Window.m_nSamplesPerSec = pwf->nSamplesPerSec;
  325.         m_pFilter->m_Window.m_nBitsPerSample = pwf->wBitsPerSample;
  326.         m_pFilter->m_Window.m_nBlockAlign = pwf->nBlockAlign;
  327.  
  328.         m_pFilter->m_Window.m_MaxValue = 128;
  329.  
  330.         m_pFilter->m_Window.m_nIndex = 0;
  331.  
  332.         if (!m_pFilter->m_Window.AllocWaveBuffers ())
  333.             return E_FAIL;
  334.  
  335.         // Reset the horizontal scroll bar
  336.         m_pFilter->m_Window.SetHorizScrollRange(m_pFilter->m_Window.m_hwndDlg);
  337.  
  338.         return NOERROR;
  339.     }
  340.     else
  341.         return hr;
  342. }
  343.  
  344. /* Implements the remaining IMemInputPin virtual methods */
  345. HRESULT
  346. CScopeInputPin::Active(void)
  347. {
  348.     return NOERROR;
  349. }
  350.  
  351. HRESULT
  352. CScopeInputPin::Inactive(void)
  353. {
  354.     return NOERROR;
  355. }
  356.  
  357.  
  358. // Here's the next block of data from the stream
  359. HRESULT CScopeInputPin::Receive(IMediaSample * pSample)
  360. {
  361.     // lock this with the filter-wide lock
  362.     CAutoLock lock(m_pFilter);
  363.  
  364.     // if we're stopped, then reject this call
  365.     // (the filter graph may be in mid-change)
  366.     if (m_pFilter->m_State == State_Stopped) {
  367.         DbgLog((LOG_ERROR,1, TEXT("Receive when stopped!")));
  368.  
  369.         return E_FAIL;
  370.     }
  371.  
  372.     HRESULT hr = NOERROR;
  373.  
  374.     // check all is well with the base class
  375.     hr = CBaseInputPin::Receive(pSample);
  376.     if (FAILED(hr)) {
  377.         return hr;
  378.     }
  379.  
  380.     /* See if the user has requested a closedown - closedown will eventually
  381.        be signaled to the filter graph group through an event which will then
  382.        look after everything such as the Inactive calls and disconnection */
  383.  
  384.     if (m_pFilter->m_fStopping == TRUE) {
  385.         return S_FALSE;
  386.     }
  387.  
  388.     /* Send the sample to the video window object for rendering */
  389.  
  390.     hr = m_pFilter->m_Window.Receive(pSample);
  391.  
  392.     /* Return the status code */
  393.     return hr;
  394. }
  395.  
  396.  
  397. //////////////////////////////////////////////////////////////////////////
  398. // Scope Window
  399. //////////////////////////////////////////////////////////////////////////
  400.  
  401. //
  402. // CScopeWindow Constructor
  403. //
  404. CScopeWindow::CScopeWindow(TCHAR *pName, CScopeFilter *pRenderer,HRESULT *phr) :
  405.     m_hInstance(g_hInst),
  406.     m_pRenderer(pRenderer),
  407.     m_hThread(INVALID_HANDLE_VALUE),
  408.     m_ThreadID(0),
  409.     m_hwndDlg(NULL),
  410.     m_hwnd(NULL),
  411.     m_pPoints1(NULL),
  412.     m_pPoints2(NULL),
  413.     m_nPoints(0),
  414.     m_bStreaming(FALSE),
  415.     m_bActivated(FALSE),
  416.     m_LastMediaSampleSize(0)
  417. {
  418.     /* Create a thread to look after the window */
  419.  
  420.     ASSERT(m_pRenderer);
  421.     m_hThread = CreateThread(NULL,                  // Security attributes
  422.                              (DWORD) 0,             // Initial stack size
  423.                              WindowMessageLoop,     // Thread start address
  424.                              (LPVOID) this,         // Thread parameter
  425.                              (DWORD) 0,             // Creation flags
  426.                              &m_ThreadID);          // Thread identifier
  427.  
  428.     /* If we couldn't create a thread the whole thing's off */
  429.  
  430.     ASSERT(m_hThread);
  431.     if (m_hThread == NULL) {
  432.         *phr = E_FAIL;
  433.         return;
  434.     }
  435.  
  436.     /* Wait until the window has been initialised */
  437.     m_SyncWorker.Wait();
  438.  
  439.     // Experimentally, lower the priority of painting
  440.     // SetThreadPriority (m_hThread, THREAD_PRIORITY_BELOW_NORMAL);
  441. }
  442.  
  443. #define WM_GOODBYE (WM_USER + 2)  // Message sent to close the window
  444.  
  445. /* Destructor */
  446. CScopeWindow::~CScopeWindow()
  447. {
  448.     /* Ensure we stop streaming and release any samples */
  449.  
  450.     StopStreaming();
  451.     InactivateWindow();
  452.  
  453.     /* Tell the thread to destroy the window */
  454.     SendMessage(m_hwndDlg, WM_GOODBYE, (WPARAM)0, (LPARAM)0);
  455.  
  456.     /* Make sure it has finished */
  457.  
  458.     ASSERT(m_hThread != NULL);
  459.     WaitForSingleObject(m_hThread,INFINITE);
  460.     if (m_pPoints1 != NULL)
  461.         delete [] m_pPoints1;
  462.     if (m_pPoints2 != NULL)
  463.         delete [] m_pPoints2;
  464.     CloseHandle(m_hThread);
  465. }
  466.  
  467.  
  468. /* This resets the latest sample stream times */
  469. HRESULT CScopeWindow::ResetStreamingTimes()
  470. {
  471.     m_StartSample = 0;
  472.     m_EndSample = 0;
  473.     return NOERROR;
  474. }
  475.  
  476.  
  477. /* This is called when we start running state so that we can store the current
  478.    base reference time from which stream time is offset from */
  479. HRESULT CScopeWindow::StartStreaming()
  480. {
  481.     CAutoLock cAutoLock(this);
  482.  
  483.     /* Are we already streaming */
  484.  
  485.     if (m_bStreaming == TRUE) {
  486.         return NOERROR;
  487.     }
  488.  
  489.     ASSERT(m_bActivated == TRUE);
  490.     m_bStreaming = TRUE;
  491.  
  492.     return NOERROR;
  493. }
  494.  
  495.  
  496. /* This is called when we stop streaming */
  497. HRESULT CScopeWindow::StopStreaming()
  498. {
  499.     CAutoLock cAutoLock(this);
  500.  
  501.     /* Have we been stopped already */
  502.  
  503.     if (m_bStreaming == FALSE) {
  504.         return NOERROR;
  505.     }
  506.  
  507.     ASSERT(m_bActivated == TRUE);
  508.     m_bStreaming = FALSE;
  509.  
  510.     return NOERROR;
  511. }
  512.  
  513.  
  514.  
  515. /* Called at the end to put the window in an inactive state */
  516. HRESULT CScopeWindow::InactivateWindow()
  517. {
  518.     /* Has the window been activated */
  519.     if (m_bActivated == FALSE) {
  520.         return S_FALSE;
  521.     }
  522.  
  523.     ASSERT(m_bStreaming == FALSE);
  524.  
  525.     m_bActivated = FALSE;
  526.     ShowWindow(m_hwndDlg,SW_HIDE);
  527.     return NOERROR;
  528. }
  529.  
  530.  
  531. /* Show the scope window */
  532. HRESULT CScopeWindow::ActivateWindow()
  533. {
  534.     /* Has the window been activated */
  535.     if (m_bActivated == TRUE) {
  536.         return S_FALSE;
  537.     }
  538.  
  539.     m_bActivated = TRUE;
  540.     ASSERT(m_bStreaming == FALSE);
  541.  
  542.     ShowWindow(m_hwndDlg,SW_SHOWNORMAL);
  543.     return NOERROR;
  544. }
  545.  
  546. BOOL CScopeWindow::OnSize(LONG Width, LONG Height)
  547. {
  548.     RECT rc;
  549.  
  550.     GetWindowRect (m_hwnd, &rc);
  551.  
  552.     return TRUE;
  553. }
  554.  
  555.  
  556. /* This function handles the WM_CLOSE message */
  557. BOOL CScopeWindow::OnClose()
  558. {
  559.     ShowWindow(m_hwndDlg,SW_HIDE);
  560.     m_pRenderer->m_fStopping = TRUE;
  561.     return TRUE;
  562. }
  563.  
  564. typedef struct GainEntry_tag {
  565.     double GainValue;
  566.     TCHAR GainText[8];
  567. } GainEntry;
  568.  
  569. GainEntry GainEntries[] =
  570. {
  571.     128.,  TEXT ("*128"),
  572.     64.,   TEXT ("*64"),
  573.     32.,   TEXT ("*32"),
  574.     16.,   TEXT ("*16"),
  575.     8.,    TEXT ("*8"),
  576.     4.,    TEXT ("*4"),
  577.     2.,    TEXT ("*2"),
  578.     1.,    TEXT ("*1"),
  579.     1./2,  TEXT ("/2"),
  580.     1./4,  TEXT ("/4"),
  581.     1./8,  TEXT ("/8"),
  582.     1./16, TEXT ("/16"),
  583.     1./32, TEXT ("/32"),
  584.     1./64, TEXT ("/64"),
  585.     1./128,TEXT ("/128"),
  586.     1./256,TEXT ("/256"),
  587.  
  588. };
  589. #define N_GAINENTRIES (sizeof(GainEntries) / sizeof (GainEntries[0]))
  590. #define GAIN_DEFAULT_INDEX 7
  591.  
  592. typedef struct TBEntry_tag {
  593.     int TBDivisor;
  594.     TCHAR TBText[16];
  595. } TBEntry;
  596.  
  597. TBEntry Timebases[] =
  598. {
  599.     10000,  TEXT ("10 uS/Div"),
  600.      5000,  TEXT ("20 uS/Div"),
  601.      2000,  TEXT ("50 uS/Div"),
  602.      1000,  TEXT ("100 uS/Div"),
  603.       500,  TEXT ("200 uS/Div"),
  604.       200,  TEXT ("500 uS/Div"),
  605.       100,  TEXT ("1 mS/Div"),
  606.        50,  TEXT ("2 mS/Div"),
  607.        20,  TEXT ("5 mS/Div"),
  608.        10,  TEXT ("10 mS/Div"),
  609.         5,  TEXT ("20 mS/Div"),
  610.         2,  TEXT ("50 mS/Div"),
  611.         1,  TEXT ("100 mS/Div")
  612. };
  613.  
  614. #define N_TIMEBASES (sizeof(Timebases) / sizeof (Timebases[0]))
  615. #define TIMEBASE_DEFAULT_INDEX 9
  616.  
  617. // Set the scroll ranges for all of the vertical trackbars
  618. void CScopeWindow::SetControlRanges(HWND hDlg)
  619. {
  620.     SendMessage(m_hwndLGain, TBM_SETRANGE, TRUE, MAKELONG(0, N_GAINENTRIES - 1) );
  621.     SendMessage(m_hwndLGain, TBM_SETPOS, TRUE, (LPARAM) GAIN_DEFAULT_INDEX);
  622.     SetDlgItemText (hDlg, IDC_L_GAIN_TEXT, GainEntries[m_LGain].GainText);
  623.  
  624.     SendMessage(m_hwndLOffset, TBM_SETRANGE, TRUE, MAKELONG(0, m_Height - 1));
  625.     SendMessage(m_hwndLOffset, TBM_SETPOS, TRUE, (LPARAM) m_Height / 2);
  626.     SetDlgItemInt (hDlg, IDC_L_OFFSET_TEXT, -m_LOffset, TRUE);
  627.  
  628.     SendMessage(m_hwndRGain, TBM_SETRANGE, TRUE, MAKELONG(0, N_GAINENTRIES - 1) );
  629.     SendMessage(m_hwndRGain, TBM_SETPOS, TRUE, (LPARAM) GAIN_DEFAULT_INDEX);
  630.     SetDlgItemText (hDlg, IDC_R_GAIN_TEXT, GainEntries[m_RGain].GainText);
  631.  
  632.     SendMessage(m_hwndROffset, TBM_SETRANGE, TRUE, MAKELONG(0, m_Height - 1) );
  633.     SendMessage(m_hwndROffset, TBM_SETPOS, TRUE, (LPARAM) m_Height / 2);
  634.     SetDlgItemInt (hDlg, IDC_R_OFFSET_TEXT, -m_ROffset, TRUE);
  635.  
  636.     SendMessage(m_hwndTimebase, TBM_SETRANGE, TRUE, MAKELONG(0, N_TIMEBASES - 1) );
  637.     SendMessage(m_hwndTimebase, TBM_SETPOS, TRUE, (LPARAM) m_nTimebase);
  638.     SetDlgItemText (hDlg, IDC_TIMEBASE_TEXT, Timebases[m_nTimebase].TBText);
  639. }
  640.  
  641. // The horizontal scrollbar handles scrolling through the 1 second circular buffer
  642. void CScopeWindow::SetHorizScrollRange(HWND hDlg)
  643. {
  644.     SendMessage(m_hwndTBScroll, TBM_SETRANGE, TRUE, MAKELONG(0, (m_nPoints - 1) / 2) );
  645.     SendMessage(m_hwndTBScroll, TBM_SETPOS, TRUE, (LPARAM) (m_nPoints - 1) / 2);
  646.  
  647.     m_TBScroll = m_nPoints - 1;
  648.  
  649.     TCHAR szFormat[80];
  650.  
  651.     switch (m_nBitsPerSample + m_nChannels) {
  652.         case 9:
  653.         // Mono, 8-bit
  654.         lstrcpy (szFormat, TEXT ("M-8-"));
  655.         break;
  656.  
  657.         case 10:
  658.         // Stereo, 8-bit
  659.         lstrcpy (szFormat, TEXT ("S-8-"));
  660.         break;
  661.  
  662.         case 17:
  663.         // Mono, 16-bit
  664.         lstrcpy (szFormat, TEXT ("M-16-"));
  665.         break;
  666.  
  667.         case 18:
  668.         // Stereo, 16-bit
  669.         lstrcpy (szFormat, TEXT ("S-16-"));
  670.         break;
  671.  
  672.         default:
  673.         lstrcpy (szFormat, TEXT (" "));
  674.         SetDlgItemText (hDlg, IDC_FORMAT, szFormat);
  675.         return;
  676.         break;
  677.  
  678.     } // End of format switch
  679.  
  680.     TCHAR szSamplingFreq[80];
  681.  
  682.     wsprintf (szSamplingFreq, "%d", m_nSamplesPerSec);
  683.     lstrcat (szFormat, szSamplingFreq);
  684.  
  685.     SetDlgItemText (hDlg, IDC_FORMAT, szFormat);
  686. }
  687.  
  688.  
  689. void CScopeWindow::ProcessHorizScrollCommands(HWND hDlg, WPARAM wParam, LPARAM lParam)
  690. {
  691.     int pos;
  692.     int command = LOWORD (wParam);
  693.  
  694.     if (command != TB_ENDTRACK &&
  695.         command != TB_THUMBTRACK &&
  696.         command != TB_LINEDOWN &&
  697.         command != TB_LINEUP &&
  698.         command != TB_PAGEUP &&
  699.         command != TB_PAGEDOWN)
  700.         return;
  701.  
  702.     ASSERT (IsWindow ((HWND) lParam));
  703.  
  704.     pos = (int) SendMessage((HWND) lParam, TBM_GETPOS, 0, 0L);
  705.  
  706.     if ((HWND) lParam == m_hwndTBScroll) {
  707.         m_TBScroll = ((m_nPoints - 1) / 2 - pos) * 2;
  708.     }
  709.  
  710.     OnPaint();
  711. }
  712.  
  713. void CScopeWindow::ProcessVertScrollCommands(HWND hDlg, WPARAM wParam, LPARAM lParam)
  714. {
  715.     int pos;
  716.     int command = LOWORD (wParam);
  717.  
  718.     if (command != TB_ENDTRACK &&
  719.         command != TB_THUMBTRACK &&
  720.         command != TB_LINEDOWN &&
  721.         command != TB_LINEUP &&
  722.         command != TB_PAGEUP &&
  723.         command != TB_PAGEDOWN)
  724.         return;
  725.  
  726.     ASSERT (IsWindow ((HWND) lParam));
  727.  
  728.     pos = (int) SendMessage((HWND) lParam, TBM_GETPOS, 0, 0L);
  729.  
  730.     if ((HWND) lParam == m_hwndLGain) {
  731.         m_LGain = pos;
  732.         SetDlgItemText (hDlg, IDC_L_GAIN_TEXT, GainEntries[m_LGain].GainText);
  733.     }
  734.     else if ((HWND) lParam == m_hwndLOffset) {
  735.         m_LOffset = pos - m_Height / 2;
  736.         SetDlgItemInt (hDlg, IDC_L_OFFSET_TEXT, -m_LOffset, TRUE);
  737.     }
  738.     else if ((HWND) lParam == m_hwndRGain) {
  739.         m_RGain = pos;
  740.         SetDlgItemText (hDlg, IDC_R_GAIN_TEXT, GainEntries[m_RGain].GainText);
  741.     }
  742.     else if ((HWND) lParam == m_hwndROffset) {
  743.         m_ROffset = pos - m_Height / 2;
  744.         SetDlgItemInt (hDlg, IDC_R_OFFSET_TEXT, -m_ROffset, TRUE);
  745.     }
  746.     else if ((HWND) lParam == m_hwndTimebase) {
  747.         m_nTimebase = pos ;
  748.         SetDlgItemText (hDlg, IDC_TIMEBASE_TEXT, Timebases[m_nTimebase].TBText);
  749.     }
  750.  
  751.     OnPaint();
  752. }
  753.  
  754. /* This is called by the worker window thread after it has created the main
  755.    window and it wants to initialise the rest of the owner objects window
  756.    variables such as the device contexts. We execute this function with the
  757.    critical section still locked. */
  758.  
  759. HRESULT CScopeWindow::InitialiseWindow(HWND hDlg)
  760. {
  761.     RECT rR;
  762.  
  763.     /* Initialise the window variables */
  764.     m_hwnd = GetDlgItem (hDlg, IDC_SCOPEWINDOW);
  765.  
  766.     /* Quick sanity check */
  767.     ASSERT(m_hwnd != NULL);
  768.  
  769.     m_nTimebase = TIMEBASE_DEFAULT_INDEX;
  770.     m_fTriggerPosZeroCrossing = 1;
  771.     m_fFreeze = 0;
  772.  
  773.     m_LGain = GAIN_DEFAULT_INDEX;
  774.     m_RGain = GAIN_DEFAULT_INDEX;
  775.     m_LOffset = 0;
  776.     m_ROffset = 0;
  777.  
  778.     m_TBScroll = 0;
  779.  
  780.     GetWindowRect (m_hwnd, &rR);
  781.     m_Width = rR.right - rR.left;
  782.     m_Height = rR.bottom - rR.top;
  783.  
  784.     m_hwndLGain =       GetDlgItem (hDlg, IDC_L_GAIN);
  785.     m_hwndLOffset =     GetDlgItem (hDlg, IDC_L_OFFSET);
  786.     m_hwndLGainText =   GetDlgItem (hDlg, IDC_L_GAIN_TEXT);
  787.     m_hwndLTitle =      GetDlgItem (hDlg, IDC_L_TITLE);
  788.  
  789.     m_hwndRGain =       GetDlgItem (hDlg, IDC_R_GAIN);
  790.     m_hwndROffset =     GetDlgItem (hDlg, IDC_R_OFFSET);
  791.     m_hwndRGainText =   GetDlgItem (hDlg, IDC_R_GAIN_TEXT);
  792.     m_hwndRTitle =      GetDlgItem (hDlg, IDC_R_TITLE);
  793.  
  794.     m_hwndTimebase =    GetDlgItem (hDlg, IDC_TIMEBASE);
  795.     m_hwndFreeze =      GetDlgItem (hDlg, IDC_FREEZE);
  796.     m_hwndTBStart =     GetDlgItem (hDlg, IDC_TS_START);
  797.     m_hwndTBEnd   =     GetDlgItem (hDlg, IDC_TS_LAST);
  798.     m_hwndTBDelta =     GetDlgItem (hDlg, IDC_TS_DELTA);
  799.     m_hwndTBScroll =    GetDlgItem (hDlg, IDC_TB_SCROLL);
  800.  
  801.     SetControlRanges(hDlg);
  802.     SetHorizScrollRange(hDlg);
  803.  
  804.     CheckDlgButton(
  805.             hDlg,            // handle of dialog box
  806.             IDC_FREEZE,        // button-control identifier
  807.             m_fFreeze);        // check state
  808.  
  809.      CheckDlgButton(
  810.             hDlg,            // handle of dialog box
  811.             IDC_TRIGGER,    // button-control identifier
  812.             m_fTriggerPosZeroCrossing);    // check state
  813.  
  814.     m_hPen1 = CreatePen (PS_SOLID, 0, RGB (0, 0xff, 0));
  815.     m_hPen2 = CreatePen (PS_SOLID, 0, RGB (0x40, 0x40, 0xff));
  816.     m_hPenTicks = CreatePen (PS_SOLID, 0, RGB (0x80, 0x80, 0x80));
  817.     m_hBrushBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
  818.  
  819.     HDC hdc = GetDC (NULL);
  820.     m_hBitmap = CreateCompatibleBitmap (hdc, m_Width, m_Height);
  821.     ReleaseDC (NULL, hdc);
  822.  
  823.     return NOERROR;
  824. }
  825.  
  826. /* This is called by the worker window thread when it receives a WM_GOODBYE
  827.    message from the window object destructor to delete all the resources we
  828.    allocated during initialisation.*/
  829. HRESULT CScopeWindow::UninitialiseWindow()
  830. {
  831.     /* Reset the window variables */
  832.     DeleteObject (m_hPen1);
  833.     DeleteObject (m_hPen2);
  834.     DeleteObject (m_hPenTicks);
  835.     DeleteObject (m_hBitmap);
  836.  
  837.     m_hwnd = NULL;
  838.  
  839.     return NOERROR;
  840. }
  841.  
  842. // The Scope window is actually a dialog box, and this is its window proc.
  843. // The only thing tricky about this is that the "this" pointer to the CScopeWindow is passed during the
  844. // WM_INITDIALOG message and is stored in the window user data.  This lets us access methods in the class
  845. // from within the dialog.
  846. BOOL CALLBACK ScopeDlgProc(
  847.     HWND  hDlg,            // handle of dialog box
  848.     UINT  uMsg,            // message
  849.     WPARAM  wParam,    // first message parameter
  850.     LPARAM  lParam     // second message parameter
  851.    )
  852. {
  853.     CScopeWindow *pScopeWindow;      // Pointer to the owning object
  854.  
  855.     /* Get the window long that holds our owner pointer */
  856.     pScopeWindow = (CScopeWindow *) GetWindowLong(hDlg, GWL_USERDATA);
  857.  
  858.     switch (uMsg) {
  859.         case WM_INITDIALOG:
  860.             pScopeWindow = (CScopeWindow *) lParam;
  861.             SetWindowLong(hDlg, (DWORD) GWL_USERDATA, (LONG) pScopeWindow);
  862.             return TRUE;
  863.  
  864.         case WM_COMMAND:
  865.             switch (wParam) {
  866.                 case IDOK:
  867.                 case IDCANCEL:
  868.                     EndDialog (hDlg, 0);
  869.                     return TRUE;
  870.  
  871.                 case IDC_FREEZE:
  872.                     pScopeWindow->m_fFreeze =
  873.                         (BOOL) IsDlgButtonChecked(
  874.                             hDlg,    // handle of dialog box
  875.                             IDC_FREEZE);    // button identifier
  876.                     pScopeWindow->DrawWaveform();
  877.                     break;
  878.  
  879.                 case IDC_TRIGGER:
  880.                     pScopeWindow->m_fTriggerPosZeroCrossing =
  881.                         (BOOL) IsDlgButtonChecked(
  882.                             hDlg,    // handle of dialog box
  883.                             IDC_TRIGGER);    // button identifier
  884.                     pScopeWindow->DrawWaveform();
  885.                     break;
  886.  
  887.                 default:
  888.                     break;
  889.             }
  890.  
  891.         case WM_VSCROLL:
  892.             pScopeWindow->ProcessVertScrollCommands(hDlg, wParam, lParam);
  893.             break;
  894.  
  895.         case WM_HSCROLL:
  896.             pScopeWindow->ProcessHorizScrollCommands(hDlg, wParam, lParam);
  897.             break;
  898.  
  899.         case WM_SIZE:
  900.             if (pScopeWindow) {
  901.                 pScopeWindow->OnSize(LOWORD(lParam), HIWORD(lParam));
  902.             }
  903.             return FALSE;
  904.  
  905.         case WM_PAINT:
  906.             ASSERT(pScopeWindow != NULL);
  907.             pScopeWindow->OnPaint();
  908.             //ValidateRect (pScopeWindow->m_hwnd, NULL);
  909.             break;
  910.  
  911.         /* We stop WM_CLOSE messages going any further by intercepting them
  912.            and then setting an abort signal flag in the owning renderer so
  913.            that it knows the user wants to quit. The renderer can then
  914.            go about deleting it's interfaces and the window helper object
  915.            which will eventually cause a WM_DESTROY message to arrive. To
  916.            make it look as though the window has been immediately closed
  917.            we hide it and then wait for the renderer to catch us up */
  918.  
  919.         case WM_CLOSE:
  920.             ASSERT(pScopeWindow != NULL);
  921.             pScopeWindow->OnClose();
  922.             return FALSE;
  923.  
  924.         /* We receive a WM_GOODBYE window message (synchronously) from the
  925.            window object destructor in which case we do actually destroy
  926.            the window and complete the process in the WM_DESTROY message */
  927.  
  928.         case WM_GOODBYE:
  929.             ASSERT(pScopeWindow != NULL);
  930.             pScopeWindow->UninitialiseWindow();
  931.             PostQuitMessage(FALSE);
  932.             EndDialog (hDlg, 0);
  933.             return FALSE;
  934. #if 0
  935.         case WM_DESTROY:
  936.             ASSERT(pScopeWindow != NULL);
  937.             PostQuitMessage(FALSE);
  938.             return FALSE;
  939. #endif
  940.  
  941.         default:
  942.             break;
  943.     }
  944.     return FALSE;
  945. };
  946.  
  947. /* This is the standard windows message loop for our worker thread. It sits
  948.    in a normal processing loop dispatching messages until it receives a quit
  949.    message, which may be generated through the owning object's destructor */
  950.  
  951. HRESULT CScopeWindow::MessageLoop()
  952. {
  953.     MSG Message;        // Windows message structure
  954.     DWORD dwResult;     // Wait return code value
  955.  
  956.     HANDLE hWait[] = { (HANDLE) m_RenderEvent };
  957.  
  958.     /* Enter the modified message loop */
  959.  
  960.     while (TRUE) {
  961.  
  962.         /* We use this to wait for two different kinds of events, the first
  963.            are the normal windows messages, the other is an event that will
  964.            be signaled when a sample is ready */
  965.  
  966.         dwResult = MsgWaitForMultipleObjects((DWORD) 1,     // Number events
  967.                                              hWait,         // Event handle
  968.                                              FALSE,         // Wait for either
  969.                                              INFINITE,      // No timeout
  970.                                              QS_ALLINPUT);  // All messages
  971.  
  972.         /* Has a sample become ready to render */
  973.         if (dwResult == WAIT_OBJECT_0) {
  974.             DrawWaveform();
  975.         }
  976.  
  977.         /* Process the thread's window message */
  978.  
  979.         while (PeekMessage(&Message,NULL,(UINT) 0,(UINT) 0,PM_REMOVE)) {
  980.  
  981.             /* Check for the WM_QUIT message */
  982.  
  983.             if (Message.message == WM_QUIT) {
  984.                 return NOERROR;
  985.             }
  986.  
  987.             /* Send the message to the window procedure */
  988.  
  989.             TranslateMessage(&Message);
  990.             DispatchMessage(&Message);
  991.         }
  992.     }
  993. }
  994.  
  995. /* This creates a window and processes it's messages on a separate thread. */
  996. DWORD __stdcall CScopeWindow::WindowMessageLoop(LPVOID lpvThreadParm)
  997. {
  998.     CScopeWindow *pScopeWindow;     // The owner renderer object
  999.  
  1000.     /* Cast the thread parameter to be our owner object */
  1001.     pScopeWindow = (CScopeWindow *) lpvThreadParm;
  1002.  
  1003.     pScopeWindow->m_hwndDlg =
  1004.         CreateDialogParam(
  1005.             pScopeWindow->m_hInstance,        // handle of application instance
  1006.             MAKEINTRESOURCE (IDD_SCOPEDIALOG),    // dialog box template
  1007.             NULL,                        // handle of owner window
  1008.             (DLGPROC) ScopeDlgProc,        // address of dialog box procedure
  1009.             (LONG) pScopeWindow             // initialization value
  1010.         );
  1011.  
  1012.     if (pScopeWindow->m_hwndDlg != NULL)
  1013.     {
  1014.  
  1015.         /* Initialise the window, then signal the constructor that it can
  1016.            continue and then unlock the object's critical section and
  1017.            process messages */
  1018.  
  1019.         pScopeWindow->InitialiseWindow(pScopeWindow->m_hwndDlg);
  1020.     }
  1021.  
  1022.         pScopeWindow->m_SyncWorker.Set();
  1023.  
  1024.     if (pScopeWindow->m_hwndDlg != NULL)
  1025.     {
  1026.         pScopeWindow->MessageLoop();
  1027.     }
  1028.  
  1029.     ExitThread(TRUE);
  1030.  
  1031.     return TRUE;
  1032. }
  1033.  
  1034. // WM_PAINT message
  1035. BOOL CScopeWindow::OnPaint()
  1036. {
  1037.     DrawWaveform();
  1038.  
  1039.     return TRUE;
  1040. }
  1041.  
  1042.  
  1043. // Clear the scope to black and draw the center tickmarks
  1044. void CScopeWindow::ClearWindow(HDC hdc)
  1045. {
  1046.     int y = m_Height / 2;
  1047.  
  1048.     SetMapMode (hdc, MM_TEXT);
  1049.     SetWindowOrgEx (hdc, 0, 0, NULL);
  1050.     SetViewportOrgEx (hdc, 0, 0, NULL);
  1051.     
  1052.     // Paint the entire window black
  1053.     PatBlt(
  1054.         hdc,    // handle of device context
  1055.         0,      // x-coord. of upper-left corner of rect. to be filled
  1056.         0,      // y-coord. of upper-left corner of rect. to be filled
  1057.         m_Width,    // width of rectangle to be filled
  1058.         m_Height,   // height of rectangle to be filled
  1059.         BLACKNESS   // raster operation code
  1060.         );
  1061.  
  1062.     // Draw the horizontal line
  1063.     HPEN hPenOld = (HPEN) SelectObject (hdc, m_hPenTicks);
  1064.     MoveToEx (hdc, 0, y, NULL);
  1065.     LineTo (hdc, m_Width, y);
  1066.  
  1067.     // Draw the tickmarks
  1068.     float inc = (float) m_Width / 10;
  1069.     int pos, j;
  1070.     int TickPoint;
  1071.     for (j = 0; j <= 10; j++) {
  1072.         if (j == 0 || j == 5 || j == 10)
  1073.             TickPoint =  m_Height / 15;
  1074.         else
  1075.             TickPoint = m_Height / 30;
  1076.         pos = (int) (j * inc);
  1077.         MoveToEx (hdc, pos, y + TickPoint, NULL);
  1078.         LineTo (hdc, pos, y - TickPoint);
  1079.     }
  1080.     SelectObject (hdc, hPenOld);
  1081. }
  1082.  
  1083.  
  1084. // Draw a part of the Oscilloscope waveform
  1085. // IndexStart and IndexEnd are pointers into the m_pPoints array (LOGICAL COORDS)
  1086. // ViewpointStart and ViewpointEnd are in SCREEN COORDS
  1087.  
  1088. void CScopeWindow::DrawPartialWaveform(HDC hdc,
  1089.     int IndexStart, int IndexEnd,
  1090.     int ViewportStart, int ViewportEnd)
  1091. {
  1092.     int nPoints = IndexEnd - IndexStart;
  1093.     int nViewportWidth = ViewportEnd - ViewportStart;
  1094.  
  1095.     ASSERT (IndexStart + nPoints < m_nPoints);
  1096.  
  1097.     // origin at lower left, x increases up, y increases to right
  1098.     SetMapMode (hdc, MM_ANISOTROPIC);
  1099.  
  1100.     SetWindowOrgEx (hdc, IndexStart, 0, NULL);
  1101.     SetWindowExtEx (hdc, nPoints, (int) (m_MaxValue / GainEntries[m_LGain].GainValue), NULL);
  1102.     SetViewportExtEx (hdc, nViewportWidth, -m_Height / 2, NULL);
  1103.     SetViewportOrgEx (hdc, ViewportStart, m_LOffset + m_Height / 2, NULL);
  1104.  
  1105.     HPEN OldPen = (HPEN) SelectObject (hdc, m_hPen1);
  1106.     Polyline (hdc, m_pPoints1 + IndexStart, nPoints + 1);
  1107.     SelectObject (hdc, OldPen);
  1108.  
  1109.     if (m_pPoints2) {
  1110.         SetWindowOrgEx (hdc, IndexStart, 0, NULL);
  1111.         SetWindowExtEx (hdc, nPoints, (int) (m_MaxValue / GainEntries[m_RGain].GainValue), NULL);
  1112.         SetViewportExtEx (hdc, nViewportWidth, -m_Height / 2, NULL);
  1113.         SetViewportOrgEx (hdc, ViewportStart, m_ROffset + m_Height / 2, NULL);
  1114.  
  1115.         HPEN OldPen = (HPEN) SelectObject (hdc, m_hPen2);
  1116.         Polyline (hdc, m_pPoints2 + IndexStart, nPoints + 1);
  1117.         SelectObject (hdc, OldPen);
  1118.     }
  1119. }
  1120.  
  1121. // Draw the full Oscilloscope waveform
  1122. void CScopeWindow::DrawWaveform(void)
  1123. {
  1124.     CAutoLock lock(m_pRenderer);
  1125.     TCHAR szT[40];
  1126.  
  1127.     if (m_pPoints1 == NULL)
  1128.         return;
  1129.  
  1130.     HDC hdc = GetWindowDC (m_hwnd);  // WindowDC has clipping region
  1131.     HDC hdcT = CreateCompatibleDC (hdc);
  1132.     HBITMAP hBitmapOld = (HBITMAP) SelectObject (hdcT, m_hBitmap);
  1133.  
  1134.     ClearWindow (hdcT);
  1135.  
  1136.     int StartOffset;
  1137.     int IndexEdge;
  1138.     int IndexStart1, IndexEnd1;
  1139.     int IndexStart2, IndexEnd2;
  1140.     int PointsToDisplay, PointsToDisplay1, PointsToDisplay2;
  1141.     int ViewportBreak;
  1142.     int OffsetTimeMS;
  1143.     BOOL fWraps;                // if segment to display wraps around 0
  1144.  
  1145.     PointsToDisplay = m_nPoints / Timebases [m_nTimebase].TBDivisor;
  1146.  
  1147.     StartOffset = (m_nIndex - 1) - m_TBScroll;
  1148.     if (StartOffset < 0)
  1149.         StartOffset += m_nPoints;
  1150.  
  1151.     if (m_fTriggerPosZeroCrossing) {
  1152.         SearchForPosZeroCrossing(StartOffset, &IndexEdge);
  1153.         IndexEnd2 = IndexEdge;
  1154.     }
  1155.     else {
  1156.         IndexEnd2 = StartOffset;
  1157.     }
  1158.     IndexStart2 = IndexEnd2 - PointsToDisplay;    // can be negative
  1159.  
  1160.     if (IndexEnd2 > m_nIndex)
  1161.         OffsetTimeMS = (m_nIndex + (m_nPoints - IndexEnd2)) * 1000 / m_nSamplesPerSec;
  1162.     else
  1163.         OffsetTimeMS = (m_nIndex - IndexEnd2) * 1000 / m_nSamplesPerSec;
  1164.  
  1165.     if (fWraps = (IndexStart2 < 0)) {
  1166.         IndexStart1 = IndexStart2 + m_nPoints;
  1167.         IndexEnd1 = m_nPoints - 1;
  1168.         IndexStart2 = 0;
  1169.  
  1170.         PointsToDisplay1 = IndexEnd1 - IndexStart1;
  1171.     }
  1172.  
  1173.     PointsToDisplay2 = IndexEnd2 - IndexStart2;
  1174.  
  1175.     if (fWraps) {
  1176.         ViewportBreak = (int) (m_Width * (float) PointsToDisplay1 / PointsToDisplay);
  1177.  
  1178.         // Draw the first section (from the end of the POINT array)
  1179.         DrawPartialWaveform(hdcT,
  1180.             IndexStart1, IndexEnd1,     // Index start, Index end
  1181.             0, ViewportBreak);          // Window start, Window end
  1182.  
  1183.         // Draw the second section (from the beginning of the POINT array)
  1184.         DrawPartialWaveform(hdcT,
  1185.             IndexStart2, IndexEnd2,     // Index start, Index end
  1186.             ViewportBreak, m_Width);    // Window start, Window end
  1187.     }
  1188.     else {
  1189.         DrawPartialWaveform(hdcT,
  1190.             IndexStart2, IndexEnd2,     // Index start, Index end
  1191.             0, m_Width);                // Window start, Window end
  1192.     }
  1193.  
  1194.     SetMapMode (hdcT, MM_TEXT);
  1195.     SetWindowOrgEx (hdcT, 0, 0, NULL);
  1196.     SetViewportOrgEx (hdcT, 0, 0, NULL);
  1197.  
  1198.     BitBlt(hdc,    // handle of destination device context
  1199.             0,    // x-coordinate of destination rectangle's upper-left corner
  1200.             0,     // y-coordinate of destination rectangle's upper-left corner
  1201.             m_Width,    // width of destination rectangle
  1202.             m_Height,    // height of destination rectangle
  1203.             hdcT,    // handle of source device context
  1204.             0,      // x-coordinate of source rectangle's upper-left corner
  1205.             0,      // y-coordinate of source rectangle's upper-left corner
  1206.             SRCCOPY    );     // raster operation code
  1207.  
  1208.     SelectObject (hdcT, hBitmapOld);
  1209.     DeleteDC (hdcT);
  1210.  
  1211.     GdiFlush();
  1212.  
  1213.     ReleaseDC (m_hwnd, hdc);
  1214.  
  1215.  
  1216.     // Show the size of the last buffer received
  1217.     wsprintf (szT, "%d", m_LastMediaSampleSize);
  1218.     SetDlgItemText (m_hwndDlg, IDC_BUFSIZE, szT);
  1219.  
  1220.     // Show the timestamps
  1221.     LONG mSStart;
  1222.     LONG mSEnd = m_EndSample.Millisecs();
  1223.  
  1224.     CRefTime rt;
  1225.     m_pRenderer->StreamTime (rt);
  1226.  
  1227.     // Delta is the difference between the last sample received and
  1228.     // the current sample playing according to the StreamTime
  1229.     LONG mSDelta = mSEnd - rt.Millisecs();
  1230.     wsprintf (szT, "%d.%d", mSDelta / 1000, abs (mSDelta) % 1000);
  1231.     SetDlgItemText (m_hwndDlg, IDC_TS_DELTA, szT);
  1232.  
  1233.     // Show the Delta point on the horizontal trackbar as the selection
  1234.     if (mSDelta < 1000) {
  1235.         int SelectStart = m_nPoints - (m_nPoints * mSDelta / 1000);
  1236.         SelectStart /= 2;
  1237.         int SelectEnd = SelectStart + m_nPoints / 100;
  1238.         SendMessage(m_hwndTBScroll, TBM_SETSEL, TRUE, MAKELONG (SelectStart, SelectEnd));
  1239.     }
  1240.     else
  1241.         SendMessage(m_hwndTBScroll, TBM_SETSEL, TRUE, 0L); // hide the selection
  1242.  
  1243.  
  1244.     // Display the begin and end times of the sweep
  1245.     mSEnd -= OffsetTimeMS;
  1246.     mSStart = mSEnd - PointsToDisplay * 1000 / m_nSamplesPerSec;
  1247.  
  1248.     wsprintf (szT, "%d.%d", mSStart / 1000, abs (mSStart) % 1000);
  1249.     SetDlgItemText (m_hwndDlg, IDC_TS_START, szT);
  1250.  
  1251.     wsprintf (szT, "%d.%d", mSEnd / 1000, abs (mSEnd) % 1000);
  1252.     SetDlgItemText (m_hwndDlg, IDC_TS_LAST, szT);
  1253.  
  1254.  
  1255. }
  1256.  
  1257. // Allocate a 1 second buffer for each channel
  1258. // This is only called when the format changes
  1259. // Return TRUE if allocations succeed
  1260. BOOL CScopeWindow::AllocWaveBuffers(void)
  1261. {
  1262.     int j;
  1263.  
  1264.     if (m_pPoints1)
  1265.         delete [] m_pPoints1;
  1266.     if (m_pPoints2)
  1267.         delete [] m_pPoints2;
  1268.     m_pPoints1 = NULL;
  1269.     m_pPoints2 = NULL;
  1270.     m_nPoints = 0;
  1271.  
  1272.     m_nPoints = m_nSamplesPerSec;
  1273.  
  1274.     if (m_pPoints1 = new POINT [m_nSamplesPerSec]) {
  1275.         m_nPoints = m_nSamplesPerSec;
  1276.         for (j = 0; j < m_nSamplesPerSec; j++)
  1277.             m_pPoints1[j].x = j;
  1278.     }
  1279.  
  1280.     if (m_nChannels == 2) {
  1281.         if (m_pPoints2 = new POINT [m_nSamplesPerSec])
  1282.             for (j = 0; j < m_nSamplesPerSec; j++)
  1283.                 m_pPoints2[j].x = j;
  1284.     }
  1285.  
  1286.     // return TRUE if allocations succeeded
  1287.     ASSERT ((m_pPoints1 != NULL) && ((m_nChannels == 2) ? (m_pPoints2 != NULL) : TRUE));
  1288.     return ((m_pPoints1 != NULL) && ((m_nChannels == 2) ? (m_pPoints2 != NULL) : TRUE));
  1289. }
  1290.  
  1291. // Searches backward for a positive going zero crossing in the waveform
  1292. void CScopeWindow::SearchForPosZeroCrossing(int StartPoint, int * IndexEdge)
  1293. {
  1294.     if (StartPoint < 0)
  1295.         StartPoint = 0;
  1296.  
  1297.     int cur, last, j;
  1298.  
  1299.     *IndexEdge = StartPoint;
  1300.  
  1301.     last = m_pPoints1[StartPoint].y;
  1302.  
  1303.     for (j = m_nPoints; j > 0; j--) {
  1304.         if (--StartPoint < 0)
  1305.             StartPoint = m_nPoints - 1;
  1306.         cur = m_pPoints1[StartPoint].y;
  1307.         if (cur < 0 && last >= 0) {
  1308.             *IndexEdge = StartPoint;
  1309.             break;
  1310.         }
  1311.         last = cur;
  1312.     }
  1313. }
  1314.  
  1315.  
  1316. // Copy the current MediaSample into a POINT array so we can use GDI
  1317. // to paint the waveform.  The POINT array contains a 1 second history
  1318. // of the past waveform.  The "Y" values are normalized to a range of
  1319. // +128 to -127 within the POINT array.
  1320. void CScopeWindow::CopyWaveform(IMediaSample *pMediaSample)
  1321. {
  1322.     BYTE *pWave;                // Pointer to image data
  1323.     int  nBytes;
  1324.     int  nSamplesPerChan;
  1325.  
  1326.     pMediaSample->GetPointer(&pWave);
  1327.     ASSERT(pWave != NULL);
  1328.  
  1329.     nBytes = pMediaSample->GetActualDataLength();
  1330.     nSamplesPerChan = nBytes / (m_nChannels * m_nBlockAlign);
  1331.  
  1332.     switch (m_nBitsPerSample + m_nChannels) {
  1333.         BYTE * pb;
  1334.         WORD * pw;
  1335.  
  1336.         case 9:
  1337.         {   // Mono, 8-bit
  1338.             pb = pWave;
  1339.             while (nSamplesPerChan--) {
  1340.                 m_pPoints1[m_nIndex].y = (int)*pb++ - 127;  // Make zero centered
  1341.                 if (++m_nIndex == m_nSamplesPerSec)
  1342.                     m_nIndex = 0;
  1343.             }
  1344.         }
  1345.         break;
  1346.  
  1347.         case 10:
  1348.         {   // Stereo, 8-bit
  1349.             pb = pWave;
  1350.             while (nSamplesPerChan--) {
  1351.                 m_pPoints1[m_nIndex].y = (int)*pb++ - 127; // Make zero centered
  1352.                 m_pPoints2[m_nIndex].y = (int)*pb++ - 127;
  1353.                 if (++m_nIndex == m_nSamplesPerSec)
  1354.                     m_nIndex = 0;
  1355.             }
  1356.         }
  1357.         break;
  1358.  
  1359.         case 17:
  1360.         { // Mono, 16-bit
  1361.             pw = (WORD *) pWave;
  1362.             while (nSamplesPerChan--) {
  1363.                 m_pPoints1[m_nIndex].y = (int) ((short) *pw++) / 256;
  1364.                 if (++m_nIndex == m_nSamplesPerSec)
  1365.                     m_nIndex = 0;
  1366.             }
  1367.         }
  1368.         break;
  1369.  
  1370.         case 18:
  1371.         { // Stereo, 16-bit
  1372.             pw = (WORD *)pWave;
  1373.             while (nSamplesPerChan--) {
  1374.                 m_pPoints1[m_nIndex].y = (int) ((short) *pw++) / 256;
  1375.                 m_pPoints2[m_nIndex].y = (int) ((short) *pw++) / 256;
  1376.                 if (++m_nIndex == m_nSamplesPerSec)
  1377.                     m_nIndex = 0;
  1378.             }
  1379.         }
  1380.         break;
  1381.  
  1382.         default:
  1383.             ASSERT(0);
  1384.             break;
  1385.  
  1386.     } // End of format switch
  1387. }
  1388.  
  1389.  
  1390. // Called when the input pin receives another sample.
  1391. // Copy the waveform to our circular 1 second buffer
  1392. HRESULT CScopeWindow::Receive(IMediaSample *pSample)
  1393. {
  1394.     CAutoLock cAutoLock(this);
  1395.  
  1396.     ASSERT(pSample != NULL);
  1397.  
  1398.     /* Check we have been activated */
  1399.  
  1400.     if (m_bActivated == FALSE) {
  1401.         return E_FAIL;
  1402.     }
  1403.  
  1404.     if (m_fFreeze) {
  1405.         return NOERROR;
  1406.     }
  1407.  
  1408.     REFERENCE_TIME tStart, tStop;
  1409.     pSample->GetTime (&tStart,&tStop);
  1410.     m_StartSample = tStart;
  1411.     m_EndSample = tStop;
  1412.  
  1413.     // !!! Ignore zero-length samples???
  1414.     if ((m_LastMediaSampleSize = pSample->GetActualDataLength()) == 0)
  1415.         return NOERROR;
  1416.  
  1417.     if (m_bStreaming == TRUE) {
  1418.         CopyWaveform (pSample);     // Copy data to our circular buffer
  1419.         SetEvent(m_RenderEvent);    // Set an event to display the new data on another thread
  1420.         return NOERROR;
  1421.     }
  1422.  
  1423.     return NOERROR;
  1424. }
  1425.  
  1426. /******************************Public*Routine******************************\
  1427. * exported entry points for registration and
  1428. * unregistration (in this case they only call
  1429. * through to default implmentations).
  1430. *
  1431. *
  1432. *
  1433. * History:
  1434. *
  1435. \**************************************************************************/
  1436. HRESULT
  1437. DllRegisterServer()
  1438. {
  1439.   return AMovieDllRegisterServer();
  1440. }
  1441.  
  1442. HRESULT
  1443. DllUnregisterServer()
  1444. {
  1445.   return AMovieDllUnregisterServer();
  1446. }
  1447.  
  1448.