.\" $NetBSD: midi.4,v 1.32 2017/07/03 21:30:58 wiz Exp $
.\"
.\" Copyright (c) 1999-2017 The NetBSD Foundation, Inc.
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to The NetBSD Foundation
.\" by Lennart Augustsson and Chapman Flack.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd April 28, 2017
.Dt MIDI 4
.Os
.Sh NAME
.Nm midi
.Nd device-independent MIDI driver layer
.Sh SYNOPSIS
.Cd "midi* at midibus?"
.Cd "midi* at pcppi?"
.Cd "pseudo-device sequencer"
.Pp
.In sys/types.h
.In sys/midiio.h
.Sh DESCRIPTION
The
.Nm
driver is the machine independent layer over anything that can
source or sink a
.Tn MIDI
data stream, whether a physical
.Tn "MIDI IN"
or
.Tn "MIDI OUT"
jack on a soundcard, cabled to some external synthesizer or input controller,
an on-board programmable tone generator, or a single jack, synthesizer, or
controller component within a complex
.Tn USB
or
.Tn IEEE1394
.Tn MIDI
device that has several
such components and appears as several
.Tn MIDI
streams.
.Ss Concepts
One
.Tn MIDI
data stream is a unidirectional stream of
.Tn MIDI
messages, as could be carried over one
.Tn MIDI
cable in the
.Tn "MIDI 1.0"
specification.
Many
.Tn MIDI
messages
carry a four-bit channel number, creating up to 16
.Tn MIDI
channels within a single
.Tn MIDI
stream.
There may be multiple consumers of a
.Tn MIDI
stream, each configured
to react only to messages on specific channels; the sets of channels different
consumers react to need not be disjoint.
Many modern devices such as multitimbral keyboards and tone generators
listen on all 16 channels, or may be viewed as collections of 16
independent consumers each listening on one channel.
.Tn MIDI
defines some messages that take no channel number, and
apply to all consumers of the
stream on which they are sent.
For an inbound stream,
.Nm
is a promiscuous
receiver, capturing all messages regardless of channel number.
For an outbound stream, the writer can specify a channel number
per message; there is no notion of binding the stream to one
destination channel in advance.
.Pp
A single
.Nm
device instance is the endpoint of one outbound stream, one
inbound stream, or one of each.
In the third case, the write and read sides are independent
.Tn MIDI
streams.
For example, a soundcard driver may map its
.Tn "MIDI OUT"
and
.Tn "MIDI IN"
jacks to the write and read sides of a single device instance, but
those jacks can be cabled to completely different pieces of gear.
Information from
.Xr dmesg 8 ,
and a diagram of any external
.Tn MIDI
cabling, will help clarify the mapping.
.Ss "Underlying drivers and MIDI protocol"
Drivers
.Nm
can attach include soundcard drivers, many of
which support a
.Tn UART
resembling Roland's
.Tn MPU401
and handled by
.Xr mpu 4 ,
.Tn "USB MIDI"
devices via
.Xr umidi 4 ,
and on-board devices that can make sounds,
whether a lowly
.Tn PC
speaker or a Yamaha
.Tn OPL .
Serial port and
.Tn IEEE1394
connections are currently science fiction.
.Pp
The
.Tn MIDI
protocol permits some forms of message compression such as running
status and hidden note-off.
Received messages on inbound streams are always canonicalized by
.Nm
before presentation to higher layers.
Messages for transmission are accepted by
.Nm
in any valid form.
.Ss "Device access"
Access to
.Nm
device instances can be through the raw device nodes,
.Pa /dev/rmidiN ,
or through the sequencer,
.Pa /dev/music .
.Ss "Raw MIDI access"
A
.Pa /dev/rmidiN
device supports
.Xr read 2 ,
.Xr write 2 ,
.Xr ioctl 2 ,
.Xr select 2 Ns / Ns
.Xr poll 2
and the corresponding
.Xr kevent 2
filters,
and may be opened
only when it is not already open.
It may be opened in
.Dv O_RDONLY ,
.Dv O_WRONLY ,
or
.Dv O_RDWR
mode, but a later
.Xr read 2
or
.Xr write 2
will return \-1 if the device has no associated
input or output stream, respectively.
.Pp
Bytes written are passed as quickly as possible to the underlying driver
as complete
.Tn MIDI
messages; a maximum of two bytes at the end of a
.Xr write 2
may remain buffered if they do not complete a message, until
completed by a following
.Xr write 2 .
.Pp
A
.Xr read 2
will not block or return
.Er EWOULDBLOCK
when it could immediately return any nonzero count, and
.Tn MIDI
messages received are available to
.Xr read 2
as soon as they are complete,
with a maximum of two received bytes remaining buffered if they do not
complete a message.
.Pp
As all
.Tn MIDI
messages are three bytes or fewer except for System Exclusive,
which can have arbitrary length, these rules imply that System Exclusive
messages are the only ones of which some bytes can be delivered before
all are available.
.Pp
System Realtime messages are passed with minimum delay in either direction,
ahead of any possible buffered incomplete message.
As a result, they will never interrupt any
.Tn MIDI
message except possibly System Exclusive.
.Pp
A
.Xr read 2
with a buffer large enough to accommodate the first complete
message available will be satisfied with as many complete messages as
will fit.
A buffer too small for the first complete
message will be filled to capacity.
Therefore, an application that reads from an
.Pa rmidi
device with buffers of three bytes or larger need never parse
across read boundaries to assemble a received message, except possibly in
the case of a System Exclusive message.
However, if the application reads through a buffering layer such as
.Xr fread 3 ,
this property will not be preserved.
.Pp
The
.Nm
driver itself supports the
.Xr ioctl 2
operations
.Dv FIOASYNC ,
.Dv FIONBIO ,
and
.Dv FIONREAD .
Underlying devices may support others.
The value returned for
.Dv FIONREAD
reflects the size in bytes of complete messages
(or System Exclusive chunks) ready to read.
If the
.Xr ioctl 2
returns
.Va n
and a
.Xr read 2
of size
.Va n
is issued,
.Va n
bytes will be read, but if a
.Xr read 2
of
size
.Va m
<
.Va n
is issued, fewer than
.Va m
bytes may be read if
.Va m
does not fall
on a message/chunk boundary.
.Pp
Raw
.Tn MIDI
access can be used to receive bulk dumps from synthesizers, download
bulk data to them, and so on.
Simple patching of one device to another can be
done at the command line, as with
.Dl $ cat -u 0<>/dev/rmidi0 1>&0
which will loop all messages received on the input stream of
.Pa rmidi0
input stream back to its output
stream in real time.
However, an attempt to record and play back music with
.Dl $ cat /dev/rmidiN >foo; cat foo >/dev/rmidiN
will be disappointing.
The file
.Pa foo
will contain all of the notes that were played, but because
.Tn MIDI
messages carry
no explicit timing, the
.Sq "playback"
will reproduce them all at once, as fast as
they can be transmitted.
To preserve timing information, the sequencer device can be used.
.Ss "Active Sensing"
The
.Tn MIDI
protocol includes a keepalive function called Active Sensing.
In any receiver that has
.Em not
received at least one Active Sense
.Tn MIDI
message, the
feature is suppressed and no timeout applies.
If at least one such message has
been received, the lapse of any subsequent 300 ms interval without receipt of
any message reflects loss of communication, and the receiver should silence any
currently sounding notes and return to non-Active-Sensing behavior.
A sender using Active Sensing generally avoids 300 ms gaps in
transmission by sending Active Sense messages (which have no other
effect) as needed when there is no other traffic to send in the
interval.
This feature can be important for
.Tn MIDI ,
which relies on separate Note On and Note Off messages, to avoid notes stuck on
indefinitely if communication is interrupted before a Note Off
message arrives.
.Pp
This protocol is supported in
.Nm .
An outbound stream will be kept alive
by sending Active Sense messages as needed, beginning after any real
traffic is sent on the stream, and continuing until the stream is closed.
On an inbound stream, if any Active Sense has been received, then a process
reading an
.Pa rmidi
device will see an end-of-file indication if the input timeout elapses.
The stream remains open, the driver reverts to enforcing no timeout, and the
process may continue to read for more input.
Subsequent receipt of an
Active Sense message will re-arm the timeout.
As received Active Sense messages are handled by
.Nm ,
they are not included among messages read from the
.Pa /dev/rmidiN
device.
.Pp
These rules support end-to-end Active Sensing behavior in simple cases
without special action in an application.
For example, in
.Dl $ cat -u /dev/rmidi0 >/dev/rmidi1
if the input stream to
.Pa rmidi0
is lost, the
.Xr cat 1
command exits; on the
.Xr close 2
of
.Pa rmidi1 ,
.Nm
ceases to send Active Sense messages, and the receiving
device will detect the loss and silence any outstanding notes.
.Ss "Access through the sequencer"
To play music using the raw
.Tn MIDI
.Tn API
would require an application to
issue many small writes with very precise timing.
The sequencer device,
.Pa /dev/music ,
can manage the timing of
.Tn MIDI
data in the kernel, to avoid
such demanding real-time constraints on a user process.
.Pp
The
.Pa /dev/music
device can be opened only when it is not already open.
When opened, the sequencer internally opens all
.Tn MIDI
instances existing
in the system that are not already open at their raw nodes; any attempts
to open them at their raw nodes while the sequencer is open will fail.
All access to the corresponding
.Tn MIDI
streams will then be through the
sequencer.
.Pp
Reads and writes of
.Pa /dev/music
pass eight-byte event structures defined in
.In sys/midiio.h
(which see for their documentation and examples of use).
Some events correspond to
.Tn MIDI
messages, and carry an integer
.Va device
field to identify one of the
.Tn MIDI
devices opened by the sequencer.
Other events carry timing information interpreted or generated by the
sequencer itself.
.Pp
A message received on an input stream is wrapped in a sequencer event
along with the device index of the stream it arrived on, and queued for
the reader of
.Pa /dev/music .
If a measurable time interval passed since the
last preceding message, a timing event that represents a delay for that interval
is queued ahead of the received event.
The sequencer handles output events by
interpreting any timing event, and routing any
.Tn MIDI
message event at the proper time to
an underlying output stream according to its
.Va device
index.
Therefore
.Dl $ cat /dev/music >foo; cat foo >/dev/music
can be expected to capture and reproduce an input performance including
timing.
.Pp
The process of playing back a complex
.Tn MIDI
file is illustrated below.
The file may contain several tracks\(emfour, in this example\(emof
.Tn MIDI
events, each marked with a device index and a time stamp, that may
overlap in time.
In the example,
.Va a ,
.Va b ,
and
.Va c
are device indices of
the three output
.Tn MIDI
streams; the left-hand digit in each input event represents a
.Tn MIDI
channel on the selected stream, and the right-hand digit represents
a time for the event's occurrence.
As illustrated, the input tracks are not firmly associated with
output streams; any track may contain events for any stream.
.Bd -literal
| | a2|4 |
a0|3 | c1|3 c0|3
| b0|2 b1|2 |
| b1|1 | c0|1
a0|0 | b0|0 |
v v v v
+---------------------------+
| merge to 1 ordered stream |
| user code, eg midiplay(1) |
+---------------------------+
b1|2
b0|2
c0|1
b1|1
b0|0
a0|0
v
_______+-------------+_______user
| /dev/music | kernel
| (sequencer) |
+-------------+
| 1 0
+-----' | '-----.
0 0 |
v v v
+-------+ +--------+ +---------+
|midi(4)| |midi(4) | |midi(4) |
|rmidia | |rmidib | |rmidic |
+-------+ +--------+ +---------+
| mpu(4)| |umidi(4)| |midisyn |
+-------+ +--------+ +---------+
| HW | | | opl(4) |
| MIDI | U +---------+
| UART | S | internal|
+-------+ B | tone |
| | |generator|
v | +---------+
external v
MIDI device external
MIDI device
.Ed
.Pp
A user process must merge the tracks into a single stream of sequencer
.Tn MIDI
and timing events in order by desired timing.
The sequencer obeys the timing events and distributes the
.Tn MIDI
events to the three destinations,
in this case two external devices connected to a sound card
.Tn UART
and a
.Tn USB
interface, and an
.Tn OPL
tone generator on a sound card.
.Sh NOTES
Use of
.Xr select 2 Ns / Ns
.Xr poll 2
with the sequencer is supported, however, there is no guarantee that a
.Xr write 2
will not block or return
.Er EWOULDBLOCK
if it begins with a timer-wait event, even if
.Xr select 2 Ns / Ns
.Xr poll 2
reported the sequencer writable.
.Pp
The delivery of a realtime message ahead of buffered bytes of an incomplete
message may cause the realtime message to seem, in a saved byte stream, to have
arrived up to 640 us earlier than it really did, at
.Tn MIDI
1.0 data rates.
Higher data rates make the effect less significant.
.Pp
Another sequencer device,
.Pa /dev/sequencer ,
is provided only for backward
compatibility with an obsolete
.Tn OSS
interface in which some sequencer events
were four-byte records.
It is not further documented here, and the
.Pa /dev/music
.Tn API
should be used in new code.
The
.Pa /dev/sequencer
emulation is implemented only for writing, and that might not be complete.
.Sh IMPLEMENTATION NOTES
Some hardware devices supporting
.Nm
lack transmit-ready interrupts, and some have the capability in
hardware but currently lack driver support.
They can be recognized by the annotation
.Li "(CPU-intensive output)"
in
.Xr dmesg 8 .
While suitable for music playback, they may have an objectionable impact on
system responsiveness during bulk transmission such as patch downloads, and
are best avoided for that purpose if other suitable devices are present.
.Pp
Buffer space in
.Nm
itself is adequate for about 200 ms of traffic at
.Tn "MIDI 1.0"
data rates, per stream.
.Pp
Event counters record bytes and messages discarded because of protocol
errors or buffer overruns, and can be viewed with
.Li "vmstat -e" .
They can be useful in diagnosing flaky cables and other communication
problems.
.Pp
.\" These two paragraphs really belong not here but in a midi(9) man page,
.\" which should one day exist.
A raw sound generator uses the
.Sy midisyn
layer to present a
.Tn MIDI
message-driven interface attachable by
.Nm .
.Pp
While
.Nm
accepts messages for transmission in any valid mixture of compressed
or canonical form, they are always presented to an underlying driver
in the form it prefers.
Drivers for simple
.Tn UART Ns -like
devices
register their preference for a compressed byte stream, while those like
.Xr umidi 4 ,
which uses a packet protocol, or
.Sy midisyn ,
which interprets complete
messages, register for intact canonical messages.
This design eliminates the
need for compression and canonicalization logic from all layers above and below
.Nm
itself.
.Sh FILES
.Bl -tag -width /dev/sequencer -compact
.It Pa /dev/rmidiN
.It Pa /dev/music
.It Pa /dev/sequencer
.El
.Sh ERRORS
In addition to other errors documented for the
.Xr write 2
family of system calls,
.Er EPROTO
can be returned if the bytes to be written on a raw
.Nm
device would violate
.Tn MIDI
protocol.
.Sh SEE ALSO
.Xr midiplay 1 ,
.Xr midirecord 1 ,
.Xr ioctl 2 ,
.Xr ossaudio 3 ,
.Xr audio 4 ,
.Xr mpu 4 ,
.Xr opl 4 ,
.Xr umidi 4
.Pp
For ports using the ISA bus:
.Xr cms 4 ,
.Xr pcppi 4 ,
.Xr sb 4
.Pp
For ports using the PCI bus:
.Xr autri 4 ,
.Xr clcs 4 ,
.Xr eap 4
.Sh HISTORY
The
.Nm
driver first appeared in
.Nx 1.4 .
It was overhauled and this manual page rewritten for
.Nx 4.0 .
.Sh BUGS
Some
.Tn OSS
sequencer events and
.Xr ioctl 2
operations are unimplemented, as
.In sys/midiio.h
notes.
.Pp
.Tn OSS
source-compatible sequencer macros should be added to
.In sys/soundcard.h ,
implemented with the
.Nx
ones in
.In sys/midiio.h ,
so sources written for OSS can be easily compiled.
.Pp
The sequencer blocks (or returns
.Er EWOULDBLOCK )
only when its buffer physically fills, which can represent an arbitrary
latency because of buffered timing events.
As a result, interrupting a process writing the sequencer may not
interrupt music playback for a considerable time.
The sequencer could enforce a reasonable latency bound
by examining timing events as they are enqueued and blocking appropriately.
.Pp
.Dv FIOASYNC
enables signal delivery to the calling process only;
.Dv FIOSETOWN
is not supported.
.Pp
The sequencer can only be a timing master, but does not send timing messages
to synchronize any slave device; it cannot be slaved to timing messages
received on any interface (which would presumably require a PLL algorithm
similar to NTP's, and expertise in that area to implement it).
The sequencer ignores timing messages received on any interface
and does not pass them along to the reading process, and the OSS
operations to change that behavior are unimplemented.
.Pp
The
.Dv SEQUENCER_TMR_TIMEBASE
.Xr ioctl 2
will report successfully setting any
timebase up to ridiculously high resolutions, though the actual
resolution, and therefore jitter, is constrained by
.Xr hz 9 .
Comparable sequencer implementations typically allow a selection from available
sources of time interrupts that may be programmable.
.Pp
The device number in a sequencer event is treated on
.Xr write 2
as index into
the array of
.Tn MIDI
devices the sequencer has opened, but on
.Xr read 2
as the
unit number of the source
.Tn MIDI
device; these are usually the same if the
sequencer has opened all the
.Tn MIDI
devices (that is, none was already open
at its raw node when the sequencer was opened), but might not be the same
otherwise.
.Pp
There is at present no way to make reception nonpromiscuous,
should anyone have a reason to want to.
.Pp
There should be ways to override default Active Sense behavior.
As one obvious
case, if an application is seen to send Active Sense explicitly,
.Nm
should refrain
from adding its own.
On receive, there should be an option to pass Active Sense through
rather than interpreting it, for apps that wish to handle or ignore it
themselves and never see
.Dv EOF .
.Pp
When a
.Nm
stream is open by the sequencer, Active Sense messages received on the stream
are passed to the sequencer and not interpreted by
.Nm .
The sequencer at present neither does anything itself with Active Sense
messages received, nor supports the
.Tn OSS
.Tn API
for making them available to the user process.
.Pp
System Exclusive messages can be received by reading a raw device, but
not by reading the sequencer; they are discarded on receipt when the
stream is open by the sequencer, rather than being presented as the
OSS-defined sequencer events.
.Pp
.Sy midisyn
is too rudimentary at present to get satisfactory results from any
onboard synth.
It lacks the required special interpretation of the
General
.Tn MIDI
percussion channel in GM mode.
More devices should be supported; some sound cards with synthesis
capability have
.Nx
drivers that implement the
.Xr audio 4
but not the
.Sy midisyn
interface.
Voice stealing algorithm does not follow the General
.Tn MIDI
Developer Guidelines.
.Pp
.Tn ALSA
sequencer compatibility is lacking, but becoming important to applications.
It would require the function of merging multiple tracks into a single ordered
stream to be moved from user space into the sequencer.
Assuming the sequencer driven by periodic interrupts, timing wheels
could be used as in
.Xr hardclock 9
itself.
Similar functionality will be in OSS4; with the right infrastructure
it should be possible to support both.
When merging
.Tn MIDI
streams, a notion of transaction
is needed to group critical message sequences.
If
.Tn ALSA
or
.Tn OSS4
have no such notion, it should be provided as an upward-compatible
extension.
.Pp
I would rather have
.Xr open 2
itself return an error (by the POSIX description
.Er ENODEV
looks most appropriate) if a read or write mode
is requested that is not supported by the instance, rather than letting
.Xr open 2
succeed and
.Xr read 2
or
.Xr write 2
return \-1, but so help me, the latter seems
the more common
.Ux
practice.