From e332fc0b9b97db0cc305d4f1f7b08c95a9bbde85 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Jul 2003 17:15:34 +0000 Subject: [PATCH] added virtual rawmidi plugin. --- src/rawmidi/Makefile.am | 2 +- src/rawmidi/rawmidi_local.h | 4 + src/rawmidi/rawmidi_symbols.c | 4 +- src/rawmidi/rawmidi_virt.c | 464 ++++++++++++++++++++++++++++++++++ 4 files changed, 472 insertions(+), 2 deletions(-) create mode 100644 src/rawmidi/rawmidi_virt.c diff --git a/src/rawmidi/Makefile.am b/src/rawmidi/Makefile.am index 5e7945ed..45cb83a3 100644 --- a/src/rawmidi/Makefile.am +++ b/src/rawmidi/Makefile.am @@ -1,6 +1,6 @@ EXTRA_LTLIBRARIES=librawmidi.la -librawmidi_la_SOURCES = rawmidi.c rawmidi_hw.c rawmidi_symbols.c +librawmidi_la_SOURCES = rawmidi.c rawmidi_hw.c rawmidi_symbols.c rawmidi_virt.c noinst_HEADERS = rawmidi_local.h all: librawmidi.la diff --git a/src/rawmidi/rawmidi_local.h b/src/rawmidi/rawmidi_local.h index 2e110460..20e24c00 100644 --- a/src/rawmidi/rawmidi_local.h +++ b/src/rawmidi/rawmidi_local.h @@ -53,3 +53,7 @@ struct _snd_rawmidi { int snd_rawmidi_hw_open(snd_rawmidi_t **input, snd_rawmidi_t **output, const char *name, int card, int device, int subdevice, int mode); + +int snd_rawmidi_virt_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, + const char *name, snd_seq_t *seq_handle, int port, + int merge, int mode); diff --git a/src/rawmidi/rawmidi_symbols.c b/src/rawmidi/rawmidi_symbols.c index 951742ea..a15dd572 100644 --- a/src/rawmidi/rawmidi_symbols.c +++ b/src/rawmidi/rawmidi_symbols.c @@ -21,9 +21,11 @@ #ifndef PIC extern const char *_snd_module_rawmidi_hw; +extern const char *_snd_module_rawmidi_virt; static const char **snd_rawmidi_open_objects[] = { - &_snd_module_rawmidi_hw + &_snd_module_rawmidi_hw, + &_snd_module_rawmidi_virt }; void *snd_rawmidi_open_symbols(void) diff --git a/src/rawmidi/rawmidi_virt.c b/src/rawmidi/rawmidi_virt.c new file mode 100644 index 00000000..2e25c07c --- /dev/null +++ b/src/rawmidi/rawmidi_virt.c @@ -0,0 +1,464 @@ +/* + * RawMIDI - Virtual (sequencer mode) + * Copyright (c) 2003 by Takashi Iwai + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include "rawmidi_local.h" +#include "seq.h" +#include "seq_midi_event.h" + +#ifndef PIC +/* entry for static linking */ +const char *_snd_module_rawmidi_virt = ""; +#endif + + +typedef struct { + int open; + + snd_seq_t *handle; + int port; + + snd_midi_event_t *midi_event; + + snd_seq_event_t *in_event; + int in_buf_size; + int in_buf_ofs; + char *in_buf_ptr; + char in_tmp_buf[16]; + + snd_seq_event_t out_event; + int pending; +} snd_rawmidi_virt_t; + +static int snd_rawmidi_virt_close(snd_rawmidi_t *rmidi) +{ + snd_rawmidi_virt_t *virt = rmidi->private_data; + virt->open--; + if (virt->open) + return 0; + snd_seq_close(virt->handle); + if (virt->midi_event) + snd_midi_event_free(virt->midi_event); + free(virt); + return 0; +} + +static int snd_rawmidi_virt_nonblock(snd_rawmidi_t *rmidi, int nonblock) +{ + snd_rawmidi_virt_t *virt = rmidi->private_data; + + return snd_seq_nonblock(virt->handle, nonblock); +} + +static int snd_rawmidi_virt_info(snd_rawmidi_t *rmidi, snd_rawmidi_info_t * info) +{ + // snd_rawmidi_virt_t *virt = rmidi->private_data; + + info->stream = rmidi->stream; + /* FIXME: what values should be there? */ + info->card = 0; + info->device = 0; + info->subdevice = 0; + info->flags = 0; + strcpy(info->id, "Virtual"); + strcpy(info->name, "Virtual RawMIDI"); + strcpy(info->subname, "Virtual RawMIDI"); + info->subdevices_count = 1; + info->subdevices_avail = 0; + return 0; +} + +static int snd_rawmidi_virt_input_params(snd_rawmidi_virt_t *virt, snd_rawmidi_params_t *params) +{ + int err; + + // snd_rawmidi_drain_input(substream); + if (params->buffer_size < sizeof(snd_seq_event_t) || + params->buffer_size > 1024L * 1024L) { + return -EINVAL; + } + if (params->buffer_size != snd_seq_get_input_buffer_size(virt->handle)) { + err = snd_seq_set_input_buffer_size(virt->handle, params->buffer_size); + if (err < 0) + return err; + params->buffer_size = snd_seq_get_input_buffer_size(virt->handle); + /* FIXME: input pool size? */ + } + return 0; +} + + +static int snd_rawmidi_virt_output_params(snd_rawmidi_virt_t *virt, snd_rawmidi_params_t *params) +{ + int err; + + // snd_rawmidi_drain_output(substream); + if (params->buffer_size < sizeof(snd_seq_event_t) || + params->buffer_size > 1024L * 1024L) { + return -EINVAL; + } + if (params->buffer_size != snd_seq_get_output_buffer_size(virt->handle)) { + err = snd_seq_set_output_buffer_size(virt->handle, params->buffer_size); + if (err < 0) + return err; + params->buffer_size = snd_seq_get_output_buffer_size(virt->handle); + } + return 0; +} + + +static int snd_rawmidi_virt_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * params) +{ + snd_rawmidi_virt_t *virt = rmidi->private_data; + params->stream = rmidi->stream; + + if (rmidi->stream == SND_RAWMIDI_STREAM_INPUT) + return snd_rawmidi_virt_input_params(virt, params); + else + return snd_rawmidi_virt_output_params(virt, params); +} + +static int snd_rawmidi_virt_status(snd_rawmidi_t *rmidi, snd_rawmidi_status_t * status) +{ + // snd_rawmidi_virt_t *virt = rmidi->private_data; + memset(status, 0, sizeof(*status)); + status->stream = rmidi->stream; + return 0; +} + +static int snd_rawmidi_virt_drop(snd_rawmidi_t *rmidi) +{ + snd_rawmidi_virt_t *virt = rmidi->private_data; + if (rmidi->stream == SND_RAWMIDI_STREAM_OUTPUT) { + snd_seq_drop_output(virt->handle); + snd_midi_event_reset_encode(virt->midi_event); + virt->pending = 0; + } else { + snd_seq_drop_input(virt->handle); + snd_midi_event_reset_decode(virt->midi_event); + virt->in_buf_ofs = 0; + } + return 0; +} + +static int snd_rawmidi_virt_drain(snd_rawmidi_t *rmidi) +{ + snd_rawmidi_virt_t *virt = rmidi->private_data; + int err; + + if (rmidi->stream == SND_RAWMIDI_STREAM_OUTPUT) { + if (virt->pending) { + err = snd_seq_event_output(virt->handle, &virt->out_event); + if (err < 0) + return err; + virt->pending = 0; + } + snd_seq_drain_output(virt->handle); + snd_seq_sync_output_queue(virt->handle); + } + return snd_rawmidi_virt_drop(rmidi); +} + +static ssize_t snd_rawmidi_virt_write(snd_rawmidi_t *rmidi, const void *buffer, size_t size) +{ + snd_rawmidi_virt_t *virt = rmidi->private_data; + ssize_t result = 0; + ssize_t size1; + int err; + + if (virt->pending) { + err = snd_seq_event_output(virt->handle, &virt->out_event); + if (err < 0) { + if (err != -EAGAIN) + /* we got some fatal error. removing this event + * at the next time + */ + virt->pending = 0; + return err; + } + virt->pending = 0; + } + + while (size > 0) { + size1 = snd_midi_event_encode(virt->midi_event, buffer, size, &virt->out_event); + if (size1 <= 0) + break; + if (virt->out_event.type == SND_SEQ_EVENT_NONE) + continue; + size -= size1; + result += size1; + buffer += size1; + snd_seq_ev_set_subs(&virt->out_event); + snd_seq_ev_set_source(&virt->out_event, virt->port); + snd_seq_ev_set_direct(&virt->out_event); + err = snd_seq_event_output(virt->handle, &virt->out_event); + if (err < 0) { + virt->pending = 1; + return result > 0 ? result : err; + } + } + + if (result > 0) + snd_seq_drain_output(virt->handle); + + return result; +} + +static ssize_t snd_rawmidi_virt_read(snd_rawmidi_t *rmidi, void *buffer, size_t size) +{ + snd_rawmidi_virt_t *virt = rmidi->private_data; + ssize_t result = 0; + int size1, err; + + while (size > 0) { + if (! virt->in_buf_ofs) { + err = snd_seq_event_input_pending(virt->handle, 1); + if (err <= 0 && result > 0) + return result; + err = snd_seq_event_input(virt->handle, &virt->in_event); + if (err < 0) + return result > 0 ? result : err; + + if (virt->in_event->type == SND_SEQ_EVENT_SYSEX) { + virt->in_buf_ptr = virt->in_event->data.ext.ptr; + virt->in_buf_size = virt->in_event->data.ext.len; + } else { + virt->in_buf_ptr = virt->in_tmp_buf; + virt->in_buf_size = snd_midi_event_decode(virt->midi_event, + virt->in_tmp_buf, + sizeof(virt->in_tmp_buf), + virt->in_event); + } + if (virt->in_buf_size <= 0) + continue; + } + size1 = virt->in_buf_size - virt->in_buf_ofs; + if ((size_t)size1 > size) { + virt->in_buf_ofs += size1 - size; + memcpy(buffer, virt->in_buf_ptr, size); + result += size; + break; + } + memcpy(buffer, virt->in_buf_ptr + virt->in_buf_ofs, size1); + size -= size1; + result += size1; + buffer += size1; + virt->in_buf_ofs = 0; + } + + return result; +} + +snd_rawmidi_ops_t snd_rawmidi_virt_ops = { + .close = snd_rawmidi_virt_close, + .nonblock = snd_rawmidi_virt_nonblock, + .info = snd_rawmidi_virt_info, + .params = snd_rawmidi_virt_params, + .status = snd_rawmidi_virt_status, + .drop = snd_rawmidi_virt_drop, + .drain = snd_rawmidi_virt_drain, + .write = snd_rawmidi_virt_write, + .read = snd_rawmidi_virt_read, +}; + + +/*! \page rawmidi RawMidi interface + +\section rawmidi_virt Virtual RawMidi interface + +The "virt" plugin creates a virtual RawMidi instance on the ALSA sequencer, +which can be accessed through the connection of the sequencer ports. +There is no connection established as default. + +For creating a virtual RawMidi instance, pass "virt" as its name at +creation. + +Example: +\code +snd_rawmidi_open(&read_handle, &write_handle, "virt", 0); +\endcode + +*/ + +int snd_rawmidi_virt_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, + const char *name, snd_seq_t *seq_handle, int port, + int merge, int mode) +{ + int err; + snd_rawmidi_t *rmidi; + snd_rawmidi_virt_t *virt = NULL; + struct pollfd pfd; + + if (inputp) + *inputp = 0; + if (outputp) + *outputp = 0; + + virt = calloc(1, sizeof(*virt)); + if (virt == NULL) { + err = -ENOMEM; + goto _err; + } + virt->handle = seq_handle; + virt->port = port; + err = snd_midi_event_new(256, &virt->midi_event); + if (err < 0) + goto _err; + snd_midi_event_init(virt->midi_event); + snd_midi_event_no_status(virt->midi_event, !merge); + + if (inputp) { + rmidi = calloc(1, sizeof(*rmidi)); + if (rmidi == NULL) { + err = -ENOMEM; + goto _err; + } + if (name) + rmidi->name = strdup(name); + rmidi->type = SND_RAWMIDI_TYPE_VIRT; + rmidi->stream = SND_RAWMIDI_STREAM_INPUT; + rmidi->mode = mode; + err = snd_seq_poll_descriptors(seq_handle, &pfd, 1, POLLIN); + if (err < 0) + goto _err; + rmidi->poll_fd = pfd.fd; + rmidi->ops = &snd_rawmidi_virt_ops; + rmidi->private_data = virt; + virt->open++; + *inputp = rmidi; + } + if (outputp) { + rmidi = calloc(1, sizeof(*rmidi)); + if (rmidi == NULL) { + err = -ENOMEM; + goto _err; + } + if (name) + rmidi->name = strdup(name); + rmidi->type = SND_RAWMIDI_TYPE_VIRT; + rmidi->stream = SND_RAWMIDI_STREAM_OUTPUT; + rmidi->mode = mode; + err = snd_seq_poll_descriptors(seq_handle, &pfd, 1, POLLOUT); + if (err < 0) + goto _err; + rmidi->poll_fd = pfd.fd; + rmidi->ops = &snd_rawmidi_virt_ops; + rmidi->private_data = virt; + virt->open++; + *outputp = rmidi; + } + + return 0; + + _err: + if (seq_handle) + snd_seq_close(seq_handle); + if (virt->midi_event) + snd_midi_event_free(virt->midi_event); + if (virt) + free(virt); + if (inputp && *inputp) + free(*inputp); + if (outputp && *outputp) + free(*outputp); + return err; +} + +int _snd_rawmidi_virt_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, + char *name, snd_config_t *root ATTRIBUTE_UNUSED, + snd_config_t *conf, int mode) +{ + snd_config_iterator_t i, next; + const char *slave_str = NULL; + int err; + int streams, seq_mode; + int merge = 1; + int port; + unsigned int caps; + snd_seq_t *seq_handle; + + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + if (snd_config_get_id(n, &id) < 0) + continue; + if (strcmp(id, "comment") == 0) + continue; + if (strcmp(id, "type") == 0) + continue; + if (strcmp(id, "slave") == 0) { + err = snd_config_get_string(n, &slave_str); + if (err < 0) + return err; + continue; + } + if (strcmp(id, "merge") == 0) { + merge = snd_config_get_bool(n); + continue; + } + return -EINVAL; + } + + streams = 0; + if (inputp) + streams |= SND_SEQ_OPEN_INPUT; + if (outputp) + streams |= SND_SEQ_OPEN_OUTPUT; + if (! streams) + return -EINVAL; + + seq_mode = 0; + if (mode & SND_RAWMIDI_NONBLOCK) + seq_mode |= O_NONBLOCK; + + if (! slave_str) + slave_str = "default"; + err = snd_seq_open_lconf(&seq_handle, slave_str, streams, seq_mode, root); + if (err < 0) + return err; + + caps = 0; + if (inputp) + caps |= SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SYNC_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; + if (outputp) + caps |= SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SYNC_READ | SND_SEQ_PORT_CAP_SUBS_READ; + if (inputp && outputp) + pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; + + port = snd_seq_create_simple_port(seq_handle, "Virtual RawMIDI", + caps, SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC); + if (port < 0) { + snd_seq_close(seq_handle); + return port; + } + + return snd_rawmidi_virt_open(inputp, outputp, name, seq_handle, port, + merge, mode); +} + +#ifndef DOC_HIDDEN +SND_DLSYM_BUILD_VERSION(_snd_rawmidi_virt_open, SND_RAWMIDI_DLSYM_VERSION); +#endif -- 2.47.1