mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -04:00
added virtual rawmidi plugin.
This commit is contained in:
parent
118bf89508
commit
e332fc0b9b
4 changed files with 472 additions and 2 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
464
src/rawmidi/rawmidi_virt.c
Normal file
464
src/rawmidi/rawmidi_virt.c
Normal file
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue