... I have a draft, or part thereof.  It is in similar spirit to the
module overviews in the API reference, and I hope it to be of some
use, if not optimal.

I was going to post this earlier, but a) it's written in a weird XML
schema, in XSLTing which to HTML I have not had much success, b) I've
been somewhat ill recently, and c) I was hesitant to do so for unknown
reasons.

It should probably be integrated with the doxygeneratees in some form;
my initial impulse was to convert it to Texinfo, but that may not be
the best of ideas.  Comments are appreciated.  No warranty or
guarantee of any sort is made, naturally.

Attached: alsa-seq.fooml, 13 KB, XML.

   ---> Drake Wilson
?xml version="1.0" encoding="utf-8"?> <!-- -*- XML -*- -->
<!DOCTYPE document SYSTEM "fooml.dtd">
<document xml:lang="en-US">
<title>ALSA 0.9.x Sequencer Interface Overview</title>
<version>Draft 0.0.0</version>
<author>Drake Wilson</author>
<copy>Copyright &copy; 2003 Drake Wilson.  Permission is granted to
copy, distribute and/or modify this document under the terms of the
GNU Free Documentation License, Version 1.1 or any later version
published by the Free Software Foundation, with no Invariant Sections,
no Front-Cover Texts, and no Back-Cover Texts.  A copy of the License
may be found at <xref uri="http://www.gnu.org/licenses/fdl.html"; />.</copy>

<section id="open_close"><name>Opening and Closing</name>

<par>In order to use the ALSA sequencer, one must first open it.  This
is done with a call to <fun>snd_seq_open</fun>, giving the name of
the sequencer device one wishes to open and the mode thereof,
similarly to an <fun>open</fun> call.  Closing the device is
accomplished with <fun>snd_seq_close</fun>, which is
similarly similar to <fun>close</fun>.  The following constants are
used for opening the sequencer device:</par>

<tablei idx="const_idx" elt="const">
<ti><til>SND_SEQ_OPEN_OUTPUT</til><tir>open for output only</tir></ti>
<ti><til>SND_SEQ_OPEN_INPUT</til><tir>open for input only</tir></ti>
<ti><til>SND_SEQ_OPEN_DUPLEX</til><tir>open for both input and
output</tir></ti>
<ti><til>SND_SEQ_NONBLOCK</til><tir>make I/O non-blocking; see also
<fun>snd_seq_nonblock</fun></tir></ti>
</tablei>

</section>
<section id="clients_ports"><name>Clients and Ports</name>

<par>In ALSA, each connection to a sequencer device is represented as a
client.  Usually, a process only connects to a sequencer device once,
and thus has only one client representation.  Each is given a client
ID, an unsigned one-byte integer (between 0 and 255 inclusive).  Each
client may have zero or more ports open; each port within a client is
given a port ID, another unsigned one-byte integer.  Client and port
IDs are fixed and are assigned by the ALSA system.  Generally,
user-space clients start at 128, MIDI device clients start at 64, and
system clients start at 0.  Each client and each port has a name,
which is an ASCII string.  Normally, ports are given names when they
are created; clients are given a default name when they are created,
which is changed later with <fun>snd_seq_set_client_name</fun>.</par>

<par>A client ID and a port ID together comprise a sequencer address,
stored as a <type>snd_seq_addr_t</type>,
with respective <code>client</code> and <code>port</code> fields.
Usually, when an address is written as a string, it is written as
client, then port, separated by either a colon or period.  The port
number is given in decimal; the client may be either given as a
decimal number or a name string.  The function
<fun>snd_seq_parse_address</fun>
parses an address in this format from ASCII string into
<type>snd_seq_addr_t</type> structure.</par>

<par>The easiest way to create and delete ports is to use the
<fun>snd_seq_create_simple_port</fun> and
<fun>snd_seq_delete_simple_port</fun> functions.  When creating a port
in this manner, one specifies a port name, as well as capabilities and
type.  The capabilities associated with a port describe what
permissions the host client has with the port, and what permissions
potential subscribers to the port have.  The type of the port
describes what sort of device is at the end.  The following constants
are used for this:</par>

<tablei idx="const_idx" elt="const">
<ti><til>SND_SEQ_PORT_CAP_READ</til><tir>allow read from port</tir></ti>
<ti><til>SND_SEQ_PORT_CAP_SUBS_READ</til><tir>allow read
subscription from port</tir></ti>
<ti><til>SND_SEQ_PORT_CAP_WRITE</til><tir>allow write to port</tir></ti>
<ti><til>SND_SEQ_PORT_CAP_SUBS_WRITE</til><tir>allow write
subscription to port</tir></ti>
<ti><til>SND_SEQ_PORT_CAP_NO_EXPORT</til><tir>disallow external
routing</tir></ti>
</tablei>

