#include "stdlib.h" #include "portmidi.h" #include "pminternal.h" #define is_empty(midi) ((midi)->tail == (midi)->head) static int pm_initialized = FALSE; int descriptor_index = 0; descriptor_node descriptors[pm_descriptor_max]; /* pm_add_device -- describe interface/device pair to library * * This is called at initialization time, once for each * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1) * The strings are retained but NOT COPIED, so do not destroy them! * * returns pmInvalidDeviceId if device memory is exceeded * otherwise returns pmNoError * */ PmError pm_add_device(char *interf, char *name, int input, void *descriptor, pm_fns_type dictionary) { if (descriptor_index >= pm_descriptor_max) { return pmInvalidDeviceId; } descriptors[descriptor_index].pub.interf = interf; descriptors[descriptor_index].pub.name = name; descriptors[descriptor_index].pub.input = input; descriptors[descriptor_index].pub.output = !input; descriptors[descriptor_index].descriptor = descriptor; descriptors[descriptor_index].dictionary = dictionary; descriptor_index++; return pmNoError; } PmError Pm_Initialize( void ) { if (!pm_initialized) { PmError err = pm_init(); /* defined by implementation specific file */ if (err) return err; pm_initialized = TRUE; } return pmNoError; } PmError Pm_Terminate( void ) { PmError err = pmNoError; if (pm_initialized) { err = pm_term(); /* defined by implementation specific file */ /* note that even when pm_term() fails, we mark portmidi as not initialized */ pm_initialized = FALSE; } return err; } int Pm_CountDevices( void ) { PmError err = Pm_Initialize(); if (err) return err; return descriptor_index; } const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ) { PmError err = Pm_Initialize(); if (err) return NULL; if (id >= 0 && id < descriptor_index) { return &descriptors[id].pub; } return NULL; } /* failure_fn -- "noop" function pointer */ /**/ PmError failure_fn(PmInternal *midi) { return pmBadPtr; } /* pm_success_fn -- "noop" function pointer */ /**/ PmError pm_success_fn(PmInternal *midi) { return pmNoError; } PmError none_write(PmInternal *midi, PmEvent *buffer, long length) { return length; /* if we return 0, caller might get into a loop */ } PmError pm_fail_fn(PmInternal *midi) { return pmBadPtr; } static PmError none_open(PmInternal *midi, void *driverInfo) { return pmBadPtr; } #define none_abort pm_fail_fn #define none_close pm_fail_fn pm_fns_node pm_none_dictionary = { none_write, none_open, none_abort, none_close }; /* Pm_Read -- read up to length longs from source into buffer */ /* * returns number of longs actually read, or error code When the reader wants data: if overflow_flag: do not get anything empty the buffer (read_ptr = write_ptr) clear overflow_flag return pmBufferOverflow get data return number of messages */ PmError Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length) { PmInternal *midi = (PmInternal *) stream; int n = 0; long head = midi->head; while (head != midi->tail && n < length) { *buffer++ = midi->buffer[head++]; if (head == midi->buffer_len) head = 0; n++; } midi->head = head; if (midi->overflow) { midi->head = midi->tail; midi->overflow = FALSE; return pmBufferOverflow; } return n; } PmError Pm_Poll( PortMidiStream *stream ) { PmInternal *midi = (PmInternal *) stream; return midi->head != midi->tail; } PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length) { PmInternal *midi = (PmInternal *) stream; return (*midi->dictionary->write)(midi, buffer, length); } PmError Pm_WriteShort( PortMidiStream *stream, long when, long msg) { PmEvent event; event.timestamp = when; event.message = msg; return Pm_Write(stream, &event, 1); } PmError Pm_OpenInput( PortMidiStream** stream, PmDeviceID inputDevice, void *inputDriverInfo, long bufferSize, PmTimeProcPtr time_proc, void *time_info, PmStream *thru) { PmInternal *midi; PmError err = Pm_Initialize(); if (err) return err; if (inputDevice < 0 || inputDevice >= descriptor_index) { return pmInvalidDeviceId; } if (!descriptors[inputDevice].pub.input) { return pmInvalidDeviceId; } midi = (PmInternal *) malloc(sizeof(PmInternal)); *stream = midi; if (!midi) return pmInsufficientMemory; midi->head = 0; midi->tail = 0; midi->dictionary = &pm_none_dictionary; midi->overflow = FALSE; midi->flush = FALSE; midi->sysex_in_progress = FALSE; midi->buffer_len = bufferSize; midi->buffer = (PmEvent *) pm_alloc(sizeof(PmEvent) * midi->buffer_len); if (!midi->buffer) return pmInsufficientMemory; midi->latency = 0; midi->thru = thru; midi->time_proc = time_proc; midi->time_info = time_info; midi->device_id = inputDevice; midi->dictionary = descriptors[inputDevice].dictionary; midi->write_flag = FALSE; err = (*midi->dictionary->open)(midi, inputDriverInfo); if (err) { pm_free(midi->buffer); *stream = NULL; } return err; } PmError Pm_OpenOutput( PortMidiStream** stream, PmDeviceID outputDevice, void *outputDriverInfo, long bufferSize, PmTimeProcPtr time_proc, void *time_info, long latency ) { PmInternal *midi; PmError err = Pm_Initialize(); if (err) return err; if (outputDevice < 0 || outputDevice >= descriptor_index) { return pmInvalidDeviceId; } if (!descriptors[outputDevice].pub.output) { return pmInvalidDeviceId; } midi = (PmInternal *) pm_alloc(sizeof(PmInternal)); *stream = midi; if (!midi) return pmInsufficientMemory; midi->head = 0; midi->tail = 0; midi->buffer_len = bufferSize; midi->buffer = NULL; midi->device_id = outputDevice; midi->dictionary = descriptors[outputDevice].dictionary; midi->time_proc = time_proc; midi->time_info = time_info; midi->latency = latency; midi->write_flag = TRUE; err = (*midi->dictionary->open)(midi, outputDriverInfo); if (err) { *stream = NULL; pm_free(midi); // Fixed by Ning Hu, Sep.2001 } return err; } PmError Pm_Abort( PortMidiStream* stream ) { PmInternal *midi = (PmInternal *) stream; return (*midi->dictionary->abort)(midi); } PmError Pm_Close( PortMidiStream *stream ) { PmInternal *midi = (PmInternal *) stream; return (*midi->dictionary->close)(midi); } const char *Pm_GetErrorText( PmError errnum ) { const char *msg; switch(errnum) { case pmNoError: msg = "Success"; break; case pmHostError: msg = "Host error."; break; case pmInvalidDeviceId: msg = "Invalid device ID."; break; case pmInsufficientMemory: msg = "Insufficient memory."; break; case pmBufferTooSmall: msg = "Buffer too small."; break; case pmBadPtr: msg = "Bad pointer."; break; case pmInternalError: msg = "Internal PortMidi Error."; break; default: msg = "Illegal error number."; break; } return msg; } long pm_next_time(PmInternal *midi) { return midi->buffer[midi->head].timestamp; } /* source should not enqueue data if overflow is set */ /* When producer has data to enqueue: if buffer is full: set overflow_flag and flush_flag return else if overflow_flag: return else if flush_flag: if sysex message is in progress: return else: clear flush_flag // fall through to enqueue data enqueue the data */ void pm_enqueue(PmInternal *midi, PmEvent *event) { long tail = midi->tail; midi->buffer[tail++] = *event; if (tail == midi->buffer_len) tail = 0; if (tail == midi->head || midi->overflow) { midi->overflow = TRUE; midi->flush = TRUE; return; } if (midi->flush) { if (midi->sysex_in_progress) return; else midi->flush = FALSE; } midi->tail = tail; } int pm_queue_full(PmInternal *midi) { long tail = midi->tail + 1; if (tail == midi->buffer_len) tail = 0; return tail == midi->head; }