~ubuntu-branches/ubuntu/warty/alsa-lib/warty

« back to all changes in this revision

Viewing changes to src/rawmidi/rawmidi_virt.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2004-09-23 19:38:45 UTC
  • Revision ID: james.westby@ubuntu.com-20040923193845-71lrqesgxij0yzn7
Tags: upstream-1.0.5
ImportĀ upstreamĀ versionĀ 1.0.5

Show diffs side-by-side

added added

removed removed

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