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

Annotation of src/usr.bin/aucat/midi.c, Revision 1.9

1.9     ! ratchov     1: /*     $OpenBSD: midi.c,v 1.8 2009/08/29 14:46:44 ratchov Exp $        */
1.1       ratchov     2: /*
                      3:  * Copyright (c) 2008 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:  *
1.7       ratchov    20:  * use shadow variables (to save NRPNs, LSB of controller)
                     21:  * in the midi merger
1.1       ratchov    22:  *
                     23:  * make output and input identical when only one
                     24:  * input is used (fix running status)
                     25:  */
                     26: #include <stdio.h>
                     27: #include <stdlib.h>
                     28: #include <string.h>
                     29:
                     30: #include "abuf.h"
                     31: #include "aproc.h"
1.3       ratchov    32: #include "conf.h"
                     33: #include "dev.h"
1.1       ratchov    34: #include "midi.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 MIDITHRU_XFER 340
                     41: #define MIDITHRU_TIMO 100000
                     42:
1.3       ratchov    43: /*
                     44:  * masks to extract command and channel of status byte
                     45:  */
                     46: #define MIDI_CMDMASK   0xf0
                     47: #define MIDI_CHANMASK  0x0f
                     48:
                     49: /*
                     50:  * MIDI status bytes of voice messages
                     51:  */
                     52: #define MIDI_NOFF      0x80            /* note off */
                     53: #define MIDI_NON       0x90            /* note on */
                     54: #define MIDI_KAT       0xa0            /* key after touch */
                     55: #define MIDI_CTL       0xb0            /* controller */
                     56: #define MIDI_PC                0xc0            /* program change */
                     57: #define MIDI_CAT       0xd0            /* channel after touch */
                     58: #define MIDI_BEND      0xe0            /* pitch bend */
                     59:
                     60: /*
                     61:  * MIDI controller numbers
                     62:  */
                     63: #define MIDI_CTLVOL    7               /* volume */
                     64: #define MIDI_CTLPAN    11              /* pan */
                     65:
                     66: /*
                     67:  * length of voice and common messages (status byte included)
                     68:  */
1.1       ratchov    69: unsigned voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
                     70: unsigned common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
                     71:
1.7       ratchov    72: /*
                     73:  * send the message stored in of ibuf->mdata to obuf
                     74:  */
1.1       ratchov    75: void
                     76: thru_flush(struct aproc *p, struct abuf *ibuf, struct abuf *obuf)
                     77: {
                     78:        unsigned ocount, itodo;
                     79:        unsigned char *odata, *idata;
                     80:
                     81:        itodo = ibuf->mused;
                     82:        idata = ibuf->mdata;
                     83:        while (itodo > 0) {
                     84:                if (!ABUF_WOK(obuf)) {
                     85:                        abuf_rdiscard(obuf, obuf->used);
                     86:                        if (p->u.thru.owner == ibuf)
                     87:                                p->u.thru.owner = NULL;
                     88:                        return;
                     89:                }
                     90:                odata = abuf_wgetblk(obuf, &ocount, 0);
                     91:                if (ocount > itodo)
                     92:                        ocount = itodo;
                     93:                memcpy(odata, idata, ocount);
                     94:                abuf_wcommit(obuf, ocount);
                     95:                itodo -= ocount;
                     96:                idata += ocount;
                     97:        }
                     98:        ibuf->mused = 0;
                     99:        p->u.thru.owner = ibuf;
                    100: }
                    101:
1.7       ratchov   102: /*
                    103:  * send the real-time message (one byte) to obuf, similar to thrui_flush()
                    104:  */
1.1       ratchov   105: void
                    106: thru_rt(struct aproc *p, struct abuf *ibuf, struct abuf *obuf, unsigned c)
                    107: {
                    108:        unsigned ocount;
                    109:        unsigned char *odata;
                    110:
                    111:        if (!ABUF_WOK(obuf)) {
                    112:                abuf_rdiscard(obuf, obuf->used);
                    113:                if (p->u.thru.owner == ibuf)
                    114:                        p->u.thru.owner = NULL;
                    115:        }
                    116:        odata = abuf_wgetblk(obuf, &ocount, 0);
                    117:        odata[0] = c;
                    118:        abuf_wcommit(obuf, 1);
                    119: }
                    120:
1.7       ratchov   121: /*
                    122:  * parse ibuf contents and store each message into obuf,
                    123:  * use at most ``todo'' bytes (for throttling)
                    124:  */
1.1       ratchov   125: void
                    126: thru_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf, unsigned todo)
                    127: {
                    128:        unsigned char *idata;
                    129:        unsigned c, icount, ioffs;
                    130:
                    131:        idata = NULL;
                    132:        icount = ioffs = 0;
                    133:        for (;;) {
                    134:                if (icount == 0) {
                    135:                        if (todo == 0)
                    136:                                break;
                    137:                        idata = abuf_rgetblk(ibuf, &icount, ioffs);
                    138:                        if (icount > todo)
                    139:                                icount = todo;
                    140:                        if (icount == 0)
                    141:                                break;
                    142:                        todo -= icount;
                    143:                        ioffs += icount;
                    144:                }
                    145:                c = *idata++;
                    146:                icount--;
                    147:                if (c < 0x80) {
                    148:                        if (ibuf->mindex == 0 && ibuf->mstatus) {
                    149:                                ibuf->mdata[ibuf->mused++] = ibuf->mstatus;
                    150:                                ibuf->mindex++;
                    151:                        }
                    152:                        ibuf->mdata[ibuf->mused++] = c;
                    153:                        ibuf->mindex++;
                    154:                        if (ibuf->mindex == ibuf->mlen) {
                    155:                                thru_flush(p, ibuf, obuf);
                    156:                                if (ibuf->mstatus >= 0xf0)
                    157:                                        ibuf->mstatus = 0;
                    158:                                ibuf->mindex = 0;
                    159:                        }
                    160:                        if (ibuf->mused == MDATA_NMAX) {
                    161:                                if (ibuf->mused == ibuf->mindex ||
                    162:                                    p->u.thru.owner == ibuf)
                    163:                                        thru_flush(p, ibuf, obuf);
                    164:                                else
                    165:                                        ibuf->mused = 0;
                    166:                        }
                    167:                } else if (c < 0xf8) {
                    168:                        if (ibuf->mused == ibuf->mindex ||
                    169:                            p->u.thru.owner == ibuf) {
                    170:                                thru_flush(p, ibuf, obuf);
                    171:                        } else
                    172:                                ibuf->mused = 0;
                    173:                        ibuf->mdata[0] = c;
                    174:                        ibuf->mused = 1;
                    175:                        ibuf->mlen = (c >= 0xf0) ?
                    176:                            common_len[c & 7] :
                    177:                            voice_len[(c >> 4) & 7];
                    178:                        if (ibuf->mlen == 1) {
                    179:                                thru_flush(p, ibuf, obuf);
                    180:                                ibuf->mindex = 0;
                    181:                                ibuf->mstatus = 0;
                    182:                                ibuf->mlen = 0;
                    183:                        } else {
                    184:                                ibuf->mstatus = c;
                    185:                                ibuf->mindex = 1;
                    186:                        }
                    187:                } else {
                    188:                        thru_rt(p, ibuf, obuf, c);
                    189:                }
                    190:        }
                    191: }
                    192:
                    193: int
                    194: thru_in(struct aproc *p, struct abuf *ibuf)
                    195: {
                    196:        struct abuf *i, *inext;
                    197:        unsigned todo;
                    198:
                    199:        if (!ABUF_ROK(ibuf))
                    200:                return 0;
                    201:        if (ibuf->mtickets == 0) {
                    202:                return 0;
                    203:        }
                    204:        todo = ibuf->used;
                    205:        if (todo > ibuf->mtickets)
                    206:                todo = ibuf->mtickets;
                    207:        ibuf->mtickets -= todo;
                    208:        for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) {
                    209:                inext = LIST_NEXT(i, oent);
                    210:                if (ibuf->duplex == i)
                    211:                        continue;
                    212:                thru_bcopy(p, ibuf, i, todo);
                    213:                (void)abuf_flush(i);
                    214:        }
                    215:        abuf_rdiscard(ibuf, todo);
                    216:        return 1;
                    217: }
                    218:
                    219: int
                    220: thru_out(struct aproc *p, struct abuf *obuf)
                    221: {
                    222:        return 0;
                    223: }
                    224:
                    225: void
                    226: thru_eof(struct aproc *p, struct abuf *ibuf)
                    227: {
                    228: }
                    229:
                    230: void
                    231: thru_hup(struct aproc *p, struct abuf *obuf)
                    232: {
                    233: }
                    234:
                    235: void
                    236: thru_newin(struct aproc *p, struct abuf *ibuf)
                    237: {
                    238:        ibuf->mused = 0;
                    239:        ibuf->mlen = 0;
                    240:        ibuf->mindex = 0;
                    241:        ibuf->mstatus = 0;
                    242:        ibuf->mtickets = MIDITHRU_XFER;
                    243: }
                    244:
                    245: void
                    246: thru_done(struct aproc *p)
                    247: {
                    248:        timo_del(&p->u.thru.timo);
                    249: }
                    250:
                    251: struct aproc_ops thru_ops = {
                    252:        "thru",
                    253:        thru_in,
                    254:        thru_out,
                    255:        thru_eof,
                    256:        thru_hup,
                    257:        thru_newin,
                    258:        NULL, /* newout */
                    259:        NULL, /* ipos */
                    260:        NULL, /* opos */
                    261:        thru_done
                    262: };
                    263:
