Index: build/premake/ext-rtmidi.lua
===================================================================
--- build/premake/ext-rtmidi.lua	(nonexistent)
+++ build/premake/ext-rtmidi.lua	(working copy)
@@ -0,0 +1,26 @@
+ 
+ project "rtmidi"
+  uuid "05BBD03D-0985-4D76-8DDD-534DA631C3A8"
+  language "C++"
+  location ( "../../build/" .. mpt_projectpathname .. "/ext" )
+  mpt_projectname = "rtmidi"
+  dofile "../../build/premake/premake-defaults-LIBorDLL.lua"
+  dofile "../../build/premake/premake-defaults.lua"
+  dofile "../../build/premake/premake-defaults-winver.lua"
+  targetname "openmpt-rtmidi"
+	filter {}
+	filter { "action:vs*" }
+		characterset "Unicode"
+	filter {}
+  files {
+   "../../include/rtmidi/RtMidi.cpp"
+  }
+  files {
+   "../../include/rtmidi/RtMidi.h"
+  }
+  defines {
+   "__WINDOWS_MM__"
+  }
+  links {
+   "winmm"
+  }

Property changes on: build/premake/ext-rtmidi.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/x-lua
\ No newline at end of property
Index: build/premake/mpt-OpenMPT-VSTi.lua
===================================================================
--- build/premake/mpt-OpenMPT-VSTi.lua	(revision 7555)
+++ build/premake/mpt-OpenMPT-VSTi.lua	(working copy)
@@ -87,8 +87,8 @@
    "opus",
    "opusfile",
    "portaudio",
-   "portmidi",
    "r8brain",
+   "rtmidi",
    "soundtouch",
    "vorbis",
   }
Index: build/premake/mpt-OpenMPT.lua
===================================================================
--- build/premake/mpt-OpenMPT.lua	(revision 7555)
+++ build/premake/mpt-OpenMPT.lua	(working copy)
@@ -122,8 +122,8 @@
    "opus",
    "opusfile",
    "portaudio",
-   "portmidi",
    "r8brain",
+   "rtmidi",
    "soundtouch",
    "vorbis",
   }
Index: build/premake/premake.lua
===================================================================
--- build/premake/premake.lua	(revision 7555)
+++ build/premake/premake.lua	(working copy)
@@ -376,8 +376,8 @@
  dofile "../../build/premake/ext-opus.lua"
  dofile "../../build/premake/ext-opusfile.lua"
  dofile "../../build/premake/ext-portaudio.lua"
- dofile "../../build/premake/ext-portmidi.lua"
  dofile "../../build/premake/ext-r8brain.lua"
+ dofile "../../build/premake/ext-rtmidi.lua"
  dofile "../../build/premake/ext-smbPitchShift.lua"
  dofile "../../build/premake/ext-soundtouch.lua"
  dofile "../../build/premake/ext-UnRAR.lua"
@@ -405,8 +405,8 @@
  dofile "../../build/premake/ext-opus.lua"
  dofile "../../build/premake/ext-opusfile.lua"
  dofile "../../build/premake/ext-portaudio.lua"
- dofile "../../build/premake/ext-portmidi.lua"
  dofile "../../build/premake/ext-r8brain.lua"
+ dofile "../../build/premake/ext-rtmidi.lua"
  dofile "../../build/premake/ext-smbPitchShift.lua"
  dofile "../../build/premake/ext-soundtouch.lua"
  dofile "../../build/premake/ext-UnRAR.lua"
@@ -429,8 +429,8 @@
  dofile "../../build/premake/ext-opus.lua"
  dofile "../../build/premake/ext-opusfile.lua"
  dofile "../../build/premake/ext-portaudio.lua"
- dofile "../../build/premake/ext-portmidi.lua"
  dofile "../../build/premake/ext-r8brain.lua"
+ dofile "../../build/premake/ext-rtmidi.lua"
  dofile "../../build/premake/ext-smbPitchShift.lua"
  dofile "../../build/premake/ext-soundtouch.lua"
  dofile "../../build/premake/ext-UnRAR.lua"
@@ -455,8 +455,8 @@
  dofile "../../build/premake/ext-opus.lua"
  dofile "../../build/premake/ext-opusfile.lua"
  dofile "../../build/premake/ext-portaudio.lua"
- dofile "../../build/premake/ext-portmidi.lua"
  dofile "../../build/premake/ext-r8brain.lua"
+ dofile "../../build/premake/ext-rtmidi.lua"
  dofile "../../build/premake/ext-smbPitchShift.lua"
  dofile "../../build/premake/ext-soundtouch.lua"
  dofile "../../build/premake/ext-UnRAR.lua"
@@ -481,8 +481,8 @@
  dofile "../../build/premake/ext-opus.lua"
  dofile "../../build/premake/ext-opusfile.lua"
  dofile "../../build/premake/ext-portaudio.lua"
- dofile "../../build/premake/ext-portmidi.lua"
  dofile "../../build/premake/ext-pugixml.lua"
+ dofile "../../build/premake/ext-rtmidi.lua"
  dofile "../../build/premake/ext-r8brain.lua"
  dofile "../../build/premake/ext-smbPitchShift.lua"
  dofile "../../build/premake/ext-soundtouch.lua"
Index: plugins/MidiInOut/MidiInOut.cpp
===================================================================
--- plugins/MidiInOut/MidiInOut.cpp	(revision 7555)
+++ plugins/MidiInOut/MidiInOut.cpp	(working copy)
@@ -20,27 +20,27 @@
 OPENMPT_NAMESPACE_BEGIN
 
 
-int MidiInOut::numInstances = 0;
-
 IMixPlugin* MidiInOut::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
 //------------------------------------------------------------------------------------------------
 {
-	return new (std::nothrow) MidiInOut(factory, sndFile, mixStruct);
+	try
+	{
+		return new (std::nothrow) MidiInOut(factory, sndFile, mixStruct);
+	} catch(RtMidiError &)
+	{
+		return nullptr;
+	}
 }
 
 
 MidiInOut::MidiInOut(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
 	: IMidiPlugin(factory, sndFile, mixStruct)
-	, latencyCompensation(true)
-	, programName(_T("Default"))
+	, m_latencyCompensation(true)
+	, m_programName(_T("Default"))
+	, inputDevice(midiIn)
+	, outputDevice(midiOut)
 //---------------------------------------------------------------------------------------
 {
-	if(!numInstances++)
-	{
-		Pt_Start(1, nullptr, nullptr);
-		Pm_Initialize();
-	}
-
 	m_mixBuffer.Initialize(2, 2);
 	InsertIntoFactoryList();
 }
@@ -50,13 +50,6 @@
 //---------------------
 {
 	Suspend();
-
-	if(--numInstances == 0)
-	{
-		// This terminates MIDI output for all instances of the plugin, so only ever do it if this was the only instance left.
-		Pm_Terminate();
-		Pt_Stop();
-	}
 }
 
 
@@ -93,7 +86,7 @@
 size_t MidiInOut::GetChunk(char *(&chunk), bool /*isBank*/)
 //---------------------------------------------------------
 {
-	const std::string programName8 = mpt::ToCharset(mpt::CharsetUTF8, programName);
+	const std::string programName8 = mpt::ToCharset(mpt::CharsetUTF8, m_programName);
 	const std::string inputName8 = mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetLocale, inputDevice.name);
 	const std::string outputName8 = mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetLocale, outputDevice.name);
 
@@ -106,13 +99,13 @@
 	mpt::IO::WriteIntLE<uint32>(s, static_cast<uint32>(inputDevice.name.size()));
 	mpt::IO::WriteIntLE<uint32>(s, outputDevice.index);
 	mpt::IO::WriteIntLE<uint32>(s, static_cast<uint32>(outputDevice.name.size()));
-	mpt::IO::WriteIntLE<uint32>(s, latencyCompensation);
+	mpt::IO::WriteIntLE<uint32>(s, m_latencyCompensation);
 	mpt::IO::WriteRaw(s, programName8.c_str(), programName8.size());
 	mpt::IO::WriteRaw(s, inputName8.c_str(), inputName8.size());
 	mpt::IO::WriteRaw(s, outputName8.c_str(), outputName8.size());
-	chunkData = s.str();
-	chunk = const_cast<char *>(chunkData.c_str());
-	return chunkData.size();
+	m_chunkData = s.str();
+	chunk = const_cast<char *>(m_chunkData.c_str());
+	return m_chunkData.size();
 }
 
 
@@ -127,28 +120,33 @@
 		return;
 
 	uint32 nameStrSize = file.ReadUint32LE();
-	PmDeviceID inID = file.ReadUint32LE();
+	MidiDevice::ID inID = file.ReadUint32LE();
 	uint32 inStrSize = file.ReadUint32LE();
