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

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