1.7       ratchov   264: /*
                    265:  * call-back invoked periodically to implement throttling at each invocation
                    266:  * gain more ``tickets'' for processing.  If one of the buffer was blocked by
                    267:  * the throttelling mechanism, then run it
                    268:  */
1.1       ratchov   269: void
                    270: thru_cb(void *addr)
                    271: {
                    272:        struct aproc *p = (struct aproc *)addr;
                    273:        struct abuf *i, *inext;
                    274:        unsigned tickets;
                    275:
                    276:        timo_add(&p->u.thru.timo, MIDITHRU_TIMO);
                    277:
                    278:        for (i = LIST_FIRST(&p->ibuflist); i != NULL; i = inext) {
                    279:                inext = LIST_NEXT(i, ient);
                    280:                tickets = i->mtickets;
                    281:                i->mtickets = MIDITHRU_XFER;
                    282:                if (tickets == 0)
                    283:                        abuf_run(i);
                    284:        }
                    285: }
                    286:
                    287: struct aproc *
                    288: thru_new(char *name)
                    289: {
                    290:        struct aproc *p;
                    291:
                    292:        p = aproc_new(&thru_ops, name);
                    293:        p->u.thru.owner = NULL;
                    294:        timo_set(&p->u.thru.timo, thru_cb, p);
                    295:        timo_add(&p->u.thru.timo, MIDITHRU_TIMO);
1.3       ratchov   296:        return p;
                    297: }
                    298:
1.7       ratchov   299: /*
                    300:  * broadcast a message to all output buffers on the behalf of ibuf.
                    301:  * ie. don't sent back the message to the sender
                    302:  */
