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