diff options
Diffstat (limited to 'portmidi/pm_test/multivirtual.c')
| -rw-r--r-- | portmidi/pm_test/multivirtual.c | 223 |
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 | |||
| 20 | int latency = 0; | ||
| 21 | |||
| 22 | static 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 | |||
| 31 | static 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 | |||
| 48 | static int msg_count[2] = {0, 0}; | ||
| 49 | |||
| 50 | void 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 | |||
| 80 | void 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 */ | ||
| 91 | void 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 | |||
| 111 | void 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 | |||
| 179 | void 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 | |||
| 198 | int 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 | } | ||
