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