summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/portmidi/pm_common
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/portmidi/pm_common')
-rw-r--r--3rdparty/portmidi/pm_common/CMakeLists.txt230
-rw-r--r--3rdparty/portmidi/pm_common/pminternal.h59
-rw-r--r--3rdparty/portmidi/pm_common/pmjni.vcproj161
-rw-r--r--3rdparty/portmidi/pm_common/pmutil.c2
-rw-r--r--3rdparty/portmidi/pm_common/pmutil.h198
-rw-r--r--3rdparty/portmidi/pm_common/portmidi-dynamic.vcproj179
-rw-r--r--3rdparty/portmidi/pm_common/portmidi-static.vcproj141
-rw-r--r--3rdparty/portmidi/pm_common/portmidi.c720
-rw-r--r--3rdparty/portmidi/pm_common/portmidi.h908
9 files changed, 1385 insertions, 1213 deletions
diff --git a/3rdparty/portmidi/pm_common/CMakeLists.txt b/3rdparty/portmidi/pm_common/CMakeLists.txt
index e1710474ae3..062887bc111 100644
--- a/3rdparty/portmidi/pm_common/CMakeLists.txt
+++ b/3rdparty/portmidi/pm_common/CMakeLists.txt
@@ -1,32 +1,69 @@
-# pm_common
+# pm_common/CMakeLists.txt -- how to build portmidi library
+
+# creates the portmidi library
+# exports PM_NEEDED_LIBS to parent. It seems that PM_NEEDED_LIBS for
+# Linux should include Thread::Thread and ALSA::ALSA, but these
+# are not visible in other CMake files, even though the portmidi
+# target is. Therefore, Thread::Thread is replaced by
+# CMAKE_THREAD_LIBS_INIT and ALSA::ALSA is replaced by ALSA_LIBRARIES.
+# Is there a better way to do this? Maybe this whole file should be
+# at the parent level.
+
+# Support alternative name for static libraries to avoid confusion.
+# (In particular, Xcode has automatically converted portmidi.a to
+# portmidi.dylib without warning, so using portmidi-static.a eliminates
+# this possibility, but default for all libs is "portmidi"):
+set(PM_STATIC_LIB_NAME "portmidi" CACHE STRING
+ "For static builds, the PortMidi library name, e.g. portmidi-static.
+ Default is portmidi")
+set(PM_ACTUAL_LIB_NAME "portmidi")
+if(NOT BUILD_SHARED_LIBS)
+ set(PM_ACTUAL_LIB_NAME ${PM_STATIC_LIB_NAME})
+endif()
# set the build directory for libportmidi.a to be in portmidi, not in
-# portmidi/pm_common
+# portmidi/pm_common. Must be done here BEFORE add_library below.
if(APPLE OR WIN32)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
# set the build directory for .dylib libraries
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
-
- # the first time CMake configures, save off CMake's built-in flags
- if(NOT DEFAULT_DEBUG_FLAGS)
- set(DEFAULT_DEBUG_FLAGS ${CMAKE_C_FLAGS_DEBUG} CACHE
- STRING "CMake's default debug flags" FORCE)
- set(DEFAULT_RELEASE_FLAGS ${CMAKE_C_FLAGS_RELEASE} CACHE
- STRING "CMake's default release flags" FORCE)
- else(NOT DEFAULT_DEBUG_FLAGS)
- message(STATUS "DEFAULT_DEBUG_FLAGS not nil: " ${DEFAULT_DEBUG_FLAGS})
- endif(NOT DEFAULT_DEBUG_FLAGS)
-else(APPLE OR WIN32)
- set(LINUX_FLAGS "-DPMALSA")
endif(APPLE OR WIN32)
-if(APPLE)
- set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.5.sdk CACHE
- PATH "-isysroot parameter for compiler" FORCE)
- set(CMAKE_C_FLAGS "-mmacosx-version-min=10.5" CACHE
- STRING "needed in conjunction with CMAKE_OSX_SYSROOT" FORCE)
-endif(APPLE)
+# we need full paths to sources because they are shared with other targets
+# (in particular pmjni). Set PMDIR to the top-level portmidi directory:
+get_filename_component(PMDIR ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
+set(PM_LIB_PUBLIC_SRC ${PMDIR}/pm_common/portmidi.c
+ ${PMDIR}/pm_common/pmutil.c
+ ${PMDIR}/porttime/porttime.c)
+add_library(portmidi ${PM_LIB_PUBLIC_SRC})
+
+# MSVCRT_DLL is "DLL" for shared runtime library, and "" for static:
+set_target_properties(portmidi PROPERTIES
+ VERSION ${LIBRARY_VERSION}
+ SOVERSION ${LIBRARY_SOVERSION}
+ OUTPUT_NAME "${PM_ACTUAL_LIB_NAME}"
+ MSVC_RUNTIME_LIBRARY
+ "MultiThreaded$<$<CONFIG:Debug>:Debug>${MSVCRT_DLL}"
+ WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
+target_include_directories(portmidi PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+
+
+option(PM_CHECK_ERRORS
+"Insert a check for error return values at the end of each PortMidi function.
+If an error is encountered, a text message is printed using printf(), the user
+is asked to type ENTER, and then exit(-1) is called to clean up and terminate
+the program.
+
+You should not use PM_CHECK_ERRORS if printf() does not work (e.g. this is not
+a console application under Windows, or there is no visible console on some
+other OS), and you should not use PM_CHECK_ERRORS if you intend to recover
+from errors rather than abruptly terminate the program." OFF)
+if(PM_CHECK_ERRORS)
+ target_compile_definitions(portmidi PRIVATE PM_CHECK_ERRORS)
+endif(PM_CHECK_ERRORS)
macro(prepend_path RESULT PATH)
set(${RESULT})
@@ -35,94 +72,79 @@ macro(prepend_path RESULT PATH)
endforeach(FILE)
endmacro(prepend_path)
-set(CMAKE_C_FLAGS_DEBUG
- "${DEFAULT_DEBUG_FLAGS} -DPM_CHECK_ERRORS=1 -DDEBUG ${LINUX_FLAGS}"
- CACHE STRING "enable extra checks for debugging" FORCE)
-
-set(CMAKE_C_FLAGS_RELEASE "${DEFAULT_RELEASE_FLAGS} ${LINUX_FLAGS}"
- CACHE STRING "flags for release version" FORCE)
+# UNIX needs pthread library
+if(NOT WIN32)
+ set(THREADS_PREFER_PTHREAD_FLAG ON)
+ find_package(Threads REQUIRED)
+endif()
# first include the appropriate system-dependent file:
-if(UNIX)
- # add the -g switch for Linux and Mac OS X (not used in Win32)
- set (CMAKE_C_FLAGS_DEBUG "-g ${CMAKE_C_FLAGS_DEBUG}"
- CACHE STRING "enable extra checks for debugging" FORCE)
- if(APPLE)
- set(MACSRC pmmacosxcm pmmac readbinaryplist finddefault)
- prepend_path(LIBSRC ../pm_mac/ ${MACSRC})
- list(APPEND LIBSRC ../porttime/ptmacosx_mach)
-
- include_directories(${CMAKE_OSX_SYSROOT}/Developer/Headers/FlatCarbon)
- set(FRAMEWORK_PATH ${CMAKE_OSX_SYSROOT}/System/Library/Frameworks)
- set(COREAUDIO_LIB "${FRAMEWORK_PATH}/CoreAudio.framework")
- set(COREFOUNDATION_LIB "${FRAMEWORK_PATH}/CoreFoundation.framework")
- set(COREMIDI_LIB "${FRAMEWORK_PATH}/CoreMIDI.framework")
- set(CORESERVICES_LIB "${FRAMEWORK_PATH}/CoreServices.framework")
- set(PM_NEEDED_LIBS ${COREAUDIO_LIB} ${COREFOUNDATION_LIB}
- ${COREMIDI_LIB} ${CORESERVICES_LIB}
- CACHE INTERNAL "")
-
- set(JAVAVM_LIB "${FRAMEWORK_PATH}/JavaVM.framework")
- set(JAVA_INCLUDE_PATHS ${JAVAVM_LIB}/Headers)
- message(STATUS "SYSROOT: " ${CMAKE_OSX_SYSROOT})
- else(APPLE)
- # LINUX settings...
- include(FindJNI)
- message(STATUS "JAVA_JVM_LIB_PATH is " ${JAVA_JVM_LIB_PATH})
- message(STATUS "JAVA_INCLUDE_PATH is " ${JAVA_INCLUDE_PATH})
- message(STATUS "JAVA_INCLUDE_PATH2 is " ${JAVA_INCLUDE_PATH2})
- message(STATUS "JAVA_JVM_LIBRARY is " ${JAVA_JVM_LIBRARY})
- set(JAVA_INCLUDE_PATHS ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2})
- # libjvm.so is found relative to JAVA_INCLUDE_PATH:
- set(JAVAVM_LIB ${JAVA_JVM_LIBRARY}/libjvm.so)
-
- set(LINUXSRC pmlinuxalsa pmlinux finddefault)
- prepend_path(LIBSRC ../pm_linux/ ${LINUXSRC})
- list(APPEND LIBSRC ../porttime/ptlinux)
-
- set(PM_NEEDED_LIBS pthread asound)
- endif(APPLE)
-else(UNIX)
- if(WIN32)
+if(UNIX AND APPLE)
+ set(Threads::Threads "" PARENT_SCOPE)
+ find_library(COREAUDIO_LIBRARY CoreAudio REQUIRED)
+ find_library(COREFOUNDATION_LIBRARY CoreFoundation REQUIRED)
+ find_library(COREMIDI_LIBRARY CoreMIDI REQUIRED)
+ find_library(CORESERVICES_LIBRARY CoreServices REQUIRED)
+ set(PM_LIB_PRIVATE_SRC
+ ${PMDIR}/porttime/ptmacosx_mach.c
+ ${PMDIR}/pm_mac/pmmac.c
+ ${PMDIR}/pm_mac/pmmacosxcm.c
+ ${PMDIR}/pm_mac/finddefault.c
+ ${PMDIR}/pm_mac/readbinaryplist.c)
+ set(PM_NEEDED_LIBS ${CMAKE_THREAD_LIBS_INIT} ${COREAUDIO_LIBRARY}
+ ${COREFOUNDATION_LIBRARY} ${COREMIDI_LIBRARY} ${CORESERVICES_LIBRARY}
+ PARENT_SCOPE)
+ target_link_libraries(portmidi PRIVATE Threads::Threads ${COREAUDIO_LIBRARY}
+ ${COREFOUNDATION_LIBRARY} ${COREMIDI_LIBRARY} ${CORESERVICES_LIBRARY})
+ # set to CMake default; is this right?:
+ set_target_properties(portmidi PROPERTIES MACOSX_RPATH ON)
+elseif(HAIKU)
+ set(PM_LIB_PRIVATE_SRC
+ ${PMDIR}/porttime/pthaiku.cpp
+ ${PMDIR}/pm_haiku/pmhaiku.cpp
+ ${PMDIR}/pm_linux/finddefault.c)
+ set(PM_NEEDED_LIBS be midi midi2 PARENT_SCOPE)
+ target_link_libraries(portmidi PRIVATE be midi midi2)
+elseif(UNIX)
+ target_compile_definitions(portmidi PRIVATE ${LINUX_FLAGS})
+ set(PM_LIB_PRIVATE_SRC
+ ${PMDIR}/porttime/ptlinux.c
+ ${PMDIR}/pm_linux/pmlinux.c
+ ${PMDIR}/pm_linux/pmlinuxnull.c
+ ${PMDIR}/pm_linux/finddefault.c)
+ if(${LINUX_DEFINES} MATCHES ".*PMALSA.*")
+ # Note that ALSA is not required if PMNULL is defined -- PortMidi will then
+ # compile without ALSA and report no MIDI devices. Later, PMSNDIO or PMJACK
+ # might be additional options.
+ find_package(ALSA REQUIRED)
+ list(APPEND PM_LIB_PRIVATE_SRC ${PMDIR}/pm_linux/pmlinuxalsa.c)
+ set(PM_NEEDED_LIBS ${CMAKE_THREAD_LIBS_INIT} ${ALSA_LIBRARIES} PARENT_SCOPE)
+ target_link_libraries(portmidi PRIVATE Threads::Threads ALSA::ALSA)
+ set(PKGCONFIG_REQUIRES_PRIVATE "alsa" PARENT_SCOPE)
+ else()
+ message(WARNING "No PMALSA, so PortMidi will not use ALSA, "
+ "and will not find or open MIDI devices.")
+ set(PM_NEEDED_LIBS ${CMAKE_THREAD_LIBS_INIT} PARENT_SCOPE)
+ target_link_libraries(portmidi PRIVATE Threads::Threads)
+ endif()
+elseif(WIN32)
+ set(PM_LIB_PRIVATE_SRC
+ ${PMDIR}/porttime/ptwinmm.c
+ ${PMDIR}/pm_win/pmwin.c
+ ${PMDIR}/pm_win/pmwinmm.c)
+ set(PM_NEEDED_LIBS winmm PARENT_SCOPE)
+ target_link_libraries(portmidi PRIVATE winmm)
+# if(NOT BUILD_SHARED_LIBS AND PM_USE_STATIC_RUNTIME)
# /MDd is multithread debug DLL, /MTd is multithread debug
# /MD is multithread DLL, /MT is multithread. Change to static:
- include(../pm_win/static.cmake)
-
- include(FindJNI)
-
- set(JAVA_INCLUDE_PATHS ${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2})
- # message(STATUS "JAVA_INCLUDE_PATHS: " ${JAVA_INCLUDE_PATHS})
-
- set(WINSRC pmwin pmwinmm)
- prepend_path(LIBSRC ../pm_win/ ${WINSRC})
- list(APPEND LIBSRC ../porttime/ptwinmm)
- set(PM_NEEDED_LIBS winmm.lib)
- endif(WIN32)
-endif(UNIX)
-set(JNI_EXTRA_LIBS ${PM_NEEDED_LIBS} ${JAVA_JVM_LIBRARY})
-
-# this completes the list of library sources by adding shared code
-list(APPEND LIBSRC pmutil portmidi)
-
-# now add the shared files to make the complete list of library sources
-add_library(portmidi-static ${LIBSRC})
-set_target_properties(portmidi-static PROPERTIES OUTPUT_NAME "portmidi_s")
-target_link_libraries(portmidi-static ${PM_NEEDED_LIBS})
+# include(../pm_win/static.cmake)
+# endif()
+else()
+ message(FATAL_ERROR "Operating system not supported.")
+endif()
-# define the jni library
-include_directories(${JAVA_INCLUDE_PATHS})
+set(PM_LIB_PUBLIC_SRC ${PM_LIB_PUBLIC_SRC} PARENT_SCOPE) # export to parent
+set(PM_LIB_PRIVATE_SRC ${PM_LIB_PRIVATE_SRC} PARENT_SCOPE) # export to parent
-set(JNISRC ${LIBSRC} ../pm_java/pmjni/pmjni.c)
-add_library(pmjni SHARED ${JNISRC})
-target_link_libraries(pmjni ${JNI_EXTRA_LIBS})
-set_target_properties(pmjni PROPERTIES EXECUTABLE_EXTENSION "jnilib")
+target_sources(portmidi PRIVATE ${PM_LIB_PRIVATE_SRC})
-# install the libraries (Linux and Mac OS X command line)
-if(UNIX)
- INSTALL(TARGETS portmidi-static pmjni
- LIBRARY DESTINATION /usr/local/lib
- ARCHIVE DESTINATION /usr/local/lib)
-# .h files installed by pm_dylib/CMakeLists.txt, so don't need them here
-# INSTALL(FILES portmidi.h ../porttime/porttime.h
-# DESTINATION /usr/local/include)
-endif(UNIX)
diff --git a/3rdparty/portmidi/pm_common/pminternal.h b/3rdparty/portmidi/pm_common/pminternal.h
index 6b6242026dd..80efbb07f64 100644
--- a/3rdparty/portmidi/pm_common/pminternal.h
+++ b/3rdparty/portmidi/pm_common/pminternal.h
@@ -1,4 +1,4 @@
-/* pminternal.h -- header for interface implementations */
+/** @file pminternal.h header for PortMidi implementations */
/* this file is included by files that implement library internals */
/* Here is a guide to implementers:
@@ -17,6 +17,8 @@
assumptions about pm_fns_type functions are given below.
*/
+/** @cond INTERNAL - add INTERNAL to Doxygen ENABLED_SECTIONS to include */
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -27,8 +29,10 @@ extern int pm_initialized; /* see note in portmidi.c */
void *pm_alloc(size_t s);
void pm_free(void *ptr);
-/* if an error occurs while opening or closing a midi stream, set these: */
-extern int pm_hosterror;
+/* if a host error (an error reported by the host MIDI API that is not
+ * mapped to a PortMidi error code) occurs in a synchronous operation
+ * (i.e., not in a callback from another thread) set these: */
+extern int pm_hosterror; /* boolean */
extern char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN];
struct pm_internal_struct;
@@ -51,31 +55,30 @@ typedef PmTimestamp (*pm_synchronize_fn)(struct pm_internal_struct *midi);
of the open fails */
typedef PmError (*pm_open_fn)(struct pm_internal_struct *midi,
void *driverInfo);
+typedef PmError (*pm_create_fn)(int is_input, const char *name,
+ void *driverInfo);
+typedef PmError (*pm_delete_fn)(PmDeviceID id);
typedef PmError (*pm_abort_fn)(struct pm_internal_struct *midi);
/* pm_close_fn should clean up all memory and close the device if any
part of the close fails. */
typedef PmError (*pm_close_fn)(struct pm_internal_struct *midi);
typedef PmError (*pm_poll_fn)(struct pm_internal_struct *midi);
-typedef void (*pm_host_error_fn)(struct pm_internal_struct *midi, char * msg,
- unsigned int len);
-typedef unsigned int (*pm_has_host_error_fn)(struct pm_internal_struct *midi);
+typedef unsigned int (*pm_check_host_error_fn)(struct pm_internal_struct *midi);
typedef struct {
pm_write_short_fn write_short; /* output short MIDI msg */
pm_begin_sysex_fn begin_sysex; /* prepare to send a sysex message */
pm_end_sysex_fn end_sysex; /* marks end of sysex message */
pm_write_byte_fn write_byte; /* accumulate one more sysex byte */
- pm_write_realtime_fn write_realtime; /* send real-time message within sysex */
+ pm_write_realtime_fn write_realtime; /* send real-time msg within sysex */
pm_write_flush_fn write_flush; /* send any accumulated but unsent data */
- pm_synchronize_fn synchronize; /* synchronize portmidi time to stream time */
+ pm_synchronize_fn synchronize; /* synchronize PM time to stream time */
pm_open_fn open; /* open MIDI device */
pm_abort_fn abort; /* abort */
pm_close_fn close; /* close device */
pm_poll_fn poll; /* read pending midi events into portmidi buffer */
- pm_has_host_error_fn has_host_error; /* true when device has had host
- error message */
- pm_host_error_fn host_error; /* provide text readable host error message
- for device (clears and resets) */
+ pm_check_host_error_fn check_host_error; /* true when device has had host */
+ /* error; sets pm_hosterror and writes message to pm_hosterror_text */
} pm_fns_node, *pm_fns_type;
@@ -83,23 +86,25 @@ typedef struct {
extern pm_fns_node pm_none_dictionary;
typedef struct {
- PmDeviceInfo pub; /* some portmidi state also saved in here (for autmatic
- device closing (see PmDeviceInfo struct) */
- void *descriptor; /* ID number passed to win32 multimedia API open */
- void *internalDescriptor; /* points to PmInternal device, allows automatic
- device closing */
+ PmDeviceInfo pub; /* some portmidi state also saved in here (for automatic
+ device closing -- see PmDeviceInfo struct) */
+ int deleted; /* is this is a deleted virtual device? */
+ void *descriptor; /* ID number passed to win32 multimedia API open,
+ * coreMIDI endpoint, etc., representing the device */
+ struct pm_internal_struct *pm_internal; /* points to PmInternal device */
+ /* when the device is open, allows automatic device closing */
pm_fns_type dictionary;
} descriptor_node, *descriptor_type;
extern int pm_descriptor_max;
-extern descriptor_type descriptors;
-extern int pm_descriptor_index;
+extern descriptor_type pm_descriptors;
+extern int pm_descriptor_len;
typedef uint32_t (*time_get_proc_type)(void *time_info);
typedef struct pm_internal_struct {
- int device_id; /* which device is open (index to descriptors) */
- short write_flag; /* MIDI_IN, or MIDI_OUT */
+ int device_id; /* which device is open (index to pm_descriptors) */
+ short is_input; /* MIDI IN (true) or MIDI OUT (false) */
PmTimeProcPtr time_proc; /* where to get the time */
void *time_info; /* pass this to get_time() */
@@ -129,7 +134,7 @@ typedef struct pm_internal_struct {
PmTimestamp now; /* set by PmWrite to current time */
int first_message; /* initially true, used to run first synchronization */
pm_fns_type dictionary; /* implementation functions */
- void *descriptor; /* system-dependent state */
+ void *api_info; /* system-dependent state */
/* the following are used to expedite sysex data */
/* on windows, in debug mode, based on some profiling, these optimizations
* cut the time to process sysex bytes from about 7.5 to 0.26 usec/byte,
@@ -138,7 +143,7 @@ typedef struct pm_internal_struct {
*/
unsigned char *fill_base; /* addr of ptr to sysex data */
uint32_t *fill_offset_ptr; /* offset of next sysex byte */
- int32_t fill_length; /* how many sysex bytes to write */
+ uint32_t fill_length; /* how many sysex bytes to write */
} PmInternal;
@@ -155,8 +160,11 @@ PmTimestamp none_synchronize(PmInternal *midi);
PmError pm_fail_fn(PmInternal *midi);
PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp);
PmError pm_success_fn(PmInternal *midi);
-PmError pm_add_device(char *interf, char *name, int input, void *descriptor,
- pm_fns_type dictionary);
+PmError pm_add_interf(const char *interf, pm_create_fn create_fn,
+ pm_delete_fn delete_fn);
+PmError pm_add_device(const char *interf, const char *name, int is_input,
+ int is_virtual, void *descriptor, pm_fns_type dictionary);
+void pm_undo_add_device(int id);
uint32_t pm_read_bytes(PmInternal *midi, const unsigned char *data, int len,
PmTimestamp timestamp);
void pm_read_short(PmInternal *midi, PmEvent *event);
@@ -176,3 +184,4 @@ int pm_find_default_device(char *pattern, int is_input);
}
#endif
+/** @endcond */
diff --git a/3rdparty/portmidi/pm_common/pmjni.vcproj b/3rdparty/portmidi/pm_common/pmjni.vcproj
deleted file mode 100644
index b783f3e0c54..00000000000
--- a/3rdparty/portmidi/pm_common/pmjni.vcproj
+++ /dev/null
@@ -1,161 +0,0 @@
-<?xml version="1.0" encoding = "Windows-1252"?>
-<VisualStudioProject
- ProjectType="Visual C++"
- Version="9.00"
- Name="pmjni"
- ProjectGUID="{87B548BD-F5CE-4D1F-9181-390966AC5855}"
- Keyword="Win32Proj">
- <Platforms>
- <Platform
- Name="Win32"/>
- </Platforms>
- <Configurations>
- <Configuration
- Name="Debug|Win32"
- OutputDirectory="Debug"
- IntermediateDirectory="pmjni.dir\Debug"
- ConfigurationType="2"
- UseOfMFC="0"
- ATLMinimizesCRunTimeLibraryUsage="FALSE"
- CharacterSet="2">
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions=" /Zm1000"
- AdditionalIncludeDirectories="..\pm_common;..\porttime;&quot;C:\Program Files\Java\jdk1.6.0_16\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_16\include\win32&quot;;"
- BasicRuntimeChecks="3"
- CompileAs="1"
- DebugInformationFormat="3"
- ExceptionHandling="0"
- InlineFunctionExpansion="0"
- Optimization="0"
- RuntimeLibrary="1"
- WarningLevel="3"
- PreprocessorDefinitions="WIN32,_WINDOWS,_DEBUG,PM_CHECK_ERRORS=1,DEBUG,CMAKE_INTDIR=\&quot;Debug\&quot;,pmjni_EXPORTS"
- AssemblerListingLocation="Debug"
- ObjectFile="$(IntDir)\"
- ProgramDataBaseFileName="../Debug/pmjni.pdb"
-/>
- <Tool
- Name="VCCustomBuildTool"/>
- <Tool
- Name="VCResourceCompilerTool"
- AdditionalIncludeDirectories="..\pm_common;..\porttime;&quot;C:\Program Files\Java\jdk1.6.0_16\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_16\include\win32&quot;;"
- PreprocessorDefinitions="WIN32,_WINDOWS,_DEBUG,PM_CHECK_ERRORS=1,DEBUG,CMAKE_INTDIR=\&quot;Debug\&quot;,pmjni_EXPORTS"/>
- <Tool
- Name="VCMIDLTool"
- PreprocessorDefinitions="WIN32,_WINDOWS,_DEBUG,PM_CHECK_ERRORS=1,DEBUG,CMAKE_INTDIR=\&quot;Debug\&quot;,pmjni_EXPORTS"
- MkTypLibCompatible="FALSE"
- TargetEnvironment="1"
- GenerateStublessProxies="TRUE"
- TypeLibraryName="$(InputName).tlb"
- OutputDirectory="$(IntDir)"
- HeaderFileName="$(InputName).h"
- DLLDataFileName=""
- InterfaceIdentifierFileName="$(InputName)_i.c"
- ProxyFileName="$(InputName)_p.c"/>
- <Tool
- Name="VCPreBuildEventTool"/>
- <Tool
- Name="VCPreLinkEventTool"/>
- <Tool
- Name="VCPostBuildEventTool"/>
- <Tool
- Name="VCLinkerTool"
- AdditionalOptions=" /STACK:10000000 /machine:I386 /debug"
- AdditionalDependencies="$(NOINHERIT) kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib winmm.lib &quot;C:\Program Files\Java\jdk1.6.0_16\include\..\lib\jvm.lib&quot; "
- OutputFile="..\Debug\pmjni.dll"
- Version="0.0"
- GenerateManifest="TRUE"
- LinkIncremental="2"
- AdditionalLibraryDirectories=""
- ProgramDataBaseFile="..\Debug\pmjni.pdb"
- GenerateDebugInformation="TRUE"
- ImportLibrary="..\Debug\pmjni.lib"/>
- </Configuration>
- <Configuration
- Name="Release|Win32"
- OutputDirectory="Release"
- IntermediateDirectory="pmjni.dir\Release"
- ConfigurationType="2"
- UseOfMFC="0"
- ATLMinimizesCRunTimeLibraryUsage="FALSE"
- CharacterSet="2">
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions=" /Zm1000"
- AdditionalIncludeDirectories="..\pm_common;..\porttime;&quot;C:\Program Files\Java\jdk1.6.0_16\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_16\include\win32&quot;;"
- CompileAs="1"
- ExceptionHandling="0"
- InlineFunctionExpansion="2"
- Optimization="2"
- RuntimeLibrary="0"
- WarningLevel="3"
- PreprocessorDefinitions="WIN32,_WINDOWS,NDEBUG,CMAKE_INTDIR=\&quot;Release\&quot;,pmjni_EXPORTS"
- AssemblerListingLocation="Release"
- ObjectFile="$(IntDir)\"
- ProgramDataBaseFileName="../Release/pmjni.pdb"
-/>
- <Tool
- Name="VCCustomBuildTool"/>
- <Tool
- Name="VCResourceCompilerTool"
- AdditionalIncludeDirectories="..\pm_common;..\porttime;&quot;C:\Program Files\Java\jdk1.6.0_16\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_16\include\win32&quot;;"
- PreprocessorDefinitions="WIN32,_WINDOWS,NDEBUG,CMAKE_INTDIR=\&quot;Release\&quot;,pmjni_EXPORTS"/>
- <Tool
- Name="VCMIDLTool"
- PreprocessorDefinitions="WIN32,_WINDOWS,NDEBUG,CMAKE_INTDIR=\&quot;Release\&quot;,pmjni_EXPORTS"
- MkTypLibCompatible="FALSE"
- TargetEnvironment="1"
- GenerateStublessProxies="TRUE"
- TypeLibraryName="$(InputName).tlb"
- OutputDirectory="$(IntDir)"
- HeaderFileName="$(InputName).h"
- DLLDataFileName=""
- InterfaceIdentifierFileName="$(InputName)_i.c"
- ProxyFileName="$(InputName)_p.c"/>
- <Tool
- Name="VCPreBuildEventTool"/>
- <Tool
- Name="VCPreLinkEventTool"/>
- <Tool
- Name="VCPostBuildEventTool"/>
- <Tool
- Name="VCLinkerTool"
- AdditionalOptions=" /STACK:10000000 /machine:I386"
- AdditionalDependencies="$(NOINHERIT) kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib winmm.lib &quot;C:\Program Files\Java\jdk1.6.0_16\include\..\lib\jvm.lib&quot; "
- OutputFile="..\Release\pmjni.dll"
- Version="0.0"
- GenerateManifest="TRUE"
- LinkIncremental="1"
- AdditionalLibraryDirectories=""
- ProgramDataBaseFile="..\Release\pmjni.pdb"
- ImportLibrary="..\Release\pmjni.lib"/>
- </Configuration>
- </Configurations>
- <Files>
- <Filter
- Name="Source Files"
- Filter="">
- <File
- RelativePath="..\pm_win\pmwin.c">
- </File>
- <File
- RelativePath="..\pm_win\pmwinmm.c">
- </File>
- <File
- RelativePath="..\porttime\ptwinmm.c">
- </File>
- <File
- RelativePath="..\pm_common\pmutil.c">
- </File>
- <File
- RelativePath="..\pm_common\portmidi.c">
- </File>
- <File
- RelativePath="..\pm_java\pmjni\pmjni.c">
- </File>
- </Filter>
- </Files>
- <Globals>
- </Globals>
-</VisualStudioProject>
diff --git a/3rdparty/portmidi/pm_common/pmutil.c b/3rdparty/portmidi/pm_common/pmutil.c
index 7d0abe35337..a70fe2fa1f8 100644
--- a/3rdparty/portmidi/pm_common/pmutil.c
+++ b/3rdparty/portmidi/pm_common/pmutil.c
@@ -8,7 +8,7 @@
#include "pmutil.h"
#include "pminternal.h"
-#if defined(WIN32) || defined(_MSC_VER)
+#ifdef WIN32
#define bzero(addr, siz) memset(addr, 0, siz)
#endif
diff --git a/3rdparty/portmidi/pm_common/pmutil.h b/3rdparty/portmidi/pm_common/pmutil.h
index ef5ee4bf84a..17f6984a0d7 100644
--- a/3rdparty/portmidi/pm_common/pmutil.h
+++ b/3rdparty/portmidi/pm_common/pmutil.h
@@ -1,31 +1,48 @@
-/* pmutil.h -- some helpful utilities for building midi
- applications that use PortMidi
+/** @file pmutil.h lock-free queue for building MIDI
+ applications with PortMidi.
+
+ PortMidi is not reentrant, and locks can suffer from priority
+ inversion. To support coordination between system callbacks, a
+ high-priority thread created with PortTime, and the main
+ application thread, PortMidi uses a lock-free, non-blocking
+ queue. The queue implementation is not particular to MIDI and is
+ available for other uses.
*/
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
+/** @defgroup grp_pmutil Lock-free Queue
+ @{
+*/
+
+/** The queue representation is opaque. Declare a queue as PmQueue * */
typedef void PmQueue;
-/*
- A single-reader, single-writer queue is created by
- Pm_QueueCreate(), which takes the number of messages and
- the message size as parameters. The queue only accepts
- fixed sized messages. Returns NULL if memory cannot be allocated.
+/** create a single-reader, single-writer queue.
+
+ @param num_msgs the number of messages the queue can hold
+
+ @param the fixed message size
+
+ @return the allocated and initialized queue, or NULL if memory
+ cannot be allocated. Allocation uses #pm_malloc().
+
+ The queue only accepts fixed sized messages.
This queue implementation uses the "light pipe" algorithm which
operates correctly even with multi-processors and out-of-order
memory writes. (see Alexander Dokumentov, "Lock-free Interprocess
- Communication," Dr. Dobbs Portal, http://www.ddj.com/,
- articleID=189401457, June 15, 2006. This algorithm requires
- that messages be translated to a form where no words contain
- zeros. Each word becomes its own "data valid" tag. Because of
- this translation, we cannot return a pointer to data still in
- the queue when the "peek" method is called. Instead, a buffer
- is preallocated so that data can be copied there. Pm_QueuePeek()
- dequeues a message into this buffer and returns a pointer to
- it. A subsequent Pm_Dequeue() will copy from this buffer.
+ Communication," Dr. Dobbs Portal, http://www.ddj.com/,
+ articleID=189401457, June 15, 2006. This algorithm requires that
+ messages be translated to a form where no words contain
+ zeros. Each word becomes its own "data valid" tag. Because of this
+ translation, we cannot return a pointer to data still in the queue
+ when the "peek" method is called. Instead, a buffer is
+ preallocated so that data can be copied there. Pm_QueuePeek()
+ dequeues a message into this buffer and returns a pointer to it. A
+ subsequent Pm_Dequeue() will copy from this buffer.
This implementation does not try to keep reader/writer data in
separate cache lines or prevent thrashing on cache lines.
@@ -36,92 +53,127 @@ typedef void PmQueue;
of the cache line, especially if messages are smaller than
cache lines. See the Dokumentov article for explanation.
- The algorithm is extended to handle "overflow" reporting. To report
- an overflow, the sender writes the current tail position to a field.
- The receiver must acknowlege receipt by zeroing the field. The sender
- will not send more until the field is zeroed.
-
- Pm_QueueDestroy() destroys the queue and frees its storage.
+ The algorithm is extended to handle "overflow" reporting. To
+ report an overflow, the sender writes the current tail position to
+ a field. The receiver must acknowlege receipt by zeroing the
+ field. The sender will not send more until the field is zeroed.
*/
-
PMEXPORT PmQueue *Pm_QueueCreate(long num_msgs, int32_t bytes_per_msg);
+
+/** destroy a queue and free its storage.
+
+ @param queue a queue created by #Pm_QueueCreate().
+
+ @return pmNoError or an error code.
+
+ Uses #pm_free().
+
+ */
PMEXPORT PmError Pm_QueueDestroy(PmQueue *queue);
-/*
- Pm_Dequeue() removes one item from the queue, copying it into msg.
- Returns 1 if successful, and 0 if the queue is empty.
- Returns pmBufferOverflow if what would have been the next thing
- in the queue was dropped due to overflow. (So when overflow occurs,
- the receiver can receive a queue full of messages before getting the
- overflow report. This protocol ensures that the reader will be
+/** remove one message from the queue, copying it into \p msg.
+
+ @param queue a queue created by #Pm_QueueCreate().
+
+ @param msg address to which the message, if any, is copied.
+
+ @return 1 if successful, and 0 if the queue is empty. Returns
+ #pmBufferOverflow if what would have been the next thing in the
+ queue was dropped due to overflow. (So when overflow occurs, the
+ receiver can receive a queue full of messages before getting the
+ overflow report. This protocol ensures that the reader will be
notified when data is lost due to overflow.
*/
PMEXPORT PmError Pm_Dequeue(PmQueue *queue, void *msg);
+/** insert one message into the queue, copying it from \p msg.
-/*
- Pm_Enqueue() inserts one item into the queue, copying it from msg.
- Returns pmNoError if successful and pmBufferOverflow if the queue was
- already full. If pmBufferOverflow is returned, the overflow flag is set.
+ @param queue a queue created by #Pm_QueueCreate().
+
+ @param msg address of the message to be enqueued.
+
+ @return #pmNoError if successful and #pmBufferOverflow if the
+ queue was already full. If #pmBufferOverflow is returned, the
+ overflow flag is set.
*/
PMEXPORT PmError Pm_Enqueue(PmQueue *queue, void *msg);
+/** test if the queue is full.
-/*
- Pm_QueueFull() returns non-zero if the queue is full
- Pm_QueueEmpty() returns non-zero if the queue is empty
+ @param queue a queue created by #Pm_QueueCreate().
- Either condition may change immediately because a parallel
- enqueue or dequeue operation could be in progress. Furthermore,
- Pm_QueueEmpty() is optimistic: it may say false, when due to
- out-of-order writes, the full message has not arrived. Therefore,
- Pm_Dequeue() could still return 0 after Pm_QueueEmpty() returns
- false. On the other hand, Pm_QueueFull() is pessimistic: if it
- returns false, then Pm_Enqueue() is guaranteed to succeed.
+ @return non-zero iff the queue is empty, and @pmBadPtr if \p queue
+ is NULL.
- Error conditions: Pm_QueueFull() returns pmBadPtr if queue is NULL.
- Pm_QueueEmpty() returns FALSE if queue is NULL.
+ The full condition may change immediately because a parallel
+ dequeue operation could be in progress. The result is
+ pessimistic: if it returns false (zero) to the single writer, then
+ #Pm_Enqueue() is guaranteed to succeed.
*/
PMEXPORT int Pm_QueueFull(PmQueue *queue);
+
+/** test if the queue is empty.
+
+ @param queue a queue created by #Pm_QueueCreate().
+
+ @return zero iff the queue is either empty or NULL.
+
+ The empty condition may change immediately because a parallel
+ enqueue operation could be in progress. Furthermore, the
+ result is optimistic: it may say false, when due to
+ out-of-order writes, the full message has not arrived. Therefore,
+ #Pm_Dequeue() could still return 0 after #Pm_QueueEmpty() returns
+ false.
+*/
PMEXPORT int Pm_QueueEmpty(PmQueue *queue);
+/** get a pointer to the item at the head of the queue.
-/*
- Pm_QueuePeek() returns a pointer to the item at the head of the queue,
- or NULL if the queue is empty. The item is not removed from the queue.
- Pm_QueuePeek() will not indicate when an overflow occurs. If you want
- to get and check pmBufferOverflow messages, use the return value of
- Pm_QueuePeek() *only* as an indication that you should call
- Pm_Dequeue(). At the point where a direct call to Pm_Dequeue() would
- return pmBufferOverflow, Pm_QueuePeek() will return NULL but internally
- clear the pmBufferOverflow flag, enabling Pm_Enqueue() to resume
- enqueuing messages. A subsequent call to Pm_QueuePeek()
- will return a pointer to the first message *after* the overflow.
- Using this as an indication to call Pm_Dequeue(), the first call
- to Pm_Dequeue() will return pmBufferOverflow. The second call will
- return success, copying the same message pointed to by the previous
- Pm_QueuePeek().
-
- When to use Pm_QueuePeek(): (1) when you need to look at the message
+ @param queue a queue created by #Pm_QueueCreate().
+
+ @result a pointer to the head message or NULL if the queue is empty.
+
+ The message is not removed from the queue. #Pm_QueuePeek() will
+ not indicate when an overflow occurs. If you want to get and check
+ #pmBufferOverflow messages, use the return value of
+ #Pm_QueuePeek() *only* as an indication that you should call
+ #Pm_Dequeue(). At the point where a direct call to #Pm_Dequeue()
+ would return #pmBufferOverflow, #Pm_QueuePeek() will return NULL,
+ but internally clear the #pmBufferOverflow flag, enabling
+ #Pm_Enqueue() to resume enqueuing messages. A subsequent call to
+ #Pm_QueuePeek() will return a pointer to the first message *after*
+ the overflow. Using this as an indication to call #Pm_Dequeue(),
+ the first call to #Pm_Dequeue() will return #pmBufferOverflow. The
+ second call will return success, copying the same message pointed
+ to by the previous #Pm_QueuePeek().
+
+ When to use #Pm_QueuePeek(): (1) when you need to look at the message
data to decide who should be called to receive it. (2) when you need
to know a message is ready but cannot accept the message.
- Note that Pm_QueuePeek() is not a fast check, so if possible, you
- might as well just call Pm_Dequeue() and accept the data if it is there.
+ Note that #Pm_QueuePeek() is not a fast check, so if possible, you
+ might as well just call #Pm_Dequeue() and accept the data if it is there.
*/
PMEXPORT void *Pm_QueuePeek(PmQueue *queue);
-/*
- Pm_SetOverflow() allows the writer (enqueuer) to signal an overflow
- condition to the reader (dequeuer). E.g. when transfering data from
- the OS to an application, if the OS indicates a buffer overrun,
- Pm_SetOverflow() can be used to insure that the reader receives a
- pmBufferOverflow result from Pm_Dequeue(). Returns pmBadPtr if queue
- is NULL, returns pmBufferOverflow if buffer is already in an overflow
- state, returns pmNoError if successfully set overflow state.
+/** allows the writer (enqueuer) to signal an overflow
+ condition to the reader (dequeuer).
+
+ @param queue a queue created by #Pm_QueueCreate().
+
+ @return #pmNoError if overflow is set, or #pmBadPtr if queue is
+ NULL, or #pmBufferOverflow if buffer is already in an overflow
+ state.
+
+ E.g., when transfering data from the OS to an application, if the
+ OS indicates a buffer overrun, #Pm_SetOverflow() can be used to
+ insure that the reader receives a #pmBufferOverflow result from
+ #Pm_Dequeue().
*/
PMEXPORT PmError Pm_SetOverflow(PmQueue *queue);
+/** @} */
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/3rdparty/portmidi/pm_common/portmidi-dynamic.vcproj b/3rdparty/portmidi/pm_common/portmidi-dynamic.vcproj
deleted file mode 100644
index 42b4a271a20..00000000000
--- a/3rdparty/portmidi/pm_common/portmidi-dynamic.vcproj
+++ /dev/null
@@ -1,179 +0,0 @@
-<?xml version="1.0" encoding = "Windows-1252"?>
-<VisualStudioProject
- ProjectType="Visual C++"
- Version="9.00"
- Name="portmidi-dynamic"
- ProjectGUID="{7283FAD1-7415-4061-A19A-FF5C7BCE9306}"
- Keyword="Win32Proj">
- <Platforms>
- <Platform
- Name="Win32"/>
- </Platforms>
- <Configurations>
- <Configuration
- Name="Debug|Win32"
- OutputDirectory="Debug"
- IntermediateDirectory="portmidi-dynamic.dir\Debug"
- ConfigurationType="2"
- UseOfMFC="0"
- ATLMinimizesCRunTimeLibraryUsage="FALSE"
- CharacterSet="2">
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions=" /Zm1000"
- AdditionalIncludeDirectories="C:\Users\rbd\portmedia\portmidi\pm_common;C:\Users\rbd\portmedia\portmidi\porttime;&quot;C:\Program Files\Java\jdk1.6.0_16\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_16\include\win32&quot;;"
- BasicRuntimeChecks="3"
- CompileAs="1"
- DebugInformationFormat="3"
- ExceptionHandling="0"
- InlineFunctionExpansion="0"
- Optimization="0"
- RuntimeLibrary="3"
- WarningLevel="3"
- PreprocessorDefinitions="WIN32,_WINDOWS,_DEBUG,PM_CHECK_ERRORS=1,DEBUG,CMAKE_INTDIR=\&quot;Debug\&quot;,portmidi_dynamic_EXPORTS"
- AssemblerListingLocation="Debug"
- ObjectFile="$(IntDir)\"
- ProgramDataBaseFileName="C:/Users/rbd/portmedia/portmidi/Debug/portmidi.pdb"
-/>
- <Tool
- Name="VCCustomBuildTool"/>
- <Tool
- Name="VCResourceCompilerTool"
- AdditionalIncludeDirectories="C:\Users\rbd\portmedia\portmidi\pm_common;C:\Users\rbd\portmedia\portmidi\porttime;&quot;C:\Program Files\Java\jdk1.6.0_16\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_16\include\win32&quot;;"
- PreprocessorDefinitions="WIN32,_WINDOWS,_DEBUG,PM_CHECK_ERRORS=1,DEBUG,CMAKE_INTDIR=\&quot;Debug\&quot;,portmidi_dynamic_EXPORTS"/>
- <Tool
- Name="VCMIDLTool"
- PreprocessorDefinitions="WIN32,_WINDOWS,_DEBUG,PM_CHECK_ERRORS=1,DEBUG,CMAKE_INTDIR=\&quot;Debug\&quot;,portmidi_dynamic_EXPORTS"
- MkTypLibCompatible="FALSE"
- TargetEnvironment="1"
- GenerateStublessProxies="TRUE"
- TypeLibraryName="$(InputName).tlb"
- OutputDirectory="$(IntDir)"
- HeaderFileName="$(InputName).h"
- DLLDataFileName=""
- InterfaceIdentifierFileName="$(InputName)_i.c"
- ProxyFileName="$(InputName)_p.c"/>
- <Tool
- Name="VCPreBuildEventTool"/>
- <Tool
- Name="VCPreLinkEventTool"/>
- <Tool
- Name="VCPostBuildEventTool"/>
- <Tool
- Name="VCLinkerTool"
- AdditionalOptions=" /STACK:10000000 /machine:I386 /debug"
- AdditionalDependencies="$(NOINHERIT) kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib winmm.lib "
- OutputFile="C:\Users\rbd\portmedia\portmidi\Debug\portmidi.dll"
- Version="0.0"
- GenerateManifest="TRUE"
- LinkIncremental="2"
- AdditionalLibraryDirectories=""
- ProgramDataBaseFile="C:\Users\rbd\portmedia\portmidi\Debug\portmidi.pdb"
- GenerateDebugInformation="TRUE"
- ImportLibrary="C:\Users\rbd\portmedia\portmidi\Debug\portmidi.lib"/>
- </Configuration>
- <Configuration
- Name="Release|Win32"
- OutputDirectory="Release"
- IntermediateDirectory="portmidi-dynamic.dir\Release"
- ConfigurationType="2"
- UseOfMFC="0"
- ATLMinimizesCRunTimeLibraryUsage="FALSE"
- CharacterSet="2">
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions=" /Zm1000"
- AdditionalIncludeDirectories="C:\Users\rbd\portmedia\portmidi\pm_common;C:\Users\rbd\portmedia\portmidi\porttime;&quot;C:\Program Files\Java\jdk1.6.0_16\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_16\include\win32&quot;;"
- CompileAs="1"
- ExceptionHandling="0"
- InlineFunctionExpansion="2"
- Optimization="2"
- RuntimeLibrary="2"
- WarningLevel="3"
- PreprocessorDefinitions="WIN32,_WINDOWS,NDEBUG,CMAKE_INTDIR=\&quot;Release\&quot;,portmidi_dynamic_EXPORTS"
- AssemblerListingLocation="Release"
- ObjectFile="$(IntDir)\"
- ProgramDataBaseFileName="C:/Users/rbd/portmedia/portmidi/Release/portmidi.pdb"
-/>
- <Tool
- Name="VCCustomBuildTool"/>
- <Tool
- Name="VCResourceCompilerTool"
- AdditionalIncludeDirectories="C:\Users\rbd\portmedia\portmidi\pm_common;C:\Users\rbd\portmedia\portmidi\porttime;&quot;C:\Program Files\Java\jdk1.6.0_16\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_16\include\win32&quot;;"
- PreprocessorDefinitions="WIN32,_WINDOWS,NDEBUG,CMAKE_INTDIR=\&quot;Release\&quot;,portmidi_dynamic_EXPORTS"/>
- <Tool
- Name="VCMIDLTool"
- PreprocessorDefinitions="WIN32,_WINDOWS,NDEBUG,CMAKE_INTDIR=\&quot;Release\&quot;,portmidi_dynamic_EXPORTS"
- MkTypLibCompatible="FALSE"
- TargetEnvironment="1"
- GenerateStublessProxies="TRUE"
- TypeLibraryName="$(InputName).tlb"
- OutputDirectory="$(IntDir)"
- HeaderFileName="$(InputName).h"
- DLLDataFileName=""
- InterfaceIdentifierFileName="$(InputName)_i.c"
- ProxyFileName="$(InputName)_p.c"/>
- <Tool
- Name="VCPreBuildEventTool"/>
- <Tool
- Name="VCPreLinkEventTool"/>
- <Tool
- Name="VCPostBuildEventTool"/>
- <Tool
- Name="VCLinkerTool"
- AdditionalOptions=" /STACK:10000000 /machine:I386"
- AdditionalDependencies="$(NOINHERIT) kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib winmm.lib "
- OutputFile="C:\Users\rbd\portmedia\portmidi\Release\portmidi.dll"
- Version="0.0"
- GenerateManifest="TRUE"
- LinkIncremental="1"
- AdditionalLibraryDirectories=""
- ProgramDataBaseFile="C:\Users\rbd\portmedia\portmidi\Release\portmidi.pdb"
- ImportLibrary="C:\Users\rbd\portmedia\portmidi\Release\portmidi.lib"/>
- </Configuration>
- </Configurations>
- <Files>
- <File
- RelativePath="C:\Users\rbd\portmedia\portmidi\pm_common\CMakeLists.txt">
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCustomBuildTool"
- Description="Building Custom Rule C:/Users/rbd/portmedia/portmidi/pm_common/CMakeLists.txt"
- CommandLine="&quot;C:\Program Files\CMake 2.6\bin\cmake.exe&quot; -HC:/Users/rbd/portmedia/portmidi -BC:/Users/rbd/portmedia/portmidi --check-stamp-file CMakeFiles/generate.stamp"
- AdditionalDependencies="C:\Users\rbd\portmedia\portmidi\pm_common\CMakeLists.txt;&quot;C:\Program Files\CMake 2.6\share\cmake-2.6\Modules\FindJNI.cmake&quot;;C:\Users\rbd\portmedia\portmidi\pm_common\CMakeLists.txt;"
- Outputs="CMakeFiles\generate.stamp"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCustomBuildTool"
- Description="Building Custom Rule C:/Users/rbd/portmedia/portmidi/pm_common/CMakeLists.txt"
- CommandLine="&quot;C:\Program Files\CMake 2.6\bin\cmake.exe&quot; -HC:/Users/rbd/portmedia/portmidi -BC:/Users/rbd/portmedia/portmidi --check-stamp-file CMakeFiles/generate.stamp"
- AdditionalDependencies="C:\Users\rbd\portmedia\portmidi\pm_common\CMakeLists.txt;&quot;C:\Program Files\CMake 2.6\share\cmake-2.6\Modules\FindJNI.cmake&quot;;C:\Users\rbd\portmedia\portmidi\pm_common\CMakeLists.txt;"
- Outputs="CMakeFiles\generate.stamp"/>
- </FileConfiguration>
- </File>
- <Filter
- Name="Source Files"
- Filter="">
- <File
- RelativePath="C:\Users\rbd\portmedia\portmidi\pm_win\pmwin.c">
- </File>
- <File
- RelativePath="C:\Users\rbd\portmedia\portmidi\pm_win\pmwinmm.c">
- </File>
- <File
- RelativePath="C:\Users\rbd\portmedia\portmidi\porttime\ptwinmm.c">
- </File>
- <File
- RelativePath="C:\Users\rbd\portmedia\portmidi\pm_common\pmutil.c">
- </File>
- <File
- RelativePath="C:\Users\rbd\portmedia\portmidi\pm_common\portmidi.c">
- </File>
- </Filter>
- </Files>
- <Globals>
- </Globals>
-</VisualStudioProject>
diff --git a/3rdparty/portmidi/pm_common/portmidi-static.vcproj b/3rdparty/portmidi/pm_common/portmidi-static.vcproj
deleted file mode 100644
index 4702b2d1e31..00000000000
--- a/3rdparty/portmidi/pm_common/portmidi-static.vcproj
+++ /dev/null
@@ -1,141 +0,0 @@
-<?xml version="1.0" encoding = "Windows-1252"?>
-<VisualStudioProject
- ProjectType="Visual C++"
- Version="9.00"
- Name="portmidi-static"
- ProjectGUID="{2985D5DA-D91E-44E0-924B-E612B6AA33F6}"
- Keyword="Win32Proj">
- <Platforms>
- <Platform
- Name="Win32"/>
- </Platforms>
- <Configurations>
- <Configuration
- Name="Debug|Win32"
- OutputDirectory="Debug"
- IntermediateDirectory="portmidi-static.dir\Debug"
- ConfigurationType="4"
- UseOfMFC="0"
- ATLMinimizesCRunTimeLibraryUsage="FALSE"
- CharacterSet="2">
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions=" /Zm1000"
- AdditionalIncludeDirectories="..\pm_common;..\porttime;&quot;C:\Program Files\Java\jdk1.6.0_16\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_16\include\win32&quot;;"
- BasicRuntimeChecks="3"
- CompileAs="1"
- DebugInformationFormat="3"
- ExceptionHandling="0"
- InlineFunctionExpansion="0"
- Optimization="0"
- RuntimeLibrary="1"
- WarningLevel="3"
- PreprocessorDefinitions="WIN32,_WINDOWS,_DEBUG,PM_CHECK_ERRORS=1,DEBUG,CMAKE_INTDIR=\&quot;Debug\&quot;"
- AssemblerListingLocation="Debug"
- ObjectFile="$(IntDir)\"
- ProgramDataBaseFileName="../Debug/portmidi_s.pdb"
-/>
- <Tool
- Name="VCCustomBuildTool"/>
- <Tool
- Name="VCResourceCompilerTool"
- AdditionalIncludeDirectories="..\pm_common;..\porttime;&quot;C:\Program Files\Java\jdk1.6.0_16\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_16\include\win32&quot;;"
- PreprocessorDefinitions="WIN32,_WINDOWS,_DEBUG,PM_CHECK_ERRORS=1,DEBUG,CMAKE_INTDIR=\&quot;Debug\&quot;"/>
- <Tool
- Name="VCMIDLTool"
- PreprocessorDefinitions="WIN32,_WINDOWS,_DEBUG,PM_CHECK_ERRORS=1,DEBUG,CMAKE_INTDIR=\&quot;Debug\&quot;"
- MkTypLibCompatible="FALSE"
- TargetEnvironment="1"
- GenerateStublessProxies="TRUE"
- TypeLibraryName="$(InputName).tlb"
- OutputDirectory="$(IntDir)"
- HeaderFileName="$(InputName).h"
- DLLDataFileName=""
- InterfaceIdentifierFileName="$(InputName)_i.c"
- ProxyFileName="$(InputName)_p.c"/>
- <Tool
- Name="VCPreBuildEventTool"/>
- <Tool
- Name="VCPreLinkEventTool"/>
- <Tool
- Name="VCPostBuildEventTool"/>
- <Tool
- Name="VCLibrarianTool"
- OutputFile="..\Debug\portmidi_s.lib"/>
- </Configuration>
- <Configuration
- Name="Release|Win32"
- OutputDirectory="Release"
- IntermediateDirectory="portmidi-static.dir\Release"
- ConfigurationType="4"
- UseOfMFC="0"
- ATLMinimizesCRunTimeLibraryUsage="FALSE"
- CharacterSet="2">
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions=" /Zm1000"
- AdditionalIncludeDirectories="..\pm_common;..\porttime;&quot;C:\Program Files\Java\jdk1.6.0_16\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_16\include\win32&quot;;"
- CompileAs="1"
- ExceptionHandling="0"
- InlineFunctionExpansion="2"
- Optimization="2"
- RuntimeLibrary="0"
- WarningLevel="3"
- PreprocessorDefinitions="WIN32,_WINDOWS,NDEBUG,CMAKE_INTDIR=\&quot;Release\&quot;"
- AssemblerListingLocation="Release"
- ObjectFile="$(IntDir)\"
- ProgramDataBaseFileName="../Release/portmidi_s.pdb"
-/>
- <Tool
- Name="VCCustomBuildTool"/>
- <Tool
- Name="VCResourceCompilerTool"
- AdditionalIncludeDirectories="..\pm_common;..\porttime;&quot;C:\Program Files\Java\jdk1.6.0_16\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_16\include\win32&quot;;"
- PreprocessorDefinitions="WIN32,_WINDOWS,NDEBUG,CMAKE_INTDIR=\&quot;Release\&quot;"/>
- <Tool
- Name="VCMIDLTool"
- PreprocessorDefinitions="WIN32,_WINDOWS,NDEBUG,CMAKE_INTDIR=\&quot;Release\&quot;"
- MkTypLibCompatible="FALSE"
- TargetEnvironment="1"
- GenerateStublessProxies="TRUE"
- TypeLibraryName="$(InputName).tlb"
- OutputDirectory="$(IntDir)"
- HeaderFileName="$(InputName).h"
- DLLDataFileName=""
- InterfaceIdentifierFileName="$(InputName)_i.c"
- ProxyFileName="$(InputName)_p.c"/>
- <Tool
- Name="VCPreBuildEventTool"/>
- <Tool
- Name="VCPreLinkEventTool"/>
- <Tool
- Name="VCPostBuildEventTool"/>
- <Tool
- Name="VCLibrarianTool"
- OutputFile="..\Release\portmidi_s.lib"/>
- </Configuration>
- </Configurations>
- <Files>
- <Filter
- Name="Source Files"
- Filter="">
- <File
- RelativePath="..\pm_win\pmwin.c">
- </File>
- <File
- RelativePath="..\pm_win\pmwinmm.c">
- </File>
- <File
- RelativePath="..\porttime\ptwinmm.c">
- </File>
- <File
- RelativePath="..\pm_common\pmutil.c">
- </File>
- <File
- RelativePath="..\pm_common\portmidi.c">
- </File>
- </Filter>
- </Files>
- <Globals>
- </Globals>
-</VisualStudioProject>
diff --git a/3rdparty/portmidi/pm_common/portmidi.c b/3rdparty/portmidi/pm_common/portmidi.c
index 2ed9171e781..7c906dcae42 100644
--- a/3rdparty/portmidi/pm_common/portmidi.c
+++ b/3rdparty/portmidi/pm_common/portmidi.c
@@ -1,7 +1,5 @@
-#ifdef _MSC_VER
- #pragma warning(disable: 4244) // stop warnings about downsize typecasts
- #pragma warning(disable: 4018) // stop warnings about signed/unsigned
-#endif
+/* portmidi.c -- cross-platform MIDI I/O library */
+/* see license.txt for license */
#include "stdlib.h"
#include "string.h"
@@ -36,12 +34,21 @@
#define is_empty(midi) ((midi)->tail == (midi)->head)
-/* this is not static so that pm_init can set it directly if
+/* this is not static so that pm_init can set it directly
* (see pmmac.c:pm_init())
*/
int pm_initialized = FALSE;
-int pm_hosterror;
+int pm_hosterror; /* boolean */
+
+/* if PM_CHECK_ERRORS is enabled, but the caller wants to
+ * handle an error condition, declare this as extern and
+ * set to FALSE (this override is provided specifically
+ * for the test program virttest.c, where pmNameConflict
+ * is expected in a call to Pm_CreateVirtualInput()):
+ */
+int pm_check_errors = TRUE;
+
char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN];
#ifdef PM_CHECK_ERRORS
@@ -54,15 +61,16 @@ static void prompt_and_exit(void)
{
char line[STRING_MAX];
printf("type ENTER...");
- fgets(line, STRING_MAX, stdin);
+ char *rslt = fgets(line, STRING_MAX, stdin);
/* this will clean up open ports: */
exit(-1);
}
-
static PmError pm_errmsg(PmError err)
{
- if (err == pmHostError) {
+ if (!pm_check_errors) { /* see pm_check_errors declaration above */
+ ;
+ } else if (err == pmHostError) {
/* it seems pointless to allocate memory and copy the string,
* so I will do the work of Pm_GetHostErrorText directly
*/
@@ -87,52 +95,193 @@ system implementation of portmidi interface
*/
int pm_descriptor_max = 0;
-int pm_descriptor_index = 0;
-descriptor_type descriptors = NULL;
+int pm_descriptor_len = 0;
+descriptor_type pm_descriptors = NULL;
+
+/* interface pm_descriptors are simple: an array of string/fnptr pairs: */
+#define MAX_INTERF 4
+static struct {
+ const char *interf;
+ pm_create_fn create_fn;
+ pm_delete_fn delete_fn;
+} pm_interf_list[MAX_INTERF];
+
+static int pm_interf_list_len = 0;
+
+
+/* pm_add_interf -- describe an interface to library
+ *
+ * This is called at initialization time, once for each
+ * supported interface (e.g., CoreMIDI). The strings
+ * are retained but NOT COPIED, so do not destroy them!
+ *
+ * The purpose is to register functions that create/delete
+ * a virtual input or output device.
+ *
+ * returns pmInsufficientMemor if interface memory is
+ * exceeded, otherwise returns pmNoError.
+ */
+PmError pm_add_interf(const char *interf, pm_create_fn create_fn,
+ pm_delete_fn delete_fn)
+{
+ if (pm_interf_list_len >= MAX_INTERF) {
+ return pmInsufficientMemory;
+ }
+ pm_interf_list[pm_interf_list_len].interf = interf;
+ pm_interf_list[pm_interf_list_len].create_fn = create_fn;
+ pm_interf_list[pm_interf_list_len].delete_fn = delete_fn;
+ pm_interf_list_len++;
+ return pmNoError;
+}
+
+
+PmError pm_create_virtual(PmInternal *midi, int is_input, const char *interf,
+ const char *name, void *device_info)
+{
+ int i;
+ if (pm_interf_list_len == 0) {
+ return pmNotImplemented;
+ }
+ if (!interf) {
+ /* default interface is the first one */
+ interf = pm_interf_list[0].interf;
+ }
+ for (i = 0; i < pm_interf_list_len; i++) {
+ if (strcmp(pm_interf_list[i].interf,
+ interf) == 0) {
+ int id = (*pm_interf_list[i].create_fn)(is_input, name,
+ device_info);
+ pm_descriptors[id].pub.is_virtual = TRUE;
+ return id;
+ }
+ }
+ return pmInterfaceNotSupported;
+}
+
/* pm_add_device -- describe interface/device pair to library
*
* This is called at intialization 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!
+ * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1).
+ * This is also called when user creates a virtual device.
+ *
+ * Normally, increasing integer indices are returned. If the device
+ * is virtual, a linear search is performed to ensure that the name
+ * is unique. If the name is already taken, the call will fail and
+ * no device is added.
+ *
+ * interf is assumed to be static memory, so it is NOT COPIED and
+ * NOT FREED.
+ * name is owned by caller, COPIED if needed, and FREED by PortMidi.
+ * Caller is resposible for freeing name when pm_add_device returns.
*
- * returns pmInvalidDeviceId if device memory is exceeded
- * otherwise returns pmNoError
+ * returns pmInvalidDeviceId if device memory is exceeded or a virtual
+ * device would take the name of an existing device.
+ * otherwise returns index (portmidi device_id) of the added device
*/
-PmError pm_add_device(char *interf, char *name, int input,
- void *descriptor, pm_fns_type dictionary) {
- if (pm_descriptor_index >= pm_descriptor_max) {
- // expand descriptors
+PmError pm_add_device(const char *interf, const char *name, int is_input,
+ int is_virtual, void *descriptor, pm_fns_type dictionary) {
+ /* printf("pm_add_device: %s %s %d %p %p\n",
+ interf, name, is_input, descriptor, dictionary); */
+ int device_id;
+ PmDeviceInfo *d;
+ /* if virtual, search for duplicate name or unused ID; otherwise,
+ * just add a new device at the next integer available:
+ */
+ for (device_id = (is_virtual ? 0 : pm_descriptor_len);
+ device_id < pm_descriptor_len; device_id++) {
+ d = &pm_descriptors[device_id].pub;
+ d->structVersion = 200;
+ if (strcmp(d->interf, interf) == 0 && strcmp(d->name, name) == 0) {
+ /* only reuse a name if it is a deleted virtual device with
+ * a matching direction (input or output) */
+ if (pm_descriptors[device_id].deleted && is_input == d->input) {
+ /* here, we know d->is_virtual because only virtual devices
+ * can be deleted, and we know is_virtual because we are
+ * in this loop.
+ */
+ pm_free((void *) d->name); /* reuse this device entry */
+ d->name = NULL;
+ break;
+ /* name conflict exists if the new device appears to others as
+ * the same direction (input or output) as the existing device.
+ * Note that virtual inputs appear to others as outputs and
+ * vice versa.
+ * The direction of the new virtual device to others is "output"
+ * if is_input, i.e., virtual inputs appear to others as outputs.
+ * The existing device appears to others as "output" if
+ * (d->is_virtual == d->input) by the same logic.
+ * The compare will detect if device directions are the same:
+ */
+ } else if (is_input == (d->is_virtual == d->input)) {
+ return pmNameConflict;
+ }
+ }
+ }
+ if (device_id >= pm_descriptor_max) {
+ // expand pm_descriptors
descriptor_type new_descriptors = (descriptor_type)
pm_alloc(sizeof(descriptor_node) * (pm_descriptor_max + 32));
if (!new_descriptors) return pmInsufficientMemory;
- if (descriptors) {
- memcpy(new_descriptors, descriptors,
+ if (pm_descriptors) {
+ memcpy(new_descriptors, pm_descriptors,
sizeof(descriptor_node) * pm_descriptor_max);
- free(descriptors);
+ pm_free(pm_descriptors);
}
pm_descriptor_max += 32;
- descriptors = new_descriptors;
+ pm_descriptors = new_descriptors;
+ }
+ if (device_id == pm_descriptor_len) {
+ pm_descriptor_len++; /* extending array of pm_descriptors */
}
- descriptors[pm_descriptor_index].pub.interf = interf;
- descriptors[pm_descriptor_index].pub.name = name;
- descriptors[pm_descriptor_index].pub.input = input;
- descriptors[pm_descriptor_index].pub.output = !input;
+ d = &pm_descriptors[device_id].pub;
+ d->interf = interf;
+ d->name = pm_alloc(strlen(name) + 1);
+ if (!d->name) {
+ return pmInsufficientMemory;
+ }
+#pragma warning(suppress: 4996) // don't use suggested strncpy_s
+ strcpy(d->name, name);
+ d->input = is_input;
+ d->output = !is_input;
+ d->is_virtual = FALSE; /* caller should set to TRUE if this is virtual */
/* default state: nothing to close (for automatic device closing) */
- descriptors[pm_descriptor_index].pub.opened = FALSE;
+ d->opened = FALSE;
+
+ pm_descriptors[device_id].deleted = FALSE;
/* ID number passed to win32 multimedia API open */
- descriptors[pm_descriptor_index].descriptor = descriptor;
+ pm_descriptors[device_id].descriptor = descriptor;
/* points to PmInternal, allows automatic device closing */
- descriptors[pm_descriptor_index].internalDescriptor = NULL;
+ pm_descriptors[device_id].pm_internal = NULL;
- descriptors[pm_descriptor_index].dictionary = dictionary;
-
- pm_descriptor_index++;
+ pm_descriptors[device_id].dictionary = dictionary;
- return pmNoError;
+ return device_id;
+}
+
+
+/* Undo a successful call to pm_add_device(). If a new device was
+ * allocated, it must be the last device in pm_descriptors, so it is
+ * easy to delete by decrementing the length of pm_descriptors, but
+ * first free the name (which was copied to the heap). Otherwise,
+ * the device must be a virtual device that was created previously
+ * and is in the interior of the array of pm_descriptors. Leave it,
+ * but mark it as deleted.
+ */
+void pm_undo_add_device(int id)
+{
+ /* Clear some fields (not all are strictly necessary) */
+ pm_descriptors[id].deleted = TRUE;
+ pm_descriptors[id].descriptor = NULL;
+ pm_descriptors[id].pm_internal = NULL;
+
+ if (id == pm_descriptor_len - 1) {
+ pm_free(pm_descriptors[id].pub.name);
+ pm_descriptor_len--;
+ }
}
@@ -144,7 +293,7 @@ int pm_find_default_device(char *pattern, int is_input)
int id = pmNoDevice;
int i;
/* first parse pattern into name, interf parts */
- char *interf_pref = (char *)""; /* initially assume it is not there */
+ char const *interf_pref = ""; /* initially assume it is not there */
char *name_pref = strstr(pattern, ", ");
if (name_pref) { /* found separator, adjust the pointer */
@@ -154,7 +303,7 @@ int pm_find_default_device(char *pattern, int is_input)
} else {
name_pref = pattern; /* whole string is the name pattern */
}
- for (i = 0; i < pm_descriptor_index; i++) {
+ for (i = 0; i < pm_descriptor_len; i++) {
const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
if (info->input == is_input &&
strstr(info->name, name_pref) &&
@@ -173,56 +322,71 @@ portmidi implementation
====================================================================
*/
-PMEXPORT int Pm_CountDevices( void ) {
+PMEXPORT int Pm_CountDevices(void)
+{
Pm_Initialize();
/* no error checking -- Pm_Initialize() does not fail */
- return pm_descriptor_index;
+ return pm_descriptor_len;
}
-PMEXPORT const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ) {
+PMEXPORT const PmDeviceInfo* Pm_GetDeviceInfo(PmDeviceID id)
+{
Pm_Initialize(); /* no error check needed */
- if (id >= 0 && id < pm_descriptor_index) {
- return &descriptors[id].pub;
+ if (id >= 0 && id < pm_descriptor_len && !pm_descriptors[id].deleted) {
+ return &pm_descriptors[id].pub;
}
return NULL;
}
/* pm_success_fn -- "noop" function pointer */
-PmError pm_success_fn(PmInternal *midi) {
+PmError pm_success_fn(PmInternal *midi)
+{
return pmNoError;
}
/* none_write -- returns an error if called */
-PmError none_write_short(PmInternal *midi, PmEvent *buffer) {
+PmError none_write_short(PmInternal *midi, PmEvent *buffer)
+{
return pmBadPtr;
}
/* pm_fail_timestamp_fn -- placeholder for begin_sysex and flush */
-PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp) {
+PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp)
+{
return pmBadPtr;
}
PmError none_write_byte(PmInternal *midi, unsigned char byte,
- PmTimestamp timestamp) {
+ PmTimestamp timestamp)
+{
return pmBadPtr;
}
/* pm_fail_fn -- generic function, returns error if called */
-PmError pm_fail_fn(PmInternal *midi) {
+PmError pm_fail_fn(PmInternal *midi)
+{
return pmBadPtr;
}
-static PmError none_open(PmInternal *midi, void *driverInfo) {
+static PmError none_open(PmInternal *midi, void *driverInfo)
+{
return pmBadPtr;
}
-static void none_get_host_error(PmInternal * midi, char * msg, unsigned int len) {
+
+static void none_get_host_error(PmInternal * midi, char * msg,
+ unsigned int len)
+{
*msg = 0; // empty string
}
-static unsigned int none_has_host_error(PmInternal * midi) {
+
+static unsigned int none_check_host_error(PmInternal * midi)
+{
return FALSE;
}
-PmTimestamp none_synchronize(PmInternal *midi) {
+
+PmTimestamp none_synchronize(PmInternal *midi)
+{
return 0;
}
@@ -238,15 +402,15 @@ pm_fns_node pm_none_dictionary = {
none_write_flush,
none_synchronize,
none_open,
- none_abort,
+ none_abort,
none_close,
none_poll,
- none_has_host_error,
- none_get_host_error
+ none_check_host_error,
};
-PMEXPORT const char *Pm_GetErrorText( PmError errnum ) {
+PMEXPORT const char *Pm_GetErrorText(PmError errnum)
+{
const char *msg;
switch(errnum)
@@ -255,47 +419,59 @@ PMEXPORT const char *Pm_GetErrorText( PmError errnum ) {
msg = "";
break;
case pmHostError:
- msg = "PortMidi: `Host error'";
+ msg = "PortMidi: Host error";
break;
case pmInvalidDeviceId:
- msg = "PortMidi: `Invalid device ID'";
+ msg = "PortMidi: Invalid device ID";
break;
case pmInsufficientMemory:
- msg = "PortMidi: `Insufficient memory'";
+ msg = "PortMidi: Insufficient memory";
break;
case pmBufferTooSmall:
- msg = "PortMidi: `Buffer too small'";
+ msg = "PortMidi: Buffer too small";
break;
case pmBadPtr:
- msg = "PortMidi: `Bad pointer'";
+ msg = "PortMidi: Bad pointer";
break;
case pmInternalError:
- msg = "PortMidi: `Internal PortMidi Error'";
+ msg = "PortMidi: Internal PortMidi Error";
break;
case pmBufferOverflow:
- msg = "PortMidi: `Buffer overflow'";
+ msg = "PortMidi: Buffer overflow";
break;
case pmBadData:
- msg = "PortMidi: `Invalid MIDI message Data'";
+ msg = "PortMidi: Invalid MIDI message Data";
break;
case pmBufferMaxSize:
- msg = "PortMidi: `Buffer cannot be made larger'";
+ msg = "PortMidi: Buffer cannot be made larger";
+ break;
+ case pmNotImplemented:
+ msg = "PortMidi: Function is not implemented";
+ break;
+ case pmInterfaceNotSupported:
+ msg = "PortMidi: Interface not supported";
break;
- default:
- msg = "PortMidi: `Illegal error number'";
+ case pmNameConflict:
+ msg = "PortMidi: Cannot create virtual device: name is taken";
+ break;
+ default:
+ msg = "PortMidi: Illegal error number";
break;
}
return msg;
}
-/* This can be called whenever you get a pmHostError return value.
+/* This can be called whenever you get a pmHostError return value
+ * or TRUE from Pm_HasHostError().
* The error will always be in the global pm_hosterror_text.
*/
-PMEXPORT void Pm_GetHostErrorText(char * msg, unsigned int len) {
+PMEXPORT void Pm_GetHostErrorText(char * msg, unsigned int len)
+{
assert(msg);
assert(len > 0);
if (pm_hosterror) {
+#pragma warning(suppress: 4996) // don't use suggested strncpy_s
strncpy(msg, (char *) pm_hosterror_text, len);
pm_hosterror = FALSE;
pm_hosterror_text[0] = 0; /* clear the message; not necessary, but it
@@ -307,24 +483,22 @@ PMEXPORT void Pm_GetHostErrorText(char * msg, unsigned int len) {
}
-PMEXPORT int Pm_HasHostError(PortMidiStream * stream) {
+PMEXPORT int Pm_HasHostError(PortMidiStream * stream)
+{
if (pm_hosterror) return TRUE;
if (stream) {
PmInternal * midi = (PmInternal *) stream;
- pm_hosterror = (*midi->dictionary->has_host_error)(midi);
- if (pm_hosterror) {
- midi->dictionary->host_error(midi, pm_hosterror_text,
- PM_HOST_ERROR_MSG_LEN);
- /* now error message is global */
- return TRUE;
- }
+ return (*midi->dictionary->check_host_error)(midi);
}
return FALSE;
}
-PMEXPORT PmError Pm_Initialize( void ) {
+PMEXPORT PmError Pm_Initialize(void)
+{
if (!pm_initialized) {
+ pm_descriptor_len = 0;
+ pm_interf_list_len = 0;
pm_hosterror = FALSE;
pm_hosterror_text[0] = 0; /* the null string */
pm_init();
@@ -334,16 +508,24 @@ PMEXPORT PmError Pm_Initialize( void ) {
}
-PMEXPORT PmError Pm_Terminate( void ) {
+PMEXPORT PmError Pm_Terminate(void)
+{
if (pm_initialized) {
pm_term();
- // if there are no devices, descriptors might still be NULL
- if (descriptors != NULL) {
- free(descriptors);
- descriptors = NULL;
+ /* if there are no devices, pm_descriptors might still be NULL */
+ if (pm_descriptors != NULL) {
+ int i; /* free names copied into pm_descriptors */
+ for (i = 0; i < pm_descriptor_len; i++) {
+ if (pm_descriptors[i].pub.name) {
+ pm_free(pm_descriptors[i].pub.name);
+ }
+ }
+ pm_free(pm_descriptors);
+ pm_descriptors = NULL;
}
- pm_descriptor_index = 0;
+ pm_descriptor_len = 0;
pm_descriptor_max = 0;
+ pm_interf_list_len = 0;
pm_initialized = FALSE;
}
return pmNoError;
@@ -354,7 +536,8 @@ PMEXPORT PmError Pm_Terminate( void ) {
/*
* returns number of messages actually read, or error code
*/
-PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length) {
+PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length)
+{
PmInternal *midi = (PmInternal *) stream;
int n = 0;
PmError err = pmNoError;
@@ -362,9 +545,9 @@ PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length) {
/* arg checking */
if(midi == NULL)
err = pmBadPtr;
- else if(!descriptors[midi->device_id].pub.opened)
+ else if(!pm_descriptors[midi->device_id].pub.opened)
err = pmBadPtr;
- else if(!descriptors[midi->device_id].pub.input)
+ else if(!pm_descriptors[midi->device_id].pub.input)
err = pmBadPtr;
/* First poll for data in the buffer...
* This either simply checks for data, or attempts first to fill the buffer
@@ -375,9 +558,7 @@ PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length) {
if (err != pmNoError) {
if (err == pmHostError) {
- midi->dictionary->host_error(midi, pm_hosterror_text,
- PM_HOST_ERROR_MSG_LEN);
- pm_hosterror = TRUE;
+ midi->dictionary->check_host_error(midi);
}
return pm_errmsg(err);
}
@@ -395,7 +576,7 @@ PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length) {
return n;
}
-PMEXPORT PmError Pm_Poll( PortMidiStream *stream )
+PMEXPORT PmError Pm_Poll(PortMidiStream *stream)
{
PmInternal *midi = (PmInternal *) stream;
PmError err;
@@ -404,23 +585,18 @@ PMEXPORT PmError Pm_Poll( PortMidiStream *stream )
/* arg checking */
if(midi == NULL)
err = pmBadPtr;
- else if (!descriptors[midi->device_id].pub.opened)
+ else if (!pm_descriptors[midi->device_id].pub.opened)
err = pmBadPtr;
- else if (!descriptors[midi->device_id].pub.input)
+ else if (!pm_descriptors[midi->device_id].pub.input)
err = pmBadPtr;
else
err = (*(midi->dictionary->poll))(midi);
if (err != pmNoError) {
- if (err == pmHostError) {
- midi->dictionary->host_error(midi, pm_hosterror_text,
- PM_HOST_ERROR_MSG_LEN);
- pm_hosterror = TRUE;
- }
return pm_errmsg(err);
}
- return !Pm_QueueEmpty(midi->queue);
+ return (PmError) !Pm_QueueEmpty(midi->queue);
}
@@ -432,11 +608,6 @@ static PmError pm_end_sysex(PmInternal *midi)
{
PmError err = (*midi->dictionary->end_sysex)(midi, 0);
midi->sysex_in_progress = FALSE;
- if (err == pmHostError) {
- midi->dictionary->host_error(midi, pm_hosterror_text,
- PM_HOST_ERROR_MSG_LEN);
- pm_hosterror = TRUE;
- }
return err;
}
@@ -445,7 +616,8 @@ static PmError pm_end_sysex(PmInternal *midi)
Pm_WriteSysEx all operate a state machine that "outputs" calls to
write_short, begin_sysex, write_byte, end_sysex, and write_realtime */
-PMEXPORT PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, int32_t length)
+PMEXPORT PmError Pm_Write(PortMidiStream *stream, PmEvent *buffer,
+ int32_t length)
{
PmInternal *midi = (PmInternal *) stream;
PmError err = pmNoError;
@@ -456,9 +628,9 @@ PMEXPORT PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, int32_t leng
/* arg checking */
if(midi == NULL)
err = pmBadPtr;
- else if(!descriptors[midi->device_id].pub.opened)
+ else if(!pm_descriptors[midi->device_id].pub.opened)
err = pmBadPtr;
- else if(!descriptors[midi->device_id].pub.output)
+ else if(!pm_descriptors[midi->device_id].pub.output)
err = pmBadPtr;
else
err = pmNoError;
@@ -569,16 +741,15 @@ PMEXPORT PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, int32_t leng
err = (*midi->dictionary->write_flush)(midi, 0);
pm_write_error:
if (err == pmHostError) {
- midi->dictionary->host_error(midi, pm_hosterror_text,
- PM_HOST_ERROR_MSG_LEN);
- pm_hosterror = TRUE;
+ midi->dictionary->check_host_error(midi);
}
error_exit:
return pm_errmsg(err);
}
-PMEXPORT PmError Pm_WriteShort(PortMidiStream *stream, PmTimestamp when, PmMessage msg)
+PMEXPORT PmError Pm_WriteShort(PortMidiStream *stream, PmTimestamp when,
+ PmMessage msg)
{
PmEvent event;
@@ -666,79 +837,110 @@ end_of_sysex:
-PMEXPORT PmError Pm_OpenInput(PortMidiStream** stream,
- PmDeviceID inputDevice,
- void *inputDriverInfo,
- int32_t bufferSize,
- PmTimeProcPtr time_proc,
- void *time_info)
+PmError pm_create_internal(PmInternal **stream, PmDeviceID device_id,
+ int is_input, int latency, PmTimeProcPtr time_proc,
+ void *time_info, int buffer_size)
{
PmInternal *midi;
- PmError err = pmNoError;
- pm_hosterror = FALSE;
- *stream = NULL;
-
- /* arg checking */
- if (inputDevice < 0 || inputDevice >= pm_descriptor_index)
- err = pmInvalidDeviceId;
- else if (!descriptors[inputDevice].pub.input)
- err = pmInvalidDeviceId;
- else if(descriptors[inputDevice].pub.opened)
- err = pmInvalidDeviceId;
-
- if (err != pmNoError)
- goto error_return;
-
+ if (device_id < 0 || device_id >= pm_descriptor_len) {
+ return pmInvalidDeviceId;
+ }
+ if (latency < 0) { /* force a legal value */
+ latency = 0;
+ }
/* create portMidi internal data */
midi = (PmInternal *) pm_alloc(sizeof(PmInternal));
*stream = midi;
if (!midi) {
- err = pmInsufficientMemory;
- goto error_return;
+ return pmInsufficientMemory;
}
- midi->device_id = inputDevice;
- midi->write_flag = FALSE;
+ midi->device_id = device_id;
+ midi->is_input = is_input;
midi->time_proc = time_proc;
+ /* if latency != 0, we need a time reference for output.
+ we always need a time reference for input.
+ If none is provided, use PortTime library */
+ if (time_proc == NULL && (latency != 0 || is_input)) {
+ if (!Pt_Started())
+ Pt_Start(1, 0, 0);
+ /* time_get does not take a parameter, so coerce */
+ midi->time_proc = (PmTimeProcPtr) Pt_Time;
+ }
midi->time_info = time_info;
- /* windows adds timestamps in the driver and these are more accurate than
- using a time_proc, so do not automatically provide a time proc. Non-win
- implementations may want to provide a default time_proc in their
- system-specific midi_out_open() method.
- */
- if (bufferSize <= 0) bufferSize = 256; /* default buffer size */
- midi->queue = Pm_QueueCreate(bufferSize, (int32_t) sizeof(PmEvent));
- if (!midi->queue) {
- /* free portMidi data */
- *stream = NULL;
- pm_free(midi);
- err = pmInsufficientMemory;
- goto error_return;
+ if (is_input) {
+ midi->latency = 0; /* unused by input */
+ if (buffer_size <= 0) buffer_size = 256; /* default buffer size */
+ midi->queue = Pm_QueueCreate(buffer_size, (int32_t) sizeof(PmEvent));
+ if (!midi->queue) {
+ /* free portMidi data */
+ *stream = NULL;
+ pm_free(midi);
+ return pmInsufficientMemory;
+ }
+ } else {
+ /* if latency zero, output immediate (timestamps ignored) */
+ /* if latency < 0, use 0 but don't return an error */
+ if (latency < 0) latency = 0;
+ midi->latency = latency;
+ midi->queue = NULL; /* unused by output; input needs to allocate: */
}
- midi->buffer_len = bufferSize; /* portMidi input storage */
- midi->latency = 0; /* not used */
+ midi->buffer_len = buffer_size; /* portMidi input storage */
midi->sysex_in_progress = FALSE;
midi->sysex_message = 0;
midi->sysex_message_count = 0;
- midi->filters = PM_FILT_ACTIVE;
+ midi->filters = (is_input ? PM_FILT_ACTIVE : 0);
midi->channel_mask = 0xFFFF;
midi->sync_time = 0;
midi->first_message = TRUE;
- midi->dictionary = descriptors[inputDevice].dictionary;
+ midi->api_info = NULL;
midi->fill_base = NULL;
midi->fill_offset_ptr = NULL;
midi->fill_length = 0;
- descriptors[inputDevice].internalDescriptor = midi;
+ midi->dictionary = pm_descriptors[device_id].dictionary;
+ pm_descriptors[device_id].pm_internal = midi;
+ return pmNoError;
+}
+
+
+PMEXPORT PmError Pm_OpenInput(PortMidiStream** stream,
+ PmDeviceID inputDevice,
+ void *inputDriverInfo,
+ int32_t bufferSize,
+ PmTimeProcPtr time_proc,
+ void *time_info)
+{
+ PmInternal *midi = NULL;
+ PmError err = pmNoError;
+ pm_hosterror = FALSE;
+ *stream = NULL; /* invariant: *stream == midi */
+
+ /* arg checking */
+ if (!pm_descriptors[inputDevice].pub.input)
+ err = pmInvalidDeviceId;
+ else if (pm_descriptors[inputDevice].pub.opened)
+ err = pmInvalidDeviceId;
+ if (err != pmNoError)
+ goto error_return;
+
+ /* common initialization of PmInternal structure (midi): */
+ err = pm_create_internal(&midi, inputDevice, TRUE, 0, time_proc,
+ time_info, bufferSize);
+ *stream = midi;
+ if (err) {
+ goto error_return;
+ }
+
/* open system dependent input device */
err = (*midi->dictionary->open)(midi, inputDriverInfo);
if (err) {
*stream = NULL;
- descriptors[inputDevice].internalDescriptor = NULL;
+ pm_descriptors[inputDevice].pm_internal = NULL;
/* free portMidi data */
Pm_QueueDestroy(midi->queue);
pm_free(midi);
} else {
/* portMidi input open successful */
- descriptors[inputDevice].pub.opened = TRUE;
+ pm_descriptors[inputDevice].pub.opened = TRUE;
}
error_return:
/* note: if there is a pmHostError, it is the responsibility
@@ -750,12 +952,12 @@ error_return:
PMEXPORT PmError Pm_OpenOutput(PortMidiStream** stream,
- PmDeviceID outputDevice,
- void *outputDriverInfo,
- int32_t bufferSize,
- PmTimeProcPtr time_proc,
- void *time_info,
- int32_t latency )
+ PmDeviceID outputDevice,
+ void *outputDriverInfo,
+ int32_t bufferSize,
+ PmTimeProcPtr time_proc,
+ void *time_info,
+ int32_t latency)
{
PmInternal *midi;
PmError err = pmNoError;
@@ -763,62 +965,33 @@ PMEXPORT PmError Pm_OpenOutput(PortMidiStream** stream,
*stream = NULL;
/* arg checking */
- if (outputDevice < 0 || outputDevice >= pm_descriptor_index)
+ if (outputDevice < 0 || outputDevice >= pm_descriptor_len)
err = pmInvalidDeviceId;
- else if (!descriptors[outputDevice].pub.output)
+ else if (!pm_descriptors[outputDevice].pub.output)
err = pmInvalidDeviceId;
- else if (descriptors[outputDevice].pub.opened)
+ else if (pm_descriptors[outputDevice].pub.opened)
err = pmInvalidDeviceId;
if (err != pmNoError)
goto error_return;
- /* create portMidi internal data */
- midi = (PmInternal *) pm_alloc(sizeof(PmInternal));
- *stream = midi;
- if (!midi) {
- err = pmInsufficientMemory;
+ /* common initialization of PmInternal structure (midi): */
+ err = pm_create_internal(&midi, outputDevice, FALSE, latency, time_proc,
+ time_info, bufferSize);
+ *stream = midi;
+ if (err) {
goto error_return;
}
- midi->device_id = outputDevice;
- midi->write_flag = TRUE;
- midi->time_proc = time_proc;
- /* if latency > 0, we need a time reference. If none is provided,
- use PortTime library */
- if (time_proc == NULL && latency != 0) {
- if (!Pt_Started())
- Pt_Start(1, 0, 0);
- /* time_get does not take a parameter, so coerce */
- midi->time_proc = (PmTimeProcPtr) Pt_Time;
- }
- midi->time_info = time_info;
- midi->buffer_len = bufferSize;
- midi->queue = NULL; /* unused by output */
- /* if latency zero, output immediate (timestamps ignored) */
- /* if latency < 0, use 0 but don't return an error */
- if (latency < 0) latency = 0;
- midi->latency = latency;
- midi->sysex_in_progress = FALSE;
- midi->sysex_message = 0; /* unused by output */
- midi->sysex_message_count = 0; /* unused by output */
- midi->filters = 0; /* not used for output */
- midi->channel_mask = 0xFFFF;
- midi->sync_time = 0;
- midi->first_message = TRUE;
- midi->dictionary = descriptors[outputDevice].dictionary;
- midi->fill_base = NULL;
- midi->fill_offset_ptr = NULL;
- midi->fill_length = 0;
- descriptors[outputDevice].internalDescriptor = midi;
+
/* open system dependent output device */
err = (*midi->dictionary->open)(midi, outputDriverInfo);
if (err) {
*stream = NULL;
- descriptors[outputDevice].internalDescriptor = NULL;
+ pm_descriptors[outputDevice].pm_internal = NULL;
/* free portMidi data */
pm_free(midi);
} else {
/* portMidi input open successful */
- descriptors[outputDevice].pub.opened = TRUE;
+ pm_descriptors[outputDevice].pub.opened = TRUE;
}
error_return:
/* note: system-dependent code must set pm_hosterror and
@@ -828,6 +1001,91 @@ error_return:
}
+static PmError create_virtual_device(const char *name, const char *interf,
+ void *device_info, int is_input)
+{
+ PmError err = pmNoError;
+ int i;
+ pm_hosterror = FALSE;
+
+ /* arg checking */
+ if (!name) {
+ err = pmInvalidDeviceId;
+ goto error_return;
+ }
+
+ Pm_Initialize(); /* just in case */
+
+ /* create the virtual device */
+ if (pm_interf_list_len == 0) {
+ return pmNotImplemented;
+ }
+ if (!interf) {
+ /* default interface is the first one */
+ interf = pm_interf_list[0].interf;
+ }
+ /* look up and call the create_fn for interf(ace), e.g. "CoreMIDI" */
+ for (i = 0; i < pm_interf_list_len; i++) {
+ if (strcmp(pm_interf_list[i].interf, interf) == 0) {
+ int id = (*pm_interf_list[i].create_fn)(is_input,
+ name, device_info);
+ /* id could be pmNameConflict or an actual descriptor index */
+ if (id >= 0) {
+ pm_descriptors[id].pub.is_virtual = TRUE;
+ }
+ err = id;
+ goto error_return;
+ }
+ }
+ err = pmInterfaceNotSupported;
+
+error_return:
+ /* note: if there is a pmHostError, it is the responsibility
+ * of the system-dependent code (*midi->dictionary->open)()
+ * to set pm_hosterror and pm_hosterror_text
+ */
+ return pm_errmsg(err);
+}
+
+
+PMEXPORT PmError Pm_CreateVirtualInput(const char *name,
+ const char *interf,
+ void *deviceInfo)
+{
+ return create_virtual_device(name, interf, deviceInfo, TRUE);
+}
+
+PMEXPORT PmError Pm_CreateVirtualOutput(const char *name, const char *interf,
+ void *deviceInfo)
+{
+ return create_virtual_device(name, interf, deviceInfo, FALSE);
+}
+
+PmError Pm_DeleteVirtualDevice(PmDeviceID id)
+{
+ int i;
+ const char *interf = pm_descriptors[id].pub.interf;
+ PmError err = pmBadData; /* returned if we cannot find the interface-
+ * specific delete function */
+ /* arg checking */
+ if (id < 0 || id >= pm_descriptor_len ||
+ pm_descriptors[id].pub.opened || pm_descriptors[id].deleted) {
+ return pmInvalidDeviceId;
+ }
+ /* delete function pointer is in interfaces list */
+ for (i = 0; i < pm_interf_list_len; i++) {
+ if (strcmp(pm_interf_list[i].interf, interf) == 0) {
+ err = (*pm_interf_list[i].delete_fn)(id);
+ break;
+ }
+ }
+ pm_descriptors[id].deleted = TRUE;
+ /* (pm_internal should already be NULL because !pub.opened) */
+ pm_descriptors[id].pm_internal = NULL;
+ pm_descriptors[id].descriptor = NULL;
+ return err;
+}
+
PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask)
{
PmInternal *midi = (PmInternal *) stream;
@@ -842,14 +1100,15 @@ PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask)
}
-PMEXPORT PmError Pm_SetFilter(PortMidiStream *stream, int32_t filters) {
+PMEXPORT PmError Pm_SetFilter(PortMidiStream *stream, int32_t filters)
+{
PmInternal *midi = (PmInternal *) stream;
PmError err = pmNoError;
/* arg checking */
if (midi == NULL)
err = pmBadPtr;
- else if (!descriptors[midi->device_id].pub.opened)
+ else if (!pm_descriptors[midi->device_id].pub.opened)
err = pmBadPtr;
else
midi->filters = filters;
@@ -857,7 +1116,8 @@ PMEXPORT PmError Pm_SetFilter(PortMidiStream *stream, int32_t filters) {
}
-PMEXPORT PmError Pm_Close( PortMidiStream *stream ) {
+PMEXPORT PmError Pm_Close(PortMidiStream *stream)
+{
PmInternal *midi = (PmInternal *) stream;
PmError err = pmNoError;
@@ -866,10 +1126,10 @@ PMEXPORT PmError Pm_Close( PortMidiStream *stream ) {
if (midi == NULL) /* midi must point to something */
err = pmBadPtr;
/* if it is an open device, the device_id will be valid */
- else if (midi->device_id < 0 || midi->device_id >= pm_descriptor_index)
+ else if (midi->device_id < 0 || midi->device_id >= pm_descriptor_len)
err = pmBadPtr;
/* and the device should be in the opened state */
- else if (!descriptors[midi->device_id].pub.opened)
+ else if (!pm_descriptors[midi->device_id].pub.opened)
err = pmBadPtr;
if (err != pmNoError)
@@ -878,8 +1138,8 @@ PMEXPORT PmError Pm_Close( PortMidiStream *stream ) {
/* close the device */
err = (*midi->dictionary->close)(midi);
/* even if an error occurred, continue with cleanup */
- descriptors[midi->device_id].internalDescriptor = NULL;
- descriptors[midi->device_id].pub.opened = FALSE;
+ pm_descriptors[midi->device_id].pm_internal = NULL;
+ pm_descriptors[midi->device_id].pub.opened = FALSE;
if (midi->queue) Pm_QueueDestroy(midi->queue);
pm_free(midi);
error_return:
@@ -889,56 +1149,57 @@ error_return:
return pm_errmsg(err);
}
-PmError Pm_Synchronize( PortMidiStream* stream ) {
+PmError Pm_Synchronize(PortMidiStream* stream)
+{
PmInternal *midi = (PmInternal *) stream;
PmError err = pmNoError;
if (midi == NULL)
err = pmBadPtr;
- else if (!descriptors[midi->device_id].pub.output)
+ else if (!pm_descriptors[midi->device_id].pub.output)
err = pmBadPtr;
- else if (!descriptors[midi->device_id].pub.opened)
+ else if (!pm_descriptors[midi->device_id].pub.opened)
err = pmBadPtr;
else
midi->first_message = TRUE;
return err;
}
-PMEXPORT PmError Pm_Abort( PortMidiStream* stream ) {
+PMEXPORT PmError Pm_Abort(PortMidiStream* stream)
+{
PmInternal *midi = (PmInternal *) stream;
PmError err;
/* arg checking */
if (midi == NULL)
err = pmBadPtr;
- else if (!descriptors[midi->device_id].pub.output)
+ else if (!pm_descriptors[midi->device_id].pub.output)
err = pmBadPtr;
- else if (!descriptors[midi->device_id].pub.opened)
+ else if (!pm_descriptors[midi->device_id].pub.opened)
err = pmBadPtr;
else
err = (*midi->dictionary->abort)(midi);
if (err == pmHostError) {
- midi->dictionary->host_error(midi, pm_hosterror_text,
- PM_HOST_ERROR_MSG_LEN);
- pm_hosterror = TRUE;
+ midi->dictionary->check_host_error(midi);
}
return pm_errmsg(err);
}
-/* pm_channel_filtered returns non-zero if the channel mask is blocking the current channel */
+/* pm_channel_filtered returns non-zero if the channel mask is
+ blocking the current channel */
#define pm_channel_filtered(status, mask) \
((((status) & 0xF0) != 0xF0) && (!(Pm_Channel((status) & 0x0F) & (mask))))
-/* The following two functions will checks to see if a MIDI message matches
- the filtering criteria. Since the sysex routines only want to filter realtime messages,
- we need to have separate routines.
+/* The following two functions will checks to see if a MIDI message
+ matches the filtering criteria. Since the sysex routines only want
+ to filter realtime messages, we need to have separate routines.
*/
-/* pm_realtime_filtered returns non-zero if the filter will kill the current message.
- Note that only realtime messages are checked here.
+/* pm_realtime_filtered returns non-zero if the filter will kill the
+ current message. Note that only realtime messages are checked here.
*/
#define pm_realtime_filtered(status, filters) \
((((status) & 0xF0) == 0xF0) && ((1 << ((status) & 0xF)) & (filters)))
@@ -959,18 +1220,21 @@ PMEXPORT PmError Pm_Abort( PortMidiStream* stream ) {
}*/
-/* pm_status_filtered returns non-zero if a filter will kill the current message, based on status.
- Note that sysex and real time are not checked. It is up to the subsystem (winmm, core midi, alsa)
- to filter sysex, as it is handled more easily and efficiently at that level.
- Realtime message are filtered in pm_realtime_filtered.
+/* pm_status_filtered returns non-zero if a filter will kill the
+ current message, based on status. Note that sysex and real time are
+ not checked. It is up to the subsystem (winmm, core midi, alsa) to
+ filter sysex, as it is handled more easily and efficiently at that
+ level. Realtime message are filtered in pm_realtime_filtered.
*/
-#define pm_status_filtered(status, filters) ((1 << (16 + ((status) >> 4))) & (filters))
+#define pm_status_filtered(status, filters) \
+ ((1 << (16 + ((status) >> 4))) & (filters))
/*
return ((status == MIDI_NOTE_ON) && (filters & PM_FILT_NOTE))
|| ((status == MIDI_NOTE_OFF) && (filters & PM_FILT_NOTE))
- || ((status == MIDI_CHANNEL_AT) && (filters & PM_FILT_CHANNEL_AFTERTOUCH))
+ || ((status == MIDI_CHANNEL_AT) &&
+ (filters & PM_FILT_CHANNEL_AFTERTOUCH))
|| ((status == MIDI_POLY_AT) && (filters & PM_FILT_POLY_AFTERTOUCH))
|| ((status == MIDI_PROGRAM) && (filters & PM_FILT_PROGRAM))
|| ((status == MIDI_CONTROL) && (filters & PM_FILT_CONTROL))
diff --git a/3rdparty/portmidi/pm_common/portmidi.h b/3rdparty/portmidi/pm_common/portmidi.h
index c57432fae12..1a4c7e7d0ab 100644
--- a/3rdparty/portmidi/pm_common/portmidi.h
+++ b/3rdparty/portmidi/pm_common/portmidi.h
@@ -48,69 +48,43 @@ extern "C" {
*
* NOTES ON HOST ERROR REPORTING:
*
- * PortMidi errors (of type PmError) are generic, system-independent errors.
- * When an error does not map to one of the more specific PmErrors, the
- * catch-all code pmHostError is returned. This means that PortMidi has
- * retained a more specific system-dependent error code. The caller can
- * get more information by calling Pm_HasHostError() to test if there is
- * a pending host error, and Pm_GetHostErrorText() to get a text string
- * describing the error. Host errors are reported on a per-device basis
- * because only after you open a device does PortMidi have a place to
- * record the host error code. I.e. only
- * those routines that receive a (PortMidiStream *) argument check and
- * report errors. One exception to this is that Pm_OpenInput() and
- * Pm_OpenOutput() can report errors even though when an error occurs,
- * there is no PortMidiStream* to hold the error. Fortunately, both
- * of these functions return any error immediately, so we do not really
- * need per-device error memory. Instead, any host error code is stored
- * in a global, pmHostError is returned, and the user can call
- * Pm_GetHostErrorText() to get the error message (and the invalid stream
- * parameter will be ignored.) The functions
- * pm_init and pm_term do not fail or raise
- * errors. The job of pm_init is to locate all available devices so that
- * the caller can get information via PmDeviceInfo(). If an error occurs,
- * the device is simply not listed as available.
- *
- * Host errors come in two flavors:
- * a) host error
- * b) host error during callback
- * These can occur w/midi input or output devices. (b) can only happen
- * asynchronously (during callback routines), whereas (a) only occurs while
- * synchronously running PortMidi and any resulting system dependent calls.
- * Both (a) and (b) are reported by the next read or write call. You can
- * also query for asynchronous errors (b) at any time by calling
- * Pm_HasHostError().
+ * PortMidi errors (of type PmError) are generic,
+ * system-independent errors. When an error does not map to one of
+ * the more specific PmErrors, the catch-all code pmHostError is
+ * returned. This means that PortMidi has retained a more specific
+ * system-dependent error code. The caller can get more information
+ * by calling Pm_GetHostErrorText() to get a text string describing
+ * the error. Host errors can arise asynchronously from callbacks,
+ * * so there is no specific return code. Asynchronous errors are
+ * checked and reported by Pm_Poll. You can also check by calling
+ * Pm_HasHostError(). If this returns TRUE, Pm_GetHostErrorText()
+ * will return a text description of the error.
*
* NOTES ON COMPILE-TIME SWITCHES
*
- * DEBUG assumes stdio and a console. Use this if you want automatic, simple
- * error reporting, e.g. for prototyping. If you are using MFC or some
- * other graphical interface with no console, DEBUG probably should be
- * undefined.
- * PM_CHECK_ERRORS more-or-less takes over error checking for return values,
- * stopping your program and printing error messages when an error
- * occurs. This also uses stdio for console text I/O.
+ * DEBUG assumes stdio and a console. Use this if you want
+ * automatic, simple error reporting, e.g. for prototyping. If
+ * you are using MFC or some other graphical interface with no
+ * console, DEBUG probably should be undefined.
+ * PM_CHECK_ERRORS more-or-less takes over error checking for
+ * return values, stopping your program and printing error
+ * messages when an error occurs. This also uses stdio for
+ * console text I/O. You can selectively disable this error
+ * checking by declaring extern int pm_check_errors; and
+ * setting pm_check_errors = FALSE; You can also reenable.
*/
+/**
+ \defgroup grp_basics Basic Definitions
+ @{
+*/
-#ifndef WIN32
-// Linux and OS X have stdint.h
#include <stdint.h>
-#else
-#ifndef INT32_DEFINED
-// rather than having users install a special .h file for windows,
-// just put the required definitions inline here. porttime.h uses
-// these too, so the definitions are (unfortunately) duplicated there
-typedef int int32_t;
-typedef unsigned int uint32_t;
-#define INT32_DEFINED
-#endif
-#endif
-//#ifdef _WINDLL
-//#define PMEXPORT __declspec(dllexport)
-//#else
+#ifdef _WINDLL
+#define PMEXPORT __declspec(dllexport)
+#else
#define PMEXPORT
-//#endif
+#endif
#ifndef FALSE
#define FALSE 0
@@ -122,113 +96,206 @@ typedef unsigned int uint32_t;
/* default size of buffers for sysex transmission: */
#define PM_DEFAULT_SYSEX_BUFFER_SIZE 1024
-/** List of portmidi errors.*/
+
typedef enum {
- pmNoError = 0,
- pmNoData = 0, /**< A "no error" return that also indicates no data avail. */
- pmGotData = 1, /**< A "no error" return that also indicates data available */
+ pmNoError = 0, /**< Normal return value indicating no error. */
+ pmNoData = 0, /**< @brief No error, also indicates no data available.
+ * Use this constant where a value greater than zero would
+ * indicate data is available.
+ */
+ pmGotData = 1, /**< A "no error" return also indicating data available. */
pmHostError = -10000,
- pmInvalidDeviceId, /** out of range or
+ pmInvalidDeviceId, /**< Out of range or
* output device when input is requested or
* input device when output is requested or
- * device is already opened
+ * device is already opened.
*/
pmInsufficientMemory,
pmBufferTooSmall,
pmBufferOverflow,
- pmBadPtr, /* PortMidiStream parameter is NULL or
+ pmBadPtr, /**< #PortMidiStream parameter is NULL or
* stream is not opened or
* stream is output when input is required or
- * stream is input when output is required */
- pmBadData, /** illegal midi data, e.g. missing EOX */
+ * stream is input when output is required. */
+ pmBadData, /**< Illegal midi data, e.g., missing EOX. */
pmInternalError,
- pmBufferMaxSize /** buffer is already as large as it can be */
- /* NOTE: If you add a new error type, be sure to update Pm_GetErrorText() */
-} PmError;
+ pmBufferMaxSize, /**< Buffer is already as large as it can be. */
+ pmNotImplemented, /**< The function is not implemented, nothing was done. */
+ pmInterfaceNotSupported, /**< The requested interface is not supported. */
+ pmNameConflict /**< Cannot create virtual device because name is taken. */
+ /* NOTE: If you add a new error type, you must update Pm_GetErrorText(). */
+} PmError; /**< @brief @enum PmError PortMidi error code; a common return type.
+ * No error is indicated by zero; errors are indicated by < 0.
+ */
/**
- Pm_Initialize() is the library initialisation function - call this before
+ Pm_Initialize() is the library initialization function - call this before
using the library.
+
+ *NOTE:* PortMidi scans for available devices when #Pm_Initialize
+ is called. To observe subsequent changes in the available
+ devices, you must shut down PortMidi by calling #Pm_Terminate and
+ then restart by calling #Pm_Initialize again. *IMPORTANT*: On
+ MacOS, #Pm_Initialize *must* always be called on the same
+ thread. Otherwise, changes in the available MIDI devices will
+ *not* be seen by PortMidi. As an example, if you start PortMidi in
+ a thread for processing MIDI, do not try to rescan devices by
+ calling #Pm_Initialize in a GUI thread. Instead, start PortMidi
+ the first time and every time in the GUI thread. Alternatively,
+ let the GUI request a restart in the MIDI thread. (These
+ restrictions only apply to macOS.) Speaking of threads, on all
+ platforms, you are allowed to call #Pm_Initialize in one thread,
+ yet send MIDI or poll for incoming MIDI in another
+ thread. However, PortMidi is not "thread safe," which means you
+ cannot allow threads to call PortMidi functions concurrently.
+
+ @return pmNoError.
+
+ PortMidi is designed to support multiple interfaces (such as ALSA,
+ CoreMIDI and WinMM). It is possible to return pmNoError because there
+ are no supported interfaces. In that case, zero devices will be
+ available.
*/
-PMEXPORT PmError Pm_Initialize( void );
+PMEXPORT PmError Pm_Initialize(void);
/**
Pm_Terminate() is the library termination function - call this after
using the library.
*/
-PMEXPORT PmError Pm_Terminate( void );
+PMEXPORT PmError Pm_Terminate(void);
-/** A single PortMidiStream is a descriptor for an open MIDI device.
-*/
+/** Represents an open MIDI device. */
typedef void PortMidiStream;
+
+/** A shorter form of #PortMidiStream. */
#define PmStream PortMidiStream
-/**
- Test whether stream has a pending host error. Normally, the client finds
- out about errors through returned error codes, but some errors can occur
- asynchronously where the client does not
- explicitly call a function, and therefore cannot receive an error code.
- The client can test for a pending error using Pm_HasHostError(). If true,
- the error can be accessed and cleared by calling Pm_GetErrorText().
- Errors are also cleared by calling other functions that can return
- errors, e.g. Pm_OpenInput(), Pm_OpenOutput(), Pm_Read(), Pm_Write(). The
- client does not need to call Pm_HasHostError(). Any pending error will be
- reported the next time the client performs an explicit function call on
- the stream, e.g. an input or output operation. Until the error is cleared,
- no new error codes will be obtained, even for a different stream.
+/** Test whether stream has a pending host error. Normally, the client
+ finds out about errors through returned error codes, but some
+ errors can occur asynchronously where the client does not
+ explicitly call a function, and therefore cannot receive an error
+ code. The client can test for a pending error using
+ Pm_HasHostError(). If true, the error can be accessed by calling
+ Pm_GetHostErrorText(). Pm_Poll() is similar to Pm_HasHostError(),
+ but if there is no error, it will return TRUE (1) if there is a
+ pending input message.
*/
-PMEXPORT int Pm_HasHostError( PortMidiStream * stream );
+PMEXPORT int Pm_HasHostError(PortMidiStream * stream);
-/** Translate portmidi error number into human readable message.
+/** Translate portmidi error number into human readable message.
These strings are constants (set at compile time) so client has
- no need to allocate storage
+ no need to allocate storage.
*/
-PMEXPORT const char *Pm_GetErrorText( PmError errnum );
+PMEXPORT const char *Pm_GetErrorText(PmError errnum);
-/** Translate portmidi host error into human readable message.
+/** Translate portmidi host error into human readable message.
These strings are computed at run time, so client has to allocate storage.
After this routine executes, the host error is cleared.
*/
PMEXPORT void Pm_GetHostErrorText(char * msg, unsigned int len);
-#define HDRLENGTH 50
-#define PM_HOST_ERROR_MSG_LEN 256u /* any host error msg will occupy less
- than this number of characters */
-
-/**
- Device enumeration mechanism.
-
- Device ids range from 0 to Pm_CountDevices()-1.
+/** Any host error msg has at most this many characters, including EOS. */
+#define PM_HOST_ERROR_MSG_LEN 256u
+/** Devices are represented as small integers. Device ids range from 0
+ to Pm_CountDevices()-1. Pm_GetDeviceInfo() is used to get information
+ about the device, and Pm_OpenInput() and PmOpenOutput() are used to
+ open the device.
*/
typedef int PmDeviceID;
+
+/** This PmDeviceID (constant) value represents no device and may be
+ returned by Pm_GetDefaultInputDeviceID() or
+ Pm_GetDefaultOutputDeviceID() if no default exists.
+*/
#define pmNoDevice -1
-typedef struct {
- int structVersion; /**< this internal structure version */
- const char *interf; /**< underlying MIDI API, e.g. MMSystem or DirectX */
- const char *name; /**< device name, e.g. USB MidiSport 1x1 */
- int input; /**< true iff input is available */
- int output; /**< true iff output is available */
- int opened; /**< used by generic PortMidi code to do error checking on arguments */
+/** MIDI device information is returned in this structure, which is
+ owned by PortMidi and read-only to applications. See Pm_GetDeviceInfo().
+*/
+#define PM_DEVICEINFO_VERS 200
+typedef struct {
+ int structVersion; /**< @brief this internal structure version */
+ const char *interf; /**< @brief underlying MIDI API, e.g.
+ "MMSystem" or "DirectX" */
+ char *name; /**< @brief device name, e.g. "USB MidiSport 1x1" */
+ int input; /**< @brief true iff input is available */
+ int output; /**< @brief true iff output is available */
+ int opened; /**< @brief used by generic PortMidi for error checking */
+ int is_virtual; /**< @brief true iff this is/was a virtual device */
} PmDeviceInfo;
-/** Get devices count, ids range from 0 to Pm_CountDevices()-1. */
-PMEXPORT int Pm_CountDevices( void );
-/**
- Pm_GetDefaultInputDeviceID(), Pm_GetDefaultOutputDeviceID()
+/** Get devices count, ids range from 0 to Pm_CountDevices()-1. */
+PMEXPORT int Pm_CountDevices(void);
+/**
Return the default device ID or pmNoDevice if there are no devices.
The result (but not pmNoDevice) can be passed to Pm_OpenMidi().
- The default device can be specified using a small application
- named pmdefaults that is part of the PortMidi distribution. This
- program in turn uses the Java Preferences object created by
+ The use of these functions is not recommended. There is no natural
+ "default device" on any system, so defaults must be set by users.
+ (Currently, PortMidi just returns the first device it finds as
+ "default".) The (unsolved) problem is how to implement simple
+ preferences for a cross-platform library. (More notes follow, but
+ you can stop reading here.)
+
+ To implement preferences, you need (1) a standard place to put
+ them, (2) a representation for the preferences, (3) a graphical
+ interface to test and set preferences, (4) a "natural" way to
+ invoke the preference-setting program. To solve (3), PortMidi
+ originally chose to use Java and Swing to implement a
+ cross-platform GUI program called "pmdefaults." Java's Preferences
+ class already provide a location (problem 1) and representation
+ (problem 2). However, this solution was complex, requiring
+ PortMidi to parse binary Java preference files and requiring users
+ to install and invoke Java programs. It did not seem possible to
+ integrate pmdefaults into the system preference subsystems on
+ macOS, Windows, and Linux, so the user had to install and run
+ pmdefaults as an application. Moreover, Java is falling out of
+ favor.
+
+ A simpler solution is pass the burden to applications. It is easy
+ to scan devices with PortMidi and build a device menu, and to save
+ menu selections in application preferences for next time. This is
+ my recommendation for any GUI program. For simple command-line
+ applications and utilities, see pm_test where all the test
+ programs now accept device numbers on the command line and/or
+ prompt for their entry.
+
+ Some advice for preferences: MIDI devices used to be built-in or
+ plug-in cards, so the numbers rarely changed. Now MIDI devices are
+ often plug-in USB devices, so device numbers change, and you
+ probably need to design to reinitialize PortMidi to rescan
+ devices. MIDI is pretty stateless, so this isn't a big problem,
+ although it means you cannot find a new device while playing or
+ recording MIDI.
+
+ Since device numbering can change whenever a USB device is plugged
+ in, preferences should record *names* of devices rather than
+ device numbers. It is simple enough to use string matching to find
+ a prefered device, so PortMidi does not provide any built-in
+ lookup function. See below for details of the Java preferences API.
+
+ In the future, I would like to remove the legacy code that parses
+ Java preference data (macOS plist, linux prefs.xml, Windows
+ registry entries) and replace it with something more useful. Maybe
+ something really simple: $HOME/.portmidi? Or maybe a new
+ pmdefaults written with PyGame? Or use QT? If applications write
+ their own preferences, maybe a minimal command line preference
+ setter is all that's needed? Or maybe command line application
+ users are happy without a preference system? Comments and
+ proposals are welcome.
+
+ For completeness, here is a description of the original use of
+ Java for preference setting: The default device can be specified
+ using a small application named pmdefaults that is part of the
+ PortMidi distribution. This program in turn uses the Java
+ Preferences object created by
java.util.prefs.Preferences.userRoot().node("/PortMidi"); the
- preference is set by calling
- prefs.put("PM_RECOMMENDED_OUTPUT_DEVICE", prefName);
- or prefs.put("PM_RECOMMENDED_INPUT_DEVICE", prefName);
+ preference is set by calling
+ prefs.put("PM_RECOMMENDED_OUTPUT_DEVICE", prefName); or
+ prefs.put("PM_RECOMMENDED_INPUT_DEVICE", prefName);
In the statements above, prefName is a string describing the
MIDI device in the form "interf, name" where interf identifies
@@ -248,143 +315,274 @@ PMEXPORT int Pm_CountDevices( void );
the entire preference string is interpreted as a name, and the
interface part is the empty string, which matches anything.
- On the MAC, preferences are stored in
- /Users/$NAME/Library/Preferences/com.apple.java.util.prefs.plist
+ On the MAC, preferences are stored in
+ /Users/$NAME/Library/Preferences/com.apple.java.util.prefs.plist
which is a binary file. In addition to the pmdefaults program,
there are utilities that can read and edit this preference file.
-
- On the PC,
-
- On Linux,
-
+ On Windows, the Registry is used. On Linux, preferences are in an
+ XML file.
*/
-PMEXPORT PmDeviceID Pm_GetDefaultInputDeviceID( void );
-/** see PmDeviceID Pm_GetDefaultInputDeviceID() */
-PMEXPORT PmDeviceID Pm_GetDefaultOutputDeviceID( void );
+PMEXPORT PmDeviceID Pm_GetDefaultInputDeviceID(void);
-/**
- PmTimestamp is used to represent a millisecond clock with arbitrary
- start time. The type is used for all MIDI timestampes and clocks.
+/** @brief see PmDeviceID Pm_GetDefaultInputDeviceID() */
+PMEXPORT PmDeviceID Pm_GetDefaultOutputDeviceID(void);
+
+/** Represents a millisecond clock with arbitrary start time.
+ This type is used for all MIDI timestamps and clocks.
*/
typedef int32_t PmTimestamp;
typedef PmTimestamp (*PmTimeProcPtr)(void *time_info);
/** TRUE if t1 before t2 */
#define PmBefore(t1,t2) ((t1-t2) < 0)
+/** @} */
/**
\defgroup grp_device Input/Output Devices Handling
@{
*/
-/**
- Pm_GetDeviceInfo() returns a pointer to a PmDeviceInfo structure
- referring to the device specified by id.
- If id is out of range the function returns NULL.
+/** Get a PmDeviceInfo structure describing a MIDI device.
+
+ @param id the device to be queried.
- The returned structure is owned by the PortMidi implementation and must
- not be manipulated or freed. The pointer is guaranteed to be valid
- between calls to Pm_Initialize() and Pm_Terminate().
-*/
-PMEXPORT const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id );
+ If \p id is out of range or if the device designates a deleted
+ virtual device, the function returns NULL.
-/**
- Pm_OpenInput() and Pm_OpenOutput() open devices.
+ The returned structure is owned by the PortMidi implementation and
+ must not be manipulated or freed. The pointer is guaranteed to be
+ valid between calls to Pm_Initialize() and Pm_Terminate().
+*/
+PMEXPORT const PmDeviceInfo *Pm_GetDeviceInfo(PmDeviceID id);
- stream is the address of a PortMidiStream pointer which will receive
- a pointer to the newly opened stream.
+/** Open a MIDI device for input.
- inputDevice is the id of the device used for input (see PmDeviceID above).
+ @param stream the address of a #PortMidiStream pointer which will
+ receive a pointer to the newly opened stream.
- inputDriverInfo is a pointer to an optional driver specific data structure
- containing additional information for device setup or handle processing.
- inputDriverInfo is never required for correct operation. If not used
- inputDriverInfo should be NULL.
+ @param inputDevice the ID of the device to be opened (see #PmDeviceID).
- outputDevice is the id of the device used for output (see PmDeviceID above.)
+ @param inputDriverInfo a pointer to an optional driver-specific
+ data structure containing additional information for device setup
+ or handle processing. This parameter is never required for correct
+ operation. If not used, specify NULL.
- outputDriverInfo is a pointer to an optional driver specific data structure
- containing additional information for device setup or handle processing.
- outputDriverInfo is never required for correct operation. If not used
- outputDriverInfo should be NULL.
+ @param bufferSize the number of input events to be buffered
+ waiting to be read using Pm_Read(). Messages will be lost if the
+ number of unread messages exceeds this value.
- For input, the buffersize specifies the number of input events to be
- buffered waiting to be read using Pm_Read(). For output, buffersize
- specifies the number of output events to be buffered waiting for output.
- (In some cases -- see below -- PortMidi does not buffer output at all
- and merely passes data to a lower-level API, in which case buffersize
- is ignored.)
-
- latency is the delay in milliseconds applied to timestamps to determine
- when the output should actually occur. (If latency is < 0, 0 is assumed.)
- If latency is zero, timestamps are ignored and all output is delivered
- immediately. If latency is greater than zero, output is delayed until the
- message timestamp plus the latency. (NOTE: the time is measured relative
- to the time source indicated by time_proc. Timestamps are absolute,
- not relative delays or offsets.) In some cases, PortMidi can obtain
- better timing than your application by passing timestamps along to the
- device driver or hardware. Latency may also help you to synchronize midi
- data to audio data by matching midi latency to the audio buffer latency.
-
- time_proc is a pointer to a procedure that returns time in milliseconds. It
- may be NULL, in which case a default millisecond timebase (PortTime) is
- used. If the application wants to use PortTime, it should start the timer
- (call Pt_Start) before calling Pm_OpenInput or Pm_OpenOutput. If the
- application tries to start the timer *after* Pm_OpenInput or Pm_OpenOutput,
- it may get a ptAlreadyStarted error from Pt_Start, and the application's
+ @param time_proc (address of) a procedure that returns time in
+ milliseconds. It may be NULL, in which case a default millisecond
+ timebase (PortTime) is used. If the application wants to use
+ PortTime, it should start the timer (call Pt_Start) before calling
+ Pm_OpenInput or Pm_OpenOutput. If the application tries to start
+ the timer *after* Pm_OpenInput or Pm_OpenOutput, it may get a
+ ptAlreadyStarted error from Pt_Start, and the application's
preferred time resolution and callback function will be ignored.
- time_proc result values are appended to incoming MIDI data, and time_proc
- times are used to schedule outgoing MIDI data (when latency is non-zero).
+ \p time_proc result values are appended to incoming MIDI data,
+ normally by mapping system-provided timestamps to the \p time_proc
+ timestamps to maintain the precision of system-provided
+ timestamps.
- time_info is a pointer passed to time_proc.
+ @param time_info is a pointer passed to time_proc.
- Example: If I provide a timestamp of 5000, latency is 1, and time_proc
- returns 4990, then the desired output time will be when time_proc returns
- timestamp+latency = 5001. This will be 5001-4990 = 11ms from now.
-
- return value:
- Upon success Pm_Open() returns PmNoError and places a pointer to a
- valid PortMidiStream in the stream argument.
- If a call to Pm_Open() fails a nonzero error code is returned (see
- PMError above) and the value of port is invalid.
+ @return #pmNoError and places a pointer to a valid
+ #PortMidiStream in the stream argument. If the open operation
+ fails, a nonzero error code is returned (see #PMError) and
+ the value of stream is invalid.
Any stream that is successfully opened should eventually be closed
by calling Pm_Close().
-
*/
-PMEXPORT PmError Pm_OpenInput( PortMidiStream** stream,
+PMEXPORT PmError Pm_OpenInput(PortMidiStream** stream,
PmDeviceID inputDevice,
void *inputDriverInfo,
int32_t bufferSize,
PmTimeProcPtr time_proc,
- void *time_info );
+ void *time_info);
+
+/** Open a MIDI device for output.
+
+ @param stream the address of a #PortMidiStream pointer which will
+ receive a pointer to the newly opened stream.
+
+ @param outputDevice the ID of the device to be opened (see #PmDeviceID).
+
+ @param outputDriverInfo a pointer to an optional driver-specific
+ data structure containing additional information for device setup
+ or handle processing. This parameter is never required for correct
+ operation. If not used, specify NULL.
+
+ @param bufferSize the number of output events to be buffered
+ waiting for output. In some cases -- see below -- PortMidi does
+ not buffer output at all and merely passes data to a lower-level
+ API, in which case \p bufferSize is ignored. Since MIDI speeds now
+ vary from 1 to 50 or more messages per ms (over USB), put some
+ thought into this number. E.g. if latency is 20ms and you want to
+ burst 100 messages in that time (5000 messages per second), you
+ should set \p bufferSize to at least 100. The default on Windows
+ assumes an average rate of 500 messages per second and in this
+ example, output would be slowed waiting for free buffers.
+
+ @param latency the delay in milliseconds applied to timestamps
+ to determine when the output should actually occur. (If latency is
+ < 0, 0 is assumed.) If latency is zero, timestamps are ignored
+ and all output is delivered immediately. If latency is greater
+ than zero, output is delayed until the message timestamp plus the
+ latency. (NOTE: the time is measured relative to the time source
+ indicated by time_proc. Timestamps are absolute, not relative
+ delays or offsets.) In some cases, PortMidi can obtain better
+ timing than your application by passing timestamps along to the
+ device driver or hardware, so the best strategy to minimize jitter
+ is: wait until the real time to send the message, compute the
+ message, attach the *ideal* output time (not the current real
+ time, because some time may have elapsed), and send the
+ message. The \p latency will be added to the timestamp, and
+ provided the elapsed computation time has not exceeded \p latency,
+ the message will be delivered according to the timestamp. If the
+ real time is already past the timestamp, the message will be
+ delivered as soon as possible. Latency may also help you to
+ synchronize MIDI data to audio data by matching \p latency to the
+ audio buffer latency.
+
+ @param time_proc (address of) a pointer to a procedure that
+ returns time in milliseconds. It may be NULL, in which case a
+ default millisecond timebase (PortTime) is used. If the
+ application wants to use PortTime, it should start the timer (call
+ Pt_Start) before calling #Pm_OpenInput or #Pm_OpenOutput. If the
+ application tries to start the timer *after* #Pm_OpenInput or
+ #Pm_OpenOutput, it may get a #ptAlreadyStarted error from #Pt_Start,
+ and the application's preferred time resolution and callback
+ function will be ignored. \p time_proc times are used to schedule
+ outgoing MIDI data (when latency is non-zero), usually by mapping
+ from time_proc timestamps to internal system timestamps to
+ maintain the precision of system-supported timing.
+
+ @param time_info a pointer passed to time_proc.
+
+ @return #pmNoError and places a pointer to a valid #PortMidiStream
+ in the stream argument. If the operation fails, a nonzero error
+ code is returned (see PMError) and the value of \p stream is
+ invalid.
+
+ Note: ALSA appears to have a fixed-size priority queue for timed
+ output messages. Testing indicates the queue can hold a little
+ over 400 3-byte MIDI messages. Thus, you can send 10,000
+ messages/second if the latency is 30ms (30ms * 10000 msgs/sec *
+ 0.001 sec/ms = 300 msgs), but not if the latency is 50ms
+ (resulting in about 500 pending messages, which is greater than
+ the 400 message limit). Since timestamps in ALSA are relative,
+ they are of less value than absolute timestamps in macOS and
+ Windows. This is a limitation of ALSA and apparently a design
+ flaw.
+
+ Example 1: If I provide a timestamp of 5000, latency is 1, and
+ time_proc returns 4990, then the desired output time will be when
+ time_proc returns timestamp+latency = 5001. This will be 5001-4990
+ = 11ms from now.
+
+ Example 2: If I want to send at exactly 5010, and latency is 10, I
+ should wait until 5000, compute the messages and provide a
+ timestamp of 5000. As long as computation takes less than 10ms,
+ the message will be delivered at time 5010.
+
+ Example 3 (recommended): It is often convenient to ignore latency.
+ E.g. if a sequence says to output at time 5010, just wait until
+ 5010, compute the message and use 5010 for the timestamp. Delivery
+ will then be at 5010+latency, but unless you are synchronizing to
+ something else, the absolute delay by latency will not matter.
-PMEXPORT PmError Pm_OpenOutput( PortMidiStream** stream,
+ Any stream that is successfully opened should eventually be closed
+ by calling Pm_Close().
+*/
+PMEXPORT PmError Pm_OpenOutput(PortMidiStream** stream,
PmDeviceID outputDevice,
void *outputDriverInfo,
int32_t bufferSize,
PmTimeProcPtr time_proc,
void *time_info,
- int32_t latency );
+ int32_t latency);
+
+/** Create a virtual input device.
+
+ @param name gives the virtual device name, which is visible to
+ other applications.
+
+ @param interf is the interface (System API) used to create the
+ device Default interfaces are "MMSystem", "CoreMIDI" and
+ "ALSA". Currently, these are the only ones implemented, but future
+ implementations could support DirectMusic, Jack, sndio, or others.
+
+ @param deviceInfo contains interface-dependent additional
+ information, e.g., hints or options. There are none at present, and
+ NULL is the recommended value.
+
+ @return a device ID or #pmNameConflict (\p name is invalid or
+ already exists) or #pmInterfaceNotSupported (\p interf is does not
+ match a supported interface).
+
+ The created virtual device appears to other applications as if it
+ is an output device. The device must be opened to obtain a stream
+ and read from it.
+
+ Virtual devices are not supported by Windows (Multimedia API). Calls
+ on Windows do nothing except return #pmNotImplemented.
+*/
+PMEXPORT PmError Pm_CreateVirtualInput(const char *name,
+ const char *interf,
+ void *deviceInfo);
+
+/** Create a virtual output device.
+
+ @param name gives the virtual device name, which is visible to
+ other applications.
+
+ @param interf is the interface (System API) used to create the
+ device Default interfaces are "MMSystem", "CoreMIDI" and
+ "ALSA". Currently, these are the only ones implemented, but future
+ implementations could support DirectMusic, Jack, sndio, or others.
+
+ @param deviceInfo contains interface-dependent additional
+ information, e.g., hints or options. There are none at present, and
+ NULL is the recommended value.
+
+ @return a device ID or #pmInvalidDeviceId (\p name is invalid or
+ already exists) or #pmInterfaceNotSupported (\p interf is does not
+ match a supported interface).
+
+ The created virtual device appears to other applications as if it
+ is an input device. The device must be opened to obtain a stream
+ and write to it.
+
+ Virtual devices are not supported by Windows (Multimedia API). Calls
+ on Windows do nothing except return #pmNotImplemented.
+*/
+PMEXPORT PmError Pm_CreateVirtualOutput(const char *name,
+ const char *interf,
+ void *deviceInfo);
+
+/** Remove a virtual device.
+
+ @param device a device ID (small integer) designating the device.
+
+ The device is removed; other applications can no longer see or open
+ this virtual device, which may be either for input or output. The
+ device must not be open. The device ID may be reused, but existing
+ devices are not renumbered. This means that the device ID could be
+ in the range from 0 to #Pm_CountDevices(), yet the device ID does
+ not designate a device. In that case, passing the ID to
+ #Pm_GetDeviceInfo() will return NULL.
+
+ @return #pmNoError if the device was deleted or #pmInvalidDeviceId
+ if the device is open, already deleted, or \p device is out of
+ range.
+*/
+PMEXPORT PmError Pm_DeleteVirtualDevice(PmDeviceID device);
/** @} */
/**
- \defgroup grp_events_filters Events and Filters Handling
+ @defgroup grp_events_filters Events and Filters Handling
@{
*/
-/* \function PmError Pm_SetFilter( PortMidiStream* stream, int32_t filters )
- Pm_SetFilter() sets filters on an open input stream to drop selected
- input types. By default, only active sensing messages are filtered.
- To prohibit, say, active sensing and sysex messages, call
- Pm_SetFilter(stream, PM_FILT_ACTIVE | PM_FILT_SYSEX);
-
- Filtering is useful when midi routing or midi thru functionality is being
- provided by the user application.
- For example, you may want to exclude timing messages (clock, MTC, start/stop/continue),
- while allowing note-related messages to pass.
- Or you may be using a sequencer or drum-machine for MIDI clock information but want to
- exclude any notes it may play.
- */
-
/* Filter bit-mask definitions */
/** filter active sensing messages (0xFE): */
#define PM_FILT_ACTIVE (1 << 0x0E)
@@ -412,7 +610,8 @@ PMEXPORT PmError Pm_OpenOutput( PortMidiStream** stream,
/** per-note aftertouch (0xA0-0xAF) */
#define PM_FILT_POLY_AFTERTOUCH (1 << 0x1A)
/** filter both channel and poly aftertouch */
-#define PM_FILT_AFTERTOUCH (PM_FILT_CHANNEL_AFTERTOUCH | PM_FILT_POLY_AFTERTOUCH)
+#define PM_FILT_AFTERTOUCH (PM_FILT_CHANNEL_AFTERTOUCH | \
+ PM_FILT_POLY_AFTERTOUCH)
/** Program changes (0xC0-0xCF) */
#define PM_FILT_PROGRAM (1 << 0x1C)
/** Control Changes (CC's) (0xB0-0xBF)*/
@@ -425,20 +624,48 @@ PMEXPORT PmError Pm_OpenOutput( PortMidiStream** stream,
#define PM_FILT_SONG_POSITION (1 << 0x02)
/** Song Select (0xF3)*/
#define PM_FILT_SONG_SELECT (1 << 0x03)
-/** Tuning request (0xF6)*/
+/** Tuning request (0xF6) */
#define PM_FILT_TUNE (1 << 0x06)
/** All System Common messages (mtc, song position, song select, tune request) */
-#define PM_FILT_SYSTEMCOMMON (PM_FILT_MTC | PM_FILT_SONG_POSITION | PM_FILT_SONG_SELECT | PM_FILT_TUNE)
+#define PM_FILT_SYSTEMCOMMON (PM_FILT_MTC | PM_FILT_SONG_POSITION | \
+ PM_FILT_SONG_SELECT | PM_FILT_TUNE)
+
+
+/* Set filters on an open input stream to drop selected input types.
+
+ @param stream an open MIDI input stream.
+ @param filters indicate message types to filter (block).
-PMEXPORT PmError Pm_SetFilter( PortMidiStream* stream, int32_t filters );
+ @return #pmNoError or an error code.
+ By default, only active sensing messages are filtered.
+ To prohibit, say, active sensing and sysex messages, call
+ Pm_SetFilter(stream, PM_FILT_ACTIVE | PM_FILT_SYSEX);
+
+ Filtering is useful when midi routing or midi thru functionality
+ is being provided by the user application.
+ For example, you may want to exclude timing messages (clock, MTC,
+ start/stop/continue), while allowing note-related messages to pass.
+ Or you may be using a sequencer or drum-machine for MIDI clock
+ information but want to exclude any notes it may play.
+ */
+PMEXPORT PmError Pm_SetFilter(PortMidiStream* stream, int32_t filters);
+
+/** Create a mask that filters one channel. */
#define Pm_Channel(channel) (1<<(channel))
-/**
- Pm_SetChannelMask() filters incoming messages based on channel.
- The mask is a 16-bit bitfield corresponding to appropriate channels.
- The Pm_Channel macro can assist in calling this function.
- i.e. to set receive only input on channel 1, call with
+
+/** Filter incoming messages based on channel.
+
+ @param stream an open MIDI input stream.
+
+ @param mask indicates channels to be received.
+
+ @return #pmNoError or an error code.
+
+ The \p mask is a 16-bit bitfield corresponding to appropriate channels.
+ The #Pm_Channel macro can assist in calling this function.
+ I.e. to receive only input on channel 1, call with
Pm_SetChannelMask(Pm_Channel(1));
Multiple channels should be OR'd together, like
Pm_SetChannelMask(Pm_Channel(10) | Pm_Channel(11))
@@ -451,71 +678,94 @@ PMEXPORT PmError Pm_SetFilter( PortMidiStream* stream, int32_t filters );
*/
PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask);
-/**
- Pm_Abort() terminates outgoing messages immediately
- The caller should immediately close the output port;
- this call may result in transmission of a partial midi message.
- There is no abort for Midi input because the user can simply
- ignore messages in the buffer and close an input device at
- any time.
+/** Terminate outgoing messages immediately.
+
+ @param stream an open MIDI output stream.
+
+ @result #pmNoError or an error code.
+
+ The caller should immediately close the output port; this call may
+ result in transmission of a partial MIDI message. There is no
+ abort for Midi input because the user can simply ignore messages
+ in the buffer and close an input device at any time. If the
+ specified behavior cannot be achieved through the system-level
+ interface (ALSA, CoreMIDI, etc.), the behavior may be that of
+ Pm_Close().
*/
-PMEXPORT PmError Pm_Abort( PortMidiStream* stream );
+PMEXPORT PmError Pm_Abort(PortMidiStream* stream);
-/**
- Pm_Close() closes a midi stream, flushing any pending buffers.
- (PortMidi attempts to close open streams when the application
- exits -- this is particularly difficult under Windows.)
-*/
-PMEXPORT PmError Pm_Close( PortMidiStream* stream );
+/** Close a midi stream, flush any pending buffers if possible.
-/**
- Pm_Synchronize() instructs PortMidi to (re)synchronize to the
- time_proc passed when the stream was opened. Typically, this
- is used when the stream must be opened before the time_proc
- reference is actually advancing. In this case, message timing
- may be erratic, but since timestamps of zero mean
- "send immediately," initialization messages with zero timestamps
- can be written without a functioning time reference and without
- problems. Before the first MIDI message with a non-zero
- timestamp is written to the stream, the time reference must
- begin to advance (for example, if the time_proc computes time
- based on audio samples, time might begin to advance when an
- audio stream becomes active). After time_proc return values
- become valid, and BEFORE writing the first non-zero timestamped
- MIDI message, call Pm_Synchronize() so that PortMidi can observe
- the difference between the current time_proc value and its
- MIDI stream time.
+ @param stream an open MIDI input or output stream.
+
+ @result #pmNoError or an error code.
+
+ If the system-level interface (ALSA, CoreMIDI, etc.) does not
+ support flushing remaining messages, the behavior may be one of
+ the following (most preferred first): block until all pending
+ timestamped messages are delivered; deliver messages to a server
+ or kernel process for later delivery but return immediately; drop
+ messages (as in Pm_Abort()). Therefore, to be safe, applications
+ should wait until the output queue is empty before calling
+ Pm_Close(). E.g. calling Pt_Sleep(100 + latency); will give a
+ 100ms "cushion" beyond latency (if any) before closing.
+*/
+PMEXPORT PmError Pm_Close(PortMidiStream* stream);
+
+/** (re)synchronize to the time_proc passed when the stream was opened.
+
+ @param stream an open MIDI input or output stream.
+
+ @result #pmNoError or an error code.
+
+ Typically, this is used when the stream must be opened before the
+ time_proc reference is actually advancing. In this case, message
+ timing may be erratic, but since timestamps of zero mean "send
+ immediately," initialization messages with zero timestamps can be
+ written without a functioning time reference and without
+ problems. Before the first MIDI message with a non-zero timestamp
+ is written to the stream, the time reference must begin to advance
+ (for example, if the time_proc computes time based on audio
+ samples, time might begin to advance when an audio stream becomes
+ active). After time_proc return values become valid, and BEFORE
+ writing the first non-zero timestamped MIDI message, call
+ Pm_Synchronize() so that PortMidi can observe the difference
+ between the current time_proc value and its MIDI stream time.
- In the more normal case where time_proc
- values advance continuously, there is no need to call
- Pm_Synchronize. PortMidi will always synchronize at the
- first output message and periodically thereafter.
+ In the more normal case where time_proc values advance
+ continuously, there is no need to call #Pm_Synchronize. PortMidi
+ will always synchronize at the first output message and
+ periodically thereafter.
*/
-PmError Pm_Synchronize( PortMidiStream* stream );
+PMEXPORT PmError Pm_Synchronize(PortMidiStream* stream);
-/**
- Pm_Message() encodes a short Midi message into a 32-bit word. If data1
+/** Encode a short Midi message into a 32-bit word. If data1
and/or data2 are not present, use zero.
-
- Pm_MessageStatus(), Pm_MessageData1(), and
- Pm_MessageData2() extract fields from a 32-bit midi message.
*/
#define Pm_Message(status, data1, data2) \
((((data2) << 16) & 0xFF0000) | \
(((data1) << 8) & 0xFF00) | \
((status) & 0xFF))
+/** Extract the status field from a 32-bit midi message. */
#define Pm_MessageStatus(msg) ((msg) & 0xFF)
+/** Extract the 1st data field (e.g., pitch) from a 32-bit midi message. */
#define Pm_MessageData1(msg) (((msg) >> 8) & 0xFF)
+/** Extract the 2nd data field (e.g., velocity) from a 32-bit midi message. */
#define Pm_MessageData2(msg) (((msg) >> 16) & 0xFF)
-typedef int32_t PmMessage; /**< see PmEvent */
+typedef uint32_t PmMessage; /**< @brief see #PmEvent */
/**
- All midi data comes in the form of PmEvent structures. A sysex
+ All MIDI data comes in the form of PmEvent structures. A sysex
message is encoded as a sequence of PmEvent structures, with each
structure carrying 4 bytes of the message, i.e. only the first
PmEvent carries the status byte.
+ All other MIDI messages take 1 to 3 bytes and are encoded in a whole
+ PmMessage with status in the low-order byte and remaining bytes
+ unused, i.e., a 3-byte note-on message will occupy 3 low-order bytes
+ of PmMessage, leaving the high-order byte unused.
+
Note that MIDI allows nested messages: the so-called "real-time" MIDI
messages can be inserted into the MIDI byte stream at any location,
including within a sysex message. MIDI real-time messages are one-byte
@@ -555,8 +805,8 @@ typedef int32_t PmMessage; /**< see PmEvent */
messages should be sent in the correct order, and timestamps MUST
be non-decreasing. See also "Example" for Pm_OpenOutput() above.
- A sysex message will generally fill many PmEvent structures. On
- output to a PortMidiStream with non-zero latency, the first timestamp
+ A sysex message will generally fill many #PmEvent structures. On
+ output to a #PortMidiStream with non-zero latency, the first timestamp
on sysex message data will determine the time to begin sending the
message. PortMidi implementations may ignore timestamps for the
remainder of the sysex message.
@@ -580,71 +830,127 @@ typedef struct {
PmTimestamp timestamp;
} PmEvent;
-/**
- @}
-*/
+/** @} */
+
/** \defgroup grp_io Reading and Writing Midi Messages
@{
*/
-/**
- Pm_Read() retrieves midi data into a buffer, and returns the number
- of events read. Result is a non-negative number unless an error occurs,
- in which case a PmError value will be returned.
+/** Retrieve midi data into a buffer.
- Buffer Overflow
+ @param stream the open input stream.
- The problem: if an input overflow occurs, data will be lost, ultimately
- because there is no flow control all the way back to the data source.
- When data is lost, the receiver should be notified and some sort of
- graceful recovery should take place, e.g. you shouldn't resume receiving
- in the middle of a long sysex message.
+ @return the number of events read, or, if the result is negative,
+ a #PmError value will be returned.
- With a lock-free fifo, which is pretty much what we're stuck with to
- enable portability to the Mac, it's tricky for the producer and consumer
- to synchronously reset the buffer and resume normal operation.
+ The Buffer Overflow Problem
- Solution: the buffer managed by PortMidi will be flushed when an overflow
- occurs. The consumer (Pm_Read()) gets an error message (pmBufferOverflow)
- and ordinary processing resumes as soon as a new message arrives. The
- remainder of a partial sysex message is not considered to be a "new
- message" and will be flushed as well.
+ The problem: if an input overflow occurs, data will be lost,
+ ultimately because there is no flow control all the way back to
+ the data source. When data is lost, the receiver should be
+ notified and some sort of graceful recovery should take place,
+ e.g. you shouldn't resume receiving in the middle of a long sysex
+ message.
+ With a lock-free fifo, which is pretty much what we're stuck with
+ to enable portability to the Mac, it's tricky for the producer and
+ consumer to synchronously reset the buffer and resume normal
+ operation.
+
+ Solution: the entire buffer managed by PortMidi will be flushed
+ when an overflow occurs. The consumer (Pm_Read()) gets an error
+ message (#pmBufferOverflow) and ordinary processing resumes as
+ soon as a new message arrives. The remainder of a partial sysex
+ message is not considered to be a "new message" and will be
+ flushed as well.
*/
-PMEXPORT int Pm_Read( PortMidiStream *stream, PmEvent *buffer, int32_t length );
+PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length);
-/**
- Pm_Poll() tests whether input is available,
- returning TRUE, FALSE, or an error value.
+/** Test whether input is available.
+
+ @param stream an open input stream.
+
+ @return TRUE, FALSE, or an error value.
+
+ If there was an asynchronous error, pmHostError is returned and you must
+ call again to determine if input is (also) available.
+
+ You should probably *not* use this function. Call Pm_Read()
+ instead. If it returns 0, then there is no data available. It is
+ possible for Pm_Poll() to return TRUE before the complete message
+ is available, so Pm_Read() could return 0 even after Pm_Poll()
+ returns TRUE. Only call Pm_Poll() if you want to know that data is
+ probably available even though you are not ready to receive data.
*/
-PMEXPORT PmError Pm_Poll( PortMidiStream *stream);
+PMEXPORT PmError Pm_Poll(PortMidiStream *stream);
-/**
- Pm_Write() writes midi data from a buffer. This may contain:
+/** Write MIDI data from a buffer.
+
+ @param stream an open output stream.
+
+ @param buffer (address of) an array of MIDI event data.
+
+ @param length the length of the \p buffer.
+
+ @return TRUE, FALSE, or an error value.
+
+ \b buffer may contain:
- short messages
- or
- sysex messages that are converted into a sequence of PmEvent
structures, e.g. sending data from a file or forwarding them
- from midi input.
+ from midi input, with 4 SysEx bytes per PmEvent message,
+ low-order byte first, until the last message, which may
+ contain from 1 to 4 bytes ending in MIDI EOX (0xF7).
+ - PortMidi allows 1-byte real-time messages to be embedded
+ within SysEx messages, but only on 4-byte boundaries so
+ that SysEx data always uses a full 4 bytes (except possibly
+ at the end). Each real-time message always occupies a full
+ PmEvent (3 of the 4 bytes in the PmEvent's message are
+ ignored) even when embedded in a SysEx message.
Use Pm_WriteSysEx() to write a sysex message stored as a contiguous
array of bytes.
Sysex data may contain embedded real-time messages.
+
+ \p buffer is managed by the caller. The buffer may be destroyed
+ as soon as this call returns.
*/
-PMEXPORT PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, int32_t length );
+PMEXPORT PmError Pm_Write(PortMidiStream *stream, PmEvent *buffer,
+ int32_t length);
-/**
- Pm_WriteShort() writes a timestamped non-system-exclusive midi message.
- Messages are delivered in order as received, and timestamps must be
- non-decreasing. (But timestamps are ignored if the stream was opened
- with latency = 0.)
+/** Write a timestamped non-system-exclusive midi message.
+
+ @param stream an open output stream.
+
+ @param when timestamp for the event.
+
+ @param msg the data for the event.
+
+ @result #pmNoError or an error code.
+
+ Messages are delivered in order, and timestamps must be
+ non-decreasing. (But timestamps are ignored if the stream was
+ opened with latency = 0, and otherwise, non-decreasing timestamps
+ are "corrected" to the lowest valid value.)
*/
-PMEXPORT PmError Pm_WriteShort( PortMidiStream *stream, PmTimestamp when, int32_t msg);
+PMEXPORT PmError Pm_WriteShort(PortMidiStream *stream, PmTimestamp when,
+ PmMessage msg);
-/**
- Pm_WriteSysEx() writes a timestamped system-exclusive midi message.
+/** Write a timestamped system-exclusive midi message.
+
+ @param stream an open output stream.
+
+ @param when timestamp for the event.
+
+ @param msg the sysex message, terminated with an EOX status byte.
+
+ @result #pmNoError or an error code.
+
+ \p msg is managed by the caller and may be destroyed when this
+ call returns.
*/
-PMEXPORT PmError Pm_WriteSysEx( PortMidiStream *stream, PmTimestamp when, unsigned char *msg);
+PMEXPORT PmError Pm_WriteSysEx(PortMidiStream *stream, PmTimestamp when,
+ unsigned char *msg);
/** @} */