1.3       ratchov   303: void
                    304: ctl_sendmsg(struct aproc *p, struct abuf *ibuf, unsigned char *msg, unsigned len)
                    305: {
                    306:        unsigned ocount, itodo;
                    307:        unsigned char *odata, *idata;
                    308:        struct abuf *i, *inext;
                    309:
                    310:        for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) {
                    311:                inext = LIST_NEXT(i, oent);
                    312:                if (i->duplex == ibuf)
                    313:                        continue;
                    314:                itodo = len;
                    315:                idata = msg;
                    316:                while (itodo > 0) {
                    317:                        if (!ABUF_WOK(i)) {
                    318:                                abuf_rdiscard(i, i->used);
                    319:                        }
                    320:                        odata = abuf_wgetblk(i, &ocount, 0);
                    321:                        if (ocount > itodo)
                    322:                                ocount = itodo;
                    323:                        memcpy(odata, idata, ocount);
                    324:                        abuf_wcommit(i, ocount);
                    325:                        itodo -= ocount;
                    326:                        idata += ocount;
                    327:                }
                    328:                (void)abuf_flush(i);
                    329:        }
                    330: }
                    331:
1.7       ratchov   332: /*
                    333:  * allocate a new slot (ie midi channel), register the given call-back
                    334:  * to be called volume is changed by MIDI. The call-back is invoked at
                    335:  * initialization to restore the saved volume.
                    336:  */
1.3       ratchov   337: int
1.5       ratchov   338: ctl_slotnew(struct aproc *p, char *who, void (*cb)(void *, unsigned), void *arg)
1.3       ratchov   339: {
                    340:        char *s;
                    341:        struct ctl_slot *slot;
1.4       ratchov   342:        char name[CTL_NAMEMAX];
                    343:        unsigned i, unit, umap = 0;
1.6       ratchov   344:        unsigned ser, bestser, bestidx;
1.3       ratchov   345:
1.4       ratchov   346:        /*
                    347:         * create a ``valid'' control name (lowcase, remove [^a-z], trucate)
                    348:         */
1.5       ratchov   349:        for (i = 0, s = who; ; s++) {
1.4       ratchov   350:                if (i == CTL_NAMEMAX - 1 || *s == '\0') {
                    351:                        name[i] = '\0';
                    352:                        break;
                    353:                } else if (*s >= 'A' && *s <= 'Z') {
                    354:                        name[i++] = *s + 'a' - 'A';
                    355:                } else if (*s >= 'a' && *s <= 'z')
                    356:                        name[i++] = *s;
                    357:        }
                    358:        if (i == 0)
                    359:                strlcpy(name, "noname", CTL_NAMEMAX);
                    360:
                    361:        /*
                    362:         * find the instance number of the control name
                    363:         */
                    364:        for (i = 0, slot = p->u.ctl.slot; i < CTL_NSLOT; i++, slot++) {
1.5       ratchov   365:                if (slot->cb == NULL)
1.4       ratchov   366:                        continue;
                    367:                if (strcmp(slot->name, name) == 0)
                    368:                        umap |= (1 << i);
                    369:        }
                    370:        for (unit = 0; unit < CTL_NSLOT; unit++) {
                    371:                if (unit == CTL_NSLOT)
1.3       ratchov   372:                        return -1;
1.4       ratchov   373:                if ((umap & (1 << i)) == 0)
1.3       ratchov   374:                        break;
                    375:        }
1.4       ratchov   376:        /*
                    377:         * find a free controller slot with the same name/unit
                    378:         */
                    379:        for (i = 0, slot = p->u.ctl.slot; i < CTL_NSLOT; i++, slot++) {
1.5       ratchov   380:                if (slot->cb == NULL &&
1.4       ratchov   381:                    strcmp(slot->name, name) == 0 &&
                    382:                    slot->unit == unit) {
1.5       ratchov   383:                        slot->cb = cb;
                    384:                        slot->arg = arg;
1.7       ratchov   385:                        slot->cb(slot->arg, slot->vol);
                    386:                        ctl_slotvol(p, i, slot->vol);
1.4       ratchov   387:                        return i;
                    388:                }
                    389:        }
                    390:
                    391:        /*
1.6       ratchov   392:         * couldn't find a matching slot, pick oldest free slot
1.4       ratchov   393:         */
1.6       ratchov   394:        bestser = 0;
                    395:        bestidx = CTL_NSLOT;
                    396:        for (i = 0, slot = p->u.ctl.slot; i < CTL_NSLOT; i++, slot++) {
                    397:                if (slot->cb != NULL)
                    398:                        continue;
                    399:                ser = p->u.ctl.serial - slot->serial;
                    400:                if (ser > bestser) {
                    401:                        bestser = ser;
                    402:                        bestidx = i;
                    403:                }
1.3       ratchov   404:        }
1.6       ratchov   405:        if (bestidx == CTL_NSLOT)
                    406:                return -1;
                    407:        slot = p->u.ctl.slot + bestidx;
1.4       ratchov   408:        strlcpy(slot->name, name, CTL_NAMEMAX);
1.6       ratchov   409:        slot->serial = p->u.ctl.serial++;
1.4       ratchov   410:        slot->unit = unit;
1.7       ratchov   411:        slot->vol = MIDI_MAXCTL;
1.5       ratchov   412:        slot->cb = cb;
                    413:        slot->arg = arg;
1.7       ratchov   414:        slot->cb(slot->arg, slot->vol);
                    415:        ctl_slotvol(p, bestidx, slot->vol);
1.6       ratchov   416:        return bestidx;
1.3       ratchov   417: }
                    418:
