PortMidi
Cross-platform MIDI IO library
Input/Output Devices Handling

Functions

PMEXPORT const PmDeviceInfoPm_GetDeviceInfo (PmDeviceID id)
 Get a PmDeviceInfo structure describing a MIDI device. More...
 
PMEXPORT PmError Pm_OpenInput (PortMidiStream **stream, PmDeviceID inputDevice, void *inputDriverInfo, int32_t bufferSize, PmTimeProcPtr time_proc, void *time_info)
 Open a MIDI device for input. More...
 
PMEXPORT PmError Pm_OpenOutput (PortMidiStream **stream, PmDeviceID outputDevice, void *outputDriverInfo, int32_t bufferSize, PmTimeProcPtr time_proc, void *time_info, int32_t latency)
 Open a MIDI device for output. More...
 
PMEXPORT PmError Pm_CreateVirtualInput (const char *name, const char *interf, void *deviceInfo)
 Create a virtual input device. More...
 
PMEXPORT PmError Pm_CreateVirtualOutput (const char *name, const char *interf, void *deviceInfo)
 Create a virtual output device. More...
 
PMEXPORT PmError Pm_DeleteVirtualDevice (PmDeviceID device)
 Remove a virtual device. More...
 

Detailed Description

Function Documentation

◆ Pm_CreateVirtualInput()

PMEXPORT PmError Pm_CreateVirtualInput ( const char *  name,
const char *  interf,
void *  deviceInfo 
)

Create a virtual input device.

Parameters
namegives the virtual device name, which is visible to other applications.
interfis the interface (System API) used to create the device Default interfaces are "MMSystem", "CoreMIDI" and "ALSA". Currently, these are the only ones implemented, but future implementations could support DirectMusic, Jack, sndio, or others.
deviceInfocontains interface-dependent additional information, e.g., hints or options. There are none at present, and NULL is the recommended value.
Returns
a device ID or pmNameConflict (name is invalid or already exists) or pmInterfaceNotSupported (interf is does not match a supported interface).

The created virtual device appears to other applications as if it is an output device. The device must be opened to obtain a stream and read from it.

Virtual devices are not supported by Windows (Multimedia API). Calls on Windows do nothing except return pmNotImplemented.

◆ Pm_CreateVirtualOutput()

PMEXPORT PmError Pm_CreateVirtualOutput ( const char *  name,
const char *  interf,
void *  deviceInfo 
)

Create a virtual output device.

Parameters
namegives the virtual device name, which is visible to other applications.
interfis the interface (System API) used to create the device Default interfaces are "MMSystem", "CoreMIDI" and "ALSA". Currently, these are the only ones implemented, but future implementations could support DirectMusic, Jack, sndio, or others.
deviceInfocontains interface-dependent additional information, e.g., hints or options. There are none at present, and NULL is the recommended value.
Returns
a device ID or pmInvalidDeviceId (name is invalid or already exists) or pmInterfaceNotSupported (interf is does not match a supported interface).

The created virtual device appears to other applications as if it is an input device. The device must be opened to obtain a stream and write to it.

Virtual devices are not supported by Windows (Multimedia API). Calls on Windows do nothing except return pmNotImplemented.

◆ Pm_DeleteVirtualDevice()

PMEXPORT PmError Pm_DeleteVirtualDevice ( PmDeviceID  device)

Remove a virtual device.

Parameters
devicea device ID (small integer) designating the device.

The device is removed; other applications can no longer see or open this virtual device, which may be either for input or output. The device must not be open. The device ID may be reused, but existing devices are not renumbered. This means that the device ID could be in the range from 0 to Pm_CountDevices(), yet the device ID does not designate a device. In that case, passing the ID to Pm_GetDeviceInfo() will return NULL.

Returns
pmNoError if the device was deleted or pmInvalidDeviceId if the device is open, already deleted, or device is out of range.

◆ Pm_GetDeviceInfo()

PMEXPORT const PmDeviceInfo * Pm_GetDeviceInfo ( PmDeviceID  id)

Get a PmDeviceInfo structure describing a MIDI device.

Parameters
idthe device to be queried.

If id is out of range or if the device designates a deleted virtual device, the function returns NULL.

The returned structure is owned by the PortMidi implementation and must not be manipulated or freed. The pointer is guaranteed to be valid between calls to Pm_Initialize() and Pm_Terminate().

◆ Pm_OpenInput()

PMEXPORT PmError Pm_OpenInput ( PortMidiStream **  stream,
PmDeviceID  inputDevice,
void *  inputDriverInfo,
int32_t  bufferSize,
PmTimeProcPtr  time_proc,
void *  time_info 
)

Open a MIDI device for input.