<tablei idx="const_idx" elt="const">
<ti><til>SND_SEQ_PORT_TYPE_APPLICATION</til><tir>port is attached
to an application device</tir></ti>
<ti><til>SND_SEQ_PORT_TYPE_SAMPLE</til><tir>port is attached to a
sampling device</tir></ti>
<ti><til>SND_SEQ_PORT_TYPE_DIRECT_SAMPLE</til><tir>port is
attached to a sampling device supporting download</tir></ti>
<ti><til>SND_SEQ_PORT_TYPE_MIDI_GENERIC</til><tir>port is attached
to a generic MIDI device</tir></ti>
<ti><til>SND_SEQ_PORT_TYPE_MIDI_GM</til><tir>port is attached to a
GM compatible device</tir></ti>
<ti><til>SND_SEQ_PORT_TYPE_MIDI_GS</til><tir>port is attached to a
GS compatible device</tir></ti>
<ti><til>SND_SEQ_PORT_TYPE_MIDI_MT32</til><tir>port is attached to
an MT-32 compatible device</tir></ti>
<ti><til>SND_SEQ_PORT_TYPE_MIDI_XG</til><tir>port is attached to
an XG compatible device</tir></ti>
<ti><til>SND_SEQ_PORT_TYPE_SYNTH</til><tir>port is attached to a
synthesizer device</tir></ti>
<ti><til>SND_SEQ_PORT_TYPE_SPECIFIC</til><tir>port is attached to
a hardware-specific type of device</tir></ti>
</tablei>

<par>It is also possible to use a <type>snd_seq_port_info_t</type>,
with <fun>snd_seq_create_port</fun> and <fun>snd_seq_delete_port</fun>
(which acts exactly or almost exactly like
<fun>snd_seq_delete_simple_port</fun>).  A
<type>snd_seq_port_info_t</type> contains a port's owning client,
name, capability bits, and type, as specified separately in
<fun>snd_seq_create_simple_port</fun>, but also information about the
number of MIDI channels and voices and the number of synthesized
voices available at a given port.  It is also possible to iterate
through ports on another client using
<fun>snd_seq_query_next_port</fun>.  Iterating through clients is
accomplished with the corresponding <type>snd_seq_client_info_t</type>
structure and <fun>snd_seq_query_next_client</fun> function.</par>

</section>
<section id="subscriptions"><name>Subscriptions</name>

<par>When ports are created with the appropriate capabilities, other
clients may subscribe to them.  Thus may events be communicated
between clients; events from a port to which other clients have
read-subscribed may be set to be broadcast to all subscribers with
<fun>snd_seq_ev_set_subs</fun>.  Events may also be set to be
broadcast to all clients and all ports with
<fun>snd_seq_ev_set_broadcast</fun>; however, excessive use of such is
not recommended, and multicasting by subscription is generally a much
better approach.</par>

<par>Subscribing to ports is done most simply with the
<fun>snd_seq_connect_from</fun> and <fun>snd_seq_connect_to</fun>
functions.  These functions take a port on the current client, and
respectively a source or destination client/port pair.  Note that the
port which is being subscribed on the current client does not have to
have subscription capability from other clients in order for it to be
connected to other ports which do have such capability.</par>

</section>
<section id="queues"><name>Queues</name>

<par>Each client may have one or more queues associated with it.  Queues
are used for scheduling events.  Each queue, like each client and each
port, also has an identification number, which is an integer and
specific to the client to which it is attached, and a name, which is
an ASCII string.  Events may be scheduled on queues with
<fun>snd_seq_ev_schedule_tick</fun>
for times in ticks or <fun>snd_seq_ev_schedule_real</fun>
for times in nanoseconds (or actually seconds and
nanoseconds&em;see <type>snd_seq_real_time_t</type>).</par>

<par>The primary structures that deal with queues are
<type>snd_seq_queue_info_t<type>, <type>snd_seq_queue_status_t</type>,
<type>snd_seq_queue_tempo_t</type>, and
<type>snd_seq_queue_timer_t</type>.  A queue info structure contains
relatively permanent data: a queue's name, owner client, flags, and
lockedness.  A queue status structure, on the other hand, contains
more transient and time-dependent data: the number of events in a
queue, the running status bits, and the time on the queue in ticks and
seconds/nanoseconds.  A queue tempo structure contains the speed
information for a queue: the PPQ resolution, &mu;sPQ tempo, and timer
skew (expressed as integers <code>skew/skew_base</code>).  A queue
timer structure contains the less mutable timer information for a
queue: the timer ID, resolution, and type.</par>

<par>Queues may be created with <fun>snd_seq_create_queue</fun>, or
just allocated with <fun>snd_seq_alloc_queue</fun> or
<fun>snd_seq_alloc_named_queue</fun>.
They may be deleted with <fun>snd_seq_free_queue</fun>.</par>

</section>
<section id="events"><name>Events</name>

<par>The primary unit of information passed through the ALSA sequencer
is the event.  Many sequencer events correspond to MIDI events; others
are control events for ALSA itself, for communication between
applications, or for other purposes.</par>

<par>To create events, one usually uses
the macros in the middle-level interface.  For MIDI events, one
generally sets an event of type
<const>SND_SEQ_EVENT_<msv>FOOBAR</msv></const> using the corresponding
macro <fun>snd_seq_ev_set_<msv>foobar</msv></fun>.  (Note
the difference in case.)  The arguments to the macro are a pointer to
the event, and the data for the event.  The following MIDI event types
are defined:</par>

