/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
**
** Overview:
** Implementation of the functions used for both MIDI in and MIDI out.
**
** Java package com.sun.media.sound defines the AbstractMidiDevice class
** which encapsulates functionalities shared by both MidiInDevice and
** MidiOutDevice classes in the same package.
**
** The Java layer classes MidiInDevice and MidiOutDevice in turn map to
** the MIDIEndpointRef data type in the CoreMIDI framework, which
** represents a source or destination for a standard 16-channel MIDI data
** stream.
*/
/*****************************************************************************/
//#define USE_ERROR
//#define USE_TRACE
#include "PLATFORM_API_MacOSX_MidiUtils.h"
#include <pthread.h>
#include <assert.h>
// Constant character string definitions of CoreMIDI's corresponding error codes.
static const char* strMIDIInvalidClient =
"An invalid MIDIClientRef was passed.";
static const char* strMIDIInvalidPort =
"An invalid MIDIPortRef was passed.";
static const char* strMIDIWrongEndpointType =
"A source endpoint was passed to a function expecting a destination, or vice versa.";
static const char* strMIDINoConnection =
"Attempt to close a non-existant connection.";
static const char* strMIDIUnknownEndpoint =
"An invalid MIDIEndpointRef was passed.";
static const char* strMIDIUnknownProperty =
"Attempt to query a property not set on the object.";
static const char* strMIDIWrongPropertyType =
"Attempt to set a property with a value not of the correct type.";
static const char* strMIDINoCurrentSetup =
"Internal error; there is no current MIDI setup object.";
static const char* strMIDIMessageSendErr =
"Communication with MIDIServer failed.";
static const char* strMIDIServerStartErr =
"Unable to start MIDIServer.";
static const char* strMIDISetupFormatErr =
"Unable to read the saved state.";
static const char* strMIDIWrongThread =
"A driver is calling a non-I/O function in the server from a thread other than"
"the server's main thread.";
static const char* strMIDIObjectNotFound =
"The requested object does not exist.";
static const char* strMIDIIDNotUnique =
"Attempt to set a non-unique kMIDIPropertyUniqueID on an object.";
/*
@enum Error Constants
@abstract The error constants unique to Core MIDI.
@discussion These are the error constants that are unique to Core MIDI. Note that Core MIDI
functions may return other codes that are not listed here.
*/
const char* strerr;
switch (err) {
case kMIDIInvalidClient:
break;
case kMIDIInvalidPort:
break;
case kMIDIWrongEndpointType:
break;
case kMIDINoConnection:
break;
case kMIDIUnknownEndpoint:
break;
case kMIDIUnknownProperty:
break;
case kMIDIWrongPropertyType:
break;
case kMIDINoCurrentSetup:
break;
case kMIDIMessageSendErr:
break;
case kMIDIServerStartErr:
break;
case kMIDISetupFormatErr:
break;
case kMIDIWrongThread:
break;
case kMIDIObjectNotFound:
break;
case kMIDIIDNotUnique:
break;
default:
strerr = "Unknown error.";
break;
}
return strerr;
}
return midi_strerror(err);
}
#ifdef USE_ERROR
const char* s = MIDI_Utils_GetErrorMsg(err);
if (s != NULL) {
}
#endif
}
// Note direction is either MIDI_IN or MIDI_OUT.
int num_endpoints;
//fprintf(stdout, "MIDIGetNumberOfSources() returns %d\n", num_endpoints);
//printf(stdout, "MIDIGetNumberOfDestinations() returns %d\n", num_endpoints);
} else {
num_endpoints = 0;
}
return (INT32) num_endpoints;
}
// Wraps calls to CFStringGetCStringPtr and CFStringGetCString to make sure
// we extract the c characters into the buffer and null-terminate it.
static void CFStringExtractCString(CFStringRef cfs, char* buffer, UINT32 bufferSize, CFStringEncoding encoding) {
if (ptr) {
} else {
// There's an error in conversion, make sure we null-terminate the buffer.
}
}
}
//
// @see com.sun.media.sound.AbstractMidiDeviceProvider.getDeviceInfo().
static int getEndpointProperty(int direction, INT32 deviceID, char *buffer, int bufferLength, CFStringRef propertyID) {
if (deviceID < 0) {
return MIDI_INVALID_DEVICEID;
}
} else {
return MIDI_INVALID_ARGUMENT;
}
if (!endpoint) {
return MIDI_INVALID_DEVICEID;
}
if (propertyID == kMIDIPropertyDriverVersion) {
"%d",
(int) driverVersion);
}
else {
}
return MIDI_ERROR_NONE;
}
// A simple utility which encapsulates CoreAudio's HostTime APIs.
// It returns the current host time in nanoseconds which when subtracted from
// a previous getCurrentTimeInNanos() result produces the delta in nanos.
return nanos;
}
}
}
INT32 MIDI_Utils_GetDeviceDescription(int direction, INT32 deviceID, char *name, UINT32 bufferLength) {
}
}
// Each MIDIPacket can contain more than one midi messages.
// This function processes the packet and adds the messages to the specified message queue.
pendingMessageStatus = 0;
while (length--) {
if (byte >= 0xF8) {
// Each RealTime Category message (ie, Status of 0xF8 to 0xFF) consists of only 1 byte, the Status.
// Except that 0xFD is an invalid status code.
//
// 0xF8 -> Midi clock
// 0xF9 -> Midi tick
// 0xFA -> Midi start
// 0xFB -> Midi continue
// 0xFC -> Midi stop
// 0xFE -> Active sense
// 0xFF -> Reset
if (byte == 0xFD) {
} else {
pendingDataLength = 0;
}
} else {
if (byte < 0x80) {
// Not a status byte -- check our history.
if (handle->readingSysExData) {
} else if (pendingDataIndex < pendingDataLength) {
if (pendingDataIndex == pendingDataLength) {
// This message is now done -- do the final processing.
if (pendingDataLength == 2) {
} else if (pendingDataLength == 1) {
} else {
}
pendingDataLength = 0;
}
} else {
// Skip this byte -- it is invalid.
}
} else {
// We have reached the end of system exclusive message -- send it finally.
0, // Don't care, windowish porting only.
TRUE);
}
pendingDataLength = 0;
pendingDataIndex = 0;
switch (byte & 0xF0) {
case 0x80: // Note off
case 0x90: // Note on
case 0xA0: // Aftertouch
case 0xB0: // Controller
case 0xE0: // Pitch wheel
pendingDataLength = 2;
break;
case 0xC0: // Program change
case 0xD0: // Channel pressure
pendingDataLength = 1;
break;
case 0xF0: {
// System common message
switch (byte) {
case 0xF0:
// System exclusive
// Allocates a CFMutableData reference to accumulate the SysEx data until EOX (0xF7) is reached.
break;
case 0xF7:
// System exclusive ends--already handled above.
// But if this is showing up outside of sysex, it's invalid.
break;
case 0xF1: // MTC quarter frame message
case 0xF3: // Song select
pendingDataLength = 1;
break;
case 0xF2: // Song position pointer
pendingDataLength = 2;
break;
case 0xF6: // Tune request
pendingDataLength = 0;
break;
default:
// Invalid message
break;
}
break;
}
default:
// This can't happen, but handle it anyway.
break;
}
}
}
if (byteIsInvalid) continue;
// If the byte is valid and pendingDataLength is 0, we are ready to send the message.
if (pendingDataLength == 0) {
MIDI_QueueAddShort(handle->h.queue, packedMsg, (INT64) (AudioConvertHostTimeToNanos(ts) + 500) / 1000, TRUE);
}
}
}
unsigned int i;
for (i = 0; i < packetList->numPackets; ++i) {
}
// Notify the waiting thread that there's data available.
if (handle) {
}
}
static void midiInit() {
if (client) {
return;
}
// This just creates an input port through which the client may receive
// incoming MIDI messages from any MIDI source.
Exit:
const char* s = MIDI_Utils_GetErrorMsg(err);
if (s != NULL) {
printf("%s\n", s);
}
}
}
int num_msgs, int num_long_msgs,
{
midiInit();
TRACE0("MIDI_Utils_OpenDevice\n");
if (!(*handle)) {
ERROR0("ERROR: MIDI_Utils_OpenDevice: out of memory\n");
return MIDI_OUT_OF_MEMORY;
}
// get the device's endpoint.
// Create queue and the pthread condition variable.
ERROR0("< ERROR: MIDI_IN_OpenDevice: could not create queue or condition variable\n");
return MIDI_OUT_OF_MEMORY;
}
}
if (!endpoint) {
// An error occurred.
return MIDI_INVALID_DEVICEID;
}
TRACE0("MIDI_Utils_OpenDevice: succeeded\n");
return err;
}
TRACE0("> MIDI_Utils_CloseDevice\n");
if (!handle) {
ERROR0("< ERROR: MIDI_Utils_CloseDevice: handle is NULL\n");
return MIDI_INVALID_HANDLE;
}
if (!handle->h.deviceHandle) {
ERROR0("< ERROR: MIDI_Utils_CloseDevice: native handle is NULL\n");
return MIDI_INVALID_HANDLE;
}
if (midiIn) {
}
if (handle->h.platformData) {
}
}
TRACE0("< MIDI_Utils_CloseDevice: succeeded\n");
return err;
}
ERROR0("ERROR: MIDI_Utils_StartDevice: handle or native is NULL\n");
return MIDI_INVALID_HANDLE;
}
// Clears all the events from the queue.
/* set the flag that we can now receive messages */
// The handle->h.platformData field contains the (pthread_cond_t*)
// associated with the source of the MIDI input stream, and is
// used in the CoreMIDI's callback to signal the arrival of new
// data.
//
// Similarly, handle->h.queue is used in the CoreMDID's callback
// to dispatch the incoming messages to the appropriate queue.
//
err = MIDIPortConnectSource(inPort, (MIDIEndpointRef) (intptr_t) (handle->h.deviceHandle), (void*) handle);
// Unschedules previous-sent packets.
}
}
return MIDI_SUCCESS; /* don't fail */
}
ERROR0("ERROR: MIDI_Utils_StopDevice: handle or native handle is NULL\n");
return MIDI_INVALID_HANDLE;
}
/* set the flag that we don't want to receive messages anymore */
// Unschedules previously-sent packets.
}
}
return MIDI_SUCCESS;
}
ERROR0("ERROR: MIDI_Utils_GetTimeStamp: handle or native handle is NULL\n");
}
}
/***************************************************************************/
/* Condition Variable Support for Mac OS X Port */
/* */
/* This works with the Native Locking Support defined below. We are using */
/* POSIX pthread_cond_t/pthread_mutex_t to do locking and synchronization. */
/* */
/* For MidiDeviceHandle* handle, the mutex reference is stored as handle-> */
/* queue->lock while the condition variabale reference is stored as handle */
/* ->platformData. */
/***************************************************************************/
// Called from Midi_Utils_Opendevice(...) to create a condition variable
// used to synchronize between the receive thread created by the CoreMIDI
// and the Java-initiated MidiInDevice run loop.
void* MIDI_CreateConditionVariable() {
return (void*) cond;
}
sched_yield();
}
return;
}
// Called from MIDI_IN_GetMessage(...) to wait for MIDI messages to become
// available via delivery from the CoreMIDI receive thread
}
return;
}
// Called from midiReadProc(...) to notify the waiting thread to unblock on
// the condition variable.
if (cond) {
}
return;
}
/**************************************************************************/
/* Native Locking Support */
/* */
/* utility functions for platform midi support where the section of code */
/* for MessageQueue implementation calls out to these functions. */
/**************************************************************************/
void* MIDI_CreateLock() {
TRACE0("MIDI_CreateLock\n");
return (void *)lock;
}
if (lock) {
TRACE0("MIDI_DestroyLock\n");
}
}
if (lock) {
}
}
if (lock) {
}
}
#endif // USE_PLATFORM_MIDI_IN || USE_PLATFORM_MIDI_OUT