[BACK]Return to midi.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / sndiod

Annotation of src/usr.bin/sndiod/midi.c, Revision 1.8

1.8     ! ratchov     1: /*     $OpenBSD: midi.c,v 1.7 2012/12/20 16:15:55 ratchov Exp $        */
1.1       ratchov     2: /*
                      3:  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     16:  */
                     17: #include <stdio.h>
                     18: #include <stdlib.h>
                     19: #include <string.h>
                     20:
                     21: #include "abuf.h"
                     22: #include "defs.h"
                     23: #include "dev.h"
                     24: #include "file.h"
                     25: #include "midi.h"
                     26: #include "miofile.h"
                     27: #include "sysex.h"
                     28: #include "utils.h"
                     29:
                     30: /*
                     31:  * input data rate is XFER / TIMO (in bytes per microsecond),
                     32:  * it must be slightly larger than the MIDI standard 3125 bytes/s
                     33:  */
                     34: #define MIDI_XFER 1
                     35: #define MIDI_TIMO 100000
                     36:
                     37: int  port_open(struct port *);
                     38: void port_imsg(void *, unsigned char *, int);
                     39: void port_omsg(void *, unsigned char *, int);
                     40: void port_fill(void *, int);
                     41: void port_exit(void *);
                     42:
                     43: struct midiops port_midiops = {
                     44:        port_imsg,
                     45:        port_omsg,
                     46:        port_fill,
                     47:        port_exit
                     48: };
                     49:
                     50: #define MIDI_NEP 32
                     51: struct midi midi_ep[MIDI_NEP];
                     52: struct timo midi_timo;
                     53: struct port *port_list = NULL;
                     54: unsigned int midi_portnum = 0;
                     55:
                     56: struct midithru {
1.2       ratchov    57:        unsigned int txmask, rxmask;
1.1       ratchov    58: #define MIDITHRU_NMAX 32
                     59: } midithru[MIDITHRU_NMAX];
                     60:
                     61: /*
                     62:  * length of voice and common messages (status byte included)
                     63:  */
                     64: unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
                     65: unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
                     66:
                     67: void
                     68: midi_log(struct midi *ep)
                     69: {
                     70:        log_puts("midi");
                     71:        log_putu(ep - midi_ep);
                     72: }
                     73:
                     74: void
                     75: midi_ontimo(void *arg)
                     76: {
                     77:        int i;
                     78:        struct midi *ep;
                     79:
                     80:        for (i = MIDI_NEP, ep = midi_ep; i > 0; i--, ep++) {
                     81:        }
                     82:        timo_add(&midi_timo, MIDI_TIMO);
                     83: }
                     84:
                     85: void
                     86: midi_init(void)
                     87: {
                     88:        timo_set(&midi_timo, midi_ontimo, NULL);
                     89:        timo_add(&midi_timo, MIDI_TIMO);
                     90: }
                     91:
                     92: void
                     93: midi_done(void)
                     94: {
                     95:        timo_del(&midi_timo);
                     96: }
                     97:
                     98: struct midi *
                     99: midi_new(struct midiops *ops, void *arg, int mode)
                    100: {
                    101:        int i;
                    102:        struct midi *ep;
                    103:
                    104:        for (i = 0, ep = midi_ep;; i++, ep++) {
                    105:                if (i == MIDI_NEP)
                    106:                        return NULL;
                    107:                if (ep->ops == NULL)
                    108:                        break;
                    109:        }
                    110:        ep->ops = ops;
                    111:        ep->arg = arg;
                    112:        ep->used = 0;
                    113:        ep->len = 0;
                    114:        ep->idx = 0;
                    115:        ep->st = 0;
                    116:        ep->txmask = 0;
1.2       ratchov   117:        ep->self = 1 << i;
                    118:        ep->tickets = 0;
1.1       ratchov   119:        ep->mode = mode;
1.2       ratchov   120:
1.1       ratchov   121:        /*
1.2       ratchov   122:         * the output buffer is the client intput
1.1       ratchov   123:         */
1.2       ratchov   124:        if (ep->mode & MODE_MIDIIN)
1.1       ratchov   125:                abuf_init(&ep->obuf, MIDI_BUFSZ);
1.2       ratchov   126:        midi_tickets(ep);
1.1       ratchov   127:        return ep;
                    128: }
                    129:
                    130: void
                    131: midi_del(struct midi *ep)
                    132: {
                    133:        int i;
1.2       ratchov   134:        struct midi *peer;
1.1       ratchov   135:
1.2       ratchov   136:        ep->txmask = 0;
                    137:        for (i = 0; i < MIDI_NEP; i++) {
                    138:                peer = midi_ep + i;
                    139:                if (peer->txmask & ep->self) {
                    140:                        peer->txmask &= ~ep->self;
                    141:                        midi_tickets(peer);
                    142:                }
                    143:        }
                    144:        for (i = 0; i < MIDITHRU_NMAX; i++) {
                    145:                midithru[i].txmask &= ~ep->self;
                    146:                midithru[i].rxmask &= ~ep->self;
                    147:        }
1.1       ratchov   148:        ep->ops = NULL;
                    149:        if (ep->mode & MODE_MIDIIN) {
                    150:                abuf_done(&ep->obuf);
                    151:        }
                    152: }
                    153:
                    154: /*
1.2       ratchov   155:  * connect two midi endpoints
1.1       ratchov   156:  */
                    157: void