<tablei idx="const_idx" elt="const">
<ti><til>SND_SEQ_EVENT_NOTEON</til>
<til>SND_SEQ_EVENT_NOTEOFF</til><tir>note on and note off.  Takes
channel, note number, and velocity
(<type>snd_seq_ev_note_t</type>).</tir></ti>
<ti><til>SND_SEQ_EVENT_NOTE</til><tir>note on <em>combined</em> with
note off.  Takes channel, note number, velocity, and duration in ticks
(<type>snd_seq_ev_note_t</type>).</tir></ti>
<ti><til>SND_SEQ_EVENT_KEYPRESS</til><tir>polyphonic pressure.  Takes
channel, note number, and velocity
(<type>snd_seq_ev_note_t</type>).</tir></ti>
<ti><til>SND_SEQ_EVENT_CHANPRESS</til><tir>channel pressure.  Takes
channel and value (<type>snd_seq_ev_ctrl_t</type>).</tir></ti>
<ti><til>SND_SEQ_EVENT_CONTROLLER</til><tir>continuous controller
data.  Takes channel, controller number, and value
(<type>snd_seq_ev_ctrl_t</type>).</tir></ti>
<ti><til>SND_SEQ_EVENT_PGMCHANGE</til><tir>program change.  Takes
channel and program (<type>snd_seq_ev_ctrl_t</type>).</tir></ti>
<ti><til>SND_SEQ_EVENT_PITCHBEND</til><tir>pitch bend.  Takes channel
and value, with value between -8192 and 8191
(<type>snd_seq_ev_ctrl_t</type>).</tir></ti>
<ti><til>SND_SEQ_EVENT_SYSEX</til><tir>system exclusive data.  Takes
length and pointer to data (<type>snd_seq_ev_ext_t</type>).</tir></ti>
</tablei>

<par>Each event also has a source, destination, and scheduling mode.
The source client is determined by the handle to which an event is
output.  The source port and destination may be set by
<fun>snd_seq_ev_set_source</fun>
and <fun>snd_seq_ev_set_dest</fun>
(which takes a client and port as separate arguments).  Broadcasting
to all and broadcasting to subscribers are special destinations, which
may be set by <fun>snd_seq_ev_set_broadcast</fun>
and <fun>snd_seq_ev_set_subs</fun>,
respectively.  The scheduling mode of an event determines whether it
is sent directly or on a queue, and if on a queue, which queue, and
when.  Direct scheduling may be set with <fun>snd_seq_ev_set_direct</fun>,
which takes only the event.  Queue scheduling may be set with
<fun>snd_seq_ev_schedule_tick</fun>, 
to give a time in ticks, or <fun>snd_seq_ev_schedule_real</fun>,
to give a time using a <fun>snd_seq_real_time_t</fun>.
Each takes event, queue ID (see <ref to="queues">Queues</ref>),
relativity (true iff the scheduling is relative to the current time),
and time (in a format dependent on which scheduling macro is being
used).</par>

<par>Events may be queried by means of the event C structure,
<type>snd_seq_event_t</type>, which has the
following elements:</par>

<tablei elt="code">
<ti><til>type</til><tir>a <type>snd_seq_event_type_t</type> with the
type of the event.</tir></ti>
<ti><til>source</til><til>dest</til><tir>
<type>snd_seq_addr_t</type> fields containing the source and
destination ports</ti>
<ti><til>queue</til><til>time</til><tir>the queue associated with
the event, and its timestamp for input or output as a
<type>snd_seq_timestamp_t</type>.</tir></ti>
<ti><til>note</til><til>control</til><til>ext</til><tir>MIDI
data associated with the event.  Which one is used depends on the type
of event; see the table above.  They are of types
<type>snd_seq_ev_note_t</type>, <type>snd_seq_ev_ctrl_t</type>, and
<type>snd_seq_ev_ext_t</type>, respectively.</tir></ti>
</tablei>

</section>
<section id="read_write"><name>Reading and Writing</name>

<par>Synchronous (blocking) I/O is primarily accomplished with the
functions <fun>snd_seq_event_output</fun> and
<fun>snd_seq_event_input</fun>.  These functions behave much like
standard blocking write and read calls.  There are also
<fun>snd_seq_event_output_buffer</fun> and
<fun>snd_seq_event_output_direct</fun>, which output without flushing
the buffer and output without buffering, respectively.  Nonblocking
I/O can be accomplished by setting nonblocking mode with the
<fun>snd_seq_nonblock</fun> call; waiting for I/O is a little bit more
complicated, and involves <fun>poll</fun>ing.  One must first call
<fun>snd_seq_poll_descriptors_count</fun> to obtain the number of
descriptors to poll, then allocate space for such and use
<fun>snd_seq_poll_descriptors</fun> to obtain the actual descriptors,
followed by a <fun>poll</fun> on them.</par>

</section>
<section id="reference"><name>Reference</name>

</section>
</document>

Attachment: pgp00000.pgp
Description: PGP signature

Reply via email to