diff options
Diffstat (limited to 'portmidi/pm_common/portmidi.c')
| -rwxr-xr-x | portmidi/pm_common/portmidi.c | 1472 |
1 files changed, 1472 insertions, 0 deletions
diff --git a/portmidi/pm_common/portmidi.c b/portmidi/pm_common/portmidi.c new file mode 100755 index 0000000..e78ee73 --- /dev/null +++ b/portmidi/pm_common/portmidi.c | |||
| @@ -0,0 +1,1472 @@ | |||
| 1 | /* portmidi.c -- cross-platform MIDI I/O library */ | ||
| 2 | /* see license.txt for license */ | ||
| 3 | |||
| 4 | #include "stdlib.h" | ||
| 5 | #include "string.h" | ||
| 6 | #include "portmidi.h" | ||
| 7 | #include "porttime.h" | ||
| 8 | #include "pmutil.h" | ||
| 9 | #include "pminternal.h" | ||
| 10 | #include <assert.h> | ||
| 11 | |||
| 12 | #define MIDI_CLOCK 0xf8 | ||
| 13 | #define MIDI_ACTIVE 0xfe | ||
| 14 | #define MIDI_STATUS_MASK 0x80 | ||
| 15 | #define MIDI_SYSEX 0xf0 | ||
| 16 | #define MIDI_EOX 0xf7 | ||
| 17 | #define MIDI_START 0xFA | ||
| 18 | #define MIDI_STOP 0xFC | ||
| 19 | #define MIDI_CONTINUE 0xFB | ||
| 20 | #define MIDI_F9 0xF9 | ||
| 21 | #define MIDI_FD 0xFD | ||
| 22 | #define MIDI_RESET 0xFF | ||
| 23 | #define MIDI_NOTE_ON 0x90 | ||
| 24 | #define MIDI_NOTE_OFF 0x80 | ||
| 25 | #define MIDI_CHANNEL_AT 0xD0 | ||
| 26 | #define MIDI_POLY_AT 0xA0 | ||
| 27 | #define MIDI_PROGRAM 0xC0 | ||
| 28 | #define MIDI_CONTROL 0xB0 | ||
| 29 | #define MIDI_PITCHBEND 0xE0 | ||
| 30 | #define MIDI_MTC 0xF1 | ||
| 31 | #define MIDI_SONGPOS 0xF2 | ||
| 32 | #define MIDI_SONGSEL 0xF3 | ||
| 33 | #define MIDI_TUNE 0xF6 | ||
| 34 | |||
| 35 | #define is_empty(midi) ((midi)->tail == (midi)->head) | ||
| 36 | |||
| 37 | /* these are not static so that (possibly) some system-dependent code | ||
| 38 | * could override the portmidi.c default which is to use the first | ||
| 39 | * device added using pm_add_device() | ||
| 40 | */ | ||
| 41 | PmDeviceID pm_default_input_device_id = -1; | ||
| 42 | PmDeviceID pm_default_output_device_id = -1; | ||
| 43 | |||
| 44 | /* this is not static so that pm_init can set it directly | ||
| 45 | * (see pmmac.c:pm_init()) | ||
| 46 | */ | ||
| 47 | int pm_initialized = FALSE; | ||
| 48 | |||
| 49 | int pm_hosterror; /* boolean */ | ||
| 50 | |||
| 51 | /* if PM_CHECK_ERRORS is enabled, but the caller wants to | ||
| 52 | * handle an error condition, declare this as extern and | ||
| 53 | * set to FALSE (this override is provided specifically | ||
| 54 | * for the test program virttest.c, where pmNameConflict | ||
| 55 | * is expected in a call to Pm_CreateVirtualInput()): | ||
| 56 | */ | ||
| 57 | int pm_check_errors = TRUE; | ||
| 58 | |||
| 59 | char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN]; | ||
| 60 | |||
| 61 | #ifdef PM_CHECK_ERRORS | ||
| 62 | |||
| 63 | #include <stdio.h> | ||
| 64 | |||
| 65 | #define STRING_MAX 80 | ||
| 66 | |||
| 67 | static void prompt_and_exit(void) | ||
| 68 | { | ||
| 69 | char line[STRING_MAX]; | ||
| 70 | printf("type ENTER..."); | ||
| 71 | char *rslt = fgets(line, STRING_MAX, stdin); | ||
| 72 | /* this will clean up open ports: */ | ||
| 73 | exit(-1); | ||
| 74 | } | ||
| 75 | |||
| 76 | static PmError pm_errmsg(PmError err) | ||
| 77 | { | ||
| 78 | if (!pm_check_errors) { /* see pm_check_errors declaration above */ | ||
| 79 | ; | ||
| 80 | } else if (err == pmHostError) { | ||
| 81 | /* it seems pointless to allocate memory and copy the string, | ||
| 82 | * so I will do the work of Pm_GetHostErrorText directly | ||
| 83 | */ | ||
| 84 | printf("PortMidi found host error...\n %s\n", pm_hosterror_text); | ||
| 85 | pm_hosterror = FALSE; | ||
| 86 | pm_hosterror_text[0] = 0; /* clear the message */ | ||
| 87 | prompt_and_exit(); | ||
| 88 | } else if (err < 0) { | ||
| 89 | printf("PortMidi call failed...\n %s\n", Pm_GetErrorText(err)); | ||
| 90 | prompt_and_exit(); | ||
| 91 | } | ||
| 92 | return err; | ||
| 93 | } | ||
| 94 | #else | ||
| 95 | #define pm_errmsg(err) err | ||
| 96 | #endif | ||
| 97 | |||
| 98 | |||
| 99 | int pm_midi_length(PmMessage msg) | ||
| 100 | { | ||
| 101 | int status, high, low; | ||
| 102 | static int high_lengths[] = { | ||
| 103 | 1, 1, 1, 1, 1, 1, 1, 1, /* 0x00 through 0x70 */ | ||
| 104 | 3, 3, 3, 3, 2, 2, 3, 1 /* 0x80 through 0xf0 */ | ||
| 105 | }; | ||
| 106 | static int low_lengths[] = { | ||
| 107 | 1, 2, 3, 2, 1, 1, 1, 1, /* 0xf0 through 0xf8 */ | ||
| 108 | 1, 1, 1, 1, 1, 1, 1, 1 /* 0xf9 through 0xff */ | ||
| 109 | }; | ||
| 110 | |||
| 111 | status = msg & 0xFF; | ||
| 112 | high = status >> 4; | ||
| 113 | low = status & 15; | ||
| 114 | |||
| 115 | return (high != 0xF) ? high_lengths[high] : low_lengths[low]; | ||
| 116 | } | ||
| 117 | |||
| 118 | |||
| 119 | /* | ||
| 120 | ==================================================================== | ||
| 121 | system implementation of portmidi interface | ||
| 122 | ==================================================================== | ||
| 123 | */ | ||
| 124 | |||
| 125 | int pm_descriptor_max = 0; | ||
| 126 | int pm_descriptor_len = 0; | ||
| 127 | descriptor_type pm_descriptors = NULL; | ||
| 128 | |||
| 129 | /* interface pm_descriptors are simple: an array of string/fnptr pairs: */ | ||
| 130 | #define MAX_INTERF 4 | ||
| 131 | static struct { | ||
| 132 | const char *interf; | ||
| 133 | pm_create_fn create_fn; | ||
| 134 | pm_delete_fn delete_fn; | ||
| 135 | } pm_interf_list[MAX_INTERF]; | ||
| 136 | |||
| 137 | static int pm_interf_list_len = 0; | ||
| 138 | |||
| 139 | |||
| 140 | /* pm_add_interf -- describe an interface to library | ||
| 141 | * | ||
| 142 | * This is called at initialization time, once for each | ||
| 143 | * supported interface (e.g., CoreMIDI). The strings | ||
| 144 | * are retained but NOT COPIED, so do not destroy them! | ||
| 145 | * | ||
| 146 | * The purpose is to register functions that create/delete | ||
| 147 | * a virtual input or output device. | ||
| 148 | * | ||
| 149 | * returns pmInsufficientMemor if interface memory is | ||
| 150 | * exceeded, otherwise returns pmNoError. | ||
| 151 | */ | ||
| 152 | PmError pm_add_interf(char *interf, pm_create_fn create_fn, | ||
| 153 | pm_delete_fn delete_fn) | ||
| 154 | { | ||
| 155 | if (pm_interf_list_len >= MAX_INTERF) { | ||
| 156 | return pmInsufficientMemory; | ||
| 157 | } | ||
| 158 | pm_interf_list[pm_interf_list_len].interf = interf; | ||
| 159 | pm_interf_list[pm_interf_list_len].create_fn = create_fn; | ||
| 160 | pm_interf_list[pm_interf_list_len].delete_fn = delete_fn; | ||
| 161 | pm_interf_list_len++; | ||
| 162 | return pmNoError; | ||
| 163 | } | ||
| 164 | |||
| 165 | |||
| 166 | PmError pm_create_virtual(PmInternal *midi, int is_input, const char *interf, | ||
| 167 | const char *name, void *device_info) | ||
| 168 | { | ||
| 169 | int i; | ||
| 170 | if (pm_interf_list_len == 0) { | ||
| 171 | return pmNotImplemented; | ||
| 172 | } | ||
| 173 | if (!interf) { | ||
| 174 | /* default interface is the first one */ | ||
| 175 | interf = pm_interf_list[0].interf; | ||
| 176 | } | ||
| 177 | for (i = 0; i < pm_interf_list_len; i++) { | ||
| 178 | if (strcmp(pm_interf_list[i].interf, | ||
| 179 | interf) == 0) { | ||
| 180 | int id = (*pm_interf_list[i].create_fn)(is_input, name, | ||
| 181 | device_info); | ||
| 182 | pm_descriptors[id].pub.is_virtual = TRUE; | ||
| 183 | return id; | ||
| 184 | } | ||
| 185 | } | ||
| 186 | return pmInterfaceNotSupported; | ||
| 187 | } | ||
| 188 | |||
| 189 | |||
| 190 | /* pm_add_device -- describe interface/device pair to library | ||
| 191 | * | ||
| 192 | * This is called at intialization time, once for each | ||
| 193 | * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1). | ||
| 194 | * This is also called when user creates a virtual device. | ||
| 195 | * | ||
| 196 | * Normally, increasing integer indices are returned. If the device | ||
| 197 | * is virtual, a linear search is performed to ensure that the name | ||
| 198 | * is unique. If the name is already taken, the call will fail and | ||
| 199 | * no device is added. | ||
| 200 | * | ||
| 201 | * interf is assumed to be static memory, so it is NOT COPIED and | ||
| 202 | * NOT FREED. | ||
| 203 | * name is owned by caller, COPIED if needed, and FREED by PortMidi. | ||
| 204 | * Caller is resposible for freeing name when pm_add_device returns. | ||
| 205 | * | ||
| 206 | * returns pmInvalidDeviceId if device memory is exceeded or a virtual | ||
| 207 | * device would take the name of an existing device. | ||
| 208 | * otherwise returns index (portmidi device_id) of the added device | ||
| 209 | */ | ||
| 210 | PmError pm_add_device(char *interf, const char *name, int is_input, | ||
| 211 | int is_virtual, void *descriptor, pm_fns_type dictionary) { | ||
| 212 | /* printf("pm_add_device: %s %s %d %p %p\n", | ||
| 213 | interf, name, is_input, descriptor, dictionary); */ | ||
| 214 | int device_id; | ||
| 215 | PmDeviceInfo *d; | ||
| 216 | /* if virtual, search for duplicate name or unused ID; otherwise, | ||
| 217 | * just add a new device at the next integer available: | ||
| 218 | */ | ||
| 219 | for (device_id = (is_virtual ? 0 : pm_descriptor_len); | ||
| 220 | device_id < pm_descriptor_len; device_id++) { | ||
| 221 | d = &pm_descriptors[device_id].pub; | ||
| 222 | d->structVersion = PM_DEVICEINFO_VERS; | ||
| 223 | if (strcmp(d->interf, interf) == 0 && strcmp(d->name, name) == 0) { | ||
| 224 | /* only reuse a name if it is a deleted virtual device with | ||
| 225 | * a matching direction (input or output) */ | ||
| 226 | if (pm_descriptors[device_id].deleted && is_input == d->input) { | ||
| 227 | /* here, we know d->is_virtual because only virtual devices | ||
| 228 | * can be deleted, and we know is_virtual because we are | ||
| 229 | * in this loop. | ||
| 230 | */ | ||
| 231 | pm_free((void *) d->name); /* reuse this device entry */ | ||
| 232 | d->name = NULL; | ||
| 233 | break; | ||
| 234 | /* name conflict exists if the new device appears to others as | ||
| 235 | * the same direction (input or output) as the existing device. | ||
| 236 | * Note that virtual inputs appear to others as outputs and | ||
| 237 | * vice versa. | ||
| 238 | * The direction of the new virtual device to others is "output" | ||
| 239 | * if is_input, i.e., virtual inputs appear to others as outputs. | ||
| 240 | * The existing device appears to others as "output" if | ||
| 241 | * (d->is_virtual == d->input) by the same logic. | ||
| 242 | * The compare will detect if device directions are the same: | ||
| 243 | */ | ||
| 244 | } else if (is_input == (d->is_virtual == d->input)) { | ||
| 245 | return pmNameConflict; | ||
| 246 | } | ||
| 247 | } | ||
| 248 | } | ||
| 249 | if (device_id >= pm_descriptor_max) { | ||
| 250 | // expand pm_descriptors | ||
| 251 | descriptor_type new_descriptors = (descriptor_type) | ||
| 252 | pm_alloc(sizeof(descriptor_node) * (pm_descriptor_max + 32)); | ||
| 253 | if (!new_descriptors) return pmInsufficientMemory; | ||
| 254 | if (pm_descriptors) { | ||
| 255 | memcpy(new_descriptors, pm_descriptors, | ||
| 256 | sizeof(descriptor_node) * pm_descriptor_max); | ||
| 257 | pm_free(pm_descriptors); | ||
| 258 | } | ||
| 259 | pm_descriptor_max += 32; | ||
| 260 | pm_descriptors = new_descriptors; | ||
| 261 | } | ||
| 262 | if (device_id == pm_descriptor_len) { | ||
| 263 | pm_descriptor_len++; /* extending array of pm_descriptors */ | ||
| 264 | } | ||
| 265 | d = &pm_descriptors[device_id].pub; | ||
| 266 | d->interf = interf; | ||
| 267 | d->name = pm_alloc(strlen(name) + 1); | ||
| 268 | if (!d->name) { | ||
| 269 | return pmInsufficientMemory; | ||
| 270 | } | ||
| 271 | #if defined(WIN32) && !defined(_WIN32) | ||
| 272 | #pragma warning(suppress: 4996) // don't use suggested strncpy_s | ||
| 273 | #endif | ||
| 274 | strcpy(d->name, name); | ||
| 275 | d->input = is_input; | ||
| 276 | d->output = !is_input; | ||
| 277 | d->is_virtual = FALSE; /* caller should set to TRUE if this is virtual */ | ||
| 278 | |||
| 279 | /* default state: nothing to close (for automatic device closing) */ | ||
| 280 | d->opened = FALSE; | ||
| 281 | |||
| 282 | pm_descriptors[device_id].deleted = FALSE; | ||
| 283 | |||
| 284 | /* ID number passed to win32 multimedia API open */ | ||
| 285 | pm_descriptors[device_id].descriptor = descriptor; | ||
| 286 | |||
| 287 | /* points to PmInternal, allows automatic device closing */ | ||
| 288 | pm_descriptors[device_id].pm_internal = NULL; | ||
| 289 | |||
| 290 | pm_descriptors[device_id].dictionary = dictionary; | ||
| 291 | |||
| 292 | /* set the defaults to the first input and output we see */ | ||
| 293 | if (is_input && pm_default_input_device_id == -1) { | ||
| 294 | pm_default_input_device_id = device_id; | ||
| 295 | } else if (!is_input && pm_default_output_device_id == -1) { | ||
| 296 | pm_default_output_device_id = device_id; | ||
| 297 | } | ||
| 298 | |||
| 299 | return device_id; | ||
| 300 | } | ||
| 301 | |||
| 302 | |||
| 303 | /* Undo a successful call to pm_add_device(). If a new device was | ||
| 304 | * allocated, it must be the last device in pm_descriptors, so it is | ||
| 305 | * easy to delete by decrementing the length of pm_descriptors, but | ||
| 306 | * first free the name (which was copied to the heap). Otherwise, | ||
| 307 | * the device must be a virtual device that was created previously | ||
| 308 | * and is in the interior of the array of pm_descriptors. Leave it, | ||
| 309 | * but mark it as deleted. | ||
| 310 | */ | ||
| 311 | void pm_undo_add_device(int id) | ||
| 312 | { | ||
| 313 | /* Clear some fields (not all are strictly necessary) */ | ||
| 314 | pm_descriptors[id].deleted = TRUE; | ||
| 315 | pm_descriptors[id].descriptor = NULL; | ||
| 316 | pm_descriptors[id].pm_internal = NULL; | ||
| 317 | |||
| 318 | if (id == pm_descriptor_len - 1) { | ||
| 319 | pm_free(pm_descriptors[id].pub.name); | ||
| 320 | pm_descriptor_len--; | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | |||
| 325 | /* utility to look up device, given a pattern, | ||
| 326 | note: pattern is modified | ||
| 327 | */ | ||
| 328 | int Pm_FindDevice(char *pattern, int is_input) | ||
| 329 | { | ||
| 330 | int id = pmNoDevice; | ||
| 331 | int i; | ||
| 332 | /* first parse pattern into name, interf parts */ | ||
| 333 | char *interf_pref = ""; /* initially assume it is not there */ | ||
| 334 | char *name_pref = strstr(pattern, ", "); | ||
| 335 | |||
| 336 | if (name_pref) { /* found separator, adjust the pointer */ | ||
| 337 | interf_pref = pattern; | ||
| 338 | name_pref[0] = 0; | ||
| 339 | name_pref += 2; | ||
| 340 | } else { | ||
| 341 | name_pref = pattern; /* whole string is the name pattern */ | ||
| 342 | } | ||
| 343 | for (i = 0; i < pm_descriptor_len; i++) { | ||
| 344 | const PmDeviceInfo *info = Pm_GetDeviceInfo(i); | ||
| 345 | if (info->input == is_input && | ||
| 346 | strstr(info->name, name_pref) && | ||
| 347 | strstr(info->interf, interf_pref)) { | ||
| 348 | id = i; | ||
| 349 | break; | ||
| 350 | } | ||
| 351 | } | ||
| 352 | return id; | ||
| 353 | } | ||
| 354 | |||
| 355 | |||
| 356 | /* | ||
| 357 | ==================================================================== | ||
| 358 | portmidi implementation | ||
| 359 | ==================================================================== | ||
| 360 | */ | ||
| 361 | |||
| 362 | PMEXPORT int Pm_CountDevices(void) | ||
| 363 | { | ||
| 364 | Pm_Initialize(); | ||
| 365 | /* no error checking -- Pm_Initialize() does not fail */ | ||
| 366 | return pm_descriptor_len; | ||
| 367 | } | ||
| 368 | |||
| 369 | |||
| 370 | PMEXPORT const PmDeviceInfo* Pm_GetDeviceInfo(PmDeviceID id) | ||
| 371 | { | ||
| 372 | Pm_Initialize(); /* no error check needed */ | ||
| 373 | if (id >= 0 && id < pm_descriptor_len && !pm_descriptors[id].deleted) { | ||
| 374 | return &pm_descriptors[id].pub; | ||
| 375 | } | ||
| 376 | return NULL; | ||
| 377 | } | ||
| 378 | |||
| 379 | /* pm_success_fn -- "noop" function pointer */ | ||
| 380 | PmError pm_success_fn(PmInternal *midi) | ||
| 381 | { | ||
| 382 | return pmNoError; | ||
| 383 | } | ||
| 384 | |||
| 385 | /* none_write -- returns an error if called */ | ||
| 386 | PmError none_write_short(PmInternal *midi, PmEvent *buffer) | ||
| 387 | { | ||
| 388 | return pmBadPtr; | ||
| 389 | } | ||
| 390 | |||
| 391 | /* pm_fail_timestamp_fn -- placeholder for begin_sysex and flush */ | ||
| 392 | PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp) | ||
| 393 | { | ||
| 394 | return pmBadPtr; | ||
| 395 | } | ||
| 396 | |||
| 397 | PmError none_write_byte(PmInternal *midi, unsigned char byte, | ||
| 398 | PmTimestamp timestamp) | ||
| 399 | { | ||
| 400 | return pmBadPtr; | ||
| 401 | } | ||
| 402 | |||
| 403 | /* pm_fail_fn -- generic function, returns error if called */ | ||
| 404 | PmError pm_fail_fn(PmInternal *midi) | ||
| 405 | { | ||
| 406 | return pmBadPtr; | ||
| 407 | } | ||
| 408 | |||
| 409 | static PmError none_open(PmInternal *midi, void *driverInfo) | ||
| 410 | { | ||
| 411 | return pmBadPtr; | ||
| 412 | } | ||
| 413 | |||
| 414 | static unsigned int none_check_host_error(PmInternal * midi) | ||
| 415 | { | ||
| 416 | return FALSE; | ||
| 417 | } | ||
| 418 | |||
| 419 | PmTimestamp none_synchronize(PmInternal *midi) | ||
| 420 | { | ||
| 421 | return 0; | ||
| 422 | } | ||
| 423 | |||
| 424 | #define none_abort pm_fail_fn | ||
| 425 | #define none_close pm_fail_fn | ||
| 426 | |||
| 427 | pm_fns_node pm_none_dictionary = { | ||
| 428 | none_write_short, | ||
| 429 | none_sysex, | ||
| 430 | none_sysex, | ||
| 431 | none_write_byte, | ||
| 432 | none_write_short, | ||
| 433 | none_write_flush, | ||
| 434 | none_synchronize, | ||
| 435 | none_open, | ||
| 436 | none_abort, | ||
| 437 | none_close, | ||
| 438 | none_poll, | ||
| 439 | none_check_host_error, | ||
| 440 | }; | ||
| 441 | |||
| 442 | |||
| 443 | PMEXPORT const char *Pm_GetErrorText(PmError errnum) | ||
| 444 | { | ||
| 445 | const char *msg; | ||
| 446 | |||
| 447 | switch(errnum) | ||
| 448 | { | ||
| 449 | case pmNoError: | ||
| 450 | msg = ""; | ||
| 451 | break; | ||
| 452 | case pmHostError: | ||
| 453 | msg = "PortMidi: Host error"; | ||
| 454 | break; | ||
| 455 | case pmInvalidDeviceId: | ||
| 456 | msg = "PortMidi: Invalid device ID"; | ||
| 457 | break; | ||
| 458 | case pmInsufficientMemory: | ||
| 459 | msg = "PortMidi: Insufficient memory"; | ||
| 460 | break; | ||
| 461 | case pmBufferTooSmall: | ||
| 462 | msg = "PortMidi: Buffer too small"; | ||
| 463 | break; | ||
| 464 | case pmBadPtr: | ||
| 465 | msg = "PortMidi: Bad pointer"; | ||
| 466 | break; | ||
| 467 | case pmInternalError: | ||
| 468 | msg = "PortMidi: Internal PortMidi Error"; | ||
| 469 | break; | ||
| 470 | case pmBufferOverflow: | ||
| 471 | msg = "PortMidi: Buffer overflow"; | ||
| 472 | break; | ||
| 473 | case pmBadData: | ||
| 474 | msg = "PortMidi: Invalid MIDI message Data"; | ||
| 475 | break; | ||
| 476 | case pmBufferMaxSize: | ||
| 477 | msg = "PortMidi: Buffer cannot be made larger"; | ||
| 478 | break; | ||
| 479 | case pmNotImplemented: | ||
| 480 | msg = "PortMidi: Function is not implemented"; | ||
| 481 | break; | ||
| 482 | case pmInterfaceNotSupported: | ||
| 483 | msg = "PortMidi: Interface not supported"; | ||
| 484 | break; | ||
| 485 | case pmNameConflict: | ||
| 486 | msg = "PortMidi: Cannot create virtual device: name is taken"; | ||
| 487 | break; | ||
| 488 | case pmDeviceRemoved: | ||
| 489 | msg = "PortMidi: Output attempted after (USB) device removed"; | ||
| 490 | break; | ||
| 491 | default: | ||
| 492 | msg = "PortMidi: Illegal error number"; | ||
| 493 | break; | ||
| 494 | } | ||
| 495 | return msg; | ||
| 496 | } | ||
| 497 | |||
| 498 | |||
| 499 | /* This can be called whenever you get a pmHostError return value | ||
| 500 | * or TRUE from Pm_HasHostError(). | ||
| 501 | * The error will always be in the global pm_hosterror_text. | ||
| 502 | */ | ||
| 503 | PMEXPORT void Pm_GetHostErrorText(char * msg, unsigned int len) | ||
| 504 | { | ||
| 505 | assert(msg); | ||
| 506 | assert(len > 0); | ||
| 507 | if (pm_hosterror) { | ||
| 508 | #if defined(WIN32) && !defined(_WIN32) | ||
| 509 | #pragma warning(suppress: 4996) // don't use suggested strncpy_s | ||
| 510 | #endif | ||
| 511 | strncpy(msg, (char *) pm_hosterror_text, len); | ||
| 512 | pm_hosterror = FALSE; | ||
| 513 | pm_hosterror_text[0] = 0; /* clear the message; not necessary, but it | ||
| 514 | might help with debugging */ | ||
| 515 | msg[len - 1] = 0; /* make sure string is terminated */ | ||
| 516 | } else { | ||
| 517 | msg[0] = 0; /* no string to return */ | ||
| 518 | } | ||
| 519 | } | ||
| 520 | |||
| 521 | |||
| 522 | PMEXPORT int Pm_HasHostError(PortMidiStream * stream) | ||
| 523 | { | ||
| 524 | if (pm_hosterror) return TRUE; | ||
| 525 | if (stream) { | ||
| 526 | PmInternal * midi = (PmInternal *) stream; | ||
| 527 | return (*midi->dictionary->check_host_error)(midi); | ||
| 528 | } | ||
| 529 | return FALSE; | ||
| 530 | } | ||
| 531 | |||
| 532 | |||
| 533 | PMEXPORT PmError Pm_Initialize(void) | ||
| 534 | { | ||
| 535 | if (!pm_initialized) { | ||
| 536 | pm_descriptor_len = 0; | ||
| 537 | pm_interf_list_len = 0; | ||
| 538 | pm_hosterror = FALSE; | ||
| 539 | pm_hosterror_text[0] = 0; /* the null string */ | ||
| 540 | pm_init(); | ||
| 541 | pm_initialized = TRUE; | ||
| 542 | } | ||
| 543 | return pmNoError; | ||
| 544 | } | ||
| 545 | |||
| 546 | |||
| 547 | PMEXPORT PmError Pm_Terminate(void) | ||
| 548 | { | ||
| 549 | if (pm_initialized) { | ||
| 550 | pm_term(); | ||
| 551 | /* if there are no devices, pm_descriptors might still be NULL */ | ||
| 552 | if (pm_descriptors != NULL) { | ||
| 553 | int i; /* free names copied into pm_descriptors */ | ||
| 554 | for (i = 0; i < pm_descriptor_len; i++) { | ||
| 555 | if (pm_descriptors[i].pub.name) { | ||
| 556 | pm_free(pm_descriptors[i].pub.name); | ||
| 557 | } | ||
| 558 | } | ||
| 559 | pm_free(pm_descriptors); | ||
| 560 | pm_descriptors = NULL; | ||
| 561 | } | ||
| 562 | pm_descriptor_len = 0; | ||
| 563 | pm_descriptor_max = 0; | ||
| 564 | pm_interf_list_len = 0; | ||
| 565 | pm_initialized = FALSE; | ||
| 566 | } | ||
| 567 | return pmNoError; | ||
| 568 | } | ||
| 569 | |||
| 570 | |||
| 571 | /* Pm_Read -- read up to length messages from source into buffer */ | ||
| 572 | /* | ||
| 573 | * returns number of messages actually read, or error code | ||
| 574 | */ | ||
| 575 | PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length) | ||
| 576 | { | ||
| 577 | PmInternal *midi = (PmInternal *) stream; | ||
| 578 | int n = 0; | ||
| 579 | PmError err = pmNoError; | ||
| 580 | pm_hosterror = FALSE; | ||
| 581 | /* arg checking */ | ||
| 582 | if(midi == NULL) | ||
| 583 | err = pmBadPtr; | ||
| 584 | else if(!pm_descriptors[midi->device_id].pub.opened) | ||
| 585 | err = pmBadPtr; | ||
| 586 | else if(!pm_descriptors[midi->device_id].pub.input) | ||
| 587 | err = pmBadPtr; | ||
| 588 | /* First poll for data in the buffer... | ||
| 589 | * This either simply checks for data, or attempts first to fill the buffer | ||
| 590 | * with data from the MIDI hardware; this depends on the implementation. | ||
| 591 | * We could call Pm_Poll here, but that would redo a lot of redundant | ||
| 592 | * parameter checking, so I copied some code from Pm_Poll to here: */ | ||
| 593 | else err = (*(midi->dictionary->poll))(midi); | ||
| 594 | |||
| 595 | if (err != pmNoError) { | ||
| 596 | if (err == pmHostError) { | ||
| 597 | midi->dictionary->check_host_error(midi); | ||
| 598 | } | ||
| 599 | return pm_errmsg(err); | ||
| 600 | } | ||
| 601 | |||
| 602 | while (n < length) { | ||
| 603 | err = Pm_Dequeue(midi->queue, buffer++); | ||
| 604 | if (err == pmBufferOverflow) { | ||
| 605 | /* ignore the data we have retreived so far */ | ||
| 606 | return pm_errmsg(pmBufferOverflow); | ||
| 607 | } else if (err == 0) { /* empty queue */ | ||
| 608 | break; | ||
| 609 | } | ||
| 610 | n++; | ||
| 611 | } | ||
| 612 | return n; | ||
| 613 | } | ||
| 614 | |||
| 615 | PMEXPORT PmError Pm_Poll(PortMidiStream *stream) | ||
| 616 | { | ||
| 617 | PmInternal *midi = (PmInternal *) stream; | ||
| 618 | PmError err; | ||
| 619 | |||
| 620 | pm_hosterror = FALSE; | ||
| 621 | /* arg checking */ | ||
| 622 | if(midi == NULL) | ||
| 623 | err = pmBadPtr; | ||
| 624 | else if (!pm_descriptors[midi->device_id].pub.opened) | ||
| 625 | err = pmBadPtr; | ||
| 626 | else if (!pm_descriptors[midi->device_id].pub.input) | ||
| 627 | err = pmBadPtr; | ||
| 628 | else | ||
| 629 | err = (*(midi->dictionary->poll))(midi); | ||
| 630 | |||
| 631 | if (err != pmNoError) { | ||
| 632 | return pm_errmsg(err); | ||
| 633 | } | ||
| 634 | |||
| 635 | return (PmError) !Pm_QueueEmpty(midi->queue); | ||
| 636 | } | ||
| 637 | |||
| 638 | |||
| 639 | /* this is called from Pm_Write and Pm_WriteSysEx to issue a | ||
| 640 | * call to the system-dependent end_sysex function and handle | ||
| 641 | * the error return | ||
| 642 | */ | ||
| 643 | static PmError pm_end_sysex(PmInternal *midi) | ||
| 644 | { | ||
| 645 | PmError err = (*midi->dictionary->end_sysex)(midi, 0); | ||
| 646 | midi->sysex_in_progress = FALSE; | ||
| 647 | return err; | ||
| 648 | } | ||
| 649 | |||
| 650 | |||
| 651 | /* to facilitate correct error-handling, Pm_Write, Pm_WriteShort, and | ||
| 652 | Pm_WriteSysEx all operate a state machine that "outputs" calls to | ||
| 653 | write_short, begin_sysex, write_byte, end_sysex, and write_realtime */ | ||
| 654 | |||
| 655 | PMEXPORT PmError Pm_Write(PortMidiStream *stream, PmEvent *buffer, | ||
| 656 | int32_t length) | ||
| 657 | { | ||
| 658 | PmInternal *midi = (PmInternal *) stream; | ||
| 659 | PmError err = pmNoError; | ||
| 660 | int i; | ||
| 661 | int bits; | ||
| 662 | |||
| 663 | pm_hosterror = FALSE; | ||
| 664 | /* arg checking */ | ||
| 665 | if (midi == NULL) { | ||
| 666 | err = pmBadPtr; | ||
| 667 | } else { | ||
| 668 | descriptor_type desc = &pm_descriptors[midi->device_id]; | ||
| 669 | if (!desc || !desc->pub.opened || | ||
| 670 | !desc->pub.output || !desc->pm_internal) { | ||
| 671 | err = pmBadPtr; | ||
| 672 | } else if (desc->pm_internal->is_removed) { | ||
| 673 | err = pmDeviceRemoved; | ||
| 674 | } | ||
| 675 | } | ||
| 676 | if (err != pmNoError) goto pm_write_error; | ||
| 677 | |||
| 678 | if (midi->latency == 0) { | ||
| 679 | midi->now = 0; | ||
| 680 | } else { | ||
| 681 | midi->now = (*(midi->time_proc))(midi->time_info); | ||
| 682 | if (midi->first_message || midi->sync_time + 100 /*ms*/ < midi->now) { | ||
| 683 | /* time to resync */ | ||
| 684 | midi->now = (*midi->dictionary->synchronize)(midi); | ||
| 685 | midi->first_message = FALSE; | ||
| 686 | } | ||
| 687 | } | ||
| 688 | /* error recovery: when a sysex is detected, we call | ||
| 689 | * dictionary->begin_sysex() followed by calls to | ||
| 690 | * dictionary->write_byte() and dictionary->write_realtime() | ||
| 691 | * until an end-of-sysex is detected, when we call | ||
| 692 | * dictionary->end_sysex(). After an error occurs, | ||
| 693 | * Pm_Write() continues to call functions. For example, | ||
| 694 | * it will continue to call write_byte() even after | ||
| 695 | * an error sending a sysex message, and end_sysex() will be | ||
| 696 | * called when an EOX or non-real-time status is found. | ||
| 697 | * When errors are detected, Pm_Write() returns immediately, | ||
| 698 | * so it is possible that this will drop data and leave | ||
| 699 | * sysex messages in a partially transmitted state. | ||
| 700 | */ | ||
| 701 | for (i = 0; i < length; i++) { | ||
| 702 | uint32_t msg = buffer[i].message; | ||
| 703 | bits = 0; | ||
| 704 | /* is this a sysex message? */ | ||
| 705 | if (Pm_MessageStatus(msg) == MIDI_SYSEX) { | ||
| 706 | if (midi->sysex_in_progress) { | ||
| 707 | /* error: previous sysex was not terminated by EOX */ | ||
| 708 | midi->sysex_in_progress = FALSE; | ||
| 709 | err = pmBadData; | ||
| 710 | goto pm_write_error; | ||
| 711 | } | ||
| 712 | midi->sysex_in_progress = TRUE; | ||
| 713 | if ((err = (*midi->dictionary->begin_sysex)(midi, | ||
| 714 | buffer[i].timestamp)) != pmNoError) | ||
| 715 | goto pm_write_error; | ||
| 716 | if ((err = (*midi->dictionary->write_byte)(midi, MIDI_SYSEX, | ||
| 717 | buffer[i].timestamp)) != pmNoError) | ||
| 718 | goto pm_write_error; | ||
| 719 | bits = 8; | ||
| 720 | /* fall through to continue sysex processing */ | ||
| 721 | } else if ((msg & MIDI_STATUS_MASK) && | ||
| 722 | (Pm_MessageStatus(msg) != MIDI_EOX)) { | ||
| 723 | /* a non-sysex message */ | ||
| 724 | if (midi->sysex_in_progress) { | ||
| 725 | /* this should be a realtime message */ | ||
| 726 | if (is_real_time(msg)) { | ||
| 727 | if ((err = (*midi->dictionary->write_realtime)(midi, | ||
| 728 | &(buffer[i]))) != pmNoError) | ||
| 729 | goto pm_write_error; | ||
| 730 | } else { | ||
| 731 | midi->sysex_in_progress = FALSE; | ||
| 732 | err = pmBadData; | ||
| 733 | /* ignore any error from this, because we already have one */ | ||
| 734 | /* pass 0 as timestamp -- it's ignored */ | ||
| 735 | (*midi->dictionary->end_sysex)(midi, 0); | ||
| 736 | goto pm_write_error; | ||
| 737 | } | ||
| 738 | } else { /* regular short midi message */ | ||
| 739 | if ((err = (*midi->dictionary->write_short)(midi, | ||
| 740 | &(buffer[i]))) != pmNoError) | ||
| 741 | goto pm_write_error; | ||
| 742 | continue; | ||
| 743 | } | ||
| 744 | } | ||
| 745 | if (midi->sysex_in_progress) { /* send sysex bytes until EOX */ | ||
| 746 | /* see if we can accelerate data transfer */ | ||
| 747 | if (bits == 0 && midi->fill_base && /* 4 bytes to copy */ | ||
| 748 | (*midi->fill_offset_ptr) + 4 <= midi->fill_length && | ||
| 749 | (msg & 0x80808080) == 0) { /* all data */ | ||
| 750 | /* copy 4 bytes from msg to fill_base + fill_offset */ | ||
| 751 | unsigned char *ptr = midi->fill_base + | ||
| 752 | *(midi->fill_offset_ptr); | ||
| 753 | ptr[0] = msg; ptr[1] = msg >> 8; | ||
| 754 | ptr[2] = msg >> 16; ptr[3] = msg >> 24; | ||
| 755 | (*midi->fill_offset_ptr) += 4; | ||
| 756 | continue; | ||
| 757 | } | ||
| 758 | /* no acceleration, so do byte-by-byte copying */ | ||
| 759 | while (bits < 32) { | ||
| 760 | unsigned char midi_byte = (unsigned char) (msg >> bits); | ||
| 761 | if ((err = (*midi->dictionary->write_byte)(midi, midi_byte, | ||
| 762 | buffer[i].timestamp)) != pmNoError) | ||
| 763 | goto pm_write_error; | ||
| 764 | if (midi_byte == MIDI_EOX) { | ||
| 765 | err = pm_end_sysex(midi); | ||
| 766 | if (err != pmNoError) goto error_exit; | ||
| 767 | break; /* from while loop */ | ||
| 768 | } | ||
| 769 | bits += 8; | ||
| 770 | } | ||
| 771 | } else { | ||
| 772 | /* not in sysex mode, but message did not start with status */ | ||
| 773 | err = pmBadData; | ||
| 774 | goto pm_write_error; | ||
| 775 | } | ||
| 776 | } | ||
| 777 | /* after all messages are processed, send the data */ | ||
| 778 | if (!midi->sysex_in_progress) | ||
| 779 | err = (*midi->dictionary->write_flush)(midi, 0); | ||
| 780 | pm_write_error: | ||
| 781 | if (err == pmHostError) { | ||
| 782 | midi->dictionary->check_host_error(midi); | ||
| 783 | } | ||
| 784 | error_exit: | ||
| 785 | return pm_errmsg(err); | ||
| 786 | } | ||
| 787 | |||
| 788 | |||
| 789 | PMEXPORT PmError Pm_WriteShort(PortMidiStream *stream, PmTimestamp when, | ||
| 790 | PmMessage msg) | ||
| 791 | { | ||
| 792 | PmEvent event; | ||
| 793 | |||
| 794 | event.timestamp = when; | ||
| 795 | event.message = msg; | ||
| 796 | return Pm_Write(stream, &event, 1); | ||
| 797 | } | ||
| 798 | |||
| 799 | |||
| 800 | PMEXPORT PmError Pm_WriteSysEx(PortMidiStream *stream, PmTimestamp when, | ||
| 801 | unsigned char *msg) | ||
| 802 | { | ||
| 803 | /* allocate buffer space for PM_DEFAULT_SYSEX_BUFFER_SIZE bytes */ | ||
| 804 | /* each PmEvent holds sizeof(PmMessage) bytes of sysex data */ | ||
| 805 | #define BUFLEN ((int) (PM_DEFAULT_SYSEX_BUFFER_SIZE / sizeof(PmMessage))) | ||
| 806 | PmEvent buffer[BUFLEN]; | ||
| 807 | int buffer_size = 1; /* first time, send 1. After that, it's BUFLEN */ | ||
| 808 | PmInternal *midi = (PmInternal *) stream; | ||
| 809 | PmError err = pmNoError; | ||
| 810 | /* the next byte in the buffer is represented by an index, bufx, and | ||
| 811 | a shift in bits */ | ||
| 812 | int shift = 0; | ||
| 813 | int bufx = 0; | ||
| 814 | buffer[0].message = 0; | ||
| 815 | buffer[0].timestamp = when; | ||
| 816 | |||
| 817 | while (1) { | ||
| 818 | /* insert next byte into buffer */ | ||
| 819 | buffer[bufx].message |= ((*msg) << shift); | ||
| 820 | shift += 8; | ||
| 821 | if (*msg++ == MIDI_EOX) break; | ||
| 822 | if (shift == 32) { | ||
| 823 | shift = 0; | ||
| 824 | bufx++; | ||
| 825 | if (bufx == buffer_size) { | ||
| 826 | err = Pm_Write(stream, buffer, buffer_size); | ||
| 827 | /* note: Pm_Write has already called errmsg() */ | ||
| 828 | if (err) return err; | ||
| 829 | /* prepare to fill another buffer */ | ||
| 830 | bufx = 0; | ||
| 831 | buffer_size = BUFLEN; | ||
| 832 | /* optimization: maybe we can just copy bytes */ | ||
| 833 | if (midi->fill_base) { | ||
| 834 | while (*(midi->fill_offset_ptr) < midi->fill_length) { | ||
| 835 | midi->fill_base[(*midi->fill_offset_ptr)++] = *msg; | ||
| 836 | if (*msg++ == MIDI_EOX) { | ||
| 837 | err = pm_end_sysex(midi); | ||
| 838 | if (err != pmNoError) return pm_errmsg(err); | ||
| 839 | goto end_of_sysex; | ||
| 840 | } | ||
| 841 | } | ||
| 842 | /* I thought that I could do a pm_Write here and | ||
| 843 | * change this if to a loop, avoiding calls in Pm_Write | ||
| 844 | * to the slower write_byte, but since | ||
| 845 | * sysex_in_progress is true, this will not flush | ||
| 846 | * the buffer, and we'll infinite loop: */ | ||
| 847 | /* err = Pm_Write(stream, buffer, 0); | ||
| 848 | if (err) return err; */ | ||
| 849 | /* instead, the way this works is that Pm_Write calls | ||
| 850 | * write_byte on 4 bytes. The first, since the buffer | ||
| 851 | * is full, will flush the buffer and allocate a new | ||
| 852 | * one. This primes the buffer so | ||
| 853 | * that we can return to the loop above and fill it | ||
| 854 | * efficiently without a lot of function calls. | ||
| 855 | */ | ||
| 856 | buffer_size = 1; /* get another message started */ | ||
| 857 | } | ||
| 858 | } | ||
| 859 | buffer[bufx].message = 0; | ||
| 860 | buffer[bufx].timestamp = when; | ||
| 861 | } | ||
| 862 | /* keep inserting bytes until you find MIDI_EOX */ | ||
| 863 | } | ||
| 864 | end_of_sysex: | ||
| 865 | /* we're finished sending full buffers, but there may | ||
| 866 | * be a partial one left. | ||
| 867 | */ | ||
| 868 | if (shift != 0) bufx++; /* add partial message to buffer len */ | ||
| 869 | if (bufx) { /* bufx is number of PmEvents to send from buffer */ | ||
| 870 | err = Pm_Write(stream, buffer, bufx); | ||
| 871 | if (err) return err; | ||
| 872 | } | ||
| 873 | return pmNoError; | ||
| 874 | } | ||
| 875 | |||
| 876 | |||
| 877 | |||
| 878 | PmError pm_create_internal(PmInternal **stream, PmDeviceID device_id, | ||
| 879 | int is_input, int latency, PmTimeProcPtr time_proc, | ||
| 880 | void *time_info, int buffer_size) | ||
| 881 | { | ||
| 882 | PmInternal *midi; | ||
| 883 | if (device_id < 0 || device_id >= pm_descriptor_len) { | ||
| 884 | return pmInvalidDeviceId; | ||
| 885 | } | ||
| 886 | if (latency < 0) { /* force a legal value */ | ||
| 887 | latency = 0; | ||
| 888 | } | ||
| 889 | /* create portMidi internal data */ | ||
| 890 | midi = (PmInternal *) pm_alloc(sizeof(PmInternal)); | ||
| 891 | *stream = midi; | ||
| 892 | if (!midi) { | ||
| 893 | return pmInsufficientMemory; | ||
| 894 | } | ||
| 895 | midi->device_id = device_id; | ||
| 896 | midi->is_input = is_input; | ||
| 897 | midi->is_removed = FALSE; | ||
| 898 | midi->time_proc = time_proc; | ||
| 899 | /* if latency != 0, we need a time reference for output. | ||
| 900 | we always need a time reference for input. | ||
| 901 | If none is provided, use PortTime library */ | ||
| 902 | if (time_proc == NULL && (latency != 0 || is_input)) { | ||
| 903 | if (!Pt_Started()) | ||
| 904 | Pt_Start(1, 0, 0); | ||
| 905 | /* time_get does not take a parameter, so coerce */ | ||
| 906 | midi->time_proc = (PmTimeProcPtr) Pt_Time; | ||
| 907 | } | ||
| 908 | midi->time_info = time_info; | ||
| 909 | if (is_input) { | ||
| 910 | midi->latency = 0; /* unused by input */ | ||
| 911 | if (buffer_size <= 0) buffer_size = 256; /* default buffer size */ | ||
| 912 | midi->queue = Pm_QueueCreate(buffer_size, (int32_t) sizeof(PmEvent)); | ||
| 913 | if (!midi->queue) { | ||
| 914 | /* free portMidi data */ | ||
| 915 | *stream = NULL; | ||
| 916 | pm_free(midi); | ||
| 917 | return pmInsufficientMemory; | ||
| 918 | } | ||
| 919 | } else { | ||
| 920 | /* if latency zero, output immediate (timestamps ignored) */ | ||
| 921 | /* if latency < 0, use 0 but don't return an error */ | ||
| 922 | if (latency < 0) latency = 0; | ||
| 923 | midi->latency = latency; | ||
| 924 | midi->queue = NULL; /* unused by output; input needs to allocate: */ | ||
| 925 | } | ||
| 926 | midi->buffer_len = buffer_size; /* portMidi input storage */ | ||
| 927 | midi->sysex_in_progress = FALSE; | ||
| 928 | midi->message = 0; | ||
| 929 | midi->message_count = 0; | ||
| 930 | midi->filters = (is_input ? PM_FILT_ACTIVE : 0); | ||
| 931 | midi->channel_mask = 0xFFFF; | ||
| 932 | midi->sync_time = 0; | ||
| 933 | midi->first_message = TRUE; | ||
| 934 | midi->api_info = NULL; | ||
| 935 | midi->fill_base = NULL; | ||
| 936 | midi->fill_offset_ptr = NULL; | ||
| 937 | midi->fill_length = 0; | ||
| 938 | midi->dictionary = pm_descriptors[device_id].dictionary; | ||
| 939 | pm_descriptors[device_id].pm_internal = midi; | ||
| 940 | return pmNoError; | ||
| 941 | } | ||
| 942 | |||
| 943 | |||
| 944 | PMEXPORT PmError Pm_OpenInput(PortMidiStream** stream, | ||
| 945 | PmDeviceID inputDevice, | ||
| 946 | void *inputDriverInfo, | ||
| 947 | int32_t bufferSize, | ||
| 948 | PmTimeProcPtr time_proc, | ||
| 949 | void *time_info) | ||
| 950 | { | ||
| 951 | PmInternal *midi; | ||
| 952 | PmError err = pmNoError; | ||
| 953 | pm_hosterror = FALSE; | ||
| 954 | *stream = NULL; /* invariant: *stream == midi */ | ||
| 955 | |||
| 956 | /* arg checking */ | ||
| 957 | if (!pm_descriptors[inputDevice].pub.input) | ||
| 958 | err = pmInvalidDeviceId; | ||
| 959 | else if (pm_descriptors[inputDevice].pub.opened) | ||
| 960 | err = pmInvalidDeviceId; | ||
| 961 | if (err != pmNoError) | ||
| 962 | goto error_return; | ||
| 963 | |||
| 964 | /* common initialization of PmInternal structure (midi): */ | ||
| 965 | err = pm_create_internal(&midi, inputDevice, TRUE, 0, time_proc, | ||
| 966 | time_info, bufferSize); | ||
| 967 | *stream = midi; | ||
| 968 | if (err) { | ||
| 969 | goto error_return; | ||
| 970 | } | ||
| 971 | |||
| 972 | /* open system dependent input device */ | ||
| 973 | err = (*midi->dictionary->open)(midi, inputDriverInfo); | ||
| 974 | if (err) { | ||
| 975 | *stream = NULL; | ||
| 976 | pm_descriptors[inputDevice].pm_internal = NULL; | ||
| 977 | /* free portMidi data */ | ||
| 978 | Pm_QueueDestroy(midi->queue); | ||
| 979 | pm_free(midi); | ||
| 980 | } else { | ||
| 981 | /* portMidi input open successful */ | ||
| 982 | pm_descriptors[inputDevice].pub.opened = TRUE; | ||
| 983 | } | ||
| 984 | error_return: | ||
| 985 | /* note: if there is a pmHostError, it is the responsibility | ||
| 986 | * of the system-dependent code (*midi->dictionary->open)() | ||
| 987 | * to set pm_hosterror and pm_hosterror_text | ||
| 988 | */ | ||
| 989 | return pm_errmsg(err); | ||
| 990 | } | ||
| 991 | |||
| 992 | |||
| 993 | PMEXPORT PmError Pm_OpenOutput(PortMidiStream** stream, | ||
| 994 | PmDeviceID outputDevice, | ||
| 995 | void *outputDriverInfo, | ||
| 996 | int32_t bufferSize, | ||
| 997 | PmTimeProcPtr time_proc, | ||
| 998 | void *time_info, | ||
| 999 | int32_t latency) | ||
| 1000 | { | ||
| 1001 | PmInternal *midi; | ||
| 1002 | PmError err = pmNoError; | ||
| 1003 | pm_hosterror = FALSE; | ||
| 1004 | *stream = NULL; | ||
| 1005 | |||
| 1006 | /* arg checking */ | ||
| 1007 | if (outputDevice < 0 || outputDevice >= pm_descriptor_len) | ||
| 1008 | err = pmInvalidDeviceId; | ||
| 1009 | else if (!pm_descriptors[outputDevice].pub.output) | ||
| 1010 | err = pmInvalidDeviceId; | ||
| 1011 | else if (pm_descriptors[outputDevice].pub.opened) | ||
| 1012 | err = pmInvalidDeviceId; | ||
| 1013 | if (err != pmNoError) | ||
| 1014 | goto error_return; | ||
| 1015 | |||
| 1016 | /* common initialization of PmInternal structure (midi): */ | ||
| 1017 | err = pm_create_internal(&midi, outputDevice, FALSE, latency, time_proc, | ||
| 1018 | time_info, bufferSize); | ||
| 1019 | *stream = midi; | ||
| 1020 | if (err) { | ||
| 1021 | goto error_return; | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | /* open system dependent output device */ | ||
| 1025 | err = (*midi->dictionary->open)(midi, outputDriverInfo); | ||
| 1026 | if (err) { | ||
| 1027 | *stream = NULL; | ||
| 1028 | pm_descriptors[outputDevice].pm_internal = NULL; | ||
| 1029 | /* free portMidi data */ | ||
| 1030 | pm_free(midi); | ||
| 1031 | } else { | ||
| 1032 | /* portMidi input open successful */ | ||
| 1033 | pm_descriptors[outputDevice].pub.opened = TRUE; | ||
| 1034 | } | ||
| 1035 | error_return: | ||
| 1036 | /* note: system-dependent code must set pm_hosterror and | ||
| 1037 | * pm_hosterror_text if a pmHostError occurs | ||
| 1038 | */ | ||
| 1039 | return pm_errmsg(err); | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | |||
| 1043 | static PmError create_virtual_device(const char *name, const char *interf, | ||
| 1044 | void *device_info, int is_input) | ||
| 1045 | { | ||
| 1046 | PmError err = pmNoError; | ||
| 1047 | int i; | ||
| 1048 | pm_hosterror = FALSE; | ||
| 1049 | |||
| 1050 | /* arg checking */ | ||
| 1051 | if (!name) { | ||
| 1052 | err = pmInvalidDeviceId; | ||
| 1053 | goto error_return; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | Pm_Initialize(); /* just in case */ | ||
| 1057 | |||
| 1058 | /* create the virtual device */ | ||
| 1059 | if (pm_interf_list_len == 0) { | ||
| 1060 | return pmNotImplemented; | ||
| 1061 | } | ||
| 1062 | if (!interf) { | ||
| 1063 | /* default interface is the first one */ | ||
| 1064 | interf = pm_interf_list[0].interf; | ||
| 1065 | } | ||
| 1066 | /* look up and call the create_fn for interf(ace), e.g. "CoreMIDI" */ | ||
| 1067 | for (i = 0; i < pm_interf_list_len; i++) { | ||
| 1068 | if (strcmp(pm_interf_list[i].interf, interf) == 0) { | ||
| 1069 | int id = (*pm_interf_list[i].create_fn)(is_input, | ||
| 1070 | name, device_info); | ||
| 1071 | /* id could be pmNameConflict or an actual descriptor index */ | ||
| 1072 | if (id >= 0) { | ||
| 1073 | pm_descriptors[id].pub.is_virtual = TRUE; | ||
| 1074 | } | ||
| 1075 | err = id; | ||
| 1076 | goto error_return; | ||
| 1077 | } | ||
| 1078 | } | ||
| 1079 | err = pmInterfaceNotSupported; | ||
| 1080 | |||
| 1081 | error_return: | ||
| 1082 | /* note: if there is a pmHostError, it is the responsibility | ||
| 1083 | * of the system-dependent code (*midi->dictionary->open)() | ||
| 1084 | * to set pm_hosterror and pm_hosterror_text | ||
| 1085 | */ | ||
| 1086 | return pm_errmsg(err); | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | |||
| 1090 | PMEXPORT PmError Pm_CreateVirtualInput(const char *name, | ||
| 1091 | const char *interf, | ||
| 1092 | void *deviceInfo) | ||
| 1093 | { | ||
| 1094 | return create_virtual_device(name, interf, deviceInfo, TRUE); | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | PMEXPORT PmError Pm_CreateVirtualOutput(const char *name, const char *interf, | ||
| 1098 | void *deviceInfo) | ||
| 1099 | { | ||
| 1100 | return create_virtual_device(name, interf, deviceInfo, FALSE); | ||
| 1101 | } | ||
| 1102 | |||
| 1103 | PmError Pm_DeleteVirtualDevice(PmDeviceID id) | ||
| 1104 | { | ||
| 1105 | int i; | ||
| 1106 | const char *interf = pm_descriptors[id].pub.interf; | ||
| 1107 | PmError err = pmBadData; /* returned if we cannot find the interface- | ||
| 1108 | * specific delete function */ | ||
| 1109 | /* arg checking */ | ||
| 1110 | if (id < 0 || id >= pm_descriptor_len || | ||
| 1111 | pm_descriptors[id].pub.opened || pm_descriptors[id].deleted) { | ||
| 1112 | return pmInvalidDeviceId; | ||
| 1113 | } | ||
| 1114 | /* delete function pointer is in interfaces list */ | ||
| 1115 | for (i = 0; i < pm_interf_list_len; i++) { | ||
| 1116 | if (strcmp(pm_interf_list[i].interf, interf) == 0) { | ||
| 1117 | err = (*pm_interf_list[i].delete_fn)(id); | ||
| 1118 | break; | ||
| 1119 | } | ||
| 1120 | } | ||
| 1121 | pm_descriptors[id].deleted = TRUE; | ||
| 1122 | /* (pm_internal should already be NULL because !pub.opened) */ | ||
| 1123 | pm_descriptors[id].pm_internal = NULL; | ||
| 1124 | pm_descriptors[id].descriptor = NULL; | ||
| 1125 | return err; | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask) | ||
| 1129 | { | ||
| 1130 | PmInternal *midi = (PmInternal *) stream; | ||
| 1131 | PmError err = pmNoError; | ||
| 1132 | |||
| 1133 | if (midi == NULL) | ||
| 1134 | err = pmBadPtr; | ||
| 1135 | else | ||
| 1136 | midi->channel_mask = mask; | ||
| 1137 | |||
| 1138 | return pm_errmsg(err); | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | |||
| 1142 | PMEXPORT PmError Pm_SetFilter(PortMidiStream *stream, int32_t filters) | ||
| 1143 | { | ||
| 1144 | PmInternal *midi = (PmInternal *) stream; | ||
| 1145 | PmError err = pmNoError; | ||
| 1146 | |||
| 1147 | /* arg checking */ | ||
| 1148 | if (midi == NULL) | ||
| 1149 | err = pmBadPtr; | ||
| 1150 | else if (!pm_descriptors[midi->device_id].pub.opened) | ||
| 1151 | err = pmBadPtr; | ||
| 1152 | else | ||
| 1153 | midi->filters = filters; | ||
| 1154 | return pm_errmsg(err); | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | |||
| 1158 | PMEXPORT PmError Pm_Close(PortMidiStream *stream) | ||
| 1159 | { | ||
| 1160 | PmInternal *midi = (PmInternal *) stream; | ||
| 1161 | PmError err = pmNoError; | ||
| 1162 | |||
| 1163 | pm_hosterror = FALSE; | ||
| 1164 | /* arg checking */ | ||
| 1165 | if (midi == NULL) /* midi must point to something */ | ||
| 1166 | err = pmBadPtr; | ||
| 1167 | /* if it is an open device, the device_id will be valid */ | ||
| 1168 | else if (midi->device_id < 0 || midi->device_id >= pm_descriptor_len) | ||
| 1169 | err = pmBadPtr; | ||
| 1170 | /* and the device should be in the opened state */ | ||
| 1171 | else if (!pm_descriptors[midi->device_id].pub.opened) | ||
| 1172 | err = pmBadPtr; | ||
| 1173 | |||
| 1174 | if (err != pmNoError) | ||
| 1175 | goto error_return; | ||
| 1176 | |||
| 1177 | /* close the device */ | ||
| 1178 | err = (*midi->dictionary->close)(midi); | ||
| 1179 | /* even if an error occurred, continue with cleanup */ | ||
| 1180 | pm_descriptors[midi->device_id].pm_internal = NULL; | ||
| 1181 | pm_descriptors[midi->device_id].pub.opened = FALSE; | ||
| 1182 | if (midi->queue) Pm_QueueDestroy(midi->queue); | ||
| 1183 | pm_free(midi); | ||
| 1184 | error_return: | ||
| 1185 | /* system dependent code must set pm_hosterror and | ||
| 1186 | * pm_hosterror_text if a pmHostError occurs. | ||
| 1187 | */ | ||
| 1188 | return pm_errmsg(err); | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | PmError Pm_Synchronize(PortMidiStream* stream) | ||
| 1192 | { | ||
| 1193 | PmInternal *midi = (PmInternal *) stream; | ||
| 1194 | PmError err = pmNoError; | ||
| 1195 | if (midi == NULL) | ||
| 1196 | err = pmBadPtr; | ||
| 1197 | else if (!pm_descriptors[midi->device_id].pub.output) | ||
| 1198 | err = pmBadPtr; | ||
| 1199 | else if (!pm_descriptors[midi->device_id].pub.opened) | ||
| 1200 | err = pmBadPtr; | ||
| 1201 | else | ||
| 1202 | midi->first_message = TRUE; | ||
| 1203 | return err; | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | PMEXPORT PmError Pm_Abort(PortMidiStream* stream) | ||
| 1207 | { | ||
| 1208 | PmInternal *midi = (PmInternal *) stream; | ||
| 1209 | PmError err; | ||
| 1210 | /* arg checking */ | ||
| 1211 | if (midi == NULL) | ||
| 1212 | err = pmBadPtr; | ||
| 1213 | else if (!pm_descriptors[midi->device_id].pub.output) | ||
| 1214 | err = pmBadPtr; | ||
| 1215 | else if (!pm_descriptors[midi->device_id].pub.opened) | ||
| 1216 | err = pmBadPtr; | ||
| 1217 | else | ||
| 1218 | err = (*midi->dictionary->abort)(midi); | ||
| 1219 | |||
| 1220 | if (err == pmHostError) { | ||
| 1221 | midi->dictionary->check_host_error(midi); | ||
| 1222 | } | ||
| 1223 | return pm_errmsg(err); | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | |||
| 1227 | |||
| 1228 | /* pm_channel_filtered returns non-zero if the channel mask is | ||
| 1229 | blocking the current channel */ | ||
| 1230 | #define pm_channel_filtered(status, mask) \ | ||
| 1231 | ((((status) & 0xF0) != 0xF0) && (!(Pm_Channel((status) & 0x0F) & (mask)))) | ||
| 1232 | |||
| 1233 | |||
| 1234 | /* The following two functions will checks to see if a MIDI message | ||
| 1235 | matches the filtering criteria. Since the sysex routines only want | ||
| 1236 | to filter realtime messages, we need to have separate routines. | ||
| 1237 | */ | ||
| 1238 | |||
| 1239 | |||
| 1240 | /* pm_realtime_filtered returns non-zero if the filter will kill the | ||
| 1241 | current message. Note that only realtime messages are checked here. | ||
| 1242 | */ | ||
| 1243 | #define pm_realtime_filtered(status, filters) \ | ||
| 1244 | ((((status) & 0xF0) == 0xF0) && ((1 << ((status) & 0xF)) & (filters))) | ||
| 1245 | |||
| 1246 | /* | ||
| 1247 | return ((status == MIDI_ACTIVE) && (filters & PM_FILT_ACTIVE)) | ||
| 1248 | || ((status == MIDI_CLOCK) && (filters & PM_FILT_CLOCK)) | ||
| 1249 | || ((status == MIDI_START) && (filters & PM_FILT_PLAY)) | ||
| 1250 | || ((status == MIDI_STOP) && (filters & PM_FILT_PLAY)) | ||
| 1251 | || ((status == MIDI_CONTINUE) && (filters & PM_FILT_PLAY)) | ||
| 1252 | || ((status == MIDI_F9) && (filters & PM_FILT_F9)) | ||
| 1253 | || ((status == MIDI_FD) && (filters & PM_FILT_FD)) | ||
| 1254 | || ((status == MIDI_RESET) && (filters & PM_FILT_RESET)) | ||
| 1255 | || ((status == MIDI_MTC) && (filters & PM_FILT_MTC)) | ||
| 1256 | || ((status == MIDI_SONGPOS) && (filters & PM_FILT_SONG_POSITION)) | ||
| 1257 | || ((status == MIDI_SONGSEL) && (filters & PM_FILT_SONG_SELECT)) | ||
| 1258 | || ((status == MIDI_TUNE) && (filters & PM_FILT_TUNE)); | ||
| 1259 | }*/ | ||
| 1260 | |||
| 1261 | |||
| 1262 | /* pm_status_filtered returns non-zero if a filter will kill the | ||
| 1263 | current message, based on status. Note that sysex and real time are | ||
| 1264 | not checked. It is up to the subsystem (winmm, core midi, alsa) to | ||
| 1265 | filter sysex, as it is handled more easily and efficiently at that | ||
| 1266 | level. Realtime message are filtered in pm_realtime_filtered. | ||
| 1267 | */ | ||
| 1268 | #define pm_status_filtered(status, filters) \ | ||
| 1269 | ((1 << (16 + ((status) >> 4))) & (filters)) | ||
| 1270 | |||
| 1271 | |||
| 1272 | /* | ||
| 1273 | return ((status == MIDI_NOTE_ON) && (filters & PM_FILT_NOTE)) | ||
| 1274 | || ((status == MIDI_NOTE_OFF) && (filters & PM_FILT_NOTE)) | ||
| 1275 | || ((status == MIDI_CHANNEL_AT) && | ||
| 1276 | (filters & PM_FILT_CHANNEL_AFTERTOUCH)) | ||
| 1277 | || ((status == MIDI_POLY_AT) && (filters & PM_FILT_POLY_AFTERTOUCH)) | ||
| 1278 | || ((status == MIDI_PROGRAM) && (filters & PM_FILT_PROGRAM)) | ||
| 1279 | || ((status == MIDI_CONTROL) && (filters & PM_FILT_CONTROL)) | ||
| 1280 | || ((status == MIDI_PITCHBEND) && (filters & PM_FILT_PITCHBEND)); | ||
| 1281 | |||
| 1282 | } | ||
| 1283 | */ | ||
| 1284 | |||
| 1285 | static void pm_flush_sysex(PmInternal *midi, PmTimestamp timestamp) | ||
| 1286 | { | ||
| 1287 | PmEvent event; | ||
| 1288 | |||
| 1289 | /* there may be nothing in the buffer */ | ||
| 1290 | if (midi->message_count == 0) return; /* nothing to flush */ | ||
| 1291 | |||
| 1292 | event.message = midi->message; | ||
| 1293 | event.timestamp = timestamp; | ||
| 1294 | /* copied from pm_read_short, avoids filtering */ | ||
| 1295 | if (Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) { | ||
| 1296 | midi->sysex_in_progress = FALSE; | ||
| 1297 | } | ||
| 1298 | midi->message_count = 0; | ||
| 1299 | midi->message = 0; | ||
| 1300 | } | ||
| 1301 | |||
| 1302 | |||
| 1303 | /* pm_read_short and pm_read_bytes | ||
| 1304 | are the interface between system-dependent MIDI input handlers | ||
| 1305 | and the system-independent PortMIDI code. | ||
| 1306 | The input handler MUST obey these rules: | ||
| 1307 | 1) all short input messages must be sent to pm_read_short, which | ||
| 1308 | enqueues them to a FIFO for the application. | ||
| 1309 | 2) each buffer of sysex bytes should be reported by calling pm_read_bytes | ||
| 1310 | (which sets midi->sysex_in_progress). After the eox byte, | ||
| 1311 | pm_read_bytes will clear sysex_in_progress | ||
| 1312 | */ | ||
| 1313 | |||
| 1314 | /* pm_read_short is the place where all input messages arrive from | ||
| 1315 | system-dependent code such as pmwinmm.c. Here, the messages | ||
| 1316 | are entered into the PortMidi input buffer. | ||
| 1317 | */ | ||
| 1318 | void pm_read_short(PmInternal *midi, PmEvent *event) | ||
| 1319 | { | ||
| 1320 | int status; | ||
| 1321 | /* arg checking */ | ||
| 1322 | assert(midi != NULL); | ||
| 1323 | /* midi filtering is applied here */ | ||
| 1324 | status = Pm_MessageStatus(event->message); | ||
| 1325 | if (!pm_status_filtered(status, midi->filters) | ||
| 1326 | && (!is_real_time(status) || | ||
| 1327 | !pm_realtime_filtered(status, midi->filters)) | ||
| 1328 | && !pm_channel_filtered(status, midi->channel_mask)) { | ||
| 1329 | /* if sysex is in progress and we get a status byte, it had | ||
| 1330 | better be a realtime message or the starting SYSEX byte; | ||
| 1331 | otherwise, we exit the sysex_in_progress state | ||
| 1332 | */ | ||
| 1333 | if (midi->sysex_in_progress && (status & MIDI_STATUS_MASK)) { | ||
| 1334 | /* two choices: real-time or not. If it's real-time, then | ||
| 1335 | * this should be delivered as a sysex byte because it is | ||
| 1336 | * embedded in a sysex message | ||
| 1337 | */ | ||
| 1338 | if (is_real_time(status)) { | ||
| 1339 | midi->message |= (status << (8 * midi->message_count++)); | ||
| 1340 | if (midi->message_count == 4) { | ||
| 1341 | pm_flush_sysex(midi, event->timestamp); | ||
| 1342 | } | ||
| 1343 | } else { /* otherwise, it's not real-time. This interrupts | ||
| 1344 | * a sysex message in progress */ | ||
| 1345 | midi->sysex_in_progress = FALSE; | ||
| 1346 | } | ||
| 1347 | } else if (Pm_Enqueue(midi->queue, event) == pmBufferOverflow) { | ||
| 1348 | midi->sysex_in_progress = FALSE; | ||
| 1349 | } | ||
| 1350 | } | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | |||
| 1354 | /* pm_read_bytes -- a sequence of bytes has been read from a device. | ||
| 1355 | * parse the bytes into PmEvents and put them in the queue. | ||
| 1356 | * midi - the midi device | ||
| 1357 | * data - the bytes | ||
| 1358 | * len - the number of bytes | ||
| 1359 | * timestamp - when were the bytes received? | ||
| 1360 | * | ||
| 1361 | * returns how many bytes processed, which is always the len parameter | ||
| 1362 | */ | ||
| 1363 | unsigned int pm_read_bytes(PmInternal *midi, const unsigned char *data, | ||
| 1364 | int len, PmTimestamp timestamp) | ||
| 1365 | { | ||
| 1366 | int i = 0; /* index into data, must not be unsigned (!) */ | ||
| 1367 | PmEvent event; | ||
| 1368 | event.timestamp = timestamp; | ||
| 1369 | assert(midi); | ||
| 1370 | |||
| 1371 | /* Since sysex messages may have embedded real-time messages, we | ||
| 1372 | * cannot simply send every consecutive group of 4 bytes as sysex | ||
| 1373 | * data. Instead, we insert each data byte into midi->message and | ||
| 1374 | * keep count using midi->message_count. If we encounter a | ||
| 1375 | * real-time message, it is sent immediately as a 1-byte PmEvent, | ||
| 1376 | * while sysex bytes are sent as PmEvents in groups of 4 bytes | ||
| 1377 | * until the sysex is either terminated by EOX (F7) or a | ||
| 1378 | * non-real-time message is encountered, indicating that the EOX | ||
| 1379 | * was dropped. | ||
| 1380 | */ | ||
| 1381 | |||
| 1382 | /* This is a finite state machine so that we can accept any number | ||
| 1383 | * of bytes, even if they contain partial messages. | ||
| 1384 | * | ||
| 1385 | * midi->sysex_in_progress says we are expecting sysex message bytes | ||
| 1386 | * (otherwise, expect a short message or real-time message) | ||
| 1387 | * midi->message accumulates bytes to enqueue for application | ||
| 1388 | * midi->message_count is the number of bytes accumulated | ||
| 1389 | * midi->short_message_count is how many bytes we need in midi->message, | ||
| 1390 | * therefore midi->message_count, before we have a complete message | ||
| 1391 | * midi->running_status is running status or 0 if there is none | ||
| 1392 | * | ||
| 1393 | * Set running status when: A status byte < F0 is received. | ||
| 1394 | * Clear running status when: A status byte from F0 through F7 is | ||
| 1395 | * received. | ||
| 1396 | * Ignore (drop) data bytes when running status is 0. | ||
| 1397 | * | ||
| 1398 | * Our output buffer (the application input buffer) can overflow | ||
| 1399 | * at any time. If that occurs, we have to clear sysex_in_progress | ||
| 1400 | * (otherwise, the buffer could be flushed and we could resume | ||
| 1401 | * inserting sysex bytes into the buffer, resulting in a continuation | ||
| 1402 | * of a sysex message even though a buffer full of bytes was dropped.) | ||
| 1403 | * | ||
| 1404 | * Since we have to parse everything and form <=4-byte PmMessages, | ||
| 1405 | * we send all messages via pm_read_short, which filters messages | ||
| 1406 | * according to midi->filters and clears sysex_in_progress on | ||
| 1407 | * buffer overflow. This also provides a "short cut" for short | ||
| 1408 | * messages that are already parsed, allowing API-specific code | ||
| 1409 | * to bypass this more expensive state machine. What if we are | ||
| 1410 | * getting a sysex message, but it is interrupted by a short | ||
| 1411 | * message (status 80-EF) and a direct call to pm_read_short? | ||
| 1412 | * Without some care, the state machine would still be in | ||
| 1413 | * sysex_in_progress mode, and subsequent data bytes would be | ||
| 1414 | * accumulated as more sysex data, which is wrong since you | ||
| 1415 | * cannot have a short message in the middle of a sysex message. | ||
| 1416 | * To avoid this problem, pm_read_short clears sysex_in_progress | ||
| 1417 | * when a non-real-time short message arrives. | ||
| 1418 | */ | ||
| 1419 | |||
| 1420 | while (i < len) { | ||
| 1421 | unsigned char byte = data[i++]; | ||
| 1422 | if (is_real_time(byte)) { | ||
| 1423 | event.message = byte; | ||
| 1424 | pm_read_short(midi, &event); | ||
| 1425 | } else if (byte & MIDI_STATUS_MASK && byte != MIDI_EOX) { | ||
| 1426 | midi->message = byte; | ||
| 1427 | midi->message_count = 1; | ||
| 1428 | if (byte == MIDI_SYSEX) { | ||
| 1429 | midi->sysex_in_progress = TRUE; | ||
| 1430 | } else { | ||
| 1431 | midi->sysex_in_progress = FALSE; | ||
| 1432 | midi->short_message_count = pm_midi_length(midi->message); | ||
| 1433 | /* maybe we're done already with a 1-byte message: */ | ||
| 1434 | if (midi->short_message_count == 1) { | ||
| 1435 | pm_read_short(midi, &event); | ||
| 1436 | midi->message_count = 0; | ||
| 1437 | } | ||
| 1438 | } | ||
| 1439 | } else if (midi->sysex_in_progress) { /* sysex data byte */ | ||
| 1440 | /* accumulate sysex message data or EOX */ | ||
| 1441 | midi->message |= (byte << (8 * midi->message_count++)); | ||
| 1442 | if (midi->message_count == 4 || byte == MIDI_EOX) { | ||
| 1443 | event.message = midi->message; | ||
| 1444 | /* enqueue if not filtered, and then if there is overflow, | ||
| 1445 | stop sysex_in_progress */ | ||
| 1446 | if (!(midi->filters & PM_FILT_SYSEX) && | ||
| 1447 | Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) { | ||
| 1448 | midi->sysex_in_progress = FALSE; | ||
| 1449 | } else if (byte == MIDI_EOX) { /* continue unless EOX */ | ||
| 1450 | midi->sysex_in_progress = FALSE; | ||
| 1451 | } | ||
| 1452 | midi->message_count = 0; | ||
| 1453 | midi->message = 0; | ||
| 1454 | } | ||
| 1455 | } else { /* no sysex in progress, must be short message */ | ||
| 1456 | if (midi->message_count == 0) { /* need a running status */ | ||
| 1457 | if (midi->running_status) { | ||
| 1458 | midi->message = midi->running_status; | ||
| 1459 | midi->message_count = 1; | ||
| 1460 | } else { /* drop data byte - not sysex and no status byte */ | ||
| 1461 | continue; | ||
| 1462 | } | ||
| 1463 | } | ||
| 1464 | midi->message |= (byte << (8 * midi->message_count++)); | ||
| 1465 | if (midi->message_count == midi->short_message_count) { | ||
| 1466 | event.message = midi->message; | ||
| 1467 | pm_read_short(midi, &event); | ||
| 1468 | } | ||
| 1469 | } | ||
| 1470 | } | ||
| 1471 | return i; | ||
| 1472 | } | ||
