- Reestructuración de ficheros y directorios general
- merge v0.01 --> Añadido fileselector - Añadidas fuentes de Gem y Pure Data - pix2jpg incluído en Gem. Archivos de construcción de Gem modificados. - Añadido fichero ompiling.txt con instrucciones de compilación
This commit is contained in:
parent
c9adfd020b
commit
e85d191b46
3100 changed files with 775434 additions and 3073 deletions
158
pd-0.44-2/portmidi/CHANGELOG.txt
Normal file
158
pd-0.44-2/portmidi/CHANGELOG.txt
Normal file
|
@ -0,0 +1,158 @@
|
|||
/* CHANGELOG FOR PORTMIDI
|
||||
*
|
||||
* 17Jan07 Roger Dannenberg
|
||||
* - Lots more help for Common Lisp user in pm_cl
|
||||
* - Minor fix to eliminate a compiler warning
|
||||
* - Went back to single library in OS X for both portmidi and porttime
|
||||
*
|
||||
* 16Jan07 Roger Dannenberg
|
||||
* - OOPS! fixed bug where short messages all had zero data
|
||||
* - Makefile.osx static library build now makes universal (i386 + ppc)
|
||||
* binaries
|
||||
*
|
||||
* 15Jan07 Roger Dannenberg
|
||||
* - multiple rewrites of sysex handling code to take care of
|
||||
* error-handling, embedded messages, message filtering,
|
||||
* driver bugs, and host limitations.
|
||||
* - fixed windows to use dwBufferLength rather than
|
||||
* dwBytesRecorded for long buffer output (fix by Nigel Brown)
|
||||
* - Win32 MME code always appends an extra zero to long buffer
|
||||
* output to work around a problem with earlier versions of Midi Yoke
|
||||
* - Added mm, a command line Midi Monitor to pm_test suite
|
||||
* - Revised copyright notice to match PortAudio/MIT license (requests
|
||||
* are moved out of the license proper and into a separate paragraph)
|
||||
*
|
||||
* 18Oct06 Roger Dannenberg
|
||||
* - replace FIFO in pmutil with Light Pipe-based multiprocessor-safe alg.
|
||||
* - replace FIFO in portmidi.c with PmQueue from pmutil
|
||||
*
|
||||
* 07Oct06 cpr & Roger Dannenberg
|
||||
* - overhaul of CoreMIDI input to handle running status and multiple
|
||||
* - messages per packet, with additional error detection
|
||||
* - added Leigh Smith and Rick Taube support for Common Lisp and
|
||||
* - dynamic link libraries in OSX
|
||||
* - initialize static global seq = NULL in pmlinuxalsa.c
|
||||
*
|
||||
* 05Sep06 Sebastien Frippiat
|
||||
* - check if (ALSA) seq exists before closing it in pm_linuxalsa_term()
|
||||
*
|
||||
* 05Sep06 Andreas Micheler and Cecilio
|
||||
* - fixed memory leak by freeing someo objects in pm_winmm_term()
|
||||
* - and another leak by freeing descriptors in Pm_Terminate()
|
||||
*
|
||||
* 23Aug06 RBD
|
||||
* - various minor fixes
|
||||
*
|
||||
* 04Nov05 Olivier Tristan
|
||||
* - changes to OS X to properly retrieve real device name on CoreMidi
|
||||
*
|
||||
* 19Jul05 Roger Dannenberg
|
||||
* - included pmBufferMaxSize in Pm_GetErrorText()
|
||||
*
|
||||
* 23Mar05 Torgier Strand Henriksen
|
||||
* - cleaner termination of porttime thread under Linux
|
||||
*
|
||||
* 15Nov04 Ben Allison
|
||||
* - sysex output now uses one buffer/message and reallocates buffer
|
||||
* - if needed
|
||||
* - filters expanded for many message types and channels
|
||||
* - detailed changes are as follows:
|
||||
* ------------- in pmwinmm.c --------------
|
||||
* - new #define symbol: OUTPUT_BYTES_PER_BUFFER
|
||||
* - change SYSEX_BYTES_PER_BUFFER to 1024
|
||||
* - added MIDIHDR_BUFFER_LENGTH(x) to correctly count midihdr buffer length
|
||||
* - change MIDIHDR_SIZE(x) to (MIDIHDR_BUFFER_LENGTH(x) + sizeof(MIDIHDR))
|
||||
* - change allocate_buffer to use new MIDIHDR_BUFFER_LENGTH macro
|
||||
* - new macros for MIDIHDR_SYSEX_SIZE and MIDIHDR_SYSEX_BUFFER_LENGTH
|
||||
* - similar to above, but counts appropriately for sysex messages
|
||||
* - added the following members to midiwinmm_struct for sysex data:
|
||||
* - LPMIDIHDR *sysex_buffers; ** pool of buffers for sysex data **
|
||||
* - int num_sysex_buffers; ** how many sysex buffers **
|
||||
* - int next_sysex_buffer; ** index of next sysexbuffer to send **
|
||||
* - HANDLE sysex_buffer_signal; ** to wait for free sysex buffer **
|
||||
* - duplicated allocate_buffer, alocate_buffers and get_free_output_buffer
|
||||
* - into equivalent sysex_buffer form
|
||||
* - changed winmm_in_open to initialize new midiwinmm_struct members and
|
||||
* - to use the new allocate_sysex_buffer() function instead of
|
||||
* - allocate_buffer()
|
||||
* - changed winmm_out_open to initialize new members, create sysex buffer
|
||||
* - signal, and allocate 2 sysex buffers
|
||||
* - changed winmm_out_delete to free sysex buffers and shut down the sysex
|
||||
* - buffer signal
|
||||
* - create new function resize_sysex_buffer which resizes m->hdr to the
|
||||
* - passed size, and corrects the midiwinmm_struct accordingly.
|
||||
* - changed winmm_write_byte to use new resize_sysex_buffer function,
|
||||
* - if resize fails, write current buffer to output and continue
|
||||
* - changed winmm_out_callback to use buffer_signal or sysex_buffer_signal
|
||||
* - depending on which buffer was finished
|
||||
* ------------- in portmidi.h --------------
|
||||
* - added pmBufferMaxSize to PmError to indicate that the buffer would be
|
||||
* - too large for the underlying API
|
||||
* - added additional filters
|
||||
* - added prototype, documentation, and helper macro for Pm_SetChannelMask
|
||||
* ------------- in portmidi.c --------------
|
||||
* - added pm_status_filtered() and pm_realtime_filtered() functions to
|
||||
* separate filtering logic from buffer logic in pm_read_short
|
||||
* - added Pm_SetChannelMask function
|
||||
* - added pm_channel_filtered() function
|
||||
* ------------- in pminternal.h --------------
|
||||
* - added member to PortMidiStream for channel mask
|
||||
*
|
||||
* 25May04 RBD
|
||||
* - removed support for MIDI THRU
|
||||
* - moved filtering from Pm_Read to pm_enqueue to avoid buffer ovfl
|
||||
* - extensive work on Mac OS X port, especially sysex and error handling
|
||||
*
|
||||
* 18May04 RBD
|
||||
* - removed side-effects from assert() calls. Now you can disable assert().
|
||||
* - no longer check pm_hosterror everywhere, fixing a bug where an open
|
||||
* failure could cause a write not to work on a previously opened port
|
||||
* until you call Pm_GetHostErrorText().
|
||||
* 16May04 RBD and Chris Roberts
|
||||
* - Some documentation wordsmithing in portmidi.h
|
||||
* - Dynamically allocate port descriptor structures
|
||||
* - Fixed parameter error in midiInPrepareBuffer and midiInAddBuffer.
|
||||
*
|
||||
* 09Oct03 RBD
|
||||
* - Changed Thru handling. Now the client does all the work and the client
|
||||
* must poll or read to keep thru messages flowing.
|
||||
*
|
||||
* 31May03 RBD
|
||||
* - Fixed various bugs.
|
||||
* - Added linux ALSA support with help from Clemens Ladisch
|
||||
* - Added Mac OS X support, implemented by Jon Parise, updated and
|
||||
* integrated by Andrew Zeldis and Zico Kolter
|
||||
* - Added latency program to build histogram of system latency using PortTime.
|
||||
*
|
||||
* 30Jun02 RBD Extensive rewrite of sysex handling. It works now.
|
||||
* Extensive reworking of error reporting and error text -- no
|
||||
* longer use dictionary call to delete data; instead, Pm_Open
|
||||
* and Pm_Close clean up before returning an error code, and
|
||||
* error text is saved in a system-independent location.
|
||||
* Wrote sysex.c to test sysex message handling.
|
||||
*
|
||||
* 15Jun02 BCT changes:
|
||||
* - Added pmHostError text handling.
|
||||
* - For robustness, check PortMidi stream args not NULL.
|
||||
* - Re-C-ANSI-fied code (changed many C++ comments to C style)
|
||||
* - Reorganized code in pmwinmm according to input/output functionality (made
|
||||
* cleanup handling easier to reason about)
|
||||
* - Fixed Pm_Write calls (portmidi.h says these should not return length but Pm_Error)
|
||||
* - Cleaned up memory handling (now system specific data deleted via dictionary
|
||||
* call in PortMidi, allows client to query host errors).
|
||||
* - Added explicit asserts to verify various aspects of pmwinmm implementation behaves as
|
||||
* logic implies it should. Specifically: verified callback routines not reentrant and
|
||||
* all verified status for all unchecked Win32 MMedia API calls perform successfully
|
||||
* - Moved portmidi initialization and clean-up routines into DLL to fix Win32 MMedia API
|
||||
* bug (i.e. if devices not explicitly closed, must reboot to debug application further).
|
||||
* With this change, clients no longer need explicitly call Pm_Initialize, Pm_Terminate, or
|
||||
* explicitly Pm_Close open devices when using WinMM version of PortMidi.
|
||||
*
|
||||
* 23Jan02 RBD Fixed bug in pmwinmm.c thru handling
|
||||
*
|
||||
* 21Jan02 RBD Added tests in Pm_OpenInput() and Pm_OpenOutput() to prevent
|
||||
* opening an input as output and vice versa.
|
||||
* Added comments and documentation.
|
||||
* Implemented Pm_Terminate().
|
||||
*
|
||||
*/
|
44
pd-0.44-2/portmidi/Makefile.am
Normal file
44
pd-0.44-2/portmidi/Makefile.am
Normal file
|
@ -0,0 +1,44 @@
|
|||
# this is built as a "libtool convenience library"
|
||||
# http://sources.redhat.com/automake/automake.html#Libtool-Convenience-Libraries
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
AM_CFLAGS = -DNEWBUFFER
|
||||
INCLUDES = -Ipm_common -Iporttime
|
||||
|
||||
noinst_LTLIBRARIES = libportmidi.la
|
||||
|
||||
libportmidi_la_SOURCES = pm_common/pmutil.c pm_common/portmidi.c
|
||||
|
||||
if LINUX
|
||||
INCLUDES += -Ipm_linux
|
||||
libportmidi_la_SOURCES += porttime/ptlinux.c \
|
||||
pm_linux/pmlinux.c \
|
||||
pm_linux/pmlinuxalsa.c
|
||||
endif
|
||||
|
||||
if MACOSX
|
||||
INCLUDES += -Ipm_mac
|
||||
libportmidi_la_SOURCES += porttime/ptmacosx_cf.c \
|
||||
pm_mac/pmmac.c \
|
||||
pm_mac/pmmacosxcm.c
|
||||
endif
|
||||
|
||||
if WINDOWS
|
||||
INCLUDES += -Ipm_win
|
||||
libportmidi_la_SOURCES += porttime/porttime.c \
|
||||
porttime/ptwinmm.c \
|
||||
pm_win/pmwin.c \
|
||||
pm_win/pmwinmm.c
|
||||
endif
|
||||
|
||||
# include the headers in the dist so you can build
|
||||
nobase_noinst_HEADERS = \
|
||||
pm_common/pminternal.h \
|
||||
pm_common/pmutil.h \
|
||||
pm_common/portmidi.h \
|
||||
pm_linux/pmlinux.h \
|
||||
pm_linux/pmlinuxalsa.h \
|
||||
pm_mac/pmmac.h \
|
||||
pm_mac/pmmacosxcm.h \
|
||||
pm_win/pmdll.h \
|
||||
pm_win/pmwinmm.h \
|
||||
porttime/porttime.h
|
662
pd-0.44-2/portmidi/Makefile.in
Normal file
662
pd-0.44-2/portmidi/Makefile.in
Normal file
|
@ -0,0 +1,662 @@
|
|||
# Makefile.in generated by automake 1.11.6 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
||||
# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
|
||||
# Foundation, Inc.
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
|
||||
VPATH = @srcdir@
|
||||
am__make_dryrun = \
|
||||
{ \
|
||||
am__dry=no; \
|
||||
case $$MAKEFLAGS in \
|
||||
*\\[\ \ ]*) \
|
||||
echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \
|
||||
| grep '^AM OK$$' >/dev/null || am__dry=yes;; \
|
||||
*) \
|
||||
for am__flg in $$MAKEFLAGS; do \
|
||||
case $$am__flg in \
|
||||
*=*|--*) ;; \
|
||||
*n*) am__dry=yes; break;; \
|
||||
esac; \
|
||||
done;; \
|
||||
esac; \
|
||||
test $$am__dry = yes; \
|
||||
}
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkglibexecdir = $(libexecdir)/@PACKAGE@
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
INSTALL_HEADER = $(INSTALL_DATA)
|
||||
transform = $(program_transform_name)
|
||||
NORMAL_INSTALL = :
|
||||
PRE_INSTALL = :
|
||||
POST_INSTALL = :
|
||||
NORMAL_UNINSTALL = :
|
||||
PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
@LINUX_TRUE@am__append_1 = -Ipm_linux
|
||||
@LINUX_TRUE@am__append_2 = porttime/ptlinux.c \
|
||||
@LINUX_TRUE@ pm_linux/pmlinux.c \
|
||||
@LINUX_TRUE@ pm_linux/pmlinuxalsa.c
|
||||
|
||||
@MACOSX_TRUE@am__append_3 = -Ipm_mac
|
||||
@MACOSX_TRUE@am__append_4 = porttime/ptmacosx_cf.c \
|
||||
@MACOSX_TRUE@ pm_mac/pmmac.c \
|
||||
@MACOSX_TRUE@ pm_mac/pmmacosxcm.c
|
||||
|
||||
@WINDOWS_TRUE@am__append_5 = -Ipm_win
|
||||
@WINDOWS_TRUE@am__append_6 = porttime/porttime.c \
|
||||
@WINDOWS_TRUE@ porttime/ptwinmm.c \
|
||||
@WINDOWS_TRUE@ pm_win/pmwin.c \
|
||||
@WINDOWS_TRUE@ pm_win/pmwinmm.c
|
||||
|
||||
subdir = portmidi
|
||||
DIST_COMMON = $(nobase_noinst_HEADERS) $(srcdir)/Makefile.am \
|
||||
$(srcdir)/Makefile.in
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/m4/android.m4 \
|
||||
$(top_srcdir)/m4/iphone.m4 $(top_srcdir)/m4/universal.m4 \
|
||||
$(top_srcdir)/m4/generated/libtool.m4 \
|
||||
$(top_srcdir)/m4/generated/ltoptions.m4 \
|
||||
$(top_srcdir)/m4/generated/ltsugar.m4 \
|
||||
$(top_srcdir)/m4/generated/ltversion.m4 \
|
||||
$(top_srcdir)/m4/generated/lt~obsolete.m4 \
|
||||
$(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
mkinstalldirs = $(install_sh) -d
|
||||
CONFIG_CLEAN_FILES =
|
||||
CONFIG_CLEAN_VPATH_FILES =
|
||||
LTLIBRARIES = $(noinst_LTLIBRARIES)
|
||||
libportmidi_la_LIBADD =
|
||||
am__libportmidi_la_SOURCES_DIST = pm_common/pmutil.c \
|
||||
pm_common/portmidi.c porttime/ptlinux.c pm_linux/pmlinux.c \
|
||||
pm_linux/pmlinuxalsa.c porttime/ptmacosx_cf.c pm_mac/pmmac.c \
|
||||
pm_mac/pmmacosxcm.c porttime/porttime.c porttime/ptwinmm.c \
|
||||
pm_win/pmwin.c pm_win/pmwinmm.c
|
||||
@LINUX_TRUE@am__objects_1 = ptlinux.lo pmlinux.lo pmlinuxalsa.lo
|
||||
@MACOSX_TRUE@am__objects_2 = ptmacosx_cf.lo pmmac.lo pmmacosxcm.lo
|
||||
@WINDOWS_TRUE@am__objects_3 = porttime.lo ptwinmm.lo pmwin.lo \
|
||||
@WINDOWS_TRUE@ pmwinmm.lo
|
||||
am_libportmidi_la_OBJECTS = pmutil.lo portmidi.lo $(am__objects_1) \
|
||||
$(am__objects_2) $(am__objects_3)
|
||||
libportmidi_la_OBJECTS = $(am_libportmidi_la_OBJECTS)
|
||||
DEFAULT_INCLUDES = -I.@am__isrc@
|
||||
depcomp = $(SHELL) $(top_srcdir)/m4/config/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
am__mv = mv -f
|
||||
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
|
||||
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
|
||||
LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
||||
--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
||||
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
|
||||
CCLD = $(CC)
|
||||
LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
||||
--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
|
||||
$(LDFLAGS) -o $@
|
||||
SOURCES = $(libportmidi_la_SOURCES)
|
||||
DIST_SOURCES = $(am__libportmidi_la_SOURCES_DIST)
|
||||
am__can_run_installinfo = \
|
||||
case $$AM_UPDATE_INFO_DIR in \
|
||||
n|no|NO) false;; \
|
||||
*) (install-info --version) >/dev/null 2>&1;; \
|
||||
esac
|
||||
HEADERS = $(nobase_noinst_HEADERS)
|
||||
ETAGS = etags
|
||||
CTAGS = ctags
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
ACLOCAL = @ACLOCAL@
|
||||
ALLOCA = @ALLOCA@
|
||||
ALSA_LIBS = @ALSA_LIBS@
|
||||
AMTAR = @AMTAR@
|
||||
AR = @AR@
|
||||
ARCH_CFLAGS = @ARCH_CFLAGS@
|
||||
ARCH_LDFLAGS = @ARCH_LDFLAGS@
|
||||
AS = @AS@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
AWK = @AWK@
|
||||
CC = @CC@
|
||||
CCDEPMODE = @CCDEPMODE@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPP = @CPP@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
CXX = @CXX@
|
||||
CXXCPP = @CXXCPP@
|
||||
CXXDEPMODE = @CXXDEPMODE@
|
||||
CXXFLAGS = @CXXFLAGS@
|
||||
CYGPATH_W = @CYGPATH_W@
|
||||
DEFS = @DEFS@
|
||||
DEPDIR = @DEPDIR@
|
||||
DLLTOOL = @DLLTOOL@
|
||||
DSYMUTIL = @DSYMUTIL@
|
||||
DUMPBIN = @DUMPBIN@
|
||||
ECHO_C = @ECHO_C@
|
||||
ECHO_N = @ECHO_N@
|
||||
ECHO_T = @ECHO_T@
|
||||
EGREP = @EGREP@
|
||||
EXEEXT = @EXEEXT@
|
||||
EXTERNAL_CFLAGS = @EXTERNAL_CFLAGS@
|
||||
EXTERNAL_EXTENSION = @EXTERNAL_EXTENSION@
|
||||
EXTERNAL_LDFLAGS = @EXTERNAL_LDFLAGS@
|
||||
EXTERNTARGET = @EXTERNTARGET@
|
||||
FGREP = @FGREP@
|
||||
GREP = @GREP@
|
||||
HAVE_MSGFMT = @HAVE_MSGFMT@
|
||||
INCLUDES = -Ipm_common -Iporttime $(am__append_1) $(am__append_3) \
|
||||
$(am__append_5)
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
JACK_LIBS = @JACK_LIBS@
|
||||
LD = @LD@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBM = @LIBM@
|
||||
LIBOBJS = @LIBOBJS@
|
||||
LIBS = @LIBS@
|
||||
LIBTOOL = @LIBTOOL@
|
||||
LIPO = @LIPO@
|
||||
LN_S = @LN_S@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
MANIFEST_TOOL = @MANIFEST_TOOL@
|
||||
MKDIR_P = @MKDIR_P@
|
||||
NM = @NM@
|
||||
NMEDIT = @NMEDIT@
|
||||
OBJDUMP = @OBJDUMP@
|
||||
OBJEXT = @OBJEXT@
|
||||
OTOOL = @OTOOL@
|
||||
OTOOL64 = @OTOOL64@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
PACKAGE_STRING = @PACKAGE_STRING@
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
PACKAGE_URL = @PACKAGE_URL@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
RANLIB = @RANLIB@
|
||||
SED = @SED@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
STRIP = @STRIP@
|
||||
VERSION = @VERSION@
|
||||
abs_builddir = @abs_builddir@
|
||||
abs_srcdir = @abs_srcdir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
ac_ct_AR = @ac_ct_AR@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_CXX = @ac_ct_CXX@
|
||||
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
|
||||
am__include = @am__include@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
am__tar = @am__tar@
|
||||
am__untar = @am__untar@
|
||||
bindir = @bindir@
|
||||
build = @build@
|
||||
build_alias = @build_alias@
|
||||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
builddir = @builddir@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = @docdir@
|
||||
dvidir = @dvidir@
|
||||
exec_prefix = @exec_prefix@
|
||||
host = @host@
|
||||
host_alias = @host_alias@
|
||||
host_cpu = @host_cpu@
|
||||
host_os = @host_os@
|
||||
host_vendor = @host_vendor@
|
||||
htmldir = @htmldir@
|
||||
includedir = @includedir@
|
||||
infodir = @infodir@
|
||||
install_sh = @install_sh@
|
||||
libdir = @libdir@
|
||||
libexecdir = @libexecdir@
|
||||
localedir = @localedir@
|
||||
localstatedir = @localstatedir@
|
||||
mandir = @mandir@
|
||||
mkdir_p = @mkdir_p@
|
||||
oldincludedir = @oldincludedir@
|
||||
pdfdir = @pdfdir@
|
||||
prefix = @prefix@
|
||||
program_transform_name = @program_transform_name@
|
||||
psdir = @psdir@
|
||||
sbindir = @sbindir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
srcdir = @srcdir@
|
||||
subdirs = @subdirs@
|
||||
sysconfdir = @sysconfdir@
|
||||
target_alias = @target_alias@
|
||||
top_build_prefix = @top_build_prefix@
|
||||
top_builddir = @top_builddir@
|
||||
top_srcdir = @top_srcdir@
|
||||
|
||||
# this is built as a "libtool convenience library"
|
||||
# http://sources.redhat.com/automake/automake.html#Libtool-Convenience-Libraries
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
AM_CFLAGS = -DNEWBUFFER
|
||||
noinst_LTLIBRARIES = libportmidi.la
|
||||
libportmidi_la_SOURCES = pm_common/pmutil.c pm_common/portmidi.c \
|
||||
$(am__append_2) $(am__append_4) $(am__append_6)
|
||||
|
||||
# include the headers in the dist so you can build
|
||||
nobase_noinst_HEADERS = \
|
||||
pm_common/pminternal.h \
|
||||
pm_common/pmutil.h \
|
||||
pm_common/portmidi.h \
|
||||
pm_linux/pmlinux.h \
|
||||
pm_linux/pmlinuxalsa.h \
|
||||
pm_mac/pmmac.h \
|
||||
pm_mac/pmmacosxcm.h \
|
||||
pm_win/pmdll.h \
|
||||
pm_win/pmwinmm.h \
|
||||
porttime/porttime.h
|
||||
|
||||
all: all-am
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .lo .o .obj
|
||||
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
|
||||
@for dep in $?; do \
|
||||
case '$(am__configure_deps)' in \
|
||||
*$$dep*) \
|
||||
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
|
||||
&& { if test -f $@; then exit 0; else break; fi; }; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
done; \
|
||||
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign portmidi/Makefile'; \
|
||||
$(am__cd) $(top_srcdir) && \
|
||||
$(AUTOMAKE) --foreign portmidi/Makefile
|
||||
.PRECIOUS: Makefile
|
||||
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
|
||||
@case '$?' in \
|
||||
*config.status*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
|
||||
*) \
|
||||
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
|
||||
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
|
||||
esac;
|
||||
|
||||
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
$(top_srcdir)/configure: $(am__configure_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(am__aclocal_m4_deps):
|
||||
|
||||
clean-noinstLTLIBRARIES:
|
||||
-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
|
||||
@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
|
||||
dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
|
||||
test "$$dir" != "$$p" || dir=.; \
|
||||
echo "rm -f \"$${dir}/so_locations\""; \
|
||||
rm -f "$${dir}/so_locations"; \
|
||||
done
|
||||
libportmidi.la: $(libportmidi_la_OBJECTS) $(libportmidi_la_DEPENDENCIES) $(EXTRA_libportmidi_la_DEPENDENCIES)
|
||||
$(LINK) $(libportmidi_la_OBJECTS) $(libportmidi_la_LIBADD) $(LIBS)
|
||||
|
||||
mostlyclean-compile:
|
||||
-rm -f *.$(OBJEXT)
|
||||
|
||||
distclean-compile:
|
||||
-rm -f *.tab.c
|
||||
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pmlinux.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pmlinuxalsa.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pmmac.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pmmacosxcm.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pmutil.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pmwin.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pmwinmm.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/portmidi.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/porttime.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ptlinux.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ptmacosx_cf.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ptwinmm.Plo@am__quote@
|
||||
|
||||
.c.o:
|
||||
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(COMPILE) -c $<
|
||||
|
||||
.c.obj:
|
||||
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
|
||||
|
||||
.c.lo:
|
||||
@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
|
||||
|
||||
pmutil.lo: pm_common/pmutil.c
|
||||
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pmutil.lo -MD -MP -MF $(DEPDIR)/pmutil.Tpo -c -o pmutil.lo `test -f 'pm_common/pmutil.c' || echo '$(srcdir)/'`pm_common/pmutil.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pmutil.Tpo $(DEPDIR)/pmutil.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pm_common/pmutil.c' object='pmutil.lo' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pmutil.lo `test -f 'pm_common/pmutil.c' || echo '$(srcdir)/'`pm_common/pmutil.c
|
||||
|
||||
portmidi.lo: pm_common/portmidi.c
|
||||
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT portmidi.lo -MD -MP -MF $(DEPDIR)/portmidi.Tpo -c -o portmidi.lo `test -f 'pm_common/portmidi.c' || echo '$(srcdir)/'`pm_common/portmidi.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/portmidi.Tpo $(DEPDIR)/portmidi.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pm_common/portmidi.c' object='portmidi.lo' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o portmidi.lo `test -f 'pm_common/portmidi.c' || echo '$(srcdir)/'`pm_common/portmidi.c
|
||||
|
||||
ptlinux.lo: porttime/ptlinux.c
|
||||
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ptlinux.lo -MD -MP -MF $(DEPDIR)/ptlinux.Tpo -c -o ptlinux.lo `test -f 'porttime/ptlinux.c' || echo '$(srcdir)/'`porttime/ptlinux.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ptlinux.Tpo $(DEPDIR)/ptlinux.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='porttime/ptlinux.c' object='ptlinux.lo' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ptlinux.lo `test -f 'porttime/ptlinux.c' || echo '$(srcdir)/'`porttime/ptlinux.c
|
||||
|
||||
pmlinux.lo: pm_linux/pmlinux.c
|
||||
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pmlinux.lo -MD -MP -MF $(DEPDIR)/pmlinux.Tpo -c -o pmlinux.lo `test -f 'pm_linux/pmlinux.c' || echo '$(srcdir)/'`pm_linux/pmlinux.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pmlinux.Tpo $(DEPDIR)/pmlinux.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pm_linux/pmlinux.c' object='pmlinux.lo' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pmlinux.lo `test -f 'pm_linux/pmlinux.c' || echo '$(srcdir)/'`pm_linux/pmlinux.c
|
||||
|
||||
pmlinuxalsa.lo: pm_linux/pmlinuxalsa.c
|
||||
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pmlinuxalsa.lo -MD -MP -MF $(DEPDIR)/pmlinuxalsa.Tpo -c -o pmlinuxalsa.lo `test -f 'pm_linux/pmlinuxalsa.c' || echo '$(srcdir)/'`pm_linux/pmlinuxalsa.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pmlinuxalsa.Tpo $(DEPDIR)/pmlinuxalsa.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pm_linux/pmlinuxalsa.c' object='pmlinuxalsa.lo' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pmlinuxalsa.lo `test -f 'pm_linux/pmlinuxalsa.c' || echo '$(srcdir)/'`pm_linux/pmlinuxalsa.c
|
||||
|
||||
ptmacosx_cf.lo: porttime/ptmacosx_cf.c
|
||||
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ptmacosx_cf.lo -MD -MP -MF $(DEPDIR)/ptmacosx_cf.Tpo -c -o ptmacosx_cf.lo `test -f 'porttime/ptmacosx_cf.c' || echo '$(srcdir)/'`porttime/ptmacosx_cf.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ptmacosx_cf.Tpo $(DEPDIR)/ptmacosx_cf.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='porttime/ptmacosx_cf.c' object='ptmacosx_cf.lo' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ptmacosx_cf.lo `test -f 'porttime/ptmacosx_cf.c' || echo '$(srcdir)/'`porttime/ptmacosx_cf.c
|
||||
|
||||
pmmac.lo: pm_mac/pmmac.c
|
||||
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pmmac.lo -MD -MP -MF $(DEPDIR)/pmmac.Tpo -c -o pmmac.lo `test -f 'pm_mac/pmmac.c' || echo '$(srcdir)/'`pm_mac/pmmac.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pmmac.Tpo $(DEPDIR)/pmmac.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pm_mac/pmmac.c' object='pmmac.lo' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pmmac.lo `test -f 'pm_mac/pmmac.c' || echo '$(srcdir)/'`pm_mac/pmmac.c
|
||||
|
||||
pmmacosxcm.lo: pm_mac/pmmacosxcm.c
|
||||
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pmmacosxcm.lo -MD -MP -MF $(DEPDIR)/pmmacosxcm.Tpo -c -o pmmacosxcm.lo `test -f 'pm_mac/pmmacosxcm.c' || echo '$(srcdir)/'`pm_mac/pmmacosxcm.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pmmacosxcm.Tpo $(DEPDIR)/pmmacosxcm.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pm_mac/pmmacosxcm.c' object='pmmacosxcm.lo' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pmmacosxcm.lo `test -f 'pm_mac/pmmacosxcm.c' || echo '$(srcdir)/'`pm_mac/pmmacosxcm.c
|
||||
|
||||
porttime.lo: porttime/porttime.c
|
||||
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT porttime.lo -MD -MP -MF $(DEPDIR)/porttime.Tpo -c -o porttime.lo `test -f 'porttime/porttime.c' || echo '$(srcdir)/'`porttime/porttime.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/porttime.Tpo $(DEPDIR)/porttime.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='porttime/porttime.c' object='porttime.lo' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o porttime.lo `test -f 'porttime/porttime.c' || echo '$(srcdir)/'`porttime/porttime.c
|
||||
|
||||
ptwinmm.lo: porttime/ptwinmm.c
|
||||
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ptwinmm.lo -MD -MP -MF $(DEPDIR)/ptwinmm.Tpo -c -o ptwinmm.lo `test -f 'porttime/ptwinmm.c' || echo '$(srcdir)/'`porttime/ptwinmm.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ptwinmm.Tpo $(DEPDIR)/ptwinmm.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='porttime/ptwinmm.c' object='ptwinmm.lo' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ptwinmm.lo `test -f 'porttime/ptwinmm.c' || echo '$(srcdir)/'`porttime/ptwinmm.c
|
||||
|
||||
pmwin.lo: pm_win/pmwin.c
|
||||
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pmwin.lo -MD -MP -MF $(DEPDIR)/pmwin.Tpo -c -o pmwin.lo `test -f 'pm_win/pmwin.c' || echo '$(srcdir)/'`pm_win/pmwin.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pmwin.Tpo $(DEPDIR)/pmwin.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pm_win/pmwin.c' object='pmwin.lo' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pmwin.lo `test -f 'pm_win/pmwin.c' || echo '$(srcdir)/'`pm_win/pmwin.c
|
||||
|
||||
pmwinmm.lo: pm_win/pmwinmm.c
|
||||
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pmwinmm.lo -MD -MP -MF $(DEPDIR)/pmwinmm.Tpo -c -o pmwinmm.lo `test -f 'pm_win/pmwinmm.c' || echo '$(srcdir)/'`pm_win/pmwinmm.c
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pmwinmm.Tpo $(DEPDIR)/pmwinmm.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pm_win/pmwinmm.c' object='pmwinmm.lo' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pmwinmm.lo `test -f 'pm_win/pmwinmm.c' || echo '$(srcdir)/'`pm_win/pmwinmm.c
|
||||
|
||||
mostlyclean-libtool:
|
||||
-rm -f *.lo
|
||||
|
||||
clean-libtool:
|
||||
-rm -rf .libs _libs
|
||||
|
||||
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
|
||||
END { if (nonempty) { for (i in files) print i; }; }'`; \
|
||||
mkid -fID $$unique
|
||||
tags: TAGS
|
||||
|
||||
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
|
||||
$(TAGS_FILES) $(LISP)
|
||||
set x; \
|
||||
here=`pwd`; \
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
|
||||
END { if (nonempty) { for (i in files) print i; }; }'`; \
|
||||
shift; \
|
||||
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
|
||||
test -n "$$unique" || unique=$$empty_fix; \
|
||||
if test $$# -gt 0; then \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
"$$@" $$unique; \
|
||||
else \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
$$unique; \
|
||||
fi; \
|
||||
fi
|
||||
ctags: CTAGS
|
||||
CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
|
||||
$(TAGS_FILES) $(LISP)
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) '{ files[$$0] = 1; nonempty = 1; } \
|
||||
END { if (nonempty) { for (i in files) print i; }; }'`; \
|
||||
test -z "$(CTAGS_ARGS)$$unique" \
|
||||
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
|
||||
$$unique
|
||||
|
||||
GTAGS:
|
||||
here=`$(am__cd) $(top_builddir) && pwd` \
|
||||
&& $(am__cd) $(top_srcdir) \
|
||||
&& gtags -i $(GTAGS_ARGS) "$$here"
|
||||
|
||||
distclean-tags:
|
||||
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
list='$(DISTFILES)'; \
|
||||
dist_files=`for file in $$list; do echo $$file; done | \
|
||||
sed -e "s|^$$srcdirstrip/||;t" \
|
||||
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
|
||||
case $$dist_files in \
|
||||
*/*) $(MKDIR_P) `echo "$$dist_files" | \
|
||||
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
|
||||
sort -u` ;; \
|
||||
esac; \
|
||||
for file in $$dist_files; do \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test -d "$(distdir)/$$file"; then \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
|
||||
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
|
||||
fi; \
|
||||
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
|
||||
else \
|
||||
test -f "$(distdir)/$$file" \
|
||||
|| cp -p $$d/$$file "$(distdir)/$$file" \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
all-am: Makefile $(LTLIBRARIES) $(HEADERS)
|
||||
installdirs:
|
||||
install: install-am
|
||||
install-exec: install-exec-am
|
||||
install-data: install-data-am
|
||||
uninstall: uninstall-am
|
||||
|
||||
install-am: all-am
|
||||
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
|
||||
|
||||
installcheck: installcheck-am
|
||||
install-strip:
|
||||
if test -z '$(STRIP)'; then \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
install; \
|
||||
else \
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
|
||||
fi
|
||||
mostlyclean-generic:
|
||||
|
||||
clean-generic:
|
||||
|
||||
distclean-generic:
|
||||
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
|
||||
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
|
||||
|
||||
maintainer-clean-generic:
|
||||
@echo "This command is intended for maintainers to use"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
clean: clean-am
|
||||
|
||||
clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
|
||||
mostlyclean-am
|
||||
|
||||
distclean: distclean-am
|
||||
-rm -rf ./$(DEPDIR)
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-compile distclean-generic \
|
||||
distclean-tags
|
||||
|
||||
dvi: dvi-am
|
||||
|
||||
dvi-am:
|
||||
|
||||
html: html-am
|
||||
|
||||
html-am:
|
||||
|
||||
info: info-am
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am:
|
||||
|
||||
install-dvi: install-dvi-am
|
||||
|
||||
install-dvi-am:
|
||||
|
||||
install-exec-am:
|
||||
|
||||
install-html: install-html-am
|
||||
|
||||
install-html-am:
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
install-info-am:
|
||||
|
||||
install-man:
|
||||
|
||||
install-pdf: install-pdf-am
|
||||
|
||||
install-pdf-am:
|
||||
|
||||
install-ps: install-ps-am
|
||||
|
||||
install-ps-am:
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
-rm -rf ./$(DEPDIR)
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
mostlyclean: mostlyclean-am
|
||||
|
||||
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
|
||||
mostlyclean-libtool
|
||||
|
||||
pdf: pdf-am
|
||||
|
||||
pdf-am:
|
||||
|
||||
ps: ps-am
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am:
|
||||
|
||||
.MAKE: install-am install-strip
|
||||
|
||||
.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
|
||||
clean-libtool clean-noinstLTLIBRARIES ctags distclean \
|
||||
distclean-compile distclean-generic distclean-libtool \
|
||||
distclean-tags distdir dvi dvi-am html html-am info info-am \
|
||||
install install-am install-data install-data-am install-dvi \
|
||||
install-dvi-am install-exec install-exec-am install-html \
|
||||
install-html-am install-info install-info-am install-man \
|
||||
install-pdf install-pdf-am install-ps install-ps-am \
|
||||
install-strip installcheck installcheck-am installdirs \
|
||||
maintainer-clean maintainer-clean-generic mostlyclean \
|
||||
mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
|
||||
pdf pdf-am ps ps-am tags uninstall uninstall-am
|
||||
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
79
pd-0.44-2/portmidi/README.txt
Executable file
79
pd-0.44-2/portmidi/README.txt
Executable file
|
@ -0,0 +1,79 @@
|
|||
README for PortMidi
|
||||
Roger Dannenberg
|
||||
|
||||
VERSION: this is the 17-Jan-07 version of PortMidi.
|
||||
|
||||
Documentation for PortMidi is found in pm_common/portmidi.h.
|
||||
|
||||
Additional documentation:
|
||||
- Windows: see pm_win/README_WIN.txt and pm_win/debugging_dlls.txt
|
||||
- Linux: see pm_linux/README_LINUX.txt
|
||||
- Mac OSX: see pm_mac/README_MAC.txt
|
||||
- Common Lisp: see pm_cl/README_CL.txt
|
||||
|
||||
---------- some notes on the design of PortMidi ----------
|
||||
|
||||
POINTERS VS DEVICE NUMBERS
|
||||
|
||||
When you open a MIDI port, PortMidi allocates a structure to
|
||||
maintain the state of the open device. Since every device is
|
||||
also listed in a table, you might think it would be simpler to
|
||||
use the table index rather than a pointer to identify a device.
|
||||
This would also help with error checking (it's hard to make
|
||||
sure a pointer is valid). PortMidi's design parallels that of
|
||||
PortAudio.
|
||||
|
||||
ERROR HANDLING
|
||||
|
||||
Error handling turned out to be much more complicated than expected.
|
||||
PortMidi functions return error codes that the caller can check.
|
||||
In addition, errors may occur asynchronously due to MIDI input.
|
||||
However, for Windows, there are virtually no errors that can
|
||||
occur if the code is correct and not passing bogus values. One
|
||||
exception is an error that the system is out of memory, but my
|
||||
guess is that one is unlikely to recover gracefully from that.
|
||||
Therefore, all errors in callbacks are guarded by assert(), which
|
||||
means not guarded at all in release configurations.
|
||||
|
||||
Ordinarily, the caller checks for an error code. If the error is
|
||||
system-dependent, pmHostError is returned and the caller can
|
||||
call Pm_GetHostErrorText to get a text description of the error.
|
||||
|
||||
Host error codes are system-specific and are recorded in the
|
||||
system-specific data allocated for each open MIDI port.
|
||||
However, if an error occurs on open or close,
|
||||
we cannot store the error with the device because there will be
|
||||
no device data (assuming PortMidi cleans up after devices that
|
||||
are not open). For open and close, we will convert the error
|
||||
to text, copy it to a global string, and set pm_hosterror, a
|
||||
global flag.
|
||||
|
||||
Similarly, whenever a Read or Write operation returns pmHostError,
|
||||
the corresponding error string is copied to a global string
|
||||
and pm_hosterror is set. This makes getting error strings
|
||||
simple and uniform, although it does cost a string copy and some
|
||||
overhead even if the user does not want to look at the error data.
|
||||
|
||||
The system-specific Read, Write, Poll, etc. implementations should
|
||||
check for asynchronous errors and return immediately if one is
|
||||
found so that these get reported. This happens in the Mac OS X
|
||||
code, where lots of things are happening in callbacks, but again,
|
||||
in Windows, there are no error codes recorded in callbacks.
|
||||
|
||||
DEBUGGING
|
||||
|
||||
If you are building a console application for research, we suggest
|
||||
compiling with the option PM_CHECK_ERRORS. This will 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.
|
||||
|
||||
The Windows version (and perhaps others) also offers a DEBUG
|
||||
compile-time option. See README_WIN.txt.
|
40
pd-0.44-2/portmidi/license.txt
Normal file
40
pd-0.44-2/portmidi/license.txt
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* PortMidi Portable Real-Time MIDI Library
|
||||
*
|
||||
* license.txt -- a copy of the PortMidi copyright notice and license information
|
||||
*
|
||||
* Latest version available at: http://www.cs.cmu.edu/~music/portmidi/
|
||||
*
|
||||
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
|
||||
* Copyright (c) 2001-2006 Roger B. Dannenberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortMidi license; however,
|
||||
* the PortMusic community also makes the following non-binding requests:
|
||||
*
|
||||
* Any person wishing to distribute modifications to the Software is
|
||||
* requested to send the modifications to the original developer so that
|
||||
* they can be incorporated into the canonical version. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
182
pd-0.44-2/portmidi/pm_common/pminternal.h
Executable file
182
pd-0.44-2/portmidi/pm_common/pminternal.h
Executable file
|
@ -0,0 +1,182 @@
|
|||
/* pminternal.h -- header for interface implementations */
|
||||
|
||||
/* this file is included by files that implement library internals */
|
||||
/* Here is a guide to implementers:
|
||||
provide an initialization function similar to pm_winmm_init()
|
||||
add your initialization function to pm_init()
|
||||
Note that your init function should never require not-standard
|
||||
libraries or fail in any way. If the interface is not available,
|
||||
simply do not call pm_add_device. This means that non-standard
|
||||
libraries should try to do dynamic linking at runtime using a DLL
|
||||
and return without error if the DLL cannot be found or if there
|
||||
is any other failure.
|
||||
implement functions as indicated in pm_fns_type to open, read, write,
|
||||
close, etc.
|
||||
call pm_add_device() for each input and output device, passing it a
|
||||
pm_fns_type structure.
|
||||
assumptions about pm_fns_type functions are given below.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* these are defined in system-specific file */
|
||||
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;
|
||||
extern char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN];
|
||||
|
||||
struct pm_internal_struct;
|
||||
|
||||
/* these do not use PmInternal because it is not defined yet... */
|
||||
typedef PmError (*pm_write_short_fn)(struct pm_internal_struct *midi,
|
||||
PmEvent *buffer);
|
||||
typedef PmError (*pm_begin_sysex_fn)(struct pm_internal_struct *midi,
|
||||
PmTimestamp timestamp);
|
||||
typedef PmError (*pm_end_sysex_fn)(struct pm_internal_struct *midi,
|
||||
PmTimestamp timestamp);
|
||||
typedef PmError (*pm_write_byte_fn)(struct pm_internal_struct *midi,
|
||||
unsigned char byte, PmTimestamp timestamp);
|
||||
typedef PmError (*pm_write_realtime_fn)(struct pm_internal_struct *midi,
|
||||
PmEvent *buffer);
|
||||
typedef PmError (*pm_write_flush_fn)(struct pm_internal_struct *midi,
|
||||
PmTimestamp timestamp);
|
||||
typedef PmTimestamp (*pm_synchronize_fn)(struct pm_internal_struct *midi);
|
||||
/* pm_open_fn should clean up all memory and close the device if any part
|
||||
of the open fails */
|
||||
typedef PmError (*pm_open_fn)(struct pm_internal_struct *midi,
|
||||
void *driverInfo);
|
||||
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 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_flush_fn write_flush; /* send any accumulated but unsent data */
|
||||
pm_synchronize_fn synchronize; /* synchronize portmidi 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_fns_node, *pm_fns_type;
|
||||
|
||||
|
||||
/* when open fails, the dictionary gets this set of functions: */
|
||||
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 */
|
||||
pm_fns_type dictionary;
|
||||
} descriptor_node, *descriptor_type;
|
||||
|
||||
extern int pm_descriptor_max;
|
||||
extern descriptor_type descriptors;
|
||||
extern int pm_descriptor_index;
|
||||
|
||||
typedef unsigned long (*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 */
|
||||
|
||||
PmTimeProcPtr time_proc; /* where to get the time */
|
||||
void *time_info; /* pass this to get_time() */
|
||||
long buffer_len; /* how big is the buffer or queue? */
|
||||
#ifdef NEWBUFFER
|
||||
PmQueue *queue;
|
||||
#else
|
||||
PmEvent *buffer; /* storage for:
|
||||
- midi input
|
||||
- midi output w/latency != 0 */
|
||||
long head;
|
||||
long tail;
|
||||
int overflow; /* set to non-zero if input is dropped */
|
||||
#endif
|
||||
long latency; /* time delay in ms between timestamps and actual output */
|
||||
/* set to zero to get immediate, simple blocking output */
|
||||
/* if latency is zero, timestamps will be ignored; */
|
||||
/* if midi input device, this field ignored */
|
||||
|
||||
int sysex_in_progress; /* when sysex status is seen, this flag becomes
|
||||
* true until EOX is seen. When true, new data is appended to the
|
||||
* stream of outgoing bytes. When overflow occurs, sysex data is
|
||||
* dropped (until an EOX or non-real-timei status byte is seen) so
|
||||
* that, if the overflow condition is cleared, we don't start
|
||||
* sending data from the middle of a sysex message. If a sysex
|
||||
* message is filtered, sysex_in_progress is false, causing the
|
||||
* message to be dropped. */
|
||||
PmMessage sysex_message; /* buffer for 4 bytes of sysex data */
|
||||
int sysex_message_count; /* how many bytes in sysex_message so far */
|
||||
|
||||
long filters; /* flags that filter incoming message classes */
|
||||
int channel_mask; /* flter incoming messages based on channel */
|
||||
PmTimestamp last_msg_time; /* timestamp of last message */
|
||||
PmTimestamp sync_time; /* time of last synchronization */
|
||||
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 */
|
||||
/* 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,
|
||||
* but this does not count time in the driver, so I don't know if it is
|
||||
* important
|
||||
*/
|
||||
unsigned char *fill_base; /* addr of ptr to sysex data */
|
||||
int *fill_offset_ptr; /* offset of next sysex byte */
|
||||
int fill_length; /* how many sysex bytes to write */
|
||||
} PmInternal;
|
||||
|
||||
|
||||
/* defined by system specific implementation, e.g. pmwinmm, used by PortMidi */
|
||||
void pm_init(void);
|
||||
void pm_term(void);
|
||||
|
||||
/* defined by portMidi, used by pmwinmm */
|
||||
PmError none_write_short(PmInternal *midi, PmEvent *buffer);
|
||||
PmError none_write_byte(PmInternal *midi, unsigned char byte,
|
||||
PmTimestamp timestamp);
|
||||
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);
|
||||
unsigned int pm_read_bytes(PmInternal *midi, unsigned char *data, int len,
|
||||
PmTimestamp timestamp);
|
||||
void pm_read_short(PmInternal *midi, PmEvent *event);
|
||||
|
||||
#define none_write_flush pm_fail_timestamp_fn
|
||||
#define none_sysex pm_fail_timestamp_fn
|
||||
#define none_poll pm_fail_fn
|
||||
#define success_poll pm_success_fn
|
||||
|
||||
#define MIDI_REALTIME_MASK 0xf8
|
||||
#define is_real_time(msg) \
|
||||
((Pm_MessageStatus(msg) & MIDI_REALTIME_MASK) == MIDI_REALTIME_MASK)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
311
pd-0.44-2/portmidi/pm_common/pmutil.c
Executable file
311
pd-0.44-2/portmidi/pm_common/pmutil.c
Executable file
|
@ -0,0 +1,311 @@
|
|||
/* pmutil.c -- some helpful utilities for building midi
|
||||
applications that use PortMidi
|
||||
*/
|
||||
#include "stdlib.h"
|
||||
#include "assert.h"
|
||||
#include "memory.h"
|
||||
#include "portmidi.h"
|
||||
#include "pmutil.h"
|
||||
#include "pminternal.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define bzero(addr, siz) memset(addr, 0, siz)
|
||||
#endif
|
||||
|
||||
// #define QUEUE_DEBUG 1
|
||||
#ifdef QUEUE_DEBUG
|
||||
#include "stdio.h"
|
||||
#endif
|
||||
|
||||
/* code is based on 4-byte words -- it should work on a 64-bit machine
|
||||
as long as a "long" has 4 bytes. This code could be generalized to
|
||||
be independent of the size of "long" */
|
||||
|
||||
typedef long int32;
|
||||
|
||||
typedef struct {
|
||||
long head;
|
||||
long tail;
|
||||
long len;
|
||||
long msg_size; /* number of int32 in a message including extra word */
|
||||
long overflow;
|
||||
long peek_overflow;
|
||||
int32 *buffer;
|
||||
int32 *peek;
|
||||
int peek_flag;
|
||||
} PmQueueRep;
|
||||
|
||||
|
||||
PmQueue *Pm_QueueCreate(long num_msgs, long bytes_per_msg)
|
||||
{
|
||||
PmQueueRep *queue = (PmQueueRep *) pm_alloc(sizeof(PmQueueRep));
|
||||
int int32s_per_msg = ((bytes_per_msg + sizeof(int32) - 1) &
|
||||
~(sizeof(int32) - 1)) / sizeof(int32);
|
||||
/* arg checking */
|
||||
if (!queue)
|
||||
return NULL;
|
||||
|
||||
/* need extra word per message for non-zero encoding */
|
||||
queue->len = num_msgs * (int32s_per_msg + 1);
|
||||
queue->buffer = (int32 *) pm_alloc(queue->len * sizeof(int32));
|
||||
bzero(queue->buffer, queue->len * sizeof(int32));
|
||||
if (!queue->buffer) {
|
||||
pm_free(queue);
|
||||
return NULL;
|
||||
} else { /* allocate the "peek" buffer */
|
||||
queue->peek = (int32 *) pm_alloc(int32s_per_msg * sizeof(int32));
|
||||
if (!queue->peek) {
|
||||
/* free everything allocated so far and return */
|
||||
pm_free(queue->buffer);
|
||||
pm_free(queue);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
bzero(queue->buffer, queue->len * sizeof(int32));
|
||||
queue->head = 0;
|
||||
queue->tail = 0;
|
||||
/* msg_size is in words */
|
||||
queue->msg_size = int32s_per_msg + 1; /* note extra word is counted */
|
||||
queue->overflow = FALSE;
|
||||
queue->peek_overflow = FALSE;
|
||||
queue->peek_flag = FALSE;
|
||||
return queue;
|
||||
}
|
||||
|
||||
|
||||
PmError Pm_QueueDestroy(PmQueue *q)
|
||||
{
|
||||
PmQueueRep *queue = (PmQueueRep *) q;
|
||||
|
||||
/* arg checking */
|
||||
if (!queue || !queue->buffer || !queue->peek)
|
||||
return pmBadPtr;
|
||||
|
||||
pm_free(queue->peek);
|
||||
pm_free(queue->buffer);
|
||||
pm_free(queue);
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
PmError Pm_Dequeue(PmQueue *q, void *msg)
|
||||
{
|
||||
long head;
|
||||
PmQueueRep *queue = (PmQueueRep *) q;
|
||||
int i;
|
||||
int32 *msg_as_int32 = (int32 *) msg;
|
||||
|
||||
/* arg checking */
|
||||
if (!queue)
|
||||
return pmBadPtr;
|
||||
/* a previous peek operation encountered an overflow, but the overflow
|
||||
* has not yet been reported to client, so do it now. No message is
|
||||
* returned, but on the next call, we will return the peek buffer.
|
||||
*/
|
||||
if (queue->peek_overflow) {
|
||||
queue->peek_overflow = FALSE;
|
||||
return pmBufferOverflow;
|
||||
}
|
||||
if (queue->peek_flag) {
|
||||
#ifdef QUEUE_DEBUG
|
||||
printf("Pm_Dequeue returns peek msg:");
|
||||
for (i = 0; i < queue->msg_size - 1; i++) {
|
||||
printf(" %d", queue->peek[i]);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
memcpy(msg, queue->peek, (queue->msg_size - 1) * sizeof(int32));
|
||||
queue->peek_flag = FALSE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
head = queue->head;
|
||||
/* if writer overflows, it writes queue->overflow = tail+1 so that
|
||||
* when the reader gets to that position in the buffer, it can
|
||||
* return the overflow condition to the reader. The problem is that
|
||||
* at overflow, things have wrapped around, so tail == head, and the
|
||||
* reader will detect overflow immediately instead of waiting until
|
||||
* it reads everything in the buffer, wrapping around again to the
|
||||
* point where tail == head. So the condition also checks that
|
||||
* queue->buffer[head] is zero -- if so, then the buffer is now
|
||||
* empty, and we're at the point in the msg stream where overflow
|
||||
* occurred. It's time to signal overflow to the reader. If
|
||||
* queue->buffer[head] is non-zero, there's a message there and we
|
||||
* should read all the way around the buffer before signalling overflow.
|
||||
* There is a write-order dependency here, but to fail, the overflow
|
||||
* field would have to be written while an entire buffer full of
|
||||
* writes are still pending. I'm assuming out-of-order writes are
|
||||
* possible, but not that many.
|
||||
*/
|
||||
if (queue->overflow == head + 1 && !queue->buffer[head]) {
|
||||
queue->overflow = 0; /* non-overflow condition */
|
||||
return pmBufferOverflow;
|
||||
}
|
||||
|
||||
/* test to see if there is data in the queue -- test from back
|
||||
* to front so if writer is simultaneously writing, we don't
|
||||
* waste time discovering the write is not finished
|
||||
*/
|
||||
for (i = queue->msg_size - 1; i >= 0; i--) {
|
||||
if (!queue->buffer[head + i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#ifdef QUEUE_DEBUG
|
||||
printf("Pm_Dequeue:");
|
||||
for (i = 0; i < queue->msg_size; i++) {
|
||||
printf(" %d", queue->buffer[head + i]);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
memcpy(msg, (char *) &queue->buffer[head + 1],
|
||||
sizeof(int32) * (queue->msg_size - 1));
|
||||
/* fix up zeros */
|
||||
i = queue->buffer[head];
|
||||
while (i < queue->msg_size) {
|
||||
int32 j;
|
||||
i--; /* msg does not have extra word so shift down */
|
||||
j = msg_as_int32[i];
|
||||
msg_as_int32[i] = 0;
|
||||
i = j;
|
||||
}
|
||||
/* signal that data has been removed by zeroing: */
|
||||
bzero((char *) &queue->buffer[head], sizeof(int32) * queue->msg_size);
|
||||
|
||||
/* update head */
|
||||
head += queue->msg_size;
|
||||
if (head == queue->len) head = 0;
|
||||
queue->head = head;
|
||||
return 1; /* success */
|
||||
}
|
||||
|
||||
|
||||
|
||||
PmError Pm_SetOverflow(PmQueue *q)
|
||||
{
|
||||
PmQueueRep *queue = (PmQueueRep *) q;
|
||||
long tail;
|
||||
/* no more enqueue until receiver acknowledges overflow */
|
||||
if (queue->overflow) return pmBufferOverflow;
|
||||
if (!queue)
|
||||
return pmBadPtr;
|
||||
tail = queue->tail;
|
||||
queue->overflow = tail + 1;
|
||||
return pmBufferOverflow;
|
||||
}
|
||||
|
||||
|
||||
PmError Pm_Enqueue(PmQueue *q, void *msg)
|
||||
{
|
||||
PmQueueRep *queue = (PmQueueRep *) q;
|
||||
long tail;
|
||||
int i;
|
||||
int32 *src = (int32 *) msg;
|
||||
int32 *ptr;
|
||||
|
||||
int32 *dest;
|
||||
|
||||
int rslt;
|
||||
/* no more enqueue until receiver acknowledges overflow */
|
||||
if (!queue) return pmBadPtr;
|
||||
if (queue->overflow) return pmBufferOverflow;
|
||||
rslt = Pm_QueueFull(q);
|
||||
/* already checked above: if (rslt == pmBadPtr) return rslt; */
|
||||
tail = queue->tail;
|
||||
if (rslt) {
|
||||
queue->overflow = tail + 1;
|
||||
return pmBufferOverflow;
|
||||
}
|
||||
|
||||
/* queue is has room for message, and overflow flag is cleared */
|
||||
ptr = &queue->buffer[tail];
|
||||
dest = ptr + 1;
|
||||
for (i = 1; i < queue->msg_size; i++) {
|
||||
int32 j = src[i - 1];
|
||||
if (!j) {
|
||||
*ptr = i;
|
||||
ptr = dest;
|
||||
} else {
|
||||
*dest = j;
|
||||
}
|
||||
dest++;
|
||||
}
|
||||
*ptr = i;
|
||||
#ifdef QUEUE_DEBUG
|
||||
printf("Pm_Enqueue:");
|
||||
for (i = 0; i < queue->msg_size; i++) {
|
||||
printf(" %d", queue->buffer[tail + i]);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
tail += queue->msg_size;
|
||||
if (tail == queue->len) tail = 0;
|
||||
queue->tail = tail;
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
int Pm_QueueEmpty(PmQueue *q)
|
||||
{
|
||||
PmQueueRep *queue = (PmQueueRep *) q;
|
||||
if (!queue) return TRUE;
|
||||
return (queue->buffer[queue->head] == 0);
|
||||
}
|
||||
|
||||
|
||||
int Pm_QueueFull(PmQueue *q)
|
||||
{
|
||||
PmQueueRep *queue = (PmQueueRep *) q;
|
||||
int tail;
|
||||
int i;
|
||||
/* arg checking */
|
||||
if (!queue)
|
||||
return pmBadPtr;
|
||||
tail = queue->tail;
|
||||
/* test to see if there is space in the queue */
|
||||
for (i = 0; i < queue->msg_size; i++) {
|
||||
if (queue->buffer[tail + i]) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void *Pm_QueuePeek(PmQueue *q)
|
||||
{
|
||||
PmQueueRep *queue = (PmQueueRep *) q;
|
||||
PmError rslt;
|
||||
long temp;
|
||||
|
||||
/* arg checking */
|
||||
if (!queue)
|
||||
return NULL;
|
||||
|
||||
if (queue->peek_flag) {
|
||||
return queue->peek;
|
||||
}
|
||||
/* this is ugly: if peek_overflow is set, then Pm_Dequeue()
|
||||
* returns immediately with pmBufferOverflow, but here, we
|
||||
* want Pm_Dequeue() to really check for data. If data is
|
||||
* there, we can return it
|
||||
*/
|
||||
temp = queue->peek_overflow;
|
||||
queue->peek_overflow = FALSE;
|
||||
rslt = Pm_Dequeue(q, queue->peek);
|
||||
queue->peek_overflow = temp;
|
||||
|
||||
if (rslt == 1) {
|
||||
queue->peek_flag = TRUE;
|
||||
return queue->peek;
|
||||
} else if (rslt == pmBufferOverflow) {
|
||||
/* when overflow is indicated, the queue is empty and the
|
||||
* first message that was dropped by Enqueue (signalling
|
||||
* pmBufferOverflow to its caller) would have been the next
|
||||
* message in the queue. Pm_QueuePeek will return NULL, but
|
||||
* remember that an overflow occurred. (see Pm_Dequeue)
|
||||
*/
|
||||
queue->peek_overflow = TRUE;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
124
pd-0.44-2/portmidi/pm_common/pmutil.h
Executable file
124
pd-0.44-2/portmidi/pm_common/pmutil.h
Executable file
|
@ -0,0 +1,124 @@
|
|||
/* pmutil.h -- some helpful utilities for building midi
|
||||
applications that use PortMidi
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
This implementation does not try to keep reader/writer data in
|
||||
separate cache lines or prevent thrashing on cache lines.
|
||||
However, this algorithm differs by doing inserts/removals in
|
||||
units of messages rather than units of machine words. Some
|
||||
performance improvement might be obtained by not clearing data
|
||||
immediately after a read, but instead by waiting for the end
|
||||
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.
|
||||
*/
|
||||
|
||||
PmQueue *Pm_QueueCreate(long num_msgs, long bytes_per_msg);
|
||||
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
|
||||
notified when data is lost due to overflow.
|
||||
*/
|
||||
PmError Pm_Dequeue(PmQueue *queue, void *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.
|
||||
*/
|
||||
PmError Pm_Enqueue(PmQueue *queue, void *msg);
|
||||
|
||||
|
||||
/*
|
||||
Pm_QueueFull() returns non-zero if the queue is full
|
||||
Pm_QueueEmpty() returns non-zero if the queue is empty
|
||||
|
||||
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.
|
||||
*/
|
||||
int Pm_QueueFull(PmQueue *queue);
|
||||
int Pm_QueueEmpty(PmQueue *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
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
PmError Pm_SetOverflow(PmQueue *queue);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
1184
pd-0.44-2/portmidi/pm_common/portmidi.c
Executable file
1184
pd-0.44-2/portmidi/pm_common/portmidi.c
Executable file
File diff suppressed because it is too large
Load diff
606
pd-0.44-2/portmidi/pm_common/portmidi.h
Executable file
606
pd-0.44-2/portmidi/pm_common/portmidi.h
Executable file
|
@ -0,0 +1,606 @@
|
|||
#ifndef PORT_MIDI_H
|
||||
#define PORT_MIDI_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* PortMidi Portable Real-Time MIDI Library
|
||||
* PortMidi API Header File
|
||||
* Latest version available at: http://www.cs.cmu.edu/~music/portmidi/
|
||||
*
|
||||
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
|
||||
* Copyright (c) 2001-2006 Roger B. Dannenberg
|
||||
*
|
||||
* Latest version available at: http://www.cs.cmu.edu/~music/portmidi/
|
||||
*
|
||||
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
|
||||
* Copyright (c) 2001-2006 Roger B. Dannenberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortMidi license; however,
|
||||
* the PortMusic community also makes the following non-binding requests:
|
||||
*
|
||||
* Any person wishing to distribute modifications to the Software is
|
||||
* requested to send the modifications to the original developer so that
|
||||
* they can be incorporated into the canonical version. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
/* CHANGELOG FOR PORTMIDI
|
||||
* (see ../CHANGELOG.txt)
|
||||
*
|
||||
* IMPORTANT INFORMATION ABOUT A WIN32 BUG:
|
||||
*
|
||||
* Windows apparently has a serious midi bug -- if you do not close ports, Windows
|
||||
* may crash. PortMidi tries to protect against this by using a DLL to clean up.
|
||||
*
|
||||
* If client exits for example with:
|
||||
* i) assert
|
||||
* ii) Ctrl^c,
|
||||
* then DLL clean-up routine called. However, when client does something
|
||||
* really bad (e.g. assigns value to NULL pointer) then DLL CLEANUP ROUTINE
|
||||
* NEVER RUNS! In this state, if you wait around long enough, you will
|
||||
* probably get the blue screen of death. Can also go into Pview and there will
|
||||
* exist zombie process that you can't kill.
|
||||
*
|
||||
* You can enable the DLL cleanup routine by defining USE_DLL_FOR_CLEANUP.
|
||||
* Do not define this preprocessor symbol if you do not want to use this
|
||||
* feature.
|
||||
*
|
||||
* 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().
|
||||
*
|
||||
* 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.
|
||||
* USE_DLL_FOR_CLEANUP is described above. (Windows only.)
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
/* default size of buffers for sysex transmission: */
|
||||
#define PM_DEFAULT_SYSEX_BUFFER_SIZE 1024
|
||||
|
||||
|
||||
typedef enum {
|
||||
pmNoError = 0,
|
||||
pmHostError = -10000,
|
||||
pmInvalidDeviceId, /* out of range or
|
||||
* output device when input is requested or
|
||||
* input device when output is requested or
|
||||
* device is already opened
|
||||
*/
|
||||
pmInsufficientMemory,
|
||||
pmBufferTooSmall,
|
||||
pmBufferOverflow,
|
||||
pmBadPtr,
|
||||
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;
|
||||
|
||||
/*
|
||||
Pm_Initialize() is the library initialisation function - call this before
|
||||
using the library.
|
||||
*/
|
||||
|
||||
PmError Pm_Initialize( void );
|
||||
|
||||
/*
|
||||
Pm_Terminate() is the library termination function - call this after
|
||||
using the library.
|
||||
*/
|
||||
|
||||
PmError Pm_Terminate( void );
|
||||
|
||||
/* A single PortMidiStream is a descriptor for an open MIDI device.
|
||||
*/
|
||||
typedef void 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.
|
||||
*/
|
||||
int Pm_HasHostError( PortMidiStream * stream );
|
||||
|
||||
|
||||
/* Translate portmidi error number into human readable message.
|
||||
These strings are constants (set at compile time) so client has
|
||||
no need to allocate storage
|
||||
*/
|
||||
const char *Pm_GetErrorText( PmError errnum );
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
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.
|
||||
|
||||
*/
|
||||
typedef int PmDeviceID;
|
||||
#define pmNoDevice -1
|
||||
typedef struct {
|
||||
int structVersion;
|
||||
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 */
|
||||
|
||||
} PmDeviceInfo;
|
||||
|
||||
|
||||
int Pm_CountDevices( void );
|
||||
/*
|
||||
Pm_GetDefaultInputDeviceID(), Pm_GetDefaultOutputDeviceID()
|
||||
|
||||
Return the default device ID or pmNoDevice if there are no devices.
|
||||
The result can be passed to Pm_OpenMidi().
|
||||
|
||||
On the PC, the user can specify a default device by
|
||||
setting an environment variable. For example, to use device #1.
|
||||
|
||||
set PM_RECOMMENDED_OUTPUT_DEVICE=1
|
||||
|
||||
The user should first determine the available device ID by using
|
||||
the supplied application "testin" or "testout".
|
||||
|
||||
In general, the registry is a better place for this kind of info,
|
||||
and with USB devices that can come and go, using integers is not
|
||||
very reliable for device identification. Under Windows, if
|
||||
PM_RECOMMENDED_OUTPUT_DEVICE (or PM_RECOMMENDED_INPUT_DEVICE) is
|
||||
*NOT* found in the environment, then the default device is obtained
|
||||
by looking for a string in the registry under:
|
||||
HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Input_Device
|
||||
and HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Output_Device
|
||||
for a string. The number of the first device with a substring that
|
||||
matches the string exactly is returned. For example, if the string
|
||||
in the registry is "USB", and device 1 is named
|
||||
"In USB MidiSport 1x1", then that will be the default
|
||||
input because it contains the string "USB".
|
||||
|
||||
In addition to the name, PmDeviceInfo has the member "interf", which
|
||||
is the interface name. (The "interface" is the underlying software
|
||||
system or API used by PortMidi to access devices. Examples are
|
||||
MMSystem, DirectX (not implemented), ALSA, OSS (not implemented), etc.)
|
||||
At present, the only Win32 interface is "MMSystem", the only Linux
|
||||
interface is "ALSA", and the only Max OS X interface is "CoreMIDI".
|
||||
To specify both the interface and the device name in the registry,
|
||||
separate the two with a comma and a space, e.g.:
|
||||
MMSystem, In USB MidiSport 1x1
|
||||
In this case, the string before the comma must be a substring of
|
||||
the "interf" string, and the string after the space must be a
|
||||
substring of the "name" name string in order to match the device.
|
||||
|
||||
Note: in the current release, the default is simply the first device
|
||||
(the input or output device with the lowest PmDeviceID).
|
||||
*/
|
||||
PmDeviceID Pm_GetDefaultInputDeviceID( void );
|
||||
PmDeviceID Pm_GetDefaultOutputDeviceID( void );
|
||||
|
||||
/*
|
||||
PmTimestamp is used to represent a millisecond clock with arbitrary
|
||||
start time. The type is used for all MIDI timestampes and clocks.
|
||||
*/
|
||||
typedef long PmTimestamp;
|
||||
typedef PmTimestamp (*PmTimeProcPtr)(void *time_info);
|
||||
|
||||
/* TRUE if t1 before t2 */
|
||||
#define PmBefore(t1,t2) ((t1-t2) < 0)
|
||||
|
||||
/*
|
||||
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.
|
||||
|
||||
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().
|
||||
*/
|
||||
const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id );
|
||||
|
||||
/*
|
||||
Pm_OpenInput() and Pm_OpenOutput() open devices.
|
||||
|
||||
stream is the address of a PortMidiStream pointer which will receive
|
||||
a pointer to the newly opened stream.
|
||||
|
||||
inputDevice is the id of the device used for input (see PmDeviceID above).
|
||||
|
||||
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.
|
||||
|
||||
outputDevice is the id of the device used for output (see PmDeviceID above.)
|
||||
|
||||
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.
|
||||
|
||||
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: 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
|
||||
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).
|
||||
|
||||
time_info is a pointer passed to time_proc.
|
||||
|
||||
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.
|
||||
|
||||
Any stream that is successfully opened should eventually be closed
|
||||
by calling Pm_Close().
|
||||
|
||||
*/
|
||||
PmError Pm_OpenInput( PortMidiStream** stream,
|
||||
PmDeviceID inputDevice,
|
||||
void *inputDriverInfo,
|
||||
long bufferSize,
|
||||
PmTimeProcPtr time_proc,
|
||||
void *time_info );
|
||||
|
||||
PmError Pm_OpenOutput( PortMidiStream** stream,
|
||||
PmDeviceID outputDevice,
|
||||
void *outputDriverInfo,
|
||||
long bufferSize,
|
||||
PmTimeProcPtr time_proc,
|
||||
void *time_info,
|
||||
long latency );
|
||||
|
||||
/*
|
||||
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 active sensing messages (0xFE): */
|
||||
#define PM_FILT_ACTIVE (1 << 0x0E)
|
||||
/* filter system exclusive messages (0xF0): */
|
||||
#define PM_FILT_SYSEX (1 << 0x00)
|
||||
/* filter clock messages (CLOCK 0xF8, START 0xFA, STOP 0xFC, and CONTINUE 0xFB) */
|
||||
#define PM_FILT_CLOCK ((1 << 0x08) | (1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B))
|
||||
/* filter play messages (start 0xFA, stop 0xFC, continue 0xFB) */
|
||||
#define PM_FILT_PLAY (1 << 0x0A)
|
||||
/* filter tick messages (0xF9) */
|
||||
#define PM_FILT_TICK (1 << 0x09)
|
||||
/* filter undefined FD messages */
|
||||
#define PM_FILT_FD (1 << 0x0D)
|
||||
/* filter undefined real-time messages */
|
||||
#define PM_FILT_UNDEFINED PM_FILT_FD
|
||||
/* filter reset messages (0xFF) */
|
||||
#define PM_FILT_RESET (1 << 0x0F)
|
||||
/* filter all real-time messages */
|
||||
#define PM_FILT_REALTIME (PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK | \
|
||||
PM_FILT_PLAY | PM_FILT_UNDEFINED | PM_FILT_RESET | PM_FILT_TICK)
|
||||
/* filter note-on and note-off (0x90-0x9F and 0x80-0x8F */
|
||||
#define PM_FILT_NOTE ((1 << 0x19) | (1 << 0x18))
|
||||
/* filter channel aftertouch (most midi controllers use this) (0xD0-0xDF)*/
|
||||
#define PM_FILT_CHANNEL_AFTERTOUCH (1 << 0x1D)
|
||||
/* 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)
|
||||
/* Program changes (0xC0-0xCF) */
|
||||
#define PM_FILT_PROGRAM (1 << 0x1C)
|
||||
/* Control Changes (CC's) (0xB0-0xBF)*/
|
||||
#define PM_FILT_CONTROL (1 << 0x1B)
|
||||
/* Pitch Bender (0xE0-0xEF*/
|
||||
#define PM_FILT_PITCHBEND (1 << 0x1E)
|
||||
/* MIDI Time Code (0xF1)*/
|
||||
#define PM_FILT_MTC (1 << 0x01)
|
||||
/* Song Position (0xF2) */
|
||||
#define PM_FILT_SONG_POSITION (1 << 0x02)
|
||||
/* Song Select (0xF3)*/
|
||||
#define PM_FILT_SONG_SELECT (1 << 0x03)
|
||||
/* 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)
|
||||
|
||||
|
||||
PmError Pm_SetFilter( PortMidiStream* stream, long filters );
|
||||
|
||||
/*
|
||||
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
|
||||
Pm_SetChannelMask(Pm_Channel(1));
|
||||
Multiple channels should be OR'd together, like
|
||||
Pm_SetChannelMask(Pm_Channel(10) | Pm_Channel(11))
|
||||
|
||||
All channels are allowed by default
|
||||
*/
|
||||
#define Pm_Channel(channel) (1<<(channel))
|
||||
|
||||
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.
|
||||
*/
|
||||
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.)
|
||||
*/
|
||||
PmError Pm_Close( PortMidiStream* stream );
|
||||
|
||||
/*
|
||||
Pm_Message() encodes a short Midi message into a long word. If data1
|
||||
and/or data2 are not present, use zero.
|
||||
|
||||
Pm_MessageStatus(), Pm_MessageData1(), and
|
||||
Pm_MessageData2() extract fields from a long-encoded midi message.
|
||||
*/
|
||||
#define Pm_Message(status, data1, data2) \
|
||||
((((data2) << 16) & 0xFF0000) | \
|
||||
(((data1) << 8) & 0xFF00) | \
|
||||
((status) & 0xFF))
|
||||
#define Pm_MessageStatus(msg) ((msg) & 0xFF)
|
||||
#define Pm_MessageData1(msg) (((msg) >> 8) & 0xFF)
|
||||
#define Pm_MessageData2(msg) (((msg) >> 16) & 0xFF)
|
||||
|
||||
/* 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.
|
||||
|
||||
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
|
||||
messages used mainly for timing (see the MIDI spec). PortMidi retains
|
||||
the order of non-real-time MIDI messages on both input and output, but
|
||||
it does not specify exactly how real-time messages are processed. This
|
||||
is particulary problematic for MIDI input, because the input parser
|
||||
must either prepare to buffer an unlimited number of sysex message
|
||||
bytes or to buffer an unlimited number of real-time messages that
|
||||
arrive embedded in a long sysex message. To simplify things, the input
|
||||
parser is allowed to pass real-time MIDI messages embedded within a
|
||||
sysex message, and it is up to the client to detect, process, and
|
||||
remove these messages as they arrive.
|
||||
|
||||
When receiving sysex messages, the sysex message is terminated
|
||||
by either an EOX status byte (anywhere in the 4 byte messages) or
|
||||
by a non-real-time status byte in the low order byte of the message.
|
||||
If you get a non-real-time status byte but there was no EOX byte, it
|
||||
means the sysex message was somehow truncated. This is not
|
||||
considered an error; e.g., a missing EOX can result from the user
|
||||
disconnecting a MIDI cable during sysex transmission.
|
||||
|
||||
A real-time message can occur within a sysex message. A real-time
|
||||
message will always occupy a full PmEvent with the status byte in
|
||||
the low-order byte of the PmEvent message field. (This implies that
|
||||
the byte-order of sysex bytes and real-time message bytes may not
|
||||
be preserved -- for example, if a real-time message arrives after
|
||||
3 bytes of a sysex message, the real-time message will be delivered
|
||||
first. The first word of the sysex message will be delivered only
|
||||
after the 4th byte arrives, filling the 4-byte PmEvent message field.
|
||||
|
||||
The timestamp field is observed when the output port is opened with
|
||||
a non-zero latency. A timestamp of zero means "use the current time",
|
||||
which in turn means to deliver the message with a delay of
|
||||
latency (the latency parameter used when opening the output port.)
|
||||
Do not expect PortMidi to sort data according to timestamps --
|
||||
messages should be sent in the correct order, and timestamps MUST
|
||||
be non-decreasing.
|
||||
|
||||
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.
|
||||
|
||||
On input, the timestamp ideally denotes the arrival time of the
|
||||
status byte of the message. The first timestamp on sysex message
|
||||
data will be valid. Subsequent timestamps may denote
|
||||
when message bytes were actually received, or they may be simply
|
||||
copies of the first timestamp.
|
||||
|
||||
Timestamps for nested messages: If a real-time message arrives in
|
||||
the middle of some other message, it is enqueued immediately with
|
||||
the timestamp corresponding to its arrival time. The interrupted
|
||||
non-real-time message or 4-byte packet of sysex data will be enqueued
|
||||
later. The timestamp of interrupted data will be equal to that of
|
||||
the interrupting real-time message to insure that timestamps are
|
||||
non-decreasing.
|
||||
*/
|
||||
typedef long PmMessage;
|
||||
typedef struct {
|
||||
PmMessage message;
|
||||
PmTimestamp timestamp;
|
||||
} PmEvent;
|
||||
|
||||
/*
|
||||
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.
|
||||
|
||||
Buffer Overflow
|
||||
|
||||
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 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.
|
||||
|
||||
*/
|
||||
PmError Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length );
|
||||
|
||||
/*
|
||||
Pm_Poll() tests whether input is available,
|
||||
returning TRUE, FALSE, or an error value.
|
||||
*/
|
||||
PmError Pm_Poll( PortMidiStream *stream);
|
||||
|
||||
/*
|
||||
Pm_Write() writes midi data from a buffer. This 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.
|
||||
|
||||
Use Pm_WriteSysEx() to write a sysex message stored as a contiguous
|
||||
array of bytes.
|
||||
|
||||
Sysex data may contain embedded real-time messages.
|
||||
*/
|
||||
PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long 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.)
|
||||
*/
|
||||
PmError Pm_WriteShort( PortMidiStream *stream, PmTimestamp when, long msg);
|
||||
|
||||
/*
|
||||
Pm_WriteSysEx() writes a timestamped system-exclusive midi message.
|
||||
*/
|
||||
PmError Pm_WriteSysEx( PortMidiStream *stream, PmTimestamp when, unsigned char *msg);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* PORT_MIDI_H */
|
43
pd-0.44-2/portmidi/pm_linux/README_LINUX.txt
Executable file
43
pd-0.44-2/portmidi/pm_linux/README_LINUX.txt
Executable file
|
@ -0,0 +1,43 @@
|
|||
README_LINUX.txt for PortMidi
|
||||
Roger Dannenberg
|
||||
29 Aug 2006
|
||||
|
||||
To make PortMidi and PortTime, go back up to the portmidi
|
||||
directory and type
|
||||
|
||||
make -f pm_linux/Makefile
|
||||
|
||||
(You can also copy pm_linux/Makefile to the portmidi
|
||||
directory and just type "make".)
|
||||
|
||||
The Makefile will build all test programs and the portmidi
|
||||
library. You may want to modify the Makefile to remove the
|
||||
PM_CHECK_ERRORS definition. For experimental software,
|
||||
especially programs running from the command line, we
|
||||
recommend using PM_CHECK_ERRORS -- it will terminate your
|
||||
program and print a helpful message if any PortMidi
|
||||
function returns an error code.
|
||||
|
||||
If you do not compile with PM_CHECK_ERRORS, you should
|
||||
check for errors yourself.
|
||||
|
||||
This code has not been carefully tested; however,
|
||||
all test programs in pm_test seem to run properly.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
29-aug-2006 Roger B. Dannenberg
|
||||
Fixed PortTime to join with time thread for clean exit.
|
||||
|
||||
28-aug-2006 Roger B. Dannenberg
|
||||
Updated this documentation.
|
||||
|
||||
08-Jun-2004 Roger B. Dannenberg
|
||||
Updated code to use new system abstraction.
|
||||
|
||||
12-Apr-2003 Roger B. Dannenberg
|
||||
Fixed pm_test/test.c to filter clocks and active messages.
|
||||
Integrated changes from Clemens Ladisch:
|
||||
cleaned up pmlinuxalsa.c
|
||||
record timestamp on sysex input
|
||||
deallocate some resources previously left open
|
60
pd-0.44-2/portmidi/pm_linux/pmlinux.c
Executable file
60
pd-0.44-2/portmidi/pm_linux/pmlinux.c
Executable file
|
@ -0,0 +1,60 @@
|
|||
/* pmlinux.c -- PortMidi os-dependent code */
|
||||
|
||||
/* This file only needs to implement pm_init(), which calls various
|
||||
routines to register the available midi devices. This file must
|
||||
be separate from the main portmidi.c file because it is system
|
||||
dependent, and it is separate from, pmlinuxalsa.c, because it
|
||||
might need to register non-alsa devices as well.
|
||||
|
||||
NOTE: if you add non-ALSA support, you need to fix :alsa_poll()
|
||||
in pmlinuxalsa.c, which assumes all input devices are ALSA.
|
||||
*/
|
||||
|
||||
#include "stdlib.h"
|
||||
#include "portmidi.h"
|
||||
#ifdef PMALSA
|
||||
#include "pmlinuxalsa.h"
|
||||
#endif
|
||||
|
||||
#ifdef PMNULL
|
||||
#include "pmlinuxnull.h"
|
||||
#endif
|
||||
|
||||
PmError pm_init()
|
||||
{
|
||||
/* Note: it is not an error for PMALSA to fail to initialize.
|
||||
* It may be a design error that the client cannot query what subsystems
|
||||
* are working properly other than by looking at the list of available
|
||||
* devices.
|
||||
*/
|
||||
#ifdef PMALSA
|
||||
pm_linuxalsa_init();
|
||||
#endif
|
||||
#ifdef PMNULL
|
||||
pm_linuxnull_init();
|
||||
#endif
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
void pm_term(void)
|
||||
{
|
||||
#ifdef PMALSA
|
||||
pm_linuxalsa_term();
|
||||
#endif
|
||||
}
|
||||
|
||||
PmDeviceID pm_default_input_device_id = -1;
|
||||
PmDeviceID pm_default_output_device_id = -1;
|
||||
|
||||
PmDeviceID Pm_GetDefaultInputDeviceID() {
|
||||
return pm_default_input_device_id;
|
||||
}
|
||||
|
||||
PmDeviceID Pm_GetDefaultOutputDeviceID() {
|
||||
return pm_default_output_device_id;
|
||||
}
|
||||
|
||||
void *pm_alloc(size_t s) { return malloc(s); }
|
||||
|
||||
void pm_free(void *ptr) { free(ptr); }
|
||||
|
5
pd-0.44-2/portmidi/pm_linux/pmlinux.h
Executable file
5
pd-0.44-2/portmidi/pm_linux/pmlinux.h
Executable file
|
@ -0,0 +1,5 @@
|
|||
/* pmlinux.h */
|
||||
|
||||
extern PmDeviceID pm_default_input_device_id;
|
||||
extern PmDeviceID pm_default_output_device_id;
|
||||
|
782
pd-0.44-2/portmidi/pm_linux/pmlinuxalsa.c
Executable file
782
pd-0.44-2/portmidi/pm_linux/pmlinuxalsa.c
Executable file
|
@ -0,0 +1,782 @@
|
|||
/*
|
||||
* pmlinuxalsa.c -- system specific definitions
|
||||
*
|
||||
* written by:
|
||||
* Roger Dannenberg (port to Alsa 0.9.x)
|
||||
* Clemens Ladisch (provided code examples and invaluable consulting)
|
||||
* Jason Cohen, Rico Colon, Matt Filippone (Alsa 0.5.x implementation)
|
||||
*/
|
||||
|
||||
#include "stdlib.h"
|
||||
#include "portmidi.h"
|
||||
#ifdef NEWBUFFER
|
||||
#include "pmutil.h"
|
||||
#endif
|
||||
#include "pminternal.h"
|
||||
#include "pmlinuxalsa.h"
|
||||
#include "string.h"
|
||||
#include "porttime.h"
|
||||
#include "pmlinux.h"
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
/* I used many print statements to debug this code. I left them in the
|
||||
* source, and you can turn them on by changing false to true below:
|
||||
*/
|
||||
#define VERBOSE_ON 0
|
||||
#define VERBOSE if (VERBOSE_ON)
|
||||
|
||||
#define MIDI_SYSEX 0xf0
|
||||
#define MIDI_EOX 0xf7
|
||||
|
||||
#if SND_LIB_MAJOR == 0 && SND_LIB_MINOR < 9
|
||||
#error needs ALSA 0.9.0 or later
|
||||
#endif
|
||||
|
||||
/* to store client/port in the device descriptor */
|
||||
#define MAKE_DESCRIPTOR(client, port) ((void*)(((client) << 8) | (port)))
|
||||
#define GET_DESCRIPTOR_CLIENT(info) ((((int)(info)) >> 8) & 0xff)
|
||||
#define GET_DESCRIPTOR_PORT(info) (((int)(info)) & 0xff)
|
||||
|
||||
#define BYTE unsigned char
|
||||
#define UINT unsigned long
|
||||
|
||||
extern pm_fns_node pm_linuxalsa_in_dictionary;
|
||||
extern pm_fns_node pm_linuxalsa_out_dictionary;
|
||||
|
||||
static snd_seq_t *seq = NULL; // all input comes here,
|
||||
// output queue allocated on seq
|
||||
static int queue, queue_used; /* one for all ports, reference counted */
|
||||
|
||||
typedef struct alsa_descriptor_struct {
|
||||
int client;
|
||||
int port;
|
||||
int this_port;
|
||||
int in_sysex;
|
||||
snd_midi_event_t *parser;
|
||||
int error; /* host error code */
|
||||
} alsa_descriptor_node, *alsa_descriptor_type;
|
||||
|
||||
|
||||
/* get_alsa_error_text -- copy error text to potentially short string */
|
||||
/**/
|
||||
static void get_alsa_error_text(char *msg, int len, int err)
|
||||
{
|
||||
int errlen = strlen(snd_strerror(err));
|
||||
if (errlen < len) {
|
||||
strcpy(msg, snd_strerror(err));
|
||||
} else if (len > 20) {
|
||||
sprintf(msg, "Alsa error %d", err);
|
||||
} else if (len > 4) {
|
||||
strcpy(msg, "Alsa");
|
||||
} else {
|
||||
msg[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* queue is shared by both input and output, reference counted */
|
||||
static PmError alsa_use_queue(void)
|
||||
{
|
||||
if (queue_used == 0) {
|
||||
snd_seq_queue_tempo_t *tempo;
|
||||
|
||||
queue = snd_seq_alloc_queue(seq);
|
||||
if (queue < 0) {
|
||||
pm_hosterror = queue;
|
||||
return pmHostError;
|
||||
}
|
||||
snd_seq_queue_tempo_alloca(&tempo);
|
||||
snd_seq_queue_tempo_set_tempo(tempo, 480000);
|
||||
snd_seq_queue_tempo_set_ppq(tempo, 480);
|
||||
pm_hosterror = snd_seq_set_queue_tempo(seq, queue, tempo);
|
||||
if (pm_hosterror < 0)
|
||||
return pmHostError;
|
||||
|
||||
snd_seq_start_queue(seq, queue, NULL);
|
||||
snd_seq_drain_output(seq);
|
||||
}
|
||||
++queue_used;
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
static void alsa_unuse_queue(void)
|
||||
{
|
||||
if (--queue_used == 0) {
|
||||
snd_seq_stop_queue(seq, queue, NULL);
|
||||
snd_seq_drain_output(seq);
|
||||
snd_seq_free_queue(seq, queue);
|
||||
VERBOSE printf("queue freed\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* midi_message_length -- how many bytes in a message? */
|
||||
static int midi_message_length(PmMessage message)
|
||||
{
|
||||
message &= 0xff;
|
||||
if (message < 0x80) {
|
||||
return 0;
|
||||
} else if (message < 0xf0) {
|
||||
static const int length[] = {3, 3, 3, 3, 2, 2, 3};
|
||||
return length[(message - 0x80) >> 4];
|
||||
} else {
|
||||
static const int length[] = {
|
||||
-1, 2, 3, 2, 0, 0, 1, -1, 1, 0, 1, 1, 1, 0, 1, 1};
|
||||
return length[message - 0xf0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static PmError alsa_out_open(PmInternal *midi, void *driverInfo)
|
||||
{
|
||||
void *client_port = descriptors[midi->device_id].descriptor;
|
||||
alsa_descriptor_type desc = (alsa_descriptor_type)
|
||||
pm_alloc(sizeof(alsa_descriptor_node));
|
||||
snd_seq_port_info_t *info;
|
||||
int err;
|
||||
|
||||
if (!desc) return pmInsufficientMemory;
|
||||
|
||||
snd_seq_port_info_alloca(&info);
|
||||
snd_seq_port_info_set_port(info, midi->device_id);
|
||||
snd_seq_port_info_set_capability(info, SND_SEQ_PORT_CAP_WRITE |
|
||||
SND_SEQ_PORT_CAP_READ);
|
||||
snd_seq_port_info_set_type(info, SND_SEQ_PORT_TYPE_MIDI_GENERIC |
|
||||
SND_SEQ_PORT_TYPE_APPLICATION);
|
||||
snd_seq_port_info_set_port_specified(info, 1);
|
||||
err = snd_seq_create_port(seq, info);
|
||||
if (err < 0) goto free_desc;
|
||||
|
||||
/* fill in fields of desc, which is passed to pm_write routines */
|
||||
midi->descriptor = desc;
|
||||
desc->client = GET_DESCRIPTOR_CLIENT(client_port);
|
||||
desc->port = GET_DESCRIPTOR_PORT(client_port);
|
||||
desc->this_port = midi->device_id;
|
||||
desc->in_sysex = 0;
|
||||
|
||||
desc->error = 0;
|
||||
|
||||
err = snd_midi_event_new(PM_DEFAULT_SYSEX_BUFFER_SIZE, &desc->parser);
|
||||
if (err < 0) goto free_this_port;
|
||||
|
||||
if (midi->latency > 0) { /* must delay output using a queue */
|
||||
err = alsa_use_queue();
|
||||
if (err < 0) goto free_parser;
|
||||
|
||||
err = snd_seq_connect_to(seq, desc->this_port, desc->client, desc->port);
|
||||
if (err < 0) goto unuse_queue; /* clean up and return on error */
|
||||
} else {
|
||||
err = snd_seq_connect_to(seq, desc->this_port, desc->client, desc->port);
|
||||
if (err < 0) goto free_parser; /* clean up and return on error */
|
||||
}
|
||||
return pmNoError;
|
||||
|
||||
unuse_queue:
|
||||
alsa_unuse_queue();
|
||||
free_parser:
|
||||
snd_midi_event_free(desc->parser);
|
||||
free_this_port:
|
||||
snd_seq_delete_port(seq, desc->this_port);
|
||||
free_desc:
|
||||
pm_free(desc);
|
||||
pm_hosterror = err;
|
||||
if (err < 0) {
|
||||
get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, err);
|
||||
}
|
||||
return pmHostError;
|
||||
}
|
||||
|
||||
|
||||
static PmError alsa_write_byte(PmInternal *midi, unsigned char byte,
|
||||
PmTimestamp timestamp)
|
||||
{
|
||||
alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
|
||||
snd_seq_event_t ev;
|
||||
int err;
|
||||
|
||||
snd_seq_ev_clear(&ev);
|
||||
if (snd_midi_event_encode_byte(desc->parser, byte, &ev) == 1) {
|
||||
snd_seq_ev_set_dest(&ev, desc->client, desc->port);
|
||||
snd_seq_ev_set_source(&ev, desc->this_port);
|
||||
if (midi->latency > 0) {
|
||||
/* compute relative time of event = timestamp - now + latency */
|
||||
PmTimestamp now = (midi->time_proc ?
|
||||
midi->time_proc(midi->time_info) :
|
||||
Pt_Time(NULL));
|
||||
int when = timestamp;
|
||||
/* if timestamp is zero, send immediately */
|
||||
/* otherwise compute time delay and use delay if positive */
|
||||
if (when == 0) when = now;
|
||||
when = (when - now) + midi->latency;
|
||||
if (when < 0) when = 0;
|
||||
VERBOSE printf("timestamp %d now %d latency %d, ",
|
||||
(int) timestamp, (int) now, midi->latency);
|
||||
VERBOSE printf("scheduling event after %d\n", when);
|
||||
/* message is sent in relative ticks, where 1 tick = 1 ms */
|
||||
snd_seq_ev_schedule_tick(&ev, queue, 1, when);
|
||||
/* NOTE: for cases where the user does not supply a time function,
|
||||
we could optimize the code by not starting Pt_Time and using
|
||||
the alsa tick time instead. I didn't do this because it would
|
||||
entail changing the queue management to start the queue tick
|
||||
count when PortMidi is initialized and keep it running until
|
||||
PortMidi is terminated. (This should be simple, but it's not
|
||||
how the code works now.) -RBD */
|
||||
} else { /* send event out without queueing */
|
||||
VERBOSE printf("direct\n");
|
||||
/* ev.queue = SND_SEQ_QUEUE_DIRECT;
|
||||
ev.dest.client = SND_SEQ_ADDRESS_SUBSCRIBERS; */
|
||||
snd_seq_ev_set_direct(&ev);
|
||||
}
|
||||
VERBOSE printf("sending event\n");
|
||||
err = snd_seq_event_output(seq, &ev);
|
||||
if (err < 0) {
|
||||
desc->error = err;
|
||||
return pmHostError;
|
||||
}
|
||||
}
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
static PmError alsa_out_close(PmInternal *midi)
|
||||
{
|
||||
alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
|
||||
if (!desc) return pmBadPtr;
|
||||
|
||||
if (pm_hosterror = snd_seq_disconnect_to(seq, desc->this_port,
|
||||
desc->client, desc->port)) {
|
||||
// if there's an error, try to delete the port anyway, but don't
|
||||
// change the pm_hosterror value so we retain the first error
|
||||
snd_seq_delete_port(seq, desc->this_port);
|
||||
} else { // if there's no error, delete the port and retain any error
|
||||
pm_hosterror = snd_seq_delete_port(seq, desc->this_port);
|
||||
}
|
||||
if (midi->latency > 0) alsa_unuse_queue();
|
||||
snd_midi_event_free(desc->parser);
|
||||
midi->descriptor = NULL; /* destroy the pointer to signify "closed" */
|
||||
pm_free(desc);
|
||||
if (pm_hosterror) {
|
||||
get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN,
|
||||
pm_hosterror);
|
||||
return pmHostError;
|
||||
}
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
static PmError alsa_in_open(PmInternal *midi, void *driverInfo)
|
||||
{
|
||||
void *client_port = descriptors[midi->device_id].descriptor;
|
||||
alsa_descriptor_type desc = (alsa_descriptor_type)
|
||||
pm_alloc(sizeof(alsa_descriptor_node));
|
||||
snd_seq_port_info_t *info;
|
||||
snd_seq_port_subscribe_t *sub;
|
||||
snd_seq_addr_t addr;
|
||||
int err;
|
||||
|
||||
if (!desc) return pmInsufficientMemory;
|
||||
|
||||
err = alsa_use_queue();
|
||||
if (err < 0) goto free_desc;
|
||||
|
||||
snd_seq_port_info_alloca(&info);
|
||||
snd_seq_port_info_set_port(info, midi->device_id);
|
||||
snd_seq_port_info_set_capability(info, SND_SEQ_PORT_CAP_WRITE |
|
||||
SND_SEQ_PORT_CAP_READ);
|
||||
snd_seq_port_info_set_type(info, SND_SEQ_PORT_TYPE_MIDI_GENERIC |
|
||||
SND_SEQ_PORT_TYPE_APPLICATION);
|
||||
snd_seq_port_info_set_port_specified(info, 1);
|
||||
err = snd_seq_create_port(seq, info);
|
||||
if (err < 0) goto free_queue;
|
||||
|
||||
/* fill in fields of desc, which is passed to pm_write routines */
|
||||
midi->descriptor = desc;
|
||||
desc->client = GET_DESCRIPTOR_CLIENT(client_port);
|
||||
desc->port = GET_DESCRIPTOR_PORT(client_port);
|
||||
desc->this_port = midi->device_id;
|
||||
desc->in_sysex = 0;
|
||||
|
||||
desc->error = 0;
|
||||
|
||||
VERBOSE printf("snd_seq_connect_from: %d %d %d\n",
|
||||
desc->this_port, desc->client, desc->port);
|
||||
snd_seq_port_subscribe_alloca(&sub);
|
||||
addr.client = snd_seq_client_id(seq);
|
||||
addr.port = desc->this_port;
|
||||
snd_seq_port_subscribe_set_dest(sub, &addr);
|
||||
addr.client = desc->client;
|
||||
addr.port = desc->port;
|
||||
snd_seq_port_subscribe_set_sender(sub, &addr);
|
||||
snd_seq_port_subscribe_set_time_update(sub, 1);
|
||||
/* this doesn't seem to work: messages come in with real timestamps */
|
||||
snd_seq_port_subscribe_set_time_real(sub, 0);
|
||||
err = snd_seq_subscribe_port(seq, sub);
|
||||
/* err =
|
||||
snd_seq_connect_from(seq, desc->this_port, desc->client, desc->port); */
|
||||
if (err < 0) goto free_this_port; /* clean up and return on error */
|
||||
return pmNoError;
|
||||
|
||||
free_this_port:
|
||||
snd_seq_delete_port(seq, desc->this_port);
|
||||
free_queue:
|
||||
alsa_unuse_queue();
|
||||
free_desc:
|
||||
pm_free(desc);
|
||||
pm_hosterror = err;
|
||||
if (err < 0) {
|
||||
get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, err);
|
||||
}
|
||||
return pmHostError;
|
||||
}
|
||||
|
||||
static PmError alsa_in_close(PmInternal *midi)
|
||||
{
|
||||
alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
|
||||
if (!desc) return pmBadPtr;
|
||||
if (pm_hosterror = snd_seq_disconnect_from(seq, desc->this_port,
|
||||
desc->client, desc->port)) {
|
||||
snd_seq_delete_port(seq, desc->this_port); /* try to close port */
|
||||
} else {
|
||||
pm_hosterror = snd_seq_delete_port(seq, desc->this_port);
|
||||
}
|
||||
alsa_unuse_queue();
|
||||
pm_free(desc);
|
||||
if (pm_hosterror) {
|
||||
get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN,
|
||||
pm_hosterror);
|
||||
return pmHostError;
|
||||
}
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
static PmError alsa_abort(PmInternal *midi)
|
||||
{
|
||||
/* NOTE: ALSA documentation is vague. This is supposed to
|
||||
* remove any pending output messages. If you can test and
|
||||
* confirm this code is correct, please update this comment. -RBD
|
||||
*/
|
||||
/* Unfortunately, I can't even compile it -- my ALSA version
|
||||
* does not implement snd_seq_remove_events_t, so this does
|
||||
* not compile. I'll try again, but it looks like I'll need to
|
||||
* upgrade my entire Linux OS -RBD
|
||||
*/
|
||||
/*
|
||||
alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
|
||||
snd_seq_remove_events_t info;
|
||||
snd_seq_addr_t addr;
|
||||
addr.client = desc->client;
|
||||
addr.port = desc->port;
|
||||
snd_seq_remove_events_set_dest(&info, &addr);
|
||||
snd_seq_remove_events_set_condition(&info, SND_SEQ_REMOVE_DEST);
|
||||
pm_hosterror = snd_seq_remove_events(seq, &info);
|
||||
if (pm_hosterror) {
|
||||
get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN,
|
||||
pm_hosterror);
|
||||
return pmHostError;
|
||||
}
|
||||
*/
|
||||
printf("WARNING: alsa_abort not implemented\n");
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
#ifdef GARBAGE
|
||||
This is old code here temporarily for reference
|
||||
static PmError alsa_write(PmInternal *midi, PmEvent *buffer, long length)
|
||||
{
|
||||
alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
|
||||
int i, bytes;
|
||||
unsigned char byte;
|
||||
long msg;
|
||||
|
||||
desc->error = 0;
|
||||
for (; length > 0; length--, buffer++) {
|
||||
VERBOSE printf("message 0x%x\n", buffer->message);
|
||||
if (Pm_MessageStatus(buffer->message) == MIDI_SYSEX)
|
||||
desc->in_sysex = TRUE;
|
||||
if (desc->in_sysex) {
|
||||
msg = buffer->message;
|
||||
for (i = 0; i < 4; i++) {
|
||||
byte = msg; /* extract next byte to send */
|
||||
alsa_write_byte(midi, byte, buffer->timestamp);
|
||||
if (byte == MIDI_EOX) {
|
||||
desc->in_sysex = FALSE;
|
||||
break;
|
||||
}
|
||||
if (desc->error < 0) break;
|
||||
msg >>= 8; /* shift next byte into position */
|
||||
}
|
||||
} else {
|
||||
bytes = midi_message_length(buffer->message);
|
||||
msg = buffer->message;
|
||||
for (i = 0; i < bytes; i++) {
|
||||
byte = msg; /* extract next byte to send */
|
||||
VERBOSE printf("sending 0x%x\n", byte);
|
||||
alsa_write_byte(midi, byte, buffer->timestamp);
|
||||
if (desc->error < 0) break;
|
||||
msg >>= 8; /* shift next byte into position */
|
||||
}
|
||||
}
|
||||
}
|
||||
if (desc->error < 0) return pmHostError;
|
||||
|
||||
VERBOSE printf("snd_seq_drain_output: 0x%x\n", seq);
|
||||
desc->error = snd_seq_drain_output(seq);
|
||||
if (desc->error < 0) return pmHostError;
|
||||
|
||||
desc->error = pmNoError;
|
||||
return pmNoError;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static PmError alsa_write_flush(PmInternal *midi, PmTimestamp timestamp)
|
||||
{
|
||||
alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
|
||||
VERBOSE printf("snd_seq_drain_output: 0x%x\n", (unsigned int) seq);
|
||||
desc->error = snd_seq_drain_output(seq);
|
||||
if (desc->error < 0) return pmHostError;
|
||||
|
||||
desc->error = pmNoError;
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
static PmError alsa_write_short(PmInternal *midi, PmEvent *event)
|
||||
{
|
||||
int bytes = midi_message_length(event->message);
|
||||
long msg = event->message;
|
||||
int i;
|
||||
alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
|
||||
for (i = 0; i < bytes; i++) {
|
||||
unsigned char byte = msg;
|
||||
VERBOSE printf("sending 0x%x\n", byte);
|
||||
alsa_write_byte(midi, byte, event->timestamp);
|
||||
if (desc->error < 0) break;
|
||||
msg >>= 8; /* shift next byte into position */
|
||||
}
|
||||
if (desc->error < 0) return pmHostError;
|
||||
desc->error = pmNoError;
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
/* alsa_sysex -- implements begin_sysex and end_sysex */
|
||||
PmError alsa_sysex(PmInternal *midi, PmTimestamp timestamp) {
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
static PmTimestamp alsa_synchronize(PmInternal *midi)
|
||||
{
|
||||
return 0; /* linux implementation does not use this synchronize function */
|
||||
/* Apparently, Alsa data is relative to the time you send it, and there
|
||||
is no reference. If this is true, this is a serious shortcoming of
|
||||
Alsa. If not true, then PortMidi has a serious shortcoming -- it
|
||||
should be scheduling relative to Alsa's time reference. */
|
||||
}
|
||||
|
||||
|
||||
static void handle_event(snd_seq_event_t *ev)
|
||||
{
|
||||
int device_id = ev->dest.port;
|
||||
PmInternal *midi = descriptors[device_id].internalDescriptor;
|
||||
PmEvent pm_ev;
|
||||
PmTimeProcPtr time_proc = midi->time_proc;
|
||||
PmTimestamp timestamp;
|
||||
|
||||
/* time stamp should be in ticks, using our queue where 1 tick = 1ms */
|
||||
assert((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_TICK);
|
||||
|
||||
/* if no time_proc, just return "native" ticks (ms) */
|
||||
if (time_proc == NULL) {
|
||||
timestamp = ev->time.tick;
|
||||
} else { /* translate time to time_proc basis */
|
||||
snd_seq_queue_status_t *queue_status;
|
||||
snd_seq_queue_status_alloca(&queue_status);
|
||||
snd_seq_get_queue_status(seq, queue, queue_status);
|
||||
/* return (now - alsa_now) + alsa_timestamp */
|
||||
timestamp = (*time_proc)(midi->time_info) + ev->time.tick -
|
||||
snd_seq_queue_status_get_tick_time(queue_status);
|
||||
}
|
||||
pm_ev.timestamp = timestamp;
|
||||
switch (ev->type) {
|
||||
case SND_SEQ_EVENT_NOTEON:
|
||||
pm_ev.message = Pm_Message(0x90 | ev->data.note.channel,
|
||||
ev->data.note.note & 0x7f,
|
||||
ev->data.note.velocity & 0x7f);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_NOTEOFF:
|
||||
pm_ev.message = Pm_Message(0x80 | ev->data.note.channel,
|
||||
ev->data.note.note & 0x7f,
|
||||
ev->data.note.velocity & 0x7f);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_KEYPRESS:
|
||||
pm_ev.message = Pm_Message(0xa0 | ev->data.note.channel,
|
||||
ev->data.note.note & 0x7f,
|
||||
ev->data.note.velocity & 0x7f);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_CONTROLLER:
|
||||
pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel,
|
||||
ev->data.control.param & 0x7f,
|
||||
ev->data.control.value & 0x7f);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_PGMCHANGE:
|
||||
pm_ev.message = Pm_Message(0xc0 | ev->data.note.channel,
|
||||
ev->data.control.value & 0x7f, 0);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_CHANPRESS:
|
||||
pm_ev.message = Pm_Message(0xd0 | ev->data.note.channel,
|
||||
ev->data.control.value & 0x7f, 0);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_PITCHBEND:
|
||||
pm_ev.message = Pm_Message(0xe0 | ev->data.note.channel,
|
||||
(ev->data.control.value + 0x2000) & 0x7f,
|
||||
((ev->data.control.value + 0x2000) >> 7) & 0x7f);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_CONTROL14:
|
||||
if (ev->data.control.param < 0x20) {
|
||||
pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel,
|
||||
ev->data.control.param,
|
||||
(ev->data.control.value >> 7) & 0x7f);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel,
|
||||
ev->data.control.param + 0x20,
|
||||
ev->data.control.value & 0x7f);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
} else {
|
||||
pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel,
|
||||
ev->data.control.param & 0x7f,
|
||||
ev->data.control.value & 0x7f);
|
||||
|
||||
pm_read_short(midi, &pm_ev);
|
||||
}
|
||||
break;
|
||||
case SND_SEQ_EVENT_SONGPOS:
|
||||
pm_ev.message = Pm_Message(0xf2,
|
||||
ev->data.control.value & 0x7f,
|
||||
(ev->data.control.value >> 7) & 0x7f);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_SONGSEL:
|
||||
pm_ev.message = Pm_Message(0xf3,
|
||||
ev->data.control.value & 0x7f, 0);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_QFRAME:
|
||||
pm_ev.message = Pm_Message(0xf1,
|
||||
ev->data.control.value & 0x7f, 0);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_START:
|
||||
pm_ev.message = Pm_Message(0xfa, 0, 0);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_CONTINUE:
|
||||
pm_ev.message = Pm_Message(0xfb, 0, 0);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_STOP:
|
||||
pm_ev.message = Pm_Message(0xfc, 0, 0);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_CLOCK:
|
||||
pm_ev.message = Pm_Message(0xf8, 0, 0);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_TUNE_REQUEST:
|
||||
pm_ev.message = Pm_Message(0xf6, 0, 0);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_RESET:
|
||||
pm_ev.message = Pm_Message(0xff, 0, 0);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_SENSING:
|
||||
pm_ev.message = Pm_Message(0xfe, 0, 0);
|
||||
pm_read_short(midi, &pm_ev);
|
||||
break;
|
||||
case SND_SEQ_EVENT_SYSEX: {
|
||||
const BYTE *ptr = (const BYTE *) ev->data.ext.ptr;
|
||||
/* assume there is one sysex byte to process */
|
||||
pm_read_bytes(midi, ptr, ev->data.ext.len, timestamp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static PmError alsa_poll(PmInternal *midi)
|
||||
{
|
||||
snd_seq_event_t *ev;
|
||||
/* expensive check for input data, gets data from device: */
|
||||
while (snd_seq_event_input_pending(seq, TRUE) > 0) {
|
||||
/* cheap check on local input buffer */
|
||||
while (snd_seq_event_input_pending(seq, FALSE) > 0) {
|
||||
/* check for and ignore errors, e.g. input overflow */
|
||||
/* note: if there's overflow, this should be reported
|
||||
* all the way through to client. Since input from all
|
||||
* devices is merged, we need to find all input devices
|
||||
* and set all to the overflow state.
|
||||
* NOTE: this assumes every input is ALSA based.
|
||||
*/
|
||||
int rslt = snd_seq_event_input(seq, &ev);
|
||||
if (rslt >= 0) {
|
||||
handle_event(ev);
|
||||
} else if (rslt == -ENOSPC) {
|
||||
int i;
|
||||
for (i = 0; i < pm_descriptor_index; i++) {
|
||||
if (descriptors[i].pub.input) {
|
||||
PmInternal *midi = (PmInternal *)
|
||||
descriptors[i].internalDescriptor;
|
||||
/* careful, device may not be open! */
|
||||
if (midi) Pm_SetOverflow(midi->queue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
static unsigned int alsa_has_host_error(PmInternal *midi)
|
||||
{
|
||||
alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
|
||||
return desc->error;
|
||||
}
|
||||
|
||||
|
||||
static void alsa_get_host_error(PmInternal *midi, char *msg, unsigned int len)
|
||||
{
|
||||
alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
|
||||
int err = (pm_hosterror || desc->error);
|
||||
get_alsa_error_text(msg, len, err);
|
||||
}
|
||||
|
||||
|
||||
pm_fns_node pm_linuxalsa_in_dictionary = {
|
||||
none_write_short,
|
||||
none_sysex,
|
||||
none_sysex,
|
||||
none_write_byte,
|
||||
none_write_short,
|
||||
none_write_flush,
|
||||
alsa_synchronize,
|
||||
alsa_in_open,
|
||||
alsa_abort,
|
||||
alsa_in_close,
|
||||
alsa_poll,
|
||||
alsa_has_host_error,
|
||||
alsa_get_host_error
|
||||
};
|
||||
|
||||
pm_fns_node pm_linuxalsa_out_dictionary = {
|
||||
alsa_write_short,
|
||||
alsa_sysex,
|
||||
alsa_sysex,
|
||||
alsa_write_byte,
|
||||
alsa_write_short, /* short realtime message */
|
||||
alsa_write_flush,
|
||||
alsa_synchronize,
|
||||
alsa_out_open,
|
||||
alsa_abort,
|
||||
alsa_out_close,
|
||||
none_poll,
|
||||
alsa_has_host_error,
|
||||
alsa_get_host_error
|
||||
};
|
||||
|
||||
|
||||
/* pm_strdup -- copy a string to the heap. Use this rather than strdup so
|
||||
* that we call pm_alloc, not malloc. This allows portmidi to avoid
|
||||
* malloc which might cause priority inversion. Probably ALSA is going
|
||||
* to call malloc anyway, so this extra work here may be pointless.
|
||||
*/
|
||||
char *pm_strdup(const char *s)
|
||||
{
|
||||
int len = strlen(s);
|
||||
char *dup = (char *) pm_alloc(len + 1);
|
||||
strcpy(dup, s);
|
||||
return dup;
|
||||
}
|
||||
|
||||
|
||||
PmError pm_linuxalsa_init( void )
|
||||
{
|
||||
int err;
|
||||
snd_seq_client_info_t *cinfo;
|
||||
snd_seq_port_info_t *pinfo;
|
||||
unsigned int caps;
|
||||
|
||||
/* Previously, the last parameter was SND_SEQ_NONBLOCK, but this
|
||||
* would cause messages to be dropped if the ALSA buffer fills up.
|
||||
* The correct behavior is for writes to block until there is
|
||||
* room to send all the data. The client should normally allocate
|
||||
* a large enough buffer to avoid blocking on output.
|
||||
* Now that blocking is enabled, the seq_event_input() will block
|
||||
* if there is no input data. This is not what we want, so must
|
||||
* call seq_event_input_pending() to avoid blocking.
|
||||
*/
|
||||
err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0);
|
||||
if (err < 0) return err;
|
||||
|
||||
snd_seq_client_info_alloca(&cinfo);
|
||||
snd_seq_port_info_alloca(&pinfo);
|
||||
|
||||
snd_seq_client_info_set_client(cinfo, -1);
|
||||
while (snd_seq_query_next_client(seq, cinfo) == 0) {
|
||||
snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
|
||||
snd_seq_port_info_set_port(pinfo, -1);
|
||||
while (snd_seq_query_next_port(seq, pinfo) == 0) {
|
||||
if (snd_seq_port_info_get_client(pinfo) == SND_SEQ_CLIENT_SYSTEM)
|
||||
continue; /* ignore Timer and Announce ports on client 0 */
|
||||
caps = snd_seq_port_info_get_capability(pinfo);
|
||||
if (!(caps & (SND_SEQ_PORT_CAP_SUBS_READ | SND_SEQ_PORT_CAP_SUBS_WRITE)))
|
||||
continue; /* ignore if you cannot read or write port */
|
||||
if (caps & SND_SEQ_PORT_CAP_SUBS_WRITE) {
|
||||
if (pm_default_output_device_id == -1)
|
||||
pm_default_output_device_id = pm_descriptor_index;
|
||||
pm_add_device("ALSA",
|
||||
pm_strdup(snd_seq_port_info_get_name(pinfo)),
|
||||
FALSE,
|
||||
MAKE_DESCRIPTOR(snd_seq_port_info_get_client(pinfo),
|
||||
snd_seq_port_info_get_port(pinfo)),
|
||||
&pm_linuxalsa_out_dictionary);
|
||||
}
|
||||
if (caps & SND_SEQ_PORT_CAP_SUBS_READ) {
|
||||
if (pm_default_input_device_id == -1)
|
||||
pm_default_input_device_id = pm_descriptor_index;
|
||||
pm_add_device("ALSA",
|
||||
pm_strdup(snd_seq_port_info_get_name(pinfo)),
|
||||
TRUE,
|
||||
MAKE_DESCRIPTOR(snd_seq_port_info_get_client(pinfo),
|
||||
snd_seq_port_info_get_port(pinfo)),
|
||||
&pm_linuxalsa_in_dictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
void pm_linuxalsa_term(void)
|
||||
{
|
||||
if (seq) {
|
||||
snd_seq_close(seq);
|
||||
pm_free(descriptors);
|
||||
descriptors = NULL;
|
||||
pm_descriptor_index = 0;
|
||||
pm_descriptor_max = 0;
|
||||
}
|
||||
}
|
6
pd-0.44-2/portmidi/pm_linux/pmlinuxalsa.h
Executable file
6
pd-0.44-2/portmidi/pm_linux/pmlinuxalsa.h
Executable file
|
@ -0,0 +1,6 @@
|
|||
/* pmlinuxalsa.h -- system-specific definitions */
|
||||
|
||||
PmError pm_linuxalsa_init(void);
|
||||
void pm_linuxalsa_term(void);
|
||||
|
||||
|
42
pd-0.44-2/portmidi/pm_mac/pmmac.c
Executable file
42
pd-0.44-2/portmidi/pm_mac/pmmac.c
Executable file
|
@ -0,0 +1,42 @@
|
|||
/* pmmac.c -- PortMidi os-dependent code */
|
||||
|
||||
/* This file only needs to implement:
|
||||
pm_init(), which calls various routines to register the
|
||||
available midi devices,
|
||||
Pm_GetDefaultInputDeviceID(), and
|
||||
Pm_GetDefaultOutputDeviceID().
|
||||
It is seperate from pmmacosxcm because we might want to register
|
||||
non-CoreMIDI devices.
|
||||
*/
|
||||
|
||||
#include "stdlib.h"
|
||||
#include "portmidi.h"
|
||||
#include "pmmacosxcm.h"
|
||||
|
||||
PmError pm_init()
|
||||
{
|
||||
return pm_macosxcm_init();
|
||||
}
|
||||
|
||||
void pm_term(void)
|
||||
{
|
||||
pm_macosxcm_term();
|
||||
}
|
||||
|
||||
PmDeviceID pm_default_input_device_id = -1;
|
||||
PmDeviceID pm_default_output_device_id = -1;
|
||||
|
||||
PmDeviceID Pm_GetDefaultInputDeviceID()
|
||||
{
|
||||
return pm_default_input_device_id;
|
||||
}
|
||||
|
||||
PmDeviceID Pm_GetDefaultOutputDeviceID() {
|
||||
return pm_default_output_device_id;
|
||||
}
|
||||
|
||||
void *pm_alloc(size_t s) { return malloc(s); }
|
||||
|
||||
void pm_free(void *ptr) { free(ptr); }
|
||||
|
||||
|
4
pd-0.44-2/portmidi/pm_mac/pmmac.h
Executable file
4
pd-0.44-2/portmidi/pm_mac/pmmac.h
Executable file
|
@ -0,0 +1,4 @@
|
|||
/* pmmac.h */
|
||||
|
||||
extern PmDeviceID pm_default_input_device_id;
|
||||
extern PmDeviceID pm_default_output_device_id;
|
934
pd-0.44-2/portmidi/pm_mac/pmmacosxcm.c
Executable file
934
pd-0.44-2/portmidi/pm_mac/pmmacosxcm.c
Executable file
|
@ -0,0 +1,934 @@
|
|||
/*
|
||||
* Platform interface to the MacOS X CoreMIDI framework
|
||||
*
|
||||
* Jon Parise <jparise at cmu.edu>
|
||||
* and subsequent work by Andrew Zeldis and Zico Kolter
|
||||
* and Roger B. Dannenberg
|
||||
*
|
||||
* $Id: pmmacosx.c,v 1.17 2002/01/27 02:40:40 jon Exp $
|
||||
*/
|
||||
|
||||
/* Notes:
|
||||
since the input and output streams are represented by MIDIEndpointRef
|
||||
values and almost no other state, we store the MIDIEndpointRef on
|
||||
descriptors[midi->device_id].descriptor. The only other state we need
|
||||
is for errors: we need to know if there is an error and if so, what is
|
||||
the error text. We use a structure with two kinds of
|
||||
host error: "error" and "callback_error". That way, asynchronous callbacks
|
||||
do not interfere with other error information.
|
||||
|
||||
OS X does not seem to have an error-code-to-text function, so we will
|
||||
just use text messages instead of error codes.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
//#define CM_DEBUG 1
|
||||
|
||||
#include "portmidi.h"
|
||||
#ifdef NEWBUFFER
|
||||
#include "pmutil.h"
|
||||
#endif
|
||||
#include "pminternal.h"
|
||||
#include "porttime.h"
|
||||
#include "pmmac.h"
|
||||
#include "pmmacosxcm.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <CoreMIDI/MIDIServices.h>
|
||||
#include <CoreAudio/HostTime.h>
|
||||
|
||||
#define PACKET_BUFFER_SIZE 1024
|
||||
|
||||
/* this is very strange: if I put in a reasonable
|
||||
number here, e.g. 128, which would allow sysex data
|
||||
to be sent 128 bytes at a time, then I lose sysex
|
||||
data in my loopback test. With a buffer size of 4,
|
||||
we put at most 4 bytes in a packet (but maybe many
|
||||
packets in a packetList), and everything works fine.
|
||||
*/
|
||||
#define SYSEX_BUFFER_SIZE 4
|
||||
|
||||
#define VERBOSE_ON 1
|
||||
#define VERBOSE if (VERBOSE_ON)
|
||||
|
||||
#define MIDI_SYSEX 0xf0
|
||||
#define MIDI_EOX 0xf7
|
||||
#define MIDI_STATUS_MASK 0x80
|
||||
|
||||
static MIDIClientRef client = NULL; /* Client handle to the MIDI server */
|
||||
static MIDIPortRef portIn = NULL; /* Input port handle */
|
||||
static MIDIPortRef portOut = NULL; /* Output port handle */
|
||||
|
||||
extern pm_fns_node pm_macosx_in_dictionary;
|
||||
extern pm_fns_node pm_macosx_out_dictionary;
|
||||
|
||||
typedef struct midi_macosxcm_struct {
|
||||
unsigned long sync_time; /* when did we last determine delta? */
|
||||
UInt64 delta; /* difference between stream time and real time in ns */
|
||||
UInt64 last_time; /* last output time */
|
||||
int first_message; /* tells midi_write to sychronize timestamps */
|
||||
int sysex_mode; /* middle of sending sysex */
|
||||
unsigned long sysex_word; /* accumulate data when receiving sysex */
|
||||
unsigned int sysex_byte_count; /* count how many received */
|
||||
char error[PM_HOST_ERROR_MSG_LEN];
|
||||
char callback_error[PM_HOST_ERROR_MSG_LEN];
|
||||
Byte packetBuffer[PACKET_BUFFER_SIZE];
|
||||
MIDIPacketList *packetList; /* a pointer to packetBuffer */
|
||||
MIDIPacket *packet;
|
||||
Byte sysex_buffer[SYSEX_BUFFER_SIZE]; /* temp storage for sysex data */
|
||||
MIDITimeStamp sysex_timestamp; /* timestamp to use with sysex data */
|
||||
/* allow for running status (is running status possible here? -rbd): -cpr */
|
||||
unsigned char last_command;
|
||||
long last_msg_length;
|
||||
} midi_macosxcm_node, *midi_macosxcm_type;
|
||||
|
||||
/* private function declarations */
|
||||
MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp);
|
||||
PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp);
|
||||
|
||||
char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint);
|
||||
|
||||
|
||||
static int
|
||||
midi_length(long msg)
|
||||
{
|
||||
int status, high, low;
|
||||
static int high_lengths[] = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, /* 0x00 through 0x70 */
|
||||
3, 3, 3, 3, 2, 2, 3, 1 /* 0x80 through 0xf0 */
|
||||
};
|
||||
static int low_lengths[] = {
|
||||
1, 1, 3, 2, 1, 1, 1, 1, /* 0xf0 through 0xf8 */
|
||||
1, 1, 1, 1, 1, 1, 1, 1 /* 0xf9 through 0xff */
|
||||
};
|
||||
|
||||
status = msg & 0xFF;
|
||||
high = status >> 4;
|
||||
low = status & 15;
|
||||
|
||||
return (high != 0xF0) ? high_lengths[high] : low_lengths[low];
|
||||
}
|
||||
|
||||
static PmTimestamp midi_synchronize(PmInternal *midi)
|
||||
{
|
||||
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
|
||||
UInt64 pm_stream_time_2 =
|
||||
AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
|
||||
PmTimestamp real_time;
|
||||
UInt64 pm_stream_time;
|
||||
/* if latency is zero and this is an output, there is no
|
||||
time reference and midi_synchronize should never be called */
|
||||
assert(midi->time_proc);
|
||||
assert(!(midi->write_flag && midi->latency == 0));
|
||||
do {
|
||||
/* read real_time between two reads of stream time */
|
||||
pm_stream_time = pm_stream_time_2;
|
||||
real_time = (*midi->time_proc)(midi->time_info);
|
||||
pm_stream_time_2 = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
|
||||
/* repeat if more than 0.5 ms has elapsed */
|
||||
} while (pm_stream_time_2 > pm_stream_time + 500000);
|
||||
m->delta = pm_stream_time - ((UInt64) real_time * (UInt64) 1000000);
|
||||
m->sync_time = real_time;
|
||||
return real_time;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
process_packet(MIDIPacket *packet, PmEvent *event,
|
||||
PmInternal *midi, midi_macosxcm_type m)
|
||||
{
|
||||
/* handle a packet of MIDI messages from CoreMIDI */
|
||||
/* there may be multiple short messages in one packet (!) */
|
||||
unsigned int remaining_length = packet->length;
|
||||
unsigned char *cur_packet_data = packet->data;
|
||||
while (remaining_length > 0) {
|
||||
if (cur_packet_data[0] == MIDI_SYSEX ||
|
||||
/* are we in the middle of a sysex message? */
|
||||
(m->last_command == 0 &&
|
||||
!(cur_packet_data[0] & MIDI_STATUS_MASK))) {
|
||||
m->last_command = 0; /* no running status */
|
||||
unsigned int amt = pm_read_bytes(midi, cur_packet_data,
|
||||
remaining_length,
|
||||
event->timestamp);
|
||||
remaining_length -= amt;
|
||||
cur_packet_data += amt;
|
||||
} else if (cur_packet_data[0] == MIDI_EOX) {
|
||||
/* this should never happen, because pm_read_bytes should
|
||||
* get and read all EOX bytes*/
|
||||
midi->sysex_in_progress = FALSE;
|
||||
m->last_command = 0;
|
||||
} else if (cur_packet_data[0] & MIDI_STATUS_MASK) {
|
||||
/* compute the length of the next (short) msg in packet */
|
||||
unsigned int cur_message_length = midi_length(cur_packet_data[0]);
|
||||
if (cur_message_length > remaining_length) {
|
||||
#ifdef DEBUG
|
||||
printf("PortMidi debug msg: not enough data");
|
||||
#endif
|
||||
/* since there's no more data, we're done */
|
||||
return;
|
||||
}
|
||||
m->last_msg_length = cur_message_length;
|
||||
m->last_command = cur_packet_data[0];
|
||||
switch (cur_message_length) {
|
||||
case 1:
|
||||
event->message = Pm_Message(cur_packet_data[0], 0, 0);
|
||||
break;
|
||||
case 2:
|
||||
event->message = Pm_Message(cur_packet_data[0],
|
||||
cur_packet_data[1], 0);
|
||||
break;
|
||||
case 3:
|
||||
event->message = Pm_Message(cur_packet_data[0],
|
||||
cur_packet_data[1],
|
||||
cur_packet_data[2]);
|
||||
break;
|
||||
default:
|
||||
/* PortMIDI internal error; should never happen */
|
||||
assert(cur_message_length == 1);
|
||||
return; /* give up on packet if continued after assert */
|
||||
}
|
||||
pm_read_short(midi, event);
|
||||
remaining_length -= m->last_msg_length;
|
||||
cur_packet_data += m->last_msg_length;
|
||||
} else if (m->last_msg_length > remaining_length + 1) {
|
||||
/* we have running status, but not enough data */
|
||||
#ifdef DEBUG
|
||||
printf("PortMidi debug msg: not enough data in CoreMIDI packet");
|
||||
#endif
|
||||
/* since there's no more data, we're done */
|
||||
return;
|
||||
} else { /* output message using running status */
|
||||
switch (m->last_msg_length) {
|
||||
case 1:
|
||||
event->message = Pm_Message(m->last_command, 0, 0);
|
||||
break;
|
||||
case 2:
|
||||
event->message = Pm_Message(m->last_command,
|
||||
cur_packet_data[0], 0);
|
||||
break;
|
||||
case 3:
|
||||
event->message = Pm_Message(m->last_command,
|
||||
cur_packet_data[0],
|
||||
cur_packet_data[1]);
|
||||
break;
|
||||
default:
|
||||
/* last_msg_length is invalid -- internal PortMIDI error */
|
||||
assert(m->last_msg_length == 1);
|
||||
}
|
||||
pm_read_short(midi, event);
|
||||
remaining_length -= (m->last_msg_length - 1);
|
||||
cur_packet_data += (m->last_msg_length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* called when MIDI packets are received */
|
||||
static void
|
||||
readProc(const MIDIPacketList *newPackets, void *refCon, void *connRefCon)
|
||||
{
|
||||
PmInternal *midi;
|
||||
midi_macosxcm_type m;
|
||||
PmEvent event;
|
||||
MIDIPacket *packet;
|
||||
unsigned int packetIndex;
|
||||
unsigned long now;
|
||||
unsigned int status;
|
||||
|
||||
#ifdef CM_DEBUG
|
||||
printf("readProc: numPackets %d: ", newPackets->numPackets);
|
||||
#endif
|
||||
|
||||
/* Retrieve the context for this connection */
|
||||
midi = (PmInternal *) connRefCon;
|
||||
m = (midi_macosxcm_type) midi->descriptor;
|
||||
assert(m);
|
||||
|
||||
/* synchronize time references every 100ms */
|
||||
now = (*midi->time_proc)(midi->time_info);
|
||||
if (m->first_message || m->sync_time + 100 /*ms*/ < now) {
|
||||
/* time to resync */
|
||||
now = midi_synchronize(midi);
|
||||
m->first_message = FALSE;
|
||||
}
|
||||
|
||||
packet = (MIDIPacket *) &newPackets->packet[0];
|
||||
/* printf("readproc packet status %x length %d\n", packet->data[0],
|
||||
packet->length); */
|
||||
for (packetIndex = 0; packetIndex < newPackets->numPackets; packetIndex++) {
|
||||
/* Set the timestamp and dispatch this message */
|
||||
event.timestamp =
|
||||
(AudioConvertHostTimeToNanos(packet->timeStamp) - m->delta) /
|
||||
(UInt64) 1000000;
|
||||
status = packet->data[0];
|
||||
/* process packet as sysex data if it begins with MIDI_SYSEX, or
|
||||
MIDI_EOX or non-status byte with no running status */
|
||||
#ifdef CM_DEBUG
|
||||
printf(" %d", packet->length);
|
||||
#endif
|
||||
if (status == MIDI_SYSEX || status == MIDI_EOX ||
|
||||
((!(status & MIDI_STATUS_MASK)) && !m->last_command)) {
|
||||
/* previously was: !(status & MIDI_STATUS_MASK)) {
|
||||
* but this could mistake running status for sysex data
|
||||
*/
|
||||
/* reset running status data -cpr */
|
||||
m->last_command = 0;
|
||||
m->last_msg_length = 0;
|
||||
/* printf("sysex packet length: %d\n", packet->length); */
|
||||
pm_read_bytes(midi, packet->data, packet->length, event.timestamp);
|
||||
} else {
|
||||
process_packet(packet, &event, midi, m);
|
||||
}
|
||||
packet = MIDIPacketNext(packet);
|
||||
}
|
||||
#ifdef CM_DEBUG
|
||||
printf("\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static PmError
|
||||
midi_in_open(PmInternal *midi, void *driverInfo)
|
||||
{
|
||||
MIDIEndpointRef endpoint;
|
||||
midi_macosxcm_type m;
|
||||
OSStatus macHostError;
|
||||
|
||||
/* insure that we have a time_proc for timing */
|
||||
if (midi->time_proc == NULL) {
|
||||
if (!Pt_Started())
|
||||
Pt_Start(1, 0, 0);
|
||||
/* time_get does not take a parameter, so coerce */
|
||||
midi->time_proc = (PmTimeProcPtr) Pt_Time;
|
||||
}
|
||||
|
||||
endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor;
|
||||
if (endpoint == NULL) {
|
||||
return pmInvalidDeviceId;
|
||||
}
|
||||
|
||||
m = (midi_macosxcm_type) pm_alloc(sizeof(midi_macosxcm_node)); /* create */
|
||||
midi->descriptor = m;
|
||||
if (!m) {
|
||||
return pmInsufficientMemory;
|
||||
}
|
||||
m->error[0] = 0;
|
||||
m->callback_error[0] = 0;
|
||||
m->sync_time = 0;
|
||||
m->delta = 0;
|
||||
m->last_time = 0;
|
||||
m->first_message = TRUE;
|
||||
m->sysex_mode = FALSE;
|
||||
m->sysex_word = 0;
|
||||
m->sysex_byte_count = 0;
|
||||
m->packetList = NULL;
|
||||
m->packet = NULL;
|
||||
m->last_command = 0;
|
||||
m->last_msg_length = 0;
|
||||
|
||||
macHostError = MIDIPortConnectSource(portIn, endpoint, midi);
|
||||
if (macHostError != noErr) {
|
||||
pm_hosterror = macHostError;
|
||||
sprintf(pm_hosterror_text,
|
||||
"Host error %ld: MIDIPortConnectSource() in midi_in_open()",
|
||||
macHostError);
|
||||
midi->descriptor = NULL;
|
||||
pm_free(m);
|
||||
return pmHostError;
|
||||
}
|
||||
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
static PmError
|
||||
midi_in_close(PmInternal *midi)
|
||||
{
|
||||
MIDIEndpointRef endpoint;
|
||||
OSStatus macHostError;
|
||||
PmError err = pmNoError;
|
||||
|
||||
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
|
||||
|
||||
if (!m) return pmBadPtr;
|
||||
|
||||
endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor;
|
||||
if (endpoint == NULL) {
|
||||
pm_hosterror = pmBadPtr;
|
||||
}
|
||||
|
||||
/* shut off the incoming messages before freeing data structures */
|
||||
macHostError = MIDIPortDisconnectSource(portIn, endpoint);
|
||||
if (macHostError != noErr) {
|
||||
pm_hosterror = macHostError;
|
||||
sprintf(pm_hosterror_text,
|
||||
"Host error %ld: MIDIPortDisconnectSource() in midi_in_close()",
|
||||
macHostError);
|
||||
err = pmHostError;
|
||||
}
|
||||
|
||||
midi->descriptor = NULL;
|
||||
pm_free(midi->descriptor);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static PmError
|
||||
midi_out_open(PmInternal *midi, void *driverInfo)
|
||||
{
|
||||
midi_macosxcm_type m;
|
||||
|
||||
m = (midi_macosxcm_type) pm_alloc(sizeof(midi_macosxcm_node)); /* create */
|
||||
midi->descriptor = m;
|
||||
if (!m) {
|
||||
return pmInsufficientMemory;
|
||||
}
|
||||
m->error[0] = 0;
|
||||
m->callback_error[0] = 0;
|
||||
m->sync_time = 0;
|
||||
m->delta = 0;
|
||||
m->last_time = 0;
|
||||
m->first_message = TRUE;
|
||||
m->sysex_mode = FALSE;
|
||||
m->sysex_word = 0;
|
||||
m->sysex_byte_count = 0;
|
||||
m->packetList = (MIDIPacketList *) m->packetBuffer;
|
||||
m->packet = NULL;
|
||||
m->last_command = 0;
|
||||
m->last_msg_length = 0;
|
||||
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
static PmError
|
||||
midi_out_close(PmInternal *midi)
|
||||
{
|
||||
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
|
||||
if (!m) return pmBadPtr;
|
||||
|
||||
midi->descriptor = NULL;
|
||||
pm_free(midi->descriptor);
|
||||
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
static PmError
|
||||
midi_abort(PmInternal *midi)
|
||||
{
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
static PmError
|
||||
midi_write_flush(PmInternal *midi, PmTimestamp timestamp)
|
||||
{
|
||||
OSStatus macHostError;
|
||||
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
|
||||
MIDIEndpointRef endpoint =
|
||||
(MIDIEndpointRef) descriptors[midi->device_id].descriptor;
|
||||
assert(m);
|
||||
assert(endpoint);
|
||||
if (m->packet != NULL) {
|
||||
/* out of space, send the buffer and start refilling it */
|
||||
macHostError = MIDISend(portOut, endpoint, m->packetList);
|
||||
m->packet = NULL; /* indicate no data in packetList now */
|
||||
if (macHostError != noErr) goto send_packet_error;
|
||||
}
|
||||
return pmNoError;
|
||||
|
||||
send_packet_error:
|
||||
pm_hosterror = macHostError;
|
||||
sprintf(pm_hosterror_text,
|
||||
"Host error %ld: MIDISend() in midi_write()",
|
||||
macHostError);
|
||||
return pmHostError;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static PmError
|
||||
send_packet(PmInternal *midi, Byte *message, unsigned int messageLength,
|
||||
MIDITimeStamp timestamp)
|
||||
{
|
||||
PmError err;
|
||||
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
|
||||
assert(m);
|
||||
|
||||
/* printf("add %d to packet %lx len %d\n", message[0], m->packet, messageLength); */
|
||||
m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer),
|
||||
m->packet, timestamp, messageLength,
|
||||
message);
|
||||
if (m->packet == NULL) {
|
||||
/* out of space, send the buffer and start refilling it */
|
||||
/* make midi->packet non-null to fool midi_write_flush into sending */
|
||||
m->packet = (MIDIPacket *) 4;
|
||||
if ((err = midi_write_flush(midi, timestamp)) != pmNoError) return err;
|
||||
m->packet = MIDIPacketListInit(m->packetList);
|
||||
assert(m->packet); /* if this fails, it's a programming error */
|
||||
m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer),
|
||||
m->packet, timestamp, messageLength,
|
||||
message);
|
||||
assert(m->packet); /* can't run out of space on first message */
|
||||
}
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
static PmError
|
||||
midi_write_short(PmInternal *midi, PmEvent *event)
|
||||
{
|
||||
long when = event->timestamp;
|
||||
long what = event->message;
|
||||
MIDITimeStamp timestamp;
|
||||
UInt64 when_ns;
|
||||
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
|
||||
Byte message[4];
|
||||
unsigned int messageLength;
|
||||
|
||||
if (m->packet == NULL) {
|
||||
m->packet = MIDIPacketListInit(m->packetList);
|
||||
/* this can never fail, right? failure would indicate something
|
||||
unrecoverable */
|
||||
assert(m->packet);
|
||||
}
|
||||
|
||||
/* compute timestamp */
|
||||
if (when == 0) when = midi->now;
|
||||
/* if latency == 0, midi->now is not valid. We will just set it to zero */
|
||||
if (midi->latency == 0) when = 0;
|
||||
when_ns = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + m->delta;
|
||||
/* make sure we don't go backward in time */
|
||||
if (when_ns < m->last_time) when_ns = m->last_time;
|
||||
m->last_time = when_ns;
|
||||
timestamp = (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns);
|
||||
|
||||
message[0] = Pm_MessageStatus(what);
|
||||
message[1] = Pm_MessageData1(what);
|
||||
message[2] = Pm_MessageData2(what);
|
||||
messageLength = midi_length(what);
|
||||
|
||||
/* Add this message to the packet list */
|
||||
return send_packet(midi, message, messageLength, timestamp);
|
||||
}
|
||||
|
||||
|
||||
static PmError
|
||||
midi_begin_sysex(PmInternal *midi, PmTimestamp when)
|
||||
{
|
||||
UInt64 when_ns;
|
||||
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
|
||||
assert(m);
|
||||
m->sysex_byte_count = 0;
|
||||
|
||||
/* compute timestamp */
|
||||
if (when == 0) when = midi->now;
|
||||
/* if latency == 0, midi->now is not valid. We will just set it to zero */
|
||||
if (midi->latency == 0) when = 0;
|
||||
when_ns = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + m->delta;
|
||||
m->sysex_timestamp = (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns);
|
||||
|
||||
if (m->packet == NULL) {
|
||||
m->packet = MIDIPacketListInit(m->packetList);
|
||||
/* this can never fail, right? failure would indicate something
|
||||
unrecoverable */
|
||||
assert(m->packet);
|
||||
}
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
static PmError
|
||||
midi_end_sysex(PmInternal *midi, PmTimestamp when)
|
||||
{
|
||||
PmError err;
|
||||
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
|
||||
assert(m);
|
||||
|
||||
/* make sure we don't go backward in time */
|
||||
if (m->sysex_timestamp < m->last_time) m->sysex_timestamp = m->last_time;
|
||||
|
||||
/* if flush has been called in the meantime, packet list is NULL */
|
||||
if (m->packet == NULL) {
|
||||
m->packet = MIDIPacketListInit(m->packetList);
|
||||
assert(m->packet);
|
||||
}
|
||||
|
||||
/* now send what's in the buffer */
|
||||
err = send_packet(midi, m->sysex_buffer, m->sysex_byte_count,
|
||||
m->sysex_timestamp);
|
||||
m->sysex_byte_count = 0;
|
||||
if (err != pmNoError) {
|
||||
m->packet = NULL; /* flush everything in the packet list */
|
||||
return err;
|
||||
}
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
static PmError
|
||||
midi_write_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp)
|
||||
{
|
||||
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
|
||||
assert(m);
|
||||
if (m->sysex_byte_count >= SYSEX_BUFFER_SIZE) {
|
||||
PmError err = midi_end_sysex(midi, timestamp);
|
||||
if (err != pmNoError) return err;
|
||||
}
|
||||
m->sysex_buffer[m->sysex_byte_count++] = byte;
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
static PmError
|
||||
midi_write_realtime(PmInternal *midi, PmEvent *event)
|
||||
{
|
||||
/* to send a realtime message during a sysex message, first
|
||||
flush all pending sysex bytes into packet list */
|
||||
PmError err = midi_end_sysex(midi, 0);
|
||||
if (err != pmNoError) return err;
|
||||
/* then we can just do a normal midi_write_short */
|
||||
return midi_write_short(midi, event);
|
||||
}
|
||||
|
||||
static unsigned int midi_has_host_error(PmInternal *midi)
|
||||
{
|
||||
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
|
||||
return (m->callback_error[0] != 0) || (m->error[0] != 0);
|
||||
}
|
||||
|
||||
|
||||
static void midi_get_host_error(PmInternal *midi, char *msg, unsigned int len)
|
||||
{
|
||||
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
|
||||
msg[0] = 0; /* initialize to empty string */
|
||||
if (m) { /* make sure there is an open device to examine */
|
||||
if (m->error[0]) {
|
||||
strncpy(msg, m->error, len);
|
||||
m->error[0] = 0; /* clear the error */
|
||||
} else if (m->callback_error[0]) {
|
||||
strncpy(msg, m->callback_error, len);
|
||||
m->callback_error[0] = 0; /* clear the error */
|
||||
}
|
||||
msg[len - 1] = 0; /* make sure string is terminated */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp)
|
||||
{
|
||||
UInt64 nanos;
|
||||
if (timestamp <= 0) {
|
||||
return (MIDITimeStamp)0;
|
||||
} else {
|
||||
nanos = (UInt64)timestamp * (UInt64)1000000;
|
||||
return (MIDITimeStamp)AudioConvertNanosToHostTime(nanos);
|
||||
}
|
||||
}
|
||||
|
||||
PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp)
|
||||
{
|
||||
UInt64 nanos;
|
||||
nanos = AudioConvertHostTimeToNanos(timestamp);
|
||||
return (PmTimestamp)(nanos / (UInt64)1000000);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Code taken from http://developer.apple.com/qa/qa2004/qa1374.html
|
||||
//////////////////////////////////////
|
||||
// Obtain the name of an endpoint without regard for whether it has connections.
|
||||
// The result should be released by the caller.
|
||||
CFStringRef EndpointName(MIDIEndpointRef endpoint, bool isExternal)
|
||||
{
|
||||
CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
|
||||
CFStringRef str;
|
||||
|
||||
// begin with the endpoint's name
|
||||
str = NULL;
|
||||
MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &str);
|
||||
if (str != NULL) {
|
||||
CFStringAppend(result, str);
|
||||
CFRelease(str);
|
||||
}
|
||||
|
||||
MIDIEntityRef entity = NULL;
|
||||
MIDIEndpointGetEntity(endpoint, &entity);
|
||||
if (entity == NULL)
|
||||
// probably virtual
|
||||
return result;
|
||||
|
||||
if (CFStringGetLength(result) == 0) {
|
||||
// endpoint name has zero length -- try the entity
|
||||
str = NULL;
|
||||
MIDIObjectGetStringProperty(entity, kMIDIPropertyName, &str);
|
||||
if (str != NULL) {
|
||||
CFStringAppend(result, str);
|
||||
CFRelease(str);
|
||||
}
|
||||
}
|
||||
// now consider the device's name
|
||||
MIDIDeviceRef device = NULL;
|
||||
MIDIEntityGetDevice(entity, &device);
|
||||
if (device == NULL)
|
||||
return result;
|
||||
|
||||
str = NULL;
|
||||
MIDIObjectGetStringProperty(device, kMIDIPropertyName, &str);
|
||||
if (str != NULL) {
|
||||
// if an external device has only one entity, throw away
|
||||
// the endpoint name and just use the device name
|
||||
if (isExternal && MIDIDeviceGetNumberOfEntities(device) < 2) {
|
||||
CFRelease(result);
|
||||
return str;
|
||||
} else {
|
||||
// does the entity name already start with the device name?
|
||||
// (some drivers do this though they shouldn't)
|
||||
// if so, do not prepend
|
||||
if (CFStringCompareWithOptions( result, /* endpoint name */
|
||||
str /* device name */,
|
||||
CFRangeMake(0, CFStringGetLength(str)), 0) != kCFCompareEqualTo) {
|
||||
// prepend the device name to the entity name
|
||||
if (CFStringGetLength(result) > 0)
|
||||
CFStringInsert(result, 0, CFSTR(" "));
|
||||
CFStringInsert(result, 0, str);
|
||||
}
|
||||
CFRelease(str);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Obtain the name of an endpoint, following connections.
|
||||
// The result should be released by the caller.
|
||||
static CFStringRef ConnectedEndpointName(MIDIEndpointRef endpoint)
|
||||
{
|
||||
CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
|
||||
CFStringRef str;
|
||||
OSStatus err;
|
||||
int i;
|
||||
|
||||
// Does the endpoint have connections?
|
||||
CFDataRef connections = NULL;
|
||||
int nConnected = 0;
|
||||
bool anyStrings = false;
|
||||
err = MIDIObjectGetDataProperty(endpoint, kMIDIPropertyConnectionUniqueID, &connections);
|
||||
if (connections != NULL) {
|
||||
// It has connections, follow them
|
||||
// Concatenate the names of all connected devices
|
||||
nConnected = CFDataGetLength(connections) / sizeof(MIDIUniqueID);
|
||||
if (nConnected) {
|
||||
const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections));
|
||||
for (i = 0; i < nConnected; ++i, ++pid) {
|
||||
MIDIUniqueID id = EndianS32_BtoN(*pid);
|
||||
MIDIObjectRef connObject;
|
||||
MIDIObjectType connObjectType;
|
||||
err = MIDIObjectFindByUniqueID(id, &connObject, &connObjectType);
|
||||
if (err == noErr) {
|
||||
if (connObjectType == kMIDIObjectType_ExternalSource ||
|
||||
connObjectType == kMIDIObjectType_ExternalDestination) {
|
||||
// Connected to an external device's endpoint (10.3 and later).
|
||||
str = EndpointName((MIDIEndpointRef)(connObject), true);
|
||||
} else {
|
||||
// Connected to an external device (10.2) (or something else, catch-all)
|
||||
str = NULL;
|
||||
MIDIObjectGetStringProperty(connObject, kMIDIPropertyName, &str);
|
||||
}
|
||||
if (str != NULL) {
|
||||
if (anyStrings)
|
||||
CFStringAppend(result, CFSTR(", "));
|
||||
else anyStrings = true;
|
||||
CFStringAppend(result, str);
|
||||
CFRelease(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CFRelease(connections);
|
||||
}
|
||||
if (anyStrings)
|
||||
return result;
|
||||
|
||||
// Here, either the endpoint had no connections, or we failed to obtain names for any of them.
|
||||
return EndpointName(endpoint, false);
|
||||
}
|
||||
|
||||
|
||||
char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint)
|
||||
{
|
||||
#ifdef OLDCODE
|
||||
MIDIEntityRef entity;
|
||||
MIDIDeviceRef device;
|
||||
|
||||
CFStringRef endpointName = NULL;
|
||||
CFStringRef deviceName = NULL;
|
||||
#endif
|
||||
CFStringRef fullName = NULL;
|
||||
CFStringEncoding defaultEncoding;
|
||||
char* newName;
|
||||
|
||||
/* get the default string encoding */
|
||||
defaultEncoding = CFStringGetSystemEncoding();
|
||||
|
||||
fullName = ConnectedEndpointName(endpoint);
|
||||
|
||||
#ifdef OLDCODE
|
||||
/* get the entity and device info */
|
||||
MIDIEndpointGetEntity(endpoint, &entity);
|
||||
MIDIEntityGetDevice(entity, &device);
|
||||
|
||||
/* create the nicely formated name */
|
||||
MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &endpointName);
|
||||
MIDIObjectGetStringProperty(device, kMIDIPropertyName, &deviceName);
|
||||
if (deviceName != NULL) {
|
||||
fullName = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@: %@"),
|
||||
deviceName, endpointName);
|
||||
} else {
|
||||
fullName = endpointName;
|
||||
}
|
||||
#endif
|
||||
/* copy the string into our buffer */
|
||||
newName = (char *) malloc(CFStringGetLength(fullName) + 1);
|
||||
CFStringGetCString(fullName, newName, CFStringGetLength(fullName) + 1,
|
||||
defaultEncoding);
|
||||
|
||||
/* clean up */
|
||||
#ifdef OLDCODE
|
||||
if (endpointName) CFRelease(endpointName);
|
||||
if (deviceName) CFRelease(deviceName);
|
||||
#endif
|
||||
if (fullName) CFRelease(fullName);
|
||||
|
||||
return newName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
pm_fns_node pm_macosx_in_dictionary = {
|
||||
none_write_short,
|
||||
none_sysex,
|
||||
none_sysex,
|
||||
none_write_byte,
|
||||
none_write_short,
|
||||
none_write_flush,
|
||||
none_synchronize,
|
||||
midi_in_open,
|
||||
midi_abort,
|
||||
midi_in_close,
|
||||
success_poll,
|
||||
midi_has_host_error,
|
||||
midi_get_host_error,
|
||||
};
|
||||
|
||||
pm_fns_node pm_macosx_out_dictionary = {
|
||||
midi_write_short,
|
||||
midi_begin_sysex,
|
||||
midi_end_sysex,
|
||||
midi_write_byte,
|
||||
midi_write_realtime,
|
||||
midi_write_flush,
|
||||
midi_synchronize,
|
||||
midi_out_open,
|
||||
midi_abort,
|
||||
midi_out_close,
|
||||
success_poll,
|
||||
midi_has_host_error,
|
||||
midi_get_host_error,
|
||||
};
|
||||
|
||||
|
||||
PmError pm_macosxcm_init(void)
|
||||
{
|
||||
ItemCount numInputs, numOutputs, numDevices;
|
||||
MIDIEndpointRef endpoint;
|
||||
int i;
|
||||
OSStatus macHostError;
|
||||
char *error_text;
|
||||
|
||||
/* Determine the number of MIDI devices on the system */
|
||||
numDevices = MIDIGetNumberOfDevices();
|
||||
numInputs = MIDIGetNumberOfSources();
|
||||
numOutputs = MIDIGetNumberOfDestinations();
|
||||
|
||||
/* Return prematurely if no devices exist on the system
|
||||
Note that this is not an error. There may be no devices.
|
||||
Pm_CountDevices() will return zero, which is correct and
|
||||
useful information
|
||||
*/
|
||||
if (numDevices <= 0) {
|
||||
return pmNoError;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize the client handle */
|
||||
macHostError = MIDIClientCreate(CFSTR("PortMidi"), NULL, NULL, &client);
|
||||
if (macHostError != noErr) {
|
||||
error_text = "MIDIClientCreate() in pm_macosxcm_init()";
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/* Create the input port */
|
||||
macHostError = MIDIInputPortCreate(client, CFSTR("Input port"), readProc,
|
||||
NULL, &portIn);
|
||||
if (macHostError != noErr) {
|
||||
error_text = "MIDIInputPortCreate() in pm_macosxcm_init()";
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/* Create the output port */
|
||||
macHostError = MIDIOutputPortCreate(client, CFSTR("Output port"), &portOut);
|
||||
if (macHostError != noErr) {
|
||||
error_text = "MIDIOutputPortCreate() in pm_macosxcm_init()";
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/* Iterate over the MIDI input devices */
|
||||
for (i = 0; i < numInputs; i++) {
|
||||
endpoint = MIDIGetSource(i);
|
||||
if (endpoint == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* set the first input we see to the default */
|
||||
if (pm_default_input_device_id == -1)
|
||||
pm_default_input_device_id = pm_descriptor_index;
|
||||
|
||||
/* Register this device with PortMidi */
|
||||
pm_add_device("CoreMIDI", cm_get_full_endpoint_name(endpoint),
|
||||
TRUE, (void*)endpoint, &pm_macosx_in_dictionary);
|
||||
}
|
||||
|
||||
/* Iterate over the MIDI output devices */
|
||||
for (i = 0; i < numOutputs; i++) {
|
||||
endpoint = MIDIGetDestination(i);
|
||||
if (endpoint == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* set the first output we see to the default */
|
||||
if (pm_default_output_device_id == -1)
|
||||
pm_default_output_device_id = pm_descriptor_index;
|
||||
|
||||
/* Register this device with PortMidi */
|
||||
pm_add_device("CoreMIDI", cm_get_full_endpoint_name(endpoint),
|
||||
FALSE, (void*)endpoint, &pm_macosx_out_dictionary);
|
||||
}
|
||||
return pmNoError;
|
||||
|
||||
error_return:
|
||||
pm_hosterror = macHostError;
|
||||
sprintf(pm_hosterror_text, "Host error %ld: %s\n", macHostError, error_text);
|
||||
pm_macosxcm_term(); /* clear out any opened ports */
|
||||
return pmHostError;
|
||||
}
|
||||
|
||||
void pm_macosxcm_term(void)
|
||||
{
|
||||
if (client != NULL) MIDIClientDispose(client);
|
||||
if (portIn != NULL) MIDIPortDispose(portIn);
|
||||
if (portOut != NULL) MIDIPortDispose(portOut);
|
||||
}
|
4
pd-0.44-2/portmidi/pm_mac/pmmacosxcm.h
Executable file
4
pd-0.44-2/portmidi/pm_mac/pmmacosxcm.h
Executable file
|
@ -0,0 +1,4 @@
|
|||
/* system-specific definitions */
|
||||
|
||||
PmError pm_macosxcm_init(void);
|
||||
void pm_macosxcm_term(void);
|
291
pd-0.44-2/portmidi/pm_win/README_WIN.txt
Executable file
291
pd-0.44-2/portmidi/pm_win/README_WIN.txt
Executable file
|
@ -0,0 +1,291 @@
|
|||
File: PortMidi Win32 Readme
|
||||
Author: Belinda Thom, June 16 2002
|
||||
Revised by: Roger Dannenberg, June 2002, May 2004
|
||||
|
||||
=============================================================================
|
||||
USING PORTMIDI:
|
||||
=============================================================================
|
||||
|
||||
PortMidi has been created using a DLL because the Win32 MMedia API doesn't
|
||||
handle midiInput properly in the debugger. Specifically, it doesn't clean up
|
||||
after itself if the user (i.e. you, a PortMidi application) hasn't explicitly
|
||||
closed all open midi input devices. This lack of cleanup can lead to much
|
||||
pain and agony, including the blue-screen-of-death. This situation becomes
|
||||
increasingly unacceptable when you are debugging your code, so a portMidi DLL
|
||||
seemed to be the most elegant solution.
|
||||
|
||||
Using Microsoft Visual C++ project files (provided with PortMidi), there
|
||||
are two configurations of the PortMidi library. The Debug version is
|
||||
intended for debugging, especially in a console application. The Debug
|
||||
version enables some extra error checking and outputs some text as well
|
||||
as a prompt to type ENTER so that you don't lose any debugging text when
|
||||
the program exits. You can turn off this extra debugging info by taking
|
||||
out the compile-time definition for DEBUG. (But leave _DEBUG, which I
|
||||
think is important for compiling in Debug mode.) This debugging version also
|
||||
defines PM_CHECK_ERRORS, which forces a check for error return codes from
|
||||
every call to PortMidi. You can disable this checking (especially if you
|
||||
want to handle error codes in your own way) by removing PM_CHECK_ERRORS
|
||||
from the predefined symbols list in the Settings dialog box.
|
||||
|
||||
PortMidi is designed to run without a console and should work perfectly
|
||||
well within a graphical user interface application. The Release version
|
||||
is both optimized and lacking the debugging printout code of the Debug
|
||||
version.
|
||||
|
||||
Read the portmidi.h file for PortMidi API details on using the PortMidi API.
|
||||
See <...>\pm_dll_test\test.c or <...>\multithread\test.c for usage examples.
|
||||
|
||||
=============================================================================
|
||||
TO INSTALL PORTMIDI:
|
||||
=============================================================================
|
||||
1) download portmidi.zip
|
||||
|
||||
2) unzip portmidi.zip into directory: <...>\portmidi
|
||||
|
||||
=============================================================================
|
||||
TO COMPILE PORTMIDI:
|
||||
=============================================================================
|
||||
|
||||
3) cd to or open the portmidi directory
|
||||
|
||||
4) start or click on the portmidi.dsw workspace
|
||||
|
||||
5) the following projects exist within this workspace:
|
||||
- portmidi (the PortMidi library)
|
||||
- pm_dll (the dll library used to close midi ports on program exit)
|
||||
- porttime (a small portable library implementing timer facilities)
|
||||
- test (simple midi I/O testing)
|
||||
- multithread (an example illustrating low-latency MIDI processing
|
||||
using a dedicated low-latency thread)
|
||||
- sysex (simple sysex message I/O testing)
|
||||
- latency (uses porttime to measure system latency)
|
||||
|
||||
6) verify that all project settings are for Win32 Debug release:
|
||||
- type Alt-F7
|
||||
- highlight all three projects in left part of Project Settings window;
|
||||
- "Settings For" should say "Win32 Debug"
|
||||
|
||||
7) use Build->Batch Build ... to build everything in the project
|
||||
|
||||
8) The settings for these projects were distributed in the zip file, so
|
||||
compile should just work.
|
||||
|
||||
9) IMPORTANT! PortMidi uses a DLL, pm_dll.dll, but there is no simple way
|
||||
to set up projects to use pm_dll. THEREFORE, you need to copy DLLs
|
||||
as follows (you can do this with <...>\portmidi\pm_win\copy-dll.bat):
|
||||
copy <...>\portmidi\pm_win\Debug\pm_dll.dll to:
|
||||
<...>\portmidi\pm_test\latencyDebug\pm_dll.dll
|
||||
<...>\portmidi\pm_test\midithreadDebug\pm_dll.dll
|
||||
<...>\portmidi\pm_test\sysexDebug\pm_dll.dll
|
||||
<...>\portmidi\pm_test\testDebug\pm_dll.dll
|
||||
<...>\portmidi\pm_test\midithruDebug\pm_dll.dll
|
||||
and copy <...>\portmidi\pm_win\Release\pm_dll.dll to:
|
||||
<...>\portmidi\pm_test\latencyRelease\pm_dll.dll
|
||||
<...>\portmidi\pm_test\midithreadRelease\pm_dll.dll
|
||||
<...>\portmidi\pm_test\sysexRelease\pm_dll.dll
|
||||
<...>\portmidi\pm_test\testRelease\pm_dll.dll
|
||||
<...>\portmidi\pm_test\midithruRelease\pm_dll.dll
|
||||
each time you rebuild the pm_dll project, these copies must be redone!
|
||||
|
||||
Since Windows will look in the executable directory for DLLs, we
|
||||
recommend that you always install a copy of pm_dll.dll (either the
|
||||
debug version or the release version) in the same directory as the
|
||||
application using PortMidi. The release DLL is about 40KB. This will
|
||||
ensure that the application uses the correct DLL.
|
||||
|
||||
10) run test project; use the menu that shows up from the command prompt to
|
||||
test that portMidi works on your system. tests include:
|
||||
- verify midi output works
|
||||
- verify midi input works
|
||||
- verify midi input w/midi thru works
|
||||
|
||||
11) run other projects if you wish: sysex, latency, midithread, mm, qtest
|
||||
|
||||
============================================================================
|
||||
TO CREATE YOUR OWN PORTMIDI CLIENT APPLICATION:
|
||||
============================================================================
|
||||
|
||||
NOTE: this section needs to be reviewed and tested. My suggestion would
|
||||
be to copy the test project file (test.dsp) and modify it. -RBD
|
||||
|
||||
The easiest way is to start a new project w/in the portMidi workspace:
|
||||
|
||||
1) To open new project:
|
||||
- File->New->Projects
|
||||
- Location: <...>\portmidi\<yourProjectName>
|
||||
- check Add to current workspace
|
||||
- select Win32 Console Application (recommended for now)
|
||||
- do *NOT* select the "make dependency" box (you will explicitly do this
|
||||
in the next step)
|
||||
- Click OK
|
||||
- Select "An Empty Project" and click Finish
|
||||
|
||||
2) Now this project will be the active project. Make it explicitly depend
|
||||
on PortMidi dll:
|
||||
- Project->Dependencies
|
||||
- Click pm_dll
|
||||
|
||||
3) Important! in order to be able to use portMidi DLL from your new project
|
||||
and set breakpoints, copy following files from <...>\pm_dll\Debug into
|
||||
<...>\<yourProjectName>\Debug directory:
|
||||
pm_dll.lib
|
||||
pm_dll.dll
|
||||
each time you rebuild pm_dll, these copies must be redone!
|
||||
|
||||
4) add whatever files you wish to add to your new project, using portMidi
|
||||
calls as desired (see USING PORTMIDI at top of this readme)
|
||||
|
||||
5) when you include portMidi files, do so like this:
|
||||
- #include "..\pm_dll\portmidi.h"
|
||||
- etc.
|
||||
|
||||
6) build and run your project
|
||||
|
||||
============================================================================
|
||||
DESIGN NOTES
|
||||
============================================================================
|
||||
|
||||
The DLL is used so that PortMidi can (usually) close open devices when the
|
||||
program terminates. Failure to close input devices under WinNT, Win2K, and
|
||||
probably later systems causes the OS to crash.
|
||||
|
||||
This is accomplished with a .LIB/.DLL pair, linking to the .LIB
|
||||
in order to access functions in the .DLL.
|
||||
|
||||
PortMidi for Win32 exists as a simple library,
|
||||
with Win32-specific code in pmwin.c and MM-specific code in pmwinmm.c.
|
||||
pmwin.c uses a DLL in pmdll.c to call Pm_Terminate() when the program
|
||||
exits to make sure that all MIDI ports are closed.
|
||||
|
||||
Orderly cleanup after errors are encountered is based on a fixed order of
|
||||
steps and state changes to reflect each step. Here's the order:
|
||||
|
||||
To open input:
|
||||
initialize return value to NULL
|
||||
- allocate the PmInternal strucure (representation of PortMidiStream)
|
||||
return value is (non-null) PmInternal structure
|
||||
- allocate midi buffer
|
||||
set buffer field of PmInternal structure
|
||||
- call system-dependent open code
|
||||
- allocate midiwinmm_type for winmm dependent data
|
||||
set descriptor field of PmInternal structure
|
||||
- open device
|
||||
set handle field of midiwinmm_type structure
|
||||
- allocate buffer 1 for sysex
|
||||
buffer is added to input port
|
||||
- allocate buffer 2 for sysex
|
||||
buffer is added to input port
|
||||
- return
|
||||
- return
|
||||
|
||||
SYSEX HANDLING -- the most complex, least exercised, and therefore most
|
||||
buggy part of PortMidi (but maybe bugs are finally gone)
|
||||
|
||||
There are three cases: simple output, stream output, input
|
||||
Each must deal with:
|
||||
1. Buffer Initialization (creating buffers)
|
||||
2. Buffer Allocation (finding a free buffer)
|
||||
3. Buffer Fill (putting bytes in the buffer)
|
||||
4. Buffer Preparation (midiOutPrepare, etc.)
|
||||
5. Buffer Send (to Midi device)
|
||||
6. Buffer Receive (in callback)
|
||||
7. Buffer Empty (removing bytes from buffer)
|
||||
8. Buffer Free (returning to the buffer pool)
|
||||
9. Buffer Finalization (returning to heap)
|
||||
|
||||
Here's how simple output handles sysex:
|
||||
1. Buffer Initialization (creating buffers)
|
||||
allocated when code tries to write first byte to a buffer
|
||||
the test is "if (!m->sysex_buffers[0]) { ... }"
|
||||
this field is initialized to NULL when device is opened
|
||||
the size is SYSEX_BYTES_PER_BUFFER
|
||||
allocate_sysex_buffers() does the initialization
|
||||
note that the actual size of the allocation includes
|
||||
additional space for a MIDIEVENT (3 longs) which are
|
||||
not used in this case
|
||||
2. Buffer Allocation (finding a free buffer)
|
||||
see get_free_sysex_buffer()
|
||||
cycle through m->sysex_buffers[] using m->next_sysex_buffer
|
||||
to determine where to look next
|
||||
if nothing is found, wait by blocking on m->sysex_buffer_signal
|
||||
this is signaled by the callback every time a message is
|
||||
received
|
||||
3. Buffer Fill (putting bytes in the buffer)
|
||||
essentially a state machine approach
|
||||
hdr->dwBytesRecorded is a position in message pointed to by m->hdr
|
||||
keep appending bytes until dwBytesRecorded >= SYSEX_BYTES_PER_BUFFER
|
||||
then send the message, reseting the state to initial values
|
||||
4. Buffer Preparation (midiOutPrepare, etc.)
|
||||
just before sending in winmm_end_sysex()
|
||||
5. Buffer Send (to Midi device)
|
||||
message is padded with zero at end (since extra space was allocated
|
||||
this is ok) -- the zero works around a bug in (an old version of)
|
||||
MIDI YOKE drivers
|
||||
dwBufferLength gets dwBytesRecorded, and dwBytesRecorded gets 0
|
||||
uses midiOutLongMsg()
|
||||
6. Buffer Receive (in callback)
|
||||
7. Buffer Empty (removing bytes from buffer)
|
||||
not applicable for output
|
||||
8. Buffer Free (returning to the buffer pool)
|
||||
unprepare message to indicate that it is free
|
||||
SetEvent on m->buffer_signal in case client is waiting
|
||||
9. Buffer Finalization (returning to heap)
|
||||
when device is closed, winmm_out_delete frees all sysex buffers
|
||||
|
||||
Here's how stream output handles sysex:
|
||||
1. Buffer Initialization (creating buffers)
|
||||
same code as simple output (see above)
|
||||
2. Buffer Allocation (finding a free buffer)
|
||||
same code as simple output (see above)
|
||||
3. Buffer Fill (putting bytes in the buffer)
|
||||
essentially a state machine approach
|
||||
m->dwBytesRecorded is a position in message
|
||||
keep appending bytes until buffer is full (one byte to spare)
|
||||
4. Buffer Preparation (midiOutPrepare, etc.)
|
||||
done before sending message
|
||||
dwBytesRecorded and dwBufferLength are set in winmm_end_sysex
|
||||
5. Buffer Send (to Midi device)
|
||||
uses midiStreamOutMsg()
|
||||
6. Buffer Receive (in callback)
|
||||
7. Buffer Empty (removing bytes from buffer)
|
||||
not applicable for output
|
||||
8. Buffer Free (returning to the buffer pool)
|
||||
unprepare message to indicate that it is free
|
||||
SetEvent on m->buffer_signal in case client is waiting
|
||||
9. Buffer Finalization (returning to heap)
|
||||
when device is closed, winmm_out_delete frees all sysex buffers
|
||||
|
||||
|
||||
Here's how input handles sysex:
|
||||
1. Buffer Initialization (creating buffers)
|
||||
two buffers are allocated in winmm_in_open
|
||||
2. Buffer Allocation (finding a free buffer)
|
||||
same code as simple output (see above)
|
||||
3. Buffer Fill (putting bytes in the buffer)
|
||||
not applicable for input
|
||||
4. Buffer Preparation (midiOutPrepare, etc.)
|
||||
done before sending message -- in winmm_in_open and in callback
|
||||
5. Buffer Send (to Midi device)
|
||||
uses midiInAddbuffer in allocate_sysex_input_buffer (called from
|
||||
winmm_in_open) and callback
|
||||
6. Buffer Receive (in callback)
|
||||
7. Buffer Empty (removing bytes from buffer)
|
||||
done without pause in loop in callback
|
||||
8. Buffer Free (returning to the buffer pool)
|
||||
done by midiInAddBuffer in callback, no pointer to buffers
|
||||
is retained except by device
|
||||
9. Buffer Finalization (returning to heap)
|
||||
when device is closed, empty buffers are delivered to callback,
|
||||
which frees them
|
||||
|
||||
IMPORTANT: In addition to the above, PortMidi now has
|
||||
"shortcuts" to optimize the transfer of sysex data. To enable
|
||||
the optimization for sysex output, the system-dependent code
|
||||
sets fields in the pmInternal structure: fill_base, fill_offset_ptr,
|
||||
and fill_length. When fill_base is non-null, the system-independent
|
||||
part of PortMidi is allowed to directly copy sysex bytes to
|
||||
"fill_base[*fill_offset_ptr++]" until *fill_offset_ptr reaches
|
||||
fill_length. See the code for details.
|
||||
|
||||
|
||||
|
17
pd-0.44-2/portmidi/pm_win/copy-dll.bat
Executable file
17
pd-0.44-2/portmidi/pm_win/copy-dll.bat
Executable file
|
@ -0,0 +1,17 @@
|
|||
copy Debug\pm_dll.dll ..\pm_test\testDebug\pm_dll.dll
|
||||
copy Debug\pm_dll.dll ..\pm_test\sysexDebug\pm_dll.dll
|
||||
copy Debug\pm_dll.dll ..\pm_test\midithreadDebug\pm_dll.dll
|
||||
copy Debug\pm_dll.dll ..\pm_test\latencyDebug\pm_dll.dll
|
||||
copy Debug\pm_dll.dll ..\pm_test\midithruDebug\pm_dll.dll
|
||||
copy Debug\pm_dll.dll ..\pm_test\qtestDebug\pm_dll.dll
|
||||
copy Debug\pm_dll.dll ..\pm_test\mmDebug\pm_dll.dll
|
||||
|
||||
copy Release\pm_dll.dll ..\pm_test\testRelease\pm_dll.dll
|
||||
copy Release\pm_dll.dll ..\pm_test\sysexRelease\pm_dll.dll
|
||||
copy Release\pm_dll.dll ..\pm_test\midithreadRelease\pm_dll.dll
|
||||
copy Release\pm_dll.dll ..\pm_test\latencyRelease\pm_dll.dll
|
||||
copy Release\pm_dll.dll ..\pm_test\midithruRelease\pm_dll.dll
|
||||
copy Release\pm_dll.dll ..\pm_test\qtestRelease\pm_dll.dll
|
||||
copy Release\pm_dll.dll ..\pm_test\mmRelease\pm_dll.dll
|
||||
|
||||
|
145
pd-0.44-2/portmidi/pm_win/debugging_dlls.txt
Executable file
145
pd-0.44-2/portmidi/pm_win/debugging_dlls.txt
Executable file
|
@ -0,0 +1,145 @@
|
|||
========================================================================================================================
|
||||
Methods for Debugging DLLs
|
||||
========================================================================================================================
|
||||
If you have the source for both the DLL and the calling program, open the project for the calling executable file and
|
||||
debug the DLL from there. If you load a DLL dynamically, you must specify it in the Additional DLLs category of the
|
||||
Debug tab in the Project Settings dialog box.
|
||||
|
||||
If you have the source for the DLL only, open the project that builds the DLL. Use the Debug tab in the Project
|
||||
Settings dialog box to specify the executable file that calls the DLL.
|
||||
|
||||
You can also debug a DLL without a project. For example, maybe you just picked up a DLL and source code but you
|
||||
don’t have an associated project or workspace. You can use the Open command on the File menu to select the .DLL
|
||||
file you want to debug. The debug information should be in either the .DLL or the related .PDB file. After
|
||||
Visual C++ opens the file, on the Build menu click Start Debug and Go to begin debugging.
|
||||
|
||||
To debug a DLL using the project for the executable file
|
||||
|
||||
From the Project menu, click Settings.
|
||||
The Project Settings dialog box appears.
|
||||
|
||||
Choose the Debug tab.
|
||||
|
||||
|
||||
In the Category drop-down list box, select General.
|
||||
|
||||
|
||||
In the Program Arguments text box, type any command-line arguments required by the executable file.
|
||||
|
||||
|
||||
In the Category drop-down list box, select Additional DLLs.
|
||||
|
||||
|
||||
In the Local Name column, type the names of DLLs to debug.
|
||||
If you are debugging remotely, the Remote Name column appears. In this column, type the complete path for the
|
||||
remote module to map to the local module name.
|
||||
|
||||
In the Preload column, select the check box if you want to load the module before debugging begins.
|
||||
|
||||
|
||||
Click OK to store the information in your project.
|
||||
|
||||
|
||||
From the Build menu, click Start Debug and Go to start the debugger.
|
||||
You can set breakpoints in the DLL or the calling program. You can open a source file for the DLL and set breakpoints
|
||||
in that file, even though it is not a part of the executable file’s project.
|
||||
|
||||
To debug a DLL using the project for the DLL
|
||||
|
||||
From the Project menu, click Settings.
|
||||
The Project Settings dialog box appears.
|
||||
|
||||
Choose the Debug tab.
|
||||
|
||||
|
||||
In the Category drop-down list box, select General.
|
||||
|
||||
|
||||
In the Executable For Debug Session text box, type the name of the executable file that calls the DLL.
|
||||
|
||||
|
||||
In the Category list box, select Additional DLLs.
|
||||
|
||||
|
||||
In the Local Module Name column, type the name of the DLLs you want to debug.
|
||||
|
||||
|
||||
Click OK to store the information in your project.
|
||||
|
||||
|
||||
Set breakpoints as required in your DLL source files or on function symbols in the DLL.
|
||||
|
||||
|
||||
From the Build menu, click Start Debug and Go to start the debugger.
|
||||
To debug a DLL created with an external project
|
||||
|
||||
From the Project menu, click Settings.
|
||||
The Project Settings dialog box appears.
|
||||
|
||||
Choose the Debug tab.
|
||||
|
||||
|
||||
In the Category drop-down list box, select General.
|
||||
|
||||
|
||||
In the Executable For Debug Session text box, type the name of the DLL that your external makefile builds.
|
||||
|
||||
|
||||
Click OK to store the information in your project.
|
||||
|
||||
|
||||
Build a debug version of the DLL with symbolic debugging information, if you don’t already have one.
|
||||
|
||||
|
||||
Follow one of the two procedures immediately preceding this one to debug the DLL.
|
||||
|
||||
========================================================================================================================
|
||||
Why Don’t My DLL Breakpoints Work?
|
||||
========================================================================================================================
|
||||
Some reasons why your breakpoints don’t work as expected are listed here, along with solutions or work-arounds for each.
|
||||
If you follow the instructions in one topic and are still having breakpoint problems, look at some of the other topics.
|
||||
Often breakpoint problems result from a combination of conditions.
|
||||
|
||||
You can't set a breakpoint in a source file when the corresponding symbolic information isn't loaded into memory by
|
||||
the debugger.
|
||||
You cannot set a breakpoint in any source file when the corresponding symbolic information will not be loaded into memory
|
||||
by the debugger.
|
||||
Symptoms include messages such as "the breakpoint cannot be set" or a simple, noninformational beep.
|
||||
|
||||
When setting breakpoints before the code to be debugged has been started, the debugger uses a breakpoint list to keep
|
||||
track of how and where to set breakpoints. When you actually begin the debugging session, the debugger loads the symbolic
|
||||
information for all the code to be debugged and then walks through its breakpoint list, attempting to set the
|
||||
breakpoints.
|
||||
|
||||
However, if one or more of the code modules have not been designated to the debugger, there will be no symbolic
|
||||
information for the debugger to use when walking through its breakpoint list. Situations where this is likely to
|
||||
occur include:
|
||||
|
||||
Attempts to set breakpoints in a DLL before the call to LoadLibrary.
|
||||
|
||||
Setting a breakpoint in an ActiveX server before the container has started the server.
|
||||
|
||||
Other similar cases.
|
||||
|
||||
To prevent this behavior in Visual C++, specify all additional DLLs and COM servers in the Additional DLLs field
|
||||
in the Debug/Options dialog box to notify the debugger that you want it to load symbolic debug information for
|
||||
additional .DLL files. When this has been done, breakpoints set in code that has not yet been loaded into memory
|
||||
will be "virtual" breakpoints. When the code is actually loaded into memory by the loader, these become physical
|
||||
breakpoints. Make sure that these additional debugging processes are not already running when you start your
|
||||
debugging session. The debugging process and these additional processes must be sychronized at the same beginning
|
||||
point to work correctly, hitting all breakpoints.
|
||||
|
||||
Breakpoints are missed when more than one copy of a DLL is on your hard disk.
|
||||
Having more than one copy of a DLL on your hard drive, especially if it is in your Windows directory, can cause
|
||||
debugger confusion. The debugger will load the symbolic information for the DLL specified to it at run time (with the
|
||||
Additional DLLs field in the Debug/Options dialog box), while Windows has actually loaded a different copy of the
|
||||
DLL itself into memory. Because there is no way to force the debugger to load a specific DLL, it is a good idea to
|
||||
keep only one version of a DLL at a time in your path, current directory, and Windows directory.
|
||||
|
||||
You can’t set "Break When Expression Has Changed" breakpoints on a variable local to a DLL.
|
||||
Setting a "Break When Expression Has Changed" breakpoint on a variable local to a DLL function before the call
|
||||
to LoadLibrary causes the breakpoint to be virtual (there are no physical addresses for the DLL in memory yet).
|
||||
Virtual breakpoints involving expressions pose a special problem. The DLL must be specified to the debugger at
|
||||
startup (causing its symbolic information to be loaded). In addition, the DLL's executable code must also be loaded
|
||||
into memory before this kind of breakpoint can be set. This means that the calling application's code must be
|
||||
executed to the point after its call to LoadLibrary before the debugger will allow this type of breakpoint to be set.
|
107
pd-0.44-2/portmidi/pm_win/pm_dll.dsp
Executable file
107
pd-0.44-2/portmidi/pm_win/pm_dll.dsp
Executable file
|
@ -0,0 +1,107 @@
|
|||
# Microsoft Developer Studio Project File - Name="pm_dll" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
|
||||
|
||||
CFG=pm_dll - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "pm_dll.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "pm_dll.mak" CFG="pm_dll - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "pm_dll - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE "pm_dll - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
MTL=midl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "pm_dll - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PM_DLL_EXPORTS" /YX /FD /c
|
||||
# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PM_DLL_EXPORTS" /YX /FD /c
|
||||
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
|
||||
|
||||
!ELSEIF "$(CFG)" == "pm_dll - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Ignore_Export_Lib 1
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PM_DLL_EXPORTS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "pm_common" /D "_WINDOWS" /D "_USRDLL" /D "PM_DLL_EXPORTS" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "USE_DLL_FOR_CLEANUP" /FR /YX /FD /GZ /c
|
||||
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "pm_dll - Win32 Release"
|
||||
# Name "pm_dll - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\pmdll.c
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\pmdll.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
49
pd-0.44-2/portmidi/pm_win/pmdll.c
Executable file
49
pd-0.44-2/portmidi/pm_win/pmdll.c
Executable file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
====================================================================
|
||||
DLL to perform action when program shuts down
|
||||
====================================================================
|
||||
*/
|
||||
|
||||
#include "windows.h"
|
||||
#include "pmdll.h"
|
||||
|
||||
static close_fn_ptr_type close_function = NULL;
|
||||
|
||||
|
||||
DLL_EXPORT pm_set_close_function(close_fn_ptr_type close_fn_ptr)
|
||||
{
|
||||
close_function = close_fn_ptr;
|
||||
}
|
||||
|
||||
|
||||
static void Initialize( void ) {
|
||||
return;
|
||||
}
|
||||
|
||||
static void Terminate( void ) {
|
||||
if (close_function) {
|
||||
(*close_function)();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, //DLL module handle
|
||||
DWORD fdwReason, //for calling function
|
||||
LPVOID lbpvReserved)//reserved
|
||||
{
|
||||
switch(fdwReason) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
/* when DLL starts, run this */
|
||||
Initialize();
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
/* when DLL ends, this run (note: verified this run */
|
||||
Terminate();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
5
pd-0.44-2/portmidi/pm_win/pmdll.h
Executable file
5
pd-0.44-2/portmidi/pm_win/pmdll.h
Executable file
|
@ -0,0 +1,5 @@
|
|||
#define DLL_EXPORT __declspec( dllexport )
|
||||
|
||||
typedef void (*close_fn_ptr_type)();
|
||||
|
||||
DLL_EXPORT pm_set_close_function(close_fn_ptr_type close_fn_ptr);
|
114
pd-0.44-2/portmidi/pm_win/pmwin.c
Executable file
114
pd-0.44-2/portmidi/pm_win/pmwin.c
Executable file
|
@ -0,0 +1,114 @@
|
|||
/* pmwin.c -- PortMidi os-dependent code */
|
||||
|
||||
/* This file only needs to implement:
|
||||
pm_init(), which calls various routines to register the
|
||||
available midi devices,
|
||||
Pm_GetDefaultInputDeviceID(), and
|
||||
Pm_GetDefaultOutputDeviceID().
|
||||
This file must
|
||||
be separate from the main portmidi.c file because it is system
|
||||
dependent, and it is separate from, say, pmwinmm.c, because it
|
||||
might need to register devices for winmm, directx, and others.
|
||||
*/
|
||||
|
||||
#include "stdlib.h"
|
||||
#include "portmidi.h"
|
||||
#include "pmutil.h"
|
||||
#include "pminternal.h"
|
||||
#include "pmwinmm.h"
|
||||
#ifdef USE_DLL_FOR_CLEANUP
|
||||
#include "pmdll.h" /* used to close ports on exit */
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
#include "stdio.h"
|
||||
#endif
|
||||
|
||||
/* pm_exit is called when the program exits.
|
||||
It calls pm_term to make sure PortMidi is properly closed.
|
||||
If DEBUG is on, we prompt for input to avoid losing error messages.
|
||||
*/
|
||||
static void pm_exit(void) {
|
||||
pm_term();
|
||||
#ifdef DEBUG
|
||||
#define STRING_MAX 80
|
||||
{
|
||||
char line[STRING_MAX];
|
||||
printf("Type ENTER...\n");
|
||||
/* note, w/o this prompting, client console application can not see one
|
||||
of its errors before closing. */
|
||||
fgets(line, STRING_MAX, stdin);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* pm_init is the windows-dependent initialization.*/
|
||||
void pm_init(void)
|
||||
{
|
||||
#ifdef USE_DLL_FOR_CLEANUP
|
||||
/* we were hoping a DLL could offer more robust cleanup after errors,
|
||||
but the DLL does not seem to run after crashes. Thus, the atexit()
|
||||
mechanism is just as powerful, and simpler to implement.
|
||||
*/
|
||||
pm_set_close_function(pm_exit);
|
||||
#ifdef DEBUG
|
||||
printf("registered pm_term with cleanup DLL\n");
|
||||
#endif
|
||||
#else
|
||||
atexit(pm_exit);
|
||||
#ifdef DEBUG
|
||||
printf("registered pm_exit with atexit()\n");
|
||||
#endif
|
||||
#endif
|
||||
pm_winmm_init();
|
||||
/* initialize other APIs (DirectX?) here */
|
||||
}
|
||||
|
||||
|
||||
void pm_term(void) {
|
||||
pm_winmm_term();
|
||||
}
|
||||
|
||||
|
||||
PmDeviceID Pm_GetDefaultInputDeviceID() {
|
||||
/* This routine should check the environment and the registry
|
||||
as specified in portmidi.h, but for now, it just returns
|
||||
the first device of the proper input/output flavor.
|
||||
*/
|
||||
int i;
|
||||
Pm_Initialize(); /* make sure descriptors exist! */
|
||||
for (i = 0; i < pm_descriptor_index; i++) {
|
||||
if (descriptors[i].pub.input) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return pmNoDevice;
|
||||
}
|
||||
|
||||
PmDeviceID Pm_GetDefaultOutputDeviceID() {
|
||||
/* This routine should check the environment and the registry
|
||||
as specified in portmidi.h, but for now, it just returns
|
||||
the first device of the proper input/output flavor.
|
||||
*/
|
||||
int i;
|
||||
Pm_Initialize(); /* make sure descriptors exist! */
|
||||
for (i = 0; i < pm_descriptor_index; i++) {
|
||||
if (descriptors[i].pub.output) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return pmNoDevice;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "stdio.h"
|
||||
|
||||
void *pm_alloc(size_t s) {
|
||||
return malloc(s);
|
||||
}
|
||||
|
||||
|
||||
void pm_free(void *ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
1467
pd-0.44-2/portmidi/pm_win/pmwinmm.c
Executable file
1467
pd-0.44-2/portmidi/pm_win/pmwinmm.c
Executable file
File diff suppressed because it is too large
Load diff
5
pd-0.44-2/portmidi/pm_win/pmwinmm.h
Executable file
5
pd-0.44-2/portmidi/pm_win/pmwinmm.h
Executable file
|
@ -0,0 +1,5 @@
|
|||
/* midiwin32.h -- system-specific definitions */
|
||||
|
||||
void pm_winmm_init( void );
|
||||
void pm_winmm_term( void );
|
||||
|
3
pd-0.44-2/portmidi/porttime/porttime.c
Executable file
3
pd-0.44-2/portmidi/porttime/porttime.c
Executable file
|
@ -0,0 +1,3 @@
|
|||
/* porttime.c -- portable API for millisecond timer */
|
||||
|
||||
/* There is no machine-independent implementation code to put here */
|
104
pd-0.44-2/portmidi/porttime/porttime.dsp
Executable file
104
pd-0.44-2/portmidi/porttime/porttime.dsp
Executable file
|
@ -0,0 +1,104 @@
|
|||
# Microsoft Developer Studio Project File - Name="porttime" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Static Library" 0x0104
|
||||
|
||||
CFG=porttime - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "porttime.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "porttime.mak" CFG="porttime - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "porttime - Win32 Release" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE "porttime - Win32 Debug" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "porttime - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
|
||||
# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo
|
||||
|
||||
!ELSEIF "$(CFG)" == "porttime - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_LIB" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "USE_DLL_FOR_CLEANUP" /YX /FD /GZ /c
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "porttime - Win32 Release"
|
||||
# Name "porttime - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\porttime.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ptwinmm.c
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\porttime.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
36
pd-0.44-2/portmidi/porttime/porttime.h
Executable file
36
pd-0.44-2/portmidi/porttime/porttime.h
Executable file
|
@ -0,0 +1,36 @@
|
|||
/* porttime.h -- portable interface to millisecond timer */
|
||||
|
||||
/* CHANGE LOG FOR PORTTIME
|
||||
10-Jun-03 Mark Nelson & RBD
|
||||
boost priority of timer thread in ptlinux.c implementation
|
||||
*/
|
||||
|
||||
/* Should there be a way to choose the source of time here? */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
typedef enum {
|
||||
ptNoError = 0,
|
||||
ptHostError = -10000,
|
||||
ptAlreadyStarted,
|
||||
ptAlreadyStopped,
|
||||
ptInsufficientMemory
|
||||
} PtError;
|
||||
|
||||
|
||||
typedef long PtTimestamp;
|
||||
|
||||
typedef void (PtCallback)( PtTimestamp timestamp, void *userData );
|
||||
|
||||
|
||||
PtError Pt_Start(int resolution, PtCallback *callback, void *userData);
|
||||
PtError Pt_Stop();
|
||||
int Pt_Started();
|
||||
PtTimestamp Pt_Time();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
127
pd-0.44-2/portmidi/porttime/ptlinux.c
Executable file
127
pd-0.44-2/portmidi/porttime/ptlinux.c
Executable file
|
@ -0,0 +1,127 @@
|
|||
/* ptlinux.c -- portable timer implementation for linux */
|
||||
|
||||
|
||||
/* IMPLEMENTATION NOTES (by Mark Nelson):
|
||||
|
||||
Unlike Windows, Linux has no system call to request a periodic callback,
|
||||
so if Pt_Start() receives a callback parameter, it must create a thread
|
||||
that wakes up periodically and calls the provided callback function.
|
||||
If running as superuser, use setpriority() to renice thread to -20.
|
||||
One could also set the timer thread to a real-time priority (SCHED_FIFO
|
||||
and SCHED_RR), but this is dangerous for This is necessary because
|
||||
if the callback hangs it'll never return. A more serious reason
|
||||
is that the current scheduler implementation busy-waits instead
|
||||
of sleeping when realtime threads request a sleep of <=2ms (as a way
|
||||
to get around the 10ms granularity), which means the thread would never
|
||||
let anyone else on the CPU.
|
||||
|
||||
CHANGE LOG
|
||||
|
||||
18-Jul-03 Roger Dannenberg -- Simplified code to set priority of timer
|
||||
thread. Simplified implementation notes.
|
||||
|
||||
*/
|
||||
/* stdlib, stdio, unistd, and sys/types were added because they appeared
|
||||
* in a Gentoo patch, but I'm not sure why they are needed. -RBD
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include "porttime.h"
|
||||
#include "sys/time.h"
|
||||
#include "sys/resource.h"
|
||||
#include "sys/timeb.h"
|
||||
#include "pthread.h"
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
static int time_started_flag = FALSE;
|
||||
static struct timeb time_offset = {0, 0, 0, 0};
|
||||
static pthread_t pt_thread_pid;
|
||||
|
||||
/* note that this is static data -- we only need one copy */
|
||||
typedef struct {
|
||||
int id;
|
||||
int resolution;
|
||||
PtCallback *callback;
|
||||
void *userData;
|
||||
} pt_callback_parameters;
|
||||
|
||||
static int pt_callback_proc_id = 0;
|
||||
|
||||
static void *Pt_CallbackProc(void *p)
|
||||
{
|
||||
pt_callback_parameters *parameters = (pt_callback_parameters *) p;
|
||||
int mytime = 1;
|
||||
/* to kill a process, just increment the pt_callback_proc_id */
|
||||
/* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id,
|
||||
parameters->id); */
|
||||
if (geteuid() == 0) setpriority(PRIO_PROCESS, 0, -20);
|
||||
while (pt_callback_proc_id == parameters->id) {
|
||||
/* wait for a multiple of resolution ms */
|
||||
struct timeval timeout;
|
||||
int delay = mytime++ * parameters->resolution - Pt_Time();
|
||||
if (delay < 0) delay = 0;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = delay * 1000;
|
||||
select(0, NULL, NULL, NULL, &timeout);
|
||||
(*(parameters->callback))(Pt_Time(), parameters->userData);
|
||||
}
|
||||
/* printf("Pt_CallbackProc exiting\n"); */
|
||||
// free(parameters);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
|
||||
{
|
||||
if (time_started_flag) return ptNoError;
|
||||
ftime(&time_offset); /* need this set before process runs */
|
||||
if (callback) {
|
||||
int res;
|
||||
pt_callback_parameters *parms = (pt_callback_parameters *)
|
||||
malloc(sizeof(pt_callback_parameters));
|
||||
if (!parms) return ptInsufficientMemory;
|
||||
parms->id = pt_callback_proc_id;
|
||||
parms->resolution = resolution;
|
||||
parms->callback = callback;
|
||||
parms->userData = userData;
|
||||
res = pthread_create(&pt_thread_pid, NULL,
|
||||
Pt_CallbackProc, parms);
|
||||
if (res != 0) return ptHostError;
|
||||
}
|
||||
time_started_flag = TRUE;
|
||||
return ptNoError;
|
||||
}
|
||||
|
||||
|
||||
PtError Pt_Stop()
|
||||
{
|
||||
/* printf("Pt_Stop called\n"); */
|
||||
pt_callback_proc_id++;
|
||||
pthread_join(pt_thread_pid, NULL);
|
||||
time_started_flag = FALSE;
|
||||
return ptNoError;
|
||||
}
|
||||
|
||||
|
||||
int Pt_Started()
|
||||
{
|
||||
return time_started_flag;
|
||||
}
|
||||
|
||||
|
||||
PtTimestamp Pt_Time()
|
||||
{
|
||||
long seconds, milliseconds;
|
||||
struct timeb now;
|
||||
ftime(&now);
|
||||
seconds = now.time - time_offset.time;
|
||||
milliseconds = now.millitm - time_offset.millitm;
|
||||
return seconds * 1000 + milliseconds;
|
||||
}
|
||||
|
||||
|
||||
|
135
pd-0.44-2/portmidi/porttime/ptmacosx_cf.c
Executable file
135
pd-0.44-2/portmidi/porttime/ptmacosx_cf.c
Executable file
|
@ -0,0 +1,135 @@
|
|||
/* ptmacosx.c -- portable timer implementation for mac os x */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#import <mach/mach.h>
|
||||
#import <mach/mach_error.h>
|
||||
#import <mach/mach_time.h>
|
||||
#import <mach/clock.h>
|
||||
|
||||
#include "porttime.h"
|
||||
|
||||
#define THREAD_IMPORTANCE 30
|
||||
#define LONG_TIME 1000000000.0
|
||||
|
||||
static int time_started_flag = FALSE;
|
||||
static CFAbsoluteTime startTime = 0.0;
|
||||
static CFRunLoopRef timerRunLoop;
|
||||
|
||||
typedef struct {
|
||||
int resolution;
|
||||
PtCallback *callback;
|
||||
void *userData;
|
||||
} PtThreadParams;
|
||||
|
||||
|
||||
void Pt_CFTimerCallback(CFRunLoopTimerRef timer, void *info)
|
||||
{
|
||||
PtThreadParams *params = (PtThreadParams*)info;
|
||||
(*params->callback)(Pt_Time(), params->userData);
|
||||
}
|
||||
|
||||
static void* Pt_Thread(void *p)
|
||||
{
|
||||
CFTimeInterval timerInterval;
|
||||
CFRunLoopTimerContext timerContext;
|
||||
CFRunLoopTimerRef timer;
|
||||
PtThreadParams *params = (PtThreadParams*)p;
|
||||
//CFTimeInterval timeout;
|
||||
|
||||
/* raise the thread's priority */
|
||||
kern_return_t error;
|
||||
thread_extended_policy_data_t extendedPolicy;
|
||||
thread_precedence_policy_data_t precedencePolicy;
|
||||
|
||||
extendedPolicy.timeshare = 0;
|
||||
error = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY,
|
||||
(thread_policy_t)&extendedPolicy,
|
||||
THREAD_EXTENDED_POLICY_COUNT);
|
||||
if (error != KERN_SUCCESS) {
|
||||
mach_error("Couldn't set thread timeshare policy", error);
|
||||
}
|
||||
|
||||
precedencePolicy.importance = THREAD_IMPORTANCE;
|
||||
error = thread_policy_set(mach_thread_self(), THREAD_PRECEDENCE_POLICY,
|
||||
(thread_policy_t)&precedencePolicy,
|
||||
THREAD_PRECEDENCE_POLICY_COUNT);
|
||||
if (error != KERN_SUCCESS) {
|
||||
mach_error("Couldn't set thread precedence policy", error);
|
||||
}
|
||||
|
||||
/* set up the timer context */
|
||||
timerContext.version = 0;
|
||||
timerContext.info = params;
|
||||
timerContext.retain = NULL;
|
||||
timerContext.release = NULL;
|
||||
timerContext.copyDescription = NULL;
|
||||
|
||||
/* create a new timer */
|
||||
timerInterval = (double)params->resolution / 1000.0;
|
||||
timer = CFRunLoopTimerCreate(NULL, startTime+timerInterval, timerInterval,
|
||||
0, 0, Pt_CFTimerCallback, &timerContext);
|
||||
|
||||
timerRunLoop = CFRunLoopGetCurrent();
|
||||
CFRunLoopAddTimer(timerRunLoop, timer, CFSTR("PtTimeMode"));
|
||||
|
||||
/* run until we're told to stop by Pt_Stop() */
|
||||
CFRunLoopRunInMode(CFSTR("PtTimeMode"), LONG_TIME, false);
|
||||
|
||||
CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, CFSTR("PtTimeMode"));
|
||||
CFRelease(timer);
|
||||
free(params);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
|
||||
{
|
||||
PtThreadParams *params = (PtThreadParams*)malloc(sizeof(PtThreadParams));
|
||||
pthread_t pthread_id;
|
||||
|
||||
printf("Pt_Start() called\n");
|
||||
|
||||
// /* make sure we're not already playing */
|
||||
if (time_started_flag) return ptAlreadyStarted;
|
||||
startTime = CFAbsoluteTimeGetCurrent();
|
||||
|
||||
if (callback) {
|
||||
|
||||
params->resolution = resolution;
|
||||
params->callback = callback;
|
||||
params->userData = userData;
|
||||
|
||||
pthread_create(&pthread_id, NULL, Pt_Thread, params);
|
||||
}
|
||||
|
||||
time_started_flag = TRUE;
|
||||
return ptNoError;
|
||||
}
|
||||
|
||||
|
||||
PtError Pt_Stop()
|
||||
{
|
||||
printf("Pt_Stop called\n");
|
||||
|
||||
CFRunLoopStop(timerRunLoop);
|
||||
time_started_flag = FALSE;
|
||||
return ptNoError;
|
||||
}
|
||||
|
||||
|
||||
int Pt_Started()
|
||||
{
|
||||
return time_started_flag;
|
||||
}
|
||||
|
||||
|
||||
PtTimestamp Pt_Time()
|
||||
{
|
||||
CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
|
||||
return (PtTimestamp) ((now - startTime) * 1000.0);
|
||||
}
|
||||
|
124
pd-0.44-2/portmidi/porttime/ptmacosx_mach.c
Executable file
124
pd-0.44-2/portmidi/porttime/ptmacosx_mach.c
Executable file
|
@ -0,0 +1,124 @@
|
|||
/* ptmacosx.c -- portable timer implementation for mac os x */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <CoreAudio/HostTime.h>
|
||||
|
||||
#import <mach/mach.h>
|
||||
#import <mach/mach_error.h>
|
||||
#import <mach/mach_time.h>
|
||||
#import <mach/clock.h>
|
||||
|
||||
#include "porttime.h"
|
||||
#include "sys/time.h"
|
||||
#include "pthread.h"
|
||||
|
||||
#define NSEC_PER_MSEC 1000000
|
||||
#define THREAD_IMPORTANCE 30
|
||||
|
||||
static int time_started_flag = FALSE;
|
||||
static UInt64 start_time;
|
||||
static pthread_t pt_thread_pid;
|
||||
|
||||
/* note that this is static data -- we only need one copy */
|
||||
typedef struct {
|
||||
int id;
|
||||
int resolution;
|
||||
PtCallback *callback;
|
||||
void *userData;
|
||||
} pt_callback_parameters;
|
||||
|
||||
static int pt_callback_proc_id = 0;
|
||||
|
||||
static void *Pt_CallbackProc(void *p)
|
||||
{
|
||||
pt_callback_parameters *parameters = (pt_callback_parameters *) p;
|
||||
int mytime = 1;
|
||||
|
||||
kern_return_t error;
|
||||
thread_extended_policy_data_t extendedPolicy;
|
||||
thread_precedence_policy_data_t precedencePolicy;
|
||||
|
||||
extendedPolicy.timeshare = 0;
|
||||
error = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY,
|
||||
(thread_policy_t)&extendedPolicy,
|
||||
THREAD_EXTENDED_POLICY_COUNT);
|
||||
if (error != KERN_SUCCESS) {
|
||||
mach_error("Couldn't set thread timeshare policy", error);
|
||||
}
|
||||
|
||||
precedencePolicy.importance = THREAD_IMPORTANCE;
|
||||
error = thread_policy_set(mach_thread_self(), THREAD_PRECEDENCE_POLICY,
|
||||
(thread_policy_t)&precedencePolicy,
|
||||
THREAD_PRECEDENCE_POLICY_COUNT);
|
||||
if (error != KERN_SUCCESS) {
|
||||
mach_error("Couldn't set thread precedence policy", error);
|
||||
}
|
||||
|
||||
|
||||
/* to kill a process, just increment the pt_callback_proc_id */
|
||||
/* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, parameters->id); */
|
||||
while (pt_callback_proc_id == parameters->id) {
|
||||
/* wait for a multiple of resolution ms */
|
||||
UInt64 wait_time;
|
||||
int delay = mytime++ * parameters->resolution - Pt_Time();
|
||||
long timestamp;
|
||||
if (delay < 0) delay = 0;
|
||||
wait_time = AudioConvertNanosToHostTime((UInt64)delay * NSEC_PER_MSEC);
|
||||
wait_time += AudioGetCurrentHostTime();
|
||||
error = mach_wait_until(wait_time);
|
||||
timestamp = Pt_Time();
|
||||
(*(parameters->callback))(timestamp, parameters->userData);
|
||||
}
|
||||
free(parameters);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
|
||||
{
|
||||
if (time_started_flag) return ptAlreadyStarted;
|
||||
start_time = AudioGetCurrentHostTime();
|
||||
|
||||
if (callback) {
|
||||
int res;
|
||||
pt_callback_parameters *parms;
|
||||
|
||||
parms = (pt_callback_parameters *) malloc(sizeof(pt_callback_parameters));
|
||||
if (!parms) return ptInsufficientMemory;
|
||||
parms->id = pt_callback_proc_id;
|
||||
parms->resolution = resolution;
|
||||
parms->callback = callback;
|
||||
parms->userData = userData;
|
||||
res = pthread_create(&pt_thread_pid, NULL, Pt_CallbackProc, parms);
|
||||
if (res != 0) return ptHostError;
|
||||
}
|
||||
|
||||
time_started_flag = TRUE;
|
||||
return ptNoError;
|
||||
}
|
||||
|
||||
|
||||
PtError Pt_Stop()
|
||||
{
|
||||
/* printf("Pt_Stop called\n"); */
|
||||
pt_callback_proc_id++;
|
||||
time_started_flag = FALSE;
|
||||
return ptNoError;
|
||||
}
|
||||
|
||||
|
||||
int Pt_Started()
|
||||
{
|
||||
return time_started_flag;
|
||||
}
|
||||
|
||||
|
||||
PtTimestamp Pt_Time()
|
||||
{
|
||||
UInt64 clock_time, nsec_time;
|
||||
clock_time = AudioGetCurrentHostTime() - start_time;
|
||||
nsec_time = AudioConvertHostTimeToNanos(clock_time);
|
||||
return (PtTimestamp)(nsec_time / NSEC_PER_MSEC);
|
||||
}
|
||||
|
65
pd-0.44-2/portmidi/porttime/ptwinmm.c
Executable file
65
pd-0.44-2/portmidi/porttime/ptwinmm.c
Executable file
|
@ -0,0 +1,65 @@
|
|||
/* ptwinmm.c -- portable timer implementation for win32 */
|
||||
|
||||
|
||||
#include "porttime.h"
|
||||
#include "windows.h"
|
||||
#include "time.h"
|
||||
|
||||
|
||||
TIMECAPS caps;
|
||||
|
||||
static long time_offset = 0;
|
||||
static int time_started_flag = FALSE;
|
||||
static long time_resolution;
|
||||
static MMRESULT timer_id;
|
||||
static PtCallback *time_callback;
|
||||
|
||||
void CALLBACK winmm_time_callback(UINT uID, UINT uMsg, DWORD dwUser,
|
||||
DWORD dw1, DWORD dw2)
|
||||
{
|
||||
(*time_callback)(Pt_Time(), (void *) dwUser);
|
||||
}
|
||||
|
||||
|
||||
PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
|
||||
{
|
||||
if (time_started_flag) return ptAlreadyStarted;
|
||||
timeBeginPeriod(resolution);
|
||||
time_resolution = resolution;
|
||||
time_offset = timeGetTime();
|
||||
time_started_flag = TRUE;
|
||||
time_callback = callback;
|
||||
if (callback) {
|
||||
timer_id = timeSetEvent(resolution, 1, winmm_time_callback,
|
||||
(DWORD) userData, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
|
||||
if (!timer_id) return ptHostError;
|
||||
}
|
||||
return ptNoError;
|
||||
}
|
||||
|
||||
|
||||
PtError Pt_Stop()
|
||||
{
|
||||
if (!time_started_flag) return ptAlreadyStopped;
|
||||
if (time_callback && timer_id) {
|
||||
timeKillEvent(timer_id);
|
||||
time_callback = NULL;
|
||||
timer_id = 0;
|
||||
}
|
||||
time_started_flag = FALSE;
|
||||
timeEndPeriod(time_resolution);
|
||||
return ptNoError;
|
||||
}
|
||||
|
||||
|
||||
int Pt_Started()
|
||||
{
|
||||
return time_started_flag;
|
||||
}
|
||||
|
||||
|
||||
PtTimestamp Pt_Time()
|
||||
{
|
||||
return timeGetTime() - time_offset;
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue