• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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