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