 
  //=========================================================================
  //
  //  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  //  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  //  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  //  PURPOSE.
  //
  // Copyright (c) 1996 - 1997  Microsoft Corporation.  All Rights Reserved.
  //
  //=========================================================================

  #include <windows.h>
  #include <streams.h>
  #include <strmif.h>
  #include <cutlist.h>
  #include "simplecl.h"

  ICutListGraphBuilder  *pCLGraphBuilder  = NULL;
  IStandardCutList      *pVideoCL         = NULL;
  IStandardCutList      *pAudioCL         = NULL;
  IGraphBuilder         *pigb             = NULL;
  IMediaControl         *pimc             = NULL;
  IMediaEventEx         *pimex            = NULL;
  IVideoWindow          *pivw             = NULL;

  IFileClip             *pVidFileClip[MAX_CLIPS];
  IFileClip             *pAudFileClip[MAX_CLIPS];
  IAMCutListElement       *pVidCLElem[MAX_CLIPS];
  IAMCutListElement       *pAudCLElem[MAX_CLIPS];

  HRESULT hr;

  int nVidElems, nAudElems;
 
  void SimpleCutList ()

    { // SimpleCutList //

      WCHAR wFile[MAX_PATH];  // file name

      // Initialize video and audio file clips and elements to NULL
      // so we can easily free objects later.
      for (int x = 0; x < MAX_CLIPS; ++x)

        { 
          pVidFileClip[x] = NULL; 
          pAudFileClip[x] = NULL; 
            pVidCLElem[x] = NULL; 
            pAudCLElem[x] = NULL; 
        };

      // Create cutlist graph builder object
      hr = CoCreateInstance(CLSID_CutListGraphBuilder, NULL, 
                            CLSCTX_INPROC, IID_ICutListGraphBuilder, 
                            (void**)&pCLGraphBuilder);

      if (FAILED(hr))
        { // CoCreateInstance of CutListGraphBuiler failed
          MessageBox(ghApp, 
                     "CoCreateInstance of CutListGraphBuiler failed",
                     APPLICATIONNAME, MB_OK);
          TearDownTheGraph();
          return;
        } // CoCreateInstance of CutListGraphBuiler failed

      // Create simple (standard) cutlist object for video
      hr = CoCreateInstance(CLSID_SimpleCutList, NULL, 
                            CLSCTX_INPROC, IID_IStandardCutList, 
                            (void**)&pVideoCL);
      if (FAILED(hr))
        { // CoCreateInstance of video SimpleCutList failed
          MessageBox(ghApp, 
                     "CoCreateInstance of video SimpleCutList failed",
                     APPLICATIONNAME, MB_OK);
          TearDownTheGraph();
          return;
        } // CoCreateInstance of video SimpleCutList failed

      // Create simple (standard) cutlist object for audio
      hr = CoCreateInstance(CLSID_SimpleCutList, NULL, 
                            CLSCTX_INPROC, IID_IStandardCutList, 
                            (void**)&pAudioCL);

      if (FAILED(hr))
        { // CoCreateInstance of audio SimpleCutList failed
          MessageBox(ghApp, 
                     "CoCreateInstance of audio SimpleCutList failed",
                     APPLICATIONNAME, MB_OK);
          TearDownTheGraph();
          return;
        } // CoCreateInstance of audio SimpleCutList failed

      // Create the individual clips and add them to the cutlist
      nVidElems = nAudElems = 0;
      for (x = 0; x < gTheSet.nNumClips; ++x)

        { // Individual clips

          MultiByteToWideChar(CP_ACP, 0, 
                              gTheSet.List[x].szFilename, 
                              -1, wFile, MAX_PATH );

          // Create a video clip object and give it the file and stream 
          // to read from.
          // SetFileAndStream will fail if we call it from a video clip 
          // object and the clip is not a video clip.
          hr = CoCreateInstance(CLSID_VideoFileClip, NULL, 
                                CLSCTX_INPROC, IID_IFileClip, 
                                (void**)&pVidFileClip[nVidElems]);

          hr = pVidFileClip[nVidElems]->SetFileAndStream(wFile, 0);

          if (SUCCEEDED(hr))

            { // Create video cut and add the clip (element) to the cutlist

              hr = pVidFileClip[nVidElems]->CreateCut(&pVidCLElem[nVidElems], 
                      gTheSet.List[x].start*SCALE, 
                      gTheSet.List[x].stop*SCALE, 
                      0, 
                      (gTheSet.List[x].stop-gTheSet.List[x].start)*SCALE, 
                      0);

              if (SUCCEEDED(hr))

                { // Add the element to the cutlist

                  hr = pVideoCL->AddElement(pVidCLElem[nVidElems], CL_DEFAULT_TIME, CL_DEFAULT_TIME);

                  if (SUCCEEDED(hr))
                    ++nVidElems;

                  else

                    { // AddElement failed so release associated objects

                      HELPER_RELEASE(pVidCLElem[nVidElems]);
                      HELPER_RELEASE(pVidFileClip[nVidElems]);
                      MessageBox(ghApp, "AddElement (video) failed!", APPLICATIONNAME, MB_OK);

                    } // AddElement failed so release associated objects

                } // Add the element to the cutlist

              else MessageBox(ghApp, "CreateCut (video) failed!", APPLICATIONNAME, MB_OK);

            } // Create video cut

          else

            { // Problems creating video stream

              HELPER_RELEASE(pVidFileClip[nVidElems]);
              MessageBox(ghApp, "SetFileAndStream (video) failed!", APPLICATIONNAME, MB_OK);

            } // Problems creating video stream

          // Create an audio clip object and give it the file and stream 
          // to read from.
          // SetFileAndStream will fail if we call it from an audio clip 
          // object and the clip is not an audio clip
          hr = CoCreateInstance(CLSID_AudioFileClip, NULL, 
                                CLSCTX_INPROC, IID_IFileClip, 
                                (void**)&pAudFileClip[nAudElems]);

          hr = pAudFileClip[nAudElems]->SetFileAndStream(wFile, 0);

          if (SUCCEEDED(hr))

            { // Create audio cut and add the clip (element) to the cutlist

	            hr = pAudFileClip[nAudElems]->CreateCut(&pAudCLElem[nAudElems], 
                      gTheSet.List[x].start*SCALE, 
                      gTheSet.List[x].stop*SCALE, 
                      0, 
                      (gTheSet.List[x].stop-gTheSet.List[x].start)*SCALE, 
                      0);

              if (SUCCEEDED(hr))

                { // Add the element to the cutlist

                  hr = pAudioCL->AddElement(pAudCLElem[nAudElems],
                                            CL_DEFAULT_TIME, 
                                            CL_DEFAULT_TIME);

                  if (SUCCEEDED(hr))
                    ++nAudElems;

                  else

                    { // AddElement failed so release associated objects

                      HELPER_RELEASE(pAudCLElem[nAudElems]);
                      HELPER_RELEASE(pAudFileClip[nAudElems]);
                      MessageBox(ghApp, "AddElement (audio) failed!", APPLICATIONNAME, MB_OK);

                    } // AddElement failed so release associated objects

                } // Add the element to the cutlist

              else MessageBox(ghApp, "CreateCut (audio) failed!", APPLICATIONNAME, MB_OK);

            } // Create audio cut

          else
          
            { // Problems creating audio stream

              HELPER_RELEASE(pAudFileClip[nAudElems]);
              MessageBox(ghApp, "SetFileAndStream (audio) failed!", APPLICATIONNAME, MB_OK);

            } // Problems creating audio stream

        } // Individual clips

      // Add the video cutlist to the filter graph
      hr = pCLGraphBuilder->AddCutList(pVideoCL, NULL);

      if (FAILED(hr)) // AddCutList (video) failed
          MessageBox(ghApp, "AddCutList (video) failed", APPLICATIONNAME, MB_OK);

      // Add the audio cutlist to the filter graph
      hr = pCLGraphBuilder->AddCutList(pAudioCL, NULL);

      if (FAILED(hr)) // AddCutList (audio) failed
          MessageBox(ghApp, "AddCutList (audio) failed", APPLICATIONNAME, MB_OK);

      if ((!pVideoCL) && (!pAudioCL))

        { // Clean up

          TearDownTheGraph();
          return;

        } // Clean up

      // Let the filter graph manager construct the the appropriate graph 
      // automatically
      hr = pCLGraphBuilder->Render();

      if (FAILED(hr))
        { // Problems rendering the graph
          if (!AMGetErrorText(hr, gszScratch, 2048))
            MessageBox(ghApp, "Problems rendering the graph!", APPLICATIONNAME, MB_OK);
          else 
            MessageBox(ghApp, gszScratch, APPLICATIONNAME, MB_OK);
          TearDownTheGraph();
          return;
        } // Problems rendering the graph

      // Retrieve the filter graph and useful interfaces
      hr = pCLGraphBuilder->GetFilterGraph(&pigb);

      if (FAILED(hr))
        { // Problems retrieving the graph pointer
          if (!AMGetErrorText(hr, gszScratch, 2048))
            MessageBox(ghApp, "Problems retrieving the graph pointer!", APPLICATIONNAME, MB_OK);
          else 
            MessageBox(ghApp, gszScratch, APPLICATIONNAME, MB_OK);
          TearDownTheGraph();
          return;
        } // Problems retrieving the graph pointer

      // QueryInterface for some basic interfaces
      pigb->QueryInterface(IID_IMediaControl, (void **)&pimc);
      pigb->QueryInterface(IID_IMediaEventEx, (void **)&pimex);
      pigb->QueryInterface(IID_IVideoWindow, (void **)&pivw);

      // Decrement the ref count on the filter graph
      pigb->Release();

      // Prepare to play in the main application window's client area

      RECT rc;
      GetClientRect(ghApp, &rc);
      hr = pivw->put_Owner((OAHWND)ghApp);
      hr = pivw->put_WindowStyle(WS_CHILD|WS_CLIPSIBLINGS);
      hr = pivw->SetWindowPosition(rc.left, rc.top, rc.right, rc.bottom);

      // Have the graph signal event via window callbacks for performance
      pimex->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0);

      // Run the graph if RenderFile succeeded
      pimc->Run();

    } // SimpleCutList //

 void TearDownTheGraph (void)

    { // TearDownTheGraph //

      if (pimc)
        pimc->Stop();

      if (pivw)

        { // Hide the playback window first thing

          pivw->put_Visible(OAFALSE);
          pivw->put_Owner(NULL);

        } //

      HELPER_RELEASE(pimex);
      HELPER_RELEASE(pimc);
      HELPER_RELEASE(pivw);

      // Remove the video cutlist from the filter graph to free resources
      if (pCLGraphBuilder && pVideoCL)
        pCLGraphBuilder->RemoveCutList(pVideoCL);

      // Remove the audio cutlist from the filter graph to free resources
      if (pCLGraphBuilder && pAudioCL)
        pCLGraphBuilder->RemoveCutList(pAudioCL);

      for (int x = 0; x < nAudElems; ++x)

        { // Release audio objects

          HELPER_RELEASE(pAudCLElem[x]);
          HELPER_RELEASE(pAudFileClip[x]);

        } // Release audio objects

      for (x = 0; x < nVidElems; ++x)

        { // Release video objects

          HELPER_RELEASE(pVidCLElem[x]);
          HELPER_RELEASE(pVidFileClip[x]);

        } // Release video objects

      HELPER_RELEASE(pVideoCL);
      HELPER_RELEASE(pAudioCL);      
      HELPER_RELEASE(pCLGraphBuilder);

    } // TearDownTheGraph //

  BOOL CALLBACK DialogProc (HWND h, UINT m, WPARAM w, LPARAM l)

    { // DialogProc //

      switch(m)

        { // Msg handling

          case WM_INITDIALOG:
            if (wDlgRes == IDD_MEDIATIMES)
              SetFocus(GetDlgItem(h, IDC_TRIMIN2));
            else return TRUE;
            break;

          case WM_COMMAND:

            switch(w)

              { // Command msg handling

                case IDOKTIMES:

                  gTheSet.List[gTheSet.nNumClips].start = GetDlgItemInt(h, IDC_TRIMIN2, NULL, FALSE);
                  gTheSet.List[gTheSet.nNumClips].stop = GetDlgItemInt(h, IDC_TRIMOUT2, NULL, FALSE);

                  EndDialog(h,1);
                  break;

                case IDOK:
                  EndDialog(h,1);
                  break;

                case IDCANCEL:
                  EndDialog(h,0);
                  break;

              } // Command msg handling

            break;

        } // Msg handling

      return 0;

    } // DialogProc //

  BOOL GetClipFileName (LPSTR szName)

    {   // GetClipFileName //

        OPENFILENAME ofn;

        ofn.lStructSize       = sizeof(OPENFILENAME);
        ofn.hwndOwner         = ghApp;
        ofn.lpstrFilter       = NULL;
        ofn.lpstrFilter       = "AVI/WAV (*.avi; *.wav)\0*.avi;*.wav\0\0\0";
        ofn.lpstrCustomFilter = NULL;
        ofn.nFilterIndex      = 1;
        *szName               = 0;
        ofn.lpstrFile         = szName;
        ofn.nMaxFile          = MAX_PATH;
        ofn.lpstrInitialDir   = NULL;
        ofn.lpstrTitle        = NULL;
        ofn.lpstrFileTitle    = NULL;
        ofn.lpstrDefExt       = "AVI;WAV";
        ofn.Flags             = OFN_FILEMUSTEXIST | OFN_READONLY | OFN_PATHMUSTEXIST;

        return GetOpenFileName((LPOPENFILENAME)&ofn);

    }   // GetClipFileName //

  LRESULT CALLBACK WndMainProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

    { // WndMainProc //

      LONG evCode;
      LONG evParam1;
      LONG evParam2;

      switch(message)

        { // Window msgs handling

          case WM_COMMAND:

            switch(wParam)

              {  // Program menu option

                case IDM_ADDFILE:

                  if (GetClipFileName(gTheSet.List[gTheSet.nNumClips].szFilename))

                    { // Add file

                      TCHAR szTitleBar[200];

                      DialogBox(ghInst, MAKEINTRESOURCE(wDlgRes = IDD_MEDIATIMES), ghApp, (DLGPROC)DialogProc);
                      gTheSet.nNumClips = gTheSet.nNumClips + 1;
                      wsprintf(szTitleBar, "SimpleCutList - %d clips(s) added.", gTheSet.nNumClips);
                      SetWindowText(ghApp, szTitleBar);

                    } // Add file

                  break;

                case IDM_RUN:
                  if (gTheSet.nNumClips > 1)
                    SimpleCutList();
                  else
                    DialogBox(ghInst, MAKEINTRESOURCE(wDlgRes = IDD_LESSTHAN2), ghApp, (DLGPROC)DialogProc);
                  break;

                case ID_FILE_ABOUT:
                  DialogBox(ghInst, MAKEINTRESOURCE(wDlgRes = IDD_ABOUT), ghApp, (DLGPROC)DialogProc);
                  break;

                case ID_FILE_EXIT:
                  if (gTheSet.nNumClips)
                    TearDownTheGraph();
                  PostQuitMessage(0);
                  break;

              }  // Program menu option

            break;

          case WM_GRAPHNOTIFY:

            if (!pimex)
              break;

            while (SUCCEEDED(pimex->GetEvent(&evCode, &evParam1, &evParam2, 0)))

              { // Spin through the events

                hr = pimex->FreeEventParams(evCode, evParam1, evParam2);

                if ((EC_COMPLETE == evCode) || (EC_USERABORT == evCode))

                  { // Finished

                    TearDownTheGraph();

                    if (EC_USERABORT == evCode)
                      gTheSet.nNumClips = 0;

                    break;

                  } // Finished
              
              } // Spin through the events

            break;

          case WM_DESTROY:
            PostQuitMessage(0);
            break;

          default:
            return DefWindowProc(hWnd, message, wParam, lParam);
	          
        } // Window msgs handling

      return FALSE;

      }  // WndMainProc //

  int PASCAL WinMain(HINSTANCE hInstC, HINSTANCE hInstP, LPSTR lpCmdLine, int nCmdShow)

    { // WinMain //
	  
      MSG msg;
      WNDCLASS wc;

      // OLE subsystem requires applications to initialize things first!
      CoInitialize(NULL);

      ZeroMemory(&wc, sizeof wc);
      wc.lpfnWndProc = WndMainProc;
      ghInst = wc.hInstance = hInstC;
      wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
      wc.lpszClassName = CLASSNAME;
      wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
      wc.hCursor = LoadCursor(NULL, IDC_ARROW);
      wc.hIcon = LoadIcon(hInstC, MAKEINTRESOURCE(IDI_CUTLIST));
      RegisterClass(&wc);

      RECT rc;

      GetWindowRect(GetDesktopWindow(), &rc);
      rc.right >>= 1;
      rc.bottom >>= 1;

      ghApp = CreateWindow(CLASSNAME,
	      APPLICATIONNAME,
	      WS_OVERLAPPEDWINDOW,
	      rc.right-200,
	      rc.bottom-200,
	      400,
	      400,
	      0,
	      0,
	      ghInst,
	      0);
		      
      ShowWindow(ghApp, SW_NORMAL);
      UpdateWindow(ghApp);

      ZeroMemory(&gTheSet, sizeof gTheSet);

      while (GetMessage(&msg,NULL,0,0))

	      {  // Message loop

	         TranslateMessage(&msg);
	         DispatchMessage(&msg);

	      }  // Message loop

      // Finished with OLE subsystem
      CoUninitialize();

      return msg.wParam;

  } // WinMain //
