MIDIInDevice.cpp
上传用户:fs3633
上传日期:2021-05-14
资源大小:909k
文件大小:13k
- /*******************************************************************************
- * MIDIInDevice.cpp - Implementation for CMIDIInDevice and related
- * classes.
- *
- * Copyright (C) 2002 Leslie Sanford
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Contact: jabberdabber@hotmail.com
- *
- * Last modified: 09/02/2003
- ******************************************************************************/
- //--------------------------------------------------------------------
- // Dependencies
- //--------------------------------------------------------------------
- #include "stdafx.h"
- #include "MIDIInDevice.h"
- #include "midi.h"
- //--------------------------------------------------------------------
- // Using declarations
- //--------------------------------------------------------------------
- using midi::CMIDIInDevice;
- using midi::CMIDIReceiver;
- using midi::CMIDIInException;
- //--------------------------------------------------------------------
- // CMIDIInHeader implementation
- //--------------------------------------------------------------------
- // Constructor
- CMIDIInDevice::CMIDIInHeader::CMIDIInHeader(HMIDIIN DevHandle,
- LPSTR Buffer,
- DWORD BufferLength) :
- m_DevHandle(DevHandle)
- {
- // Initialize header
- m_MIDIHdr.lpData = Buffer;
- m_MIDIHdr.dwBufferLength = BufferLength;
- m_MIDIHdr.dwFlags = 0;
- // Prepare header
- MMRESULT Result = ::midiInPrepareHeader(DevHandle, &m_MIDIHdr,
- sizeof m_MIDIHdr);
- // If an error occurred, throw exception
- if(Result != MMSYSERR_NOERROR)
- {
- throw CMIDIInException(Result);
- }
- }
- // Destructor
- CMIDIInDevice::CMIDIInHeader::~CMIDIInHeader()
- {
- ::midiInUnprepareHeader(m_DevHandle, &m_MIDIHdr,
- sizeof m_MIDIHdr);
- }
- // Add system exclusive buffer to queue
- void CMIDIInDevice::CMIDIInHeader::AddSysExBuffer()
- {
- MMRESULT Result = ::midiInAddBuffer(m_DevHandle, &m_MIDIHdr,
- sizeof m_MIDIHdr);
- // If an error occurred, throw exception
- if(Result != MMSYSERR_NOERROR)
- {
- throw CMIDIInException(Result);
- }
- }
- //--------------------------------------------------------------------
- // CHeaderQueue implementation
- //--------------------------------------------------------------------
- // Constructor
- CMIDIInDevice::CHeaderQueue::CHeaderQueue()
- {
- ::InitializeCriticalSection(&m_CriticalSection);
- }
- // Destructor
- CMIDIInDevice::CHeaderQueue::~CHeaderQueue()
- {
- RemoveAll();
- ::DeleteCriticalSection(&m_CriticalSection);
- }
- // Add header to queue
- void CMIDIInDevice::CHeaderQueue::AddHeader(
- CMIDIInDevice::CMIDIInHeader *Header)
- {
- ::EnterCriticalSection(&m_CriticalSection);
- m_HdrQueue.push(Header);
- ::LeaveCriticalSection(&m_CriticalSection);
- }
- // Remove header from queue
- void CMIDIInDevice::CHeaderQueue::RemoveHeader()
- {
- ::EnterCriticalSection(&m_CriticalSection);
- if(!m_HdrQueue.empty())
- {
- delete m_HdrQueue.front();
- m_HdrQueue.pop();
- }
- ::LeaveCriticalSection(&m_CriticalSection);
- }
- // Empty header queue
- void CMIDIInDevice::CHeaderQueue::RemoveAll()
- {
- ::EnterCriticalSection(&m_CriticalSection);
- while(!m_HdrQueue.empty())
- {
- delete m_HdrQueue.front();
- m_HdrQueue.pop();
- }
- ::LeaveCriticalSection(&m_CriticalSection);
- }
- // Determines if the header queue is empty
- bool CMIDIInDevice::CHeaderQueue::IsEmpty()
- {
- bool Result;
- ::EnterCriticalSection(&m_CriticalSection);
- Result = m_HdrQueue.empty();
- ::LeaveCriticalSection(&m_CriticalSection);
- return Result;
- }
- //--------------------------------------------------------------------
- // CMIDIInDevice implementation
- //--------------------------------------------------------------------
- // Constructs CMIDIInDevice object in an closed state
- CMIDIInDevice::CMIDIInDevice() :
- m_Receiver(0),
- m_State(CLOSED)
- {
- // If we are unable to create signalling event, throw exception
- if(!CreateEvent())
- {
- throw CMIDIInEventFailure();
- }
- }
- // Constructs CMIDIInDevice object in an closed state and initializes the
- // MIDI receiver
- CMIDIInDevice::CMIDIInDevice(CMIDIReceiver &Receiver) :
- m_Receiver(&Receiver),
- m_State(CLOSED)
- {
- // If we are unable to create signalling event, throw exception
- if(!CreateEvent())
- {
- throw CMIDIInEventFailure();
- }
- }
- // Constructs CMIDIInDevice object in an opened state
- CMIDIInDevice::CMIDIInDevice(UINT DeviceId, CMIDIReceiver &Receiver) :
- m_Receiver(&Receiver),
- m_State(CLOSED)
- {
- // Open device
- Open(DeviceId);
- // If we are unable to create signalling event, throw exception
- if(!CreateEvent())
- {
- Close();
- throw CMIDIInEventFailure();
- }
- }
- // Destruction
- CMIDIInDevice::~CMIDIInDevice()
- {
- // Close device
- Close();
- // Close handle to signalling event
- ::CloseHandle(m_Event);
- }
- // Opens the MIDI input device
- void CMIDIInDevice::Open(UINT DeviceId)
- {
- // Makes sure the previous device, if any, is closed before
- // opening another one
- Close();
- // Open MIDI input device
- MMRESULT Result = ::midiInOpen(&m_DevHandle, DeviceId,
- reinterpret_cast<DWORD>(MidiInProc),
- reinterpret_cast<DWORD>(this),
- CALLBACK_FUNCTION);
- // If we are able to open the device, change state
- if(Result == MMSYSERR_NOERROR)
- {
- m_State = OPENED;
- }
- // Else opening failed, throw exception
- else
- {
- throw CMIDIInException(Result);
- }
- }
- // Closes the MIDI input device
- void CMIDIInDevice::Close()
- {
- // If the device is recording, stop recording before closing the
- // device
- if(m_State == RECORDING)
- {
- StopRecording();
- }
- // If the device is opened...
- if(m_State == OPENED)
- {
- // Close the device
- MMRESULT Result = ::midiInClose(m_DevHandle);
- // If a failure occurred, throw exception
- if(Result != MMSYSERR_NOERROR)
- {
- throw CMIDIInException(Result);
- }
- // Change state
- m_State = CLOSED;
- }
- }
- // Adds a buffer for receiving system exclusive messages
- void CMIDIInDevice::AddSysExBuffer(LPSTR Buffer, DWORD BufferLength)
- {
- CMIDIInHeader *Header;
- try
- {
- // Create new header
- Header = new CMIDIInHeader(m_DevHandle, Buffer, BufferLength);
- }
- // If memory allocation failed, throw exception
- catch(const std::bad_alloc &)
- {
- throw CMIDIInMemFailure();
- }
- // If preparation for the header failed, rethrow exception
- catch(const CMIDIInDevice &)
- {
- throw;
- }
- try
- {
- // Add header to queue
- Header->AddSysExBuffer();
- m_HdrQueue.AddHeader(Header);
- }
- // If we are unable to add the buffer to the queue, delete header
- // and throw exception
- catch(const CMIDIInDevice &)
- {
- delete Header;
- throw;
- }
- }
- // Starts the recording process
- void CMIDIInDevice::StartRecording()
- {
- // Only begin recording if the MIDI input device has been opened
- if(m_State == OPENED)
- {
- // Change state
- m_State = RECORDING;
- m_Thread = ::AfxBeginThread((AFX_THREADPROC)HeaderProc, this);
- // Start recording
- MMRESULT Result = ::midiInStart(m_DevHandle);
- // If recording attempt failed...
- if(Result != MMSYSERR_NOERROR)
- {
- // Revert back to opened state
- m_State = OPENED;
- // Signal the worker thread to finish
- ::SetEvent(m_Event);
- ::WaitForSingleObject(m_Thread->m_hThread, INFINITE);
- // Throw exception
- throw CMIDIInException(Result);
- }
- }
- }
- // Stops the recording process
- void CMIDIInDevice::StopRecording()
- {
- // If the device is in fact recording...
- if(m_State == RECORDING)
- {
- // Change state
- m_State = OPENED;
- // Signal the worker thread to finish
- ::SetEvent(m_Event);
- ::WaitForSingleObject(m_Thread->m_hThread, INFINITE);
- // Reset the MIDI input device
- ::midiInReset(m_DevHandle);
- // Empty header queue
- m_HdrQueue.RemoveAll();
- }
- }
- // Registers the MIDI receiver. Returns the previous receiver.
- CMIDIReceiver *CMIDIInDevice::SetReceiver(CMIDIReceiver &Receiver)
- {
- CMIDIReceiver *PrevReceiver = m_Receiver;
- m_Receiver = &Receiver;
- return PrevReceiver;
- }
- // Determines if the MIDI input device is opened
- bool CMIDIInDevice::IsOpen() const
- {
- return ((m_State == OPENED) || (m_State == RECORDING));
- }
- // Determines if the MIDI input device is recording
- bool CMIDIInDevice::IsRecording() const
- {
- return (m_State == RECORDING);
- }
- // Gets Id for this device
- UINT CMIDIInDevice::GetDevID() const
- {
- UINT DeviceID;
- MMRESULT Result = ::midiInGetID(m_DevHandle, &DeviceID);
- if(Result != MMSYSERR_NOERROR)
- {
- throw CMIDIInException(Result);
- }
- return DeviceID;
- }
- // Gets the capabilities of a particular MIDI input device
- void CMIDIInDevice::GetDevCaps(UINT DeviceId, MIDIINCAPS &Caps)
- {
- MMRESULT Result = ::midiInGetDevCaps(DeviceId, &Caps,
- sizeof Caps);
- // If we are not able to retrieve device capabilities, throw
- // exception
- if(Result != MMSYSERR_NOERROR)
- {
- throw CMIDIInException(Result);
- }
- }
- // Creates event for signalling header thread
- bool CMIDIInDevice::CreateEvent()
- {
- bool Result = true;
- m_Event = ::CreateEvent(NULL, FALSE, FALSE, NULL);
- // If event creation failed, record failure
- if(m_Event == NULL)
- {
- Result = false;
- }
- return Result;
- }
- // Called by Windows when a MIDI input event occurs
- void CALLBACK CMIDIInDevice::MidiInProc(HMIDIIN MidiIn, UINT Msg,
- DWORD Instance, DWORD Param1,
- DWORD Param2)
- {
- CMIDIInDevice *Device;
-
- Device = reinterpret_cast<CMIDIInDevice *>(Instance);
- if(Device->m_Receiver != 0)
- {
- switch(Msg)
- {
- case MIM_DATA: // Short message received
- Device->m_Receiver->ReceiveMsg(Param1, Param2);
- break;
- case MIM_ERROR: // Invalid short message received
- Device->m_Receiver->OnError(Param1, Param2);
- break;
- case MIM_LONGDATA: // System exclusive message received
- if(Device->m_State == RECORDING)
- {
- // Retrieve data, send it to receiver, and notify header
- // thread that we are done with the system exclusive
- // message
- MIDIHDR *MidiHdr = reinterpret_cast<MIDIHDR *>(Param1);
- Device->m_Receiver->ReceiveMsg(MidiHdr->lpData,
- MidiHdr->dwBytesRecorded,
- Param2);
- ::SetEvent(Device->m_Event);
- }
- break;
- case MIM_LONGERROR: // Invalid system exclusive message received
- if(Device->m_State == RECORDING)
- {
- // Retrieve data, send it to receiver, and notify header
- // thread that we are done with the system exclusive
- // message
- MIDIHDR *MidiHdr = reinterpret_cast<MIDIHDR *>(Param1);
- Device->m_Receiver->OnError(MidiHdr->lpData,
- MidiHdr->dwBytesRecorded,
- Param2);
- ::SetEvent(Device->m_Event);
- }
- break;
- }
- }
- }
- // Header worker thread
- DWORD CMIDIInDevice::HeaderProc(LPVOID Parameter)
- {
- CMIDIInDevice *Device;
-
- Device = reinterpret_cast<CMIDIInDevice *>(Parameter);
- // Continue while the MIDI input device is recording
- while(Device->m_State == RECORDING)
- {
- ::WaitForSingleObject(Device->m_Event, INFINITE);
- // Make sure we are still recording
- if(Device->m_State == RECORDING)
- {
- // Remove the finished header
- Device->m_HdrQueue.RemoveHeader();
- }
- }
- return 0;
- }