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 <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
snd_rawmidi_virtual_close(snd_rawmidi_t * rmidi)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
snd_rawmidi_virtual_nonblock(snd_rawmidi_t * rmidi,int nonblock)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
snd_rawmidi_virtual_info(snd_rawmidi_t * rmidi,snd_rawmidi_info_t * info)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
snd_rawmidi_virtual_input_params(snd_rawmidi_virtual_t * virt,snd_rawmidi_params_t * params)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
snd_rawmidi_virtual_output_params(snd_rawmidi_virtual_t * virt,snd_rawmidi_params_t * params)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
snd_rawmidi_virtual_params(snd_rawmidi_t * rmidi,snd_rawmidi_params_t * params)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
snd_rawmidi_virtual_status(snd_rawmidi_t * rmidi,snd_rawmidi_status_t * status)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
snd_rawmidi_virtual_drop(snd_rawmidi_t * rmidi)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
snd_rawmidi_virtual_drain(snd_rawmidi_t * rmidi)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
snd_rawmidi_virtual_write(snd_rawmidi_t * rmidi,const void * buffer,size_t size)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
snd_rawmidi_virtual_read(snd_rawmidi_t * rmidi,void * buffer,size_t size)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 memcpy(buffer, virt->in_buf_ptr + virt->in_buf_ofs, size);
267 virt->in_buf_ofs += 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
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)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 = NULL;
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) {
387 if (virt->midi_event)
388 snd_midi_event_free(virt->midi_event);
389 free(virt);
390 }
391 if (inputp)
392 free(*inputp);
393 if (outputp)
394 free(*outputp);
395 free(rmidi);
396 return err;
397 }
398
_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)399 int _snd_rawmidi_virtual_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
400 char *name, snd_config_t *root ATTRIBUTE_UNUSED,
401 snd_config_t *conf, int mode)
402 {
403 snd_config_iterator_t i, next;
404 const char *slave_str = NULL;
405 int err;
406 int streams, seq_mode;
407 int merge = 1;
408 int port;
409 unsigned int caps;
410 snd_seq_t *seq_handle;
411
412 snd_config_for_each(i, next, conf) {
413 snd_config_t *n = snd_config_iterator_entry(i);
414 const char *id;
415 if (snd_config_get_id(n, &id) < 0)
416 continue;
417 if (snd_rawmidi_conf_generic_id(id))
418 continue;
419 if (strcmp(id, "slave") == 0) {
420 err = snd_config_get_string(n, &slave_str);
421 if (err < 0)
422 return err;
423 continue;
424 }
425 if (strcmp(id, "merge") == 0) {
426 merge = snd_config_get_bool(n);
427 continue;
428 }
429 return -EINVAL;
430 }
431
432 streams = 0;
433 if (inputp)
434 streams |= SND_SEQ_OPEN_INPUT;
435 if (outputp)
436 streams |= SND_SEQ_OPEN_OUTPUT;
437 if (! streams)
438 return -EINVAL;
439
440 seq_mode = 0;
441 if (mode & SND_RAWMIDI_NONBLOCK)
442 seq_mode |= SND_SEQ_NONBLOCK;
443
444 if (! slave_str)
445 slave_str = "default";
446 err = _snd_seq_open_lconf(&seq_handle, slave_str, streams, seq_mode,
447 root, conf);
448 if (err < 0)
449 return err;
450
451 caps = 0;
452 if (inputp)
453 caps |= SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SYNC_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
454 if (outputp)
455 caps |= SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SYNC_READ | SND_SEQ_PORT_CAP_SUBS_READ;
456 if (inputp && outputp)
457 caps |= SNDRV_SEQ_PORT_CAP_DUPLEX;
458
459 port = snd_seq_create_simple_port(seq_handle, "Virtual RawMIDI",
460 caps, SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC);
461 if (port < 0) {
462 snd_seq_close(seq_handle);
463 return port;
464 }
465
466 return snd_rawmidi_virtual_open(inputp, outputp, name, seq_handle, port,
467 merge, mode);
468 }
469
470 #ifndef DOC_HIDDEN
471 SND_DLSYM_BUILD_VERSION(_snd_rawmidi_virtual_open, SND_RAWMIDI_DLSYM_VERSION);
472 #endif
473