home *** CD-ROM | disk | FTP | other *** search
/ NEXT Generation 27 / NEXT27.iso / pc / demos / emperor / dx3.exe / SDK / SAMPLES / IKLOWNS / CGMIDI.CPP < prev    next >
C/C++ Source or Header  |  1996-08-28  |  20KB  |  784 lines

  1. /*===========================================================================*\
  2. |
  3. |  File:        cgmidi.cpp
  4. |
  5. |  Description: 
  6. |       
  7. |-----------------------------------------------------------------------------
  8. |
  9. |  Copyright (C) 1995-1996 Microsoft Corporation.  All Rights Reserved.
  10. |
  11. |  Written by Moss Bay Engineering, Inc. under contract to Microsoft Corporation
  12. |
  13. \*===========================================================================*/
  14. /**************************************************************************
  15.  
  16.     (C) Copyright 1995-1996 Microsoft Corp.  All rights reserved.
  17.  
  18.     You have a royalty-free right to use, modify, reproduce and 
  19.     distribute the Sample Files (and/or any modified version) in 
  20.     any way you find useful, provided that you agree that 
  21.     Microsoft has no warranty obligations or liability for any 
  22.     Sample Application Files which are modified. 
  23.  
  24.     we do not recomend you base your game on IKlowns, start with one of
  25.     the other simpler sample apps in the GDK
  26.  
  27.  **************************************************************************/
  28.  
  29. #include <windows.h>
  30. #include <windowsx.h>
  31. #include <mmsystem.h>
  32. #include "cgmidi.h"
  33. #include "MDSPlay.H"
  34.  
  35.  
  36. #define FOURCC_MIDS mmioFOURCC('M','I','D','S')
  37. #define FOURCC_fmt  mmioFOURCC('f','m','t',' ')
  38. #define FOURCC_data mmioFOURCC('d','a','t','a')
  39.  
  40. // Format of structs within a MDS file
  41. //
  42. // 'fmt ' chunk
  43. //
  44. #define MDS_F_NOSTREAMID    0x00000001
  45. typedef struct
  46. {
  47.     DWORD           dwTimeFormat;       // Low word == time format in SMF format
  48.     DWORD           cbMaxBuffer;        // Guaranteed max buffer size
  49.     DWORD           dwFlags;
  50. } MIDSFMT;
  51.  
  52. // 'data' chunk buffer header
  53. //
  54. typedef struct
  55. {
  56.     DWORD           tkStart;            // Absolute tick offset at start of buffer
  57.     DWORD           cbBuffer;           // Bytes in this buffer
  58. } MIDSBUFFER;
  59.  
  60. // An image handle points to this
  61. //
  62.  
  63. #define MDSI_F_RESET        0x00000001L
  64. #define MDSI_F_LOOP         0x00000002L
  65. #define MDSI_F_PAUSED       0x00000004L
  66.  
  67. #define FOURCC_MDSI         mmioFOURCC('M','D','S','I')
  68.  
  69. #define V_HIMAGE(x) \
  70.     if (((MDSIMAGE*)x)->fccSig != FOURCC_MDSI) \
  71.         return MDS_ERR_INVALHANDLE;
  72.  
  73. typedef struct
  74. {
  75.     FOURCC          fccSig;             // MDS image handle signature
  76.     MIDSFMT         fmt;                // MDS file format header
  77.     PBYTE           pbBufferAlloc;      // All MIDIHDR's/buffers
  78.     HMIDISTRM       hms;                // MIDI stream if open
  79.     DWORD           fdwImage;           // Generic flags
  80.     DWORD           cBuffers;           // Total buffers
  81.     DWORD           cBuffersInUse;      // Buffers MMSYSTEM owns right now
  82. } MDSIMAGE;
  83.  
  84. DWORD   ParseImage(MDSIMAGE* pImage, PBYTE pbImage, DWORD cbImage);
  85. BOOL    Decompress(LPMIDIHDR lpmhSrc, LPMIDIHDR lpmhDst);
  86.  
  87. void FAR PASCAL midiCallback(HMIDISTRM hms, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2);
  88.  
  89. #define MDS_F_IMAGEFLAGS    (MDS_F_MEMORY|MDS_F_FILENAME)
  90.  
  91. // LoadMDSImage
  92. //
  93. // Allocate space for the handle structure MDSImage and get the 
  94. // image into memory if it's in a file. Call ParseImage to
  95. // allocate buffers and parse the image into them.
  96. //
  97. DWORD LoadMDSImage(HANDLE *hImage, PBYTE pbImage, DWORD cbImage, DWORD fdw)
  98. {
  99.     DWORD                       dwRet       = MDS_SUCCESS;
  100.     MDSIMAGE*                   pImage      = NULL;
  101.     BOOL                        fIsMapped   = FALSE;
  102.     HANDLE                      hInFile     = INVALID_HANDLE_VALUE;
  103.     HANDLE                      hInFileMap  = NULL;
  104.  
  105.     // Must have one of the two image flags
  106.     //
  107.     if ((!(fdw & MDS_F_IMAGEFLAGS)) ||
  108.         ((fdw & MDS_F_IMAGEFLAGS) == MDS_F_IMAGEFLAGS))
  109.     {
  110.         dwRet = MDS_ERR_BADFLAGS;
  111.         goto Load_Cleanup;
  112.     }
  113.     
  114.     // Allocate the handle
  115.     //          
  116.     pImage = (MDSIMAGE*)LocalAlloc(LPTR, sizeof(MDSIMAGE));
  117.     if (!pImage)
  118.     {
  119.         dwRet = MDS_ERR_NOMEM;
  120.         goto Load_Cleanup;
  121.     }
  122.  
  123.     pImage->fccSig          = FOURCC_MDSI;
  124.     pImage->hms             = NULL;
  125.     pImage->cBuffersInUse   = 0;
  126.  
  127.  
  128.  
  129.     // Read the image if we need to 
  130.     //
  131.     if (!(fdw & MDS_F_MEMORY))
  132.     {
  133.         fIsMapped = TRUE;
  134.  
  135.         // Try to map the file as an image
  136.         //
  137.         hInFile = CreateFile(
  138.             (LPSTR)pbImage, GENERIC_READ, FILE_SHARE_READ, NULL,
  139.             OPEN_EXISTING, 
  140.             FILE_ATTRIBUTE_NORMAL, NULL);
  141.  
  142.         pbImage = NULL;
  143.         if (INVALID_HANDLE_VALUE == hInFile)
  144.         {
  145.             dwRet = MDS_ERR_NOFILE;
  146.             goto Load_Cleanup;
  147.         }
  148.  
  149.         cbImage = GetFileSize(hInFile, NULL);
  150.  
  151.         hInFileMap = CreateFileMapping(
  152.             hInFile, NULL, PAGE_READONLY, 0, 0, NULL);
  153.         if (NULL == hInFileMap)
  154.         {
  155.             dwRet = MDS_ERR_NOFILE;
  156.             goto Load_Cleanup;
  157.         }
  158.  
  159.         pbImage = (PBYTE)MapViewOfFile(
  160.             hInFileMap, FILE_MAP_READ, 0, 0, 0);
  161.         if (NULL == pbImage)
  162.         {
  163.             dwRet = MDS_ERR_NOFILE;
  164.             goto Load_Cleanup;
  165.         }
  166.     }
  167.  
  168.     // pbImage now points to the file image in memory. Attempt to parse it.
  169.     //
  170.     dwRet = ParseImage(pImage, pbImage, cbImage);
  171.  
  172. Load_Cleanup:
  173.     if (dwRet)
  174.     {
  175.         if (pImage) LocalFree((HLOCAL)pImage);
  176.     }
  177.     else
  178.     {
  179.         *hImage = (HANDLE)pImage;
  180.     }
  181.  
  182.     if (fIsMapped)
  183.     {
  184.         if (NULL != pbImage)                    UnmapViewOfFile(pbImage);
  185.         if (NULL != hInFileMap)                 CloseHandle(hInFileMap);
  186.         if (INVALID_HANDLE_VALUE != hInFile)    CloseHandle(hInFile);
  187.     }
  188.  
  189.     return dwRet;
  190. }
  191.  
  192. // Given the file image, allocate MIDI stream buffers and put the 
  193. // image data into them. The file image will go away when LoadMDSImage
  194. // returns, so this routine must save all important info somewhere off
  195. // the handle structure pImage.
  196. //
  197. DWORD ParseImage(MDSIMAGE* pImage, PBYTE pbImage, DWORD cbImage)
  198. {
  199.     DWORD                       dwRet       = MDS_SUCCESS;
  200.     DWORD                       cbChk;
  201.     DWORD                       idx;
  202.     MIDIHDR                     mhSrc;
  203.     LPMIDIHDR                   lpmh;
  204.     MIDSBUFFER                  mb;
  205.  
  206.     pImage->pbBufferAlloc = NULL;
  207.  
  208.     // Parse: RIFF + cbChk (size of rest of file) + MIDS
  209.     //
  210.     if ((cbImage < 2*sizeof(FOURCC) + sizeof(DWORD)) ||
  211.         (FOURCC_RIFF != *(FOURCC*)pbImage) ||
  212.         (FOURCC_MIDS != *(FOURCC*)(pbImage + sizeof(FOURCC) + sizeof(DWORD))))
  213.     {
  214.         dwRet = MDS_ERR_BADFILE;
  215.         goto Parse_Cleanup;
  216.     }
  217.  
  218.     // Note: can't subtract off size of 'fmt ' FOURCC until we check against
  219.     // cbChk size... it's included in the RIFF chunk, not the header
  220.     //
  221.     cbChk = *(((PDWORD)pbImage)+1);
  222.     pbImage += sizeof(FOURCC) + sizeof(DWORD);
  223.     cbImage -= sizeof(FOURCC) + sizeof(DWORD);
  224.  
  225.     if (cbImage < cbChk)
  226.     {
  227.         dwRet = MDS_ERR_BADFILE;
  228.         goto Parse_Cleanup;
  229.     }
  230.  
  231.     pbImage += sizeof(FOURCC);
  232.     cbImage -= sizeof(FOURCC);
  233.  
  234.  
  235.     // Should have 'fmt ' chunk first
  236.     //
  237.     if ((cbImage < sizeof(FOURCC) + sizeof(DWORD)) ||
  238.         (FOURCC_fmt != *(FOURCC*)pbImage) ||
  239.         ((cbChk = *(PDWORD)(pbImage + sizeof(FOURCC))) > cbImage) ||
  240.         cbChk < sizeof(pImage->fmt))
  241.     {
  242.         dwRet = MDS_ERR_BADFILE;
  243.         goto Parse_Cleanup;
  244.     }
  245.  
  246.     pbImage += sizeof(FOURCC) + sizeof(DWORD);
  247.     cbImage -= sizeof(FOURCC) + sizeof(DWORD);
  248.  
  249.     // Already validated size, copy format chunk
  250.     //
  251.     pImage->fmt = *(MIDSFMT*)pbImage;
  252.     
  253.     pbImage += cbChk;
  254.     cbImage -= cbChk;
  255.  
  256.     // Should get buffers next
  257.     //
  258.     if ((cbImage < sizeof(FOURCC) + sizeof(DWORD)) ||
  259.         (FOURCC_data != *(FOURCC*)pbImage) ||
  260.         ((cbChk = *(PDWORD)(pbImage + sizeof(FOURCC))) > cbImage) ||
  261.         cbChk < sizeof(DWORD))
  262.     {
  263.         dwRet = MDS_ERR_BADFILE;
  264.         goto Parse_Cleanup;
  265.     }
  266.  
  267.     pImage->cBuffers = *(PDWORD)(pbImage + sizeof(FOURCC) + sizeof(DWORD));
  268.  
  269.     pbImage += sizeof(FOURCC) + 2*sizeof(DWORD);
  270.     cbImage -= sizeof(FOURCC) + 2*sizeof(DWORD);
  271.  
  272.     // Now copy the data out, decompressing if needed
  273.     //
  274.  
  275.     // Total decompressed size including MIDIHDR's to hold them
  276.     //
  277.     // Allocate as one big block and put them into a buffer list
  278.     //
  279.     cbChk = pImage->cBuffers * (sizeof(MIDIHDR) + pImage->fmt.cbMaxBuffer);
  280.     pImage->pbBufferAlloc = (PBYTE)GlobalAllocPtr(
  281.         GMEM_MOVEABLE|GMEM_SHARE, cbChk);
  282.     if (NULL == pImage->pbBufferAlloc)
  283.     {
  284.         dwRet = MDS_ERR_NOMEM;
  285.         goto Parse_Cleanup;
  286.     }
  287.     
  288.     lpmh = (LPMIDIHDR)(pImage->pbBufferAlloc);
  289.     for (idx = pImage->cBuffers; idx; --idx)
  290.     {
  291.         lpmh->lpData = (LPSTR)(lpmh + 1);
  292.         lpmh->dwBufferLength = pImage->fmt.cbMaxBuffer;
  293.         lpmh->dwFlags = 0;
  294.         lpmh->dwUser = (DWORD)pImage;
  295.         lpmh->lpNext = NULL;
  296.  
  297.         if (cbImage < sizeof(mb))
  298.         {
  299.             dwRet = MDS_ERR_BADFILE;
  300.             goto Parse_Cleanup;
  301.         }
  302.  
  303.         mb = *(MIDSBUFFER*)pbImage;
  304.         cbImage -= sizeof(mb);
  305.         pbImage += sizeof(mb);
  306.  
  307.         if (mb.cbBuffer > pImage->fmt.cbMaxBuffer ||
  308.             mb.cbBuffer > cbImage)
  309.         {
  310.             dwRet = MDS_ERR_BADFILE;
  311.             goto Parse_Cleanup;
  312.         }
  313.  
  314.         if (!(pImage->fmt.dwFlags & MDS_F_NOSTREAMID))
  315.         {
  316.             lpmh->dwBytesRecorded = mb.cbBuffer;
  317.             hmemcpy(lpmh->lpData, pbImage, mb.cbBuffer);
  318.         }
  319.         else
  320.         {
  321.             mhSrc.lpData = (LPSTR)pbImage;
  322.             mhSrc.dwBufferLength = mhSrc.dwBytesRecorded = mb.cbBuffer;
  323.             if (!Decompress(&mhSrc, lpmh))
  324.             {
  325.                 dwRet = MDS_ERR_BADFILE;
  326.                 goto Parse_Cleanup;
  327.             }
  328.         }
  329.  
  330.         cbImage -= mb.cbBuffer;
  331.         pbImage += mb.cbBuffer;
  332.  
  333.         lpmh = (LPMIDIHDR)(((PBYTE)lpmh) + sizeof(MIDIHDR) + pImage->fmt.cbMaxBuffer);
  334.     }
  335.  
  336.  
  337. Parse_Cleanup:
  338.  
  339.     if (dwRet)
  340.     {
  341.         if (pImage->pbBufferAlloc) {
  342.         GlobalFreePtr(pImage->pbBufferAlloc);
  343.         }
  344.     }
  345.     
  346.     return dwRet;
  347. }
  348.  
  349. // Compression is simply removing the DWORD of stream ID per event since it's
  350. // 0 most of the time unless some really funky authoring is being done. This 
  351. // will save 1 of 3 DWORD's on a MIDI short event, reducing the file size
  352. // to 2/3 of what it was.
  353. //
  354. BOOL Decompress(LPMIDIHDR lpmhSrc, LPMIDIHDR lpmhDst)
  355. {
  356.     LPDWORD                 lpSrc       = (LPDWORD)lpmhSrc->lpData;
  357.     LPDWORD                 lpDst       = (LPDWORD)lpmhDst->lpData;
  358.     DWORD                   cbSrc       = lpmhSrc->dwBytesRecorded;
  359.     DWORD                   cbDst       = lpmhDst->dwBufferLength;
  360.     DWORD                   cbExtra;
  361.  
  362.     // Total buffer length must be a DWORD multiple
  363.     //
  364.     if (cbSrc & 3)
  365.         return FALSE;
  366.  
  367.     // !!! OPTIMIZE THIS LOOP !!!
  368.     //
  369.     while (cbSrc)
  370.     {   
  371.         // Need at least space for delta-t, stream-id, event DWORD
  372.         // 
  373.         if (cbDst < 3 * sizeof(DWORD))
  374.             return FALSE;
  375.  
  376.                                             
  377.         // Event delta-time
  378.         //
  379.         *lpDst++ = *lpSrc++;                
  380.         cbSrc -= sizeof(DWORD);
  381.  
  382.         // Any event left?
  383.         //
  384.         if (!cbSrc)
  385.             return FALSE;
  386.  
  387.         // Stream ID
  388.         *lpDst++ = 0;
  389.         cbDst -= 2*sizeof(DWORD);
  390.  
  391.         // Now copy the actual event data
  392.         //
  393.         cbExtra = 0;
  394.         if ((*lpSrc) & 0x80000000L)
  395.             cbExtra = (*lpSrc) & 0x00FFFFFFL;
  396.  
  397.         // Long event length is byte aligned, but data is padded to next DWORD
  398.         // in file. 
  399.         //
  400.         cbExtra = (cbExtra + 3) & ~3;
  401.  
  402.         // Event DWORD itself
  403.         //
  404.         *lpDst++ = *lpSrc++;
  405.         cbSrc -= sizeof(DWORD);
  406.         cbDst -= sizeof(DWORD);
  407.  
  408.         // Long event parameter data
  409.         //
  410.         if (cbExtra)
  411.         {
  412.             if (cbExtra > cbSrc || cbExtra > cbDst)
  413.                 return FALSE;
  414.             
  415.             hmemcpy(lpDst, lpSrc, cbExtra);
  416.         }
  417.  
  418. //      assert(0 == (cbExtra % sizeof(DWORD)));
  419.  
  420.         lpDst += (cbExtra / sizeof(DWORD));
  421.         lpSrc += (cbExtra / sizeof(DWORD));
  422.         cbSrc -= cbExtra;
  423.         cbDst -= cbExtra;
  424.     }
  425.  
  426.     lpmhDst->dwBytesRecorded = (((LPBYTE)lpDst) - (LPBYTE)(lpmhDst->lpData));
  427.  
  428.     return TRUE;
  429. }
  430.  
  431. // FreeMDSImage
  432. //
  433. // Get rid of all resources associated with this handle. Stops playback
  434. // if running.
  435. //
  436. DWORD FreeMDSImage(HANDLE hImage)
  437. {
  438.     MDSIMAGE*                   pImage;
  439.  
  440.     V_HIMAGE(hImage);
  441.     pImage = (MDSIMAGE*)hImage;
  442.     
  443.     if (NULL != pImage->hms)
  444.         StopMDS(hImage);
  445.  
  446.     if (pImage->pbBufferAlloc)  GlobalFreePtr(pImage->pbBufferAlloc);
  447.  
  448.     // Toast signature in case free'ing the block doesn't; this will
  449.     // cause future V_HIMAGE's to faile
  450.     //
  451.     pImage->fccSig = FOURCC_data;
  452.  
  453.     LocalFree((HLOCAL)pImage);
  454.  
  455.     return MDS_SUCCESS;
  456. }
  457.  
  458. // PlayMDS
  459. //
  460. // Start playback.
  461. //  Open the device if needed.
  462. //  Send the ready list
  463. //
  464. DWORD PlayMDS(HANDLE hImage, DWORD fdw)
  465. {
  466.     DWORD                       dwRet   = MDS_SUCCESS;
  467.     MDSIMAGE*                   pImage;
  468.     LPMIDIHDR                   lpmh;
  469.     UINT                        uDeviceID;
  470.     BOOL                        fCloseOnFail = FALSE;
  471.     DWORD                       idx;
  472.     MIDIPROPTIMEDIV             mptd;
  473.  
  474.     V_HIMAGE(hImage);
  475.  
  476.     pImage = (MDSIMAGE*)hImage;
  477.     
  478.     if (pImage->hms && !(pImage->fdwImage & MDSI_F_PAUSED))
  479.         return MDS_ERR_BADSTATE;
  480.  
  481.     if (!(pImage->hms))
  482.     {
  483.         fCloseOnFail = TRUE;
  484.  
  485.         // Starting from scratch. Try to open the MIDI device
  486.         //
  487.         uDeviceID = MIDI_MAPPER;
  488.         if (MMSYSERR_NOERROR != midiStreamOpen(
  489.             &pImage->hms, 
  490.             &uDeviceID, 
  491.             1, 
  492.             (DWORD)midiCallback, 
  493.             0L, 
  494.             CALLBACK_FUNCTION))
  495.         {
  496.             dwRet = MDS_ERR_MIDIERROR;
  497.             goto Play_Cleanup;
  498.         }
  499.  
  500.         mptd.cbStruct = sizeof(mptd);
  501.         mptd.dwTimeDiv = pImage->fmt.dwTimeFormat;
  502.  
  503.         if (MMSYSERR_NOERROR !=midiStreamProperty(
  504.             pImage->hms, (LPBYTE)&mptd, MIDIPROP_SET|MIDIPROP_TIMEDIV))
  505.         {
  506.             dwRet = MDS_ERR_MIDIERROR;
  507.             goto Play_Cleanup;
  508.         }
  509.  
  510.         // Headers are put back into the ready queue by a midiOutReset on
  511.         // stop, but are not guaranteed to be in correct order. Resend 
  512.         // directly from the allocated chunk-of-all-buffers
  513.         //
  514.  
  515. //      assert(0 == pImage->cBuffersInUse);
  516.  
  517.         lpmh = (LPMIDIHDR)(pImage->pbBufferAlloc);
  518.         for (idx = pImage->cBuffers; idx; --idx)
  519.         {
  520.             if (MMSYSERR_NOERROR != midiOutPrepareHeader(
  521.                                 (HMIDIOUT)pImage->hms, lpmh, sizeof(*lpmh)) ||
  522.                 MMSYSERR_NOERROR != midiStreamOut(
  523.                 pImage->hms, lpmh, sizeof(*lpmh)))
  524.             {
  525.                 dwRet = MDS_ERR_MIDIERROR;
  526.                 goto Play_Cleanup;
  527.             }
  528.  
  529.             ++pImage->cBuffersInUse;
  530.  
  531.             lpmh = (LPMIDIHDR)(((PBYTE)lpmh) + sizeof(MIDIHDR) + lpmh->dwBufferLength);
  532.         }       
  533.     }
  534.  
  535.     // Whether we're starting or resuming from paused, just need
  536.     // to restart
  537.  
  538.     pImage->fdwImage &= ~MDSI_F_LOOP;
  539.     if (fdw & MDS_F_LOOP)
  540.         pImage->fdwImage |= MDSI_F_LOOP;
  541.  
  542.     pImage->fdwImage &= ~MDSI_F_PAUSED;
  543.     if (MMSYSERR_NOERROR != midiStreamRestart(
  544.         pImage->hms))
  545.     {
  546.         dwRet = MDS_ERR_MIDIERROR;
  547.         goto Play_Cleanup;
  548.     }
  549.  
  550. Play_Cleanup:
  551.  
  552.     if (dwRet)
  553.     {
  554.         if (fCloseOnFail && pImage->hms)
  555.             StopMDS(hImage);
  556.     }
  557.     
  558.     return dwRet;
  559. }
  560.  
  561. // PauseMDS
  562. //
  563. // Pause the stream if it's playing
  564. //
  565. DWORD PauseMDS(HANDLE hImage)
  566. {
  567.     MDSIMAGE*                   pImage;
  568.  
  569.     V_HIMAGE(hImage);
  570.  
  571.     pImage = (MDSIMAGE*)hImage;
  572.  
  573.     if (NULL == pImage->hms)
  574.         return MDS_ERR_BADSTATE;
  575.     
  576.     if (pImage->fdwImage & MDSI_F_PAUSED)
  577.         return MDS_SUCCESS;
  578.  
  579.     if (MMSYSERR_NOERROR != midiStreamPause(
  580.         pImage->hms))
  581.         return MDS_ERR_MIDIERROR;
  582.  
  583.     pImage->fdwImage |= MDSI_F_PAUSED;
  584.  
  585.     return MDS_SUCCESS;
  586. }
  587.  
  588. // StopMDS
  589. //
  590. // Stop the stream, reset to the start, close the device
  591. // Do NOT free stream tho
  592. //
  593. DWORD StopMDS(HANDLE hImage)
  594. {
  595.     MDSIMAGE*                   pImage;
  596.     DWORD                       idx;
  597.     LPMIDIHDR                   lpmh;
  598.  
  599.     V_HIMAGE(hImage);
  600.  
  601.     pImage = (MDSIMAGE*)hImage;
  602.         
  603.     if (NULL == pImage->hms)
  604.         return MDS_ERR_BADSTATE;
  605.  
  606.     // Remove all the buffers from MMSYSTEM
  607.     //
  608.     pImage->fdwImage |= MDSI_F_RESET;
  609.     if (MMSYSERR_NOERROR != midiOutReset(
  610.                 (HMIDIOUT)pImage->hms))
  611.     {
  612.         pImage->fdwImage &= ~MDSI_F_RESET;
  613.         return MDS_ERR_MIDIERROR;
  614.     }
  615.  
  616. //  assert(0 == pImage->cBuffersInUse);
  617.  
  618.     // Unprepare everyone
  619.     //
  620.     lpmh = (LPMIDIHDR)(pImage->pbBufferAlloc);
  621.     for (idx = pImage->cBuffers; idx; --idx)
  622.     {
  623.                 midiOutUnprepareHeader((HMIDIOUT)pImage->hms, lpmh, sizeof(*lpmh));
  624.         lpmh = (LPMIDIHDR)(((PBYTE)lpmh) + sizeof(MIDIHDR) + lpmh->dwBufferLength);
  625.     }       
  626.     
  627.     midiStreamClose(pImage->hms);
  628.     pImage->hms = NULL;
  629.     pImage->fdwImage = 0;
  630.  
  631.     return MDS_SUCCESS;
  632. }
  633.  
  634.  
  635. // Callback
  636. //
  637. // Keep things rolling or collect the buffers back in the queue
  638. //
  639. void FAR PASCAL midiCallback(HMIDISTRM hms, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
  640. {
  641.     MDSIMAGE*                   pImage;
  642.     LPMIDIHDR                   lpmh        = (LPMIDIHDR)dw1;
  643.  
  644.     if (uMsg != MOM_DONE)
  645.         return;
  646.  
  647. //  assert(NULL != lpmh);
  648.     pImage = (MDSIMAGE*)lpmh->dwUser;
  649. //  assert(FOURCC_MDSI == pImage->fccSig);
  650.  
  651.     pImage = (MDSIMAGE*)lpmh->dwUser;
  652.  
  653.     if ((pImage->fdwImage & MDSI_F_LOOP) && !(pImage->fdwImage & MDSI_F_RESET))
  654.         if (MMSYSERR_NOERROR == midiStreamOut(
  655.             pImage->hms, lpmh, sizeof(*lpmh)))
  656.             return;
  657.             
  658.     --pImage->cBuffersInUse;
  659. }
  660.  
  661.  
  662.  
  663.  
  664.  
  665.  
  666.  
  667.  
  668.  
  669.  
  670.  
  671.  
  672. typedef struct _MIDI_INFO
  673. {
  674.     HANDLE  hImage;
  675.     BOOL    fPlaying;
  676. } MIDI_INFO;
  677.  
  678. static MIDI_INFO    *pMidi = NULL;
  679.  
  680. /* Plays a given MIDI file using MCI_OPEN, MCI_PLAY. Returns as soon as
  681.  * playback begins. The window procedure function for the given window
  682.  * will be notified when playback is complete.
  683.  */
  684. BOOL playMusic(LPSTR lpszMIDIFileName, BOOL fAutoStart)
  685. {
  686.     if (pMidi != NULL) {
  687.     closeMusic();
  688.     }
  689.  
  690.     pMidi = new MIDI_INFO;
  691.  
  692.     
  693.     if( LoadMDSImage( &(pMidi->hImage),
  694.               (PBYTE)lpszMIDIFileName,
  695.               0,
  696.               MDS_F_FILENAME ) != 0 ) {
  697.     delete pMidi;
  698.     pMidi = NULL;
  699.     return (FALSE);
  700.     }
  701.  
  702.     pMidi->fPlaying = FALSE;
  703.     if( fAutoStart ) {
  704.     if( PlayMDS(pMidi->hImage, MDS_F_LOOP) != 0 ) {
  705.         FreeMDSImage(pMidi->hImage);
  706.         delete pMidi;
  707.         pMidi = NULL;
  708.         return (FALSE);
  709.     }
  710.     pMidi->fPlaying = TRUE;
  711.  
  712.     }
  713.  
  714.     
  715.     return (TRUE);
  716. }
  717.  
  718.  
  719. void resumeMusic()
  720. {
  721.     if ( pMidi == NULL  ) {
  722.     // Error
  723.     return;
  724.     }
  725.  
  726.     if( PlayMDS(pMidi->hImage, MDS_F_LOOP) != 0 ) {
  727.     // Error
  728.     return;
  729.     }   
  730.     pMidi->fPlaying = TRUE;
  731.  
  732. }   
  733.  
  734. void pauseMusic()
  735. {
  736.     if ( pMidi == NULL  ) {
  737.     // Error
  738.     return;
  739.     }
  740.  
  741.     if( PauseMDS(pMidi->hImage) != 0 ) {
  742.     // Error
  743.     return;
  744.     }   
  745.     pMidi->fPlaying = FALSE;
  746.  
  747. }   
  748.  
  749. void restartMusic()
  750. {
  751.     if ( pMidi == NULL  ) {
  752.     // Error
  753.     return;
  754.     }
  755.  
  756.  
  757.     if( StopMDS(pMidi->hImage) != 0 ) {
  758.     // Error
  759.     return;
  760.     }   
  761.     if( PlayMDS(pMidi->hImage, MDS_F_LOOP) != 0 ) {
  762.     // Error
  763.     return;
  764.     }   
  765.     pMidi->fPlaying = TRUE;
  766.  
  767. }
  768.  
  769. void closeMusic()
  770. {
  771.     if ( pMidi == NULL  ) {
  772.     // Error
  773.     return;
  774.     }
  775.  
  776.     StopMDS(pMidi->hImage);
  777.     FreeMDSImage(pMidi->hImage);
  778.     
  779.     delete pMidi;
  780.     pMidi = NULL;
  781.  
  782.         
  783. }   
  784.