Parameters
streamthe address of a PortMidiStream pointer which will receive a pointer to the newly opened stream.
inputDevicethe ID of the device to be opened (see PmDeviceID).
inputDriverInfoa pointer to an optional driver-specific data structure containing additional information for device setup or handle processing. This parameter is never required for correct operation. If not used, specify NULL.
bufferSizethe number of input events to be buffered waiting to be read using Pm_Read(). Messages will be lost if the number of unread messages exceeds this value.
time_proc(address of) a procedure that returns time in milliseconds. It may be NULL, in which case a default millisecond timebase (PortTime) is used. If the application wants to use PortTime, it should start the timer (call Pt_Start) before calling Pm_OpenInput or Pm_OpenOutput. If the application tries to start the timer after Pm_OpenInput or Pm_OpenOutput, it may get a ptAlreadyStarted error from Pt_Start, and the application's preferred time resolution and callback function will be ignored. time_proc result values are appended to incoming MIDI data, normally by mapping system-provided timestamps to the time_proc timestamps to maintain the precision of system-provided timestamps.
time_infois a pointer passed to time_proc.
Returns
pmNoError and places a pointer to a valid PortMidiStream in the stream argument. If the open operation fails, a nonzero error code is returned (see #PMError) and the value of stream is invalid.

Any stream that is successfully opened should eventually be closed by calling Pm_Close().

◆ Pm_OpenOutput()

PMEXPORT PmError Pm_OpenOutput ( PortMidiStream **  stream,
PmDeviceID  outputDevice,
void *  outputDriverInfo,
int32_t  bufferSize,
PmTimeProcPtr  time_proc,
void *  time_info,
int32_t  latency 
)

Open a MIDI device for output.

Parameters
streamthe address of a PortMidiStream pointer which will receive a pointer to the newly opened stream.
outputDevicethe ID of the device to be opened (see PmDeviceID).
outputDriverInfoa pointer to an optional driver-specific data structure containing additional information for device setup or handle processing. This parameter is never required for correct operation. If not used, specify NULL.
bufferSizethe number of output events to be buffered waiting for output. In some cases – see below – PortMidi does not buffer output at all and merely passes data to a lower-level API, in which case bufferSize is ignored. Since MIDI speeds now vary from 1 to 50 or more messages per ms (over USB), put some thought into this number. E.g. if latency is 20ms and you want to burst 100 messages in that time (5000 messages per second), you should set bufferSize to at least 100. The default on Windows assumes an average rate of 500 messages per second and in this example, output would be slowed waiting for free buffers.
latencythe delay in milliseconds applied to timestamps to determine when the output should actually occur. (If latency is < 0, 0 is assumed.) If latency is zero, timestamps are ignored and all output is delivered immediately. If latency is greater than zero, output is delayed until the message timestamp plus the latency. (NOTE: the time is measured relative to the time source indicated by time_proc. Timestamps are absolute, not relative delays or offsets.) In some cases, PortMidi can obtain better timing than your application by passing timestamps along to the device driver or hardware, so the best strategy to minimize jitter is: wait until the real time to send the message, compute the message, attach the ideal output time (not the current real time, because some time may have elapsed), and send the message. The latency will be added to the timestamp, and provided the elapsed computation time has not exceeded latency, the message will be delivered according to the timestamp. If the real time is already past the timestamp, the message will be delivered as soon as possible. Latency may also help you to synchronize MIDI data to audio data by matching latency to the audio buffer latency.
time_proc(address of) a pointer to a procedure that returns time in milliseconds. It may be NULL, in which case a default millisecond timebase (PortTime) is used. If the application wants to use PortTime, it should start the timer (call Pt_Start) before calling Pm_OpenInput or Pm_OpenOutput. If the application tries to start the timer after Pm_OpenInput or Pm_OpenOutput, it may get a #ptAlreadyStarted error from Pt_Start, and the application's preferred time resolution and callback function will be ignored. time_proc times are used to schedule outgoing MIDI data (when latency is non-zero), usually by mapping from time_proc timestamps to internal system timestamps to maintain the precision of system-supported timing.
time_infoa pointer passed to time_proc.
Returns
pmNoError and places a pointer to a valid PortMidiStream in the stream argument. If the operation fails, a nonzero error code is returned (see PMError) and the value of stream is invalid.

Note: ALSA appears to have a fixed-size priority queue for timed output messages. Testing indicates the queue can hold a little over 400 3-byte MIDI messages. Thus, you can send 10,000 messages/second if the latency is 30ms (30ms * 10000 msgs/sec * 0.001 sec/ms = 300 msgs), but not if the latency is 50ms (resulting in about 500 pending messages, which is greater than the 400 message limit). Since timestamps in ALSA are relative, they are of less value than absolute timestamps in macOS and Windows. This is a limitation of ALSA and apparently a design flaw.

Example 1: If I provide a timestamp of 5000, latency is 1, and time_proc returns 4990, then the desired output time will be when time_proc returns timestamp+latency = 5001. This will be 5001-4990 = 11ms from now.

Example 2: If I want to send at exactly 5010, and latency is 10, I should wait until 5000, compute the messages and provide a timestamp of 5000. As long as computation takes less than 10ms, the message will be delivered at time 5010.

Example 3 (recommended): It is often convenient to ignore latency. E.g. if a sequence says to output at time 5010, just wait until 5010, compute the message and use 5010 for the timestamp. Delivery will then be at 5010+latency, but unless you are synchronizing to something else, the absolute delay by latency will not matter.

Any stream that is successfully opened should eventually be closed by calling Pm_Close().