diff --git a/include/seq.h b/include/seq.h new file mode 100644 index 00000000..ccd50bb0 --- /dev/null +++ b/include/seq.h @@ -0,0 +1,55 @@ +/**************************************************************************** + * * + * seq.h * + * Sequencer * + * * + ****************************************************************************/ + +#define SND_SEQ_OPEN_OUT (O_WRONLY) +#define SND_SEQ_OPEN_IN (O_RDONLY) +#define SND_SEQ_OPEN (O_RDWR) + +#ifdef __cplusplus +extern "C" { +#endif + +int snd_seq_open(void **handle, int mode); +int snd_seq_close(void *handle); +int snd_seq_file_descriptor(void *handle); +int snd_seq_block_mode(void *handle, int enable); +int snd_seq_client_id(void *handle); +int snd_seq_system_info(void *handle, snd_seq_system_info_t *info); +int snd_seq_get_client_info(void *handle, snd_seq_client_info_t *info); +int snd_seq_get_any_client_info(void *handle, int client, snd_seq_client_info_t *info); +int snd_seq_set_client_info(void *handle, snd_seq_client_info_t *info); +int snd_seq_create_port(void *handle, snd_seq_port_info_t *info); +int snd_seq_delete_port(void *handle, snd_seq_port_info_t *info); +int snd_seq_get_port_info(void *handle, int port, snd_seq_port_info_t *info); +int snd_seq_get_any_port_info(void *handle, int client, int port, snd_seq_port_info_t *info); +int snd_seq_set_port_info(void *handle, int port, snd_seq_port_info_t *info); +int snd_seq_subscribe_port(void *handle, snd_seq_port_subscribe_t *sub); +int snd_seq_unsubscribe_port(void *handle, snd_seq_port_subscribe_t *sub); +int snd_seq_get_queue_info(void *handle, int q, snd_seq_queue_info_t *queue); +int snd_seq_set_queue_info(void *handle, int q, snd_seq_queue_info_t *queue); +int snd_seq_get_queue_client(void *handle, int q, snd_seq_queue_client_t *queue); +int snd_seq_set_queue_client(void *handle, int q, snd_seq_queue_client_t *queue); +int snd_seq_alloc_queue(void *handle, snd_seq_queue_info_t *queue); +int snd_seq_free_queue(void *handle, int q); +/* event routines */ +snd_seq_event_t *snd_seq_create_event(void); +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(void *handle, snd_seq_event_t *ev); +int snd_seq_event_input(void *handle, snd_seq_event_t **ev); +int snd_seq_flush_output(void *handle); +int snd_seq_drain_output(void *handle); +int snd_seq_drain_input(void *handle); +/* misc */ +void snd_seq_set_bit(int nr, void *array); +int snd_seq_change_bit(int nr, void *array); +int snd_seq_get_bit(int nr, void *array); + +#ifdef __cplusplus +} +#endif + diff --git a/src/seq/Makefile.am b/src/seq/Makefile.am new file mode 100644 index 00000000..778893d3 --- /dev/null +++ b/src/seq/Makefile.am @@ -0,0 +1,7 @@ +EXTRA_LTLIBRARIES=libseq.la + +libseq_la_SOURCES = seq.c +all: libseq.la + + +INCLUDES=-I$(top_srcdir)/include diff --git a/src/seq/seq.c b/src/seq/seq.c new file mode 100644 index 00000000..d3e3c1b0 --- /dev/null +++ b/src/seq/seq.c @@ -0,0 +1,670 @@ +/* + * Sequencer Interface - main file + * Copyright (c) 1998 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "asoundlib.h" + +#define SND_FILE_SEQ "/dev/snd/seq" +#define SND_SEQ_VERSION_MAX SND_PROTOCOL_VERSION( 0, 0, 1 ) +#define SND_SEQ_OBUF_SIZE (16*1024) /* should be configurable */ +#define SND_SEQ_IBUF_SIZE (4*1024) /* should be configurable */ + +typedef struct snd_stru_seq_cell { + snd_seq_event_t ev; + struct snd_stru_seq_cell *next; +} snd_seq_cell_t; + +typedef struct { + int client; /* client number */ + int fd; + /* buffers */ + char *obuf; /* output buffer */ + int obufsize; /* output buffer size */ + int obufused; /* output buffer used size */ + char *ibuf; /* input buffer */ + int ibufsize; /* input buffer size */ + /* input queue */ + int cells; + snd_seq_cell_t *head; + snd_seq_cell_t *tail; +} snd_seq_t; + +int snd_seq_open(void **handle, int mode) +{ + int fd, ver, client, flg; + char filename[32]; + snd_seq_t *seq; + + *handle = NULL; + sprintf(filename, SND_FILE_SEQ); + if ((fd = open(filename, mode)) < 0) + return -errno; + if (ioctl(fd, SND_SEQ_IOCTL_PVERSION, &ver) < 0) { + close(fd); + return -errno; + } + if (SND_PROTOCOL_UNCOMPATIBLE(ver, SND_SEQ_VERSION_MAX)) { + close(fd); + return -SND_ERROR_UNCOMPATIBLE_VERSION; + } + if (ioctl(fd, SND_SEQ_IOCTL_CLIENT_ID, &client) < 0) { + close(fd); + return -errno; + } + seq = (snd_seq_t *) calloc(1, sizeof(snd_seq_t)); + if (seq == NULL) { + close(fd); + return -ENOMEM; + } + seq->client = client; + seq->fd = fd; + flg = 3; + if (mode == SND_SEQ_OPEN_OUT || mode == SND_SEQ_OPEN) + seq->obuf = (char *) malloc(seq->obufsize = SND_SEQ_OBUF_SIZE); + else + flg &= ~1; + if (mode == SND_SEQ_OPEN_IN || mode == SND_SEQ_OPEN) + seq->ibuf = (char *) malloc(seq->ibufsize = SND_SEQ_IBUF_SIZE); + else + flg &= ~2; + if ((!seq->obuf && (flg & 1)) || (!seq->ibuf && (flg & 2))) { + if (seq->obuf) + free(seq->obuf); + if (seq->ibuf) + free(seq->ibuf); + free(seq); + return -ENOMEM; + } + *handle = seq; + return 0; +} + +int snd_seq_close(void *handle) +{ + snd_seq_t *seq; + int res; + + seq = (snd_seq_t *) handle; + if (!seq) + return -EINVAL; + res = close(seq->fd) < 0 ? -errno : 0; + free(seq); + return res; +} + +int snd_seq_file_descriptor(void *handle) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq) + return -EINVAL; + return seq->fd; +} + +int snd_seq_block_mode(void *handle, int enable) +{ + snd_seq_t *seq; + long flags; + + seq = (snd_seq_t *) handle; + if (!seq) + return -EINVAL; + if (fcntl(seq->fd, F_GETFL, &flags) < 0) + return -errno; + if (enable) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + if (fcntl(seq->fd, F_SETFL, &flags) < 0) + return -errno; + return 0; +} + +int snd_seq_client_id(void *handle) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq) + return -EINVAL; + return seq->client; +} + +int snd_seq_system_info(void *handle, snd_seq_system_info_t * info) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !info) + return -EINVAL; + if (ioctl(seq->fd, SND_SEQ_IOCTL_SYSTEM_INFO, info) < 0) + return -errno; + return 0; +} + +int snd_seq_get_client_info(void *handle, snd_seq_client_info_t * info) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !info) + return -EINVAL; + return snd_seq_get_any_client_info(handle, seq->client, info); +} + +int snd_seq_get_any_client_info(void *handle, int client, snd_seq_client_info_t * info) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !info || client < 0) + return -EINVAL; + bzero(info, sizeof(snd_seq_client_info_t)); + info->client = client; + if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_CLIENT_INFO, info) < 0) + return -errno; + return 0; +} + +int snd_seq_set_client_info(void *handle, snd_seq_client_info_t * info) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !info) + return -EINVAL; + if (ioctl(seq->fd, SND_SEQ_IOCTL_SET_CLIENT_INFO, info) < 0) + return -errno; + return 0; +} + +int snd_seq_create_port(void *handle, snd_seq_port_info_t * port) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !port) + return -EINVAL; + port->client = seq->client; + if (ioctl(seq->fd, SND_SEQ_IOCTL_CREATE_PORT, port) < 0) + return -errno; + return 0; +} + +int snd_seq_delete_port(void *handle, snd_seq_port_info_t * port) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !port) + return -EINVAL; + port->client = seq->client; + if (ioctl(seq->fd, SND_SEQ_IOCTL_DELETE_PORT, port) < 0) + return -errno; + return 0; +} + +int snd_seq_get_port_info(void *handle, int port, snd_seq_port_info_t * info) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !info || port < 0) + return -EINVAL; + return snd_seq_get_any_port_info(handle, seq->client, port, info); +} + +int snd_seq_get_any_port_info(void *handle, int client, int port, snd_seq_port_info_t * info) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !info || client < 0 || port < 0) + return -EINVAL; + bzero(info, sizeof(snd_seq_port_info_t)); + info->client = client; + info->port = port; + if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_PORT_INFO, info) < 0) + return -errno; + return 0; +} + +int snd_seq_set_port_info(void *handle, int port, snd_seq_port_info_t * info) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !info || port < 0) + return -EINVAL; + info->port = port; + if (ioctl(seq->fd, SND_SEQ_IOCTL_SET_PORT_INFO, info) < 0) + return -errno; + return 0; +} + +int snd_seq_subscribe_port(void *handle, snd_seq_port_subscribe_t * sub) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !sub) + return -EINVAL; + if (ioctl(seq->fd, SND_SEQ_IOCTL_SUBSCRIBE_PORT, sub) < 0) + return -errno; + return 0; +} + +int snd_seq_unsubscribe_port(void *handle, snd_seq_port_subscribe_t * sub) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !sub) + return -EINVAL; + if (ioctl(seq->fd, SND_SEQ_IOCTL_UNSUBSCRIBE_PORT, sub) < 0) + return -errno; + return 0; +} + +int snd_seq_get_queue_info(void *handle, int q, snd_seq_queue_info_t * info) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !info) + return -EINVAL; + bzero(info, sizeof(snd_seq_queue_info_t)); + info->queue = q; + if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_QUEUE_INFO, info) < 0) + return -errno; + return 0; +} + +int snd_seq_set_queue_info(void *handle, int q, snd_seq_queue_info_t * info) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !info) + return -EINVAL; + info->queue = q; + if (ioctl(seq->fd, SND_SEQ_IOCTL_SET_QUEUE_INFO, info) < 0) + return -errno; + return 0; +} + +int snd_seq_get_queue_client(void *handle, int q, snd_seq_queue_client_t * info) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !info) + return -EINVAL; + bzero(info, sizeof(snd_seq_queue_client_t)); + info->queue = q; + info->client = seq->client; + if (ioctl(seq->fd, SND_SEQ_IOCTL_GET_QUEUE_CLIENT, info) < 0) + return -errno; + return 0; +} + +int snd_seq_set_queue_client(void *handle, int q, snd_seq_queue_client_t * info) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !info) + return -EINVAL; + info->queue = q; + info->client = seq->client; + if (ioctl(seq->fd, SND_SEQ_IOCTL_SET_QUEUE_CLIENT, info) < 0) + return -errno; + return 0; +} + +int snd_seq_alloc_queue(void *handle, snd_seq_queue_info_t *info) +{ + int i, err; + snd_seq_system_info_t sysinfo; + snd_seq_queue_info_t inf; + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq) + return -EINVAL; + if ((err = snd_seq_system_info(handle, &sysinfo))<0) + return err; + for (i = 0; i < sysinfo.queues; i++) { + if ((err = snd_seq_get_queue_info(handle, i, &inf))<0) + continue; + if (inf.locked) + continue; + inf.locked = 1; + inf.owner = seq->client; + if ((err = snd_seq_set_queue_info(handle, i, &inf))<0) + continue; + if (info) + memcpy(info, &inf, sizeof(snd_seq_queue_info_t)); + return i; + } + return -EBUSY; +} + +int snd_seq_free_queue(void *handle, int q) +{ + int err; + snd_seq_t *seq; + snd_seq_queue_info_t inf; + + seq = (snd_seq_t *) handle; + if (!seq) + return -EINVAL; + if ((err = snd_seq_get_queue_info(handle, q, &inf))<0) + return err; + if (inf.locked && inf.owner == seq->client) { + inf.locked = 0; + inf.owner = -1; + if ((err = snd_seq_set_queue_info(handle, q, &inf))<0) + return err; + } + return 0; +} + +snd_seq_event_t *snd_seq_create_event(void) +{ + return (snd_seq_event_t *) calloc(1, sizeof(snd_seq_event_t)); +} + +static int snd_seq_free_event_static(snd_seq_event_t *ev) +{ + if (!ev) + return -EINVAL; + switch (ev->flags & SND_SEQ_EVENT_LENGTH_MASK) { + case SND_SEQ_EVENT_LENGTH_VARIABLE: + if (ev->data.ext.ptr) + free(ev->data.ext.ptr); + break; + } + return 0; +} + +int snd_seq_free_event(snd_seq_event_t *ev) +{ + int err; + + if ((err = snd_seq_free_event_static(ev))<0) + return err; + free(ev); + return 0; +} + +int snd_seq_event_length(snd_seq_event_t *ev) +{ + int len = sizeof(snd_seq_event_t); + + if (!ev) + return -EINVAL; + switch (ev->flags & SND_SEQ_EVENT_LENGTH_MASK) { + case SND_SEQ_EVENT_LENGTH_VARIABLE: + len += ev->data.ext.len; + break; + } + return len; +} + +int snd_seq_event_output(void *handle, snd_seq_event_t *ev) +{ + int len; + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq || !ev) + return -EINVAL; + len = snd_seq_event_length(ev); + if ((seq->obufsize - seq->obufused) < len) { + snd_seq_flush_output(handle); + if ((seq->obufsize - seq->obufused) < len) + return -ENOMEM; + } + memcpy(seq->obuf + seq->obufused, ev, sizeof(snd_seq_event_t)); + seq->obufused += sizeof(snd_seq_event_t); + switch (ev->flags & SND_SEQ_EVENT_LENGTH_MASK) { + case SND_SEQ_EVENT_LENGTH_VARIABLE: + memcpy(seq->obuf + seq->obufused, ev->data.ext.ptr, ev->data.ext.len); + seq->obufused += ev->data.ext.len; + break; + } + return seq->obufused; +} + +static snd_seq_cell_t *snd_seq_create_cell(snd_seq_event_t *ev) +{ + snd_seq_cell_t *cell; + + cell = (snd_seq_cell_t *) calloc(1, sizeof(snd_seq_cell_t)); + if (!cell) + return NULL; + if (ev) + memcpy(&cell->ev, ev, sizeof(snd_seq_event_t)); + return cell; +} + +static int snd_seq_free_cell(snd_seq_cell_t *cell) +{ + if (!cell) + return -EINVAL; + snd_seq_free_event_static(&cell->ev); + free(cell); + return 0; +} + +static snd_seq_cell_t *snd_seq_input_cell_out(snd_seq_t *seq) +{ + snd_seq_cell_t *cell; + + if (!seq) + return NULL; + if (seq->head) { + cell = seq->head; + seq->head = cell->next; + seq->cells--; + if (!seq->head) + seq->tail = NULL; + return cell; + } + return NULL; +} + +static int snd_seq_input_cell_in(snd_seq_t *seq, snd_seq_cell_t *cell) +{ + if (!seq) + return -EINVAL; + cell->next = NULL; + if (!seq->tail) { + seq->head = seq->tail = cell; + } else { + seq->tail->next = cell; + seq->tail = cell; + } + seq->cells++; + return 0; +} + +static int snd_seq_input_cell_available(snd_seq_t *seq) +{ + if (!seq) + return -EINVAL; + return seq->cells > 0; +} + +static int snd_seq_decode_event(char **buf, int *len, snd_seq_event_t *ev) +{ + if (!ev || !buf || !*buf || !len ) + return -EINVAL; + if (*len < sizeof(snd_seq_event_t)) { + *len = 0; + return -ENOENT; + } + memcpy(ev, *buf, sizeof(snd_seq_event_t)); + *buf += sizeof(snd_seq_event_t); + *len -= sizeof(snd_seq_event_t); + switch (ev->flags & SND_SEQ_EVENT_LENGTH_MASK) { + case SND_SEQ_EVENT_LENGTH_VARIABLE: + if (*len < ev->data.ext.len) { + *len = 0; + return -ENOENT; + } + if (ev->data.ext.len > 0) { + ev->data.ext.ptr = (char *) malloc(ev->data.ext.len); + if (!(ev->data.ext.ptr)) { + *buf += ev->data.ext.len; + *len -= ev->data.ext.len; + return -ENOENT; + } + memcpy(ev->data.ext.ptr, *buf, ev->data.ext.len); + *buf += ev->data.ext.len; + *len -= ev->data.ext.len; + } + break; + } + return 0; +} + +/* + * Current implementation uses FIFO cache. + */ + +int snd_seq_event_input(void *handle, snd_seq_event_t **ev) +{ + snd_seq_t *seq; + snd_seq_cell_t *cell; + char *buf; + int count; + + *ev = NULL; + seq = (snd_seq_t *) handle; + if (!seq) + return -EINVAL; + if (snd_seq_input_cell_available(seq)) { + *ev = snd_seq_create_event(); + if (*ev == NULL) + return -ENOMEM; + cell = snd_seq_input_cell_out(seq); + memcpy(*ev, &cell->ev, sizeof(snd_seq_event_t)); + return seq->cells; + } + count = read(seq->fd, seq->ibuf, seq->ibufsize); + if (count < 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); + } + } + } + return seq->cells; +} + +int snd_seq_flush_output(void *handle) +{ + snd_seq_t *seq; + int result; + + seq = (snd_seq_t *) handle; + if (!seq) + return -EINVAL; + if (seq->obufused <= 0) + return 0; + result = write(seq->fd, seq->obuf, seq->obufused); + if (result < 0) { + snd_seq_drain_output(handle); + return -errno; + } + if (result < seq->obufused) + memmove(seq->obuf, seq->obuf + result, seq->obufused - result); + seq->obufused -= result; + return seq->obufused; +} + +int snd_seq_drain_output(void *handle) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq) + return -EINVAL; + seq->obufused = 0; + return 0; +} + +int snd_seq_drain_input(void *handle) +{ + snd_seq_t *seq; + + seq = (snd_seq_t *) handle; + if (!seq) + return -EINVAL; + while (snd_seq_input_cell_available(seq)) + snd_seq_free_cell(snd_seq_input_cell_out(seq)); + return 0; +} + +void snd_seq_set_bit(int nr, void *array) +{ + ((unsigned int *)array)[nr >> 5] |= 1UL << (nr & 31); +} + +int snd_seq_change_bit(int nr, void *array) +{ + int result; + + result = ((((unsigned int *)array)[nr >> 5]) & (1UL << (nr & 31))) ? 1 : 0; + ((unsigned int *)array)[nr >> 5] |= 1UL << (nr & 31); + return result; +} + +int snd_seq_get_bit(int nr, void *array) +{ + return ((((unsigned int *)array)[nr >> 5]) & (1UL << (nr & 31))) ? 1 : 0; +} +