home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / Players / BGMusic / bgmusic.cpp next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  15.0 KB  |  444 lines

  1. //------------------------------------------------------------------------------
  2. // File: BGMusic.cpp
  3. //
  4. // Desc: A simple playback applicaiton that plays a cyclic set of media
  5. //       files of the same type. This is the code required to use DirectShow
  6. //       to play compressed audio in the background of your title in a
  7. //       seamless manner.
  8. //
  9. // Copyright (c) 1999-2001 Microsoft Corporation. All rights reserved.
  10. //------------------------------------------------------------------------------
  11.  
  12. #include <windows.h>
  13. #include <dshow.h>
  14. #include <tchar.h>
  15. #include <malloc.h>
  16. #include "resource.h"
  17.  
  18. //------------------------------------------------------------------------------
  19. // Forward Declarations
  20. //------------------------------------------------------------------------------
  21. HRESULT GraphInit(void);
  22. HWND AppInit(HINSTANCE hInstance);
  23. void AppMessageLoop(void);
  24. void AppCleanUp(void);
  25. HRESULT SwapSourceFilter(void); 
  26. void ShowCurrentFile(HWND hWnd);
  27. const TCHAR* DXUtil_GetDXSDKMediaPath();
  28. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
  29. VOID CALLBACK MyTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
  30.  
  31. //------------------------------------------------------------------------------
  32. // Macros
  33. //------------------------------------------------------------------------------
  34. #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
  35.  
  36. //------------------------------------------------------------------------------
  37. // Constants
  38. //------------------------------------------------------------------------------
  39. #define CLASSNAME       TEXT("BGMusicPlayer")
  40. #define APPNAME         TEXT("BGMusic Player")
  41. #define APPWIDTH        200
  42. #define APPHEIGHT       100
  43. #define MEDIA_TIMEOUT   (10 * 1000)  // 10 seconds, represented in milliseconds
  44.  
  45. //------------------------------------------------------------------------------
  46. // Global Variables
  47. //------------------------------------------------------------------------------
  48. // DirectShow Graph, Filter & Pins used
  49. IGraphBuilder *g_pGraphBuilder = NULL;
  50. IMediaControl *g_pMediaControl = NULL;
  51. IMediaSeeking *g_pMediaSeeking = NULL;
  52. IBaseFilter   *g_pSourceCurrent = NULL;
  53. IBaseFilter   *g_pSourceNext = NULL;
  54. TCHAR          g_szCurrentFile[128];
  55. HWND           g_hwndApp;
  56.  
  57. // File names & variables to track current file
  58. LPCTSTR pstrFiles[] = 
  59. {
  60.     TEXT("track1.mp3\0"),
  61.     TEXT("track2.mp3\0"),
  62.     TEXT("track3.mp3\0"),
  63. };
  64.  
  65. int g_iNumFiles = 3, g_iNextFile = 0;
  66.  
  67.  
  68. //------------------------------------------------------------------------------
  69. // Name: WinMain()
  70. // Desc: Main Entry point for the app. Calls the Initialization routines and
  71. //       then calls the main processing loop.
  72. //------------------------------------------------------------------------------
  73. int APIENTRY WinMain(HINSTANCE hInstance,
  74.                      HINSTANCE hPrevInstance,
  75.                      LPSTR     lpCmdLine,
  76.                      int       nCmdShow)
  77. {
  78.     // Initialize application window
  79.     if (! AppInit(hInstance))
  80.         return 0;
  81.  
  82.     // Initialize DirectShow components and build initial graph
  83.     if (SUCCEEDED (GraphInit()))
  84.     {
  85.         // Main Message Loop
  86.         AppMessageLoop();
  87.     }
  88.  
  89.     // Clean up
  90.     AppCleanUp();
  91.     return 0;
  92. }
  93.  
  94.  
  95. //------------------------------------------------------------------------------
  96. // Name: GraphInit()
  97. // Desc: Initialization of DirectShow components and initial graph
  98. //------------------------------------------------------------------------------
  99. HRESULT GraphInit(void)
  100. {
  101.     HRESULT hr;
  102.     // Initialize COM
  103.     if (FAILED (hr = CoInitialize(NULL)) )
  104.         return hr;
  105.  
  106.     // Create DirectShow Graph
  107.     if (FAILED (hr = CoCreateInstance(CLSID_FilterGraph, NULL,
  108.                                       CLSCTX_INPROC, IID_IGraphBuilder,
  109.                                       reinterpret_cast<void **>(&g_pGraphBuilder))) )
  110.         return hr;
  111.  
  112.     // Get the IMediaControl Interface
  113.     if (FAILED (g_pGraphBuilder->QueryInterface(IID_IMediaControl,
  114.                                  reinterpret_cast<void **>(&g_pMediaControl))))
  115.         return hr;
  116.  
  117.     // Get the IMediaControl Interface
  118.     if (FAILED (g_pGraphBuilder->QueryInterface(IID_IMediaSeeking,
  119.                                  reinterpret_cast<void **>(&g_pMediaSeeking))))
  120.         return hr;
  121.  
  122.     // Create Source Filter for first file
  123.     g_iNextFile = 0;
  124.  
  125.     // Create the intial graph
  126.     if (FAILED (SwapSourceFilter()))
  127.         return hr;
  128.  
  129.     // Set a timer for switching the sources
  130.     SetTimer(g_hwndApp, 0, MEDIA_TIMEOUT, (TIMERPROC) MyTimerProc);
  131.  
  132.     return S_OK;
  133. }
  134.  
  135.  
  136. //------------------------------------------------------------------------------
  137. // Name: AppInit()
  138. // Desc: Initialization of application window
  139. //------------------------------------------------------------------------------
  140. HWND AppInit(HINSTANCE hInstance)
  141. {
  142.     WNDCLASS wc;
  143.  
  144.     // Register the window class
  145.     ZeroMemory(&wc, sizeof wc);
  146.     wc.lpfnWndProc   = WndProc;
  147.     wc.hInstance     = hInstance;
  148.     wc.lpszClassName = CLASSNAME;
  149.     wc.lpszMenuName  = NULL;
  150.     wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
  151.     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  152.     wc.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_BGMUSIC));
  153.     if(!RegisterClass(&wc))
  154.         return 0;
  155.  
  156.     // Create the main window without support for resizing
  157.     g_hwndApp = CreateWindow(CLASSNAME, APPNAME,
  158.                     WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU
  159.                                   | WS_MINIMIZEBOX | WS_VISIBLE,
  160.                     CW_USEDEFAULT, CW_USEDEFAULT,
  161.                     APPWIDTH, APPHEIGHT,
  162.                     0, 0, hInstance, 0);
  163.  
  164.     return g_hwndApp;
  165. }
  166.  
  167.  
  168. //------------------------------------------------------------------------------
  169. // Name: WndProcLoop()
  170. // Desc: Main Message Processor for the Application
  171. //------------------------------------------------------------------------------
  172. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  173. {
  174.     switch(message)
  175.     {
  176.         case WM_PAINT:
  177.             ShowCurrentFile(hWnd);
  178.             break;
  179.  
  180.         case WM_CLOSE:
  181.         case WM_DESTROY:
  182.             PostQuitMessage(0);
  183.             break;
  184.     }
  185.  
  186.     return DefWindowProc(hWnd, message, wParam, lParam);
  187. }
  188.  
  189.  
  190. //------------------------------------------------------------------------------
  191. // Name: ShowCurrentFile()
  192. // Desc: Display the currently playing media file in the main window
  193. //------------------------------------------------------------------------------
  194. void ShowCurrentFile(HWND hWnd)
  195. {
  196.     PAINTSTRUCT ps;
  197.     RECT rc;
  198.     TCHAR szMsg[128];
  199.  
  200.     BeginPaint(hWnd, &ps);
  201.     HDC hdc = GetDC(hWnd);
  202.     GetWindowRect(hWnd, &rc);
  203.     
  204.     // Set the text color to bright green against black background
  205.     SetTextColor(hdc, RGB(80, 255, 80));
  206.     SetBkColor(hdc, RGB(0,0,0));
  207.  
  208.     // Decide where to place the text (centered in window)
  209.     int X = (rc.right - rc.left) / 2;
  210.     int Y = (rc.bottom - rc.top) / 3;
  211.     SetTextAlign(hdc, TA_CENTER | VTA_CENTER);
  212.  
  213.     // Update the text string
  214.     wsprintf(szMsg, _T("Playing %s\0"), g_szCurrentFile);
  215.     ExtTextOut(hdc, X, Y, ETO_OPAQUE, NULL, szMsg, _tcslen(szMsg), 0);
  216.  
  217.     EndPaint(hWnd, &ps);
  218. }
  219.  
  220.  
  221. //------------------------------------------------------------------------------
  222. // Name: AppMessageLoop()
  223. // Desc: Main Message Loop for the Application
  224. //------------------------------------------------------------------------------
  225. void AppMessageLoop(void)
  226. {
  227.     MSG msg={0};
  228.  
  229.     // Main message loop:
  230.     while (GetMessage(&msg, NULL, 0, 0))
  231.     {
  232.         if (! TranslateAccelerator(msg.hwnd, NULL, &msg) )
  233.         {
  234.             TranslateMessage(&msg) ;
  235.             DispatchMessage(&msg) ;
  236.         }
  237.     }
  238.  
  239.     return;
  240. }
  241.  
  242.  
  243. //------------------------------------------------------------------------------
  244. // Name: AppCleanUp)
  245. // Desc: Clean up the application
  246. //------------------------------------------------------------------------------
  247. void AppCleanUp(void)
  248. {
  249.     // Stop playback
  250.     if (g_pMediaControl)
  251.         g_pMediaControl->Stop();
  252.  
  253.     // Release all remaining pointers
  254.     SAFE_RELEASE( g_pSourceNext);
  255.     SAFE_RELEASE( g_pSourceCurrent);
  256.     SAFE_RELEASE( g_pMediaSeeking);
  257.     SAFE_RELEASE( g_pMediaControl);
  258.     SAFE_RELEASE( g_pGraphBuilder);
  259.  
  260.     // Clean up COM
  261.     CoUninitialize();
  262.     return;
  263. }
  264.  
  265.  
  266. //------------------------------------------------------------------------------
  267. // MyTimerProc - Callback when the timer goes off
  268. //------------------------------------------------------------------------------
  269. VOID CALLBACK MyTimerProc(
  270.   HWND hwnd,     // handle to window
  271.   UINT uMsg,     // WM_TIMER message
  272.   UINT idEvent,  // timer identifier
  273.   DWORD dwTime   // current system time
  274. )
  275. {
  276.     SwapSourceFilter();
  277.  
  278.     // Update the "current file" text message
  279.     RECT rc;
  280.     GetWindowRect(hwnd, &rc);
  281.     InvalidateRect(hwnd, &rc, TRUE);
  282. }
  283.  
  284.  
  285. //------------------------------------------------------------------------------
  286. // Name: SwapSourceFilter()
  287. // Desc: This routine is used to change the source file in the current graph.
  288. //       First the graph is stopped, then the current source filter is removed.
  289. //       The new source filter is added, the output pin on this filter is
  290. //       rendered, and playback is restarted.
  291. //
  292. //       When this routine is called during initialization, there is no
  293. //       currently running graph. In that case, Stop becomes a no-op. The source
  294. //       filter is added to an empty graph. Then during the render call, all
  295. //       necessary filters required to play this source are added to the graph.
  296. //
  297. //       On subsequent calls, Stopping the graph allows filters to be removed.
  298. //       When the old source filter is removed, all other filters are still
  299. //       left in the graph. The new source filter is added, and then the render
  300. //       operation reconnects the graph. Since all of the necessary filters for
  301. //       playback are already in the graph (if the two files have the same file
  302. //       type), these filters are reused. Existing filters in the graph are
  303. //       always used first, if possible, during a Render operation. This avoids
  304. //       having to create new filter instances with each change.
  305. //------------------------------------------------------------------------------
  306. HRESULT SwapSourceFilter(void)
  307. {
  308.     HRESULT hr = S_OK;
  309.     IPin *pPin = NULL;
  310.     TCHAR szFilename[MAX_PATH];
  311.     WCHAR wFileName[MAX_PATH];
  312.  
  313.     // Determine the file to load based on DirectX Media path (from SDK)
  314.     _tcscpy( szFilename, DXUtil_GetDXSDKMediaPath() );
  315.     _tcscat( szFilename, pstrFiles[g_iNextFile % g_iNumFiles]);
  316.     _tcscpy( g_szCurrentFile, pstrFiles[g_iNextFile % g_iNumFiles]);
  317.     g_iNextFile++;
  318.  
  319.     // Make sure that this file exists
  320.     DWORD dwAttr = GetFileAttributes(szFilename);
  321.     if (dwAttr == (DWORD) -1)
  322.         return ERROR_FILE_NOT_FOUND;
  323.  
  324.     #ifndef UNICODE
  325.         MultiByteToWideChar(CP_ACP, 0, szFilename, -1, wFileName, MAX_PATH);
  326.     #else
  327.         lstrcpy(wFileName, szFilename);
  328.     #endif
  329.  
  330.     // OPTIMIZATION OPPORTUNITY
  331.     // This will open the file, which is expensive. To optimize, this
  332.     // should be done earlier, ideally as soon as we knew this was the
  333.     // next file to ensure that the file load doesn't add to the
  334.     // filter swapping time & cause a hiccup.
  335.     //
  336.     // Add the new source filter to the graph. (Graph can still be running)
  337.     hr = g_pGraphBuilder->AddSourceFilter(wFileName, wFileName, &g_pSourceNext);
  338.  
  339.     // Get the first output pin of the new source filter. Audio sources 
  340.     // typically have only one output pin, so for most audio cases finding 
  341.     // any output pin is sufficient.
  342.     if (SUCCEEDED(hr)) {
  343.         hr = g_pSourceNext->FindPin(L"Output", &pPin);  
  344.     }
  345.  
  346.     // Stop the graph
  347.     if (SUCCEEDED(hr)) {
  348.         hr = g_pMediaControl->Stop();
  349.     }
  350.  
  351.     // Break all connections on the filters. You can do this by adding 
  352.     // and removing each filter in the graph
  353.     if (SUCCEEDED(hr)) {
  354.         IEnumFilters *pFilterEnum = NULL;
  355.  
  356.         if (SUCCEEDED(hr = g_pGraphBuilder->EnumFilters(&pFilterEnum))) {
  357.             int iFiltCount = 0;
  358.             int iPos = 0;
  359.  
  360.             // Need to know how many filters. If we add/remove filters during the
  361.             // enumeration we'll invalidate the enumerator
  362.             while (S_OK == pFilterEnum->Skip(1)) {
  363.                 iFiltCount++;
  364.             }
  365.  
  366.             // Allocate space, then pull out all of the 
  367.             IBaseFilter **ppFilters = reinterpret_cast<IBaseFilter **>
  368.                                       (_alloca(sizeof(IBaseFilter *) * iFiltCount));
  369.             pFilterEnum->Reset();
  370.  
  371.             while (S_OK == pFilterEnum->Next(1, &(ppFilters[iPos++]), NULL));
  372.             SAFE_RELEASE(pFilterEnum);
  373.  
  374.             for (iPos = 0; iPos < iFiltCount; iPos++) {
  375.                 g_pGraphBuilder->RemoveFilter(ppFilters[iPos]);
  376.                 // Put the filter back, unless it is the old source
  377.                 if (ppFilters[iPos] != g_pSourceCurrent) {
  378.                     g_pGraphBuilder->AddFilter(ppFilters[iPos], NULL);
  379.                 }
  380.                 SAFE_RELEASE(ppFilters[iPos]);
  381.             }
  382.         }
  383.     }
  384.  
  385.     // We have the new ouput pin. Render it
  386.     if (SUCCEEDED(hr)) {
  387.         hr = g_pGraphBuilder->Render(pPin);
  388.         g_pSourceCurrent = g_pSourceNext;
  389.         g_pSourceNext = NULL;
  390.     }
  391.  
  392.     SAFE_RELEASE(pPin);
  393.     SAFE_RELEASE(g_pSourceNext); // In case of errors
  394.  
  395.     // Re-seek the graph to the beginning
  396.     if (SUCCEEDED(hr)) {
  397.         LONGLONG llPos = 0;
  398.         hr = g_pMediaSeeking->SetPositions(&llPos, AM_SEEKING_AbsolutePositioning,
  399.                                            &llPos, AM_SEEKING_NoPositioning);
  400.     } 
  401.  
  402.     // Start the graph
  403.     if (SUCCEEDED(hr)) {
  404.         hr = g_pMediaControl->Run();
  405.     }
  406.  
  407.     // Release the old source filter.
  408.     SAFE_RELEASE(g_pSourceCurrent)
  409.     return S_OK;   
  410. }
  411.  
  412.  
  413. //-----------------------------------------------------------------------------
  414. // Name: DXUtil_GetDXSDKMediaPath()
  415. // Desc: Returns the DirectX SDK media path
  416. //-----------------------------------------------------------------------------
  417. const TCHAR* DXUtil_GetDXSDKMediaPath()
  418. {
  419.     static TCHAR strNull[2] = _T("");
  420.     static TCHAR strPath[MAX_PATH];
  421.     DWORD dwType;
  422.     DWORD dwSize = MAX_PATH;
  423.     HKEY  hKey;
  424.  
  425.     // Open the appropriate registry key
  426.     LONG lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  427.                                 _T("Software\\Microsoft\\DirectX SDK"),
  428.                                 0, KEY_READ, &hKey );
  429.     if( ERROR_SUCCESS != lResult )
  430.         return strNull;
  431.  
  432.     lResult = RegQueryValueEx( hKey, _T("DX81SDK Samples Path"), NULL,
  433.                               &dwType, (BYTE*)strPath, &dwSize );
  434.     RegCloseKey( hKey );
  435.  
  436.     if( ERROR_SUCCESS != lResult )
  437.         return strNull;
  438.  
  439.     _tcscat( strPath, _T("\\Media\\") );
  440.  
  441.     return strPath;
  442. }
  443.  
  444.