Roadmap¶
This will be developed into a proper roadmap but for now it’s more of a list of ideas.
Various Improvements to MIDI File Parsing¶
- add
mido.exceptions.MidiParseError
. - add better error handling to MIDI file parser as discussed in issue #63.
- support RIFF MIDI files (issue #43)
- support MIDI files that end in empty meta message (issue #42)
Better Support for Concurrency and Multithreading¶
Mido was not originally designed for multithreading. Locks were added to the port base classes as an attempt to get around this but it is a crude solution that has created numerous problems and headaches.
The RtMido backend has abandoned locking in favor of using RtMidi’s
user callback to feed a queue. If you write your port so that
send()
, receive()
and poll()
are thread safe the rest of
the rest of the API will be as well.
For ports that do actual I/O (MIDI devices, sockets, files, pipes
etc.) it is always best for the port itself to ensure thread
safety. It’s less clear what to do for utility ports like
MultiPort
.
Mido is currently not very good at multiplexing input. You can use
MultiPort
and multi_receive()
, but since it can’t actually
block on more than one port it uses poll and wait. This uses more
resources and adds latency.
The alternative is to use callbacks, but only a few backends support (and some like PortMidi fake them with a thread that polls and waits, taking you back to square one). Programming with callbacks also forces you to deal with multithreading (since the callback runs in a different thread) and to break your program flow up into callback handlers. This is not always desirable.
In Go the solution would be to use channels instead of ports. Each port would then have its own channel with a goroutine reading from the device and feeding the channel, and the language would take care of the multiplexing. I am not sure how one would achieve something like this in Python.
Making Messages Immutable¶
See: https://github.com/mido/mido/issues/36
The current workaround is frozen messages (mido.freeze
).
In any case, the documentation should be updated to encourage copying over mutation.
Native Backends (ALSA, JACK, CoreMIDI, Windows MIDI)¶
No Default Backend?¶
Currently one backend is chosen as the default. Perhaps it would be
better to require the user to specify the backend with
$MIDO_BACKEND
or mido.set_backend()
.
New API for Creating New Port Types¶
The current system uses multiple inheritance making the code very hard to follow and reason about:
- too much magic and too much you need to keep in your head
- attributes like
self.name
andself.closed
appear in your name space and you have to dig around in the base classes to see where they come from - there is a
self._parser
in every port even if you don’t need it self._parser
is not thread safeBaseInput.receive()
needs to account for all the numerous current and historical behaviors ofself._receive()
.- blocking and nonblocking receive can not be protected by the same
lock (since a call to
receive()
would then block a call topoll()
), which means ports that due true blocking need to do their own locking. - if you want to do our own locking you need to remember to set
_locking=False
. This will replace the lock with a dummy lock, which while doing nothing still adds a bit of overhead.
A good principle is for any part of the code to know as little as possible about the rest of the code. For example a backend port should only need to worry about:
- opening and closing the device
- reading and writing data (blocking and nonblocking)
It should not have to worry about things like autoreset
,
closed=True/False
and iteration. Also, as long as its send()
and receive()/poll()
methods are thread safe the rest of the API
will be as well.
Some alternatives to subclassing:
- embedding: write a basic port and wrap it in a
FancyPort
which provides the rest of the API - mixins: write a basic port and use mixins (
PortMethods
,InputMethods
,OutputMethods
) to import the rest of the API.
Maybe¶
add a way to convert between MIDI file types as suggested in issue #92.
RtMidi backend: allow user to list ports without client name and ALSA port numbers.
Add native backends? See https://github.com/mido/mido-native-backends
Currently all backends ignore
active_sensing
messages because they create a lot of noise and are usually not very useful. Should this be changed (perhaps as an option)?Filtering can be turned off with:
- rtmidi:
self._rt.ignore_types(False, False, False)
- portmidi:
pm.lib.Pa_SetFilter(self._stream, 0)
- rtmidi_python:
self._rt.ignore_types(False, False, False)
- pygame: (is there a way to configure this?)
- amidi: (not sure if this receives
active_sensing
already)
- rtmidi:
Refactor
rtmidi
andrtmidi_python
backends to avoid code duplication. This would givertmidi_python
all of the features ofrtmidi
(as long as they are supported in the package).Add more fine grained error types, for example
PortNotFound
instead of justIOError
. (This should be a subclass so old code still works.) One problem here is that new code that usesPortNotFound
would not work with backends that raiseIOError
.