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

midi

开发平台:

Visual C++

  1. /*******************************************************************************
  2.  * MIDIInDevice.cpp - Implementation for CMIDIInDevice 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/02/2003
  24.  ******************************************************************************/
  25. //--------------------------------------------------------------------
  26. // Dependencies
  27. //--------------------------------------------------------------------
  28. #include "stdafx.h"
  29. #include "MIDIInDevice.h"
  30. #include "midi.h"
  31. //--------------------------------------------------------------------
  32. // Using declarations
  33. //--------------------------------------------------------------------
  34. using midi::CMIDIInDevice;
  35. using midi::CMIDIReceiver;
  36. using midi::CMIDIInException;
  37. //--------------------------------------------------------------------
  38. // CMIDIInHeader implementation
  39. //--------------------------------------------------------------------
  40. // Constructor
  41. CMIDIInDevice::CMIDIInHeader::CMIDIInHeader(HMIDIIN DevHandle,
  42.                                             LPSTR Buffer, 
  43.                                             DWORD BufferLength) :
  44. m_DevHandle(DevHandle)
  45. {
  46.     // Initialize header
  47.     m_MIDIHdr.lpData         = Buffer;
  48.     m_MIDIHdr.dwBufferLength = BufferLength;
  49.     m_MIDIHdr.dwFlags        = 0;
  50.     // Prepare header
  51.     MMRESULT Result = ::midiInPrepareHeader(DevHandle, &m_MIDIHdr,
  52.                                             sizeof m_MIDIHdr);
  53.     // If an error occurred, throw exception
  54.     if(Result != MMSYSERR_NOERROR)
  55.     {
  56.         throw CMIDIInException(Result);
  57.     }
  58. }
  59. // Destructor
  60. CMIDIInDevice::CMIDIInHeader::~CMIDIInHeader()
  61. {
  62.     ::midiInUnprepareHeader(m_DevHandle, &m_MIDIHdr, 
  63.                             sizeof m_MIDIHdr);
  64. }
  65. // Add system exclusive buffer to queue
  66. void CMIDIInDevice::CMIDIInHeader::AddSysExBuffer()
  67. {
  68.     MMRESULT Result = ::midiInAddBuffer(m_DevHandle, &m_MIDIHdr,
  69.                                         sizeof m_MIDIHdr);
  70.     // If an error occurred, throw exception
  71.     if(Result != MMSYSERR_NOERROR)
  72.     {
  73.         throw CMIDIInException(Result);
  74.     }
  75. }
  76. //--------------------------------------------------------------------
  77. // CHeaderQueue implementation
  78. //--------------------------------------------------------------------
  79. // Constructor
  80. CMIDIInDevice::CHeaderQueue::CHeaderQueue()
  81. {
  82.     ::InitializeCriticalSection(&m_CriticalSection);
  83. }
  84. // Destructor
  85. CMIDIInDevice::CHeaderQueue::~CHeaderQueue()
  86. {
  87.     RemoveAll();
  88.     ::DeleteCriticalSection(&m_CriticalSection);
  89. }
  90. // Add header to queue
  91. void CMIDIInDevice::CHeaderQueue::AddHeader(
  92.                                  CMIDIInDevice::CMIDIInHeader *Header)
  93. {
  94.     ::EnterCriticalSection(&m_CriticalSection);
  95.     m_HdrQueue.push(Header);
  96.     ::LeaveCriticalSection(&m_CriticalSection);
  97. }
  98. // Remove header from queue
  99. void CMIDIInDevice::CHeaderQueue::RemoveHeader()
  100. {
  101.     ::EnterCriticalSection(&m_CriticalSection);
  102.     if(!m_HdrQueue.empty())
  103.     {
  104.         delete m_HdrQueue.front();
  105.         m_HdrQueue.pop();
  106.     }
  107.     ::LeaveCriticalSection(&m_CriticalSection);
  108. }
  109. // Empty header queue
  110. void CMIDIInDevice::CHeaderQueue::RemoveAll()
  111. {
  112.     ::EnterCriticalSection(&m_CriticalSection);
  113.     while(!m_HdrQueue.empty())
  114.     {
  115.         delete m_HdrQueue.front();
  116.         m_HdrQueue.pop();
  117.     }
  118.     ::LeaveCriticalSection(&m_CriticalSection);
  119. }
  120. // Determines if the header queue is empty
  121. bool CMIDIInDevice::CHeaderQueue::IsEmpty()
  122. {
  123.     bool Result;
  124.     ::EnterCriticalSection(&m_CriticalSection);
  125.     Result = m_HdrQueue.empty();
  126.     ::LeaveCriticalSection(&m_CriticalSection);
  127.     return Result;
  128. }
  129. //--------------------------------------------------------------------
  130. // CMIDIInDevice implementation
  131. //--------------------------------------------------------------------
  132. // Constructs CMIDIInDevice object in an closed state
  133. CMIDIInDevice::CMIDIInDevice() :
  134. m_Receiver(0),
  135. m_State(CLOSED)
  136. {
  137.     // If we are unable to create signalling event, throw exception
  138.     if(!CreateEvent())
  139.     {
  140.         throw CMIDIInEventFailure();
  141.     }
  142. }
  143. // Constructs CMIDIInDevice object in an closed state and initializes the 
  144. // MIDI receiver
  145. CMIDIInDevice::CMIDIInDevice(CMIDIReceiver &Receiver) :
  146. m_Receiver(&Receiver),
  147. m_State(CLOSED)
  148. {
  149.     // If we are unable to create signalling event, throw exception
  150.     if(!CreateEvent())
  151.     {
  152.         throw CMIDIInEventFailure();
  153.     }
  154. }
  155. // Constructs CMIDIInDevice object in an opened state
  156. CMIDIInDevice::CMIDIInDevice(UINT DeviceId, CMIDIReceiver &Receiver) :
  157. m_Receiver(&Receiver),
  158. m_State(CLOSED)
  159. {
  160.     // Open device
  161.     Open(DeviceId);
  162.     // If we are unable to create signalling event, throw exception
  163.     if(!CreateEvent())
  164.     {
  165.         Close();
  166.         throw CMIDIInEventFailure();
  167.     }
  168. }
  169. // Destruction
  170. CMIDIInDevice::~CMIDIInDevice()
  171. {
  172.     // Close device
  173.     Close();
  174.     // Close handle to signalling event
  175.     ::CloseHandle(m_Event);
  176. }
  177. // Opens the MIDI input device
  178. void CMIDIInDevice::Open(UINT DeviceId)
  179. {
  180.     // Makes sure the previous device, if any, is closed before 
  181.     // opening another one
  182.     Close();
  183.     // Open MIDI input device
  184.     MMRESULT Result = ::midiInOpen(&m_DevHandle, DeviceId, 
  185.                                   reinterpret_cast<DWORD>(MidiInProc),
  186.                                   reinterpret_cast<DWORD>(this),
  187.                                   CALLBACK_FUNCTION);
  188.     // If we are able to open the device, change state
  189.     if(Result == MMSYSERR_NOERROR)
  190.     {
  191.         m_State = OPENED;
  192.     }
  193.     // Else opening failed, throw exception
  194.     else
  195.     {
  196.         throw CMIDIInException(Result);
  197.     }
  198. }
  199. // Closes the MIDI input device
  200. void CMIDIInDevice::Close()
  201. {
  202.     // If the device is recording, stop recording before closing the 
  203.     // device
  204.     if(m_State == RECORDING)
  205.     {
  206.         StopRecording();
  207.     }
  208.     // If the device is opened...
  209.     if(m_State == OPENED)
  210.     {
  211.         // Close the device
  212.         MMRESULT Result = ::midiInClose(m_DevHandle);
  213.         // If a failure occurred, throw exception
  214.         if(Result != MMSYSERR_NOERROR)
  215.         {
  216.             throw CMIDIInException(Result);
  217.         }
  218.         // Change state
  219.         m_State = CLOSED;
  220.     }
  221. }
  222. // Adds a buffer for receiving system exclusive messages
  223. void CMIDIInDevice::AddSysExBuffer(LPSTR Buffer, DWORD BufferLength)
  224. {
  225.     CMIDIInHeader *Header;
  226.     try
  227.     {
  228.         // Create new header
  229.         Header = new CMIDIInHeader(m_DevHandle, Buffer, BufferLength);
  230.     }
  231.     // If memory allocation failed, throw exception
  232.     catch(const std::bad_alloc &)
  233.     {
  234.         throw CMIDIInMemFailure();
  235.     }
  236.     // If preparation for the header failed, rethrow exception
  237.     catch(const CMIDIInDevice &)
  238.     {
  239.         throw;
  240.     }
  241.     try
  242.     {
  243.         // Add header to queue
  244.         Header->AddSysExBuffer();
  245.         m_HdrQueue.AddHeader(Header);
  246.     }
  247.     // If we are unable to add the buffer to the queue, delete header
  248.     // and throw exception
  249.     catch(const CMIDIInDevice &)
  250.     {
  251.         delete Header;
  252.         throw;
  253.     }
  254. }
  255. // Starts the recording process
  256. void CMIDIInDevice::StartRecording()
  257. {
  258.     // Only begin recording if the MIDI input device has been opened
  259.     if(m_State == OPENED)
  260.     { 
  261.         // Change state
  262.         m_State = RECORDING;
  263.         m_Thread = ::AfxBeginThread((AFX_THREADPROC)HeaderProc, this);
  264.         // Start recording
  265.         MMRESULT Result = ::midiInStart(m_DevHandle);
  266.         // If recording attempt failed...
  267.         if(Result != MMSYSERR_NOERROR)
  268.         {
  269.             // Revert back to opened state
  270.             m_State = OPENED;
  271.             // Signal the worker thread to finish
  272.             ::SetEvent(m_Event);
  273. ::WaitForSingleObject(m_Thread->m_hThread, INFINITE);
  274.             // Throw exception
  275.             throw CMIDIInException(Result);
  276.         }
  277.     }
  278. }
  279. // Stops the recording process
  280. void CMIDIInDevice::StopRecording()
  281. {
  282.     // If the device is in fact recording...
  283.     if(m_State == RECORDING)
  284.     {
  285.         // Change state
  286.         m_State = OPENED;
  287.         // Signal the worker thread to finish
  288.         ::SetEvent(m_Event);
  289. ::WaitForSingleObject(m_Thread->m_hThread, INFINITE);
  290.         // Reset the MIDI input device
  291.         ::midiInReset(m_DevHandle);
  292.         // Empty header queue
  293.         m_HdrQueue.RemoveAll();
  294.     }
  295. }
  296. // Registers the MIDI receiver. Returns the previous receiver.
  297. CMIDIReceiver *CMIDIInDevice::SetReceiver(CMIDIReceiver &Receiver)
  298. {
  299.     CMIDIReceiver *PrevReceiver = m_Receiver;
  300.     m_Receiver = &Receiver;
  301.     return PrevReceiver;
  302. }
  303. // Determines if the MIDI input device is opened
  304. bool CMIDIInDevice::IsOpen() const
  305. {
  306.     return ((m_State == OPENED) || (m_State == RECORDING));
  307. }
  308. // Determines if the MIDI input device is recording
  309. bool CMIDIInDevice::IsRecording() const
  310. {
  311.     return (m_State == RECORDING);
  312. }
  313. // Gets Id for this device
  314. UINT CMIDIInDevice::GetDevID() const
  315. {
  316.     UINT DeviceID;
  317.     MMRESULT Result = ::midiInGetID(m_DevHandle, &DeviceID);
  318.     if(Result != MMSYSERR_NOERROR)
  319.     {
  320.         throw CMIDIInException(Result);
  321.     }
  322.     return DeviceID;
  323. }
  324. // Gets the capabilities of a particular MIDI input device
  325. void CMIDIInDevice::GetDevCaps(UINT DeviceId, MIDIINCAPS &Caps)
  326. {
  327.     MMRESULT Result = ::midiInGetDevCaps(DeviceId, &Caps, 
  328.                                          sizeof Caps);
  329.     // If we are not able to retrieve device capabilities, throw 
  330.     // exception
  331.     if(Result != MMSYSERR_NOERROR)
  332.     {
  333.         throw CMIDIInException(Result);
  334.     }
  335. }
  336. // Creates event for signalling header thread
  337. bool CMIDIInDevice::CreateEvent()
  338. {
  339.     bool Result = true;
  340.     m_Event = ::CreateEvent(NULL, FALSE, FALSE, NULL);
  341.     // If event creation failed, record failure
  342.     if(m_Event == NULL)
  343.     {
  344.         Result = false;
  345.     }
  346.     return Result;
  347. }
  348. // Called by Windows when a MIDI input event occurs
  349. void CALLBACK CMIDIInDevice::MidiInProc(HMIDIIN MidiIn, UINT Msg,
  350.                                         DWORD Instance, DWORD Param1,
  351.                                         DWORD Param2)
  352. {
  353.     CMIDIInDevice *Device;
  354.     
  355.     Device = reinterpret_cast<CMIDIInDevice *>(Instance);
  356.     if(Device->m_Receiver != 0)
  357.     {
  358.         switch(Msg)
  359.         {
  360.         case MIM_DATA:      // Short message received
  361.             Device->m_Receiver->ReceiveMsg(Param1, Param2);
  362.             break;
  363.         case MIM_ERROR:     // Invalid short message received
  364.             Device->m_Receiver->OnError(Param1, Param2);
  365.             break;
  366.         case MIM_LONGDATA:  // System exclusive message received
  367.             if(Device->m_State == RECORDING)
  368.             {
  369.                 // Retrieve data, send it to receiver, and notify header
  370.                 // thread that we are done with the system exclusive 
  371.                 // message
  372.                 MIDIHDR *MidiHdr = reinterpret_cast<MIDIHDR *>(Param1);
  373.                 Device->m_Receiver->ReceiveMsg(MidiHdr->lpData, 
  374.                                                MidiHdr->dwBytesRecorded, 
  375.                                                Param2);
  376.                 ::SetEvent(Device->m_Event);
  377.             }
  378.             break;
  379.         case MIM_LONGERROR: // Invalid system exclusive message received
  380.             if(Device->m_State == RECORDING)
  381.             {
  382.                 // Retrieve data, send it to receiver, and notify header
  383.                 // thread that we are done with the system exclusive 
  384.                 // message
  385.                 MIDIHDR *MidiHdr = reinterpret_cast<MIDIHDR *>(Param1);
  386.                 Device->m_Receiver->OnError(MidiHdr->lpData,
  387.                                             MidiHdr->dwBytesRecorded,
  388.                                             Param2);
  389.                 ::SetEvent(Device->m_Event);
  390.             }
  391.             break;
  392.         }
  393.     }
  394. }
  395. // Header worker thread
  396. DWORD CMIDIInDevice::HeaderProc(LPVOID Parameter)
  397. {
  398.     CMIDIInDevice *Device; 
  399.     
  400.     Device = reinterpret_cast<CMIDIInDevice *>(Parameter);
  401.     // Continue while the MIDI input device is recording
  402.     while(Device->m_State == RECORDING)
  403.     {
  404.         ::WaitForSingleObject(Device->m_Event, INFINITE);
  405.         // Make sure we are still recording
  406.         if(Device->m_State == RECORDING)
  407.         {
  408.             // Remove the finished header
  409.             Device->m_HdrQueue.RemoveHeader();
  410.         }
  411.     }
  412.     return 0;
  413. }