aboutsummaryrefslogtreecommitdiff
path: root/portmidi/pm_test/multivirtual.c
diff options
context:
space:
mode:
Diffstat (limited to 'portmidi/pm_test/multivirtual.c')
-rw-r--r--portmidi/pm_test/multivirtual.c223
1 files changed, 223 insertions, 0 deletions
diff --git a/portmidi/pm_test/multivirtual.c b/portmidi/pm_test/multivirtual.c
new file mode 100644
index 0000000..b90d860
--- /dev/null
+++ b/portmidi/pm_test/multivirtual.c
@@ -0,0 +1,223 @@
1/* multivirtual.c -- test for creating two input and two output virtual ports */
2/*
3 * Roger B. Dannenberg
4 * Oct 2021
5 */
6#include "portmidi.h"
7#include "porttime.h"
8#include "stdlib.h"
9#include "stdio.h"
10#include "string.h"
11#include "assert.h"
12
13#define OUTPUT_BUFFER_SIZE 0
14#define DEVICE_INFO NULL
15#define DRIVER_INFO NULL
16#define TIME_PROC ((PmTimeProcPtr) Pt_Time)
17#define TIME_INFO NULL
18#define TIME_START Pt_Start(1, 0, 0) /* timer started w/millisecond accuracy */
19
20int latency = 0;
21
22static void prompt_and_exit(void)
23{
24 printf("type ENTER...");
25 while (getchar() != '\n') ;
26 /* this will clean up open ports: */
27 exit(-1);
28}
29
30
31static PmError checkerror(PmError err)
32{
33 if (err == pmHostError) {
34 /* it seems pointless to allocate memory and copy the string,
35 * so I will do the work of Pm_GetHostErrorText directly
36 */
37 char errmsg[80];
38 Pm_GetHostErrorText(errmsg, 80);
39 printf("PortMidi found host error...\n %s\n", errmsg);
40 prompt_and_exit();
41 } else if (err < 0) {
42 printf("PortMidi call failed...\n %s\n", Pm_GetErrorText(err));
43 prompt_and_exit();
44 }
45 return err;
46}
47
48static int msg_count[2] = {0, 0};
49
50void poll_input(PmStream *in, int which)
51{
52 PmEvent buffer[1];
53 int pitch, expected, length;
54 PmError status = Pm_Poll(in);
55 if (status == TRUE) {
56 length = Pm_Read(in, buffer, 1);
57 if (length > 0) {
58 printf("Got message %d from portmidi%d: "
59 "time %ld, %2x %2x %2x\n",
60 msg_count[which], which + 1, (long) buffer[0].timestamp,
61 (status = Pm_MessageStatus(buffer[0].message)),
62 (pitch = Pm_MessageData1(buffer[0].message)),
63 Pm_MessageData2(buffer[0].message));
64 if (status == 0x90) { /* 1 & 2 are on/off 60, 3 & 4 are 61, etc. */
65 expected = (((msg_count[which] - 1) / 2) % 12) + 60 +
66 which * 12;
67 if (pitch != expected) {
68 printf("WARNING: expected pitch %d, got pitch %d\n",
69 expected, pitch);
70 }
71 }
72 msg_count[which]++;
73 } else {
74 assert(0);
75 }
76 }
77}
78
79
80void wait_until(PmTimestamp when, PmStream *in1, PmStream *in2)
81{
82 while (when > Pt_Time()) {
83 poll_input(in1, 0);
84 poll_input(in2, 1);
85 Pt_Sleep(10);
86 }
87}
88
89
90/* create one virtual output device and one input device */
91void init(const char *name, PmStream **midi_out, PmStream **midi_in,
92 int *id_out, int *id_in)
93{
94 PmEvent buffer[1];
95
96 *id_out = checkerror(Pm_CreateVirtualOutput(name, NULL, DEVICE_INFO));
97 checkerror(Pm_OpenOutput(midi_out, *id_out, DRIVER_INFO, OUTPUT_BUFFER_SIZE,
98 TIME_PROC, TIME_INFO, latency));
99 printf("Virtual Output \"%s\" id %d created and opened.\n", name, *id_out);
100
101 *id_in = checkerror(Pm_CreateVirtualInput(name, NULL, DRIVER_INFO));
102 checkerror(Pm_OpenInput(midi_in, *id_in, NULL, 0, NULL, NULL));
103 printf("Virtual Input \"%s\" id %d created and opened.\n", name, *id_in);
104 Pm_SetFilter(*midi_in, PM_FILT_ACTIVE | PM_FILT_CLOCK | PM_FILT_SYSEX);
105 /* empty the buffer after setting filter, just in case anything
106 got through */
107 while (Pm_Read(*midi_in, buffer, 1)) ;
108}
109
110
111void main_test(int num)
112{
113 PmStream *midi1_out;
114 PmStream *midi2_out;
115 PmStream *midi1_in;
116 PmStream *midi2_in;
117 int id1_out;
118 int id2_out;
119 int id1_in;
120 int id2_in;
121 int32_t next_time;
122 PmEvent buffer[1];
123 int pitch = 60;
124 int expected_count = num + 1; /* add 1 for MIDI Program message */
125
126 /* It is recommended to start timer before Midi; otherwise, PortMidi may
127 start the timer with its (default) parameters
128 */
129 TIME_START;
130
131 init("portmidi1", &midi1_out, &midi1_in, &id1_out, &id1_in);
132 init("portmidi2", &midi2_out, &midi2_in, &id2_out, &id2_in);
133
134 printf("Type ENTER to send messages: ");
135 while (getchar() != '\n') ;
136
137 buffer[0].timestamp = Pt_Time();
138#define PROGRAM 0
139 buffer[0].message = Pm_Message(0xC0, PROGRAM, 0);
140 Pm_Write(midi1_out, buffer, 1);
141 Pm_Write(midi2_out, buffer, 1);
142 next_time = Pt_Time() + 1000; /* wait 1s */
143 while (num > 0) {
144 wait_until(next_time, midi1_in, midi2_in);
145 Pm_WriteShort(midi1_out, next_time, Pm_Message(0x90, pitch, 100));
146 Pm_WriteShort(midi2_out, next_time, Pm_Message(0x90, pitch + 12, 100));
147 printf("Note On pitch %d\n", pitch);
148 num--;
149 next_time += 500;
150
151 wait_until(next_time, midi1_in, midi2_in);
152 Pm_WriteShort(midi1_out, next_time, Pm_Message(0x90, pitch, 0));
153 Pm_WriteShort(midi2_out, next_time, Pm_Message(0x90, pitch + 12, 0));
154 printf("Note Off pitch %d\n", pitch);
155 num--;
156 pitch = (pitch + 1) % 12 + 60;
157 next_time += 500;
158 }
159 wait_until(next_time, midi1_in, midi2_in); /* get final note-offs */
160
161 printf("Got %d messages from portmidi1 and %d from portmidi2; "
162 "expected %d.\n", msg_count[0], msg_count[1], expected_count);
163
164 /* close devices (this not explicitly needed in most implementations) */
165 printf("ready to close...");
166 checkerror(Pm_Close(midi1_out));
167 checkerror(Pm_Close(midi2_out));
168 checkerror(Pm_Close(midi1_in));
169 checkerror(Pm_Close(midi2_in));
170 printf("done closing.\nNow delete the virtual devices...");
171 checkerror(Pm_DeleteVirtualDevice(id1_out));
172 checkerror(Pm_DeleteVirtualDevice(id1_in));
173 checkerror(Pm_DeleteVirtualDevice(id2_out));
174 checkerror(Pm_DeleteVirtualDevice(id2_in));
175 printf("done deleting.\n");
176}
177
178
179void show_usage()
180{
181 printf("Usage: multivirtual [-h] [-l latency-in-ms] [n]\n"
182 " -h for this message,\n"
183 " -l ms designates latency for precise timing (default 0),\n"
184 " n is number of message to send each output, not counting\n"
185 " initial program change.\n"
186 "sends change program to 1, then one note per second with 0.5s on,\n"
187 "0.5s off, for n/2 seconds to both output ports portmidi1 and\n"
188 "portmidi2. portmidi1 gets pitches from C4 (60). portmidi2 gets\n"
189 "pitches an octave higher. Latency >0 uses the device driver for \n"
190 "precise timing (see PortMidi documentation). Inputs print what\n"
191 "they get and print WARNING if they get something unexpected.\n"
192 "The expected test is use two instances of testio to loop\n"
193 "portmidi1 back to portmidi1 and portmidi2 back to portmidi2.\n");
194 exit(0);
195}
196
197
198int main(int argc, char *argv[])
199{
200 int num = 10;
201 int i;
202 for (i = 1; i < argc; i++) {
203 if (strcmp(argv[i], "-h") == 0) {
204 show_usage();
205 } else if (strcmp(argv[i], "-l") == 0 && (i + 1 < argc)) {
206 i = i + 1;
207 latency = atoi(argv[i]);
208 printf("Latency will be %d\n", latency);
209 } else {
210 num = atoi(argv[1]);
211 if (num <= 0) {
212 show_usage();
213 }
214 printf("Sending %d messages.\n", num);
215 }
216 }
217
218 main_test(num);
219
220 printf("finished sendvirtual test...type ENTER to quit...");
221 while (getchar() != '\n') ;
222 return 0;
223}