-	PmDeviceID outID = file.ReadUint32LE();
+	MidiDevice::ID outID = file.ReadUint32LE();
 	uint32 outStrSize = file.ReadUint32LE();
-	latencyCompensation = file.ReadUint32LE() != 0;
+	m_latencyCompensation = file.ReadUint32LE() != 0;
 
 	std::string s;
 	file.ReadString<mpt::String::maybeNullTerminated>(s, nameStrSize);
-	programName = mpt::ToCString(mpt::CharsetUTF8, s);
+	m_programName = mpt::ToCString(mpt::CharsetUTF8, s);
 
 	file.ReadString<mpt::String::maybeNullTerminated>(s, inStrSize);
 	s = mpt::ToCharset(mpt::CharsetLocale, mpt::CharsetUTF8, s);
-	if(s != GetDeviceName(inID) || !IsInputDevice(inID))
+	if(s != inputDevice.GetPortName(inID))
 	{
 		// Stored name differs from actual device name - try finding another device with the same name.
-		const PmDeviceInfo *device;
-		for(PmDeviceID i = 0; (device = Pm_GetDeviceInfo(i)) != nullptr; i++)
+		unsigned int ports = midiIn.getPortCount();
+		for(unsigned int i = 0; i < ports; i++)
 		{
-			if(device->input && s == device->name)
+			try
 			{
-				inID = i;
-				break;
+				if(s == inputDevice.GetPortName(i))
+				{
+					inID = i;
+					break;
+				}
+			} catch(RtMidiError &)
+			{
 			}
 		}
 	}
@@ -155,16 +153,21 @@
 
 	file.ReadString<mpt::String::maybeNullTerminated>(s, outStrSize);
 	s = mpt::ToCharset(mpt::CharsetLocale, mpt::CharsetUTF8, s);
-	if(s != GetDeviceName(outID) || !IsOutputDevice(outID))
+	if(s != outputDevice.GetPortName(outID))
 	{
 		// Stored name differs from actual device name - try finding another device with the same name.
-		const PmDeviceInfo *device;
-		for(PmDeviceID i = 0; (device = Pm_GetDeviceInfo(i)) != nullptr; i++)
+		unsigned int ports = midiOut.getPortCount();
+		for(unsigned int i = 0; i < ports; i++)
 		{
-			if(device->output && s == device->name)
+			try
 			{
-				outID = i;
-				break;
+				if(s == outputDevice.GetPortName(i))
+				{
+					outID = i;
+					break;
+				}
+			} catch(RtMidiError &)
+			{
 			}
 		}
 	}
