Home | History | Annotate | Download | only in rawmidi
      1 /*
      2  *  RawMIDI - Virtual (sequencer mode)
      3  *  Copyright (c) 2003 by Takashi Iwai <tiwai (at) suse.de>
      4  *
      5  *
      6  *   This library is free software; you can redistribute it and/or modify
      7  *   it under the terms of the GNU Lesser General Public License as
      8  *   published by the Free Software Foundation; either version 2.1 of
      9  *   the License, or (at your option) any later version.
     10  *
     11  *   This program is distributed in the hope that it will be useful,
     12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  *   GNU Lesser General Public License for more details.
     15  *
     16  *   You should have received a copy of the GNU Lesser General Public
     17  *   License along with this library; if not, write to the Free Software
     18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
     19  *
     20  */
     21 
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 #include <unistd.h>
     25 #include <string.h>
     26 #include <fcntl.h>
     27 #include <sys/ioctl.h>
     28 #include "rawmidi_local.h"
     29 #include "seq.h"
     30 #include "seq_midi_event.h"
     31 
     32 #ifndef PIC
     33 /* entry for static linking */
     34 const char *_snd_module_rawmidi_virt = "";
     35 #endif
     36 
     37 
     38 #ifndef DOC_HIDDEN
     39 typedef struct {
     40 	int open;
     41 
     42 	snd_seq_t *handle;
     43 	int port;
     44 
     45 	snd_midi_event_t *midi_event;
     46 
     47 	snd_seq_event_t *in_event;
     48 	int in_buf_size;
     49 	int in_buf_ofs;
     50 	char *in_buf_ptr;
     51 	char in_tmp_buf[16];
     52 
     53 	snd_seq_event_t out_event;
     54 	int pending;
     55 } snd_rawmidi_virtual_t;
     56 
     57 int _snd_seq_open_lconf(snd_seq_t **seqp, const char *name,
     58 			int streams, int mode, snd_config_t *lconf,
     59 			snd_config_t *parent_conf);
     60 #endif
     61 
     62 static int snd_rawmidi_virtual_close(snd_rawmidi_t *rmidi)
     63 {
     64 	snd_rawmidi_virtual_t *virt = rmidi->private_data;
     65 	virt->open--;
     66 	if (virt->open)
     67 		return 0;
     68 	snd_seq_close(virt->handle);
     69 	if (virt->midi_event)
     70 		snd_midi_event_free(virt->midi_event);
     71 	free(virt);
     72 	return 0;
     73 }
     74 
     75 static int snd_rawmidi_virtual_nonblock(snd_rawmidi_t *rmidi, int nonblock)
     76 {
     77 	snd_rawmidi_virtual_t *virt = rmidi->private_data;
     78 
     79 	return snd_seq_nonblock(virt->handle, nonblock);
     80 }
     81 
     82 static int snd_rawmidi_virtual_info(snd_rawmidi_t *rmidi, snd_rawmidi_info_t * info)
     83 {
     84 	// snd_rawmidi_virtual_t *virt = rmidi->private_data;
     85 
     86 	info->stream = rmidi->stream;
     87 	/* FIXME: what values should be there? */
     88 	info->card = 0;
     89 	info->device = 0;
     90 	info->subdevice = 0;
     91 	info->flags = 0;
     92 	strcpy((char *)info->id, "Virtual");
     93 	strcpy((char *)info->name, "Virtual RawMIDI");
     94 	strcpy((char *)info->subname, "Virtual RawMIDI");
     95 	info->subdevices_count = 1;
     96 	info->subdevices_avail = 0;
     97 	return 0;
     98 }
     99 
    100 static int snd_rawmidi_virtual_input_params(snd_rawmidi_virtual_t *virt, snd_rawmidi_params_t *params)
    101 {
    102 	int err;
    103 
    104 	// snd_rawmidi_drain_input(substream);
    105 	if (params->buffer_size < sizeof(snd_seq_event_t) ||
    106 	    params->buffer_size > 1024L * 1024L) {
    107 		return -EINVAL;
    108 	}
    109 	if (params->buffer_size != snd_seq_get_input_buffer_size(virt->handle)) {
    110 		err = snd_seq_set_input_buffer_size(virt->handle, params->buffer_size);
    111 		if (err < 0)
    112 			return err;
    113 		params->buffer_size = snd_seq_get_input_buffer_size(virt->handle);
    114 		/* FIXME: input pool size? */
    115 	}
    116 	return 0;
    117 }
    118 
    119 
    120 static int snd_rawmidi_virtual_output_params(snd_rawmidi_virtual_t *virt, snd_rawmidi_params_t *params)
    121 {
    122 	int err;
    123 
    124 	// snd_rawmidi_drain_output(substream);
    125 	if (params->buffer_size < sizeof(snd_seq_event_t) ||
    126 	    params->buffer_size > 1024L * 1024L) {
    127 		return -EINVAL;
    128 	}
    129 	if (params->buffer_size != snd_seq_get_output_buffer_size(virt->handle)) {
    130 		err = snd_seq_set_output_buffer_size(virt->handle, params->buffer_size);
    131 		if (err < 0)
    132 			return err;
    133 		params->buffer_size = snd_seq_get_output_buffer_size(virt->handle);
    134 	}
    135 	return 0;
    136 }
    137 
    138 
    139 static int snd_rawmidi_virtual_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * params)
    140 {
    141 	snd_rawmidi_virtual_t *virt = rmidi->private_data;
    142 	params->stream = rmidi->stream;
    143 
    144 	if (rmidi->stream == SND_RAWMIDI_STREAM_INPUT)
    145 		return snd_rawmidi_virtual_input_params(virt, params);
    146 	else
    147 		return snd_rawmidi_virtual_output_params(virt, params);
    148 }
    149 
    150 static int snd_rawmidi_virtual_status(snd_rawmidi_t *rmidi, snd_rawmidi_status_t * status)
    151 {
    152 	// snd_rawmidi_virtual_t *virt = rmidi->private_data;
    153 	memset(status, 0, sizeof(*status));
    154 	status->stream = rmidi->stream;
    155 	return 0;
    156 }
    157 
    158 static int snd_rawmidi_virtual_drop(snd_rawmidi_t *rmidi)
    159 {
    160 	snd_rawmidi_virtual_t *virt = rmidi->private_data;
    161 	if (rmidi->stream == SND_RAWMIDI_STREAM_OUTPUT) {
    162 		snd_seq_drop_output(virt->handle);
    163 		snd_midi_event_reset_encode(virt->midi_event);
    164 		virt->pending = 0;
    165 	} else {
    166 		snd_seq_drop_input(virt->handle);
    167 		snd_midi_event_reset_decode(virt->midi_event);
    168 		virt->in_buf_ofs = 0;
    169 	}
    170 	return 0;
    171 }
    172 
    173 static int snd_rawmidi_virtual_drain(snd_rawmidi_t *rmidi)
    174 {
    175 	snd_rawmidi_virtual_t *virt = rmidi->private_data;
    176 	int err;
    177 
    178 	if (rmidi->stream == SND_RAWMIDI_STREAM_OUTPUT) {
    179 		if (virt->pending) {
    180 			err = snd_seq_event_output(virt->handle, &virt->out_event);
    181 			if (err < 0)
    182 				return err;
    183 			virt->pending = 0;
    184 		}
    185 		snd_seq_drain_output(virt->handle);
    186 		snd_seq_sync_output_queue(virt->handle);
    187 	}
    188 	return snd_rawmidi_virtual_drop(rmidi);
    189 }
    190 
    191 static ssize_t snd_rawmidi_virtual_write(snd_rawmidi_t *rmidi, const void *buffer, size_t size)
    192 {
    193 	snd_rawmidi_virtual_t *virt = rmidi->private_data;
    194 	ssize_t result = 0;
    195 	ssize_t size1;
    196 	int err;
    197 
    198 	if (virt->pending) {
    199 		err = snd_seq_event_output(virt->handle, &virt->out_event);
    200 		if (err < 0) {
    201 			if (err != -EAGAIN)
    202 				/* we got some fatal error. removing this event
    203 				 * at the next time
    204 				 */
    205 				virt->pending = 0;
    206 			return err;
    207 		}
    208 		virt->pending = 0;
    209 	}
    210 
    211 	while (size > 0) {
    212 		size1 = snd_midi_event_encode(virt->midi_event, buffer, size, &virt->out_event);
    213 		if (size1 <= 0)
    214 			break;
    215 		size -= size1;
    216 		result += size1;
    217 		buffer += size1;
    218 		if (virt->out_event.type == SND_SEQ_EVENT_NONE)
    219 			continue;
    220 		snd_seq_ev_set_subs(&virt->out_event);
    221 		snd_seq_ev_set_source(&virt->out_event, virt->port);
    222 		snd_seq_ev_set_direct(&virt->out_event);
    223 		err = snd_seq_event_output(virt->handle, &virt->out_event);
    224 		if (err < 0) {
    225 			virt->pending = 1;
    226 			return result > 0 ? result : err;
    227 		}
    228 	}
    229 
    230 	if (result > 0)
    231 		snd_seq_drain_output(virt->handle);
    232 
    233 	return result;
    234 }
    235 
    236 static ssize_t snd_rawmidi_virtual_read(snd_rawmidi_t *rmidi, void *buffer, size_t size)
    237 {
    238 	snd_rawmidi_virtual_t *virt = rmidi->private_data;
    239 	ssize_t result = 0;
    240 	int size1, err;
    241 
    242 	while (size > 0) {
    243 		if (! virt->in_buf_ofs) {
    244 			err = snd_seq_event_input_pending(virt->handle, 1);
    245 			if (err <= 0 && result > 0)
    246 				return result;
    247 			err = snd_seq_event_input(virt->handle, &virt->in_event);
    248 			if (err < 0)
    249 				return result > 0 ? result : err;
    250 
    251 			if (virt->in_event->type == SND_SEQ_EVENT_SYSEX) {
    252 				virt->in_buf_ptr = virt->in_event->data.ext.ptr;
    253 				virt->in_buf_size = virt->in_event->data.ext.len;
    254 			} else {
    255 				virt->in_buf_ptr = virt->in_tmp_buf;
    256 				virt->in_buf_size = snd_midi_event_decode(virt->midi_event,
    257 									  (unsigned char *)virt->in_tmp_buf,
    258 									  sizeof(virt->in_tmp_buf),
    259 									  virt->in_event);
    260 			}
    261 			if (virt->in_buf_size <= 0)
    262 				continue;
    263 		}
    264 		size1 = virt->in_buf_size - virt->in_buf_ofs;
    265 		if ((size_t)size1 > size) {
    266 			virt->in_buf_ofs += size1 - size;
    267 			memcpy(buffer, virt->in_buf_ptr, size);
    268 			result += size;
    269 			break;
    270 		}
    271 		memcpy(buffer, virt->in_buf_ptr + virt->in_buf_ofs, size1);
    272 		size -= size1;
    273 		result += size1;
    274 		buffer += size1;
    275 		virt->in_buf_ofs = 0;
    276 	}
    277 
    278 	return result;
    279 }
    280 
    281 static const snd_rawmidi_ops_t snd_rawmidi_virtual_ops = {
    282 	.close = snd_rawmidi_virtual_close,
    283 	.nonblock = snd_rawmidi_virtual_nonblock,
    284 	.info = snd_rawmidi_virtual_info,
    285 	.params = snd_rawmidi_virtual_params,
    286 	.status = snd_rawmidi_virtual_status,
    287 	.drop = snd_rawmidi_virtual_drop,
    288 	.drain = snd_rawmidi_virtual_drain,
    289 	.write = snd_rawmidi_virtual_write,
    290 	.read = snd_rawmidi_virtual_read,
    291 };
    292 
    293 
    294 /*! \page rawmidi RawMidi interface
    295 
    296 \section rawmidi_virt Virtual RawMidi interface
    297 
    298 The "virtual" plugin creates a virtual RawMidi instance on the ALSA
    299 sequencer, which can be accessed through the connection of the sequencer
    300 ports.
    301 There is no connection established as default.
    302 
    303 For creating a virtual RawMidi instance, pass "virtual" as its name at
    304 creation.
    305 
    306 Example:
    307 \code
    308 snd_rawmidi_open(&read_handle, &write_handle, "virtual", 0);
    309 \endcode
    310 
    311 */
    312 
    313 int snd_rawmidi_virtual_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
    314 			     const char *name, snd_seq_t *seq_handle, int port,
    315 			     int merge, int mode)
    316 {
    317 	int err;
    318 	snd_rawmidi_t *rmidi;
    319 	snd_rawmidi_virtual_t *virt = NULL;
    320 	struct pollfd pfd;
    321 
    322 	if (inputp)
    323 		*inputp = 0;
    324 	if (outputp)
    325 		*outputp = 0;
    326 
    327 	virt = calloc(1, sizeof(*virt));
    328 	if (virt == NULL) {
    329 		err = -ENOMEM;
    330 		goto _err;
    331 	}
    332 	virt->handle = seq_handle;
    333 	virt->port = port;
    334 	err = snd_midi_event_new(256, &virt->midi_event);
    335 	if (err < 0)
    336 		goto _err;
    337 	snd_midi_event_init(virt->midi_event);
    338 	snd_midi_event_no_status(virt->midi_event, !merge);
    339 
    340 	if (inputp) {
    341 		rmidi = calloc(1, sizeof(*rmidi));
    342 		if (rmidi == NULL) {
    343 			err = -ENOMEM;
    344 			goto _err;
    345 		}
    346 		if (name)
    347 			rmidi->name = strdup(name);
    348 		rmidi->type = SND_RAWMIDI_TYPE_VIRTUAL;
    349 		rmidi->stream = SND_RAWMIDI_STREAM_INPUT;
    350 		rmidi->mode = mode;
    351 		err = snd_seq_poll_descriptors(seq_handle, &pfd, 1, POLLIN);
    352 		if (err < 0)
    353 			goto _err;
    354 		rmidi->poll_fd = pfd.fd;
    355 		rmidi->ops = &snd_rawmidi_virtual_ops;
    356 		rmidi->private_data = virt;
    357 		virt->open++;
    358 		*inputp = rmidi;
    359 	}
    360 	if (outputp) {
    361 		rmidi = calloc(1, sizeof(*rmidi));
    362 		if (rmidi == NULL) {
    363 			err = -ENOMEM;
    364 			goto _err;
    365 		}
    366 		if (name)
    367 			rmidi->name = strdup(name);
    368 		rmidi->type = SND_RAWMIDI_TYPE_VIRTUAL;
    369 		rmidi->stream = SND_RAWMIDI_STREAM_OUTPUT;
    370 		rmidi->mode = mode;
    371 		err = snd_seq_poll_descriptors(seq_handle, &pfd, 1, POLLOUT);
    372 		if (err < 0)
    373 			goto _err;
    374 		rmidi->poll_fd = pfd.fd;
    375 		rmidi->ops = &snd_rawmidi_virtual_ops;
    376 		rmidi->private_data = virt;
    377 		virt->open++;
    378 		*outputp = rmidi;
    379 	}
    380 
    381 	return 0;
    382 
    383  _err:
    384 	if (seq_handle)
    385 		snd_seq_close(seq_handle);
    386 	if (virt->midi_event)
    387 		snd_midi_event_free(virt->midi_event);
    388 	free(virt);
    389 	if (inputp)
    390 		free(*inputp);
    391 	if (outputp)
    392 		free(*outputp);
    393 	return err;
    394 }
    395 
    396 int _snd_rawmidi_virtual_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
    397 			   char *name, snd_config_t *root ATTRIBUTE_UNUSED,
    398 			   snd_config_t *conf, int mode)
    399 {
    400 	snd_config_iterator_t i, next;
    401 	const char *slave_str = NULL;
    402 	int err;
    403 	int streams, seq_mode;
    404 	int merge = 1;
    405 	int port;
    406 	unsigned int caps;
    407 	snd_seq_t *seq_handle;
    408 
    409 	snd_config_for_each(i, next, conf) {
    410 		snd_config_t *n = snd_config_iterator_entry(i);
    411 		const char *id;
    412 		if (snd_config_get_id(n, &id) < 0)
    413 			continue;
    414 		if (snd_rawmidi_conf_generic_id(id))
    415 			continue;
    416 		if (strcmp(id, "slave") == 0) {
    417 			err = snd_config_get_string(n, &slave_str);
    418 			if (err < 0)
    419 				return err;
    420 			continue;
    421 		}
    422 		if (strcmp(id, "merge") == 0) {
    423 			merge = snd_config_get_bool(n);
    424 			continue;
    425 		}
    426 		return -EINVAL;
    427 	}
    428 
    429 	streams = 0;
    430 	if (inputp)
    431 		streams |= SND_SEQ_OPEN_INPUT;
    432 	if (outputp)
    433 		streams |= SND_SEQ_OPEN_OUTPUT;
    434 	if (! streams)
    435 		return -EINVAL;
    436 
    437 	seq_mode = 0;
    438 	if (mode & SND_RAWMIDI_NONBLOCK)
    439 		seq_mode |= SND_SEQ_NONBLOCK;
    440 
    441 	if (! slave_str)
    442 		slave_str = "default";
    443 	err = _snd_seq_open_lconf(&seq_handle, slave_str, streams, seq_mode,
    444 				  root, conf);
    445 	if (err < 0)
    446 		return err;
    447 
    448 	caps = 0;
    449 	if (inputp)
    450 		caps |= SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SYNC_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
    451 	if (outputp)
    452 		caps |= SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SYNC_READ | SND_SEQ_PORT_CAP_SUBS_READ;
    453 	if (inputp && outputp)
    454 		caps |= SNDRV_SEQ_PORT_CAP_DUPLEX;
    455 
    456 	port = snd_seq_create_simple_port(seq_handle, "Virtual RawMIDI",
    457 					  caps, SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC);
    458 	if (port < 0) {
    459 		snd_seq_close(seq_handle);
    460 		return port;
    461 	}
    462 
    463 	return snd_rawmidi_virtual_open(inputp, outputp, name, seq_handle, port,
    464 				     merge, mode);
    465 }
    466 
    467 #ifndef DOC_HIDDEN
    468 SND_DLSYM_BUILD_VERSION(_snd_rawmidi_virtual_open, SND_RAWMIDI_DLSYM_VERSION);
    469 #endif
    470