Skip to content

Commit

Permalink
Bug fix in sysex handling and incorporation of various minor changes …
Browse files Browse the repository at this point in the history
…for header guards and compiler warnings. Also, fixed mm (midi monitor) ability to filter messages and control output. Details: The interface between system-dependent and system-independent code was originally based on the idea that the system did a lot of work to parse out messages, so it should be simple to push these messages to the PortMidi input queue. This should have avoided re-parsing MIDI bytes and enabled fast word-at-a-time handling of sysex data. However, MacOS MIDI handling is largely undocumented and has many complexities: packets can contain multiple messages or partial (sysex) messages, messages can have embedded 1-byte real-time messages. To handle these special cases, code became messy and to add to the complexity, the mess was split between system-dependent (mmmacosxcm.c) and system-independent (portmidi.c) code. Even after years, a new bug was found by te-johan, and there may be other problems with embedded real-time messages. Rather than continuing to patch the code, this commit greatly simplifies code by completely reparsing MIDI in system-independent code, but retains the option of passing in fully formed MIDI short messages.
  • Loading branch information
rbdannenberg committed Mar 24, 2024
1 parent 928520f commit dba8357
Show file tree
Hide file tree
Showing 18 changed files with 412 additions and 501 deletions.
7 changes: 0 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ endif()
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})

option(BUILD_PMDEFAULTS
"build the legacy Java application PmDefaults" OFF)
option(BUILD_JAVA_NATIVE_INTERFACE
"build the Java PortMidi interface library" OFF)

Expand All @@ -92,11 +90,6 @@ if(BUILD_JAVA_NATIVE_INTERFACE)
set(PMJNI_IF_EXISTS "pmjni") # used by INSTALL below
else(BUILD_JAVA_NATIVE_INTERFACE)
set(PMJNI_IF_EXISTS "") # used by INSTALL below
if(BUILD_PMDEFAULTS)
message(FATAL_ERROR
"Cannot build PM_Defaults program (BUILD_PM_DEFAULTS) without option "
"BUILD_JAVA_NATIVE_INTERFACE")
endif(BUILD_PMDEFAULTS)
endif(BUILD_JAVA_NATIVE_INTERFACE)


Expand Down
156 changes: 89 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,82 +24,104 @@ make # compile sources and build PortMidi library
sudo make install # if you want to install to your system
```

**PmDefaults** is a Java-based program for setting default MIDI
devices. It is necessary if you use `Pm_DefaultInputDeviceID()` or
`Pm_DefaultOutputDeviceID()` to avoid implementing your own device
browsing, selection and preferences in your applications. Enable
`BUILD_PMDEFAULTS` and `BUILD_JAVA_NATIVE_INTERFACE` in ccmake, and
see `pm_java/README.txt` for more information.

See also notes in `pm_mac/README_MAC.txt`, `pm_win/README_WIN.txt` and
`pm_linux/README_LINUX.txt`.

## What's New?

PortMidi has some fixes for Apple M1 cpus as of May 23, 2022. This has not yet
been formally released to allow for further testing, but please use the latest
code if you want to run on an M1.

PortMidi has some changes in 2021:

- added Pm_CreateVirtualInput() and Pm_CreateVirtualOutput() functions that allow
applications to create named ports analogous to devices.

- improvements for macOS CoreMIDI include higher data rates for devices, better
handling of Unicode interface names in addition to virtual device creation.

- the notion of default devices, Pm_GetDefaultInputDeviceID(),
Pm_GetDefaultOutputDeviceID and the PmDefaults program have fallen into disuse
and are now deprecated.

- Native Interfaces for Python, Java, Go, Rust, Lua and more seem best left
to individual repos, so support within this repo has been dropped. A Java
interface is still here and probably usable -- let me know if you need it
and/or would like to help bring it up to date. I am happy to help with,
link to, or collaborate in supporting PortMidi for other languages.

For up-to-date PortMidi for languages other than C/C++, check with
developers. As of 27 Sep 2021, this (and SourceForge) is the only repo with
the features described above.
## 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.
- [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)
- [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:
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/PortMidi/portsmf).
A Standard MIDI File (SMF) (and more) library is in the [portsmf
repository](https://github.com/PortMidi/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.
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.
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.
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.
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.
12 changes: 7 additions & 5 deletions pm_common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,13 @@ if(NOT WIN32)
endif()

# Check for 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)
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)
Expand Down
9 changes: 6 additions & 3 deletions pm_common/pminternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,10 @@ typedef struct pm_internal_struct {
* 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 sysex_message; /* buffer for 4 bytes of sysex data */
int sysex_message_count; /* how many bytes in sysex_message so far */

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 */
Expand All @@ -148,6 +149,8 @@ typedef struct pm_internal_struct {
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);
Expand Down
5 changes: 5 additions & 0 deletions pm_common/pmutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
available for other uses.
*/

#ifndef PORTMIDI_PMUTIL_H
#define PORTMIDI_PMUTIL_H

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
Expand Down Expand Up @@ -177,3 +180,5 @@ PMEXPORT PmError Pm_SetOverflow(PmQueue *queue);
#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif // PORTMIDI_PMUTIL_H
Loading

0 comments on commit dba8357

Please sign in to comment.