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 */
32 const char *_snd_module_rawmidi_virt = "";
33 #endif
34
35
36 #ifndef DOC_HIDDEN
37 typedef 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
55 int _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
snd_rawmidi_virtual_close(snd_rawmidi_t *rmidi)60 static 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
snd_rawmidi_virtual_nonblock(snd_rawmidi_t *rmidi, int nonblock)73 static 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
snd_rawmidi_virtual_info(snd_rawmidi_t *rmidi, snd_rawmidi_info_t * info)80 static 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
snd_rawmidi_virtual_input_params(snd_rawmidi_virtual_t *virt, snd_rawmidi_params_t *params)98 static 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
snd_rawmidi_virtual_output_params(snd_rawmidi_virtual_t *virt, snd_rawmidi_params_t *params)118 static 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
snd_rawmidi_virtual_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * params)137 static 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
snd_rawmidi_virtual_status(snd_rawmidi_t *rmidi, snd_rawmidi_status_t * status)148 static 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
snd_rawmidi_virtual_drop(snd_rawmidi_t *rmidi)156 static 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
snd_rawmidi_virtual_drain(snd_rawmidi_t *rmidi)171 static 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
snd_rawmidi_virtual_write(snd_rawmidi_t *rmidi, const void *buffer, size_t size)189 static 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
snd_rawmidi_virtual_read(snd_rawmidi_t *rmidi, void *buffer, size_t size)234 static 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
279 static 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
296 The "virtual" plugin creates a virtual RawMidi instance on the ALSA
297 sequencer, which can be accessed through the connection of the sequencer
298 ports.
299 There is no connection established as default.
300
301 For creating a virtual RawMidi instance, pass "virtual" as its name at
302 creation.
303
304 Example:
305 \code
306 snd_rawmidi_open(&read_handle, &write_handle, "virtual", 0);
307 \endcode
308
309 */
310
snd_rawmidi_virtual_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, const char *name, snd_seq_t *seq_handle, int port, int merge, int mode)311 int 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
_snd_rawmidi_virtual_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, char *name, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, int mode)397 int _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
469 SND_DLSYM_BUILD_VERSION(_snd_rawmidi_virtual_open, SND_RAWMIDI_DLSYM_VERSION);
470 #endif
471