PortMidi
Cross-platform MIDI IO library
|
Classes | |
struct | PmDeviceInfo |
Macros | |
#define | PM_DEFAULT_SYSEX_BUFFER_SIZE 1024 |
#define | PmStream PortMidiStream |
A shorter form of PortMidiStream. | |
#define | PM_HOST_ERROR_MSG_LEN 256u |
Any host error msg has at most this many characters, including EOS. | |
#define | pmNoDevice -1 |
This PmDeviceID (constant) value represents no device and may be returned by Pm_GetDefaultInputDeviceID() or Pm_GetDefaultOutputDeviceID() if no default exists. | |
#define | PM_DEVICEINFO_VERS 200 |
MIDI device information is returned in this structure, which is owned by PortMidi and read-only to applications. More... | |
#define | PmBefore(t1, t2) ((t1-t2) < 0) |
TRUE if t1 before t2. | |
Typedefs | |
typedef void | PortMidiStream |
Represents an open MIDI device. | |
typedef int | PmDeviceID |
Devices are represented as small integers. More... | |
typedef int32_t | PmTimestamp |
Represents a millisecond clock with arbitrary start time. More... | |
typedef PmTimestamp(* | PmTimeProcPtr) (void *time_info) |
Enumerations | |
enum | PmError { pmNoError = 0 , pmNoData = 0 , pmGotData = 1 , pmHostError = -10000 , pmInvalidDeviceId , pmInsufficientMemory , pmBufferTooSmall , pmBufferOverflow , pmBadPtr , pmBadData , pmInternalError , pmBufferMaxSize , pmNotImplemented , pmInterfaceNotSupported , pmNameConflict } |
PortMidi error code; a common return type. More... | |
Functions | |
PMEXPORT PmError | Pm_Initialize (void) |
Pm_Initialize() is the library initialization function - call this before using the library. More... | |
PMEXPORT PmError | Pm_Terminate (void) |
Pm_Terminate() is the library termination function - call this after using the library. | |
PMEXPORT int | Pm_HasHostError (PortMidiStream *stream) |
Test whether stream has a pending host error. More... | |
PMEXPORT const char * | Pm_GetErrorText (PmError errnum) |
Translate portmidi error number into human readable message. More... | |
PMEXPORT void | Pm_GetHostErrorText (char *msg, unsigned int len) |
Translate portmidi host error into human readable message. More... | |
PMEXPORT int | Pm_CountDevices (void) |
Get devices count, ids range from 0 to Pm_CountDevices()-1. | |
PMEXPORT PmDeviceID | Pm_GetDefaultInputDeviceID (void) |
Return the default device ID or pmNoDevice if there are no devices. More... | |
PMEXPORT PmDeviceID | Pm_GetDefaultOutputDeviceID (void) |
see PmDeviceID Pm_GetDefaultInputDeviceID() | |
#define PM_DEVICEINFO_VERS 200 |
MIDI device information is returned in this structure, which is owned by PortMidi and read-only to applications.
See Pm_GetDeviceInfo().
typedef int PmDeviceID |
Devices are represented as small integers.
Device ids range from 0 to Pm_CountDevices()-1. Pm_GetDeviceInfo() is used to get information about the device, and Pm_OpenInput() and PmOpenOutput() are used to open the device.
typedef int32_t PmTimestamp |
Represents a millisecond clock with arbitrary start time.
This type is used for all MIDI timestamps and clocks.
enum PmError |
PortMidi error code; a common return type.
No error is indicated by zero; errors are indicated by < 0.
Enumerator | |
---|---|
pmNoError | Normal return value indicating no error. |
pmNoData | No error, also indicates no data available. Use this constant where a value greater than zero would indicate data is available. |
pmGotData | A "no error" return also indicating data available. |
pmInvalidDeviceId | Out of range or output device when input is requested or input device when output is requested or device is already opened. |
pmBadPtr | PortMidiStream parameter is NULL or stream is not opened or stream is output when input is required or stream is input when output is required. |
pmBadData | Illegal midi data, e.g., missing EOX. |
pmBufferMaxSize | Buffer is already as large as it can be. |
pmNotImplemented | The function is not implemented, nothing was done. |
pmInterfaceNotSupported | The requested interface is not supported. |
pmNameConflict | Cannot create virtual device because name is taken. |
PMEXPORT PmDeviceID Pm_GetDefaultInputDeviceID | ( | void | ) |
Return the default device ID or pmNoDevice if there are no devices.
The result (but not pmNoDevice) can be passed to Pm_OpenMidi().
The use of these functions is not recommended. There is no natural "default device" on any system, so defaults must be set by users. (Currently, PortMidi just returns the first device it finds as "default".) The (unsolved) problem is how to implement simple preferences for a cross-platform library. (More notes follow, but you can stop reading here.)
To implement preferences, you need (1) a standard place to put them, (2) a representation for the preferences, (3) a graphical interface to test and set preferences, (4) a "natural" way to invoke the preference-setting program. To solve (3), PortMidi originally chose to use Java and Swing to implement a cross-platform GUI program called "pmdefaults." Java's Preferences class already provide a location (problem 1) and representation (problem 2). However, this solution was complex, requiring PortMidi to parse binary Java preference files and requiring users to install and invoke Java programs. It did not seem possible to integrate pmdefaults into the system preference subsystems on macOS, Windows, and Linux, so the user had to install and run pmdefaults as an application. Moreover, Java is falling out of favor.
A simpler solution is pass the burden to applications. It is easy to scan devices with PortMidi and build a device menu, and to save menu selections in application preferences for next time. This is my recommendation for any GUI program. For simple command-line applications and utilities, see pm_test where all the test programs now accept device numbers on the command line and/or prompt for their entry.
Some advice for preferences: MIDI devices used to be built-in or plug-in cards, so the numbers rarely changed. Now MIDI devices are often plug-in USB devices, so device numbers change, and you probably need to design to reinitialize PortMidi to rescan devices. MIDI is pretty stateless, so this isn't a big problem, although it means you cannot find a new device while playing or recording MIDI.
Since device numbering can change whenever a USB device is plugged in, preferences should record names of devices rather than device numbers. It is simple enough to use string matching to find a prefered device, so PortMidi does not provide any built-in lookup function. See below for details of the Java preferences API.
In the future, I would like to remove the legacy code that parses Java preference data (macOS plist, linux prefs.xml, Windows registry entries) and replace it with something more useful. Maybe something really simple: $HOME/.portmidi? Or maybe a new pmdefaults written with PyGame? Or use QT? If applications write their own preferences, maybe a minimal command line preference setter is all that's needed? Or maybe command line application users are happy without a preference system? Comments and proposals are welcome.
For completeness, here is a description of the original use of Java for preference setting: The default device can be specified using a small application named pmdefaults that is part of the PortMidi distribution. This program in turn uses the Java Preferences object created by java.util.prefs.Preferences.userRoot().node("/PortMidi"); the preference is set by calling prefs.put("PM_RECOMMENDED_OUTPUT_DEVICE", prefName); or prefs.put("PM_RECOMMENDED_INPUT_DEVICE", prefName);
In the statements above, prefName is a string describing the MIDI device in the form "interf, name" where interf identifies the underlying software system or API used by PortMdi to access devices and name is the name of the device. These correspond to the interf and name fields of a PmDeviceInfo. (Currently supported interfaces are "MMSystem" for Win32, "ALSA" for Linux, and "CoreMIDI" for OS X, so in fact, there is no choice of interface.) In "interf, name", the strings are actually substrings of the full interface and name strings. For example, the preference "Core, Sport" will match a device with interface "CoreMIDI" and name "In USB MidiSport 1x1". It will also match "CoreMIDI" and "In USB MidiSport 2x2". The devices are enumerated in device ID order, so the lowest device ID that matches the pattern becomes the default device. Finally, if the comma-space (", ") separator between interface and name parts of the preference is not found, the entire preference string is interpreted as a name, and the interface part is the empty string, which matches anything.
On the MAC, preferences are stored in /Users/$NAME/Library/Preferences/com.apple.java.util.prefs.plist which is a binary file. In addition to the pmdefaults program, there are utilities that can read and edit this preference file. On Windows, the Registry is used. On Linux, preferences are in an XML file.
PMEXPORT const char * Pm_GetErrorText | ( | PmError | errnum | ) |
Translate portmidi error number into human readable message.
These strings are constants (set at compile time) so client has no need to allocate storage.
PMEXPORT void Pm_GetHostErrorText | ( | char * | msg, |
unsigned int | len | ||
) |
Translate portmidi host error into human readable message.
These strings are computed at run time, so client has to allocate storage. After this routine executes, the host error is cleared.
PMEXPORT int Pm_HasHostError | ( | PortMidiStream * | stream | ) |
Test whether stream has a pending host error.
Normally, the client finds out about errors through returned error codes, but some errors can occur asynchronously where the client does not explicitly call a function, and therefore cannot receive an error code. The client can test for a pending error using Pm_HasHostError(). If true, the error can be accessed by calling Pm_GetHostErrorText(). Pm_Poll() is similar to Pm_HasHostError(), but if there is no error, it will return TRUE (1) if there is a pending input message.
PMEXPORT PmError Pm_Initialize | ( | void | ) |
Pm_Initialize() is the library initialization function - call this before using the library.
PortMidi is designed to support multiple interfaces (such as ALSA, CoreMIDI and WinMM). It is possible to return pmNoError because there are no supported interfaces. In that case, zero devices will be available.