1.7       ratchov   419: /*
                    420:  * release the given slot
                    421:  */
1.3       ratchov   422: void
                    423: ctl_slotdel(struct aproc *p, int index)
                    424: {
1.5       ratchov   425:        p->u.ctl.slot[index].cb = NULL;
1.3       ratchov   426: }
                    427:
1.7       ratchov   428: /*
                    429:  * notifty the mixer that volume changed, called by whom allocad the slot using
                    430:  * ctl_slotnew(). Note: it doesn't make sens to call this from within the
                    431:  * call-back.
                    432:  */
1.3       ratchov   433: void
                    434: ctl_slotvol(struct aproc *p, int slot, unsigned vol)
                    435: {
                    436:        unsigned char msg[3];
                    437:
1.7       ratchov   438:        p->u.ctl.slot[slot].vol = vol;
1.3       ratchov   439:        msg[0] = MIDI_CTL | slot;
                    440:        msg[1] = MIDI_CTLVOL;
                    441:        msg[2] = vol;
                    442:        ctl_sendmsg(p, NULL, msg, 3);
                    443: }
                    444:
1.7       ratchov   445: /*
                    446:  * handle a MIDI event received from ibuf
                    447:  */
1.3       ratchov   448: void
                    449: ctl_ev(struct aproc *p, struct abuf *ibuf)
                    450: {
                    451:        unsigned chan;
1.5       ratchov   452:        struct ctl_slot *slot;
1.3       ratchov   453:        if ((ibuf->mdata[0] & MIDI_CMDMASK) == MIDI_CTL &&
                    454:            ibuf->mdata[1] == MIDI_CTLVOL) {
                    455:                chan = ibuf->mdata[0] & MIDI_CHANMASK;
                    456:                if (chan >= CTL_NSLOT)
                    457:                        return;
1.5       ratchov   458:                slot = p->u.ctl.slot + chan;
                    459:                if (slot->cb == NULL)
1.3       ratchov   460:                        return;
1.7       ratchov   461:                slot->vol = ibuf->mdata[2];
                    462:                slot->cb(slot->arg, slot->vol);
1.3       ratchov   463:                ctl_sendmsg(p, ibuf, ibuf->mdata, ibuf->mlen);
                    464:        }
                    465: }
                    466:
                    467: int
                    468: ctl_in(struct aproc *p, struct abuf *ibuf)
                    469: {
                    470:        unsigned char *idata;
                    471:        unsigned c, i, icount;
                    472:
                    473:        if (!ABUF_ROK(ibuf))
                    474:                return 0;
                    475:        idata = abuf_rgetblk(ibuf, &icount, 0);
                    476:        for (i = 0; i < icount; i++) {
                    477:                c = *idata++;
1.8       ratchov   478:                if (c >= 0xf8) {
                    479:                        /* clock events not used yet */
                    480:                } else if (c >= 0xf0) {
                    481:                        if (ibuf->mstatus == 0xf0 && c == 0xf7 &&
                    482:                            ibuf->mindex < MDATA_NMAX) {
                    483:                                ibuf->mdata[ibuf->mindex++] = c;
                    484:                                ctl_ev(p, ibuf);
                    485:                                continue;
                    486:                        }
                    487:                        ibuf->mdata[0] = c;
                    488:                        ibuf->mlen = common_len[c & 7];
                    489:                        ibuf->mstatus = c;
                    490:                        ibuf->mindex = 1;
1.3       ratchov   491:                } else if (c >= 0x80) {
                    492:                        ibuf->mdata[0] = c;
                    493:                        ibuf->mlen = voice_len[(c >> 4) & 7];
                    494:                        ibuf->mstatus = c;
                    495:                        ibuf->mindex = 1;
                    496:                } else if (ibuf->mstatus) {
1.8       ratchov   497:                        if (ibuf->mindex == MDATA_NMAX)
                    498:                                continue;
1.3       ratchov   499:                        if (ibuf->mindex == 0)
                    500:                                ibuf->mdata[ibuf->mindex++] = ibuf->mstatus;
                    501:                        ibuf->mdata[ibuf->mindex++] = c;
                    502:                        if (ibuf->mindex == ibuf->mlen) {
                    503:                                ctl_ev(p, ibuf);
                    504:                                ibuf->mindex = 0;
                    505:                        }
                    506:                }
                    507:        }
                    508:        abuf_rdiscard(ibuf, icount);
                    509:        return 1;
                    510: }
                    511:
                    512: int
                    513: ctl_out(struct aproc *p, struct abuf *obuf)
                    514: {
                    515:        return 0;
                    516: }
                    517:
                    518: void
                    519: ctl_eof(struct aproc *p, struct abuf *ibuf)
                    520: {
                    521: }
                    522:
                    523: void
                    524: ctl_hup(struct aproc *p, struct abuf *obuf)
                    525: {
                    526: }
                    527:
                    528: void
                    529: ctl_newin(struct aproc *p, struct abuf *ibuf)
                    530: {
                    531:        ibuf->mused = 0;
                    532:        ibuf->mlen = 0;
                    533:        ibuf->mindex = 0;
                    534:        ibuf->mstatus = 0;
                    535: }
                    536:
                    537: void
                    538: ctl_done(struct aproc *p)
                    539: {
                    540: }
                    541:
                    542: struct aproc_ops ctl_ops = {
                    543:        "ctl",
                    544:        ctl_in,
                    545:        ctl_out,
                    546:        ctl_eof,
                    547:        ctl_hup,
                    548:        ctl_newin,
                    549:        NULL, /* newout */
                    550:        NULL, /* ipos */
                    551:        NULL, /* opos */
                    552:        ctl_done
                    553: };
                    554:
                    555: struct aproc *
                    556: ctl_new(char *name)
                    557: {
                    558:        struct aproc *p;
1.5       ratchov   559:        struct ctl_slot *s;
1.3       ratchov   560:        unsigned i;
                    561:
                    562:        p = aproc_new(&ctl_ops, name);
1.6       ratchov   563:        p->u.ctl.serial = 0;
1.5       ratchov   564:        for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) {
1.3       ratchov   565:                p->u.ctl.slot[i].unit = i;
1.5       ratchov   566:                p->u.ctl.slot[i].cb = NULL;
1.7       ratchov   567:                p->u.ctl.slot[i].vol = MIDI_MAXCTL;
1.6       ratchov   568:                p->u.ctl.slot[i].serial = p->u.ctl.serial++;
1.3       ratchov   569:                strlcpy(p->u.ctl.slot[i].name, "unknown", CTL_NAMEMAX);
                    570:        }
1.1       ratchov   571:        return p;
                    572: }
                    573: