diff options
author | Takashi Iwai <tiwai@suse.de> | 2003-10-20 14:04:53 +0000 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2003-10-20 14:04:53 +0000 |
commit | 701d0614f12d76dd9e733d6529a73d3d5828a974 (patch) | |
tree | cd7e0be3db8861d730583969c8063bd1092b970a /src/seq | |
parent | e489376d5def17e6ce80b53b5e8b5cca8abc4894 (diff) |
more documents as introduction.
Diffstat (limited to 'src/seq')
-rw-r--r-- | src/seq/seq.c | 750 |
1 files changed, 748 insertions, 2 deletions
diff --git a/src/seq/seq.c b/src/seq/seq.c index 6ffedfc7..4b4da164 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -30,7 +30,753 @@ /*! \page seq Sequencer interface -<P>Description... +\section seq_general Genral + +The ALSA sequencer interface is designed to deliver the MIDI-like +events between clients/ports. +A typical usage is the MIDI patch-bay. A MIDI application can be +connected arbitrarily from/to the other MIDI clients. +The routing between clients can be changed dynamically, so the +application can handle incoming or outgoing MIDI events regardless of +the devices or the application connections. + +The sequencer core stuff only takes care of two things: +scheduling events and dispatching them to the destination at the +right time. All processing of MIDI events has to be done within the clients. +The event can be dispatched immediately without queueing, too. +The event scheduling can be done either on a MIDI tempo queue or +on a wallclock-time queue. + +\section seq_client Client and Port + +A <i>client</i> is created at each time #snd_seq_open() is called. +Later on, the attributes of client such as its name string can be changed +via #snd_seq_set_client_info(). There are helper functions for ease of use, +e.g. #snd_seq_set_client_name() and #snd_seq_set_client_event_filter(). +A typical code would be like below: +\code +// create a new client +snd_seq_t *open_client() +{ + snd_seq_t *handle; + int err; + err = snd_seq_open(&handle, "default", SND_SEQ_OPEN_INPUT, 0); + if (err < 0) + return NULL; + snd_seq_set_client_name(handle, "My Client"); + return handle; +} +\endcode + +You'll need to know the id number of the client eventually, for example, +when accessing to a certain port (see the section \ref seq_subs). +The client id can be obtained by #snd_seq_client_id() function. + +A client can have one or more <i>ports</i> to communicate between other +clients. A port is corresponding to the MIDI port in the case of MIDI device, +but in general it is nothing but the access point between other clients. +Each port may have capability flags, which specify the read/write +accessbility and subscription permissions of the port. +For creation of a port, call #snd_seq_create_port() \endlink +with the appropirate port attribute specified in #snd_seq_port_info_t +reocrd. + +For creating a port for the normal use, there is a helper function +#snd_seq_create_simple_port(). An example with this function is like below. +\code +// create a new port; return the port id +// port will be writable and accept the write-subscription. +int my_new_port(snd_seq_t *handle) +{ + return snd_seq_create_simple_port(handle, "my port", + SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, + SND_SEQ_PORT_TYPE_MIDI_GENERIC); +} +\endcode + +\section seq_memory Memory Pool + +Each client owns memory pools on kernel space +for each input and output events. +Here, input and output mean +input (read) from other clients and output (write) to others, respectively. +Since memory pool of each client is independent from others, +it avoids such a situation that a client eats the whole events pool +and interfere other clients' responce. + +The all scheduled output events or input events from dispatcher are stored +on these pools until delivered to other clients or extracted to user space. +The size of input/output pools can be changed independently. +The output pool has also a room size, which is used to wake up the +thread when it falls into sleep in blocking write mode. + +Note that ports on the same client share the same memory pool. +If a port fills the memory pool, another can't use it any more. +For avoiding this, multiple clients can be used. + +For chancing the pool size and the condition, access to #snd_seq_client_pool_t +record. There are helper functions, #snd_seq_set_client_pool_output(), +#snd_seq_set_client_pool_output_room() and #snd_seq_set_client_pool_input(), +for setting the total output-pool size, the output-room size and the input-pool +size, respectively. + +\section seq_subs Subscription + +One of the new features in ALSA sequencer system is <i>subscription</i> of ports. +In general, subscription is a connection between two sequencer ports. +Even though an event can be delivered to a port without subscription +using an explicit destination address, +the subscription mechanism provides us more abstraction. + +Suppose a MIDI input device which sends events from a keyboard. +The port associated with this device has READ capability - which means +this port is readable from other ports. +If a user program wants to capture events from keyboard and store them +as MIDI stream, this program must subscribe itself to the MIDI port +for read. +Then, a connection from MIDI input port to this program is established. +From this time, events from keyboard are automatically sent to this program. +Timestamps will be updated according to the subscribed queue. +\code + MIDI input port (keyboard) + | + V + ALSA sequencer - update timestamp + | + V + application port +\endcode + +There is another subscription type for opposite direction: +Suppose a MIDI sequencer program which sends events to a MIDI output device. +In ALSA system, MIDI device is not opened until the associated MIDI port +is accessed. Thus, in order to activate MIDI device, we have to subscribe +to MIDI port for write. +After this connection is established, events will be properly sent +to MIDI output device. +\code + application port + | + V + ALSA sequencer - events are scheduled + | + V + MIDI output port (WaveTable etc.) +\endcode + +From the viewpoint of subscription, the examples above are special cases. +Basically, subscription means the connection between two arbitrary ports. +For example, imagine a filter application which modifies +the MIDI events like program, velocity or chorus effects. +This application can accept arbitrary MIDI input +and send to arbitrary port, just like a Unix pipe application using +stdin and stdout files. +We can even connect several filter applictions which work individually +in order to process the MIDI events. +Subscription can be used for this purpose. +The connection between ports can be done also by the "third" client. +Thus, filter applications have to manage +only input and output events regardless of receiver/sender addresses. +\code + sequencer port #1 + | + V + ALSA sequencer (scheduled or real-time) + | + V + sequencer port #2 +\endcode + +For the detail about subscription, see the section \ref seq_subs_more. + +\section seq_events Sequencer Events + +Messaging between clients is performed by sending events from one client to +another. These events contain high-level MIDI oriented messages or sequencer +specific messages. + +All the sequencer events are stored in a sequencer event record, +#snd_seq_event_t type. +Application can send and receive these event records to/from other +clients via sequencer. +An event has several stroage types according to its usage. +For example, a SYSEX message is stored on the variable length event, +and a large synth sample data is delivered using a user-space data pointer. + + +\subsection seq_ev_struct Structure of an event + +An event consists of the following items: +<ul> +<li>The type of the event +<li>Event flags. It describes various conditions: + <ul> + <li>time stamp; "real time" / "song ticks" + <li>time mode; "absolute" / "relative to current time" + </ul> +<li>Timestamp of the event. +<li>Scheduling queue id. +<li>Source address of the event, given by the combination + of client id and port id numbers. +<li>Destination address of the event. +<li>The actual event data. (up to 12 bytes) +</ul> + +The actual record is shown in #snd_seq_event_t. +The type field contains the type of the event +(1 byte). +The flags field consists of bit flags which +describe several conditions of the event (1 byte). +It includes the time-stamp mode, data storage type, and scheduling prority. +The tag field is an arbitrary tag. +This tag can used for removing a distinct event from the event queue +via #snd_seq_remove_events(). +The queue field is the queue id for scheduling. +The source and dest fields are source and destination addresses. +The data field is a union of event data. + +\subsction seq_ev_queue Scheduling queue + +An event can be delivered either on scheduled or direct dispatch mode. +On the scheduling mode, an event is once stored on the priority queue +and delivered later (or even immediately) to the destination, +whereas on the direct disatch mode, an event is passed to the destination +without any queue. + +For a scheduled delivery, a queue to process the event must exist. +Usually, a client creates its own queue by +#snd_seq_alloc_queue() function. +Alternatively, a queue may be shared among several clients. +For scheduling an event on the specified queue, +a client needs to fill queue field +with the preferred queue id. + +Meanwhile, for dispatching an event directly, just +use #SND_SEQ_QUEUE_DIRECT as the target queue id. +A macro #snd_seq_ev_set_direct() is provided for ease +and compatibility. + +Note that scheduling at the current or earlier time is different +from the direct dispatch mode even though the event is delivered immediately. +On the former scheme, an event is once stored on priority queue, then +delivered actually. Thus, it acquires a space from memory pool. +On the other hand, the latter is passed without using memory pool. +Although the direct dispatched event needs less memory, it means also +that the event cannot be resent if the destination is unable to receive it +momentarily. + +\subsection seq_ev_time Time stamp + +The timestamp of the event can either specified in +<i>real time</i> or in <i>song ticks</i>. +The former means the wallclock time while the latter corresponds to +the MIDI ticks. +Which format is used is determined by the event flags. + +The resolution of real-time value is in nano second. +Since 64 bit length is required for the actual time calculation, +it is represented by +a structure of pair of second and nano second +defined as #snd_seq_real_time_t type. +The song tick is defined simply as a 32 bit integer, +defined as #snd_seq_tick_time_t type. +The time stored in an event record is a union of these two different +time values. + +Note that the time format used for real time events is very similar to +timeval struct used for unix system time. +The absurd resolution of the timestamps allows us to perform very accurate +conversions between songposition and real time. Round-off errors can be +neglected. + +If a timestamp with a +<i>relative</i> timestamp is delivered to ALSA, the +specified timestamp will be used as an offset to the current time of the +queue the event is sent into. +An <i>absolute</i> timestamp is on the contrary the time +counted from the moment when the queue started. + +An client that relies on these relative timestamps is the MIDI input port. +As each sequencer queue has it's own clock the only way to deliver events at +the right time is by using the relative timestamp format. When the event +arrives at the queue it is normalised to absolute format. + +The timestamp format is specified in the flag bitfield masked by +#SND_SEQ_TIME_STAMP_MASK. +To schedule the event in a real-time queue or in a tick queue, +macros #snd_seq_ev_schedule_real() and +#snd_seq_ev_schedule_tick() are provided, respectively. + +\subsection seq_ev_addr Source and destination addresses + +To identify the source and destination of an event, the addressing field +contains a combination of client id and port id numbers, defined as +#snd_seq_addr_t type. +When an event is passed to sequencer from a client, sequencer fills +source.client field +with the sender's id automatically. +It is the responsibility of sender client to +fill the port id of source.port and +both client and port of dest field. + +If an existing address is set to the destination, +the event is simplly delivered to it. +When #SND_SEQ_ADDRESS_SUBSCRIBERS is set to the destination client id, +the event is delivered to all the clients connected to the source port. + + +A sequencer core has two pre-defined system ports on the system client +#SND_SEQ_CLIENT_SYSTEM: #SND_SEQ_PORT_SYSTEM_TIMER and #SND_SEQ_PORT_SYSTEM_ANNOUNCE. +The #SND_SEQ_PORT_SYSTEM_TIMER is the system timer port, +and #SND_SEQ_PORT_SYSTEM_ANNOUNCE is the system +announce port. +In order to control a queue from a client, client should send a +queue-control event +like start, stop and continue queue, change tempo, etc. +to the system timer port. +Then the sequencer system handles the queue according to the received event. +This port supports subscription. The received timer events are +broadcasted to all subscribed clients. + +The latter port does not receive messages but supports subscription. +When each client or port is attached, detached or modified, +an announcement is sent to subscribers from this port. + +\subsection seq_ev_data Data storage type + +Some events like SYSEX message, however, need larger data space +than the standard data. +For such events, ALSA sequencer provides seveal different data storage types. +The data type is specified in the flag bits masked by #SND_SEQ_EVENT_LENGTH_MASK. +The following data types are available: + +\par Fixed size data +Normal events stores their parameters on +data field (12 byte). +The flag-bit type is #SND_SEQ_EVENT_LENGTH_FIXED. +A macro #snd_seq_ev_set_fixed() is provided to set this type. + +\par Variable length data +SYSEX or a returned error use this type. +The actual data is stored on an extra allocated space. +On sequecer kernel, the whole extra-data is duplicated, so that the event +can be scheduled on queue. +The data contains only the length and the +pointer of extra-data. +The flag-bit type is #SND_SEQ_EVENT_LENGTH_VARIABLE. +A macro #snd_seq_ev_set_variable() is provided to set this type. + +\par User-space data +This type refers also an extra data space like variable length data, +but the extra-data is not duplicated but +but referred as a user-space data on kernel, +so that it reduces the time and resource for transferring +large bulk of data like synth sample wave. +This data type, however, can be used only for direct dispatch mode, +and supposed to be used only for a special purpose like a bulk data +transfer. +The data length and pointer are stored also in +data.ext field as well as variable length data. +The flag-bit type is #SND_SEQ_EVENT_LENGTH_VARUSR. +A macro #snd_seq_ev_set_varusr() is provided to set this type. + +\subsection seq_ev_sched Scheduling priority + +There are two priorities for scheduling: +\par Normal priority +If an event with the same scheduling time is already present on the queue, +the new event is appended to the older. +\par High priority +If an event with the same scheduling time is already present on the queue, +the new event is inserted before others. + +The scheduling priority is set in the flag bitfeld masked by #SND_SEQ_PRIORITY_MASK. +A macro #snd_seq_ev_set_priority() is provided to set the mode type. + +\section seq_queue Event Queues +\subsection seq_ev_control Creation of a queue + +Creating a queue is done usually by calling #snd_seq_alloc_queue. +You can create a queue with a certain name by #snd_seq_alloc_named_queue(), too. +\code +// create a queue and return its id +int my_queue(snd_seq_t *handle) +{ + return snd_seq_alloc_named_queue(handle, "my queue"); +} +\endcode +These functions are the wrapper to the function #snd_seq_create_queue(). +For releasing the allocated queue, call #snd_seq_free_queue() with the +obtained queue id. + +Once when a queue is created, the two queues are associated to that +queue record in fact: one is the realtime queue and another is the +tick queue. These two queues are bound together to work +synchronously. Hence, when you schedule an event, you have to choose +which queue type is used as described in the section \ref +seq_ev_time. + +\subsection seq_ev_tempo Setting queue tempo + +The tempo (or the speed) of the scheduling queue is variable. +In the case of <i>tick</i> queue, the tempo is controlled +in the manner of MIDI. There are two parameters to define the +actual tempo, PPQ (pulse per quarter note) and MIDI tempo. +The former defines the base resolution of the ticks, while +the latter defines the beat tempo in microseconds. +As default, 96 PPQ and 120 BPM are used, respectively. +That is, the tempo is set to 500000 (= 60 * 1000000 / 120). +Note that PPQ cannot be changed while the queue is running. +It must be set before the queue is started. + +On the other hand, in the case of <i>realtime</i> queue, the +time resolution is fixed to nanosecononds. There is, however, +a parameter to change the speed of this queue, called <i>skew</i>. +You can make the queue faster or slower by setting the skew value +bigger or smaller. In the API, the skew is defined by two values, +the skew base and the skew value. The actual skew is the fraction +of them, <i>value/base</i>. As default, the skew base is set to 16bit +(0x10000) and the skew value is the identical, so that the queue is +processed as well as in the real world. + +When the tempo of realtime queue is changed, the tempo of +the associated tick queue is changed together, too. +That's the reason why two queues are created always. +This feature can be used to synchronize the event queue with +the external synchronization source like SMPTE. In such a case, +the realtime queue is skewed to match with the external source, +so that both the realtime timestamp and the MIDI timestamp are +synchronized. + +For setting these tempo parameters, use #snd_seq_queue_tempo_t record. +For example, to set the tempo of the queue <code>q</code> to +48 PPQ, 60 BPM, +\code +void set_tempo(snd_seq_t *handle) +{ + snd_seq_queue_tempo_t *tempo; + snd_seq_queue_tempo_alloca(&tempo); + snd_seq_queue_tempo_set_tempo(tempo, 1000000); // 60 BPM + snd_seq_queue_tempo_set_ppq(tempo, 48); // 48 PPQ + snd_seq_set_queue_tempo(handle, tempo); +} +\endcode + +For changing the (running) queue's tempo on the fly, you can either +set the tempo via #snd_seq_queue_tempo() or send a MIDI tempo event +to the system timer port. For example, +\code +int change_tempo(snd_seq_t *handle, int q, unsigned int tempo) +{ + snd_seq_event_t ev; + snd_seq_ev_clear(&ev); + ev.dest.client = SND_SEQ_CLIENT_SYSTEM; + ev.dest.port = SND_SEQ_PORT_SYSTEM_TIMER; + ev.source.client = my_client_id; + ev.source.port = my_port_id; + ev.queue = SND_SEQ_QUEUE_DIRECT; // no scheduling + ev.data.queue.queue = q; // affected queue id + ev.data.queue.value = tempo; // new tempo in microsec. + return snd_seq_event_output(handle, &ev); +} +\endcode +There is a helper function to do this easily, +#snd_seq_change_queue_tempo(). +Set NULL to the last argument, if you don't need any +special settings. + +In the above example, the tempo is changed immediately after +the buffer is flushed by #snd_seq_drain_output() call. +You can schedule the event in a certain queue so that the tempo +change happes at the scheduled time, too. + +\subsection seq_ev_start Starting and stopping a queue + +To start, stop, or continue a queue, you need to send a queue-control +event to the system timer port as well. There are helper functions, +#snd_seq_start_queue(), #snd_seq_stop_queue() and +#snd_seq_continue_queue(). +Note that if the last argument of these functions is NULL, the +event is sent (i.e. operated) immediately after the buffer flush. +If you want to schedule the event at the certain time, set up +the event record and provide the pointer of that event record as the +argument. + +Only calling these functions doesn't deliver the event to the +sequencer core but only put to the output buffer. You'll need to +call #snd_seq_drain_output() eventually. + + +\section seq_subs_more More inside the subscription + +\subsection seq_subs_perm Permissions + +Each ALSA port can have capability flags. +The most basic capability flags are +#SND_SEQ_PORT_CAP_READ and #SND_SEQ_PORT_CAP_WRITE. +The former means that the port allows to send events to other ports, +whereas the latter capability menas +that the port allows to receive events from other ports. +You may have noticed that meanings of \c READ and \c WRITE +are permissions of the port from the viewpoint of other ports. + +For allowing subscription from/to other clients, another capability +flags must be set together with read/write capabilities above. +For allowing read and write subscriptions, +#SND_SEQ_PORT_CAP_SUBS_READ and +#SND_SEQ_PORT_CAP_SUBS_WRITE are used, +respectively. +For example, the port with MIDI input device always has +#SND_SEQ_PORT_CAP_SUBS_READ capability, +and the port with MIDI output device always has +#SND_SEQ_PORT_CAP_SUBS_WRITE capability together with +#SND_SEQ_PORT_CAP_READ and #SND_SEQ_PORT_CAP_WRITE capabilities, +respectively. +Obviously, these flags have no influence +if \c READ or \c WRITE> capability is not set. + +Note that these flags are not necessary if the client subscribes itself +to the spcified port. +For example, when a port makes READ subscription +to MIDI input port, this port must have #SND_SEQ_PORT_CAP_WRITE capability, +but no #SND_SEQ_PORT_CAP_SUBS_WRITE capability is required. +Only MIDI input port must have #SND_SEQ_PORT_SUBS_READ capability. + +As default, the connection of ports via the third client is always allowed +if proper read and write (subscription) capabilities are set both to the +source and destination ports. +For prohibiting this behavior, set a capability +#SND_SEQ_PORT_CAP_NO_EXPORT to the port. +If this flag is set, subscription must be done by sender or receiver +client itself. +It is useful to avoid unexpected disconnection. +The ports which won't accept subscription should have this capability +for better security. + +\subsection seq_subs_handle Subscription handlers + +In ALSA library, subscription is done via +#snd_seq_subscribe_port() function. +It takes the argument of #snd_seq_port_subscribe_t record pointer. +Suppose that you have a client which will receive data from +a MIDI input device. The source and destination addresses +are like the below; +\code +snd_seq_addr_t sender, dest; +sender.client = MIDI_input_client; +sender.port = MIDI_input_port; +dest.client = my_client; +dest.port = my_port; +\endcode +To set these values as the connection call like this. +\code +snd_seq_port_subscribe_t *subs; +snd_seq_port_subscribe_alloca(&subs); +snd_seq_port_subscribe_set_sender(subs, &sender); +snd_seq_port_subscribe_set_dest(subs, &dest); +snd_seq_subscribe_port(handle, subs); +\endcode + +When the connection should be exclusively done only between +a certain pair, set <i>exclusive</i> attribute to the subscription +record before calling #snd_seq_port_subscribe. +\code +snd_seq_port_subscribe_set_exclusive(subs, 1); +\endcode +The succeeding subscriptions will be refused. + +The timestamp can be updated independently on each connection. +When set up, the timestamp of incoming queue to the destination port +is updated automatically to the time of the specified queue. +\code +snd_seq_port_subscribe_set_time_update(subs, 1); +snd_seq_port_subscribe_set_queue(subs, q); +\endcode +For getting the wallclock time (sec/nsec pair), set <i>real</i> attribute: +\code +snd_seq_port_subscribe_set_time_real(subs, 1); +\endcode +Otherwise, the timestamp is stored in tick unit. +This feature is useful when receiving events from MIDI input device. +The event time is automatically set in the event record. + +Note that an outsider client may connect other ports. +In this case, however, the subscription may be refused +if #SND_SEQ_PORT_CAP_NO_EXPORT capability is set in either sender or receiver port. + +\section seq_subs_ex Examples of subscription + +\subsection seq_subs_ex_capt Capture from keyboard + +Assume MIDI input port = 64:0, application port = 128:0, and +queue for timestamp = 1 with real-time stamp. +The application port must have capabilty #SND_SEQ_PORT_CAP_WRITE. +\code +void capture_keyboard(snd_seq_t *seq) +{ + snd_seq_addr_t sender, dest; + snd_seq_port_subscribe_t *subs; + sender.client = 64; + sender.port = 0; + dest.client = 128; + dest.port = 0; + snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_subscribe_set_sender(subs, &sender); + snd_seq_port_subscribe_set_dest(subs, &dest); + snd_seq_port_subscribe_set_queue(subs, 1); + snd_seq_port_subscribe_set_time_update(subs, 1); + snd_seq_port_subscribe_set_time_real(subs, 1); + snd_seq_subscribe_port(seq, subs); +} +\endcode + +\subsection seq_subs_ex_out Output to MIDI device + +Assume MIDI output port = 65:1 and application port = 128:0. +The application port must have capabilty #SND_SEQ_PORT_CAP_READ. +\code +void subscribe_output(snd_seq_t *seq) +{ + snd_seq_addr_t sender, dest; + snd_seq_port_subscribe_t *subs; + sender.client = 128; + sender.port = 0; + dest.client = 65; + dest.port = 1; + snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_subscribe_set_sender(subs, &sender); + snd_seq_port_subscribe_set_dest(subs, &dest); + snd_seq_subscribe_port(seq, subs); +} +\endcode +This example can be simplified by using #snd_seq_connect_to() function. +\code +void subscribe_output(snd_seq_t *seq) +{ + snd_seq_connect_to(seq, 0, 65, 1); +} +\endcode + +\subsection seq_subs_ex_arbit Arbitrary connection + +Assume connection from application 128:0 to 129:0, +and that subscription is done by the third application (130:0). +The sender must have capabilities both +#SND_SEQ_PORT_CAP_READ and +#SND_SEQ_PORT_SUBS_READ, +and the receiver +#SND_SEQ_PORT_CAP_WRITE and +#SND_SEQ_PORT_CAP_SUBS_WRITE, respectively. +\code +// ..in the third application (130:0) .. +void coupling(snd_seq_t *seq) +{ + snd_seq_addr_t sender, dest; + snd_seq_port_subscribe_t *subs; + sender.client = 128; + sender.port = 0; + dest.client = 129; + dest.port = 0; + snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_subscribe_set_sender(subs, &sender); + snd_seq_port_subscribe_set_dest(subs, &dest); + snd_seq_subscribe_port(seq, subs); +} +\endcode + +\section seq_ex_event Event Processing + +\subsection seq_ex_address Addressing + +Now, two ports are connected by subscription. Then how to send events? + +The subscribed port doesn't have to know the exact sender address. +Instead, there is a special address for subscribers, +#SND_SEQ_ADDRESS_SUBSCRIBERS. +The sender must set this value as the destination client. +Destination port is ignored. + +The other values in source and destination addresses are identical with +the normal event record. +If the event is scheduled, proper queue and timestamp values must be set. + +There is a convenient function to set the address in an event record. +In order to set destination as subscribers, use +#snd_seq_ev_set_subs(). + +\subsection Scheduled Delivery + +If we send an event at the scheduled time <code>t</code> (tick) +on the queue <code>Q</code>, +the sender must set both schedule queue and time in the +event record. +The program appears like this: +\code +void schedule_event(snd_seq_t *seq) +{ + snd_seq_event_t ev; + + snd_seq_ev_clear(&ev); + snd_seq_ev_set_source(&ev, my_port); + snd_seq_ev_set_subs(&ev); + snd_seq_ev_schedule_tick(&ev, Q, 0, t); + ... // set event type, data, so on.. + + snd_seq_event_output(seq, &ev); + ... + snd_seq_drain_output(seq); // if necessary +} +\endcode +Of course, you can use realtime stamp, too. + +\subsection seq_ex_direct Direct Delivery + +If the event is sent immediately without enqueued, the sender doesn't take +care of queue and timestamp. +As well as the case above, there is a function to set the direct delivery, +#snd_seq_ev_set_direct(). +The program can be more simplified as follows: +\code +void direct_delivery(snd_seq_t *seq) +{ + snd_seq_event_t ev; + + snd_seq_ev_clear(&ev); + snd_seq_ev_set_source(&ev, port); + snd_seq_ev_set_subs(&ev); + snd_seq_ev_set_direct(&ev); + ... // set event type, data, so on.. + + snd_seq_event_output(seq, &ev); + snd_seq_drain_output(seq); +} +\endcode +You should flush event soon after output event. +Otherwise, the event is enqueued on output queue of ALSA library +(not in the kernel!), and will be never processed until +this queue becomes full. + +\subsection seq_ex_filter Filter Application + +A typical filter program, which receives an event and sends it immediately +after some modification, will appear as following: +\code +void event_filter(snd_seq_t *seq, snd_seq_event_t *ev) +{ + snd_seq_event_t *ev; + + while (snd_seq_event_input(seq, &ev) >= 0) { + //.. modify input event .. + + snd_seq_ev_set_source(ev, my_port); + snd_seq_ev_set_subs(ev); + snd_seq_ev_set_direct(ev); + snd_seq_event_output(seq, &ev); + snd_seq_drain_output(seq); + snd_seq_free_event(ev); + } +} +\endcode */ @@ -201,7 +947,7 @@ static int snd_seq_open_noupdate(snd_seq_t **seqp, snd_config_t *root, * - #SND_SEQ_OPEN_INPUT - open the sequencer for input only * - #SND_SEQ_OPEN_DUPLEX - open the sequencer for output and input * \note Internally, these are translated to \c O_WRONLY, \c O_RDONLY and - * \O_RDWR respectively and used as the second argument to the C library + * \c O_RDWR respectively and used as the second argument to the C library * open() call. * \param mode Optional modifier. Can be either 0, or * #SND_SEQ_NONBLOCK, which will make read/write operations |