diff options
78 files changed, 10 insertions, 20596 deletions
diff --git a/example3.c b/example3.c deleted file mode 100644 index a2103f2..0000000 --- a/example3.c +++ /dev/null @@ -1,53 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "portmidi/pm_common/portmidi.h" - -#define NUM_INPUTS 16 - -int main() { - PmError err; - PmStream *stream; - PmEvent events[128]; - int i, num_events; - - // Initialize PortMIDI - err = Pm_Initialize(); - if (err != pmNoError) { - fprintf(stderr, "Error initializing PortMIDI: %s\n", Pm_GetErrorText(err)); - exit(1); - } - - /* // Open a MIDI input device */ - stream = (PortMidiStream *)Pm_OpenInput(&err, NULL, NULL, NUM_INPUTS, NULL, 0); - if (err != pmNoError) { - fprintf(stderr, "Error opening MIDI input device: %s\n", Pm_GetErrorText(err)); - Pm_Terminate(); - exit(1); - } - - /* // Read MIDI messages from the input device */ - /* while (1) { */ - /* num_events = Pm_Read(stream, events, 128); */ - /* if (num_events > 0) { */ - /* for (i = 0; i < num_events; i++) { */ - /* if (events[i].message & 0xff00) { */ - /* // This is a status message (note on, note off, etc.) */ - /* printf("Message: 0x%02x\n", events[i].message); */ - /* /1* printf("Status: 0x%02x, Data 1: 0x%02x, Data 2: 0x%02x\n", *1/ */ - /* /1* events[i].message & 0xf0, events[i].message[0], events[i].message[1]); *1/ */ - /* } */ - /* } */ - /* } */ - /* } */ - - /* // Close the MIDI input device */ - Pm_Close(stream); - - // Terminate PortMIDI - Pm_Terminate(); - - return 0; -} - diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..5f77125 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,5 @@ +tests: + $(CC) -Wall example1.c ../minisdl_audio.c -lm -ldl -lpthread -o example1 + $(CC) -Wall example2.c ../minisdl_audio.c -lm -ldl -lpthread -o example2 + $(CC) -Wall example4.c -lasound -lm -o example4 + diff --git a/example1.c b/examples/example1.c index 200fd1b..91800ad 100644 --- a/example1.c +++ b/examples/example1.c @@ -1,7 +1,7 @@ #define TSF_IMPLEMENTATION
-#include "tsf.h"
+#include "../tsf.h"
-#include "minisdl_audio.h"
+#include "../minisdl_audio.h"
//This is a minimal SoundFont with a single loopin saw-wave sample/instrument/preset (484 bytes)
const static unsigned char MinimalSoundFont[] = {
diff --git a/example2.c b/examples/example2.c index cfc0fc0..b4c7d73 100644 --- a/example2.c +++ b/examples/example2.c @@ -1,7 +1,7 @@ -#include "minisdl_audio.h"
+#include "../minisdl_audio.h"
#define TSF_IMPLEMENTATION
-#include "tsf.h"
+#include "../tsf.h"
// Holds the global instance pointer
static tsf* g_TinySoundFont;
@@ -35,7 +35,7 @@ int main(int argc, char *argv[]) { }
// Load the SoundFont from a file
- g_TinySoundFont = tsf_load_filename("sf2/florestan-subset.sf2");
+ g_TinySoundFont = tsf_load_filename("../sf2/florestan-subset.sf2");
if (!g_TinySoundFont) {
fprintf(stderr, "Could not load SoundFont\n");
return 1;
diff --git a/example4.c b/examples/example4.c index 1dc9ebe..1dc9ebe 100644 --- a/example4.c +++ b/examples/example4.c diff --git a/portmidi.h b/portmidi.h deleted file mode 100755 index 8696a73..0000000 --- a/portmidi.h +++ /dev/null @@ -1,974 +0,0 @@ -#ifndef PORTMIDI_PORTMIDI_H -#define PORTMIDI_PORTMIDI_H - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* - * PortMidi Portable Real-Time MIDI Library - * PortMidi API Header File - * Latest version available at: http://sourceforge.net/projects/portmedia - * - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * Copyright (c) 2001-2006 Roger B. Dannenberg - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortMidi license; however, - * the PortMusic community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/* CHANGELOG FOR PORTMIDI - * (see ../CHANGELOG.txt) - * - * NOTES ON HOST ERROR REPORTING: - * - * PortMidi errors (of type PmError) are generic, - * system-independent errors. When an error does not map to one of - * the more specific PmErrors, the catch-all code pmHostError is - * returned. This means that PortMidi has retained a more specific - * system-dependent error code. The caller can get more information - * by calling Pm_GetHostErrorText() to get a text string describing - * the error. Host errors can arise asynchronously from callbacks, - * * so there is no specific return code. Asynchronous errors are - * checked and reported by Pm_Poll. You can also check by calling - * Pm_HasHostError(). If this returns TRUE, Pm_GetHostErrorText() - * will return a text description of the error. - * - * NOTES ON COMPILE-TIME SWITCHES - * - * DEBUG assumes stdio and a console. Use this if you want - * automatic, simple error reporting, e.g. for prototyping. If - * you are using MFC or some other graphical interface with no - * console, DEBUG probably should be undefined. - * PM_CHECK_ERRORS more-or-less takes over error checking for - * return values, stopping your program and printing error - * messages when an error occurs. This also uses stdio for - * console text I/O. You can selectively disable this error - * checking by declaring extern int pm_check_errors; and - * setting pm_check_errors = FALSE; You can also reenable. - */ -/** - \defgroup grp_basics Basic Definitions - @{ -*/ - -#include <stdint.h> - -#ifdef _WINDLL -#define PMEXPORT __declspec(dllexport) -#else -#define PMEXPORT -#endif - -#ifndef FALSE - #define FALSE 0 -#endif -#ifndef TRUE - #define TRUE 1 -#endif - -/* default size of buffers for sysex transmission: */ -#define PM_DEFAULT_SYSEX_BUFFER_SIZE 1024 - - -typedef enum { - pmNoError = 0, /**< Normal return value indicating no error. */ - pmNoData = 0, /**< @brief No error, also indicates no data available. - * Use this constant where a value greater than zero would - * indicate data is available. - */ - pmGotData = 1, /**< A "no error" return also indicating data available. */ - pmHostError = -10000, - pmInvalidDeviceId, /**< Out of range or - * output device when input is requested or - * input device when output is requested or - * device is already opened. - */ - pmInsufficientMemory, - pmBufferTooSmall, - pmBufferOverflow, - 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. */ - pmInternalError, - 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. */ - pmDeviceRemoved /**< Output attempted after (USB) device was removed. */ - /* NOTE: If you add a new error type, you must update Pm_GetErrorText(). */ -} PmError; /**< @brief @enum PmError PortMidi error code; a common return type. - * No error is indicated by zero; errors are indicated by < 0. - */ - -/** - Pm_Initialize() is the library initialization function - call this before - using the library. - - *NOTE:* PortMidi scans for available devices when #Pm_Initialize - is called. To observe subsequent changes in the available - devices, you must shut down PortMidi by calling #Pm_Terminate and - then restart by calling #Pm_Initialize again. *IMPORTANT*: On - MacOS, #Pm_Initialize *must* always be called on the same - thread. Otherwise, changes in the available MIDI devices will - *not* be seen by PortMidi. As an example, if you start PortMidi in - a thread for processing MIDI, do not try to rescan devices by - calling #Pm_Initialize in a GUI thread. Instead, start PortMidi - the first time and every time in the GUI thread. Alternatively, - let the GUI request a restart in the MIDI thread. (These - restrictions only apply to macOS.) Speaking of threads, on all - platforms, you are allowed to call #Pm_Initialize in one thread, - yet send MIDI or poll for incoming MIDI in another - thread. However, PortMidi is not "thread safe," which means you - cannot allow threads to call PortMidi functions concurrently. - - @return pmNoError. - - 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. -*/ -PMEXPORT PmError Pm_Initialize(void); - -/** - Pm_Terminate() is the library termination function - call this after - using the library. -*/ -PMEXPORT PmError Pm_Terminate(void); - -/** Represents an open MIDI device. */ -typedef void PortMidiStream; - -/** A shorter form of #PortMidiStream. */ -#define PmStream PortMidiStream - -/** 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 int Pm_HasHostError(PortMidiStream * stream); - - -/** 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 const char *Pm_GetErrorText(PmError errnum); - -/** 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 void Pm_GetHostErrorText(char * msg, unsigned int len); - -/** Any host error msg has at most this many characters, including EOS. */ -#define PM_HOST_ERROR_MSG_LEN 256u - -/** 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 int PmDeviceID; - -/** This PmDeviceID (constant) value represents no device and may be - returned by Pm_GetDefaultInputDeviceID() or - Pm_GetDefaultOutputDeviceID() if no default exists. -*/ -#define pmNoDevice -1 - -/** MIDI device information is returned in this structure, which is - owned by PortMidi and read-only to applications. See Pm_GetDeviceInfo(). -*/ -#define PM_DEVICEINFO_VERS 200 -typedef struct { - int structVersion; /**< @brief this internal structure version */ - const char *interf; /**< @brief underlying MIDI API, e.g. - "MMSystem" or "DirectX" */ - char *name; /**< @brief device name, e.g. "USB MidiSport 1x1" */ - int input; /**< @brief true iff input is available */ - int output; /**< @brief true iff output is available */ - int opened; /**< @brief used by generic PortMidi for error checking */ - int is_virtual; /**< @brief true iff this is/was a virtual device */ -} PmDeviceInfo; - -/** MIDI system-dependent device or driver info is passed in this - structure, which is owned by the caller. -*/ -#define PM_SYSDEPINFO_VERS 210 - -enum PmSysDepPropertyKey { - pmKeyNone = 0, /**< a "noop" key value */ - /** CoreMIDI Manufacturer name, value is string */ - pmKeyCoreMidiManufacturer = 1, - /** Linux ALSA snd_seq_port_info_set_name, value is a string. Can be - passed in PmSysDepInfo to Pm_OpenInput or Pm_OpenOutput when opening - a device. The created port will be named accordingly and will be - visible for externally made connections (subscriptions). (Linux ALSA - ports are always enabled for this, but only get application-specific - names if you give it one.) This key/value is ignored when opening - virtual ports, which are named when they are created.) */ - pmKeyAlsaPortName = 2, - /** Linux ALSA snd_seq_set_client_name, value is a string. - Can be passed in PmSysDepInfo to Pm_OpenInput or Pm_OpenOutput. - Pm_CreateVirtualInput or Pm_CreateVirtualOutput. Will override - any previously set client name and applies to all ports. */ - pmKeyAlsaClientName = 3 - /* if system-dependent code introduces more options, register - the key here to avoid conflicts. */ -}; - -/** System-dependent information can be passed when creating and opening - ports using this data structure, which stores alternating keys and - values (addresses). See `pm_test/sendvirtual.c`, `pm_test/recvvirtual.c`, - and `pm_test/testio.c` for examples. - */ -typedef struct { - int structVersion; /**< @brief this structure version */ - int length; /**< @brief number of properties in this structure */ - struct { - enum PmSysDepPropertyKey key; - const void *value; - } properties[]; -} PmSysDepInfo; - - -/** Get devices count, ids range from 0 to Pm_CountDevices()-1. */ -PMEXPORT int Pm_CountDevices(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", so if there *is* a default, implementors should use - pm_add_device to add system default input and output devices - first.) - - The recommended 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. - - On linux, you can create virtual ports and use an external program - to set up inter-application and device connections. - - 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. -*/ -PMEXPORT PmDeviceID Pm_GetDefaultInputDeviceID(void); - -/** @brief see PmDeviceID Pm_GetDefaultInputDeviceID() */ -PMEXPORT PmDeviceID Pm_GetDefaultOutputDeviceID(void); - -/** Find a device that matches a pattern. - - @param pattern a substring of the device name, or if the pattern - contains the two-character separator ", ", then the first part of - the pattern represents a device interface substring and the second - part after the separator represents a device name substring. - - @param is_input restricts the search to an input when true, or an - output when false. - - @return the number of the first device whose device interface - contains the interface pattern (if any), whose device name - contains the name pattern, and whose direction (input or output) - matches the #is_input parameter. If no match is found, #pmNoDevice - (-1) is returned. -*/ -PMEXPORT PmDeviceID Pm_FindDevice(char *pattern, int is_input); - - -/** Represents a millisecond clock with arbitrary start time. - This type is used for all MIDI timestamps and clocks. -*/ -typedef int32_t PmTimestamp; -typedef PmTimestamp (*PmTimeProcPtr)(void *time_info); - -/** TRUE if t1 before t2 */ -#define PmBefore(t1,t2) (((t1)-(t2)) < 0) -/** @} */ -/** - \defgroup grp_device Input/Output Devices Handling - @{ -*/ -/** Get a PmDeviceInfo structure describing a MIDI device. - - @param id the device to be queried. - - If \p 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(). -*/ -PMEXPORT const PmDeviceInfo *Pm_GetDeviceInfo(PmDeviceID id); - -/** Open a MIDI device for input. - - @param stream the address of a #PortMidiStream pointer which will - receive a pointer to the newly opened stream. - - @param inputDevice the ID of the device to be opened (see #PmDeviceID). - - @param inputSysDepInfo a pointer to an optional system-dependent - data structure (a #PmSysDepInfo struct) containing additional - information for device setup or handle processing. This parameter - is never required for correct operation. If not used, specify - NULL. Declared `void *` here for backward compatibility. Note that - with Linux ALSA, you can use this parameter to specify a client name - and port name. - - @param bufferSize the 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. - - @param 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. - \p time_proc result values are appended to incoming MIDI data, - normally by mapping system-provided timestamps to the \p time_proc - timestamps to maintain the precision of system-provided - timestamps. - - @param time_info is a pointer passed to time_proc. - - @return #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(). -*/ -PMEXPORT PmError Pm_OpenInput(PortMidiStream** stream, - PmDeviceID inputDevice, - void *inputSysDepInfo, - int32_t bufferSize, - PmTimeProcPtr time_proc, - void *time_info); - -/** Open a MIDI device for output. - - @param stream the address of a #PortMidiStream pointer which will - receive a pointer to the newly opened stream. - - @param outputDevice the ID of the device to be opened (see #PmDeviceID). - - @param inputSysDepInfo a pointer to an optional system-specific - data structure (a #PmSysDepInfo struct) containing additional - information for device setup or handle processing. This parameter - is never required for correct operation. If not used, specify - NULL. Declared `void *` here for backward compatibility. Note that - with Linux ALSA, you can use this parameter to specify a client name - and port name. - - @param bufferSize the 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 \p 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 \p 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. - - @param latency the 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 \p latency will be added to the timestamp, and - provided the elapsed computation time has not exceeded \p 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 \p latency to the - audio buffer latency. - - @param 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. \p 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. - - @param time_info a pointer passed to time_proc. - - @return #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 \p 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(). -*/ -PMEXPORT PmError Pm_OpenOutput(PortMidiStream** stream, - PmDeviceID outputDevice, - void *outputSysDepInfo, - int32_t bufferSize, - PmTimeProcPtr time_proc, - void *time_info, - int32_t latency); - -/** Create a virtual input device. - - @param name gives the virtual device name, which is visible to - other applications. - - @param interf is 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. - - @param sysDepInfo contains interface-dependent additional - information (a #PmSysDepInfo struct), e.g., hints or options. This - parameter is never required for correct operation. If not used, - specify NULL. Declared `void *` here for backward compatibility. - - @return a device ID or #pmNameConflict (\p name is invalid or - already exists) or #pmInterfaceNotSupported (\p 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. -*/ -PMEXPORT PmError Pm_CreateVirtualInput(const char *name, - const char *interf, - void *sysDepInfo); - -/** Create a virtual output device. - - @param name gives the virtual device name, which is visible to - other applications. - - @param interf is 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. - - @param sysDepInfo contains interface-dependent additional - information (a #PmSysDepInfo struct), e.g., hints or options. This - parameter is never required for correct operation. If not used, - specify NULL. Declared `void *` here for backward compatibility. - - @return a device ID or #pmInvalidDeviceId (\p name is invalid or - already exists) or #pmInterfaceNotSupported (\p 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. -*/ -PMEXPORT PmError Pm_CreateVirtualOutput(const char *name, - const char *interf, - void *sysDepInfo); - -/** Remove a virtual device. - - @param device a 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. - - @return #pmNoError if the device was deleted or #pmInvalidDeviceId - if the device is open, already deleted, or \p device is out of - range. -*/ -PMEXPORT PmError Pm_DeleteVirtualDevice(PmDeviceID device); - /** @} */ - -/** - @defgroup grp_events_filters Events and Filters Handling - @{ -*/ - -/* Filter bit-mask definitions */ -/** filter active sensing messages (0xFE): */ -#define PM_FILT_ACTIVE (1 << 0x0E) -/** filter system exclusive messages (0xF0): */ -#define PM_FILT_SYSEX (1 << 0x00) -/** filter MIDI clock message (0xF8) */ -#define PM_FILT_CLOCK (1 << 0x08) -/** filter play messages (start 0xFA, stop 0xFC, continue 0xFB) */ -#define PM_FILT_PLAY ((1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B)) -/** filter tick messages (0xF9) */ -#define PM_FILT_TICK (1 << 0x09) -/** filter undefined FD messages */ -#define PM_FILT_FD (1 << 0x0D) -/** filter undefined real-time messages */ -#define PM_FILT_UNDEFINED PM_FILT_FD -/** filter reset messages (0xFF) */ -#define PM_FILT_RESET (1 << 0x0F) -/** filter all real-time messages */ -#define PM_FILT_REALTIME (PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK | \ - PM_FILT_PLAY | PM_FILT_UNDEFINED | PM_FILT_RESET | PM_FILT_TICK) -/** filter note-on and note-off (0x90-0x9F and 0x80-0x8F */ -#define PM_FILT_NOTE ((1 << 0x19) | (1 << 0x18)) -/** filter channel aftertouch (most midi controllers use this) (0xD0-0xDF)*/ -#define PM_FILT_CHANNEL_AFTERTOUCH (1 << 0x1D) -/** per-note aftertouch (0xA0-0xAF) */ -#define PM_FILT_POLY_AFTERTOUCH (1 << 0x1A) -/** filter both channel and poly aftertouch */ -#define PM_FILT_AFTERTOUCH (PM_FILT_CHANNEL_AFTERTOUCH | \ - PM_FILT_POLY_AFTERTOUCH) -/** Program changes (0xC0-0xCF) */ -#define PM_FILT_PROGRAM (1 << 0x1C) -/** Control Changes (CC's) (0xB0-0xBF)*/ -#define PM_FILT_CONTROL (1 << 0x1B) -/** Pitch Bender (0xE0-0xEF*/ -#define PM_FILT_PITCHBEND (1 << 0x1E) -/** MIDI Time Code (0xF1)*/ -#define PM_FILT_MTC (1 << 0x01) -/** Song Position (0xF2) */ -#define PM_FILT_SONG_POSITION (1 << 0x02) -/** Song Select (0xF3)*/ -#define PM_FILT_SONG_SELECT (1 << 0x03) -/** Tuning request (0xF6) */ -#define PM_FILT_TUNE (1 << 0x06) -/** All System Common messages (mtc, song position, song select, tune request) */ -#define PM_FILT_SYSTEMCOMMON (PM_FILT_MTC | PM_FILT_SONG_POSITION | \ - PM_FILT_SONG_SELECT | PM_FILT_TUNE) - - -/* Set filters on an open input stream to drop selected input types. - - @param stream an open MIDI input stream. - - @param filters indicate message types to filter (block). - - @return #pmNoError or an error code. - - By default, only active sensing messages are filtered. - To prohibit, say, active sensing and sysex messages, call - Pm_SetFilter(stream, PM_FILT_ACTIVE | PM_FILT_SYSEX); - - Filtering is useful when midi routing or midi thru functionality - is being provided by the user application. - For example, you may want to exclude timing messages (clock, MTC, - start/stop/continue), while allowing note-related messages to pass. - Or you may be using a sequencer or drum-machine for MIDI clock - information but want to exclude any notes it may play. - */ -PMEXPORT PmError Pm_SetFilter(PortMidiStream* stream, int32_t filters); - -/** Create a mask that filters one channel. */ -#define Pm_Channel(channel) (1<<(channel)) - -/** Filter incoming messages based on channel. - - @param stream an open MIDI input stream. - - @param mask indicates channels to be received. - - @return #pmNoError or an error code. - - The \p mask is a 16-bit bitfield corresponding to appropriate channels. - The #Pm_Channel macro can assist in calling this function. - I.e. to receive only input on channel 1, call with - Pm_SetChannelMask(Pm_Channel(1)); - Multiple channels should be OR'd together, like - Pm_SetChannelMask(Pm_Channel(10) | Pm_Channel(11)) - - Note that channels are numbered 0 to 15 (not 1 to 16). Most - synthesizer and interfaces number channels starting at 1, but - PortMidi numbers channels starting at 0. - - All channels are allowed by default -*/ -PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask); - -/** Terminate outgoing messages immediately. - - @param stream an open MIDI output stream. - - @result #pmNoError or an error code. - - The caller should immediately close the output port; this call may - result in transmission of a partial MIDI message. There is no - abort for Midi input because the user can simply ignore messages - in the buffer and close an input device at any time. If the - specified behavior cannot be achieved through the system-level - interface (ALSA, CoreMIDI, etc.), the behavior may be that of - Pm_Close(). - */ -PMEXPORT PmError Pm_Abort(PortMidiStream* stream); - -/** Close a midi stream, flush any pending buffers if possible. - - @param stream an open MIDI input or output stream. - - @result #pmNoError or an error code. - - If the system-level interface (ALSA, CoreMIDI, etc.) does not - support flushing remaining messages, the behavior may be one of - the following (most preferred first): block until all pending - timestamped messages are delivered; deliver messages to a server - or kernel process for later delivery but return immediately; drop - messages (as in Pm_Abort()). Therefore, to be safe, applications - should wait until the output queue is empty before calling - Pm_Close(). E.g. calling Pt_Sleep(100 + latency); will give a - 100ms "cushion" beyond latency (if any) before closing. -*/ -PMEXPORT PmError Pm_Close(PortMidiStream* stream); - -/** (re)synchronize to the time_proc passed when the stream was opened. - - @param stream an open MIDI input or output stream. - - @result #pmNoError or an error code. - - Typically, this is used when the stream must be opened before the - time_proc reference is actually advancing. In this case, message - timing may be erratic, but since timestamps of zero mean "send - immediately," initialization messages with zero timestamps can be - written without a functioning time reference and without - problems. Before the first MIDI message with a non-zero timestamp - is written to the stream, the time reference must begin to advance - (for example, if the time_proc computes time based on audio - samples, time might begin to advance when an audio stream becomes - active). After time_proc return values become valid, and BEFORE - writing the first non-zero timestamped MIDI message, call - Pm_Synchronize() so that PortMidi can observe the difference - between the current time_proc value and its MIDI stream time. - - In the more normal case where time_proc values advance - continuously, there is no need to call #Pm_Synchronize. PortMidi - will always synchronize at the first output message and - periodically thereafter. -*/ -PMEXPORT PmError Pm_Synchronize(PortMidiStream* stream); - - -/** Encode a short Midi message into a 32-bit word. If data1 - and/or data2 are not present, use zero. -*/ -#define Pm_Message(status, data1, data2) \ - ((((data2) << 16) & 0xFF0000) | \ - (((data1) << 8) & 0xFF00) | \ - ((status) & 0xFF)) -/** Extract the status field from a 32-bit midi message. */ -#define Pm_MessageStatus(msg) ((msg) & 0xFF) -/** Extract the 1st data field (e.g., pitch) from a 32-bit midi message. */ -#define Pm_MessageData1(msg) (((msg) >> 8) & 0xFF) -/** Extract the 2nd data field (e.g., velocity) from a 32-bit midi message. */ -#define Pm_MessageData2(msg) (((msg) >> 16) & 0xFF) - -typedef uint32_t PmMessage; /**< @brief see #PmEvent */ -/** - All MIDI data comes in the form of PmEvent structures. A sysex - message is encoded as a sequence of PmEvent structures, with each - structure carrying 4 bytes of the message, i.e. only the first - PmEvent carries the status byte. - - All other MIDI messages take 1 to 3 bytes and are encoded in a whole - PmMessage with status in the low-order byte and remaining bytes - unused, i.e., a 3-byte note-on message will occupy 3 low-order bytes - of PmMessage, leaving the high-order byte unused. - - Note that MIDI allows nested messages: the so-called "real-time" MIDI - messages can be inserted into the MIDI byte stream at any location, - including within a sysex message. MIDI real-time messages are one-byte - messages used mainly for timing (see the MIDI spec). PortMidi retains - the order of non-real-time MIDI messages on both input and output, but - it does not specify exactly how real-time messages are processed. This - is particulary problematic for MIDI input, because the input parser - must either prepare to buffer an unlimited number of sysex message - bytes or to buffer an unlimited number of real-time messages that - arrive embedded in a long sysex message. To simplify things, the input - parser is allowed to pass real-time MIDI messages embedded within a - sysex message, and it is up to the client to detect, process, and - remove these messages as they arrive. - - When receiving sysex messages, the sysex message is terminated - by either an EOX status byte (anywhere in the 4 byte messages) or - by a non-real-time status byte in the low order byte of the message. - If you get a non-real-time status byte but there was no EOX byte, it - means the sysex message was somehow truncated. This is not - considered an error; e.g., a missing EOX can result from the user - disconnecting a MIDI cable during sysex transmission. - - A real-time message can occur within a sysex message. A real-time - message will always occupy a full PmEvent with the status byte in - the low-order byte of the PmEvent message field. (This implies that - the byte-order of sysex bytes and real-time message bytes may not - be preserved -- for example, if a real-time message arrives after - 3 bytes of a sysex message, the real-time message will be delivered - first. The first word of the sysex message will be delivered only - after the 4th byte arrives, filling the 4-byte PmEvent message field. - - The timestamp field is observed when the output port is opened with - a non-zero latency. A timestamp of zero means "use the current time", - which in turn means to deliver the message with a delay of - latency (the latency parameter used when opening the output port.) - Do not expect PortMidi to sort data according to timestamps -- - messages should be sent in the correct order, and timestamps MUST - be non-decreasing. See also "Example" for Pm_OpenOutput() above. - - A sysex message will generally fill many #PmEvent structures. On - output to a #PortMidiStream with non-zero latency, the first timestamp - on sysex message data will determine the time to begin sending the - message. PortMidi implementations may ignore timestamps for the - remainder of the sysex message. - - On input, the timestamp ideally denotes the arrival time of the - status byte of the message. The first timestamp on sysex message - data will be valid. Subsequent timestamps may denote - when message bytes were actually received, or they may be simply - copies of the first timestamp. - - Timestamps for nested messages: If a real-time message arrives in - the middle of some other message, it is enqueued immediately with - the timestamp corresponding to its arrival time. The interrupted - non-real-time message or 4-byte packet of sysex data will be enqueued - later. The timestamp of interrupted data will be equal to that of - the interrupting real-time message to insure that timestamps are - non-decreasing. - */ -typedef struct { - PmMessage message; - PmTimestamp timestamp; -} PmEvent; - -/** @} */ - -/** \defgroup grp_io Reading and Writing Midi Messages - @{ -*/ -/** Retrieve midi data into a buffer. - - @param stream the open input stream. - - @return the number of events read, or, if the result is negative, - a #PmError value will be returned. - - The Buffer Overflow Problem - - The problem: if an input overflow occurs, data will be lost, - ultimately because there is no flow control all the way back to - the data source. When data is lost, the receiver should be - notified and some sort of graceful recovery should take place, - e.g. you shouldn't resume receiving in the middle of a long sysex - message. - - With a lock-free fifo, which is pretty much what we're stuck with - to enable portability to the Mac, it's tricky for the producer and - consumer to synchronously reset the buffer and resume normal - operation. - - Solution: the entire buffer managed by PortMidi will be flushed - when an overflow occurs. The consumer (Pm_Read()) gets an error - message (#pmBufferOverflow) and ordinary processing resumes as - soon as a new message arrives. The remainder of a partial sysex - message is not considered to be a "new message" and will be - flushed as well. -*/ -PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length); - -/** Test whether input is available. - - @param stream an open input stream. - - @return TRUE, FALSE, or an error value. - - If there was an asynchronous error, pmHostError is returned and you must - call again to determine if input is (also) available. - - You should probably *not* use this function. Call Pm_Read() - instead. If it returns 0, then there is no data available. It is - possible for Pm_Poll() to return TRUE before the complete message - is available, so Pm_Read() could return 0 even after Pm_Poll() - returns TRUE. Only call Pm_Poll() if you want to know that data is - probably available even though you are not ready to receive data. -*/ -PMEXPORT PmError Pm_Poll(PortMidiStream *stream); - -/** Write MIDI data from a buffer. - - @param stream an open output stream. - - @param buffer (address of) an array of MIDI event data. - - @param length the length of the \p buffer. - - @return TRUE, FALSE, or an error value. - - \b buffer may contain: - - short messages - - sysex messages that are converted into a sequence of PmEvent - structures, e.g. sending data from a file or forwarding them - from midi input, with 4 SysEx bytes per PmEvent message, - low-order byte first, until the last message, which may - contain from 1 to 4 bytes ending in MIDI EOX (0xF7). - - PortMidi allows 1-byte real-time messages to be embedded - within SysEx messages, but only on 4-byte boundaries so - that SysEx data always uses a full 4 bytes (except possibly - at the end). Each real-time message always occupies a full - PmEvent (3 of the 4 bytes in the PmEvent's message are - ignored) even when embedded in a SysEx message. - - Use Pm_WriteSysEx() to write a sysex message stored as a contiguous - array of bytes. - - Sysex data may contain embedded real-time messages. - - \p buffer is managed by the caller. The buffer may be destroyed - as soon as this call returns. -*/ -PMEXPORT PmError Pm_Write(PortMidiStream *stream, PmEvent *buffer, - int32_t length); - -/** Write a timestamped non-system-exclusive midi message. - - @param stream an open output stream. - - @param when timestamp for the event. - - @param msg the data for the event. - - @result #pmNoError or an error code. - - Messages are delivered in order, and timestamps must be - non-decreasing. (But timestamps are ignored if the stream was - opened with latency = 0, and otherwise, non-decreasing timestamps - are "corrected" to the lowest valid value.) -*/ -PMEXPORT PmError Pm_WriteShort(PortMidiStream *stream, PmTimestamp when, - PmMessage msg); - -/** Write a timestamped system-exclusive midi message. - - @param stream an open output stream. - - @param when timestamp for the event. - - @param msg the sysex message, terminated with an EOX status byte. - - @result #pmNoError or an error code. - - \p msg is managed by the caller and may be destroyed when this - call returns. -*/ -PMEXPORT PmError Pm_WriteSysEx(PortMidiStream *stream, PmTimestamp when, - unsigned char *msg); - -/** @} */ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* PORTMIDI_PORTMIDI_H */ diff --git a/portmidi/.github/workflows/build.yml b/portmidi/.github/workflows/build.yml deleted file mode 100644 index 351b5cb..0000000 --- a/portmidi/.github/workflows/build.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: build - -on: - push: - pull_request: - -jobs: - build: - strategy: - fail-fast: false - matrix: - include: - - name: Ubuntu - os: ubuntu-latest - install_dir: ~/portmidi - cmake_extras: -DCMAKE_BUILD_TYPE=RelWithDebInfo - - name: macOS - os: macos-latest - install_dir: ~/portmidi - cmake_extras: -DCMAKE_BUILD_TYPE=RelWithDebInfo - - name: Windows - os: windows-latest - install_dir: C:\portmidi - cmake_config: --config RelWithDebInfo - - name: ${{ matrix.name }} - runs-on: ${{ matrix.os }} - steps: - - name: Check out Git repository - uses: actions/checkout@v2 - - name: "[Ubuntu] Install dependencies" - run: sudo apt install -y libasound2-dev - if: runner.os == 'Linux' - - name: Configure - run: cmake -D CMAKE_INSTALL_PREFIX=${{ matrix.install_dir }} ${{ matrix.cmake_extras }} -S . -B build - - name: Build - run: cmake --build build ${{ matrix.cmake_config }} - env: - CMAKE_BUILD_PARALLEL_LEVEL: 2 - - name: Install - run: cmake --install . ${{ matrix.cmake_config }} - working-directory: build - - name: Upload Build Artifact - uses: actions/upload-artifact@v2 - with: - name: ${{ matrix.name }} portmidi build - path: ${{ matrix.install_dir }} diff --git a/portmidi/.github/workflows/docs.yml b/portmidi/.github/workflows/docs.yml deleted file mode 100644 index d0e251b..0000000 --- a/portmidi/.github/workflows/docs.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Generate Docs - -on: - push: - branches: - - main - workflow_dispatch: - -jobs: - doxygen: - name: Doxygen - runs-on: ubuntu-latest - steps: - - name: "Check out repository" - uses: actions/checkout@v2 - - - name: Install Doxygen - run: sudo apt-get update && sudo apt-get install -y --no-install-recommends doxygen - - - name: Generate Documentation - run: doxygen - working-directory: . - - - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: docs/html diff --git a/portmidi/.gitignore b/portmidi/.gitignore deleted file mode 100644 index 19d6650..0000000 --- a/portmidi/.gitignore +++ /dev/null @@ -1,62 +0,0 @@ -.DS_Store -build*/ -*~ -CMakeCache.txt -CMakeFiles/ -CMakeScripts/ -/portmidi.pc -/x64/ -/Debug/ -/Release/ -/pm_java/pmdefaults/pmdefaults.jar -/pm_java/pmdefaults.sln -/pm_java/pmjni.dir/ -/pm_java/x64/ -portmidi.build/ -cmake_install.cmake -*.xcodeproj/ -/.vs/ -/portmidi.sln -*.vcxproj -*.vcxproj.filters -*.vcxproj.user -/Makefile -/libportmidi.so* -/libportmidi.a -/libportmidi_static.a -/libpmjni.so* -/packaging/PortMidiConfig.cmake -/packaging/PortMidiConfigVersion.cmake -/packaging/portmidi.pc -/pm_common/Makefile -/pm_common/portmidi.dir/ -/pm_java/Makefile -/pm_test/Debug/ -/pm_test/Release/ -/pm_test/Makefile -/pm_test/fastrcv -/pm_test/fastrcv.dir/ -/pm_test/latency -/pm_test/latency.dir/ -/pm_test/midiclock -/pm_test/midiclock.dir/ -/pm_test/midithread -/pm_test/midithread.dir/ -/pm_test/midithru -/pm_test/midithru.dir/ -/pm_test/mm -/pm_test/mm.dir/ -/pm_test/multivirtual -/pm_test/qtest -/pm_test/qtest.dir/ -/pm_test/recvvirtual -/pm_test/sendvirtual -/pm_test/sysex -/pm_test/sysex.dir/ -/pm_test/testio -/pm_test/testio.dir/ -/pm_test/virttest -/pm_test/fast -/pm_test/fast.dir/ -/pm_test/pmlist -/pm_test/pmlist.dir/ diff --git a/portmidi/CHANGELOG.txt b/portmidi/CHANGELOG.txt deleted file mode 100644 index fee4330..0000000 --- a/portmidi/CHANGELOG.txt +++ /dev/null @@ -1,213 +0,0 @@ -/* CHANGELOG FOR PORTMIDI - * - * 21Feb22 v2.0.3 Roger Dannenberg - * - this version allows multiple hardware devices to have the same name. - * - * 03Jan22 v2.0.2 Roger Dannenberg - * - many changes for CMake including install support - * - bare-bones Java and PmDefaults support. It runs, but no - * installation. - * - * 16Sep21 Roger Dannenberg - * - Added CreateVirtualInput and CreateVirtualOutput functions (macOS - * & Linux) only. - * - Fix for unicode endpoints on macOS CoreMIDI. - * - Parsing in macOS of realtime message embedded in short messages - * (can this actually happen?) - * - renamed pm_test/test.c to pm_test/testio.c - * - with this release, pm_java, pm_csharp, pm_cl, pm_python, pm_qt - * are marked as "legacy code" and README.txt's refer to other - * projects. I had hoped for "one-stop shopping" for language - * bindings, but developers decided to move work to independent - * repositories. Maybe that's better. - * - * 19Oct09 Roger Dannenberg - * - Changes dynamic library names from portmidi_d to portmidi to - * be backward-compatible with programs expecting a library by - * the old name. - * - * 04Oct09 Roger Dannenberg - * - Converted to using Cmake. - * - Renamed static and dynamic library files to portmidi_s and portmidi_d - * - Eliminated VC9 and VC8 files (went back to simply test.vcproj, etc., - * use Cmake to switch from the provided VC9 files to VC8 or other) - * - Many small changes to prepare for 64-bit architectures (but only - * tested on 32-bit machines) - * - * 16Jun09 Roger Dannenberg - * - Started using Microsoft Visual C++ Version 9 (Express). Converted - * all *-VC9.vcproj file to *.vcproj and renamed old project files to - * *-VC8.proj. Previously, output from VC9 went to special VC9 files, - * that breaks any program or script looking for output in release or - * debug files, so now both compiler version output to the same folders. - * Now, debug version uses static linking with debug DLL runtime, and - * release version uses static linking with statically linked runtime. - * Converted to Inno Setup and worked on scripts to make things build - * properly, especially pmdefaults. - * - * 02Jan09 Roger Dannenberg - * - Created Java interface and wrote PmDefaults application to set - * values for Pm_GetDefaultInputDeviceID() and - * Pm_GetDefaultOutputDeviceID(). Other fixes. - * - * 19Jun08 Roger Dannenberg and Austin Sung - * - Removed USE_DLL_FOR_CLEANUP -- Windows 2000 through Vista seem to be - * fixed now, and can recover if MIDI ports are left open - * - Various other minor patches - * - * 17Jan07 Roger Dannenberg - * - Lots more help for Common Lisp user in pm_cl - * - Minor fix to eliminate a compiler warning - * - Went back to single library in OS X for both portmidi and porttime - * - * 16Jan07 Roger Dannenberg - * - OOPS! fixed bug where short messages all had zero data - * - Makefile.osx static library build now makes universal (i386 + ppc) - * binaries - * - * 15Jan07 Roger Dannenberg - * - multiple rewrites of sysex handling code to take care of - * error-handling, embedded messages, message filtering, - * driver bugs, and host limitations. - * - fixed windows to use dwBufferLength rather than - * dwBytesRecorded for long buffer output (fix by Nigel Brown) - * - Win32 MME code always appends an extra zero to long buffer - * output to work around a problem with earlier versions of Midi Yoke - * - Added mm, a command line Midi Monitor to pm_test suite - * - Revised copyright notice to match PortAudio/MIT license (requests - * are moved out of the license proper and into a separate paragraph) - * - * 18Oct06 Roger Dannenberg - * - replace FIFO in pmutil with Light Pipe-based multiprocessor-safe alg. - * - replace FIFO in portmidi.c with PmQueue from pmutil - * - * 07Oct06 cpr & Roger Dannenberg - * - overhaul of CoreMIDI input to handle running status and multiple - * - messages per packet, with additional error detection - * - added Leigh Smith and Rick Taube support for Common Lisp and - * - dynamic link libraries in OSX - * - initialize static global seq = NULL in pmlinuxalsa.c - * - * 05Sep06 Sebastien Frippiat - * - check if (ALSA) seq exists before closing it in pm_linuxalsa_term() - * - * 05Sep06 Andreas Micheler and Cecilio - * - fixed memory leak by freeing someo objects in pm_winmm_term() - * - and another leak by freeing descriptors in Pm_Terminate() - * - * 23Aug06 RBD - * - various minor fixes - * - * 04Nov05 Olivier Tristan - * - changes to OS X to properly retrieve real device name on CoreMidi - * - * 19Jul05 Roger Dannenberg - * - included pmBufferMaxSize in Pm_GetErrorText() - * - * 23Mar05 Torgier Strand Henriksen - * - cleaner termination of porttime thread under Linux - * - * 15Nov04 Ben Allison - * - sysex output now uses one buffer/message and reallocates buffer - * - if needed - * - filters expanded for many message types and channels - * - detailed changes are as follows: - * ------------- in pmwinmm.c -------------- - * - new #define symbol: OUTPUT_BYTES_PER_BUFFER - * - change SYSEX_BYTES_PER_BUFFER to 1024 - * - added MIDIHDR_BUFFER_LENGTH(x) to correctly count midihdr buffer length - * - change MIDIHDR_SIZE(x) to (MIDIHDR_BUFFER_LENGTH(x) + sizeof(MIDIHDR)) - * - change allocate_buffer to use new MIDIHDR_BUFFER_LENGTH macro - * - new macros for MIDIHDR_SYSEX_SIZE and MIDIHDR_SYSEX_BUFFER_LENGTH - * - similar to above, but counts appropriately for sysex messages - * - added the following members to midiwinmm_struct for sysex data: - * - LPMIDIHDR *sysex_buffers; ** pool of buffers for sysex data ** - * - int num_sysex_buffers; ** how many sysex buffers ** - * - int next_sysex_buffer; ** index of next sysexbuffer to send ** - * - HANDLE sysex_buffer_signal; ** to wait for free sysex buffer ** - * - duplicated allocate_buffer, alocate_buffers and get_free_output_buffer - * - into equivalent sysex_buffer form - * - changed winmm_in_open to initialize new midiwinmm_struct members and - * - to use the new allocate_sysex_buffer() function instead of - * - allocate_buffer() - * - changed winmm_out_open to initialize new members, create sysex buffer - * - signal, and allocate 2 sysex buffers - * - changed winmm_out_delete to free sysex buffers and shut down the sysex - * - buffer signal - * - create new function resize_sysex_buffer which resizes m->hdr to the - * - passed size, and corrects the midiwinmm_struct accordingly. - * - changed winmm_write_byte to use new resize_sysex_buffer function, - * - if resize fails, write current buffer to output and continue - * - changed winmm_out_callback to use buffer_signal or sysex_buffer_signal - * - depending on which buffer was finished - * ------------- in portmidi.h -------------- - * - added pmBufferMaxSize to PmError to indicate that the buffer would be - * - too large for the underlying API - * - added additional filters - * - added prototype, documentation, and helper macro for Pm_SetChannelMask - * ------------- in portmidi.c -------------- - * - added pm_status_filtered() and pm_realtime_filtered() functions to - * separate filtering logic from buffer logic in pm_read_short - * - added Pm_SetChannelMask function - * - added pm_channel_filtered() function - * ------------- in pminternal.h -------------- - * - added member to PortMidiStream for channel mask - * - * 25May04 RBD - * - removed support for MIDI THRU - * - moved filtering from Pm_Read to pm_enqueue to avoid buffer ovfl - * - extensive work on Mac OS X port, especially sysex and error handling - * - * 18May04 RBD - * - removed side-effects from assert() calls. Now you can disable assert(). - * - no longer check pm_hosterror everywhere, fixing a bug where an open - * failure could cause a write not to work on a previously opened port - * until you call Pm_GetHostErrorText(). - * 16May04 RBD and Chris Roberts - * - Some documentation wordsmithing in portmidi.h - * - Dynamically allocate port descriptor structures - * - Fixed parameter error in midiInPrepareBuffer and midiInAddBuffer. - * - * 09Oct03 RBD - * - Changed Thru handling. Now the client does all the work and the client - * must poll or read to keep thru messages flowing. - * - * 31May03 RBD - * - Fixed various bugs. - * - Added linux ALSA support with help from Clemens Ladisch - * - Added Mac OS X support, implemented by Jon Parise, updated and - * integrated by Andrew Zeldis and Zico Kolter - * - Added latency program to build histogram of system latency using PortTime. - * - * 30Jun02 RBD Extensive rewrite of sysex handling. It works now. - * Extensive reworking of error reporting and error text -- no - * longer use dictionary call to delete data; instead, Pm_Open - * and Pm_Close clean up before returning an error code, and - * error text is saved in a system-independent location. - * Wrote sysex.c to test sysex message handling. - * - * 15Jun02 BCT changes: - * - Added pmHostError text handling. - * - For robustness, check PortMidi stream args not NULL. - * - Re-C-ANSI-fied code (changed many C++ comments to C style) - * - Reorganized code in pmwinmm according to input/output functionality (made - * cleanup handling easier to reason about) - * - Fixed Pm_Write calls (portmidi.h says these should not return length but Pm_Error) - * - Cleaned up memory handling (now system specific data deleted via dictionary - * call in PortMidi, allows client to query host errors). - * - Added explicit asserts to verify various aspects of pmwinmm implementation behaves as - * logic implies it should. Specifically: verified callback routines not reentrant and - * all verified status for all unchecked Win32 MMedia API calls perform successfully - * - Moved portmidi initialization and clean-up routines into DLL to fix Win32 MMedia API - * bug (i.e. if devices not explicitly closed, must reboot to debug application further). - * With this change, clients no longer need explicitly call Pm_Initialize, Pm_Terminate, or - * explicitly Pm_Close open devices when using WinMM version of PortMidi. - * - * 23Jan02 RBD Fixed bug in pmwinmm.c thru handling - * - * 21Jan02 RBD Added tests in Pm_OpenInput() and Pm_OpenOutput() to prevent - * opening an input as output and vice versa. - * Added comments and documentation. - * Implemented Pm_Terminate(). - * - */ diff --git a/portmidi/CMakeLists.txt b/portmidi/CMakeLists.txt deleted file mode 100644 index 0107e8c..0000000 --- a/portmidi/CMakeLists.txt +++ /dev/null @@ -1,188 +0,0 @@ -# portmidi -# Roger B. Dannenberg (and others) -# Sep 2009 - 2021 - -cmake_minimum_required(VERSION 3.21) -# (ALSA::ALSA new in 3.12 and used in pm_common/CMakeLists.txt) -# Some Java stuff failed on 3.17 but works with 3.20+ - -cmake_policy(SET CMP0091 NEW) # enables MSVC_RUNTIME_LIBRARY target property - -# Previously, PortMidi versions were simply SVN commit version numbers. -# Versions are now in the form x.y.z -# Changed 1.0 to 2.0 because API is extended with virtual ports: -set(SOVERSION "2") -set(VERSION "2.0.4") - -project(portmidi VERSION "${VERSION}" - DESCRIPTION "Cross-Platform MIDI IO") - -set(LIBRARY_SOVERSION "${SOVERSION}") -set(LIBRARY_VERSION "${VERSION}") - -option(BUILD_SHARED_LIBS "Build shared libraries" ON) - -option(PM_USE_STATIC_RUNTIME - "Use MSVC static runtime. Only applies when BUILD_SHARED_LIBS is OFF" - ON) - -option(USE_SNDIO "Use sndio" OFF) - -# MSVCRT_DLL is used to construct the MSVC_RUNTIME_LIBRARY property -# (see pm_common/CMakeLists.txt and pm_test/CMakeLists.txt) -if(PM_USE_STATIC_RUNTIME AND NOT BUILD_SHARED_LIBS) - set(MSVCRT_DLL "") -else() - set(MSVCRT_DLL "DLL") -endif() - -# Always build with position-independent code (-fPIC) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) - -set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9 CACHE STRING - "make for this OS version or higher") - -# PM_ACTUAL_LIB_NAME is in this scope -- see pm_common/CMakeLists.txt -# PM_NEEDED_LIBS is in this scope -- see pm_common/CMakeLists.txt - -include(GNUInstallDirs) - -# Build Types -# credit: http://cliutils.gitlab.io/modern-cmake/chapters/features.html -set(DEFAULT_BUILD_TYPE "Release") -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS - "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.") - set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE - STRING "Choose the type of build." FORCE) - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Release" "MinSizeRel" "RelWithDebInfo") -endif() - -# where to put libraries? Everything goes here in this directory -# (or Debug or Release, depending on the OS) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - -option(BUILD_JAVA_NATIVE_INTERFACE - "build the Java PortMidi interface library" OFF) - -# Defines are used in both portmidi (in pm_common/) and pmjni (in pm_java), -# so define them here to be inherited by both libraries. -# -# PortMidi software architecture supports multiple system API's to lower- -# level MIDI drivers, e.g. PMNULL (no drivers), Jack (but not supported yet), -# and sndio (BSD, not supported yet). Interfaces are selected by defining, -# e.g., PMALSA. (In principle, we should require PMCOREMIDI (for macOS) -# and PMWINMM (for windows), but these are assumed. -# -if(APPLE OR WIN32) -else(APPLE_OR_WIN32) - set(LINUX_DEFINES "PMALSA" CACHE STRING "must define either PMALSA or PMNULL") - add_compile_definitions(${LINUX_DEFINES}) -endif(APPLE OR WIN32) - -if(BUILD_JAVA_NATIVE_INTERFACE) - message(WARNING - "Java API and PmDefaults program updated 2021, but support has " - "been discontinued. If you need/use this, let developers know.") - set(PMJNI_IF_EXISTS "pmjni") # used by INSTALL below -else(BUILD_JAVA_NATIVE_INTERFACE) - set(PMJNI_IF_EXISTS "") # used by INSTALL below -endif(BUILD_JAVA_NATIVE_INTERFACE) - - -# Something like this might help if you need to build for a specific cpu type: -# set(CMAKE_OSX_ARCHITECTURES x86_64 CACHE STRING -# "change to support other architectures" FORCE) - -include_directories(pm_common porttime) -add_subdirectory(pm_common) - -option(BUILD_PORTMIDI_TESTS - "Build test programs, including midi monitor (mm)" OFF) -if(BUILD_PORTMIDI_TESTS) - add_subdirectory(pm_test) -endif(BUILD_PORTMIDI_TESTS) - -# See note above about Java support (probably) discontinued -if(BUILD_JAVA_NATIVE_INTERFACE) - add_subdirectory(pm_java) -endif(BUILD_JAVA_NATIVE_INTERFACE) - -# Install the libraries and headers (Linux and Mac OS X command line) -INSTALL(TARGETS portmidi ${PMJNI_IF_EXISTS} - EXPORT PortMidiTargets - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") - -INSTALL(FILES - pm_common/portmidi.h - pm_common/pmutil.h - porttime/porttime.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - -# pkgconfig - generate pc file -# See https://cmake.org/cmake/help/latest/command/configure_file.html -if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") - set(PKGCONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}") -else() - set(PKGCONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") -endif() -if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") - set(PKGCONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}") -else() - set(PKGCONFIG_LIBDIR "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") -endif() -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/packaging/portmidi.pc.in - ${CMAKE_CURRENT_BINARY_DIR}/packaging/portmidi.pc @ONLY) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/packaging/portmidi.pc - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) - -# CMake config -set(PORTMIDI_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/PortMidi") -install( - EXPORT PortMidiTargets - FILE PortMidiTargets.cmake - NAMESPACE PortMidi:: - DESTINATION "${PORTMIDI_INSTALL_CMAKEDIR}" -) -include(CMakePackageConfigHelpers) -configure_package_config_file(packaging/PortMidiConfig.cmake.in - "${CMAKE_CURRENT_BINARY_DIR}/packaging/PortMidiConfig.cmake" - INSTALL_DESTINATION "${PORTMIDI_INSTALL_CMAKEDIR}" -) -write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/packaging/PortMidiConfigVersion.cmake" - VERSION "${CMAKE_PROJECT_VERSION}" - COMPATIBILITY SameMajorVersion -) -install( - FILES - "${CMAKE_CURRENT_BINARY_DIR}/packaging/PortMidiConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/packaging/PortMidiConfigVersion.cmake" - DESTINATION "${PORTMIDI_INSTALL_CMAKEDIR}" -) - - - - -# Finding out what CMake is doing is really hard, e.g. COMPILE_FLAGS -# does not include COMPILE_OPTIONS or COMPILE_DEFINTIONS. Thus, the -# following report is probably not complete... -MESSAGE(STATUS "PortMidi Library name: " ${PM_ACTUAL_LIB_NAME}) -MESSAGE(STATUS "Build type: " ${CMAKE_BUILD_TYPE}) -MESSAGE(STATUS "Library Type: " ${LIB_TYPE}) -MESSAGE(STATUS "Compiler flags: " ${CMAKE_CXX_COMPILE_FLAGS}) -get_directory_property(prop COMPILE_DEFINITIONS) -MESSAGE(STATUS "Compile definitions: " ${prop}) -get_directory_property(prop COMPILE_OPTIONS) -MESSAGE(STATUS "Compile options: " ${prop}) -MESSAGE(STATUS "Compiler cxx debug flags: " ${CMAKE_CXX_FLAGS_DEBUG}) -MESSAGE(STATUS "Compiler cxx release flags: " ${CMAKE_CXX_FLAGS_RELEASE}) -MESSAGE(STATUS "Compiler cxx min size flags: " ${CMAKE_CXX_FLAGS_MINSIZEREL}) -MESSAGE(STATUS "Compiler cxx flags: " ${CMAKE_CXX_FLAGS}) - diff --git a/portmidi/Doxyfile b/portmidi/Doxyfile deleted file mode 100644 index 95e3708..0000000 --- a/portmidi/Doxyfile +++ /dev/null @@ -1,2682 +0,0 @@ -# Doxyfile 1.9.2 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed in -# front of the TAG it is preceding. -# -# All text after a single hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists, items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (\" \"). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the configuration -# file that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# https://www.gnu.org/software/libiconv/ for the list of possible encodings. -# The default value is: UTF-8. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by -# double-quotes, unless you are using Doxywizard) that should identify the -# project for which the documentation is generated. This name is used in the -# title of most generated pages and in a few other places. -# The default value is: My Project. - -PROJECT_NAME = PortMidi - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. This -# could be handy for archiving the generated documentation or if some version -# control system is used. - -PROJECT_NUMBER = - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a -# quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = "Cross-platform MIDI IO library" - -# With the PROJECT_LOGO tag one can specify a logo or an icon that is included -# in the documentation. The maximum height of the logo should not exceed 55 -# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy -# the logo to the output directory. - -PROJECT_LOGO = portmusic_logo.png - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path -# into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If -# left blank the current directory will be used. - -OUTPUT_DIRECTORY = ../github-portmidi-portmidi_docs - -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. -# The default value is: NO. - -CREATE_SUBDIRS = NO - -# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII -# characters to appear in the names of generated files. If set to NO, non-ASCII -# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode -# U+3044. -# The default value is: NO. - -ALLOW_UNICODE_NAMES = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. -# The default value is: English. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member -# descriptions after the members that are listed in the file and class -# documentation (similar to Javadoc). Set to NO to disable this. -# The default value is: YES. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief -# description of a member or function before the detailed description -# -# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. -# The default value is: YES. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator that is -# used to form the text in various listings. Each string in this list, if found -# as the leading text of the brief description, will be stripped from the text -# and the result, after processing the whole list, is used as the annotated -# text. Otherwise, the brief description is used as-is. If left blank, the -# following values are used ($name is automatically replaced with the name of -# the entity):The $name class, The $name widget, The $name file, is, provides, -# specifies, contains, represents, a, an and the. - -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief -# description. -# The default value is: NO. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. -# The default value is: NO. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path -# before files name in the file list and in the header files. If set to NO the -# shortest path that makes the file name unique will be used -# The default value is: YES. - -FULL_PATH_NAMES = YES - -# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. -# Stripping is only done if one of the specified strings matches the left-hand -# part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to -# strip. -# -# Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. -# This tag requires that the tag FULL_PATH_NAMES is set to YES. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the -# path mentioned in the documentation of a class, which tells the reader which -# header file to include in order to use a class. If left blank only the name of -# the header file containing the class definition is used. Otherwise one should -# specify the list of include paths that are normally passed to the compiler -# using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't -# support long names like on DOS, Mac, or CD-ROM. -# The default value is: NO. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) -# The default value is: NO. - -JAVADOC_AUTOBRIEF = YES - -# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line -# such as -# /*************** -# as being the beginning of a Javadoc-style comment "banner". If set to NO, the -# Javadoc-style will behave just like regular comments and it will not be -# interpreted by doxygen. -# The default value is: NO. - -JAVADOC_BANNER = NO - -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) -# The default value is: NO. - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as -# a brief description. This used to be the default behavior. The new default is -# to treat a multi-line C++ comment block as a detailed description. Set this -# tag to YES if you prefer the old behavior instead. -# -# Note that setting this tag to YES also means that rational rose comments are -# not recognized any more. -# The default value is: NO. - -MULTILINE_CPP_IS_BRIEF = NO - -# By default Python docstrings are displayed as preformatted text and doxygen's -# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the -# doxygen's special commands can be used and the contents of the docstring -# documentation blocks is shown as doxygen documentation. -# The default value is: YES. - -PYTHON_DOCSTRING = YES - -# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the -# documentation from any documented member that it re-implements. -# The default value is: YES. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new -# page for each member. If set to NO, the documentation of a member will be part -# of the file/class/namespace that contains it. -# The default value is: NO. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen -# uses this value to replace tabs by spaces in code fragments. -# Minimum value: 1, maximum value: 16, default value: 4. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that act as commands in -# the documentation. An alias has the form: -# name=value -# For example adding -# "sideeffect=@par Side Effects:^^" -# will allow you to put the command \sideeffect (or @sideeffect) in the -# documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". Note that you cannot put \n's in the value part of an alias -# to insert newlines (in the resulting output). You can put ^^ in the value part -# of an alias to insert a newline as if a physical newline was in the original -# file. When you need a literal { or } or , in the value part of an alias you -# have to escape them by means of a backslash (\), this can lead to conflicts -# with the commands \{ and \} for these it is advised to use the version @{ and -# @} or use a double escape (\\{ and \\}) - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources -# only. Doxygen will then generate output that is more tailored for C. For -# instance, some of the names that are used will be different. The list of all -# members will be omitted, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or -# Python sources only. Doxygen will then generate output that is more tailored -# for that language. For instance, namespaces will be presented as packages, -# qualified scopes will look different, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources. Doxygen will then generate output that is tailored for Fortran. -# The default value is: NO. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for VHDL. -# The default value is: NO. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice -# sources only. Doxygen will then generate output that is more tailored for that -# language. For instance, namespaces will be presented as modules, types will be -# separated into more groups, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_SLICE = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, -# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, -# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: -# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser -# tries to guess whether the code is fixed or free formatted code, this is the -# default for Fortran type files). For instance to make doxygen treat .inc files -# as Fortran files (default is PHP), and .f files as C (default is Fortran), -# use: inc=Fortran f=C. -# -# Note: For files without extension you can use no_extension as a placeholder. -# -# Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. When specifying no_extension you should add -# * to the FILE_PATTERNS. -# -# Note see also the list of default file extension mappings. - -EXTENSION_MAPPING = - -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments -# according to the Markdown format, which allows for more readable -# documentation. See https://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in -# case of backward compatibilities issues. -# The default value is: YES. - -MARKDOWN_SUPPORT = YES - -# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up -# to that level are automatically included in the table of contents, even if -# they do not have an id attribute. -# Note: This feature currently applies only to Markdown headings. -# Minimum value: 0, maximum value: 99, default value: 5. -# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. - -TOC_INCLUDE_HEADINGS = 5 - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by putting a % sign in front of the word or -# globally by setting AUTOLINK_SUPPORT to NO. -# The default value is: YES. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. -# The default value is: NO. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. -# The default value is: NO. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. -# The default value is: NO. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. -# This will only work if the methods are indeed getting or setting a simple -# type. If this is not the case, or you want to show the methods anyway, you -# should set this option to NO. -# The default value is: YES. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. -# The default value is: NO. - -DISTRIBUTE_GROUP_DOC = NO - -# If one adds a struct or class to a group and this option is enabled, then also -# any nested class or struct is added to the same group. By default this option -# is disabled and one has to add nested compounds explicitly via \ingroup. -# The default value is: NO. - -GROUP_NESTED_COMPOUNDS = NO - -# Set the SUBGROUPING tag to YES to allow class member groups of the same type -# (for instance a group of public functions) to be put as a subgroup of that -# type (e.g. under the Public Functions section). Set it to NO to prevent -# subgrouping. Alternatively, this can be done per class using the -# \nosubgrouping command. -# The default value is: YES. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions -# are shown inside the group in which they are included (e.g. using \ingroup) -# instead of on a separate page (for HTML and Man pages) or section (for LaTeX -# and RTF). -# -# Note that this feature does not work in combination with -# SEPARATE_MEMBER_PAGES. -# The default value is: NO. - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions -# with only public data fields or simple typedef fields will be shown inline in -# the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO, structs, classes, and unions are shown on a separate page (for HTML and -# Man pages) or section (for LaTeX and RTF). -# The default value is: NO. - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or -# enum is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically be -# useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. -# The default value is: NO. - -TYPEDEF_HIDES_STRUCT = YES - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can be -# an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The -# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range -# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest -# the optimal cache size from a speed point of view. -# Minimum value: 0, maximum value: 9, default value: 0. - -LOOKUP_CACHE_SIZE = 0 - -# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use -# during processing. When set to 0 doxygen will based this on the number of -# cores available in the system. You can set it explicitly to a value larger -# than 0 to get more control over the balance between CPU load and processing -# speed. At this moment only the input processing can be done using multiple -# threads. Since this is still an experimental feature the default is set to 1, -# which effectively disables parallel processing. Please report any issues you -# encounter. Generating dot graphs in parallel is controlled by the -# DOT_NUM_THREADS setting. -# Minimum value: 0, maximum value: 32, default value: 1. - -NUM_PROC_THREADS = 1 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in -# documentation are documented, even if no documentation was available. Private -# class members and static file members will be hidden unless the -# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. -# Note: This will also disable the warnings about undocumented members that are -# normally produced when WARNINGS is set to YES. -# The default value is: NO. - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will -# be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual -# methods of a class will be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIV_VIRTUAL = NO - -# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal -# scope will be included in the documentation. -# The default value is: NO. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be -# included in the documentation. -# The default value is: NO. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO, -# only classes defined in header files are included. Does not have any effect -# for Java sources. -# The default value is: YES. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. If set to YES, local methods, -# which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO, only methods in the interface are -# included. -# The default value is: NO. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base name of -# the file that contains the anonymous namespace. By default anonymous namespace -# are hidden. -# The default value is: NO. - -EXTRACT_ANON_NSPACES = NO - -# If this flag is set to YES, the name of an unnamed parameter in a declaration -# will be determined by the corresponding definition. By default unnamed -# parameters remain unnamed in the output. -# The default value is: YES. - -RESOLVE_UNNAMED_PARAMS = YES - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all -# undocumented members inside documented classes or files. If set to NO these -# members will be included in the various overviews, but no documentation -# section is generated. This option has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. If set -# to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# declarations. If set to NO, these declarations will be included in the -# documentation. -# The default value is: NO. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO, these -# blocks will be appended to the function's detailed documentation block. -# The default value is: NO. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation that is typed after a -# \internal command is included. If the tag is set to NO then the documentation -# will be excluded. Set it to YES to include the internal documentation. -# The default value is: NO. - -INTERNAL_DOCS = NO - -# With the correct setting of option CASE_SENSE_NAMES doxygen will better be -# able to match the capabilities of the underlying filesystem. In case the -# filesystem is case sensitive (i.e. it supports files in the same directory -# whose names only differ in casing), the option must be set to YES to properly -# deal with such files in case they appear in the input. For filesystems that -# are not case sensitive the option should be be set to NO to properly deal with -# output files written for symbols that only differ in casing, such as for two -# classes, one named CLASS and the other named Class, and to also support -# references to files without having to specify the exact matching casing. On -# Windows (including Cygwin) and MacOS, users should typically set this option -# to NO, whereas on Linux or other Unix flavors it should typically be set to -# YES. -# The default value is: system dependent. - -CASE_SENSE_NAMES = NO - -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES, the -# scope will be hidden. -# The default value is: NO. - -HIDE_SCOPE_NAMES = YES - -# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will -# append additional text to a page's title, such as Class Reference. If set to -# YES the compound reference will be hidden. -# The default value is: NO. - -HIDE_COMPOUND_REFERENCE= NO - -# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class -# will show which file needs to be included to use the class. -# The default value is: YES. - -SHOW_HEADERFILE = YES - -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of -# the files that are included by a file in the documentation of that file. -# The default value is: YES. - -SHOW_INCLUDE_FILES = YES - -# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each -# grouped member an include statement to the documentation, telling the reader -# which file to include in order to use the member. -# The default value is: NO. - -SHOW_GROUPED_MEMB_INC = NO - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include -# files with double quotes in the documentation rather than with sharp brackets. -# The default value is: NO. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the -# documentation for inline members. -# The default value is: YES. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the -# (detailed) documentation of file and class members alphabetically by member -# name. If set to NO, the members will appear in declaration order. -# The default value is: YES. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief -# descriptions of file, namespace and class members alphabetically by member -# name. If set to NO, the members will appear in declaration order. Note that -# this will also influence the order of the classes in the class list. -# The default value is: NO. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the -# (brief and detailed) documentation of class members so that constructors and -# destructors are listed first. If set to NO the constructors will appear in the -# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. -# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief -# member documentation. -# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting -# detailed member documentation. -# The default value is: NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy -# of group names into alphabetical order. If set to NO the group names will -# appear in their defined order. -# The default value is: NO. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by -# fully-qualified names, including namespaces. If set to NO, the class list will -# be sorted only by class name, not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the alphabetical -# list. -# The default value is: NO. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper -# type resolution of all parameters of a function it will reject a match between -# the prototype and the implementation of a member function even if there is -# only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still -# accept a match between prototype and implementation in such cases. -# The default value is: NO. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo -# list. This list is created by putting \todo commands in the documentation. -# The default value is: YES. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test -# list. This list is created by putting \test commands in the documentation. -# The default value is: YES. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug -# list. This list is created by putting \bug commands in the documentation. -# The default value is: YES. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) -# the deprecated list. This list is created by putting \deprecated commands in -# the documentation. -# The default value is: YES. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional documentation -# sections, marked by \if <section_label> ... \endif and \cond <section_label> -# ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the -# initial value of a variable or macro / define can have for it to appear in the -# documentation. If the initializer consists of more lines than specified here -# it will be hidden. Use a value of 0 to hide initializers completely. The -# appearance of the value of individual variables and macros / defines can be -# controlled using \showinitializer or \hideinitializer command in the -# documentation regardless of this setting. -# Minimum value: 0, maximum value: 10000, default value: 30. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES, the -# list will mention the files that were used to generate the documentation. -# The default value is: YES. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This -# will remove the Files entry from the Quick Index and from the Folder Tree View -# (if specified). -# The default value is: YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces -# page. This will remove the Namespaces entry from the Quick Index and from the -# Folder Tree View (if specified). -# The default value is: YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command command input-file, where command is the value of the -# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file -# version. For an example see the documentation. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can -# optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. See also section "Changing the -# layout of pages" for information. -# -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE -# tag is left empty. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files containing -# the reference definitions. This must be a list of .bib files. The .bib -# extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. -# For LaTeX the style of the bibliography can be controlled using -# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. See also \cite for info how to create references. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the -# messages are off. -# The default value is: NO. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES -# this implies that the warnings are on. -# -# Tip: Turn warnings on while writing the documentation. -# The default value is: YES. - -WARNINGS = YES - -# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate -# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag -# will automatically be disabled. -# The default value is: YES. - -WARN_IF_UNDOCUMENTED = YES - -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as documenting some parameters in -# a documented function twice, or documenting parameters that don't exist or -# using markup commands wrongly. -# The default value is: YES. - -WARN_IF_DOC_ERROR = YES - -# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete -# function parameter documentation. If set to NO, doxygen will accept that some -# parameters have no documentation without warning. -# The default value is: YES. - -WARN_IF_INCOMPLETE_DOC = YES - -# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that -# are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong parameter -# documentation, but not about the absence of documentation. If EXTRACT_ALL is -# set to YES then this flag will automatically be disabled. See also -# WARN_IF_INCOMPLETE_DOC -# The default value is: NO. - -WARN_NO_PARAMDOC = NO - -# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS -# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but -# at the end of the doxygen process doxygen will return with a non-zero status. -# Possible values are: NO, YES and FAIL_ON_WARNINGS. -# The default value is: NO. - -WARN_AS_ERROR = NO - -# The WARN_FORMAT tag determines the format of the warning messages that doxygen -# can produce. The string should contain the $file, $line, and $text tags, which -# will be replaced by the file and line number from which the warning originated -# and the warning text. Optionally the format may contain $version, which will -# be replaced by the version of the file (if it could be obtained via -# FILE_VERSION_FILTER) -# The default value is: $file:$line: $text. - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning and error -# messages should be written. If left blank the output is written to standard -# error (stderr). - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag is used to specify the files and/or directories that contain -# documented source files. You may enter file names like myfile.cpp or -# directories like /usr/src/myproject. Separate the files or directories with -# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING -# Note: If this tag is empty the current directory is searched. - -INPUT =pm_common porttime/porttime.h pm_common/pmutil.h - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses -# libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: -# https://www.gnu.org/software/libiconv/) for the list of possible encodings. -# The default value is: UTF-8. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# read by doxygen. -# -# Note the list of default checked file patterns might differ from the list of -# default file extension mappings. -# -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, -# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C -# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, -# *.vhdl, *.ucf, *.qsf and *.ice. - -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.idl \ - *.ddl \ - *.odl \ - *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.l \ - *.cs \ - *.d \ - *.php \ - *.php4 \ - *.php5 \ - *.phtml \ - *.inc \ - *.m \ - *.markdown \ - *.md \ - *.mm \ - *.dox \ - *.py \ - *.pyw \ - *.f90 \ - *.f95 \ - *.f03 \ - *.f08 \ - *.f18 \ - *.f \ - *.for \ - *.vhd \ - *.vhdl \ - *.ucf \ - *.qsf \ - *.ice - -# The RECURSIVE tag can be used to specify whether or not subdirectories should -# be searched for input files as well. -# The default value is: NO. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. -# The default value is: NO. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* - -EXCLUDE_SYMBOLS = TRUE, FALSE, PMEXPORT - -# The EXAMPLE_PATH tag can be used to specify one or more files or directories -# that contain example code fragments that are included (see the \include -# command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank all -# files are included. - -EXAMPLE_PATTERNS = * - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude commands -# irrespective of the value of the RECURSIVE tag. -# The default value is: NO. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or directories -# that contain images that are to be included in the documentation (see the -# \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command: -# -# <filter> <input-file> -# -# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the -# name of an input file. Doxygen will then use the output that the filter -# program writes to standard output. If FILTER_PATTERNS is specified, this tag -# will be ignored. -# -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: pattern=filter -# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how -# filters are used. If the FILTER_PATTERNS tag is empty or if none of the -# patterns match the file name, INPUT_FILTER is applied. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will also be used to filter the input files that are used for -# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). -# The default value is: NO. - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and -# it is also possible to disable source filtering for a specific pattern using -# *.ext= (so without naming a filter). -# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will be -# generated. Documented entities will be cross-referenced with these sources. -# -# Note: To get rid of all source code in the generated output, make sure that -# also VERBATIM_HEADERS is set to NO. -# The default value is: NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. -# The default value is: NO. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any -# special comment blocks from generated source code fragments. Normal C, C++ and -# Fortran comments will always remain visible. -# The default value is: YES. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# entity all documented functions referencing it will be listed. -# The default value is: NO. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES then for each documented function -# all documented entities called/used by that function will be listed. -# The default value is: NO. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES then the hyperlinks from functions in REFERENCES_RELATION and -# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will -# link to the documentation. -# The default value is: YES. - -REFERENCES_LINK_SOURCE = YES - -# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the -# source code will show a tooltip with additional information such as prototype, -# brief description and links to the definition and documentation. Since this -# will make the HTML file larger and loading of large files a bit slower, you -# can opt to disable this feature. -# The default value is: YES. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -SOURCE_TOOLTIPS = YES - -# If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in -# source browser. The htags tool is part of GNU's global source tagging system -# (see https://www.gnu.org/software/global/global.html). You will need version -# 4.8.6 or higher. -# -# To use it do the following: -# - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file -# - Make sure the INPUT points to the root of the source tree -# - Run doxygen as normal -# -# Doxygen will invoke htags (and that will in turn invoke gtags), so these -# tools must be available from the command line (i.e. in the search path). -# -# The result: instead of the source browser generated by doxygen, the links to -# source code will now point to the output of htags. -# The default value is: NO. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a -# verbatim copy of the header file for each class for which an include is -# specified. Set to NO to disable this. -# See also: Section \class. -# The default value is: YES. - -VERBATIM_HEADERS = YES - -# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the -# clang parser (see: -# http://clang.llvm.org/) for more accurate parsing at the cost of reduced -# performance. This can be particularly helpful with template rich C++ code for -# which doxygen's built-in parser lacks the necessary type information. -# Note: The availability of this option depends on whether or not doxygen was -# generated with the -Duse_libclang=ON option for CMake. -# The default value is: NO. - -CLANG_ASSISTED_PARSING = NO - -# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS -# tag is set to YES then doxygen will add the directory of each input to the -# include path. -# The default value is: YES. -# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. - -CLANG_ADD_INC_PATHS = YES - -# If clang assisted parsing is enabled you can provide the compiler with command -# line options that you would normally use when invoking the compiler. Note that -# the include paths will already be set by doxygen for the files and directories -# specified with INPUT and INCLUDE_PATH. -# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. - -CLANG_OPTIONS = - -# If clang assisted parsing is enabled you can provide the clang parser with the -# path to the directory containing a file called compile_commands.json. This -# file is the compilation database (see: -# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the -# options used when the source files were built. This is equivalent to -# specifying the -p option to a clang tool, such as clang-check. These options -# will then be passed to the parser. Any options specified with CLANG_OPTIONS -# will be added as well. -# Note: The availability of this option depends on whether or not doxygen was -# generated with the -Duse_libclang=ON option for CMake. - -CLANG_DATABASE_PATH = - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all -# compounds will be generated. Enable this if the project contains a lot of -# classes, structs, unions or interfaces. -# The default value is: YES. - -ALPHABETICAL_INDEX = YES - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output -# The default value is: YES. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_OUTPUT = docs - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each -# generated HTML page (for example: .htm, .php, .asp). -# The default value is: .html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a -# standard header. -# -# To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. -# the setting GENERATE_TREEVIEW). It is highly recommended to start with a -# default header using -# doxygen -w html new_header.html new_footer.html new_stylesheet.css -# YourConfigFile -# and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally -# uses. -# Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description -# of the possible markers and block names see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard -# footer. See HTML_HEADER for more information on how to generate a default -# footer and what special commands can be used inside the footer. See also -# section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style -# sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. -# See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. -# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as -# it is more robust and this tag (HTML_STYLESHEET) will in the future become -# obsolete. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined -# cascading style sheets that are included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. -# This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefore more robust against future updates. -# Doxygen will copy the style sheet files to the output directory. -# Note: The order of the extra style sheet files is of importance (e.g. the last -# style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that the -# files will be copied as-is; there are no commands or markers available. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a color-wheel, see -# https://en.wikipedia.org/wiki/Hue for more information. For instance the value -# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 -# purple, and 360 is red again. -# Minimum value: 0, maximum value: 359, default value: 220. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use gray-scales only. A -# value of 255 will produce the most vivid colors. -# Minimum value: 0, maximum value: 255, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the -# luminance component of the colors in the HTML output. Values below 100 -# gradually make the output lighter, whereas values above 100 make the output -# darker. The value divided by 100 is the actual gamma applied, so 80 represents -# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not -# change the gamma. -# Minimum value: 40, maximum value: 240, default value: 80. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = NO - -# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML -# documentation will contain a main index with vertical navigation menus that -# are dynamically created via JavaScript. If disabled, the navigation index will -# consists of multiple levels of tabs that are statically embedded in every HTML -# page. Disable this option to support browsers that do not have JavaScript, -# like the Qt help browser. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_MENUS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries -# shown in the various tree structured indices initially; the user can expand -# and collapse entries dynamically later on. Doxygen will expand the tree to -# such a level that at most the specified number of entries are visible (unless -# a fully collapsed tree already exceeds this amount). So setting the number of -# entries 1 will produce a full collapsed tree by default. 0 is a special value -# representing an infinite number of entries and will result in a full expanded -# tree by default. -# Minimum value: 0, maximum value: 9999, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files will be -# generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: -# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To -# create a documentation set, doxygen will generate a Makefile in the HTML -# output directory. Running make will produce the docset in that directory and -# running make install will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy -# genXcode/_index.html for more information. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_DOCSET = NO - -# This tag determines the name of the docset feed. A documentation feed provides -# an umbrella under which multiple documentation sets from a single provider -# (such as a company or product suite) can be grouped. -# The default value is: Doxygen generated docs. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# This tag specifies a string that should uniquely identify the documentation -# set bundle. This should be a reverse domain-name style string, e.g. -# com.mycompany.MyDocSet. Doxygen will append .docset to the name. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. -# The default value is: org.doxygen.Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. -# The default value is: Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three -# additional HTML index files: index.hhp, index.hhc, and index.hhk. The -# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# on Windows. In the beginning of 2021 Microsoft took the original page, with -# a.o. the download links, offline the HTML help workshop was already many years -# in maintenance mode). You can download the HTML help workshop from the web -# archives at Installation executable (see: -# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo -# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). -# -# The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML -# files are now used as the Windows 98 help format, and will replace the old -# Windows help format (.hlp) on all Windows platforms in the future. Compressed -# HTML files also contain an index, a table of contents, and you can search for -# words in the documentation. The HTML workshop also contains a viewer for -# compressed HTML files. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_HTMLHELP = NO - -# The CHM_FILE tag can be used to specify the file name of the resulting .chm -# file. You can add a path in front of the file if the result should not be -# written to the html output directory. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_FILE = - -# The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler (hhc.exe). If non-empty, -# doxygen will try to run the HTML help compiler on the generated index.hhp. -# The file has to be specified with full path. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -HHC_LOCATION = - -# The GENERATE_CHI flag controls if a separate .chi index file is generated -# (YES) or that it should be included in the main .chm file (NO). -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -GENERATE_CHI = NO - -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) -# and project file content. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_INDEX_ENCODING = - -# The BINARY_TOC flag controls whether a binary table of contents is generated -# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it -# enables the Previous and Next buttons. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members to -# the table of contents of the HTML help documentation and to the tree view. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that -# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help -# (.qch) of the generated HTML documentation. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify -# the file name of the resulting .qch file. The path specified is relative to -# the HTML output folder. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help -# Project output. For more information please see Qt Help Project / Namespace -# (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt -# Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). -# The default value is: doc. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_VIRTUAL_FOLDER = doc - -# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom -# filter to add. For more information please see Qt Help Project / Custom -# Filters (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's filter section matches. Qt Help Project / Filter Attributes (see: -# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_SECT_FILTER_ATTRS = - -# The QHG_LOCATION tag can be used to specify the location (absolute path -# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to -# run qhelpgenerator on the generated .qhp file. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be -# generated, together with the HTML files, they form an Eclipse help plugin. To -# install this plugin and make it available under the help contents menu in -# Eclipse, the contents of the directory containing the HTML and XML files needs -# to be copied into the plugins directory of eclipse. The name of the directory -# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. -# After copying Eclipse needs to be restarted before the help appears. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the Eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have this -# name. Each documentation set should have its own identifier. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# If you want full control over the layout of the generated HTML pages it might -# be necessary to disable the index and replace it with your own. The -# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top -# of each HTML page. A value of NO enables the index and the value YES disables -# it. Since the tabs in the index contain the same information as the navigation -# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. If the tag -# value is set to YES, a side panel will be generated containing a tree-like -# index structure (just like the one that is generated for HTML Help). For this -# to work a browser that supports JavaScript, DHTML, CSS and frames is required -# (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine tune the look of the index (see "Fine-tuning the output"). As an -# example, the default style sheet generated by doxygen has an example that -# shows how to put an image at the root of the tree instead of the PROJECT_NAME. -# Since the tree basically has the same information as the tab index, you could -# consider setting DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_TREEVIEW = YES - -# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the -# FULL_SIDEBAR option determines if the side bar is limited to only the treeview -# area (value NO) or if it should extend to the full height of the window (value -# YES). Setting this to YES gives a layout similar to -# https://docs.readthedocs.io with more room for contents, but less room for the -# project logo, title, and description. If either GENERATOR_TREEVIEW or -# DISABLE_INDEX is set to NO, this option has no effect. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FULL_SIDEBAR = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. -# -# Note that a value of 0 will completely suppress the enum values from appearing -# in the overview section. -# Minimum value: 0, maximum value: 20, default value: 4. -# This tag requires that the tag GENERATE_HTML is set to YES. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used -# to set the initial width (in pixels) of the frame in which the tree is shown. -# Minimum value: 0, maximum value: 1500, default value: 250. -# This tag requires that the tag GENERATE_HTML is set to YES. - -TREEVIEW_WIDTH = 250 - -# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to -# external symbols imported via tag files in a separate window. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -EXT_LINKS_IN_WINDOW = NO - -# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg -# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see -# https://inkscape.org) to generate formulas as SVG images instead of PNGs for -# the HTML output. These images will generally look nicer at scaled resolutions. -# Possible values are: png (the default) and svg (looks nicer but requires the -# pdf2svg or inkscape tool). -# The default value is: png. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FORMULA_FORMAT = png - -# Use this tag to change the font size of LaTeX formulas included as images in -# the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML -# output directory to force them to be regenerated. -# Minimum value: 8, maximum value: 50, default value: 10. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - -# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands -# to create new LaTeX commands to be used in formulas as building blocks. See -# the section "Including formulas" for details. - -FORMULA_MACROFILE = - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# https://www.mathjax.org) which uses client side JavaScript for the rendering -# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX -# installed or if you want to formulas look prettier in the HTML output. When -# enabled you may also need to install MathJax separately and configure the path -# to it using the MATHJAX_RELPATH option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -USE_MATHJAX = NO - -# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. -# Note that the different versions of MathJax have different requirements with -# regards to the different settings, so it is possible that also other MathJax -# settings have to be changed when switching between the different MathJax -# versions. -# Possible values are: MathJax_2 and MathJax_3. -# The default value is: MathJax_2. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_VERSION = MathJax_2 - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. For more details about the output format see MathJax -# version 2 (see: -# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 -# (see: -# http://docs.mathjax.org/en/latest/web/components/output.html). -# Possible values are: HTML-CSS (which is slower, but has the best -# compatibility. This is the name for Mathjax version 2, for MathJax version 3 -# this will be translated into chtml), NativeMML (i.e. MathML. Only supported -# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This -# is the name for Mathjax version 3, for MathJax version 2 this will be -# translated into HTML-CSS) and SVG. -# The default value is: HTML-CSS. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the HTML -# output directory using the MATHJAX_RELPATH option. The destination directory -# should contain the MathJax.js script. For instance, if the mathjax directory -# is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax -# Content Delivery Network so you can quickly see the result without installing -# MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. The default value is: -# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 -# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_RELPATH = - -# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax -# extension names that should be enabled during MathJax rendering. For example -# for MathJax version 2 (see https://docs.mathjax.org/en/v2.7-latest/tex.html -# #tex-and-latex-extensions): -# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols -# For example for MathJax version 3 (see -# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): -# MATHJAX_EXTENSIONS = ams -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces -# of code that will be used on startup of the MathJax code. See the MathJax site -# (see: -# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an -# example see the documentation. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and -# should work on any modern browser. Note that when using HTML help -# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) -# there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then -# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to -# search using the keyboard; to jump to the search box use <access key> + S -# (what the <access key> is depends on the OS and browser, but it is typically -# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down -# key> to jump into the search results window, the results can be navigated -# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel -# the search. The filter options can be selected when the cursor is inside the -# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> -# to select a filter and <Enter> or <escape> to activate or cancel the filter -# option. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -SEARCHENGINE = YES - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using JavaScript. There -# are two flavors of web server based searching depending on the EXTERNAL_SEARCH -# setting. When disabled, doxygen will generate a PHP script for searching and -# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing -# and searching needs to be provided by external tools. See the section -# "External Indexing and Searching" for details. -# The default value is: NO. -# This tag requires that the tag SEARCHENGINE is set to YES. - -SERVER_BASED_SEARCH = NO - -# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP -# script for searching. Instead the search results are written to an XML file -# which needs to be processed by an external indexer. Doxygen will invoke an -# external search engine pointed to by the SEARCHENGINE_URL option to obtain the -# search results. -# -# Doxygen ships with an example indexer (doxyindexer) and search engine -# (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: -# https://xapian.org/). -# -# See the section "External Indexing and Searching" for details. -# The default value is: NO. -# This tag requires that the tag SEARCHENGINE is set to YES. - -EXTERNAL_SEARCH = NO - -# The SEARCHENGINE_URL should point to a search engine hosted by a web server -# which will return the search results when EXTERNAL_SEARCH is enabled. -# -# Doxygen ships with an example indexer (doxyindexer) and search engine -# (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: -# https://xapian.org/). See the section "External Indexing and Searching" for -# details. -# This tag requires that the tag SEARCHENGINE is set to YES. - -SEARCHENGINE_URL = - -# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed -# search data is written to a file for indexing by an external tool. With the -# SEARCHDATA_FILE tag the name of this file can be specified. -# The default file is: searchdata.xml. -# This tag requires that the tag SEARCHENGINE is set to YES. - -SEARCHDATA_FILE = searchdata.xml - -# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the -# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is -# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple -# projects and redirect the results back to the right project. -# This tag requires that the tag SEARCHENGINE is set to YES. - -EXTERNAL_SEARCH_ID = - -# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen -# projects other than the one defined by this configuration file, but that are -# all added to the same external search index. Each project needs to have a -# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of -# to a relative location where the documentation can be found. The format is: -# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... -# This tag requires that the tag SEARCHENGINE is set to YES. - -EXTRA_SEARCH_MAPPINGS = - -#--------------------------------------------------------------------------- -# Configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. -# The default value is: YES. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: latex. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. -# -# Note that when not enabling USE_PDFLATEX the default is latex when enabling -# USE_PDFLATEX the default is pdflatex and when in the later case latex is -# chosen this is overwritten by pdflatex. For specific output languages the -# default can have been set differently, this depends on the implementation of -# the output language. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_CMD_NAME = - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate -# index for LaTeX. -# Note: This tag is used in the Makefile / make.bat. -# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file -# (.tex). -# The default file is: makeindex. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -MAKEINDEX_CMD_NAME = makeindex - -# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to -# generate index for LaTeX. In case there is no backslash (\) as first character -# it will be automatically added in the LaTeX code. -# Note: This tag is used in the generated output file (.tex). -# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. -# The default value is: makeindex. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_MAKEINDEX_CMD = makeindex - -# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX -# documents. This may be useful for small projects and may help to save some -# trees in general. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used by the -# printer. -# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x -# 14 inches) and executive (7.25 x 10.5 inches). -# The default value is: a4. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -PAPER_TYPE = a4 - -# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names -# that should be included in the LaTeX output. The package can be specified just -# by its name or with the correct syntax as to be used with the LaTeX -# \usepackage command. To get the times font for instance you can specify : -# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} -# To use the option intlimits with the amsmath package you can specify: -# EXTRA_PACKAGES=[intlimits]{amsmath} -# If left blank no extra packages will be included. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for -# the generated LaTeX document. The header should contain everything until the -# first chapter. If it is left blank doxygen will generate a standard header. It -# is highly recommended to start with a default header using -# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty -# and then modify the file new_header.tex. See also section "Doxygen usage" for -# information on how to generate the default header that doxygen normally uses. -# -# Note: Only use a user-defined header if you know what you are doing! -# Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. The following -# commands have a special meaning inside the header (and footer): For a -# description of the possible markers and block names see the documentation. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_HEADER = - -# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for -# the generated LaTeX document. The footer should contain everything after the -# last chapter. If it is left blank doxygen will generate a standard footer. See -# LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. See also section "Doxygen -# usage" for information on how to generate the default footer that doxygen -# normally uses. Note: Only use a user-defined footer if you know what you are -# doing! -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_FOOTER = - -# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined -# LaTeX style sheets that are included after the standard style sheets created -# by doxygen. Using this option one can overrule certain style aspects. Doxygen -# will copy the style sheet files to the output directory. -# Note: The order of the extra style sheet files is of importance (e.g. the last -# style sheet in the list overrules the setting of the previous ones in the -# list). -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_EXTRA_STYLESHEET = - -# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the LATEX_OUTPUT output -# directory. Note that the files will be copied as-is; there are no commands or -# markers available. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_EXTRA_FILES = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is -# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will -# contain links (just like the HTML output) instead of page references. This -# makes the output suitable for online browsing using a PDF viewer. -# The default value is: YES. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as -# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX -# files. Set this option to YES, to get a higher quality PDF documentation. -# -# See also section LATEX_CMD_NAME for selecting the engine. -# The default value is: YES. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode -# command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_BATCHMODE = NO - -# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the -# index chapters (such as File Index, Compound Index, etc.) in the output. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_HIDE_INDICES = NO - -# The LATEX_BIB_STYLE tag can be used to specify the style to use for the -# bibliography, e.g. plainnat, or ieeetr. See -# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. -# The default value is: plain. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_BIB_STYLE = plain - -# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_TIMESTAMP = NO - -# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) -# path from which the emoji images will be read. If a relative path is entered, -# it will be relative to the LATEX_OUTPUT directory. If left blank the -# LATEX_OUTPUT directory will be used. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_EMOJI_DIRECTORY = - -#--------------------------------------------------------------------------- -# Configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The -# RTF output is optimized for Word 97 and may not look too pretty with other RTF -# readers/editors. -# The default value is: NO. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: rtf. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF -# documents. This may be useful for small projects and may help to save some -# trees in general. -# The default value is: NO. -# This tag requires that the tag GENERATE_RTF is set to YES. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will -# contain hyperlink fields. The RTF file will contain links (just like the HTML -# output) instead of page references. This makes the output suitable for online -# browsing using Word or some other Word compatible readers that support those -# fields. -# -# Note: WordPad (write) and others do not support links. -# The default value is: NO. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# configuration file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. -# -# See also section "Doxygen usage" for information on how to generate the -# default style sheet that doxygen normally uses. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's configuration file. A template extensions file can be -# generated using doxygen -e rtf extensionFile. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for -# classes and files. -# The default value is: NO. - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. A directory man3 will be created inside the directory specified by -# MAN_OUTPUT. -# The default directory is: man. -# This tag requires that the tag GENERATE_MAN is set to YES. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to the generated -# man pages. In case the manual section does not start with a number, the number -# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is -# optional. -# The default value is: .3. -# This tag requires that the tag GENERATE_MAN is set to YES. - -MAN_EXTENSION = .3 - -# The MAN_SUBDIR tag determines the name of the directory created within -# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by -# MAN_EXTENSION with the initial . removed. -# This tag requires that the tag GENERATE_MAN is set to YES. - -MAN_SUBDIR = - -# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it -# will generate one additional man file for each entity documented in the real -# man page(s). These additional files only source the real man page, but without -# them the man command would be unable to find the correct page. -# The default value is: NO. -# This tag requires that the tag GENERATE_MAN is set to YES. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# Configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that -# captures the structure of the code including all documentation. -# The default value is: NO. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: xml. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_OUTPUT = xml - -# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program -# listings (including syntax highlighting and cross-referencing information) to -# the XML output. Note that enabling this will significantly increase the size -# of the XML output. -# The default value is: YES. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_PROGRAMLISTING = YES - -# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include -# namespace members in file scope as well, matching the HTML output. -# The default value is: NO. -# This tag requires that the tag GENERATE_XML is set to YES. - -XML_NS_MEMB_FILE_SCOPE = NO - -#--------------------------------------------------------------------------- -# Configuration options related to the DOCBOOK output -#--------------------------------------------------------------------------- - -# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files -# that can be used to generate PDF. -# The default value is: NO. - -GENERATE_DOCBOOK = NO - -# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in -# front of it. -# The default directory is: docbook. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_OUTPUT = docbook - -#--------------------------------------------------------------------------- -# Configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures -# the structure of the code including all documentation. Note that this feature -# is still experimental and incomplete at the moment. -# The default value is: NO. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# Configuration options related to Sqlite3 output -#--------------------------------------------------------------------------- - -#--------------------------------------------------------------------------- -# Configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module -# file that captures the structure of the code including all documentation. -# -# Note that this feature is still experimental and incomplete at the moment. -# The default value is: NO. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary -# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI -# output from the Perl module output. -# The default value is: NO. -# This tag requires that the tag GENERATE_PERLMOD is set to YES. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely -# formatted so it can be parsed by a human reader. This is useful if you want to -# understand what is going on. On the other hand, if this tag is set to NO, the -# size of the Perl module output will be much smaller and Perl will parse it -# just the same. -# The default value is: YES. -# This tag requires that the tag GENERATE_PERLMOD is set to YES. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file are -# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful -# so different doxyrules.make files included by the same Makefile don't -# overwrite each other's variables. -# This tag requires that the tag GENERATE_PERLMOD is set to YES. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all -# C-preprocessor directives found in the sources and include files. -# The default value is: YES. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names -# in the source code. If set to NO, only conditional compilation will be -# performed. Macro expansion can be done in a controlled way by setting -# EXPAND_ONLY_PREDEF to YES. -# The default value is: NO. -# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then -# the macro expansion is limited to the macros specified with the PREDEFINED and -# EXPAND_AS_DEFINED tags. -# The default value is: NO. -# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES, the include files in the -# INCLUDE_PATH will be searched if a #include is found. -# The default value is: YES. -# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by the -# preprocessor. -# This tag requires that the tag SEARCH_INCLUDES is set to YES. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will be -# used. -# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that are -# defined before the preprocessor is started (similar to the -D option of e.g. -# gcc). The argument of the tag is a list of macros of the form: name or -# name=definition (no spaces). If the definition and the "=" are omitted, "=1" -# is assumed. To prevent a macro definition from being undefined via #undef or -# recursively expanded use the := operator instead of the = operator. -# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this -# tag can be used to specify a list of macro names that should be expanded. The -# macro definition that is found in the sources will be used. Use the PREDEFINED -# tag if you want to use a different macro definition that overrules the -# definition found in the source code. -# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will -# remove all references to function-like macros that are alone on a line, have -# an all uppercase name, and do not end with a semicolon. Such function macros -# are typically used for boiler-plate code, and will confuse the parser if not -# removed. -# The default value is: YES. -# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration options related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES tag can be used to specify one or more tag files. For each tag -# file the location of the external documentation should be added. The format of -# a tag file without this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where loc1 and loc2 can be relative or absolute paths or URLs. See the -# section "Linking to external documentation" for more information about the use -# of tag files. -# Note: Each tag file must have a unique name (where the name does NOT include -# the path). If a tag file is not located in the directory in which doxygen is -# run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create a -# tag file that is based on the input files it reads. See section "Linking to -# external documentation" for more information about the usage of tag files. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES, all external class will be listed in -# the class index. If set to NO, only the inherited external classes will be -# listed. -# The default value is: NO. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will be -# listed. -# The default value is: YES. - -EXTERNAL_GROUPS = YES - -# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in -# the related pages index. If set to NO, only the current project's pages will -# be listed. -# The default value is: YES. - -EXTERNAL_PAGES = YES - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = NO - -# You can include diagrams made with dia in doxygen documentation. Doxygen will -# then run dia to produce the diagram and insert it in the documentation. The -# DIA_PATH tag allows you to specify the directory where the dia binary resides. -# If left empty dia is assumed to be found in the default search path. - -DIA_PATH = - -# If set to YES the inheritance and collaboration graphs will hide inheritance -# and usage relations if the target is undocumented or is not a class. -# The default value is: YES. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz (see: -# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent -# Bell Labs. The other options in this section have no effect if this option is -# set to NO -# The default value is: NO. - -HAVE_DOT = NO - -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed -# to run in parallel. When set to 0 doxygen will base this on the number of -# processors available in the system. You can set it explicitly to a value -# larger than 0 to get control over the balance between CPU load and processing -# speed. -# Minimum value: 0, maximum value: 32, default value: 0. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_NUM_THREADS = 0 - -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_FONTNAME = Helvetica - -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the default font as specified with -# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set -# the path where dot can find it using this tag. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_FONTPATH = - -# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for -# each documented class showing the direct and indirect inheritance relations. -# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a -# graph for each documented class showing the direct and indirect implementation -# dependencies (inheritance, containment, and class references variables) of the -# class with other documented classes. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for -# groups, showing the direct groups dependencies. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -UML_LOOK = NO - -# If the UML_LOOK tag is enabled, the fields and methods are shown inside the -# class node. If there are many fields or methods and many nodes the graph may -# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the -# number of items for each type to make the size more manageable. Set this to 0 -# for no limit. Note that the threshold may be exceeded by 50% before the limit -# is enforced. So when you set the threshold to 10, up to 15 fields may appear, -# but if the number exceeds 15, the total amount of fields shown is limited to -# 10. -# Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag UML_LOOK is set to YES. - -UML_LIMIT_NUM_FIELDS = 10 - -# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and -# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS -# tag is set to YES, doxygen will add type and arguments for attributes and -# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen -# will not generate fields with class member information in the UML graphs. The -# class diagrams will look similar to the default class diagrams but using UML -# notation for the relationships. -# Possible values are: NO, YES and NONE. -# The default value is: NO. -# This tag requires that the tag UML_LOOK is set to YES. - -DOT_UML_DETAILS = NO - -# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters -# to display on a single line. If the actual line length exceeds this threshold -# significantly it will wrapped across multiple lines. Some heuristics are apply -# to avoid ugly line breaks. -# Minimum value: 0, maximum value: 1000, default value: 17. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_WRAP_THRESHOLD = 17 - -# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and -# collaboration graphs will show the relations between templates and their -# instances. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -TEMPLATE_RELATIONS = NO - -# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to -# YES then doxygen will generate a graph for each documented file showing the -# direct and indirect include dependencies of the file with other documented -# files. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -INCLUDE_GRAPH = YES - -# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are -# set to YES then doxygen will generate a graph for each documented file showing -# the direct and indirect include dependencies of the file with other documented -# files. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH tag is set to YES then doxygen will generate a call -# dependency graph for every global function or class method. -# -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. Disabling a call graph can be -# accomplished by means of the command \hidecallgraph. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller -# dependency graph for every global function or class method. -# -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. Disabling a caller graph can be -# accomplished by means of the command \hidecallergraph. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical -# hierarchy of all classes instead of a textual one. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the -# dependencies a directory has on other directories in a graphical way. The -# dependency relations are determined by the #include relations between the -# files in the directories. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. For an explanation of the image formats see the section -# output formats in the documentation of the dot tool (Graphviz (see: -# http://www.graphviz.org/)). -# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order -# to make the SVG files visible in IE 9+ (other browsers do not have this -# requirement). -# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, -# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and -# png:gdiplus:gdiplus. -# The default value is: png. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_IMAGE_FORMAT = png - -# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to -# enable generation of interactive SVG images that allow zooming and panning. -# -# Note that this requires a modern browser other than Internet Explorer. Tested -# and working are Firefox, Chrome, Safari, and Opera. -# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make -# the SVG files visible. Older versions of IE do not have SVG support. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -INTERACTIVE_SVG = NO - -# The DOT_PATH tag can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the \dotfile -# command). -# This tag requires that the tag HAVE_DOT is set to YES. - -DOTFILE_DIRS = - -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the \mscfile -# command). - -MSCFILE_DIRS = - -# The DIAFILE_DIRS tag can be used to specify one or more directories that -# contain dia files that are included in the documentation (see the \diafile -# command). - -DIAFILE_DIRS = - -# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file. If left blank, it is assumed -# PlantUML is not used or called during a preprocessing step. Doxygen will -# generate a warning when it encounters a \startuml command in this case and -# will not generate output for the diagram. - -PLANTUML_JAR_PATH = - -# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a -# configuration file for plantuml. - -PLANTUML_CFG_FILE = - -# When using plantuml, the specified paths are searched for files specified by -# the !include statement in a plantuml block. - -PLANTUML_INCLUDE_PATH = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes -# that will be shown in the graph. If the number of nodes in a graph becomes -# larger than this value, doxygen will truncate the graph, which is visualized -# by representing a node as a red box. Note that doxygen if the number of direct -# children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that -# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. -# Minimum value: 0, maximum value: 10000, default value: 50. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs -# generated by dot. A depth value of 3 means that only nodes reachable from the -# root by following a path via at most 3 edges will be shown. Nodes that lay -# further from the root node will be omitted. Note that setting this option to 1 -# or 2 may greatly reduce the computation time needed for large code bases. Also -# note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. -# Minimum value: 0, maximum value: 1000, default value: 0. -# This tag requires that the tag HAVE_DOT is set to YES. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) support -# this, this feature is disabled by default. -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page -# explaining the meaning of the various boxes and arrows in the dot generated -# graphs. -# The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate -# files that are used to generate the various graphs. -# -# Note: This setting is not only used for dot files but also for msc temporary -# files. -# The default value is: YES. - -DOT_CLEANUP = YES diff --git a/portmidi/README.md b/portmidi/README.md deleted file mode 100644 index 3f0463c..0000000 --- a/portmidi/README.md +++ /dev/null @@ -1,128 +0,0 @@ -# PortMidi - Cross-Platform MIDI IO - -This is the canonical release of PortMidi. - -See other repositories within [PortMidi](https://github.com/PortMidi) -for related code and bindings (although currently, not much is here). - -## [Full C API documentation is here.](https://portmidi.github.io/portmidi_docs/) - -## Compiling and Using PortMidi - -Use CMake (or ccmake) to create a Makefile for Linux/BSD or a -project file for Xcode or MS Visual Studio. Use make or an IDE to compile: -``` -sudo apt install libasound2-dev # on Linux, you need ALSA -cd portmidi # start in the top-level portmidi directory -ccmake . # set any options interactively, type c to configure - # type g to generate a Makefile or IDE project - # type q to quit - # (alternatively, run the CMake GUI and use - # Configure and Generate buttons to build IDE project) -make # compile sources and build PortMidi library - # (alternatively, open project file with your IDE) -sudo make install # if you want to install to your system -``` - -## Installation - -My advice is to build PortMidi and statically link it to your -application. This gives you more control over versions. However, -installing PortMidi to your system is preferred by some, and the -following should do it: -``` -cmake . -make -sudo make install -``` - -## Language Bindings - -Here is a guide to some other projects using PortMidi. There is not -much coordination, so let us know if there are better or alternative -bindings for these and other languages: - -- Python: Various libraries and packages exist; search and ye shall - find. If you wouldn't like to do research, check out [mido](https://mido.readthedocs.io/en/stable/) -- [SML](https://github.com/jh-midi/portmidi-sml2) -- [OCaml](https://ocaml.org/p/portmidi/0.1) -- [Haskell](https://hackage.haskell.org/package/PortMidi) -- [Erlang](https://hexdocs.pm/portmidi/PortMidi.html) -- [Julia](https://github.com/SteffenPL/PortMidi.jl) -- [C#](https://github.com/net-core-audio/portmidi) -- [Rust](https://musitdev.github.io/portmidi-rs/) -- [Go](https://github.com/rakyll/portmidi) -- [Odin](https://pkg.odin-lang.org/vendor/portmidi/) -- [Serpent](https://sourceforge.net/projects/serpent/) - a real-time - Python-like language has PortMidi built-in, a MIDI-timestamp-aware - scheduler, and GUI support for device selection. -- [Pd (Pure Data)](https://puredata.info/) uses PortMidi. - - -## What's New? - -(Not so new, but significant:) Support for the **PmDefaults** program, -which enabled a graphical interface to select default MIDI devices, -has been removed for lack of interest. This allowed us to also remove -C code to read and parse Java preference files on various systems, -simplifying the library. **PmDefaults** allowed simple command-line -programs to use `Pm_DefaultInputDeviceID()` and -`Pm_DefaultOutputDeviceID()` rather than creating device selection -interfaces. Now, you should either pass devices on the command line or -create your own selection interface when building a GUI -application. (See pm_tests for examples.) `Pm_DefaultInputDeviceID()` -and `Pm_DefaultOutputDeviceID()` now return a valid device if -possible, but they may not actually reflect any user preference. - -Haiku support in a minimal implementation. See TODO's in source. - -sndio is also minimally supported, allowing basic PortMidi functions -in OpenBSD, FreeBSD, and NetBSD by setting USE_SNDIO for CMake, but -not delayed/timestamped output and virtual devices. - -# Other Repositories - -PortMidi used to be part of the PortMedia suite, but this repo has -been reduced to mostly just C/C++ code for PortMidi. You will find -some other repositories in this PortMidi project set up for language -bindings (volunteers and contributors are invited!). Other code -removed from previous releases of PortMedia include: - -## PortSMF - -A Standard MIDI File (SMF) (and more) library is in the [portsmf -repository](https://github.com/rbdannenberg/portsmf). - -PortSMF is a library for reading/writing/editing Standard MIDI -Files. It is actually much more, with a general representation of -events and updates with properties consisting of attributes and typed -values. Familiar properties of pitch, time, duration, and channel are -built into events and updates to make them faster to access and more -compact. - -To my knowledge, PortSMF has the most complete and useful handling of -MIDI tempo tracks. E.g., you can edit notes according to either beat -or time, and you can edit tempo tracks, for example, flattening the -tempo while preserving the beat alignment, preserving the real time -while changing the tempo or stretching the tempo over some interval. - -In addition to Standard MIDI Files, PortSMF supports an ASCII -representation called Allegro. PortSMF and Allegro are used for -Audacity Note Tracks. - -## scorealign - -Scorealign used to be part of the PortMedia suite. It is now at the -[scorealign repository](https://github.com/rbdannenberg/scorealign). - -Scorealign aligns audio-to-audio, audio-to-MIDI or MIDI-to-MIDI using -dynamic time warping (DTW) of a computed chromagram -representation. There are some added smoothing tricks to improve -performance. This library is written in C and runs substantially -faster than most other implementations, especially those written in -MATLAB, due to the core DTW algorithm. Users should be warned that -while chromagrams are robust features for alignment, they achieve -robustness by operating at fairly high granularity, e.g., durations of -around 100ms, which limits time precision. Other more recent -algorithms can doubtless do better, but be cautious of claims, since -it all depends on what assumptions you can make about the music. diff --git a/portmidi/README.txt b/portmidi/README.txt deleted file mode 100755 index e09aa2e..0000000 --- a/portmidi/README.txt +++ /dev/null @@ -1,88 +0,0 @@ -README for PortMidi
-
-Roger B. Dannenberg
-
-Documentation for PortMidi is found in pm_common/portmidi.h.
-Documentation in HTML is available at portmidi.github.io/portmidi_docs/
-
-Additional documentation:
- - README.md (overview, how to build, what's new)
- - Windows: see pm_win/README_WIN.txt and pm_win/debugging_dlls.txt
- - Linux: see pm_linux/README_LINUX.txt
- - Mac OSX: see pm_mac/README_MAC.txt
- - Other Languages: look for other repos at github.com/PortMidi,
- and search README.md for pointers to other projects.
-
----------- some notes on the design of PortMidi ----------
-
-POINTERS VS DEVICE NUMBERS
-
-When you open a MIDI port, PortMidi allocates a structure to
-maintain the state of the open device. Since every device is
-also listed in a table, you might think it would be simpler to
-use the table index rather than a pointer to identify a device.
-This would also help with error checking (it's hard to make
-sure a pointer is valid). PortMidi's design parallels that of
-PortAudio.
-
-ERROR HANDLING
-
-Error handling turned out to be much more complicated than expected.
-PortMidi functions return error codes that the caller can check.
-In addition, errors may occur asynchronously due to MIDI input.
-However, for Windows, there are virtually no errors that can
-occur if the code is correct and not passing bogus values. One
-exception is an error that the system is out of memory, but my
-guess is that one is unlikely to recover gracefully from that.
-Therefore, all errors in callbacks are guarded by assert(), which
-means not guarded at all in release configurations.
-
-Ordinarily, the caller checks for an error code. If the error is
-system-dependent, pmHostError is returned and the caller can
-call Pm_GetHostErrorText to get a text description of the error.
-
-Host error codes are system-specific and are recorded in the
-system-specific data allocated for each open MIDI port.
-However, if an error occurs on open or close,
-we cannot store the error with the device because there will be
-no device data (assuming PortMidi cleans up after devices that
-are not open). For open and close, we will convert the error
-to text, copy it to a global string, and set pm_hosterror, a
-global flag.
-
-Similarly, whenever a Read or Write operation returns pmHostError,
-the corresponding error string is copied to a global string
-and pm_hosterror is set. This makes getting error strings
-simple and uniform, although it does cost a string copy and some
-overhead even if the user does not want to look at the error data.
-
-The system-specific Read, Write, Poll, etc. implementations should
-check for asynchronous errors and return immediately if one is
-found so that these get reported. This happens in the Mac OS X
-code, where lots of things are happening in callbacks, but again,
-in Windows, there are no error codes recorded in callbacks.
-
-DEBUGGING
-
-If you are building a console application for research, we suggest
-compiling with the option PM_CHECK_ERRORS. This will insert a
-check for error return values at the end of each PortMidi
-function. If an error is encountered, a text message is printed
-using printf(), the user is asked to type ENTER, and then exit(-1)
-is called to clean up and terminate the program.
-
-You should not use PM_CHECK_ERRORS if printf() does not work
-(e.g. this is not a console application under Windows, or there
-is no visible console on some other OS), and you should not use
-PM_CHECK_ERRORS if you intend to recover from errors rather than
-abruptly terminate the program.
-
-The Windows version (and perhaps others) also offers a DEBUG
-compile-time option. See README_WIN.txt.
-
-RELEASE
-
-To make a new release, update VERSION variable in CMakeLists.txt.
-
-Update CHANGELOG.txt. What's new?
-
diff --git a/portmidi/license.txt b/portmidi/license.txt deleted file mode 100644 index c757b37..0000000 --- a/portmidi/license.txt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * PortMidi Portable Real-Time MIDI Library - * - * license.txt -- a copy of the PortMidi copyright notice and license information - * - * Latest version available at: http://sourceforge.net/projects/portmedia - * - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * Copyright (c) 2001-2009 Roger B. Dannenberg - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortMidi license; however, - * the PortMusic community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ diff --git a/portmidi/packaging/PortMidiConfig.cmake.in b/portmidi/packaging/PortMidiConfig.cmake.in deleted file mode 100644 index 0d24f4d..0000000 --- a/portmidi/packaging/PortMidiConfig.cmake.in +++ /dev/null @@ -1,15 +0,0 @@ -@PACKAGE_INIT@ - -include(CMakeFindDependencyMacro) -if(UNIX AND NOT APPLE AND NOT HAIKU AND (@LINUX_DEFINES@ MATCHES ".*PMALSA.*")) - find_dependency(ALSA) -endif() - -if(NOT WIN32) - set(THREADS_PREFER_PTHREAD_FLAG ON) - find_package(Threads REQUIRED) -endif() - -include("${CMAKE_CURRENT_LIST_DIR}/PortMidiTargets.cmake") - -check_required_components(PortMidi) diff --git a/portmidi/packaging/portmidi.pc.in b/portmidi/packaging/portmidi.pc.in deleted file mode 100644 index e9d929c..0000000 --- a/portmidi/packaging/portmidi.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@CMAKE_INSTALL_PREFIX@ -exec_prefix=${prefix} -libdir=@PKGCONFIG_LIBDIR@ -includedir=@PKGCONFIG_INCLUDEDIR@ - -Name: @CMAKE_PROJECT_NAME@ -Description: @CMAKE_PROJECT_DESCRIPTION@ -Version: @CMAKE_PROJECT_VERSION@ -Cflags: -I${includedir} -Libs: -L${libdir} -l@CMAKE_PROJECT_NAME@ -Requires.private: @PKGCONFIG_REQUIRES_PRIVATE@ diff --git a/portmidi/pm_common/CMakeLists.txt b/portmidi/pm_common/CMakeLists.txt deleted file mode 100644 index 1ad54ad..0000000 --- a/portmidi/pm_common/CMakeLists.txt +++ /dev/null @@ -1,167 +0,0 @@ -# pm_common/CMakeLists.txt -- how to build portmidi library - -# creates the portmidi library -# exports PM_NEEDED_LIBS to parent. It seems that PM_NEEDED_LIBS for -# Linux should include Thread::Thread and ALSA::ALSA, but these -# are not visible in other CMake files, even though the portmidi -# target is. Therefore, Thread::Thread is replaced by -# CMAKE_THREAD_LIBS_INIT and ALSA::ALSA is replaced by ALSA_LIBRARIES. -# Is there a better way to do this? Maybe this whole file should be -# at the parent level. - -# Support alternative name for static libraries to avoid confusion. -# (In particular, Xcode has automatically converted portmidi.a to -# portmidi.dylib without warning, so using portmidi-static.a eliminates -# this possibility, but default for all libs is "portmidi"): -set(PM_STATIC_LIB_NAME "portmidi" CACHE STRING - "For static builds, the PortMidi library name, e.g. portmidi-static. - Default is portmidi") -set(PM_ACTUAL_LIB_NAME "portmidi") -if(NOT BUILD_SHARED_LIBS) - set(PM_ACTUAL_LIB_NAME ${PM_STATIC_LIB_NAME}) -endif() - -# set the build directory for libportmidi.a to be in portmidi, not in -# portmidi/pm_common. Must be done here BEFORE add_library below. -if(APPLE OR WIN32) - set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) - # set the build directory for .dylib libraries - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) -endif(APPLE OR WIN32) - -# we need full paths to sources because they are shared with other targets -# (in particular pmjni). Set PMDIR to the top-level portmidi directory: -get_filename_component(PMDIR ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY) -set(PM_LIB_PUBLIC_SRC ${PMDIR}/pm_common/portmidi.c - ${PMDIR}/pm_common/pmutil.c - ${PMDIR}/porttime/porttime.c) -add_library(portmidi ${PM_LIB_PUBLIC_SRC}) - -# MSVCRT_DLL is "DLL" for shared runtime library, and "" for static: -set_target_properties(portmidi PROPERTIES - VERSION ${LIBRARY_VERSION} - SOVERSION ${LIBRARY_SOVERSION} - OUTPUT_NAME "${PM_ACTUAL_LIB_NAME}" - MSVC_RUNTIME_LIBRARY - "MultiThreaded$<$<CONFIG:Debug>:Debug>${MSVCRT_DLL}" - WINDOWS_EXPORT_ALL_SYMBOLS TRUE) -target_include_directories(portmidi PUBLIC - $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> - $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>) - - -option(PM_CHECK_ERRORS -"Insert a check for error return values at the end of each PortMidi function. -If an error is encountered, a text message is printed using printf(), the user -is asked to type ENTER, and then exit(-1) is called to clean up and terminate -the program. - -You should not use PM_CHECK_ERRORS if printf() does not work (e.g. this is not -a console application under Windows, or there is no visible console on some -other OS), and you should not use PM_CHECK_ERRORS if you intend to recover -from errors rather than abruptly terminate the program." OFF) -if(PM_CHECK_ERRORS) - target_compile_definitions(portmidi PRIVATE PM_CHECK_ERRORS) -endif(PM_CHECK_ERRORS) - -macro(prepend_path RESULT PATH) - set(${RESULT}) - foreach(FILE ${ARGN}) - list(APPEND ${RESULT} "${PATH}${FILE}") - endforeach(FILE) -endmacro(prepend_path) - -# UNIX needs pthread library -if(NOT WIN32) - set(THREADS_PREFER_PTHREAD_FLAG ON) - find_package(Threads REQUIRED) -endif() - -# Check for sndio -if(USE_SNDIO) - include (FindPackageHandleStandardArgs) - find_path(SNDIO_INCLUDE_DIRS NAMES sndio.h) - find_library(SNDIO_LIBRARY sndio) - find_package_handle_standard_args(Sndio - REQUIRED_VARS SNDIO_LIBRARY SNDIO_INCLUDE_DIRS) -endif(USE_SNDIO) - -# first include the appropriate system-dependent file: -if(SNDIO_FOUND AND USE_SNDIO) - set(PM_LIB_PRIVATE_SRC - ${PMDIR}/porttime/ptlinux.c - ${PMDIR}/pm_sndio/pmsndio.c) - set(PM_NEEDED_LIBS Threads::Threads ${SNDIO_LIBRARY} PARENT_SCOPE) - target_link_libraries(portmidi PRIVATE Threads::Threads ${SNDIO_LIBRARY}) - target_include_directories(portmidi PRIVATE ${SNDIO_INCLUDE_DIRS}) -elseif(UNIX AND APPLE) - set(Threads::Threads "" PARENT_SCOPE) - set(PM_LIB_PRIVATE_SRC - ${PMDIR}/porttime/ptmacosx_mach.c - ${PMDIR}/pm_mac/pmmac.c - ${PMDIR}/pm_mac/pmmacosxcm.c) - set(PM_NEEDED_LIBS - ${CMAKE_THREAD_LIBS_INIT} - -Wl,-framework,CoreAudio - -Wl,-framework,CoreFoundation - -Wl,-framework,CoreMidi - -Wl,-framework,CoreServices - PARENT_SCOPE) - target_link_libraries(portmidi PRIVATE - Threads::Threads - -Wl,-framework,CoreAudio - -Wl,-framework,CoreFoundation - -Wl,-framework,CoreMidi - -Wl,-framework,CoreServices - ) - # set to CMake default; is this right?: - set_target_properties(portmidi PROPERTIES MACOSX_RPATH ON) -elseif(HAIKU) - set(PM_LIB_PRIVATE_SRC - ${PMDIR}/porttime/pthaiku.cpp - ${PMDIR}/pm_haiku/pmhaiku.cpp) - set(PM_NEEDED_LIBS be midi midi2 PARENT_SCOPE) - target_link_libraries(portmidi PRIVATE be midi midi2) -elseif(UNIX) - target_compile_definitions(portmidi PRIVATE ${LINUX_FLAGS}) - set(PM_LIB_PRIVATE_SRC - ${PMDIR}/porttime/ptlinux.c - ${PMDIR}/pm_linux/pmlinux.c - ${PMDIR}/pm_linux/pmlinuxnull.c) - if(${LINUX_DEFINES} MATCHES ".*PMALSA.*") - # Note that ALSA is not required if PMNULL is defined -- PortMidi will then - # compile without ALSA and report no MIDI devices. Later, PMSNDIO or PMJACK - # might be additional options. - find_package(ALSA REQUIRED) - list(APPEND PM_LIB_PRIVATE_SRC ${PMDIR}/pm_linux/pmlinuxalsa.c) - set(PM_NEEDED_LIBS ${CMAKE_THREAD_LIBS_INIT} ${ALSA_LIBRARIES} PARENT_SCOPE) - target_link_libraries(portmidi PRIVATE Threads::Threads ALSA::ALSA) - set(PKGCONFIG_REQUIRES_PRIVATE "alsa" PARENT_SCOPE) - else() - message(WARNING "No PMALSA, so PortMidi will not use ALSA, " - "and will not find or open MIDI devices.") - set(PM_NEEDED_LIBS ${CMAKE_THREAD_LIBS_INIT} PARENT_SCOPE) - target_link_libraries(portmidi PRIVATE Threads::Threads) - endif() -elseif(WIN32) - set(PM_LIB_PRIVATE_SRC - ${PMDIR}/porttime/ptwinmm.c - ${PMDIR}/pm_win/pmwin.c - ${PMDIR}/pm_win/pmwinmm.c) - set(PM_NEEDED_LIBS winmm PARENT_SCOPE) - target_link_libraries(portmidi PRIVATE winmm) -# if(NOT BUILD_SHARED_LIBS AND PM_USE_STATIC_RUNTIME) - # /MDd is multithread debug DLL, /MTd is multithread debug - # /MD is multithread DLL, /MT is multithread. Change to static: -# include(../pm_win/static.cmake) -# endif() -else() - message(FATAL_ERROR "Operating system not supported.") -endif() - -set(PM_LIB_PUBLIC_SRC ${PM_LIB_PUBLIC_SRC} PARENT_SCOPE) # export to parent -set(PM_LIB_PRIVATE_SRC ${PM_LIB_PRIVATE_SRC} PARENT_SCOPE) # export to parent - -target_sources(portmidi PRIVATE ${PM_LIB_PRIVATE_SRC}) - diff --git a/portmidi/pm_common/pminternal.h b/portmidi/pm_common/pminternal.h deleted file mode 100755 index 8b3d8f5..0000000 --- a/portmidi/pm_common/pminternal.h +++ /dev/null @@ -1,190 +0,0 @@ -/** @file pminternal.h header for PortMidi implementations */ - -/* this file is included by files that implement library internals */ -/* Here is a guide to implementers: - provide an initialization function similar to pm_winmm_init() - add your initialization function to pm_init() - Note that your init function should never require not-standard - libraries or fail in any way. If the interface is not available, - simply do not call pm_add_device. This means that non-standard - libraries should try to do dynamic linking at runtime using a DLL - and return without error if the DLL cannot be found or if there - is any other failure. - implement functions as indicated in pm_fns_type to open, read, write, - close, etc. - call pm_add_device() for each input and output device, passing it a - pm_fns_type structure. - assumptions about pm_fns_type functions are given below. - */ - -/** @cond INTERNAL - add INTERNAL to Doxygen ENABLED_SECTIONS to include */ - -#ifdef __cplusplus -extern "C" { -#endif - -extern int pm_initialized; /* see note in portmidi.c */ -extern PmDeviceID pm_default_input_device_id; -extern PmDeviceID pm_default_output_device_id; - -/* these are defined in system-specific file */ -void *pm_alloc(size_t s); -void pm_free(void *ptr); - -/* if a host error (an error reported by the host MIDI API that is not - * mapped to a PortMidi error code) occurs in a synchronous operation - * (i.e., not in a callback from another thread) set these: */ -extern int pm_hosterror; /* boolean */ -extern char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN]; - -struct pm_internal_struct; - -/* these do not use PmInternal because it is not defined yet... */ -typedef PmError (*pm_write_short_fn)(struct pm_internal_struct *midi, - PmEvent *buffer); -typedef PmError (*pm_begin_sysex_fn)(struct pm_internal_struct *midi, - PmTimestamp timestamp); -typedef PmError (*pm_end_sysex_fn)(struct pm_internal_struct *midi, - PmTimestamp timestamp); -typedef PmError (*pm_write_byte_fn)(struct pm_internal_struct *midi, - unsigned char byte, PmTimestamp timestamp); -typedef PmError (*pm_write_realtime_fn)(struct pm_internal_struct *midi, - PmEvent *buffer); -typedef PmError (*pm_write_flush_fn)(struct pm_internal_struct *midi, - PmTimestamp timestamp); -typedef PmTimestamp (*pm_synchronize_fn)(struct pm_internal_struct *midi); -/* pm_open_fn should clean up all memory and close the device if any part - of the open fails */ -typedef PmError (*pm_open_fn)(struct pm_internal_struct *midi, - void *driverInfo); -typedef PmError (*pm_create_fn)(int is_input, const char *name, - void *driverInfo); -typedef PmError (*pm_delete_fn)(PmDeviceID id); -typedef PmError (*pm_abort_fn)(struct pm_internal_struct *midi); -/* pm_close_fn should clean up all memory and close the device if any - part of the close fails. */ -typedef PmError (*pm_close_fn)(struct pm_internal_struct *midi); -typedef PmError (*pm_poll_fn)(struct pm_internal_struct *midi); -typedef unsigned int (*pm_check_host_error_fn)(struct pm_internal_struct *midi); - -typedef struct { - pm_write_short_fn write_short; /* output short MIDI msg */ - pm_begin_sysex_fn begin_sysex; /* prepare to send a sysex message */ - pm_end_sysex_fn end_sysex; /* marks end of sysex message */ - pm_write_byte_fn write_byte; /* accumulate one more sysex byte */ - pm_write_realtime_fn write_realtime; /* send real-time msg within sysex */ - pm_write_flush_fn write_flush; /* send any accumulated but unsent data */ - pm_synchronize_fn synchronize; /* synchronize PM time to stream time */ - pm_open_fn open; /* open MIDI device */ - pm_abort_fn abort; /* abort */ - pm_close_fn close; /* close device */ - pm_poll_fn poll; /* read pending midi events into portmidi buffer */ - pm_check_host_error_fn check_host_error; /* true when device has had host */ - /* error; sets pm_hosterror and writes message to pm_hosterror_text */ -} pm_fns_node, *pm_fns_type; - - -/* when open fails, the dictionary gets this set of functions: */ -extern pm_fns_node pm_none_dictionary; - -typedef struct { - PmDeviceInfo pub; /* some portmidi state also saved in here (for automatic - device closing -- see PmDeviceInfo struct) */ - int deleted; /* is this is a deleted virtual device? */ - void *descriptor; /* ID number passed to win32 multimedia API open, - * coreMIDI endpoint, etc., representing the device */ - struct pm_internal_struct *pm_internal; /* points to PmInternal device */ - /* when the device is open, allows automatic device closing */ - pm_fns_type dictionary; -} descriptor_node, *descriptor_type; - -extern int pm_descriptor_max; -extern descriptor_type pm_descriptors; -extern int pm_descriptor_len; - -typedef uint32_t (*time_get_proc_type)(void *time_info); - -typedef struct pm_internal_struct { - int device_id; /* which device is open (index to pm_descriptors) */ - short is_input; /* MIDI IN (true) or MIDI OUT (false) */ - short is_removed; /* MIDI device was removed */ - PmTimeProcPtr time_proc; /* where to get the time */ - void *time_info; /* pass this to get_time() */ - int32_t buffer_len; /* how big is the buffer or queue? */ - PmQueue *queue; - - int32_t latency; /* time delay in ms between timestamps and actual output */ - /* set to zero to get immediate, simple blocking output */ - /* if latency is zero, timestamps will be ignored; */ - /* if midi input device, this field ignored */ - - int sysex_in_progress; /* when sysex status is seen, this flag becomes - * true until EOX is seen. When true, new data is appended to the - * stream of outgoing bytes. When overflow occurs, sysex data is - * dropped (until an EOX or non-real-timei status byte is seen) so - * that, if the overflow condition is cleared, we don't start - * sending data from the middle of a sysex message. If a sysex - * message is filtered, sysex_in_progress is false, causing the - * message to be dropped. */ - PmMessage message; /* buffer for 4 bytes of sysex data */ - int message_count; /* how many bytes in sysex_message so far */ - int short_message_count; /* how many bytes are expected in short message */ - unsigned char running_status; /* running status byte or zero if none */ - int32_t filters; /* flags that filter incoming message classes */ - int32_t channel_mask; /* filter incoming messages based on channel */ - PmTimestamp last_msg_time; /* timestamp of last message */ - PmTimestamp sync_time; /* time of last synchronization */ - PmTimestamp now; /* set by PmWrite to current time */ - int first_message; /* initially true, used to run first synchronization */ - pm_fns_type dictionary; /* implementation functions */ - void *api_info; /* system-dependent state */ - /* the following are used to expedite sysex data */ - /* on windows, in debug mode, based on some profiling, these optimizations - * cut the time to process sysex bytes from about 7.5 to 0.26 usec/byte, - * but this does not count time in the driver, so I don't know if it is - * important - */ - unsigned char *fill_base; /* addr of ptr to sysex data */ - uint32_t *fill_offset_ptr; /* offset of next sysex byte */ - uint32_t fill_length; /* how many sysex bytes to write */ -} PmInternal; - -/* what is the length of this short message? */ -int pm_midi_length(PmMessage msg); - -/* defined by system specific implementation, e.g. pmwinmm, used by PortMidi */ -void pm_init(void); -void pm_term(void); - -/* defined by portMidi, used by pmwinmm */ -PmError none_write_short(PmInternal *midi, PmEvent *buffer); -PmError none_write_byte(PmInternal *midi, unsigned char byte, - PmTimestamp timestamp); -PmTimestamp none_synchronize(PmInternal *midi); - -PmError pm_fail_fn(PmInternal *midi); -PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp); -PmError pm_success_fn(PmInternal *midi); -PmError pm_add_interf(char *interf, pm_create_fn create_fn, - pm_delete_fn delete_fn); -PmError pm_add_device(char *interf, const char *name, int is_input, - int is_virtual, void *descriptor, pm_fns_type dictionary); -void pm_undo_add_device(int id); -uint32_t pm_read_bytes(PmInternal *midi, const unsigned char *data, int len, - PmTimestamp timestamp); -void pm_read_short(PmInternal *midi, PmEvent *event); - -#define none_write_flush pm_fail_timestamp_fn -#define none_sysex pm_fail_timestamp_fn -#define none_poll pm_fail_fn -#define success_poll pm_success_fn - -#define MIDI_REALTIME_MASK 0xf8 -#define is_real_time(msg) \ - ((Pm_MessageStatus(msg) & MIDI_REALTIME_MASK) == MIDI_REALTIME_MASK) - -#ifdef __cplusplus -} -#endif - -/** @endcond */ diff --git a/portmidi/pm_common/pmutil.c b/portmidi/pm_common/pmutil.c deleted file mode 100755 index a70fe2f..0000000 --- a/portmidi/pm_common/pmutil.c +++ /dev/null @@ -1,284 +0,0 @@ -/* pmutil.c -- some helpful utilities for building midi - applications that use PortMidi - */ -#include <stdlib.h> -#include <assert.h> -#include <string.h> -#include "portmidi.h" -#include "pmutil.h" -#include "pminternal.h" - -#ifdef WIN32 -#define bzero(addr, siz) memset(addr, 0, siz) -#endif - -// #define QUEUE_DEBUG 1 -#ifdef QUEUE_DEBUG -#include "stdio.h" -#endif - -typedef struct { - long head; - long tail; - long len; - long overflow; - int32_t msg_size; /* number of int32_t in a message including extra word */ - int32_t peek_overflow; - int32_t *buffer; - int32_t *peek; - int32_t peek_flag; -} PmQueueRep; - - -PMEXPORT PmQueue *Pm_QueueCreate(long num_msgs, int32_t bytes_per_msg) -{ - int32_t int32s_per_msg = - (int32_t) (((bytes_per_msg + sizeof(int32_t) - 1) & - ~(sizeof(int32_t) - 1)) / sizeof(int32_t)); - PmQueueRep *queue = (PmQueueRep *) pm_alloc(sizeof(PmQueueRep)); - if (!queue) /* memory allocation failed */ - return NULL; - - /* need extra word per message for non-zero encoding */ - queue->len = num_msgs * (int32s_per_msg + 1); - queue->buffer = (int32_t *) pm_alloc(queue->len * sizeof(int32_t)); - bzero(queue->buffer, queue->len * sizeof(int32_t)); - if (!queue->buffer) { - pm_free(queue); - return NULL; - } else { /* allocate the "peek" buffer */ - queue->peek = (int32_t *) pm_alloc(int32s_per_msg * sizeof(int32_t)); - if (!queue->peek) { - /* free everything allocated so far and return */ - pm_free(queue->buffer); - pm_free(queue); - return NULL; - } - } - bzero(queue->buffer, queue->len * sizeof(int32_t)); - queue->head = 0; - queue->tail = 0; - /* msg_size is in words */ - queue->msg_size = int32s_per_msg + 1; /* note extra word is counted */ - queue->overflow = FALSE; - queue->peek_overflow = FALSE; - queue->peek_flag = FALSE; - return queue; -} - - -PMEXPORT PmError Pm_QueueDestroy(PmQueue *q) -{ - PmQueueRep *queue = (PmQueueRep *) q; - - /* arg checking */ - if (!queue || !queue->buffer || !queue->peek) - return pmBadPtr; - - pm_free(queue->peek); - pm_free(queue->buffer); - pm_free(queue); - return pmNoError; -} - - -PMEXPORT PmError Pm_Dequeue(PmQueue *q, void *msg) -{ - long head; - PmQueueRep *queue = (PmQueueRep *) q; - int i; - int32_t *msg_as_int32 = (int32_t *) msg; - - /* arg checking */ - if (!queue) - return pmBadPtr; - /* a previous peek operation encountered an overflow, but the overflow - * has not yet been reported to client, so do it now. No message is - * returned, but on the next call, we will return the peek buffer. - */ - if (queue->peek_overflow) { - queue->peek_overflow = FALSE; - return pmBufferOverflow; - } - if (queue->peek_flag) { - memcpy(msg, queue->peek, (queue->msg_size - 1) * sizeof(int32_t)); - queue->peek_flag = FALSE; - return pmGotData; - } - - head = queue->head; - /* if writer overflows, it writes queue->overflow = tail+1 so that - * when the reader gets to that position in the buffer, it can - * return the overflow condition to the reader. The problem is that - * at overflow, things have wrapped around, so tail == head, and the - * reader will detect overflow immediately instead of waiting until - * it reads everything in the buffer, wrapping around again to the - * point where tail == head. So the condition also checks that - * queue->buffer[head] is zero -- if so, then the buffer is now - * empty, and we're at the point in the msg stream where overflow - * occurred. It's time to signal overflow to the reader. If - * queue->buffer[head] is non-zero, there's a message there and we - * should read all the way around the buffer before signalling overflow. - * There is a write-order dependency here, but to fail, the overflow - * field would have to be written while an entire buffer full of - * writes are still pending. I'm assuming out-of-order writes are - * possible, but not that many. - */ - if (queue->overflow == head + 1 && !queue->buffer[head]) { - queue->overflow = 0; /* non-overflow condition */ - return pmBufferOverflow; - } - - /* test to see if there is data in the queue -- test from back - * to front so if writer is simultaneously writing, we don't - * waste time discovering the write is not finished - */ - for (i = queue->msg_size - 1; i >= 0; i--) { - if (!queue->buffer[head + i]) { - return pmNoData; - } - } - memcpy(msg, (char *) &queue->buffer[head + 1], - sizeof(int32_t) * (queue->msg_size - 1)); - /* fix up zeros */ - i = queue->buffer[head]; - while (i < queue->msg_size) { - int32_t j; - i--; /* msg does not have extra word so shift down */ - j = msg_as_int32[i]; - msg_as_int32[i] = 0; - i = j; - } - /* signal that data has been removed by zeroing: */ - bzero((char *) &queue->buffer[head], sizeof(int32_t) * queue->msg_size); - - /* update head */ - head += queue->msg_size; - if (head == queue->len) head = 0; - queue->head = head; - return pmGotData; /* success */ -} - - - -PMEXPORT PmError Pm_SetOverflow(PmQueue *q) -{ - PmQueueRep *queue = (PmQueueRep *) q; - long tail; - /* arg checking */ - if (!queue) - return pmBadPtr; - /* no more enqueue until receiver acknowledges overflow */ - if (queue->overflow) return pmBufferOverflow; - tail = queue->tail; - queue->overflow = tail + 1; - return pmBufferOverflow; -} - - -PMEXPORT PmError Pm_Enqueue(PmQueue *q, void *msg) -{ - PmQueueRep *queue = (PmQueueRep *) q; - long tail; - int i; - int32_t *src = (int32_t *) msg; - int32_t *ptr; - int32_t *dest; - int rslt; - if (!queue) - return pmBadPtr; - /* no more enqueue until receiver acknowledges overflow */ - if (queue->overflow) return pmBufferOverflow; - rslt = Pm_QueueFull(q); - /* already checked above: if (rslt == pmBadPtr) return rslt; */ - tail = queue->tail; - if (rslt) { - queue->overflow = tail + 1; - return pmBufferOverflow; - } - - /* queue is has room for message, and overflow flag is cleared */ - ptr = &queue->buffer[tail]; - dest = ptr + 1; - for (i = 1; i < queue->msg_size; i++) { - int32_t j = src[i - 1]; - if (!j) { - *ptr = i; - ptr = dest; - } else { - *dest = j; - } - dest++; - } - *ptr = i; - tail += queue->msg_size; - if (tail == queue->len) tail = 0; - queue->tail = tail; - return pmNoError; -} - - -PMEXPORT int Pm_QueueEmpty(PmQueue *q) -{ - PmQueueRep *queue = (PmQueueRep *) q; - return (!queue) || /* null pointer -> return "empty" */ - (queue->buffer[queue->head] == 0 && !queue->peek_flag); -} - - -PMEXPORT int Pm_QueueFull(PmQueue *q) -{ - long tail; - int i; - PmQueueRep *queue = (PmQueueRep *) q; - /* arg checking */ - if (!queue) - return pmBadPtr; - tail = queue->tail; - /* test to see if there is space in the queue */ - for (i = 0; i < queue->msg_size; i++) { - if (queue->buffer[tail + i]) { - return TRUE; - } - } - return FALSE; -} - - -PMEXPORT void *Pm_QueuePeek(PmQueue *q) -{ - PmError rslt; - int32_t temp; - PmQueueRep *queue = (PmQueueRep *) q; - /* arg checking */ - if (!queue) - return NULL; - - if (queue->peek_flag) { - return queue->peek; - } - /* this is ugly: if peek_overflow is set, then Pm_Dequeue() - * returns immediately with pmBufferOverflow, but here, we - * want Pm_Dequeue() to really check for data. If data is - * there, we can return it - */ - temp = queue->peek_overflow; - queue->peek_overflow = FALSE; - rslt = Pm_Dequeue(q, queue->peek); - queue->peek_overflow = temp; - - if (rslt == 1) { - queue->peek_flag = TRUE; - return queue->peek; - } else if (rslt == pmBufferOverflow) { - /* when overflow is indicated, the queue is empty and the - * first message that was dropped by Enqueue (signalling - * pmBufferOverflow to its caller) would have been the next - * message in the queue. Pm_QueuePeek will return NULL, but - * remember that an overflow occurred. (see Pm_Dequeue) - */ - queue->peek_overflow = TRUE; - } - return NULL; -} - diff --git a/portmidi/pm_common/pmutil.h b/portmidi/pm_common/pmutil.h deleted file mode 100755 index 46c618e..0000000 --- a/portmidi/pm_common/pmutil.h +++ /dev/null @@ -1,184 +0,0 @@ -/** @file pmutil.h lock-free queue for building MIDI
- applications with PortMidi.
-
- PortMidi is not reentrant, and locks can suffer from priority
- inversion. To support coordination between system callbacks, a
- high-priority thread created with PortTime, and the main
- application thread, PortMidi uses a lock-free, non-blocking
- queue. The queue implementation is not particular to MIDI and is
- available for other uses.
- */
-
-#ifndef PORTMIDI_PMUTIL_H
-#define PORTMIDI_PMUTIL_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-/** @defgroup grp_pmutil Lock-free Queue
- @{
-*/
-
-/** The queue representation is opaque. Declare a queue as PmQueue * */
-typedef void PmQueue;
-
-/** create a single-reader, single-writer queue.
-
- @param num_msgs the number of messages the queue can hold
-
- @param the fixed message size
-
- @return the allocated and initialized queue, or NULL if memory
- cannot be allocated. Allocation uses #pm_malloc().
-
- The queue only accepts fixed sized messages.
-
- This queue implementation uses the "light pipe" algorithm which
- operates correctly even with multi-processors and out-of-order
- memory writes. (see Alexander Dokumentov, "Lock-free Interprocess
- Communication," Dr. Dobbs Portal, http://www.ddj.com/,
- articleID=189401457, June 15, 2006. This algorithm requires that
- messages be translated to a form where no words contain
- zeros. Each word becomes its own "data valid" tag. Because of this
- translation, we cannot return a pointer to data still in the queue
- when the "peek" method is called. Instead, a buffer is
- preallocated so that data can be copied there. Pm_QueuePeek()
- dequeues a message into this buffer and returns a pointer to it. A
- subsequent Pm_Dequeue() will copy from this buffer.
-
- This implementation does not try to keep reader/writer data in
- separate cache lines or prevent thrashing on cache lines.
- However, this algorithm differs by doing inserts/removals in
- units of messages rather than units of machine words. Some
- performance improvement might be obtained by not clearing data
- immediately after a read, but instead by waiting for the end
- of the cache line, especially if messages are smaller than
- cache lines. See the Dokumentov article for explanation.
-
- The algorithm is extended to handle "overflow" reporting. To
- report an overflow, the sender writes the current tail position to
- a field. The receiver must acknowlege receipt by zeroing the
- field. The sender will not send more until the field is zeroed.
- */
-PMEXPORT PmQueue *Pm_QueueCreate(long num_msgs, int32_t bytes_per_msg);
-
-/** destroy a queue and free its storage.
-
- @param queue a queue created by #Pm_QueueCreate().
-
- @return pmNoError or an error code.
-
- Uses #pm_free().
-
- */
-PMEXPORT PmError Pm_QueueDestroy(PmQueue *queue);
-
-/** remove one message from the queue, copying it into \p msg.
-
- @param queue a queue created by #Pm_QueueCreate().
-
- @param msg address to which the message, if any, is copied.
-
- @return 1 if successful, and 0 if the queue is empty. Returns
- #pmBufferOverflow if what would have been the next thing in the
- queue was dropped due to overflow. (So when overflow occurs, the
- receiver can receive a queue full of messages before getting the
- overflow report. This protocol ensures that the reader will be
- notified when data is lost due to overflow.
- */
-PMEXPORT PmError Pm_Dequeue(PmQueue *queue, void *msg);
-
-/** insert one message into the queue, copying it from \p msg.
-
- @param queue a queue created by #Pm_QueueCreate().
-
- @param msg address of the message to be enqueued.
-
- @return #pmNoError if successful and #pmBufferOverflow if the
- queue was already full. If #pmBufferOverflow is returned, the
- overflow flag is set.
- */
-PMEXPORT PmError Pm_Enqueue(PmQueue *queue, void *msg);
-
-/** test if the queue is full.
-
- @param queue a queue created by #Pm_QueueCreate().
-
- @return non-zero iff the queue is empty, and @pmBadPtr if \p queue
- is NULL.
-
- The full condition may change immediately because a parallel
- dequeue operation could be in progress. The result is
- pessimistic: if it returns false (zero) to the single writer, then
- #Pm_Enqueue() is guaranteed to succeed.
- */
-PMEXPORT int Pm_QueueFull(PmQueue *queue);
-
-/** test if the queue is empty.
-
- @param queue a queue created by #Pm_QueueCreate().
-
- @return zero iff the queue is either empty or NULL.
-
- The empty condition may change immediately because a parallel
- enqueue operation could be in progress. Furthermore, the
- result is optimistic: it may say false, when due to
- out-of-order writes, the full message has not arrived. Therefore,
- #Pm_Dequeue() could still return 0 after #Pm_QueueEmpty() returns
- false.
-*/
-PMEXPORT int Pm_QueueEmpty(PmQueue *queue);
-
-/** get a pointer to the item at the head of the queue.
-
- @param queue a queue created by #Pm_QueueCreate().
-
- @result a pointer to the head message or NULL if the queue is empty.
-
- The message is not removed from the queue. #Pm_QueuePeek() will
- not indicate when an overflow occurs. If you want to get and check
- #pmBufferOverflow messages, use the return value of
- #Pm_QueuePeek() *only* as an indication that you should call
- #Pm_Dequeue(). At the point where a direct call to #Pm_Dequeue()
- would return #pmBufferOverflow, #Pm_QueuePeek() will return NULL,
- but internally clear the #pmBufferOverflow flag, enabling
- #Pm_Enqueue() to resume enqueuing messages. A subsequent call to
- #Pm_QueuePeek() will return a pointer to the first message *after*
- the overflow. Using this as an indication to call #Pm_Dequeue(),
- the first call to #Pm_Dequeue() will return #pmBufferOverflow. The
- second call will return success, copying the same message pointed
- to by the previous #Pm_QueuePeek().
-
- When to use #Pm_QueuePeek(): (1) when you need to look at the message
- data to decide who should be called to receive it. (2) when you need
- to know a message is ready but cannot accept the message.
-
- Note that #Pm_QueuePeek() is not a fast check, so if possible, you
- might as well just call #Pm_Dequeue() and accept the data if it is there.
- */
-PMEXPORT void *Pm_QueuePeek(PmQueue *queue);
-
-/** allows the writer (enqueuer) to signal an overflow
- condition to the reader (dequeuer).
-
- @param queue a queue created by #Pm_QueueCreate().
-
- @return #pmNoError if overflow is set, or #pmBadPtr if queue is
- NULL, or #pmBufferOverflow if buffer is already in an overflow
- state.
-
- E.g., when transfering data from the OS to an application, if the
- OS indicates a buffer overrun, #Pm_SetOverflow() can be used to
- insure that the reader receives a #pmBufferOverflow result from
- #Pm_Dequeue().
- */
-PMEXPORT PmError Pm_SetOverflow(PmQueue *queue);
-
-/** @} */
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif // PORTMIDI_PMUTIL_H
diff --git a/portmidi/pm_common/portmidi.c b/portmidi/pm_common/portmidi.c deleted file mode 100755 index e78ee73..0000000 --- a/portmidi/pm_common/portmidi.c +++ /dev/null @@ -1,1472 +0,0 @@ -/* portmidi.c -- cross-platform MIDI I/O library */ -/* see license.txt for license */ - -#include "stdlib.h" -#include "string.h" -#include "portmidi.h" -#include "porttime.h" -#include "pmutil.h" -#include "pminternal.h" -#include <assert.h> - -#define MIDI_CLOCK 0xf8 -#define MIDI_ACTIVE 0xfe -#define MIDI_STATUS_MASK 0x80 -#define MIDI_SYSEX 0xf0 -#define MIDI_EOX 0xf7 -#define MIDI_START 0xFA -#define MIDI_STOP 0xFC -#define MIDI_CONTINUE 0xFB -#define MIDI_F9 0xF9 -#define MIDI_FD 0xFD -#define MIDI_RESET 0xFF -#define MIDI_NOTE_ON 0x90 -#define MIDI_NOTE_OFF 0x80 -#define MIDI_CHANNEL_AT 0xD0 -#define MIDI_POLY_AT 0xA0 -#define MIDI_PROGRAM 0xC0 -#define MIDI_CONTROL 0xB0 -#define MIDI_PITCHBEND 0xE0 -#define MIDI_MTC 0xF1 -#define MIDI_SONGPOS 0xF2 -#define MIDI_SONGSEL 0xF3 -#define MIDI_TUNE 0xF6 - -#define is_empty(midi) ((midi)->tail == (midi)->head) - -/* these are not static so that (possibly) some system-dependent code - * could override the portmidi.c default which is to use the first - * device added using pm_add_device() - */ -PmDeviceID pm_default_input_device_id = -1; -PmDeviceID pm_default_output_device_id = -1; - -/* this is not static so that pm_init can set it directly - * (see pmmac.c:pm_init()) - */ -int pm_initialized = FALSE; - -int pm_hosterror; /* boolean */ - -/* if PM_CHECK_ERRORS is enabled, but the caller wants to - * handle an error condition, declare this as extern and - * set to FALSE (this override is provided specifically - * for the test program virttest.c, where pmNameConflict - * is expected in a call to Pm_CreateVirtualInput()): - */ -int pm_check_errors = TRUE; - -char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN]; - -#ifdef PM_CHECK_ERRORS - -#include <stdio.h> - -#define STRING_MAX 80 - -static void prompt_and_exit(void) -{ - char line[STRING_MAX]; - printf("type ENTER..."); - char *rslt = fgets(line, STRING_MAX, stdin); - /* this will clean up open ports: */ - exit(-1); -} - -static PmError pm_errmsg(PmError err) -{ - if (!pm_check_errors) { /* see pm_check_errors declaration above */ - ; - } else if (err == pmHostError) { - /* it seems pointless to allocate memory and copy the string, - * so I will do the work of Pm_GetHostErrorText directly - */ - printf("PortMidi found host error...\n %s\n", pm_hosterror_text); - pm_hosterror = FALSE; - pm_hosterror_text[0] = 0; /* clear the message */ - prompt_and_exit(); - } else if (err < 0) { - printf("PortMidi call failed...\n %s\n", Pm_GetErrorText(err)); - prompt_and_exit(); - } - return err; -} -#else -#define pm_errmsg(err) err -#endif - - -int pm_midi_length(PmMessage msg) -{ - int status, high, low; - static int high_lengths[] = { - 1, 1, 1, 1, 1, 1, 1, 1, /* 0x00 through 0x70 */ - 3, 3, 3, 3, 2, 2, 3, 1 /* 0x80 through 0xf0 */ - }; - static int low_lengths[] = { - 1, 2, 3, 2, 1, 1, 1, 1, /* 0xf0 through 0xf8 */ - 1, 1, 1, 1, 1, 1, 1, 1 /* 0xf9 through 0xff */ - }; - - status = msg & 0xFF; - high = status >> 4; - low = status & 15; - - return (high != 0xF) ? high_lengths[high] : low_lengths[low]; -} - - -/* -==================================================================== -system implementation of portmidi interface -==================================================================== -*/ - -int pm_descriptor_max = 0; -int pm_descriptor_len = 0; -descriptor_type pm_descriptors = NULL; - -/* interface pm_descriptors are simple: an array of string/fnptr pairs: */ -#define MAX_INTERF 4 -static struct { - const char *interf; - pm_create_fn create_fn; - pm_delete_fn delete_fn; -} pm_interf_list[MAX_INTERF]; - -static int pm_interf_list_len = 0; - - -/* pm_add_interf -- describe an interface to library - * - * This is called at initialization time, once for each - * supported interface (e.g., CoreMIDI). The strings - * are retained but NOT COPIED, so do not destroy them! - * - * The purpose is to register functions that create/delete - * a virtual input or output device. - * - * returns pmInsufficientMemor if interface memory is - * exceeded, otherwise returns pmNoError. - */ -PmError pm_add_interf(char *interf, pm_create_fn create_fn, - pm_delete_fn delete_fn) -{ - if (pm_interf_list_len >= MAX_INTERF) { - return pmInsufficientMemory; - } - pm_interf_list[pm_interf_list_len].interf = interf; - pm_interf_list[pm_interf_list_len].create_fn = create_fn; - pm_interf_list[pm_interf_list_len].delete_fn = delete_fn; - pm_interf_list_len++; - return pmNoError; -} - - -PmError pm_create_virtual(PmInternal *midi, int is_input, const char *interf, - const char *name, void *device_info) -{ - int i; - if (pm_interf_list_len == 0) { - return pmNotImplemented; - } - if (!interf) { - /* default interface is the first one */ - interf = pm_interf_list[0].interf; - } - for (i = 0; i < pm_interf_list_len; i++) { - if (strcmp(pm_interf_list[i].interf, - interf) == 0) { - int id = (*pm_interf_list[i].create_fn)(is_input, name, - device_info); - pm_descriptors[id].pub.is_virtual = TRUE; - return id; - } - } - return pmInterfaceNotSupported; -} - - -/* pm_add_device -- describe interface/device pair to library - * - * This is called at intialization time, once for each - * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1). - * This is also called when user creates a virtual device. - * - * Normally, increasing integer indices are returned. If the device - * is virtual, a linear search is performed to ensure that the name - * is unique. If the name is already taken, the call will fail and - * no device is added. - * - * interf is assumed to be static memory, so it is NOT COPIED and - * NOT FREED. - * name is owned by caller, COPIED if needed, and FREED by PortMidi. - * Caller is resposible for freeing name when pm_add_device returns. - * - * returns pmInvalidDeviceId if device memory is exceeded or a virtual - * device would take the name of an existing device. - * otherwise returns index (portmidi device_id) of the added device - */ -PmError pm_add_device(char *interf, const char *name, int is_input, - int is_virtual, void *descriptor, pm_fns_type dictionary) { - /* printf("pm_add_device: %s %s %d %p %p\n", - interf, name, is_input, descriptor, dictionary); */ - int device_id; - PmDeviceInfo *d; - /* if virtual, search for duplicate name or unused ID; otherwise, - * just add a new device at the next integer available: - */ - for (device_id = (is_virtual ? 0 : pm_descriptor_len); - device_id < pm_descriptor_len; device_id++) { - d = &pm_descriptors[device_id].pub; - d->structVersion = PM_DEVICEINFO_VERS; - if (strcmp(d->interf, interf) == 0 && strcmp(d->name, name) == 0) { - /* only reuse a name if it is a deleted virtual device with - * a matching direction (input or output) */ - if (pm_descriptors[device_id].deleted && is_input == d->input) { - /* here, we know d->is_virtual because only virtual devices - * can be deleted, and we know is_virtual because we are - * in this loop. - */ - pm_free((void *) d->name); /* reuse this device entry */ - d->name = NULL; - break; - /* name conflict exists if the new device appears to others as - * the same direction (input or output) as the existing device. - * Note that virtual inputs appear to others as outputs and - * vice versa. - * The direction of the new virtual device to others is "output" - * if is_input, i.e., virtual inputs appear to others as outputs. - * The existing device appears to others as "output" if - * (d->is_virtual == d->input) by the same logic. - * The compare will detect if device directions are the same: - */ - } else if (is_input == (d->is_virtual == d->input)) { - return pmNameConflict; - } - } - } - if (device_id >= pm_descriptor_max) { - // expand pm_descriptors - descriptor_type new_descriptors = (descriptor_type) - pm_alloc(sizeof(descriptor_node) * (pm_descriptor_max + 32)); - if (!new_descriptors) return pmInsufficientMemory; - if (pm_descriptors) { - memcpy(new_descriptors, pm_descriptors, - sizeof(descriptor_node) * pm_descriptor_max); - pm_free(pm_descriptors); - } - pm_descriptor_max += 32; - pm_descriptors = new_descriptors; - } - if (device_id == pm_descriptor_len) { - pm_descriptor_len++; /* extending array of pm_descriptors */ - } - d = &pm_descriptors[device_id].pub; - d->interf = interf; - d->name = pm_alloc(strlen(name) + 1); - if (!d->name) { - return pmInsufficientMemory; - } -#if defined(WIN32) && !defined(_WIN32) -#pragma warning(suppress: 4996) // don't use suggested strncpy_s -#endif - strcpy(d->name, name); - d->input = is_input; - d->output = !is_input; - d->is_virtual = FALSE; /* caller should set to TRUE if this is virtual */ - - /* default state: nothing to close (for automatic device closing) */ - d->opened = FALSE; - - pm_descriptors[device_id].deleted = FALSE; - - /* ID number passed to win32 multimedia API open */ - pm_descriptors[device_id].descriptor = descriptor; - - /* points to PmInternal, allows automatic device closing */ - pm_descriptors[device_id].pm_internal = NULL; - - pm_descriptors[device_id].dictionary = dictionary; - - /* set the defaults to the first input and output we see */ - if (is_input && pm_default_input_device_id == -1) { - pm_default_input_device_id = device_id; - } else if (!is_input && pm_default_output_device_id == -1) { - pm_default_output_device_id = device_id; - } - - return device_id; -} - - -/* Undo a successful call to pm_add_device(). If a new device was - * allocated, it must be the last device in pm_descriptors, so it is - * easy to delete by decrementing the length of pm_descriptors, but - * first free the name (which was copied to the heap). Otherwise, - * the device must be a virtual device that was created previously - * and is in the interior of the array of pm_descriptors. Leave it, - * but mark it as deleted. - */ -void pm_undo_add_device(int id) -{ - /* Clear some fields (not all are strictly necessary) */ - pm_descriptors[id].deleted = TRUE; - pm_descriptors[id].descriptor = NULL; - pm_descriptors[id].pm_internal = NULL; - - if (id == pm_descriptor_len - 1) { - pm_free(pm_descriptors[id].pub.name); - pm_descriptor_len--; - } -} - - -/* utility to look up device, given a pattern, - note: pattern is modified - */ -int Pm_FindDevice(char *pattern, int is_input) -{ - int id = pmNoDevice; - int i; - /* first parse pattern into name, interf parts */ - char *interf_pref = ""; /* initially assume it is not there */ - char *name_pref = strstr(pattern, ", "); - - if (name_pref) { /* found separator, adjust the pointer */ - interf_pref = pattern; - name_pref[0] = 0; - name_pref += 2; - } else { - name_pref = pattern; /* whole string is the name pattern */ - } - for (i = 0; i < pm_descriptor_len; i++) { - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - if (info->input == is_input && - strstr(info->name, name_pref) && - strstr(info->interf, interf_pref)) { - id = i; - break; - } - } - return id; -} - - -/* -==================================================================== -portmidi implementation -==================================================================== -*/ - -PMEXPORT int Pm_CountDevices(void) -{ - Pm_Initialize(); - /* no error checking -- Pm_Initialize() does not fail */ - return pm_descriptor_len; -} - - -PMEXPORT const PmDeviceInfo* Pm_GetDeviceInfo(PmDeviceID id) -{ - Pm_Initialize(); /* no error check needed */ - if (id >= 0 && id < pm_descriptor_len && !pm_descriptors[id].deleted) { - return &pm_descriptors[id].pub; - } - return NULL; -} - -/* pm_success_fn -- "noop" function pointer */ -PmError pm_success_fn(PmInternal *midi) -{ - return pmNoError; -} - -/* none_write -- returns an error if called */ -PmError none_write_short(PmInternal *midi, PmEvent *buffer) -{ - return pmBadPtr; -} - -/* pm_fail_timestamp_fn -- placeholder for begin_sysex and flush */ -PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp) -{ - return pmBadPtr; -} - -PmError none_write_byte(PmInternal *midi, unsigned char byte, - PmTimestamp timestamp) -{ - return pmBadPtr; -} - -/* pm_fail_fn -- generic function, returns error if called */ -PmError pm_fail_fn(PmInternal *midi) -{ - return pmBadPtr; -} - -static PmError none_open(PmInternal *midi, void *driverInfo) -{ - return pmBadPtr; -} - -static unsigned int none_check_host_error(PmInternal * midi) -{ - return FALSE; -} - -PmTimestamp none_synchronize(PmInternal *midi) -{ - return 0; -} - -#define none_abort pm_fail_fn -#define none_close pm_fail_fn - -pm_fns_node pm_none_dictionary = { - none_write_short, - none_sysex, - none_sysex, - none_write_byte, - none_write_short, - none_write_flush, - none_synchronize, - none_open, - none_abort, - none_close, - none_poll, - none_check_host_error, -}; - - -PMEXPORT const char *Pm_GetErrorText(PmError errnum) -{ - const char *msg; - - switch(errnum) - { - case pmNoError: - msg = ""; - break; - case pmHostError: - msg = "PortMidi: Host error"; - break; - case pmInvalidDeviceId: - msg = "PortMidi: Invalid device ID"; - break; - case pmInsufficientMemory: - msg = "PortMidi: Insufficient memory"; - break; - case pmBufferTooSmall: - msg = "PortMidi: Buffer too small"; - break; - case pmBadPtr: - msg = "PortMidi: Bad pointer"; - break; - case pmInternalError: - msg = "PortMidi: Internal PortMidi Error"; - break; - case pmBufferOverflow: - msg = "PortMidi: Buffer overflow"; - break; - case pmBadData: - msg = "PortMidi: Invalid MIDI message Data"; - break; - case pmBufferMaxSize: - msg = "PortMidi: Buffer cannot be made larger"; - break; - case pmNotImplemented: - msg = "PortMidi: Function is not implemented"; - break; - case pmInterfaceNotSupported: - msg = "PortMidi: Interface not supported"; - break; - case pmNameConflict: - msg = "PortMidi: Cannot create virtual device: name is taken"; - break; - case pmDeviceRemoved: - msg = "PortMidi: Output attempted after (USB) device removed"; - break; - default: - msg = "PortMidi: Illegal error number"; - break; - } - return msg; -} - - -/* This can be called whenever you get a pmHostError return value - * or TRUE from Pm_HasHostError(). - * The error will always be in the global pm_hosterror_text. - */ -PMEXPORT void Pm_GetHostErrorText(char * msg, unsigned int len) -{ - assert(msg); - assert(len > 0); - if (pm_hosterror) { -#if defined(WIN32) && !defined(_WIN32) -#pragma warning(suppress: 4996) // don't use suggested strncpy_s -#endif - strncpy(msg, (char *) pm_hosterror_text, len); - pm_hosterror = FALSE; - pm_hosterror_text[0] = 0; /* clear the message; not necessary, but it - might help with debugging */ - msg[len - 1] = 0; /* make sure string is terminated */ - } else { - msg[0] = 0; /* no string to return */ - } -} - - -PMEXPORT int Pm_HasHostError(PortMidiStream * stream) -{ - if (pm_hosterror) return TRUE; - if (stream) { - PmInternal * midi = (PmInternal *) stream; - return (*midi->dictionary->check_host_error)(midi); - } - return FALSE; -} - - -PMEXPORT PmError Pm_Initialize(void) -{ - if (!pm_initialized) { - pm_descriptor_len = 0; - pm_interf_list_len = 0; - pm_hosterror = FALSE; - pm_hosterror_text[0] = 0; /* the null string */ - pm_init(); - pm_initialized = TRUE; - } - return pmNoError; -} - - -PMEXPORT PmError Pm_Terminate(void) -{ - if (pm_initialized) { - pm_term(); - /* if there are no devices, pm_descriptors might still be NULL */ - if (pm_descriptors != NULL) { - int i; /* free names copied into pm_descriptors */ - for (i = 0; i < pm_descriptor_len; i++) { - if (pm_descriptors[i].pub.name) { - pm_free(pm_descriptors[i].pub.name); - } - } - pm_free(pm_descriptors); - pm_descriptors = NULL; - } - pm_descriptor_len = 0; - pm_descriptor_max = 0; - pm_interf_list_len = 0; - pm_initialized = FALSE; - } - return pmNoError; -} - - -/* Pm_Read -- read up to length messages from source into buffer */ -/* - * returns number of messages actually read, or error code - */ -PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length) -{ - PmInternal *midi = (PmInternal *) stream; - int n = 0; - PmError err = pmNoError; - pm_hosterror = FALSE; - /* arg checking */ - if(midi == NULL) - err = pmBadPtr; - else if(!pm_descriptors[midi->device_id].pub.opened) - err = pmBadPtr; - else if(!pm_descriptors[midi->device_id].pub.input) - err = pmBadPtr; - /* First poll for data in the buffer... - * This either simply checks for data, or attempts first to fill the buffer - * with data from the MIDI hardware; this depends on the implementation. - * We could call Pm_Poll here, but that would redo a lot of redundant - * parameter checking, so I copied some code from Pm_Poll to here: */ - else err = (*(midi->dictionary->poll))(midi); - - if (err != pmNoError) { - if (err == pmHostError) { - midi->dictionary->check_host_error(midi); - } - return pm_errmsg(err); - } - - while (n < length) { - err = Pm_Dequeue(midi->queue, buffer++); - if (err == pmBufferOverflow) { - /* ignore the data we have retreived so far */ - return pm_errmsg(pmBufferOverflow); - } else if (err == 0) { /* empty queue */ - break; - } - n++; - } - return n; -} - -PMEXPORT PmError Pm_Poll(PortMidiStream *stream) -{ - PmInternal *midi = (PmInternal *) stream; - PmError err; - - pm_hosterror = FALSE; - /* arg checking */ - if(midi == NULL) - err = pmBadPtr; - else if (!pm_descriptors[midi->device_id].pub.opened) - err = pmBadPtr; - else if (!pm_descriptors[midi->device_id].pub.input) - err = pmBadPtr; - else - err = (*(midi->dictionary->poll))(midi); - - if (err != pmNoError) { - return pm_errmsg(err); - } - - return (PmError) !Pm_QueueEmpty(midi->queue); -} - - -/* this is called from Pm_Write and Pm_WriteSysEx to issue a - * call to the system-dependent end_sysex function and handle - * the error return - */ -static PmError pm_end_sysex(PmInternal *midi) -{ - PmError err = (*midi->dictionary->end_sysex)(midi, 0); - midi->sysex_in_progress = FALSE; - return err; -} - - -/* to facilitate correct error-handling, Pm_Write, Pm_WriteShort, and - Pm_WriteSysEx all operate a state machine that "outputs" calls to - write_short, begin_sysex, write_byte, end_sysex, and write_realtime */ - -PMEXPORT PmError Pm_Write(PortMidiStream *stream, PmEvent *buffer, - int32_t length) -{ - PmInternal *midi = (PmInternal *) stream; - PmError err = pmNoError; - int i; - int bits; - - pm_hosterror = FALSE; - /* arg checking */ - if (midi == NULL) { - err = pmBadPtr; - } else { - descriptor_type desc = &pm_descriptors[midi->device_id]; - if (!desc || !desc->pub.opened || - !desc->pub.output || !desc->pm_internal) { - err = pmBadPtr; - } else if (desc->pm_internal->is_removed) { - err = pmDeviceRemoved; - } - } - if (err != pmNoError) goto pm_write_error; - - if (midi->latency == 0) { - midi->now = 0; - } else { - midi->now = (*(midi->time_proc))(midi->time_info); - if (midi->first_message || midi->sync_time + 100 /*ms*/ < midi->now) { - /* time to resync */ - midi->now = (*midi->dictionary->synchronize)(midi); - midi->first_message = FALSE; - } - } - /* error recovery: when a sysex is detected, we call - * dictionary->begin_sysex() followed by calls to - * dictionary->write_byte() and dictionary->write_realtime() - * until an end-of-sysex is detected, when we call - * dictionary->end_sysex(). After an error occurs, - * Pm_Write() continues to call functions. For example, - * it will continue to call write_byte() even after - * an error sending a sysex message, and end_sysex() will be - * called when an EOX or non-real-time status is found. - * When errors are detected, Pm_Write() returns immediately, - * so it is possible that this will drop data and leave - * sysex messages in a partially transmitted state. - */ - for (i = 0; i < length; i++) { - uint32_t msg = buffer[i].message; - bits = 0; - /* is this a sysex message? */ - if (Pm_MessageStatus(msg) == MIDI_SYSEX) { - if (midi->sysex_in_progress) { - /* error: previous sysex was not terminated by EOX */ - midi->sysex_in_progress = FALSE; - err = pmBadData; - goto pm_write_error; - } - midi->sysex_in_progress = TRUE; - if ((err = (*midi->dictionary->begin_sysex)(midi, - buffer[i].timestamp)) != pmNoError) - goto pm_write_error; - if ((err = (*midi->dictionary->write_byte)(midi, MIDI_SYSEX, - buffer[i].timestamp)) != pmNoError) - goto pm_write_error; - bits = 8; - /* fall through to continue sysex processing */ - } else if ((msg & MIDI_STATUS_MASK) && - (Pm_MessageStatus(msg) != MIDI_EOX)) { - /* a non-sysex message */ - if (midi->sysex_in_progress) { - /* this should be a realtime message */ - if (is_real_time(msg)) { - if ((err = (*midi->dictionary->write_realtime)(midi, - &(buffer[i]))) != pmNoError) - goto pm_write_error; - } else { - midi->sysex_in_progress = FALSE; - err = pmBadData; - /* ignore any error from this, because we already have one */ - /* pass 0 as timestamp -- it's ignored */ - (*midi->dictionary->end_sysex)(midi, 0); - goto pm_write_error; - } - } else { /* regular short midi message */ - if ((err = (*midi->dictionary->write_short)(midi, - &(buffer[i]))) != pmNoError) - goto pm_write_error; - continue; - } - } - if (midi->sysex_in_progress) { /* send sysex bytes until EOX */ - /* see if we can accelerate data transfer */ - if (bits == 0 && midi->fill_base && /* 4 bytes to copy */ - (*midi->fill_offset_ptr) + 4 <= midi->fill_length && - (msg & 0x80808080) == 0) { /* all data */ - /* copy 4 bytes from msg to fill_base + fill_offset */ - unsigned char *ptr = midi->fill_base + - *(midi->fill_offset_ptr); - ptr[0] = msg; ptr[1] = msg >> 8; - ptr[2] = msg >> 16; ptr[3] = msg >> 24; - (*midi->fill_offset_ptr) += 4; - continue; - } - /* no acceleration, so do byte-by-byte copying */ - while (bits < 32) { - unsigned char midi_byte = (unsigned char) (msg >> bits); - if ((err = (*midi->dictionary->write_byte)(midi, midi_byte, - buffer[i].timestamp)) != pmNoError) - goto pm_write_error; - if (midi_byte == MIDI_EOX) { - err = pm_end_sysex(midi); - if (err != pmNoError) goto error_exit; - break; /* from while loop */ - } - bits += 8; - } - } else { - /* not in sysex mode, but message did not start with status */ - err = pmBadData; - goto pm_write_error; - } - } - /* after all messages are processed, send the data */ - if (!midi->sysex_in_progress) - err = (*midi->dictionary->write_flush)(midi, 0); -pm_write_error: - if (err == pmHostError) { - midi->dictionary->check_host_error(midi); - } -error_exit: - return pm_errmsg(err); -} - - -PMEXPORT PmError Pm_WriteShort(PortMidiStream *stream, PmTimestamp when, - PmMessage msg) -{ - PmEvent event; - - event.timestamp = when; - event.message = msg; - return Pm_Write(stream, &event, 1); -} - - -PMEXPORT PmError Pm_WriteSysEx(PortMidiStream *stream, PmTimestamp when, - unsigned char *msg) -{ - /* allocate buffer space for PM_DEFAULT_SYSEX_BUFFER_SIZE bytes */ - /* each PmEvent holds sizeof(PmMessage) bytes of sysex data */ - #define BUFLEN ((int) (PM_DEFAULT_SYSEX_BUFFER_SIZE / sizeof(PmMessage))) - PmEvent buffer[BUFLEN]; - int buffer_size = 1; /* first time, send 1. After that, it's BUFLEN */ - PmInternal *midi = (PmInternal *) stream; - PmError err = pmNoError; - /* the next byte in the buffer is represented by an index, bufx, and - a shift in bits */ - int shift = 0; - int bufx = 0; - buffer[0].message = 0; - buffer[0].timestamp = when; - - while (1) { - /* insert next byte into buffer */ - buffer[bufx].message |= ((*msg) << shift); - shift += 8; - if (*msg++ == MIDI_EOX) break; - if (shift == 32) { - shift = 0; - bufx++; - if (bufx == buffer_size) { - err = Pm_Write(stream, buffer, buffer_size); - /* note: Pm_Write has already called errmsg() */ - if (err) return err; - /* prepare to fill another buffer */ - bufx = 0; - buffer_size = BUFLEN; - /* optimization: maybe we can just copy bytes */ - if (midi->fill_base) { - while (*(midi->fill_offset_ptr) < midi->fill_length) { - midi->fill_base[(*midi->fill_offset_ptr)++] = *msg; - if (*msg++ == MIDI_EOX) { - err = pm_end_sysex(midi); - if (err != pmNoError) return pm_errmsg(err); - goto end_of_sysex; - } - } - /* I thought that I could do a pm_Write here and - * change this if to a loop, avoiding calls in Pm_Write - * to the slower write_byte, but since - * sysex_in_progress is true, this will not flush - * the buffer, and we'll infinite loop: */ - /* err = Pm_Write(stream, buffer, 0); - if (err) return err; */ - /* instead, the way this works is that Pm_Write calls - * write_byte on 4 bytes. The first, since the buffer - * is full, will flush the buffer and allocate a new - * one. This primes the buffer so - * that we can return to the loop above and fill it - * efficiently without a lot of function calls. - */ - buffer_size = 1; /* get another message started */ - } - } - buffer[bufx].message = 0; - buffer[bufx].timestamp = when; - } - /* keep inserting bytes until you find MIDI_EOX */ - } -end_of_sysex: - /* we're finished sending full buffers, but there may - * be a partial one left. - */ - if (shift != 0) bufx++; /* add partial message to buffer len */ - if (bufx) { /* bufx is number of PmEvents to send from buffer */ - err = Pm_Write(stream, buffer, bufx); - if (err) return err; - } - return pmNoError; -} - - - -PmError pm_create_internal(PmInternal **stream, PmDeviceID device_id, - int is_input, int latency, PmTimeProcPtr time_proc, - void *time_info, int buffer_size) -{ - PmInternal *midi; - if (device_id < 0 || device_id >= pm_descriptor_len) { - return pmInvalidDeviceId; - } - if (latency < 0) { /* force a legal value */ - latency = 0; - } - /* create portMidi internal data */ - midi = (PmInternal *) pm_alloc(sizeof(PmInternal)); - *stream = midi; - if (!midi) { - return pmInsufficientMemory; - } - midi->device_id = device_id; - midi->is_input = is_input; - midi->is_removed = FALSE; - midi->time_proc = time_proc; - /* if latency != 0, we need a time reference for output. - we always need a time reference for input. - If none is provided, use PortTime library */ - if (time_proc == NULL && (latency != 0 || is_input)) { - if (!Pt_Started()) - Pt_Start(1, 0, 0); - /* time_get does not take a parameter, so coerce */ - midi->time_proc = (PmTimeProcPtr) Pt_Time; - } - midi->time_info = time_info; - if (is_input) { - midi->latency = 0; /* unused by input */ - if (buffer_size <= 0) buffer_size = 256; /* default buffer size */ - midi->queue = Pm_QueueCreate(buffer_size, (int32_t) sizeof(PmEvent)); - if (!midi->queue) { - /* free portMidi data */ - *stream = NULL; - pm_free(midi); - return pmInsufficientMemory; - } - } else { - /* if latency zero, output immediate (timestamps ignored) */ - /* if latency < 0, use 0 but don't return an error */ - if (latency < 0) latency = 0; - midi->latency = latency; - midi->queue = NULL; /* unused by output; input needs to allocate: */ - } - midi->buffer_len = buffer_size; /* portMidi input storage */ - midi->sysex_in_progress = FALSE; - midi->message = 0; - midi->message_count = 0; - midi->filters = (is_input ? PM_FILT_ACTIVE : 0); - midi->channel_mask = 0xFFFF; - midi->sync_time = 0; - midi->first_message = TRUE; - midi->api_info = NULL; - midi->fill_base = NULL; - midi->fill_offset_ptr = NULL; - midi->fill_length = 0; - midi->dictionary = pm_descriptors[device_id].dictionary; - pm_descriptors[device_id].pm_internal = midi; - return pmNoError; -} - - -PMEXPORT PmError Pm_OpenInput(PortMidiStream** stream, - PmDeviceID inputDevice, - void *inputDriverInfo, - int32_t bufferSize, - PmTimeProcPtr time_proc, - void *time_info) -{ - PmInternal *midi; - PmError err = pmNoError; - pm_hosterror = FALSE; - *stream = NULL; /* invariant: *stream == midi */ - - /* arg checking */ - if (!pm_descriptors[inputDevice].pub.input) - err = pmInvalidDeviceId; - else if (pm_descriptors[inputDevice].pub.opened) - err = pmInvalidDeviceId; - if (err != pmNoError) - goto error_return; - - /* common initialization of PmInternal structure (midi): */ - err = pm_create_internal(&midi, inputDevice, TRUE, 0, time_proc, - time_info, bufferSize); - *stream = midi; - if (err) { - goto error_return; - } - - /* open system dependent input device */ - err = (*midi->dictionary->open)(midi, inputDriverInfo); - if (err) { - *stream = NULL; - pm_descriptors[inputDevice].pm_internal = NULL; - /* free portMidi data */ - Pm_QueueDestroy(midi->queue); - pm_free(midi); - } else { - /* portMidi input open successful */ - pm_descriptors[inputDevice].pub.opened = TRUE; - } -error_return: - /* note: if there is a pmHostError, it is the responsibility - * of the system-dependent code (*midi->dictionary->open)() - * to set pm_hosterror and pm_hosterror_text - */ - return pm_errmsg(err); -} - - -PMEXPORT PmError Pm_OpenOutput(PortMidiStream** stream, - PmDeviceID outputDevice, - void *outputDriverInfo, - int32_t bufferSize, - PmTimeProcPtr time_proc, - void *time_info, - int32_t latency) -{ - PmInternal *midi; - PmError err = pmNoError; - pm_hosterror = FALSE; - *stream = NULL; - - /* arg checking */ - if (outputDevice < 0 || outputDevice >= pm_descriptor_len) - err = pmInvalidDeviceId; - else if (!pm_descriptors[outputDevice].pub.output) - err = pmInvalidDeviceId; - else if (pm_descriptors[outputDevice].pub.opened) - err = pmInvalidDeviceId; - if (err != pmNoError) - goto error_return; - - /* common initialization of PmInternal structure (midi): */ - err = pm_create_internal(&midi, outputDevice, FALSE, latency, time_proc, - time_info, bufferSize); - *stream = midi; - if (err) { - goto error_return; - } - - /* open system dependent output device */ - err = (*midi->dictionary->open)(midi, outputDriverInfo); - if (err) { - *stream = NULL; - pm_descriptors[outputDevice].pm_internal = NULL; - /* free portMidi data */ - pm_free(midi); - } else { - /* portMidi input open successful */ - pm_descriptors[outputDevice].pub.opened = TRUE; - } -error_return: - /* note: system-dependent code must set pm_hosterror and - * pm_hosterror_text if a pmHostError occurs - */ - return pm_errmsg(err); -} - - -static PmError create_virtual_device(const char *name, const char *interf, - void *device_info, int is_input) -{ - PmError err = pmNoError; - int i; - pm_hosterror = FALSE; - - /* arg checking */ - if (!name) { - err = pmInvalidDeviceId; - goto error_return; - } - - Pm_Initialize(); /* just in case */ - - /* create the virtual device */ - if (pm_interf_list_len == 0) { - return pmNotImplemented; - } - if (!interf) { - /* default interface is the first one */ - interf = pm_interf_list[0].interf; - } - /* look up and call the create_fn for interf(ace), e.g. "CoreMIDI" */ - for (i = 0; i < pm_interf_list_len; i++) { - if (strcmp(pm_interf_list[i].interf, interf) == 0) { - int id = (*pm_interf_list[i].create_fn)(is_input, - name, device_info); - /* id could be pmNameConflict or an actual descriptor index */ - if (id >= 0) { - pm_descriptors[id].pub.is_virtual = TRUE; - } - err = id; - goto error_return; - } - } - err = pmInterfaceNotSupported; - -error_return: - /* note: if there is a pmHostError, it is the responsibility - * of the system-dependent code (*midi->dictionary->open)() - * to set pm_hosterror and pm_hosterror_text - */ - return pm_errmsg(err); -} - - -PMEXPORT PmError Pm_CreateVirtualInput(const char *name, - const char *interf, - void *deviceInfo) -{ - return create_virtual_device(name, interf, deviceInfo, TRUE); -} - -PMEXPORT PmError Pm_CreateVirtualOutput(const char *name, const char *interf, - void *deviceInfo) -{ - return create_virtual_device(name, interf, deviceInfo, FALSE); -} - -PmError Pm_DeleteVirtualDevice(PmDeviceID id) -{ - int i; - const char *interf = pm_descriptors[id].pub.interf; - PmError err = pmBadData; /* returned if we cannot find the interface- - * specific delete function */ - /* arg checking */ - if (id < 0 || id >= pm_descriptor_len || - pm_descriptors[id].pub.opened || pm_descriptors[id].deleted) { - return pmInvalidDeviceId; - } - /* delete function pointer is in interfaces list */ - for (i = 0; i < pm_interf_list_len; i++) { - if (strcmp(pm_interf_list[i].interf, interf) == 0) { - err = (*pm_interf_list[i].delete_fn)(id); - break; - } - } - pm_descriptors[id].deleted = TRUE; - /* (pm_internal should already be NULL because !pub.opened) */ - pm_descriptors[id].pm_internal = NULL; - pm_descriptors[id].descriptor = NULL; - return err; -} - -PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask) -{ - PmInternal *midi = (PmInternal *) stream; - PmError err = pmNoError; - - if (midi == NULL) - err = pmBadPtr; - else - midi->channel_mask = mask; - - return pm_errmsg(err); -} - - -PMEXPORT PmError Pm_SetFilter(PortMidiStream *stream, int32_t filters) -{ - PmInternal *midi = (PmInternal *) stream; - PmError err = pmNoError; - - /* arg checking */ - if (midi == NULL) - err = pmBadPtr; - else if (!pm_descriptors[midi->device_id].pub.opened) - err = pmBadPtr; - else - midi->filters = filters; - return pm_errmsg(err); -} - - -PMEXPORT PmError Pm_Close(PortMidiStream *stream) -{ - PmInternal *midi = (PmInternal *) stream; - PmError err = pmNoError; - - pm_hosterror = FALSE; - /* arg checking */ - if (midi == NULL) /* midi must point to something */ - err = pmBadPtr; - /* if it is an open device, the device_id will be valid */ - else if (midi->device_id < 0 || midi->device_id >= pm_descriptor_len) - err = pmBadPtr; - /* and the device should be in the opened state */ - else if (!pm_descriptors[midi->device_id].pub.opened) - err = pmBadPtr; - - if (err != pmNoError) - goto error_return; - - /* close the device */ - err = (*midi->dictionary->close)(midi); - /* even if an error occurred, continue with cleanup */ - pm_descriptors[midi->device_id].pm_internal = NULL; - pm_descriptors[midi->device_id].pub.opened = FALSE; - if (midi->queue) Pm_QueueDestroy(midi->queue); - pm_free(midi); -error_return: - /* system dependent code must set pm_hosterror and - * pm_hosterror_text if a pmHostError occurs. - */ - return pm_errmsg(err); -} - -PmError Pm_Synchronize(PortMidiStream* stream) -{ - PmInternal *midi = (PmInternal *) stream; - PmError err = pmNoError; - if (midi == NULL) - err = pmBadPtr; - else if (!pm_descriptors[midi->device_id].pub.output) - err = pmBadPtr; - else if (!pm_descriptors[midi->device_id].pub.opened) - err = pmBadPtr; - else - midi->first_message = TRUE; - return err; -} - -PMEXPORT PmError Pm_Abort(PortMidiStream* stream) -{ - PmInternal *midi = (PmInternal *) stream; - PmError err; - /* arg checking */ - if (midi == NULL) - err = pmBadPtr; - else if (!pm_descriptors[midi->device_id].pub.output) - err = pmBadPtr; - else if (!pm_descriptors[midi->device_id].pub.opened) - err = pmBadPtr; - else - err = (*midi->dictionary->abort)(midi); - - if (err == pmHostError) { - midi->dictionary->check_host_error(midi); - } - return pm_errmsg(err); -} - - - -/* pm_channel_filtered returns non-zero if the channel mask is - blocking the current channel */ -#define pm_channel_filtered(status, mask) \ - ((((status) & 0xF0) != 0xF0) && (!(Pm_Channel((status) & 0x0F) & (mask)))) - - -/* The following two functions will checks to see if a MIDI message - matches the filtering criteria. Since the sysex routines only want - to filter realtime messages, we need to have separate routines. - */ - - -/* pm_realtime_filtered returns non-zero if the filter will kill the - current message. Note that only realtime messages are checked here. - */ -#define pm_realtime_filtered(status, filters) \ - ((((status) & 0xF0) == 0xF0) && ((1 << ((status) & 0xF)) & (filters))) - -/* - return ((status == MIDI_ACTIVE) && (filters & PM_FILT_ACTIVE)) - || ((status == MIDI_CLOCK) && (filters & PM_FILT_CLOCK)) - || ((status == MIDI_START) && (filters & PM_FILT_PLAY)) - || ((status == MIDI_STOP) && (filters & PM_FILT_PLAY)) - || ((status == MIDI_CONTINUE) && (filters & PM_FILT_PLAY)) - || ((status == MIDI_F9) && (filters & PM_FILT_F9)) - || ((status == MIDI_FD) && (filters & PM_FILT_FD)) - || ((status == MIDI_RESET) && (filters & PM_FILT_RESET)) - || ((status == MIDI_MTC) && (filters & PM_FILT_MTC)) - || ((status == MIDI_SONGPOS) && (filters & PM_FILT_SONG_POSITION)) - || ((status == MIDI_SONGSEL) && (filters & PM_FILT_SONG_SELECT)) - || ((status == MIDI_TUNE) && (filters & PM_FILT_TUNE)); -}*/ - - -/* pm_status_filtered returns non-zero if a filter will kill the - current message, based on status. Note that sysex and real time are - not checked. It is up to the subsystem (winmm, core midi, alsa) to - filter sysex, as it is handled more easily and efficiently at that - level. Realtime message are filtered in pm_realtime_filtered. - */ -#define pm_status_filtered(status, filters) \ - ((1 << (16 + ((status) >> 4))) & (filters)) - - -/* - return ((status == MIDI_NOTE_ON) && (filters & PM_FILT_NOTE)) - || ((status == MIDI_NOTE_OFF) && (filters & PM_FILT_NOTE)) - || ((status == MIDI_CHANNEL_AT) && - (filters & PM_FILT_CHANNEL_AFTERTOUCH)) - || ((status == MIDI_POLY_AT) && (filters & PM_FILT_POLY_AFTERTOUCH)) - || ((status == MIDI_PROGRAM) && (filters & PM_FILT_PROGRAM)) - || ((status == MIDI_CONTROL) && (filters & PM_FILT_CONTROL)) - || ((status == MIDI_PITCHBEND) && (filters & PM_FILT_PITCHBEND)); - -} -*/ - -static void pm_flush_sysex(PmInternal *midi, PmTimestamp timestamp) -{ - PmEvent event; - - /* there may be nothing in the buffer */ - if (midi->message_count == 0) return; /* nothing to flush */ - - event.message = midi->message; - event.timestamp = timestamp; - /* copied from pm_read_short, avoids filtering */ - if (Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) { - midi->sysex_in_progress = FALSE; - } - midi->message_count = 0; - midi->message = 0; -} - - -/* pm_read_short and pm_read_bytes - are the interface between system-dependent MIDI input handlers - and the system-independent PortMIDI code. - The input handler MUST obey these rules: - 1) all short input messages must be sent to pm_read_short, which - enqueues them to a FIFO for the application. - 2) each buffer of sysex bytes should be reported by calling pm_read_bytes - (which sets midi->sysex_in_progress). After the eox byte, - pm_read_bytes will clear sysex_in_progress - */ - -/* pm_read_short is the place where all input messages arrive from - system-dependent code such as pmwinmm.c. Here, the messages - are entered into the PortMidi input buffer. - */ -void pm_read_short(PmInternal *midi, PmEvent *event) -{ - int status; - /* arg checking */ - assert(midi != NULL); - /* midi filtering is applied here */ - status = Pm_MessageStatus(event->message); - if (!pm_status_filtered(status, midi->filters) - && (!is_real_time(status) || - !pm_realtime_filtered(status, midi->filters)) - && !pm_channel_filtered(status, midi->channel_mask)) { - /* if sysex is in progress and we get a status byte, it had - better be a realtime message or the starting SYSEX byte; - otherwise, we exit the sysex_in_progress state - */ - if (midi->sysex_in_progress && (status & MIDI_STATUS_MASK)) { - /* two choices: real-time or not. If it's real-time, then - * this should be delivered as a sysex byte because it is - * embedded in a sysex message - */ - if (is_real_time(status)) { - midi->message |= (status << (8 * midi->message_count++)); - if (midi->message_count == 4) { - pm_flush_sysex(midi, event->timestamp); - } - } else { /* otherwise, it's not real-time. This interrupts - * a sysex message in progress */ - midi->sysex_in_progress = FALSE; - } - } else if (Pm_Enqueue(midi->queue, event) == pmBufferOverflow) { - midi->sysex_in_progress = FALSE; - } - } -} - - -/* pm_read_bytes -- a sequence of bytes has been read from a device. - * parse the bytes into PmEvents and put them in the queue. - * midi - the midi device - * data - the bytes - * len - the number of bytes - * timestamp - when were the bytes received? - * - * returns how many bytes processed, which is always the len parameter - */ -unsigned int pm_read_bytes(PmInternal *midi, const unsigned char *data, - int len, PmTimestamp timestamp) -{ - int i = 0; /* index into data, must not be unsigned (!) */ - PmEvent event; - event.timestamp = timestamp; - assert(midi); - - /* Since sysex messages may have embedded real-time messages, we - * cannot simply send every consecutive group of 4 bytes as sysex - * data. Instead, we insert each data byte into midi->message and - * keep count using midi->message_count. If we encounter a - * real-time message, it is sent immediately as a 1-byte PmEvent, - * while sysex bytes are sent as PmEvents in groups of 4 bytes - * until the sysex is either terminated by EOX (F7) or a - * non-real-time message is encountered, indicating that the EOX - * was dropped. - */ - - /* This is a finite state machine so that we can accept any number - * of bytes, even if they contain partial messages. - * - * midi->sysex_in_progress says we are expecting sysex message bytes - * (otherwise, expect a short message or real-time message) - * midi->message accumulates bytes to enqueue for application - * midi->message_count is the number of bytes accumulated - * midi->short_message_count is how many bytes we need in midi->message, - * therefore midi->message_count, before we have a complete message - * midi->running_status is running status or 0 if there is none - * - * Set running status when: A status byte < F0 is received. - * Clear running status when: A status byte from F0 through F7 is - * received. - * Ignore (drop) data bytes when running status is 0. - * - * Our output buffer (the application input buffer) can overflow - * at any time. If that occurs, we have to clear sysex_in_progress - * (otherwise, the buffer could be flushed and we could resume - * inserting sysex bytes into the buffer, resulting in a continuation - * of a sysex message even though a buffer full of bytes was dropped.) - * - * Since we have to parse everything and form <=4-byte PmMessages, - * we send all messages via pm_read_short, which filters messages - * according to midi->filters and clears sysex_in_progress on - * buffer overflow. This also provides a "short cut" for short - * messages that are already parsed, allowing API-specific code - * to bypass this more expensive state machine. What if we are - * getting a sysex message, but it is interrupted by a short - * message (status 80-EF) and a direct call to pm_read_short? - * Without some care, the state machine would still be in - * sysex_in_progress mode, and subsequent data bytes would be - * accumulated as more sysex data, which is wrong since you - * cannot have a short message in the middle of a sysex message. - * To avoid this problem, pm_read_short clears sysex_in_progress - * when a non-real-time short message arrives. - */ - - while (i < len) { - unsigned char byte = data[i++]; - if (is_real_time(byte)) { - event.message = byte; - pm_read_short(midi, &event); - } else if (byte & MIDI_STATUS_MASK && byte != MIDI_EOX) { - midi->message = byte; - midi->message_count = 1; - if (byte == MIDI_SYSEX) { - midi->sysex_in_progress = TRUE; - } else { - midi->sysex_in_progress = FALSE; - midi->short_message_count = pm_midi_length(midi->message); - /* maybe we're done already with a 1-byte message: */ - if (midi->short_message_count == 1) { - pm_read_short(midi, &event); - midi->message_count = 0; - } - } - } else if (midi->sysex_in_progress) { /* sysex data byte */ - /* accumulate sysex message data or EOX */ - midi->message |= (byte << (8 * midi->message_count++)); - if (midi->message_count == 4 || byte == MIDI_EOX) { - event.message = midi->message; - /* enqueue if not filtered, and then if there is overflow, - stop sysex_in_progress */ - if (!(midi->filters & PM_FILT_SYSEX) && - Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) { - midi->sysex_in_progress = FALSE; - } else if (byte == MIDI_EOX) { /* continue unless EOX */ - midi->sysex_in_progress = FALSE; - } - midi->message_count = 0; - midi->message = 0; - } - } else { /* no sysex in progress, must be short message */ - if (midi->message_count == 0) { /* need a running status */ - if (midi->running_status) { - midi->message = midi->running_status; - midi->message_count = 1; - } else { /* drop data byte - not sysex and no status byte */ - continue; - } - } - midi->message |= (byte << (8 * midi->message_count++)); - if (midi->message_count == midi->short_message_count) { - event.message = midi->message; - pm_read_short(midi, &event); - } - } - } - return i; -} diff --git a/portmidi/pm_common/portmidi.h b/portmidi/pm_common/portmidi.h deleted file mode 100755 index 8696a73..0000000 --- a/portmidi/pm_common/portmidi.h +++ /dev/null @@ -1,974 +0,0 @@ -#ifndef PORTMIDI_PORTMIDI_H -#define PORTMIDI_PORTMIDI_H - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* - * PortMidi Portable Real-Time MIDI Library - * PortMidi API Header File - * Latest version available at: http://sourceforge.net/projects/portmedia - * - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * Copyright (c) 2001-2006 Roger B. Dannenberg - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortMidi license; however, - * the PortMusic community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/* CHANGELOG FOR PORTMIDI - * (see ../CHANGELOG.txt) - * - * NOTES ON HOST ERROR REPORTING: - * - * PortMidi errors (of type PmError) are generic, - * system-independent errors. When an error does not map to one of - * the more specific PmErrors, the catch-all code pmHostError is - * returned. This means that PortMidi has retained a more specific - * system-dependent error code. The caller can get more information - * by calling Pm_GetHostErrorText() to get a text string describing - * the error. Host errors can arise asynchronously from callbacks, - * * so there is no specific return code. Asynchronous errors are - * checked and reported by Pm_Poll. You can also check by calling - * Pm_HasHostError(). If this returns TRUE, Pm_GetHostErrorText() - * will return a text description of the error. - * - * NOTES ON COMPILE-TIME SWITCHES - * - * DEBUG assumes stdio and a console. Use this if you want - * automatic, simple error reporting, e.g. for prototyping. If - * you are using MFC or some other graphical interface with no - * console, DEBUG probably should be undefined. - * PM_CHECK_ERRORS more-or-less takes over error checking for - * return values, stopping your program and printing error - * messages when an error occurs. This also uses stdio for - * console text I/O. You can selectively disable this error - * checking by declaring extern int pm_check_errors; and - * setting pm_check_errors = FALSE; You can also reenable. - */ -/** - \defgroup grp_basics Basic Definitions - @{ -*/ - -#include <stdint.h> - -#ifdef _WINDLL -#define PMEXPORT __declspec(dllexport) -#else -#define PMEXPORT -#endif - -#ifndef FALSE - #define FALSE 0 -#endif -#ifndef TRUE - #define TRUE 1 -#endif - -/* default size of buffers for sysex transmission: */ -#define PM_DEFAULT_SYSEX_BUFFER_SIZE 1024 - - -typedef enum { - pmNoError = 0, /**< Normal return value indicating no error. */ - pmNoData = 0, /**< @brief No error, also indicates no data available. - * Use this constant where a value greater than zero would - * indicate data is available. - */ - pmGotData = 1, /**< A "no error" return also indicating data available. */ - pmHostError = -10000, - pmInvalidDeviceId, /**< Out of range or - * output device when input is requested or - * input device when output is requested or - * device is already opened. - */ - pmInsufficientMemory, - pmBufferTooSmall, - pmBufferOverflow, - 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. */ - pmInternalError, - 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. */ - pmDeviceRemoved /**< Output attempted after (USB) device was removed. */ - /* NOTE: If you add a new error type, you must update Pm_GetErrorText(). */ -} PmError; /**< @brief @enum PmError PortMidi error code; a common return type. - * No error is indicated by zero; errors are indicated by < 0. - */ - -/** - Pm_Initialize() is the library initialization function - call this before - using the library. - - *NOTE:* PortMidi scans for available devices when #Pm_Initialize - is called. To observe subsequent changes in the available - devices, you must shut down PortMidi by calling #Pm_Terminate and - then restart by calling #Pm_Initialize again. *IMPORTANT*: On - MacOS, #Pm_Initialize *must* always be called on the same - thread. Otherwise, changes in the available MIDI devices will - *not* be seen by PortMidi. As an example, if you start PortMidi in - a thread for processing MIDI, do not try to rescan devices by - calling #Pm_Initialize in a GUI thread. Instead, start PortMidi - the first time and every time in the GUI thread. Alternatively, - let the GUI request a restart in the MIDI thread. (These - restrictions only apply to macOS.) Speaking of threads, on all - platforms, you are allowed to call #Pm_Initialize in one thread, - yet send MIDI or poll for incoming MIDI in another - thread. However, PortMidi is not "thread safe," which means you - cannot allow threads to call PortMidi functions concurrently. - - @return pmNoError. - - 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. -*/ -PMEXPORT PmError Pm_Initialize(void); - -/** - Pm_Terminate() is the library termination function - call this after - using the library. -*/ -PMEXPORT PmError Pm_Terminate(void); - -/** Represents an open MIDI device. */ -typedef void PortMidiStream; - -/** A shorter form of #PortMidiStream. */ -#define PmStream PortMidiStream - -/** 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 int Pm_HasHostError(PortMidiStream * stream); - - -/** 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 const char *Pm_GetErrorText(PmError errnum); - -/** 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 void Pm_GetHostErrorText(char * msg, unsigned int len); - -/** Any host error msg has at most this many characters, including EOS. */ -#define PM_HOST_ERROR_MSG_LEN 256u - -/** 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 int PmDeviceID; - -/** This PmDeviceID (constant) value represents no device and may be - returned by Pm_GetDefaultInputDeviceID() or - Pm_GetDefaultOutputDeviceID() if no default exists. -*/ -#define pmNoDevice -1 - -/** MIDI device information is returned in this structure, which is - owned by PortMidi and read-only to applications. See Pm_GetDeviceInfo(). -*/ -#define PM_DEVICEINFO_VERS 200 -typedef struct { - int structVersion; /**< @brief this internal structure version */ - const char *interf; /**< @brief underlying MIDI API, e.g. - "MMSystem" or "DirectX" */ - char *name; /**< @brief device name, e.g. "USB MidiSport 1x1" */ - int input; /**< @brief true iff input is available */ - int output; /**< @brief true iff output is available */ - int opened; /**< @brief used by generic PortMidi for error checking */ - int is_virtual; /**< @brief true iff this is/was a virtual device */ -} PmDeviceInfo; - -/** MIDI system-dependent device or driver info is passed in this - structure, which is owned by the caller. -*/ -#define PM_SYSDEPINFO_VERS 210 - -enum PmSysDepPropertyKey { - pmKeyNone = 0, /**< a "noop" key value */ - /** CoreMIDI Manufacturer name, value is string */ - pmKeyCoreMidiManufacturer = 1, - /** Linux ALSA snd_seq_port_info_set_name, value is a string. Can be - passed in PmSysDepInfo to Pm_OpenInput or Pm_OpenOutput when opening - a device. The created port will be named accordingly and will be - visible for externally made connections (subscriptions). (Linux ALSA - ports are always enabled for this, but only get application-specific - names if you give it one.) This key/value is ignored when opening - virtual ports, which are named when they are created.) */ - pmKeyAlsaPortName = 2, - /** Linux ALSA snd_seq_set_client_name, value is a string. - Can be passed in PmSysDepInfo to Pm_OpenInput or Pm_OpenOutput. - Pm_CreateVirtualInput or Pm_CreateVirtualOutput. Will override - any previously set client name and applies to all ports. */ - pmKeyAlsaClientName = 3 - /* if system-dependent code introduces more options, register - the key here to avoid conflicts. */ -}; - -/** System-dependent information can be passed when creating and opening - ports using this data structure, which stores alternating keys and - values (addresses). See `pm_test/sendvirtual.c`, `pm_test/recvvirtual.c`, - and `pm_test/testio.c` for examples. - */ -typedef struct { - int structVersion; /**< @brief this structure version */ - int length; /**< @brief number of properties in this structure */ - struct { - enum PmSysDepPropertyKey key; - const void *value; - } properties[]; -} PmSysDepInfo; - - -/** Get devices count, ids range from 0 to Pm_CountDevices()-1. */ -PMEXPORT int Pm_CountDevices(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", so if there *is* a default, implementors should use - pm_add_device to add system default input and output devices - first.) - - The recommended 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. - - On linux, you can create virtual ports and use an external program - to set up inter-application and device connections. - - 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. -*/ -PMEXPORT PmDeviceID Pm_GetDefaultInputDeviceID(void); - -/** @brief see PmDeviceID Pm_GetDefaultInputDeviceID() */ -PMEXPORT PmDeviceID Pm_GetDefaultOutputDeviceID(void); - -/** Find a device that matches a pattern. - - @param pattern a substring of the device name, or if the pattern - contains the two-character separator ", ", then the first part of - the pattern represents a device interface substring and the second - part after the separator represents a device name substring. - - @param is_input restricts the search to an input when true, or an - output when false. - - @return the number of the first device whose device interface - contains the interface pattern (if any), whose device name - contains the name pattern, and whose direction (input or output) - matches the #is_input parameter. If no match is found, #pmNoDevice - (-1) is returned. -*/ -PMEXPORT PmDeviceID Pm_FindDevice(char *pattern, int is_input); - - -/** Represents a millisecond clock with arbitrary start time. - This type is used for all MIDI timestamps and clocks. -*/ -typedef int32_t PmTimestamp; -typedef PmTimestamp (*PmTimeProcPtr)(void *time_info); - -/** TRUE if t1 before t2 */ -#define PmBefore(t1,t2) (((t1)-(t2)) < 0) -/** @} */ -/** - \defgroup grp_device Input/Output Devices Handling - @{ -*/ -/** Get a PmDeviceInfo structure describing a MIDI device. - - @param id the device to be queried. - - If \p 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(). -*/ -PMEXPORT const PmDeviceInfo *Pm_GetDeviceInfo(PmDeviceID id); - -/** Open a MIDI device for input. - - @param stream the address of a #PortMidiStream pointer which will - receive a pointer to the newly opened stream. - - @param inputDevice the ID of the device to be opened (see #PmDeviceID). - - @param inputSysDepInfo a pointer to an optional system-dependent - data structure (a #PmSysDepInfo struct) containing additional - information for device setup or handle processing. This parameter - is never required for correct operation. If not used, specify - NULL. Declared `void *` here for backward compatibility. Note that - with Linux ALSA, you can use this parameter to specify a client name - and port name. - - @param bufferSize the 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. - - @param 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. - \p time_proc result values are appended to incoming MIDI data, - normally by mapping system-provided timestamps to the \p time_proc - timestamps to maintain the precision of system-provided - timestamps. - - @param time_info is a pointer passed to time_proc. - - @return #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(). -*/ -PMEXPORT PmError Pm_OpenInput(PortMidiStream** stream, - PmDeviceID inputDevice, - void *inputSysDepInfo, - int32_t bufferSize, - PmTimeProcPtr time_proc, - void *time_info); - -/** Open a MIDI device for output. - - @param stream the address of a #PortMidiStream pointer which will - receive a pointer to the newly opened stream. - - @param outputDevice the ID of the device to be opened (see #PmDeviceID). - - @param inputSysDepInfo a pointer to an optional system-specific - data structure (a #PmSysDepInfo struct) containing additional - information for device setup or handle processing. This parameter - is never required for correct operation. If not used, specify - NULL. Declared `void *` here for backward compatibility. Note that - with Linux ALSA, you can use this parameter to specify a client name - and port name. - - @param bufferSize the 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 \p 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 \p 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. - - @param latency the 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 \p latency will be added to the timestamp, and - provided the elapsed computation time has not exceeded \p 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 \p latency to the - audio buffer latency. - - @param 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. \p 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. - - @param time_info a pointer passed to time_proc. - - @return #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 \p 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(). -*/ -PMEXPORT PmError Pm_OpenOutput(PortMidiStream** stream, - PmDeviceID outputDevice, - void *outputSysDepInfo, - int32_t bufferSize, - PmTimeProcPtr time_proc, - void *time_info, - int32_t latency); - -/** Create a virtual input device. - - @param name gives the virtual device name, which is visible to - other applications. - - @param interf is 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. - - @param sysDepInfo contains interface-dependent additional - information (a #PmSysDepInfo struct), e.g., hints or options. This - parameter is never required for correct operation. If not used, - specify NULL. Declared `void *` here for backward compatibility. - - @return a device ID or #pmNameConflict (\p name is invalid or - already exists) or #pmInterfaceNotSupported (\p 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. -*/ -PMEXPORT PmError Pm_CreateVirtualInput(const char *name, - const char *interf, - void *sysDepInfo); - -/** Create a virtual output device. - - @param name gives the virtual device name, which is visible to - other applications. - - @param interf is 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. - - @param sysDepInfo contains interface-dependent additional - information (a #PmSysDepInfo struct), e.g., hints or options. This - parameter is never required for correct operation. If not used, - specify NULL. Declared `void *` here for backward compatibility. - - @return a device ID or #pmInvalidDeviceId (\p name is invalid or - already exists) or #pmInterfaceNotSupported (\p 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. -*/ -PMEXPORT PmError Pm_CreateVirtualOutput(const char *name, - const char *interf, - void *sysDepInfo); - -/** Remove a virtual device. - - @param device a 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. - - @return #pmNoError if the device was deleted or #pmInvalidDeviceId - if the device is open, already deleted, or \p device is out of - range. -*/ -PMEXPORT PmError Pm_DeleteVirtualDevice(PmDeviceID device); - /** @} */ - -/** - @defgroup grp_events_filters Events and Filters Handling - @{ -*/ - -/* Filter bit-mask definitions */ -/** filter active sensing messages (0xFE): */ -#define PM_FILT_ACTIVE (1 << 0x0E) -/** filter system exclusive messages (0xF0): */ -#define PM_FILT_SYSEX (1 << 0x00) -/** filter MIDI clock message (0xF8) */ -#define PM_FILT_CLOCK (1 << 0x08) -/** filter play messages (start 0xFA, stop 0xFC, continue 0xFB) */ -#define PM_FILT_PLAY ((1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B)) -/** filter tick messages (0xF9) */ -#define PM_FILT_TICK (1 << 0x09) -/** filter undefined FD messages */ -#define PM_FILT_FD (1 << 0x0D) -/** filter undefined real-time messages */ -#define PM_FILT_UNDEFINED PM_FILT_FD -/** filter reset messages (0xFF) */ -#define PM_FILT_RESET (1 << 0x0F) -/** filter all real-time messages */ -#define PM_FILT_REALTIME (PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK | \ - PM_FILT_PLAY | PM_FILT_UNDEFINED | PM_FILT_RESET | PM_FILT_TICK) -/** filter note-on and note-off (0x90-0x9F and 0x80-0x8F */ -#define PM_FILT_NOTE ((1 << 0x19) | (1 << 0x18)) -/** filter channel aftertouch (most midi controllers use this) (0xD0-0xDF)*/ -#define PM_FILT_CHANNEL_AFTERTOUCH (1 << 0x1D) -/** per-note aftertouch (0xA0-0xAF) */ -#define PM_FILT_POLY_AFTERTOUCH (1 << 0x1A) -/** filter both channel and poly aftertouch */ -#define PM_FILT_AFTERTOUCH (PM_FILT_CHANNEL_AFTERTOUCH | \ - PM_FILT_POLY_AFTERTOUCH) -/** Program changes (0xC0-0xCF) */ -#define PM_FILT_PROGRAM (1 << 0x1C) -/** Control Changes (CC's) (0xB0-0xBF)*/ -#define PM_FILT_CONTROL (1 << 0x1B) -/** Pitch Bender (0xE0-0xEF*/ -#define PM_FILT_PITCHBEND (1 << 0x1E) -/** MIDI Time Code (0xF1)*/ -#define PM_FILT_MTC (1 << 0x01) -/** Song Position (0xF2) */ -#define PM_FILT_SONG_POSITION (1 << 0x02) -/** Song Select (0xF3)*/ -#define PM_FILT_SONG_SELECT (1 << 0x03) -/** Tuning request (0xF6) */ -#define PM_FILT_TUNE (1 << 0x06) -/** All System Common messages (mtc, song position, song select, tune request) */ -#define PM_FILT_SYSTEMCOMMON (PM_FILT_MTC | PM_FILT_SONG_POSITION | \ - PM_FILT_SONG_SELECT | PM_FILT_TUNE) - - -/* Set filters on an open input stream to drop selected input types. - - @param stream an open MIDI input stream. - - @param filters indicate message types to filter (block). - - @return #pmNoError or an error code. - - By default, only active sensing messages are filtered. - To prohibit, say, active sensing and sysex messages, call - Pm_SetFilter(stream, PM_FILT_ACTIVE | PM_FILT_SYSEX); - - Filtering is useful when midi routing or midi thru functionality - is being provided by the user application. - For example, you may want to exclude timing messages (clock, MTC, - start/stop/continue), while allowing note-related messages to pass. - Or you may be using a sequencer or drum-machine for MIDI clock - information but want to exclude any notes it may play. - */ -PMEXPORT PmError Pm_SetFilter(PortMidiStream* stream, int32_t filters); - -/** Create a mask that filters one channel. */ -#define Pm_Channel(channel) (1<<(channel)) - -/** Filter incoming messages based on channel. - - @param stream an open MIDI input stream. - - @param mask indicates channels to be received. - - @return #pmNoError or an error code. - - The \p mask is a 16-bit bitfield corresponding to appropriate channels. - The #Pm_Channel macro can assist in calling this function. - I.e. to receive only input on channel 1, call with - Pm_SetChannelMask(Pm_Channel(1)); - Multiple channels should be OR'd together, like - Pm_SetChannelMask(Pm_Channel(10) | Pm_Channel(11)) - - Note that channels are numbered 0 to 15 (not 1 to 16). Most - synthesizer and interfaces number channels starting at 1, but - PortMidi numbers channels starting at 0. - - All channels are allowed by default -*/ -PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask); - -/** Terminate outgoing messages immediately. - - @param stream an open MIDI output stream. - - @result #pmNoError or an error code. - - The caller should immediately close the output port; this call may - result in transmission of a partial MIDI message. There is no - abort for Midi input because the user can simply ignore messages - in the buffer and close an input device at any time. If the - specified behavior cannot be achieved through the system-level - interface (ALSA, CoreMIDI, etc.), the behavior may be that of - Pm_Close(). - */ -PMEXPORT PmError Pm_Abort(PortMidiStream* stream); - -/** Close a midi stream, flush any pending buffers if possible. - - @param stream an open MIDI input or output stream. - - @result #pmNoError or an error code. - - If the system-level interface (ALSA, CoreMIDI, etc.) does not - support flushing remaining messages, the behavior may be one of - the following (most preferred first): block until all pending - timestamped messages are delivered; deliver messages to a server - or kernel process for later delivery but return immediately; drop - messages (as in Pm_Abort()). Therefore, to be safe, applications - should wait until the output queue is empty before calling - Pm_Close(). E.g. calling Pt_Sleep(100 + latency); will give a - 100ms "cushion" beyond latency (if any) before closing. -*/ -PMEXPORT PmError Pm_Close(PortMidiStream* stream); - -/** (re)synchronize to the time_proc passed when the stream was opened. - - @param stream an open MIDI input or output stream. - - @result #pmNoError or an error code. - - Typically, this is used when the stream must be opened before the - time_proc reference is actually advancing. In this case, message - timing may be erratic, but since timestamps of zero mean "send - immediately," initialization messages with zero timestamps can be - written without a functioning time reference and without - problems. Before the first MIDI message with a non-zero timestamp - is written to the stream, the time reference must begin to advance - (for example, if the time_proc computes time based on audio - samples, time might begin to advance when an audio stream becomes - active). After time_proc return values become valid, and BEFORE - writing the first non-zero timestamped MIDI message, call - Pm_Synchronize() so that PortMidi can observe the difference - between the current time_proc value and its MIDI stream time. - - In the more normal case where time_proc values advance - continuously, there is no need to call #Pm_Synchronize. PortMidi - will always synchronize at the first output message and - periodically thereafter. -*/ -PMEXPORT PmError Pm_Synchronize(PortMidiStream* stream); - - -/** Encode a short Midi message into a 32-bit word. If data1 - and/or data2 are not present, use zero. -*/ -#define Pm_Message(status, data1, data2) \ - ((((data2) << 16) & 0xFF0000) | \ - (((data1) << 8) & 0xFF00) | \ - ((status) & 0xFF)) -/** Extract the status field from a 32-bit midi message. */ -#define Pm_MessageStatus(msg) ((msg) & 0xFF) -/** Extract the 1st data field (e.g., pitch) from a 32-bit midi message. */ -#define Pm_MessageData1(msg) (((msg) >> 8) & 0xFF) -/** Extract the 2nd data field (e.g., velocity) from a 32-bit midi message. */ -#define Pm_MessageData2(msg) (((msg) >> 16) & 0xFF) - -typedef uint32_t PmMessage; /**< @brief see #PmEvent */ -/** - All MIDI data comes in the form of PmEvent structures. A sysex - message is encoded as a sequence of PmEvent structures, with each - structure carrying 4 bytes of the message, i.e. only the first - PmEvent carries the status byte. - - All other MIDI messages take 1 to 3 bytes and are encoded in a whole - PmMessage with status in the low-order byte and remaining bytes - unused, i.e., a 3-byte note-on message will occupy 3 low-order bytes - of PmMessage, leaving the high-order byte unused. - - Note that MIDI allows nested messages: the so-called "real-time" MIDI - messages can be inserted into the MIDI byte stream at any location, - including within a sysex message. MIDI real-time messages are one-byte - messages used mainly for timing (see the MIDI spec). PortMidi retains - the order of non-real-time MIDI messages on both input and output, but - it does not specify exactly how real-time messages are processed. This - is particulary problematic for MIDI input, because the input parser - must either prepare to buffer an unlimited number of sysex message - bytes or to buffer an unlimited number of real-time messages that - arrive embedded in a long sysex message. To simplify things, the input - parser is allowed to pass real-time MIDI messages embedded within a - sysex message, and it is up to the client to detect, process, and - remove these messages as they arrive. - - When receiving sysex messages, the sysex message is terminated - by either an EOX status byte (anywhere in the 4 byte messages) or - by a non-real-time status byte in the low order byte of the message. - If you get a non-real-time status byte but there was no EOX byte, it - means the sysex message was somehow truncated. This is not - considered an error; e.g., a missing EOX can result from the user - disconnecting a MIDI cable during sysex transmission. - - A real-time message can occur within a sysex message. A real-time - message will always occupy a full PmEvent with the status byte in - the low-order byte of the PmEvent message field. (This implies that - the byte-order of sysex bytes and real-time message bytes may not - be preserved -- for example, if a real-time message arrives after - 3 bytes of a sysex message, the real-time message will be delivered - first. The first word of the sysex message will be delivered only - after the 4th byte arrives, filling the 4-byte PmEvent message field. - - The timestamp field is observed when the output port is opened with - a non-zero latency. A timestamp of zero means "use the current time", - which in turn means to deliver the message with a delay of - latency (the latency parameter used when opening the output port.) - Do not expect PortMidi to sort data according to timestamps -- - messages should be sent in the correct order, and timestamps MUST - be non-decreasing. See also "Example" for Pm_OpenOutput() above. - - A sysex message will generally fill many #PmEvent structures. On - output to a #PortMidiStream with non-zero latency, the first timestamp - on sysex message data will determine the time to begin sending the - message. PortMidi implementations may ignore timestamps for the - remainder of the sysex message. - - On input, the timestamp ideally denotes the arrival time of the - status byte of the message. The first timestamp on sysex message - data will be valid. Subsequent timestamps may denote - when message bytes were actually received, or they may be simply - copies of the first timestamp. - - Timestamps for nested messages: If a real-time message arrives in - the middle of some other message, it is enqueued immediately with - the timestamp corresponding to its arrival time. The interrupted - non-real-time message or 4-byte packet of sysex data will be enqueued - later. The timestamp of interrupted data will be equal to that of - the interrupting real-time message to insure that timestamps are - non-decreasing. - */ -typedef struct { - PmMessage message; - PmTimestamp timestamp; -} PmEvent; - -/** @} */ - -/** \defgroup grp_io Reading and Writing Midi Messages - @{ -*/ -/** Retrieve midi data into a buffer. - - @param stream the open input stream. - - @return the number of events read, or, if the result is negative, - a #PmError value will be returned. - - The Buffer Overflow Problem - - The problem: if an input overflow occurs, data will be lost, - ultimately because there is no flow control all the way back to - the data source. When data is lost, the receiver should be - notified and some sort of graceful recovery should take place, - e.g. you shouldn't resume receiving in the middle of a long sysex - message. - - With a lock-free fifo, which is pretty much what we're stuck with - to enable portability to the Mac, it's tricky for the producer and - consumer to synchronously reset the buffer and resume normal - operation. - - Solution: the entire buffer managed by PortMidi will be flushed - when an overflow occurs. The consumer (Pm_Read()) gets an error - message (#pmBufferOverflow) and ordinary processing resumes as - soon as a new message arrives. The remainder of a partial sysex - message is not considered to be a "new message" and will be - flushed as well. -*/ -PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length); - -/** Test whether input is available. - - @param stream an open input stream. - - @return TRUE, FALSE, or an error value. - - If there was an asynchronous error, pmHostError is returned and you must - call again to determine if input is (also) available. - - You should probably *not* use this function. Call Pm_Read() - instead. If it returns 0, then there is no data available. It is - possible for Pm_Poll() to return TRUE before the complete message - is available, so Pm_Read() could return 0 even after Pm_Poll() - returns TRUE. Only call Pm_Poll() if you want to know that data is - probably available even though you are not ready to receive data. -*/ -PMEXPORT PmError Pm_Poll(PortMidiStream *stream); - -/** Write MIDI data from a buffer. - - @param stream an open output stream. - - @param buffer (address of) an array of MIDI event data. - - @param length the length of the \p buffer. - - @return TRUE, FALSE, or an error value. - - \b buffer may contain: - - short messages - - sysex messages that are converted into a sequence of PmEvent - structures, e.g. sending data from a file or forwarding them - from midi input, with 4 SysEx bytes per PmEvent message, - low-order byte first, until the last message, which may - contain from 1 to 4 bytes ending in MIDI EOX (0xF7). - - PortMidi allows 1-byte real-time messages to be embedded - within SysEx messages, but only on 4-byte boundaries so - that SysEx data always uses a full 4 bytes (except possibly - at the end). Each real-time message always occupies a full - PmEvent (3 of the 4 bytes in the PmEvent's message are - ignored) even when embedded in a SysEx message. - - Use Pm_WriteSysEx() to write a sysex message stored as a contiguous - array of bytes. - - Sysex data may contain embedded real-time messages. - - \p buffer is managed by the caller. The buffer may be destroyed - as soon as this call returns. -*/ -PMEXPORT PmError Pm_Write(PortMidiStream *stream, PmEvent *buffer, - int32_t length); - -/** Write a timestamped non-system-exclusive midi message. - - @param stream an open output stream. - - @param when timestamp for the event. - - @param msg the data for the event. - - @result #pmNoError or an error code. - - Messages are delivered in order, and timestamps must be - non-decreasing. (But timestamps are ignored if the stream was - opened with latency = 0, and otherwise, non-decreasing timestamps - are "corrected" to the lowest valid value.) -*/ -PMEXPORT PmError Pm_WriteShort(PortMidiStream *stream, PmTimestamp when, - PmMessage msg); - -/** Write a timestamped system-exclusive midi message. - - @param stream an open output stream. - - @param when timestamp for the event. - - @param msg the sysex message, terminated with an EOX status byte. - - @result #pmNoError or an error code. - - \p msg is managed by the caller and may be destroyed when this - call returns. -*/ -PMEXPORT PmError Pm_WriteSysEx(PortMidiStream *stream, PmTimestamp when, - unsigned char *msg); - -/** @} */ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* PORTMIDI_PORTMIDI_H */ diff --git a/portmidi/pm_haiku/pmhaiku.cpp b/portmidi/pm_haiku/pmhaiku.cpp deleted file mode 100644 index 0c592f1..0000000 --- a/portmidi/pm_haiku/pmhaiku.cpp +++ /dev/null @@ -1,473 +0,0 @@ -/* pmhaiku.cpp -- PortMidi os-dependent code */ - -#include <stdio.h> -#include <stdlib.h> -#include <vector> -#include <MidiConsumer.h> -#include <MidiEndpoint.h> -#include <MidiProducer.h> -#include <MidiRoster.h> -#include <MidiSynth.h> -#include "portmidi.h" -#include "pmutil.h" -#include "pminternal.h" - -namespace { - struct PmInputConsumer : BMidiLocalConsumer { - PmInputConsumer(PmInternal *midi) : - BMidiLocalConsumer("PortMidi input consumer"), - midi(midi) - { - } - - - void Data(uchar *data, size_t length, bool atomic, bigtime_t time) - { - if (!atomic) - return; // should these be also supported? - - if (data[0] == B_SYS_EX_START) { - pm_read_bytes(midi, data, length, time / 1000); - } else { - PmEvent event; - switch (length) { - case 1: - event.message = Pm_Message(data[0], 0, 0); - break; - case 2: - event.message = Pm_Message(data[0], data[1], 0); - break; - case 3: - event.message = Pm_Message(data[0], data[1], data[2]); - break; - default: - printf("Unexpected message length for short message, got %" B_PRIuSIZE "\n", length); - break; - } - event.timestamp = time / 1000; - pm_read_short(midi, &event); - } - } - - private: - PmInternal *midi; - }; - - struct PmOutputInfo { - BMidiLocalProducer *producer; - std::vector<unsigned char> sysexBuffer; - }; - - struct PmSynthOutputInfo { - BMidiSynth midiSynth; - std::vector<unsigned char> sysexBuffer; - }; - - - PmTimestamp synchronize(PmInternal *midi) - { - return 0; - } - - - PmError in_open(PmInternal *midi, void *driverInfo) - { - int32 endpointID = (int32)(intptr_t)pm_descriptors[midi->device_id].descriptor; - BMidiProducer *producer = BMidiRoster::FindProducer(endpointID); - if (!producer) - return pmInvalidDeviceId; - PmInputConsumer *consumer = new PmInputConsumer(midi); - status_t status = producer->Connect(consumer); - if (status != B_OK) { - consumer->Release(); - producer->Release(); - strcpy(pm_hosterror_text, strerror(status)); - pm_hosterror = TRUE; - return pmHostError; - } - midi->api_info = consumer; - producer->Release(); - return pmNoError; - } - - - PmError in_abort(PmInternal *midi) - { - return pmNoError; - } - - - PmError in_close(PmInternal *midi) - { - int32 endpointID = (int32)(intptr_t)pm_descriptors[midi->device_id].descriptor; - BMidiProducer *producer = BMidiRoster::FindProducer(endpointID); - if (!producer) - return pmInvalidDeviceId; - PmInputConsumer *consumer = (PmInputConsumer*)midi->api_info; - status_t status = producer->Disconnect(consumer); - if (status != B_OK) { - consumer->Release(); - producer->Release(); - strcpy(pm_hosterror_text, strerror(status)); - pm_hosterror = TRUE; - return pmHostError; - } - consumer->Release(); - midi->api_info = NULL; - producer->Release(); - return pmNoError; - } - - - PmError out_open(PmInternal *midi, void *driverInfo) - { - int32 endpointID = (int32)(intptr_t)pm_descriptors[midi->device_id].descriptor; - BMidiConsumer *consumer = BMidiRoster::FindConsumer(endpointID); - if (!consumer) - return pmInvalidDeviceId; - BMidiLocalProducer *producer = new BMidiLocalProducer("PortMidi output producer"); - status_t status = producer->Connect(consumer); - if (status != B_OK) { - consumer->Release(); - producer->Release(); - strcpy(pm_hosterror_text, strerror(status)); - pm_hosterror = TRUE; - return pmHostError; - } - PmOutputInfo *info = new PmOutputInfo; - info->producer = producer; - midi->api_info = info; - consumer->Release(); - return pmNoError; - } - - - PmError out_abort(PmInternal *midi) - { - return pmNoError; - } - - - PmError out_close(PmInternal *midi) - { - int32 endpointID = (int32)(intptr_t)pm_descriptors[midi->device_id].descriptor; - BMidiConsumer *consumer = BMidiRoster::FindConsumer(endpointID); - if (!consumer) - return pmInvalidDeviceId; - PmOutputInfo *info = (PmOutputInfo*)midi->api_info; - status_t status = info->producer->Disconnect(consumer); - if (status != B_OK) { - consumer->Release(); - midi->api_info = NULL; - info->producer->Release(); - delete info; - strcpy(pm_hosterror_text, strerror(status)); - pm_hosterror = TRUE; - return pmHostError; - } - consumer->Release(); - midi->api_info = NULL; - info->producer->Release(); - delete info; - return pmNoError; - } - - - PmError write_short(PmInternal *midi, PmEvent *buffer) - { - PmOutputInfo *info = (PmOutputInfo*)midi->api_info; - uchar data[3]; - data[0] = Pm_MessageStatus(buffer->message); - data[1] = Pm_MessageData1(buffer->message); - data[2] = Pm_MessageData2(buffer->message); - size_t length = pm_midi_length(data[0]); - - info->producer->SprayData(data, length, true, buffer->timestamp * 1000); - - // TODO: handle latency != 0 - return pmNoError; - } - - - PmError begin_sysex(PmInternal *midi, PmTimestamp timestamp) - { - PmOutputInfo *info = (PmOutputInfo*)midi->api_info; - info->sysexBuffer.clear(); - return pmNoError; - } - - - PmError end_sysex(PmInternal *midi, PmTimestamp timestamp) - { - PmOutputInfo *info = (PmOutputInfo*)midi->api_info; - info->producer->SpraySystemExclusive(&info->sysexBuffer[0], info->sysexBuffer.size(), timestamp * 1000); - info->sysexBuffer.clear(); - return pmNoError; - } - - - PmError write_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp) - { - PmOutputInfo *info = (PmOutputInfo*)midi->api_info; - info->sysexBuffer.push_back(byte); - return pmNoError; - } - - - PmError write_realtime(PmInternal *midi, PmEvent *buffer) - { - PmOutputInfo *info = (PmOutputInfo*)midi->api_info; - info->producer->SpraySystemRealTime(Pm_MessageStatus(buffer->message), buffer->timestamp * 1000); - return pmNoError; - } - - - PmError synth_open(PmInternal *midi, void *driverInfo) - { - PmSynthOutputInfo *info = new PmSynthOutputInfo; - info->midiSynth.EnableInput(true, true); - midi->api_info = info; - return pmNoError; - } - - - PmError synth_abort(PmInternal *midi) - { - return pmNoError; - } - - - PmError synth_close(PmInternal *midi) - { - PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; - delete info; - midi->api_info = NULL; - return pmNoError; - } - - - PmError write_short_synth(PmInternal *midi, PmEvent *buffer) - { - PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; - uchar data[3]; - data[0] = Pm_MessageStatus(buffer->message); - data[1] = Pm_MessageData1(buffer->message); - data[2] = Pm_MessageData2(buffer->message); - - switch(data[0] & 0xf0) { - case B_NOTE_OFF: - info->midiSynth.NoteOff((data[0] & 0x0f) + 1, data[1], data[2], buffer->timestamp); - break; - case B_NOTE_ON: - info->midiSynth.NoteOn((data[0] & 0x0f) + 1, data[1], data[2], buffer->timestamp); - break; - case B_KEY_PRESSURE: - info->midiSynth.KeyPressure((data[0] & 0x0f + 1), data[1], data[2], buffer->timestamp); - break; - case B_CONTROL_CHANGE: - info->midiSynth.ControlChange((data[0] & 0x0f) + 1, data[1], data[2], buffer->timestamp); - break; - case B_PROGRAM_CHANGE: - info->midiSynth.ProgramChange((data[0] & 0x0f) + 1, data[1], buffer->timestamp); - break; - case B_CHANNEL_PRESSURE: - info->midiSynth.ChannelPressure((data[0] & 0x0f) + 1, data[1], buffer->timestamp); - break; - case B_PITCH_BEND: - info->midiSynth.PitchBend((data[0] & 0x0f) + 1, data[1], data[2], buffer->timestamp); - break; - } - - // TODO: handle latency != 0 - return pmNoError; - } - - - PmError begin_sysex_synth(PmInternal *midi, PmTimestamp timestamp) - { - PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; - info->sysexBuffer.clear(); - return pmNoError; - } - - - PmError end_sysex_synth(PmInternal *midi, PmTimestamp timestamp) - { - PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; - info->midiSynth.SystemExclusive(&info->sysexBuffer[0], info->sysexBuffer.size(), timestamp); - info->sysexBuffer.clear(); - return pmNoError; - } - - - PmError write_byte_synth(PmInternal *midi, unsigned char byte, PmTimestamp timestamp) - { - PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; - info->sysexBuffer.push_back(byte); - return pmNoError; - } - - - PmError write_realtime_synth(PmInternal *midi, PmEvent *buffer) - { - PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; - info->midiSynth.SystemRealTime(Pm_MessageStatus(buffer->message), buffer->timestamp); - return pmNoError; - } - - - PmError write_flush(PmInternal *midi, PmTimestamp timestamp) - { - return pmNoError; - } - - - unsigned int check_host_error(PmInternal *midi) - { - return 0; - } - - - pm_fns_node pm_in_dictionary = { - none_write_short, - none_sysex, - none_sysex, - none_write_byte, - none_write_short, - none_write_flush, - synchronize, - in_open, - in_abort, - in_close, - success_poll, - check_host_error - }; - - pm_fns_node pm_out_dictionary = { - write_short, - begin_sysex, - end_sysex, - write_byte, - write_realtime, - write_flush, - synchronize, - out_open, - out_abort, - out_close, - none_poll, - check_host_error - }; - - - pm_fns_node pm_synth_dictionary = { - write_short_synth, - begin_sysex_synth, - end_sysex_synth, - write_byte_synth, - write_realtime_synth, - write_flush, - synchronize, - synth_open, - synth_abort, - synth_close, - none_poll, - check_host_error - }; - - - PmError create_virtual(int is_input, const char *name, void *driverInfo) - { - BMidiEndpoint *endpoint = is_input ? (BMidiEndpoint*)new BMidiLocalProducer(name) : new BMidiLocalConsumer(name); - if (!endpoint->IsValid()) { - endpoint->Release(); - strcpy(pm_hosterror_text, "Endpoint could not be created"); - pm_hosterror = TRUE; - return pmHostError; - } - status_t status = endpoint->Register(); - if (status != B_OK) { - endpoint->Release(); - strcpy(pm_hosterror_text, strerror(status)); - pm_hosterror = TRUE; - return pmHostError; - } - return pm_add_device(const_cast<char*>("Haiku MIDI kit"), name, is_input, TRUE, (void*)(intptr_t)endpoint->ID(), is_input ? &pm_in_dictionary : &pm_out_dictionary); - } - - PmError delete_virtual(PmDeviceID id) - { - int32 endpointID = (int32)(intptr_t)pm_descriptors[id].descriptor; - BMidiEndpoint *endpoint = BMidiRoster::FindEndpoint(endpointID); - //TODO: handle connected producers and consumers - status_t status = endpoint->Unregister(); - // release twice to actually free the endpoint (FindEndpoint increases the ref-count) - endpoint->Release(); - endpoint->Release(); - if (status != B_OK) { - strcpy(pm_hosterror_text, strerror(status)); - pm_hosterror = TRUE; - return pmHostError; - } - return pmNoError; - } -} - -extern "C" { - void pm_init() - { - pm_add_interf(const_cast<char*>("Haiku MIDI kit"), create_virtual, delete_virtual); - - pm_add_device(const_cast<char*>("Haiku MIDI kit"), "Soft Synth", FALSE, FALSE, NULL, &pm_synth_dictionary); - - int32 id = 0; - BMidiEndpoint *endpoint; - - while ((endpoint = BMidiRoster::NextEndpoint(&id)) != NULL) { - bool isInput = endpoint->IsProducer(); - pm_add_device(const_cast<char*>("Haiku MIDI kit"), endpoint->Name(), isInput, FALSE, (void*)(intptr_t)id, isInput ? &pm_in_dictionary : &pm_out_dictionary); - endpoint->Release(); - } - } - - - void pm_term() - { - int i; - for (i = 0; i < pm_descriptor_len; i++) { - PmInternal *midi = pm_descriptors[i].pm_internal; - if (midi && midi->api_info) { - // device is still open, close it - (*midi->dictionary->close)(midi); - } - if (pm_descriptors[i].pub.is_virtual && !pm_descriptors[i].deleted) { - delete_virtual(i); - } - } - } - - - PmDeviceID Pm_GetDefaultInputDeviceID() - { - Pm_Initialize(); - return pm_default_input_device_id; - } - - - PmDeviceID Pm_GetDefaultOutputDeviceID() - { - Pm_Initialize(); - return pm_default_output_device_id; - } - - - void *pm_alloc(size_t s) - { - return malloc(s); - } - - - void pm_free(void *ptr) - { - free(ptr); - } -} diff --git a/portmidi/pm_java/CMakeLists.txt b/portmidi/pm_java/CMakeLists.txt deleted file mode 100644 index 55a20f4..0000000 --- a/portmidi/pm_java/CMakeLists.txt +++ /dev/null @@ -1,56 +0,0 @@ -# pm_java/CMakeLists.txt -- builds pmjni - -find_package(Java) -message(STATUS "Java_JAVA_EXECUTABLE is " ${Java_JAVA_EXECUTABLE}) - -# Build pmjni -# this CMakeLists.txt is only loaded if BUILD_JAVA_NATIVE_INTERFACE -# This jni library includes portmidi sources to give just -# one library for JPortMidi users to manage rather than two. -if(UNIX) - include(FindJNI) - # message(STATUS "JAVA_JVM_LIB_PATH is " ${JAVA_JVM_LIB_PATH}) - # message(STATUS "JAVA_INCLUDE_PATH is " ${JAVA_INCLUDE_PATH}) - # note: should use JAVA_JVM_LIB_PATH, but it is not set properly - # note: user might need to set JAVA_INCLUDE_PATH manually - # - # this will probably break on BSD and other Unix systems; the fix - # depends on whether FindJNI can find Java or not. If yes, then - # we should try to rely on automatically set JAVA_INCLUDE_PATH and - # JAVA_INCLUDE_PATH2; if no, then we need to make both JAVA_INCLUDE_PATH - # and JAVA_INCLUDE_PATH2 set by user (will need clear documentation - # because JAVA_INCLUDE_PATH2 is pretty obscure) - set(JAVA_INCLUDE_PATH ${JAVA_INCLUDE_PATH-UNKNOWN} - CACHE STRING "where to find Java SDK include directory") - # libjvm.so is found relative to JAVA_INCLUDE_PATH: - if (HAIKU) - set(JAVA_INCLUDE_PATHS ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH}/haiku) - else() - set(JAVA_INCLUDE_PATHS ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH}/linux) - endif() -elseif(WIN32) - include(FindJNI) - # note: should use JAVA_JVM_LIB_PATH, but it is not set properly - set(JAVAVM_LIB ${JAVA_INCLUDE_PATH}/../lib/jvm.lib) - - set(JAVA_INCLUDE_PATHS ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2}) - # message(STATUS "JAVA_INCLUDE_PATHS: " ${JAVA_INCLUDE_PATHS}) - # message(STATUS "JAVAVM_LIB: " ${JAVAVM_LIB}) -endif() - -add_library(pmjni SHARED pmjni/pmjni.c) -target_sources(pmjni PRIVATE ${PM_LIB_PUBLIC_SRC} ${PM_LIB_PRIVATE_SRC}) -message(STATUS "Java paths ${JAVA_INCLUDE_PATHS}") -# message(STATUS "Java pmjni src: pmjni/pmjni.c ${PM_LIB_SHARED_SRC} " -# "${PM_LIB_PRIVATE_SRC}") -target_include_directories(pmjni PUBLIC ${JAVA_INCLUDE_PATHS}) -target_link_libraries(pmjni ${PM_NEEDED_LIBS}) -set_target_properties(pmjni PROPERTIES - VERSION ${LIBRARY_VERSION} - SOVERSION ${LIBRARY_SOVERSION} - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" - ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" - EXECUTABLE_EXTENSION "jnilib" - MACOSX_RPATH ON) - diff --git a/portmidi/pm_java/README.txt b/portmidi/pm_java/README.txt deleted file mode 100644 index d1e5ad5..0000000 --- a/portmidi/pm_java/README.txt +++ /dev/null @@ -1,62 +0,0 @@ -README.txt
-Roger B. Dannenberg
-16 Jun 2009
-updated 2021
-
-This directory implements a JNI library so that Java programs can use
-the PortMidi API. This was mainly created to implement PmDefaults, a
-program to set default input and output devices for PortMidi
-applications. Because it is rarely used, PmDefaults was dropped from
-PortMidi starting with v3. I recommend you implement per-application
-preferences and store default PortMidi device numbers for input and
-output there. (Or better yet, store device *names* since numbers can
-change if you plug in or remove USB devices.)
-
-Even without PmDefaults, a PortMidi API for Java is probably an
-improvement over other Java libraries, but there is very little MIDI
-development in Java, so I have not maintained this API. The only thing
-probably seriously wrong now is an interface to the
-Pm_CreateVirtualInput and Pm_CreateVirtualOutput functions, which are
-new additions.
-
-I will leave the code here, and if there is a demand, please either
-update it or let your needs be known. Perhaps I or someone can help.
-
-==================================================================
-
-BUILDING Java EXTERNAL LIBRARY
-
-You must have a JDK installed (Java development kit including javac
-(the Java compiler), jni.h, etc.
-
-Test java on the command line, e.g., type: javac -version
-
-Enable these options in the main CMakeLists.txt file (run CMake
-from your top-level repository directory):
- BUILD_JAVA_NATIVE_INTERFACE
-In my Ubuntu linux with jdk-15, ccmake was unable to find my JDK, so
-I have to manually set CMake variables as follows (type 't' to see
-these in ccmake):
- JAVA_AWT_INCLUDE_PATH /usr/lib/jvm/jdk-15/include
- JAVA_AWT_LIBRARY /usr/lib/jvm/jdk-15/lib
- JAVA_INCLUDE_PATH /usr/lib/jvm/jdk-15/include
- JAVA_INCLUDE_PATH2 /usr/lib/jvm/jdk-15/include
- JAVA_JVM_LIBRARY /usr/lib/jvm/jdk-15/lib
-Of course, your paths may differ.
-
-
----- old implementation notes ----
-
-For Windows, we use the free software JavaExe.exe. The copy here was
-downloaded from
-
-http://software.techrepublic.com.com/abstract.aspx?kw=javaexe&docid=767485
-
-I found this page by visiting http://software.techrepublic.com.com and
-searching in the "Software" category for "JavaExe"
-
-JavaExe works by placing the JavaExe.exe file in the directory with the
-Java application jar file and then *renaming* JavaExe.exe to the name
-of the jar file, but keeping the .exe extension. (See make.bat for this
-step.) Documentation for JavaExe can be obtained by downloading the
-whole program from the URL(s) above.
diff --git a/portmidi/pm_java/jportmidi/JPortMidi.java b/portmidi/pm_java/jportmidi/JPortMidi.java deleted file mode 100644 index 7116e19..0000000 --- a/portmidi/pm_java/jportmidi/JPortMidi.java +++ /dev/null @@ -1,541 +0,0 @@ -package jportmidi; - -/* PortMidi is a general class intended for any Java program using - the PortMidi library. It encapsulates JPortMidiApi with a more - object-oriented interface. A single PortMidi object can manage - up to one input stream and one output stream. - - This class is not safely callable from multiple threads. It - is the client's responsibility to periodically call the Poll - method which checks for midi input and handles it. -*/ - -import jportmidi.*; -import jportmidi.JPortMidiApi.*; - -public class JPortMidi { - - // timecode to send message immediately - public final int NOW = 0; - - // midi codes - public final int MIDI_NOTE_OFF = 0x80; - public final int MIDI_NOTE_ON = 0x90; - public final int CTRL_ALL_OFF = 123; - public final int MIDI_PITCH_BEND = 0xE0; - public final int MIDI_CLOCK = 0xF8; - public final int MIDI_CONTROL = 0xB0; - public final int MIDI_PROGRAM = 0xC0; - public final int MIDI_START = 0xFA; - public final int MIDI_STOP = 0xFC; - public final int MIDI_POLY_TOUCH = 0xA0; - public final int MIDI_TOUCH = 0xD0; - - // error code -- cannot refresh device list while stream is open: - public final int pmStreamOpen = -5000; - public final int pmOutputNotOpen = -4999; - - // access to JPortMidiApi is through a single, global instance - private static JPortMidiApi pm; - // a reference count tracks how many objects have it open - private static int pmRefCount = 0; - private static int openCount = 0; - - public int error; // user can check here for error codes - private PortMidiStream input; - private PortMidiStream output; - private PmEvent buffer; - protected int timestamp; // remember timestamp from incoming messages - protected boolean trace = false; // used to print midi msgs for debugging - - - public JPortMidi() throws JPortMidiException { - if (pmRefCount == 0) { - pm = new JPortMidiApi(); - pmRefCount++; - System.out.println("Calling Pm_Initialize"); - checkError(pm.Pm_Initialize()); - System.out.println("Called Pm_Initialize"); - } - buffer = new PmEvent(); - } - - public boolean getTrace() { return trace; } - - // set the trace flag and return previous value - public boolean setTrace(boolean flag) { - boolean previous = trace; - trace = flag; - return previous; - } - - // WARNING: you must not call this if any devices are open - public void refreshDeviceLists() - throws JPortMidiException - { - if (openCount > 0) { - throw new JPortMidiException(pmStreamOpen, - "RefreshDeviceLists called while stream is open"); - } - if (trace) System.out.println("Pm_Terminate"); - checkError(pm.Pm_Terminate()); - if (trace) System.out.println("Pm_Initialize"); - checkError(pm.Pm_Initialize()); - } - - // there is no control over when/whether this is called, but it seems - // to be a good idea to close things when this object is collected - public void finalize() { - if (input != null) { - error = pm.Pm_Close(input); - } - if (input != null) { - int rslt = pm.Pm_Close(output); - // we may lose an error code from closing output, but don't - // lose any real error from closing input... - if (error == pm.pmNoError) error = rslt; - } - pmRefCount--; - if (pmRefCount == 0) { - error = pm.Pm_Terminate(); - } - } - - int checkError(int err) throws JPortMidiException - { - // note that Pm_Read and Pm_Write return positive result values - // which are not errors, so compare with >= - if (err >= pm.pmNoError) return err; - if (err == pm.pmHostError) { - throw new JPortMidiException(err, pm.Pm_GetHostErrorText()); - } else { - throw new JPortMidiException(err, pm.Pm_GetErrorText(err)); - } - } - - // ******** ACCESS TO TIME *********** - - public void timeStart(int resolution) throws JPortMidiException { - checkError(pm.Pt_TimeStart(resolution)); - } - - public void timeStop() throws JPortMidiException { - checkError(pm.Pt_TimeStop()); - } - - public int timeGet() { - return pm.Pt_Time(); - } - - public boolean timeStarted() { - return pm.Pt_TimeStarted(); - } - - // ******* QUERY DEVICE INFORMATION ********* - - public int countDevices() throws JPortMidiException { - return checkError(pm.Pm_CountDevices()); - } - - public int getDefaultInputDeviceID() throws JPortMidiException { - return checkError(pm.Pm_GetDefaultInputDeviceID()); - } - - public int getDefaultOutputDeviceID() throws JPortMidiException { - return checkError(pm.Pm_GetDefaultOutputDeviceID()); - } - - public String getDeviceInterf(int i) { - return pm.Pm_GetDeviceInterf(i); - } - - public String getDeviceName(int i) { - return pm.Pm_GetDeviceName(i); - } - - public boolean getDeviceInput(int i) { - return pm.Pm_GetDeviceInput(i); - } - - public boolean getDeviceOutput(int i) { - return pm.Pm_GetDeviceOutput(i); - } - - // ********** MIDI INTERFACE ************ - - public boolean isOpenInput() { - return input != null; - } - - public void openInput(int inputDevice, int bufferSize) - throws JPortMidiException - { - openInput(inputDevice, "", bufferSize); - } - - public void openInput(int inputDevice, String inputDriverInfo, int bufferSize) - throws JPortMidiException - { - if (isOpenInput()) pm.Pm_Close(input); - else input = new PortMidiStream(); - if (trace) { - System.out.println("openInput " + getDeviceName(inputDevice)); - } - checkError(pm.Pm_OpenInput(input, inputDevice, - inputDriverInfo, bufferSize)); - // if no exception, then increase count of open streams - openCount++; - } - - public boolean isOpenOutput() { - return output != null; - } - - public void openOutput(int outputDevice, int bufferSize, int latency) - throws JPortMidiException - { - openOutput(outputDevice, "", bufferSize, latency); - } - - public void openOutput(int outputDevice, String outputDriverInfo, - int bufferSize, int latency) throws JPortMidiException { - if (isOpenOutput()) pm.Pm_Close(output); - else output = new PortMidiStream(); - if (trace) { - System.out.println("openOutput " + getDeviceName(outputDevice)); - } - checkError(pm.Pm_OpenOutput(output, outputDevice, outputDriverInfo, - bufferSize, latency)); - // if no exception, then increase count of open streams - openCount++; - } - - public void setFilter(int filters) throws JPortMidiException { - if (input == null) return; // no effect if input not open - checkError(pm.Pm_SetFilter(input, filters)); - } - - public void setChannelMask(int mask) throws JPortMidiException { - if (input == null) return; // no effect if input not open - checkError(pm.Pm_SetChannelMask(input, mask)); - } - - public void abort() throws JPortMidiException { - if (output == null) return; // no effect if output not open - checkError(pm.Pm_Abort(output)); - } - - // In keeping with the idea that this class represents an input and output, - // there are separate Close methods for input and output streams, avoiding - // the need for clients to ever deal directly with a stream object - public void closeInput() throws JPortMidiException { - if (input == null) return; // no effect if input not open - checkError(pm.Pm_Close(input)); - input = null; - openCount--; - } - - public void closeOutput() throws JPortMidiException { - if (output == null) return; // no effect if output not open - checkError(pm.Pm_Close(output)); - output = null; - openCount--; - } - - // Poll should be called by client to process input messages (if any) - public void poll() throws JPortMidiException { - if (input == null) return; // does nothing until input is opened - while (true) { - int rslt = pm.Pm_Read(input, buffer); - checkError(rslt); - if (rslt == 0) return; // no more messages - handleMidiIn(buffer); - } - } - - public void writeShort(int when, int msg) throws JPortMidiException { - if (output == null) - throw new JPortMidiException(pmOutputNotOpen, - "Output stream not open"); - if (trace) { - System.out.println("writeShort: " + Integer.toHexString(msg)); - } - checkError(pm.Pm_WriteShort(output, when, msg)); - } - - public void writeSysEx(int when, byte msg[]) throws JPortMidiException { - if (output == null) - throw new JPortMidiException(pmOutputNotOpen, - "Output stream not open"); - if (trace) { - System.out.print("writeSysEx: "); - for (int i = 0; i < msg.length; i++) { - System.out.print(Integer.toHexString(msg[i])); - } - System.out.print("\n"); - } - checkError(pm.Pm_WriteSysEx(output, when, msg)); - } - - public int midiChanMessage(int chan, int status, int data1, int data2) { - return (((data2 << 16) & 0xFF0000) | - ((data1 << 8) & 0xFF00) | - (status & 0xF0) | - (chan & 0xF)); - } - - public int midiMessage(int status, int data1, int data2) { - return ((((data2) << 16) & 0xFF0000) | - (((data1) << 8) & 0xFF00) | - ((status) & 0xFF)); - } - - public void midiAllOff(int channel) throws JPortMidiException { - midiAllOff(channel, NOW); - } - - public void midiAllOff(int chan, int when) throws JPortMidiException { - writeShort(when, midiChanMessage(chan, MIDI_CONTROL, CTRL_ALL_OFF, 0)); - } - - public void midiPitchBend(int chan, int value) throws JPortMidiException { - midiPitchBend(chan, value, NOW); - } - - public void midiPitchBend(int chan, int value, int when) - throws JPortMidiException { - writeShort(when, - midiChanMessage(chan, MIDI_PITCH_BEND, value, value >> 7)); - } - - public void midiClock() throws JPortMidiException { - midiClock(NOW); - } - - public void midiClock(int when) throws JPortMidiException { - writeShort(when, midiMessage(MIDI_CLOCK, 0, 0)); - } - - public void midiControl(int chan, int control, int value) - throws JPortMidiException { - midiControl(chan, control, value, NOW); - } - - public void midiControl(int chan, int control, int value, int when) - throws JPortMidiException { - writeShort(when, midiChanMessage(chan, MIDI_CONTROL, control, value)); - } - - public void midiNote(int chan, int pitch, int vel) - throws JPortMidiException { - midiNote(chan, pitch, vel, NOW); - } - - public void midiNote(int chan, int pitch, int vel, int when) - throws JPortMidiException { - writeShort(when, midiChanMessage(chan, MIDI_NOTE_ON, pitch, vel)); - } - - public void midiProgram(int chan, int program) - throws JPortMidiException { - midiProgram(chan, program, NOW); - } - - public void midiProgram(int chan, int program, int when) - throws JPortMidiException { - writeShort(when, midiChanMessage(chan, MIDI_PROGRAM, program, 0)); - } - - public void midiStart() - throws JPortMidiException { - midiStart(NOW); - } - - public void midiStart(int when) - throws JPortMidiException { - writeShort(when, midiMessage(MIDI_START, 0, 0)); - } - - public void midiStop() - throws JPortMidiException { - midiStop(NOW); - } - - public void midiStop(int when) - throws JPortMidiException { - writeShort(when, midiMessage(MIDI_STOP, 0, 0)); - } - - public void midiPolyTouch(int chan, int key, int value) - throws JPortMidiException { - midiPolyTouch(chan, key, value, NOW); - } - - public void midiPolyTouch(int chan, int key, int value, int when) - throws JPortMidiException { - writeShort(when, midiChanMessage(chan, MIDI_POLY_TOUCH, key, value)); - } - - public void midiTouch(int chan, int value) - throws JPortMidiException { - midiTouch(chan, value, NOW); - } - - public void midiTouch(int chan, int value, int when) - throws JPortMidiException { - writeShort(when, midiChanMessage(chan, MIDI_TOUCH, value, 0)); - } - - // ****** now we implement the message handlers ****** - - // an array for incoming sysex messages that can grow. - // The downside is that after getting a message, we - - private byte sysexBuffer[] = null; - private int sysexBufferIndex = 0; - - void sysexBufferReset() { - sysexBufferIndex = 0; - if (sysexBuffer == null) sysexBuffer = new byte[256]; - } - - void sysexBufferCheck() { - if (sysexBuffer.length < sysexBufferIndex + 4) { - byte bigger[] = new byte[sysexBuffer.length * 2]; - for (int i = 0; i < sysexBufferIndex; i++) { - bigger[i] = sysexBuffer[i]; - } - sysexBuffer = bigger; - } - // now we have space to write some bytes - } - - // call this to insert Sysex and EOX status bytes - // call sysexBufferAppendBytes to insert anything else - void sysexBufferAppendStatus(byte status) { - sysexBuffer[sysexBufferIndex++] = status; - } - - void sysexBufferAppendBytes(int msg, int len) { - for (int i = 0; i < len; i++) { - byte b = (byte) msg; - if ((msg & 0x80) != 0) { - if (b == 0xF7) { // end of sysex - sysexBufferAppendStatus(b); - sysex(sysexBuffer, sysexBufferIndex); - return; - } - // recursively handle embedded real-time messages - PmEvent buffer = new PmEvent(); - buffer.timestamp = timestamp; - buffer.message = b; - handleMidiIn(buffer); - } else { - sysexBuffer[sysexBufferIndex++] = b; - } - msg = msg >> 8; - } - } - - void sysexBegin(int msg) { - sysexBufferReset(); // start from 0, we have at least 256 bytes now - sysexBufferAppendStatus((byte) (msg & 0xFF)); // first byte is special - sysexBufferAppendBytes(msg >> 8, 3); // process remaining bytes - } - - public void handleMidiIn(PmEvent buffer) - { - if (trace) { - System.out.println("handleMidiIn: " + - Integer.toHexString(buffer.message)); - } - // rather than pass timestamps to every handler, where typically - // timestamps are ignored, just save the timestamp as a member - // variable where methods can access it if they want it - timestamp = buffer.timestamp; - int status = buffer.message & 0xFF; - if (status < 0x80) { - sysexBufferCheck(); // make enough space - sysexBufferAppendBytes(buffer.message, 4); // process 4 bytes - return; - } - int command = status & 0xF0; - int channel = status & 0x0F; - int data1 = (buffer.message >> 8) & 0xFF; - int data2 = (buffer.message >> 16) & 0xFF; - switch (command) { - case MIDI_NOTE_OFF: - noteOff(channel, data1, data2); break; - case MIDI_NOTE_ON: - if (data2 > 0) { - noteOn(channel, data1, data2); break; - } else { - noteOff(channel, data1); - } - break; - case MIDI_CONTROL: - control(channel, data1, data2); break; - case MIDI_POLY_TOUCH: - polyTouch(channel, data1, data2); break; - case MIDI_TOUCH: - touch(channel, data1); break; - case MIDI_PITCH_BEND: - pitchBend(channel, (data1 + (data2 << 7)) - 8192); break; - case MIDI_PROGRAM: - program(channel, data1); break; - case 0xF0: - switch (channel) { - case 0: sysexBegin(buffer.message); break; - case 1: mtcQuarterFrame(data1); - case 2: songPosition(data1 + (data2 << 7)); break; - case 3: songSelect(data1); break; - case 4: /* unused */ break; - case 5: /* unused */ break; - case 6: tuneRequest(); break; - case 7: sysexBufferAppendBytes(buffer.message, buffer.message); break; - case 8: clock(); break; - case 9: tick(); break; - case 0xA: clockStart(); break; - case 0xB: clockContinue(); break; - case 0xC: clockStop(); break; - case 0xD: /* unused */ break; - case 0xE: activeSense(); break; - case 0xF: reset(); break; - } - } - } - - // the value ranges from +8181 to -8192. The interpretation is - // synthesizer dependent. Often the range is +/- one whole step - // (two semitones), but the range is usually adjustable within - // the synthesizer. - void pitchBend(int channel, int value) { return; } - void control(int channel, int control, int value) { return; } - void noteOn(int channel, int pitch, int velocity) { return; } - // you can handle velocity in note-off if you want, but the default - // is to drop the velocity and call the simpler NoteOff handler - void noteOff(int channel, int pitch, int velocity) { - noteOff(channel, pitch); - } - // if the subclass wants to implement NoteOff with velocity, it - // should override the following to make sure all NoteOffs are handled - void noteOff(int channel, int pitch) { return; } - void program(int channel, int program) { return; } - // the byte array may be bigger than the message, length tells how - // many bytes in the array are part of the message - void sysex(byte[] msg, int length) { return; } - void polyTouch(int channel, int key, int value) { return; } - void touch(int channel, int value) { return; } - void mtcQuarterFrame(int value) { return; } - // the value is a 14-bit integer representing 16th notes - void songPosition(int value) { return; } - void songSelect(int value) { return; } - void tuneRequest() { return; } - void clock() { return; } // represents 1/24th of a quarter note - void tick() { return; } // represents 10ms - void clockStart() { return; } - void clockStop() { return; } - void clockContinue() { return; } - void activeSense() { return; } - void reset() { return; } -} diff --git a/portmidi/pm_java/jportmidi/JPortMidiApi.java b/portmidi/pm_java/jportmidi/JPortMidiApi.java deleted file mode 100644 index 45dd9d9..0000000 --- a/portmidi/pm_java/jportmidi/JPortMidiApi.java +++ /dev/null @@ -1,117 +0,0 @@ -package jportmidi; - -public class JPortMidiApi { - public static class PortMidiStream { - private long address; - } - public static class PmEvent { - public int message; - public int timestamp; - } - - // PmError bindings - public final int pmNoError = 0; - public final int pmNoData = 0; - public final int pmGotData = -1; - public final int pmHostError = -10000; - public final int pmInvalidDeviceId = -9999; - public final int pmInsufficientMemory = -9998; - public final int pmBufferTooSmall = -9997; - public final int pmBufferOverflow = -9996; - public final int pmBadPtr = -9995; - public final int pmBadData = -9994; - public final int pmInternalError = -9993; - public final int pmBufferMaxSize = -9992; - - static public native int Pm_Initialize(); - static public native int Pm_Terminate(); - static public native int Pm_HasHostError(PortMidiStream stream); - static public native String Pm_GetErrorText(int errnum); - static public native String Pm_GetHostErrorText(); - final int pmNoDevice = -1; - static public native int Pm_CountDevices(); - static public native int Pm_GetDefaultInputDeviceID(); - static public native int Pm_GetDefaultOutputDeviceID(); - static public native String Pm_GetDeviceInterf(int i); - static public native String Pm_GetDeviceName(int i); - static public native boolean Pm_GetDeviceInput(int i); - static public native boolean Pm_GetDeviceOutput(int i); - static public native int Pm_OpenInput(PortMidiStream stream, - int inputDevice, - String inputDriverInfo, - int bufferSize); - static public native int Pm_OpenOutput(PortMidiStream stream, - int outputDevice, - String outnputDriverInfo, - int bufferSize, - int latency); - final static public int PM_FILT_ACTIVE = (1 << 0x0E); - final static public int PM_FILT_SYSEX = (1 << 0x00); - final static public int PM_FILT_CLOCK = (1 << 0x08); - final static public int PM_FILT_PLAY = - (1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B); - final static public int PM_FILT_TICK = (1 << 0x09); - final static public int PM_FILT_FD = (1 << 0x0D); - final static public int PM_FILT_UNDEFINED = PM_FILT_FD; - final static public int PM_FILT_RESET = (1 << 0x0F); - final static public int PM_FILT_REALTIME = - PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK; - final static public int PM_FILT_NOTE = (1 << 0x19) | (1 << 0x18); - final static public int PM_FILT_CHANNEL_AFTERTOUCH = (1 << 0x1D); - final static public int PM_FILT_POLY_AFTERTOUCH = (1 << 0x1A); - final static public int PM_FILT_AFTERTOUCH = - (PM_FILT_CHANNEL_AFTERTOUCH | PM_FILT_POLY_AFTERTOUCH); - final static public int PM_FILT_PROGRAM = (1 << 0x1C); - final static public int PM_FILT_CONTROL = (1 << 0x1B); - final static public int PM_FILT_PITCHBEND = (1 << 0x1E); - final static public int PM_FILT_MTC = (1 << 0x01); - final static public int PM_FILT_SONG_POSITION = (1 << 0x02); - final static public int PM_FILT_SONG_SELECT = (1 << 0x03); - final static public int PM_FILT_TUNE = (1 << 0x06); - final static public int PM_FILT_SYSTEMCOMMON = - (PM_FILT_MTC | PM_FILT_SONG_POSITION | - PM_FILT_SONG_SELECT | PM_FILT_TUNE); - static public native int Pm_SetFilter(PortMidiStream stream, int filters); - static public int Pm_Channel(int channel) { return 1 << channel; } - final static public native int Pm_SetChannelMask(PortMidiStream stream, - int mask); - final static public native int Pm_Abort(PortMidiStream stream); - final static public native int Pm_Close(PortMidiStream stream); - static public int Pm_Message(int status, int data1, int data2) { - return (((data2 << 16) & 0xFF0000) | - ((data1 << 8) & 0xFF00) | - (status & 0xFF)); - } - static public int Pm_MessageStatus(int msg) { - return msg & 0xFF; - } - static public int Pm_MessageData1(int msg) { - return (msg >> 8) & 0xFF; - } - static public int Pm_MessageData2(int msg) { - return (msg >> 16) & 0xFF; - } - // only supports reading one buffer at a time - static public native int Pm_Read(PortMidiStream stream, PmEvent buffer); - static public native int Pm_Poll(PortMidiStream stream); - // only supports writing one buffer at a time - static public native int Pm_Write(PortMidiStream stream, PmEvent buffer); - static public native int Pm_WriteShort(PortMidiStream stream, - int when, int msg); - static public native int Pm_WriteSysEx(PortMidiStream stream, - int when, byte msg[]); - - public final int ptNoError = 0; - public final int ptAlreadyStarted = -10000; - public final int ptAlreadyStopped = -9999; - public final int PtInsufficientMemory = -9998; - static public native int Pt_TimeStart(int resolution); - static public native int Pt_TimeStop(); - static public native int Pt_Time(); - static public native boolean Pt_TimeStarted(); - static { - System.out.println("Loading pmjni"); - System.loadLibrary("pmjni"); - System.out.println("done loading pmjni"); - } -} diff --git a/portmidi/pm_java/jportmidi/JPortMidiException.java b/portmidi/pm_java/jportmidi/JPortMidiException.java deleted file mode 100644 index 9be8aaf..0000000 --- a/portmidi/pm_java/jportmidi/JPortMidiException.java +++ /dev/null @@ -1,12 +0,0 @@ -// JPortMidiException -- thrown by JPortMidi methods - -package jportmidi; - -public class JPortMidiException extends Exception { - public int error = 0; - public JPortMidiException(int err, String msg) { - super(msg); - error = err; - } -} - diff --git a/portmidi/pm_java/make.bat b/portmidi/pm_java/make.bat deleted file mode 100644 index ff15c2b..0000000 --- a/portmidi/pm_java/make.bat +++ /dev/null @@ -1,50 +0,0 @@ -@echo off
-
-rem This is an out-of-date script for Windows to build a Java application
-rem (PmDefaults) with PortMidi external library.xb
-
-rem Compile the java PortMidi interface classes.
-javac jportmidi/*.java
-
-rem Compile the pmdefaults application.
-javac -classpath . pmdefaults/*.java
-
-rem Temporarily copy the portmusic_logo.png file here to add to the jar file.
-copy pmdefaults\portmusic_logo.png . > nul
-
-rem Create a directory to hold the distribution.
-mkdir win32
-
-rem Attempt to copy the interface DLL to the distribution directory.
-
-if exist "..\release\pmjni.dll" goto have-dll
-
-echo "ERROR: pmjni.dll not found!"
-exit /b 1
-
-:have-dll
-copy "..\release\pmjni.dll" win32\pmjni.dll > nul
-
-rem Create a java archive (jar) file of the distribution.
-jar cmf pmdefaults\manifest.txt win32\pmdefaults.jar pmdefaults\*.class portmusic_logo.png jportmidi\*.class
-
-rem Clean up the temporary image file now that it is in the jar file.
-del portmusic_logo.png
-
-rem Copy the java execution code obtained from
-rem http://devwizard.free.fr/html/en/JavaExe.html to the distribution
-rem directory. The copy also renames the file to our desired executable
-rem name.
-copy JavaExe.exe win32\pmdefaults.exe > nul
-
-rem Integrate the icon into the executable using UpdateRsrcJavaExe from
-rem http://devwizard.free.fr
-UpdateRsrcJavaExe -run -exe=win32\pmdefaults.exe -ico=pmdefaults\pmdefaults.ico
-
-rem Copy the 32-bit windows read me file to the distribution directory.
-copy pmdefaults\readme-win32.txt win32\README.txt > nul
-
-rem Copy the license file to the distribution directory.
-copy pmdefaults\pmdefaults-license.txt win32\license.txt > nul
-
-echo "You can run pmdefaults.exe in win32"
diff --git a/portmidi/pm_java/pmjni/jportmidi_JportMidiApi.h b/portmidi/pm_java/pmjni/jportmidi_JportMidiApi.h deleted file mode 100644 index 2208be6..0000000 --- a/portmidi/pm_java/pmjni/jportmidi_JportMidiApi.h +++ /dev/null @@ -1,293 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include <jni.h> -/* Header for class jportmidi_JPortMidiApi */ - -#ifndef _Included_jportmidi_JPortMidiApi -#define _Included_jportmidi_JPortMidiApi -#ifdef __cplusplus -extern "C" { -#endif -#undef jportmidi_JPortMidiApi_PM_FILT_ACTIVE -#define jportmidi_JPortMidiApi_PM_FILT_ACTIVE 16384L -#undef jportmidi_JPortMidiApi_PM_FILT_SYSEX -#define jportmidi_JPortMidiApi_PM_FILT_SYSEX 1L -#undef jportmidi_JPortMidiApi_PM_FILT_CLOCK -#define jportmidi_JPortMidiApi_PM_FILT_CLOCK 256L -#undef jportmidi_JPortMidiApi_PM_FILT_PLAY -#define jportmidi_JPortMidiApi_PM_FILT_PLAY 7168L -#undef jportmidi_JPortMidiApi_PM_FILT_TICK -#define jportmidi_JPortMidiApi_PM_FILT_TICK 512L -#undef jportmidi_JPortMidiApi_PM_FILT_FD -#define jportmidi_JPortMidiApi_PM_FILT_FD 8192L -#undef jportmidi_JPortMidiApi_PM_FILT_UNDEFINED -#define jportmidi_JPortMidiApi_PM_FILT_UNDEFINED 8192L -#undef jportmidi_JPortMidiApi_PM_FILT_RESET -#define jportmidi_JPortMidiApi_PM_FILT_RESET 32768L -#undef jportmidi_JPortMidiApi_PM_FILT_REALTIME -#define jportmidi_JPortMidiApi_PM_FILT_REALTIME 16641L -#undef jportmidi_JPortMidiApi_PM_FILT_NOTE -#define jportmidi_JPortMidiApi_PM_FILT_NOTE 50331648L -#undef jportmidi_JPortMidiApi_PM_FILT_CHANNEL_AFTERTOUCH -#define jportmidi_JPortMidiApi_PM_FILT_CHANNEL_AFTERTOUCH 536870912L -#undef jportmidi_JPortMidiApi_PM_FILT_POLY_AFTERTOUCH -#define jportmidi_JPortMidiApi_PM_FILT_POLY_AFTERTOUCH 67108864L -#undef jportmidi_JPortMidiApi_PM_FILT_AFTERTOUCH -#define jportmidi_JPortMidiApi_PM_FILT_AFTERTOUCH 603979776L -#undef jportmidi_JPortMidiApi_PM_FILT_PROGRAM -#define jportmidi_JPortMidiApi_PM_FILT_PROGRAM 268435456L -#undef jportmidi_JPortMidiApi_PM_FILT_CONTROL -#define jportmidi_JPortMidiApi_PM_FILT_CONTROL 134217728L -#undef jportmidi_JPortMidiApi_PM_FILT_PITCHBEND -#define jportmidi_JPortMidiApi_PM_FILT_PITCHBEND 1073741824L -#undef jportmidi_JPortMidiApi_PM_FILT_MTC -#define jportmidi_JPortMidiApi_PM_FILT_MTC 2L -#undef jportmidi_JPortMidiApi_PM_FILT_SONG_POSITION -#define jportmidi_JPortMidiApi_PM_FILT_SONG_POSITION 4L -#undef jportmidi_JPortMidiApi_PM_FILT_SONG_SELECT -#define jportmidi_JPortMidiApi_PM_FILT_SONG_SELECT 8L -#undef jportmidi_JPortMidiApi_PM_FILT_TUNE -#define jportmidi_JPortMidiApi_PM_FILT_TUNE 64L -#undef jportmidi_JPortMidiApi_PM_FILT_SYSTEMCOMMON -#define jportmidi_JPortMidiApi_PM_FILT_SYSTEMCOMMON 78L -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_Initialize - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1Initialize - (JNIEnv *, jclass); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_Terminate - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1Terminate - (JNIEnv *, jclass); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_HasHostError - * Signature: (Ljportmidi/JPortMidiApi/PortMidiStream;)I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1HasHostError - (JNIEnv *, jclass, jobject); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_GetErrorText - * Signature: (I)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetErrorText - (JNIEnv *, jclass, jint); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_GetHostErrorText - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetHostErrorText - (JNIEnv *, jclass); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_CountDevices - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1CountDevices - (JNIEnv *, jclass); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_GetDefaultInputDeviceID - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetDefaultInputDeviceID - (JNIEnv *, jclass); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_GetDefaultOutputDeviceID - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetDefaultOutputDeviceID - (JNIEnv *, jclass); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_GetDeviceInterf - * Signature: (I)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetDeviceInterf - (JNIEnv *, jclass, jint); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_GetDeviceName - * Signature: (I)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetDeviceName - (JNIEnv *, jclass, jint); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_GetDeviceInput - * Signature: (I)Z - */ -JNIEXPORT jboolean JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetDeviceInput - (JNIEnv *, jclass, jint); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_GetDeviceOutput - * Signature: (I)Z - */ -JNIEXPORT jboolean JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetDeviceOutput - (JNIEnv *, jclass, jint); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_OpenInput - * Signature: (Ljportmidi/JPortMidiApi/PortMidiStream;ILjava/lang/String;I)I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1OpenInput - (JNIEnv *, jclass, jobject, jint, jstring, jint); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_OpenOutput - * Signature: (Ljportmidi/JPortMidiApi/PortMidiStream;ILjava/lang/String;II)I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1OpenOutput - (JNIEnv *, jclass, jobject, jint, jstring, jint, jint); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_SetFilter - * Signature: (Ljportmidi/JPortMidiApi/PortMidiStream;I)I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1SetFilter - (JNIEnv *, jclass, jobject, jint); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_SetChannelMask - * Signature: (Ljportmidi/JPortMidiApi/PortMidiStream;I)I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1SetChannelMask - (JNIEnv *, jclass, jobject, jint); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_Abort - * Signature: (Ljportmidi/JPortMidiApi/PortMidiStream;)I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1Abort - (JNIEnv *, jclass, jobject); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_Close - * Signature: (Ljportmidi/JPortMidiApi/PortMidiStream;)I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1Close - (JNIEnv *, jclass, jobject); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_Read - * Signature: (Ljportmidi/JPortMidiApi/PortMidiStream;Ljportmidi/JPortMidiApi/PmEvent;)I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1Read - (JNIEnv *, jclass, jobject, jobject); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_Poll - * Signature: (Ljportmidi/JPortMidiApi/PortMidiStream;)I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1Poll - (JNIEnv *, jclass, jobject); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_Write - * Signature: (Ljportmidi/JPortMidiApi/PortMidiStream;Ljportmidi/JPortMidiApi/PmEvent;)I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1Write - (JNIEnv *, jclass, jobject, jobject); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_WriteShort - * Signature: (Ljportmidi/JPortMidiApi/PortMidiStream;II)I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1WriteShort - (JNIEnv *, jclass, jobject, jint, jint); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pm_WriteSysEx - * Signature: (Ljportmidi/JPortMidiApi/PortMidiStream;I[B)I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1WriteSysEx - (JNIEnv *, jclass, jobject, jint, jbyteArray); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pt_TimeStart - * Signature: (I)I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pt_1TimeStart - (JNIEnv *, jclass, jint); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pt_TimeStop - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pt_1TimeStop - (JNIEnv *, jclass); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pt_Time - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pt_1Time - (JNIEnv *, jclass); - -/* - * Class: jportmidi_JPortMidiApi - * Method: Pt_TimeStarted - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL Java_jportmidi_JPortMidiApi_Pt_1TimeStarted - (JNIEnv *, jclass); - -#ifdef __cplusplus -} -#endif -#endif -/* Header for class jportmidi_JPortMidiApi_PmEvent */ - -#ifndef _Included_jportmidi_JPortMidiApi_PmEvent -#define _Included_jportmidi_JPortMidiApi_PmEvent -#ifdef __cplusplus -extern "C" { -#endif -#ifdef __cplusplus -} -#endif -#endif -/* Header for class jportmidi_JPortMidiApi_PortMidiStream */ - -#ifndef _Included_jportmidi_JPortMidiApi_PortMidiStream -#define _Included_jportmidi_JPortMidiApi_PortMidiStream -#ifdef __cplusplus -extern "C" { -#endif -#ifdef __cplusplus -} -#endif -#endif diff --git a/portmidi/pm_java/pmjni/pmjni.c b/portmidi/pm_java/pmjni/pmjni.c deleted file mode 100644 index c60cffb..0000000 --- a/portmidi/pm_java/pmjni/pmjni.c +++ /dev/null @@ -1,354 +0,0 @@ -#include "portmidi.h" -#include "porttime.h" -#include "jportmidi_JportMidiApi.h" -#include <stdio.h> - -// these macros assume JNIEnv *env is declared and valid: -// -#define CLASS(c, obj) jclass c = (*env)->GetObjectClass(env, obj) -#define ADDRESS_FID(fid, c) \ - jfieldID fid = (*env)->GetFieldID(env, c, "address", "J") -// Uses Java Long (64-bit) to make sure there is room to store a -// pointer. Cast this to a C long (either 32 or 64 bit) to match -// the size of a pointer. Finally cast int to pointer. All this -// is supposed to avoid C compiler warnings and (worse) losing -// address bits. -#define PMSTREAM(obj, fid) ((PmStream *) (intptr_t) (*env)->GetLongField(env, obj, fid)) -// Cast stream to long to convert integer to pointer, then expand -// integer to 64-bit jlong. This avoids compiler warnings. -#define SET_PMSTREAM(obj, fid, stream) \ - (*env)->SetLongField(env, obj, fid, (jlong) (intptr_t) stream) - - -/* - * Method: Pm_Initialize - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1Initialize - (JNIEnv *env, jclass cl) -{ - return Pm_Initialize(); -} - - -/* - * Method: Pm_Terminate - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1Terminate - (JNIEnv *env, jclass cl) -{ - return Pm_Terminate(); -} - - -/* - * Method: Pm_HasHostError - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1HasHostError - (JNIEnv *env, jclass cl, jobject jstream) -{ - CLASS(c, jstream); - ADDRESS_FID(fid, c); - return Pm_HasHostError(PMSTREAM(jstream, fid)); -} - - -/* - * Method: Pm_GetErrorText - */ -JNIEXPORT jstring JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetErrorText - (JNIEnv *env, jclass cl, jint i) -{ - return (*env)->NewStringUTF(env, Pm_GetErrorText(i)); -} - - -/* - * Method: Pm_GetHostErrorText - */ -JNIEXPORT jstring JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetHostErrorText - (JNIEnv *env, jclass cl) -{ - char msg[PM_HOST_ERROR_MSG_LEN]; - Pm_GetHostErrorText(msg, PM_HOST_ERROR_MSG_LEN); - return (*env)->NewStringUTF(env, msg); -} - - -/* - * Method: Pm_CountDevices - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1CountDevices - (JNIEnv *env, jclass cl) -{ - return Pm_CountDevices(); -} - - -/* - * Method: Pm_GetDefaultInputDeviceID - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetDefaultInputDeviceID - (JNIEnv *env, jclass cl) -{ - return Pm_GetDefaultInputDeviceID(); -} - - -/* - * Method: Pm_GetDefaultOutputDeviceID - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetDefaultOutputDeviceID - (JNIEnv *env, jclass cl) -{ - return Pm_GetDefaultOutputDeviceID(); -} - - -/* - * Method: Pm_GetDeviceInterf - */ -JNIEXPORT jstring JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetDeviceInterf - (JNIEnv *env, jclass cl, jint i) -{ - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - if (!info) return NULL; - return (*env)->NewStringUTF(env, info->interf); -} - - -/* - * Method: Pm_GetDeviceName - */ -JNIEXPORT jstring JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetDeviceName - (JNIEnv *env, jclass cl, jint i) -{ - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - if (!info) return NULL; - return (*env)->NewStringUTF(env, info->name); -} - - -/* - * Method: Pm_GetDeviceInput - */ -JNIEXPORT jboolean JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetDeviceInput - (JNIEnv *env, jclass cl, jint i) -{ - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - if (!info) return (jboolean) 0; - return (jboolean) info->input; -} - - -/* - * Method: Pm_GetDeviceOutput - */ -JNIEXPORT jboolean JNICALL Java_jportmidi_JPortMidiApi_Pm_1GetDeviceOutput - (JNIEnv *env, jclass cl, jint i) -{ - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - if (!info) return (jboolean) 0; - return (jboolean) info->output; -} - - -/* - * Method: Pm_OpenInput - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1OpenInput - (JNIEnv *env, jclass cl, - jobject jstream, jint index, jstring extras, jint bufsiz) -{ - PmError rslt; - PortMidiStream *stream; - CLASS(c, jstream); - ADDRESS_FID(fid, c); - rslt = Pm_OpenInput(&stream, index, NULL, bufsiz, NULL, NULL); - SET_PMSTREAM(jstream, fid, stream); - return rslt; -} - - -/* - * Method: Pm_OpenOutput - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1OpenOutput - (JNIEnv *env, jclass cl, jobject jstream, jint index, jstring extras, - jint bufsiz, jint latency) -{ - PmError rslt; - PortMidiStream *stream; - CLASS(c, jstream); - ADDRESS_FID(fid, c); - rslt = Pm_OpenOutput(&stream, index, NULL, bufsiz, NULL, NULL, latency); - SET_PMSTREAM(jstream, fid, stream); - return rslt; -} - - -/* - * Method: Pm_SetFilter - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1SetFilter - (JNIEnv *env, jclass cl, jobject jstream, jint filters) -{ - CLASS(c, jstream); - ADDRESS_FID(fid, c); - return Pm_SetFilter(PMSTREAM(jstream, fid), filters); -} - - -/* - * Method: Pm_SetChannelMask - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1SetChannelMask - (JNIEnv *env, jclass cl, jobject jstream, jint mask) -{ - CLASS(c, jstream); - ADDRESS_FID(fid, c); - return Pm_SetChannelMask(PMSTREAM(jstream, fid), mask); -} - - -/* - * Method: Pm_Abort - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1Abort - (JNIEnv *env, jclass cl, jobject jstream) -{ - CLASS(c, jstream); - ADDRESS_FID(fid, c); - return Pm_Abort(PMSTREAM(jstream, fid)); -} - - -/* - * Method: Pm_Close - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1Close - (JNIEnv *env, jclass cl, jobject jstream) -{ - CLASS(c, jstream); - ADDRESS_FID(fid, c); - return Pm_Close(PMSTREAM(jstream, fid)); -} - - -/* - * Method: Pm_Read - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1Read - (JNIEnv *env, jclass cl, jobject jstream, jobject jpmevent) -{ - CLASS(jstream_class, jstream); - ADDRESS_FID(address_fid, jstream_class); - jclass jpmevent_class = (*env)->GetObjectClass(env, jpmevent); - jfieldID message_fid = - (*env)->GetFieldID(env, jpmevent_class, "message", "I"); - jfieldID timestamp_fid = - (*env)->GetFieldID(env, jpmevent_class, "timestamp", "I"); - PmEvent buffer; - PmError rslt = Pm_Read(PMSTREAM(jstream, address_fid), &buffer, 1); - (*env)->SetIntField(env, jpmevent, message_fid, buffer.message); - (*env)->SetIntField(env, jpmevent, timestamp_fid, buffer.timestamp); - return rslt; -} - - -/* - * Method: Pm_Poll - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1Poll - (JNIEnv *env, jclass cl, jobject jstream) -{ - CLASS(c, jstream); - ADDRESS_FID(fid, c); - return Pm_Poll(PMSTREAM(jstream, fid)); -} - - -/* - * Method: Pm_Write - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1Write - (JNIEnv *env, jclass cl, jobject jstream, jobject jpmevent) -{ - CLASS(jstream_class, jstream); - ADDRESS_FID(address_fid, jstream_class); - jclass jpmevent_class = (*env)->GetObjectClass(env, jpmevent); - jfieldID message_fid = - (*env)->GetFieldID(env, jpmevent_class, "message", "I"); - jfieldID timestamp_fid = - (*env)->GetFieldID(env, jpmevent_class, "timestamp", "I"); - // note that we call WriteShort because it's simpler than constructing - // a buffer and passing it to Pm_Write - return Pm_WriteShort(PMSTREAM(jstream, address_fid), - (*env)->GetIntField(env, jpmevent, timestamp_fid), - (*env)->GetIntField(env, jpmevent, message_fid)); -} - - -/* - * Method: Pm_WriteShort - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1WriteShort - (JNIEnv *env, jclass cl, jobject jstream, jint when, jint msg) -{ - CLASS(c, jstream); - ADDRESS_FID(fid, c); - return Pm_WriteShort(PMSTREAM(jstream, fid), when, msg); -} - - -/* - * Method: Pm_WriteSysEx - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pm_1WriteSysEx - (JNIEnv *env, jclass cl, jobject jstream, jint when, jbyteArray jmsg) -{ - CLASS(c, jstream); - ADDRESS_FID(fid, c); - jbyte *bytes = (*env)->GetByteArrayElements(env, jmsg, 0); - PmError rslt = Pm_WriteSysEx(PMSTREAM(jstream, fid), when, - (unsigned char *) bytes); - (*env)->ReleaseByteArrayElements(env, jmsg, bytes, 0); - return rslt; -} - -/* - * Method: Pt_TimeStart - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pt_1TimeStart - (JNIEnv *env, jclass c, jint resolution) -{ - return Pt_Start(resolution, NULL, NULL); -} - -/* - * Method: Pt_TimeStop - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pt_1TimeStop - (JNIEnv *env, jclass c) - { - return Pt_Stop(); - } - -/* - * Method: Pt_Time - */ -JNIEXPORT jint JNICALL Java_jportmidi_JPortMidiApi_Pt_1Time - (JNIEnv *env, jclass c) - { - return Pt_Time(); - } - -/* - * Method: Pt_TimeStarted - */ -JNIEXPORT jboolean JNICALL Java_jportmidi_JPortMidiApi_Pt_1TimeStarted - (JNIEnv *env, jclass c) -{ - return Pt_Started(); -} - - diff --git a/portmidi/pm_java/pmjni/pmjni.rc b/portmidi/pm_java/pmjni/pmjni.rc deleted file mode 100644 index 1b7522b..0000000 --- a/portmidi/pm_java/pmjni/pmjni.rc +++ /dev/null @@ -1,63 +0,0 @@ -// Microsoft Visual C++ generated resource script.
-//
-#include "resource.h"
-
-#define APSTUDIO_READONLY_SYMBOLS
-/////////////////////////////////////////////////////////////////////////////
-//
-// Generated from the TEXTINCLUDE 2 resource.
-//
-#include "afxres.h"
-
-/////////////////////////////////////////////////////////////////////////////
-#undef APSTUDIO_READONLY_SYMBOLS
-
-/////////////////////////////////////////////////////////////////////////////
-// English (U.S.) resources
-
-#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
-#ifdef _WIN32
-LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
-#pragma code_page(1252)
-#endif //_WIN32
-
-#ifdef APSTUDIO_INVOKED
-/////////////////////////////////////////////////////////////////////////////
-//
-// TEXTINCLUDE
-//
-
-1 TEXTINCLUDE
-BEGIN
- "resource.h\0"
-END
-
-2 TEXTINCLUDE
-BEGIN
- "#include ""afxres.h""\r\n"
- "\0"
-END
-
-3 TEXTINCLUDE
-BEGIN
- "\r\n"
- "\0"
-END
-
-#endif // APSTUDIO_INVOKED
-
-#endif // English (U.S.) resources
-/////////////////////////////////////////////////////////////////////////////
-
-
-
-#ifndef APSTUDIO_INVOKED
-/////////////////////////////////////////////////////////////////////////////
-//
-// Generated from the TEXTINCLUDE 3 resource.
-//
-
-
-/////////////////////////////////////////////////////////////////////////////
-#endif // not APSTUDIO_INVOKED
-
diff --git a/portmidi/pm_linux/README_LINUX.txt b/portmidi/pm_linux/README_LINUX.txt deleted file mode 100755 index cfbc43f..0000000 --- a/portmidi/pm_linux/README_LINUX.txt +++ /dev/null @@ -1,99 +0,0 @@ -README_LINUX.txt for PortMidi -Roger Dannenberg -6 Dec 2012, revised May 2022 - -Contents: - To make PortMidi - The pmdefaults program - Setting LD_LIBRARY_PATH - A note about amd64 - Using autoconf - Using configure - Changelog - - -See ../README.md for general instructions. - -THE pmdefaults PROGRAM - -(This may be obsolete. It is older than `../README.md` which -also discusses pmdefaults, and Java support may be removed -unless someone claims they use it... -RBD) - -You should install pmdefaults. It provides a graphical interface -for selecting default MIDI IN and OUT devices so that you don't -have to build device selection interfaces into all your programs -and so users have a single place to set a preference. - -Follow the instructions above to run ccmake, making sure that -CMAKE_BUILD_TYPE is Release. Run make as described above. Then: - -sudo make install - -This will install PortMidi libraries and the pmdefault program. -You must alos have the environment variable LD_LIBRARY_PATH set -to include /usr/local/lib (where libpmjni.so is installed). - -Now, you can run pmdefault. - - -SETTING LD_LIBRARY_PATH - -pmdefaults will not work unless LD_LIBRARY_PATH includes a -directory (normally /usr/local/lib) containing libpmjni.so, -installed as described above. - -To set LD_LIBRARY_PATH, you might want to add this to your -~/.profile (if you use the bash shell): - -LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib -export LD_LIBRARY_PATH - - -A NOTE ABOUT AMD64: - -When compiling portmidi under linux on an AMD64, I had to add the -fPIC -flag to the gcc flags. - -Reason: when trying to build John Harrison's pyPortMidi gcc bailed out -with this error: - -./linux/libportmidi.a(pmlinux.o): relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC -./linux/libportmidi.a: could not read symbols: Bad value -collect2: ld returned 1 exit status -error: command 'gcc' failed with exit status 1 - -What they said: -http://www.gentoo.org/proj/en/base/amd64/howtos/index.xml?part=1&chap=3 -On certain architectures (AMD64 amongst them), shared libraries *must* -be "PIC-enabled". - -CHANGELOG - -27-may-2022 Roger B. Dannenberg - Some updates to this file. - -6-dec-2012 Roger B. Dannenberg - Copied notes on Autoconf from Audacity sources - -22-jan-2010 Roger B. Dannenberg - Updated instructions about Java paths - -14-oct-2009 Roger B. Dannenberg - Using CMake now for building and configuration - -29-aug-2006 Roger B. Dannenberg - Fixed PortTime to join with time thread for clean exit. - -28-aug-2006 Roger B. Dannenberg - Updated this documentation. - -08-Jun-2004 Roger B. Dannenberg - Updated code to use new system abstraction. - -12-Apr-2003 Roger B. Dannenberg - Fixed pm_test/test.c to filter clocks and active messages. - Integrated changes from Clemens Ladisch: - cleaned up pmlinuxalsa.c - record timestamp on sysex input - deallocate some resources previously left open diff --git a/portmidi/pm_linux/pmlinux.c b/portmidi/pm_linux/pmlinux.c deleted file mode 100755 index 3766427..0000000 --- a/portmidi/pm_linux/pmlinux.c +++ /dev/null @@ -1,68 +0,0 @@ -/* pmlinux.c -- PortMidi os-dependent code */
-
-/* This file only needs to implement pm_init(), which calls various
- routines to register the available midi devices. This file must
- be separate from the main portmidi.c file because it is system
- dependent, and it is separate from, pmlinuxalsa.c, because it
- might need to register non-alsa devices as well.
-
- NOTE: if you add non-ALSA support, you need to fix :alsa_poll()
- in pmlinuxalsa.c, which assumes all input devices are ALSA.
- */
-
-#include "stdlib.h"
-#include "portmidi.h"
-#include "pmutil.h"
-#include "pminternal.h"
-
-#ifdef PMALSA
- #include "pmlinuxalsa.h"
-#endif
-
-#ifdef PMNULL
- #include "pmlinuxnull.h"
-#endif
-
-#if !(defined(PMALSA) || defined(PMNULL))
-#error One of PMALSA or PMNULL must be defined
-#endif
-
-void pm_init()
-{
- /* Note: it is not an error for PMALSA to fail to initialize.
- * It may be a design error that the client cannot query what subsystems
- * are working properly other than by looking at the list of available
- * devices.
- */
-#ifdef PMALSA
- pm_linuxalsa_init();
-#endif
-#ifdef PMNULL
- pm_linuxnull_init();
-#endif
-}
-
-void pm_term(void)
-{
- #ifdef PMALSA
- pm_linuxalsa_term();
- #endif
- #ifdef PMNULL
- pm_linuxnull_term();
- #endif
-}
-
-PmDeviceID Pm_GetDefaultInputDeviceID() {
- Pm_Initialize();
- return pm_default_input_device_id;
-}
-
-PmDeviceID Pm_GetDefaultOutputDeviceID() {
- Pm_Initialize();
- return pm_default_output_device_id;
-}
-
-void *pm_alloc(size_t s) { return malloc(s); }
-
-void pm_free(void *ptr) { free(ptr); }
-
diff --git a/portmidi/pm_linux/pmlinuxalsa.c b/portmidi/pm_linux/pmlinuxalsa.c deleted file mode 100755 index b2e43e1..0000000 --- a/portmidi/pm_linux/pmlinuxalsa.c +++ /dev/null @@ -1,893 +0,0 @@ -/*
- * pmlinuxalsa.c -- system specific definitions
- *
- * written by:
- * Roger Dannenberg (port to Alsa 0.9.x)
- * Clemens Ladisch (provided code examples and invaluable consulting)
- * Jason Cohen, Rico Colon, Matt Filippone (Alsa 0.5.x implementation)
- */
-
-/* omit this code if PMALSA is not defined -- this mechanism allows
- * selection of different MIDI interfaces (at compile time).
- */
-#ifdef PMALSA
-
-#include "stdlib.h"
-#include "portmidi.h"
-#include "pmutil.h"
-#include "pminternal.h"
-#include "pmlinuxalsa.h"
-#include "string.h"
-#include "porttime.h"
-
-#include <alsa/asoundlib.h>
-
-/* I used many print statements to debug this code. I left them in the
- * source, and you can turn them on by changing false to true below:
- */
-#define VERBOSE_ON 0
-#define VERBOSE if (VERBOSE_ON)
-
-#define MIDI_SYSEX 0xf0
-#define MIDI_EOX 0xf7
-
-#if SND_LIB_MAJOR == 0 && SND_LIB_MINOR < 9
-#error needs ALSA 0.9.0 or later
-#endif
-
-/* to store client/port in the device descriptor */
-#define MAKE_DESCRIPTOR(client, port) ((void*)(long)(((client) << 8) | (port)))
-#define GET_DESCRIPTOR_CLIENT(info) ((((long)(info)) >> 8) & 0xff)
-#define GET_DESCRIPTOR_PORT(info) (((long)(info)) & 0xff)
-
-#define BYTE unsigned char
-
-extern pm_fns_node pm_linuxalsa_in_dictionary;
-extern pm_fns_node pm_linuxalsa_out_dictionary;
-
-static snd_seq_t *seq = NULL; // all input comes here,
- // output queue allocated on seq
-static int queue, queue_used; /* one for all ports, reference counted */
-
-#define PORT_IS_CLOSED -999999
-
-typedef struct alsa_info_struct {
- int is_virtual;
- int client;
- int port;
- int this_port;
- int in_sysex;
- snd_midi_event_t *parser;
-} alsa_info_node, *alsa_info_type;
-
-
-/* get_alsa_error_text -- copy error text to potentially short string */
-/**/
-static void get_alsa_error_text(char *msg, int len, int err)
-{
- int errlen = strlen(snd_strerror(err));
- if (errlen > 0 && errlen < len) {
- strcpy(msg, snd_strerror(err));
- } else if (len > 20) {
- sprintf(msg, "Alsa error %d", err);
- } else {
- msg[0] = 0;
- }
-}
-
-
-static PmError check_hosterror(int err)
-{
- if (err < 0) {
- get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, err);
- pm_hosterror = TRUE;
- return pmHostError;
- }
- return pmNoError;
-}
-
-
-/* queue is shared by both input and output, reference counted */
-static PmError alsa_use_queue(void)
-{
- int err = 0;
- if (queue_used == 0) {
- snd_seq_queue_tempo_t *tempo;
-
- queue = snd_seq_alloc_queue(seq);
- if (queue < 0) {
- return check_hosterror(queue);
- }
- snd_seq_queue_tempo_alloca(&tempo);
- snd_seq_queue_tempo_set_tempo(tempo, 480000);
- snd_seq_queue_tempo_set_ppq(tempo, 480);
- err = snd_seq_set_queue_tempo(seq, queue, tempo);
- if (err < 0) {
- return check_hosterror(err);
- }
- snd_seq_start_queue(seq, queue, NULL);
- snd_seq_drain_output(seq);
- }
- ++queue_used;
- return pmNoError;
-}
-
-
-static void alsa_unuse_queue(void)
-{
- if (--queue_used == 0) {
- snd_seq_stop_queue(seq, queue, NULL);
- snd_seq_drain_output(seq);
- snd_seq_free_queue(seq, queue);
- VERBOSE printf("queue freed\n");
- }
-}
-
-
-/* midi_message_length -- how many bytes in a message? */
-static int midi_message_length(PmMessage message)
-{
- message &= 0xff;
- if (message < 0x80) {
- return 0;
- } else if (message < 0xf0) {
- static const int length[] = {3, 3, 3, 3, 2, 2, 3};
- return length[(message - 0x80) >> 4];
- } else {
- static const int length[] = {
- -1, 2, 3, 2, 0, 0, 1, -1, 1, 0, 1, 1, 1, 0, 1, 1};
- return length[message - 0xf0];
- }
-}
-
-
-static alsa_info_type alsa_info_create(int client_port, long id, int is_virtual)
-{
- alsa_info_type info = (alsa_info_type) pm_alloc(sizeof(alsa_info_node));
- info->is_virtual = is_virtual;
- info->this_port = id;
- info->client = GET_DESCRIPTOR_CLIENT(client_port);
- info->port = GET_DESCRIPTOR_PORT(client_port);
- info->in_sysex = 0;
- return info;
-}
-
-
-/* search system dependent extra parameters for string */
-static const char *get_sysdep_name(enum PmSysDepPropertyKey key,
- PmSysDepInfo *info)
-{
- /* the version where all current properties were introduced is 210 */
- if (info && info->structVersion >= 210) {
- int i;
- for (i = 0; i < info->length; i++) { /* search for key */
- if (info->properties[i].key == key) {
- return info->properties[i].value;
- }
- }
- }
- return NULL;
-}
-
-
-static void maybe_set_client_name(PmSysDepInfo *driverInfo)
-{
- if (!seq) { // make sure seq is created and we have info
- return;
- }
-
- const char *client_name = get_sysdep_name(pmKeyAlsaClientName,
- (PmSysDepInfo *) driverInfo);
- if (client_name) {
- snd_seq_set_client_name(seq, client_name);
- printf("maybe_set_client_name set client to %s\n", client_name);
- }
-}
-
-
-static PmError alsa_out_open(PmInternal *midi, void *driverInfo)
-{
- int id = midi->device_id;
- void *client_port = pm_descriptors[id].descriptor;
- alsa_info_type ainfo = alsa_info_create((long) client_port, id,
- pm_descriptors[id].pub.is_virtual);
- snd_seq_port_info_t *pinfo;
- int err = 0;
- int using_the_queue = 0;
-
- if (!ainfo) return pmInsufficientMemory;
- midi->api_info = ainfo;
-
- snd_seq_port_info_alloca(&pinfo);
- if (!ainfo->is_virtual) {
- snd_seq_port_info_set_port(pinfo, id);
- snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_WRITE |
- SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ);
- snd_seq_port_info_set_type(pinfo, SND_SEQ_PORT_TYPE_MIDI_GENERIC |
- SND_SEQ_PORT_TYPE_APPLICATION);
- const char *port_name = get_sysdep_name(pmKeyAlsaPortName,
- (PmSysDepInfo *) driverInfo);
- if (port_name) {
- snd_seq_port_info_set_name(pinfo, port_name);
- }
- snd_seq_port_info_set_port_specified(pinfo, 1);
-
- err = snd_seq_create_port(seq, pinfo);
- if (err < 0) goto free_ainfo;
-
- }
-
- err = snd_midi_event_new(PM_DEFAULT_SYSEX_BUFFER_SIZE, &ainfo->parser);
- if (err < 0) goto free_this_port;
-
- if (midi->latency > 0) { /* must delay output using a queue */
- err = alsa_use_queue();
- if (err < 0) goto free_parser;
- using_the_queue++;
- }
-
- if (!ainfo->is_virtual) {
- err = snd_seq_connect_to(seq, ainfo->this_port, ainfo->client,
- ainfo->port);
- if (err < 0) goto unuse_queue; /* clean up and return on error */
- }
-
- maybe_set_client_name(driverInfo);
-
- return pmNoError;
-
- unuse_queue:
- if (using_the_queue > 0) /* only for latency>0 case */
- alsa_unuse_queue();
- free_parser:
- snd_midi_event_free(ainfo->parser);
- free_this_port:
- snd_seq_delete_port(seq, ainfo->this_port);
- free_ainfo:
- pm_free(ainfo);
- return check_hosterror(err);
-}
-
-
-static PmError alsa_write_byte(PmInternal *midi, unsigned char byte,
- PmTimestamp timestamp)
-{
- alsa_info_type info = (alsa_info_type) midi->api_info;
- snd_seq_event_t ev;
- int err = 0;
-
- snd_seq_ev_clear(&ev);
- if (snd_midi_event_encode_byte(info->parser, byte, &ev) == 1) {
- if (info->is_virtual) {
- snd_seq_ev_set_subs(&ev);
- } else {
- snd_seq_ev_set_dest(&ev, info->client, info->port);
- }
- snd_seq_ev_set_source(&ev, info->this_port);
- if (midi->latency > 0) {
- /* compute relative time of event = timestamp - now + latency */
- PmTimestamp now = (midi->time_proc ?
- midi->time_proc(midi->time_info) :
- Pt_Time());
- int when = timestamp;
- /* if timestamp is zero, send immediately */
- /* otherwise compute time delay and use delay if positive */
- if (when == 0) when = now;
- when = (when - now) + midi->latency;
- if (when < 0) when = 0;
- VERBOSE printf("timestamp %d now %d latency %d, ",
- (int) timestamp, (int) now, midi->latency);
- VERBOSE printf("scheduling event after %d\n", when);
- /* message is sent in relative ticks, where 1 tick = 1 ms */
- snd_seq_ev_schedule_tick(&ev, queue, 1, when);
- /* NOTE: for cases where the user does not supply a time function,
- we could optimize the code by not starting Pt_Time and using
- the alsa tick time instead. I didn't do this because it would
- entail changing the queue management to start the queue tick
- count when PortMidi is initialized and keep it running until
- PortMidi is terminated. (This should be simple, but it's not
- how the code works now.) -RBD */
- } else { /* send event out without queueing */
- VERBOSE printf("direct\n");
- /* ev.queue = SND_SEQ_QUEUE_DIRECT;
- ev.dest.client = SND_SEQ_ADDRESS_SUBSCRIBERS; */
- snd_seq_ev_set_direct(&ev);
- }
- VERBOSE printf("sending event, timestamp %d (%d+%dns) (%s, %s)\n",
- ev.time.tick, ev.time.time.tv_sec, ev.time.time.tv_nsec,
- (ev.flags & SND_SEQ_TIME_STAMP_MASK ? "real" : "tick"),
- (ev.flags & SND_SEQ_TIME_MODE_MASK ? "rel" : "abs"));
- err = snd_seq_event_output(seq, &ev);
- }
- return check_hosterror(err);
-}
-
-
-static PmError alsa_out_close(PmInternal *midi)
-{
- alsa_info_type info = (alsa_info_type) midi->api_info;
- int err = 0;
- int error2 = 0;
- if (!info) return pmBadPtr;
-
- if (info->this_port != PORT_IS_CLOSED) {
- if (!info->is_virtual) {
- err = snd_seq_disconnect_to(seq, info->this_port,
- info->client, info->port);
- /* even if there was an error, we still try to delete the port */
- error2 = snd_seq_delete_port(seq, info->this_port);
-
- if (!err) { /* retain original error if there was one */
- err = error2; /* otherwise, use port delete status */
- }
- }
- }
- if (midi->latency > 0) alsa_unuse_queue();
- snd_midi_event_free(info->parser);
- midi->api_info = NULL; /* destroy the pointer to signify "closed" */
- pm_free(info);
- return check_hosterror(err);
-}
-
-
-static PmError alsa_create_virtual(int is_input, const char *name,
- void *device_info)
-{
- snd_seq_port_info_t *pinfo;
- int err;
- int client, port;
-
- /* we need the id to set the port. */
- PmDeviceID id = pm_add_device("ALSA", name, is_input, TRUE, NULL,
- (is_input ? &pm_linuxalsa_in_dictionary :
- &pm_linuxalsa_out_dictionary));
- if (id < 0) { /* error -- out of memory? */
- return id;
- }
- snd_seq_port_info_alloca(&pinfo);
- snd_seq_port_info_set_capability(pinfo,
- (is_input ? SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE :
- SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ));
- snd_seq_port_info_set_type(pinfo, SND_SEQ_PORT_TYPE_MIDI_GENERIC |
- SND_SEQ_PORT_TYPE_APPLICATION);
- snd_seq_port_info_set_name(pinfo, name);
- snd_seq_port_info_set_port(pinfo, id);
- snd_seq_port_info_set_port_specified(pinfo, 1);
- /* next 3 lines needed to generate timestamp - PaulLiu */
- snd_seq_port_info_set_timestamping(pinfo, 1);
- snd_seq_port_info_set_timestamp_real(pinfo, 0);
- snd_seq_port_info_set_timestamp_queue(pinfo, queue);
-
- err = snd_seq_create_port(seq, pinfo);
- if (err < 0) {
- pm_undo_add_device(id);
- return check_hosterror(err);
- }
-
- client = snd_seq_port_info_get_client(pinfo);
- port = snd_seq_port_info_get_port(pinfo);
- pm_descriptors[id].descriptor = MAKE_DESCRIPTOR(client, port);
- return id;
-}
-
-
- static PmError alsa_delete_virtual(PmDeviceID id)
- {
- int err = snd_seq_delete_port(seq, id);
- return check_hosterror(err);
- }
-
-
-static PmError alsa_in_open(PmInternal *midi, void *driverInfo)
-{
- int id = midi->device_id;
- void *client_port = pm_descriptors[id].descriptor;
- alsa_info_type ainfo = alsa_info_create((long) client_port, id,
- pm_descriptors[id].pub.is_virtual);
- snd_seq_port_info_t *pinfo;
- snd_seq_port_subscribe_t *sub;
- snd_seq_addr_t addr;
- int err = 0;
- int is_virtual = pm_descriptors[id].pub.is_virtual;
-
- if (!ainfo) return pmInsufficientMemory;
- midi->api_info = ainfo;
-
- err = alsa_use_queue();
- if (err < 0) goto free_ainfo;
-
- snd_seq_port_info_alloca(&pinfo);
- if (is_virtual) {
- ainfo->is_virtual = TRUE;
- if (snd_seq_get_port_info(seq, ainfo->port, pinfo)) {
- pinfo = NULL;
- goto free_ainfo;
- }
- } else {
- /* create a port for this alsa client (seq) where the port
- number matches the portmidi device ID of the input device */
- snd_seq_port_info_set_port(pinfo, id);
- snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_WRITE |
- SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_WRITE);
-
- snd_seq_port_info_set_type(pinfo, SND_SEQ_PORT_TYPE_MIDI_GENERIC |
- SND_SEQ_PORT_TYPE_APPLICATION);
- snd_seq_port_info_set_port_specified(pinfo, 1);
-
- const char *port_name = get_sysdep_name(pmKeyAlsaPortName,
- (PmSysDepInfo *) driverInfo);
- if (port_name) {
- snd_seq_port_info_set_name(pinfo, port_name);
- }
-
- err = snd_seq_create_port(seq, pinfo);
- if (err < 0) goto free_queue;
-
- /* forward messages from input to this alsa client, so this
- * alsa client is the destination, and the destination port is the
- * port we just created using the device ID as port number
- */
- snd_seq_port_subscribe_alloca(&sub);
- addr.client = snd_seq_client_id(seq);
- addr.port = ainfo->this_port;
- snd_seq_port_subscribe_set_dest(sub, &addr);
-
- /* forward from the sender which is the device named by
- client and port */
- addr.client = ainfo->client;
- addr.port = ainfo->port;
- snd_seq_port_subscribe_set_sender(sub, &addr);
- snd_seq_port_subscribe_set_time_update(sub, 1);
- /* this doesn't seem to work: messages come in with real timestamps */
- snd_seq_port_subscribe_set_time_real(sub, 0);
- err = snd_seq_subscribe_port(seq, sub);
- if (err < 0) goto free_this_port; /* clean up and return on error */
- }
-
- maybe_set_client_name(driverInfo);
-
- return pmNoError;
- free_this_port:
- snd_seq_delete_port(seq, ainfo->this_port);
- free_queue:
- alsa_unuse_queue();
- free_ainfo:
- pm_free(ainfo);
- return check_hosterror(err);
-}
-
-static PmError alsa_in_close(PmInternal *midi)
-{
- int err = 0;
- alsa_info_type info = (alsa_info_type) midi->api_info;
- if (!info) return pmBadPtr;
- /* virtual ports stay open because the represent devices */
- if (!info->is_virtual && info->this_port != PORT_IS_CLOSED) {
- err = snd_seq_delete_port(seq, info->this_port);
- }
- alsa_unuse_queue();
- midi->api_info = NULL;
- pm_free(info);
- return check_hosterror(err);
-}
-
-
-static PmError alsa_abort(PmInternal *midi)
-{
- /* NOTE: ALSA documentation is vague. This is supposed to
- * remove any pending output messages. If you can test and
- * confirm this code is correct, please update this comment. -RBD
- */
- /* Unfortunately, I can't even compile it -- my ALSA version
- * does not implement snd_seq_remove_events_t, so this does
- * not compile. I'll try again, but it looks like I'll need to
- * upgrade my entire Linux OS -RBD
- */
- /*
- info_type info = (info_type) midi->api_info;
- snd_seq_remove_events_t info;
- snd_seq_addr_t addr;
- addr.client = info->client;
- addr.port = info->port;
- snd_seq_remove_events_set_dest(&info, &addr);
- snd_seq_remove_events_set_condition(&info, SND_SEQ_REMOVE_DEST);
- pm_hosterror = snd_seq_remove_events(seq, &info);
- if (pm_hosterror) {
- get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN,
- pm_hosterror);
- return pmHostError;
- }
- */
- printf("WARNING: alsa_abort not implemented\n");
- return pmNoError;
-}
-
-
-static PmError alsa_write_flush(PmInternal *midi, PmTimestamp timestamp)
-{
- int err;
- alsa_info_type info = (alsa_info_type) midi->api_info;
- if (!info) return pmBadPtr;
- VERBOSE printf("snd_seq_drain_output: %p\n", seq);
- err = snd_seq_drain_output(seq);
- return check_hosterror(err);
-}
-
-
-static PmError alsa_write_short(PmInternal *midi, PmEvent *event)
-{
- int bytes = midi_message_length(event->message);
- PmMessage msg = event->message;
- int i;
- alsa_info_type info = (alsa_info_type) midi->api_info;
- if (!info) return pmBadPtr;
- for (i = 0; i < bytes; i++) {
- unsigned char byte = msg;
- VERBOSE printf("sending 0x%x\n", byte);
- alsa_write_byte(midi, byte, event->timestamp);
- if (pm_hosterror) break;
- msg >>= 8; /* shift next byte into position */
- }
- if (pm_hosterror) return pmHostError;
- return pmNoError;
-}
-
-
-/* alsa_sysex -- implements begin_sysex and end_sysex */
-PmError alsa_sysex(PmInternal *midi, PmTimestamp timestamp) {
- return pmNoError;
-}
-
-
-static PmTimestamp alsa_synchronize(PmInternal *midi)
-{
- return 0; /* linux implementation does not use this synchronize function */
- /* Apparently, Alsa data is relative to the time you send it, and there
- is no reference. If this is true, this is a serious shortcoming of
- Alsa. If not true, then PortMidi has a serious shortcoming -- it
- should be scheduling relative to Alsa's time reference. */
-}
-
-
-static void handle_event(snd_seq_event_t *ev)
-{
- int device_id = ev->dest.port;
- PmInternal *midi = pm_descriptors[device_id].pm_internal;
- // There is a race condition when closing a device and
- // continuing to poll other open devices. The closed device may
- // have outstanding events from before the close operation.
- if (!midi) {
- return;
- }
- PmEvent pm_ev;
- PmTimestamp timestamp = midi->time_proc(midi->time_info);
-
- /* time stamp should be in ticks, using our queue where 1 tick = 1ms */
- /* assert((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_TICK);
- * Currently, event timestamp is ignored. See long note below. */
-
- VERBOSE {
- /* translate time to time_proc basis */
- snd_seq_queue_status_t *queue_status;
- snd_seq_queue_status_alloca(&queue_status);
- snd_seq_get_queue_status(seq, queue, queue_status);
- printf("handle_event: alsa_now %d, "
- "event timestamp %d (%d+%dns) (%s, %s)\n",
- snd_seq_queue_status_get_tick_time(queue_status),
- ev->time.tick, ev->time.time.tv_sec, ev->time.time.tv_nsec,
- (ev->flags & SND_SEQ_TIME_STAMP_MASK ? "real" : "tick"),
- (ev->flags & SND_SEQ_TIME_MODE_MASK ? "rel" : "abs"));
- /* OLD: portmidi timestamp is (now - alsa_now) + alsa_timestamp */
- /* timestamp = (*time_proc)(midi->time_info) + ev->time.tick -
- snd_seq_queue_status_get_tick_time(queue_status); */
- }
- /* CURRENT: portmidi timestamp is "now". In a test, timestamps from
- * hardware (MIDI over USB) were timestamped with the current ALSA
- * time (snd_seq_queue_status_get_tick_time) and flags indicating
- * absolute ticks, but timestamps from another application's virtual
- * port, sent direct with 0 absolute ticks, were received with a
- * large value that is apparently the time since the start time of
- * the other application. Without any reference to our local time,
- * this seems useless. PortMidi is supposed to return the local
- * PortMidi time of the arrival of the message, so the best we can
- * do is set the timestamp to our local clock. This seems to be a
- * design flaw in ALSA -- I pointed this out a decade ago, but if
- * there is a workaround, I'd still like to know. Maybe there is a
- * way to use absolute real time and maybe that's sharable across
- * applications by referencing the system time?
- */
- pm_ev.timestamp = timestamp;
- switch (ev->type) {
- case SND_SEQ_EVENT_NOTEON:
- pm_ev.message = Pm_Message(0x90 | ev->data.note.channel,
- ev->data.note.note & 0x7f,
- ev->data.note.velocity & 0x7f);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_NOTEOFF:
- pm_ev.message = Pm_Message(0x80 | ev->data.note.channel,
- ev->data.note.note & 0x7f,
- ev->data.note.velocity & 0x7f);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_KEYPRESS:
- pm_ev.message = Pm_Message(0xa0 | ev->data.note.channel,
- ev->data.note.note & 0x7f,
- ev->data.note.velocity & 0x7f);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_CONTROLLER:
- pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel,
- ev->data.control.param & 0x7f,
- ev->data.control.value & 0x7f);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_PGMCHANGE:
- pm_ev.message = Pm_Message(0xc0 | ev->data.note.channel,
- ev->data.control.value & 0x7f, 0);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_CHANPRESS:
- pm_ev.message = Pm_Message(0xd0 | ev->data.note.channel,
- ev->data.control.value & 0x7f, 0);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_PITCHBEND:
- pm_ev.message = Pm_Message(0xe0 | ev->data.note.channel,
- (ev->data.control.value + 0x2000) & 0x7f,
- ((ev->data.control.value + 0x2000) >> 7) & 0x7f);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_CONTROL14:
- if (ev->data.control.param < 0x20) {
- pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel,
- ev->data.control.param,
- (ev->data.control.value >> 7) & 0x7f);
- pm_read_short(midi, &pm_ev);
- pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel,
- ev->data.control.param + 0x20,
- ev->data.control.value & 0x7f);
- pm_read_short(midi, &pm_ev);
- } else {
- pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel,
- ev->data.control.param & 0x7f,
- ev->data.control.value & 0x7f);
-
- pm_read_short(midi, &pm_ev);
- }
- break;
- case SND_SEQ_EVENT_SONGPOS:
- pm_ev.message = Pm_Message(0xf2,
- ev->data.control.value & 0x7f,
- (ev->data.control.value >> 7) & 0x7f);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_SONGSEL:
- pm_ev.message = Pm_Message(0xf3,
- ev->data.control.value & 0x7f, 0);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_QFRAME:
- pm_ev.message = Pm_Message(0xf1,
- ev->data.control.value & 0x7f, 0);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_START:
- pm_ev.message = Pm_Message(0xfa, 0, 0);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_CONTINUE:
- pm_ev.message = Pm_Message(0xfb, 0, 0);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_STOP:
- pm_ev.message = Pm_Message(0xfc, 0, 0);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_CLOCK:
- pm_ev.message = Pm_Message(0xf8, 0, 0);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_TUNE_REQUEST:
- pm_ev.message = Pm_Message(0xf6, 0, 0);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_RESET:
- pm_ev.message = Pm_Message(0xff, 0, 0);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_SENSING:
- pm_ev.message = Pm_Message(0xfe, 0, 0);
- pm_read_short(midi, &pm_ev);
- break;
- case SND_SEQ_EVENT_SYSEX: {
- const BYTE *ptr = (const BYTE *) ev->data.ext.ptr;
- /* assume there is one sysex byte to process */
- pm_read_bytes(midi, ptr, ev->data.ext.len, timestamp);
- break;
- }
- case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: {
- /* this happens if you have an input port open and the
- * device or application with virtual ports closes. We
- * mark the port as closed to avoid closing a 2nd time
- * when Pm_Close() is called.
- */
- alsa_info_type info = (alsa_info_type) midi->api_info;
- /* printf("SND_SEQ_EVENT_UNSUBSCRIBE message\n"); */
- info->this_port = PORT_IS_CLOSED;
- break;
- }
- case SND_SEQ_EVENT_PORT_SUBSCRIBED:
- break; /* someone connected to a virtual output port, not reported */
- default:
- printf("portmidi handle_event: not handled type %x\n", ev->type);
- break;
- }
-}
-
-
-static PmError alsa_poll(PmInternal *midi)
-{
- if (!midi) {
- return pmBadPtr;
- }
- snd_seq_event_t *ev;
- /* expensive check for input data, gets data from device: */
- while (snd_seq_event_input_pending(seq, TRUE) > 0) {
- /* cheap check on local input buffer */
- while (snd_seq_event_input_pending(seq, FALSE) > 0) {
- /* check for and ignore errors, e.g. input overflow */
- /* note: if there's overflow, this should be reported
- * all the way through to client. Since input from all
- * devices is merged, we need to find all input devices
- * and set all to the overflow state.
- * NOTE: this assumes every input is ALSA based.
- */
- int rslt = snd_seq_event_input(seq, &ev);
- if (rslt >= 0) {
- handle_event(ev);
- } else if (rslt == -ENOSPC) {
- int i;
- for (i = 0; i < pm_descriptor_len; i++) {
- if (pm_descriptors[i].pub.input) {
- PmInternal *midi_i = pm_descriptors[i].pm_internal;
- /* careful, device may not be open! */
- if (midi_i) Pm_SetOverflow(midi_i->queue);
- }
- }
- }
- }
- }
- return pmNoError;
-}
-
-
-static unsigned int alsa_check_host_error(PmInternal *midi)
-{
- return FALSE;
-}
-
-
-pm_fns_node pm_linuxalsa_in_dictionary = {
- none_write_short,
- none_sysex,
- none_sysex,
- none_write_byte,
- none_write_short,
- none_write_flush,
- alsa_synchronize,
- alsa_in_open,
- alsa_abort,
- alsa_in_close,
- alsa_poll,
- alsa_check_host_error
-};
-
-pm_fns_node pm_linuxalsa_out_dictionary = {
- alsa_write_short,
- alsa_sysex,
- alsa_sysex,
- alsa_write_byte,
- alsa_write_short, /* short realtime message */
- alsa_write_flush,
- alsa_synchronize,
- alsa_out_open,
- alsa_abort,
- alsa_out_close,
- none_poll,
- alsa_check_host_error
-};
-
-
-/* pm_strdup -- copy a string to the heap. Use this rather than strdup so
- * that we call pm_alloc, not malloc. This allows portmidi to avoid
- * malloc which might cause priority inversion. Probably ALSA is going
- * to call malloc anyway, so this extra work here may be pointless.
- */
-char *pm_strdup(const char *s)
-{
- int len = strlen(s);
- char *dup = (char *) pm_alloc(len + 1);
- strcpy(dup, s);
- return dup;
-}
-
-
-PmError pm_linuxalsa_init(void)
-{
- int err;
- snd_seq_client_info_t *cinfo;
- snd_seq_port_info_t *pinfo;
- unsigned int caps;
-
- /* Register interface ALSA with create_virtual fn */
- pm_add_interf("ALSA", &alsa_create_virtual, &alsa_delete_virtual);
-
- /* Previously, the last parameter was SND_SEQ_NONBLOCK, but this
- * would cause messages to be dropped if the ALSA buffer fills up.
- * The correct behavior is for writes to block until there is
- * room to send all the data. The client should normally allocate
- * a large enough buffer to avoid blocking on output.
- * Now that blocking is enabled, the seq_event_input() will block
- * if there is no input data. This is not what we want, so must
- * call seq_event_input_pending() to avoid blocking.
- */
- err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0);
- if (err < 0) goto error_return;
-
- snd_seq_client_info_alloca(&cinfo);
- snd_seq_port_info_alloca(&pinfo);
-
- snd_seq_client_info_set_client(cinfo, -1);
- while (snd_seq_query_next_client(seq, cinfo) == 0) {
- snd_seq_port_info_set_client(pinfo,
- snd_seq_client_info_get_client(cinfo));
- snd_seq_port_info_set_port(pinfo, -1);
- while (snd_seq_query_next_port(seq, pinfo) == 0) {
- if (snd_seq_port_info_get_client(pinfo) == SND_SEQ_CLIENT_SYSTEM)
- continue; /* ignore Timer and Announce ports on client 0 */
- caps = snd_seq_port_info_get_capability(pinfo);
- if (!(caps & (SND_SEQ_PORT_CAP_SUBS_READ |
- SND_SEQ_PORT_CAP_SUBS_WRITE)))
- continue; /* ignore if you cannot read or write port */
- if (caps & SND_SEQ_PORT_CAP_SUBS_WRITE) {
- if (pm_default_output_device_id == -1)
- pm_default_output_device_id = pm_descriptor_len;
- pm_add_device("ALSA",
- pm_strdup(snd_seq_port_info_get_name(pinfo)),
- FALSE, FALSE,
- MAKE_DESCRIPTOR(snd_seq_port_info_get_client(pinfo),
- snd_seq_port_info_get_port(pinfo)),
- &pm_linuxalsa_out_dictionary);
- }
- if (caps & SND_SEQ_PORT_CAP_SUBS_READ) {
- if (pm_default_input_device_id == -1)
- pm_default_input_device_id = pm_descriptor_len;
- pm_add_device("ALSA",
- pm_strdup(snd_seq_port_info_get_name(pinfo)),
- TRUE, FALSE,
- MAKE_DESCRIPTOR(snd_seq_port_info_get_client(pinfo),
- snd_seq_port_info_get_port(pinfo)),
- &pm_linuxalsa_in_dictionary);
- }
- }
- }
- return pmNoError;
- error_return:
- pm_linuxalsa_term(); /* clean up */
- return check_hosterror(err);
-}
-
-
-void pm_linuxalsa_term(void)
-{
- if (seq) {
- snd_seq_close(seq);
- pm_free(pm_descriptors);
- pm_descriptors = NULL;
- pm_descriptor_len = 0;
- pm_descriptor_max = 0;
- }
-}
-
-#endif
diff --git a/portmidi/pm_linux/pmlinuxalsa.h b/portmidi/pm_linux/pmlinuxalsa.h deleted file mode 100755 index d4bff16..0000000 --- a/portmidi/pm_linux/pmlinuxalsa.h +++ /dev/null @@ -1,6 +0,0 @@ -/* pmlinuxalsa.h -- system-specific definitions */
-
-PmError pm_linuxalsa_init(void);
-void pm_linuxalsa_term(void);
-
-
diff --git a/portmidi/pm_linux/pmlinuxnull.c b/portmidi/pm_linux/pmlinuxnull.c deleted file mode 100644 index 257f3d6..0000000 --- a/portmidi/pm_linux/pmlinuxnull.c +++ /dev/null @@ -1,31 +0,0 @@ -/*
- * pmlinuxnull.c -- system specific definitions
- *
- * written by:
- * Roger Dannenberg
- *
- * If there is no ALSA, you can define PMNULL and build PortMidi. It will
- * not report any devices, so you will not be able to open any, but if
- * you wanted to disable MIDI from some application, this could be used.
- * Mainly, this code shows the possibility of supporting multiple
- * interfaces, e.g., ALSA and Sndio on BSD, or ALSA and Jack on Linux.
- * But as of Dec, 2021, the only supported MIDI API for Linux is ALSA.
- */
-
-#ifdef PMNULL
-
-#include "portmidi.h"
-#include "pmlinuxnull.h"
-
-
-PmError pm_linuxnull_init(void)
-{
- return pmNoError;
-}
-
-
-void pm_linuxnull_term(void)
-{
-}
-
-#endif
diff --git a/portmidi/pm_linux/pmlinuxnull.h b/portmidi/pm_linux/pmlinuxnull.h deleted file mode 100644 index 9835825..0000000 --- a/portmidi/pm_linux/pmlinuxnull.h +++ /dev/null @@ -1,6 +0,0 @@ -/* pmlinuxnull.h -- system-specific definitions */
-
-PmError pm_linuxnull_init(void);
-void pm_linuxnull_term(void);
-
-
diff --git a/portmidi/pm_mac/Makefile.osx b/portmidi/pm_mac/Makefile.osx deleted file mode 100755 index 2044381..0000000 --- a/portmidi/pm_mac/Makefile.osx +++ /dev/null @@ -1,125 +0,0 @@ -# MAKEFILE FOR PORTMIDI
-
-# Roger B. Dannenberg
-# Sep 2009
-
-# NOTE: PortMidi is currently built and tested with CMake.
-# This makefile is probably broken, but if you want to
-# directly use make, start here, and please contribute any
-# fixes.
-
-# NOTE: you can use
-# make -f pm_mac/Makefile.osx configuration=Release
-# to override the default Debug configuration
-configuration=Release
-
-PF=/usr/local
-
-# For debugging, define PM_CHECK_ERRORS
-ifeq ($(configuration),Release)
- CONFIG = Release
-else
- CONFIG = Debug
-endif
-
-current: all
-
-all: $(CONFIG)/CMakeCache.txt
- cd $(CONFIG); make
-
-$(CONFIG)/CMakeCache.txt:
- rm -f $(CONFIG)/CMakeCache.txt
- mkdir -p $(CONFIG)
- cd $(CONFIG); cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=$(CONFIG)
-
-
-**** For instructions: make -f pm_mac/Makefile.osx help ****\n'
-
-help:
- echo $$'\n\n\
-This is help for portmidi/pm_mac/Makefile.osx\n\n\
-Installation path for dylib is $(PF)\n\
-To build Release version libraries and test applications,\n \
-make -f pm_mac/Makefile.osx\n\
-To build Debug version libraries and test applications,\n \
-make -f pm_mac/Makefile.osx configuration=Debug\n\
-To install universal dynamic library,\n \
-sudo make -f pm_mac/Makefile.osx install\n\
-To install universal dynamic library with xcode,\n \
-make -f pm_mac/Makefile.osx install-with-xcode\n\
-To make PmDefaults Java application,\n \
-make -f pm_mac/Makefile.osx pmdefaults\n\n \
-configuration = $(configuration)\n'
-
-
-clean:
- rm -f *.o *~ core* */*.o */*/*.o */*~ */core* pm_test/*/pm_dll.dll
- rm -f *.opt *.ncb *.plg pm_win/Debug/pm_dll.lib pm_win/Release/pm_dll.lib
- rm -f pm_test/*.opt pm_test/*.ncb
- rm -f pm_java/pmjni/*.o pm_java/pmjni/*~ pm_java/*.h
- rm -rf Release/CMakeFiles Debug/CMakeFiles
- rm -rf pm_mac/pmdefaults/lib pm_mac/pmdefaults/src
-
-cleaner: clean
- rm -rf pm_mac/build
- rm -rf pm_mac/Debug pm_mac/Release pm_test/Debug pm_test/Release
- rm -f Debug/*.dylib Release/*.dylib
- rm -f pm_java/pmjni/Debug/*.jnilib
- rm -f pm_java/pmjni/Release/*.jnilib
-
-cleanest: cleaner
- rm -f Debug/CMakeCache.txt Release/CMakeCache.txt
- rm -f CMakeCache.txt
- rm -f Debug/libportmidi_s.a Release/libportmidi_s.a
- rm -f pm_test/Debug/test pm_test/Debug/sysex pm_test/Debug/midithread
- rm -f pm_test/Debug/latency pm_test/Debug/midithru
- rm -f pm_test/Debug/qtest pm_test/Debug/mm
- rm -f pm_test/Release/test pm_test/Release/sysex pm_test/Release/midithread
- rm -f pm_test/Release/latency pm_test/Release/midithru
- rm -f pm_test/Release/qtest pm_test/Release/mm
- rm -f pm_java/*/*.class
- rm -f pm_java/pmjni/jportmidi_JPortMidiApi_PortMidiStream.h
-
-backup: cleanest
- cd ..; zip -r portmidi.zip portmidi
-
-install: porttime/porttime.h pm_common/portmidi.h \
- $(CONFIG)/libportmidi.dylib
- install porttime/porttime.h $(PF)/include/
- install pm_common/portmidi.h $(PF)/include
- install $(CONFIG)/libportmidi.dylib $(PF)/lib/
-
-# note - this uses xcode to build and install portmidi universal binaries
-install-with-xcode:
- sudo xcodebuild -project pm_mac/pm_mac.xcodeproj \
- -configuration Release install DSTROOT=/
-
-##### build pmdefault ######
-
-pm_java/pmjni/jportmidi_JPortMidiApi.h: pm_java/jportmidi/JPortMidiApi.class
- cd pm_java; javah jportmidi.JPortMidiApi
- mv pm_java/jportmidi_JportMidiApi.h pm_java/pmjni
-
-JAVASRC = pmdefaults/PmDefaultsFrame.java \
- pmdefaults/PmDefaults.java \
- jportmidi/JPortMidiApi.java jportmidi/JPortMidi.java \
- jportmidi/JPortMidiException.java
-
-# this compiles ALL of the java code
-pm_java/jportmidi/JPortMidiApi.class: $(JAVASRC:%=pm_java/%)
- cd pm_java; javac $(JAVASRC)
-
-$(CONFIG)/libpmjni.dylib:
- mkdir -p $(CONFIG)
- cd $(CONFIG); make -f ../pm_mac/$(MAKEFILE)
-
-pmdefaults: $(CONFIG)/libpmjni.dylib pm_java/jportmidi/JPortMidiApi.class
-ifeq ($(CONFIG),Debug)
- echo "Error: you cannot build pmdefaults in a Debug configuration \n\
- You should use configuration=Release in the Makefile command line. "
- @exit 2
-endif
- xcodebuild -project pm_mac/pm_mac.xcodeproj \
- -configuration Release -target PmDefaults
- echo "pmdefaults java application is made"
-
diff --git a/portmidi/pm_mac/README_MAC.txt b/portmidi/pm_mac/README_MAC.txt deleted file mode 100644 index 41e8341..0000000 --- a/portmidi/pm_mac/README_MAC.txt +++ /dev/null @@ -1,65 +0,0 @@ -README_MAC.txt for PortMidi
-Roger Dannenberg
-20 nov 2009
-
-revised Mar 2024 to remove pmdefaults references
-revised Jan 2022 for the PortMidi/portmidi repo on github.com
-revised 20 Sep 2010 for Xcode 4.3.2 and CMake 2.8.8
-
-This documents how I build PortMidi for macOS. It's not the only way,
-and command-line/scripting enthusiasts will say it's not even a good
-way. Feel free to contribute your approach if you are willing to
-describe it carefully and test it.
-
-Install Xcode and the CMake application, CMake.app. I use the GUI
-version of CMake which makes it easy to see/edit variables and
-options.
-
-==== USING CMAKE ====
-
-Run CMake.app and select your portmidi repo working directory as the
-location for source and build. (Yes, I use so called "in-tree"
-builds -- it doesn't hurt, but I don't think it is necessary.)
-
-Default settings should all be fine, but select options under BUILD if
-you wish:
-
-BUILD_NATIVE_JAVA_INTERFACE to build a Java interface (JNI) library.
-
-BUILD_PORTMIDI_TESTS to create some test programs. Of particular
-interest are test/mm, a handy command-line MIDI Input Monitor, and
-test/testio, a simple command-line program to send or receive some
-MIDI notes in case you need a quick test: What devices do I have? Does
-this input work? Does this output work?
-
-I disable BUILD_SHARED_LIBS and always link statically: Static linking only
-adds about 40KB to any application and then you don't have to worry
-about versions, instally, copying or finding the dynamic link library,
-etc.
-
-To make sure you link statically, I rename the library to
-libportmidi_static.a. To do this, set PM_STATIC_LIB_NAME (in CMake,
-under the "PM" group) to "portmidi_static", and of course your
-application will have to specify portmidi_static as the library to
-link to.
-
-If you are building simple command-line applications, you might want
-to enable PM_CHECK_ERRORS. If you do, then calls into the PortMidi
-library will print error messages and exit in the event of an error
-(such as trying to open a device that does not exist). This saves you
-from having to check for errors everytime you call a library function
-or getting confused when errors are detected but not reported. For
-high-quality applications, do NOT enable PM_CHECK_ERRORS -- any
-failure could immediately abort your whole application, which is not
-very friendly to users.
-
-Click on Configure (maybe a couple of times).
-
-Click on Generate and make an Xcode project.
-
-Open portmidi/portmidi.xcodeproj with Xcode and build what you
-need. The simplest thing is to build the ALL_BUILD target. Be careful
-to specify a Debug or Release depending on what you want. "ALL_BUILD"
-is a misnomer -- it only builds the version you select.
-
-
diff --git a/portmidi/pm_mac/pmmac.c b/portmidi/pm_mac/pmmac.c deleted file mode 100755 index 48ac17a..0000000 --- a/portmidi/pm_mac/pmmac.c +++ /dev/null @@ -1,44 +0,0 @@ -/* pmmac.c -- PortMidi os-dependent code */
-
-/* This file only needs to implement:
-pm_init(), which calls various routines to register the
-available midi devices,
-Pm_GetDefaultInputDeviceID(), and
-Pm_GetDefaultOutputDeviceID().
-It is seperate from pmmacosxcm because we might want to register
-non-CoreMIDI devices.
-*/
-
-#include "stdlib.h"
-#include "portmidi.h"
-#include "pmutil.h"
-#include "pminternal.h"
-#include "pmmacosxcm.h"
-
-void pm_init(void)
-{
- pm_macosxcm_init();
-}
-
-
-void pm_term(void)
-{
- pm_macosxcm_term();
-}
-
-PmDeviceID Pm_GetDefaultInputDeviceID(void)
-{
- Pm_Initialize();
- return pm_default_input_device_id;
-}
-
-PmDeviceID Pm_GetDefaultOutputDeviceID(void) {
- Pm_Initialize();
- return pm_default_output_device_id;
-}
-
-void *pm_alloc(size_t s) { return malloc(s); }
-
-void pm_free(void *ptr) { free(ptr); }
-
-
diff --git a/portmidi/pm_mac/pmmacosxcm.c b/portmidi/pm_mac/pmmacosxcm.c deleted file mode 100755 index e8b196c..0000000 --- a/portmidi/pm_mac/pmmacosxcm.c +++ /dev/null @@ -1,1179 +0,0 @@ -/* - * Platform interface to the MacOS X CoreMIDI framework - * - * Jon Parise <jparise at cmu.edu> - * and subsequent work by Andrew Zeldis and Zico Kolter - * and Roger B. Dannenberg - * - * $Id: pmmacosx.c,v 1.17 2002/01/27 02:40:40 jon Exp $ - */ - -/* Notes: - - Since the input and output streams are represented by - MIDIEndpointRef values and almost no other state, we store the - MIDIEndpointRef on pm_descriptors[midi->device_id].descriptor. - - OS X does not seem to have an error-code-to-text function, so we - will just use text messages instead of error codes. - - Virtual device input synchronization: Once we create a virtual - device, it is always "on" and receiving messages, but it must drop - messages unless the device has been opened with Pm_OpenInput. To - open, the main thread should create all the data structures, then - call OSMemoryBarrier so that writes are observed, then set - is_opened = TRUE. To close without locks, we need to get the - callback to set is_opened to FALSE before we free data structures; - otherwise, there's a race condition where closing could delete - structures in use by the virtual_read_callback function. We send - 8 MIDI resets (FF) in a single packet to our own port to signal - the virtual_read_callback to close it. Then, we wait for the - callback to recognize the "close" packet and reset is_opened. - - Device scanning is done when you first open an application. - PortMIDI does not actively update the devices. Instead, you must - Pm_Terminate() and Pm_Initialize(), basically starting over. But - CoreMIDI does not have a way to shut down(!), and even - MIDIClientDispose() somehow retains state (and docs say do not - call it even if it worked). The solution, apparently, is to - call CFRunLoopRunInMode(), which somehow updates CoreMIDI - state. - - But when do we call CFRunLoopRunInMode()? I tried calling it - in midi_in_poll() which is called when you call Pm_Read() since - that is called often. I observed that this caused the program - to block for as long as 50ms and fairly often for 2 or 3ms. - What was Apple thinking? Is it really OK to design systems that - can only function with a tricky multi-threaded, non-blocking - priority-based solution, and then not provide a proof of concept - or documentation? Or is Apple's design really flawed? If anyone - at Apple reads this, please let me know -- I'm curious. - - But I digress... Here's the PortMidi approach: Since - CFRunLoopRunInMode() is potentially a non-realtime operation, - we only call it in Pm_Initialize(), where other calls to look - up devices and device names are quite slow to begin with. Again, - PortMidi does not actively scan for new or deleted devices, so - if devices change, you won't see it until the next Pm_Terminate - and Pm_Initialize. - - Calling CFRunLoopRunInMode() once is probably not enough. There - might be better way, but it seems to work to just call it 100 - times and insert 20 1ms delays (in case some inter-process - communication or synchronization is going on). - This adds 20ms to the wall time of Pm_Initialize(), but it - typically runs 30ms to much more (~4s), so this has little impact. - */ - -#include <stdlib.h> - -/* turn on lots of debugging print statements */ -#define CM_DEBUG if (0) -/* #define CM_DEBUG if (1) */ - -#include "portmidi.h" -#include "pmutil.h" -#include "pminternal.h" -#include "porttime.h" -#include "pmmacosxcm.h" - -#include <stdio.h> -#include <string.h> - -#include <CoreServices/CoreServices.h> -#include <CoreMIDI/MIDIServices.h> -#include <CoreAudio/HostTime.h> -#include <unistd.h> -#include <libkern/OSAtomic.h> - -#define PACKET_BUFFER_SIZE 1024 -/* maximum overall data rate (OS X limits MIDI rate in case there - * is a cycle among IAC ports. - */ - -#define MAX_BYTES_PER_S 5400 - -/* Apple reports that packets are dropped when the MIDI bytes/sec - exceeds 15000. This is computed by "tracking the number of MIDI - bytes scheduled into 1-second buckets over the last six seconds and - averaging these counts." This was confirmed in measurements - (2021) with pm_test/fast.c and pm_test/fastrcv.c Now, in 2022, with - macOS 12, pm_test/fast{rcv}.c show problems begin at 6000 bytes/sec. - Previously, we set MAX_BYTES_PER_S to 14000. This is reduced to - 5400 based on testing (which shows 5700 is too high) to fix the - packet loss problem that showed up with macOS 12. - - Experiments show this restriction applies to IAC bus MIDI, but not - to hardware interfaces. (I measured 0.5 Mbps each way over USB to a - Teensy 3.2 microcontroller implementing a USB MIDI loopback. Maybe - it would get 1 Mbps one-way, which would make the CoreMIDI - restriction 18x slower than USB. Maybe other USB MIDI - implementations are faster -- USB top speed for other protocols is - certainly higher than 1 Mbps!) - - This is apparently based on timestamps, not on real time, so we - have to avoid constructing packets that schedule high speed output - regardless of when writes occur. The solution is to alter - timestamps to limit data rates. This adds a slight time - distortion, e.g. an 11 note chord with all notes on the same - timestamp will be altered so that the last message is delayed by - 11 messages x 3 bytes/message / 5400 bytes/second = 6.1 ms. - Note that this is about 2x MIDI speed, but at least 18x slower - than USB MIDI. - - Altering timestamps creates another problem, which is that a sender - that exceeds the maximum rate can queue up an unbounded number of - messages. With non-USB MIDI devices, you could be writing 5x faster - to CoreMIDI than the hardware interface can send, causing an - unbounded backlog, not to mention that the output stream will be a - steady byte stream (e.g., one 3-byte MIDI message every 0.55 ms), - losing any original timing or rhythm. PortMidi does not guarantee - delivery if, over the long run, you write faster than the hardware - can send. - - The LIMIT_RATE symbol, if defined (which is the default), enables - code to modify timestamps for output to an IAC device as follows: - - Before a packet is formed, the message timestamp is set to the - maximum of the PortMidi timestamp (converted to CoreMIDI time) - and min_next_time. After each send, min_next_time is updated to - the packet time + packet length * delay_per_byte, which limits - the scheduled bytes-per-second. Also, after each packet list - flush, min_next_time is updated to the maximum of min_next_time - and the real time, which prevents many bytes to be scheduled in - the past. (We could more directly just say packets are never - scheduled in the past, but we prefer to get the current time -- a - system call -- only when we perform the more expensive operation - of flushing packets, so that's when we update min_next_time to - the current real time. If we are sending a lot, we have to flush - a lot, so the time will be updated frequently when it matters.) - - This possible adjustment to timestamps can distort accurate - timestamps by up to 0.556 us per 3-byte MIDI message. - - Nothing blocks the sender from queueing up an arbitrary number of - messages. Timestamps should be used for accurate timing by sending - timestamped messages a little ahead of real time, not for - scheduling an entire MIDI sequence at once! - */ -#define LIMIT_RATE 1 - -#define SYSEX_BUFFER_SIZE 128 -/* What is the maximum PortMidi device number for an IAC device? A - * cleaner design would be to not use the endpoint as our device - * representation. Instead, we could have a private extensible struct - * to keep all device information, including whether the device is - * implemented with the AppleMIDIIACDriver, which we need because we - * have to limit the data rate to this particular driver to avoid - * dropping messages. Rather than rewrite a lot of code, I am just - * allocating 64 bytes to flag which devices are IAC ones. If an IAC - * device number is greater than 63, PortMidi will fail to limit - * writes to it, but will not complain and will not access memory - * outside the 64-element array of char. - */ -#define MAX_IAC_NUM 63 - -#define VERBOSE_ON 1 -#define VERBOSE if (VERBOSE_ON) - -#define MIDI_SYSEX 0xf0 -#define MIDI_EOX 0xf7 -#define MIDI_CLOCK 0xf8 -#define MIDI_STATUS_MASK 0x80 - -// "Ref"s are pointers on 32-bit machines and ints on 64 bit machines -// NULL_REF is our representation of either 0 or NULL -#ifdef __LP64__ -#define NULL_REF 0 -#else -#define NULL_REF NULL -#endif - -static MIDIClientRef client = NULL_REF; /* Client handle to the MIDI server */ -static MIDIPortRef portIn = NULL_REF; /* Input port handle */ -static MIDIPortRef portOut = NULL_REF; /* Output port handle */ -static char isIAC[MAX_IAC_NUM + 1]; /* is device an IAC device */ - -extern pm_fns_node pm_macosx_in_dictionary; -extern pm_fns_node pm_macosx_out_dictionary; - -typedef struct coremidi_info_struct { - int is_virtual; /* virtual device (TRUE) or actual device (FALSE)? */ - UInt64 delta; /* difference between stream time and real time in ns */ - int sysex_mode; /* middle of sending sysex */ - uint32_t sysex_word; /* accumulate data when receiving sysex */ - uint32_t sysex_byte_count; /* count how many received */ - char error[PM_HOST_ERROR_MSG_LEN]; - char callback_error[PM_HOST_ERROR_MSG_LEN]; - Byte packetBuffer[PACKET_BUFFER_SIZE]; - MIDIPacketList *packetList; /* a pointer to packetBuffer */ - MIDIPacket *packet; - Byte sysex_buffer[SYSEX_BUFFER_SIZE]; /* temp storage for sysex data */ - MIDITimeStamp sysex_timestamp; /* host timestamp to use with sysex data */ - /* allow for running status (is running status possible here? -rbd): -cpr */ - UInt64 min_next_time; /* when can the next send take place? (host time) */ - int isIACdevice; - Float64 us_per_host_tick; /* host clock frequency, units of min_next_time */ - UInt64 host_ticks_per_byte; /* host clock units per byte at maximum rate */ -} coremidi_info_node, *coremidi_info_type; - -/* private function declarations */ -MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp); // returns host time -PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp); // returns ms - -char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint, int *iac_flag); - -static PmError check_hosterror(OSStatus err, const char *msg) -{ - if (err != noErr) { - snprintf(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, "Host error %ld: %s", (long) err, msg); - pm_hosterror = TRUE; - return pmHostError; - } - return pmNoError; -} - - -static PmTimestamp midi_synchronize(PmInternal *midi) -{ - coremidi_info_type info = (coremidi_info_type) midi->api_info; - UInt64 pm_stream_time_2 = // current time in ns - AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); - PmTimestamp real_time; // in ms - UInt64 pm_stream_time; // in ns - /* if latency is zero and this is an output, there is no - time reference and midi_synchronize should never be called */ - assert(midi->time_proc); - assert(midi->is_input || midi->latency != 0); - do { - /* read real_time between two reads of stream time */ - pm_stream_time = pm_stream_time_2; - real_time = (*midi->time_proc)(midi->time_info); - pm_stream_time_2 = AudioConvertHostTimeToNanos( - AudioGetCurrentHostTime()); - /* repeat if more than 0.5 ms has elapsed */ - } while (pm_stream_time_2 > pm_stream_time + 500000); - info->delta = pm_stream_time - ((UInt64) real_time * (UInt64) 1000000); - midi->sync_time = real_time; - return real_time; -} - - -/* called when MIDI packets are received */ -static void read_callback(const MIDIPacketList *newPackets, PmInternal *midi) -{ - PmTimestamp timestamp; - MIDIPacket *packet; - unsigned int packetIndex; - uint32_t now; - /* Retrieve the context for this connection */ - coremidi_info_type info = (coremidi_info_type) midi->api_info; - assert(info); - - CM_DEBUG printf("read_callback: numPackets %d: ", newPackets->numPackets); - - /* synchronize time references every 100ms */ - now = (*midi->time_proc)(midi->time_info); - if (midi->first_message || midi->sync_time + 100 /*ms*/ < now) { - /* time to resync */ - now = midi_synchronize(midi); - midi->first_message = FALSE; - } - - packet = (MIDIPacket *) &newPackets->packet[0]; - /* hardware devices get untimed messages and apply timestamps. We - * want to preserve them because they should be more accurate than - * applying the current time here. virtual devices just pass on the - * packet->timeStamp, which could be anything. PortMidi says the - * PortMidi timestamp is the time the message is received. We do not - * know if we are receiving from a device driver or a virtual device. - * PortMidi sends to virtual devices get a current timestamp, so we - * can treat them as the receive time. If the timestamp is zero, - * suggested by CoreMIDI as the value to use for immediate delivery, - * then we plug in `now` which is obtained above. If another - * application sends bogus non-zero timestamps, we will convert them - * to this port's reference time and pass them as event.timestamp. - * Receiver beware. - */ - CM_DEBUG printf("read_callback packet @ %lld ns (host %lld) " - "status %x length %d\n", - AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()), - AudioGetCurrentHostTime(), - packet->data[0], packet->length); - for (packetIndex = 0; packetIndex < newPackets->numPackets; packetIndex++) { - /* Set the timestamp and dispatch this message */ - CM_DEBUG printf(" packet->timeStamp %lld ns %lld host\n", - packet->timeStamp, - AudioConvertHostTimeToNanos(packet->timeStamp)); - if (packet->timeStamp == 0) { - timestamp = now; - } else { - timestamp = (PmTimestamp) /* explicit conversion */ ( - (AudioConvertHostTimeToNanos(packet->timeStamp) - info->delta) / - (UInt64) 1000000); - } - pm_read_bytes(midi, packet->data, packet->length, timestamp); - packet = MIDIPacketNext(packet); - } -} - -/* callback for real devices - redirects to read_callback */ -static void device_read_callback(const MIDIPacketList *newPackets, - void *refCon, void *connRefCon) -{ - read_callback(newPackets, (PmInternal *) connRefCon); -} - - -/* callback for virtual devices - redirects to read_callback */ -static void virtual_read_callback(const MIDIPacketList *newPackets, - void *refCon, void *connRefCon) -{ - /* this refCon is the device ID -- if there is a valid ID and - the pm_descriptors table has a non-null pointer to a PmInternal, - then then device is open and should receive this data */ - PmDeviceID id = (PmDeviceID) (size_t) refCon; - if (id >= 0 && id < pm_descriptor_len) { - if (pm_descriptors[id].pub.opened) { - /* check for close request (7 reset status bytes): */ - if (newPackets->numPackets == 1 && - newPackets->packet[0].length == 8 && - /* CoreMIDI declares packets with 4-byte alignment, so we - * should be safe to test for 8 0xFF's as 2 32-bit values: */ - *(SInt32 *) &newPackets->packet[0].data[0] == -1 && - *(SInt32 *) &newPackets->packet[0].data[4] == -1) { - CM_DEBUG printf("got close request packet\n"); - pm_descriptors[id].pub.opened = FALSE; - return; - } else { - read_callback(newPackets, pm_descriptors[id].pm_internal); - } - } - } -} - - -/* allocate and initialize our internal coremidi connection info */ -static coremidi_info_type create_macosxcm_info(int is_virtual, int is_input) -{ - coremidi_info_type info = (coremidi_info_type) - pm_alloc(sizeof(coremidi_info_node)); - if (!info) { - return NULL; - } - info->is_virtual = is_virtual; - info->delta = 0; - info->sysex_mode = FALSE; - info->sysex_word = 0; - info->sysex_byte_count = 0; - info->packet = NULL; - info->min_next_time = 0; - info->isIACdevice = FALSE; - info->us_per_host_tick = 1000000.0 / AudioGetHostClockFrequency(); - info->host_ticks_per_byte = - (UInt64) (1000000.0 / (info->us_per_host_tick * MAX_BYTES_PER_S)); - info->packetList = (is_input ? NULL : - (MIDIPacketList *) info->packetBuffer); - return info; -} - - -static PmError midi_in_open(PmInternal *midi, void *driverInfo) -{ - MIDIEndpointRef endpoint; - coremidi_info_type info; - OSStatus macHostError; - int is_virtual = pm_descriptors[midi->device_id].pub.is_virtual; - - /* if this is an external device, descriptor is a MIDIEndpointRef. - * if this is a virtual device for this application, descriptor is NULL. - */ - if (!is_virtual) { - endpoint = (MIDIEndpointRef) (intptr_t) - pm_descriptors[midi->device_id].descriptor; - if (endpoint == NULL_REF) { - return pmInvalidDeviceId; - } - } - - info = create_macosxcm_info(is_virtual, TRUE); - midi->api_info = info; - if (!info) { - return pmInsufficientMemory; - } - if (!is_virtual) { - macHostError = MIDIPortConnectSource(portIn, endpoint, midi); - if (macHostError != noErr) { - midi->api_info = NULL; - pm_free(info); - return check_hosterror(macHostError, - "MIDIPortConnectSource() in midi_in_open()"); - } - } - return pmNoError; -} - -static PmError midi_in_close(PmInternal *midi) -{ - MIDIEndpointRef endpoint; - OSStatus macHostError; - PmError err = pmNoError; - - coremidi_info_type info = (coremidi_info_type) midi->api_info; - - if (!info) return pmBadPtr; - - endpoint = (MIDIEndpointRef) (intptr_t) - pm_descriptors[midi->device_id].descriptor; - if (endpoint == NULL_REF) { - return pmBadPtr; - } - - if (!info->is_virtual) { - /* shut off the incoming messages before freeing data structures */ - macHostError = MIDIPortDisconnectSource(portIn, endpoint); - /* If the source closes, you get paramErr == -50 here. It seems - * possible to monitor changes like sources closing by getting - * notifications ALL changes, but the CoreMIDI documentation is - * really terrible overall, and it seems easier to just ignore - * this host error. - */ - if (macHostError != noErr && macHostError != -50) { - pm_hosterror = TRUE; - err = check_hosterror(macHostError, - "MIDIPortDisconnectSource() in midi_in_close()"); - } - } else { - /* make "close virtual port" message */ - SInt64 close_port_bytes = 0xFFFFFFFFFFFFFFFF; - /* memory requirements: packet count (4), timestamp (8), length (2), - * data (8). Total: 22, but we allocate plenty more: - */ - Byte packetBuffer[64]; - MIDIPacketList *plist = (MIDIPacketList *) packetBuffer; - MIDIPacket *packet = MIDIPacketListInit(plist); - MIDIPacketListAdd(plist, 64, packet, 0, 8, - (const Byte *) &close_port_bytes); - macHostError = MIDISend(portOut, endpoint, plist); - if (macHostError != noErr) { - err = check_hosterror(macHostError, "MIDISend() (PortMidi close " - "port packet) in midi_in_close()"); - } - /* when packet is delivered, callback thread will clear opened; - * we must wait for that before removing the input queues etc. - * Maybe this could use signals of some kind, but if signals use - * locks, locks can cause priority inversion problems, so we will - * just sleep as needed. On the MIDI timescale, inserting a 0.5ms - * latency should be OK, as the application has no business - * opening/closing devices during time-critical moments. - * - * We expect the MIDI thread to close the device quickly (<0.5ms), - * but we wait up to 50ms in case something terrible happens like - * getting paged out in the middle of deliving packets to this - * virtual device. If there is still no response, we time out and - * force the close without the MIDI thread (even this will probably - * succeed - the problem would be: this thread proceeds to delete - * the input queues, and the freed memory is reallocated and - * overwritten so that queues are no longer usable. Meanwhile, - * the MIDI thread has already begun to deliver packets, so the - * check for opened == TRUE passed, but MIDI thread does not insert - * into queue until queue is freed, reallocated and overwritten. - */ - for (int i = 0; i < 100; i++) { /* up to 50ms delay */ - if (!pm_descriptors[midi->device_id].pub.opened) { - break; - } - usleep(500); /* 0.5ms */ - } - pm_descriptors[midi->device_id].pub.opened = FALSE; /* force it */ - } - midi->api_info = NULL; - pm_free(info); - return err; -} - - -static PmError midi_out_open(PmInternal *midi, void *driverInfo) -{ - coremidi_info_type info; - int is_virtual = pm_descriptors[midi->device_id].pub.is_virtual; - - info = create_macosxcm_info(is_virtual, FALSE); - if (midi->device_id <= MAX_IAC_NUM) { - info->isIACdevice = isIAC[midi->device_id]; - CM_DEBUG printf("midi_out_open isIACdevice %d\n", info->isIACdevice); - } - midi->api_info = info; - if (!info) { - return pmInsufficientMemory; - } - return pmNoError; -} - - -static PmError midi_out_close(PmInternal *midi) -{ - coremidi_info_type info = (coremidi_info_type) midi->api_info; - if (!info) return pmBadPtr; - midi->api_info = NULL; - pm_free(info); - return pmNoError; -} - - -/* MIDIDestinationCreate apparently cannot create a virtual device - * without a callback and a "refCon" parameter, but when we create - * a virtual device, we do not want a PortMidi stream yet -- that - * should wait for the user to open the stream. So, for the refCon, - * use the PortMidi device ID. The callback will check if the - * device is opened within PortMidi, and if so, use the pm_descriptors - * table to locate the corresponding PmStream. - */ -static PmError midi_create_virtual(int is_input, const char *name, - void *device_info) -{ - OSStatus macHostError; - MIDIEndpointRef endpoint; - CFStringRef nameRef; - PmDeviceID id = pm_add_device("CoreMIDI", name, is_input, TRUE, NULL, - (is_input ? &pm_macosx_in_dictionary : - &pm_macosx_out_dictionary)); - if (id < 0) { /* error -- out of memory or name conflict? */ - return id; - } - - nameRef = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); - if (is_input) { - macHostError = MIDIDestinationCreate(client, nameRef, - virtual_read_callback, (void *) (intptr_t) id, &endpoint); - } else { - macHostError = MIDISourceCreate(client, nameRef, &endpoint); - } - CFRelease(nameRef); - - if (macHostError != noErr) { - /* undo the device we just allocated */ - pm_undo_add_device(id); - return check_hosterror(macHostError, (is_input ? - "MIDIDestinationCreateWithProtocol() in midi_create_virtual()" : - "MIDISourceCreateWithProtocol() in midi_create_virtual()")); - } - - /* Do we have a manufacturer name? If not, set to "PortMidi" */ - const char *mfr_name = "PortMidi"; - PmSysDepInfo *info = (PmSysDepInfo *) device_info; - /* the version where pmKeyCoreMidiManufacturer was introduced is 210 */ - if (info && info->structVersion >= 210) { - int i; - for (i = 0; i < info->length; i++) { /* search for key */ - if (info->properties[i].key == pmKeyCoreMidiManufacturer) { - mfr_name = info->properties[i].value; - break; - } /* no other keys are recognized; they are ignored */ - } - } - nameRef = CFStringCreateWithCString(NULL, mfr_name, kCFStringEncodingUTF8); - MIDIObjectSetStringProperty(endpoint, kMIDIPropertyManufacturer, nameRef); - CFRelease(nameRef); - - pm_descriptors[id].descriptor = (void *) (intptr_t) endpoint; - return id; -} - - -static PmError midi_delete_virtual(PmDeviceID id) -{ - MIDIEndpointRef endpoint; - OSStatus macHostError; - - endpoint = (MIDIEndpointRef) (long) pm_descriptors[id].descriptor; - if (endpoint == NULL_REF) { - return pmBadPtr; - } - macHostError = MIDIEndpointDispose(endpoint); - return check_hosterror(macHostError, - "MIDIEndpointDispose() in midi_in_close()"); -} - - -static PmError midi_abort(PmInternal *midi) -{ - OSStatus macHostError; - MIDIEndpointRef endpoint = (MIDIEndpointRef) (intptr_t) - pm_descriptors[midi->device_id].descriptor; - macHostError = MIDIFlushOutput(endpoint); - return check_hosterror(macHostError, - "MIDIFlushOutput() in midi_abort()"); -} - - -static PmError midi_write_flush(PmInternal *midi, PmTimestamp timestamp) -{ - OSStatus macHostError = 0; - coremidi_info_type info = (coremidi_info_type) midi->api_info; - MIDIEndpointRef endpoint = (MIDIEndpointRef) (intptr_t) - pm_descriptors[midi->device_id].descriptor; - assert(info); - assert(endpoint); - if (info->packet != NULL) { - /* out of space, send the buffer and start refilling it */ - /* update min_next_time each flush to support rate limit */ - UInt64 host_now = AudioGetCurrentHostTime(); - if (host_now > info->min_next_time) - info->min_next_time = host_now; - if (info->is_virtual) { - macHostError = MIDIReceived(endpoint, info->packetList); - } else { - macHostError = MIDISend(portOut, endpoint, info->packetList); - } - info->packet = NULL; /* indicate no data in packetList now */ - } - return check_hosterror(macHostError, (info->is_virtual ? - "MIDIReceived() in midi_write()" : - "MIDISend() in midi_write()")); -} - - -static PmError send_packet(PmInternal *midi, Byte *message, - unsigned int messageLength, MIDITimeStamp timestamp) -{ - PmError err; - coremidi_info_type info = (coremidi_info_type) midi->api_info; - assert(info); - - CM_DEBUG printf("add %d to packet %p len %d timestamp %lld @ %lld ns " - "(host %lld)\n", - message[0], info->packet, messageLength, timestamp, - AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()), - AudioGetCurrentHostTime()); - info->packet = MIDIPacketListAdd(info->packetList, - sizeof(info->packetBuffer), info->packet, - timestamp, messageLength, message); -#if LIMIT_SEND_RATE - info->byte_count += messageLength; -#endif - if (info->packet == NULL) { - /* out of space, send the buffer and start refilling it */ - /* make midi->packet non-null to fool midi_write_flush into sending */ - info->packet = (MIDIPacket *) 4; - /* timestamp is 0 because midi_write_flush ignores timestamp since - * timestamps are already in packets. The timestamp parameter is here - * because other API's need it. midi_write_flush can be called - * from system-independent code that must be cross-API. - */ - if ((err = midi_write_flush(midi, 0)) != pmNoError) return err; - info->packet = MIDIPacketListInit(info->packetList); - assert(info->packet); /* if this fails, it's a programming error */ - info->packet = MIDIPacketListAdd(info->packetList, - sizeof(info->packetBuffer), info->packet, - timestamp, messageLength, message); - assert(info->packet); /* can't run out of space on first message */ - } - return pmNoError; -} - - -static PmError midi_write_short(PmInternal *midi, PmEvent *event) -{ - PmTimestamp when = event->timestamp; - PmMessage what = event->message; - MIDITimeStamp timestamp; - coremidi_info_type info = (coremidi_info_type) midi->api_info; - Byte message[4]; - unsigned int messageLength; - - if (info->packet == NULL) { - info->packet = MIDIPacketListInit(info->packetList); - /* this can never fail, right? failure would indicate something - unrecoverable */ - assert(info->packet); - } - - /* PortMidi specifies that incoming timestamps are the receive - * time. Devices attach their receive times, but virtual devices - * do not. Instead, they pass along whatever timestamp was sent to - * them. We do not know if we are connected to real or virtual - * device. To avoid wild timestamps on the receiving end, we - * consider 2 cases: PortMidi timestamp is zero or latency is - * zero. Both mean send immediately, so we attach the current time - * which will go out immediately and arrive with a sensible - * timestamp (not zero and not zero mapped to the client's local - * time). Otherwise, we assume the timestamp is reasonable. It - * might be slighly in the past, but we pass it along after - * translation to MIDITimeStamp units. - * - * Compute timestamp: use current time if timestamp is zero or - * latency is zero. Both mean no timing and send immediately. - */ - if (when == 0 || midi->latency == 0) { - timestamp = AudioGetCurrentHostTime(); - } else { /* translate PortMidi time + latency to CoreMIDI time */ - timestamp = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + - info->delta; - timestamp = AudioConvertNanosToHostTime(timestamp); - } - - message[0] = Pm_MessageStatus(what); - message[1] = Pm_MessageData1(what); - message[2] = Pm_MessageData2(what); - messageLength = pm_midi_length((int32_t) what); - -#ifdef LIMIT_RATE - /* Make sure we go forward in time. */ - if (timestamp < info->min_next_time) { - timestamp = info->min_next_time; - } - /* Note that if application is way behind and slowly catching up, then - * timestamps could be increasing faster than real time, and since - * timestamps are used to estimate data rate, our estimate could be - * low, causing CoreMIDI to drop packets. This seems very unlikely. - */ - if (info->isIACdevice || info->is_virtual) { - info->min_next_time = timestamp + messageLength * - info->host_ticks_per_byte; - } -#endif - /* Add this message to the packet list */ - return send_packet(midi, message, messageLength, timestamp); -} - - -static PmError midi_begin_sysex(PmInternal *midi, PmTimestamp when) -{ - UInt64 when_ns; - coremidi_info_type info = (coremidi_info_type) midi->api_info; - assert(info); - info->sysex_byte_count = 0; - - /* compute timestamp */ - if (when == 0) when = midi->now; - /* if latency == 0, midi->now is not valid. We will just set it to zero */ - if (midi->latency == 0) when = 0; - when_ns = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + - info->delta; - info->sysex_timestamp = - (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns); - UInt64 now; /* only make system time call when writing a virtual port */ - if (info->is_virtual && info->sysex_timestamp < - (now = AudioGetCurrentHostTime())) { - info->sysex_timestamp = now; - } - - if (info->packet == NULL) { - info->packet = MIDIPacketListInit(info->packetList); - /* this can never fail, right? failure would indicate something - unrecoverable */ - assert(info->packet); - } - return pmNoError; -} - - -static PmError midi_end_sysex(PmInternal *midi, PmTimestamp when) -{ - PmError err; - coremidi_info_type info = (coremidi_info_type) midi->api_info; - assert(info); - -#ifdef LIMIT_RATE - /* make sure we go foreward in time */ - if (info->sysex_timestamp < info->min_next_time) - info->sysex_timestamp = info->min_next_time; - - if (info->isIACdevice) { - info->min_next_time = info->sysex_timestamp + info->sysex_byte_count * - info->host_ticks_per_byte; - } -#endif - - /* now send what's in the buffer */ - err = send_packet(midi, info->sysex_buffer, info->sysex_byte_count, - info->sysex_timestamp); - info->sysex_byte_count = 0; - if (err != pmNoError) { - info->packet = NULL; /* flush everything in the packet list */ - } - return err; -} - - -static PmError midi_write_byte(PmInternal *midi, unsigned char byte, - PmTimestamp timestamp) -{ - coremidi_info_type info = (coremidi_info_type) midi->api_info; - assert(info); - if (info->sysex_byte_count >= SYSEX_BUFFER_SIZE) { - PmError err = midi_end_sysex(midi, timestamp); - if (err != pmNoError) return err; - } - info->sysex_buffer[info->sysex_byte_count++] = byte; - return pmNoError; -} - - -static PmError midi_write_realtime(PmInternal *midi, PmEvent *event) -{ - /* to send a realtime message during a sysex message, first - flush all pending sysex bytes into packet list */ - PmError err = midi_end_sysex(midi, 0); - if (err != pmNoError) return err; - /* then we can just do a normal midi_write_short */ - return midi_write_short(midi, event); -} - - -static unsigned int midi_check_host_error(PmInternal *midi) -{ - return FALSE; -} - - -MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp) -{ - UInt64 nanos; - if (timestamp <= 0) { - return (MIDITimeStamp)0; - } else { - nanos = (UInt64)timestamp * (UInt64)1000000; - return (MIDITimeStamp)AudioConvertNanosToHostTime(nanos); - } -} - - -PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp) -{ - UInt64 nanos; - nanos = AudioConvertHostTimeToNanos(timestamp); - return (PmTimestamp)(nanos / (UInt64)1000000); -} - - -// -// Code taken from http://developer.apple.com/qa/qa2004/qa1374.html -////////////////////////////////////// -// Obtain the name of an endpoint without regard for whether it has connections. -// The result should be released by the caller. -CFStringRef EndpointName(MIDIEndpointRef endpoint, bool isExternal, - int *iac_flag) -{ - CFMutableStringRef result = CFStringCreateMutable(NULL, 0); - CFStringRef str; - *iac_flag = FALSE; - - // begin with the endpoint's name - str = NULL; - MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &str); - if (str != NULL) { - CFStringAppend(result, str); - CFRelease(str); - } - MIDIEntityRef entity = NULL_REF; - MIDIEndpointGetEntity(endpoint, &entity); - if (entity == NULL_REF) { - // probably virtual - return result; - } - if (!isExternal) { /* detect IAC devices */ - //extern const CFStringRef kMIDIPropertyDriverOwner; - MIDIObjectGetStringProperty(entity, kMIDIPropertyDriverOwner, &str); - if (str != NULL) { - char s[32]; /* driver name may truncate, but that's OK */ - CFStringGetCString(str, s, 31, kCFStringEncodingUTF8); - s[31] = 0; /* make sure it is terminated just to be safe */ - CM_DEBUG printf("driver %s\n", s); - *iac_flag = (strcmp(s, "com.apple.AppleMIDIIACDriver") == 0); - } - } - - if (CFStringGetLength(result) == 0) { - // endpoint name has zero length -- try the entity - str = NULL; - MIDIObjectGetStringProperty(entity, kMIDIPropertyName, &str); - if (str != NULL) { - CFStringAppend(result, str); - CFRelease(str); - } - } - // now consider the device's name - MIDIDeviceRef device = NULL_REF; - MIDIEntityGetDevice(entity, &device); - if (device == NULL_REF) - return result; - - str = NULL; - MIDIObjectGetStringProperty(device, kMIDIPropertyName, &str); - if (CFStringGetLength(result) == 0) { - CFRelease(result); - return str; - } - if (str != NULL) { - // if an external device has only one entity, throw away - // the endpoint name and just use the device name - if (isExternal && MIDIDeviceGetNumberOfEntities(device) < 2) { - CFRelease(result); - return str; - } else { - if (CFStringGetLength(str) == 0) { - CFRelease(str); - return result; - } - // does the entity name already start with the device name? - // (some drivers do this though they shouldn't) - // if so, do not prepend - if (CFStringCompareWithOptions(result, /* endpoint name */ - str, /* device name */ - CFRangeMake(0, CFStringGetLength(str)), 0) != - kCFCompareEqualTo) { - // prepend the device name to the entity name - if (CFStringGetLength(result) > 0) - CFStringInsert(result, 0, CFSTR(" ")); - CFStringInsert(result, 0, str); - } - CFRelease(str); - } - } - return result; -} - - -// Obtain the name of an endpoint, following connections. -// The result should be released by the caller. -static CFStringRef ConnectedEndpointName(MIDIEndpointRef endpoint, - int *iac_flag) -{ - CFMutableStringRef result = CFStringCreateMutable(NULL, 0); - CFStringRef str; - OSStatus err; - long i; - - // Does the endpoint have connections? - CFDataRef connections = NULL; - long nConnected = 0; - bool anyStrings = false; - err = MIDIObjectGetDataProperty(endpoint, kMIDIPropertyConnectionUniqueID, - &connections); - if (connections != NULL) { - // It has connections, follow them - // Concatenate the names of all connected devices - nConnected = CFDataGetLength(connections) / - (int32_t) sizeof(MIDIUniqueID); - if (nConnected) { - const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections)); - for (i = 0; i < nConnected; ++i, ++pid) { - MIDIUniqueID id = EndianS32_BtoN(*pid); - MIDIObjectRef connObject; - MIDIObjectType connObjectType; - err = MIDIObjectFindByUniqueID(id, &connObject, - &connObjectType); - if (err == noErr) { - if (connObjectType == kMIDIObjectType_ExternalSource || - connObjectType == kMIDIObjectType_ExternalDestination) { - // Connected to an external device's endpoint (>=10.3) - str = EndpointName((MIDIEndpointRef)(connObject), true, - iac_flag); - } else { - // Connected to an external device (10.2) - // (or something else, catch-all) - str = NULL; - MIDIObjectGetStringProperty(connObject, - kMIDIPropertyName, &str); - } - if (str != NULL) { - if (anyStrings) - CFStringAppend(result, CFSTR(", ")); - else anyStrings = true; - CFStringAppend(result, str); - CFRelease(str); - } - } - } - } - CFRelease(connections); - } - if (anyStrings) - return result; // caller should release result - - CFRelease(result); - - // Here, either the endpoint had no connections, or we failed to - // obtain names for any of them. - return EndpointName(endpoint, false, iac_flag); -} - - -char *cm_get_full_endpoint_name(MIDIEndpointRef endpoint, int *iac_flag) -{ - /* Thanks to Dan Wilcox for fixes for Unicode handling */ - CFStringRef fullName = ConnectedEndpointName(endpoint, iac_flag); - CFIndex utf16_len = CFStringGetLength(fullName) + 1; - CFIndex max_byte_len = CFStringGetMaximumSizeForEncoding( - utf16_len, kCFStringEncodingUTF8) + 1; - char* pmname = (char *) pm_alloc(CFStringGetLength(fullName) + 1); - - /* copy the string into our buffer; note that there may be some wasted - space, but the total waste is not large */ - CFStringGetCString(fullName, pmname, max_byte_len, kCFStringEncodingUTF8); - - /* clean up */ - if (fullName) CFRelease(fullName); - return pmname; -} - - -pm_fns_node pm_macosx_in_dictionary = { - none_write_short, - none_sysex, - none_sysex, - none_write_byte, - none_write_short, - none_write_flush, - none_synchronize, - midi_in_open, - midi_abort, - midi_in_close, - success_poll, - midi_check_host_error -}; - -pm_fns_node pm_macosx_out_dictionary = { - midi_write_short, - midi_begin_sysex, - midi_end_sysex, - midi_write_byte, - midi_write_realtime, - midi_write_flush, - midi_synchronize, - midi_out_open, - midi_abort, - midi_out_close, - success_poll, - midi_check_host_error -}; - - -/* We do nothing with callbacks, but generating the callbacks also - * updates CoreMIDI state. Callback may not be essential, but calling - * the CFRunLoopRunInMode is necessary. - */ -void cm_notify(const MIDINotification *msg, void *refCon) -{ - /* for debugging, trace change notifications: - const char *descr[] = { - "undefined (0)", - "kMIDIMsgSetupChanged", - "kMIDIMsgObjectAdded", - "kMIDIMsgObjectRemoved", - "kMIDIMsgPropertyChanged", - "kMIDIMsgThruConnectionsChanged", - "kMIDIMsgSerialPortOwnerChanged", - "kMIDIMsgIOError"}; - - printf("MIDI Notify, messageID %d (%s)\n", (int) msg->messageID, - descr[(int) msg->messageID]); - */ - return; -} - - -PmError pm_macosxcm_init(void) -{ - ItemCount numInputs, numOutputs, numDevices; - MIDIEndpointRef endpoint; - OSStatus macHostError = noErr; - char *error_text; - - memset(isIAC, 0, sizeof(isIAC)); /* initialize all FALSE */ - - /* Register interface CoreMIDI with create_virtual fn */ - pm_add_interf("CoreMIDI", &midi_create_virtual, &midi_delete_virtual); - /* no check for error return because this always succeeds */ - - /* Determine the number of MIDI devices on the system */ - numDevices = MIDIGetNumberOfDevices(); - - /* Return prematurely if no devices exist on the system - Note that this is not an error. There may be no devices. - Pm_CountDevices() will return zero, which is correct and - useful information - */ - if (numDevices <= 0) { - return pmNoError; - } - - /* Initialize the client handle */ - if (client == NULL_REF) { - macHostError = MIDIClientCreate(CFSTR("PortMidi"), &cm_notify, NULL, - &client); - } else { /* see notes above on device scanning */ - for (int i = 0; i < 100; i++) { - // look for any changes before scanning for devices - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true); - if (i % 5 == 0) Pt_Sleep(1); /* insert 20 delays */ - } - } - if (macHostError != noErr) { - error_text = "MIDIClientCreate() in pm_macosxcm_init()"; - goto error_return; - } - numInputs = MIDIGetNumberOfSources(); - numOutputs = MIDIGetNumberOfDestinations(); - - /* Create the input port */ - macHostError = MIDIInputPortCreate(client, CFSTR("Input port"), - device_read_callback, NULL, &portIn); - if (macHostError != noErr) { - error_text = "MIDIInputPortCreate() in pm_macosxcm_init()"; - goto error_return; - } - - /* Create the output port */ - macHostError = MIDIOutputPortCreate(client, CFSTR("Output port"), &portOut); - if (macHostError != noErr) { - error_text = "MIDIOutputPortCreate() in pm_macosxcm_init()"; - goto error_return; - } - - /* Iterate over the MIDI input devices */ - for (int i = 0; i < numInputs; i++) { - int iac_flag; - endpoint = MIDIGetSource(i); - if (endpoint == NULL_REF) { - continue; - } - /* Register this device with PortMidi */ - pm_add_device("CoreMIDI", - cm_get_full_endpoint_name(endpoint, &iac_flag), TRUE, FALSE, - (void *) (intptr_t) endpoint, &pm_macosx_in_dictionary); - } - - /* Iterate over the MIDI output devices */ - for (int i = 0; i < numOutputs; i++) { - int iac_flag; - PmDeviceID id; - endpoint = MIDIGetDestination(i); - if (endpoint == NULL_REF) { - continue; - } - /* Register this device with PortMidi */ - id = pm_add_device("CoreMIDI", - cm_get_full_endpoint_name(endpoint, &iac_flag), FALSE, FALSE, - (void *) (intptr_t) endpoint, &pm_macosx_out_dictionary); - /* if this is an IAC device, tuck that info away for write functions */ - if (iac_flag && id <= MAX_IAC_NUM) { - isIAC[id] = TRUE; - } - } - return pmNoError; - -error_return: - pm_macosxcm_term(); /* clear out any opened ports */ - return check_hosterror(macHostError, error_text); -} - -void pm_macosxcm_term(void) -{ - /* docs say do not explicitly dispose of client - if (client != NULL_REF) MIDIClientDispose(client); */ - if (portIn != NULL_REF) MIDIPortDispose(portIn); - if (portOut != NULL_REF) MIDIPortDispose(portOut); -} diff --git a/portmidi/pm_mac/pmmacosxcm.h b/portmidi/pm_mac/pmmacosxcm.h deleted file mode 100755 index 166a0b7..0000000 --- a/portmidi/pm_mac/pmmacosxcm.h +++ /dev/null @@ -1,4 +0,0 @@ -/* system-specific definitions */
-
-PmError pm_macosxcm_init(void);
-void pm_macosxcm_term(void);
diff --git a/portmidi/pm_sndio/pmsndio.c b/portmidi/pm_sndio/pmsndio.c deleted file mode 100644 index 0c1ea11..0000000 --- a/portmidi/pm_sndio/pmsndio.c +++ /dev/null @@ -1,365 +0,0 @@ -/* pmsndio.c -- PortMidi os-dependent code */ - -#include <stdlib.h> -#include <stdio.h> -#include <sndio.h> -#include <string.h> -#include <poll.h> -#include <errno.h> -#include <pthread.h> -#include "portmidi.h" -#include "pmutil.h" -#include "pminternal.h" -#include "porttime.h" - -#define NDEVS 9 -#define SYSEX_MAXLEN 1024 - -#define SYSEX_START 0xf0 -#define SYSEX_END 0xf7 - -extern pm_fns_node pm_sndio_in_dictionary; -extern pm_fns_node pm_sndio_out_dictionary; - -/* length of voice and common messages (status byte included) */ -unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 }; -unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 }; - -struct mio_dev { - char name[16]; - struct mio_hdl *hdl; - int mode; - char errmsg[PM_HOST_ERROR_MSG_LEN]; - pthread_t thread; -} devs[NDEVS]; - -static void set_mode(struct mio_dev *, unsigned int); - -void pm_init() -{ - int i, j, k = 0; - char devices[][16] = {"midithru", "rmidi", "midi", "snd"}; - - /* default */ - strcpy(devs[0].name, MIO_PORTANY); - pm_add_device("SNDIO", devs[k].name, TRUE, FALSE, (void *) &devs[k], - &pm_sndio_in_dictionary); - pm_add_device("SNDIO", devs[k].name, FALSE, FALSE, (void *) &devs[k], - &pm_sndio_out_dictionary); - k++; - - for (i = 0; i < 4; i++) { - for (j = 0; j < 2; j++) { - sprintf(devs[k].name, "%s/%d", devices[i], j); - pm_add_device("SNDIO", devs[k].name, TRUE, FALSE, (void *) &devs[k], - &pm_sndio_in_dictionary); - pm_add_device("SNDIO", devs[k].name, FALSE, FALSE, (void *) &devs[k], - &pm_sndio_out_dictionary); - k++; - } - } - - // this is set when we return to Pm_Initialize, but we need it - // now in order to (successfully) call Pm_CountDevices() - pm_initialized = TRUE; - pm_default_input_device_id = 0; - pm_default_output_device_id = 1; -} - -void pm_term(void) -{ - int i; - for(i = 0; i < NDEVS; i++) { - if (devs[i].mode != 0) { - set_mode(&devs[i], 0); - if (devs[i].thread) { - pthread_join(devs[i].thread, NULL); - devs[i].thread = NULL; - } - } - } -} - -PmDeviceID Pm_GetDefaultInputDeviceID() { - Pm_Initialize(); - return pm_default_input_device_id; -} - -PmDeviceID Pm_GetDefaultOutputDeviceID() { - Pm_Initialize(); - return pm_default_output_device_id; -} - -void *pm_alloc(size_t s) { return malloc(s); } - -void pm_free(void *ptr) { free(ptr); } - -/* midi_message_length -- how many bytes in a message? */ -static int midi_message_length(PmMessage message) -{ - unsigned char st = message & 0xff; - if (st >= 0xf8) - return 1; - else if (st >= 0xf0) - return common_len[st & 7]; - else if (st >= 0x80) - return voice_len[(st >> 4) & 7]; - else - return 0; -} - -void* input_thread(void *param) -{ - PmInternal *midi = (PmInternal*)param; - struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor; - struct pollfd pfd[1]; - nfds_t nfds; - unsigned char st = 0, c = 0; - int rc, revents, idx = 0, len = 0; - size_t todo = 0; - unsigned char buf[0x200], *p; - PmEvent pm_ev, pm_ev_rt; - unsigned char sysex_data[SYSEX_MAXLEN]; - - while(dev->mode & MIO_IN) { - if (todo == 0) { - nfds = mio_pollfd(dev->hdl, pfd, POLLIN); - rc = poll(pfd, nfds, 100); - if (rc < 0) { - if (errno == EINTR) - continue; - break; - } - revents = mio_revents(dev->hdl, pfd); - if (!(revents & POLLIN)) - continue; - - todo = mio_read(dev->hdl, buf, sizeof(buf)); - if (todo == 0) - continue; - p = buf; - } - c = *p++; - todo--; - - if (c >= 0xf8) { - pm_ev_rt.message = c; - pm_ev_rt.timestamp = Pt_Time(); - pm_read_short(midi, &pm_ev_rt); - } else if (c == SYSEX_END) { - /* note: PortMidi is designed to avoid the need for SYSEX_MAXLEN. - With the new implementation of pm_read_bytes, it would be - better to simply call pm_read_bytes() and let it parse buf, - which can contain any number of whole or partial messages with - interleaved realtime messages. I did not change the code because - I cannot test it. -RBD */ - if (st == SYSEX_START) { - sysex_data[idx++] = c; - pm_read_bytes(midi, sysex_data, idx, Pt_Time()); - } - st = 0; - idx = 0; - } else if (c == SYSEX_START) { - st = c; - idx = 0; - sysex_data[idx++] = c; - } else if (c >= 0xf0) { - pm_ev.message = c; - len = common_len[c & 7]; - st = c; - idx = 1; - } else if (c >= 0x80) { - pm_ev.message = c; - len = voice_len[(c >> 4) & 7]; - st = c; - idx = 1; - } else if (st == SYSEX_START) { - if (idx == SYSEX_MAXLEN) { - fprintf(stderr, "the message is too long\n"); - idx = st = 0; - } else { - sysex_data[idx++] = c; - } - } else if (st) { - if (idx == 0 && st != SYSEX_START) - pm_ev.message |= (c << (8 * idx++)); - pm_ev.message |= (c << (8 * idx++)); - if (idx == len) { - pm_read_short(midi, &pm_ev); - if (st >= 0xf0) - st = 0; - idx = 0; - } - } - } - - pthread_exit(NULL); - return NULL; -} - -static void set_mode(struct mio_dev *dev, unsigned int mode) { - if (dev->mode != 0) - mio_close(dev->hdl); - dev->mode = 0; - if (mode != 0) - dev->hdl = mio_open(dev->name, mode, 0); - if (dev->hdl) - dev->mode = mode; -} - -static PmError sndio_out_open(PmInternal *midi, void *driverInfo) -{ - struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor; - - if (dev->mode & MIO_OUT) - return pmNoError; - - set_mode(dev, dev->mode | MIO_OUT); - if (!(dev->mode & MIO_OUT)) { - snprintf(dev->errmsg, PM_HOST_ERROR_MSG_LEN, - "mio_open (output) failed: %s\n", dev->name); - return pmHostError; - } - - return pmNoError; -} - -static PmError sndio_in_open(PmInternal *midi, void *driverInfo) -{ - struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor; - - if (dev->mode & MIO_IN) - return pmNoError; - - set_mode(dev, dev->mode | MIO_IN); - if (!(dev->mode & MIO_IN)) { - snprintf(dev->errmsg, PM_HOST_ERROR_MSG_LEN, - "mio_open (input) failed: %s\n", dev->name); - return pmHostError; - } - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_create(&dev->thread, &attr, input_thread, ( void* )midi); - return pmNoError; -} - -static PmError sndio_out_close(PmInternal *midi) -{ - struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor; - - if (dev->mode & MIO_OUT) - set_mode(dev, dev->mode & ~MIO_OUT); - return pmNoError; -} - -static PmError sndio_in_close(PmInternal *midi) -{ - struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor; - - if (dev->mode & MIO_IN) { - set_mode(dev, dev->mode & ~MIO_IN); - pthread_join(dev->thread, NULL); - dev->thread = NULL; - } - return pmNoError; -} - -static PmError sndio_abort(PmInternal *midi) -{ - return pmNoError; -} - -static PmTimestamp sndio_synchronize(PmInternal *midi) -{ - return 0; -} - -static PmError do_write(struct mio_dev *dev, const void *addr, size_t nbytes) -{ - size_t w = mio_write(dev->hdl, addr, nbytes); - - if (w != nbytes) { - snprintf(dev->errmsg, PM_HOST_ERROR_MSG_LEN, - "mio_write failed, bytes written:%zu\n", w); - return pmHostError; - } - return pmNoError; -} - -static PmError sndio_write_byte(PmInternal *midi, unsigned char byte, - PmTimestamp timestamp) -{ - struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor; - - return do_write(dev, &byte, 1); -} - -static PmError sndio_write_short(PmInternal *midi, PmEvent *event) -{ - struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor; - int nbytes = midi_message_length(event->message); - - if (midi->latency > 0) { - /* XXX the event should be queued for later playback */ - return do_write(dev, &event->message, nbytes); - } else { - return do_write(dev, &event->message, nbytes); - } - return pmNoError; -} - -static PmError sndio_write_flush(PmInternal *midi, PmTimestamp timestamp) -{ - return pmNoError; -} - -PmError sndio_sysex(PmInternal *midi, PmTimestamp timestamp) -{ - return pmNoError; -} - -static unsigned int sndio_has_host_error(PmInternal *midi) -{ - struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor; - - return (dev->errmsg[0] != '\0'); -} - -static void sndio_get_host_error(PmInternal *midi, char *msg, unsigned int len) -{ - struct mio_dev *dev = pm_descriptors[midi->device_id].descriptor; - - strlcpy(msg, dev->errmsg, len); - dev->errmsg[0] = '\0'; -} - -pm_fns_node pm_sndio_in_dictionary = { - none_write_short, - none_sysex, - none_sysex, - none_write_byte, - none_write_short, - none_write_flush, - sndio_synchronize, - sndio_in_open, - sndio_abort, - sndio_in_close, - success_poll, - sndio_has_host_error, -}; - -pm_fns_node pm_sndio_out_dictionary = { - sndio_write_short, - sndio_sysex, - sndio_sysex, - sndio_write_byte, - sndio_write_short, - sndio_write_flush, - sndio_synchronize, - sndio_out_open, - sndio_abort, - sndio_out_close, - none_poll, - sndio_has_host_error, -}; - diff --git a/portmidi/pm_sndio/pmsndio.h b/portmidi/pm_sndio/pmsndio.h deleted file mode 100644 index 4096d9b..0000000 --- a/portmidi/pm_sndio/pmsndio.h +++ /dev/null @@ -1,5 +0,0 @@ -/* pmsndio.h */ - -extern PmDeviceID pm_default_input_device_id; -extern PmDeviceID pm_default_output_device_id; - diff --git a/portmidi/pm_test/CMakeLists.txt b/portmidi/pm_test/CMakeLists.txt deleted file mode 100644 index ae0fe48..0000000 --- a/portmidi/pm_test/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -# CMake file to build tests in this directory: pm_test - -# set the build directory to be in portmidi, not in portmidi/pm_test -# this is required for Xcode: -if(APPLE) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) -endif(APPLE) - -# if(WIN32) -# if(NOT BUILD_SHARED_LIBS) - # /MDd is multithread debug DLL, /MTd is multithread debug - # /MD is multithread DLL, /MT is multithread. Change to static: -# include(../pm_win/static.cmake) -# endif() -# endif(WIN32) - -if(HAIKU) - add_compile_options(-fPIC) # Haiku x86_64 needs this explicitly -endif() - -macro(add_test name) - add_executable(${name} ${name}.c) - target_link_libraries(${name} PRIVATE portmidi) - set_property(TARGET ${name} PROPERTY MSVC_RUNTIME_LIBRARY - "MultiThreaded$<$<CONFIG:Debug>:Debug>${MSVCRT_DLL}") -endmacro(add_test) - -add_test(testio) -add_test(midithread) -add_test(midithru) -add_test(sysex) -add_test(latency) -add_test(mm) -add_test(midiclock) -add_test(qtest) -add_test(fast) -add_test(fastrcv) -add_test(pmlist) -if(WIN32) -# windows does not implement Pm_CreateVirtualInput or Pm_CreateVirtualOutput -else(WIN32) -add_test(recvvirtual) -add_test(sendvirtual) -add_test(multivirtual) -add_test(virttest) -endif(WIN32) diff --git a/portmidi/pm_test/README.txt b/portmidi/pm_test/README.txt deleted file mode 100644 index 6c0c7ab..0000000 --- a/portmidi/pm_test/README.txt +++ /dev/null @@ -1,363 +0,0 @@ -README.txt - for pm_test directory - -These are all test programs for PortMidi - -Because device numbers depend on the system, there is no automated -script to run all tests on PortMidi. - -To run the full set of tests manually: - -Note: everything is run from the ../Debug or ../Release directory. -Actual or example input is marked with >>, e.g., >>0 means type 0<ENTER> -Comments are shown in square brackets [like this] - -1. ./qtest -- output should show a bunch of tests and no error message. - -2. ./testio [test input] -Latency in ms: >>0 -enter your choice... >>1 -Type input number: >>6 [pick a working input device] -[play some notes, look for note-on (0x90) with pitch and velocity data] - -3. ./testio [test input (fail w/assert)] -Latency in ms: >>0 -enter your choice... >>2 -Type input number: >>6 [pick a working input device] -[play some notes, program will abort after 5 messages -(this test only applies to a Debug build, otherwise -the assert() macro is disabled.)] - -4. ./testio [test input (fail w/NULL assign)] -Latency in ms: >>0 -enter your choice... >>3 -Type input number: >>6 [pick a working input device] -[play some notes, program will Segmentation fault after 5 messages -(this test may not Segfault in the Release build; if not -try testing with a Debug build.)] - -5. ./testio [test output, no latency] -Latency in ms: >>0 -enter your choice... >>4 -Type output number: >>2 [pick a working output device] ->> [type ENTER when prompted (7 times)] -[hear note on, note off, note on, note off, chord] - -6. ./testio [test output, latency > 0] -Latency in ms: >>300 -enter your choice... >>4 -Type output number: >>2 [pick a working output device] ->> [type ENTER when prompted (7 times)] -[hear note on, note off, note on, note off, arpeggiated chord - (delay of 300ms should be apparent)] - -7. ./testio [for both, no latency] -Latency in ms: >>0 -enter your choice... >>5 -Type input number: >>6 [pick a working input device] -Type output number: >>2 [pick a working output device] -[play notes on input, hear them on output] - -8. ./testio [for both, latency > 0] -Latency in ms: >>300 -enter your choice... >>5 -Type input number: >>6 [pick a working input device] -Type output number: >>2 [pick a working output device] -[play notes on input, hear them on output (delay of 300ms is apparent)] - -9. ./testio [stream test] -Latency in ms: >>0 [does not matter] -enter your choice... >>6 -Type output number: >>2 [pick a working output device] ->> [type ENTER to start] -[hear 4 notes: C D E F# at one note per second, then all turn off] -ready to close and terminate... (type ENTER) :>> [type ENTER (twice)] - -10. ./testio [isochronous out] -Latency in ms: >>300 -enter your choice... >>7 -Type output number: >>2 [pick a working output device] -ready to send program 1 change... (type ENTER): >> [type ENTER] -[hear 80 notes, exactly 4 notes per second, no jitter] - -11. ./latency [no MIDI, histogram] -Choose timer period (in ms, >= 1): >>1 -? >>1 [No MIDI traffic option] -[wait about 10 seconds] ->> [type ENTER] -[output should be something like ... Maximum latency: 1 milliseconds] - -12. ./latency [MIDI input, histogram] -Choose timer period (in ms, >= 1): >>1 -? >>2 [MIDI input option] -Midi input device number: >>6 [pick a working input device] -[wait about 5 seconds, play input for 10 seconds ] ->> [type ENTER] -[output should be something like ... Maximum latency: 3 milliseconds] - -13. ./latency [MIDI output, histogram] -Choose timer period (in ms, >= 1): >>1 -? >>3 [MIDI output option] -Midi output device number: >>2 [pick a working output device] -Midi output should be sent every __ callback iterations: >>50 -[wait until you hear notes for 5 or 10 seconds] ->> [type ENTER to stop] -[output should be something like ... Maximum latency: 2 milliseconds] - -14. ./latency [MIDI input and output, histogram] -Choose timer period (in ms, >= 1): >>1 -? >>4 [MIDI input and output option] -Midi input device number: >>6 [pick a working input device] -Midi output device number: >>2 [pick a working output device] -Midi output should be sent every __ callback iterations: >>50 -[wait until you hear notes, simultaneously play notes for 5 or 10 seconds] ->> [type ENTER to stop] -[output should be something like ... Maximum latency: 1 milliseconds] - -15. ./mm [test with device input] -Type input device number: >>6 [pick a working input device] -[play some notes, see notes printed] ->>q [Type q ENTER when finished to exit] - -16. ./midithread -i 6 -o 2 [use working input/output device numbers] ->>5 [enter a transposition number] -[play some notes, hear parallel 4ths] ->>q [quit after ENTER a couple of times] - -17. ./midiclock [in one shell] - ./mm [in another shell] -[Goal is send clock messages to MIDI monitor program. This requires - either a hardware loopback (MIDI cable from OUT to IN on interface) - or a software loopback (macOS IAC bus or ALSA MIDI Through Port)] -[For midiclock application:] - Type output device number: >>0 [pick a device with loopback] - Type ENTER to start MIDI CLOCK: >> [type ENTER] -[For mm application:] - Type input device number: >>1 [pick device with loopback] - [Wait a few seconds] - >>s [to get Clock Count] - >>s [expect to get a higher Clock Count] -[For midiclock application:] - >>c [turn off clocks] -[For mm application:] - >>s [to get Clock Count] - >>s [expect to Clock Count stays the same] -[For midiclock application:] - >>t [turn on time code, see Time Code Quarter Frame messages from mm] - >>q [to quit] -[For mm application:] - >>q [to quit] - -18. ./midithru -i 6 -o 2 [use working input/output device numbers] -[Play notes on input evice; notes are sent immediately and also with a - 2 sec delay to the output device; program terminates in 60 seconds or - when you play B3 (B below Middle C)] ->> [ENTER to exit] - -19. ./recvvirtual -h [in one shell, macOS and Linux only] - ./recvvirtual -m vvv [for mac, or -c vvv -p vvvport for linux] - ./testio [in another shell] -[For testio application:] - Latency in ms: >>0 - enter your choice... >>4 [test output] - Type output number: >>9 [select the "portmidi (output)" device] - [type ENTER to each prompt, see that recvvirtual "Got message 0" - through "Got message 9"] - >> [ENTER to quit] -[For recvvirtual application:] - >> [ENTER to quit] - -20. ./sendvirtual -h [in one shell, macOS and Linux only] - ./sendvirtual -m vvv [for mac, or -c vvv -p vvvport for linux] - ./mm [in another shell] -[For mm application:] - Type input device number: >>10 [select the "portmidi" device] -[For sendvirtual application:] - Type ENTER to send messages: >> [type ENTER] - [see NoteOn and off messages received by mm for Key 60-64] - >> [ENTER to quit] -[For mm application:] - >>q [and ENTER twice to quit] - -21. ./sysex [no latency] -[This requires either a hardware loopback (MIDI cable from OUT to IN - on interface) or a software loopback (macOS IAC bus or ALSA MIDI - Through Port)] ->>l [for loopback test] -Type output device number: >>0 [pick output device to loopback] -Latency in milliseconds: >>0 -Type input device number: >>0 [pick input device for loopback] -[Program will send 100,000 bytes. After awhile, program will quit. - You can read the Cummulative bytes/sec value.] - -22. ./sysex [latency > 0] -[This requires either a hardware loopback (MIDI cable from OUT to IN - on interface) or a software loopback (macOS IAC bus or ALSA MIDI - Through Port)] ->>l [for loopback test] -Type output device number: >>0 [pick output device to loopback] -Latency in milliseconds: >>100 -Type input device number: >>0 [pick input device for loopback] -[Program will send 100,000 bytes. After awhile, program will quit. You - can read the Cummulative bytes/sec value; it is affected by latency.] - -23. ./fast [no latency] - ./fastrcv [in another shell] -[This is a speed check, especially for macOSX IAC bus connections, - which are known to drop messages if you send messages too fast. - fast and fastrcv must use a loopback to function.] -[In fastrcv:] - Input device number: >>1 [pick a non-hardware device if possible] -[In fast:] - Latency in ms: >>0 - Rate in messages per second: >>10000 - Duration in seconds: >>10 - Output device number: >>0 [pick a non-hardware device if possible] - sending output... -[see message counts and times; on Linux you should get about 10000 - messages/s; on macOS you should get about 1800 messages/s; Windows - does not have software ports, so data rate might be limited by the - loopback device you use.] - -Check output of fastrcv: there should be no errors, just msg/sec.] - -24. ./fast [latency > 0] - ./fastrcv [in another shell] -[This is a speed check, especially for macOSX IAC bus connections, - which are known to drop messages if you send messages too fast. - fast and fastrcv must use a loopback to function.] -[In fastrcv:] - Input device number: >>1 [pick a non-hardware device if possible] -[In fast:] - Latency in ms: >>30 [Note for ALSA, use latency * msgs/ms < 400] - Rate in messages per second: >>10000 - Duration in seconds: >>10 - Output device number: >>0 [pick a non-hardware device if possible] - sending output... -[see message counts and times; on Linux you should get about 10000 - messages/s; on macOS you should get about 1800 messages/s; Windows - does not have software ports, so data rate might be limited by the - loopback device you use.] - -Check output of fastrcv: there should be no errors, just msg/sec.] - -25. ./fast [virtual output port, latency = 0, macOS and Linux only] - ./fastrcv [in another shell] -[Start fast first:] - Latency in ms: >>0 - Rate in messages per second: >>10000 - Duration in seconds: >>10 - Output device number: >>9 [enter number listed for "Create virtual - port named 'fast' (output)"] - Pausing so you can connect a receiver to the newly created - "fast" port. Type ENTER to proceed: -[In fastrcv:] - Input device number: >>3 [pick the device named "fast (input)"] -[In fast:] - >> [type ENTER to start] -[see message counts and times as above ] - -Check output of fastrcv: there should be no errors, just msg/sec.] - -26. ./fast [virtual output port, latency > 0, macOS and Linux only] - ./fastrcv [in another shell] -[Start fast first:] - Latency in ms: >>30 [Note for ALSA, use latency * msgs/ms < 400] - Rate in messages per second: >>10000 - Duration in seconds: >>10 - Output device number: >>9 [enter number listed for "Create virtual - port named 'fast' (output)"] - Pausing so you can connect a receiver to the newly created - "fast" port. Type ENTER to proceed: -[In fastrcv:] - Input device number: >>3 [pick the device named "fast (input)"] -[In fast:] - >> [type ENTER to start] -[see message counts and times as above ] - -Check output of fastrcv: there should be no errors, just msg/sec.] - -27. ./fast [latency = 0, macOS and Linux only] - ./fastrcv [virtual input port, in another shell] -[In fastrcv:] - Input device number: >>8 [enter number listed for "Create virtual - port named 'fastrcv' (input)"] -[In fast:] - Latency in ms: >>0 - Rate in messages per second: >>10000 - Duration in seconds: >>10 - Output device number: >>7 [pick the device named "fastrcv (output)"] - sending output... -[see message counts and times as above ] - -Check output of fastrcv: there should be no errors, just msg/sec.] - -28. ./fast [latency > 0, macOS and Linux only] - ./fastrcv [virtual input port, in another shell] -[In fastrcv:] - Input device number: >>8 [enter number listed for "Create virtual - port named 'fastrcv' (input)"] -[In fast:] - Latency in ms: >>30 [Note for ALSA, use latency * msgs/ms < 400] - Rate in messages per second: >>10000 - Duration in seconds: >>10 - Output device number: >>7 [pick the device named "fastrcv (output)"] - sending output... -[see message counts and times as above ] - -Check output of fastrcv: there should be no errors, just msg/sec.] - -29. ./midithru -v -n [virtual input and output, macOS and Linux only] - ./fast [latency = 0] - ./fastrcv [in another shell] -[Start midithru first, it will run for 60 seconds] -[In fastrcv:] - Input device number: >>3 [pick the device named - port named "midithru (input)"] -[In fast:] - Latency in ms: >>0 - Rate in messages per second: >>10000 - Duration in seconds: >>10 - Output device number: >>8 [pick the device named "midithru (output)"] - sending output... -[see message counts and times as above, on Mac, output from fast to - midithru AND output from midithru to fastrcv are rate limited, so - as in other tests, it will take more than 10s to receive all the - messages and the receiving message rate will be about 1800 messages/second] - -30. ./multivirtual [macOS and Linux only] - ./testio - ./testio -[Start multivirtual first] -[In first testio:] - Latency in ms: >>0 - enter your choice... >>5 [test both] - Type input number: >>1 [pick portmidi1 (input) - Type output number: >>4 [pick portmidi1 (output) -[In second testio:] - Latency in ms: >>10 - enter your choice... >>5 [test both] - Type input number: >>2 [pick portmidi2 (input) - Type output number: >>5 [pick portmidi2 (output) -[In multivirtual:] - Type ENTER to send messages: >> [type ENTER to start] -[see that each testio gets 11 messages (0 to 10) at reasonable times - (e.g. 2077 to 7580, and the "@" times (real times) should match the - timestamps). multivirtual should also report reasonable times and - line near the end of output should be "Got 11 messages from - portmidi1 and 11 from portmidi2; expected 11."] - -31. ./multivirtual [macOS and Linux only] - ./multivirtual -[Second instance should report "PortMidi call failed... - PortMidi: Cannot create virtual device: name is taken"] - -32. pmlist - ./pmlist [check the output] - [plug in or remove a device] - >> [type RETURN] - [check for changes in device list] - >>q - - - - diff --git a/portmidi/pm_test/fast.c b/portmidi/pm_test/fast.c deleted file mode 100644 index 102697e..0000000 --- a/portmidi/pm_test/fast.c +++ /dev/null @@ -1,290 +0,0 @@ -/* fast.c -- send many MIDI messages very fast. - * - * This is a stress test created to explore reports of - * pm_write() call blocking (forever) on Linux when - * sending very dense MIDI sequences. - * - * Modified 8 Aug 2017 with -n to send expired timestamps - * to test a theory about why Linux ALSA hangs in Audacity. - * - * Modified 9 Aug 2017 with -m, -p to test when timestamps are - * wrapping from negative to positive or positive to negative. - * - * Roger B. Dannenberg, Aug 2017 - */ - -#include "portmidi.h" -#include "porttime.h" -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "assert.h" - -#define DEVICE_INFO NULL -#define DRIVER_INFO NULL -#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */ - -#define STRING_MAX 80 /* used for console input */ -// need to get declaration for Sleep() -#ifdef WIN32 -#include "windows.h" -#else -#include <unistd.h> -#define Sleep(n) usleep(n * 1000) -#endif - - -int32_t latency = 0; -int32_t msgrate = 0; -int deviceno = -9999; -int duration = 0; -int expired_timestamps = FALSE; -int use_timeoffset = 0; - -/* read a number from console */ -/**/ -int get_number(const char *prompt) -{ - int n = 0, i; - fputs(prompt, stdout); - while (n != 1) { - n = scanf("%d", &i); - while (getchar() != '\n') ; - } - return i; -} - - -/* get_time -- the time reference. Normally, this will be the default - * time, Pt_Time(), but if you use the -p or -m option, the time - * reference will start at an offset of -10s for -m, or - * maximum_time - 10s for -p, so that we can observe what happens - * with negative time or when time changes sign or wraps (by - * generating output for more than 10s). - */ -PmTimestamp get_time(void *info) -{ - PmTimestamp now = (PmTimestamp) (Pt_Time() + use_timeoffset); - return now; -} - - -void fast_test() -{ - PmStream *midi; - char line[STRING_MAX]; - int pause = FALSE; /* pause if this is a virtual output port */ - PmError err = pmNoError; - /* output buffer size should be a little more than - msgrate * latency / 1000. PortMidi will guarantee - a minimum of latency / 2 */ - int buffer_size = msgrate * latency / 900; - PmTimestamp start, now; - int msgcnt = 0; - int polling_count = 0; - int pitch = 60; - int printtime = 1000; - - /* It is recommended to start timer before PortMidi */ - TIME_START; - - /* open output device */ - if (deviceno == Pm_CountDevices()) { - deviceno = Pm_CreateVirtualOutput("fast", NULL, DEVICE_INFO); - if (deviceno >= 0) { - err = Pm_OpenOutput(&midi, deviceno, DRIVER_INFO, buffer_size, - get_time, NULL, latency); - pause = TRUE; - } - } else if (err >= pmNoError) { - err = Pm_OpenOutput(&midi, deviceno, DRIVER_INFO, buffer_size, - get_time, NULL, latency); - } - if (err == pmHostError) { - Pm_GetHostErrorText(line, STRING_MAX); - printf("PortMidi found host error...\n %s\n", line); - goto done; - } else if (err < 0) { - printf("PortMidi call failed...\n %s\n", Pm_GetErrorText(err)); - goto done; - } - printf("Midi Output opened with %ld ms latency.\n", (long) latency); - if (pause) { - printf("Pausing so you can connect a receiver to the newly created\n" - " \"fast\" port. Type ENTER to proceed: "); - while (getchar() != '\n') ; - } - /* wait a sec after printing previous line */ - start = get_time(NULL) + 1000; - while (start > get_time(NULL)) { - Sleep(10); - } - printf("sending output...\n"); - fflush(stdout); /* make sure message goes to console */ - - /* every 10ms send on/off pairs at timestamps set to current time */ - now = get_time(NULL); - /* if expired_timestamps, we want to send timestamps that have - * expired. They should be sent immediately, but there's a suggestion - * that negative delay might cause problems in the ALSA implementation - * so this is something we can test using the -n flag. - */ - if (expired_timestamps) { - now = now - 2 * latency; - } - - while (((PmTimestamp) (now - start)) < duration * 1000 || pitch != 60) { - /* how many messages do we send? Total should be - * (elapsed * rate) / 1000 - */ - int send_total = (((PmTimestamp) ((now - start))) * msgrate) / 1000; - /* always send until pitch would be 60 so if we run again, the - next pitch (60) will be expected */ - if (msgcnt < send_total) { - if ((msgcnt & 1) == 0) { - Pm_WriteShort(midi, now, Pm_Message(0x90, pitch, 100)); - } else { - Pm_WriteShort(midi, now, Pm_Message(0x90, pitch, 0)); - /* play 60, 61, 62, ... 71, then wrap back to 60, 61, ... */ - pitch = (pitch - 59) % 12 + 60; - } - msgcnt += 1; - if (((PmTimestamp) (now - start)) >= printtime) { - printf("%d at %dms, polling count %d\n", msgcnt, now - start, - polling_count); - fflush(stdout); /* make sure message goes to console */ - printtime += 1000; /* next msg in 1s */ - } - } - now = get_time(NULL); - polling_count++; - } - /* close device (this not explicitly needed in most implementations) */ - printf("ready to close and terminate... (type RETURN):"); - while (getchar() != '\n') ; - - Pm_Close(midi); - done: - Pm_Terminate(); - printf("done closing and terminating...\n"); -} - - -void show_usage() -{ - printf("Usage: fast [-h] [-l latency] [-r rate] [-d device] [-s dur] " - "[-n] [-p] [-m]\n" - ", where latency is in ms,\n" - " rate is messages per second,\n" - " device is the PortMidi device number,\n" - " dur is the length of the test in seconds,\n" - " -n means send timestamps in the past,\n" - " -p means use a large positive time offset,\n" - " -m means use a large negative time offset, and\n" - " -h means help.\n"); -} - -int main(int argc, char *argv[]) -{ - int default_in; - int default_out; - char *deflt; - int i = 0; - int latency_valid = FALSE; - int rate_valid = FALSE; - int device_valid = FALSE; - int dur_valid = FALSE; - - if (sizeof(void *) == 8) - printf("Apparently this is a 64-bit machine.\n"); - else if (sizeof(void *) == 4) - printf ("Apparently this is a 32-bit machine.\n"); - - if (argc <= 1) { - show_usage(); - } else { - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-h") == 0) { - show_usage(); - } else if (strcmp(argv[i], "-l") == 0 && (i + 1 < argc)) { - i = i + 1; - latency = atoi(argv[i]); - printf("Latency will be %ld\n", (long) latency); - latency_valid = TRUE; - } else if (strcmp(argv[i], "-r") == 0) { - i = i + 1; - msgrate = atoi(argv[i]); - printf("Rate will be %d messages/second\n", msgrate); - rate_valid = TRUE; - } else if (strcmp(argv[i], "-d") == 0) { - i = i + 1; - deviceno = atoi(argv[i]); - printf("Device will be %d\n", deviceno); - } else if (strcmp(argv[i], "-s") == 0) { - i = i + 1; - duration = atoi(argv[i]); - printf("Duration will be %d seconds\n", duration); - dur_valid = TRUE; - } else if (strcmp(argv[i], "-n") == 0) { - printf("Sending expired timestamps (-n)\n"); - expired_timestamps = TRUE; - } else if (strcmp(argv[i], "-p") == 0) { - printf("Time offset set to 2147473648 (-p)\n"); - use_timeoffset = 2147473648; - } else if (strcmp(argv[i], "-m") == 0) { - printf("Time offset set to -10000 (-m)\n"); - use_timeoffset = -10000; - } else { - show_usage(); - } - } - } - - if (!latency_valid) { - // coerce to known size - latency = (int32_t) get_number("Latency in ms: "); - } - - if (!rate_valid) { - // coerce from "%d" to known size - msgrate = (int32_t) get_number("Rate in messages per second: "); - } - - if (!dur_valid) { - duration = get_number("Duration in seconds: "); - } - - /* list device information */ - default_in = Pm_GetDefaultInputDeviceID(); - default_out = Pm_GetDefaultOutputDeviceID(); - for (i = 0; i < Pm_CountDevices(); i++) { - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - if (info->output) { - printf("%d: %s, %s", i, info->interf, info->name); - if (i == deviceno) { - device_valid = TRUE; - deflt = "selected "; - } else if (i == default_out) { - deflt = "default "; - } else { - deflt = ""; - } - printf(" (%soutput)\n", deflt); - } - } - printf("%d: Create virtual port named \"fast\"", i); - if (i == deviceno) { - device_valid = TRUE; - deflt = "selected "; - } else { - deflt = ""; - } - printf(" (%soutput)\n", deflt); - - if (!device_valid) { - deviceno = get_number("Output device number: "); - } - - fast_test(); - return 0; -} diff --git a/portmidi/pm_test/fastrcv.c b/portmidi/pm_test/fastrcv.c deleted file mode 100644 index dabf9fa..0000000 --- a/portmidi/pm_test/fastrcv.c +++ /dev/null @@ -1,255 +0,0 @@ -/* fastrcv.c -- send many MIDI messages very fast. - * - * This is a stress test created to explore reports of - * pm_write() call blocking (forever) on Linux when - * sending very dense MIDI sequences. - * - * Modified 8 Aug 2017 with -n to send expired timestamps - * to test a theory about why Linux ALSA hangs in Audacity. - * - * Roger B. Dannenberg, Aug 2017 - */ - -#include "portmidi.h" -#include "porttime.h" -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "assert.h" - -#define INPUT_BUFFER_SIZE 1000 /* big to avoid losing any input */ -#define DEVICE_INFO NULL -#define DRIVER_INFO NULL -#define TIME_PROC ((PmTimeProcPtr) Pt_Time) -#define TIME_INFO NULL -#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */ - -#define STRING_MAX 80 /* used for console input */ -// need to get declaration for Sleep() -#ifdef WIN32 -#include "windows.h" -#else -#include <unistd.h> -#define Sleep(n) usleep(n * 1000) -#endif - - -int deviceno = -9999; -int verbose = FALSE; - - -static void prompt_and_exit(void) -{ - printf("type ENTER..."); - while (getchar() != '\n') ; - /* this will clean up open ports: */ - exit(-1); -} - - -static PmError checkerror(PmError err) -{ - if (err == pmHostError) { - /* it seems pointless to allocate memory and copy the string, - * so I will do the work of Pm_GetHostErrorText directly - */ - char errmsg[80]; - Pm_GetHostErrorText(errmsg, 80); - printf("PortMidi found host error...\n %s\n", errmsg); - prompt_and_exit(); - } else if (err < 0) { - printf("PortMidi call failed...\n %s\n", Pm_GetErrorText(err)); - prompt_and_exit(); - } - return err; -} - - -/* read a number from console */ -/**/ -int get_number(const char *prompt) -{ - int n = 0, i; - fputs(prompt, stdout); - while (n != 1) { - n = scanf("%d", &i); - while (getchar() != '\n') ; - } - return i; -} - - -void fastrcv_test() -{ - PmStream * midi; - PmError status, length; - PmEvent buffer[1]; - PmTimestamp start; - /* every 10ms read all messages, keep counts */ - /* every 1000ms, print report */ - int msgcnt = 0; - /* expect repeating sequence of 60 through 71, alternating on/off */ - int expected_pitch = 60; - int expected_on = TRUE; - int report_time; - PmTimestamp last_timestamp = -1; - PmTimestamp last_delta = -1; - - /* It is recommended to start timer before PortMidi */ - TIME_START; - - /* open output device */ - if (deviceno == Pm_CountDevices()) { - int id = Pm_CreateVirtualInput("fastrcv", NULL, DEVICE_INFO); - if (id < 0) checkerror(id); /* error reporting */ - checkerror(Pm_OpenInput(&midi, id, DRIVER_INFO, - INPUT_BUFFER_SIZE, TIME_PROC, TIME_INFO)); - } else { - Pm_OpenInput(&midi, deviceno, DRIVER_INFO, INPUT_BUFFER_SIZE, - TIME_PROC, TIME_INFO); - } - printf("Midi Input opened.\n"); - - /* wait a sec after printing previous line */ - start = Pt_Time() + 1000; - while (start > Pt_Time()) { - Sleep(10); - } - - report_time = Pt_Time() + 1000; /* report every 1s */ - while (TRUE) { - PmTimestamp now = Pt_Time(); - status = Pm_Poll(midi); - if (status == TRUE) { - length = Pm_Read(midi, buffer, 1); - if (length > 0) { - int status = Pm_MessageStatus(buffer[0].message); - if (status == 0x80) { /* convert NoteOff to NoteOn, vel=0 */ - status = 0x90; - buffer[0].message = Pm_Message(status, - Pm_MessageData1(buffer[0].message), 0); - } - /* only listen to NOTEON messages */ - if (status == 0x90) { - int pitch = Pm_MessageData1(buffer[0].message); - int vel = Pm_MessageData2(buffer[0].message); - int is_on = (vel > 0); - if (verbose) { - printf("Note pitch %d vel %d\n", pitch, vel); - } - msgcnt++; - if (pitch != expected_pitch || expected_on != is_on) { - printf("Unexpected note-on: pitch %d vel %d, " - "expected: pitch %d Note%s\n", pitch, vel, - expected_pitch, (expected_on ? "On" : "Off")); - } - if (is_on) { - expected_on = FALSE; - expected_pitch = pitch; - } else { - expected_on = TRUE; - expected_pitch = (pitch + 1) % 72; - if (expected_pitch < 60) expected_pitch = 60; - } - if (last_timestamp >= 0) { - last_delta = buffer[0].timestamp - last_timestamp; - } - last_timestamp = buffer[0].timestamp; - } - } - } - if (now >= report_time) { - printf("%d msgs/sec", msgcnt); - /* if available, print the last timestamp and last delta time */ - if (last_timestamp >= 0) { - printf(" last timestamp %d", (int) last_timestamp); - last_timestamp = -1; - } - if (last_delta >= 0) { - printf(" last delta time %d", (int) last_delta); - last_delta = -1; - } - printf("\n"); - report_time += 1000; - msgcnt = 0; - } - } -} - - -void show_usage() -{ - printf("Usage: fastrcv [-h] [-v] [-d device], where\n" - "device is the PortMidi device number,\n" - "-h means help,\n" - "-v means verbose (print messages)\n"); -} - -int main(int argc, char *argv[]) -{ - int default_in; - int default_out; - char *deflt; - - int i = 0; - int test_input = 0, test_output = 0, test_both = 0; - int stream_test = 0; - int device_valid = FALSE; - - if (sizeof(void *) == 8) - printf("Apparently this is a 64-bit machine.\n"); - else if (sizeof(void *) == 4) - printf ("Apparently this is a 32-bit machine.\n"); - - if (argc <= 1) { - show_usage(); - } else { - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-h") == 0) { - show_usage(); - } else if (strcmp(argv[i], "-v") == 0) { - verbose = TRUE; - } else if (strcmp(argv[i], "-d") == 0) { - i = i + 1; - deviceno = atoi(argv[i]); - printf("Device will be %d\n", deviceno); - } else { - show_usage(); - } - } - } - - /* list device information */ - default_in = Pm_GetDefaultInputDeviceID(); - default_out = Pm_GetDefaultOutputDeviceID(); - for (i = 0; i < Pm_CountDevices(); i++) { - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - if (!info->output) { - printf("%d: %s, %s", i, info->interf, info->name); - if (i == deviceno) { - device_valid = TRUE; - deflt = "selected "; - } else if (i == default_out) { - deflt = "default "; - } else { - deflt = ""; - } - printf(" (%sinput)\n", deflt); - } - } - printf("%d: Create virtual port named \"fastrcv\"", i); - if (i == deviceno) { - device_valid = TRUE; - deflt = "selected "; - } else { - deflt = ""; - } - printf(" (%sinput)\n", deflt); - - if (!device_valid) { - deviceno = get_number("Input device number: "); - } - - fastrcv_test(); - return 0; -} diff --git a/portmidi/pm_test/latency.c b/portmidi/pm_test/latency.c deleted file mode 100755 index 06ea80d..0000000 --- a/portmidi/pm_test/latency.c +++ /dev/null @@ -1,287 +0,0 @@ -/* latency.c -- measure latency of OS */
-
-#include "porttime.h"
-#include "portmidi.h"
-#include "stdlib.h"
-#include "stdio.h"
-#include "string.h"
-#include "assert.h"
-
-/* Latency is defined here to mean the time starting when a
- process becomes ready to run, and ending when the process
- actually runs. Latency is due to contention for the
- processor, usually due to other processes, OS activity
- including device drivers handling interrupts, and
- waiting for the scheduler to suspend the currently running
- process and activate the one that is waiting.
-
- Latency can affect PortMidi applications: if a process fails
- to wake up promptly, MIDI input may sit in the input buffer
- waiting to be handled, and MIDI output may not be generated
- with accurate timing. Using the latency parameter when
- opening a MIDI output port allows the caller to defer timing
- to PortMidi, which in most implementations will pass the
- data on to the OS. By passing timestamps and data to the
- OS kernel, device driver, or even hardware, there are fewer
- sources of latency that can affect the ultimate timing of
- the data. On the other hand, the application must generate
- and deliver the data ahead of the timestamp. The amount by
- which data is computed early must be at least as large as
- the worst-case latency to avoid timing problems.
-
- Latency is even more important in audio applications. If an
- application lets an audio output buffer underflow, an audible
- pop or click is produced. Audio input buffers can overflow,
- causing data to be lost. In general the audio buffers must
- be large enough to buffer the worst-case latency that the
- application will encounter.
-
- This program measures latency by recording the difference
- between the scheduled callback time and the current real time.
- We do not really know the scheduled callback time, so we will
- record the differences between the real time of each callback
- and the real time of the previous callback. Differences that
- are larger than the scheduled difference are recorded. Smaller
- differences indicate the system is recovering from an earlier
- latency, so these are ignored.
- Since printing by the callback process can cause all sorts of
- delays, this program records latency observations in a
- histogram. When the program is stopped, the histogram is
- printed to the console.
-
- Optionally the system can be tested under a load of MIDI input,
- MIDI output, or both. If MIDI input is selected, the callback
- thread will read any waiting MIDI events each iteration. You
- must generate events on this interface for the test to actually
- put any appreciable load on PortMidi. If MIDI output is
- selected, alternating note on and note off events are sent each
- X iterations, where you specify X. For example, with a timer
- callback period of 2ms and X=1, a MIDI event is sent every 2ms.
-
-
- INTERPRETING RESULTS: Time is quantized to 1ms, so there is
- some uncertainty due to rounding. A microsecond latency that
- spans the time when the clock is incremented will be reported
- as a latency of 1. On the other hand, a latency of almost
- 1ms that falls between two clock ticks will be reported as
- zero. In general, if the highest nonzero bin is numbered N,
- then the maximum latency is N+1.
-
-CHANGE LOG
-
-18-Jul-03 Mark Nelson -- Added code to generate MIDI or receive
- MIDI during test, and made period user-settable.
- */
-
-#define HIST_LEN 21 /* how many 1ms bins in the histogram */
-
-#define STRING_MAX 80 /* used for console input */
-
-#define INPUT_BUFFER_SIZE 100
-#define OUTPUT_BUFFER_SIZE 0
-
-#ifndef max
-#define max(a, b) ((a) > (b) ? (a) : (b))
-#endif
-#ifndef min
-#define min(a, b) ((a) <= (b) ? (a) : (b))
-#endif
-
-int get_number(const char *prompt);
-
-PtTimestamp previous_callback_time = 0;
-
-int period; /* milliseconds per callback */
-
-int histogram[HIST_LEN];
-int max_latency = 0; /* worst latency observed */
-int out_of_range = 0; /* how many points outside of HIST_LEN? */
-
-int test_in, test_out; /* test MIDI in and/or out? */
-int output_period; /* output MIDI every __ iterations if test_out true */
-int iteration = 0;
-PmStream *in, *out;
-int note_on = 0; /* is the note currently on? */
-
-/* callback function for PortTime -- computes histogram */
-void pt_callback(PtTimestamp timestamp, void *userData)
-{
- PtTimestamp difference = timestamp - previous_callback_time - period;
- previous_callback_time = timestamp;
-
- /* allow 5 seconds for the system to settle down */
- if (timestamp < 5000) return;
-
- iteration++;
- /* send a note on/off if user requested it */
- if (test_out && (iteration % output_period == 0)) {
- PmEvent buffer[1];
- buffer[0].timestamp = Pt_Time();
- if (note_on) {
- /* note off */
- buffer[0].message = Pm_Message(0x90, 60, 0);
- note_on = 0;
- } else {
- /* note on */
- buffer[0].message = Pm_Message(0x90, 60, 100);
- note_on = 1;
- }
- Pm_Write(out, buffer, 1);
- iteration = 0;
- }
-
- /* read all waiting events (if user requested) */
- if (test_in) {
- PmError status;
- PmEvent buffer[1];
- do {
- status = Pm_Poll(in);
- if (status == TRUE) {
- Pm_Read(in,buffer,1);
- }
- } while (status == TRUE);
- }
-
- if (difference < 0) return; /* ignore when system is "catching up" */
-
- /* update the histogram */
- if (difference < HIST_LEN) {
- histogram[difference]++;
- } else {
- out_of_range++;
- }
-
- if (max_latency < difference) max_latency = difference;
-}
-
-
-int main()
-{
- int i;
- int len;
- int choice;
- PtTimestamp stop;
- printf("Latency histogram.\n");
- period = 0;
- while (period < 1) {
- period = get_number("Choose timer period (in ms, >= 1): ");
- }
- printf("Benchmark with:\n\t%s\n\t%s\n\t%s\n\t%s\n",
- "1. No MIDI traffic",
- "2. MIDI input",
- "3. MIDI output",
- "4. MIDI input and output");
- choice = get_number("? ");
- switch (choice) {
- case 1: test_in = 0; test_out = 0; break;
- case 2: test_in = 1; test_out = 0; break;
- case 3: test_in = 0; test_out = 1; break;
- case 4: test_in = 1; test_out = 1; break;
- default: assert(0);
- }
- if (test_in || test_out) {
- /* list device information */
- for (i = 0; i < Pm_CountDevices(); i++) {
- const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
- if ((test_in && info->input) ||
- (test_out && info->output)) {
- printf("%d: %s, %s", i, info->interf, info->name);
- if (info->input) printf(" (input)");
- if (info->output) printf(" (output)");
- printf("\n");
- }
- }
- /* open stream(s) */
- if (test_in) {
- int i = get_number("MIDI input device number: ");
- Pm_OpenInput(&in,
- i,
- NULL,
- INPUT_BUFFER_SIZE,
- (PmTimestamp (*)(void *)) Pt_Time,
- NULL);
- /* turn on filtering; otherwise, input might overflow in the
- 5-second period before timer callback starts reading midi */
- Pm_SetFilter(in, PM_FILT_ACTIVE | PM_FILT_CLOCK);
- }
- if (test_out) {
- int i = get_number("MIDI output device number: ");
- PmEvent buffer[1];
- Pm_OpenOutput(&out,
- i,
- NULL,
- OUTPUT_BUFFER_SIZE,
- (PmTimestamp (*)(void *)) Pt_Time,
- NULL,
- 0); /* no latency scheduling */
-
- /* send a program change to force a status byte -- this fixes
- a problem with a buggy linux MidiSport driver, and shouldn't
- hurt anything else
- */
- buffer[0].timestamp = 0;
- buffer[0].message = Pm_Message(0xC0, 0, 0); /* program change */
- Pm_Write(out, buffer, 1);
-
- output_period = get_number(
- "MIDI out should be sent every __ callback iterations: ");
-
- assert(output_period >= 1);
- }
- }
-
- printf("Latency measurements will start in 5 seconds. "
- "Type return to stop: ");
- Pt_Start(period, &pt_callback, 0);
- while (getchar() != '\n') ;
- stop = Pt_Time();
- Pt_Stop();
-
- /* courteously turn off the last note, if necessary */
- if (note_on) {
- PmEvent buffer[1];
- buffer[0].timestamp = Pt_Time();
- buffer[0].message = Pm_Message(0x90, 60, 0);
- Pm_Write(out, buffer, 1);
- }
-
- /* print the histogram */
- printf("Duration of test: %g seconds\n\n", max(0, stop - 5000) * 0.001);
- printf("Latency(ms) Number of occurrences\n");
- /* avoid printing beyond last non-zero histogram entry */
- len = min(HIST_LEN, max_latency + 1);
- for (i = 0; i < len; i++) {
- printf("%2d %10d\n", i, histogram[i]);
- }
- printf("Number of points greater than %dms: %d\n",
- HIST_LEN - 1, out_of_range);
- printf("Maximum latency: %d milliseconds\n", max_latency);
- printf("\nNote that due to rounding, actual latency can be 1ms higher\n");
- printf("than the numbers reported here.\n");
- printf("Type return to exit...");
- while (getchar() != '\n') ;
-
- if(choice == 2)
- Pm_Close(in);
- else if(choice == 3)
- Pm_Close(out);
- else if(choice == 4)
- {
- Pm_Close(in);
- Pm_Close(out);
- }
- return 0;
-}
-
-
-/* read a number from console */
-int get_number(const char *prompt)
-{
- int n = 0, i;
- fputs(prompt, stdout);
- while (n != 1) {
- n = scanf("%d", &i);
- while (getchar() != '\n') ;
- }
- return i;
-}
diff --git a/portmidi/pm_test/midiclock.c b/portmidi/pm_test/midiclock.c deleted file mode 100644 index f0a6897..0000000 --- a/portmidi/pm_test/midiclock.c +++ /dev/null @@ -1,282 +0,0 @@ -/* miditime.c -- a test program that sends midi clock and MTC */ - -#include "portmidi.h" -#include "porttime.h" -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <assert.h> -#include <ctype.h> - -#ifndef false -#define false 0 -#define true 1 -#endif - -#define private static -typedef int boolean; - -#define MIDI_TIME_CLOCK 0xf8 -#define MIDI_START 0xfa -#define MIDI_CONTINUE 0xfb -#define MIDI_STOP 0xfc -#define MIDI_Q_FRAME 0xf1 - -#define OUTPUT_BUFFER_SIZE 0 -#define DRIVER_INFO NULL -#define TIME_PROC ((PmTimeProcPtr) Pt_Time) -#define TIME_INFO NULL -#define LATENCY 0 -#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */ - -#define STRING_MAX 80 /* used for console input */ - -/* to determine ms per clock: - * time per beat in seconds = 60 / tempo - * multiply by 1000 to get time per beat in ms: 60000 / tempo - * divide by 24 CLOCKs per beat: (60000/24) / tempo - * simplify: 2500 / tempo - */ -#define TEMPO_TO_CLOCK 2500.0 - -boolean done = false; -PmStream *midi; -/* shared flags to control callback output generation: */ -boolean clock_running = false; -boolean send_start_stop = false; -boolean time_code_running = false; -boolean active = false; /* tells callback to do its thing */ -float tempo = 60.0F; -/* protocol for handing off portmidi to callback thread: - main owns portmidi - main sets active = true: ownership transfers to callback - main sets active = false: main requests ownership - callback sees active == false, yields ownership back to main - main waits 2ms to make sure callback has a chance to yield - (stop making PortMidi calls), then assumes it can close - PortMidi - */ - -/* timer_poll -- the timer callback function */ -/* - * All MIDI sends take place here - */ -void timer_poll(PtTimestamp timestamp, void *userData) -{ - static int callback_owns_portmidi = false; - static PmTimestamp clock_start_time = 0; - static double next_clock_time = 0; - /* SMPTE time */ - static int frames = 0; - static int seconds = 0; - static int minutes = 0; - static int hours = 0; - static int mtc_count = 0; /* where are we in quarter frame sequence? */ - static int smpte_start_time = 0; - static double next_smpte_time = 0; - #define QUARTER_FRAME_PERIOD (1.0 / 120.0) /* 30fps, 1/4 frame */ - - if (callback_owns_portmidi && !active) { - /* main is requesting (by setting active to false) that we shut down */ - callback_owns_portmidi = false; - return; - } - if (!active) return; /* main still getting ready or it's closing down */ - callback_owns_portmidi = true; /* main is ready, we have portmidi */ - if (send_start_stop) { - if (clock_running) { - Pm_WriteShort(midi, 0, MIDI_STOP); - } else { - Pm_WriteShort(midi, 0, MIDI_START); - clock_start_time = timestamp; - next_clock_time = TEMPO_TO_CLOCK / tempo; - } - clock_running = !clock_running; - send_start_stop = false; /* until main sets it again */ - /* note that there's a slight race condition here: main could - set send_start_stop asynchronously, but we assume user is - typing slower than the clock rate */ - } - if (clock_running) { - if ((timestamp - clock_start_time) > next_clock_time) { - Pm_WriteShort(midi, 0, MIDI_TIME_CLOCK); - next_clock_time += TEMPO_TO_CLOCK / tempo; - } - } - if (time_code_running) { - int data = 0; // initialization avoids compiler warning - if ((timestamp - smpte_start_time) < next_smpte_time) - return; - switch (mtc_count) { - case 0: /* frames low nibble */ - data = frames; - break; - case 1: /* frames high nibble */ - data = frames >> 4; - break; - case 2: /* frames seconds low nibble */ - data = seconds; - break; - case 3: /* frames seconds high nibble */ - data = seconds >> 4; - break; - case 4: /* frames minutes low nibble */ - data = minutes; - break; - case 5: /* frames minutes high nibble */ - data = minutes >> 4; - break; - case 6: /* hours low nibble */ - data = hours; - break; - case 7: /* hours high nibble */ - data = hours >> 4; - break; - } - data &= 0xF; /* take only 4 bits */ - Pm_WriteShort(midi, 0, - Pm_Message(MIDI_Q_FRAME, (mtc_count << 4) + data, 0)); - mtc_count = (mtc_count + 1) & 7; /* wrap around */ - if (mtc_count == 0) { /* update time by two frames */ - frames += 2; - if (frames >= 30) { - frames = 0; - seconds++; - if (seconds >= 60) { - seconds = 0; - minutes++; - if (minutes >= 60) { - minutes = 0; - hours++; - /* just let hours wrap if it gets that far */ - } - } - } - } - next_smpte_time += QUARTER_FRAME_PERIOD; - } else { /* time_code_running is false */ - smpte_start_time = timestamp; - /* so that when it finally starts, we'll be in sync */ - } -} - - -/* read a number from console */ -/**/ -int get_number(const char *prompt) -{ - int n = 0, i; - fputs(prompt, stdout); - while (n != 1) { - n = scanf("%d", &i); - while (getchar() != '\n') ; - } - return i; -} - -/**************************************************************************** -* showhelp -* Effect: print help text -****************************************************************************/ - -private void showhelp() -{ - printf("\n"); - printf("t toggles sending MIDI Time Code (MTC)\n"); - printf("c toggles sending MIDI CLOCK (initially on)\n"); - printf("m to set tempo (from 1bpm to 300bpm)\n"); - printf("q quits\n"); - printf("\n"); -} - -/**************************************************************************** -* doascii -* Inputs: -* char c: input character -* Effect: interpret to control output -****************************************************************************/ - -private void doascii(char c) -{ - if (isupper(c)) c = tolower(c); - if (c == 'q') done = true; - else if (c == 'c') { - printf("%s MIDI CLOCKs\n", (clock_running ? "Stopping" : "Starting")); - send_start_stop = true; - } else if (c == 't') { - printf("%s MIDI Time Code\n", - (time_code_running ? "Stopping" : "Starting")); - time_code_running = !time_code_running; - } else if (c == 'm') { - int input_tempo = get_number("Enter new tempo (bpm): "); - if (input_tempo >= 1 && input_tempo <= 300) { - printf("Changing tempo to %d\n", input_tempo); - tempo = (float) input_tempo; - } else { - printf("Tempo range is 1 to 300, current tempo is %g bpm\n", - tempo); - } - } else { - showhelp(); - } -} - - -/* main - prompt for parameters, start processing */ -/* - * Prompt user to type return. - * Then send START and MIDI CLOCK for 60 beats/min. - * Commands: - * t - toggle sending MIDI Time Code (MTC) - * c - toggle sending MIDI CLOCK - * m - set tempo - * q - quit - */ -int main(int argc, char **argv) -{ - int outp; - PmError err; - int i; - if (argc > 1) { - printf("Warning: command line arguments ignored\n"); - } - showhelp(); - /* use porttime callback to send midi */ - Pt_Start(1, timer_poll, 0); - /* list device information */ - printf("MIDI output devices:\n"); - for (i = 0; i < Pm_CountDevices(); i++) { - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - if (info->output) printf("%d: %s, %s\n", i, info->interf, info->name); - } - outp = get_number("Type output device number: "); - err = Pm_OpenOutput(&midi, outp, DRIVER_INFO, OUTPUT_BUFFER_SIZE, - TIME_PROC, TIME_INFO, LATENCY); - if (err) { - puts(Pm_GetErrorText(err)); - goto error_exit_no_device; - } - active = true; - - printf("Type ENTER to start MIDI CLOCK:\n"); - while (getchar() != '\n') ; - send_start_stop = true; /* send START and then CLOCKs */ - - while (!done) { - doascii(getchar()); - while (getchar() != '\n') ; - } - - active = false; - Pt_Sleep(2); /* this is to allow callback to complete -- it's - real time, so it's either ok and it runs on - time, or there's no point to synchronizing - with it */ - /* now we "own" portmidi again */ - Pm_Close(midi); - error_exit_no_device: - Pt_Stop(); - Pm_Terminate(); - exit(0); -} - diff --git a/portmidi/pm_test/midithread.c b/portmidi/pm_test/midithread.c deleted file mode 100755 index ea0613b..0000000 --- a/portmidi/pm_test/midithread.c +++ /dev/null @@ -1,343 +0,0 @@ -/* midithread.c -- example program showing how to do midi processing - in a preemptive thread - - Notes: if you handle midi I/O from your main program, there will be - some delay before handling midi messages whenever the program is - doing something like file I/O, graphical interface updates, etc. - - To handle midi with minimal delay, you should do all midi processing - in a separate, high priority thread. A convenient way to get a high - priority thread in windows is to use the timer callback provided by - the PortTime library. That is what we show here. - - If the high priority thread writes to a file, prints to the console, - or does just about anything other than midi processing, this may - create delays, so all this processing should be off-loaded to the - "main" process or thread. Communication between threads can be tricky. - If one thread is writing at the same time the other is reading, very - tricky race conditions can arise, causing programs to behave - incorrectly, but only under certain timing conditions -- a terrible - thing to debug. Advanced programmers know this as a synchronization - problem. See any operating systems textbook for the complete story. - - To avoid synchronization problems, a simple, reliable approach is - to communicate via messages. PortMidi offers a message queue as a - datatype, and operations to insert and remove messages. Use two - queues as follows: midi_to_main transfers messages from the midi - thread to the main thread, and main_to_midi transfers messages from - the main thread to the midi thread. Queues are safe for use between - threads as long as ONE thread writes and ONE thread reads. You must - NEVER allow two threads to write to the same queue. - - This program transposes incoming midi data by an amount controlled - by the main program. To change the transposition, type an integer - followed by return. The main program sends this via a message queue - to the midi thread. To quit, type 'q' followed by return. - - The midi thread can also send a pitch to the main program on request. - Type 'm' followed by return to wait for the next midi message and - print the pitch. - - This program illustrates: - Midi processing in a high-priority thread. - Communication with a main process via message queues. - - */ - -#include "stdio.h" -#include "stdlib.h" -#include "string.h" -#include "assert.h" -#include "portmidi.h" -#include "pmutil.h" -#include "porttime.h" - -/* if INPUT_BUFFER_SIZE is 0, PortMidi uses a default value */ -#define INPUT_BUFFER_SIZE 0 - -#define OUTPUT_BUFFER_SIZE 100 -#define DRIVER_INFO NULL -#define TIME_PROC NULL -#define TIME_INFO NULL -/* use zero latency because we want output to be immediate */ -#define LATENCY 0 - -#define STRING_MAX 80 - -/**********************************/ -/* DATA USED ONLY BY process_midi */ -/* (except during initialization) */ -/**********************************/ - -int active = FALSE; -int monitor = FALSE; -int midi_thru = TRUE; - -int transpose; -PmStream *midi_in; -PmStream *midi_out; - -/****************************/ -/* END OF process_midi DATA */ -/****************************/ - -/* shared queues */ -PmQueue *midi_to_main; -PmQueue *main_to_midi; - -#define QUIT_MSG 1000 -#define MONITOR_MSG 1001 -#define THRU_MSG 1002 - -/* timer interrupt for processing midi data */ -void process_midi(PtTimestamp timestamp, void *userData) -{ - PmError result; - PmEvent buffer; /* just one message at a time */ - int32_t msg; - - /* do nothing until initialization completes */ - if (!active) - return; - - /* check for messages */ - do { - result = Pm_Dequeue(main_to_midi, &msg); - if (result) { - if (msg >= -127 && msg <= 127) - transpose = msg; - else if (msg == QUIT_MSG) { - /* acknowledge receipt of quit message */ - Pm_Enqueue(midi_to_main, &msg); - active = FALSE; - return; - } else if (msg == MONITOR_MSG) { - /* main has requested a pitch. monitor is a flag that - * records the request: - */ - monitor = TRUE; - } else if (msg == THRU_MSG) { - /* toggle Thru on or off */ - midi_thru = !midi_thru; - } - } - } while (result); - - /* see if there is any midi input to process */ - do { - result = Pm_Poll(midi_in); - if (result) { - int status, data1, data2; - if (Pm_Read(midi_in, &buffer, 1) == pmBufferOverflow) - continue; - if (midi_thru) - Pm_Write(midi_out, &buffer, 1); - /* unless there was overflow, we should have a message now */ - status = Pm_MessageStatus(buffer.message); - data1 = Pm_MessageData1(buffer.message); - data2 = Pm_MessageData2(buffer.message); - if ((status & 0xF0) == 0x90 || - (status & 0xF0) == 0x80) { - - /* this is a note-on or note-off, so transpose and send */ - data1 += transpose; - - /* keep within midi pitch range, keep proper pitch class */ - while (data1 > 127) - data1 -= 12; - while (data1 < 0) - data1 += 12; - - /* send the message */ - buffer.message = Pm_Message(status, data1, data2); - Pm_Write(midi_out, &buffer, 1); - - /* if monitor is set, send the pitch to the main thread */ - if (monitor) { - Pm_Enqueue(midi_to_main, &data1); - monitor = FALSE; /* only send one pitch per request */ - } - } - } - } while (result); -} - -void exit_with_message(char *msg) -{ - printf("%s\n", msg); - while (getchar() != '\n') ; - exit(1); -} - -int main(int argc, char *argv[]) -{ - int32_t n; - const PmDeviceInfo *info; - char line[STRING_MAX]; - int spin; - int done = FALSE; - int i; - int input = -1, output = -1; - - printf("Usage: midithread [-i input] [-o output]\n" - "where input and output are portmidi device numbers\n"); - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-i") == 0) { - i++; - input = atoi(argv[i]); - printf("Input device number: %d\n", input); - } else if (strcmp(argv[i], "-o") == 0) { - i++; - output = atoi(argv[i]); - printf("Output device number: %d\n", output); - } else { - return -1; - } - } - printf("begin PortMidi multithread test...\n"); - - /* note that it is safe to call PortMidi from the main thread for - initialization and opening devices. You should not make any - calls to PortMidi from this thread once the midi thread begins. - to make PortMidi calls. - */ - - /* make the message queues */ - /* messages can be of any size and any type, but all messages in - * a given queue must have the same size. We'll just use int32_t's - * for our messages in this simple example - */ - midi_to_main = Pm_QueueCreate(32, sizeof(int32_t)); - assert(midi_to_main != NULL); - main_to_midi = Pm_QueueCreate(32, sizeof(int32_t)); - assert(main_to_midi != NULL); - - /* a little test of enqueue and dequeue operations. Ordinarily, - * you would call Pm_Enqueue from one thread and Pm_Dequeue from - * the other. Since the midi thread is not running, this is safe. - */ - n = 1234567890; - Pm_Enqueue(midi_to_main, &n); - n = 987654321; - Pm_Enqueue(midi_to_main, &n); - Pm_Dequeue(midi_to_main, &n); - if (n != 1234567890) { - exit_with_message("Pm_Dequeue produced unexpected result."); - } - Pm_Dequeue(midi_to_main, &n); - if(n != 987654321) { - exit_with_message("Pm_Dequeue produced unexpected result."); - } - - /* always start the timer before you start midi */ - Pt_Start(1, &process_midi, 0); /* start a timer with millisecond accuracy */ - /* the timer will call our function, process_midi() every millisecond */ - - Pm_Initialize(); - - output = (output < 0 ? Pm_GetDefaultOutputDeviceID() : output); - info = Pm_GetDeviceInfo(output); - if (info == NULL) { - printf("Could not open output device (%d).", output); - exit_with_message(""); - } - printf("Opening output device %s %s\n", info->interf, info->name); - - /* use zero latency because we want output to be immediate */ - Pm_OpenOutput(&midi_out, - output, - DRIVER_INFO, - OUTPUT_BUFFER_SIZE, - TIME_PROC, - TIME_INFO, - LATENCY); - - input = (input < 0 ? Pm_GetDefaultInputDeviceID() : input); - info = Pm_GetDeviceInfo(input); - if (info == NULL) { - printf("Could not open default input device (%d).", input); - exit_with_message(""); - } - printf("Opening input device %s %s\n", info->interf, info->name); - Pm_OpenInput(&midi_in, - input, - DRIVER_INFO, - INPUT_BUFFER_SIZE, - TIME_PROC, - TIME_INFO); - - active = TRUE; /* enable processing in the midi thread -- yes, this - is a shared variable without synchronization, but - this simple assignment is safe */ - - printf("Enter midi input; it will be transformed as specified by...\n"); - printf("Type 'q' to quit, 'm' to monitor next pitch, t to toggle thru or\n" - "type a number to specify transposition.\n" - "Must terminate with [ENTER]\n"); - - while (!done) { - int32_t msg; - int input; - int len; - if (!fgets(line, STRING_MAX, stdin)) break; /* no stdin? */ - /* remove the newline: */ - len = (int) strlen(line); - if (len > 0) line[len - 1] = 0; /* overwrite the newline char */ - if (strcmp(line, "q") == 0) { - msg = QUIT_MSG; - Pm_Enqueue(main_to_midi, &msg); - /* wait for acknowlegement */ - do { - spin = Pm_Dequeue(midi_to_main, &msg); - } while (spin == 0); /* spin */ ; - done = TRUE; /* leave the command loop and wrap up */ - } else if (strcmp(line, "m") == 0) { - msg = MONITOR_MSG; - Pm_Enqueue(main_to_midi, &msg); - printf("Waiting for note...\n"); - do { - spin = Pm_Dequeue(midi_to_main, &msg); - } while (spin == 0); /* spin */ ; - // convert int32_t to long for safe printing - printf("... pitch is %ld\n", (long) msg); - } else if (strcmp(line, "t") == 0) { - /* reading midi_thru asynchronously could give incorrect results, - e.g. if you type "t" twice before the midi thread responds to - the first one, but we'll do it this way anyway. Perhaps a more - correct way would be to wait for an acknowledgement message - containing the new state. */ - printf("Setting THRU %s\n", (midi_thru ? "off" : "on")); - msg = THRU_MSG; - Pm_Enqueue(main_to_midi, &msg); - } else if (sscanf(line, "%d", &input) == 1) { - if (input >= -127 && input <= 127) { - /* send transposition value, make sur */ - printf("Transposing by %d\n", input); - msg = (int32_t) input; - Pm_Enqueue(main_to_midi, &msg); - } else { - printf("Transposition must be within -127...127\n"); - } - } else { - printf("%s\n%s\n", - "Type 'q[ENTER]' to quit, 'm[ENTER]' to monitor next pitch, or", - "enter a number to specify transposition."); - } - } - - /* at this point, midi thread is inactive and we need to shut down - * the midi input and output - */ - Pt_Stop(); /* stop the timer */ - Pm_QueueDestroy(midi_to_main); - Pm_QueueDestroy(main_to_midi); - - /* Belinda! if close fails here, some memory is deleted, right??? */ - Pm_Close(midi_in); - Pm_Close(midi_out); - - fputs("finished portMidi multithread test.\n" - "type ENTER to quit:", stdout); - while (getchar() != '\n') ; - return 0; -} diff --git a/portmidi/pm_test/midithru.c b/portmidi/pm_test/midithru.c deleted file mode 100755 index 94b4f13..0000000 --- a/portmidi/pm_test/midithru.c +++ /dev/null @@ -1,455 +0,0 @@ -/* midithru.c -- example program implementing background thru processing */
-
-/* suppose you want low-latency midi-thru processing, but your
- application wants to take advantage of the input buffer and
- timestamped data so that it does not have to operate with very low
- latency.
-
- This program illustrates how to use a timer callback from PortTime
- to implement a low-latency process that handles midi thru,
- including correctly merging midi data from the application with
- midi data from the input port.
-
- The main application, which runs in the main program thread, will
- use an interface similar to that of PortMidi, but since PortMidi
- does not allow concurrent threads to share access to a stream, the
- application will call private methods that transfer MIDI messages
- to and from the timer thread using lock-free queues. All PortMidi
- API calls are made from the timer thread.
- */
-
-/* DESIGN
-
-All setup will be done by the main thread. Then, all direct access to
-PortMidi will be handed off to the timer callback thread.
-
-After this hand-off, the main thread will get/send messages via a queue.
-
-The goal is to send incoming messages to the midi output while merging
-any midi data generated by the application. Sysex is a problem here
-because you cannot insert (merge) a midi message while a sysex is in
-progress. There are at least three ways to implement midi thru with
-sysex messages:
-
-1) Turn them off. If your application does not need them, turn them off
- with Pm_SetFilter(midi_in, PM_FILT_ACTIVE | PM_FILT_SYSEX). You will
- not receive sysex (or active sensing messages), so you will not have
- to handle them.
-
-2) Make them atomic. As you receive sysex messages, copy the data into
- a (big) buffer. Ideally, expand the buffer as needed -- sysex messages
- do not have any maximum length. Even more ideally, use a list structure
- and real-time memory allocation to avoid latency in the timer thread.
- When a full sysex message is received, send it to the midi output all
- at once.
-
-3) Process sysex incrementally. Send sysex data to midi output as it
- arrives. Block any non-real-time messages from the application until
- the sysex message completes. There is the risk that an incomplete
- sysex message will block messages forever, so implement a 5-second
- timeout: if no sysex data is seen for 5 seconds, release the block,
- possibly losing the rest of the sysex message.
-
- Application messages must be processed similarly: once started, a
- sysex message will block MIDI THRU processing. We will assume that
- the application will not abort a sysex message, so timeouts are not
- necessary here.
-
-This code implements (3).
-
-Latency is also an issue. PortMidi requires timestamps to be in
-non-decreasing order. Since we'll be operating with a low-latency
-timer thread, we can just set the latency to zero meaning timestamps
-are ignored by PortMidi. This will allow thru to go through with
-minimal latency. The application, however, needs to use timestamps
-because we assume it is high latency (the whole purpose of this
-example is to illustrate how to get low-latency thru with a high-latency
-application.) So the callback thread will implement midi timing by
-observing timestamps. The current timestamp will be available in the
-global variable current_timestamp.
-
-*/
-
-
-#include "stdio.h"
-#include "stdlib.h"
-#include "string.h"
-#include "assert.h"
-#include "portmidi.h"
-#include "pmutil.h"
-#include "porttime.h"
-
-#define MIDI_SYSEX 0xf0
-#define MIDI_EOX 0xf7
-#define STRING_MAX 80 /* used for console input */
-
-/* active is set true when midi processing should start, must be
- * volatile to force thread to check for updates by other thread */
-int active = FALSE;
-/* process_midi_exit_flag is set when the timer thread shuts down;
- * must be volatile so it is re-read in the while loop that waits on it */
-volatile int process_midi_exit_flag;
-
-PmStream *midi_in;
-PmStream *midi_out;
-
-/* shared queues */
-#define IN_QUEUE_SIZE 1024
-#define OUT_QUEUE_SIZE 1024
-PmQueue *in_queue;
-PmQueue *out_queue;
-/* this is volatile because it is set in the process_midi callback and
- * the main thread reads it to sense elapsed time. Without volatile, the
- * optimizer can put it in a register and not see the updates.
- */
-volatile PmTimestamp current_timestamp = 0;
-int thru_sysex_in_progress = FALSE;
-int app_sysex_in_progress = FALSE;
-PmTimestamp last_timestamp = 0;
-
-
-static void prompt_and_exit(void)
-{
- printf("type ENTER...");
- while (getchar() != '\n') ;
- /* this will clean up open ports: */
- exit(-1);
-}
-
-
-static PmError checkerror(PmError err)
-{
- if (err == pmHostError) {
- /* it seems pointless to allocate memory and copy the string,
- * so I will do the work of Pm_GetHostErrorText directly
- */
- char errmsg[80];
- Pm_GetHostErrorText(errmsg, 80);
- printf("PortMidi found host error...\n %s\n", errmsg);
- prompt_and_exit();
- } else if (err < 0) {
- printf("PortMidi call failed...\n %s\n", Pm_GetErrorText(err));
- prompt_and_exit();
- }
- return err;
-}
-
-
-/* time proc parameter for Pm_MidiOpen */
-PmTimestamp midithru_time_proc(void *info)
-{
- return current_timestamp;
-}
-
-
-/* timer interrupt for processing midi data.
- Incoming data is delivered to main program via in_queue.
- Outgoing data from main program is delivered via out_queue.
- Incoming data from midi_in is copied with low latency to midi_out.
- Sysex messages from either source block messages from the other.
- */
-void process_midi(PtTimestamp timestamp, void *userData)
-{
- PmError result;
- PmEvent buffer; /* just one message at a time */
-
- current_timestamp++; /* update every millisecond */
-
- /* do nothing until initialization completes */
- if (!active) {
- /* this flag signals that no more midi processing will be done */
- process_midi_exit_flag = TRUE;
- return;
- }
-
- /* see if there is any midi input to process */
- if (!app_sysex_in_progress) {
- do {
- result = Pm_Poll(midi_in);
- if (result) {
- int status;
- PmError rslt = Pm_Read(midi_in, &buffer, 1);
- if (rslt == pmBufferOverflow)
- continue;
- assert(rslt == 1);
-
- /* record timestamp of most recent data */
- last_timestamp = current_timestamp;
-
- /* the data might be the end of a sysex message that
- has timed out, in which case we must ignore it.
- It's a continuation of a sysex message if status
- is actually a data byte (high-order bit is zero). */
- status = Pm_MessageStatus(buffer.message);
- if (((status & 0x80) == 0) && !thru_sysex_in_progress) {
- continue; /* ignore this data */
- }
-
- /* implement midi thru */
- /* note that you could output to multiple ports or do other
- processing here if you wanted
- */
- /* printf("thru: %x\n", buffer.message); */
- Pm_Write(midi_out, &buffer, 1);
-
- /* send the message to the application */
- /* you might want to filter clock or active sense messages here
- to avoid sending a bunch of junk to the application even if
- you want to send it to MIDI THRU
- */
- Pm_Enqueue(in_queue, &buffer);
-
- /* sysex processing */
- if (status == MIDI_SYSEX) thru_sysex_in_progress = TRUE;
- else if ((status & 0xF8) != 0xF8) {
- /* not MIDI_SYSEX and not real-time, so */
- thru_sysex_in_progress = FALSE;
- }
- if (thru_sysex_in_progress && /* look for EOX */
- (((buffer.message & 0xFF) == MIDI_EOX) ||
- (((buffer.message >> 8) & 0xFF) == MIDI_EOX) ||
- (((buffer.message >> 16) & 0xFF) == MIDI_EOX) ||
- (((buffer.message >> 24) & 0xFF) == MIDI_EOX))) {
- thru_sysex_in_progress = FALSE;
- }
- }
- } while (result);
- }
-
-
- /* see if there is application midi data to process */
- while (!Pm_QueueEmpty(out_queue)) {
- /* see if it is time to output the next message */
- PmEvent *next = (PmEvent *) Pm_QueuePeek(out_queue);
- assert(next); /* must be non-null because queue is not empty */
- if (next->timestamp <= current_timestamp) {
- /* time to send a message, first make sure it's not blocked */
- int status = Pm_MessageStatus(next->message);
- if ((status & 0xF8) == 0xF8) {
- ; /* real-time messages are not blocked */
- } else if (thru_sysex_in_progress) {
- /* maybe sysex has timed out (output becomes unblocked) */
- if (last_timestamp + 5000 < current_timestamp) {
- thru_sysex_in_progress = FALSE;
- } else break; /* output is blocked, so exit loop */
- }
- Pm_Dequeue(out_queue, &buffer);
- Pm_Write(midi_out, &buffer, 1);
-
- /* inspect message to update app_sysex_in_progress */
- if (status == MIDI_SYSEX) app_sysex_in_progress = TRUE;
- else if ((status & 0xF8) != 0xF8) {
- /* not MIDI_SYSEX and not real-time, so */
- app_sysex_in_progress = FALSE;
- }
- if (app_sysex_in_progress && /* look for EOX */
- (((buffer.message & 0xFF) == MIDI_EOX) ||
- (((buffer.message >> 8) & 0xFF) == MIDI_EOX) ||
- (((buffer.message >> 16) & 0xFF) == MIDI_EOX) ||
- (((buffer.message >> 24) & 0xFF) == MIDI_EOX))) {
- app_sysex_in_progress = FALSE;
- }
- } else break; /* wait until indicated timestamp */
- }
-}
-
-
-void exit_with_message(char *msg)
-{
-#define STRING_MAX 80
- printf("%s\nType ENTER...", msg);
- while (getchar() != '\n') ;
- exit(1);
-}
-
-
-void initialize(int input, int output, int virtual)
-/* set up midi processing thread and open midi streams */
-{
- /* note that it is safe to call PortMidi from the main thread for
- initialization and opening devices. You should not make any
- calls to PortMidi from this thread once the midi thread begins.
- to make PortMidi calls.
- */
-
- /* note that this routine provides minimal error checking. If
- you use the PortMidi library compiled with PM_CHECK_ERRORS,
- then error messages will be printed and the program will exit
- if an error is encountered. Otherwise, you should add some
- error checking to this code.
- */
-
- const PmDeviceInfo *info;
-
- /* make the message queues */
- in_queue = Pm_QueueCreate(IN_QUEUE_SIZE, sizeof(PmEvent));
- assert(in_queue != NULL);
- out_queue = Pm_QueueCreate(OUT_QUEUE_SIZE, sizeof(PmEvent));
- assert(out_queue != NULL);
-
- /* always start the timer before you start midi */
- Pt_Start(1, &process_midi, 0); /* start a timer with millisecond accuracy */
- /* the timer will call our function, process_midi() every millisecond */
-
- Pm_Initialize();
-
- if (output < 0) {
- if (!virtual) {
- output = Pm_GetDefaultOutputDeviceID();
- }
- }
- if (output >= 0) {
- info = Pm_GetDeviceInfo(output);
- if (info == NULL) {
- printf("Could not open default output device (%d).", output);
- exit_with_message("");
- }
-
- printf("Opening output device %s %s\n", info->interf, info->name);
-
- /* use zero latency because we want output to be immediate */
- Pm_OpenOutput(&midi_out,
- output,
- NULL /* driver info */,
- OUT_QUEUE_SIZE,
- &midithru_time_proc,
- NULL /* time info */,
- 0 /* Latency */);
- } else { /* send to virtual port */
- int id; - printf("Opening virtual output device \"midithru\"\n");
- id = Pm_CreateVirtualOutput("midithru", NULL, NULL); - if (id < 0) checkerror(id); /* error reporting */
- checkerror(Pm_OpenOutput(&midi_out, id, NULL, OUT_QUEUE_SIZE,
- &midithru_time_proc, NULL, 0));
- }
- if (input < 0) {
- if (!virtual) {
- input = Pm_GetDefaultInputDeviceID();
- }
- }
- if (input >= 0) {
- info = Pm_GetDeviceInfo(input);
- if (info == NULL) {
- printf("Could not open default input device (%d).", input);
- exit_with_message("");
- }
-
- printf("Opening input device %s %s\n", info->interf, info->name);
- Pm_OpenInput(&midi_in,
- input,
- NULL /* driver info */,
- 0 /* use default input size */,
- &midithru_time_proc,
- NULL /* time info */);
- } else { /* receive from virtual port */
- int id; - printf("Opening virtual input device \"midithru\"\n");
- id = Pm_CreateVirtualInput("midithru", NULL, NULL); - if (id < 0) checkerror(id); /* error reporting */
- checkerror(Pm_OpenInput(&midi_in, id, NULL, 0,
- &midithru_time_proc, NULL));
- }
- /* Note: if you set a filter here, then this will filter what goes
- to the MIDI THRU port. You may not want to do this.
- */
- Pm_SetFilter(midi_in, PM_FILT_ACTIVE | PM_FILT_CLOCK);
-
- active = TRUE; /* enable processing in the midi thread -- yes, this
- is a shared variable without synchronization, but
- this simple assignment is safe */
-
-}
-
-
-void finalize()
-{
- /* the timer thread could be in the middle of accessing PortMidi stuff */
- /* to detect that it is done, we first clear process_midi_exit_flag and
- then wait for the timer thread to set it
- */
- process_midi_exit_flag = FALSE;
- active = FALSE;
- /* busy wait for flag from timer thread that it is done */
- while (!process_midi_exit_flag) ;
- /* at this point, midi thread is inactive and we need to shut down
- * the midi input and output
- */
- Pt_Stop(); /* stop the timer */
- Pm_QueueDestroy(in_queue);
- Pm_QueueDestroy(out_queue);
-
- Pm_Close(midi_in);
- Pm_Close(midi_out);
-
- Pm_Terminate();
-}
-
-
-int main(int argc, char *argv[])
-{
- PmTimestamp last_time = 0;
- PmEvent buffer;
- int i;
- int input = -1, output = -1;
- int virtual = FALSE;
- int delay_enable = TRUE;
-
- printf("Usage: midithru [-i input] [-o output] [-v] [-n]\n"
- "where input and output are portmidi device numbers\n"
- "if -v and input and/or output are not specified,\n"
- "then virtual ports are created and used instead.\n"
- "-n turns off the default MIDI delay effect.\n");
- for (i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-i") == 0) {
- i++;
- input = atoi(argv[i]);
- printf("Input device number: %d\n", input);
- } else if (strcmp(argv[i], "-o") == 0) {
- i++;
- output = atoi(argv[i]);
- printf("Output device number: %d\n", output);
- } else if (strcmp(argv[i], "-v") == 0) {
- virtual = TRUE;
- } else if (strcmp(argv[i], "-n") == 0) {
- delay_enable = FALSE;
- printf("delay_effect is disabled\n");
- } else {
- return -1;
- }
- }
- printf("begin PortMidi midithru program...\n");
-
- initialize(input, output, virtual); /* set up and start midi processing */
-
- printf("This program will run for 60 seconds, "
- "or until you play B below middle C,\n"
- "All input is sent immediately, implementing software MIDI THRU.\n"
- "Also, all input is echoed with a 2 second delay.\n");
-
- while (current_timestamp < 60000) {
- /* just to make the point that this is not a low-latency process,
- spin until half a second has elapsed */
- last_time = last_time + 500;
- while (last_time > current_timestamp) ;
-
- /* now read data and send it after changing timestamps */
- while (Pm_Dequeue(in_queue, &buffer) == 1) {
- /* printf("timestamp %d\n", buffer.timestamp); */
- /* printf("message %x\n", buffer.message); */
- if (delay_enable) {
- buffer.timestamp = buffer.timestamp + 2000; /* delay */
- Pm_Enqueue(out_queue, &buffer);
- }
- /* play B3 to break out of loop */
- if (Pm_MessageStatus(buffer.message) == 0x90 &&
- Pm_MessageData1(buffer.message) == 59) {
- goto quit_now;
- }
- }
- }
-quit_now:
- finalize();
- exit_with_message("finished PortMidi midithru program.");
- return 0; /* never executed, but keeps the compiler happy */
-}
diff --git a/portmidi/pm_test/mm.c b/portmidi/pm_test/mm.c deleted file mode 100755 index ab9d32e..0000000 --- a/portmidi/pm_test/mm.c +++ /dev/null @@ -1,595 +0,0 @@ -/* mm.c -- midi monitor */
-
-/*****************************************************************************
-* Change Log
-* Date | Change
-*-----------+-----------------------------------------------------------------
-* 7-Apr-86 | Created changelog
-* 31-Jan-90 | GWL : use new cmdline
-* 5-Apr-91 | JDW : Further changes
-* 16-Feb-92 | GWL : eliminate label mmexit:; add error recovery
-* 18-May-92 | GWL : continuous clocks, etc.
-* 17-Jan-94 | GWL : option to display notes
-* 20-Nov-06 | RBD : port mm.c from CMU Midi Toolkit to PortMidi
-* | mm.c -- revealing MIDI secrets for over 20 years!
-*****************************************************************************/
-
-#include "stdlib.h"
-#include "ctype.h"
-#include "string.h"
-#include "stdio.h"
-#include "porttime.h"
-#include "portmidi.h"
-
-#define STRING_MAX 80
-
-#define MIDI_CODE_MASK 0xf0
-#define MIDI_CHN_MASK 0x0f
-/*#define MIDI_REALTIME 0xf8
- #define MIDI_CHAN_MODE 0xfa */
-#define MIDI_OFF_NOTE 0x80
-#define MIDI_ON_NOTE 0x90
-#define MIDI_POLY_TOUCH 0xa0
-#define MIDI_CTRL 0xb0
-#define MIDI_CH_PROGRAM 0xc0
-#define MIDI_TOUCH 0xd0
-#define MIDI_BEND 0xe0
-
-#define MIDI_SYSEX 0xf0
-#define MIDI_Q_FRAME 0xf1
-#define MIDI_SONG_POINTER 0xf2
-#define MIDI_SONG_SELECT 0xf3
-#define MIDI_TUNE_REQ 0xf6
-#define MIDI_EOX 0xf7
-#define MIDI_TIME_CLOCK 0xf8
-#define MIDI_START 0xfa
-#define MIDI_CONTINUE 0xfb
-#define MIDI_STOP 0xfc
-#define MIDI_ACTIVE_SENSING 0xfe
-#define MIDI_SYS_RESET 0xff
-
-#define MIDI_ALL_SOUND_OFF 0x78
-#define MIDI_RESET_CONTROLLERS 0x79
-#define MIDI_LOCAL 0x7a
-#define MIDI_ALL_OFF 0x7b
-#define MIDI_OMNI_OFF 0x7c
-#define MIDI_OMNI_ON 0x7d
-#define MIDI_MONO_ON 0x7e
-#define MIDI_POLY_ON 0x7f
-
-
-#define private static
-
-#ifndef false
-#define false 0
-#define true 1
-#endif
-
-typedef int boolean;
-
-int debug = false; /* never set, but referenced by userio.c */
-PmStream *midi_in; /* midi input */
-boolean active = false; /* set when midi_in is ready for reading */
-boolean in_sysex = false; /* we are reading a sysex message */
-boolean inited = false; /* suppress printing during command line parsing */
-boolean done = false; /* when true, exit */
-boolean notes = true; /* show notes? */
-boolean controls = true; /* show continuous controllers */
-boolean bender = true; /* record pitch bend etc.? */
-boolean excldata = true; /* record system exclusive data? */
-boolean verbose = true; /* show text representation? */
-boolean realdata = true; /* record real time messages? */
-boolean clksencnt = true; /* clock and active sense count on */
-boolean chmode = true; /* show channel mode messages */
-boolean pgchanges = true; /* show program changes */
-boolean flush = false; /* flush all pending MIDI data */
-
-uint32_t filter = 0; /* remember state of midi filter */
-
-uint32_t clockcount = 0; /* count of clocks */
-uint32_t actsensecount = 0; /* cout of active sensing bytes */
-uint32_t notescount = 0; /* #notes since last request */
-uint32_t notestotal = 0; /* total #notes */
-
-char val_format[] = " Val %d\n";
-
-/*****************************************************************************
-* Imported variables
-*****************************************************************************/
-
-extern int abort_flag;
-
-/*****************************************************************************
-* Routines local to this module
-*****************************************************************************/
-
-private void mmexit(int code);
-private void output(PmMessage data);
-private int put_pitch(int p);
-private void showhelp();
-private void showbytes(PmMessage data, int len, boolean newline);
-private void showstatus(boolean flag);
-private void doascii(char c);
-private int get_number(const char *prompt);
-
-
-/* read a number from console */
-/**/
-int get_number(const char *prompt)
-{
- int n = 0, i;
- fputs(prompt, stdout);
- while (n != 1) {
- n = scanf("%d", &i);
- while (getchar() != '\n') ;
- }
- return i;
-}
-
-
-void receive_poll(PtTimestamp timestamp, void *userData)
-{
- PmEvent event;
- int count;
- if (!active) return;
- while ((count = Pm_Read(midi_in, &event, 1))) {
- if (count == 1) output(event.message);
- else puts(Pm_GetErrorText(count));
- }
-}
-
-
-/****************************************************************************
-* main
-* Effect: prompts for parameters, starts monitor
-****************************************************************************/
-
-int main(int argc, char **argv)
-{
- char *argument;
- int inp;
- PmError err;
- int i;
- if (argc > 1) { /* first arg can change defaults */
- argument = argv[1];
- while (*argument) doascii(*argument++);
- }
- showhelp();
- /* use porttime callback to empty midi queue and print */
- Pt_Start(1, receive_poll, 0);
- /* list device information */
- puts("MIDI input devices:");
- for (i = 0; i < Pm_CountDevices(); i++) {
- const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
- if (info->input) printf("%d: %s, %s\n", i, info->interf, info->name);
- }
- inp = get_number("Type input device number: ");
- err = Pm_OpenInput(&midi_in, inp, NULL, 512, NULL, NULL);
- if (err) {
- puts(Pm_GetErrorText(err));
- Pt_Stop();
- mmexit(1);
- }
- Pm_SetFilter(midi_in, filter);
- inited = true; /* now can document changes, set filter */
- printf("Midi Monitor ready.\n");
- active = true;
- while (!done) {
- doascii(getchar());
- while (getchar() != '\n') ;
- }
- active = false;
- Pm_Close(midi_in);
- Pt_Stop();
- Pm_Terminate();
- mmexit(0);
- return 0; // make the compiler happy be returning a value
-}
-
-
-/****************************************************************************
-* doascii
-* Inputs:
-* char c: input character
-* Effect: interpret to revise flags
-****************************************************************************/
-
-private void doascii(char c)
-{
- if (isupper(c)) c = tolower(c);
- if (c == 'q') done = true;
- else if (c == 'b') {
- bender = !bender;
- filter ^= PM_FILT_PITCHBEND;
- if (inited)
- printf("Pitch Bend, etc. %s\n", (bender ? "ON" : "OFF"));
- } else if (c == 'c') {
- controls = !controls;
- filter ^= PM_FILT_CONTROL;
- if (inited)
- printf("Control Change %s\n", (controls ? "ON" : "OFF"));
- } else if (c == 'h') {
- pgchanges = !pgchanges;
- filter ^= PM_FILT_PROGRAM;
- if (inited)
- printf("Program Changes %s\n", (pgchanges ? "ON" : "OFF"));
- } else if (c == 'n') {
- notes = !notes;
- filter ^= PM_FILT_NOTE;
- if (inited)
- printf("Notes %s\n", (notes ? "ON" : "OFF"));
- } else if (c == 'x') {
- excldata = !excldata;
- filter ^= PM_FILT_SYSEX;
- if (inited)
- printf("System Exclusive data %s\n", (excldata ? "ON" : "OFF"));
- } else if (c == 'r') {
- realdata = !realdata;
- filter ^= (PM_FILT_PLAY | PM_FILT_RESET | PM_FILT_TICK | PM_FILT_UNDEFINED);
- if (inited)
- printf("Real Time messages %s\n", (realdata ? "ON" : "OFF"));
- } else if (c == 'k') {
- clksencnt = !clksencnt;
- if (inited) {
- printf("Clock and Active Sense Counting %s\n", (clksencnt ? "ON" : "OFF"));
- printf("Resetting Clock and Active Sense counts.\n");
- clockcount = actsensecount = 0;
- }
- } else if (c == 's') {
- if (inited) {
- printf("Clock Count %ld\nActive Sense Count %ld\n",
- (long) clockcount, (long) actsensecount);
- }
- } else if (c == 't') {
- notestotal+=notescount;
- if (inited)
- printf("This Note Count %ld\nTotal Note Count %ld\n",
- (long) notescount, (long) notestotal);
- notescount=0;
- } else if (c == 'v') {
- verbose = !verbose;
- if (inited)
- printf("Verbose %s\n", (verbose ? "ON" : "OFF"));
- } else if (c == 'm') {
- chmode = !chmode;
- if (inited)
- printf("Channel Mode Messages %s", (chmode ? "ON" : "OFF"));
- } else {
- if (inited) {
- if (c == ' ') {
- PmEvent event;
- while (Pm_Read(midi_in, &event, 1)) ; /* flush midi input */
- printf("...FLUSHED MIDI INPUT\n\n");
- } else showhelp();
- }
- }
- if (inited) Pm_SetFilter(midi_in, filter);
-}
-
-
-
-private void mmexit(int code)
-{
- /* if this is not being run from a console, maybe we should wait for
- * the user to read error messages before exiting
- */
- exit(code);
-}
-
-
-/****************************************************************************
-* output
-* Inputs:
-* data: midi message buffer holding one command or 4 bytes of sysex msg
-* Effect: format and print midi data
-****************************************************************************/
-
-char vel_format[] = " Vel %d\n";
-
-private void output(PmMessage data)
-{
- int command; /* the current command */
- int chan; /* the midi channel of the current event */
- int len; /* used to get constant field width */
-
- /* printf("output data %8x; ", data); */
-
- command = Pm_MessageStatus(data) & MIDI_CODE_MASK;
- chan = Pm_MessageStatus(data) & MIDI_CHN_MASK;
-
- if (in_sysex || Pm_MessageStatus(data) == MIDI_SYSEX) {
-#define sysex_max 16
- int i;
- PmMessage data_copy = data;
- in_sysex = true;
- /* look for MIDI_EOX in first 3 bytes
- * if realtime messages are embedded in sysex message, they will
- * be printed as if they are part of the sysex message
- */
- for (i = 0; (i < 4) && ((data_copy & 0xFF) != MIDI_EOX); i++)
- data_copy >>= 8;
- if (i < 4) {
- in_sysex = false;
- i++; /* include the EOX byte in output */
- }
- showbytes(data, i, verbose);
- if (verbose) printf("System Exclusive\n");
- } else if (command == MIDI_ON_NOTE && Pm_MessageData2(data) != 0) {
- notescount++;
- if (notes) {
- showbytes(data, 3, verbose);
- if (verbose) {
- printf("NoteOn Chan %2d Key %3d ", chan, Pm_MessageData1(data));
- len = put_pitch(Pm_MessageData1(data));
- printf(vel_format + len, Pm_MessageData2(data));
- }
- }
- } else if ((command == MIDI_ON_NOTE /* && Pm_MessageData2(data) == 0 */ ||
- command == MIDI_OFF_NOTE)) {
- if (notes) {
- showbytes(data, 3, verbose);
- if (verbose) {
- printf("NoteOff Chan %2d Key %3d ", chan,
- Pm_MessageData1(data));
- len = put_pitch(Pm_MessageData1(data));
- printf(vel_format + len, Pm_MessageData2(data));
- }
- }
- } else if (command == MIDI_CH_PROGRAM) {
- if (pgchanges) {
- showbytes(data, 2, verbose);
- if (verbose) {
- printf(" ProgChg Chan %2d Prog %2d\n", chan,
- Pm_MessageData1(data) + 1);
- }
- }
- } else if (command == MIDI_CTRL) {
- /* controls 121 (MIDI_RESET_CONTROLLER) to 127 are channel
- * mode messages. */
- if (Pm_MessageData1(data) < MIDI_ALL_SOUND_OFF) {
- if (controls) {
- showbytes(data, 3, verbose);
- if (verbose) {
- printf("CtrlChg Chan %2d Ctrl %2d Val %2d\n",
- chan, Pm_MessageData1(data), Pm_MessageData2(data));
- }
- } else /* channel mode */ if (chmode) {
- showbytes(data, 3, verbose);
- if (verbose) {
- switch (Pm_MessageData1(data)) {
- case MIDI_ALL_SOUND_OFF:
- printf("All Sound Off, Chan %2d\n", chan);
- break;
- case MIDI_RESET_CONTROLLERS:
- printf("Reset All Controllers, Chan %2d\n", chan);
- break;
- case MIDI_LOCAL:
- printf("LocCtrl Chan %2d %s\n",
- chan, Pm_MessageData2(data) ? "On" : "Off");
- break;
- case MIDI_ALL_OFF:
- printf("All Off Chan %2d\n", chan);
- break;
- case MIDI_OMNI_OFF:
- printf("OmniOff Chan %2d\n", chan);
- break;
- case MIDI_OMNI_ON:
- printf("Omni On Chan %2d\n", chan);
- break;
- case MIDI_MONO_ON:
- printf("Mono On Chan %2d\n", chan);
- if (Pm_MessageData2(data))
- printf(" to %d received channels\n",
- Pm_MessageData2(data));
- else
- printf(" to all received channels\n");
- break;
- case MIDI_POLY_ON:
- printf("Poly On Chan %2d\n", chan);
- break;
- }
- }
- }
- }
- } else if (command == MIDI_POLY_TOUCH) {
- if (bender) {
- showbytes(data, 3, verbose);
- if (verbose) {
- printf("P.Touch Chan %2d Key %2d ", chan,
- Pm_MessageData1(data));
- len = put_pitch(Pm_MessageData1(data));
- printf(val_format + len, Pm_MessageData2(data));
- }
- }
- } else if (command == MIDI_TOUCH) {
- if (bender) {
- showbytes(data, 2, verbose);
- if (verbose) {
- printf(" A.Touch Chan %2d Val %2d\n", chan,
- Pm_MessageData1(data));
- }
- }
- } else if (command == MIDI_BEND) {
- if (bender) {
- showbytes(data, 3, verbose);
- if (verbose) {
- printf("P.Bend Chan %2d Val %2d\n", chan,
- (Pm_MessageData1(data) + (Pm_MessageData2(data)<<7)));
- }
- }
- } else if (Pm_MessageStatus(data) == MIDI_SONG_POINTER) {
- showbytes(data, 3, verbose);
- if (verbose) {
- printf(" Song Position %d\n",
- (Pm_MessageData1(data) + (Pm_MessageData2(data)<<7)));
- }
- } else if (Pm_MessageStatus(data) == MIDI_SONG_SELECT) {
- showbytes(data, 2, verbose);
- if (verbose) {
- printf(" Song Select %d\n", Pm_MessageData1(data));
- }
- } else if (Pm_MessageStatus(data) == MIDI_TUNE_REQ) {
- showbytes(data, 1, verbose);
- if (verbose) {
- printf(" Tune Request\n");
- }
- } else if (Pm_MessageStatus(data) == MIDI_Q_FRAME) {
- if (realdata) {
- showbytes(data, 2, verbose);
- if (verbose) {
- printf(" Time Code Quarter Frame Type %d Values %d\n",
- (Pm_MessageData1(data) & 0x70) >> 4,
- Pm_MessageData1(data) & 0xf);
- }
- }
- } else if (Pm_MessageStatus(data) == MIDI_START) {
- if (realdata) {
- showbytes(data, 1, verbose);
- if (verbose) {
- printf(" Start\n");
- }
- }
- } else if (Pm_MessageStatus(data) == MIDI_CONTINUE) {
- if (realdata) {
- showbytes(data, 1, verbose);
- if (verbose) {
- printf(" Continue\n");
- }
- }
- } else if (Pm_MessageStatus(data) == MIDI_STOP) {
- if (realdata) {
- showbytes(data, 1, verbose);
- if (verbose) {
- printf(" Stop\n");
- }
- }
- } else if (Pm_MessageStatus(data) == MIDI_SYS_RESET) {
- if (realdata) {
- showbytes(data, 1, verbose);
- if (verbose) {
- printf(" System Reset\n");
- }
- }
- } else if (Pm_MessageStatus(data) == MIDI_TIME_CLOCK) {
- clockcount++;
- if (clksencnt) {
- showbytes(data, 1, verbose);
- if (verbose) {
- printf(" Clock\n");
- }
- }
- } else if (Pm_MessageStatus(data) == MIDI_ACTIVE_SENSING) {
- actsensecount++;
- if (clksencnt) {
- showbytes(data, 1, verbose);
- if (verbose) {
- printf(" Active Sensing\n");
- }
- }
- } else showbytes(data, 3, verbose);
- fflush(stdout);
-}
-
-
-/****************************************************************************
-* put_pitch
-* Inputs:
-* int p: pitch number
-* Effect: write out the pitch name for a given number
-****************************************************************************/
-
-private int put_pitch(int p)
-{
- char result[8];
- static char *ptos[] = {
- "c", "cs", "d", "ef", "e", "f", "fs", "g",
- "gs", "a", "bf", "b" };
- /* note octave correction below */
- sprintf(result, "%s%d", ptos[p % 12], (p / 12) - 1);
- fputs(result, stdout);
- return (int) strlen(result);
-}
-
-
-/****************************************************************************
-* showbytes
-* Effect: print hex data, precede with newline if asked
-****************************************************************************/
-
-char nib_to_hex[] = "0123456789ABCDEF";
-
-private void showbytes(PmMessage data, int len, boolean newline)
-{
- int count = 0;
- int i;
-
-/* if (newline) {
- putchar('\n');
- count++;
- } */
- for (i = 0; i < len; i++) {
- putchar(nib_to_hex[(data >> 4) & 0xF]);
- putchar(nib_to_hex[data & 0xF]);
- count += 2;
- if (count > 72) {
- putchar('.');
- putchar('.');
- putchar('.');
- break;
- }
- data >>= 8;
- }
- putchar(' ');
-}
-
-
-
-/****************************************************************************
-* showhelp
-* Effect: print help text
-****************************************************************************/
-
-private void showhelp()
-{
- printf("\n");
- printf(" Item Reported Range Item Reported Range\n");
- printf(" ------------- ----- ------------- -----\n");
- printf(" Channels 1 - 16 Programs 1 - 128\n");
- printf(" Controllers 0 - 127 After Touch 0 - 127\n");
- printf(" Loudness 0 - 127 Pitch Bend 0 - 16383, "
- "center = 8192\n");
- printf(" Pitches 0 - 127, 60 = c4 = middle C\n");
- printf(" \n");
- printf("n toggles notes");
- showstatus(notes);
- printf("t displays noteon count since last t\n");
- printf("b toggles pitch bend, aftertouch");
- showstatus(bender);
- printf("c toggles continuous control");
- showstatus(controls);
- printf("h toggles program changes");
- showstatus(pgchanges);
- printf("x toggles system exclusive");
- showstatus(excldata);
- printf("k toggles clock and sense messages, clears counts");
- showstatus(clksencnt);
- printf("r toggles other real time messages & SMPTE");
- showstatus(realdata);
- printf("s displays clock and sense count since last k\n");
- printf("m toggles channel mode messages");
- showstatus(chmode);
- printf("v toggles verbose text");
- showstatus(verbose);
- printf("q quits\n");
- printf("\n");
-}
-
-/****************************************************************************
-* showstatus
-* Effect: print status of flag
-****************************************************************************/
-
-private void showstatus(boolean flag)
-{
- printf(", now %s\n", flag ? "ON" : "OFF");
-}
diff --git a/portmidi/pm_test/multivirtual.c b/portmidi/pm_test/multivirtual.c deleted file mode 100644 index b90d860..0000000 --- a/portmidi/pm_test/multivirtual.c +++ /dev/null @@ -1,223 +0,0 @@ -/* multivirtual.c -- test for creating two input and two output virtual ports */ -/* - * Roger B. Dannenberg - * Oct 2021 - */ -#include "portmidi.h" -#include "porttime.h" -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "assert.h" - -#define OUTPUT_BUFFER_SIZE 0 -#define DEVICE_INFO NULL -#define DRIVER_INFO NULL -#define TIME_PROC ((PmTimeProcPtr) Pt_Time) -#define TIME_INFO NULL -#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */ - -int latency = 0; - -static void prompt_and_exit(void) -{ - printf("type ENTER..."); - while (getchar() != '\n') ; - /* this will clean up open ports: */ - exit(-1); -} - - -static PmError checkerror(PmError err) -{ - if (err == pmHostError) { - /* it seems pointless to allocate memory and copy the string, - * so I will do the work of Pm_GetHostErrorText directly - */ - char errmsg[80]; - Pm_GetHostErrorText(errmsg, 80); - printf("PortMidi found host error...\n %s\n", errmsg); - prompt_and_exit(); - } else if (err < 0) { - printf("PortMidi call failed...\n %s\n", Pm_GetErrorText(err)); - prompt_and_exit(); - } - return err; -} - -static int msg_count[2] = {0, 0}; - -void poll_input(PmStream *in, int which) -{ - PmEvent buffer[1]; - int pitch, expected, length; - PmError status = Pm_Poll(in); - if (status == TRUE) { - length = Pm_Read(in, buffer, 1); - if (length > 0) { - printf("Got message %d from portmidi%d: " - "time %ld, %2x %2x %2x\n", - msg_count[which], which + 1, (long) buffer[0].timestamp, - (status = Pm_MessageStatus(buffer[0].message)), - (pitch = Pm_MessageData1(buffer[0].message)), - Pm_MessageData2(buffer[0].message)); - if (status == 0x90) { /* 1 & 2 are on/off 60, 3 & 4 are 61, etc. */ - expected = (((msg_count[which] - 1) / 2) % 12) + 60 + - which * 12; - if (pitch != expected) { - printf("WARNING: expected pitch %d, got pitch %d\n", - expected, pitch); - } - } - msg_count[which]++; - } else { - assert(0); - } - } -} - - -void wait_until(PmTimestamp when, PmStream *in1, PmStream *in2) -{ - while (when > Pt_Time()) { - poll_input(in1, 0); - poll_input(in2, 1); - Pt_Sleep(10); - } -} - - -/* create one virtual output device and one input device */ -void init(const char *name, PmStream **midi_out, PmStream **midi_in, - int *id_out, int *id_in) -{ - PmEvent buffer[1]; - - *id_out = checkerror(Pm_CreateVirtualOutput(name, NULL, DEVICE_INFO)); - checkerror(Pm_OpenOutput(midi_out, *id_out, DRIVER_INFO, OUTPUT_BUFFER_SIZE, - TIME_PROC, TIME_INFO, latency)); - printf("Virtual Output \"%s\" id %d created and opened.\n", name, *id_out); - - *id_in = checkerror(Pm_CreateVirtualInput(name, NULL, DRIVER_INFO)); - checkerror(Pm_OpenInput(midi_in, *id_in, NULL, 0, NULL, NULL)); - printf("Virtual Input \"%s\" id %d created and opened.\n", name, *id_in); - Pm_SetFilter(*midi_in, PM_FILT_ACTIVE | PM_FILT_CLOCK | PM_FILT_SYSEX); - /* empty the buffer after setting filter, just in case anything - got through */ - while (Pm_Read(*midi_in, buffer, 1)) ; -} - - -void main_test(int num) -{ - PmStream *midi1_out; - PmStream *midi2_out; - PmStream *midi1_in; - PmStream *midi2_in; - int id1_out; - int id2_out; - int id1_in; - int id2_in; - int32_t next_time; - PmEvent buffer[1]; - int pitch = 60; - int expected_count = num + 1; /* add 1 for MIDI Program message */ - - /* It is recommended to start timer before Midi; otherwise, PortMidi may - start the timer with its (default) parameters - */ - TIME_START; - - init("portmidi1", &midi1_out, &midi1_in, &id1_out, &id1_in); - init("portmidi2", &midi2_out, &midi2_in, &id2_out, &id2_in); - - printf("Type ENTER to send messages: "); - while (getchar() != '\n') ; - - buffer[0].timestamp = Pt_Time(); -#define PROGRAM 0 - buffer[0].message = Pm_Message(0xC0, PROGRAM, 0); - Pm_Write(midi1_out, buffer, 1); - Pm_Write(midi2_out, buffer, 1); - next_time = Pt_Time() + 1000; /* wait 1s */ - while (num > 0) { - wait_until(next_time, midi1_in, midi2_in); - Pm_WriteShort(midi1_out, next_time, Pm_Message(0x90, pitch, 100)); - Pm_WriteShort(midi2_out, next_time, Pm_Message(0x90, pitch + 12, 100)); - printf("Note On pitch %d\n", pitch); - num--; - next_time += 500; - - wait_until(next_time, midi1_in, midi2_in); - Pm_WriteShort(midi1_out, next_time, Pm_Message(0x90, pitch, 0)); - Pm_WriteShort(midi2_out, next_time, Pm_Message(0x90, pitch + 12, 0)); - printf("Note Off pitch %d\n", pitch); - num--; - pitch = (pitch + 1) % 12 + 60; - next_time += 500; - } - wait_until(next_time, midi1_in, midi2_in); /* get final note-offs */ - - printf("Got %d messages from portmidi1 and %d from portmidi2; " - "expected %d.\n", msg_count[0], msg_count[1], expected_count); - - /* close devices (this not explicitly needed in most implementations) */ - printf("ready to close..."); - checkerror(Pm_Close(midi1_out)); - checkerror(Pm_Close(midi2_out)); - checkerror(Pm_Close(midi1_in)); - checkerror(Pm_Close(midi2_in)); - printf("done closing.\nNow delete the virtual devices..."); - checkerror(Pm_DeleteVirtualDevice(id1_out)); - checkerror(Pm_DeleteVirtualDevice(id1_in)); - checkerror(Pm_DeleteVirtualDevice(id2_out)); - checkerror(Pm_DeleteVirtualDevice(id2_in)); - printf("done deleting.\n"); -} - - -void show_usage() -{ - printf("Usage: multivirtual [-h] [-l latency-in-ms] [n]\n" - " -h for this message,\n" - " -l ms designates latency for precise timing (default 0),\n" - " n is number of message to send each output, not counting\n" - " initial program change.\n" - "sends change program to 1, then one note per second with 0.5s on,\n" - "0.5s off, for n/2 seconds to both output ports portmidi1 and\n" - "portmidi2. portmidi1 gets pitches from C4 (60). portmidi2 gets\n" - "pitches an octave higher. Latency >0 uses the device driver for \n" - "precise timing (see PortMidi documentation). Inputs print what\n" - "they get and print WARNING if they get something unexpected.\n" - "The expected test is use two instances of testio to loop\n" - "portmidi1 back to portmidi1 and portmidi2 back to portmidi2.\n"); - exit(0); -} - - -int main(int argc, char *argv[]) -{ - int num = 10; - int i; - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-h") == 0) { - show_usage(); - } else if (strcmp(argv[i], "-l") == 0 && (i + 1 < argc)) { - i = i + 1; - latency = atoi(argv[i]); - printf("Latency will be %d\n", latency); - } else { - num = atoi(argv[1]); - if (num <= 0) { - show_usage(); - } - printf("Sending %d messages.\n", num); - } - } - - main_test(num); - - printf("finished sendvirtual test...type ENTER to quit..."); - while (getchar() != '\n') ; - return 0; -} diff --git a/portmidi/pm_test/pmlist.c b/portmidi/pm_test/pmlist.c deleted file mode 100644 index 5e3d1db..0000000 --- a/portmidi/pm_test/pmlist.c +++ /dev/null @@ -1,63 +0,0 @@ -/* pmlist.c -- list portmidi devices and numbers - * - * This program lists devices. When you type return, it - * restarts portmidi and lists devices again. It is mainly - * a test for shutting down and restarting. - * - * Roger B. Dannenberg, Feb 2022 - */ - -#include "portmidi.h" -#include "porttime.h" -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "assert.h" - -#define DEVICE_INFO NULL -#define DRIVER_INFO NULL -#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */ - -#define STRING_MAX 80 /* used for console input */ - -void show_usage() -{ - printf("Usage: pmlist [-h]\n -h means help.\n" - " Type return to rescan and list devices, q<ret> to quit\n"); -} - - -int main(int argc, char *argv[]) -{ - if (argc > 1) { - show_usage(); - exit(0); - } - - while (1) { - char input[STRING_MAX]; - const char *deflt; - const char *in_or_out; - int default_in, default_out, i; - - // Pm_Initialize(); - /* list device information */ - default_in = Pm_GetDefaultInputDeviceID(); - default_out = Pm_GetDefaultOutputDeviceID(); - for (i = 0; i < Pm_CountDevices(); i++) { - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - printf("%d: %s, %s", i, info->interf, info->name); - deflt = ""; - if (i == default_out || i == default_in) { - deflt = "default "; - } - in_or_out = (info->input ? "input" : "output"); - printf(" (%s%s)\n", deflt, in_or_out); - } - if (fgets(input, STRING_MAX, stdin) && input[0] == 'q') { - return 0; - } - Pm_Terminate(); - } - return 0; -} diff --git a/portmidi/pm_test/qtest.c b/portmidi/pm_test/qtest.c deleted file mode 100644 index 14d803e..0000000 --- a/portmidi/pm_test/qtest.c +++ /dev/null @@ -1,174 +0,0 @@ -#include "portmidi.h" -#include "pmutil.h" -#include "stdlib.h" -#include "stdio.h" - - -/* make_msg -- make a psuedo-random message of length n whose content - * is purely a function of i - */ -void make_msg(long msg[], int n, int i) -{ - int j; - for (j = 0; j < n; j++) { - msg[j] = i % (j + 5); - } -} - - -/* print_msg -- print the content of msg of length n to stdout */ -/**/ -void print_msg(long msg[], int n) -{ - int i; - for (i = 0; i < n; i++) { - printf(" %li", msg[i]); - } -} - - -/* cmp_msg -- compare two messages of length n */ -/**/ -int cmp_msg(long msg[], long msg2[], int n, int i) -{ - int j; - for (j = 0; j < n; j++) { - if (msg[j] != msg2[j]) { - printf("Received message %d doesn't match sent message\n", i); - printf("in: "); print_msg(msg, n); printf("\n"); - printf("out:"); print_msg(msg2, n); printf("\n"); - return FALSE; - } - } - return TRUE; -} - - -int main() -{ - int msg_len; - for (msg_len = 4; msg_len < 100; msg_len += 5) { - PmQueue *queue = Pm_QueueCreate(100, msg_len * sizeof(long)); - int i; - long msg[100]; - long msg2[100]; - - printf("msg_len = %d\n", msg_len); - if (!queue) { - printf("Could not allocate queue\n"); - return 1; - } - - /* insert/remove 1000 messages */ - printf("test 1\n"); - for (i = 0; i < 1357; i++) { - make_msg(msg, msg_len, i); - if (Pm_Enqueue(queue, msg)) { - printf("Pm_Enqueue error\n"); - return 1; - } - if (Pm_Dequeue(queue, msg2) != 1) { - printf("Pm_Dequeue error\n"); - return 1; - } - if (!cmp_msg(msg, msg2, msg_len, i)) { - return 1; - } - } - - /* make full */ - printf("test 2\n"); - for (i = 0; i < 100; i++) { - make_msg(msg, msg_len, i); - if (Pm_Enqueue(queue, msg)) { - printf("Pm_Enqueue error\n"); - return 1; - } - } - - /* alternately remove and insert */ - for (i = 100; i < 1234; i++) { - make_msg(msg, msg_len, i - 100); /* what we expect */ - if (Pm_Dequeue(queue, msg2) != 1) { - printf("Pm_Dequeue error\n"); - return 1; - } - if (!cmp_msg(msg, msg2, msg_len, i)) { - return 1; - } - make_msg(msg, msg_len, i); - if (Pm_Enqueue(queue, msg)) { - printf("Pm_Enqueue error\n"); - return 1; - } - } - - /* remove all */ - while (!Pm_QueueEmpty(queue)) { - make_msg(msg, msg_len, i - 100); /* what we expect */ - if (Pm_Dequeue(queue, msg2) != 1) { - printf("Pm_Dequeue error\n"); - return 1; - } - if (!cmp_msg(msg, msg2, msg_len, i)) { - return 1; - } - i++; - } - if (i != 1334) { - printf("Message count error\n"); - return 1; - } - - /* now test overflow */ - printf("test 3\n"); - for (i = 0; i < 110; i++) { - make_msg(msg, msg_len, i); - if (Pm_Enqueue(queue, msg) == pmBufferOverflow) { - break; /* this is supposed to execute after 100 messages */ - } - } - for (i = 0; i < 100; i++) { - make_msg(msg, msg_len, i); - if (Pm_Dequeue(queue, msg2) != 1) { - printf("Pm_Dequeue error\n"); - return 1; - } - if (!cmp_msg(msg, msg2, msg_len, i)) { - return 1; - } - } - /* we should detect overflow after removing 100 messages */ - if (Pm_Dequeue(queue, msg2) != pmBufferOverflow) { - printf("Pm_Dequeue overflow expected\n"); - return 1; - } - - /* after overflow is detected (and cleared), sender can - * send again - */ - /* see if function is restored, also test peek */ - printf("test 4\n"); - for (i = 0; i < 1357; i++) { - long *peek; - make_msg(msg, msg_len, i); - if (Pm_Enqueue(queue, msg)) { - printf("Pm_Enqueue error\n"); - return 1; - } - peek = (long *) Pm_QueuePeek(queue); - if (!cmp_msg(msg, peek, msg_len, i)) { - return 1; - } - if (Pm_Dequeue(queue, msg2) != 1) { - printf("Pm_Dequeue error\n"); - return 1; - } - if (!cmp_msg(msg, msg2, msg_len, i)) { - return 1; - } - } - Pm_QueueDestroy(queue); - } - return 0; -} diff --git a/portmidi/pm_test/recvvirtual.c b/portmidi/pm_test/recvvirtual.c deleted file mode 100644 index f8d9848..0000000 --- a/portmidi/pm_test/recvvirtual.c +++ /dev/null @@ -1,175 +0,0 @@ -#include "portmidi.h" -#include "porttime.h" -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "assert.h" - -#define INPUT_BUFFER_SIZE 100 -#define TIME_PROC ((PmTimeProcPtr) Pt_Time) -#define TIME_INFO NULL -#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */ - -#define STRING_MAX 80 /* used for console input */ - -char *portname = "portmidi"; - -PmSysDepInfo *sysdepinfo = NULL; -char *port_name = "portmidi"; - -static void set_sysdepinfo(char m_or_p, const char *name) -{ - if (!sysdepinfo) { - // allocate some space we will alias with open-ended PmDriverInfo: - // there is space for 4 parameters: - static char dimem[sizeof(PmSysDepInfo) + sizeof(void *) * 8]; - sysdepinfo = (PmSysDepInfo *) dimem; - // build the driver info structure: - sysdepinfo->structVersion = PM_SYSDEPINFO_VERS; - sysdepinfo->length = 0; - } - if (sysdepinfo->length > 1) { - printf("Error: sysdepinfo was allocated to hold 2 parameters\n"); - exit(1); - } - int i = sysdepinfo->length++; - enum PmSysDepPropertyKey k = pmKeyNone; - if (m_or_p == 'm') k = pmKeyCoreMidiManufacturer; - else if (m_or_p == 'p') k = pmKeyAlsaPortName; - else if (m_or_p == 'c') k = pmKeyAlsaClientName; - sysdepinfo->properties[i].key = k; - sysdepinfo->properties[i].value = name; -} - - -static void prompt_and_exit(void) -{ - printf("type ENTER..."); - while (getchar() != '\n') ; - /* this will clean up open ports: */ - exit(-1); -} - - -static PmError checkerror(PmError err) -{ - if (err == pmHostError) { - /* it seems pointless to allocate memory and copy the string, - * so I will do the work of Pm_GetHostErrorText directly - */ - char errmsg[80]; - Pm_GetHostErrorText(errmsg, 80); - printf("PortMidi found host error...\n %s\n", errmsg); - prompt_and_exit(); - } else if (err < 0) { - printf("PortMidi call failed...\n %s\n", Pm_GetErrorText(err)); - prompt_and_exit(); - } - return err; -} - - -void main_test_input(int num) -{ - PmStream *midi; - PmError status, length; - PmEvent buffer[1]; - int id; - int i = 0; /* count messages as they arrive */ - /* It is recommended to start timer before Midi; otherwise, PortMidi may - start the timer with its (default) parameters - */ - TIME_START; - - /* create a virtual input device */ - id = checkerror(Pm_CreateVirtualInput(port_name, NULL, sysdepinfo)); - checkerror(Pm_OpenInput(&midi, id, sysdepinfo, 0, NULL, NULL)); - - printf("Midi Input opened. Reading %d Midi messages...\n", num); - Pm_SetFilter(midi, PM_FILT_ACTIVE | PM_FILT_CLOCK | PM_FILT_SYSEX); - /* empty the buffer after setting filter, just in case anything - got through */ - while (Pm_Poll(midi)) { - Pm_Read(midi, buffer, 1); - } - /* now start paying attention to messages */ - while (i < num) { - status = Pm_Poll(midi); - if (status == TRUE) { - length = Pm_Read(midi, buffer, 1); - if (length > 0) { - printf("Got message %d: time %ld, %2lx %2lx %2lx\n", - i, - (long) buffer[0].timestamp, - (long) Pm_MessageStatus(buffer[0].message), - (long) Pm_MessageData1(buffer[0].message), - (long) Pm_MessageData2(buffer[0].message)); - i++; - } else { - assert(0); - } - } - } - - /* close device (this not explicitly needed in most implementations) */ - printf("ready to close..."); - Pm_Close(midi); - printf("done closing.\nNow delete the virtual device..."); - checkerror(Pm_DeleteVirtualDevice(id)); - printf("done deleting.\n"); -} - - -void show_usage() -{ - printf("Usage: recvvirtual [-h] [-m manufacturer] [-c clientname] " - "[-p portname] [n]\n" - " -h for this message,\n" - " -m name designates a manufacturer name (macOS only),\n" - " -c name designates a client name (linux only),\n" - " -p name designates a port name (linux only),\n" - " n is number of message to wait for.\n"); - exit(0); -} - - -int main(int argc, char *argv[]) -{ - char line[STRING_MAX]; - int num = 10; - int i; - if (argc <= 1) { - show_usage(); - } - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-h") == 0) { - show_usage(); - } else if (strcmp(argv[i], "-m") == 0 && (i + 1 < argc)) { - i = i + 1; - set_sysdepinfo('m', argv[i]); - printf("Manufacturer name will be %s\n", argv[i]); - } else if (strcmp(argv[i], "-p") == 0 && (i + 1 < argc)) { - i = i + 1; - port_name = argv[i]; - set_sysdepinfo('p', port_name); - printf("Port name will be %s\n", port_name); - } else if (strcmp(argv[i], "-c") == 0 && (i + 1 < argc)) { - i = i + 1; - set_sysdepinfo('c', argv[i]); - printf("Client name will be %s\n", argv[i]); - } else { - num = atoi(argv[i]); - if (num <= 0) { - printf("Zero value or non-number for n\n"); - show_usage(); - } - printf("Waiting for %d messages.\n", num); - } - } - - main_test_input(num); - - printf("finished portMidi test...type ENTER to quit..."); - while (getchar() != '\n') ; - return 0; -} diff --git a/portmidi/pm_test/sendvirtual.c b/portmidi/pm_test/sendvirtual.c deleted file mode 100644 index a60a48f..0000000 --- a/portmidi/pm_test/sendvirtual.c +++ /dev/null @@ -1,194 +0,0 @@ -/* sendvirtual.c -- test for creating a virtual device and sending to it */ -/* - * Roger B. Dannenberg - * Sep 2021 - */ -#include "portmidi.h" -#include "porttime.h" -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "assert.h" - -#define OUTPUT_BUFFER_SIZE 0 -#define TIME_PROC ((PmTimeProcPtr) Pt_Time) -#define TIME_INFO NULL -#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */ - -int latency = 0; -PmSysDepInfo *sysdepinfo = NULL; -char *port_name = "portmidi"; - -static void set_sysdepinfo(char m_or_p, const char *name) -{ - if (!sysdepinfo) { - // allocate some space we will alias with open-ended PmDriverInfo: - // there is space for 4 parameters: - static char dimem[sizeof(PmSysDepInfo) + sizeof(void *) * 8]; - sysdepinfo = (PmSysDepInfo *) dimem; - // build the driver info structure: - sysdepinfo->structVersion = PM_SYSDEPINFO_VERS; - sysdepinfo->length = 0; - } - if (sysdepinfo->length > 1) { - printf("Error: sysdepinfo was allocated to hold 2 parameters\n"); - exit(1); - } - int i = sysdepinfo->length++; - enum PmSysDepPropertyKey k = pmKeyNone; - if (m_or_p == 'm') k = pmKeyCoreMidiManufacturer; - else if (m_or_p == 'p') k = pmKeyAlsaPortName; - else if (m_or_p == 'c') k = pmKeyAlsaClientName; - sysdepinfo->properties[i].key = k; - sysdepinfo->properties[i].value = name; -} - - -static void prompt_and_exit(void) -{ - printf("type ENTER..."); - while (getchar() != '\n') ; - /* this will clean up open ports: */ - exit(-1); -} - - -static PmError checkerror(PmError err) -{ - if (err == pmHostError) { - /* it seems pointless to allocate memory and copy the string, - * so I will do the work of Pm_GetHostErrorText directly - */ - char errmsg[80]; - Pm_GetHostErrorText(errmsg, 80); - printf("PortMidi found host error...\n %s\n", errmsg); - prompt_and_exit(); - } else if (err < 0) { - printf("PortMidi call failed...\n %s\n", Pm_GetErrorText(err)); - prompt_and_exit(); - } - return err; -} - - -void wait_until(PmTimestamp when) -{ - PtTimestamp now = Pt_Time(); - if (when > now) { - Pt_Sleep(when - now); - } -} - - -void main_test_output(int num) -{ - PmStream *midi; - int32_t next_time; - PmEvent buffer[1]; - PmTimestamp timestamp; - int pitch = 60; - int id; - - /* It is recommended to start timer before Midi; otherwise, PortMidi may - start the timer with its (default) parameters - */ - TIME_START; - - /* create a virtual output device */ - id = checkerror(Pm_CreateVirtualOutput(port_name, NULL, sysdepinfo)); - checkerror(Pm_OpenOutput(&midi, id, sysdepinfo, OUTPUT_BUFFER_SIZE, - TIME_PROC, TIME_INFO, latency)); - - printf("Midi Output Virtual Device \"%s\" created.\n", port_name); - printf("Type ENTER to send messages: "); - while (getchar() != '\n') ; - - buffer[0].timestamp = Pt_Time(); -#define PROGRAM 0 - buffer[0].message = Pm_Message(0xC0, PROGRAM, 0); - Pm_Write(midi, buffer, 1); - next_time = Pt_Time() + 1000; /* wait 1s */ - while (num > 0) { - wait_until(next_time); - Pm_WriteShort(midi, next_time, Pm_Message(0x90, pitch, 100)); - printf("Note On pitch %d\n", pitch); - num--; - next_time += 500; - - wait_until(next_time); - Pm_WriteShort(midi, next_time, Pm_Message(0x90, pitch, 0)); - printf("Note Off pitch %d\n", pitch); - num--; - pitch = (pitch + 1) % 12 + 60; - next_time += 500; - } - - /* close device (this not explicitly needed in most implementations) */ - printf("ready to close..."); - Pm_Close(midi); - printf("done closing.\nNow delete the virtual device..."); - checkerror(Pm_DeleteVirtualDevice(id)); - printf("done deleting.\n"); -} - - -void show_usage() -{ - printf("Usage: sendvirtual [-h] [-l latency-in-ms] [-m manufacturer] " - "[-c clientname] [-p portname] [n]\n" - " -h for this message,\n" - " -l ms designates latency for precise timing (default 0),\n" - " -m name designates a manufacturer name (macOS only),\n" - " -c name designates a client name (linux only),\n" - " -p name designates a port name (linux only),\n" - " n is number of message to send.\n" - "sends change program to 1, then one note per second with 0.5s on,\n" - "0.5s off, for n/2 seconds. Latency >0 uses the device driver for \n" - "precise timing (see PortMidi documentation).\n"); - exit(0); -} - - -int main(int argc, char *argv[]) -{ - int num = 10; - int i; - if (argc <= 1) { - show_usage(); - } - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-h") == 0) { - show_usage(); - } else if (strcmp(argv[i], "-l") == 0 && (i + 1 < argc)) { - i = i + 1; - latency = atoi(argv[i]); - printf("Latency will be %d\n", latency); - } else if (strcmp(argv[i], "-m") == 0 && (i + 1 < argc)) { - i = i + 1; - set_sysdepinfo('m', argv[i]); - printf("Manufacturer name will be %s\n", argv[i]); - } else if (strcmp(argv[i], "-p") == 0 && (i + 1 < argc)) { - i = i + 1; - port_name = argv[i]; - set_sysdepinfo('p', port_name); - printf("Port name will be %s\n", port_name); - } else if (strcmp(argv[i], "-c") == 0 && (i + 1 < argc)) { - i = i + 1; - set_sysdepinfo('c', argv[i]); - printf("Client name will be %s\n", argv[i]); - } else { - num = atoi(argv[i]); - if (num <= 0) { - printf("Zero value or non-number for n\n"); - show_usage(); - } - printf("Sending %d messages.\n", num); - } - } - - main_test_output(num); - - printf("finished sendvirtual test...type ENTER to quit..."); - while (getchar() != '\n') ; - return 0; -} diff --git a/portmidi/pm_test/sysex.c b/portmidi/pm_test/sysex.c deleted file mode 100755 index c2c7187..0000000 --- a/portmidi/pm_test/sysex.c +++ /dev/null @@ -1,556 +0,0 @@ -/* sysex.c -- example program showing how to send and receive sysex
- messages
-
- Messages are stored in a file using 2-digit hexadecimal numbers,
- one per byte, separated by blanks, with up to 32 numbers per line:
- F0 14 A7 4B ...
-
- */
-
-#include "stdio.h"
-#include "stdlib.h"
-#include "assert.h"
-#include "portmidi.h"
-#include "porttime.h"
-#include "string.h"
-#ifdef WIN32
-// need to get declaration for Sleep()
-#include "windows.h"
-#else
-#include <unistd.h>
-#define Sleep(n) usleep(n * 1000)
-#endif
-
-// enable some extra printing
-#ifndef VERBOSE
-#define VERBOSE 0
-#endif
-
-#define MIDI_SYSEX 0xf0
-#define MIDI_EOX 0xf7
-
-#define STRING_MAX 80
-
-#ifndef true
-#define true 1
-#define false 0
-#endif
-
-int latency = 0;
-
-/* read a number from console */
-/**/
-int get_number(const char *prompt)
-{
- int n = 0, i;
- fputs(prompt, stdout);
- while (n != 1) {
- n = scanf("%d", &i);
- while (getchar() != '\n') ;
- }
- return i;
-}
-
-
-/* loopback test -- send/rcv from 2 to 1000 bytes of random midi data */
-/**/
-void loopback_test()
-{
- int outp;
- int inp;
- PmStream *midi_in;
- PmStream *midi_out;
- unsigned char msg[1024];
- int32_t len;
- int i;
- int data;
- PmEvent event;
- int shift;
- long total_bytes = 0;
- int32_t begin_time;
-
- Pt_Start(1, 0, 0);
-
- printf("Connect a midi cable from an output port to an input port.\n");
- printf("This test will send random data via sysex message from output\n");
- printf("to input and check that the correct data was received.\n");
- outp = get_number("Type output device number: ");
- /* Open output with 1ms latency -- when latency is non-zero, the Win32
- implementation supports sending sysex messages incrementally in a
- series of buffers. This is nicer than allocating a big buffer for the
- message, and it also seems to work better. Either way works.
- */
- while ((latency = get_number(
- "Latency in milliseconds (0 to send data immediatedly,\n"
- " >0 to send timestamped messages): ")) < 0);
- Pm_OpenOutput(&midi_out, outp, NULL, 0, NULL, NULL, latency);
- inp = get_number("Type input device number: ");
- /* since we are going to send and then receive, make sure the input buffer
- is large enough for the entire message */
- Pm_OpenInput(&midi_in, inp, NULL, 512, NULL, NULL);
-
- srand((unsigned int) Pt_Time()); /* seed for random numbers */
-
- begin_time = Pt_Time();
- while (total_bytes < 100000) {
- PmError count;
- int32_t start_time;
- int error_position = -1; /* 0; -1; -1 for continuous */
- int expected = 0;
- int actual = 0;
- /* this modification will run until an error is detected */
- /* set error_position above to 0 for interactive, -1 for */
- /* continuous */
- if (error_position >= 0) {
- int c;
- printf("Type return to send message, q to quit: ");
- while ((c = getchar()) != '\n') {
- if (c == 'q') goto cleanup;
- }
- }
-
- /* compose the message */
- len = rand() % 998 + 2; /* len only counts data bytes */
- msg[0] = (char) MIDI_SYSEX; /* start of SYSEX message */
- /* data bytes go from 1 to len */
- for (i = 0; i < len; i++) {
-/* pick whether data is sequential or random... (docs say random) */
-#define DATA_EXPR (i+1)
-// #define DATA_EXPR rand()
- msg[i + 1] = DATA_EXPR & 0x7f; /* MIDI data */
- }
- /* final EOX goes in len+1, total of len+2 bytes in msg */
- msg[len + 1] = (char) MIDI_EOX;
-
- /* sanity check: before we send, there should be no queued data */
- count = Pm_Read(midi_in, &event, 1);
-
- if (count != 0) {
- printf("Before sending anything, a MIDI message was found in\n");
- printf("the input buffer. Please try again.\n");
- break;
- }
-
- /* send the message two ways: 1) Pm_WriteSysEx, 2) Pm_Write */
- if (total_bytes & 1) {
- printf("Sending %d byte sysex msg via Pm_WriteSysEx.\n", len + 2);
- Pm_WriteSysEx(midi_out, 0, msg);
- } else {
- PmEvent event = {0, 0};
- int bits = 0;
- printf("Sending %d byte sysex msg via Pm_Write(s).\n", len + 2);
- for (i = 0; i < len + 2; i++) {
- event.message |= (msg[i] << bits);
- bits += 8;
- if (bits == 32) { /* full message - send it */
- Pm_Write(midi_out, &event, 1);
- bits = 0;
- event.message = 0;
- }
- }
- if (bits > 0) { /* last message is partially full */
- Pm_Write(midi_out, &event, 1);
- }
- }
-
- /* receive the message and compare to msg[] */
- data = 0;
- shift = 0;
- i = 0;
- start_time = Pt_Time();
- if (VERBOSE) {
- printf("start_time %d\n", start_time);
- }
- error_position = -1;
- /* allow up to 2 seconds for transmission */
- while (data != MIDI_EOX && start_time + 2000 > Pt_Time()) {
- count = Pm_Read(midi_in, &event, 1);
- if (count == 0) {
- Sleep(1); /* be nice: give some CPU time to the system */
- continue; /* continue polling for input */
- }
- if (VERBOSE) {
- printf("read %08x ", event.message);
- fflush(stdout);
- }
- /* compare 4 bytes of data until you reach an eox */
- for (shift = 0; shift < 32 && (data != MIDI_EOX); shift += 8) {
- data = (event.message >> shift) & 0xFF;
- if (data != msg[i] && error_position < 0) {
- error_position = i;
- expected = msg[i];
- actual = data;
- }
- i++;
- }
- }
- if (error_position >= 0) {
- printf("Error at time %d byte %d: sent %x recd %x.\n", Pt_Time(),
- error_position, expected, actual);
- break;
- } else if (i != len + 2) {
- printf("Error at time %d: byte %d not received.\n", Pt_Time(), i);
- break;
- } else {
- int seconds = (Pt_Time() - begin_time) / 1000;
- if (seconds == 0) seconds = 1;
- printf("Correctly received %d byte sysex message.\n", i);
- total_bytes += i;
- printf("Cummulative bytes/sec: %d, %d%% done.\n",
- (int) (total_bytes / seconds),
- (int) (100 * total_bytes / 100000));
- }
- }
-cleanup:
- Pm_Close(midi_out);
- Pm_Close(midi_in);
- return;
-}
-
-
-/* send_multiple test -- send many sysex messages */
-/**/
-void send_multiple_test()
-{
- int outp;
- int length;
- int num_msgs;
- PmStream *midi_out;
- unsigned char msg[1024];
- int i;
- PtTimestamp start_time;
- PtTimestamp stop_time;
-
- Pt_Start(1, 0, 0);
-
- printf("This is for performance testing. You should be sending to this\n");
- printf("program running the receive multiple test. Do NOT send to\n");
- printf("a synthesizer or you risk reprogramming it\n");
- outp = get_number("Type output device number: ");
- while ((latency = get_number(
- "Latency in milliseconds (0 to send data immediatedly,\n"
- " >0 to send timestamped messages): ")) < 0);
- Pm_OpenOutput(&midi_out, outp, NULL, 0, NULL, NULL, latency);
- while ((length = get_number("Message length (7 - 1024): ")) < 7 ||
- length > 1024) ;
- while ((num_msgs = get_number("Number of messages: ")) < 1);
- /* latency, length, and num_msgs should now all be valid */
- /* compose the message except for sequence number in first 5 bytes */
- msg[0] = (char) MIDI_SYSEX;
- for (i = 6; i < length - 1; i++) {
- msg[i] = i % 128; /* this is just filler */
- }
- msg[length - 1] = (char) MIDI_EOX;
-
- start_time = Pt_Time();
- /* send the messages */
- for (i = num_msgs; i > 0; i--) {
- /* insert sequence number into first 5 data bytes */
- /* sequence counts down to zero */
- int j;
- int count = i;
- /* 7 bits of message count i goes into each data byte */
- for (j = 1; j <= 5; j++) {
- msg[j] = count & 127;
- count >>= 7;
- }
- /* send the message */
- Pm_WriteSysEx(midi_out, 0, msg);
- }
- stop_time = Pt_Time();
- Pm_Close(midi_out);
- return;
-}
-
-#define MAX_MSG_LEN 1024
-static unsigned char receive_msg[MAX_MSG_LEN];
-static int receive_msg_index;
-static int receive_msg_length;
-static int receive_msg_count;
-static int receive_msg_error;
-static int receive_msg_messages;
-static PmStream *receive_msg_midi_in;
-static int receive_poll_running;
-
-/* receive_poll -- callback function to check for midi input */
-/**/
-void receive_poll(PtTimestamp timestamp, void *userData)
-{
- PmError count;
- PmEvent event;
- int shift;
- int data = 0;
- int i;
-
- if (!receive_poll_running) return; /* wait until midi device is opened */
- shift = 0;
- while (data != MIDI_EOX) {
- count = Pm_Read(receive_msg_midi_in, &event, 1);
- if (count == 0) return;
-
- /* compare 4 bytes of data until you reach an eox */
- for (shift = 0; shift < 32 && (data != MIDI_EOX); shift += 8) {
- receive_msg[receive_msg_index++] = data =
- (event.message >> shift) & 0xFF;
- if (receive_msg_index >= MAX_MSG_LEN) {
- printf("error: incoming sysex too long\n");
- goto error;
- }
- }
- }
- /* check the message */
- if (receive_msg_length == 0) {
- receive_msg_length = receive_msg_index;
- }
- if (receive_msg_length != receive_msg_index) {
- printf("error: incoming sysex wrong length\n");
- goto error;
- }
- if (receive_msg[0] != MIDI_SYSEX) {
- printf("error: incoming sysex missing status byte\n");
- goto error;
- }
- /* get and check the count */
- count = 0;
- for (i = 0; i < 5; i++) {
- count += receive_msg[i + 1] << (7 * i);
- }
- if (receive_msg_count == -1) {
- receive_msg_count = count;
- receive_msg_messages = count;
- }
- if (receive_msg_count != count) {
- printf("error: incoming sysex has wrong count\n");
- goto error;
- }
- for (i = 6; i < receive_msg_index - 1; i++) {
- if (receive_msg[i] != i % 128) {
- printf("error: incoming sysex has bad data\n");
- goto error;
- }
- }
- if (receive_msg[receive_msg_length - 1] != MIDI_EOX) goto error;
- receive_msg_index = 0; /* get ready for next message */
- receive_msg_count--;
- return;
- error:
- receive_msg_error = 1;
- return;
-}
-
-
-/* receive_multiple_test -- send/rcv from 2 to 1000 bytes of random midi data */
-/**/
-void receive_multiple_test()
-{
- PmError err;
- int inp;
-
- printf("This test expects to receive data sent by the send_multiple test\n");
- printf("The test will check that correct data is received.\n");
-
- /* Important: start PortTime first -- if it is not started first, it will
- be started by PortMidi, and then our attempt to open again will fail */
- receive_poll_running = false;
- if ((err = Pt_Start(1, receive_poll, 0))) {
- printf("PortTime error code: %d\n", err);
- goto cleanup;
- }
- inp = get_number("Type input device number: ");
- Pm_OpenInput(&receive_msg_midi_in, inp, NULL, 512, NULL, NULL);
- receive_msg_index = 0;
- receive_msg_length = 0;
- receive_msg_count = -1;
- receive_msg_error = 0;
- receive_poll_running = true;
- while ((!receive_msg_error) && (receive_msg_count != 0)) {
-#ifdef WIN32
- Sleep(1000);
-#else
- sleep(1); /* block and wait */
-#endif
- }
- if (receive_msg_error) {
- printf("Receive_multiple test encountered an error\n");
- } else {
- printf("Receive_multiple test successfully received %d sysex messages\n",
- receive_msg_messages);
- }
-cleanup:
- receive_poll_running = false;
- Pm_Close(receive_msg_midi_in);
- Pt_Stop();
- return;
-}
-
-
-#define is_real_time_msg(msg) ((0xF0 & Pm_MessageStatus(msg)) == 0xF8)
-
-
-void receive_sysex()
-{
- char line[80];
- FILE *f;
- PmStream *midi;
- int shift = 0;
- int data = 0;
- int bytes_on_line = 0;
- PmEvent msg;
-
- /* determine which output device to use */
- int i = get_number("Type input device number: ");
-
- /* open input device */
- Pm_OpenInput(&midi, i, NULL, 512, NULL, NULL);
- printf("Midi Input opened, type file for sysex data: ");
-
- /* open file */
- if (!fgets(line, STRING_MAX, stdin)) return; /* no more stdin? */
- /* remove the newline character */
- if (strlen(line) > 0) line[strlen(line) - 1] = 0;
- f = fopen(line, "w");
- if (!f) {
- printf("Could not open %s\n", line);
- Pm_Close(midi);
- return;
- }
-
- printf("Ready to receive a sysex message\n");
-
- /* read data and write to file */
- while (data != MIDI_EOX) {
- PmError count;
- count = Pm_Read(midi, &msg, 1);
- /* CAUTION: this causes busy waiting. It would be better to
- be in a polling loop to avoid being compute bound. PortMidi
- does not support a blocking read since this is so seldom
- useful.
- */
- if (count == 0) continue;
- /* ignore real-time messages */
- if (is_real_time_msg(Pm_MessageStatus(msg.message))) continue;
-
- /* write 4 bytes of data until you reach an eox */
- for (shift = 0; shift < 32 && (data != MIDI_EOX); shift += 8) {
- data = (msg.message >> shift) & 0xFF;
- /* if this is a status byte that's not MIDI_EOX, the sysex
- message is incomplete and there is no more sysex data */
- if (data & 0x80 && data != MIDI_EOX) break;
- fprintf(f, "%2x ", data);
- if (++bytes_on_line >= 16) {
- fprintf(f, "\n");
- bytes_on_line = 0;
- }
- }
- }
- fclose(f);
- Pm_Close(midi);
-}
-
-
-void send_sysex()
-{
- char line[80];
- FILE *f;
- PmStream *midi;
- int data;
- int shift = 0;
- PmEvent msg;
-
- /* determine which output device to use */
- int i = get_number("Type output device number: ");
- while ((latency = get_number(
- "Latency in milliseconds (0 to send data immediatedly,\n"
- " >0 to send timestamped messages): ")) < 0);
-
- msg.timestamp = 0; /* no need for timestamp */
-
- /* open output device */
- Pm_OpenOutput(&midi, i, NULL, 0, NULL, NULL, latency);
- printf("Midi Output opened, type file with sysex data: ");
-
- /* open file */
- if (!fgets(line, STRING_MAX, stdin)) return; /* no more stdin? */
- /* remove the newline character */
- if (strlen(line) > 0) line[strlen(line) - 1] = 0;
- f = fopen(line, "r");
- if (!f) {
- printf("Could not open %s\n", line);
- Pm_Close(midi);
- return;
- }
-
- /* read file and send data */
- msg.message = 0;
- while (1) {
- /* get next byte from file */
-
- if (fscanf(f, "%x", &data) == 1) {
- /* printf("read %x, ", data); */
- /* OR byte into message at proper offset */
- msg.message |= (data << shift);
- shift += 8;
- }
- /* send the message if it's full (shift == 32) or if we are at end */
- if (shift == 32 || data == MIDI_EOX) {
- /* this will send sysex data 4 bytes at a time -- it would
- be much more efficient to send multiple PmEvents at once
- but this method is simpler. See Pm_WriteSysEx for a more
- efficient code example.
- */
- Pm_Write(midi, &msg, 1);
- msg.message = 0;
- shift = 0;
- }
- if (data == MIDI_EOX) { /* end of message */
- fclose(f);
- Pm_Close(midi);
- return;
- }
- }
-}
-
-
-int main()
-{
- int i;
-
- /* list device information */
- for (i = 0; i < Pm_CountDevices(); i++) {
- const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
- printf("%d: %s, %s", i, info->interf, info->name);
- if (info->input) printf(" (input)");
- if (info->output) printf(" (output)");
- printf("\n");
- }
- while (1) {
- char cmd;
- printf("Type r to receive sysex, s to send,"
- " l for loopback test, m to send multiple,"
- " n to receive multiple, q to quit: ");
- cmd = getchar();
- while (getchar() != '\n') ;
- switch (cmd) {
- case 'r':
- receive_sysex();
- break;
- case 's':
- send_sysex();
- break;
- case 'l':
- loopback_test();
- break;
- case 'm':
- send_multiple_test();
- break;
- case 'n':
- receive_multiple_test();
- break;
- case 'q':
- exit(0);
- default:
- break;
- }
- }
- return 0;
-}
diff --git a/portmidi/pm_test/testio.c b/portmidi/pm_test/testio.c deleted file mode 100755 index 2711286..0000000 --- a/portmidi/pm_test/testio.c +++ /dev/null @@ -1,594 +0,0 @@ -#include "portmidi.h" -#include "porttime.h" -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "assert.h" - -#define INPUT_BUFFER_SIZE 100 -#define OUTPUT_BUFFER_SIZE 0 -#define TIME_PROC ((int32_t (*)(void *)) Pt_Time) -#define TIME_INFO NULL -#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */ - -#define WAIT_FOR_ENTER while (getchar() != '\n') ; - -int32_t latency = 0; -int verbose = FALSE; -PmSysDepInfo *sysdepinfo = NULL; - -/* crash the program to test whether midi ports are closed */ -/**/ -void doSomethingReallyStupid() { - int * tmp = NULL; - *tmp = 5; -} - - -/* exit the program without any explicit cleanup */ -/**/ -void doSomethingStupid() { - assert(0); -} - - -/* read a number from console */ -/**/ -int get_number(const char *prompt) -{ - int n = 0, i; - fputs(prompt, stdout); - while (n != 1) { - n = scanf("%d", &i); - WAIT_FOR_ENTER - } - return i; -} - - -static void set_sysdepinfo(char m_or_p, const char *name) -{ - if (!sysdepinfo) { - // allocate some space we will alias with open-ended PmDriverInfo: - // there is space for 4 parameters: - static char dimem[sizeof(PmSysDepInfo) + sizeof(void *) * 8]; - sysdepinfo = (PmSysDepInfo *) dimem; - // build the driver info structure: - sysdepinfo->structVersion = PM_SYSDEPINFO_VERS; - sysdepinfo->length = 0; - } - if (sysdepinfo->length > 1) { - printf("Error: sysdepinfo was allocated to hold 2 parameters\n"); - exit(1); - } - int i = sysdepinfo->length++; - enum PmSysDepPropertyKey k = pmKeyNone; - if (m_or_p == 'm') k = pmKeyCoreMidiManufacturer; - else if (m_or_p == 'p') k = pmKeyAlsaPortName; - else if (m_or_p == 'c') k = pmKeyAlsaClientName; - sysdepinfo->properties[i].key = k; - sysdepinfo->properties[i].value = name; -} - - -/* - * the somethingStupid parameter can be set to simulate a program crash. - * We want PortMidi to close Midi ports automatically in the event of a - * crash because Windows does not (and this may cause an OS crash) - */ -void main_test_input(unsigned int somethingStupid) { - PmStream * midi; - PmError status, length; - PmEvent buffer[1]; - int num = 10; - int i = get_number("Type input number: "); - /* It is recommended to start timer before Midi; otherwise, PortMidi may - start the timer with its (default) parameters - */ - TIME_START; - - /* open input device */ - Pm_OpenInput(&midi, - i, - sysdepinfo, - INPUT_BUFFER_SIZE, - TIME_PROC, - TIME_INFO); - - printf("Midi Input opened. Reading %d Midi messages...\n", num); - Pm_SetFilter(midi, PM_FILT_ACTIVE | PM_FILT_CLOCK | PM_FILT_SYSEX); - /* empty the buffer after setting filter, just in case anything - got through */ - while (Pm_Poll(midi)) { - Pm_Read(midi, buffer, 1); - } - /* now start paying attention to messages */ - i = 0; /* count messages as they arrive */ - while (i < num) { - status = Pm_Poll(midi); - if (status == TRUE) { - length = Pm_Read(midi, buffer, 1); - if (length > 0) { - printf("Got message %d @ time %ld: timestamp %ld, " - "%2lx %2lx %2lx\n", i, (long) Pt_Time(), - (long) buffer[0].timestamp, - (long) Pm_MessageStatus(buffer[0].message), - (long) Pm_MessageData1(buffer[0].message), - (long) Pm_MessageData2(buffer[0].message)); - i++; - } else { - assert(0); - } - } - /* simulate crash if somethingStupid is 1 or 2 */ - if ((i > (num/2)) && (somethingStupid == 1)) { - doSomethingStupid(); - } else if ((i > (num/2)) && (somethingStupid == 2)) { - doSomethingReallyStupid(); - } - } - - /* close device (this not explicitly needed in most implementations) */ - printf("ready to close..."); - - Pm_Close(midi); - printf("done closing..."); -} - - - -void main_test_output(int isochronous_test) -{ - PmStream * midi; - int32_t off_time; - int chord[] = { 60, 67, 76, 83, 90 }; - #define chord_size 5 - PmEvent buffer[chord_size]; - PmTimestamp timestamp; - - /* determine which output device to use */ - int i = get_number("Type output number: "); - - /* It is recommended to start timer before PortMidi */ - TIME_START; - - /* open output device -- since PortMidi avoids opening a timer - when latency is zero, we will pass in a NULL timer pointer - for that case. If PortMidi tries to access the time_proc, - we will crash, so this test will tell us something. */ - Pm_OpenOutput(&midi, - i, - sysdepinfo, - OUTPUT_BUFFER_SIZE, - (latency == 0 ? NULL : TIME_PROC), - (latency == 0 ? NULL : TIME_INFO), - latency); - printf("Midi Output opened with %ld ms latency.\n", (long) latency); - - /* output note on/off w/latency offset; hold until user prompts */ - printf("ready to send program 1 change... (type ENTER):"); - WAIT_FOR_ENTER - /* if we were writing midi for immediate output, we could always use - timestamps of zero, but since we may be writing with latency, we - will explicitly set the timestamp to "now" by getting the time. - The source of timestamps should always correspond to the TIME_PROC - and TIME_INFO parameters used in Pm_OpenOutput(). */ - buffer[0].timestamp = Pt_Time(); - /* Send a program change to increase the chances we will hear notes */ - /* Program 0 is usually a piano, but you can change it here: */ -#define PROGRAM 0 - buffer[0].message = Pm_Message(0xC0, PROGRAM, 0); - Pm_Write(midi, buffer, 1); - - if (isochronous_test) { // play 4 notes per sec for 20s - int count; - PmTimestamp start; - if (latency < 100) { - printf("Warning: latency < 100, but this test sends messages" - " at times that are jittered by up to 100ms, so you" - " may hear uneven timing\n"); - } - printf("Starting in 1s..."); fflush(stdout); - Pt_Sleep(1000); - start = Pt_Time(); - for (count = 0; count < 80; count++) { - PmTimestamp next_time; - buffer[0].timestamp = start + count * 250; - buffer[0].message = Pm_Message(0x90, 69, 100); - buffer[1].timestamp = start + count * 250 + 200; - buffer[1].message = Pm_Message(0x90, 69, 0); - Pm_Write(midi, buffer, 2); - next_time = start + (count + 1) * 250; - // sleep for a random time up to 100ms to add jitter to - // the times at which we send messages. PortMidi timing - // should remove the jitter if latency > 100 - while (Pt_Time() < next_time) { - Pt_Sleep(rand() % 100); - } - } - printf("Done sending 80 notes at 4 notes per second.\n"); - } else { - PmError err = 0; - printf("ready to note-on... (type ENTER):"); - WAIT_FOR_ENTER - buffer[0].timestamp = Pt_Time(); - buffer[0].message = Pm_Message(0x90, 60, 100); - if ((err = Pm_Write(midi, buffer, 1))) { - printf("Pm_Write returns error: %d (%s)\n", - err, Pm_GetErrorText(err)); - if (err == pmHostError) { - char errmsg[128]; - Pm_GetHostErrorText(errmsg, 127); - printf(" Host error: %s\n", errmsg); - } - } - printf("ready to note-off... (type ENTER):"); - WAIT_FOR_ENTER - buffer[0].timestamp = Pt_Time(); - buffer[0].message = Pm_Message(0x90, 60, 0); - if ((err = Pm_Write(midi, buffer, 1))) { - printf("Pm_Write returns error: %d (%s)\n", - err, Pm_GetErrorText(err)); - if (err == pmHostError) { - char errmsg[128]; - Pm_GetHostErrorText(errmsg, 127); - printf(" Host error: %s\n", errmsg); - } - } - - /* output short note on/off w/latency offset; hold until user prompts */ - printf("ready to note-on (short form)... (type ENTER):"); - WAIT_FOR_ENTER - Pm_WriteShort(midi, Pt_Time(), - Pm_Message(0x90, 60, 100)); - printf("ready to note-off (short form)... (type ENTER):"); - WAIT_FOR_ENTER - Pm_WriteShort(midi, Pt_Time(), - Pm_Message(0x90, 60, 0)); - - /* output several note on/offs to test timing. - Should be 1s between notes */ - if (latency == 0) { - printf("chord should not arpeggiate, latency == 0\n"); - } else { - printf("chord should arpeggiate (latency = %ld > 0\n", - (long) latency); - } - printf("ready to chord-on/chord-off... (type ENTER):"); - WAIT_FOR_ENTER - timestamp = Pt_Time(); - printf("starting timestamp %ld\n", (long) timestamp); - for (i = 0; i < chord_size; i++) { - buffer[i].timestamp = timestamp + 1000 * i; - buffer[i].message = Pm_Message(0x90, chord[i], 100); - } - Pm_Write(midi, buffer, chord_size); - - off_time = timestamp + 1000 + chord_size * 1000; - while (Pt_Time() < off_time) - /* There was a report that Pm_Write with zero length sent last - * message again, so call Pm_Write here to see if note repeats - */ - Pm_Write(midi, buffer, 0); - Pt_Sleep(20); /* wait */ - - for (i = 0; i < chord_size; i++) { - buffer[i].timestamp = timestamp + 1000 * i; - buffer[i].message = Pm_Message(0x90, chord[i], 0); - } - Pm_Write(midi, buffer, chord_size); - } - - /* close device (this not explicitly needed in most implementations) */ - printf("ready to close and terminate... (type ENTER):"); - WAIT_FOR_ENTER - - Pm_Close(midi); - Pm_Terminate(); - printf("done closing and terminating...\n"); -} - - -void main_test_both() -{ - int i = 0; - int in, out; - PmStream * midi, * midiOut; - PmEvent buffer[1]; - PmError status, length; - int num = 11; - - in = get_number("Type input number: "); - out = get_number("Type output number: "); - - /* In is recommended to start timer before PortMidi */ - TIME_START; - - Pm_OpenOutput(&midiOut, - out, - sysdepinfo, - OUTPUT_BUFFER_SIZE, - TIME_PROC, - TIME_INFO, - latency); - printf("Midi Output opened with %ld ms latency.\n", (long) latency); - /* open input device */ - Pm_OpenInput(&midi, - in, - sysdepinfo, - INPUT_BUFFER_SIZE, - TIME_PROC, - TIME_INFO); - printf("Midi Input opened. Reading %d Midi messages...\n", num); - Pm_SetFilter(midi, PM_FILT_ACTIVE | PM_FILT_CLOCK); - /* empty the buffer after setting filter, just in case anything - got through */ - while (Pm_Poll(midi)) { - Pm_Read(midi, buffer, 1); - } - i = 0; - while (i < num) { - status = Pm_Poll(midi); - if (status == TRUE) { - length = Pm_Read(midi,buffer,1); - if (length > 0) { - Pm_Write(midiOut, buffer, 1); - printf("Got message %d @ time %ld: timestamp %ld, " - "%2lx %2lx %2lx\n", i, (long) Pt_Time(), - (long) buffer[0].timestamp, - (long) Pm_MessageStatus(buffer[0].message), - (long) Pm_MessageData1(buffer[0].message), - (long) Pm_MessageData2(buffer[0].message)); - i++; - } else { - assert(0); - } - } - } - /* allow time for last message to go out */ - Pt_Sleep(100 + latency); - - /* close midi devices */ - Pm_Close(midi); - Pm_Close(midiOut); - Pm_Terminate(); -} - - -/* main_test_stream exercises windows winmm API's stream mode */ -/* The winmm stream mode is used for latency>0, and sends - timestamped messages. The timestamps are relative (delta) - times, whereas PortMidi times are absolute. Since peculiar - things happen when messages are not always sent in advance, - this function allows us to exercise the system and test it. - */ -void main_test_stream() { - PmStream * midi; - PmEvent buffer[16]; - - /* determine which output device to use */ - int i = get_number("Type output number: "); - - latency = 500; /* ignore LATENCY for this test and - fix the latency at 500ms */ - - /* It is recommended to start timer before PortMidi */ - TIME_START; - - /* open output device */ - Pm_OpenOutput(&midi, - i, - sysdepinfo, - OUTPUT_BUFFER_SIZE, - TIME_PROC, - TIME_INFO, - latency); - printf("Midi Output opened with %ld ms latency.\n", (long) latency); - - /* output note on/off w/latency offset; hold until user prompts */ - printf("ready to send output... (type ENTER):"); - WAIT_FOR_ENTER - - /* if we were writing midi for immediate output, we could always use - timestamps of zero, but since we may be writing with latency, we - will explicitly set the timestamp to "now" by getting the time. - The source of timestamps should always correspond to the TIME_PROC - and TIME_INFO parameters used in Pm_OpenOutput(). */ - buffer[0].timestamp = Pt_Time(); - buffer[0].message = Pm_Message(0xC0, 0, 0); - buffer[1].timestamp = buffer[0].timestamp; - buffer[1].message = Pm_Message(0x90, 60, 100); - buffer[2].timestamp = buffer[0].timestamp + 1000; - buffer[2].message = Pm_Message(0x90, 62, 100); - buffer[3].timestamp = buffer[0].timestamp + 2000; - buffer[3].message = Pm_Message(0x90, 64, 100); - buffer[4].timestamp = buffer[0].timestamp + 3000; - buffer[4].message = Pm_Message(0x90, 66, 100); - buffer[5].timestamp = buffer[0].timestamp + 4000; - buffer[5].message = Pm_Message(0x90, 60, 0); - buffer[6].timestamp = buffer[0].timestamp + 4000; - buffer[6].message = Pm_Message(0x90, 62, 0); - buffer[7].timestamp = buffer[0].timestamp + 4000; - buffer[7].message = Pm_Message(0x90, 64, 0); - buffer[8].timestamp = buffer[0].timestamp + 4000; - buffer[8].message = Pm_Message(0x90, 66, 0); - - Pm_Write(midi, buffer, 9); -#ifdef SEND8 - /* Now, we're ready for the real test. - Play 4 notes at now, now+500, now+1000, and now+1500 - Then wait until now+2000. - Play 4 more notes as before. - We should hear 8 evenly spaced notes. */ - now = Pt_Time(); - for (i = 0; i < 4; i++) { - buffer[i * 2].timestamp = now + (i * 500); - buffer[i * 2].message = Pm_Message(0x90, 60, 100); - buffer[i * 2 + 1].timestamp = now + 250 + (i * 500); - buffer[i * 2 + 1].message = Pm_Message(0x90, 60, 0); - } - Pm_Write(midi, buffer, 8); - - while (Pt_Time() < now + 2500) - Pt_Sleep(10); - /* now we are 500 ms behind schedule, but since the latency - is 500, the delay should not be audible */ - now += 2000; - for (i = 0; i < 4; i++) { - buffer[i * 2].timestamp = now + (i * 500); - buffer[i * 2].message = Pm_Message(0x90, 60, 100); - buffer[i * 2 + 1].timestamp = now + 250 + (i * 500); - buffer[i * 2 + 1].message = Pm_Message(0x90, 60, 0); - } - Pm_Write(midi, buffer, 8); -#endif - /* close device (this not explicitly needed in most implementations) */ - printf("ready to close and terminate... (type ENTER):"); - WAIT_FOR_ENTER - - Pm_Close(midi); - Pm_Terminate(); - printf("done closing and terminating...\n"); -} - - -void show_usage() -{ - printf("Usage: test [-h] [-l latency-in-ms] [-c clientname] " - "[-p portname] [-v]\n" - " -h for this help message (only)\n" - " -l for latency\n" - " -c name designates a client name (linux only),\n" - " -p name designates a port name (linux only),\n" - " -v for verbose (enables more output)\n"); -} - -int main(int argc, char *argv[]) -{ - int default_in; - int default_out; - int i = 0, n = 0; - int test_input = 0, test_output = 0, test_both = 0, somethingStupid = 0; - int isochronous_test = 0; - int stream_test = 0; - int latency_valid = FALSE; - - show_usage(); - if (sizeof(void *) == 8) - printf("Apparently this is a 64-bit machine.\n"); - else if (sizeof(void *) == 4) - printf ("Apparently this is a 32-bit machine.\n"); - - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-h") == 0) { - exit(0); - } else if (strcmp(argv[i], "-p") == 0 && (i + 1 < argc)) { - i = i + 1; - const char *port_name = argv[i]; - set_sysdepinfo('p', port_name); - printf("Port name will be %s\n", port_name); - } else if (strcmp(argv[i], "-c") == 0 && (i + 1 < argc)) { - i = i + 1; - set_sysdepinfo('c', argv[i]); - printf("Client name will be %s\n", argv[i]); - } else if (strcmp(argv[i], "-l") == 0 && (i + 1 < argc)) { - i = i + 1; - latency = atoi(argv[i]); - printf("Latency will be %ld\n", (long) latency); - latency_valid = TRUE; - } else if (strcmp(argv[i], "-v") == 0) { - printf("Verbose is now TRUE\n"); - verbose = TRUE; /* not currently used for anything */ - } else { - show_usage(); - exit(0); - } - } - - while (!latency_valid) { - int lat; // declared int to match "%d" - printf("Latency in ms: "); - if (scanf("%d", &lat) == 1) { - latency = (int32_t) lat; // coerce from "%d" to known size - latency_valid = TRUE; - } - } - - /* determine what type of test to run */ - printf("begin portMidi test...\n"); - printf("enter your choice...\n 1: test input\n" - " 2: test input (fail w/assert)\n" - " 3: test input (fail w/NULL assign)\n" - " 4: test output\n 5: test both\n" - " 6: stream test (for WinMM)\n" - " 7. isochronous out\n"); - while (n != 1) { - n = scanf("%d", &i); - WAIT_FOR_ENTER - switch(i) { - case 1: - test_input = 1; - break; - case 2: - test_input = 1; - somethingStupid = 1; - break; - case 3: - test_input = 1; - somethingStupid = 2; - break; - case 4: - test_output = 1; - break; - case 5: - test_both = 1; - break; - case 6: - stream_test = 1; - break; - case 7: - test_output = 1; - isochronous_test = 1; - break; - default: - printf("got %d (invalid input)\n", n); - break; - } - } - - /* list device information */ - default_in = Pm_GetDefaultInputDeviceID(); - default_out = Pm_GetDefaultOutputDeviceID(); - for (i = 0; i < Pm_CountDevices(); i++) { - char *deflt; - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - if (((test_input | test_both) & info->input) | - ((test_output | test_both | stream_test) & info->output)) { - printf("%d: %s, %s", i, info->interf, info->name); - if (info->input) { - deflt = (i == default_in ? "default " : ""); - printf(" (%sinput)", deflt); - } - if (info->output) { - deflt = (i == default_out ? "default " : ""); - printf(" (%soutput)", deflt); - } - printf("\n"); - } - } - - /* run test */ - if (stream_test) { - main_test_stream(); - } else if (test_input) { - main_test_input(somethingStupid); - } else if (test_output) { - main_test_output(isochronous_test); - } else if (test_both) { - main_test_both(); - } - - printf("finished portMidi test...type ENTER to quit..."); - WAIT_FOR_ENTER - return 0; -} diff --git a/portmidi/pm_test/txdata.syx b/portmidi/pm_test/txdata.syx deleted file mode 100755 index 1e06e5a..0000000 --- a/portmidi/pm_test/txdata.syx +++ /dev/null @@ -1,257 +0,0 @@ -20 0 1d 4 c 6 0 34 1 4d 4 d 1f 7 3 6
- c 5e 4 4d d b 18 5 3 6 0 3d 1 4a 16 18
-1f 8 3 6 d 0 1 63 4 13 3a 23 0 0 0 2
- c 2 4 0 63 32 0 0 0 32 0 47 72 61 6e 64
-50 69 61 6e 6f 63 63 63 32 32 32 0 0 0 0 0
-10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f 9 9 f c 27 2 35 37 10 1f 4 3 4
- d 19 4 56 5 16 1f f 8 d c 0 43 60 4 e
-1f c 3 7 e 0 43 63 5 10 3c 14 8 2 1b 56
- 5 2 4 0 63 32 0 0 0 32 0 4c 6f 54 69 6e
-65 38 31 5a 20 63 63 63 32 32 32 0 7f 0 1 0
-18 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f e f e 9 0 3 43 2d e 1f f 5 7
- f 16 43 5a 0 0 1f 12 6 8 d 0 3 63 4 0
-1f 12 6 8 f 0 2 63 4 6 34 14 0 1 2 4e
-18 2 4 0 63 32 0 32 0 32 0 44 79 6e 6f 6d
-69 74 65 45 50 63 63 63 32 32 32 0 70 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f b 1 b 8 18 40 5f a e 1f 1f 0 a
- f 0 40 5f 4 0 1f 1f 0 a f 0 40 63 5 6
-1f 1f 0 a f 0 40 5f 0 8 1f 20 0 3 0 5a
-18 4 4 0 63 32 32 0 0 32 0 50 65 72 63 4f
-72 67 61 6e 20 63 63 63 32 32 32 0 0 0 0 0
- 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f b 7 f 9 0 4 49 13 13 1f 8 7 5
- e 0 2 58 0 c 1f 6 4 6 f 23 3 46 10 a
-1f 7 8 c d 0 2 63 8 b 2 1c 0 0 0 52
-18 4 4 0 63 32 0 32 0 32 0 54 68 69 6e 20
-43 6c 61 76 20 63 63 63 32 32 32 0 70 0 20 0
-10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f c 0 6 1 a 4 50 20 e 1f c 0 6
- 1 a 4 50 1f 8 1f b 9 5 e 0 2 63 5 e
-1f b 9 5 e 0 3 63 4 8 4 1a 0 0 0 52
-1d 2 4 0 63 32 0 32 0 32 0 42 72 69 74 65
-43 65 6c 73 74 63 63 63 32 32 32 0 20 0 26 0
- 1 0 8 4 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 f 1f 4 8 f 0 3a 51 4 b e 1f 0 8
- f 0 22 4b 4 3 f 1a b 8 d 0 3b 36 9 3
-12 1f 0 8 f 0 22 5d 4 b 3a 1e 19 5 0 52
-18 4 4 0 63 32 0 0 0 32 0 54 72 75 6d 70
-65 74 38 31 5a 63 63 63 32 32 32 0 0 0 50 0
-51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 c 5 0 8 0 0 2 4a 4 b f 1f 0 8
- f 0 2 3f 4 3 1f f 0 8 0 23 3 44 b 3
-10 1f 0 9 f 0 2 5e 4 c 3a 1f 19 7 0 52
-18 4 4 0 63 32 0 0 0 32 0 46 6c 75 67 65
-6c 68 6f 72 6e 63 63 63 32 32 32 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 10 1f 0 8 f 0 42 4a 0 3 11 1f 0 8
- f a 43 51 0 3 11 9 0 8 d 0 42 2b 16 6
-10 1f 0 9 f 0 42 63 4 b 3a 1e 9 9 0 5a
-24 4 4 0 63 32 31 0 0 32 0 52 61 73 70 41
-6c 74 6f 20 20 63 63 63 32 32 32 0 10 0 20 0
-54 0 20 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 10 9 2 6 d 0 41 3e 4 15 c b 2 3
- e 0 41 4f 4 12 c e 2 8 d 0 42 4b a 1c
- d b 1 9 e 0 3 63 a 14 0 23 f 2 1b 5e
-18 4 5 0 63 28 50 32 0 32 0 48 61 72 6d 6f
-6e 69 63 61 20 63 63 63 32 32 32 0 50 10 50 0
-50 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1c 2 0 4 e 63 0 4e 4 3 d 5 0 6
- e 63 1 56 a 8 12 7 0 6 9 63 2 47 1b e
- a a 0 5 f 0 1 63 4 b 32 1a 8 d 0 52
- c 4 4 0 63 32 0 0 0 32 0 44 6f 75 62 6c
-65 42 61 73 73 63 63 63 32 32 32 0 10 0 0 0
- 3 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 b 4 0 4 f 14 2 49 9 6 a 7 0 4
- f 14 2 51 a 0 8 1f 0 5 f 0 1 63 9 6
- a 1f 0 5 f 0 1 63 a 0 3c 1f 6 9 0 52
- 5 4 4 0 63 32 0 0 0 32 0 48 69 53 74 72
-69 6e 67 20 31 63 63 63 32 32 32 0 2 0 30 0
-32 0 10 5 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 10 13 f 4 a 0 3 3b 14 14 1f e 8 7
- 9 0 2 42 5 e 18 13 d 9 c 0 2 3c 13 8
-1f 11 7 4 f 0 42 63 4 10 3a 1b 0 0 0 52
-1d 4 4 0 63 32 0 0 0 32 0 48 61 72 70 20
-20 20 20 20 20 63 63 63 32 32 32 8 0 0 21 0
- 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f 6 6 4 f 0 40 48 5 0 c 8 7 5
- f 5 0 52 4 0 f 7 3 7 e 8 3 63 4 6
- f 8 4 5 f 0 3 63 4 6 7c 1f 0 6 0 4a
-11 2 4 0 63 32 0 0 0 32 0 46 61 6e 66 61
-72 54 70 74 73 63 63 63 32 32 32 6 1 0 38 0
- 8 0 48 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 d b 0 1 c 0 2 2c 3d 3 d 7 0 1
- c 0 2 1f 3c 3 d 1f 0 5 f 0 2 63 5 6
- d 1f 0 5 f 0 2 63 4 0 3c 63 0 2f 0 53
-11 4 4 0 63 32 0 0 0 32 0 42 72 65 61 74
-68 4f 72 67 6e 63 63 63 32 32 32 4 30 5 50 0
-11 0 18 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f 9 0 6 0 27 2 51 19 b 1c 6 0 8
- 0 37 2 47 a 3 1f a 0 9 0 3d 2 4d a e
-1f 12 8 8 f 0 3 61 4 b 28 1f 0 3 0 52
- c 3 4 0 63 32 1 32 0 32 0 4e 79 6c 6f 6e
-47 75 69 74 20 63 63 63 32 32 32 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f e e f f 0 3 48 2d 6 1f f 4 f
- f 25 3 5b 0 0 1f 12 6 c e 1c 3 55 0 10
-1f 13 7 8 e 6 4 62 4 e 3b 14 0 0 0 42
-18 2 4 0 63 32 0 32 0 32 0 47 75 69 74 61
-72 20 23 31 20 63 63 63 32 32 32 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f 19 8 a 3 0 3 63 10 18 1f c 5 b
- 5 0 3 52 0 b 1f 19 6 b 5 0 3 63 a 16
-1f f 11 9 7 0 4 63 4 3 3a 14 0 0 0 42
-18 2 4 0 63 32 0 32 0 32 0 46 75 6e 6b 79
-20 50 69 63 6b 63 63 63 32 32 32 0 30 0 0 0
- 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f 1 0 8 4 0 3 3d a 1e 1f 1 0 8
- 0 0 0 43 0 10 1f 9 6 8 c 1b 7 46 1c 1e
-1f 9 0 9 9 0 1 63 4 3 3a 1c 0 0 0 52
- c 4 5 0 63 4b 0 0 0 32 0 45 6c 65 63 42
-61 73 73 20 31 63 63 63 32 32 32 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f f f e 9 0 3 46 1d 16 1f f 5 e
- e d 3 63 0 b 1f 13 6 5 d 1c 3 63 0 0
-1f 13 6 8 f 0 4 63 4 6 3b 1f 0 0 0 42
- c 4 4 0 63 32 0 32 0 32 0 53 79 6e 46 75
-6e 6b 42 61 73 63 63 63 32 32 32 d 6c 0 0 0
-70 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f 10 7 8 3 0 3 4f 4 3 1f 9 0 8
- 0 0 1 4a 0 b 1f 11 0 8 0 0 1 47 4 8
-1f 9 0 8 0 0 0 63 0 b 39 19 0 7 0 52
- c 2 4 0 63 32 0 32 0 32 0 4c 61 74 65 6c
-79 42 61 73 73 63 63 63 32 32 32 2 0 0 0 0
-40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 13 12 0 9 d 22 0 51 0 b 1f 14 0 5
- 8 24 40 5c 0 3 1f 11 0 6 c 2c 0 53 9 0
-10 1f 0 b f 0 0 5c a e 3a 22 11 e 1e 5e
-18 7 4 0 63 32 0 32 0 32 0 53 79 6e 63 20
-4c 65 61 64 20 63 63 63 32 32 32 0 70 0 40 0
- 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 13 1e 0 9 e 0 0 63 3f b 1f 14 0 5
- e 24 1 51 4 3 1f 14 0 f 1 0 41 4d 8 3
- f 1f 0 b f 0 2 63 4 b 3b 20 11 12 33 56
-18 4 4 0 63 37 e 0 0 32 0 4a 61 7a 7a 20
-46 6c 75 74 65 63 63 63 32 32 32 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 15 13 d 3 d 1e 2 50 18 e 15 14 9 4
- c 1e 2 56 11 8 1b 1f f 7 f 0 1 63 4 6
-1a 1f e 6 f 0 2 63 4 0 7c b 0 8 0 62
-18 4 4 0 63 32 0 0 0 32 0 4a 61 76 61 20
-4a 69 76 65 20 63 63 63 32 32 32 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f 0 0 4 f 0 40 63 3c 0 b 8 7 7
- f 5 0 63 4 6 f 5 3 7 f 8 0 3b 5 6
- e 8 4 5 f 0 3 63 3 0 7e 1d 6 f 0 4a
-11 0 4 0 63 32 0 0 0 32 0 42 61 61 64 42
-72 65 61 74 68 63 63 63 32 32 32 6 30 0 38 0
- 1 0 46 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f 0 0 4 f 0 40 47 2f 0 e 8 7 7
- f 5 0 4c 0 6 13 1c d c 6 8 0 63 5 6
-14 11 d b 0 0 3 63 4 0 7a 10 0 51 0 68
-17 0 4 0 63 32 0 0 0 32 0 56 6f 63 61 6c
-4e 75 74 73 20 63 63 63 32 32 32 6 30 0 30 0
- 1 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f 1f 0 5 f 0 0 41 32 3 1f 14 10 5
- 5 1 2 63 7 3 1f b 12 8 f 0 1 63 c 3
-1f 1f f 8 f 0 1 63 4 3 39 23 0 0 0 62
-18 7 4 0 63 32 0 0 0 32 0 57 61 74 65 72
-47 6c 61 73 73 63 63 63 32 32 32 0 0 0 0 0
- 0 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 16 2 0 4 6 9 1 4f 8 0 19 e 1 4
- 0 20 1 43 19 0 1f 12 10 6 7 0 0 54 3d 3
-16 d 6 6 2 1e 3 61 8 e 3a 20 1 14 0 42
- c 2 4 2 63 63 63 0 0 32 0 46 75 7a 7a 79
-20 4b 6f 74 6f 63 63 63 32 32 32 0 0 0 0 b
-50 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1c 8 0 3 e 0 1 55 12 3 1c 7 0 1
- e 2e 1 58 27 b e 4 0 2 a 0 2 63 4 a
- d 9 0 2 c 1 2 63 10 b 4 54 0 47 0 53
-18 7 4 0 63 32 0 0 0 32 0 42 72 74 68 62
-65 6c 6c 73 20 63 63 63 32 32 32 0 4 0 40 0
-40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1a 4 1 1 b 16 0 47 5 3 15 e 0 1
- d 0 0 4c 5 16 1c 6 4 2 7 0 0 63 4 16
-18 18 3 1 e 0 0 5e 4 10 24 7 0 4 0 62
-24 4 4 0 63 32 0 0 0 32 0 54 75 62 65 20
-42 65 6c 6c 73 63 63 63 32 32 32 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f 1f 13 3 0 0 0 5f 3d 6 1f 12 13 2
- 0 0 1 52 5 2 1f 14 13 3 0 0 1 56 28 5
-1e b 13 f 9 0 0 63 6 3 3b 63 0 63 0 73
-23 7 4 0 63 32 0 0 0 32 0 4e 6f 69 73 65
-20 53 68 6f 74 63 63 63 32 32 32 8 0 0 0 8
- 0 0 0 6 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 1f 16 0 3 7 0 1 50 0 3 1f 18 3 3
- 3 22 0 63 0 14 1d 7 6 3 6 0 1 3c 8 3
-1f 5 7 3 0 0 1 63 4 1b 39 23 0 8 0 42
-18 4 4 0 63 32 0 0 0 32 0 48 61 6e 64 20
-44 72 75 6d 20 63 63 63 32 32 32 0 1 0 3 0
- 1 0 1 3 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- 0 0 7d f7
\ No newline at end of file diff --git a/portmidi/pm_test/virttest.c b/portmidi/pm_test/virttest.c deleted file mode 100644 index 1aeb09b..0000000 --- a/portmidi/pm_test/virttest.c +++ /dev/null @@ -1,339 +0,0 @@ -/* virttest.c -- test for creating/deleting virtual ports */ -/* - * Roger B. Dannenberg - * Oct 2021 - -This test is performed by running 2 instances of the program. The -first instance makes input and output ports named portmidi and waits -for a message. The second tries to do the same, but will fail because -portmidi already exists. It then opens portmidi (both input and -output). In greater detail: - -FIRST INSTANCE SECOND INSTANCE --------------- --------------- - -initialize PortMidi initialize PortMidi -create portmidi in -create portmidi out -wait for input - create portmidi in -> fails - open portmidi in/out - send to portmidi -recv from portmidi -send to portmidi -wait 1s recv from portmidi - close portmidi in and out - terminate PortMidi -list all devices: - - check for correct number - - check for good description of portmidi in port (open) - - check for good description of portmidi out port (open) -close portmidi in -list all devices: - - check for correct number - - check for good description of portmidi in port (closed) - - check for good description of portmidi out port (open) -close portmidi out -list all devices: - - check for correct number - - check for good description of portmidi in port (closed) - - check for good description of portmidi out port (closed) -delete portmidi in - - check for correct number - - check for NULL description of portmidi in port - - check for good description of portmidi out port (closed) -delete portmidi out - - check for correct number - - check for NULL description of portmidi in port - - check for NULL description of portmidi out port -terminate portmidi -REPEAT 3 TIMES wait 2 seconds to give head start to other instance - REPEAT 3 TIMES - */ - -#include "portmidi.h" -#include "porttime.h" -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "assert.h" - -#define OUTPUT_BUFFER_SIZE 0 -#define INPUT_BUFFER_SIZE 10 -#define DEVICE_INFO NULL -#define DRIVER_INFO NULL -#define TIME_PROC ((PmTimeProcPtr) Pt_Time) -#define TIME_INFO NULL -#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */ - - -static void prompt_and_exit(void) -{ - printf("type ENTER..."); - while (getchar() != '\n') ; - /* this will clean up open ports: */ - exit(-1); -} - - -static PmError printerror(PmError err, const char *msg) -{ - if (err == pmHostError) { - /* it seems pointless to allocate memory and copy the string, - * so I will do the work of Pm_GetHostErrorText directly - */ - char errmsg[80]; - Pm_GetHostErrorText(errmsg, 80); - printf("%s\n %s\n", msg, errmsg); - } else if (err < 0) { - printf("%s\n %s\n", msg, Pm_GetErrorText(err)); - } - return err; -} - - -static PmError checkerror(PmError err) -{ - if (err < 0) { - printerror(err, "PortMidi call failed..."); - prompt_and_exit(); - } - return err; -} - - -void wait_until(PmTimestamp when) -{ - PtTimestamp now = Pt_Time(); - if (when > now) { - Pt_Sleep(when - now); - } -} - - -void show_usage() -{ - printf("Usage: virttest\n" - " run two instances to test virtual port create/delete\n"); -} - - -void check_info(int id, char stat, int input, int virtual) -{ - const PmDeviceInfo *info = Pm_GetDeviceInfo(id); - if (stat == 'd') { - if (info) { - printf("Expected device %d to be deleted.\n", id); - prompt_and_exit(); - } - return; - } - if (!info) { - printf("Expected device %d to not be deleted.\n", id); - prompt_and_exit(); - } - if (strcmp("portmidi", info->name) != 0) { - printf("Device %d name is %s, not \"portmidi\".\n", id, info->name); - prompt_and_exit(); - } - if (info->input != input || (!info->output) != input) { - printf("Device %d input/output fields are wrong.\n", id); - prompt_and_exit(); - } - if ((!info->opened && stat == 'o') || (info->opened && stat == 'c')) { - printf("Device %d opened==%d, status should be %c.\n", id, - info->opened, stat); - prompt_and_exit(); - } - if (info->is_virtual != virtual) { - printf("Expected device %d to be virtual.\n", id); - prompt_and_exit(); - } -} - - -/* stat is 'o' for open, 'c' for closed, 'd' for deleted device */ -void check_ports(int cnt, int in_id, char in_stat, - int out_id, char out_stat, int virtual) -{ - if (cnt != Pm_CountDevices()) { - printf("Device count changed from %d to %d.\n", cnt, Pm_CountDevices()); - prompt_and_exit(); - } - check_info(in_id, in_stat, TRUE, virtual); - check_info(out_id, out_stat, FALSE, virtual); -} - - -void devices_list() -{ - int i; - for (i = 0; i < Pm_CountDevices(); i++) { - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - if (info) { - printf("%d: %s %s %s %s\n", i, info->name, - (info->input ? "input" : "output"), - (info->is_virtual ? "virtual" : "real_device"), - (info->opened ? "opened" : "closed")); - } - } -} - - -void test2() -{ - PmStream *out = NULL; - PmStream *in = NULL; - int out_id; - int in_id; - PmEvent buffer[1]; - PmTimestamp timestamp; - int pitch = 60; - int device_count = 0; - int i; - - printf("This must be virttest instance #2\n"); - - /* find and open portmidi in and out */ - device_count = Pm_CountDevices(); - for (i = 0; i < device_count; i++) { - const PmDeviceInfo *info = Pm_GetDeviceInfo(i); - if (info && strcmp(info->name, "portmidi") == 0) { - if (info->input) { - checkerror(Pm_OpenInput(&in, i, DRIVER_INFO, - INPUT_BUFFER_SIZE, TIME_PROC, TIME_INFO)); - in_id = i; - } else { - checkerror(Pm_OpenOutput(&out, i, DRIVER_INFO, - OUTPUT_BUFFER_SIZE, NULL, NULL, 0)); - out_id = i; - } - } - } - if (!in) { - printf("Did not open portmidi as input (virtual output).\n"); - prompt_and_exit(); - } - if (!out) { - printf("Did not open portmidi as output (virtual input).\n"); - prompt_and_exit(); - } - printf("Input device %d and output device %d are open.\n", in_id, out_id); - - /* send a message */ - buffer[0].timestamp = 0; - buffer[0].message = Pm_Message(0x90, pitch, 100); - checkerror(Pm_Write(out, buffer, 1)); - - /* wait for reply */ - printf("Sent message, waiting for reply...\n"); - while (Pm_Read(in, buffer, 1) < 1) Pt_Sleep(10); - - printf("********** GOT THE MESSAGE, SHUTTING DOWN ************\n"); - - /* close in */ - checkerror(Pm_Close(in)); - check_ports(device_count, in_id, 'c', out_id, 'o', FALSE); - printf("Closed input %d\n", in_id); - - /* close out */ - checkerror(Pm_Close(out)); - check_ports(device_count, in_id, 'c', out_id, 'c', FALSE); - printf("Closed output %d\n", out_id); - - Pt_Sleep(1000); - /* wrap it up */ - Pm_Terminate(); - printf("Got reply and terminated...\n"); - Pt_Sleep(2000); /* 2 seconds because other is waiting 1s. */ - /* 1 more second to make sure other shuts down before test repeats. */ -} - -extern int pm_check_errors; - -void test() -{ - PmStream *out; - PmStream *in; - int out_id; - int in_id; - PmEvent buffer[1]; - PmTimestamp timestamp; - int device_count = 0; - - TIME_START; - - printf("******** INITIALIZING PORTMIDI ***********\n"); - timestamp = Pt_Time(); - Pm_Initialize(); - printf("Pm_Initialize took %dms\n", Pt_Time() - timestamp); - devices_list(); - - pm_check_errors = FALSE; /* otherwise, PM_CHECK_ERRORS, if defined, */ - /* can cause this program to report an error and exit on pmNameConflict. */ - in_id = Pm_CreateVirtualInput("portmidi", NULL, DEVICE_INFO); - pm_check_errors = TRUE; /* there should be no other errors */ - if (in_id < 0) { - printerror(in_id, "Pm_CreateVirtualInput failed..."); - test2(); - return; - } - printf("Created portmidi virtual input; this is virttest instance #1\n"); - out_id = checkerror(Pm_CreateVirtualOutput("portmidi", NULL, DRIVER_INFO)); - device_count = Pm_CountDevices(); - - checkerror(Pm_OpenInput(&in, in_id, NULL, 0, NULL, NULL)); - checkerror(Pm_OpenOutput(&out, out_id, DRIVER_INFO, OUTPUT_BUFFER_SIZE, - TIME_PROC, TIME_INFO, 0)); - printf("Created/Opened input %d and output %d\n", in_id, out_id); - Pm_SetFilter(in, PM_FILT_ACTIVE | PM_FILT_CLOCK | PM_FILT_SYSEX); - /* empty the buffer after setting filter, just in case anything - got through */ - while (Pm_Read(in, buffer, 1)) ; - - /* wait for input */ - printf("Waiting for input...\n"); - while (Pm_Read(in, buffer, 1) < 1) Pt_Sleep(10); - - /* send two replies (only one would be fine) */ - checkerror(Pm_Write(out, buffer, 1)); - printf("Received input, writing output...\n"); - - /* wait 1s so receiver can get the message before we shut down */ - Pt_Sleep(1000); - printf("****** Closing everything and shutting down...\n"); - - /* expect 2 open ports */ - check_ports(device_count, in_id, 'o', out_id, 'o', TRUE); - /* close in */ - checkerror(Pm_Close(in)); - check_ports(device_count, in_id, 'c', out_id, 'o', TRUE); - - /* close out */ - checkerror(Pm_Close(out)); - check_ports(device_count, in_id, 'c', out_id, 'c', TRUE); - - /* delete in */ - checkerror(Pm_DeleteVirtualDevice(in_id)); - check_ports(device_count, in_id, 'd', out_id, 'c', TRUE); - - /* delete out */ - checkerror(Pm_DeleteVirtualDevice(out_id)); - check_ports(device_count, in_id, 'd', out_id, 'd', TRUE); - - /* we are done */ - Pm_Terminate(); -} - - -int main(int argc, char *argv[]) -{ - int i; - show_usage(); - for (i = 0; i < 3; i++) { - test(); - } - printf("finished virttest (SUCCESS). Type ENTER to quit..."); - while (getchar() != '\n') ; - return 0; -} diff --git a/portmidi/pm_win/README_WIN.txt b/portmidi/pm_win/README_WIN.txt deleted file mode 100755 index 8bfb467..0000000 --- a/portmidi/pm_win/README_WIN.txt +++ /dev/null @@ -1,174 +0,0 @@ -File: PortMidi Win32 Readme
-Author: Belinda Thom, June 16 2002
-Revised by: Roger Dannenberg, June 2002, May 2004, June 2007,
- Umpei Kurokawa, June 2007
- Roger Dannenberg Sep 2009, May 2022
-
-Contents:
- Using Portmidi
- To Install Portmidi
- To Compile Portmidi
- About Cmake
- Using other versions of Visual C++
- To Create Your Own Portmidi Client Application
-
-
-
-=============================================================================
-USING PORTMIDI:
-=============================================================================
-
-I recommend building a static library and linking with your
-application. PortMidi is not large. See ../README.md for
-basic compiling instructions.
-
-The Windows version has a couple of extra switches: You can define
-DEBUG and MMDEBUG for a few extra messages (see the code).
-
-If PM_CHECK_ERRORS is defined, PortMidi reports and exits on any
-error. This requires terminal output to see, and aborts your
-application, so it's only intended for quick command line programs
-where you do not care to check return values and handle errors
-more robustly.
-
-PortMidi is designed to run without a console and should work perfectly
-well within a graphical user interface application.
-
-Read the portmidi.h file for PortMidi API details on using the PortMidi API.
-See <...>\pm_test\testio.c and other files in pm_test for usage examples.
-
-There are many other programs in pm_test, including a MIDI monitor.
-
-
-============================================================================
-DESIGN NOTES
-============================================================================
-
-Orderly cleanup after errors are encountered is based on a fixed order of
-steps and state changes to reflect each step. Here's the order:
-
-To open input:
- initialize return value to NULL
- - allocate the PmInternal strucure (representation of PortMidiStream)
- return value is (non-null) PmInternal structure
- - allocate midi buffer
- set buffer field of PmInternal structure
- - call system-dependent open code
- - allocate midiwinmm_type for winmm dependent data
- set descriptor field of PmInternal structure
- - open device
- set handle field of midiwinmm_type structure
- - allocate buffers
- - start device
- - return
- - return
-
-SYSEX HANDLING
-
-There are three cases: simple output, stream output, input
-Each must deal with:
- 1. Buffer Initialization (creating buffers)
- 2. Buffer Allocation (finding a free buffer)
- 3. Buffer Fill (putting bytes in the buffer)
- 4. Buffer Preparation (midiOutPrepare, etc.)
- 5. Buffer Send (to Midi device)
- 6. Buffer Receive (in callback)
- 7. Buffer Empty (removing bytes from buffer)
- 8. Buffer Free (returning to the buffer pool)
- 9. Buffer Finalization (returning to heap)
-
-Here's how simple output handles sysex:
- 1. Buffer Initialization (creating buffers)
- allocated when code tries to write first byte to a buffer
- the test is "if (!m->sysex_buffers[0]) { ... }"
- this field is initialized to NULL when device is opened
- the size is SYSEX_BYTES_PER_BUFFER
- allocate_sysex_buffers() does the initialization
- note that the actual size of the allocation includes
- additional space for a MIDIEVENT (3 longs) which are
- not used in this case
- 2. Buffer Allocation (finding a free buffer)
- see get_free_sysex_buffer()
- cycle through m->sysex_buffers[] using m->next_sysex_buffer
- to determine where to look next
- if nothing is found, wait by blocking on m->sysex_buffer_signal
- this is signaled by the callback every time a message is
- received
- 3. Buffer Fill (putting bytes in the buffer)
- essentially a state machine approach
- hdr->dwBytesRecorded is a position in message pointed to by m->hdr
- keep appending bytes until dwBytesRecorded >= SYSEX_BYTES_PER_BUFFER
- then send the message, reseting the state to initial values
- 4. Buffer Preparation (midiOutPrepare, etc.)
- just before sending in winmm_end_sysex()
- 5. Buffer Send (to Midi device)
- message is padded with zero at end (since extra space was allocated
- this is ok) -- the zero works around a bug in (an old version of)
- MIDI YOKE drivers
- dwBufferLength gets dwBytesRecorded, and dwBytesRecorded gets 0
- uses midiOutLongMsg()
- 6. Buffer Receive (in callback)
- 7. Buffer Empty (removing bytes from buffer)
- not applicable for output
- 8. Buffer Free (returning to the buffer pool)
- unprepare message to indicate that it is free
- SetEvent on m->buffer_signal in case client is waiting
- 9. Buffer Finalization (returning to heap)
- when device is closed, winmm_out_delete frees all sysex buffers
-
-Here's how stream output handles sysex:
- 1. Buffer Initialization (creating buffers)
- same code as simple output (see above)
- 2. Buffer Allocation (finding a free buffer)
- same code as simple output (see above)
- 3. Buffer Fill (putting bytes in the buffer)
- essentially a state machine approach
- m->dwBytesRecorded is a position in message
- keep appending bytes until buffer is full (one byte to spare)
- 4. Buffer Preparation (midiOutPrepare, etc.)
- done before sending message
- dwBytesRecorded and dwBufferLength are set in winmm_end_sysex
- 5. Buffer Send (to Midi device)
- uses midiStreamOutMsg()
- 6. Buffer Receive (in callback)
- 7. Buffer Empty (removing bytes from buffer)
- not applicable for output
- 8. Buffer Free (returning to the buffer pool)
- unprepare message to indicate that it is free
- SetEvent on m->buffer_signal in case client is waiting
- 9. Buffer Finalization (returning to heap)
- when device is closed, winmm_out_delete frees all sysex buffers
-
-
-Here's how input handles sysex:
- 1. Buffer Initialization (creating buffers)
- two buffers are allocated in winmm_in_open
- 2. Buffer Allocation (finding a free buffer)
- same code as simple output (see above)
- 3. Buffer Fill (putting bytes in the buffer)
- not applicable for input
- 4. Buffer Preparation (midiOutPrepare, etc.)
- done before sending message -- in winmm_in_open and in callback
- 5. Buffer Send (to Midi device)
- uses midiInAddbuffer in allocate_sysex_input_buffer (called from
- winmm_in_open) and callback
- 6. Buffer Receive (in callback)
- 7. Buffer Empty (removing bytes from buffer)
- done without pause in loop in callback
- 8. Buffer Free (returning to the buffer pool)
- done by midiInAddBuffer in callback, no pointer to buffers
- is retained except by device
- 9. Buffer Finalization (returning to heap)
- when device is closed, empty buffers are delivered to callback,
- which frees them
-
-IMPORTANT: In addition to the above, PortMidi now has
-"shortcuts" to optimize the transfer of sysex data. To enable
-the optimization for sysex output, the system-dependent code
-sets fields in the pmInternal structure: fill_base, fill_offset_ptr,
-and fill_length. When fill_base is non-null, the system-independent
-part of PortMidi is allowed to directly copy sysex bytes to
-"fill_base[*fill_offset_ptr++]" until *fill_offset_ptr reaches
-fill_length. See the code for details.
-
-
diff --git a/portmidi/pm_win/debugging_dlls.txt b/portmidi/pm_win/debugging_dlls.txt deleted file mode 100755 index 82b81a5..0000000 --- a/portmidi/pm_win/debugging_dlls.txt +++ /dev/null @@ -1,145 +0,0 @@ -========================================================================================================================
-Methods for Debugging DLLs
-========================================================================================================================
-If you have the source for both the DLL and the calling program, open the project for the calling executable file and
-debug the DLL from there. If you load a DLL dynamically, you must specify it in the Additional DLLs category of the
-Debug tab in the Project Settings dialog box.
-
-If you have the source for the DLL only, open the project that builds the DLL. Use the Debug tab in the Project
-Settings dialog box to specify the executable file that calls the DLL.
-
-You can also debug a DLL without a project. For example, maybe you just picked up a DLL and source code but you
-don’t have an associated project or workspace. You can use the Open command on the File menu to select the .DLL
-file you want to debug. The debug information should be in either the .DLL or the related .PDB file. After
-Visual C++ opens the file, on the Build menu click Start Debug and Go to begin debugging.
-
-To debug a DLL using the project for the executable file
-
-From the Project menu, click Settings.
-The Project Settings dialog box appears.
-
-Choose the Debug tab.
-
-
-In the Category drop-down list box, select General.
-
-
-In the Program Arguments text box, type any command-line arguments required by the executable file.
-
-
-In the Category drop-down list box, select Additional DLLs.
-
-
-In the Local Name column, type the names of DLLs to debug.
-If you are debugging remotely, the Remote Name column appears. In this column, type the complete path for the
-remote module to map to the local module name.
-
-In the Preload column, select the check box if you want to load the module before debugging begins.
-
-
-Click OK to store the information in your project.
-
-
-From the Build menu, click Start Debug and Go to start the debugger.
-You can set breakpoints in the DLL or the calling program. You can open a source file for the DLL and set breakpoints
-in that file, even though it is not a part of the executable file’s project.
-
-To debug a DLL using the project for the DLL
-
-From the Project menu, click Settings.
-The Project Settings dialog box appears.
-
-Choose the Debug tab.
-
-
-In the Category drop-down list box, select General.
-
-
-In the Executable For Debug Session text box, type the name of the executable file that calls the DLL.
-
-
-In the Category list box, select Additional DLLs.
-
-
-In the Local Module Name column, type the name of the DLLs you want to debug.
-
-
-Click OK to store the information in your project.
-
-
-Set breakpoints as required in your DLL source files or on function symbols in the DLL.
-
-
-From the Build menu, click Start Debug and Go to start the debugger.
-To debug a DLL created with an external project
-
-From the Project menu, click Settings.
-The Project Settings dialog box appears.
-
-Choose the Debug tab.
-
-
-In the Category drop-down list box, select General.
-
-
-In the Executable For Debug Session text box, type the name of the DLL that your external makefile builds.
-
-
-Click OK to store the information in your project.
-
-
-Build a debug version of the DLL with symbolic debugging information, if you don’t already have one.
-
-
-Follow one of the two procedures immediately preceding this one to debug the DLL.
-
-========================================================================================================================
-Why Don’t My DLL Breakpoints Work?
-========================================================================================================================
-Some reasons why your breakpoints don’t work as expected are listed here, along with solutions or work-arounds for each.
-If you follow the instructions in one topic and are still having breakpoint problems, look at some of the other topics.
-Often breakpoint problems result from a combination of conditions.
-
-You can't set a breakpoint in a source file when the corresponding symbolic information isn't loaded into memory by
-the debugger.
-You cannot set a breakpoint in any source file when the corresponding symbolic information will not be loaded into memory
-by the debugger.
-Symptoms include messages such as "the breakpoint cannot be set" or a simple, noninformational beep.
-
-When setting breakpoints before the code to be debugged has been started, the debugger uses a breakpoint list to keep
-track of how and where to set breakpoints. When you actually begin the debugging session, the debugger loads the symbolic
-information for all the code to be debugged and then walks through its breakpoint list, attempting to set the
-breakpoints.
-
-However, if one or more of the code modules have not been designated to the debugger, there will be no symbolic
-information for the debugger to use when walking through its breakpoint list. Situations where this is likely to
-occur include:
-
-Attempts to set breakpoints in a DLL before the call to LoadLibrary.
-
-Setting a breakpoint in an ActiveX server before the container has started the server.
-
-Other similar cases.
-
-To prevent this behavior in Visual C++, specify all additional DLLs and COM servers in the Additional DLLs field
-in the Debug/Options dialog box to notify the debugger that you want it to load symbolic debug information for
-additional .DLL files. When this has been done, breakpoints set in code that has not yet been loaded into memory
-will be "virtual" breakpoints. When the code is actually loaded into memory by the loader, these become physical
-breakpoints. Make sure that these additional debugging processes are not already running when you start your
-debugging session. The debugging process and these additional processes must be sychronized at the same beginning
-point to work correctly, hitting all breakpoints.
-
-Breakpoints are missed when more than one copy of a DLL is on your hard disk.
-Having more than one copy of a DLL on your hard drive, especially if it is in your Windows directory, can cause
-debugger confusion. The debugger will load the symbolic information for the DLL specified to it at run time (with the
-Additional DLLs field in the Debug/Options dialog box), while Windows has actually loaded a different copy of the
-DLL itself into memory. Because there is no way to force the debugger to load a specific DLL, it is a good idea to
-keep only one version of a DLL at a time in your path, current directory, and Windows directory.
-
-You can’t set "Break When Expression Has Changed" breakpoints on a variable local to a DLL.
-Setting a "Break When Expression Has Changed" breakpoint on a variable local to a DLL function before the call
-to LoadLibrary causes the breakpoint to be virtual (there are no physical addresses for the DLL in memory yet).
-Virtual breakpoints involving expressions pose a special problem. The DLL must be specified to the debugger at
-startup (causing its symbolic information to be loaded). In addition, the DLL's executable code must also be loaded
-into memory before this kind of breakpoint can be set. This means that the calling application's code must be
-executed to the point after its call to LoadLibrary before the debugger will allow this type of breakpoint to be set.
diff --git a/portmidi/pm_win/pmwin.c b/portmidi/pm_win/pmwin.c deleted file mode 100755 index 5cb73b0..0000000 --- a/portmidi/pm_win/pmwin.c +++ /dev/null @@ -1,98 +0,0 @@ -/* pmwin.c -- PortMidi os-dependent code */
-
-/* This file only needs to implement:
- pm_init(), which calls various routines to register the
- available midi devices,
- Pm_GetDefaultInputDeviceID(), and
- Pm_GetDefaultOutputDeviceID().
- This file must
- be separate from the main portmidi.c file because it is system
- dependent, and it is separate from, say, pmwinmm.c, because it
- might need to register devices for winmm, directx, and others.
-
- */
-
-#include "stdlib.h"
-#include "portmidi.h"
-#include "pmutil.h"
-#include "pminternal.h"
-#include "pmwinmm.h"
-#ifdef DEBUG
-#include "stdio.h"
-#endif
-#include <windows.h>
-
-/* pm_exit is called when the program exits.
- It calls pm_term to make sure PortMidi is properly closed.
- If DEBUG is on, we prompt for input to avoid losing error messages.
- */
-static void pm_exit(void) {
- pm_term();
-}
-
-
-static BOOL WINAPI ctrl_c_handler(DWORD fdwCtrlType)
-{
- exit(1); /* invokes pm_exit() */
- ExitProcess(1); /* probably never called */
- return TRUE;
-}
-
-/* pm_init is the windows-dependent initialization.*/
-void pm_init(void)
-{
- atexit(pm_exit);
- SetConsoleCtrlHandler(ctrl_c_handler, TRUE);
-#ifdef DEBUG
- printf("registered pm_exit with atexit()\n");
-#endif
- pm_winmm_init();
- /* initialize other APIs (DirectX?) here */
-}
-
-
-void pm_term(void) {
- pm_winmm_term();
-}
-
-
-static PmDeviceID pm_get_default_device_id(int is_input, char *key) {
-#define PATTERN_MAX 256
- /* Find first input or device -- this is the default. */
- PmDeviceID id = pmNoDevice;
- int i;
- Pm_Initialize(); /* make sure descriptors exist! */
- for (i = 0; i < pm_descriptor_len; i++) {
- if (pm_descriptors[i].pub.input == is_input) {
- id = i;
- break;
- }
- }
- return id;
-}
-
-
-PmDeviceID Pm_GetDefaultInputDeviceID() {
- return pm_get_default_device_id(TRUE,
- "/P/M_/R/E/C/O/M/M/E/N/D/E/D_/I/N/P/U/T_/D/E/V/I/C/E");
-}
-
-
-PmDeviceID Pm_GetDefaultOutputDeviceID() {
- return pm_get_default_device_id(FALSE,
- "/P/M_/R/E/C/O/M/M/E/N/D/E/D_/O/U/T/P/U/T_/D/E/V/I/C/E");
-}
-
-
-#include "stdio.h"
-
-void *pm_alloc(size_t s) {
- return malloc(s);
-}
-
-
-void pm_free(void *ptr) {
- free(ptr);
-}
-
-
diff --git a/portmidi/pm_win/pmwinmm.c b/portmidi/pm_win/pmwinmm.c deleted file mode 100755 index 6f4b6f3..0000000 --- a/portmidi/pm_win/pmwinmm.c +++ /dev/null @@ -1,1196 +0,0 @@ -/* pmwinmm.c -- system specific definitions */
-
-#ifndef _WIN32_WINNT
- /* without this define, InitializeCriticalSectionAndSpinCount is
- * undefined. This version level means "Windows 2000 and higher"
- */
- #define _WIN32_WINNT 0x0500
-#endif
-
-#define UNICODE 1
-#include <wchar.h>
-#include "windows.h"
-#include "mmsystem.h"
-#include "portmidi.h"
-#include "pmutil.h"
-#include "pminternal.h"
-#include "pmwinmm.h"
-#include <string.h>
-#include "porttime.h"
-#ifndef UNICODE
-#error Expected UNICODE to be defined
-#endif
-
-
-/* asserts used to verify portMidi code logic is sound; later may want
- something more graceful */
-#include <assert.h>
-#ifdef MMDEBUG
-/* this printf stuff really important for debugging client app w/host errors.
- probably want to do something else besides read/write from/to console
- for portability, however */
-#define STRING_MAX 80
-#include "stdio.h"
-#endif
-
-#define streql(x, y) (strcmp(x, y) == 0)
-
-#define MIDI_SYSEX 0xf0
-#define MIDI_EOX 0xf7
-
-/* callback routines */
-static void CALLBACK winmm_in_callback(HMIDIIN hMidiIn,
- UINT wMsg, DWORD_PTR dwInstance,
- DWORD_PTR dwParam1, DWORD_PTR dwParam2);
-static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg,
- DWORD_PTR dwInstance,
- DWORD_PTR dwParam1,
- DWORD_PTR dwParam2);
-
-extern pm_fns_node pm_winmm_in_dictionary;
-extern pm_fns_node pm_winmm_out_dictionary;
-
-static void winmm_out_delete(PmInternal *midi); /* forward reference */
-
-/*
-A note about buffers: WinMM seems to hold onto buffers longer than
-one would expect, e.g. when I tried using 2 small buffers to send
-long sysex messages, at some point WinMM held both buffers. This problem
-was fixed by making buffers bigger. Therefore, it seems that there should
-be enough buffer space to hold a whole sysex message.
-
-The bufferSize passed into Pm_OpenInput (passed into here as buffer_len)
-will be used to estimate the largest sysex message (= buffer_len * 4 bytes).
-Call that the max_sysex_len = buffer_len * 4.
-
-For simple midi output (latency == 0), allocate 3 buffers, each with half
-the size of max_sysex_len, but each at least 256 bytes.
-
-For stream output, there will already be enough space in very short
-buffers, so use them, but make sure there are at least 16.
-
-For input, use many small buffers rather than 2 large ones so that when
-there are short sysex messages arriving frequently (as in control surfaces)
-there will be more free buffers to fill. Use max_sysex_len / 64 buffers,
-but at least 16, of size 64 bytes each.
-
-The following constants help to represent these design parameters:
-*/
-#define NUM_SIMPLE_SYSEX_BUFFERS 3
-#define MIN_SIMPLE_SYSEX_LEN 256
-
-#define MIN_STREAM_BUFFERS 16
-#define STREAM_BUFFER_LEN 24
-
-#define INPUT_SYSEX_LEN 64
-#define MIN_INPUT_BUFFERS 16
-
-/* if we run out of space for output (assume this is due to a sysex msg,
- expand by up to NUM_EXPANSION_BUFFERS in increments of EXPANSION_BUFFER_LEN
- */
-#define NUM_EXPANSION_BUFFERS 128
-#define EXPANSION_BUFFER_LEN 1024
-
-/* A sysex buffer has 3 DWORDS as a header plus the actual message size */
-#define MIDIHDR_SYSEX_BUFFER_LENGTH(x) ((x) + sizeof(long)*3)
-/* A MIDIHDR with a sysex message is the buffer length plus the header size */
-#define MIDIHDR_SYSEX_SIZE(x) (MIDIHDR_SYSEX_BUFFER_LENGTH(x) + sizeof(MIDIHDR))
-
-/*
-==============================================================================
-win32 mmedia system specific structure passed to midi callbacks
-==============================================================================
-*/
-
-/* global winmm device info */
-MIDIINCAPS *midi_in_caps = NULL;
-MIDIINCAPS midi_in_mapper_caps;
-UINT midi_num_inputs = 0;
-MIDIOUTCAPS *midi_out_caps = NULL;
-MIDIOUTCAPS midi_out_mapper_caps;
-UINT midi_num_outputs = 0;
-
-/* per device info */
-typedef struct winmm_info_struct {
- union {
- HMIDISTRM stream; /* windows handle for stream */
- HMIDIOUT out; /* windows handle for out calls */
- HMIDIIN in; /* windows handle for in calls */
- } handle;
-
- /* midi output messages are sent in these buffers, which are allocated
- * in a round-robin fashion, using next_buffer as an index
- */
- LPMIDIHDR *buffers; /* pool of buffers for midi in or out data */
- int max_buffers; /* length of buffers array */
- int buffers_expanded; /* buffers array expanded for extra msgs? */
- int num_buffers; /* how many buffers allocated in buffers array */
- int next_buffer; /* index of next buffer to send */
- HANDLE buffer_signal; /* used to wait for buffer to become free */
- unsigned long last_time; /* last output time */
- int first_message; /* flag: treat first message differently */
- int sysex_mode; /* middle of sending sysex */
- unsigned long sysex_word; /* accumulate data when receiving sysex */
- unsigned int sysex_byte_count; /* count how many received */
- LPMIDIHDR hdr; /* the message accumulating sysex to send */
- unsigned long sync_time; /* when did we last determine delta? */
- long delta; /* difference between stream time and
- real time */
- CRITICAL_SECTION lock; /* prevents reentrant callbacks (input only) */
-} winmm_info_node, *winmm_info_type;
-
-
-/*
-=============================================================================
-general MIDI device queries
-=============================================================================
-*/
-
-/* add a device after converting device (product) name to UTF-8 */
-static void pm_add_device_w(char *api, WCHAR *device_name, int is_input,
- int is_virtual, void *descriptor, pm_fns_type dictionary)
-{
- char utf8name[4 * MAXPNAMELEN];
- WideCharToMultiByte(CP_UTF8, 0, device_name, -1,
- utf8name, 4 * MAXPNAMELEN - 1, NULL, NULL);
- /* ignore errors here -- if pm_descriptor_max is exceeded,
- some devices will not be accessible. */
- pm_add_device(api, utf8name, is_input, is_virtual, descriptor, dictionary);
-}
-
-
-static void pm_winmm_general_inputs()
-{
- UINT i;
- WORD wRtn;
- midi_num_inputs = midiInGetNumDevs();
- midi_in_caps = (MIDIINCAPS *) pm_alloc(sizeof(MIDIINCAPS) *
- midi_num_inputs);
- if (midi_in_caps == NULL) {
- /* if you can't open a particular system-level midi interface
- * (such as winmm), we just consider that system or API to be
- * unavailable and move on without reporting an error.
- */
- return;
- }
-
- for (i = 0; i < midi_num_inputs; i++) {
- wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) & midi_in_caps[i],
- sizeof(MIDIINCAPS));
- if (wRtn == MMSYSERR_NOERROR) {
- pm_add_device_w("MMSystem", midi_in_caps[i].szPname, TRUE, FALSE,
- (void *) (intptr_t) i, &pm_winmm_in_dictionary);
- }
- }
-}
-
-
-static void pm_winmm_mapper_input()
-{
- WORD wRtn;
- /* Note: if MIDIMAPPER opened as input (documentation implies you
- can, but current system fails to retrieve input mapper
- capabilities) then you still should retrieve some form of
- setup info. */
- wRtn = midiInGetDevCaps((UINT) MIDIMAPPER,
- (LPMIDIINCAPS) & midi_in_mapper_caps,
- sizeof(MIDIINCAPS));
- if (wRtn == MMSYSERR_NOERROR) {
- pm_add_device_w("MMSystem", midi_in_mapper_caps.szPname, TRUE, FALSE,
- (void *) (intptr_t) MIDIMAPPER,
- &pm_winmm_in_dictionary);
- }
-}
-
-
-static void pm_winmm_general_outputs()
-{
- UINT i;
- DWORD wRtn;
- midi_num_outputs = midiOutGetNumDevs();
- midi_out_caps = pm_alloc(sizeof(MIDIOUTCAPS) * midi_num_outputs);
-
- if (midi_out_caps == NULL) {
- /* no error is reported -- see pm_winmm_general_inputs */
- return ;
- }
-
- for (i = 0; i < midi_num_outputs; i++) {
- wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) & midi_out_caps[i],
- sizeof(MIDIOUTCAPS));
- if (wRtn == MMSYSERR_NOERROR) {
- pm_add_device_w("MMSystem", midi_out_caps[i].szPname, FALSE, FALSE,
- (void *) (intptr_t) i, &pm_winmm_out_dictionary);
- }
- }
-}
-
-
-static void pm_winmm_mapper_output()
-{
- WORD wRtn;
- /* Note: if MIDIMAPPER opened as output (pseudo MIDI device
- maps device independent messages into device dependant ones,
- via NT midimapper program) you still should get some setup info */
- wRtn = midiOutGetDevCaps((UINT) MIDIMAPPER, (LPMIDIOUTCAPS)
- & midi_out_mapper_caps, sizeof(MIDIOUTCAPS));
- if (wRtn == MMSYSERR_NOERROR) {
- pm_add_device_w("MMSystem", midi_out_mapper_caps.szPname, FALSE, FALSE,
- (void *) (intptr_t) MIDIMAPPER,
- &pm_winmm_out_dictionary);
- }
-}
-
-
-/*
-============================================================================
-host error handling
-============================================================================
-*/
-
-static unsigned int winmm_check_host_error(PmInternal *midi)
-{
- return FALSE;
-}
-
-
-/*
-=============================================================================
-buffer handling
-=============================================================================
-*/
-static MIDIHDR *allocate_buffer(long data_size)
-{
- LPMIDIHDR hdr = (LPMIDIHDR) pm_alloc(MIDIHDR_SYSEX_SIZE(data_size));
- MIDIEVENT *evt;
- if (!hdr) return NULL;
- evt = (MIDIEVENT *) (hdr + 1); /* place MIDIEVENT after header */
- hdr->lpData = (LPSTR) evt;
- hdr->dwBufferLength = MIDIHDR_SYSEX_BUFFER_LENGTH(data_size);
- hdr->dwBytesRecorded = 0;
- hdr->dwFlags = 0;
- hdr->dwUser = hdr->dwBufferLength;
- return hdr;
-}
-
-
-static PmError allocate_buffers(winmm_info_type info, long data_size,
- long count)
-{
- int i;
- /* buffers is an array of count pointers to MIDIHDR/MIDIEVENT struct */
- info->num_buffers = 0; /* in case no memory can be allocated */
- info->buffers = (LPMIDIHDR *) pm_alloc(sizeof(LPMIDIHDR) * count);
- if (!info->buffers) return pmInsufficientMemory;
- info->max_buffers = count;
- for (i = 0; i < count; i++) {
- LPMIDIHDR hdr = allocate_buffer(data_size);
- if (!hdr) { /* free everything allocated so far and return */
- for (i = i - 1; i >= 0; i--) pm_free(info->buffers[i]);
- pm_free(info->buffers);
- info->max_buffers = 0;
- return pmInsufficientMemory;
- }
- info->buffers[i] = hdr; /* this may be NULL if allocation fails */
- }
- info->num_buffers = count;
- return pmNoError;
-}
-
-
-static LPMIDIHDR get_free_output_buffer(PmInternal *midi)
-{
- LPMIDIHDR r = NULL;
- winmm_info_type info = (winmm_info_type) midi->api_info;
- while (TRUE) {
- int i;
- for (i = 0; i < info->num_buffers; i++) {
- /* cycle through buffers, modulo info->num_buffers */
- info->next_buffer++;
- if (info->next_buffer >= info->num_buffers) info->next_buffer = 0;
- r = info->buffers[info->next_buffer];
- if ((r->dwFlags & MHDR_PREPARED) == 0) goto found_buffer;
- }
- /* after scanning every buffer and not finding anything, block */
- if (WaitForSingleObject(info->buffer_signal, 1000) == WAIT_TIMEOUT) {
-#ifdef MMDEBUG
- printf("PortMidi warning: get_free_output_buffer() "
- "wait timed out after 1000ms\n");
-#endif
- /* if we're trying to send a sysex message, maybe the
- * message is too big and we need more message buffers.
- * Expand the buffer pool by 128KB using 1024-byte buffers.
- */
- /* first, expand the buffers array if necessary */
- if (!info->buffers_expanded) {
- LPMIDIHDR *new_buffers = (LPMIDIHDR *) pm_alloc(
- (info->num_buffers + NUM_EXPANSION_BUFFERS) *
- sizeof(LPMIDIHDR));
- /* if no memory, we could return a no-memory error, but user
- * probably will be unprepared to deal with it. Maybe the
- * MIDI driver is temporarily hung so we should just wait.
- * I don't know the right answer, but waiting is easier.
- */
- if (!new_buffers) continue;
- /* copy buffers to new_buffers and replace buffers */
- memcpy(new_buffers, info->buffers,
- info->num_buffers * sizeof(LPMIDIHDR));
- pm_free(info->buffers);
- info->buffers = new_buffers;
- info->max_buffers = info->num_buffers + NUM_EXPANSION_BUFFERS;
- info->buffers_expanded = TRUE;
- }
- /* next, add one buffer and return it */
- if (info->num_buffers < info->max_buffers) {
- r = allocate_buffer(EXPANSION_BUFFER_LEN);
- /* again, if there's no memory, we may not really be
- * dead -- maybe the system is temporarily hung and
- * we can just wait longer for a message buffer */
- if (!r) continue;
- info->buffers[info->num_buffers++] = r;
- goto found_buffer; /* break out of 2 loops */
- }
- /* else, we've allocated all NUM_EXPANSION_BUFFERS buffers,
- * and we have no free buffers to send. We'll just keep
- * polling to see if any buffers show up.
- */
- }
- }
-found_buffer:
- r->dwBytesRecorded = 0;
- /* actual buffer length is saved in dwUser field */
- r->dwBufferLength = (DWORD) r->dwUser;
- return r;
-}
-
-/*
-============================================================================
-begin midi input implementation
-============================================================================
-*/
-
-
-static unsigned int allocate_input_buffer(HMIDIIN h, long buffer_len)
-{
- LPMIDIHDR hdr = allocate_buffer(buffer_len);
- if (!hdr) return pmInsufficientMemory;
- /* note: pm_hosterror is normally a boolean, but here, we store Win
- * error code. The caller must test the value for nonzero, set
- * pm_hosterror_text, and then set pm_hosterror to TRUE */
- pm_hosterror = midiInPrepareHeader(h, hdr, sizeof(MIDIHDR));
- if (pm_hosterror) {
- pm_free(hdr);
- return pm_hosterror;
- }
- pm_hosterror = midiInAddBuffer(h, hdr, sizeof(MIDIHDR));
- return pm_hosterror;
-}
-
-
-static winmm_info_type winmm_info_create()
-{
- winmm_info_type info = (winmm_info_type) pm_alloc(sizeof(winmm_info_node));
- info->handle.in = NULL;
- info->handle.out = NULL;
- info->buffers = NULL; /* not used for input */
- info->num_buffers = 0; /* not used for input */
- info->max_buffers = 0; /* not used for input */
- info->buffers_expanded = FALSE; /* not used for input */
- info->next_buffer = 0; /* not used for input */
- info->buffer_signal = 0; /* not used for input */
- info->last_time = 0;
- info->first_message = TRUE; /* not used for input */
- info->sysex_mode = FALSE;
- info->sysex_word = 0;
- info->sysex_byte_count = 0;
- info->hdr = NULL; /* not used for input */
- info->sync_time = 0;
- info->delta = 0;
- return info;
-}
-
-
-static void report_hosterror(LPWCH error_msg)
-{
- WideCharToMultiByte(CP_UTF8, 0, error_msg, -1, pm_hosterror_text,
- sizeof(pm_hosterror_text), NULL, NULL);
- if (pm_hosterror == MMSYSERR_NOMEM) {
- /* add explanation to Window's confusing error message */
- /* if there's room: */
- if (PM_HOST_ERROR_MSG_LEN - strlen(pm_hosterror_text) > 60) {
- strcat_s(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN,
- " Probably this MIDI device is open "
- "in another application.");
- }
- }
- pm_hosterror = TRUE;
-}
-
-
-static void report_hosterror_in()
-{
- WCHAR error_msg[PM_HOST_ERROR_MSG_LEN];
- int err = midiInGetErrorText(pm_hosterror, error_msg,
- PM_HOST_ERROR_MSG_LEN);
- assert(err == MMSYSERR_NOERROR);
- report_hosterror(error_msg);
-}
-
-
-static void report_hosterror_out()
-{
- WCHAR error_msg[PM_HOST_ERROR_MSG_LEN];
- int err = midiOutGetErrorText(pm_hosterror, error_msg,
- PM_HOST_ERROR_MSG_LEN);
- assert(err == MMSYSERR_NOERROR);
- report_hosterror(error_msg);
-}
-
-
-static PmError winmm_in_open(PmInternal *midi, void *driverInfo)
-{
- DWORD dwDevice;
- int i = midi->device_id;
- int max_sysex_len = midi->buffer_len * 4;
- int num_input_buffers = max_sysex_len / INPUT_SYSEX_LEN;
- winmm_info_type info;
-
- dwDevice = (DWORD) (intptr_t) pm_descriptors[i].descriptor;
-
- /* create system dependent device data */
- info = winmm_info_create();
- midi->api_info = info;
- if (!info) goto no_memory;
- /* 4000 is based on Windows documentation -- that's the value used
- in the memory manager. It's small enough that it should not
- hurt performance even if it's not optimal.
- */
- InitializeCriticalSectionAndSpinCount(&info->lock, 4000);
- /* open device */
- pm_hosterror = midiInOpen(
- &(info->handle.in), /* input device handle */
- dwDevice, /* device ID */
- (DWORD_PTR) winmm_in_callback, /* callback address */
- (DWORD_PTR) midi, /* callback instance data */
- CALLBACK_FUNCTION); /* callback is a procedure */
- if (pm_hosterror) goto free_descriptor;
-
- if (num_input_buffers < MIN_INPUT_BUFFERS)
- num_input_buffers = MIN_INPUT_BUFFERS;
- for (i = 0; i < num_input_buffers; i++) {
- if (allocate_input_buffer(info->handle.in, INPUT_SYSEX_LEN)) {
- /* either pm_hosterror was set, or the proper return code
- is pmInsufficientMemory */
- goto close_device;
- }
- }
- /* start device */
- pm_hosterror = midiInStart(info->handle.in);
- if (!pm_hosterror) {
- return pmNoError;
- }
-
- /* undo steps leading up to the detected error */
-
- /* ignore return code (we already have an error to report) */
- midiInReset(info->handle.in);
-close_device:
- midiInClose(info->handle.in); /* ignore return code */
-free_descriptor:
- midi->api_info = NULL;
- pm_free(info);
-no_memory:
- if (pm_hosterror) {
- report_hosterror_in();
- return pmHostError;
- }
- /* if !pm_hosterror, then the error must be pmInsufficientMemory */
- return pmInsufficientMemory;
- /* note: if we return an error code, the device will be
- closed and memory will be freed. It's up to the caller
- to free the parameter midi */
-}
-
-
-/* winmm_in_close -- close an open midi input device */
-/*
- * assume midi is non-null (checked by caller)
- */
-static PmError winmm_in_close(PmInternal *midi)
-{
- winmm_info_type info = (winmm_info_type) midi->api_info;
- if (!info) return pmBadPtr;
- /* device to close */
- if ((pm_hosterror = midiInStop(info->handle.in))) {
- midiInReset(info->handle.in); /* try to reset and close port */
- midiInClose(info->handle.in);
- } else if ((pm_hosterror = midiInReset(info->handle.in))) {
- midiInClose(info->handle.in); /* best effort to close midi port */
- } else {
- pm_hosterror = midiInClose(info->handle.in);
- }
- midi->api_info = NULL;
- DeleteCriticalSection(&info->lock);
- pm_free(info); /* delete */
- if (pm_hosterror) {
- report_hosterror_in();
- return pmHostError;
- }
- return pmNoError;
-}
-
-
-/* Callback function executed via midiInput SW interrupt (via midiInOpen). */
-static void FAR PASCAL winmm_in_callback(
- HMIDIIN hMidiIn, /* midiInput device Handle */
- UINT wMsg, /* midi msg */
- DWORD_PTR dwInstance, /* application data */
- DWORD_PTR dwParam1, /* MIDI data */
- DWORD_PTR dwParam2) /* device timestamp (wrt most recent midiInStart) */
-{
- PmInternal *midi = (PmInternal *) dwInstance;
- winmm_info_type info = (winmm_info_type) midi->api_info;
-
- /* NOTE: we do not just EnterCriticalSection() here because an
- * MIM_CLOSE message arrives when the port is closed, but then
- * the info->lock has been destroyed.
- */
-
- switch (wMsg) {
- case MIM_DATA: {
- /* if this callback is reentered with data, we're in trouble.
- * It's hard to imagine that Microsoft would allow callbacks
- * to be reentrant -- isn't the model that this is like a
- * hardware interrupt? -- but I've seen reentrant behavior
- * using a debugger, so it happens.
- */
- EnterCriticalSection(&info->lock);
-
- /* dwParam1 is MIDI data received, packed into DWORD w/ 1st byte of
- message LOB;
- dwParam2 is time message received by input device driver, specified
- in [ms] from when midiInStart called.
- each message is expanded to include the status byte */
-
- if ((dwParam1 & 0x80) == 0) {
- /* not a status byte -- ignore it. This happened running the
- sysex.c test under Win2K with MidiMan USB 1x1 interface,
- but I can't reproduce it. -RBD
- */
- /* printf("non-status byte found\n"); */
- } else { /* data to process */
- PmEvent event;
- if (midi->time_proc)
- dwParam2 = (*midi->time_proc)(midi->time_info);
- event.timestamp = (PmTimestamp)dwParam2;
- event.message = (PmMessage)dwParam1;
- pm_read_short(midi, &event);
- }
- LeaveCriticalSection(&info->lock);
- break;
- }
- case MIM_LONGDATA: {
- MIDIHDR *lpMidiHdr = (MIDIHDR *) dwParam1;
- unsigned char *data = (unsigned char *) lpMidiHdr->lpData;
- unsigned int processed = 0;
- int remaining = lpMidiHdr->dwBytesRecorded;
-
- EnterCriticalSection(&info->lock);
- /* printf("midi_in_callback -- lpMidiHdr %x, %d bytes, %2x...\n",
- lpMidiHdr, lpMidiHdr->dwBytesRecorded, *data); */
- if (midi->time_proc)
- dwParam2 = (*midi->time_proc)(midi->time_info);
- /* can there be more than one message in one buffer? */
- /* assume yes and iterate through them */
- pm_read_bytes(midi, data + processed, remaining, (PmTimestamp)dwParam2);
-
- /* when a device is closed, the pending MIM_LONGDATA buffers are
- returned to this callback with dwBytesRecorded == 0. In this
- case, we do not want to send them back to the interface (if
- we do, the interface will not close, and Windows OS may hang). */
- if (lpMidiHdr->dwBytesRecorded > 0) {
- MMRESULT rslt;
- lpMidiHdr->dwBytesRecorded = 0;
- lpMidiHdr->dwFlags = 0;
-
- /* note: no error checking -- can this actually fail? */
- rslt = midiInPrepareHeader(hMidiIn, lpMidiHdr, sizeof(MIDIHDR));
- assert(rslt == MMSYSERR_NOERROR);
- /* note: I don't think this can fail except possibly for
- * MMSYSERR_NOMEM, but the pain of reporting this
- * unlikely but probably catastrophic error does not seem
- * worth it.
- */
- rslt = midiInAddBuffer(hMidiIn, lpMidiHdr, sizeof(MIDIHDR));
- assert(rslt == MMSYSERR_NOERROR);
- LeaveCriticalSection(&info->lock);
- } else {
- midiInUnprepareHeader(hMidiIn,lpMidiHdr,sizeof(MIDIHDR));
- LeaveCriticalSection(&info->lock);
- pm_free(lpMidiHdr);
- }
- break;
- }
- case MIM_OPEN:
- break;
- case MIM_CLOSE:
- break;
- case MIM_ERROR:
- /* printf("MIM_ERROR\n"); */
- break;
- case MIM_LONGERROR:
- /* printf("MIM_LONGERROR\n"); */
- break;
- default:
- break;
- }
-}
-
-/*
-===========================================================================
-begin midi output implementation
-===========================================================================
-*/
-
-/* begin helper routines used by midiOutStream interface */
-
-/* add_to_buffer -- adds timestamped short msg to buffer, returns fullp */
-static int add_to_buffer(winmm_info_type m, LPMIDIHDR hdr,
- unsigned long delta, unsigned long msg)
-{
- unsigned long *ptr = (unsigned long *)
- (hdr->lpData + hdr->dwBytesRecorded);
- *ptr++ = delta; /* dwDeltaTime */
- *ptr++ = 0; /* dwStream */
- *ptr++ = msg; /* dwEvent */
- hdr->dwBytesRecorded += 3 * sizeof(long);
- /* if the addition of three more words (a message) would extend beyond
- the buffer length, then return TRUE (full)
- */
- return hdr->dwBytesRecorded + 3 * sizeof(long) > hdr->dwBufferLength;
-}
-
-
-static PmTimestamp pm_time_get(winmm_info_type info)
-{
- MMTIME mmtime;
- MMRESULT wRtn;
- mmtime.wType = TIME_TICKS;
- mmtime.u.ticks = 0;
- wRtn = midiStreamPosition(info->handle.stream, &mmtime, sizeof(mmtime));
- assert(wRtn == MMSYSERR_NOERROR);
- return mmtime.u.ticks;
-}
-
-
-/* end helper routines used by midiOutStream interface */
-
-
-static PmError winmm_out_open(PmInternal *midi, void *driverInfo)
-{
- DWORD dwDevice;
- int i = midi->device_id;
- winmm_info_type info;
- MIDIPROPTEMPO propdata;
- MIDIPROPTIMEDIV divdata;
- int max_sysex_len = midi->buffer_len * 4;
- int output_buffer_len;
- int num_buffers;
- dwDevice = (DWORD) (intptr_t) pm_descriptors[i].descriptor;
-
- /* create system dependent device data */
- info = winmm_info_create();
- midi->api_info = info;
- if (!info) goto no_memory;
- /* create a signal */
- info->buffer_signal = CreateEvent(NULL, FALSE, FALSE, NULL);
- /* this should only fail when there are very serious problems */
- assert(info->buffer_signal);
- /* open device */
- if (midi->latency == 0) {
- /* use simple midi out calls */
- pm_hosterror = midiOutOpen(
- (LPHMIDIOUT) & info->handle.out, /* device Handle */
- dwDevice, /* device ID */
- /* note: same callback fn as for StreamOpen: */
- (DWORD_PTR) winmm_streamout_callback, /* callback fn */
- (DWORD_PTR) midi, /* callback instance data */
- CALLBACK_FUNCTION); /* callback type */
- } else {
- /* use stream-based midi output (schedulable in future) */
- pm_hosterror = midiStreamOpen(
- &info->handle.stream, /* device Handle */
- (LPUINT) & dwDevice, /* device ID pointer */
- 1, /* reserved, must be 1 */
- (DWORD_PTR) winmm_streamout_callback,
- (DWORD_PTR) midi, /* callback instance data */
- CALLBACK_FUNCTION);
- }
- if (pm_hosterror != MMSYSERR_NOERROR) {
- goto free_descriptor;
- }
-
- if (midi->latency == 0) {
- num_buffers = NUM_SIMPLE_SYSEX_BUFFERS;
- output_buffer_len = max_sysex_len / num_buffers;
- if (output_buffer_len < MIN_SIMPLE_SYSEX_LEN)
- output_buffer_len = MIN_SIMPLE_SYSEX_LEN;
- } else {
- num_buffers = max(midi->buffer_len, midi->latency / 2);
- if (num_buffers < MIN_STREAM_BUFFERS)
- num_buffers = MIN_STREAM_BUFFERS;
- output_buffer_len = STREAM_BUFFER_LEN;
-
- propdata.cbStruct = sizeof(MIDIPROPTEMPO);
- propdata.dwTempo = 480000; /* microseconds per quarter */
- pm_hosterror = midiStreamProperty(info->handle.stream,
- (LPBYTE) & propdata,
- MIDIPROP_SET | MIDIPROP_TEMPO);
- if (pm_hosterror) goto close_device;
-
- divdata.cbStruct = sizeof(MIDIPROPTEMPO);
- divdata.dwTimeDiv = 480; /* divisions per quarter */
- pm_hosterror = midiStreamProperty(info->handle.stream,
- (LPBYTE) & divdata,
- MIDIPROP_SET | MIDIPROP_TIMEDIV);
- if (pm_hosterror) goto close_device;
- }
- /* allocate buffers */
- if (allocate_buffers(info, output_buffer_len, num_buffers))
- goto free_buffers;
- /* start device */
- if (midi->latency != 0) {
- pm_hosterror = midiStreamRestart(info->handle.stream);
- if (pm_hosterror != MMSYSERR_NOERROR) goto free_buffers;
- }
- return pmNoError;
-
-free_buffers:
- /* buffers are freed below by winmm_out_delete */
-close_device:
- midiOutClose(info->handle.out);
-free_descriptor:
- midi->api_info = NULL;
- winmm_out_delete(midi); /* frees buffers and m */
-no_memory:
- if (pm_hosterror) {
- report_hosterror_out();
- return pmHostError;
- }
- return pmInsufficientMemory;
-}
-
-
-/* winmm_out_delete -- carefully free data associated with midi */
-/**/
-static void winmm_out_delete(PmInternal *midi)
-{
- int i;
- /* delete system dependent device data */
- winmm_info_type info = (winmm_info_type) midi->api_info;
- if (info) {
- if (info->buffer_signal) {
- /* don't report errors -- better not to stop cleanup */
- CloseHandle(info->buffer_signal);
- }
- /* if using stream output, free buffers */
- for (i = 0; i < info->num_buffers; i++) {
- if (info->buffers[i]) pm_free(info->buffers[i]);
- }
- info->num_buffers = 0;
- pm_free(info->buffers);
- info->max_buffers = 0;
- }
- midi->api_info = NULL;
- pm_free(info); /* delete */
-}
-
-
-/* see comments for winmm_in_close */
-static PmError winmm_out_close(PmInternal *midi)
-{
- winmm_info_type info = (winmm_info_type) midi->api_info;
- if (info->handle.out) {
- /* device to close */
- if (midi->latency == 0) {
- pm_hosterror = midiOutClose(info->handle.out);
- } else {
- pm_hosterror = midiStreamClose(info->handle.stream);
- }
- /* regardless of outcome, free memory */
- winmm_out_delete(midi);
- }
- if (pm_hosterror) {
- report_hosterror_out();
- return pmHostError;
- }
- return pmNoError;
-}
-
-
-static PmError winmm_out_abort(PmInternal *midi)
-{
- winmm_info_type info = (winmm_info_type) midi->api_info;
-
- /* only stop output streams */
- if (midi->latency > 0) {
- pm_hosterror = midiStreamStop(info->handle.stream);
- if (pm_hosterror) {
- report_hosterror_out();
- return pmHostError;
- }
- }
- return pmNoError;
-}
-
-
-static PmError winmm_write_flush(PmInternal *midi, PmTimestamp timestamp)
-{
- winmm_info_type info = (winmm_info_type) midi->api_info;
- assert(info);
- if (info->hdr) {
- pm_hosterror = midiOutPrepareHeader(info->handle.out, info->hdr,
- sizeof(MIDIHDR));
- if (pm_hosterror) {
- /* do not send message */
- } else if (midi->latency == 0) {
- /* As pointed out by Nigel Brown, 20Sep06, dwBytesRecorded
- * should be zero. This is set in get_free_sysex_buffer().
- * The msg length goes in dwBufferLength in spite of what
- * Microsoft documentation says (or doesn't say). */
- info->hdr->dwBufferLength = info->hdr->dwBytesRecorded;
- info->hdr->dwBytesRecorded = 0;
- pm_hosterror = midiOutLongMsg(info->handle.out, info->hdr,
- sizeof(MIDIHDR));
- } else {
- pm_hosterror = midiStreamOut(info->handle.stream, info->hdr,
- sizeof(MIDIHDR));
- }
- midi->fill_base = NULL;
- info->hdr = NULL;
- if (pm_hosterror) {
- report_hosterror_out();
- return pmHostError;
- }
- }
- return pmNoError;
-}
-
-
-static PmError winmm_write_short(PmInternal *midi, PmEvent *event)
-{
- winmm_info_type info = (winmm_info_type) midi->api_info;
- PmError rslt = pmNoError;
- assert(info);
-
- if (midi->latency == 0) { /* use midiOut interface, ignore timestamps */
- pm_hosterror = midiOutShortMsg(info->handle.out, event->message);
- if (pm_hosterror) {
- if (info->hdr) { /* device disconnect may delete hdr */
- info->hdr->dwFlags = 0; /* release the buffer */
- }
- report_hosterror_out();
- return pmHostError;
- }
- } else { /* use midiStream interface -- pass data through buffers */
- unsigned long when = event->timestamp;
- unsigned long delta;
- int full;
- if (when == 0) when = midi->now;
- /* when is in real_time; translate to intended stream time */
- when = when + info->delta + midi->latency;
- /* make sure we don't go backward in time */
- if (when < info->last_time) when = info->last_time;
- delta = when - info->last_time;
- info->last_time = when;
- /* before we insert any data, we must have a buffer */
- if (info->hdr == NULL) {
- /* stream interface: buffers allocated when stream is opened */
- info->hdr = get_free_output_buffer(midi);
- }
- full = add_to_buffer(info, info->hdr, delta, event->message);
- /* note: winmm_write_flush sets pm_hosterror etc. on host error */
- if (full) rslt = winmm_write_flush(midi, when);
- }
- return rslt;
-}
-
-#define winmm_begin_sysex winmm_write_flush
-#ifndef winmm_begin_sysex
-static PmError winmm_begin_sysex(PmInternal *midi, PmTimestamp timestamp)
-{
- winmm_info_type m = (winmm_info_type) midi->api_info;
- PmError rslt = pmNoError;
-
- if (midi->latency == 0) {
- /* do nothing -- it's handled in winmm_write_byte */
- } else {
- /* sysex expects an empty sysex buffer, so send whatever is here */
- rslt = winmm_write_flush(midi);
- }
- return rslt;
-}
-#endif
-
-static PmError winmm_end_sysex(PmInternal *midi, PmTimestamp timestamp)
-{
- /* could check for callback_error here, but I haven't checked
- * what happens if we exit early and don't finish the sysex msg
- * and clean up
- */
- winmm_info_type info = (winmm_info_type) midi->api_info;
- PmError rslt = pmNoError;
- LPMIDIHDR hdr = info->hdr;
- if (!hdr) return rslt; /* something bad happened earlier,
- do not report an error because it would have been
- reported (at least) once already */
- /* a(n old) version of MIDI YOKE requires a zero byte after
- * the sysex message, but do not increment dwBytesRecorded: */
- hdr->lpData[hdr->dwBytesRecorded] = 0;
- if (midi->latency == 0) {
-#ifdef DEBUG_PRINT_BEFORE_SENDING_SYSEX
- /* DEBUG CODE: */
- { int i; int len = info->hdr->dwBufferLength;
- printf("OutLongMsg %d ", len);
- for (i = 0; i < len; i++) {
- printf("%2x ", (unsigned char) (info->hdr->lpData[i]));
- }
- }
-#endif
- } else {
- /* Using stream interface. There are accumulated bytes in info->hdr
- to send using midiStreamOut
- */
- /* add bytes recorded to MIDIEVENT length, but don't
- count the MIDIEVENT data (3 longs) */
- MIDIEVENT *evt = (MIDIEVENT *) (hdr->lpData);
- evt->dwEvent += hdr->dwBytesRecorded - 3 * sizeof(long);
- /* round up BytesRecorded to multiple of 4 */
- hdr->dwBytesRecorded = (hdr->dwBytesRecorded + 3) & ~3;
- }
- rslt = winmm_write_flush(midi, timestamp);
- return rslt;
-}
-
-
-static PmError winmm_write_byte(PmInternal *midi, unsigned char byte,
- PmTimestamp timestamp)
-{
- /* write a sysex byte */
- PmError rslt = pmNoError;
- winmm_info_type info = (winmm_info_type) midi->api_info;
- LPMIDIHDR hdr = info->hdr;
- unsigned char *msg_buffer;
- assert(info);
- if (!hdr) {
- info->hdr = hdr = get_free_output_buffer(midi);
- assert(hdr);
- midi->fill_base = (unsigned char *) info->hdr->lpData;
- midi->fill_offset_ptr = (uint32_t *) &(hdr->dwBytesRecorded);
- /* when buffer fills, Pm_WriteSysEx will revert to calling
- * pmwin_write_byte, which expect to have space, so leave
- * one byte free for pmwin_write_byte. Leave another byte
- * of space for zero after message to make early version of
- * MIDI YOKE driver happy -- therefore dwBufferLength - 2 */
- midi->fill_length = hdr->dwBufferLength - 2;
- if (midi->latency != 0) {
- unsigned long when = (unsigned long) timestamp;
- unsigned long delta;
- unsigned long *ptr;
- if (when == 0) when = midi->now;
- /* when is in real_time; translate to intended stream time */
- when = when + info->delta + midi->latency;
- /* make sure we don't go backward in time */
- if (when < info->last_time) when = info->last_time;
- delta = when - info->last_time;
- info->last_time = when;
-
- ptr = (unsigned long *) hdr->lpData;
- *ptr++ = delta;
- *ptr++ = 0;
- *ptr = MEVT_F_LONG;
- hdr->dwBytesRecorded = 3 * sizeof(long);
- /* data will be added at an offset of dwBytesRecorded ... */
- }
- }
- /* add the data byte */
- msg_buffer = (unsigned char *) (hdr->lpData);
- msg_buffer[hdr->dwBytesRecorded++] = byte;
-
- /* see if buffer is full, leave one byte extra for pad */
- if (hdr->dwBytesRecorded >= hdr->dwBufferLength - 1) {
- /* write what we've got and continue */
- rslt = winmm_end_sysex(midi, timestamp);
- }
- return rslt;
-}
-
-
-static PmTimestamp winmm_synchronize(PmInternal *midi)
-{
- winmm_info_type info;
- unsigned long pm_stream_time_2;
- unsigned long real_time;
- unsigned long pm_stream_time;
-
- /* only synchronize if we are using stream interface */
- if (midi->latency == 0) return 0;
-
- /* figure out the time */
- info = (winmm_info_type) midi->api_info;
- pm_stream_time_2 = pm_time_get(info);
-
- do {
- /* read real_time between two reads of stream time */
- pm_stream_time = pm_stream_time_2;
- real_time = (*midi->time_proc)(midi->time_info);
- pm_stream_time_2 = pm_time_get(info);
- /* repeat if more than 1ms elapsed */
- } while (pm_stream_time_2 > pm_stream_time + 1);
- info->delta = pm_stream_time - real_time;
- info->sync_time = real_time;
- return real_time;
-}
-
-
-/* winmm_streamout_callback -- unprepare (free) buffer header */
-static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg,
- DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
-{
- PmInternal *midi = (PmInternal *) dwInstance;
- winmm_info_type info = (winmm_info_type) midi->api_info;
- LPMIDIHDR hdr = (LPMIDIHDR) dwParam1;
- int err;
-
- /* Even if an error is pending, I think we should unprepare msgs and
- signal their arrival
- */
- /* printf("streamout_callback: hdr %x, wMsg %x, MOM_DONE %x\n",
- hdr, wMsg, MOM_DONE); */
- if (wMsg == MOM_DONE) {
- MMRESULT ret = midiOutUnprepareHeader(info->handle.out, hdr,
- sizeof(MIDIHDR));
- assert(ret == MMSYSERR_NOERROR);
- } else if (wMsg == MOM_CLOSE) {
- /* The streaming API gets a callback when the device is closed.
- * The non-streaming API gets a callback when the device is
- * removed or closed. It is misleading to set is_removed when
- * the device is closed normally, but in that case, midi itself
- * will be freed immediately, so there should be no way to
- * observe is_removed == TRUE. On the other hand, if the device
- * is removed, setting is_removed will cause PortMidi to return
- * the pmDeviceRemoved error on attempts to output to the device.
- * In the case of normal closing, due to midiOutClose(),
- * the call below is reentrant (!), but for some reason this does
- * not cause an error or infinite recursion, so we are not taking
- * any precautions to flag midi as "in the process of closing."
- */
- midi->is_removed = TRUE;
- midiOutClose(info->handle.out);
- }
- /* signal client in case it is blocked waiting for buffer */
- err = SetEvent(info->buffer_signal);
- assert(err); /* false -> error */
-}
-
-
-/*
-===========================================================================
-begin exported functions
-===========================================================================
-*/
-
-#define winmm_in_abort pm_fail_fn
-pm_fns_node pm_winmm_in_dictionary = {
- none_write_short,
- none_sysex,
- none_sysex,
- none_write_byte,
- none_write_short,
- none_write_flush,
- winmm_synchronize,
- winmm_in_open,
- winmm_in_abort,
- winmm_in_close,
- success_poll,
- winmm_check_host_error
- };
-
-pm_fns_node pm_winmm_out_dictionary = {
- winmm_write_short,
- winmm_begin_sysex,
- winmm_end_sysex,
- winmm_write_byte,
- /* short realtime message: */ winmm_write_short,
- winmm_write_flush,
- winmm_synchronize,
- winmm_out_open,
- winmm_out_abort,
- winmm_out_close,
- none_poll,
- winmm_check_host_error
- };
-
-
-/* initialize winmm interface. Note that if there is something wrong
- with winmm (e.g. it is not supported or installed), it is not an
- error. We should simply return without having added any devices to
- the table. Hence, no error code is returned. Furthermore, this init
- code is called along with every other supported interface, so the
- user would have a very hard time figuring out what hardware and API
- generated the error. Finally, it would add complexity to pmwin.c to
- remember where the error code came from in order to convert to text.
- */
-void pm_winmm_init( void )
-{
- pm_winmm_mapper_input();
- pm_winmm_mapper_output();
- pm_winmm_general_inputs();
- pm_winmm_general_outputs();
-}
-
-
-/* no error codes are returned, even if errors are encountered, because
- there is probably nothing the user could do (e.g. it would be an error
- to retry.
- */
-void pm_winmm_term( void )
-{
- int i;
-#ifdef MMDEBUG
- int doneAny = 0;
- printf("pm_winmm_term called\n");
-#endif
- for (i = 0; i < pm_descriptor_len; i++) {
- PmInternal *midi = pm_descriptors[i].pm_internal;
- if (midi) {
- winmm_info_type info = (winmm_info_type) midi->api_info;
- if (info->handle.out) {
- /* close next open device*/
-#ifdef MMDEBUG
- if (doneAny == 0) {
- printf("begin closing open devices...\n");
- doneAny = 1;
- }
-#endif
- /* close all open ports */
- (*midi->dictionary->close)(midi);
- }
- }
- }
- if (midi_in_caps) {
- pm_free(midi_in_caps);
- midi_in_caps = NULL;
- }
- if (midi_out_caps) {
- pm_free(midi_out_caps);
- midi_out_caps = NULL;
- }
-#ifdef MMDEBUG
- if (doneAny) {
- printf("warning: devices were left open. They have been closed.\n");
- }
- printf("pm_winmm_term exiting\n");
-#endif
- pm_descriptor_len = 0;
-}
diff --git a/portmidi/pm_win/pmwinmm.h b/portmidi/pm_win/pmwinmm.h deleted file mode 100755 index 4a353e2..0000000 --- a/portmidi/pm_win/pmwinmm.h +++ /dev/null @@ -1,5 +0,0 @@ -/* pmwinmm.h -- system-specific definitions for windows multimedia API */
-
-void pm_winmm_init( void );
-void pm_winmm_term( void );
-
diff --git a/portmidi/pm_win/static.cmake b/portmidi/pm_win/static.cmake deleted file mode 100644 index 7fdad18..0000000 --- a/portmidi/pm_win/static.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# static.cmake -- change flags to link with static runtime libraries
-#
-# Even when BUILD_SHARED_LIBS is OFF, CMake specifies linking wtih
-# multithread DLL, so you give inconsistent linking instructions
-# resulting in warning messages from MS Visual Studio. If you want
-# a static binary, I've found this approach works to eliminate
-# warnings and make everything static:
-#
-# Changes /MD (multithread DLL) to /MT (multithread static)
-
-if(MSVC)
- foreach(flag_var
- CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
- CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO
- CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
- CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO)
- if(${flag_var} MATCHES "/MD")
- string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
- endif(${flag_var} MATCHES "/MD")
- endforeach(flag_var)
-
- message(STATUS
- "Note: overriding CMAKE_*_FLAGS_* to use Visual C static multithread library")
-endif(MSVC)
diff --git a/portmidi/portmusic_logo.png b/portmidi/portmusic_logo.png Binary files differdeleted file mode 100644 index 17a063a..0000000 --- a/portmidi/portmusic_logo.png +++ /dev/null diff --git a/portmidi/porttime/porttime.c b/portmidi/porttime/porttime.c deleted file mode 100755 index 71b06f4..0000000 --- a/portmidi/porttime/porttime.c +++ /dev/null @@ -1,3 +0,0 @@ -/* porttime.c -- portable API for millisecond timer */
-
-/* There is no machine-independent implementation code to put here */
diff --git a/portmidi/porttime/porttime.h b/portmidi/porttime/porttime.h deleted file mode 100755 index 0a61c5c..0000000 --- a/portmidi/porttime/porttime.h +++ /dev/null @@ -1,103 +0,0 @@ -/** @file porttime.h portable interface to millisecond timer. */ - -/* CHANGE LOG FOR PORTTIME - 10-Jun-03 Mark Nelson & RBD - boost priority of timer thread in ptlinux.c implementation - */ - -#ifndef PORTMIDI_PORTTIME_H -#define PORTMIDI_PORTTIME_H - -/* Should there be a way to choose the source of time here? */ - -#ifdef WIN32 -#ifndef INT32_DEFINED -// rather than having users install a special .h file for windows, -// just put the required definitions inline here. portmidi.h uses -// these too, so the definitions are (unfortunately) duplicated there -typedef int int32_t; -typedef unsigned int uint32_t; -#define INT32_DEFINED -#endif -#else -#include <stdint.h> // needed for int32_t -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef PMEXPORT -#ifdef _WINDLL -#define PMEXPORT __declspec(dllexport) -#else -#define PMEXPORT -#endif -#endif - -/** @defgroup grp_porttime PortTime: Millisecond Timer - @{ -*/ - -typedef enum { - ptNoError = 0, /* success */ - ptHostError = -10000, /* a system-specific error occurred */ - ptAlreadyStarted, /* cannot start timer because it is already started */ - ptAlreadyStopped, /* cannot stop timer because it is already stopped */ - ptInsufficientMemory /* memory could not be allocated */ -} PtError; /**< @brief @enum PtError PortTime error code; a common return type. - * No error is indicated by zero; errors are indicated by < 0. - */ - -/** real time or time offset in milliseconds. */ -typedef int32_t PtTimestamp; - -/** a function that gets a current time */ -typedef void (PtCallback)(PtTimestamp timestamp, void *userData); - -/** start a real-time clock service. - - @param resolution the timer resolution in ms. The time will advance every - \p resolution ms. - - @param callback a function pointer to be called every resolution ms. - - @param userData is passed to \p callback as a parameter. - - @return #ptNoError on success. See #PtError for other values. -*/ -PMEXPORT PtError Pt_Start(int resolution, PtCallback *callback, void *userData); - -/** stop the timer. - - @return #ptNoError on success. See #PtError for other values. -*/ -PMEXPORT PtError Pt_Stop(void); - -/** test if the timer is running. - - @return TRUE or FALSE -*/ -PMEXPORT int Pt_Started(void); - -/** get the current time in ms. - - @return the current time -*/ -PMEXPORT PtTimestamp Pt_Time(void); - -/** pauses the current thread, allowing other threads to run. - - @param duration the length of the pause in ms. The true duration - of the pause may be rounded to the nearest or next clock tick - as determined by resolution in #Pt_Start(). -*/ -PMEXPORT void Pt_Sleep(int32_t duration); - -/** @} */ - -#ifdef __cplusplus -} -#endif - -#endif // PORTMIDI_PORTTIME_H diff --git a/portmidi/porttime/pthaiku.cpp b/portmidi/porttime/pthaiku.cpp deleted file mode 100644 index 9d8de14..0000000 --- a/portmidi/porttime/pthaiku.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// pthaiku.cpp - portable timer implementation for Haiku - -#include "porttime.h" -#include <Looper.h> -#include <MessageRunner.h> -#include <OS.h> - -namespace { - const uint32 timerMessage = 'PTTM'; - - struct TimerLooper : BLooper { - TimerLooper() : BLooper() { - } - - - virtual void MessageReceived(BMessage *message) - { - PtCallback *callback; - void *userData; - if (message->what == timerMessage && message->FindPointer("callback", (void**)&callback) == B_OK && message->FindPointer("userData", &userData) == B_OK) { - (*callback)(Pt_Time(), userData); - } - BLooper::MessageReceived(message); - } - }; - - bool time_started_flag = false; - bigtime_t time_offset; - TimerLooper *timerLooper; - BMessageRunner *timerRunner; -} - -extern "C" { - PtError Pt_Start(int resolution, PtCallback *callback, void *userData) - { - if (time_started_flag) return ptAlreadyStarted; - time_offset = system_time(); - if (callback) { - timerLooper = new TimerLooper; - timerLooper->Run(); - BMessenger target(timerLooper); - BMessage message(timerMessage); - message.AddPointer("callback", (void*)callback); - message.AddPointer("userData", userData); - bigtime_t interval = resolution * 1000; - timerRunner = new BMessageRunner(target, &message, interval); - if(timerRunner->InitCheck() != B_OK) { - delete timerRunner; - timerRunner = NULL; - timerLooper->PostMessage(B_QUIT_REQUESTED); - timerLooper = NULL; - return ptHostError; - } - } - time_started_flag = true; - return ptNoError; - } - - - PtError Pt_Stop() - { - if (!time_started_flag) return ptAlreadyStopped; - time_started_flag = false; - delete timerRunner; - timerRunner = NULL; - timerLooper->PostMessage(B_QUIT_REQUESTED); - timerLooper = NULL; - return ptNoError; - } - - - int Pt_Started() - { - return time_started_flag; - } - - - PtTimestamp Pt_Time() - { - return (system_time() - time_offset) / 1000; - } - - - void Pt_Sleep(int32_t duration) - { - snooze(duration * 1000); - } -} diff --git a/portmidi/porttime/ptlinux.c b/portmidi/porttime/ptlinux.c deleted file mode 100755 index c4af5c1..0000000 --- a/portmidi/porttime/ptlinux.c +++ /dev/null @@ -1,139 +0,0 @@ -/* ptlinux.c -- portable timer implementation for linux */ - - -/* IMPLEMENTATION NOTES (by Mark Nelson): - -Unlike Windows, Linux has no system call to request a periodic callback, -so if Pt_Start() receives a callback parameter, it must create a thread -that wakes up periodically and calls the provided callback function. -If running as superuser, use setpriority() to renice thread to -20. -One could also set the timer thread to a real-time priority (SCHED_FIFO -and SCHED_RR), but this is dangerous for This is necessary because -if the callback hangs it'll never return. A more serious reason -is that the current scheduler implementation busy-waits instead -of sleeping when realtime threads request a sleep of <=2ms (as a way -to get around the 10ms granularity), which means the thread would never -let anyone else on the CPU. - -CHANGE LOG - -18-Jul-03 Roger Dannenberg -- Simplified code to set priority of timer - thread. Simplified implementation notes. - -*/ -/* stdlib, stdio, unistd, and sys/types were added because they appeared - * in a Gentoo patch, but I'm not sure why they are needed. -RBD - */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <sys/types.h> -#include "porttime.h" -#include "time.h" -#include "sys/resource.h" -#include "pthread.h" - -#define TRUE 1 -#define FALSE 0 - -#ifndef CLOCK_MONOTONIC_RAW -#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC -#endif - -static int time_started_flag = FALSE; -static struct timespec time_offset = {0, 0}; -static pthread_t pt_thread_pid; -static int pt_thread_created = FALSE; - -/* note that this is static data -- we only need one copy */ -typedef struct { - int id; - int resolution; - PtCallback *callback; - void *userData; -} pt_callback_parameters; - -static int pt_callback_proc_id = 0; - -static void *Pt_CallbackProc(void *p) -{ - pt_callback_parameters *parameters = (pt_callback_parameters *) p; - int mytime = 1; - /* to kill a process, just increment the pt_callback_proc_id */ - /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, - parameters->id); */ - if (geteuid() == 0) setpriority(PRIO_PROCESS, 0, -20); - while (pt_callback_proc_id == parameters->id) { - /* wait for a multiple of resolution ms */ - struct timeval timeout; - int delay = mytime++ * parameters->resolution - Pt_Time(); - if (delay < 0) delay = 0; - timeout.tv_sec = 0; - timeout.tv_usec = delay * 1000; - select(0, NULL, NULL, NULL, &timeout); - (*(parameters->callback))(Pt_Time(), parameters->userData); - } - /* printf("Pt_CallbackProc exiting\n"); */ - // free(parameters); - return NULL; -} - - -PtError Pt_Start(int resolution, PtCallback *callback, void *userData) -{ - if (time_started_flag) return ptNoError; - /* need this set before process runs: */ - clock_gettime(CLOCK_MONOTONIC_RAW, &time_offset); - if (callback) { - int res; - pt_callback_parameters *parms = (pt_callback_parameters *) - malloc(sizeof(pt_callback_parameters)); - if (!parms) return ptInsufficientMemory; - parms->id = pt_callback_proc_id; - parms->resolution = resolution; - parms->callback = callback; - parms->userData = userData; - res = pthread_create(&pt_thread_pid, NULL, - Pt_CallbackProc, parms); - if (res != 0) return ptHostError; - pt_thread_created = TRUE; - } - time_started_flag = TRUE; - return ptNoError; -} - - -PtError Pt_Stop() -{ - /* printf("Pt_Stop called\n"); */ - pt_callback_proc_id++; - if (pt_thread_created) { - pthread_join(pt_thread_pid, NULL); - pt_thread_created = FALSE; - } - time_started_flag = FALSE; - return ptNoError; -} - - -int Pt_Started() -{ - return time_started_flag; -} - - -PtTimestamp Pt_Time() -{ - long seconds, ms; - struct timespec now; - clock_gettime(CLOCK_MONOTONIC_RAW, &now); - seconds = now.tv_sec - time_offset.tv_sec; - ms = (now.tv_nsec - time_offset.tv_nsec) / 1000000; /* round down */ - return seconds * 1000 + ms; -} - - -void Pt_Sleep(int32_t duration) -{ - usleep(duration * 1000); -} diff --git a/portmidi/porttime/ptmacosx_cf.c b/portmidi/porttime/ptmacosx_cf.c deleted file mode 100755 index a174c86..0000000 --- a/portmidi/porttime/ptmacosx_cf.c +++ /dev/null @@ -1,140 +0,0 @@ -/* ptmacosx.c -- portable timer implementation for mac os x */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <pthread.h>
-#include <CoreFoundation/CoreFoundation.h>
-
-#import <mach/mach.h>
-#import <mach/mach_error.h>
-#import <mach/mach_time.h>
-#import <mach/clock.h>
-
-#include "porttime.h"
-
-#define THREAD_IMPORTANCE 30
-#define LONG_TIME 1000000000.0
-
-static int time_started_flag = FALSE;
-static CFAbsoluteTime startTime = 0.0;
-static CFRunLoopRef timerRunLoop;
-
-typedef struct {
- int resolution;
- PtCallback *callback;
- void *userData;
-} PtThreadParams;
-
-
-void Pt_CFTimerCallback(CFRunLoopTimerRef timer, void *info)
-{
- PtThreadParams *params = (PtThreadParams*)info;
- (*params->callback)(Pt_Time(), params->userData);
-}
-
-static void* Pt_Thread(void *p)
-{
- CFTimeInterval timerInterval;
- CFRunLoopTimerContext timerContext;
- CFRunLoopTimerRef timer;
- PtThreadParams *params = (PtThreadParams*)p;
- //CFTimeInterval timeout;
-
- /* raise the thread's priority */
- kern_return_t error;
- thread_extended_policy_data_t extendedPolicy;
- thread_precedence_policy_data_t precedencePolicy;
-
- extendedPolicy.timeshare = 0;
- error = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY,
- (thread_policy_t)&extendedPolicy,
- THREAD_EXTENDED_POLICY_COUNT);
- if (error != KERN_SUCCESS) {
- mach_error("Couldn't set thread timeshare policy", error);
- }
-
- precedencePolicy.importance = THREAD_IMPORTANCE;
- error = thread_policy_set(mach_thread_self(), THREAD_PRECEDENCE_POLICY,
- (thread_policy_t)&precedencePolicy,
- THREAD_PRECEDENCE_POLICY_COUNT);
- if (error != KERN_SUCCESS) {
- mach_error("Couldn't set thread precedence policy", error);
- }
-
- /* set up the timer context */
- timerContext.version = 0;
- timerContext.info = params;
- timerContext.retain = NULL;
- timerContext.release = NULL;
- timerContext.copyDescription = NULL;
-
- /* create a new timer */
- timerInterval = (double)params->resolution / 1000.0;
- timer = CFRunLoopTimerCreate(NULL, startTime+timerInterval, timerInterval,
- 0, 0, Pt_CFTimerCallback, &timerContext);
-
- timerRunLoop = CFRunLoopGetCurrent();
- CFRunLoopAddTimer(timerRunLoop, timer, CFSTR("PtTimeMode"));
-
- /* run until we're told to stop by Pt_Stop() */
- CFRunLoopRunInMode(CFSTR("PtTimeMode"), LONG_TIME, false);
-
- CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, CFSTR("PtTimeMode"));
- CFRelease(timer);
- free(params);
-
- return NULL;
-}
-
-PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
-{
- PtThreadParams *params = (PtThreadParams*)malloc(sizeof(PtThreadParams));
- pthread_t pthread_id;
-
- printf("Pt_Start() called\n");
-
- // /* make sure we're not already playing */
- if (time_started_flag) return ptAlreadyStarted;
- startTime = CFAbsoluteTimeGetCurrent();
-
- if (callback) {
-
- params->resolution = resolution;
- params->callback = callback;
- params->userData = userData;
-
- pthread_create(&pthread_id, NULL, Pt_Thread, params);
- }
-
- time_started_flag = TRUE;
- return ptNoError;
-}
-
-
-PtError Pt_Stop()
-{
- printf("Pt_Stop called\n");
-
- CFRunLoopStop(timerRunLoop);
- time_started_flag = FALSE;
- return ptNoError;
-}
-
-
-int Pt_Started()
-{
- return time_started_flag;
-}
-
-
-PtTimestamp Pt_Time()
-{
- CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
- return (PtTimestamp) ((now - startTime) * 1000.0);
-}
-
-
-void Pt_Sleep(int32_t duration)
-{
- usleep(duration * 1000);
-}
diff --git a/portmidi/porttime/ptmacosx_mach.c b/portmidi/porttime/ptmacosx_mach.c deleted file mode 100755 index 1390af8..0000000 --- a/portmidi/porttime/ptmacosx_mach.c +++ /dev/null @@ -1,204 +0,0 @@ -/* ptmacosx.c -- portable timer implementation for mac os x */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <CoreAudio/HostTime.h>
-
-#import <mach/mach.h>
-#import <mach/mach_error.h>
-#import <mach/mach_time.h>
-#import <mach/clock.h>
-#include <unistd.h>
-#include <AvailabilityMacros.h>
-
-#include "porttime.h"
-#include "sys/time.h"
-#include "pthread.h"
-
-#ifndef NSEC_PER_MSEC
-#define NSEC_PER_MSEC 1000000
-#endif
-#define THREAD_IMPORTANCE 63
-
-// QOS headers are available as of macOS 10.10
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
-#include "sys/qos.h"
-#define HAVE_APPLE_QOS 1
-#else
-#undef HAVE_APPLE_QOS
-#endif
-
-static int time_started_flag = FALSE;
-static UInt64 start_time;
-static pthread_t pt_thread_pid;
-
-/* note that this is static data -- we only need one copy */
-typedef struct {
- int id;
- int resolution;
- PtCallback *callback;
- void *userData;
-} pt_callback_parameters;
-
-static int pt_callback_proc_id = 0;
-
-static void *Pt_CallbackProc(void *p)
-{
- pt_callback_parameters *parameters = (pt_callback_parameters *) p;
- int mytime = 1;
-
- kern_return_t error;
- thread_extended_policy_data_t extendedPolicy;
- thread_precedence_policy_data_t precedencePolicy;
-
- extendedPolicy.timeshare = 0;
- error = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY,
- (thread_policy_t)&extendedPolicy,
- THREAD_EXTENDED_POLICY_COUNT);
- if (error != KERN_SUCCESS) {
- mach_error("Couldn't set thread timeshare policy", error);
- }
-
- precedencePolicy.importance = THREAD_IMPORTANCE;
- error = thread_policy_set(mach_thread_self(), THREAD_PRECEDENCE_POLICY,
- (thread_policy_t)&precedencePolicy,
- THREAD_PRECEDENCE_POLICY_COUNT);
- if (error != KERN_SUCCESS) {
- mach_error("Couldn't set thread precedence policy", error);
- }
-
- // Most important, set real-time constraints.
-
- // Define the guaranteed and max fraction of time for the audio thread.
- // These "duty cycle" values can range from 0 to 1. A value of 0.5
- // means the scheduler would give half the time to the thread.
- // These values have empirically been found to yield good behavior.
- // Good means that audio performance is high and other threads won't starve.
- const double kGuaranteedAudioDutyCycle = 0.75;
- const double kMaxAudioDutyCycle = 0.85;
-
- // Define constants determining how much time the audio thread can
- // use in a given time quantum. All times are in milliseconds.
-
- // About 128 frames @44.1KHz
- const double kTimeQuantum = 2.9;
-
- // Time guaranteed each quantum.
- const double kAudioTimeNeeded = kGuaranteedAudioDutyCycle * kTimeQuantum;
-
- // Maximum time each quantum.
- const double kMaxTimeAllowed = kMaxAudioDutyCycle * kTimeQuantum;
-
- // Get the conversion factor from milliseconds to absolute time
- // which is what the time-constraints call needs.
- mach_timebase_info_data_t tb_info;
- mach_timebase_info(&tb_info);
- double ms_to_abs_time =
- ((double)tb_info.denom / (double)tb_info.numer) * 1000000;
-
- thread_time_constraint_policy_data_t time_constraints;
- time_constraints.period = (uint32_t)(kTimeQuantum * ms_to_abs_time);
- time_constraints.computation = (uint32_t)(kAudioTimeNeeded * ms_to_abs_time);
- time_constraints.constraint = (uint32_t)(kMaxTimeAllowed * ms_to_abs_time);
- time_constraints.preemptible = 0;
-
- error = thread_policy_set(mach_thread_self(),
- THREAD_TIME_CONSTRAINT_POLICY,
- (thread_policy_t)&time_constraints,
- THREAD_TIME_CONSTRAINT_POLICY_COUNT);
- if (error != KERN_SUCCESS) {
- mach_error("Couldn't set thread precedence policy", error);
- }
-
- /* to kill a process, just increment the pt_callback_proc_id */
- /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id,
- parameters->id); */
- while (pt_callback_proc_id == parameters->id) {
- /* wait for a multiple of resolution ms */
- UInt64 wait_time;
- int delay = mytime++ * parameters->resolution - Pt_Time();
- PtTimestamp timestamp;
- if (delay < 0) delay = 0;
- wait_time = AudioConvertNanosToHostTime((UInt64)delay * NSEC_PER_MSEC);
- wait_time += AudioGetCurrentHostTime();
- mach_wait_until(wait_time);
- timestamp = Pt_Time();
- (*(parameters->callback))(timestamp, parameters->userData);
- }
- free(parameters);
- return NULL;
-}
-
-
-PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
-{
- if (time_started_flag) return ptAlreadyStarted;
- start_time = AudioGetCurrentHostTime();
-
- if (callback) {
- int res;
- pt_callback_parameters *parms;
-
- parms = (pt_callback_parameters *) malloc(sizeof(pt_callback_parameters));
- if (!parms) return ptInsufficientMemory;
- parms->id = pt_callback_proc_id;
- parms->resolution = resolution;
- parms->callback = callback;
- parms->userData = userData;
-
-#ifdef HAVE_APPLE_QOS
- pthread_attr_t qosAttribute;
- pthread_attr_init(&qosAttribute);
- pthread_attr_set_qos_class_np(&qosAttribute,
- QOS_CLASS_USER_INTERACTIVE, 0);
-
- res = pthread_create(&pt_thread_pid, &qosAttribute, Pt_CallbackProc,
- parms);
-#else
- res = pthread_create(&pt_thread_pid, NULL, Pt_CallbackProc, parms);
-#endif
-
- struct sched_param sp;
- memset(&sp, 0, sizeof(struct sched_param));
- sp.sched_priority = sched_get_priority_max(SCHED_RR);
- if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == -1) {
- return ptHostError;
- }
-
- if (res != 0) return ptHostError;
- }
-
- time_started_flag = TRUE;
- return ptNoError;
-}
-
-
-PtError Pt_Stop(void)
-{
- /* printf("Pt_Stop called\n"); */
- pt_callback_proc_id++;
- pthread_join(pt_thread_pid, NULL);
- time_started_flag = FALSE;
- return ptNoError;
-}
-
-
-int Pt_Started(void)
-{
- return time_started_flag;
-}
-
-
-PtTimestamp Pt_Time(void)
-{
- UInt64 clock_time, nsec_time;
- clock_time = AudioGetCurrentHostTime() - start_time;
- nsec_time = AudioConvertHostTimeToNanos(clock_time);
- return (PtTimestamp)(nsec_time / NSEC_PER_MSEC);
-}
-
-
-void Pt_Sleep(int32_t duration)
-{
- usleep(duration * 1000);
-}
diff --git a/portmidi/porttime/ptwinmm.c b/portmidi/porttime/ptwinmm.c deleted file mode 100755 index 5204659..0000000 --- a/portmidi/porttime/ptwinmm.c +++ /dev/null @@ -1,70 +0,0 @@ -/* ptwinmm.c -- portable timer implementation for win32 */
-
-
-#include "porttime.h"
-#include "windows.h"
-#include "time.h"
-
-
-TIMECAPS caps;
-
-static long time_offset = 0;
-static int time_started_flag = FALSE;
-static long time_resolution;
-static MMRESULT timer_id;
-static PtCallback *time_callback;
-
-void CALLBACK winmm_time_callback(UINT uID, UINT uMsg, DWORD_PTR dwUser,
- DWORD_PTR dw1, DWORD_PTR dw2)
-{
- (*time_callback)(Pt_Time(), (void *) dwUser);
-}
-
-
-PMEXPORT PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
-{
- if (time_started_flag) return ptAlreadyStarted;
- timeBeginPeriod(resolution);
- time_resolution = resolution;
- time_offset = timeGetTime();
- time_started_flag = TRUE;
- time_callback = callback;
- if (callback) {
- timer_id = timeSetEvent(resolution, 1, winmm_time_callback,
- (DWORD_PTR) userData, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
- if (!timer_id) return ptHostError;
- }
- return ptNoError;
-}
-
-
-PMEXPORT PtError Pt_Stop()
-{
- if (!time_started_flag) return ptAlreadyStopped;
- if (time_callback && timer_id) {
- timeKillEvent(timer_id);
- time_callback = NULL;
- timer_id = 0;
- }
- time_started_flag = FALSE;
- timeEndPeriod(time_resolution);
- return ptNoError;
-}
-
-
-PMEXPORT int Pt_Started()
-{
- return time_started_flag;
-}
-
-
-PMEXPORT PtTimestamp Pt_Time()
-{
- return timeGetTime() - time_offset;
-}
-
-
-PMEXPORT void Pt_Sleep(int32_t duration)
-{
- Sleep(duration);
-}
|
