]> git.alsa-project.org Git - alsa-lib.git/commitdiff
added virtual rawmidi plugin.
authorTakashi Iwai <tiwai@suse.de>
Tue, 29 Jul 2003 17:15:34 +0000 (17:15 +0000)
committerTakashi Iwai <tiwai@suse.de>
Tue, 29 Jul 2003 17:15:34 +0000 (17:15 +0000)
src/rawmidi/Makefile.am
src/rawmidi/rawmidi_local.h
src/rawmidi/rawmidi_symbols.c
src/rawmidi/rawmidi_virt.c [new file with mode: 0644]

index 5e7945ed011aaa70b9b89d44118f5fdc44b1ebc8..45cb83a3ef7774e35ab873facae8514b1924136e 100644 (file)
@@ -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
index 2e110460b0f3caa3cc918547c0d08a8cf65e6156..20e24c00364a204dfb4c27e8e171ca09806382ea 100644 (file)
@@ -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);
index 951742eaca3c6b977923a3e6322d07005b197a4c..a15dd5724c525f16621364a63eb6c07a06df74d9 100644 (file)
 #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 (file)
index 0000000..2e25c07
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ *  RawMIDI - Virtual (sequencer mode)
+ *  Copyright (c) 2003 by Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *   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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#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