diff --git a/CoreMIDI4J/Build/Create JNI Header.xml b/CoreMIDI4J/Build/Create JNI Header.xml index 2a9bc7d..7cbe1d5 100755 --- a/CoreMIDI4J/Build/Create JNI Header.xml +++ b/CoreMIDI4J/Build/Create JNI Header.xml @@ -9,9 +9,11 @@ force="yes"> + + diff --git a/CoreMIDI4J/Build/Release/CoreMIDI4J.jar b/CoreMIDI4J/Build/Release/CoreMIDI4J.jar index cc9c4af..538e37e 100644 Binary files a/CoreMIDI4J/Build/Release/CoreMIDI4J.jar and b/CoreMIDI4J/Build/Release/CoreMIDI4J.jar differ diff --git a/CoreMIDI4J/Build/Release/CoreMidi4J.zip b/CoreMIDI4J/Build/Release/CoreMidi4J.zip index 352956f..9e9f794 100644 Binary files a/CoreMIDI4J/Build/Release/CoreMidi4J.zip and b/CoreMIDI4J/Build/Release/CoreMidi4J.zip differ diff --git a/CoreMIDI4J/Build/Release/libCoreMidi4J.dylib b/CoreMIDI4J/Build/Release/libCoreMidi4J.dylib index a208b8b..2b6b84f 100755 Binary files a/CoreMIDI4J/Build/Release/libCoreMidi4J.dylib and b/CoreMIDI4J/Build/Release/libCoreMidi4J.dylib differ diff --git a/CoreMIDI4J/Native/CoreMidi4J/CoreMidi4J.h b/CoreMIDI4J/Native/CoreMidi4J/CoreMidi4J.h index cff5b22..151925f 100644 --- a/CoreMIDI4J/Native/CoreMidi4J/CoreMidi4J.h +++ b/CoreMIDI4J/Native/CoreMidi4J/CoreMidi4J.h @@ -23,6 +23,25 @@ JNIEXPORT jint JNICALL Java_uk_co_xfactorylibrarians_coremidi4j_CoreMidiClient_c JNIEXPORT void JNICALL Java_uk_co_xfactorylibrarians_coremidi4j_CoreMidiClient_disposeClient (JNIEnv *, jobject, jint); +#ifdef __cplusplus +} +#endif +#endif +/* Header for class uk_co_xfactorylibrarians_coremidi4j_CoreMidiDestination */ + +#ifndef _Included_uk_co_xfactorylibrarians_coremidi4j_CoreMidiDestination +#define _Included_uk_co_xfactorylibrarians_coremidi4j_CoreMidiDestination +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: uk_co_xfactorylibrarians_coremidi4j_CoreMidiDestination + * Method: getMicroSecondTime + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_uk_co_xfactorylibrarians_coremidi4j_CoreMidiDestination_getMicroSecondTime + (JNIEnv *, jobject); + #ifdef __cplusplus } #endif @@ -34,8 +53,6 @@ JNIEXPORT void JNICALL Java_uk_co_xfactorylibrarians_coremidi4j_CoreMidiClient_d #ifdef __cplusplus extern "C" { #endif -#undef uk_co_xfactorylibrarians_coremidi4j_CoreMidiDeviceProvider_BUFFER_SIZE -#define uk_co_xfactorylibrarians_coremidi4j_CoreMidiDeviceProvider_BUFFER_SIZE 2048L #undef uk_co_xfactorylibrarians_coremidi4j_CoreMidiDeviceProvider_DEVICE_MAP_SIZE #define uk_co_xfactorylibrarians_coremidi4j_CoreMidiDeviceProvider_DEVICE_MAP_SIZE 20L /* Inaccessible static: midiProperties */ @@ -153,3 +170,22 @@ JNIEXPORT void JNICALL Java_uk_co_xfactorylibrarians_coremidi4j_CoreMidiOutputPo } #endif #endif +/* Header for class uk_co_xfactorylibrarians_coremidi4j_CoreMidiSource */ + +#ifndef _Included_uk_co_xfactorylibrarians_coremidi4j_CoreMidiSource +#define _Included_uk_co_xfactorylibrarians_coremidi4j_CoreMidiSource +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: uk_co_xfactorylibrarians_coremidi4j_CoreMidiSource + * Method: getMicroSecondTime + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_uk_co_xfactorylibrarians_coremidi4j_CoreMidiSource_getMicroSecondTime + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/CoreMIDI4J/Native/CoreMidi4J/CoreMidi4J.xcodeproj/project.pbxproj b/CoreMIDI4J/Native/CoreMidi4J/CoreMidi4J.xcodeproj/project.pbxproj index 1bd2914..edb2463 100644 --- a/CoreMIDI4J/Native/CoreMidi4J/CoreMidi4J.xcodeproj/project.pbxproj +++ b/CoreMIDI4J/Native/CoreMidi4J/CoreMidi4J.xcodeproj/project.pbxproj @@ -8,6 +8,10 @@ /* Begin PBXBuildFile section */ 650999561C303B4100879DFB /* libCoreMidi4J.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 65E46C1D1B89DB8200CC6075 /* libCoreMidi4J.dylib */; }; + 655485591C4172B3002975D6 /* CoreMidiSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 655485571C4172B3002975D6 /* CoreMidiSource.cpp */; }; + 6554855A1C4172B3002975D6 /* CoreMidiSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 655485581C4172B3002975D6 /* CoreMidiSource.h */; }; + 6554855D1C417683002975D6 /* CoreMidiDestination.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6554855B1C417683002975D6 /* CoreMidiDestination.cpp */; }; + 6554855E1C417683002975D6 /* CoreMidiDestination.h in Headers */ = {isa = PBXBuildFile; fileRef = 6554855C1C417683002975D6 /* CoreMidiDestination.h */; }; 65E46C271B89DBAF00CC6075 /* CoreMidi4J.h in Headers */ = {isa = PBXBuildFile; fileRef = 65E46C251B89DBAF00CC6075 /* CoreMidi4J.h */; }; 65E46C291B89DC6700CC6075 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65E46C281B89DC6700CC6075 /* Cocoa.framework */; }; 65E46C2B1B89DC7200CC6075 /* CoreMIDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65E46C2A1B89DC7200CC6075 /* CoreMIDI.framework */; }; @@ -35,6 +39,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 655485571C4172B3002975D6 /* CoreMidiSource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; name = CoreMidiSource.cpp; path = CoreMidi4J.xcodeproj/../CoreMidiSource.cpp; sourceTree = ""; tabWidth = 2; }; + 655485581C4172B3002975D6 /* CoreMidiSource.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = CoreMidiSource.h; path = CoreMidi4J.xcodeproj/../CoreMidiSource.h; sourceTree = ""; tabWidth = 2; }; + 6554855B1C417683002975D6 /* CoreMidiDestination.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; name = CoreMidiDestination.cpp; path = CoreMidi4J.xcodeproj/../CoreMidiDestination.cpp; sourceTree = ""; tabWidth = 2; }; + 6554855C1C417683002975D6 /* CoreMidiDestination.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = CoreMidiDestination.h; path = CoreMidi4J.xcodeproj/../CoreMidiDestination.h; sourceTree = ""; tabWidth = 2; }; 65E46C1D1B89DB8200CC6075 /* libCoreMidi4J.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libCoreMidi4J.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 65E46C251B89DBAF00CC6075 /* CoreMidi4J.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = CoreMidi4J.h; sourceTree = ""; tabWidth = 2; wrapsLines = 0; }; 65E46C281B89DC6700CC6075 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; @@ -72,12 +80,16 @@ 65E46C251B89DBAF00CC6075 /* CoreMidi4J.h */, 65EE982D1C2414BE000DDF1A /* CoreMidiClient.cpp */, 65EE982E1C2414BE000DDF1A /* CoreMidiClient.h */, + 6554855B1C417683002975D6 /* CoreMidiDestination.cpp */, + 6554855C1C417683002975D6 /* CoreMidiDestination.h */, 65EE98261C240C66000DDF1A /* CoreMidiDeviceProvider.cpp */, 65EE98311C241529000DDF1A /* CoreMidiDeviceProvider.h */, 65EE98361C241615000DDF1A /* CoreMidiInputPort.cpp */, 65EE98371C241615000DDF1A /* CoreMidiInputPort.h */, 65EE98321C2415B1000DDF1A /* CoreMidiOutputPort.cpp */, 65EE98331C2415B1000DDF1A /* CoreMidiOutputPort.h */, + 655485571C4172B3002975D6 /* CoreMidiSource.cpp */, + 655485581C4172B3002975D6 /* CoreMidiSource.h */, 65EE982B1C2412C4000DDF1A /* CoreMidi4JUtilities.cpp */, 65EE982A1C240D37000DDF1A /* CoreMidi4JUtilities.h */, 65E46C1E1B89DB8200CC6075 /* Products */, @@ -102,7 +114,9 @@ 65EE98351C2415B1000DDF1A /* CoreMidiOutputPort.h in Headers */, 65E46C271B89DBAF00CC6075 /* CoreMidi4J.h in Headers */, 65EE98391C241615000DDF1A /* CoreMidiInputPort.h in Headers */, + 6554855E1C417683002975D6 /* CoreMidiDestination.h in Headers */, 65EE98301C2414BE000DDF1A /* CoreMidiClient.h in Headers */, + 6554855A1C4172B3002975D6 /* CoreMidiSource.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -165,7 +179,9 @@ files = ( 65EE98281C240C66000DDF1A /* CoreMidiDeviceProvider.cpp in Sources */, 65EE982C1C2412C4000DDF1A /* CoreMidi4JUtilities.cpp in Sources */, + 655485591C4172B3002975D6 /* CoreMidiSource.cpp in Sources */, 65EE982F1C2414BE000DDF1A /* CoreMidiClient.cpp in Sources */, + 6554855D1C417683002975D6 /* CoreMidiDestination.cpp in Sources */, 65EE98341C2415B1000DDF1A /* CoreMidiOutputPort.cpp in Sources */, 65EE98381C241615000DDF1A /* CoreMidiInputPort.cpp in Sources */, ); diff --git a/CoreMIDI4J/Native/CoreMidi4J/CoreMidi4J.xcodeproj/project.xcworkspace/xcuserdata/derek.xcuserdatad/UserInterfaceState.xcuserstate b/CoreMIDI4J/Native/CoreMidi4J/CoreMidi4J.xcodeproj/project.xcworkspace/xcuserdata/derek.xcuserdatad/UserInterfaceState.xcuserstate index 3f32ca9..2f312dc 100644 Binary files a/CoreMIDI4J/Native/CoreMidi4J/CoreMidi4J.xcodeproj/project.xcworkspace/xcuserdata/derek.xcuserdatad/UserInterfaceState.xcuserstate and b/CoreMIDI4J/Native/CoreMidi4J/CoreMidi4J.xcodeproj/project.xcworkspace/xcuserdata/derek.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/CoreMIDI4J/Native/CoreMidi4J/CoreMidiDestination.cpp b/CoreMIDI4J/Native/CoreMidi4J/CoreMidiDestination.cpp new file mode 100644 index 0000000..afac494 --- /dev/null +++ b/CoreMIDI4J/Native/CoreMidi4J/CoreMidiDestination.cpp @@ -0,0 +1,62 @@ +/** + * Title: CoreMIDI4J + * Description: Core MIDI Device Provider for Java on OS X + * Copyright: Copyright (c) 2015-2016 + * Company: x.factory Librarians + * + * @author Derek Cook + * + * CoreMIDI4J is an open source Service Provider Interface for supporting external MIDI devices on MAC OS X + * + * This file is part of the XCODE project that provides the native implementation of CoreMIDI4J + * + * CREDITS - This library uses principles established by OSXMIDI4J, but converted so it operates at the JNI level with no additional libraries required + * + */ + +#include "CoreMidiDestination.h" + +/* + * Obtains the current system time in microseconds. + * + * Class: uk_co_xfactorylibrarians_coremidi4j_CoreMidiDestination + * Method: getMicroSecondTime + * Signature: ()J + * + * @return The current system time in microseconds. + * + */ + +JNIEXPORT jlong JNICALL Java_uk_co_xfactorylibrarians_coremidi4j_CoreMidiDestination_getMicroSecondTime(JNIEnv *, jobject) { + + static mach_timebase_info_data_t sTimebaseInfo; // Will hold conversion factor for timestamps + + // If this is the first time we've run, get the timebase. + // We can use denom == 0 to indicate that sTimebaseInfo is + // uninitialised because it makes no sense to have a zero + // denominator in a fraction. + if ( sTimebaseInfo.denom == 0 ) { + + (void) mach_timebase_info(&sTimebaseInfo); + + } + + uint64_t currentMachTime = mach_absolute_time(); + + // Convert the timestamp from Mach Absolute Time Units to microseconds, + // as expected by Java MIDI. The first step is based on Apple Tech Q&A 1398, + // https://developer.apple.com/library/mac/qa/qa1398/_index.html + // + // Because we are converting to microseconds rather than nanoseconds, we can start + // by dividing by 1000, which should eliminate the risk of overflow described in the + // comment below (copied in from the Q&A), which evidently should not have been an issue + // until 584.9 years after the most recent system boot anyway, according to + // http://lists.apple.com/archives/darwin-kernel/2012/Sep/msg00008.html + // + // Do the maths. We hope that the multiplication doesn't + // overflow; the price you pay for working in fixed point. + uint64_t timestamp = (currentMachTime / 1000) * sTimebaseInfo.numer / sTimebaseInfo.denom; + + return timestamp; + +} diff --git a/CoreMIDI4J/Native/CoreMidi4J/CoreMidiDestination.h b/CoreMIDI4J/Native/CoreMidi4J/CoreMidiDestination.h new file mode 100644 index 0000000..fe0facc --- /dev/null +++ b/CoreMIDI4J/Native/CoreMidi4J/CoreMidiDestination.h @@ -0,0 +1,27 @@ +/** + * Title: CoreMIDI4J + * Description: Core MIDI Device Provider for Java on OS X + * Copyright: Copyright (c) 2015-2016 + * Company: x.factory Librarians + * + * @author Derek Cook + * + * CoreMIDI4J is an open source Service Provider Interface for supporting external MIDI devices on MAC OS X + * + * This file is part of the XCODE project that provides the native implementation of CoreMIDI4J + * + * CREDITS - This library uses principles established by OSXMIDI4J, but converted so it operates at the JNI level with no additional libraries required + * + */ + +#ifndef ____CoreMidiDestination__ +#define ____CoreMidiDestination__ + +#include "CoreMidi4J.h" +#include "CoreMidi4JUtilities.h" + +#include +#include +#include + +#endif /* defined(____CoreMidiDestination__) */ diff --git a/CoreMIDI4J/Native/CoreMidi4J/CoreMidiSource.cpp b/CoreMIDI4J/Native/CoreMidi4J/CoreMidiSource.cpp new file mode 100644 index 0000000..ed7b121 --- /dev/null +++ b/CoreMIDI4J/Native/CoreMidi4J/CoreMidiSource.cpp @@ -0,0 +1,63 @@ +/** + * Title: CoreMIDI4J + * Description: Core MIDI Device Provider for Java on OS X + * Copyright: Copyright (c) 2015-2016 + * Company: x.factory Librarians + * + * @author Derek Cook + * + * CoreMIDI4J is an open source Service Provider Interface for supporting external MIDI devices on MAC OS X + * + * This file is part of the XCODE project that provides the native implementation of CoreMIDI4J + * + * CREDITS - This library uses principles established by OSXMIDI4J, but converted so it operates at the JNI level with no additional libraries required + * + */ + +#include "CoreMidiSource.h" + +/* + * Obtains the current system time in microseconds. + * + * Class: uk_co_xfactorylibrarians_coremidi4j_CoreMidiSource + * Method: getMicroSecondTime + * Signature: ()J + * + * @return The current system time in microseconds. + * + */ + +JNIEXPORT jlong JNICALL Java_uk_co_xfactorylibrarians_coremidi4j_CoreMidiSource_getMicroSecondTime(JNIEnv *, jobject) { + + static mach_timebase_info_data_t sTimebaseInfo; // Will hold conversion factor for timestamps + + // If this is the first time we've run, get the timebase. + // We can use denom == 0 to indicate that sTimebaseInfo is + // uninitialised because it makes no sense to have a zero + // denominator in a fraction. + if ( sTimebaseInfo.denom == 0 ) { + + (void) mach_timebase_info(&sTimebaseInfo); + + } + + uint64_t currentMachTime = mach_absolute_time(); + + // Convert the timestamp from Mach Absolute Time Units to microseconds, + // as expected by Java MIDI. The first step is based on Apple Tech Q&A 1398, + // https://developer.apple.com/library/mac/qa/qa1398/_index.html + // + // Because we are converting to microseconds rather than nanoseconds, we can start + // by dividing by 1000, which should eliminate the risk of overflow described in the + // comment below (copied in from the Q&A), which evidently should not have been an issue + // until 584.9 years after the most recent system boot anyway, according to + // http://lists.apple.com/archives/darwin-kernel/2012/Sep/msg00008.html + // + // Do the maths. We hope that the multiplication doesn't + // overflow; the price you pay for working in fixed point. + uint64_t timestamp = (currentMachTime / 1000) * sTimebaseInfo.numer / sTimebaseInfo.denom; + + return timestamp; + +} + diff --git a/CoreMIDI4J/Native/CoreMidi4J/CoreMidiSource.h b/CoreMIDI4J/Native/CoreMidi4J/CoreMidiSource.h new file mode 100644 index 0000000..8a0528e --- /dev/null +++ b/CoreMIDI4J/Native/CoreMidi4J/CoreMidiSource.h @@ -0,0 +1,27 @@ +/** + * Title: CoreMIDI4J + * Description: Core MIDI Device Provider for Java on OS X + * Copyright: Copyright (c) 2015-2016 + * Company: x.factory Librarians + * + * @author Derek Cook + * + * CoreMIDI4J is an open source Service Provider Interface for supporting external MIDI devices on MAC OS X + * + * This file is part of the XCODE project that provides the native implementation of CoreMIDI4J + * + * CREDITS - This library uses principles established by OSXMIDI4J, but converted so it operates at the JNI level with no additional libraries required + * + */ + +#ifndef ____CoreMidiSource__ +#define ____CoreMidiSource__ + +#include "CoreMidi4J.h" +#include "CoreMidi4JUtilities.h" + +#include +#include +#include + +#endif /* defined(____CoreMidiSource__) */ diff --git a/CoreMIDI4J/src/uk/co/xfactorylibrarians/coremidi4j/CoreMidiDestination.java b/CoreMIDI4J/src/uk/co/xfactorylibrarians/coremidi4j/CoreMidiDestination.java index 473f7c7..c95a98d 100644 --- a/CoreMIDI4J/src/uk/co/xfactorylibrarians/coremidi4j/CoreMidiDestination.java +++ b/CoreMIDI4J/src/uk/co/xfactorylibrarians/coremidi4j/CoreMidiDestination.java @@ -31,6 +31,7 @@ public class CoreMidiDestination implements MidiDevice { private final CoreMidiDeviceInfo info; private boolean isOpen; + private long startTime; // The system time in microseconds when the port was opened /** * Default constructor. @@ -72,6 +73,9 @@ public Info getDeviceInfo() { public void open() throws MidiUnavailableException { isOpen = true; + + // Get the system time in microseconds + startTime = this.getMicroSecondTime(); } @@ -105,20 +109,19 @@ public boolean isOpen() { } /** - * Obtains the current time-stamp of the device, in microseconds. - * This interface does not support time-stamps, so it should always return -1. + * Obtains the time in microseconds that has elapsed since this MIDI Device was opened. + * + * @return the time in microseconds that has elapsed since this MIDI Device was opened. * * @see javax.sound.midi.MidiDevice#getMicrosecondPosition() * - * @return Always -1 as this device does not support timestamps. - * */ @Override public long getMicrosecondPosition() { - // Not supported - return -1; + // Return the elapsed time in Microseconds + return this.getMicroSecondTime() - startTime; } @@ -220,4 +223,28 @@ public List getTransmitters() { } + ////////////////////////////// + ///// JNI Interfaces + ////////////////////////////// + + /** + * Static method for loading the native library + * + */ + + static { + + System.loadLibrary("CoreMIDI4J"); + + } + + /** + * Obtains the current system time in microseconds. + * + * @return The current system time in microseconds. + * + */ + + private native long getMicroSecondTime(); + } diff --git a/CoreMIDI4J/src/uk/co/xfactorylibrarians/coremidi4j/CoreMidiSource.java b/CoreMIDI4J/src/uk/co/xfactorylibrarians/coremidi4j/CoreMidiSource.java index 17e6bda..481796b 100644 --- a/CoreMIDI4J/src/uk/co/xfactorylibrarians/coremidi4j/CoreMidiSource.java +++ b/CoreMIDI4J/src/uk/co/xfactorylibrarians/coremidi4j/CoreMidiSource.java @@ -46,6 +46,7 @@ public class CoreMidiSource implements MidiDevice { private boolean wasFirstByteReceived = false; // Gets set to true when we read first byte of two-byte message. private Vector sysexMessageData; // Accumulates runs of SYSEX data values until we see the end of message. private int sysexMessageLength = 0; // Tracks the total SYSEX data length accumulated. + private long startTime; // The system time in microseconds when the port was opened /** * Default constructor. @@ -100,6 +101,9 @@ public void open() throws MidiUnavailableException { // And connect to it this.input.connectSource(this); isOpen = true; + + // Get the system time in microseconds + startTime = this.getMicroSecondTime(); } catch (CoreMidiException e) { @@ -164,10 +168,9 @@ public boolean isOpen() { } /** - * Obtains the current time-stamp of the device, in microseconds. - * This interface does not support time-stamps, so it should always return -1. + * Obtains the time in microseconds that has elapsed since this MIDI Device was opened. * - * @return Always -1 as this device does not support timestamps. + * @return the time in microseconds that has elapsed since this MIDI Device was opened. * * @see javax.sound.midi.MidiDevice#getMicrosecondPosition() * @@ -176,8 +179,8 @@ public boolean isOpen() { @Override public long getMicrosecondPosition() { - // Not supported - return -1; + // Return the elapsed time in Microseconds + return this.getMicroSecondTime() - startTime; } @@ -755,5 +758,29 @@ private String getHexString(byte[] aByte) { return new String(sbuf).trim(); } + + ////////////////////////////// + ///// JNI Interfaces + ////////////////////////////// + + /** + * Static method for loading the native library + * + */ + + static { + + System.loadLibrary("CoreMIDI4J"); + + } + + /** + * Obtains the current system time in microseconds. + * + * @return The current system time in microseconds. + * + */ + + private native long getMicroSecondTime(); }