|
@@ -78,9 +78,6 @@ void RtMidi :: getCompiledApi( std::vector<RtMidi::Api> &apis ) throw()
|
|
|
#if defined(__WINDOWS_MM__)
|
|
|
apis.push_back( WINDOWS_MM );
|
|
|
#endif
|
|
|
-#if defined(__WINDOWS_KS__)
|
|
|
- apis.push_back( WINDOWS_KS );
|
|
|
-#endif
|
|
|
#if defined(__RTMIDI_DUMMY__)
|
|
|
apis.push_back( RTMIDI_DUMMY );
|
|
|
#endif
|
|
@@ -108,10 +105,6 @@ void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string clientName, uns
|
|
|
if ( api == WINDOWS_MM )
|
|
|
rtapi_ = new MidiInWinMM( clientName, queueSizeLimit );
|
|
|
#endif
|
|
|
-#if defined(__WINDOWS_KS__)
|
|
|
- if ( api == WINDOWS_KS )
|
|
|
- rtapi_ = new MidiInWinKS( clientName, queueSizeLimit );
|
|
|
-#endif
|
|
|
#if defined(__MACOSX_CORE__)
|
|
|
if ( api == MACOSX_CORE )
|
|
|
rtapi_ = new MidiInCore( clientName, queueSizeLimit );
|
|
@@ -181,10 +174,6 @@ void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string clientName )
|
|
|
if ( api == WINDOWS_MM )
|
|
|
rtapi_ = new MidiOutWinMM( clientName );
|
|
|
#endif
|
|
|
-#if defined(__WINDOWS_KS__)
|
|
|
- if ( api == WINDOWS_KS )
|
|
|
- rtapi_ = new MidiOutWinKS( clientName );
|
|
|
-#endif
|
|
|
#if defined(__MACOSX_CORE__)
|
|
|
if ( api == MACOSX_CORE )
|
|
|
rtapi_ = new MidiOutCore( clientName );
|
|
@@ -2434,1040 +2423,6 @@ void MidiOutWinMM :: sendMessage( std::vector<unsigned char> *message )
|
|
|
|
|
|
#endif // __WINDOWS_MM__
|
|
|
|
|
|
-// *********************************************************************//
|
|
|
-// API: WINDOWS Kernel Streaming
|
|
|
-//
|
|
|
-// Written by Sebastien Alaiwan, 2012.
|
|
|
-//
|
|
|
-// NOTE BY GARY: much of the KS-specific code below probably should go in a separate file.
|
|
|
-//
|
|
|
-// *********************************************************************//
|
|
|
-
|
|
|
-#if defined(__WINDOWS_KS__)
|
|
|
-
|
|
|
-#include <string>
|
|
|
-#include <vector>
|
|
|
-#include <memory>
|
|
|
-#include <stdexcept>
|
|
|
-#include <sstream>
|
|
|
-#include <windows.h>
|
|
|
-#include <setupapi.h>
|
|
|
-#include <mmsystem.h>
|
|
|
-
|
|
|
-#include "ks.h"
|
|
|
-#include "ksmedia.h"
|
|
|
-
|
|
|
-#define INSTANTIATE_GUID(a) GUID const a = { STATIC_ ## a }
|
|
|
-
|
|
|
-INSTANTIATE_GUID(GUID_NULL);
|
|
|
-INSTANTIATE_GUID(KSPROPSETID_Pin);
|
|
|
-INSTANTIATE_GUID(KSPROPSETID_Connection);
|
|
|
-INSTANTIATE_GUID(KSPROPSETID_Topology);
|
|
|
-INSTANTIATE_GUID(KSINTERFACESETID_Standard);
|
|
|
-INSTANTIATE_GUID(KSMEDIUMSETID_Standard);
|
|
|
-INSTANTIATE_GUID(KSDATAFORMAT_TYPE_MUSIC);
|
|
|
-INSTANTIATE_GUID(KSDATAFORMAT_SUBTYPE_MIDI);
|
|
|
-INSTANTIATE_GUID(KSDATAFORMAT_SPECIFIER_NONE);
|
|
|
-
|
|
|
-#undef INSTANTIATE_GUID
|
|
|
-
|
|
|
-typedef std::basic_string<TCHAR> tstring;
|
|
|
-
|
|
|
-inline bool IsValid(HANDLE handle)
|
|
|
-{
|
|
|
- return handle != NULL && handle != INVALID_HANDLE_VALUE;
|
|
|
-}
|
|
|
-
|
|
|
-class ComException : public std::runtime_error
|
|
|
-{
|
|
|
-private:
|
|
|
- static std::string MakeString(std::string const& s, HRESULT hr)
|
|
|
- {
|
|
|
- std::stringstream ss;
|
|
|
- ss << "(error 0x" << std::hex << hr << ")";
|
|
|
- return s + ss.str();
|
|
|
- }
|
|
|
-
|
|
|
-public:
|
|
|
- ComException(std::string const& s, HRESULT hr) :
|
|
|
- std::runtime_error(MakeString(s, hr))
|
|
|
- {
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-template<typename TFilterType>
|
|
|
-class CKsEnumFilters
|
|
|
-{
|
|
|
-public:
|
|
|
- ~CKsEnumFilters()
|
|
|
- {
|
|
|
- DestroyLists();
|
|
|
- }
|
|
|
-
|
|
|
- void EnumFilters(GUID const* categories, size_t numCategories)
|
|
|
- {
|
|
|
- DestroyLists();
|
|
|
-
|
|
|
- if (categories == 0)
|
|
|
- throw std::runtime_error("CKsEnumFilters: invalid argument");
|
|
|
-
|
|
|
- // Get a handle to the device set specified by the guid
|
|
|
- HDEVINFO hDevInfo = ::SetupDiGetClassDevs(&categories[0], NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
|
|
- if (!IsValid(hDevInfo))
|
|
|
- throw std::runtime_error("CKsEnumFilters: no devices found");
|
|
|
-
|
|
|
- // Loop through members of the set and get details for each
|
|
|
- for ( int iClassMember=0; iClassMember++ ) {
|
|
|
- try {
|
|
|
- SP_DEVICE_INTERFACE_DATA DID;
|
|
|
- DID.cbSize = sizeof(DID);
|
|
|
- DID.Reserved = 0;
|
|
|
-
|
|
|
- bool fRes = ::SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &categories[0], iClassMember, &DID);
|
|
|
- if (!fRes)
|
|
|
- break;
|
|
|
-
|
|
|
- // Get filter friendly name
|
|
|
- HKEY hRegKey = ::SetupDiOpenDeviceInterfaceRegKey(hDevInfo, &DID, 0, KEY_READ);
|
|
|
- if (hRegKey == INVALID_HANDLE_VALUE)
|
|
|
- throw std::runtime_error("CKsEnumFilters: interface has no registry");
|
|
|
-
|
|
|
- char friendlyName[256];
|
|
|
- DWORD dwSize = sizeof friendlyName;
|
|
|
- LONG lval = ::RegQueryValueEx(hRegKey, TEXT("FriendlyName"), NULL, NULL, (LPBYTE)friendlyName, &dwSize);
|
|
|
- ::RegCloseKey(hRegKey);
|
|
|
- if (lval != ERROR_SUCCESS)
|
|
|
- throw std::runtime_error("CKsEnumFilters: interface has no friendly name");
|
|
|
-
|
|
|
- // Get details for the device registered in this class
|
|
|
- DWORD const cbItfDetails = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + MAX_PATH * sizeof(WCHAR);
|
|
|
- std::vector<BYTE> buffer(cbItfDetails);
|
|
|
-
|
|
|
- SP_DEVICE_INTERFACE_DETAIL_DATA* pDevInterfaceDetails = reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(&buffer[0]);
|
|
|
- pDevInterfaceDetails->cbSize = sizeof(*pDevInterfaceDetails);
|
|
|
-
|
|
|
- SP_DEVINFO_DATA DevInfoData;
|
|
|
- DevInfoData.cbSize = sizeof(DevInfoData);
|
|
|
- DevInfoData.Reserved = 0;
|
|
|
-
|
|
|
- fRes = ::SetupDiGetDeviceInterfaceDetail(hDevInfo, &DID, pDevInterfaceDetails, cbItfDetails, NULL, &DevInfoData);
|
|
|
- if (!fRes)
|
|
|
- throw std::runtime_error("CKsEnumFilters: could not get interface details");
|
|
|
-
|
|
|
- // check additional category guids which may (or may not) have been supplied
|
|
|
- for (size_t i=1; i < numCategories; ++i) {
|
|
|
- SP_DEVICE_INTERFACE_DATA DIDAlias;
|
|
|
- DIDAlias.cbSize = sizeof(DIDAlias);
|
|
|
- DIDAlias.Reserved = 0;
|
|
|
-
|
|
|
- fRes = ::SetupDiGetDeviceInterfaceAlias(hDevInfo, &DID, &categories[i], &DIDAlias);
|
|
|
- if (!fRes)
|
|
|
- throw std::runtime_error("CKsEnumFilters: could not get interface alias");
|
|
|
-
|
|
|
- // Check if the this interface alias is enabled.
|
|
|
- if (!DIDAlias.Flags || (DIDAlias.Flags & SPINT_REMOVED))
|
|
|
- throw std::runtime_error("CKsEnumFilters: interface alias is not enabled");
|
|
|
- }
|
|
|
-
|
|
|
- std::auto_ptr<TFilterType> pFilter(new TFilterType(pDevInterfaceDetails->DevicePath, friendlyName));
|
|
|
-
|
|
|
- pFilter->Instantiate();
|
|
|
- pFilter->FindMidiPins();
|
|
|
- pFilter->Validate();
|
|
|
-
|
|
|
- m_Filters.push_back(pFilter.release());
|
|
|
- }
|
|
|
- catch (std::runtime_error const& e) {
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- ::SetupDiDestroyDeviceInfoList(hDevInfo);
|
|
|
- }
|
|
|
-
|
|
|
-private:
|
|
|
- void DestroyLists()
|
|
|
- {
|
|
|
- for (size_t i=0;i < m_Filters.size();++i)
|
|
|
- delete m_Filters[i];
|
|
|
- m_Filters.clear();
|
|
|
- }
|
|
|
-
|
|
|
-public:
|
|
|
- // TODO: make this private.
|
|
|
- std::vector<TFilterType*> m_Filters;
|
|
|
-};
|
|
|
-
|
|
|
-class CKsObject
|
|
|
-{
|
|
|
-public:
|
|
|
- CKsObject(HANDLE handle) : m_handle(handle)
|
|
|
- {
|
|
|
- }
|
|
|
-
|
|
|
-protected:
|
|
|
- HANDLE m_handle;
|
|
|
-
|
|
|
- void SetProperty(REFGUID guidPropertySet, ULONG nProperty, void* pvValue, ULONG cbValue)
|
|
|
- {
|
|
|
- KSPROPERTY ksProperty;
|
|
|
- memset(&ksProperty, 0, sizeof ksProperty);
|
|
|
- ksProperty.Set = guidPropertySet;
|
|
|
- ksProperty.Id = nProperty;
|
|
|
- ksProperty.Flags = KSPROPERTY_TYPE_SET;
|
|
|
-
|
|
|
- HRESULT hr = DeviceIoControlKsProperty(ksProperty, pvValue, cbValue);
|
|
|
- if (FAILED(hr))
|
|
|
- throw ComException("CKsObject::SetProperty: could not set property", hr);
|
|
|
- }
|
|
|
-
|
|
|
-private:
|
|
|
-
|
|
|
- HRESULT DeviceIoControlKsProperty(KSPROPERTY& ksProperty, void* pvValue, ULONG cbValue)
|
|
|
- {
|
|
|
- ULONG ulReturned;
|
|
|
- return ::DeviceIoControl(
|
|
|
- m_handle,
|
|
|
- IOCTL_KS_PROPERTY,
|
|
|
- &ksProperty,
|
|
|
- sizeof(ksProperty),
|
|
|
- pvValue,
|
|
|
- cbValue,
|
|
|
- &ulReturned,
|
|
|
- NULL);
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-class CKsPin;
|
|
|
-
|
|
|
-class CKsFilter : public CKsObject
|
|
|
-{
|
|
|
- friend class CKsPin;
|
|
|
-
|
|
|
-public:
|
|
|
- CKsFilter(tstring const& name, std::string const& sFriendlyName);
|
|
|
- virtual ~CKsFilter();
|
|
|
-
|
|
|
- virtual void Instantiate();
|
|
|
-
|
|
|
- template<typename T>
|
|
|
- T GetPinProperty(ULONG nPinId, ULONG nProperty)
|
|
|
- {
|
|
|
- ULONG ulReturned = 0;
|
|
|
- T value;
|
|
|
-
|
|
|
- KSP_PIN ksPProp;
|
|
|
- ksPProp.Property.Set = KSPROPSETID_Pin;
|
|
|
- ksPProp.Property.Id = nProperty;
|
|
|
- ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
|
|
|
- ksPProp.PinId = nPinId;
|
|
|
- ksPProp.Reserved = 0;
|
|
|
-
|
|
|
- HRESULT hr = ::DeviceIoControl(
|
|
|
- m_handle,
|
|
|
- IOCTL_KS_PROPERTY,
|
|
|
- &ksPProp,
|
|
|
- sizeof(KSP_PIN),
|
|
|
- &value,
|
|
|
- sizeof(value),
|
|
|
- &ulReturned,
|
|
|
- NULL);
|
|
|
- if (FAILED(hr))
|
|
|
- throw ComException("CKsFilter::GetPinProperty: failed to retrieve property", hr);
|
|
|
-
|
|
|
- return value;
|
|
|
- }
|
|
|
-
|
|
|
- void GetPinPropertyMulti(ULONG nPinId, REFGUID guidPropertySet, ULONG nProperty, PKSMULTIPLE_ITEM* ppKsMultipleItem)
|
|
|
- {
|
|
|
- HRESULT hr;
|
|
|
-
|
|
|
- KSP_PIN ksPProp;
|
|
|
- ksPProp.Property.Set = guidPropertySet;
|
|
|
- ksPProp.Property.Id = nProperty;
|
|
|
- ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
|
|
|
- ksPProp.PinId = nPinId;
|
|
|
- ksPProp.Reserved = 0;
|
|
|
-
|
|
|
- ULONG cbMultipleItem = 0;
|
|
|
- hr = ::DeviceIoControl(m_handle,
|
|
|
- IOCTL_KS_PROPERTY,
|
|
|
- &ksPProp.Property,
|
|
|
- sizeof(KSP_PIN),
|
|
|
- NULL,
|
|
|
- 0,
|
|
|
- &cbMultipleItem,
|
|
|
- NULL);
|
|
|
- if (FAILED(hr))
|
|
|
- throw ComException("CKsFilter::GetPinPropertyMulti: cannot get property", hr);
|
|
|
-
|
|
|
- *ppKsMultipleItem = (PKSMULTIPLE_ITEM) new BYTE[cbMultipleItem];
|
|
|
-
|
|
|
- ULONG ulReturned = 0;
|
|
|
- hr = ::DeviceIoControl(
|
|
|
- m_handle,
|
|
|
- IOCTL_KS_PROPERTY,
|
|
|
- &ksPProp,
|
|
|
- sizeof(KSP_PIN),
|
|
|
- (PVOID)*ppKsMultipleItem,
|
|
|
- cbMultipleItem,
|
|
|
- &ulReturned,
|
|
|
- NULL);
|
|
|
- if (FAILED(hr))
|
|
|
- throw ComException("CKsFilter::GetPinPropertyMulti: cannot get property", hr);
|
|
|
- }
|
|
|
-
|
|
|
- std::string const& GetFriendlyName() const
|
|
|
- {
|
|
|
- return m_sFriendlyName;
|
|
|
- }
|
|
|
-
|
|
|
-protected:
|
|
|
-
|
|
|
- std::vector<CKsPin*> m_Pins; // this list owns the pins.
|
|
|
-
|
|
|
- std::vector<CKsPin*> m_RenderPins;
|
|
|
- std::vector<CKsPin*> m_CapturePins;
|
|
|
-
|
|
|
-private:
|
|
|
- std::string const m_sFriendlyName; // friendly name eg "Virus TI Synth"
|
|
|
- tstring const m_sName; // Filter path, eg "\\?\usb#vid_133e&pid_0815...\vtimidi02"
|
|
|
-};
|
|
|
-
|
|
|
-class CKsPin : public CKsObject
|
|
|
-{
|
|
|
-public:
|
|
|
- CKsPin(CKsFilter* pFilter, ULONG nId);
|
|
|
- virtual ~CKsPin();
|
|
|
-
|
|
|
- virtual void Instantiate();
|
|
|
-
|
|
|
- void ClosePin();
|
|
|
-
|
|
|
- void SetState(KSSTATE ksState);
|
|
|
-
|
|
|
- void WriteData(KSSTREAM_HEADER* pKSSTREAM_HEADER, OVERLAPPED* pOVERLAPPED);
|
|
|
- void ReadData(KSSTREAM_HEADER* pKSSTREAM_HEADER, OVERLAPPED* pOVERLAPPED);
|
|
|
-
|
|
|
- KSPIN_DATAFLOW GetDataFlow() const
|
|
|
- {
|
|
|
- return m_DataFlow;
|
|
|
- }
|
|
|
-
|
|
|
- bool IsSink() const
|
|
|
- {
|
|
|
- return m_Communication == KSPIN_COMMUNICATION_SINK
|
|
|
- || m_Communication == KSPIN_COMMUNICATION_BOTH;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-protected:
|
|
|
- PKSPIN_CONNECT m_pKsPinConnect; // creation parameters of pin
|
|
|
- CKsFilter* const m_pFilter;
|
|
|
-
|
|
|
- ULONG m_cInterfaces;
|
|
|
- PKSIDENTIFIER m_pInterfaces;
|
|
|
- PKSMULTIPLE_ITEM m_pmiInterfaces;
|
|
|
-
|
|
|
- ULONG m_cMediums;
|
|
|
- PKSIDENTIFIER m_pMediums;
|
|
|
- PKSMULTIPLE_ITEM m_pmiMediums;
|
|
|
-
|
|
|
- ULONG m_cDataRanges;
|
|
|
- PKSDATARANGE m_pDataRanges;
|
|
|
- PKSMULTIPLE_ITEM m_pmiDataRanges;
|
|
|
-
|
|
|
- KSPIN_DATAFLOW m_DataFlow;
|
|
|
- KSPIN_COMMUNICATION m_Communication;
|
|
|
-};
|
|
|
-
|
|
|
-CKsFilter::CKsFilter(tstring const& sName, std::string const& sFriendlyName) :
|
|
|
- CKsObject(INVALID_HANDLE_VALUE),
|
|
|
- m_sFriendlyName(sFriendlyName),
|
|
|
- m_sName(sName)
|
|
|
-{
|
|
|
- if (sName.empty())
|
|
|
- throw std::runtime_error("CKsFilter::CKsFilter: name can't be empty");
|
|
|
-}
|
|
|
-
|
|
|
-CKsFilter::~CKsFilter()
|
|
|
-{
|
|
|
- for (size_t i=0;i < m_Pins.size();++i)
|
|
|
- delete m_Pins[i];
|
|
|
-
|
|
|
- if (IsValid(m_handle))
|
|
|
- ::CloseHandle(m_handle);
|
|
|
-}
|
|
|
-
|
|
|
-void CKsFilter::Instantiate()
|
|
|
-{
|
|
|
- m_handle = CreateFile(
|
|
|
- m_sName.c_str(),
|
|
|
- GENERIC_READ | GENERIC_WRITE,
|
|
|
- 0,
|
|
|
- NULL,
|
|
|
- OPEN_EXISTING,
|
|
|
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
|
|
- NULL);
|
|
|
-
|
|
|
- if (!IsValid(m_handle))
|
|
|
- {
|
|
|
- DWORD const dwError = GetLastError();
|
|
|
- throw ComException("CKsFilter::Instantiate: can't open driver", HRESULT_FROM_WIN32(dwError));
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-CKsPin::CKsPin(CKsFilter* pFilter, ULONG PinId) :
|
|
|
- CKsObject(INVALID_HANDLE_VALUE),
|
|
|
- m_pKsPinConnect(NULL),
|
|
|
- m_pFilter(pFilter)
|
|
|
-{
|
|
|
- m_Communication = m_pFilter->GetPinProperty<KSPIN_COMMUNICATION>(PinId, KSPROPERTY_PIN_COMMUNICATION);
|
|
|
- m_DataFlow = m_pFilter->GetPinProperty<KSPIN_DATAFLOW>(PinId, KSPROPERTY_PIN_DATAFLOW);
|
|
|
-
|
|
|
- // Interfaces
|
|
|
- m_pFilter->GetPinPropertyMulti(
|
|
|
- PinId,
|
|
|
- KSPROPSETID_Pin,
|
|
|
- KSPROPERTY_PIN_INTERFACES,
|
|
|
- &m_pmiInterfaces);
|
|
|
-
|
|
|
- m_cInterfaces = m_pmiInterfaces->Count;
|
|
|
- m_pInterfaces = (PKSPIN_INTERFACE)(m_pmiInterfaces + 1);
|
|
|
-
|
|
|
- // Mediums
|
|
|
- m_pFilter->GetPinPropertyMulti(
|
|
|
- PinId,
|
|
|
- KSPROPSETID_Pin,
|
|
|
- KSPROPERTY_PIN_MEDIUMS,
|
|
|
- &m_pmiMediums);
|
|
|
-
|
|
|
- m_cMediums = m_pmiMediums->Count;
|
|
|
- m_pMediums = (PKSPIN_MEDIUM)(m_pmiMediums + 1);
|
|
|
-
|
|
|
- // Data ranges
|
|
|
- m_pFilter->GetPinPropertyMulti(
|
|
|
- PinId,
|
|
|
- KSPROPSETID_Pin,
|
|
|
- KSPROPERTY_PIN_DATARANGES,
|
|
|
- &m_pmiDataRanges);
|
|
|
-
|
|
|
- m_cDataRanges = m_pmiDataRanges->Count;
|
|
|
- m_pDataRanges = (PKSDATARANGE)(m_pmiDataRanges + 1);
|
|
|
-}
|
|
|
-
|
|
|
-CKsPin::~CKsPin()
|
|
|
-{
|
|
|
- ClosePin();
|
|
|
-
|
|
|
- delete[] (BYTE*)m_pKsPinConnect;
|
|
|
- delete[] (BYTE*)m_pmiDataRanges;
|
|
|
- delete[] (BYTE*)m_pmiInterfaces;
|
|
|
- delete[] (BYTE*)m_pmiMediums;
|
|
|
-}
|
|
|
-
|
|
|
-void CKsPin::ClosePin()
|
|
|
-{
|
|
|
- if (IsValid(m_handle)) {
|
|
|
- SetState(KSSTATE_STOP);
|
|
|
- ::CloseHandle(m_handle);
|
|
|
- }
|
|
|
- m_handle = INVALID_HANDLE_VALUE;
|
|
|
-}
|
|
|
-
|
|
|
-void CKsPin::SetState(KSSTATE ksState)
|
|
|
-{
|
|
|
- SetProperty(KSPROPSETID_Connection, KSPROPERTY_CONNECTION_STATE, &ksState, sizeof(ksState));
|
|
|
-}
|
|
|
-
|
|
|
-void CKsPin::Instantiate()
|
|
|
-{
|
|
|
- if (!m_pKsPinConnect)
|
|
|
- throw std::runtime_error("CKsPin::Instanciate: abstract pin");
|
|
|
-
|
|
|
- DWORD const dwResult = KsCreatePin(m_pFilter->m_handle, m_pKsPinConnect, GENERIC_WRITE | GENERIC_READ, &m_handle);
|
|
|
- if (dwResult != ERROR_SUCCESS)
|
|
|
- throw ComException("CKsMidiCapFilter::CreateRenderPin: Pin instanciation failed", HRESULT_FROM_WIN32(dwResult));
|
|
|
-}
|
|
|
-
|
|
|
-void CKsPin::WriteData(KSSTREAM_HEADER* pKSSTREAM_HEADER, OVERLAPPED* pOVERLAPPED)
|
|
|
-{
|
|
|
- DWORD cbWritten;
|
|
|
- BOOL fRes = ::DeviceIoControl(
|
|
|
- m_handle,
|
|
|
- IOCTL_KS_WRITE_STREAM,
|
|
|
- NULL,
|
|
|
- 0,
|
|
|
- pKSSTREAM_HEADER,
|
|
|
- pKSSTREAM_HEADER->Size,
|
|
|
- &cbWritten,
|
|
|
- pOVERLAPPED);
|
|
|
- if (!fRes) {
|
|
|
- DWORD const dwError = GetLastError();
|
|
|
- if (dwError != ERROR_IO_PENDING)
|
|
|
- throw ComException("CKsPin::WriteData: DeviceIoControl failed", HRESULT_FROM_WIN32(dwError));
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void CKsPin::ReadData(KSSTREAM_HEADER* pKSSTREAM_HEADER, OVERLAPPED* pOVERLAPPED)
|
|
|
-{
|
|
|
- DWORD cbReturned;
|
|
|
- BOOL fRes = ::DeviceIoControl(
|
|
|
- m_handle,
|
|
|
- IOCTL_KS_READ_STREAM,
|
|
|
- NULL,
|
|
|
- 0,
|
|
|
- pKSSTREAM_HEADER,
|
|
|
- pKSSTREAM_HEADER->Size,
|
|
|
- &cbReturned,
|
|
|
- pOVERLAPPED);
|
|
|
- if (!fRes) {
|
|
|
- DWORD const dwError = GetLastError();
|
|
|
- if (dwError != ERROR_IO_PENDING)
|
|
|
- throw ComException("CKsPin::ReadData: DeviceIoControl failed", HRESULT_FROM_WIN32(dwError));
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-class CKsMidiFilter : public CKsFilter
|
|
|
-{
|
|
|
-public:
|
|
|
- void FindMidiPins();
|
|
|
-
|
|
|
-protected:
|
|
|
- CKsMidiFilter(tstring const& sPath, std::string const& sFriendlyName);
|
|
|
-};
|
|
|
-
|
|
|
-class CKsMidiPin : public CKsPin
|
|
|
-{
|
|
|
-public:
|
|
|
- CKsMidiPin(CKsFilter* pFilter, ULONG nId);
|
|
|
-};
|
|
|
-
|
|
|
-class CKsMidiRenFilter : public CKsMidiFilter
|
|
|
-{
|
|
|
-public:
|
|
|
- CKsMidiRenFilter(tstring const& sPath, std::string const& sFriendlyName);
|
|
|
- CKsMidiPin* CreateRenderPin();
|
|
|
-
|
|
|
- void Validate()
|
|
|
- {
|
|
|
- if (m_RenderPins.empty())
|
|
|
- throw std::runtime_error("Could not find a MIDI render pin");
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-class CKsMidiCapFilter : public CKsMidiFilter
|
|
|
-{
|
|
|
-public:
|
|
|
- CKsMidiCapFilter(tstring const& sPath, std::string const& sFriendlyName);
|
|
|
- CKsMidiPin* CreateCapturePin();
|
|
|
-
|
|
|
- void Validate()
|
|
|
- {
|
|
|
- if (m_CapturePins.empty())
|
|
|
- throw std::runtime_error("Could not find a MIDI capture pin");
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-CKsMidiFilter::CKsMidiFilter(tstring const& sPath, std::string const& sFriendlyName) :
|
|
|
- CKsFilter(sPath, sFriendlyName)
|
|
|
-{
|
|
|
-}
|
|
|
-
|
|
|
-void CKsMidiFilter::FindMidiPins()
|
|
|
-{
|
|
|
- ULONG numPins = GetPinProperty<ULONG>(0, KSPROPERTY_PIN_CTYPES);
|
|
|
-
|
|
|
- for (ULONG iPin = 0; iPin < numPins; ++iPin) {
|
|
|
- try {
|
|
|
- KSPIN_COMMUNICATION com = GetPinProperty<KSPIN_COMMUNICATION>(iPin, KSPROPERTY_PIN_COMMUNICATION);
|
|
|
- if (com != KSPIN_COMMUNICATION_SINK && com != KSPIN_COMMUNICATION_BOTH)
|
|
|
- throw std::runtime_error("Unknown pin communication value");
|
|
|
-
|
|
|
- m_Pins.push_back(new CKsMidiPin(this, iPin));
|
|
|
- }
|
|
|
- catch (std::runtime_error const&) {
|
|
|
- // pin instanciation has failed, continue to the next pin.
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- m_RenderPins.clear();
|
|
|
- m_CapturePins.clear();
|
|
|
-
|
|
|
- for (size_t i = 0; i < m_Pins.size(); ++i) {
|
|
|
- CKsPin* const pPin = m_Pins[i];
|
|
|
-
|
|
|
- if (pPin->IsSink()) {
|
|
|
- if (pPin->GetDataFlow() == KSPIN_DATAFLOW_IN)
|
|
|
- m_RenderPins.push_back(pPin);
|
|
|
- else
|
|
|
- m_CapturePins.push_back(pPin);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (m_RenderPins.empty() && m_CapturePins.empty())
|
|
|
- throw std::runtime_error("No valid pins found on the filter.");
|
|
|
-}
|
|
|
-
|
|
|
-CKsMidiRenFilter::CKsMidiRenFilter(tstring const& sPath, std::string const& sFriendlyName) :
|
|
|
- CKsMidiFilter(sPath, sFriendlyName)
|
|
|
-{
|
|
|
-}
|
|
|
-
|
|
|
-CKsMidiPin* CKsMidiRenFilter::CreateRenderPin()
|
|
|
-{
|
|
|
- if (m_RenderPins.empty())
|
|
|
- throw std::runtime_error("Could not find a MIDI render pin");
|
|
|
-
|
|
|
- CKsMidiPin* pPin = (CKsMidiPin*)m_RenderPins[0];
|
|
|
- pPin->Instantiate();
|
|
|
- return pPin;
|
|
|
-}
|
|
|
-
|
|
|
-CKsMidiCapFilter::CKsMidiCapFilter(tstring const& sPath, std::string const& sFriendlyName) :
|
|
|
- CKsMidiFilter(sPath, sFriendlyName)
|
|
|
-{
|
|
|
-}
|
|
|
-
|
|
|
-CKsMidiPin* CKsMidiCapFilter::CreateCapturePin()
|
|
|
-{
|
|
|
- if (m_CapturePins.empty())
|
|
|
- throw std::runtime_error("Could not find a MIDI capture pin");
|
|
|
-
|
|
|
- CKsMidiPin* pPin = (CKsMidiPin*)m_CapturePins[0];
|
|
|
- pPin->Instantiate();
|
|
|
- return pPin;
|
|
|
-}
|
|
|
-
|
|
|
-CKsMidiPin::CKsMidiPin(CKsFilter* pFilter, ULONG nId) :
|
|
|
- CKsPin(pFilter, nId)
|
|
|
-{
|
|
|
- DWORD const cbPinCreateSize = sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT);
|
|
|
- m_pKsPinConnect = (PKSPIN_CONNECT) new BYTE[cbPinCreateSize];
|
|
|
-
|
|
|
- m_pKsPinConnect->Interface.Set = KSINTERFACESETID_Standard;
|
|
|
- m_pKsPinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
|
|
|
- m_pKsPinConnect->Interface.Flags = 0;
|
|
|
- m_pKsPinConnect->Medium.Set = KSMEDIUMSETID_Standard;
|
|
|
- m_pKsPinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
|
|
|
- m_pKsPinConnect->Medium.Flags = 0;
|
|
|
- m_pKsPinConnect->PinId = nId;
|
|
|
- m_pKsPinConnect->PinToHandle = NULL;
|
|
|
- m_pKsPinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
|
|
|
- m_pKsPinConnect->Priority.PrioritySubClass = 1;
|
|
|
-
|
|
|
- // point m_pDataFormat to just after the pConnect struct
|
|
|
- KSDATAFORMAT* m_pDataFormat = (KSDATAFORMAT*)(m_pKsPinConnect + 1);
|
|
|
- m_pDataFormat->FormatSize = sizeof(KSDATAFORMAT);
|
|
|
- m_pDataFormat->Flags = 0;
|
|
|
- m_pDataFormat->SampleSize = 0;
|
|
|
- m_pDataFormat->Reserved = 0;
|
|
|
- m_pDataFormat->MajorFormat = GUID(KSDATAFORMAT_TYPE_MUSIC);
|
|
|
- m_pDataFormat->SubFormat = GUID(KSDATAFORMAT_SUBTYPE_MIDI);
|
|
|
- m_pDataFormat->Specifier = GUID(KSDATAFORMAT_SPECIFIER_NONE);
|
|
|
-
|
|
|
- bool hasStdStreamingInterface = false;
|
|
|
- bool hasStdStreamingMedium = false;
|
|
|
-
|
|
|
- for ( ULONG i = 0; i < m_cInterfaces; i++ ) {
|
|
|
- if (m_pInterfaces[i].Set == KSINTERFACESETID_Standard
|
|
|
- && m_pInterfaces[i].Id == KSINTERFACE_STANDARD_STREAMING)
|
|
|
- hasStdStreamingInterface = true;
|
|
|
- }
|
|
|
-
|
|
|
- for (ULONG i = 0; i < m_cMediums; i++) {
|
|
|
- if (m_pMediums[i].Set == KSMEDIUMSETID_Standard
|
|
|
- && m_pMediums[i].Id == KSMEDIUM_STANDARD_DEVIO)
|
|
|
- hasStdStreamingMedium = true;
|
|
|
- }
|
|
|
-
|
|
|
- if (!hasStdStreamingInterface) // No standard streaming interfaces on the pin
|
|
|
- throw std::runtime_error("CKsMidiPin::CKsMidiPin: no standard streaming interface");
|
|
|
-
|
|
|
- if (!hasStdStreamingMedium) // No standard streaming mediums on the pin
|
|
|
- throw std::runtime_error("CKsMidiPin::CKsMidiPin: no standard streaming medium");
|
|
|
-
|
|
|
- bool hasMidiDataRange = false;
|
|
|
-
|
|
|
- BYTE const* pDataRangePtr = reinterpret_cast<BYTE const*>(m_pDataRanges);
|
|
|
-
|
|
|
- for (ULONG i = 0; i < m_cDataRanges; ++i) {
|
|
|
- KSDATARANGE const* pDataRange = reinterpret_cast<KSDATARANGE const*>(pDataRangePtr);
|
|
|
-
|
|
|
- if (pDataRange->SubFormat == KSDATAFORMAT_SUBTYPE_MIDI) {
|
|
|
- hasMidiDataRange = true;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- pDataRangePtr += pDataRange->FormatSize;
|
|
|
- }
|
|
|
-
|
|
|
- if (!hasMidiDataRange) // No MIDI dataranges on the pin
|
|
|
- throw std::runtime_error("CKsMidiPin::CKsMidiPin: no MIDI datarange");
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-struct WindowsKsData
|
|
|
-{
|
|
|
- WindowsKsData() : m_pPin(NULL), m_Buffer(1024), m_hInputThread(NULL)
|
|
|
- {
|
|
|
- memset(&overlapped, 0, sizeof(OVERLAPPED));
|
|
|
- m_hExitEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
- overlapped.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
- m_hInputThread = NULL;
|
|
|
- }
|
|
|
-
|
|
|
- ~WindowsKsData()
|
|
|
- {
|
|
|
- ::CloseHandle(overlapped.hEvent);
|
|
|
- ::CloseHandle(m_hExitEvent);
|
|
|
- }
|
|
|
-
|
|
|
- OVERLAPPED overlapped;
|
|
|
- CKsPin* m_pPin;
|
|
|
- std::vector<unsigned char> m_Buffer;
|
|
|
- std::auto_ptr<CKsEnumFilters<CKsMidiCapFilter> > m_pCaptureEnum;
|
|
|
- std::auto_ptr<CKsEnumFilters<CKsMidiRenFilter> > m_pRenderEnum;
|
|
|
- HANDLE m_hInputThread;
|
|
|
- HANDLE m_hExitEvent;
|
|
|
-};
|
|
|
-
|
|
|
-// *********************************************************************//
|
|
|
-// API: WINDOWS Kernel Streaming
|
|
|
-// Class Definitions: MidiInWinKS
|
|
|
-// *********************************************************************//
|
|
|
-
|
|
|
-static DWORD WINAPI midiKsInputThread(VOID* pUser)
|
|
|
-{
|
|
|
- MidiInApi::RtMidiInData* data = static_cast<MidiInApi::RtMidiInData*>(pUser);
|
|
|
- WindowsKsData* apiData = static_cast<WindowsKsData*>(data->apiData);
|
|
|
-
|
|
|
- HANDLE hEvents[] = { apiData->overlapped.hEvent, apiData->m_hExitEvent };
|
|
|
-
|
|
|
- while ( true ) {
|
|
|
- KSSTREAM_HEADER packet;
|
|
|
- memset(&packet, 0, sizeof packet);
|
|
|
- packet.Size = sizeof(KSSTREAM_HEADER);
|
|
|
- packet.PresentationTime.Time = 0;
|
|
|
- packet.PresentationTime.Numerator = 1;
|
|
|
- packet.PresentationTime.Denominator = 1;
|
|
|
- packet.Data = &apiData->m_Buffer[0];
|
|
|
- packet.DataUsed = 0;
|
|
|
- packet.FrameExtent = apiData->m_Buffer.size();
|
|
|
- apiData->m_pPin->ReadData(&packet, &apiData->overlapped);
|
|
|
-
|
|
|
- DWORD dwRet = ::WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
|
|
|
-
|
|
|
- if ( dwRet == WAIT_OBJECT_0 ) {
|
|
|
- // parse packet
|
|
|
- unsigned char* pData = (unsigned char*)packet.Data;
|
|
|
- unsigned int iOffset = 0;
|
|
|
-
|
|
|
- while ( iOffset < packet.DataUsed ) {
|
|
|
- KSMUSICFORMAT* pMusic = (KSMUSICFORMAT*)&pData[iOffset];
|
|
|
- iOffset += sizeof(KSMUSICFORMAT);
|
|
|
-
|
|
|
- MidiInApi::MidiMessage message;
|
|
|
- message.timeStamp = 0;
|
|
|
- for(size_t i=0;i < pMusic->ByteCount;++i)
|
|
|
- message.bytes.push_back(pData[iOffset+i]);
|
|
|
-
|
|
|
- if ( data->usingCallback ) {
|
|
|
- RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback)data->userCallback;
|
|
|
- callback(message.timeStamp, &message.bytes, data->userData);
|
|
|
- }
|
|
|
- else {
|
|
|
- // As long as we haven't reached our queue size limit, push the message.
|
|
|
- if ( data->queue.size < data->queue.ringSize ) {
|
|
|
- data->queue.ring[data->queue.back++] = message;
|
|
|
- if(data->queue.back == data->queue.ringSize)
|
|
|
- data->queue.back = 0;
|
|
|
- data->queue.size++;
|
|
|
- }
|
|
|
- else
|
|
|
- std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
|
|
|
- }
|
|
|
-
|
|
|
- iOffset += pMusic->ByteCount;
|
|
|
-
|
|
|
- // re-align on 32 bits
|
|
|
- if ( iOffset % 4 != 0 )
|
|
|
- iOffset += (4 - iOffset % 4);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- break;
|
|
|
- }
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-MidiInWinKS :: MidiInWinKS( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
|
|
|
-{
|
|
|
- initialize( clientName );
|
|
|
-}
|
|
|
-
|
|
|
-void MidiInWinKS :: initialize( const std::string& clientName )
|
|
|
-{
|
|
|
- WindowsKsData* data = new WindowsKsData;
|
|
|
- apiData_ = (void*)data;
|
|
|
- inputData_.apiData = data;
|
|
|
-
|
|
|
- GUID const aguidEnumCats[] =
|
|
|
- {
|
|
|
- { STATIC_KSCATEGORY_AUDIO }, { STATIC_KSCATEGORY_CAPTURE }
|
|
|
- };
|
|
|
- data->m_pCaptureEnum.reset(new CKsEnumFilters<CKsMidiCapFilter> );
|
|
|
- data->m_pCaptureEnum->EnumFilters(aguidEnumCats, 2);
|
|
|
-}
|
|
|
-
|
|
|
-MidiInWinKS :: ~MidiInWinKS()
|
|
|
-{
|
|
|
- WindowsKsData* data = static_cast<WindowsKsData*>(apiData_);
|
|
|
- try {
|
|
|
- if ( data->m_pPin )
|
|
|
- closePort();
|
|
|
- }
|
|
|
- catch(...) {
|
|
|
- }
|
|
|
-
|
|
|
- delete data;
|
|
|
-}
|
|
|
-
|
|
|
-void MidiInWinKS :: openPort( unsigned int portNumber, const std::string portName )
|
|
|
-{
|
|
|
- WindowsKsData* data = static_cast<WindowsKsData*>(apiData_);
|
|
|
-
|
|
|
- if ( portNumber < 0 || portNumber >= data->m_pCaptureEnum->m_Filters.size() ) {
|
|
|
- std::stringstream ost;
|
|
|
- ost << "MidiInWinKS::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
|
|
- errorString_ = ost.str();
|
|
|
- error( RtMidiError::WARNING, errorString_ );
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- CKsMidiCapFilter* pFilter = data->m_pCaptureEnum->m_Filters[portNumber];
|
|
|
- data->m_pPin = pFilter->CreateCapturePin();
|
|
|
-
|
|
|
- if ( data->m_pPin == NULL ) {
|
|
|
- std::stringstream ost;
|
|
|
- ost << "MidiInWinKS::openPort: KS error opening port (could not create pin)";
|
|
|
- errorString_ = ost.str();
|
|
|
- error( RtMidiError::WARNING, errorString_ );
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- data->m_pPin->SetState(KSSTATE_RUN);
|
|
|
-
|
|
|
- DWORD threadId;
|
|
|
- data->m_hInputThread = ::CreateThread(NULL, 0, &midiKsInputThread, &inputData_, 0, &threadId);
|
|
|
- if ( data->m_hInputThread == NULL ) {
|
|
|
- std::stringstream ost;
|
|
|
- ost << "MidiInWinKS::openPort: Could not create input thread : Windows error " << GetLastError() << std::endl;;
|
|
|
- errorString_ = ost.str();
|
|
|
- error( RtMidiError::WARNING, errorString_ );
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- connected_ = true;
|
|
|
-}
|
|
|
-
|
|
|
-void MidiInWinKS :: openVirtualPort( const std::string portName )
|
|
|
-{
|
|
|
- // This function cannot be implemented for the Windows KS MIDI API.
|
|
|
- errorString_ = "MidiInWinKS::openVirtualPort: cannot be implemented in Windows KS MIDI API!";
|
|
|
- error( RtMidiError::WARNING, errorString_ );
|
|
|
-}
|
|
|
-
|
|
|
-unsigned int MidiInWinKS :: getPortCount()
|
|
|
-{
|
|
|
- WindowsKsData* data = static_cast<WindowsKsData*>(apiData_);
|
|
|
- return (unsigned int)data->m_pCaptureEnum->m_Filters.size();
|
|
|
-}
|
|
|
-
|
|
|
-std::string MidiInWinKS :: getPortName(unsigned int portNumber)
|
|
|
-{
|
|
|
- WindowsKsData* data = static_cast<WindowsKsData*>(apiData_);
|
|
|
-
|
|
|
- if (portNumber < 0 || portNumber >= data->m_pCaptureEnum->m_Filters.size()) {
|
|
|
- std::stringstream ost;
|
|
|
- ost << "MidiInWinKS::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
|
|
- errorString_ = ost.str();
|
|
|
- error( RtMidiError::WARNING, errorString_ );
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- CKsMidiCapFilter* pFilter = data->m_pCaptureEnum->m_Filters[portNumber];
|
|
|
- return pFilter->GetFriendlyName();
|
|
|
-}
|
|
|
-
|
|
|
-void MidiInWinKS :: closePort()
|
|
|
-{
|
|
|
- WindowsKsData* data = static_cast<WindowsKsData*>(apiData_);
|
|
|
- connected_ = false;
|
|
|
-
|
|
|
- if (data->m_hInputThread) {
|
|
|
- ::SignalObjectAndWait(data->m_hExitEvent, data->m_hInputThread, INFINITE, FALSE);
|
|
|
- ::CloseHandle(data->m_hInputThread);
|
|
|
- }
|
|
|
-
|
|
|
- if (data->m_pPin) {
|
|
|
- data->m_pPin->SetState(KSSTATE_PAUSE);
|
|
|
- data->m_pPin->SetState(KSSTATE_STOP);
|
|
|
- data->m_pPin->ClosePin();
|
|
|
- data->m_pPin = NULL;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// *********************************************************************//
|
|
|
-// API: WINDOWS Kernel Streaming
|
|
|
-// Class Definitions: MidiOutWinKS
|
|
|
-// *********************************************************************//
|
|
|
-
|
|
|
-MidiOutWinKS :: MidiOutWinKS( const std::string clientName ) : MidiOutApi()
|
|
|
-{
|
|
|
- initialize( clientName );
|
|
|
-}
|
|
|
-
|
|
|
-void MidiOutWinKS :: initialize( const std::string& clientName )
|
|
|
-{
|
|
|
- WindowsKsData* data = new WindowsKsData;
|
|
|
-
|
|
|
- data->m_pPin = NULL;
|
|
|
- data->m_pRenderEnum.reset(new CKsEnumFilters<CKsMidiRenFilter> );
|
|
|
- GUID const aguidEnumCats[] =
|
|
|
- {
|
|
|
- { STATIC_KSCATEGORY_AUDIO }, { STATIC_KSCATEGORY_RENDER }
|
|
|
- };
|
|
|
- data->m_pRenderEnum->EnumFilters(aguidEnumCats, 2);
|
|
|
-
|
|
|
- apiData_ = (void*)data;
|
|
|
-}
|
|
|
-
|
|
|
-MidiOutWinKS :: ~MidiOutWinKS()
|
|
|
-{
|
|
|
- // Close a connection if it exists.
|
|
|
- closePort();
|
|
|
-
|
|
|
- // Cleanup.
|
|
|
- WindowsKsData* data = static_cast<WindowsKsData*>(apiData_);
|
|
|
- delete data;
|
|
|
-}
|
|
|
-
|
|
|
-void MidiOutWinKS :: openPort( unsigned int portNumber, const std::string portName )
|
|
|
-{
|
|
|
- WindowsKsData* data = static_cast<WindowsKsData*>(apiData_);
|
|
|
-
|
|
|
- if (portNumber < 0 || portNumber >= data->m_pRenderEnum->m_Filters.size()) {
|
|
|
- std::stringstream ost;
|
|
|
- ost << "MidiOutWinKS::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
|
|
- errorString_ = ost.str();
|
|
|
- error( RtMidiError::WARNING, errorString_ );
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- CKsMidiRenFilter* pFilter = data->m_pRenderEnum->m_Filters[portNumber];
|
|
|
- data->m_pPin = pFilter->CreateRenderPin();
|
|
|
-
|
|
|
- if (data->m_pPin == NULL) {
|
|
|
- std::stringstream ost;
|
|
|
- ost << "MidiOutWinKS::openPort: KS error opening port (could not create pin)";
|
|
|
- errorString_ = ost.str();
|
|
|
- error( RtMidiError::WARNING, errorString_ );
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- data->m_pPin->SetState(KSSTATE_RUN);
|
|
|
- connected_ = true;
|
|
|
-}
|
|
|
-
|
|
|
-void MidiOutWinKS :: openVirtualPort( const std::string portName )
|
|
|
-{
|
|
|
- // This function cannot be implemented for the Windows KS MIDI API.
|
|
|
- errorString_ = "MidiOutWinKS::openVirtualPort: cannot be implemented in Windows KS MIDI API!";
|
|
|
- error( RtMidiError::WARNING, errorString_ );
|
|
|
-}
|
|
|
-
|
|
|
-unsigned int MidiOutWinKS :: getPortCount()
|
|
|
-{
|
|
|
- WindowsKsData* data = static_cast<WindowsKsData*>(apiData_);
|
|
|
- return (unsigned int)data->m_pRenderEnum->m_Filters.size();
|
|
|
-}
|
|
|
-
|
|
|
-std::string MidiOutWinKS :: getPortName( unsigned int portNumber )
|
|
|
-{
|
|
|
- WindowsKsData* data = static_cast<WindowsKsData*>(apiData_);
|
|
|
-
|
|
|
- if ( portNumber < 0 || portNumber >= data->m_pRenderEnum->m_Filters.size() ) {
|
|
|
- std::stringstream ost;
|
|
|
- ost << "MidiOutWinKS::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
|
|
- errorString_ = ost.str();
|
|
|
- error( RtMidiError::WARNING, errorString_ );
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- CKsMidiRenFilter* pFilter = data->m_pRenderEnum->m_Filters[portNumber];
|
|
|
- return pFilter->GetFriendlyName();
|
|
|
-}
|
|
|
-
|
|
|
-void MidiOutWinKS :: closePort()
|
|
|
-{
|
|
|
- WindowsKsData* data = static_cast<WindowsKsData*>(apiData_);
|
|
|
- connected_ = false;
|
|
|
-
|
|
|
- if ( data->m_pPin ) {
|
|
|
- data->m_pPin->SetState(KSSTATE_PAUSE);
|
|
|
- data->m_pPin->SetState(KSSTATE_STOP);
|
|
|
- data->m_pPin->ClosePin();
|
|
|
- data->m_pPin = NULL;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void MidiOutWinKS :: sendMessage(std::vector<unsigned char>* pMessage)
|
|
|
-{
|
|
|
- std::vector<unsigned char> const& msg = *pMessage;
|
|
|
- WindowsKsData* data = static_cast<WindowsKsData*>(apiData_);
|
|
|
- size_t iNumMidiBytes = msg.size();
|
|
|
- size_t pos = 0;
|
|
|
-
|
|
|
- // write header
|
|
|
- KSMUSICFORMAT* pKsMusicFormat = reinterpret_cast<KSMUSICFORMAT*>(&data->m_Buffer[pos]);
|
|
|
- pKsMusicFormat->TimeDeltaMs = 0;
|
|
|
- pKsMusicFormat->ByteCount = iNumMidiBytes;
|
|
|
- pos += sizeof(KSMUSICFORMAT);
|
|
|
-
|
|
|
- // write MIDI bytes
|
|
|
- if ( pos + iNumMidiBytes > data->m_Buffer.size() ) {
|
|
|
- std::stringstream ost;
|
|
|
- ost << "KsMidiInput::Write: MIDI buffer too small. Required " << pos + iNumMidiBytes << " bytes, only has " << data->m_Buffer.size();
|
|
|
- errorString_ = ost.str();
|
|
|
- error( RtMidiError::WARNING, errorString_ );
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if ( data->m_pPin == NULL ) {
|
|
|
- std::stringstream ost;
|
|
|
- ost << "MidiOutWinKS::sendMessage: port is not open";
|
|
|
- errorString_ = ost.str();
|
|
|
- error( RtMidiError::WARNING, errorString_ );
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- memcpy(&data->m_Buffer[pos], &msg[0], iNumMidiBytes);
|
|
|
- pos += iNumMidiBytes;
|
|
|
-
|
|
|
- KSSTREAM_HEADER packet;
|
|
|
- memset(&packet, 0, sizeof packet);
|
|
|
- packet.Size = sizeof(packet);
|
|
|
- packet.PresentationTime.Time = 0;
|
|
|
- packet.PresentationTime.Numerator = 1;
|
|
|
- packet.PresentationTime.Denominator = 1;
|
|
|
- packet.Data = const_cast<unsigned char*>(&data->m_Buffer[0]);
|
|
|
- packet.DataUsed = ((pos+3)/4)*4;
|
|
|
- packet.FrameExtent = data->m_Buffer.size();
|
|
|
-
|
|
|
- data->m_pPin->WriteData(&packet, NULL);
|
|
|
-}
|
|
|
-
|
|
|
-#endif // __WINDOWS_KS__
|
|
|
|
|
|
//*********************************************************************//
|
|
|
// API: UNIX JACK
|