@@ -177,7 +180,7 @@
 void MidiInOut::SetParameter(PlugParamIndex index, PlugParamValue value)
 //----------------------------------------------------------------------
 {
-	PmDeviceID newDevice = ParameterToDeviceID(value);
+	MidiDevice::ID newDevice = ParameterToDeviceID(value);
 	OpenDevice(newDevice, (index == kInputParameter));
 
 	// Update selection in editor
@@ -236,61 +239,59 @@
 void MidiInOut::Process(float *, float *, uint32 numFrames)
 //---------------------------------------------------------
 {
-	MPT_LOCK_GUARD<mpt::mutex> lock(mutex);
+	MPT_LOCK_GUARD<mpt::mutex> lock(m_mutex);
 
-	if(outputDevice.stream != nullptr)
+	if(midiOut.isPortOpen())
 	{
 		// Send MIDI clock
-		if(nextClock < 1)
+		if(m_nextClock < 1)
 		{
-			Pm_WriteShort(outputDevice.stream, Now(), 0xF8);
+			std::vector<unsigned char> message(1, 0xF8);
+			midiOut.sendMessage(&message);
 			double bpm = m_SndFile.GetCurrentBPM();
 			if(bpm != 0.0)
 			{
-				nextClock += 2.5 * m_SndFile.GetSampleRate() / bpm;
+				m_nextClock += 2.5 * m_SndFile.GetSampleRate() / bpm;
 			}
 		}
-		nextClock -= numFrames;
+		m_nextClock -= numFrames;
 	}
+}
 
-	// We don't do any audio processing here, but we process incoming MIDI events.
-	if(inputDevice.stream == nullptr)
-		return;
 
-	while(Pm_Poll(inputDevice.stream))
+void MidiInOut::InputCallback(double /*deltatime*/, std::vector<unsigned char> &message)
+//--------------------------------------------------------------------------------------
+{
+	// We will check the bypass status before passing on the message, and not before entering the function,
+	// because otherwise we might read garbage if we toggle bypass status in the middle of a SysEx message.
+	bool isBypassed = IsBypassed();
+	if(message.empty())
 	{
-		// Read incoming MIDI events.
-		PmEvent buffer;
-		Pm_Read(inputDevice.stream, &buffer, 1);
-
-		// Discard events if bypassed
-		if(IsBypassed())
-			continue;
-
-		mpt::byte message[sizeof(buffer.message)];
-		memcpy(message, &buffer.message, sizeof(message));
-
-		if(!bufferedMessage.empty())
+		return;
+	} else if(!m_bufferedMessage.empty())
+	{
+		// SysEx message (continued)
+		m_bufferedMessage.insert(m_bufferedMessage.end(), message.begin(), message.end());
+		if(message.back() == 0xF7)
 		{
-			bufferedMessage.push_back(buffer.message);
-			if(buffer.message & 0x80808080)
-			{
-				// End of message found!
-				ReceiveSysex(bufferedMessage.data(), bufferedMessage.size() * sizeof(bufferedMessage[0]));
-				bufferedMessage.clear();
-			}
-			continue;
-		} else if(message[0] == 0xF0)
-		{
-			// Start of SysEx message...
-			if(message[1] != 0xF7 && message[2] != 0xF7 && message[3] != 0xF7)
-				bufferedMessage.push_back(buffer.message);	// ...but not the end!
-			else
-				ReceiveSysex(message, sizeof(message));
-			continue;
+			// End of message found!
+			if(!isBypassed)
+				ReceiveSysex(m_bufferedMessage.data(), m_bufferedMessage.size());
+			m_bufferedMessage.clear();
 		}
-
-		ReceiveMidi(buffer.message);
+	} else if(message.front() == 0xF0)
+	{
+		// Start of SysEx message...
+		if(message.back() != 0xF7)
+			m_bufferedMessage.insert(m_bufferedMessage.end(), message.begin(), message.end());	// ...but not the end!
+		else if(!isBypassed)
+			ReceiveSysex(message.data(), message.size());
+	} else if(!isBypassed)
+	{
+		// Regular message
+		uint32 msg = 0;
+		memcpy(&msg, message.data(), std::min(message.size(), sizeof(msg)));
+		ReceiveMidi(msg);
 	}
 }
 
@@ -301,12 +302,13 @@
 {
 	// Resume MIDI I/O
 	m_isResumed = true;
-	nextClock = 0;
+	m_nextClock = 0;
 	OpenDevice(inputDevice.index, true);
 	OpenDevice(outputDevice.index, false);
-	if(outputDevice.stream != nullptr)
+	if(midiOut.isPortOpen())
 	{
-		Pm_WriteShort(outputDevice.stream, Now(), 0xFA);	// Start
+		std::vector<unsigned char> message(1, 0xFA);	// Start
+		midiOut.sendMessage(&message);
 	}
 }
 
@@ -316,9 +318,10 @@
 //-----------------------
 {
 	// Suspend MIDI I/O
-	if(outputDevice.stream != nullptr)
+	if(midiOut.isPortOpen())
 	{
-		Pm_WriteShort(outputDevice.stream, Now(), 0xFC);	// Stop
+		std::vector<unsigned char> message(1, 0xFC);	// Stop
+		midiOut.sendMessage(&message);
 	}
 	CloseDevice(inputDevice);
 	CloseDevice(outputDevice);
@@ -330,11 +333,12 @@
 void MidiInOut::PositionChanged()
 //-------------------------------
 {
-	if(outputDevice.stream != nullptr)
+	if(midiOut.isPortOpen())
 	{
-		const PtTimestamp now = Now();
-		Pm_WriteShort(outputDevice.stream, now, 0xFC);	// Stop
-		Pm_WriteShort(outputDevice.stream, now, 0xFA);	// Start
+		std::vector<unsigned char> message;
+		message.push_back(0xFC);	// Stop
+		message.push_back(0xFA);	// Start
+		midiOut.sendMessage(&message);
 	}
 }
 
@@ -343,27 +347,30 @@
 bool MidiInOut::MidiSend(uint32 midiCode)
 //---------------------------------------
 {
-	if(outputDevice.stream == nullptr || IsBypassed())
+	if(!midiOut.isPortOpen() || IsBypassed())
 	{
 		// We need an output device to send MIDI messages to.
 		return true;
 	}
 
-	Pm_WriteShort(outputDevice.stream, Now(), midiCode);
+	std::vector<unsigned char> message(3, 0);
+	memcpy(&message[0], &midiCode, 3);
+	midiOut.sendMessage(&message);
 	return true;
 }
 
 
-bool MidiInOut::MidiSysexSend(const void *message, uint32 /*length*/)
-//-------------------------------------------------------------------
+bool MidiInOut::MidiSysexSend(const void *sysex, uint32 length)
+//-------------------------------------------------------------
 {
-	if(outputDevice.stream == nullptr || IsBypassed())
+	if(!midiOut.isPortOpen() || IsBypassed())
 	{
 		// We need an output device to send MIDI messages to.
 		return true;
 	}
 
-	Pm_WriteSysEx(outputDevice.stream, Now(), const_cast<unsigned char *>(static_cast<const unsigned char *>(message)));
+	std::vector<unsigned char> message(static_cast<const unsigned char *>(sysex), static_cast<const unsigned char *>(sysex) + length);
+	midiOut.sendMessage(&message);
 	return true;
 }
 
@@ -405,20 +412,13 @@
 }
 
 
-static PmTimestamp PtTimeWrapper(void* /*time_info*/)
-//---------------------------------------------------
-{
-	return Pt_Time();
-}
-
-
 // Open a device for input or output.
-void MidiInOut::OpenDevice(PmDeviceID newDevice, bool asInputDevice)
-//------------------------------------------------------------------
+void MidiInOut::OpenDevice(MidiDevice::ID newDevice, bool asInputDevice)
+//----------------------------------------------------------------------
 {
 	MidiDevice &device = asInputDevice ? inputDevice : outputDevice;
 
-	if(device.index == newDevice && device.stream != nullptr)
+	if(device.index == newDevice && device.stream.isPortOpen())
 	{
 		// No need to re-open this device.
 		return;
@@ -427,46 +427,41 @@
 	CloseDevice(device);
 
 	device.index = newDevice;
+	device.stream.closePort();
 
 	if(device.index == kNoDevice)
 	{
 		// Dummy device
-		device = MidiDevice();
+		device.name = "<none>";
 		return;
 	}
 
-	PmError result = pmNoError;
-	if(m_isResumed)
+	device.name = device.GetPortName(newDevice);
+	//if(m_isResumed)
 	{
 		// Don't open MIDI devices if we're not processing.
 		// This has to be done since we receive MIDI events in processReplacing(),
 		// so if no processing is happening, some irrelevant events might be queued until the next processing happens...
-		MPT_LOCK_GUARD<mpt::mutex> lock(mutex);
-		if(asInputDevice)
+		MPT_LOCK_GUARD<mpt::mutex> lock(m_mutex);
+		
+		try
 		{
-			result = Pm_OpenInput(&device.stream, newDevice, nullptr, 0, nullptr, nullptr);
-		} else
+			device.stream.openPort(newDevice);
+			if(asInputDevice)
+			{
+				midiIn.setCallback(InputCallback, this);
+				midiIn.ignoreTypes(false, true, true);
+			}
+		} catch(RtMidiError &error)
 		{
-			if(latencyCompensation)
+			device.name = "Unavailable";
+			MidiInOutEditor *editor = dynamic_cast<MidiInOutEditor *>(GetEditor());
+			if(editor != nullptr)
 			{
-				// buffer of 10000 events
-				result = Pm_OpenOutput(&device.stream, newDevice, nullptr, 10000, PtTimeWrapper, nullptr, Util::Round<PtTimestamp>(1000.0 * GetOutputLatency()));
-			} else
-			{
-				result = Pm_OpenOutput(&device.stream, newDevice, nullptr, 0, nullptr, nullptr, 0);
+				Reporting::Error("MIDI device cannot be opened:" + error.getMessage(), "MIDI Input / Output", editor);
 			}
 		}
 	}
-
-	// Update current device name
-	device.name = GetDeviceName(device.index);
-
-	MidiInOutEditor *editor = dynamic_cast<MidiInOutEditor *>(GetEditor());
-	if(result != pmNoError && editor != nullptr)
-	{
-		// Display a warning if the editor is open.
-		Reporting::Error("MIDI device cannot be opened!", "MIDI Input / Output", editor);
-	}
 }
 
 
@@ -474,60 +469,26 @@
 void MidiInOut::CloseDevice(MidiDevice &device)
 //---------------------------------------------
 {
-	if(device.stream != nullptr)
+	if(device.stream.isPortOpen())
 	{
-		MPT_LOCK_GUARD<mpt::mutex> lock(mutex);
-		Pm_Close(device.stream);
-		device.stream = nullptr;
+		MPT_LOCK_GUARD<mpt::mutex> lock(m_mutex);
+		device.stream.closePort();
 	}
 }
 
 
 // Get a device name
-const char *MidiInOut::GetDeviceName(PmDeviceID index) const
-//-----------------------------------------------------------
+std::string MidiDevice::GetPortName(MidiDevice::ID port)
+//------------------------------------------------------
 {
-	const PmDeviceInfo *deviceInfo = Pm_GetDeviceInfo(index);
-	if(deviceInfo != nullptr)
-		return deviceInfo->name;
-	else
-		return "Unavailable";
+	std::string portName = stream.getPortName(port);
+#if MPT_OS_WINDOWS
+	// Remove auto-appended port number
+	if(portName.length() >= 2)
+		return portName.substr(0, portName.find_last_of(' '));
+#endif
+	return portName;
 }
 
 
-bool MidiInOut::IsInputDevice(PmDeviceID index) const
-//----------------------------------------------------
-{
-	if(index == kNoDevice)
-		return true;
-
-	const PmDeviceInfo *deviceInfo = Pm_GetDeviceInfo(index);
-	if(deviceInfo != nullptr)
-		return deviceInfo->input != 0;
-	else
-		return false;
-}
-
-
-bool MidiInOut::IsOutputDevice(PmDeviceID index) const
-//----------------------------------------------------
-{
-	if(index == kNoDevice)
-		return true;
-
-	const PmDeviceInfo *deviceInfo = Pm_GetDeviceInfo(index);
-	if(deviceInfo != nullptr)
-		return deviceInfo->output != 0;
-	else
-		return false;
-}
-
-
-PtTimestamp MidiInOut::Now() const
-//--------------------------------
-{
-	return (latencyCompensation ? Pt_Time() : 0);
-}
-
-
 OPENMPT_NAMESPACE_END
Index: plugins/MidiInOut/MidiInOut.h
===================================================================
--- plugins/MidiInOut/MidiInOut.h	(revision 7555)
+++ plugins/MidiInOut/MidiInOut.h	(working copy)
@@ -12,8 +12,7 @@
 
 #include "../../common/mptMutex.h"
 #include "../../soundlib/plugins/PlugInterface.h"
-#include <portmidi/pm_common/portmidi.h>
-#include <portmidi/porttime/porttime.h>
+#include "../../include/rtmidi/RtMidi.h"
 
 
 OPENMPT_NAMESPACE_BEGIN
@@ -24,16 +23,20 @@
 //==============
 {
 public:
-	PmDeviceID index;
-	PortMidiStream *stream;
+	typedef unsigned int ID;
+
+	RtMidi &stream;
 	std::string name;
+	ID index;
 
 public:
-	MidiDevice()
-		: index(-1)	// MidiInOut::kNoDevice
-		, stream(nullptr)
+	MidiDevice(RtMidi &stream)
+		: stream(stream)
 		, name("<none>")
+		, index(ID(-1))	// MidiInOut::kNoDevice
 	{ }
+
+	std::string GetPortName(ID port);
 };
 
 
@@ -56,18 +59,21 @@
 		kMaxDevices = 65536,		// Should be a power of 2 to avoid rounding errors.
 	};
 
-	std::string chunkData;					// Storage for GetChunk
-	std::vector<uint32> bufferedMessage;	// For receiving SysEx messages
-	mpt::mutex mutex;
-	double nextClock;
+	std::string m_chunkData;						// Storage for GetChunk
+	std::vector<unsigned char> m_bufferedMessage;	// For receiving SysEx messages
+	mpt::mutex m_mutex;
+	double m_nextClock;
 
 	// I/O device settings
+	RtMidiIn midiIn;
+	RtMidiOut midiOut;
 	MidiDevice inputDevice;
 	MidiDevice outputDevice;
-	bool latencyCompensation;
+	bool m_latencyCompensation;
 
-	CString programName;
-	static int numInstances;
+#ifdef MODPLUG_TRACKER
+	CString m_programName;
+#endif
 
 public:
 	static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
@@ -75,13 +81,13 @@
 	~MidiInOut();
 
 	// Translate a VST parameter to a PortMidi device ID
-	static PmDeviceID ParameterToDeviceID(float value)
+	static MidiDevice::ID ParameterToDeviceID(float value)
 	{
-		return static_cast<PmDeviceID>(value * static_cast<float>(kMaxDevices)) - 1;
+		return static_cast<MidiDevice::ID>(value * static_cast<float>(kMaxDevices)) - 1;
 	}
 
 	// Translate a PortMidi device ID to a VST parameter