1.2       ratchov   158: midi_link(struct midi *ep, struct midi *peer)
1.1       ratchov   159: {
1.2       ratchov   160:        if (ep->mode & MODE_MIDIOUT) {
                    161:                ep->txmask |= peer->self;
                    162:                midi_tickets(ep);
                    163:        }
                    164:        if (ep->mode & MODE_MIDIIN) {
                    165: #ifdef DEBUG
                    166:                if (ep->obuf.used > 0) {
                    167:                        midi_log(ep);
                    168:                        log_puts(": linked with non-empty buffer\n");
                    169:                        panic();
                    170:                }
                    171: #endif
                    172:                /* ep has empry buffer, so no need to call midi_tickets() */
                    173:                peer->txmask |= ep->self;
1.1       ratchov   174:        }
                    175: }
                    176:
                    177: /*
1.2       ratchov   178:  * add the midi endpoint in the ``tag'' midi thru box
1.1       ratchov   179:  */
                    180: void
1.2       ratchov   181: midi_tag(struct midi *ep, unsigned int tag)
1.1       ratchov   182: {
1.2       ratchov   183:        struct midi *peer;
                    184:        struct midithru *t = midithru + tag;
1.1       ratchov   185:        int i;
                    186:
1.2       ratchov   187:        if (ep->mode & MODE_MIDIOUT) {
                    188:                ep->txmask |= t->txmask;
                    189:                midi_tickets(ep);
                    190:        }
                    191:        if (ep->mode & MODE_MIDIIN) {
                    192: #ifdef DEBUG
                    193:                if (ep->obuf.used > 0) {
                    194:                        midi_log(ep);
                    195:                        log_puts(": tagged with non-empty buffer\n");
                    196:                        panic();
                    197:                }
                    198: #endif
                    199:                for (i = 0; i < MIDI_NEP; i++) {
                    200:                        if (!(t->rxmask & (1 << i)))
                    201:                                continue;
                    202:                        peer = midi_ep + i;
                    203:                        peer->txmask |= ep->self;
                    204:                }
1.1       ratchov   205:        }
1.2       ratchov   206:        if (ep->mode & MODE_MIDIOUT)
                    207:                t->rxmask |= ep->self;
                    208:        if (ep->mode & MODE_MIDIIN)
                    209:                t->txmask |= ep->self;
1.1       ratchov   210: }
                    211:
                    212: /*
1.6       ratchov   213:  * broadcast the given message to other endpoints
1.1       ratchov   214:  */
                    215: void
                    216: midi_send(struct midi *iep, unsigned char *msg, int size)
                    217: {
                    218:        struct midi *oep;
                    219:        int i;
                    220:
                    221: #ifdef DEBUG
                    222:        if (log_level >= 4) {
                    223:                midi_log(iep);
                    224:                log_puts(": sending:");
                    225:                for (i = 0; i < size; i++) {
                    226:                        log_puts(" ");
                    227:                        log_putx(msg[i]);
                    228:                }
                    229:                log_puts("\n");
                    230:        }
                    231: #endif
                    232:        for (i = 0; i < MIDI_NEP ; i++) {
                    233:                if ((iep->txmask & (1 << i)) == 0)
                    234:                        continue;
                    235:                oep = midi_ep + i;
                    236:                if (msg[0] <= 0x7f) {
                    237:                        if (oep->owner != iep)
                    238:                                continue;
                    239:                } else if (msg[0] <= 0xf7)
                    240:                        oep->owner = iep;
                    241: #ifdef DEBUG
                    242:                if (log_level >= 4) {
                    243:                        midi_log(iep);
                    244:                        log_puts(" -> ");
                    245:                        midi_log(oep);
                    246:                        log_puts("\n");
                    247:                }
                    248: #endif
                    249:                oep->ops->omsg(oep->arg, msg, size);
                    250:        }
                    251: }
                    252:
