Просмотр исходного кода

Removed Windows kernel streaming code because it was incomplete and uncompilable. The code still exists in the winks branch.

Gary Scavone 11 лет назад
Родитель
Сommit
d07f6011cf
5 измененных файлов с 6 добавлено и 1095 удалено
  1. 0 1045
      RtMidi.cpp
  2. 1 39
      RtMidi.h
  3. 3 10
      doc/doxygen/tutorial.txt
  4. 1 0
      doc/release.txt
  5. 1 1
      readme

+ 0 - 1045
RtMidi.cpp

@@ -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

+ 1 - 39
RtMidi.h

@@ -124,7 +124,6 @@ class RtMidi
     LINUX_ALSA,     /*!< The Advanced Linux Sound Architecture API. */
     UNIX_JACK,      /*!< The JACK Low-Latency MIDI Server API. */
     WINDOWS_MM,     /*!< The Microsoft Multimedia MIDI API. */
-    WINDOWS_KS,     /*!< The Microsoft Kernel Streaming MIDI API. */
     RTMIDI_DUMMY    /*!< A compilable but non-functional API. */
   };
 
@@ -566,7 +565,7 @@ inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback ) {
 //
 // **************************************************************** //
 
-#if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__) && !defined(__WINDOWS_KS__)
+#if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__)
   #define __RTMIDI_DUMMY__
 #endif
 
@@ -724,43 +723,6 @@ class MidiOutWinMM: public MidiOutApi
 
 #endif
 
-#if defined(__WINDOWS_KS__)
-
-class MidiInWinKS: public MidiInApi
-{
- public:
-  MidiInWinKS( const std::string clientName, unsigned int queueSizeLimit );
-  ~MidiInWinKS( void );
-  RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_KS; };
-  void openPort( unsigned int portNumber, const std::string portName );
-  void openVirtualPort( const std::string portName );
-  void closePort( void );
-  unsigned int getPortCount( void );
-  std::string getPortName( unsigned int portNumber );
-
- protected:
-  void initialize( const std::string& clientName );
-};
-
-class MidiOutWinKS: public MidiOutApi
-{
- public:
-  MidiOutWinKS( const std::string clientName );
-  ~MidiOutWinKS( void );
-  RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_KS; };
-  void openPort( unsigned int portNumber, const std::string portName );
-  void openVirtualPort( const std::string portName );
-  void closePort( void );
-  unsigned int getPortCount( void );
-  std::string getPortName( unsigned int portNumber );
-  void sendMessage( std::vector<unsigned char> *message );
-
- protected:
-  void initialize( const std::string& clientName );
-};
-
-#endif
-
 #if defined(__RTMIDI_DUMMY__)
 
 class MidiInDummy: public MidiInApi

+ 3 - 10
doc/doxygen/tutorial.txt

@@ -4,7 +4,7 @@
 
 \section intro Introduction
 
-RtMidi is a set of C++ classes (RtMidiIn, RtMidiOut and API-specific classes) that provides a common API (Application Programming Interface) for realtime MIDI input/output across Linux (ALSA & JACK), Macintosh OS X (CoreMIDI & JACK), and Windows (Multimedia Library & Kernel Streaming) operating systems.  RtMidi significantly simplifies the process of interacting with computer MIDI hardware and software.  It was designed with the following goals:
+RtMidi is a set of C++ classes (RtMidiIn, RtMidiOut and API-specific classes) that provides a common API (Application Programming Interface) for realtime MIDI input/output across Linux (ALSA & JACK), Macintosh OS X (CoreMIDI & JACK), and Windows (Multimedia Library) operating systems.  RtMidi significantly simplifies the process of interacting with computer MIDI hardware and software.  It was designed with the following goals:
 
 <UL>
   <LI>object oriented C++ design</LI>
@@ -19,7 +19,7 @@ MIDI input and output functionality are separated into two classes, RtMidiIn and
 
 \section whatsnew What's New (Version 2.1)
 
-A minor API change was made. The RtError class was renamed RtMidiError and embedded directly in RtMidi.h.  Thus, all references to RtError should be renamed to RtMidiError and the RtError.h file should be deleted.
+A minor API change was made. The RtError class was renamed RtMidiError and embedded directly in RtMidi.h.  Thus, all references to RtError should be renamed to RtMidiError and the RtError.h file should be deleted.  The Windows Kernel Streaming support was removed because it was uncompilable and incomplete.
 
 \section download Download
 
@@ -375,13 +375,6 @@ In order to compile RtMidi for a specific OS and API, it is necessary to supply
   <TD><TT>winmm.lib, multithreaded</TT></TD>
   <TD><I>compiler specific</I></TD>
 </TR>
-<TR>
-  <TD>Windows</TD>
-  <TD>Kernel Streaming</TD>
-  <TD>__WINDOWS_KS__</TD>
-  <TD><TT>ks.h, ksmedia.h, setupapi.lib, ksuser.lib, multithreaded</TT></TD>
-  <TD><I>compiler specific</I></TD>
-</TR>
 </TABLE>
 <P>
 
@@ -393,7 +386,7 @@ If you are having problems getting RtMidi to run on your system, try passing the
 
 \section multi Using Simultaneous Multiple APIs
 
-Support for each MIDI API is encapsulated in specific MidiInApi or MidiOutApi subclasses, making it possible to compile and instantiate multiple API-specific subclasses on a given operating system.  For example, one can compile both CoreMIDI and JACK support on the OS-X operating system by providing the appropriate preprocessor definitions for each.  In a run-time situation, one might first attempt to determine whether any JACK ports are available.  This can be done by specifying the api argument RtMidi::UNIX_JACK when attempting to create an instance of RtMidiIn or RtMidiOut.  If no available ports are found, then an instance of RtMidi with the api argument RtMidi::MACOSX_CORE can be created.  Alternately, if no api argument is specified, RtMidi will first look for JACK ports and if none are found, then CoreMIDI ports (in linux, the search order is JACK and then ALSA; in Windows, the search order is WinMM and then WinKS).  In theory, it should also be possible to have separate instances of RtMidi open at the same time with different underlying API support, though this has not been tested.
+Support for each MIDI API is encapsulated in specific MidiInApi or MidiOutApi subclasses, making it possible to compile and instantiate multiple API-specific subclasses on a given operating system.  For example, one can compile both CoreMIDI and JACK support on the OS-X operating system by providing the appropriate preprocessor definitions for each.  In a run-time situation, one might first attempt to determine whether any JACK ports are available.  This can be done by specifying the api argument RtMidi::UNIX_JACK when attempting to create an instance of RtMidiIn or RtMidiOut.  If no available ports are found, then an instance of RtMidi with the api argument RtMidi::MACOSX_CORE can be created.  Alternately, if no api argument is specified, RtMidi will first look for JACK ports and if none are found, then CoreMIDI ports (in linux, the search order is JACK and then ALSA.  In theory, it should also be possible to have separate instances of RtMidi open at the same time with different underlying API support, though this has not been tested.
 
 The static function RtMidi::getCompiledApi() is provided to determine the available compiled API support.  The function RtMidi::getCurrentApi() indicates the API selected for a given RtMidi instance.
 

+ 1 - 0
doc/release.txt

@@ -16,6 +16,7 @@ v2.1.0: (30 March 2014)
 - Windows update to avoid lockups when shutting down while sending/receiving sysex messages (ptarabbia)
 - OS-X fix to avoid empty messages in callbacks when ignoring sysex messages and split sysexes are received (codepainters)
 - ALSA openPort fix to better distinguish sender and receiver (Russell Smyth)
+- Windows Kernel Streaming support removed because it was uncompilable and incomplete
 
 v2.0.1: (26 July 2012)
 - small fixes for problems reported by Chris Arndt (scoping, preprocessor, and include)

+ 1 - 1
readme

@@ -1,4 +1,4 @@
-RtMidi - a set of C++ classes that provide a common API for realtime MIDI input/output across Linux (ALSA & JACK), Macintosh OS X (CoreMidi & JACK) and Windows (Multimedia & Kernel Streaming).
+RtMidi - a set of C++ classes that provide a common API for realtime MIDI input/output across Linux (ALSA & JACK), Macintosh OS X (CoreMidi & JACK) and Windows (Multimedia).
 
 By Gary P. Scavone, 2003-2014.