2 #pragma warning(disable: 4244)
3 #pragma warning(disable: 4018)
11 #include "pminternal.h"
14 #define MIDI_CLOCK 0xf8
15 #define MIDI_ACTIVE 0xfe
16 #define MIDI_STATUS_MASK 0x80
17 #define MIDI_SYSEX 0xf0
19 #define MIDI_START 0xFA
20 #define MIDI_STOP 0xFC
21 #define MIDI_CONTINUE 0xFB
24 #define MIDI_RESET 0xFF
25 #define MIDI_NOTE_ON 0x90
26 #define MIDI_NOTE_OFF 0x80
27 #define MIDI_CHANNEL_AT 0xD0
28 #define MIDI_POLY_AT 0xA0
29 #define MIDI_PROGRAM 0xC0
30 #define MIDI_CONTROL 0xB0
31 #define MIDI_PITCHBEND 0xE0
33 #define MIDI_SONGPOS 0xF2
34 #define MIDI_SONGSEL 0xF3
35 #define MIDI_TUNE 0xF6
37 #define is_empty(midi) ((midi)->tail == (midi)->head)
42 int pm_initialized = FALSE;
45 char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN];
47 #ifdef PM_CHECK_ERRORS
53 static void prompt_and_exit(
void)
55 char line[STRING_MAX];
56 printf(
"type ENTER...");
57 fgets(line, STRING_MAX, stdin);
63 static PmError pm_errmsg(PmError err)
65 if (err == pmHostError) {
69 printf(
"PortMidi found host error...\n %s\n", pm_hosterror_text);
71 pm_hosterror_text[0] = 0;
74 printf(
"PortMidi call failed...\n %s\n", Pm_GetErrorText(err));
80 #define pm_errmsg(err) err
89 int pm_descriptor_max = 0;
90 int pm_descriptor_index = 0;
91 descriptor_type descriptors = NULL;
102 PmError pm_add_device(
char *interf,
char *name,
int input,
103 void *descriptor, pm_fns_type dictionary) {
104 if (pm_descriptor_index >= pm_descriptor_max) {
106 descriptor_type new_descriptors = (descriptor_type)
107 pm_alloc(
sizeof(descriptor_node) * (pm_descriptor_max + 32));
108 if (!new_descriptors)
return pmInsufficientMemory;
110 memcpy(new_descriptors, descriptors,
111 sizeof(descriptor_node) * pm_descriptor_max);
114 pm_descriptor_max += 32;
115 descriptors = new_descriptors;
117 descriptors[pm_descriptor_index].pub.interf = interf;
118 descriptors[pm_descriptor_index].pub.name = name;
119 descriptors[pm_descriptor_index].pub.input = input;
120 descriptors[pm_descriptor_index].pub.output = !input;
123 descriptors[pm_descriptor_index].pub.opened = FALSE;
126 descriptors[pm_descriptor_index].descriptor = descriptor;
129 descriptors[pm_descriptor_index].internalDescriptor = NULL;
131 descriptors[pm_descriptor_index].dictionary = dictionary;
133 pm_descriptor_index++;
142 int pm_find_default_device(
char *pattern,
int is_input)
147 char *interf_pref =
"";
148 char *name_pref = strstr(pattern,
", ");
151 interf_pref = pattern;
157 for (i = 0; i < pm_descriptor_index; i++) {
159 if (info->
input == is_input &&
160 strstr(info->
name, name_pref) &&
161 strstr(info->
interf, interf_pref)) {
176 PMEXPORT
int Pm_CountDevices(
void ) {
179 return pm_descriptor_index;
185 if (
id >= 0 &&
id < pm_descriptor_index) {
186 return &descriptors[id].pub;
192 PmError pm_success_fn(PmInternal *midi) {
197 PmError none_write_short(PmInternal *midi,
PmEvent *buffer) {
202 PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp) {
206 PmError none_write_byte(PmInternal *midi,
unsigned char byte,
207 PmTimestamp timestamp) {
212 PmError pm_fail_fn(PmInternal *midi) {
216 static PmError none_open(PmInternal *midi,
void *driverInfo) {
219 static void none_get_host_error(PmInternal * midi,
char * msg,
unsigned int len) {
222 static unsigned int none_has_host_error(PmInternal * midi) {
225 PmTimestamp none_synchronize(PmInternal *midi) {
229 #define none_abort pm_fail_fn
230 #define none_close pm_fail_fn
232 pm_fns_node pm_none_dictionary = {
249 PMEXPORT
const char *Pm_GetErrorText( PmError errnum ) {
258 msg =
"PortMidi: `Host error'";
260 case pmInvalidDeviceId:
261 msg =
"PortMidi: `Invalid device ID'";
263 case pmInsufficientMemory:
264 msg =
"PortMidi: `Insufficient memory'";
266 case pmBufferTooSmall:
267 msg =
"PortMidi: `Buffer too small'";
270 msg =
"PortMidi: `Bad pointer'";
272 case pmInternalError:
273 msg =
"PortMidi: `Internal PortMidi Error'";
275 case pmBufferOverflow:
276 msg =
"PortMidi: `Buffer overflow'";
279 msg =
"PortMidi: `Invalid MIDI message Data'";
281 case pmBufferMaxSize:
282 msg =
"PortMidi: `Buffer cannot be made larger'";
285 msg =
"PortMidi: `Illegal error number'";
295 PMEXPORT
void Pm_GetHostErrorText(
char * msg,
unsigned int len) {
299 strncpy(msg, (
char *) pm_hosterror_text, len);
300 pm_hosterror = FALSE;
301 pm_hosterror_text[0] = 0;
310 PMEXPORT
int Pm_HasHostError(PortMidiStream * stream) {
311 if (pm_hosterror)
return TRUE;
313 PmInternal * midi = (PmInternal *) stream;
314 pm_hosterror = (*midi->dictionary->has_host_error)(midi);
316 midi->dictionary->host_error(midi, pm_hosterror_text,
317 PM_HOST_ERROR_MSG_LEN);
326 PMEXPORT PmError Pm_Initialize(
void ) {
327 if (!pm_initialized) {
328 pm_hosterror = FALSE;
329 pm_hosterror_text[0] = 0;
331 pm_initialized = TRUE;
337 PMEXPORT PmError Pm_Terminate(
void ) {
338 if (pm_initialized) {
341 if (descriptors != NULL) {
345 pm_descriptor_index = 0;
346 pm_descriptor_max = 0;
347 pm_initialized = FALSE;
358 PmInternal *midi = (PmInternal *) stream;
360 PmError err = pmNoError;
361 pm_hosterror = FALSE;
365 else if(!descriptors[midi->device_id].pub.opened)
367 else if(!descriptors[midi->device_id].pub.input)
374 else err = (*(midi->dictionary->poll))(midi);
376 if (err != pmNoError) {
377 if (err == pmHostError) {
378 midi->dictionary->host_error(midi, pm_hosterror_text,
379 PM_HOST_ERROR_MSG_LEN);
382 return pm_errmsg(err);
386 PmError err = Pm_Dequeue(midi->queue, buffer++);
387 if (err == pmBufferOverflow) {
389 return pm_errmsg(pmBufferOverflow);
390 }
else if (err == 0) {
398 PMEXPORT PmError
Pm_Poll( PortMidiStream *stream )
400 PmInternal *midi = (PmInternal *) stream;
403 pm_hosterror = FALSE;
407 else if (!descriptors[midi->device_id].pub.opened)
409 else if (!descriptors[midi->device_id].pub.input)
412 err = (*(midi->dictionary->poll))(midi);
414 if (err != pmNoError) {
415 if (err == pmHostError) {
416 midi->dictionary->host_error(midi, pm_hosterror_text,
417 PM_HOST_ERROR_MSG_LEN);
420 return pm_errmsg(err);
423 return !Pm_QueueEmpty(midi->queue);
431 static PmError pm_end_sysex(PmInternal *midi)
433 PmError err = (*midi->dictionary->end_sysex)(midi, 0);
434 midi->sysex_in_progress = FALSE;
435 if (err == pmHostError) {
436 midi->dictionary->host_error(midi, pm_hosterror_text,
437 PM_HOST_ERROR_MSG_LEN);
450 PmInternal *midi = (PmInternal *) stream;
451 PmError err = pmNoError;
455 pm_hosterror = FALSE;
459 else if(!descriptors[midi->device_id].pub.opened)
461 else if(!descriptors[midi->device_id].pub.output)
466 if (err != pmNoError)
goto pm_write_error;
468 if (midi->latency == 0) {
471 midi->now = (*(midi->time_proc))(midi->time_info);
472 if (midi->first_message || midi->sync_time + 100 < midi->now) {
474 midi->now = (*midi->dictionary->synchronize)(midi);
475 midi->first_message = FALSE;
491 for (i = 0; i < length; i++) {
492 uint32_t msg = buffer[i].message;
495 if (Pm_MessageStatus(msg) == MIDI_SYSEX) {
496 if (midi->sysex_in_progress) {
498 midi->sysex_in_progress = FALSE;
502 midi->sysex_in_progress = TRUE;
503 if ((err = (*midi->dictionary->begin_sysex)(midi,
504 buffer[i].timestamp)) != pmNoError)
506 if ((err = (*midi->dictionary->write_byte)(midi, MIDI_SYSEX,
507 buffer[i].timestamp)) != pmNoError)
511 }
else if ((msg & MIDI_STATUS_MASK) &&
512 (Pm_MessageStatus(msg) != MIDI_EOX)) {
514 if (midi->sysex_in_progress) {
516 if (is_real_time(msg)) {
517 if ((err = (*midi->dictionary->write_realtime)(midi,
518 &(buffer[i]))) != pmNoError)
521 midi->sysex_in_progress = FALSE;
525 (*midi->dictionary->end_sysex)(midi, 0);
529 if ((err = (*midi->dictionary->write_short)(midi,
530 &(buffer[i]))) != pmNoError)
535 if (midi->sysex_in_progress) {
537 if (bits == 0 && midi->fill_base &&
538 (*midi->fill_offset_ptr) + 4 <= midi->fill_length &&
539 (msg & 0x80808080) == 0) {
541 unsigned char *ptr = midi->fill_base +
542 *(midi->fill_offset_ptr);
543 ptr[0] = msg; ptr[1] = msg >> 8;
544 ptr[2] = msg >> 16; ptr[3] = msg >> 24;
545 (*midi->fill_offset_ptr) += 4;
550 unsigned char midi_byte = (
unsigned char) (msg >> bits);
551 if ((err = (*midi->dictionary->write_byte)(midi, midi_byte,
552 buffer[i].timestamp)) != pmNoError)
554 if (midi_byte == MIDI_EOX) {
555 err = pm_end_sysex(midi);
556 if (err != pmNoError)
goto error_exit;
568 if (!midi->sysex_in_progress)
569 err = (*midi->dictionary->write_flush)(midi, 0);
571 if (err == pmHostError) {
572 midi->dictionary->host_error(midi, pm_hosterror_text,
573 PM_HOST_ERROR_MSG_LEN);
577 return pm_errmsg(err);
585 event.timestamp = when;
596 #define BUFLEN ((int) (PM_DEFAULT_SYSEX_BUFFER_SIZE / sizeof(PmMessage)))
599 PmInternal *midi = (PmInternal *) stream;
604 buffer[0].message = 0;
605 buffer[0].timestamp = when;
609 buffer[bufx].message |= ((*msg) << shift);
611 if (*msg++ == MIDI_EOX)
break;
615 if (bufx == buffer_size) {
616 PmError err =
Pm_Write(stream, buffer, buffer_size);
621 buffer_size = BUFLEN;
623 if (midi->fill_base) {
625 while (*(midi->fill_offset_ptr) < midi->fill_length) {
626 midi->fill_base[(*midi->fill_offset_ptr)++] = *msg;
627 if (*msg++ == MIDI_EOX) {
628 err = pm_end_sysex(midi);
629 if (err != pmNoError)
return pm_errmsg(err);
650 buffer[bufx].message = 0;
651 buffer[bufx].timestamp = when;
659 if (shift != 0) bufx++;
661 PmError err =
Pm_Write(stream, buffer, bufx);
670 PmDeviceID inputDevice,
671 void *inputDriverInfo,
673 PmTimeProcPtr time_proc,
677 PmError err = pmNoError;
678 pm_hosterror = FALSE;
682 if (inputDevice < 0 || inputDevice >= pm_descriptor_index)
683 err = pmInvalidDeviceId;
684 else if (!descriptors[inputDevice].pub.input)
685 err = pmInvalidDeviceId;
686 else if(descriptors[inputDevice].pub.opened)
687 err = pmInvalidDeviceId;
689 if (err != pmNoError)
693 midi = (PmInternal *) pm_alloc(
sizeof(PmInternal));
696 err = pmInsufficientMemory;
699 midi->device_id = inputDevice;
700 midi->write_flag = FALSE;
701 midi->time_proc = time_proc;
702 midi->time_info = time_info;
708 if (bufferSize <= 0) bufferSize = 256;
709 midi->queue = Pm_QueueCreate(bufferSize, (int32_t)
sizeof(
PmEvent));
714 err = pmInsufficientMemory;
717 midi->buffer_len = bufferSize;
719 midi->sysex_in_progress = FALSE;
720 midi->sysex_message = 0;
721 midi->sysex_message_count = 0;
723 midi->channel_mask = 0xFFFF;
725 midi->first_message = TRUE;
726 midi->dictionary = descriptors[inputDevice].dictionary;
727 midi->fill_base = NULL;
728 midi->fill_offset_ptr = NULL;
729 midi->fill_length = 0;
730 descriptors[inputDevice].internalDescriptor = midi;
732 err = (*midi->dictionary->open)(midi, inputDriverInfo);
735 descriptors[inputDevice].internalDescriptor = NULL;
737 Pm_QueueDestroy(midi->queue);
741 descriptors[inputDevice].pub.opened = TRUE;
748 return pm_errmsg(err);
752 PMEXPORT PmError Pm_OpenOutput(PortMidiStream** stream,
753 PmDeviceID outputDevice,
754 void *outputDriverInfo,
756 PmTimeProcPtr time_proc,
761 PmError err = pmNoError;
762 pm_hosterror = FALSE;
766 if (outputDevice < 0 || outputDevice >= pm_descriptor_index)
767 err = pmInvalidDeviceId;
768 else if (!descriptors[outputDevice].pub.output)
769 err = pmInvalidDeviceId;
770 else if (descriptors[outputDevice].pub.opened)
771 err = pmInvalidDeviceId;
772 if (err != pmNoError)
776 midi = (PmInternal *) pm_alloc(
sizeof(PmInternal));
779 err = pmInsufficientMemory;
782 midi->device_id = outputDevice;
783 midi->write_flag = TRUE;
784 midi->time_proc = time_proc;
787 if (time_proc == NULL && latency != 0) {
791 midi->time_proc = (PmTimeProcPtr) Pt_Time;
793 midi->time_info = time_info;
794 midi->buffer_len = bufferSize;
798 if (latency < 0) latency = 0;
799 midi->latency = latency;
800 midi->sysex_in_progress = FALSE;
801 midi->sysex_message = 0;
802 midi->sysex_message_count = 0;
804 midi->channel_mask = 0xFFFF;
806 midi->first_message = TRUE;
807 midi->dictionary = descriptors[outputDevice].dictionary;
808 midi->fill_base = NULL;
809 midi->fill_offset_ptr = NULL;
810 midi->fill_length = 0;
811 descriptors[outputDevice].internalDescriptor = midi;
813 err = (*midi->dictionary->open)(midi, outputDriverInfo);
816 descriptors[outputDevice].internalDescriptor = NULL;
821 descriptors[outputDevice].pub.opened = TRUE;
827 return pm_errmsg(err);
833 PmInternal *midi = (PmInternal *) stream;
834 PmError err = pmNoError;
839 midi->channel_mask = mask;
841 return pm_errmsg(err);
845 PMEXPORT PmError Pm_SetFilter(PortMidiStream *stream, int32_t filters) {
846 PmInternal *midi = (PmInternal *) stream;
847 PmError err = pmNoError;
852 else if (!descriptors[midi->device_id].pub.opened)
855 midi->filters = filters;
856 return pm_errmsg(err);
860 PMEXPORT PmError
Pm_Close( PortMidiStream *stream ) {
861 PmInternal *midi = (PmInternal *) stream;
862 PmError err = pmNoError;
864 pm_hosterror = FALSE;
869 else if (midi->device_id < 0 || midi->device_id >= pm_descriptor_index)
872 else if (!descriptors[midi->device_id].pub.opened)
875 if (err != pmNoError)
879 err = (*midi->dictionary->close)(midi);
881 descriptors[midi->device_id].internalDescriptor = NULL;
882 descriptors[midi->device_id].pub.opened = FALSE;
883 if (midi->queue) Pm_QueueDestroy(midi->queue);
889 return pm_errmsg(err);
893 PmInternal *midi = (PmInternal *) stream;
894 PmError err = pmNoError;
897 else if (!descriptors[midi->device_id].pub.output)
899 else if (!descriptors[midi->device_id].pub.opened)
902 midi->first_message = TRUE;
906 PMEXPORT PmError
Pm_Abort( PortMidiStream* stream ) {
907 PmInternal *midi = (PmInternal *) stream;
912 else if (!descriptors[midi->device_id].pub.output)
914 else if (!descriptors[midi->device_id].pub.opened)
917 err = (*midi->dictionary->abort)(midi);
919 if (err == pmHostError) {
920 midi->dictionary->host_error(midi, pm_hosterror_text,
921 PM_HOST_ERROR_MSG_LEN);
924 return pm_errmsg(err);
930 #define pm_channel_filtered(status, mask) \
931 ((((status) & 0xF0) != 0xF0) && (!(Pm_Channel((status) & 0x0F) & (mask))))
943 #define pm_realtime_filtered(status, filters) \
944 ((((status) & 0xF0) == 0xF0) && ((1 << ((status) & 0xF)) & (filters)))
967 #define pm_status_filtered(status, filters) ((1 << (16 + ((status) >> 4))) & (filters))
982 static void pm_flush_sysex(PmInternal *midi, PmTimestamp timestamp)
987 if (midi->sysex_message_count == 0)
return;
989 event.message = midi->sysex_message;
990 event.timestamp = timestamp;
992 if (Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) {
993 midi->sysex_in_progress = FALSE;
995 midi->sysex_message_count = 0;
996 midi->sysex_message = 0;
1015 void pm_read_short(PmInternal *midi,
PmEvent *event)
1019 assert(midi != NULL);
1021 status = Pm_MessageStatus(event->message);
1022 if (!pm_status_filtered(status, midi->filters)
1023 && (!is_real_time(status) ||
1024 !pm_realtime_filtered(status, midi->filters))
1025 && !pm_channel_filtered(status, midi->channel_mask)) {
1030 if (midi->sysex_in_progress && (status & MIDI_STATUS_MASK)) {
1035 if (is_real_time(status)) {
1036 midi->sysex_message |=
1037 (status << (8 * midi->sysex_message_count++));
1038 if (midi->sysex_message_count == 4) {
1039 pm_flush_sysex(midi, event->timestamp);
1043 midi->sysex_in_progress = FALSE;
1045 }
else if (Pm_Enqueue(midi->queue, event) == pmBufferOverflow) {
1046 midi->sysex_in_progress = FALSE;
1055 unsigned int pm_read_bytes(PmInternal *midi,
const unsigned char *data,
1056 int len, PmTimestamp timestamp)
1060 event.timestamp = timestamp;
1067 if (len == 0)
return 0;
1068 if (!midi->sysex_in_progress) {
1070 unsigned char byte = data[i++];
1071 if (
byte == MIDI_SYSEX &&
1072 !pm_realtime_filtered(
byte, midi->filters)) {
1073 midi->sysex_in_progress = TRUE;
1076 }
else if (
byte == MIDI_EOX) {
1077 midi->sysex_in_progress = FALSE;
1079 }
else if (
byte & MIDI_STATUS_MASK) {
1089 event.message = byte;
1090 pm_read_short(midi, &event);
1098 while (i < len && midi->sysex_in_progress) {
1099 if (midi->sysex_message_count == 0 && i <= len - 4 &&
1100 ((event.message = (((
PmMessage) data[i]) |
1105 if (Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) {
1106 midi->sysex_in_progress = FALSE;
1112 unsigned char byte = data[i++];
1113 if (is_real_time(
byte) &&
1114 pm_realtime_filtered(
byte, midi->filters)) {
1117 midi->sysex_message |=
1118 (
byte << (8 * midi->sysex_message_count++));
1119 if (
byte == MIDI_EOX) {
1120 midi->sysex_in_progress = FALSE;
1121 pm_flush_sysex(midi, event.timestamp);
1123 }
else if (midi->sysex_message_count == 4) {
1124 pm_flush_sysex(midi, event.timestamp);
PMEXPORT const PmDeviceInfo * Pm_GetDeviceInfo(PmDeviceID id)
Pm_GetDeviceInfo() returns a pointer to a PmDeviceInfo structure referring to the device specified by...
PMEXPORT PmError Pm_OpenInput(PortMidiStream **stream, PmDeviceID inputDevice, void *inputDriverInfo, int32_t bufferSize, PmTimeProcPtr time_proc, void *time_info)
Pm_OpenInput() and Pm_OpenOutput() open devices.
#define PM_FILT_ACTIVE
filter active sensing messages (0xFE):
PMEXPORT PmError Pm_Close(PortMidiStream *stream)
Pm_Close() closes a midi stream, flushing any pending buffers.
PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask)
Pm_SetChannelMask() filters incoming messages based on channel.
PMEXPORT PmError Pm_Abort(PortMidiStream *stream)
Pm_Abort() terminates outgoing messages immediately The caller should immediately close the output po...
int32_t PmMessage
see PmEvent
PmError Pm_Synchronize(PortMidiStream *stream)
Pm_Synchronize() instructs PortMidi to (re)synchronize to the time_proc passed when the stream was op...
PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length)
Pm_Read() retrieves midi data into a buffer, and returns the number of events read.
PMEXPORT PmError Pm_WriteShort(PortMidiStream *stream, PmTimestamp when, PmMessage msg)
Pm_WriteShort() writes a timestamped non-system-exclusive midi message.
PMEXPORT PmError Pm_WriteSysEx(PortMidiStream *stream, PmTimestamp when, unsigned char *msg)
Pm_WriteSysEx() writes a timestamped system-exclusive midi message.
PMEXPORT PmError Pm_Poll(PortMidiStream *stream)
Pm_Poll() tests whether input is available, returning TRUE, FALSE, or an error value.
PMEXPORT PmError Pm_Write(PortMidiStream *stream, PmEvent *buffer, int32_t length)
Pm_Write() writes midi data from a buffer.
const char * name
device name, e.g.
const char * interf
underlying MIDI API, e.g.
int input
true iff input is available
All midi data comes in the form of PmEvent structures.