[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.3

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