-	static float DeviceIDToParameter(PmDeviceID index)
+	static float DeviceIDToParameter(MidiDevice::ID index)
 	{
 		return static_cast<float>(index + 1) / static_cast<float>(kMaxDevices);
 	}
@@ -125,16 +131,12 @@
 #ifdef MODPLUG_TRACKER
 	virtual CString GetDefaultEffectName() { return _T("MIDI Input / Output"); }
 
-	// Cache a range of names, in case one-by-one retrieval would be slow (e.g. when using plugin bridge)
-	virtual void CacheProgramNames(int32, int32) { }
-	virtual void CacheParameterNames(int32, int32) { }
-
 	virtual CString GetParamName(PlugParamIndex param);
 	virtual CString GetParamLabel(PlugParamIndex) { return CString(); }
 	virtual CString GetParamDisplay(PlugParamIndex param);
-	virtual CString GetCurrentProgramName() { return programName; }
-	virtual void SetCurrentProgramName(const CString &name) { programName = name; }
-	virtual CString GetProgramName(int32) { return programName; }
+	virtual CString GetCurrentProgramName() { return m_programName; }
+	virtual void SetCurrentProgramName(const CString &name) { m_programName = name; }
+	virtual CString GetProgramName(int32) { return m_programName; }
 	virtual CString GetPluginVendor() { return _T("OpenMPT Project"); }
 
 	virtual bool HasEditor() const { return true; }
