
1.2.1 Client applications
There are roughly 4 categories of applications for clients:
The other categories are more specific to the ALSA sequencer, and are needed (especially the device drivers) to get any sound out of it.
1.2.2 Client interfaces
There exist two type of interfaces for clients: a user-land interface and a
kernel mode interface. Which one is to be used depends on the place the
client is executing.
Each type has its own strengths and weaknesses:
Efficient blocking I/O (ie. no polling) can be performed by using the select() call for I/O multiplexing.
This /dev/sndseq device has to support being opened r/w by multiple concurrent clients, much like pty's etc. If this is not possible to implement, we can revert to simply using a whole range of character devices /dev/sndseq0 ... /dev/sndseq254 for each possible client connection (ugly!), and make it a library call to allocate a free client.
Other possibility is to implement an interface to multiple clients using multiple open of /dev/sndseq file. The open() sys-call opens only sequencer which isn't connected to any event queue / timer / kernel clients. Ioctl calls will provide these connections. Resources (event queue, clients) should be identified with some key (probably string) which allows to application specify exact resource which want create / connect. For security some scheme has to be added to prevent application accessing clients that belong to other applications. Per client I/O blocking and read()/write() is not possible in this scenario???
Comments please...
Latest news:
I just wrote a test program, and found out that multiple open of /dev/sndseq works great. The file structure passed on all operation (read/write/ioctl etc) allows us to set a private_data field, to identify this client connection. The user-land interface will exist of one /dev/sndseq device that can be opened by all the user-land clients. Each time a client opens the /dev/sndseq the sequencer connects it to a free client slot.
The presence of multiple user-land applications allows for some nice features:
The module level interface exists of following:
Example:
Midi input driver receives midi bytes (interrupt driven), once a complete
midi message is received, it is send to the sequencer. This event (which has
current timestamp) goes directly to the clients that requested reception of
the note events. If there is a kernel mode midi thru module, it is directly
called, the midi event is perhaps transformed (swapped channel/port), and
send back to the sequencer, where it's directly dispatched to the midi (or
synth) client, which plays the event. Nice low-latency midi patch-bay!
Because midi runs at 31.250 kbps, it will take 0.96 ms to receive a note on
message (without running state). Sending this event will also take 0.96 ms,
so the event arrives 1.92 ms later at the playback device, which is faster
than the typical delay within synthesizers before a sound is produced.
Other applications (apart from drivers) could be support modules for high-end sequencers, that need fine-graded real-time control.
1.2.5 Client communication
The event passing mechanism is well suited for real-time controls, note
events etc. But to access very specific functions of a device (client) like
for instance downloading samples to a sample player, or changing the
microcode for a DSP or on-board processor a different interface will have to
be provided. Make this a special 'for the device or application', or use the
sequencer as a multiplexer to pass the data to the driver.
Perhaps large data streams (samples!) can be sent directly to the client, bypassing the priority queue. The data can be encapsulated in messages, and transmitted as a packetised stream.
1.2.6 MIDI ports
Note that there is no such thing as a MIDI port, synth device or mixer in
this picture. The idea behind this is that a driver for a midi port should
be implemented as a 'kernel module'. This 'MIDI Port' module can of course
be part of a low-level midi port, and register is from there. There is no
need to have both a MIDI low-level module and a MIDI sequencer interface
module.
To prevent the misery of stuck notes a low-level midi driver should keep an image of which notes are active. In case a note\_off message is missed (which is an application error!) the driver can shut the notes when asked (all note off message).
The presence of a priority queue also offers the opportunity to process NOTE events what come with an length. On reception of such an event (by eg. a midi driver client), the note can be started and the corresponding note off can be enqueued. Using this facility the chance of hanging notes because of abrupt abortion of a midi player will be reduced to 0.
1.2.7 OSS Compatibility
Because almost every MIDI application that exists for Unix makes use of the
OSS /dev/sequencer or /dev/music interface, it's a must to provide backwards
compatibility for these applications. Users then can use the new sequencer
engine, while keeping their old applications. The application writers then
can migrate to the new sequencer and make use of improved capabilities.
To achieve the compatibility a wrapper for /dev/sequencer and /dev/music devices has to be implemented (as a loadable module). This wrapper can simply map the OSS events to the corresponding sequencer calls.
1.2.8 Using OSS as the workhorse
At the time of writing for this document, the only currently available midi
and synth driver is the OSS. If a client task is developed that presents
itself to the sequencer core as a bunch of input and output device, and
simply does read/writes to the OSS /dev/music interface (perhaps directly
call the exported functions); a sequencer can be developed and used while
using the OSS as workhorse / low-level driver.
| Version 0.036, April 2nd, 1999 | Usage: |
| Copyright (c) 1998 by Frank van de Pol, Netherlands | Advanced Linux Sound Architecture |