1.2       ratchov   253: /*
                    254:  * determine if we have gained more input tickets, and if so call the
                    255:  * fill() call-back to notify the i/o layer that it can send more data
                    256:  */
                    257: void
                    258: midi_tickets(struct midi *iep)
                    259: {
                    260:        int i, tickets, avail, maxavail;
                    261:        struct midi *oep;
                    262:
                    263:        maxavail = MIDI_BUFSZ;
                    264:        for (i = 0; i < MIDI_NEP ; i++) {
                    265:                if ((iep->txmask & (1 << i)) == 0)
                    266:                        continue;
                    267:                oep = midi_ep + i;
                    268:                avail = oep->obuf.len - oep->obuf.used;
                    269:                if (maxavail > avail)
                    270:                        maxavail = avail;
                    271:        }
                    272:
                    273:        /*
                    274:         * in the worst case output message is twice the
                    275:         * input message (2-byte messages with running status)
                    276:         */
                    277:        tickets = maxavail / 2 - iep->tickets;
                    278:        if (tickets > 0) {
                    279:                iep->tickets += tickets;
                    280:                iep->ops->fill(iep->arg, tickets);
                    281:        }
                    282: }
                    283:
                    284: /*
                    285:  * recalculate tickets of endpoints sending data to this one
                    286:  */
1.1       ratchov   287: void
                    288: midi_fill(struct midi *oep)
                    289: {
1.2       ratchov   290:        int i;
1.1       ratchov   291:        struct midi *iep;
                    292:
1.2       ratchov   293:        for (i = 0; i < MIDI_NEP; i++) {
1.1       ratchov   294:                iep = midi_ep + i;
1.2       ratchov   295:                if (iep->txmask & oep->self)
                    296:                        midi_tickets(iep);
1.1       ratchov   297:        }
                    298: }
                    299:
                    300: /*
1.2       ratchov   301:  * parse then give data chunk, and calling imsg() for each message
1.1       ratchov   302:  */
                    303: void