@@ -155,18 +157,12 @@
 
 protected:
 	// Open a device for input or output.
-	void OpenDevice(PmDeviceID newDevice, bool asInputDevice);
+	void OpenDevice(MidiDevice::ID newDevice, bool asInputDevice);
 	// Close an active device.
 	void CloseDevice(MidiDevice &device);
-	// Get a device name
-	const char *GetDeviceName(PmDeviceID index) const;
-	// Check if the given device is an input device
-	bool IsInputDevice(PmDeviceID index) const;
-	// Check if the given device is an output device
-	bool IsOutputDevice(PmDeviceID index) const;
 
-	// Get current timestamp for sending
-	PtTimestamp Now() const;
+	static void InputCallback(double deltatime, std::vector<unsigned char> *message, void *userData) { static_cast<MidiInOut *>(userData)->InputCallback(deltatime, *message); }
+	void InputCallback(double deltatime, std::vector<unsigned char> &message);
 };
 
 
Index: plugins/MidiInOut/MidiInOutEditor.cpp
===================================================================
--- plugins/MidiInOut/MidiInOutEditor.cpp	(revision 7555)
+++ plugins/MidiInOut/MidiInOutEditor.cpp	(working copy)
@@ -14,6 +14,7 @@
 #include "MidiInOutEditor.h"
 #include "../../mptrack/Mptrack.h"
 #include "../../mptrack/resource.h"
+#include "../../include/rtmidi/RtMidi.h"
 
 
 OPENMPT_NAMESPACE_BEGIN
@@ -51,7 +52,7 @@
 //--------------------------------------------
 {
 	Create(IDD_MIDI_IO_PLUGIN, parent);
-	m_latencyCheck.SetCheck(static_cast<MidiInOut &>(m_VstPlugin).latencyCompensation ? BST_CHECKED : BST_UNCHECKED);
+	m_latencyCheck.SetCheck(static_cast<MidiInOut &>(m_VstPlugin).m_latencyCompensation ? BST_CHECKED : BST_UNCHECKED);
 	PopulateLists();
 	return CAbstractVstEditor::OpenEditor(parent);
 }
@@ -75,32 +76,38 @@
 	int selectOutputItem = 0;
 	MidiInOut &plugin = static_cast<MidiInOut &>(m_VstPlugin);
 
-	const PmDeviceInfo *device;
-	CString deviceName;
-
-	// Go through all PortMidi devices
-	for(PmDeviceID i = 0; (device = Pm_GetDeviceInfo(i)) != nullptr; i++)
+	// Go through all RtMidi devices
+	unsigned int ports = plugin.midiIn.getPortCount();
+	std::string portName;
+	for(unsigned int i = 0; i < ports; i++)
 	{
-		if(device->input)
+		try
 		{
-			// We can actually receive MIDI data on this device.
-			deviceName = theApp.GetFriendlyMIDIPortName(device->name, true) + _T(" [") + CString(device->interf) + _T("]");
-			int result = m_inputCombo.AddString(deviceName);
+			portName = theApp.GetFriendlyMIDIPortName(plugin.inputDevice.GetPortName(i).c_str(), true);
+			int result = m_inputCombo.AddString(portName.c_str());
 			m_inputCombo.SetItemData(result, i);
 
 			if(result != CB_ERR && i == plugin.inputDevice.index)
 				selectInputItem = result;
 		}
+		catch(RtMidiError &)
+		{
+		}
+	}
 
-		if(device->output)
+	ports = plugin.midiOut.getPortCount();
+	for(unsigned int i = 0; i < ports; i++)
+	{
+		try
 		{
-			// We can actually output MIDI data on this device.
-			deviceName = theApp.GetFriendlyMIDIPortName(device->name, false) + _T(" [") + CString(device->interf) + _T("]");
-			int result = m_outputCombo.AddString(deviceName);
+			portName = theApp.GetFriendlyMIDIPortName(plugin.outputDevice.GetPortName(i).c_str(), false);
+			int result = m_outputCombo.AddString(portName.c_str());
 			m_outputCombo.SetItemData(result, i);
 
 			if(result != CB_ERR && i == plugin.outputDevice.index)
 				selectOutputItem = result;
+		} catch(RtMidiError &)
+		{
 		}
 	}
 
@@ -113,13 +120,13 @@
 
 
 // Refresh current input / output device in GUI
-void MidiInOutEditor::SetCurrentDevice(CComboBox &combo, PmDeviceID device)
-//-------------------------------------------------------------------------
+void MidiInOutEditor::SetCurrentDevice(CComboBox &combo, MidiDevice::ID device)
+//-----------------------------------------------------------------------------
 {
 	int items = combo.GetCount();
 	for(int i = 0; i < items; i++)
 	{
-		if(static_cast<PmDeviceID>(combo.GetItemData(i)) == device)
+		if(static_cast<MidiDevice::ID>(combo.GetItemData(i)) == device)
 		{
 			combo.SetCurSel(i);
 			break;
@@ -132,7 +139,7 @@
 //------------------------------------------------------------------------------
 {
 	// Update device ID and notify plugin.
-	PmDeviceID newDevice = static_cast<PmDeviceID>(combo.GetItemData(combo.GetCurSel()));
+	MidiDevice::ID newDevice = static_cast<MidiDevice::ID>(combo.GetItemData(combo.GetCurSel()));
 	plugin.SetParameter(param, MidiInOut::DeviceIDToParameter(newDevice));
 	plugin.AutomateParameter(param);
 }
@@ -157,7 +164,7 @@
 {
 	MidiInOut &plugin = static_cast<MidiInOut &>(m_VstPlugin);
 	plugin.CloseDevice(plugin.outputDevice);
-	plugin.latencyCompensation = (m_latencyCheck.GetCheck() != BST_UNCHECKED);
+	plugin.m_latencyCompensation = (m_latencyCheck.GetCheck() != BST_UNCHECKED);
 	plugin.OpenDevice(plugin.outputDevice.index, false);
 }
 
Index: plugins/MidiInOut/MidiInOutEditor.h
===================================================================
--- plugins/MidiInOut/MidiInOutEditor.h	(revision 7555)
+++ plugins/MidiInOut/MidiInOutEditor.h	(working copy)
@@ -13,7 +13,6 @@
 #ifdef MODPLUG_TRACKER
 
 #include "../../mptrack/AbstractVstEditor.h"
-#include <portmidi/pm_common/portmidi.h>
 
 OPENMPT_NAMESPACE_BEGIN
 
@@ -32,7 +31,7 @@
 	MidiInOutEditor(MidiInOut &plugin);
 
 	// Refresh current input / output device in GUI
-	void SetCurrentDevice(bool asInputDevice, PmDeviceID device)
+	void SetCurrentDevice(bool asInputDevice, MidiDevice::ID device)
 	{
 		CComboBox &combo = asInputDevice ? m_inputCombo : m_outputCombo;
 		SetCurrentDevice(combo, device);
@@ -47,7 +46,7 @@
 	// Update lists of available input / output devices
 	void PopulateLists();
 	// Refresh current input / output device in GUI
-	void SetCurrentDevice(CComboBox &combo, PmDeviceID device);
+	void SetCurrentDevice(CComboBox &combo, MidiDevice::ID device);
 
 	virtual void DoDataExchange(CDataExchange* pDX);
 
