mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -04:00
Takashi Iwai <iwai@ww.uni-erlangen.de>
Wed, 22 Dec 1999 15:51:37 +0100
Changes in alsa-driver:
1. SND_SEQ_DEST_DIRECT was removed. For direct dispatching, specify
the scheduling queue as SND_SEQ_QUEUE_DIRECT.
Source compatibility is kept if alsa-lib functions are used to set
or check direct mode.
Tested: pmidi, playmidi1, aconnect, aseqview, OSS emulation
2. The error event can be bounced to the sender as an encapsulated
event with SND_SEQ_EVENT_BOUNCE type. If the orignal event is
variable length (like SYSEX), the data body is added after the
copied event data. (To access to it, use
snd_seq_event_bounce_ext_data() macro.)
This feature is enabled only when the sender is opened with read
flag and has a client-filter flag SND_SEQ_FILTER_BOUNCE, so that
as default it is disabled.
3. Maximum size of VARIABLE length event is defined as 2048 bytes.
Larger events will be refused.
Note that this is not the size of VARUSR nor VARIPC event.
4. Return error code was revised.
Possible errors for read():
-ENXIO invalid client or file open mode
-ENOSPC FIFO overflow (the flag is cleared after this error report)
-EINVAL no enough user-space buffer to write the whole event
-EFAULT seg. fault during copy to user space
Possible errors for write():
-ENXIO invalid client or file open mode
-ENOMEM malloc failed
-EFAULT seg. fault during copy from user space
-EINVAL invalid event
-EAGAIN no space in output pool
-EINTR interrupts while sleep
-EMLINK too many hops
others depends on return value from driver event_input callback
Changes in alsa-lib:
5. Behavior of snd_seq_flush_output() was changed.
It flushes data in output buffer as much as possible. This has
two advantages. One is that most data can be sent at one call,
and another is that the error in write() can be detected directly
from this function.
The demerit of this implementation is that in non-blocking mode, it
tries twice write() even if write pool of sequencer is full, which
is not actually error. In this case, flush_output() always returns
-EAGAIN error code.
After an error is detected (except for -EAGAIN in non-blocking
mode), you should remove the error event via
snd_seq_extract_output() as below. Otherwise, it'll remain in
output buffer and cause the same error again.
6. Some functions were added:
snd_seq_exract_output(seq, &event)
This extracts the first event on *output* buffer. This will
be useful to know (and remove) the event which causes error in
write().
snd_seq_event_input_pending(seq, fetch)
Retunrs the number of events on input buffer.
If no events exist in input buffer, it fetches from sequencer
(only when flag is true).
snd_seq_event_input_selective(seq, &ev, type, blocking)
Retrieve the event from input with the given event type.
If blocking is true, it waits until the target event is
received. It'll be useful for checking bounced error or
waiting for hand-shake results (eg. loading insruments).
This commit is contained in:
parent
b4bbbdbc9b
commit
244d345fda
3 changed files with 194 additions and 44 deletions
|
|
@ -61,7 +61,10 @@ int snd_seq_free_event(snd_seq_event_t *ev);
|
|||
int snd_seq_event_length(snd_seq_event_t *ev);
|
||||
int snd_seq_event_output(snd_seq_t *handle, snd_seq_event_t *ev);
|
||||
int snd_seq_event_input(snd_seq_t *handle, snd_seq_event_t **ev);
|
||||
int snd_seq_event_input_pending(snd_seq_t *seq, int fetch_sequencer);
|
||||
int snd_seq_event_input_selective(snd_seq_t *seq, snd_seq_event_t **ev, int type, int blocking);
|
||||
int snd_seq_flush_output(snd_seq_t *handle);
|
||||
int snd_seq_extract_output(snd_seq_t *handle, snd_seq_event_t **ev);
|
||||
int snd_seq_drain_output(snd_seq_t *handle);
|
||||
int snd_seq_drain_input(snd_seq_t *handle);
|
||||
int snd_seq_remove_events(snd_seq_t *handle, snd_seq_remove_events_t *info);
|
||||
|
|
|
|||
223
src/seq/seq.c
223
src/seq/seq.c
|
|
@ -592,10 +592,10 @@ static int snd_seq_decode_event(char **buf, int *len, snd_seq_event_t *ev)
|
|||
if (ev->data.ext.len > 0) {
|
||||
ev->data.ext.ptr = (char *) malloc(ev->data.ext.len);
|
||||
if (!(ev->data.ext.ptr)) {
|
||||
ev->flags &= ~SND_SEQ_EVENT_LENGTH_MASK; /* clear flag */
|
||||
ev->flags &= ~SND_SEQ_EVENT_LENGTH_MASK; /* clear flag */
|
||||
*buf += ev->data.ext.len;
|
||||
*len -= ev->data.ext.len;
|
||||
return -ENOENT;
|
||||
return -ENOMEM;
|
||||
}
|
||||
memcpy(ev->data.ext.ptr, *buf, ev->data.ext.len);
|
||||
*buf += ev->data.ext.len;
|
||||
|
|
@ -610,71 +610,224 @@ static int snd_seq_decode_event(char **buf, int *len, snd_seq_event_t *ev)
|
|||
* Current implementation uses FIFO cache.
|
||||
*/
|
||||
|
||||
static int snd_seq_event_read_buffer(snd_seq_t *seq)
|
||||
{
|
||||
char *buf;
|
||||
int count;
|
||||
|
||||
count = read(seq->fd, seq->ibuf, seq->ibufsize);
|
||||
if (count < 0)
|
||||
return -errno;
|
||||
buf = seq->ibuf;
|
||||
while (count > 0) {
|
||||
snd_seq_cell_t *cell = snd_seq_create_cell(NULL);
|
||||
if (cell == NULL)
|
||||
return -ENOMEM;
|
||||
if (snd_seq_decode_event(&buf, &count, &cell->ev)<0) {
|
||||
snd_seq_free_cell(cell);
|
||||
} else {
|
||||
snd_seq_input_cell_in(seq, cell);
|
||||
}
|
||||
}
|
||||
return seq->cells;
|
||||
}
|
||||
|
||||
/*
|
||||
* retrieve an event from FIFO
|
||||
* if fifo is empty, read events from sequencer
|
||||
*/
|
||||
int snd_seq_event_input(snd_seq_t *seq, snd_seq_event_t **ev)
|
||||
{
|
||||
snd_seq_cell_t *cell;
|
||||
char *buf;
|
||||
int count;
|
||||
|
||||
*ev = NULL;
|
||||
if (!seq)
|
||||
return -EINVAL;
|
||||
|
||||
if (! snd_seq_input_cell_available(seq)) {
|
||||
int err = snd_seq_event_read_buffer(seq);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (snd_seq_input_cell_available(seq)) {
|
||||
*ev = snd_seq_create_event();
|
||||
if (*ev == NULL)
|
||||
return -ENOMEM;
|
||||
cell = snd_seq_input_cell_out(seq);
|
||||
if (cell == NULL)
|
||||
return -ENOMEM;
|
||||
memcpy(*ev, &cell->ev, sizeof(snd_seq_event_t));
|
||||
/* clear flag to avoid free copied data */
|
||||
cell->ev.flags &= ~SND_SEQ_EVENT_LENGTH_MASK;
|
||||
snd_seq_free_cell(cell);
|
||||
return seq->cells;
|
||||
}
|
||||
count = read(seq->fd, seq->ibuf, seq->ibufsize);
|
||||
if (count < 0)
|
||||
return seq->cells;
|
||||
}
|
||||
|
||||
/*
|
||||
* read input data from sequencer if available
|
||||
*/
|
||||
static int snd_seq_event_input_feed(snd_seq_t *seq, struct timeval *timeout)
|
||||
{
|
||||
fd_set rfds;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(seq->fd, &rfds);
|
||||
if (select(seq->fd + 1, &rfds, NULL, NULL, timeout) < 0)
|
||||
return -errno;
|
||||
buf = seq->ibuf;
|
||||
while (count > 0) {
|
||||
if (*ev == NULL) { /* first event */
|
||||
*ev = snd_seq_create_event();
|
||||
if (*ev == NULL)
|
||||
return -ENOMEM;
|
||||
if (snd_seq_decode_event(&buf, &count, *ev)<0) {
|
||||
snd_seq_free_event(*ev);
|
||||
*ev = NULL;
|
||||
}
|
||||
} else {
|
||||
cell = snd_seq_create_cell(NULL);
|
||||
if (cell == NULL)
|
||||
return -ENOMEM;
|
||||
if (snd_seq_decode_event(&buf, &count, &cell->ev)<0) {
|
||||
snd_seq_free_cell(cell);
|
||||
} else {
|
||||
snd_seq_input_cell_in(seq, cell);
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(seq->fd, &rfds))
|
||||
return snd_seq_event_read_buffer(seq);
|
||||
return seq->cells;
|
||||
}
|
||||
|
||||
/*
|
||||
* check events in input queue
|
||||
*/
|
||||
int snd_seq_event_input_pending(snd_seq_t *seq, int fetch_sequencer)
|
||||
{
|
||||
if (snd_seq_input_cell_available(seq))
|
||||
return seq->cells;
|
||||
|
||||
if (fetch_sequencer) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
return snd_seq_event_input_feed(seq, &tv);
|
||||
}
|
||||
return seq->cells;
|
||||
}
|
||||
|
||||
/* search the first matched event in input buffer and retrieve it */
|
||||
static int snd_seq_event_retrieve(snd_seq_t *seq, snd_seq_event_t **ev, int type)
|
||||
{
|
||||
snd_seq_cell_t *c, *prev;
|
||||
|
||||
prev = NULL;
|
||||
*ev = NULL;
|
||||
for (c = seq->head; c; prev = c, c = c->next) {
|
||||
if (c->ev.type == type) {
|
||||
*ev = snd_seq_create_event();
|
||||
if (*ev == NULL)
|
||||
return -ENOMEM;
|
||||
if (prev)
|
||||
prev->next = c->next;
|
||||
else
|
||||
seq->head = c->next;
|
||||
if (seq->tail == c)
|
||||
seq->tail = prev;
|
||||
seq->cells--;
|
||||
memcpy(*ev, &c->ev, sizeof(snd_seq_event_t));
|
||||
/* clear flag to avoid free copied data */
|
||||
c->ev.flags &= ~SND_SEQ_EVENT_LENGTH_MASK;
|
||||
snd_seq_free_cell(c);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* retrieve an event from input FIFO with a specified type
|
||||
*/
|
||||
int snd_seq_event_input_selective(snd_seq_t *seq, snd_seq_event_t **ev, int type, int blocking)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!seq)
|
||||
return -EINVAL;
|
||||
|
||||
err = snd_seq_event_retrieve(seq, ev, type);
|
||||
if (err < 0)
|
||||
return err;
|
||||
else if (*ev)
|
||||
return seq->cells;
|
||||
|
||||
/* read from sequencer */
|
||||
if (blocking) {
|
||||
/* try until the event is found */
|
||||
while (*ev == NULL) {
|
||||
err = snd_seq_event_input_feed(seq, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_seq_event_retrieve(seq, ev, type);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
/* try to read and check the new input buffer */
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
err = snd_seq_event_input_feed(seq, &tv);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_seq_event_retrieve(seq, ev, type);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return seq->cells;
|
||||
}
|
||||
|
||||
/*
|
||||
* flush output buffer to sequencer
|
||||
*/
|
||||
int snd_seq_flush_output(snd_seq_t *seq)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (!seq)
|
||||
return -EINVAL;
|
||||
if (seq->obufused <= 0)
|
||||
return 0;
|
||||
result = write(seq->fd, seq->obuf, seq->obufused);
|
||||
if (result < 0)
|
||||
return -errno;
|
||||
|
||||
if (result < seq->obufused)
|
||||
memmove(seq->obuf, seq->obuf + result, seq->obufused - result);
|
||||
seq->obufused -= result;
|
||||
while (seq->obufused > 0) {
|
||||
result = write(seq->fd, seq->obuf, seq->obufused);
|
||||
if (result < 0)
|
||||
return -errno;
|
||||
if (result < seq->obufused)
|
||||
memmove(seq->obuf, seq->obuf + result, seq->obufused - result);
|
||||
seq->obufused -= result;
|
||||
}
|
||||
return seq->obufused;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* extract the first event in output buffer
|
||||
*/
|
||||
int snd_seq_extract_output(snd_seq_t *seq, snd_seq_event_t **ev_res)
|
||||
{
|
||||
char *buf;
|
||||
int len, olen, err;
|
||||
|
||||
if (!seq || !ev_res)
|
||||
return -EINVAL;
|
||||
*ev_res = NULL;
|
||||
if ((olen = seq->obufused) > 0) {
|
||||
snd_seq_event_t *ev;
|
||||
ev = snd_seq_create_event();
|
||||
if (ev == NULL)
|
||||
return -ENOMEM;
|
||||
buf = seq->obuf;
|
||||
len = olen;
|
||||
err = snd_seq_decode_event(&buf, &len, ev);
|
||||
if (err < 0) {
|
||||
snd_seq_free_event(ev);
|
||||
return err;
|
||||
}
|
||||
if (len > 0)
|
||||
memmove(seq->obuf, seq->obuf + (olen - len), len);
|
||||
seq->obufused = len;
|
||||
*ev_res = ev;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* clear output buffer and remove events in sequencer queue
|
||||
*/
|
||||
int snd_seq_drain_output(snd_seq_t *seq)
|
||||
{
|
||||
snd_seq_remove_events_t rminfo;
|
||||
|
|
|
|||
|
|
@ -33,18 +33,14 @@
|
|||
/* direct passing (without queued) */
|
||||
void snd_seq_ev_set_direct(snd_seq_event_t *ev)
|
||||
{
|
||||
ev->flags &= ~SND_SEQ_DEST_MASK;
|
||||
ev->flags |= SND_SEQ_DEST_DIRECT;
|
||||
ev->queue = SND_SEQ_ADDRESS_UNKNOWN; /* XXX */
|
||||
ev->queue = SND_SEQ_QUEUE_DIRECT;
|
||||
}
|
||||
|
||||
/* queued on tick */
|
||||
void snd_seq_ev_schedule_tick(snd_seq_event_t *ev, int q, int relative,
|
||||
snd_seq_tick_time_t tick)
|
||||
{
|
||||
ev->flags &= ~(SND_SEQ_DEST_MASK | SND_SEQ_TIME_STAMP_MASK |
|
||||
SND_SEQ_TIME_MODE_MASK);
|
||||
ev->flags |= SND_SEQ_DEST_QUEUE;
|
||||
ev->flags &= ~(SND_SEQ_TIME_STAMP_MASK | SND_SEQ_TIME_MODE_MASK);
|
||||
ev->flags |= SND_SEQ_TIME_STAMP_TICK;
|
||||
ev->flags |= relative ? SND_SEQ_TIME_MODE_REL : SND_SEQ_TIME_MODE_ABS;
|
||||
ev->time.tick = tick;
|
||||
|
|
@ -55,9 +51,7 @@ void snd_seq_ev_schedule_tick(snd_seq_event_t *ev, int q, int relative,
|
|||
void snd_seq_ev_schedule_real(snd_seq_event_t *ev, int q, int relative,
|
||||
snd_seq_real_time_t *real)
|
||||
{
|
||||
ev->flags &= ~(SND_SEQ_DEST_MASK | SND_SEQ_TIME_STAMP_MASK |
|
||||
SND_SEQ_TIME_MODE_MASK);
|
||||
ev->flags |= SND_SEQ_DEST_QUEUE;
|
||||
ev->flags &= ~( SND_SEQ_TIME_STAMP_MASK | SND_SEQ_TIME_MODE_MASK);
|
||||
ev->flags |= SND_SEQ_TIME_STAMP_REAL;
|
||||
ev->flags |= relative ? SND_SEQ_TIME_MODE_REL : SND_SEQ_TIME_MODE_ABS;
|
||||
ev->time.real = *real;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue