diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..940728e --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +/CMakeCache.txt +/CMakeFiles/ +/CMakeScripts/ +/Debug/ +/Release/ +/cmake_install.cmake +/pm_common/cmake_install.cmake +/pm_common/portmidi.build/ +/pm_java/CMakeFiles/ +/pm_java/CMakeScripts/ +/pm_java/cmake_install.cmake +/pm_java/pmdefaults.xcodeproj/ +/pm_java/pmdefaults/pmdefaults.jar +/pm_java/portmidi.build/ +/pm_test/cmake_install.cmake +/pm_test/portmidi.build/ +/portmidi.build/ +/portmidi.xcodeproj/ + diff --git a/CMakeLists.txt b/CMakeLists.txt index a77a6ff..355a0d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ # 20 Sep 2009 cmake_minimum_required(VERSION 2.8.12) -set(CMAKE_OSX_DEPLOYMENT_TARGET 10.6 CACHE STRING +set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9 CACHE STRING "make for this OS version or higher") if(UNIX) diff --git a/Doxyfile b/Doxyfile index a48a513..95e3708 100644 --- a/Doxyfile +++ b/Doxyfile @@ -51,14 +51,14 @@ PROJECT_BRIEF = "Cross-platform MIDI IO library" # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. -PROJECT_LOGO = /Users/rbd/portmedia/portmidi/portmusic_logo.png +PROJECT_LOGO = portmusic_logo.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = doc +OUTPUT_DIRECTORY = ../github-portmidi-portmidi_docs # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -871,9 +871,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -#INPUT =/Users/rbd/portmedia/portmidi/pm_common /Users/rbd/portmedia/portmidi/porttime /Users/rbd/portmedia/portmidi/pm_linux - -INPUT =pm_common/portmidi.h porttime/porttime.h pm_common/pmutil.h +INPUT =pm_common porttime/porttime.h pm_common/pmutil.h # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -1233,7 +1231,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = html +HTML_OUTPUT = docs # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). diff --git a/pm_common/CMakeLists.txt b/pm_common/CMakeLists.txt index 753e070..f0ec935 100644 --- a/pm_common/CMakeLists.txt +++ b/pm_common/CMakeLists.txt @@ -23,8 +23,8 @@ else(APPLE OR WIN32) endif(APPLE OR WIN32) if(APPLE) - set(PM_OSX_VERSION "10.7" CACHE STRING - "selects PM_OSX_SDK and macosx-version-min C flag") +# set(PM_OSX_VERSION "10.7" CACHE STRING +# "selects PM_OSX_SDK and macosx-version-min C flag") set(PM_OSX_SDK "/Developer/SDKs/MacOSX${PM_OSX_VERSION}.sdk") set(CMAKE_OSX_SYSROOT ${PM_OSC_SDK} CACHE PATH "-isysroot parameter for compiler") diff --git a/pm_java/CMakeLists.txt b/pm_java/CMakeLists.txt index 704510b..946ce8a 100644 --- a/pm_java/CMakeLists.txt +++ b/pm_java/CMakeLists.txt @@ -1,5 +1,7 @@ # pm_java +find_package(Java) +message(STATUS "Java_JAVA_EXECUTABLE is " ${Java_JAVA_EXECUTABLE}) if(BUILD_PMDEFAULTS) set(JPORTMIDICLASS JPortMidi.class JPortMidiException.class JPortMidiApi.class) @@ -34,41 +36,37 @@ if(BUILD_PMDEFAULTS) MAIN_DEPENDENCY pmdefaults/PmDefaults.java DEPENDS pmdefaults/PmDefaultsFrame.java WORKING_DIRECTORY .) - add_custom_target(pmdefaults ALL - DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/pmdefaults.jar) +# add_custom_target(pmdefaults ALL +# DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/pmdefaults.jar) if(UNIX) - add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/pmdefaults.jar - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/pmdefaults - COMMAND cp pmdefaults/portmusic_logo.png . - COMMAND jar cmf pmdefaults/manifest.txt pmdefaults.jar - pmdefaults/*.class portmusic_logo.png jportmidi/*.class - COMMAND mv pmdefaults.jar ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} - COMMAND rm portmusic_logo.png - # create a command to run pmdefaults: - COMMAND cp pmdefaults/pmdefaults ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} - COMMAND chmod +x ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/pmdefaults - MAIN_DEPENDENCY pmdefaults/PmDefaults.class - DEPENDS ${PMDEFAULTS_ALL} - WORKING_DIRECTORY .) + project(pmdefaults NONE) + include(UseJava) + add_jar(pmdefaults + SOURCES pmdefaults/PmDefaults.java pmdefaults/PmDefaultsFrame.java + jportmidi/JPortMidi.java jportmidi/JPortMidiApi.java + jportmidi/JPortMidiException.java + RESOURCES NAMESPACE "." pmdefaults/portmusic_logo.png + MANIFEST pmdefaults/manifest.txt + OUTPUT_DIR pmdefaults) if(BUILD_JAVA_NATIVE_INTERFACE) include(FindJNI) - # message(STATUS "JAVA_JVM_LIB_PATH is " ${JAVA_JVM_LIB_PATH}) - # message(STATUS "JAVA_INCLUDE_PATH is " ${JAVA_INCLUDE_PATH}) - # note: should use JAVA_JVM_LIB_PATH, but it is not set properly - # note: user might need to set JAVA_INCLUDE_PATH manually + # message(STATUS "JAVA_JVM_LIB_PATH is " ${JAVA_JVM_LIB_PATH}) + # message(STATUS "JAVA_INCLUDE_PATH is " ${JAVA_INCLUDE_PATH}) + # note: should use JAVA_JVM_LIB_PATH, but it is not set properly + # note: user might need to set JAVA_INCLUDE_PATH manually # - # this will probably break on BSD and other Unix systems; the fix - # depends on whether FindJNI can find Java or not. If yes, then - # we should try to rely on automatically set JAVA_INCLUDE_PATH and - # JAVA_INCLUDE_PATH2; if no, then we need to make both JAVA_INCLUDE_PATH - # and JAVA_INCLUDE_PATH2 set by user (will need clear documentation - # because JAVA_INCLUDE_PATH2 is pretty obscure) + # this will probably break on BSD and other Unix systems; the fix + # depends on whether FindJNI can find Java or not. If yes, then + # we should try to rely on automatically set JAVA_INCLUDE_PATH and + # JAVA_INCLUDE_PATH2; if no, then we need to make both JAVA_INCLUDE_PATH + # and JAVA_INCLUDE_PATH2 set by user (will need clear documentation + # because JAVA_INCLUDE_PATH2 is pretty obscure) set(JAVA_INCLUDE_PATH ${JAVA_INCLUDE_PATH-UNKNOWN} - CACHE STRING "where to find Java SDK include directory") - set(JAVA_INCLUDE_PATHS ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH}/linux) - # libjvm.so is found relative to JAVA_INCLUDE_PATH: - set(JAVAVM_LIB ${JAVA_INCLUDE_PATH}/../jre/lib/i386/client/libjvm.so) - endif(BUILD_JAVA_NATIVE_INTERFACE) + CACHE STRING "where to find Java SDK include directory") + set(JAVA_INCLUDE_PATHS ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH}/linux) + # libjvm.so is found relative to JAVA_INCLUDE_PATH: + set(JAVAVM_LIB ${JAVA_INCLUDE_PATH}/../jre/lib/i386/client/libjvm.so) + endif(BUILD_JAVA_NATIVE_INTERFACE) if(APPLE) else(APPLE) # install the libraries (Linux only) @@ -92,7 +90,7 @@ if(BUILD_PMDEFAULTS) # /MD is multithread DLL, /MT is multithread if(BUILD_JAVA_NATIVE_INTERFACE) - include(FindJNI) + include(FindJNI) # note: should use JAVA_JVM_LIB_PATH, but it is not set properly set(JAVAVM_LIB ${JAVA_INCLUDE_PATH}/../lib/jvm.lib) @@ -109,10 +107,8 @@ endif(BUILD_PMDEFAULTS) # define the jni library include_directories(${JAVA_INCLUDE_PATHS}) -set(JNISRC ${PROJECT_SOURCE_DIR}/pm_java/pmjni/pmjni.c) +set(JNISRC ${PROJECT_SOURCE_DIR}/pmjni/pmjni.c) -# THIS DID NOT WORK - REMOVE WHEN pmjni on Windows works again: -# # note: PM_LIBSRC is all source for PortMidi, which becomes part of pmjni. # This means there is no dependency on a separate PortMidi library, and # pmjni can access internal PortMidi implementation variables and not @@ -123,8 +119,9 @@ target_link_libraries(pmjni ${PM_NEEDED_LIBS}) set_target_properties(pmjni PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set_target_properties(pmjni PROPERTIES EXECUTABLE_EXTENSION "jnilib" - MACOSX_RPATH ON) + MACOSX_RPATH ON) set(PMJNI_LIBRARY pmjni) +add_dependencies(pmdefaults pmjni) diff --git a/pm_java/README.txt b/pm_java/README.txt index db35fe7..3234151 100644 --- a/pm_java/README.txt +++ b/pm_java/README.txt @@ -52,7 +52,9 @@ In Windows: [java must be executable from the command line] cd portmidi\pm_java -- change to this directory pmdefaults -- runs pmdefaults.bat -In macOS: +In macOS and Linux: + cd portmidi\pm_java\pmdefaults -- change to this directory + ./pmdefaults -- shell script to invoke java [java must be executable from the command line] diff --git a/pm_java/pmdefaults/pmdefaults b/pm_java/pmdefaults/pmdefaults index 79d8b7f..95967b8 100755 --- a/pm_java/pmdefaults/pmdefaults +++ b/pm_java/pmdefaults/pmdefaults @@ -1 +1 @@ -java -Djava.library.path=. -jar pmdefaults.jar > /dev/null +java -Djava.library.path=../../Release:../../Debug -jar pmdefaults.jar > /dev/null diff --git a/porttime/ptmacosx_mach.c b/porttime/ptmacosx_mach.c index 81d8c3b..10c3554 100755 --- a/porttime/ptmacosx_mach.c +++ b/porttime/ptmacosx_mach.c @@ -17,7 +17,12 @@ #ifndef NSEC_PER_MSEC #define NSEC_PER_MSEC 1000000 #endif -#define THREAD_IMPORTANCE 30 +#define THREAD_IMPORTANCE 63 + +// do we ever NOT have Cocoa? +#ifndef HAVE_COCOA +#define HAVE_COCOA 1 +#endif static int time_started_flag = FALSE; static UInt64 start_time; @@ -58,19 +63,62 @@ static void *Pt_CallbackProc(void *p) mach_error("Couldn't set thread precedence policy", error); } + // Most important, set real-time constraints. + + // Define the guaranteed and max fraction of time for the audio thread. + // These "duty cycle" values can range from 0 to 1. A value of 0.5 + // means the scheduler would give half the time to the thread. + // These values have empirically been found to yield good behavior. + // Good means that audio performance is high and other threads won't starve. + const double kGuaranteedAudioDutyCycle = 0.75; + const double kMaxAudioDutyCycle = 0.85; + + // Define constants determining how much time the audio thread can + // use in a given time quantum. All times are in milliseconds. + + // About 128 frames @44.1KHz + const double kTimeQuantum = 2.9; + + // Time guaranteed each quantum. + const double kAudioTimeNeeded = kGuaranteedAudioDutyCycle * kTimeQuantum; + + // Maximum time each quantum. + const double kMaxTimeAllowed = kMaxAudioDutyCycle * kTimeQuantum; + // Get the conversion factor from milliseconds to absolute time + // which is what the time-constraints call needs. + mach_timebase_info_data_t tb_info; + mach_timebase_info(&tb_info); + double ms_to_abs_time = + ((double)tb_info.denom / (double)tb_info.numer) * 1000000; + + thread_time_constraint_policy_data_t time_constraints; + time_constraints.period = (uint32_t)(kTimeQuantum * ms_to_abs_time); + time_constraints.computation = (uint32_t)(kAudioTimeNeeded * ms_to_abs_time); + time_constraints.constraint = (uint32_t)(kMaxTimeAllowed * ms_to_abs_time); + time_constraints.preemptible = 0; + + error = thread_policy_set(mach_thread_self(), + THREAD_TIME_CONSTRAINT_POLICY, + (thread_policy_t)&time_constraints, + THREAD_TIME_CONSTRAINT_POLICY_COUNT); + if (error != KERN_SUCCESS) { + mach_error("Couldn't set thread precedence policy", error); + } + /* to kill a process, just increment the pt_callback_proc_id */ - /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, parameters->id); */ + /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, + parameters->id); */ while (pt_callback_proc_id == parameters->id) { /* wait for a multiple of resolution ms */ UInt64 wait_time; int delay = mytime++ * parameters->resolution - Pt_Time(); - PtTimestamp timestamp; + PtTimestamp timestamp; if (delay < 0) delay = 0; wait_time = AudioConvertNanosToHostTime((UInt64)delay * NSEC_PER_MSEC); wait_time += AudioGetCurrentHostTime(); - error = mach_wait_until(wait_time); - timestamp = Pt_Time(); + mach_wait_until(wait_time); + timestamp = Pt_Time(); (*(parameters->callback))(timestamp, parameters->userData); } free(parameters); @@ -93,7 +141,26 @@ PtError Pt_Start(int resolution, PtCallback *callback, void *userData) parms->resolution = resolution; parms->callback = callback; parms->userData = userData; + +#ifdef HAVE_COCOA + pthread_attr_t qosAttribute; + pthread_attr_init(&qosAttribute); + pthread_attr_set_qos_class_np(&qosAttribute, + QOS_CLASS_USER_INTERACTIVE, 0); + + res = pthread_create(&pt_thread_pid, &qosAttribute, Pt_CallbackProc, + parms); +#else res = pthread_create(&pt_thread_pid, NULL, Pt_CallbackProc, parms); +#endif + + struct sched_param sp; + memset(&sp, 0, sizeof(struct sched_param)); + sp.sched_priority = sched_get_priority_max(SCHED_RR); + if (pthread_setschedparam(pthread_self(), SCHED_RR, &sp) == -1) { + return ptHostError; + } + if (res != 0) return ptHostError; } @@ -102,7 +169,7 @@ PtError Pt_Start(int resolution, PtCallback *callback, void *userData) } -PtError Pt_Stop() +PtError Pt_Stop(void) { /* printf("Pt_Stop called\n"); */ pt_callback_proc_id++; @@ -112,13 +179,13 @@ PtError Pt_Stop() } -int Pt_Started() +int Pt_Started(void) { return time_started_flag; } -PtTimestamp Pt_Time() +PtTimestamp Pt_Time(void) { UInt64 clock_time, nsec_time; clock_time = AudioGetCurrentHostTime() - start_time;