MIDIOutDevice.cpp
上传用户:fs3633
上传日期:2021-05-14
资源大小:909k
文件大小:10k
源码类别:

midi

开发平台:

Visual C++

  1. /*******************************************************************************
  2.  * MIDIOutDevice.cpp - Implementation for CMIDIOutDevice and related 
  3.  *                     classes.
  4.  *
  5.  * Copyright (C) 2002 Leslie Sanford
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  *
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20.  *
  21.  * Contact: jabberdabber@hotmail.com
  22.  *
  23.  * Last modified: 09/03/2003
  24.  ******************************************************************************/
  25. //--------------------------------------------------------------------
  26. // Dependencies
  27. //--------------------------------------------------------------------
  28. #include "MIDIOutDevice.h"
  29. #include "midi.h"
  30. //--------------------------------------------------------------------
  31. // Using declarations
  32. //--------------------------------------------------------------------
  33. using midi::CMIDIOutDevice;
  34. using midi::CMIDIOutException;
  35. //--------------------------------------------------------------------
  36. // CMIDIOutHeader implementation
  37. //--------------------------------------------------------------------
  38. // Constructor
  39. CMIDIOutDevice::CMIDIOutHeader::CMIDIOutHeader(HMIDIOUT DevHandle,
  40.                                                LPSTR Msg, 
  41.                                                DWORD MsgLength) :
  42. m_DevHandle(DevHandle)
  43. {
  44.     // Initialize header
  45.     m_MIDIHdr.lpData         = Msg;
  46.     m_MIDIHdr.dwBufferLength = MsgLength;
  47.     m_MIDIHdr.dwFlags        = 0;
  48.     // Prepare header
  49.     MMRESULT Result = ::midiOutPrepareHeader(DevHandle, &m_MIDIHdr,
  50.                                              sizeof m_MIDIHdr);
  51.     // If an error occurred, throw exception
  52.     if(Result != MMSYSERR_NOERROR)
  53.     {
  54.         throw CMIDIOutException(Result);
  55.     }
  56. }
  57. // Destructor
  58. CMIDIOutDevice::CMIDIOutHeader::~CMIDIOutHeader()
  59. {
  60.     ::midiOutUnprepareHeader(m_DevHandle, &m_MIDIHdr, 
  61.                              sizeof m_MIDIHdr);
  62. }
  63. // Sends long message
  64. void CMIDIOutDevice::CMIDIOutHeader::SendMsg()
  65. {
  66.     MMRESULT Result = ::midiOutLongMsg(m_DevHandle, &m_MIDIHdr,
  67.                                        sizeof m_MIDIHdr);
  68.     if(Result != MMSYSERR_NOERROR)
  69.     {
  70.         throw CMIDIOutException(Result);
  71.     }
  72. }
  73. //--------------------------------------------------------------------
  74. // CHeaderQueue implementation
  75. //--------------------------------------------------------------------
  76. // Constructor
  77. CMIDIOutDevice::CHeaderQueue::CHeaderQueue()
  78. {
  79.     ::InitializeCriticalSection(&m_CriticalSection);
  80. }
  81. // Destructor
  82. CMIDIOutDevice::CHeaderQueue::~CHeaderQueue()
  83. {
  84.     RemoveAll();
  85.     ::DeleteCriticalSection(&m_CriticalSection);
  86. }
  87. // Add header to queue
  88. void CMIDIOutDevice::CHeaderQueue::AddHeader(
  89.                                CMIDIOutDevice::CMIDIOutHeader *Header)
  90. {
  91.     ::EnterCriticalSection(&m_CriticalSection);
  92.     m_HdrQueue.push(Header);
  93.     ::LeaveCriticalSection(&m_CriticalSection);
  94. }
  95. // Remove header from queue
  96. void CMIDIOutDevice::CHeaderQueue::RemoveHeader()
  97. {
  98.     ::EnterCriticalSection(&m_CriticalSection);
  99.     if(!m_HdrQueue.empty())
  100.     {
  101.         delete m_HdrQueue.front();
  102.         m_HdrQueue.pop();
  103.     }
  104.     ::LeaveCriticalSection(&m_CriticalSection);
  105. }
  106. // Empty header queue
  107. void CMIDIOutDevice::CHeaderQueue::RemoveAll()
  108. {
  109.     ::EnterCriticalSection(&m_CriticalSection);
  110.     while(!m_HdrQueue.empty())
  111.     {
  112.         delete m_HdrQueue.front();
  113.         m_HdrQueue.pop();
  114.     }
  115.     ::LeaveCriticalSection(&m_CriticalSection);
  116. }
  117. // Determines if the header queue is empty
  118. bool CMIDIOutDevice::CHeaderQueue::IsEmpty()
  119. {
  120.     bool Result;
  121.     ::EnterCriticalSection(&m_CriticalSection);
  122.     Result = m_HdrQueue.empty();
  123.     ::LeaveCriticalSection(&m_CriticalSection);
  124.     return Result;
  125. }
  126. //--------------------------------------------------------------------
  127. // CMIDIOutDevice implementation
  128. //--------------------------------------------------------------------
  129. // Constructs CMIDIOutDevice object in an closed state
  130. CMIDIOutDevice::CMIDIOutDevice() :
  131. m_State(CLOSED)
  132. {
  133.     // If we are unable to create signalling event, throw exception
  134.     if(!CreateEvent())
  135.     {
  136.         throw CMIDIOutEventFailure();
  137.     }
  138. }
  139. // Constructs CMIDIOutDevice object in an opened state
  140. CMIDIOutDevice::CMIDIOutDevice(UINT DeviceId) :
  141. m_State(CLOSED)
  142. {
  143.     // Open device
  144.     Open(DeviceId);
  145.     // If we are unable to create signalling event, throw exception
  146.     if(!CreateEvent())
  147.     {
  148.         Close();
  149.         throw CMIDIOutEventFailure();
  150.     }
  151. }
  152. // Destruction
  153. CMIDIOutDevice::~CMIDIOutDevice()
  154. {
  155.     // Close device
  156.     Close();
  157.     // Close handle to signalling event
  158.     ::CloseHandle(m_Event);
  159. }
  160. // Opens the MIDI output device
  161. void CMIDIOutDevice::Open(UINT DeviceId)
  162. {
  163.     // Makes sure the previous device, if any, is closed before 
  164.     // opening another one
  165.     Close();
  166.     // Open MIDI output device
  167.     MMRESULT Result = ::midiOutOpen(&m_DevHandle, DeviceId, 
  168.                                  reinterpret_cast<DWORD>(MidiOutProc),
  169.                                  reinterpret_cast<DWORD>(this),
  170.                                  CALLBACK_FUNCTION);
  171.     // If we are able to open the device, change state
  172.     if(Result == MMSYSERR_NOERROR)
  173.     {
  174.         m_State = OPENED;
  175.         m_WorkerThread = AfxBeginThread(
  176.             reinterpret_cast<AFX_THREADPROC>(HeaderProc), this);
  177.     }
  178.     // Else opening failed, throw exception
  179.     else
  180.     {
  181.         ::SetEvent(m_Event);
  182.         ::WaitForSingleObject(m_WorkerThread->m_hThread, INFINITE);
  183.         throw CMIDIOutException(Result);
  184.     }
  185. }
  186. // Closes the MIDI output device
  187. void CMIDIOutDevice::Close()
  188. {
  189.     // Only close an already opened device
  190.     if(m_State == OPENED)
  191.     {
  192.         // Change state
  193.         m_State = CLOSED;
  194.         // Notify our worker thread and wait for it to finish
  195.         ::SetEvent(m_Event);
  196.         ::WaitForSingleObject(m_WorkerThread->m_hThread, INFINITE);
  197.         // Empty header queue - we're finished with the headers
  198.         m_HdrQueue.RemoveAll();
  199.         // Close the MIDI output device
  200.         ::midiOutClose(m_DevHandle);
  201.     }
  202. }
  203. // Sends short message
  204. void CMIDIOutDevice::SendMsg(DWORD Msg)
  205. {
  206.     if(m_State == OPENED)
  207.     {
  208.         MMRESULT Result = ::midiOutShortMsg(m_DevHandle, Msg);
  209.         if(Result != MMSYSERR_NOERROR)
  210.         {
  211.             throw CMIDIOutException(Result);
  212.         }
  213.     }
  214. }
  215. // Sends long message
  216. void CMIDIOutDevice::SendMsg(LPSTR Msg, DWORD MsgLength)
  217. {
  218.     if(m_State == OPENED)
  219.     {  
  220.         CMIDIOutHeader *Header;
  221.         try
  222.         {
  223.             // Create new header to send system exclusive message
  224.             Header = new CMIDIOutHeader(m_DevHandle, Msg, MsgLength);
  225.         }
  226.         // If memory allocation failed, throw exception
  227.         catch(const std::bad_alloc &)
  228.         {
  229.             throw CMIDIOutMemFailure();
  230.         }
  231.         // If preparing the header failed, rethrow exception
  232.         catch(const CMIDIOutException &)
  233.         {
  234.             throw;
  235.         }
  236.         try
  237.         {
  238.             // Send system exclusive data
  239.             Header->SendMsg();
  240.             // Add header to queue
  241.             m_HdrQueue.AddHeader(Header);
  242.         }
  243.         // If sending system exclusive msg failed, release header
  244.         // and rethrow exception
  245.         catch(const CMIDIOutException &)
  246.         {
  247.             delete Header;
  248.             throw;
  249.         }
  250.     }
  251. }
  252. // Determines if the MIDI output device is opened
  253. bool CMIDIOutDevice::IsOpen() const
  254. {
  255.     return (m_State == OPENED);
  256. }
  257. // Gets the Id for this device
  258. UINT CMIDIOutDevice::GetDevID() const
  259. {
  260.     UINT DeviceID;
  261.     MMRESULT Result = ::midiOutGetID(m_DevHandle, &DeviceID);
  262.     if(Result != MMSYSERR_NOERROR)
  263.     {
  264.         throw CMIDIOutException(Result);
  265.     }
  266.     return DeviceID;
  267. }
  268. // Gets the capabilities of a particular MIDI output device
  269. void CMIDIOutDevice::GetDevCaps(UINT DeviceId, MIDIOUTCAPS &Caps)
  270. {
  271.     MMRESULT Result = ::midiOutGetDevCaps(DeviceId, &Caps, 
  272.                                           sizeof Caps);
  273.     // If we are not able to retrieve device capabilities, throw 
  274.     // exception
  275.     if(Result != MMSYSERR_NOERROR)
  276.     {
  277.         throw CMIDIOutException(Result);
  278.     }
  279. }
  280. // Creates event for signalling header thread
  281. bool CMIDIOutDevice::CreateEvent()
  282. {
  283.     bool Result = true;
  284.     m_Event = ::CreateEvent(NULL, FALSE, FALSE, NULL);
  285.     // If event creation failed, record failure
  286.     if(m_Event == NULL)
  287.     {
  288.         Result = false;
  289.     }
  290.     return Result;
  291. }
  292. // Called by Windows when a MIDI input event occurs
  293. void CALLBACK CMIDIOutDevice::MidiOutProc(HMIDIOUT MidiOut, UINT Msg,
  294.                                           DWORD Instance, DWORD Param1,
  295.                                           DWORD Param2)
  296. {
  297.     CMIDIOutDevice *Device;
  298.     
  299.     Device = reinterpret_cast<CMIDIOutDevice *>(Instance);
  300.     if(Msg == MOM_DONE)
  301.     {
  302.         ::SetEvent(Device->m_Event);
  303.     }
  304. }
  305. // Header worker thread
  306. DWORD CMIDIOutDevice::HeaderProc(LPVOID Parameter)
  307. {
  308.     CMIDIOutDevice *Device; 
  309.     
  310.     Device = reinterpret_cast<CMIDIOutDevice *>(Parameter);
  311.     // Continue while the MIDI output device is open
  312.     while(Device->m_State == OPENED)
  313.     {
  314.         ::WaitForSingleObject(Device->m_Event, INFINITE);
  315.         // Make sure the device is still open
  316.         if(Device->m_State == OPENED)
  317.         {
  318.             // Remove the finished header
  319.             Device->m_HdrQueue.RemoveHeader();
  320.         }
  321.     }
  322.     return 0;
  323. }