aboutsummaryrefslogtreecommitdiff
path: root/portmidi/pm_test/testio.c
diff options
context:
space:
mode:
Diffstat (limited to 'portmidi/pm_test/testio.c')
-rwxr-xr-xportmidi/pm_test/testio.c594
1 files changed, 0 insertions, 594 deletions
diff --git a/portmidi/pm_test/testio.c b/portmidi/pm_test/testio.c
deleted file mode 100755
index 2711286..0000000
--- a/portmidi/pm_test/testio.c
+++ /dev/null
@@ -1,594 +0,0 @@
1#include "portmidi.h"
2#include "porttime.h"
3#include "stdlib.h"
4#include "stdio.h"
5#include "string.h"
6#include "assert.h"
7
8#define INPUT_BUFFER_SIZE 100
9#define OUTPUT_BUFFER_SIZE 0
10#define TIME_PROC ((int32_t (*)(void *)) Pt_Time)
11#define TIME_INFO NULL
12#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */
13
14#define WAIT_FOR_ENTER while (getchar() != '\n') ;
15
16int32_t latency = 0;
17int verbose = FALSE;
18PmSysDepInfo *sysdepinfo = NULL;
19
20/* crash the program to test whether midi ports are closed */
21/**/
22void doSomethingReallyStupid() {
23 int * tmp = NULL;
24 *tmp = 5;
25}
26
27
28/* exit the program without any explicit cleanup */
29/**/
30void doSomethingStupid() {
31 assert(0);
32}
33
34
35/* read a number from console */
36/**/
37int get_number(const char *prompt)
38{
39 int n = 0, i;
40 fputs(prompt, stdout);
41 while (n != 1) {
42 n = scanf("%d", &i);
43 WAIT_FOR_ENTER
44 }
45 return i;
46}
47
48
49static void set_sysdepinfo(char m_or_p, const char *name)
50{
51 if (!sysdepinfo) {
52 // allocate some space we will alias with open-ended PmDriverInfo:
53 // there is space for 4 parameters:
54 static char dimem[sizeof(PmSysDepInfo) + sizeof(void *) * 8];
55 sysdepinfo = (PmSysDepInfo *) dimem;
56 // build the driver info structure:
57 sysdepinfo->structVersion = PM_SYSDEPINFO_VERS;
58 sysdepinfo->length = 0;
59 }
60 if (sysdepinfo->length > 1) {
61 printf("Error: sysdepinfo was allocated to hold 2 parameters\n");
62 exit(1);
63 }
64 int i = sysdepinfo->length++;
65 enum PmSysDepPropertyKey k = pmKeyNone;
66 if (m_or_p == 'm') k = pmKeyCoreMidiManufacturer;
67 else if (m_or_p == 'p') k = pmKeyAlsaPortName;
68 else if (m_or_p == 'c') k = pmKeyAlsaClientName;
69 sysdepinfo->properties[i].key = k;
70 sysdepinfo->properties[i].value = name;
71}
72
73
74/*
75 * the somethingStupid parameter can be set to simulate a program crash.
76 * We want PortMidi to close Midi ports automatically in the event of a
77 * crash because Windows does not (and this may cause an OS crash)
78 */
79void main_test_input(unsigned int somethingStupid) {
80 PmStream * midi;
81 PmError status, length;
82 PmEvent buffer[1];
83 int num = 10;
84 int i = get_number("Type input number: ");
85 /* It is recommended to start timer before Midi; otherwise, PortMidi may
86 start the timer with its (default) parameters
87 */
88 TIME_START;
89
90 /* open input device */
91 Pm_OpenInput(&midi,
92 i,
93 sysdepinfo,
94 INPUT_BUFFER_SIZE,
95 TIME_PROC,
96 TIME_INFO);
97
98 printf("Midi Input opened. Reading %d Midi messages...\n", num);
99 Pm_SetFilter(midi, PM_FILT_ACTIVE | PM_FILT_CLOCK | PM_FILT_SYSEX);
100 /* empty the buffer after setting filter, just in case anything
101 got through */
102 while (Pm_Poll(midi)) {
103 Pm_Read(midi, buffer, 1);
104 }
105 /* now start paying attention to messages */
106 i = 0; /* count messages as they arrive */
107 while (i < num) {
108 status = Pm_Poll(midi);
109 if (status == TRUE) {
110 length = Pm_Read(midi, buffer, 1);
111 if (length > 0) {
112 printf("Got message %d @ time %ld: timestamp %ld, "
113 "%2lx %2lx %2lx\n", i, (long) Pt_Time(),
114 (long) buffer[0].timestamp,
115 (long) Pm_MessageStatus(buffer[0].message),
116 (long) Pm_MessageData1(buffer[0].message),
117 (long) Pm_MessageData2(buffer[0].message));
118 i++;
119 } else {
120 assert(0);
121 }
122 }
123 /* simulate crash if somethingStupid is 1 or 2 */
124 if ((i > (num/2)) && (somethingStupid == 1)) {
125 doSomethingStupid();
126 } else if ((i > (num/2)) && (somethingStupid == 2)) {
127 doSomethingReallyStupid();
128 }
129 }
130
131 /* close device (this not explicitly needed in most implementations) */
132 printf("ready to close...");
133
134 Pm_Close(midi);
135 printf("done closing...");
136}
137
138
139
140void main_test_output(int isochronous_test)
141{
142 PmStream * midi;
143 int32_t off_time;
144 int chord[] = { 60, 67, 76, 83, 90 };
145 #define chord_size 5
146 PmEvent buffer[chord_size];
147 PmTimestamp timestamp;
148
149 /* determine which output device to use */
150 int i = get_number("Type output number: ");
151
152 /* It is recommended to start timer before PortMidi */
153 TIME_START;
154
155 /* open output device -- since PortMidi avoids opening a timer
156 when latency is zero, we will pass in a NULL timer pointer
157 for that case. If PortMidi tries to access the time_proc,
158 we will crash, so this test will tell us something. */
159 Pm_OpenOutput(&midi,
160 i,
161 sysdepinfo,
162 OUTPUT_BUFFER_SIZE,
163 (latency == 0 ? NULL : TIME_PROC),
164 (latency == 0 ? NULL : TIME_INFO),
165 latency);
166 printf("Midi Output opened with %ld ms latency.\n", (long) latency);
167
168 /* output note on/off w/latency offset; hold until user prompts */
169 printf("ready to send program 1 change... (type ENTER):");
170 WAIT_FOR_ENTER
171 /* if we were writing midi for immediate output, we could always use
172 timestamps of zero, but since we may be writing with latency, we
173 will explicitly set the timestamp to "now" by getting the time.
174 The source of timestamps should always correspond to the TIME_PROC
175 and TIME_INFO parameters used in Pm_OpenOutput(). */
176 buffer[0].timestamp = Pt_Time();
177 /* Send a program change to increase the chances we will hear notes */
178 /* Program 0 is usually a piano, but you can change it here: */
179#define PROGRAM 0
180 buffer[0].message = Pm_Message(0xC0, PROGRAM, 0);
181 Pm_Write(midi, buffer, 1);
182
183 if (isochronous_test) { // play 4 notes per sec for 20s
184 int count;
185 PmTimestamp start;
186 if (latency < 100) {
187 printf("Warning: latency < 100, but this test sends messages"
188 " at times that are jittered by up to 100ms, so you"
189 " may hear uneven timing\n");
190 }
191 printf("Starting in 1s..."); fflush(stdout);
192 Pt_Sleep(1000);
193 start = Pt_Time();
194 for (count = 0; count < 80; count++) {
195 PmTimestamp next_time;
196 buffer[0].timestamp = start + count * 250;
197 buffer[0].message = Pm_Message(0x90, 69, 100);
198 buffer[1].timestamp = start + count * 250 + 200;
199 buffer[1].message = Pm_Message(0x90, 69, 0);
200 Pm_Write(midi, buffer, 2);
201 next_time = start + (count + 1) * 250;
202 // sleep for a random time up to 100ms to add jitter to
203 // the times at which we send messages. PortMidi timing
204 // should remove the jitter if latency > 100
205 while (Pt_Time() < next_time) {
206 Pt_Sleep(rand() % 100);
207 }
208 }
209 printf("Done sending 80 notes at 4 notes per second.\n");
210 } else {
211 PmError err = 0;
212 printf("ready to note-on... (type ENTER):");
213 WAIT_FOR_ENTER
214 buffer[0].timestamp = Pt_Time();
215 buffer[0].message = Pm_Message(0x90, 60, 100);
216 if ((err = Pm_Write(midi, buffer, 1))) {
217 printf("Pm_Write returns error: %d (%s)\n",
218 err, Pm_GetErrorText(err));
219 if (err == pmHostError) {
220 char errmsg[128];
221 Pm_GetHostErrorText(errmsg, 127);
222 printf(" Host error: %s\n", errmsg);
223 }
224 }
225 printf("ready to note-off... (type ENTER):");
226 WAIT_FOR_ENTER
227 buffer[0].timestamp = Pt_Time();
228 buffer[0].message = Pm_Message(0x90, 60, 0);
229 if ((err = Pm_Write(midi, buffer, 1))) {
230 printf("Pm_Write returns error: %d (%s)\n",
231 err, Pm_GetErrorText(err));
232 if (err == pmHostError) {
233 char errmsg[128];
234 Pm_GetHostErrorText(errmsg, 127);
235 printf(" Host error: %s\n", errmsg);
236 }
237 }
238
239 /* output short note on/off w/latency offset; hold until user prompts */
240 printf("ready to note-on (short form)... (type ENTER):");
241 WAIT_FOR_ENTER
242 Pm_WriteShort(midi, Pt_Time(),
243 Pm_Message(0x90, 60, 100));
244 printf("ready to note-off (short form)... (type ENTER):");
245 WAIT_FOR_ENTER
246 Pm_WriteShort(midi, Pt_Time(),
247 Pm_Message(0x90, 60, 0));
248
249 /* output several note on/offs to test timing.
250 Should be 1s between notes */
251 if (latency == 0) {
252 printf("chord should not arpeggiate, latency == 0\n");
253 } else {
254 printf("chord should arpeggiate (latency = %ld > 0\n",
255 (long) latency);
256 }
257 printf("ready to chord-on/chord-off... (type ENTER):");
258 WAIT_FOR_ENTER
259 timestamp = Pt_Time();
260 printf("starting timestamp %ld\n", (long) timestamp);
261 for (i = 0; i < chord_size; i++) {
262 buffer[i].timestamp = timestamp + 1000 * i;
263 buffer[i].message = Pm_Message(0x90, chord[i], 100);
264 }
265 Pm_Write(midi, buffer, chord_size);
266
267 off_time = timestamp + 1000 + chord_size * 1000;
268 while (Pt_Time() < off_time)
269 /* There was a report that Pm_Write with zero length sent last
270 * message again, so call Pm_Write here to see if note repeats
271 */
272 Pm_Write(midi, buffer, 0);
273 Pt_Sleep(20); /* wait */
274
275 for (i = 0; i < chord_size; i++) {
276 buffer[i].timestamp = timestamp + 1000 * i;
277 buffer[i].message = Pm_Message(0x90, chord[i], 0);
278 }
279 Pm_Write(midi, buffer, chord_size);
280 }
281
282 /* close device (this not explicitly needed in most implementations) */
283 printf("ready to close and terminate... (type ENTER):");
284 WAIT_FOR_ENTER
285
286 Pm_Close(midi);
287 Pm_Terminate();
288 printf("done closing and terminating...\n");
289}
290
291
292void main_test_both()
293{
294 int i = 0;
295 int in, out;
296 PmStream * midi, * midiOut;
297 PmEvent buffer[1];
298 PmError status, length;
299 int num = 11;
300
301 in = get_number("Type input number: ");
302 out = get_number("Type output number: ");
303
304 /* In is recommended to start timer before PortMidi */
305 TIME_START;
306
307 Pm_OpenOutput(&midiOut,
308 out,
309 sysdepinfo,
310 OUTPUT_BUFFER_SIZE,
311 TIME_PROC,
312 TIME_INFO,
313 latency);
314 printf("Midi Output opened with %ld ms latency.\n", (long) latency);
315 /* open input device */
316 Pm_OpenInput(&midi,
317 in,
318 sysdepinfo,
319 INPUT_BUFFER_SIZE,
320 TIME_PROC,
321 TIME_INFO);
322 printf("Midi Input opened. Reading %d Midi messages...\n", num);
323 Pm_SetFilter(midi, PM_FILT_ACTIVE | PM_FILT_CLOCK);
324 /* empty the buffer after setting filter, just in case anything
325 got through */
326 while (Pm_Poll(midi)) {
327 Pm_Read(midi, buffer, 1);
328 }
329 i = 0;
330 while (i < num) {
331 status = Pm_Poll(midi);
332 if (status == TRUE) {
333 length = Pm_Read(midi,buffer,1);
334 if (length > 0) {
335 Pm_Write(midiOut, buffer, 1);
336 printf("Got message %d @ time %ld: timestamp %ld, "
337 "%2lx %2lx %2lx\n", i, (long) Pt_Time(),
338 (long) buffer[0].timestamp,
339 (long) Pm_MessageStatus(buffer[0].message),
340 (long) Pm_MessageData1(buffer[0].message),
341 (long) Pm_MessageData2(buffer[0].message));
342 i++;
343 } else {
344 assert(0);
345 }
346 }
347 }
348 /* allow time for last message to go out */
349 Pt_Sleep(100 + latency);
350
351 /* close midi devices */
352 Pm_Close(midi);
353 Pm_Close(midiOut);
354 Pm_Terminate();
355}
356
357
358/* main_test_stream exercises windows winmm API's stream mode */
359/* The winmm stream mode is used for latency>0, and sends
360 timestamped messages. The timestamps are relative (delta)
361 times, whereas PortMidi times are absolute. Since peculiar
362 things happen when messages are not always sent in advance,
363 this function allows us to exercise the system and test it.
364 */
365void main_test_stream() {
366 PmStream * midi;
367 PmEvent buffer[16];
368
369 /* determine which output device to use */
370 int i = get_number("Type output number: ");
371
372 latency = 500; /* ignore LATENCY for this test and
373 fix the latency at 500ms */
374
375 /* It is recommended to start timer before PortMidi */
376 TIME_START;
377
378 /* open output device */
379 Pm_OpenOutput(&midi,
380 i,
381 sysdepinfo,
382 OUTPUT_BUFFER_SIZE,
383 TIME_PROC,
384 TIME_INFO,
385 latency);
386 printf("Midi Output opened with %ld ms latency.\n", (long) latency);
387
388 /* output note on/off w/latency offset; hold until user prompts */
389 printf("ready to send output... (type ENTER):");
390 WAIT_FOR_ENTER
391
392 /* if we were writing midi for immediate output, we could always use
393 timestamps of zero, but since we may be writing with latency, we
394 will explicitly set the timestamp to "now" by getting the time.
395 The source of timestamps should always correspond to the TIME_PROC
396 and TIME_INFO parameters used in Pm_OpenOutput(). */
397 buffer[0].timestamp = Pt_Time();
398 buffer[0].message = Pm_Message(0xC0, 0, 0);
399 buffer[1].timestamp = buffer[0].timestamp;
400 buffer[1].message = Pm_Message(0x90, 60, 100);
401 buffer[2].timestamp = buffer[0].timestamp + 1000;
402 buffer[2].message = Pm_Message(0x90, 62, 100);
403 buffer[3].timestamp = buffer[0].timestamp + 2000;
404 buffer[3].message = Pm_Message(0x90, 64, 100);
405 buffer[4].timestamp = buffer[0].timestamp + 3000;
406 buffer[4].message = Pm_Message(0x90, 66, 100);
407 buffer[5].timestamp = buffer[0].timestamp + 4000;
408 buffer[5].message = Pm_Message(0x90, 60, 0);
409 buffer[6].timestamp = buffer[0].timestamp + 4000;
410 buffer[6].message = Pm_Message(0x90, 62, 0);
411 buffer[7].timestamp = buffer[0].timestamp + 4000;
412 buffer[7].message = Pm_Message(0x90, 64, 0);
413 buffer[8].timestamp = buffer[0].timestamp + 4000;
414 buffer[8].message = Pm_Message(0x90, 66, 0);
415
416 Pm_Write(midi, buffer, 9);
417#ifdef SEND8
418 /* Now, we're ready for the real test.
419 Play 4 notes at now, now+500, now+1000, and now+1500
420 Then wait until now+2000.
421 Play 4 more notes as before.
422 We should hear 8 evenly spaced notes. */
423 now = Pt_Time();
424 for (i = 0; i < 4; i++) {
425 buffer[i * 2].timestamp = now + (i * 500);
426 buffer[i * 2].message = Pm_Message(0x90, 60, 100);
427 buffer[i * 2 + 1].timestamp = now + 250 + (i * 500);
428 buffer[i * 2 + 1].message = Pm_Message(0x90, 60, 0);
429 }
430 Pm_Write(midi, buffer, 8);
431
432 while (Pt_Time() < now + 2500)
433 Pt_Sleep(10);
434 /* now we are 500 ms behind schedule, but since the latency
435 is 500, the delay should not be audible */
436 now += 2000;
437 for (i = 0; i < 4; i++) {
438 buffer[i * 2].timestamp = now + (i * 500);
439 buffer[i * 2].message = Pm_Message(0x90, 60, 100);
440 buffer[i * 2 + 1].timestamp = now + 250 + (i * 500);
441 buffer[i * 2 + 1].message = Pm_Message(0x90, 60, 0);
442 }
443 Pm_Write(midi, buffer, 8);
444#endif
445 /* close device (this not explicitly needed in most implementations) */
446 printf("ready to close and terminate... (type ENTER):");
447 WAIT_FOR_ENTER
448
449 Pm_Close(midi);
450 Pm_Terminate();
451 printf("done closing and terminating...\n");
452}
453
454
455void show_usage()
456{
457 printf("Usage: test [-h] [-l latency-in-ms] [-c clientname] "
458 "[-p portname] [-v]\n"
459 " -h for this help message (only)\n"
460 " -l for latency\n"
461 " -c name designates a client name (linux only),\n"
462 " -p name designates a port name (linux only),\n"
463 " -v for verbose (enables more output)\n");
464}
465
466int main(int argc, char *argv[])
467{
468 int default_in;
469 int default_out;
470 int i = 0, n = 0;
471 int test_input = 0, test_output = 0, test_both = 0, somethingStupid = 0;
472 int isochronous_test = 0;
473 int stream_test = 0;
474 int latency_valid = FALSE;
475
476 show_usage();
477 if (sizeof(void *) == 8)
478 printf("Apparently this is a 64-bit machine.\n");
479 else if (sizeof(void *) == 4)
480 printf ("Apparently this is a 32-bit machine.\n");
481
482 for (i = 1; i < argc; i++) {
483 if (strcmp(argv[i], "-h") == 0) {
484 exit(0);
485 } else if (strcmp(argv[i], "-p") == 0 && (i + 1 < argc)) {
486 i = i + 1;
487 const char *port_name = argv[i];
488 set_sysdepinfo('p', port_name);
489 printf("Port name will be %s\n", port_name);
490 } else if (strcmp(argv[i], "-c") == 0 && (i + 1 < argc)) {
491 i = i + 1;
492 set_sysdepinfo('c', argv[i]);
493 printf("Client name will be %s\n", argv[i]);
494 } else if (strcmp(argv[i], "-l") == 0 && (i + 1 < argc)) {
495 i = i + 1;
496 latency = atoi(argv[i]);
497 printf("Latency will be %ld\n", (long) latency);
498 latency_valid = TRUE;
499 } else if (strcmp(argv[i], "-v") == 0) {
500 printf("Verbose is now TRUE\n");
501 verbose = TRUE; /* not currently used for anything */
502 } else {
503 show_usage();
504 exit(0);
505 }
506 }
507
508 while (!latency_valid) {
509 int lat; // declared int to match "%d"
510 printf("Latency in ms: ");
511 if (scanf("%d", &lat) == 1) {
512 latency = (int32_t) lat; // coerce from "%d" to known size
513 latency_valid = TRUE;
514 }
515 }
516
517 /* determine what type of test to run */
518 printf("begin portMidi test...\n");
519 printf("enter your choice...\n 1: test input\n"
520 " 2: test input (fail w/assert)\n"
521 " 3: test input (fail w/NULL assign)\n"
522 " 4: test output\n 5: test both\n"
523 " 6: stream test (for WinMM)\n"
524 " 7. isochronous out\n");
525 while (n != 1) {
526 n = scanf("%d", &i);
527 WAIT_FOR_ENTER
528 switch(i) {
529 case 1:
530 test_input = 1;
531 break;
532 case 2:
533 test_input = 1;
534 somethingStupid = 1;
535 break;
536 case 3:
537 test_input = 1;
538 somethingStupid = 2;
539 break;
540 case 4:
541 test_output = 1;
542 break;
543 case 5:
544 test_both = 1;
545 break;
546 case 6:
547 stream_test = 1;
548 break;
549 case 7:
550 test_output = 1;
551 isochronous_test = 1;
552 break;
553 default:
554 printf("got %d (invalid input)\n", n);
555 break;
556 }
557 }
558
559 /* list device information */
560 default_in = Pm_GetDefaultInputDeviceID();
561 default_out = Pm_GetDefaultOutputDeviceID();
562 for (i = 0; i < Pm_CountDevices(); i++) {
563 char *deflt;
564 const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
565 if (((test_input | test_both) & info->input) |
566 ((test_output | test_both | stream_test) & info->output)) {
567 printf("%d: %s, %s", i, info->interf, info->name);
568 if (info->input) {
569 deflt = (i == default_in ? "default " : "");
570 printf(" (%sinput)", deflt);
571 }
572 if (info->output) {
573 deflt = (i == default_out ? "default " : "");
574 printf(" (%soutput)", deflt);
575 }
576 printf("\n");
577 }
578 }
579
580 /* run test */
581 if (stream_test) {
582 main_test_stream();
583 } else if (test_input) {
584 main_test_input(somethingStupid);
585 } else if (test_output) {
586 main_test_output(isochronous_test);
587 } else if (test_both) {
588 main_test_both();
589 }
590
591 printf("finished portMidi test...type ENTER to quit...");
592 WAIT_FOR_ENTER
593 return 0;
594}