1.2       ratchov   304: midi_in(struct midi *iep, unsigned char *idata, int icount)
1.1       ratchov   305: {
                    306:        int i;
                    307:        unsigned char c;
                    308:
                    309:        for (i = 0; i < icount; i++) {
                    310:                c = *idata++;
                    311:                if (c >= 0xf8) {
                    312:                        if (c != MIDI_ACK)
                    313:                                iep->ops->imsg(iep->arg, &c, 1);
                    314:                } else if (c == SYSEX_END) {
                    315:                        if (iep->st == SYSEX_START) {
                    316:                                iep->msg[iep->idx++] = c;
                    317:                                iep->ops->imsg(iep->arg, iep->msg, iep->idx);
                    318:                        }
                    319:                        iep->st = 0;
                    320:                        iep->idx = 0;
                    321:                } else if (c >= 0xf0) {
                    322:                        iep->msg[0] = c;
                    323:                        iep->len = common_len[c & 7];
                    324:                        iep->st = c;
                    325:                        iep->idx = 1;
                    326:                } else if (c >= 0x80) {
                    327:                        iep->msg[0] = c;
                    328:                        iep->len = voice_len[(c >> 4) & 7];
                    329:                        iep->st = c;
                    330:                        iep->idx = 1;
                    331:                } else if (iep->st) {
                    332:                        if (iep->idx == 0 && iep->st != SYSEX_START)
                    333:                                iep->msg[iep->idx++] = iep->st;
                    334:                        iep->msg[iep->idx++] = c;
                    335:                        if (iep->idx == iep->len) {
                    336:                                iep->ops->imsg(iep->arg, iep->msg, iep->idx);
                    337:                                if (iep->st >= 0xf0)
                    338:                                        iep->st = 0;
                    339:                                iep->idx = 0;
                    340:                        } else if (iep->idx == MIDI_MSGMAX) {
                    341:                                /* sysex continued */
                    342:                                iep->ops->imsg(iep->arg, iep->msg, iep->idx);
                    343:                                iep->idx = 0;
                    344:                        }
                    345:                }
                    346:        }
1.2       ratchov   347:        iep->tickets -= icount;
                    348:        if (iep->tickets < 0)
                    349:                iep->tickets = 0;
1.7       ratchov   350:        midi_tickets(iep);
1.1       ratchov   351: }
                    352:
                    353: /*
                    354:  * store the given message in the output buffer
                    355:  */
                    356: void
                    357: midi_out(struct midi *oep, unsigned char *idata, int icount)
                    358: {
                    359:        unsigned char *odata;
                    360:        int ocount;
                    361: #ifdef DEBUG
                    362:        int i;
                    363: #endif
                    364:
                    365:        while (icount > 0) {
                    366:                if (oep->obuf.used == oep->obuf.len) {
                    367: #ifdef DEBUG
                    368:                        if (log_level >= 2) {
                    369:                                midi_log(oep);
1.2       ratchov   370:                                log_puts(": too slow, discarding ");
1.1       ratchov   371:                                log_putu(oep->obuf.used);
                    372:                                log_puts(" bytes\n");
                    373:                        }
                    374: #endif
                    375:                        abuf_rdiscard(&oep->obuf, oep->obuf.used);
                    376:                        oep->owner = NULL;
                    377:                        return;
                    378:                }
                    379:                odata = abuf_wgetblk(&oep->obuf, &ocount);
                    380:                if (ocount > icount)
                    381:                        ocount = icount;
                    382:                memcpy(odata, idata, ocount);
                    383: #ifdef DEBUG
                    384:                if (log_level >= 4) {
                    385:                        midi_log(oep);
                    386:                        log_puts(": out: ");
                    387:                        for (i = 0; i < ocount; i++) {
                    388:                                log_puts(" ");
                    389:                                log_putx(odata[i]);
                    390:                        }
                    391:                        log_puts("\n");
                    392:                }
                    393: #endif
                    394:                abuf_wcommit(&oep->obuf, ocount);
                    395:                icount -= ocount;
                    396:                idata += ocount;
                    397:        }
                    398: }
                    399:
                    400: #ifdef DEBUG
                    401: void
                    402: port_log(struct port *p)
                    403: {
                    404:        midi_log(p->midi);
                    405: }
                    406: #endif
                    407:
                    408: void
                    409: port_imsg(void *arg, unsigned char *msg, int size)
                    410: {
                    411:        struct port *p = arg;
                    412:
                    413:        midi_send(p->midi, msg, size);
                    414: }
                    415:
                    416:
                    417: void
                    418: port_omsg(void *arg, unsigned char *msg, int size)
                    419: {
                    420:        struct port *p = arg;
                    421:
                    422:        midi_out(p->midi, msg, size);
                    423: }
                    424:
                    425: void
                    426: port_fill(void *arg, int count)
                    427: {
                    428:        /* no flow control */
                    429: }
                    430:
                    431: void
                    432: port_exit(void *arg)
                    433: {
                    434: #ifdef DEBUG
                    435:        struct port *p = arg;
                    436:
                    437:        if (log_level >= 3) {
                    438:                port_log(p);
1.3       ratchov   439:                log_puts(": port exit\n");
                    440:                panic();
1.1       ratchov   441:        }
                    442: #endif
                    443: }
                    444:
                    445: /*
                    446:  * create a new midi port
                    447:  */
                    448: struct port *
1.4       ratchov   449: port_new(char *path, unsigned int mode, int hold)
1.1       ratchov   450: {
                    451:        struct port *c;
                    452:
                    453:        c = xmalloc(sizeof(struct port));
                    454:        c->path = path;
                    455:        c->state = PORT_CFG;
1.4       ratchov   456:        c->hold = hold;
1.1       ratchov   457:        c->midi = midi_new(&port_midiops, c, mode);
                    458:        midi_portnum++;
                    459:        c->next = port_list;
                    460:        port_list = c;
                    461:        return c;
                    462: }
                    463:
                    464: /*
                    465:  * destroy the given midi port
                    466:  */
                    467: void
                    468: port_del(struct port *c)
                    469: {
                    470:        struct port **p;
                    471:
                    472:        if (c->state != PORT_CFG)
                    473:                port_close(c);
                    474:        midi_del(c->midi);
                    475:        for (p = &port_list; *p != c; p = &(*p)->next) {
                    476: #ifdef DEBUG
                    477:                if (*p == NULL) {
                    478:                        log_puts("port to delete not on list\n");
                    479:                        panic();
                    480:                }
                    481: #endif
                    482:        }
                    483:        *p = c->next;
                    484:        xfree(c);
                    485: }
                    486:
1.3       ratchov   487: int
                    488: port_ref(struct port *c)
                    489: {
                    490: #ifdef DEBUG
                    491:        if (log_level >= 3) {
                    492:                port_log(c);
                    493:                log_puts(": port requested\n");
                    494:        }
                    495: #endif
                    496:        if (c->state == PORT_CFG && !port_open(c))
                    497:                return 0;
                    498:        return 1;
                    499: }
                    500:
                    501: void
                    502: port_unref(struct port *c)
                    503: {
                    504:        int i, rxmask;
                    505:
                    506: #ifdef DEBUG
                    507:        if (log_level >= 3) {
                    508:                port_log(c);
                    509:                log_puts(": port released\n");
                    510:        }
                    511: #endif
                    512:        for (rxmask = 0, i = 0; i < MIDI_NEP; i++)
                    513:                rxmask |= midi_ep[i].txmask;
1.8     ! ratchov   514:        if ((rxmask & ~c->midi->self) == 0 && c->state == PORT_INIT && !c->hold)
1.5       ratchov   515:                port_drain(c);
1.3       ratchov   516: }
                    517:
1.1       ratchov   518: struct port *
                    519: port_bynum(int num)
                    520: {
                    521:        struct port *p;
                    522:
                    523:        for (p = port_list; p != NULL; p = p->next) {
                    524:                if (num-- == 0)
                    525:                        return p;
                    526:        }
                    527:        return NULL;
                    528: }
                    529:
                    530: int
                    531: port_open(struct port *c)
                    532: {
                    533:        if (!port_mio_open(c)) {
                    534:                if (log_level >= 1) {
                    535:                        log_puts(c->path);
                    536:                        log_puts(": failed to open midi port\n");
                    537:                }
                    538:                return 0;
                    539:        }
                    540:        c->state = PORT_INIT;
                    541:        return 1;
                    542: }
                    543:
                    544: int
                    545: port_close(struct port *c)
                    546: {
1.3       ratchov   547:        int i;
                    548:        struct midi *ep;
1.1       ratchov   549: #ifdef DEBUG
                    550:        if (c->state == PORT_CFG) {
                    551:                port_log(c);
                    552:                log_puts(": can't close port (not opened)\n");
1.3       ratchov   553:                panic();
1.1       ratchov   554:        }
                    555: #endif
1.3       ratchov   556:        c->state = PORT_CFG;
1.1       ratchov   557:        port_mio_close(c);
1.3       ratchov   558:
                    559:        for (i = 0; i < MIDI_NEP; i++) {
                    560:                ep = midi_ep + i;
                    561:                if ((ep->txmask & c->midi->self) ||
                    562:                    (c->midi->txmask & ep->self))
                    563:                        ep->ops->exit(ep->arg);
                    564:        }
1.1       ratchov   565:        return 1;
                    566: }
                    567:
1.5       ratchov   568: void
                    569: port_drain(struct port *c)
                    570: {
                    571:        struct midi *ep = c->midi;
                    572:
                    573:        if (!(ep->mode & MODE_MIDIOUT) || ep->obuf.used == 0)
                    574:                port_close(c);
                    575:        else {
                    576:                c->state = PORT_DRAIN;
                    577: #ifdef DEBUG
                    578:                if (log_level >= 3) {
                    579:                        port_log(c);
                    580:                        log_puts(": draining\n");
                    581:                }
                    582: #endif
                    583:        }
                    584: }
                    585:
1.1       ratchov   586: int
                    587: port_init(struct port *c)
                    588: {
1.4       ratchov   589:        if (c->hold)
                    590:                return port_open(c);
                    591:        return 1;
1.1       ratchov   592: }
                    593:
                    594: void
                    595: port_done(struct port *c)
                    596: {
1.5       ratchov   597:        if (c->state == PORT_INIT)
                    598:                port_drain(c);
1.1       ratchov   599: }