gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

mm.c (19371B)


      1 /* mm.c -- midi monitor */
      2 
      3 /*****************************************************************************
      4 *       Change Log
      5 *  Date | Change
      6 *-----------+-----------------------------------------------------------------
      7 *  7-Apr-86 | Created changelog
      8 * 31-Jan-90 | GWL : use new cmdline
      9 *  5-Apr-91 | JDW : Further changes
     10 * 16-Feb-92 | GWL : eliminate label mmexit:; add error recovery
     11 * 18-May-92 | GWL : continuous clocks, etc.
     12 * 17-Jan-94 | GWL : option to display notes
     13 * 20-Nov-06 | RBD : port mm.c from CMU Midi Toolkit to PortMidi
     14 *           |       mm.c -- revealing MIDI secrets for over 20 years!
     15 *****************************************************************************/
     16 
     17 #include "stdlib.h"
     18 #include "ctype.h"
     19 #include "string.h"
     20 #include "stdio.h"
     21 #include "porttime.h"
     22 #include "portmidi.h"
     23 
     24 #define STRING_MAX 80
     25 
     26 #define MIDI_CODE_MASK  0xf0
     27 #define MIDI_CHN_MASK   0x0f
     28 /*#define MIDI_REALTIME   0xf8
     29   #define MIDI_CHAN_MODE  0xfa */
     30 #define MIDI_OFF_NOTE   0x80
     31 #define MIDI_ON_NOTE    0x90
     32 #define MIDI_POLY_TOUCH 0xa0
     33 #define MIDI_CTRL       0xb0
     34 #define MIDI_CH_PROGRAM 0xc0
     35 #define MIDI_TOUCH      0xd0
     36 #define MIDI_BEND       0xe0
     37 
     38 #define MIDI_SYSEX      0xf0
     39 #define MIDI_Q_FRAME	0xf1
     40 #define MIDI_SONG_POINTER 0xf2
     41 #define MIDI_SONG_SELECT 0xf3
     42 #define MIDI_TUNE_REQ	0xf6
     43 #define MIDI_EOX        0xf7
     44 #define MIDI_TIME_CLOCK 0xf8
     45 #define MIDI_START      0xfa
     46 #define MIDI_CONTINUE	0xfb
     47 #define MIDI_STOP       0xfc
     48 #define MIDI_ACTIVE_SENSING 0xfe
     49 #define MIDI_SYS_RESET  0xff
     50 
     51 #define MIDI_ALL_SOUND_OFF 0x78
     52 #define MIDI_RESET_CONTROLLERS 0x79
     53 #define MIDI_LOCAL	0x7a
     54 #define MIDI_ALL_OFF	0x7b
     55 #define MIDI_OMNI_OFF	0x7c
     56 #define MIDI_OMNI_ON	0x7d
     57 #define MIDI_MONO_ON	0x7e
     58 #define MIDI_POLY_ON	0x7f
     59 
     60 
     61 #define private static
     62 
     63 #ifndef false
     64 #define false 0
     65 #define true 1
     66 #endif
     67 
     68 typedef int boolean;
     69 
     70 int debug = false;	/* never set, but referenced by userio.c */
     71 PmStream *midi_in;      /* midi input */
     72 boolean active = false;     /* set when midi_in is ready for reading */
     73 boolean in_sysex = false;   /* we are reading a sysex message */
     74 boolean inited = false;     /* suppress printing during command line parsing */
     75 boolean done = false;       /* when true, exit */
     76 boolean notes = true;       /* show notes? */
     77 boolean controls = true;    /* show continuous controllers */
     78 boolean bender = true;      /* record pitch bend etc.? */
     79 boolean excldata = true;    /* record system exclusive data? */
     80 boolean verbose = true;     /* show text representation? */
     81 boolean realdata = true;    /* record real time messages? */
     82 boolean clksencnt = true;   /* clock and active sense count on */
     83 boolean chmode = true;      /* show channel mode messages */
     84 boolean pgchanges = true;   /* show program changes */
     85 boolean flush = false;	    /* flush all pending MIDI data */
     86 
     87 uint32_t filter = 0;            /* remember state of midi filter */
     88 
     89 uint32_t clockcount = 0;        /* count of clocks */
     90 uint32_t actsensecount = 0;     /* cout of active sensing bytes */
     91 uint32_t notescount = 0;        /* #notes since last request */
     92 uint32_t notestotal = 0;        /* total #notes */
     93 
     94 char val_format[] = "    Val %d\n";
     95 
     96 /*****************************************************************************
     97 *    Imported variables
     98 *****************************************************************************/
     99 
    100 extern  int     abort_flag;
    101 
    102 /*****************************************************************************
    103 *    Routines local to this module
    104 *****************************************************************************/
    105 
    106 private    void    mmexit(int code);
    107 private    void    output(PmMessage data);
    108 private    int     put_pitch(int p);
    109 private    void    showhelp();
    110 private    void    showbytes(PmMessage data, int len, boolean newline);
    111 private    void    showstatus(boolean flag);
    112 private    void    doascii(char c);
    113 private    int     get_number(char *prompt);
    114 
    115 
    116 /* read a number from console */
    117 /**/
    118 int get_number(char *prompt)
    119 {
    120     char line[STRING_MAX];
    121     int n = 0, i;
    122     printf(prompt);
    123     while (n != 1) {
    124         n = scanf("%d", &i);
    125         fgets(line, STRING_MAX, stdin);
    126 
    127     }
    128     return i;
    129 }
    130 
    131 
    132 void receive_poll(PtTimestamp timestamp, void *userData)
    133 {
    134     PmEvent event;
    135     int count;
    136     if (!active) return;
    137     while ((count = Pm_Read(midi_in, &event, 1))) {
    138         if (count == 1) output(event.message);
    139         else            printf(Pm_GetErrorText(count));
    140     }
    141 }
    142 
    143 
    144 /****************************************************************************
    145 *               main
    146 * Effect: prompts for parameters, starts monitor
    147 ****************************************************************************/
    148 
    149 int main(int argc, char **argv)
    150 {
    151     char *argument;
    152     int inp;
    153     PmError err;
    154     int i;
    155     if (argc > 1) { /* first arg can change defaults */
    156         argument = argv[1];
    157         while (*argument) doascii(*argument++);
    158     }
    159     showhelp();
    160     /* use porttime callback to empty midi queue and print */
    161     Pt_Start(1, receive_poll, 0);
    162     /* list device information */
    163     printf("MIDI input devices:\n");
    164     for (i = 0; i < Pm_CountDevices(); i++) {
    165         const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
    166         if (info->input) printf("%d: %s, %s\n", i, info->interf, info->name);
    167     }
    168     inp = get_number("Type input device number: ");
    169     err = Pm_OpenInput(&midi_in, inp, NULL, 512, NULL, NULL);
    170     if (err) {
    171         printf(Pm_GetErrorText(err));
    172         Pt_Stop();
    173         mmexit(1);
    174     }
    175     Pm_SetFilter(midi_in, filter);
    176     inited = true; /* now can document changes, set filter */
    177     printf("Midi Monitor ready.\n");
    178     active = true;
    179     while (!done) {
    180         char s[100];
    181         if (fgets(s, 100, stdin)) {
    182             doascii(s[0]);
    183         }
    184     }
    185     active = false;
    186     Pm_Close(midi_in);
    187     Pt_Stop();
    188     Pm_Terminate();
    189     mmexit(0);
    190     return 0; // make the compiler happy be returning a value
    191 }
    192 
    193 
    194 /****************************************************************************
    195 *               doascii
    196 * Inputs:
    197 *    char c: input character
    198 * Effect: interpret to revise flags
    199 ****************************************************************************/
    200 
    201 private void doascii(char c)
    202 {
    203     if (isupper(c)) c = tolower(c);
    204     if (c == 'q') done = true;
    205     else if (c == 'b') {
    206         bender = !bender;
    207         filter ^= PM_FILT_PITCHBEND;
    208         if (inited)
    209             printf("Pitch Bend, etc. %s\n", (bender ? "ON" : "OFF"));
    210     } else if (c == 'c') {
    211         controls = !controls;
    212         filter ^= PM_FILT_CONTROL;
    213         if (inited)
    214             printf("Control Change %s\n", (controls ? "ON" : "OFF"));
    215     } else if (c == 'h') {
    216         pgchanges = !pgchanges;
    217         filter ^= PM_FILT_PROGRAM;
    218         if (inited)
    219             printf("Program Changes %s\n", (pgchanges ? "ON" : "OFF"));
    220     } else if (c == 'n') {
    221         notes = !notes;
    222         filter ^= PM_FILT_NOTE;
    223         if (inited)
    224             printf("Notes %s\n", (notes ? "ON" : "OFF"));
    225     } else if (c == 'x') {
    226         excldata = !excldata;
    227         filter ^= PM_FILT_SYSEX;
    228         if (inited)
    229             printf("System Exclusive data %s\n", (excldata ? "ON" : "OFF"));
    230     } else if (c == 'r') {
    231         realdata = !realdata;
    232         filter ^= (PM_FILT_PLAY | PM_FILT_RESET | PM_FILT_TICK | PM_FILT_UNDEFINED);
    233         if (inited)
    234             printf("Real Time messages %s\n", (realdata ? "ON" : "OFF"));
    235     } else if (c == 'k') {
    236         clksencnt = !clksencnt;
    237         filter ^= PM_FILT_CLOCK;
    238         if (inited)
    239             printf("Clock and Active Sense Counting %s\n", (clksencnt ? "ON" : "OFF"));
    240         if (!clksencnt) clockcount = actsensecount = 0;
    241     } else if (c == 's') {
    242         if (clksencnt) {
    243             if (inited)
    244                 printf("Clock Count %ld\nActive Sense Count %ld\n", 
    245                         (long) clockcount, (long) actsensecount);
    246         } else if (inited) {
    247             printf("Clock Counting not on\n");
    248         }
    249     } else if (c == 't') {
    250         notestotal+=notescount;
    251         if (inited)
    252             printf("This Note Count %ld\nTotal Note Count %ld\n",
    253                     (long) notescount, (long) notestotal);
    254         notescount=0;
    255     } else if (c == 'v') {
    256         verbose = !verbose;
    257         if (inited)
    258             printf("Verbose %s\n", (verbose ? "ON" : "OFF"));
    259     } else if (c == 'm') {
    260         chmode = !chmode;
    261         if (inited)
    262             printf("Channel Mode Messages %s", (chmode ? "ON" : "OFF"));
    263     } else {
    264         if (inited) {
    265             if (c == ' ') {
    266                 PmEvent event;
    267                 while (Pm_Read(midi_in, &event, 1)) ;	/* flush midi input */
    268                 printf("...FLUSHED MIDI INPUT\n\n");
    269             } else showhelp();
    270         }
    271     }
    272     if (inited) Pm_SetFilter(midi_in, filter);
    273 }
    274 
    275 
    276 
    277 private void mmexit(int code)
    278 {
    279     /* if this is not being run from a console, maybe we should wait for
    280      * the user to read error messages before exiting
    281      */
    282     exit(code);
    283 }
    284 
    285 
    286 /****************************************************************************
    287 *               output
    288 * Inputs:
    289 *    data: midi message buffer holding one command or 4 bytes of sysex msg
    290 * Effect: format and print  midi data
    291 ****************************************************************************/
    292 
    293 char vel_format[] = "    Vel %d\n";
    294 
    295 private void output(PmMessage data)
    296 {
    297     int command;    /* the current command */
    298     int chan;   /* the midi channel of the current event */
    299     int len;    /* used to get constant field width */
    300 
    301     /* printf("output data %8x; ", data); */
    302 
    303     command = Pm_MessageStatus(data) & MIDI_CODE_MASK;
    304     chan = Pm_MessageStatus(data) & MIDI_CHN_MASK;
    305 
    306     if (in_sysex || Pm_MessageStatus(data) == MIDI_SYSEX) {
    307 #define sysex_max 16
    308         int i;
    309         PmMessage data_copy = data;
    310         in_sysex = true;
    311         /* look for MIDI_EOX in first 3 bytes 
    312          * if realtime messages are embedded in sysex message, they will
    313          * be printed as if they are part of the sysex message
    314          */
    315         for (i = 0; (i < 4) && ((data_copy & 0xFF) != MIDI_EOX); i++) 
    316             data_copy >>= 8;
    317         if (i < 4) {
    318             in_sysex = false;
    319             i++; /* include the EOX byte in output */
    320         }
    321         showbytes(data, i, verbose);
    322         if (verbose) printf("System Exclusive\n");
    323     } else if (command == MIDI_ON_NOTE && Pm_MessageData2(data) != 0) {
    324         notescount++;
    325         if (notes) {
    326             showbytes(data, 3, verbose);
    327             if (verbose) {
    328                 printf("NoteOn  Chan %2d Key %3d ", chan, Pm_MessageData1(data));
    329                 len = put_pitch(Pm_MessageData1(data));
    330                 printf(vel_format + len, Pm_MessageData2(data));
    331             }
    332         }
    333     } else if ((command == MIDI_ON_NOTE /* && Pm_MessageData2(data) == 0 */ ||
    334                command == MIDI_OFF_NOTE) && notes) {
    335         showbytes(data, 3, verbose);
    336         if (verbose) {
    337             printf("NoteOff Chan %2d Key %3d ", chan, Pm_MessageData1(data));
    338             len = put_pitch(Pm_MessageData1(data));
    339             printf(vel_format + len, Pm_MessageData2(data));
    340         }
    341     } else if (command == MIDI_CH_PROGRAM && pgchanges) {
    342         showbytes(data, 2, verbose);
    343         if (verbose) {
    344             printf("  ProgChg Chan %2d Prog %2d\n", chan, Pm_MessageData1(data) + 1);
    345         }
    346     } else if (command == MIDI_CTRL) {
    347                /* controls 121 (MIDI_RESET_CONTROLLER) to 127 are channel
    348                 * mode messages. */
    349         if (Pm_MessageData1(data) < MIDI_ALL_SOUND_OFF) {
    350             showbytes(data, 3, verbose);
    351             if (verbose) {
    352                 printf("CtrlChg Chan %2d Ctrl %2d Val %2d\n",
    353                        chan, Pm_MessageData1(data), Pm_MessageData2(data));
    354             }
    355         } else /* channel mode */ if (chmode) {
    356             showbytes(data, 3, verbose);
    357             if (verbose) {
    358                 switch (Pm_MessageData1(data)) {
    359                   case MIDI_ALL_SOUND_OFF:
    360                       printf("All Sound Off, Chan %2d\n", chan);
    361                     break;
    362                   case MIDI_RESET_CONTROLLERS:
    363                     printf("Reset All Controllers, Chan %2d\n", chan);
    364                     break;
    365                   case MIDI_LOCAL:
    366                     printf("LocCtrl Chan %2d %s\n",
    367                             chan, Pm_MessageData2(data) ? "On" : "Off");
    368                     break;
    369                   case MIDI_ALL_OFF:
    370                     printf("All Off Chan %2d\n", chan);
    371                     break;
    372                   case MIDI_OMNI_OFF:
    373                     printf("OmniOff Chan %2d\n", chan);
    374                     break;
    375                   case MIDI_OMNI_ON:
    376                     printf("Omni On Chan %2d\n", chan);
    377                     break;
    378                   case MIDI_MONO_ON:
    379                     printf("Mono On Chan %2d\n", chan);
    380                     if (Pm_MessageData2(data))
    381                         printf(" to %d received channels\n", Pm_MessageData2(data));
    382                     else
    383                         printf(" to all received channels\n");
    384                     break;
    385                   case MIDI_POLY_ON:
    386                     printf("Poly On Chan %2d\n", chan);
    387                     break;
    388                 }
    389             }
    390         }
    391     } else if (command == MIDI_POLY_TOUCH && bender) {
    392         showbytes(data, 3, verbose);
    393         if (verbose) {
    394             printf("P.Touch Chan %2d Key %2d ", chan, Pm_MessageData1(data));
    395             len = put_pitch(Pm_MessageData1(data));
    396             printf(val_format + len, Pm_MessageData2(data));
    397         }
    398     } else if (command == MIDI_TOUCH && bender) {
    399         showbytes(data, 2, verbose);
    400         if (verbose) {
    401             printf("  A.Touch Chan %2d Val %2d\n", chan, Pm_MessageData1(data));
    402         }
    403     } else if (command == MIDI_BEND && bender) {
    404         showbytes(data, 3, verbose);
    405         if (verbose) {
    406             printf("P.Bend  Chan %2d Val %2d\n", chan,
    407                     (Pm_MessageData1(data) + (Pm_MessageData2(data)<<7)));
    408         }
    409     } else if (Pm_MessageStatus(data) == MIDI_SONG_POINTER) {
    410         showbytes(data, 3, verbose);
    411         if (verbose) {
    412             printf("    Song Position %d\n",
    413                     (Pm_MessageData1(data) + (Pm_MessageData2(data)<<7)));
    414         }
    415     } else if (Pm_MessageStatus(data) == MIDI_SONG_SELECT) {
    416         showbytes(data, 2, verbose);
    417         if (verbose) {
    418             printf("    Song Select %d\n", Pm_MessageData1(data));
    419         }
    420     } else if (Pm_MessageStatus(data) == MIDI_TUNE_REQ) {
    421         showbytes(data, 1, verbose);
    422         if (verbose) {
    423             printf("    Tune Request\n");
    424         }
    425     } else if (Pm_MessageStatus(data) == MIDI_Q_FRAME && realdata) {
    426         showbytes(data, 2, verbose);
    427         if (verbose) {
    428             printf("    Time Code Quarter Frame Type %d Values %d\n",
    429                     (Pm_MessageData1(data) & 0x70) >> 4, Pm_MessageData1(data) & 0xf);
    430         }
    431     } else if (Pm_MessageStatus(data) == MIDI_START && realdata) {
    432         showbytes(data, 1, verbose);
    433         if (verbose) {
    434             printf("    Start\n");
    435         }
    436     } else if (Pm_MessageStatus(data) == MIDI_CONTINUE && realdata) {
    437         showbytes(data, 1, verbose);
    438         if (verbose) {
    439             printf("    Continue\n");
    440         }
    441     } else if (Pm_MessageStatus(data) == MIDI_STOP && realdata) {
    442         showbytes(data, 1, verbose);
    443         if (verbose) {
    444             printf("    Stop\n");
    445         }
    446     } else if (Pm_MessageStatus(data) == MIDI_SYS_RESET && realdata) {
    447         showbytes(data, 1, verbose);
    448         if (verbose) {
    449             printf("    System Reset\n");
    450         }
    451     } else if (Pm_MessageStatus(data) == MIDI_TIME_CLOCK) {
    452         if (clksencnt) clockcount++;
    453         else if (realdata) {
    454             showbytes(data, 1, verbose);
    455             if (verbose) {
    456                 printf("    Clock\n");
    457             }
    458         }
    459     } else if (Pm_MessageStatus(data) == MIDI_ACTIVE_SENSING) {
    460         if (clksencnt) actsensecount++;
    461         else if (realdata) {
    462             showbytes(data, 1, verbose);
    463             if (verbose) {
    464                 printf("    Active Sensing\n");
    465             }
    466         }
    467     } else showbytes(data, 3, verbose);
    468     fflush(stdout);
    469 }
    470 
    471 
    472 /****************************************************************************
    473 *               put_pitch
    474 * Inputs:
    475 *    int p: pitch number
    476 * Effect: write out the pitch name for a given number
    477 ****************************************************************************/
    478 
    479 private int put_pitch(int p)
    480 {
    481     char result[8];
    482     static char *ptos[] = {
    483         "c", "cs", "d", "ef", "e", "f", "fs", "g",
    484         "gs", "a", "bf", "b"    };
    485     /* note octave correction below */
    486     sprintf(result, "%s%d", ptos[p % 12], (p / 12) - 1);
    487     printf(result);
    488     return strlen(result);
    489 }
    490 
    491 
    492 /****************************************************************************
    493 *               showbytes
    494 * Effect: print hex data, precede with newline if asked
    495 ****************************************************************************/
    496 
    497 char nib_to_hex[] = "0123456789ABCDEF";
    498 
    499 private void showbytes(PmMessage data, int len, boolean newline)
    500 {
    501     int count = 0;
    502     int i;
    503 
    504 /*    if (newline) {
    505         putchar('\n');
    506         count++;
    507     } */
    508     for (i = 0; i < len; i++) {
    509         putchar(nib_to_hex[(data >> 4) & 0xF]);
    510         putchar(nib_to_hex[data & 0xF]);
    511         count += 2;
    512         if (count > 72) {
    513             putchar('.');
    514             putchar('.');
    515             putchar('.');
    516             break;
    517         }
    518         data >>= 8;
    519     }
    520     putchar(' ');
    521 }
    522 
    523 
    524 
    525 /****************************************************************************
    526 *               showhelp
    527 * Effect: print help text
    528 ****************************************************************************/
    529 
    530 private void showhelp()
    531 {
    532     printf("\n");
    533     printf("   Item Reported  Range     Item Reported  Range\n");
    534     printf("   -------------  -----     -------------  -----\n");
    535     printf("   Channels       1 - 16    Programs       1 - 128\n");
    536     printf("   Controllers    0 - 127   After Touch    0 - 127\n");
    537     printf("   Loudness       0 - 127   Pitch Bend     0 - 16383, center = 8192\n");
    538     printf("   Pitches        0 - 127, 60 = c4 = middle C\n");
    539     printf(" \n");
    540     printf("n toggles notes");
    541     showstatus(notes);
    542     printf("t displays noteon count since last t\n");
    543     printf("b toggles pitch bend, aftertouch");
    544     showstatus(bender);
    545     printf("c toggles continuous control");
    546     showstatus(controls);
    547     printf("h toggles program changes");
    548     showstatus(pgchanges);
    549     printf("x toggles system exclusive");
    550     showstatus(excldata);
    551     printf("k toggles clock and sense counting only");
    552     showstatus(clksencnt);
    553     printf("r toggles other real time messages & SMPTE");
    554     showstatus(realdata);
    555     printf("s displays clock and sense count since last k\n");
    556     printf("m toggles channel mode messages");
    557     showstatus(chmode);
    558     printf("v toggles verbose text");
    559     showstatus(verbose);
    560     printf("q quits\n");
    561     printf("\n");
    562 }
    563 
    564 /****************************************************************************
    565 *               showstatus
    566 * Effect: print status of flag
    567 ****************************************************************************/
    568 
    569 private void showstatus(boolean flag)
    570 {
    571     printf(", now %s\n", flag ? "ON" : "OFF" );
    572 }