diff options
author | Robert Jonsson <spamatica@gmail.com> | 2010-10-13 19:34:22 +0000 |
---|---|---|
committer | Robert Jonsson <spamatica@gmail.com> | 2010-10-13 19:34:22 +0000 |
commit | 8a2c2824a59d7644e13bc52c9a0ecbd641f21f95 (patch) | |
tree | 064ad3f2bf8daab0ad27b128abd86a9bbdb1e496 /muse2/muse/driver | |
parent | a27706d9629e8b592cca4659f865b70adef24e6d (diff) |
new branch muse2, first checkin
Diffstat (limited to 'muse2/muse/driver')
-rw-r--r-- | muse2/muse/driver/Makefile.am | 10 | ||||
-rw-r--r-- | muse2/muse/driver/Makefile.in | 595 | ||||
-rw-r--r-- | muse2/muse/driver/alsamidi.cpp | 917 | ||||
-rw-r--r-- | muse2/muse/driver/alsamidi.h | 53 | ||||
-rw-r--r-- | muse2/muse/driver/alsatimer.cpp | 225 | ||||
-rw-r--r-- | muse2/muse/driver/alsatimer.h | 52 | ||||
-rw-r--r-- | muse2/muse/driver/audiodev.h | 74 | ||||
-rw-r--r-- | muse2/muse/driver/dummyaudio.cpp | 454 | ||||
-rw-r--r-- | muse2/muse/driver/jack.cpp | 2173 | ||||
-rw-r--r-- | muse2/muse/driver/jackaudio.h | 97 | ||||
-rw-r--r-- | muse2/muse/driver/jackmidi.cpp | 1563 | ||||
-rw-r--r-- | muse2/muse/driver/jackmidi.h | 156 | ||||
-rw-r--r-- | muse2/muse/driver/rtctimer.cpp | 155 | ||||
-rw-r--r-- | muse2/muse/driver/rtctimer.h | 44 | ||||
-rw-r--r-- | muse2/muse/driver/timerdev.h | 41 |
15 files changed, 6609 insertions, 0 deletions
diff --git a/muse2/muse/driver/Makefile.am b/muse2/muse/driver/Makefile.am new file mode 100644 index 00000000..da3eba07 --- /dev/null +++ b/muse2/muse/driver/Makefile.am @@ -0,0 +1,10 @@ +include $(top_srcdir)/common.am + +noinst_LIBRARIES = libdriver.a + +AM_CXXFLAGS += $(JACK_CFLAGS) + +libdriver_a_SOURCES = audiodev.h alsamidi.cpp alsamidi.h jack.cpp jackaudio.h \ + dummyaudio.cpp alsatimer.cpp alsatimer.h timerdev.h rtctimer.cpp rtctimer.h \ + jackmidi.cpp jackmidi.h + diff --git a/muse2/muse/driver/Makefile.in b/muse2/muse/driver/Makefile.in new file mode 100644 index 00000000..8c773db5 --- /dev/null +++ b/muse2/muse/driver/Makefile.in @@ -0,0 +1,595 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 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@ +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@ +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(top_srcdir)/common.am +subdir = muse/driver +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/aclocal-include.m4 \ + $(top_srcdir)/m4/alsa.m4 $(top_srcdir)/m4/docbook.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +libdriver_a_AR = $(AR) $(ARFLAGS) +libdriver_a_LIBADD = +am_libdriver_a_OBJECTS = alsamidi.$(OBJEXT) jack.$(OBJEXT) \ + dummyaudio.$(OBJEXT) alsatimer.$(OBJEXT) rtctimer.$(OBJEXT) \ + jackmidi.$(OBJEXT) +libdriver_a_OBJECTS = $(am_libdriver_a_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +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 = $(libdriver_a_SOURCES) +DIST_SOURCES = $(libdriver_a_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALSA_CFLAGS = @ALSA_CFLAGS@ +ALSA_LIBS = @ALSA_LIBS@ +AMTAR = @AMTAR@ +AR = @AR@ +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@ +DOCBOOKSTYLE = @DOCBOOKSTYLE@ +DOCBOOKTARGETS = @DOCBOOKTARGETS@ +DOT = @DOT@ +DOTPATH = @DOTPATH@ +DOXYGEN = @DOXYGEN@ +DOXYGEN_TREEVIEW = @DOXYGEN_TREEVIEW@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FLUIDSYNTHDIRS = @FLUIDSYNTHDIRS@ +FST_CFLAGS = @FST_CFLAGS@ +FST_LIBS = @FST_LIBS@ +Fluidsynth_CFLAGS = @Fluidsynth_CFLAGS@ +Fluidsynth_LIBS = @Fluidsynth_LIBS@ +GIVERTCAP = @GIVERTCAP@ +GREP = @GREP@ +HAVEDOT = @HAVEDOT@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JACK_CFLAGS = @JACK_CFLAGS@ +JACK_LIBS = @JACK_LIBS@ +JADE = @JADE@ +LASH_CFLAGS = @LASH_CFLAGS@ +LASH_LIBS = @LASH_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LO_CFLAGS = @LO_CFLAGS@ +LO_LIBS = @LO_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MUSECXXFLAGS = @MUSECXXFLAGS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NSGMLS = @NSGMLS@ +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@ +PCH = @PCH@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_INCLUDES = @PYTHON_INCLUDES@ +PYTHON_LIB = @PYTHON_LIB@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +QTDIR_BIN = @QTDIR_BIN@ +QTDIR_INC = @QTDIR_INC@ +QT_LIBS = @QT_LIBS@ +RANLIB = @RANLIB@ +SAMPLERATE_CFLAGS = @SAMPLERATE_CFLAGS@ +SAMPLERATE_LIBS = @SAMPLERATE_LIBS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SNDFILE_CFLAGS = @SNDFILE_CFLAGS@ +SNDFILE_LIBS = @SNDFILE_LIBS@ +STRIP = @STRIP@ +SUIDBUILD = @SUIDBUILD@ +SUIDINSTALL = @SUIDINSTALL@ +USE_SSE = @USE_SSE@ +UUID_CFLAGS = @UUID_CFLAGS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XMKMF = @XMKMF@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +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@ +have_docbook = @have_docbook@ +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@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +synth_fluid = @synth_fluid@ +synth_fluidsynth = @synth_fluidsynth@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CXXFLAGS = $(MUSECXXFLAGS) -I.. -I$(top_srcdir)/synti \ + -I$(top_srcdir)/muse/widgets -DQT_SHARED -DQT_THREAD_SUPPORT \ + -DQT_PLUGIN $(QTDIR_INC) -DQT3_SUPPORT $(JACK_CFLAGS) +AM_CPPFLAGS = +MOC = $(QTDIR_BIN)/moc +#UIC = $(QTDIR_BIN)/uic3 +UIC = /usr/bin/uic3 +UIFILES = $(wildcard *.ui) +MOCFILES = $(shell for h in $(filter %.h,$(SOURCES)); do \ + if grep -q Q_OBJECT $$h; then \ + echo $$h | sed "s/\(.*\)\.h/moc_\1.cpp/"; \ + fi; \ + done) + +BUILT_SOURCES = $(MOCFILES) $(UIFILES:%.ui=%.h) +MOSTLYCLEANFILES = $(MOCFILES) $(UIFILES:%.ui=%.h) +SUFFIXES = .ui .h.gch +noinst_LIBRARIES = libdriver.a +libdriver_a_SOURCES = audiodev.h alsamidi.cpp alsamidi.h jack.cpp jackaudio.h \ + dummyaudio.cpp alsatimer.cpp alsatimer.h timerdev.h rtctimer.cpp rtctimer.h \ + jackmidi.cpp jackmidi.h + +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .ui .h.gch .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/common.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) --gnu muse/driver/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu muse/driver/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-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libdriver.a: $(libdriver_a_OBJECTS) $(libdriver_a_DEPENDENCIES) + -rm -f libdriver.a + $(libdriver_a_AR) libdriver.a $(libdriver_a_OBJECTS) $(libdriver_a_LIBADD) + $(RANLIB) libdriver.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alsamidi.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alsatimer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dummyaudio.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jack.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jackmidi.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rtctimer.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +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: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LIBRARIES) +installdirs: +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) 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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) + +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." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ + 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: all check install install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLIBRARIES 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 + + +de: + @for base in $(MOCFILES); do echo -e "\t$${base}.cpp $${base}.h \\"; done + +nde: + @for base in $(MOCFILES); do echo -e "\tmoc_$${base}.cpp \\"; done + +.ui.o: %.h + $(UIC) -L $(top_srcdir)/muse/widgets -o $*.cpp -impl $*.h $*.ui + $(MOC) $*.h >> $*.cpp + $(CXXCOMPILE) -c $*.cpp -o $*.o + $(RM) $*.cpp moc_$*.cpp moc_$*.o + +.ui.lo: %.h + $(UIC) -L $(top_srcdir)/muse/widgets -o $*.cpp -impl $*.h $*.ui + $(MOC) $*.h >> $*.cpp + $(LTCXXCOMPILE) -c $*.cpp -o $*.lo + $(RM) $*.cpp moc_$*.cpp moc_$*.lo moc_$*.o + +moc_%.cpp: %.h + $(MOC) $< -o $@ + +%.h: %.ui + $(UIC) -o $@ $< +# $(UIC) -L $(top_srcdir)/muse/widgets -o $@ $< + +# 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: diff --git a/muse2/muse/driver/alsamidi.cpp b/muse2/muse/driver/alsamidi.cpp new file mode 100644 index 00000000..3b15fc6f --- /dev/null +++ b/muse2/muse/driver/alsamidi.cpp @@ -0,0 +1,917 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: alsamidi.cpp,v 1.8.2.7 2009/11/19 04:20:33 terminator356 Exp $ +// (C) Copyright 2000-2001 Werner Schweer (ws@seh.de) +//========================================================= + +#include <stdio.h> + +#include "alsamidi.h" +#include "globals.h" +#include "midi.h" +#include "mididev.h" +#include "../midiport.h" +#include "../midiseq.h" +#include "../midictrl.h" +#include "../audio.h" +#include "mpevent.h" +//#include "sync.h" +#include "utils.h" +#include "audiodev.h" +#include "xml.h" + +static int alsaSeqFdi = -1; +static int alsaSeqFdo = -1; + +snd_seq_t* alsaSeq; +static snd_seq_addr_t musePort; + +//--------------------------------------------------------- +// MidiAlsaDevice +//--------------------------------------------------------- + +MidiAlsaDevice::MidiAlsaDevice(const snd_seq_addr_t& a, const QString& n) + : MidiDevice(n) + { + adr = a; + init(); + } + +//--------------------------------------------------------- +// selectWfd +//--------------------------------------------------------- + +int MidiAlsaDevice::selectWfd() + { + return alsaSeqFdo; + } + +//--------------------------------------------------------- +// open +//--------------------------------------------------------- + +QString MidiAlsaDevice::open() +{ + _openFlags &= _rwFlags; // restrict to available bits + snd_seq_port_subscribe_t* subs; + // Allocated on stack, no need to call snd_seq_port_subscribe_free() later. + snd_seq_port_subscribe_alloca(&subs); + + QString estr; + int wer = 0; + int rer = 0; + + // subscribe for writing + if (_openFlags & 1) + { + snd_seq_port_subscribe_set_sender(subs, &musePort); + snd_seq_port_subscribe_set_dest(subs, &adr); + // Not already subscribed (or error)? Then try subscribing. + if(snd_seq_get_port_subscription(alsaSeq, subs) < 0) + { + //int error = snd_seq_subscribe_port(alsaSeq, subs); + wer = snd_seq_subscribe_port(alsaSeq, subs); + //if (error < 0) + if(wer < 0) + //return QString("Play: ")+QString(snd_strerror(error)); + estr += (QString("Play: ") + QString(snd_strerror(wer)) + QString(" ")); + } + if(!wer) + _writeEnable = true; + } + + // subscribe for reading + if (_openFlags & 2) + { + snd_seq_port_subscribe_set_dest(subs, &musePort); + snd_seq_port_subscribe_set_sender(subs, &adr); + // Not already subscribed (or error)? Then try subscribing. + if(snd_seq_get_port_subscription(alsaSeq, subs) < 0) + { + //int error = snd_seq_subscribe_port(alsaSeq, subs); + rer = snd_seq_subscribe_port(alsaSeq, subs); + //if (error < 0) + if(rer < 0) + //return QString("Rec: ") + QString(snd_strerror(error)); + estr += (QString("Rec: ") + QString(snd_strerror(rer))); + } + if(!rer) + _readEnable = true; + } + + + if(wer < 0 || rer < 0) + return estr; + + return QString("OK"); +} + +//--------------------------------------------------------- +// close +//--------------------------------------------------------- + +void MidiAlsaDevice::close() +{ + snd_seq_port_subscribe_t* subs; + // Allocated on stack, no need to call snd_seq_port_subscribe_free() later. + snd_seq_port_subscribe_alloca(&subs); + + // Changed by T356. This function appears to be called only by MidiPort::setMidiDevice(), + // which closes then opens the device. + // Because the open flags are set BEFORE setMidiDevice() is called, we must ignore the flags. + // + // NOTE: Tested: The read unsubscribe works ok but not the write. + // As viewed in say, qjackctl, the connection is clearly lost, + // but strangely the events are still accepted, ie, playback notes + // are still heard etc. Tried an alsa midi device AND external fluidsynth inst. + // + // Also, jack running and with jack midi disabled, we get messages like + // MidiAlsaDevice::0x84512c0 putEvent(): midi write error: No such device + // dst 16:0 + // only sometimes (not when playing notes), but with jack midi turned on, + // we don't get the messages. With jack stopped we get the messages + // no matter if jack midi is turned on or not. + + //if (_openFlags & 1) { + //if (!(_openFlags & 1)) + { + snd_seq_port_subscribe_set_sender(subs, &musePort); + snd_seq_port_subscribe_set_dest(subs, &adr); + + // Already subscribed? Then unsubscribe. + if(!snd_seq_get_port_subscription(alsaSeq, subs)) + { + if(!snd_seq_unsubscribe_port(alsaSeq, subs)) + _writeEnable = false; + else + printf("MidiAlsaDevice::close Error unsubscribing alsa midi port for writing\n"); + } + else + _writeEnable = false; + } + + //if (_openFlags & 2) { + //if (!(_openFlags & 2)) + { + snd_seq_port_subscribe_set_dest(subs, &musePort); + snd_seq_port_subscribe_set_sender(subs, &adr); + + // Already subscribed? Then unsubscribe. + if(!snd_seq_get_port_subscription(alsaSeq, subs)) + { + if(!snd_seq_unsubscribe_port(alsaSeq, subs)) + _readEnable = false; + else + printf("MidiAlsaDevice::close Error unsubscribing alsa midi port for reading\n"); + } + else + _readEnable = false; + } +} + +//--------------------------------------------------------- +// writeRouting +//--------------------------------------------------------- + +void MidiAlsaDevice::writeRouting(int level, Xml& xml) const +{ + // p3.3.45 + // If this device is not actually in use by the song, do not write any routes. + // This prevents bogus routes from being saved and propagated in the med file. + if(midiPort() == -1) + return; + + QString s; + /* + //if(rwFlags() & 2) // Readable + { + //RouteList* rl = _inRoutes; + //for (ciRoute r = rl->begin(); r != rl->end(); ++r) + for (ciRoute r = _inRoutes.begin(); r != _inRoutes.end(); ++r) + { + // Since an ALSA midi device supports read + write, this is the only way we can tell if this route is using the device as input. + if(r->type == Route::TRACK_ROUTE) + continue; + + if(!r->name().isEmpty()) + { + xml.tag(level++, "Route"); + + //xml.strTag(level, "srcNode", r->name()); + xml.tag(level, "source type=\"%d\" name=\"%s\"/", r->type, r->name().latin1()); + + //xml.strTag(level, "dstNode", name()); + xml.tag(level, "dest type=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, name().latin1()); + + xml.etag(level--, "Route"); + } + } + } + */ + + for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r) + { + //if(r->type != Route::TRACK_ROUTE) + //{ + // printf("MidiAlsaDevice::writeRouting Warning out route is not TRACK_ROUTE type\n"); + // continue; + //} + + if(!r->name().isEmpty()) + { + //xml.tag(level++, "Route"); + + s = QT_TR_NOOP("Route"); + if(r->channel != -1) + s += QString(QT_TR_NOOP(" channel=\"%1\"")).arg(r->channel); + xml.tag(level++, s); + + /* + //xml.strTag(level, "srcNode", name()); + if(r->channel != -1) + //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, r->channel, name().latin1()); + //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, r->channel, name().latin1()); + xml.tag(level, "source devtype=\"%d\" channel=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, r->channel, name().latin1()); + else + //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, name().latin1()); + //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, name().latin1()); + */ + //xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, name().latin1()); + xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, Xml::xmlString(name()).latin1()); + + /* + //xml.strTag(level, "dstNode", r->name()); + if(r->channel != -1) + { + if(r->type == Route::MIDI_DEVICE_ROUTE) + xml.tag(level, "dest devtype=\"%d\" channel=\"%d\" name=\"%s\"/", r->device->deviceType(), r->channel, r->name().latin1()); + else + xml.tag(level, "dest type=\"%d\" channel=\"%d\" name=\"%s\"/", r->type, r->channel, r->name().latin1()); + } + else + { + if(r->type == Route::MIDI_DEVICE_ROUTE) + xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", r->device->deviceType(), r->name().latin1()); + else + xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().latin1()); + } + */ + + s = QT_TR_NOOP("dest"); + if(r->type == Route::MIDI_DEVICE_ROUTE) + s += QString(QT_TR_NOOP(" devtype=\"%1\"")).arg(r->device->deviceType()); + else + if(r->type != Route::TRACK_ROUTE) + s += QString(QT_TR_NOOP(" type=\"%1\"")).arg(r->type); + //s += QString(QT_TR_NOOP(" name=\"%1\"/")).arg(r->name()); + s += QString(QT_TR_NOOP(" name=\"%1\"/")).arg(Xml::xmlString(r->name())); + xml.tag(level, s); + + xml.etag(level--, "Route"); + } + } +} + +//--------------------------------------------------------- +// putEvent +//--------------------------------------------------------- + +bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e) + { + if (midiOutputTrace) { + printf("MidiOut: midiAlsa: "); + e.dump(); + } + int chn = e.channel(); + int a = e.dataA(); + int b = e.dataB(); + + snd_seq_event_t event; + memset(&event, 0, sizeof(event)); + event.queue = SND_SEQ_QUEUE_DIRECT; + event.source = musePort; + event.dest = adr; + + switch(e.type()) { + case ME_NOTEON: + snd_seq_ev_set_noteon(&event, chn, a, b); + break; + case ME_NOTEOFF: + snd_seq_ev_set_noteoff(&event, chn, a, 0); + break; + case ME_PROGRAM: + snd_seq_ev_set_pgmchange(&event, chn, a); + break; + case ME_CONTROLLER: +#if 1 + snd_seq_ev_set_controller(&event, chn, a, b); +#else + { + int a = e.dataA(); + int b = e.dataB(); + int chn = e.channel(); + // p3.3.37 + //if (a < 0x1000) { // 7 Bit Controller + if (a < CTRL_14_OFFSET) { // 7 Bit Controller + snd_seq_ev_set_controller(&event, chn, a, b); + } + //else if (a < 0x20000) { // 14 bit high resolution controller + else if (a < CTRL_RPN_OFFSET) { // 14 bit high resolution controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + a = (ctrlH << 7) + ctrlL; + snd_seq_ev_set_controller(&event, chn, a, b); + event.type = SND_SEQ_EVENT_CONTROL14; + } + //else if (a < 0x30000) { // RPN 7-Bit Controller + else if (a < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + a = (ctrlH << 7) + ctrlL; + b <<= 7; + snd_seq_ev_set_controller(&event, chn, a, b); + event.type = SND_SEQ_EVENT_REGPARAM; + } + //else if (a < 0x40000) { // NRPN 7-Bit Controller + else if (a < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + a = (ctrlH << 7) + ctrlL; + b <<= 7; + snd_seq_ev_set_controller(&event, chn, a, b); + event.type = SND_SEQ_EVENT_NONREGPARAM; + } + //else if (a < 0x60000) { // RPN14 Controller + else if (a < CTRL_NRPN14_OFFSET) { // RPN14 Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + a = (ctrlH << 7) + ctrlL; + snd_seq_ev_set_controller(&event, chn, a, b); + event.type = SND_SEQ_EVENT_REGPARAM; + } + //else if (a < 0x70000) { // NRPN14 Controller + else if (a < CTRL_NONE_OFFSET) { // NRPN14 Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + a = (ctrlH << 7) + ctrlL; + snd_seq_ev_set_controller(&event, chn, a, b); + event.type = SND_SEQ_EVENT_NONREGPARAM; + } + else { + printf("putEvent: unknown controller type 0x%x\n", a); + } + } +#endif + break; + case ME_PITCHBEND: + snd_seq_ev_set_pitchbend(&event, chn, a); + break; + case ME_POLYAFTER: + // chnEvent2(chn, 0xa0, a, b); + break; + case ME_AFTERTOUCH: + snd_seq_ev_set_chanpress(&event, chn, a); + break; + case ME_SYSEX: + { + const unsigned char* p = e.data(); + int n = e.len(); + int len = n + sizeof(event) + 2; + char buf[len]; + event.type = SND_SEQ_EVENT_SYSEX; + event.flags = SND_SEQ_EVENT_LENGTH_VARIABLE; + event.data.ext.len = n + 2; + event.data.ext.ptr = (void*)(buf + sizeof(event)); + memcpy(buf, &event, sizeof(event)); + char* pp = buf + sizeof(event); + *pp++ = 0xf0; + memcpy(pp, p, n); + pp += n; + *pp = 0xf7; + return putEvent(&event); + } + case ME_SONGPOS: + event.data.control.value = a; + event.type = SND_SEQ_EVENT_SONGPOS; + break; + case ME_CLOCK: + event.type = SND_SEQ_EVENT_CLOCK; + break; + case ME_START: + event.type = SND_SEQ_EVENT_START; + break; + case ME_CONTINUE: + event.type = SND_SEQ_EVENT_CONTINUE; + break; + case ME_STOP: + event.type = SND_SEQ_EVENT_STOP; + break; + default: + printf("MidiAlsaDevice::putEvent(): event type %d not implemented\n", + e.type()); + return true; + } + return putEvent(&event); + } + +//--------------------------------------------------------- +// putEvent +// return false if event is delivered +//--------------------------------------------------------- + +bool MidiAlsaDevice::putEvent(snd_seq_event_t* event) + { + int error; + + do { + error = snd_seq_event_output_direct(alsaSeq, event); + int len = snd_seq_event_length(event); + if (error == len) { +// printf(".");fflush(stdout); + return false; + } + if (error < 0) { + if (error == -12) { +// printf("?");fflush(stdout); + return true; + } + else { + fprintf(stderr, "MidiAlsaDevice::%p putEvent(): midi write error: %s\n", + this, snd_strerror(error)); + fprintf(stderr, " dst %d:%d\n", adr.client, adr.port); + //exit(-1); + } + } + else + fprintf(stderr, "MidiAlsaDevice::putEvent(): midi write returns %d, expected %d: %s\n", + error, len, snd_strerror(error)); + } while (error == -12); + return true; + } + +//--------------------------------------------------------- +// initMidiAlsa +// return true on error +//--------------------------------------------------------- + +bool initMidiAlsa() + { + if (debugMsg) + printf("initMidiAlsa\n"); + int error = snd_seq_open(&alsaSeq, "hw", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); + if (error < 0) { + fprintf(stderr, "Could not open ALSA sequencer: %s\n", + snd_strerror(error)); + return true; + } + const int inCap = SND_SEQ_PORT_CAP_SUBS_READ; + const int outCap = SND_SEQ_PORT_CAP_SUBS_WRITE; + + snd_seq_client_info_t *cinfo; + snd_seq_client_info_alloca(&cinfo); + snd_seq_client_info_set_client(cinfo, -1); + + while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) { + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca(&pinfo); + 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(alsaSeq, pinfo) >= 0) { + unsigned int capability = snd_seq_port_info_get_capability(pinfo); + if ((capability & outCap) == 0) { + const char *name = snd_seq_port_info_get_name(pinfo); + if (strcmp("Timer", name) == 0 || + strcmp("Announce", name) == 0 || + strcmp("Receiver", name) == 0) + continue; + } + snd_seq_addr_t adr = *snd_seq_port_info_get_addr(pinfo); + MidiAlsaDevice* dev = new MidiAlsaDevice(adr, QString(snd_seq_port_info_get_name(pinfo))); + int flags = 0; + if (capability & outCap) + flags |= 1; + if (capability & inCap) + flags |= 2; + dev->setrwFlags(flags); + if (debugMsg) + printf("ALSA port add: <%s>, %d:%d flags %d 0x%0x\n", + snd_seq_port_info_get_name(pinfo), + adr.client, adr.port, + flags, capability); + midiDevices.add(dev); + + /* + // Experimental... Need to list 'sensible' devices first and ignore unwanted ones... + // Add instance last in midi device list. + for(int i = 0; i < MIDI_PORTS; ++i) + { + MidiPort* mp = &midiPorts[i]; + if(mp->device() == 0) + { + // midiSeq might not be initialzed yet! + //midiSeq->msgSetMidiDevice(mp, dev); + mp->setMidiDevice(dev); + + //muse->changeConfig(true); // save configuration file + //update(); + break; + } + } + */ + + } + } + + // p3.3.38 + //snd_seq_set_client_name(alsaSeq, "MusE Sequencer"); + snd_seq_set_client_name(alsaSeq, audioDevice->clientName()); + + int ci = snd_seq_poll_descriptors_count(alsaSeq, POLLIN); + int co = snd_seq_poll_descriptors_count(alsaSeq, POLLOUT); + + if (ci > 1 || co > 1) { + printf("ALSA midi: cannot handle more than one poll fd\n"); + abort(); + } + + struct pollfd pfdi[ci]; + struct pollfd pfdo[co]; + snd_seq_poll_descriptors(alsaSeq, pfdi, ci, POLLIN); + snd_seq_poll_descriptors(alsaSeq, pfdo, co, POLLOUT); + alsaSeqFdo = pfdo[0].fd; + alsaSeqFdi = pfdi[0].fd; + + int port = snd_seq_create_simple_port(alsaSeq, "MusE Port 0", + inCap | outCap | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE, + SND_SEQ_PORT_TYPE_APPLICATION); + if (port < 0) { + perror("create port"); + exit(1); + } + musePort.port = port; + musePort.client = snd_seq_client_id(alsaSeq); + + //----------------------------------------- + // subscribe to "Announce" + // this enables callbacks for any + // alsa port changes + //----------------------------------------- + + snd_seq_addr_t aadr; + aadr.client = SND_SEQ_CLIENT_SYSTEM; + aadr.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE; + + snd_seq_port_subscribe_t* subs; + snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_subscribe_set_dest(subs, &musePort); + snd_seq_port_subscribe_set_sender(subs, &aadr); + error = snd_seq_subscribe_port(alsaSeq, subs); + if (error < 0) { + printf("Alsa: Subscribe System failed: %s", snd_strerror(error)); + return true; + } + return false; + } + +struct AlsaPort { + snd_seq_addr_t adr; + char* name; + int flags; + AlsaPort(snd_seq_addr_t a, const char* s, int f) { + adr = a; + name = strdup(s); + flags = f; + } + }; + +static std::list<AlsaPort> portList; + +//--------------------------------------------------------- +// alsaScanMidiPorts +//--------------------------------------------------------- + +void alsaScanMidiPorts() + { +// printf("alsa scan midi ports\n"); + const int inCap = SND_SEQ_PORT_CAP_SUBS_READ; + const int outCap = SND_SEQ_PORT_CAP_SUBS_WRITE; + + portList.clear(); + + snd_seq_client_info_t* cinfo; + snd_seq_client_info_alloca(&cinfo); + snd_seq_client_info_set_client(cinfo, 0); + + while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) { + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca(&pinfo); + 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(alsaSeq, pinfo) >= 0) { + unsigned int capability = snd_seq_port_info_get_capability(pinfo); + if (((capability & outCap) == 0) + && ((capability & inCap) == 0)) + continue; + snd_seq_addr_t adr; + const char* name; + adr = *snd_seq_port_info_get_addr(pinfo); + name = snd_seq_port_info_get_name(pinfo); + if (adr.client == musePort.client && adr.port == musePort.port) + continue; + int flags = 0; + if (capability & outCap) + flags |= 1; + if (capability & inCap) + flags |= 2; +// printf("ALSA port add: <%s>, flags %d\n", name, flags); + portList.push_back(AlsaPort(adr, name, flags)); + } + } + // + // check for devices to delete + // + for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end();) { + MidiAlsaDevice* d = dynamic_cast<MidiAlsaDevice*>(*i); + if (d == 0) { + ++i; + continue; + } + std::list<AlsaPort>::iterator k = portList.begin(); + for (; k != portList.end(); ++k) { + if (k->adr.client == d->adr.client + && k->adr.port == d->adr.port) { + break; + } + } + if (k == portList.end()) { + if (d->midiPort() != -1) + midiPorts[d->midiPort()].setMidiDevice(0); + iMidiDevice k = i; +// printf("erase device\n"); + ++i; + midiDevices.erase(k); + } + else { + ++i; + } + } + // + // check for devices to add + // + for (std::list<AlsaPort>::iterator k = portList.begin(); k != portList.end(); ++k) { + iMidiDevice i = midiDevices.begin(); +// printf("ALSA port: <%s>\n", k->name); + for (;i != midiDevices.end(); ++i) { + MidiAlsaDevice* d = dynamic_cast<MidiAlsaDevice*>(*i); + if (d == 0) + continue; + if ((k->adr.client == d->adr.client) && (k->adr.port == d->adr.port)) { + break; + } + } + if (i == midiDevices.end()) { + // add device + MidiAlsaDevice* dev = new MidiAlsaDevice(k->adr, + QString(k->name)); + dev->setrwFlags(k->flags); + midiDevices.add(dev); +// printf("add device\n"); + } + } + } + +//--------------------------------------------------------- +// alsaSelectRfd +//--------------------------------------------------------- + +int alsaSelectRfd() + { + return alsaSeqFdi; + } + +//--------------------------------------------------------- +// alsaSelectWfd +//--------------------------------------------------------- + +int alsaSelectWfd() + { + return alsaSeqFdo; + } + +//--------------------------------------------------------- +// processInput +//--------------------------------------------------------- + +void alsaProcessMidiInput() +{ + MidiRecordEvent event; + snd_seq_event_t* ev; + + for (;;) + { + int rv = snd_seq_event_input(alsaSeq, &ev); +// printf("AlsaInput %d\n", rv); + if (rv < 0) { +// printf("AlsaMidi: read error %s\n", snd_strerror(rv)); + return; + } + switch(ev->type) { + case SND_SEQ_EVENT_PORT_SUBSCRIBED: + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: + return; + case SND_SEQ_EVENT_CLIENT_START: + case SND_SEQ_EVENT_CLIENT_EXIT: + // return; + // on first start of a software synthesizer we only + // get CLIENT_START event and no PORT_START, why? + + case SND_SEQ_EVENT_PORT_START: + case SND_SEQ_EVENT_PORT_EXIT: + alsaScanMidiPorts(); + audio->midiPortsChanged(); // signal gui + snd_seq_free_event(ev); + return; + } + + int curPort = -1; + MidiAlsaDevice* mdev = 0; + // + // find real source device + // + for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) { + MidiAlsaDevice* d = dynamic_cast<MidiAlsaDevice*>(*i); + if (d && d->adr.client == ev->source.client + && d->adr.port == ev->source.port) { + curPort = d->midiPort(); + mdev = d; + } + } + + if (mdev == 0 || curPort == -1) { + if (debugMsg) { + fprintf(stderr, "no port %d:%d found for received alsa event\n", + ev->source.client, ev->source.port); + } + snd_seq_free_event(ev); + return; + } + + /* + if(curPort == -1) + { + if(mdev == 0) + { + if (debugMsg) + { + fprintf(stderr, "no port %d:%d found for received alsa event\n", + ev->source.client, ev->source.port); + } + } + else + { + // Allow the sync detect mechanisms to work, even if device is not assigned to a port. + if(ev->type == SND_SEQ_EVENT_CLOCK) + mdev->syncInfo().trigMCSyncDetect(); + else + if(ev->type == SND_SEQ_EVENT_TICK) + mdev->syncInfo().trigTickDetect(); + } + snd_seq_free_event(ev); + return; + } + */ + + event.setType(0); // mark as unused + event.setPort(curPort); + event.setB(0); + + switch(ev->type) + { + case SND_SEQ_EVENT_NOTEON: + case SND_SEQ_EVENT_KEYPRESS: + event.setChannel(ev->data.note.channel); + event.setType(ME_NOTEON); + event.setA(ev->data.note.note); + event.setB(ev->data.note.velocity); + break; + + case SND_SEQ_EVENT_NOTEOFF: + event.setChannel(ev->data.note.channel); + event.setType(ME_NOTEOFF); + event.setA(ev->data.note.note); + event.setB(ev->data.note.velocity); + break; + + case SND_SEQ_EVENT_CHANPRESS: + event.setChannel(ev->data.control.channel); + event.setType(ME_AFTERTOUCH); + event.setA(ev->data.control.value); + break; + + case SND_SEQ_EVENT_PGMCHANGE: + event.setChannel(ev->data.control.channel); + event.setType(ME_PROGRAM); + event.setA(ev->data.control.value); + break; + + case SND_SEQ_EVENT_PITCHBEND: + event.setChannel(ev->data.control.channel); + event.setType(ME_PITCHBEND); + event.setA(ev->data.control.value); + break; + + case SND_SEQ_EVENT_CONTROLLER: + event.setChannel(ev->data.control.channel); + event.setType(ME_CONTROLLER); + event.setA(ev->data.control.param); + event.setB(ev->data.control.value); + break; + + case SND_SEQ_EVENT_CLOCK: + midiSeq->realtimeSystemInput(curPort, ME_CLOCK); + //mdev->syncInfo().trigMCSyncDetect(); + break; + + case SND_SEQ_EVENT_START: + midiSeq->realtimeSystemInput(curPort, ME_START); + break; + + case SND_SEQ_EVENT_CONTINUE: + midiSeq->realtimeSystemInput(curPort, ME_CONTINUE); + break; + + case SND_SEQ_EVENT_STOP: + midiSeq->realtimeSystemInput(curPort, ME_STOP); + break; + + case SND_SEQ_EVENT_TICK: + midiSeq->realtimeSystemInput(curPort, ME_TICK); + //mdev->syncInfo().trigTickDetect(); + break; + + case SND_SEQ_EVENT_SYSEX: + + // TODO: Deal with large sysex, which are broken up into chunks! + // For now, do not accept if the first byte is not SYSEX or the last byte is not EOX, + // meaning it's a chunk, possibly with more chunks to follow. + if((*((unsigned char*)ev->data.ext.ptr) != ME_SYSEX) || + (*(((unsigned char*)ev->data.ext.ptr) + ev->data.ext.len - 1) != ME_SYSEX_END)) + { + printf("MusE: alsaProcessMidiInput sysex chunks not supported!\n"); + break; + } + + event.setTime(0); // mark as used + event.setType(ME_SYSEX); + event.setData((unsigned char*)(ev->data.ext.ptr)+1, + ev->data.ext.len-2); + break; + case SND_SEQ_EVENT_PORT_SUBSCRIBED: + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: // write port is released + break; + case SND_SEQ_EVENT_SONGPOS: + midiSeq->setSongPosition(curPort, ev->data.control.value); + break; + case SND_SEQ_EVENT_SENSING: + break; + case SND_SEQ_EVENT_QFRAME: + midiSeq->mtcInputQuarter(curPort, ev->data.control.value); + break; + // case SND_SEQ_EVENT_CLIENT_START: + // case SND_SEQ_EVENT_CLIENT_EXIT: + // case SND_SEQ_EVENT_CLIENT_CHANGE: + // case SND_SEQ_EVENT_PORT_CHANGE: + // case SND_SEQ_EVENT_SONGSEL: + // case SND_SEQ_EVENT_TIMESIGN: + // case SND_SEQ_EVENT_KEYSIGN: + // case SND_SEQ_EVENT_SETPOS_TICK: + // case SND_SEQ_EVENT_SETPOS_TIME: + // case SND_SEQ_EVENT_TEMPO: + // case SND_SEQ_EVENT_TUNE_REQUEST: + // case SND_SEQ_EVENT_RESET: + + // case SND_SEQ_EVENT_NOTE: + // case SND_SEQ_EVENT_CONTROL14: + // case SND_SEQ_EVENT_NONREGPARAM: + // case SND_SEQ_EVENT_REGPARAM: + default: + printf("ALSA Midi input: type %d not handled\n", ev->type); + break; + } + if(event.type()) + { + mdev->recordEvent(event); + // p3.3.26 1/23/10 Moved to MidiDevice now. Anticipating Jack midi support, so don't make it ALSA specific. Tim. + //if(ev->type != SND_SEQ_EVENT_SYSEX) + // Trigger general activity indicator detector. Sysex has no channel, don't trigger. + // midiPorts[curPort].syncInfo().trigActDetect(event.channel()); + } + + snd_seq_free_event(ev); + if (rv == 0) + break; + } +} + diff --git a/muse2/muse/driver/alsamidi.h b/muse2/muse/driver/alsamidi.h new file mode 100644 index 00000000..6c19ff0d --- /dev/null +++ b/muse2/muse/driver/alsamidi.h @@ -0,0 +1,53 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: alsamidi.h,v 1.2 2004/01/14 09:06:43 wschweer Exp $ +// (C) Copyright 2001 Werner Schweer (ws@seh.de) +//========================================================= + +#ifndef __ALSAMIDI_H__ +#define __ALSAMIDI_H__ + +#include <config.h> +#include <alsa/asoundlib.h> + +#include "mididev.h" + +class Xml; + +//--------------------------------------------------------- +// MidiAlsaDevice +//--------------------------------------------------------- + +class MidiAlsaDevice : public MidiDevice { + public: + snd_seq_addr_t adr; + + private: + virtual QString open(); + virtual void close(); + virtual void processInput() {} + virtual int selectRfd() { return -1; } + virtual int selectWfd(); + + bool putEvent(snd_seq_event_t*); + virtual bool putMidiEvent(const MidiPlayEvent&); + + public: + MidiAlsaDevice() {} + MidiAlsaDevice(const snd_seq_addr_t&, const QString& name); + virtual ~MidiAlsaDevice() {} + virtual void* clientPort() { return (void*)&adr; } + virtual void writeRouting(int, Xml&) const; + virtual inline int deviceType() { return ALSA_MIDI; } + }; + +extern bool initMidiAlsa(); +extern int alsaSelectRfd(); +extern int alsaSelectWfd(); +extern void alsaProcessMidiInput(); +extern void alsaScanMidiPorts(); + +#endif + + diff --git a/muse2/muse/driver/alsatimer.cpp b/muse2/muse/driver/alsatimer.cpp new file mode 100644 index 00000000..d851410d --- /dev/null +++ b/muse2/muse/driver/alsatimer.cpp @@ -0,0 +1,225 @@ + //========================================================= + // MusE + // Linux Music Editor + // $Id: alsatimer.cpp,v 1.1.2.9 2009/03/28 01:46:10 terminator356 Exp $ + // + // Plenty of code borrowed from timer.c example in + // alsalib 1.0.7 + // + // (C) Copyright 2004 Robert Jonsson (rj@spamatica.se) + //========================================================= + + #include "alsatimer.h" + #include <climits> + +#define TIMER_DEBUG 0 + + AlsaTimer::AlsaTimer() + { + if(TIMER_DEBUG) + fprintf(stderr,"AlsaTimer::AlsaTimer(this=%p) called\n",this); + handle = NULL; + id = NULL; + info = NULL; + params = NULL; + findBest = true; + } + + AlsaTimer::~AlsaTimer() + { + if(TIMER_DEBUG) + fprintf(stderr,"AlsaTimer::~AlsaTimer(this=%p) called\n",this); + if (handle) + snd_timer_close(handle); + if (id) snd_timer_id_free(id); + if (info) snd_timer_info_free(info); + if (params) snd_timer_params_free(params); + } + + signed int AlsaTimer::initTimer() + { + if(TIMER_DEBUG) + printf("AlsaTimer::initTimer(this=%p)\n",this); + + int err; + int devclass = SND_TIMER_CLASS_GLOBAL; + int sclass = SND_TIMER_CLASS_NONE; + int card = 0; + int device = SND_TIMER_GLOBAL_SYSTEM; + int subdevice = 0; + int test_ids[] = { SND_TIMER_GLOBAL_SYSTEM + , SND_TIMER_GLOBAL_RTC +#ifdef SND_TIMER_GLOBAL_HPET + , SND_TIMER_GLOBAL_HPET +#endif + }; + int max_ids = sizeof(test_ids) / sizeof(int); + long best_res = LONG_MAX; + //int best_dev = -1; // SND_TIMER_GLOBAL_SYSTEM; + int best_dev = SND_TIMER_GLOBAL_SYSTEM; // p3.3.51 + int i; + + if (id || info || params) { + fprintf(stderr,"AlsaTimer::initTimer(): called on initialised timer!\n"); + return fds->fd; + } + snd_timer_id_malloc(&id); + snd_timer_info_malloc(&info); + snd_timer_params_malloc(¶ms); + + if (findBest) { + for (i = 0; i < max_ids; ++i) { + device = test_ids[i]; + sprintf(timername, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i", devclass, sclass, card, device, subdevice); + if ((err = snd_timer_open(&handle, timername, SND_TIMER_OPEN_NONBLOCK)) < 0) { + continue; + } + if ((err = snd_timer_info(handle, info)) < 0) { + snd_timer_close(handle); + continue; + } + // select a non slave timer with the lowest resolution value + int is_slave = snd_timer_info_is_slave(info); + long res = snd_timer_info_get_resolution(info); + if ((is_slave == 0) && (best_res > res)) { + best_res = res; + best_dev = device; + } + snd_timer_close(handle); + } + device = best_dev; + } + + // p3.3.51 Removed. + //if(best_dev==-1) + // return -1; // no working timer found + + sprintf(timername, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i", devclass, sclass, card, device, subdevice); + if ((err = snd_timer_open(&handle, timername, SND_TIMER_OPEN_NONBLOCK))<0) { + fprintf(stderr, "AlsaTimer::initTimer(): timer open %i (%s)\n", err, snd_strerror(err)); + return -1; // p3.3.51 + } + + if ((err = snd_timer_info(handle, info)) < 0) { + fprintf(stderr, "AlsaTimer::initTimer(): timer info %i (%s)\n", err, snd_strerror(err)); + return -1; + } + + //if(debugMsg) + fprintf(stderr, "AlsaTimer::initTimer(): best available ALSA timer: %s\n", snd_timer_info_get_name(info)); + + snd_timer_params_set_auto_start(params, 1); + snd_timer_params_set_ticks(params, 1); + + if ((err = snd_timer_params(handle, params)) < 0) { + fprintf(stderr, "AlsaTimer::initTimer(): timer params %i (%s)\n", err, snd_strerror(err)); + return -1; + } + + count = snd_timer_poll_descriptors_count(handle); + fds = (pollfd *)calloc(count, sizeof(pollfd)); + if (fds == NULL) { + fprintf(stderr, "AlsaTimer::initTimer(): malloc error\n"); + return -1; + } + if ((err = snd_timer_poll_descriptors(handle, fds, count)) < 0) { + fprintf(stderr, "AlsaTimer::initTimer(): snd_timer_poll_descriptors error: %s\n", snd_strerror(err)); + return -1; + } + return fds->fd; + } + + unsigned int AlsaTimer::setTimerResolution(unsigned int resolution) + { + if(TIMER_DEBUG) + printf("AlsaTimer::setTimerResolution(%d)\n",resolution); + /* Resolution of an AlsaTimer is fixed - it cannot be set */ + return 0; + } + + unsigned int AlsaTimer::setTimerFreq(unsigned int freq) + { + signed int err; + unsigned int setTick, actFreq; + + if(TIMER_DEBUG) + printf("AlsaTimer::setTimerFreq(this=%p)\n",this); + + setTick = (1000000000 / snd_timer_info_get_resolution(info)) / freq; + + if (setTick == 0) { + // return, print error if freq is below 500 (timing will suffer) + if (((1000000000.0 / snd_timer_info_get_resolution(info)) / snd_timer_params_get_ticks(params)) < 500) { + fprintf(stderr,"AlsaTimer::setTimerTicks(): requested freq %u Hz too high for timer (max is %g)\n", + freq, 1000000000.0 / snd_timer_info_get_resolution(info)); + fprintf(stderr," freq stays at %ld Hz\n", + (long int)((1000000000.0 / snd_timer_info_get_resolution(info)) / snd_timer_params_get_ticks(params))); + } + + return 0; + } + actFreq = (1000000000 / snd_timer_info_get_resolution(info)) / setTick; + if (actFreq != freq) { + fprintf(stderr,"AlsaTimer::setTimerTicks(): warning: requested %u Hz, actual freq is %u Hz\n", + freq, actFreq); + } + if(TIMER_DEBUG) + printf("AlsaTimer::setTimerFreq(): Setting ticks (period) to %d ticks\n", setTick); + snd_timer_params_set_auto_start(params, 1); + snd_timer_params_set_ticks(params, setTick); + if ((err = snd_timer_params(handle, params)) < 0) { + fprintf(stderr, "AlsaTimer::setTimerFreq(): timer params %i (%s)\n", err, snd_strerror(err)); + return 0; + } + + return actFreq; + } + + unsigned int AlsaTimer::getTimerResolution() + { + return snd_timer_info_get_resolution(info); + } + + unsigned int AlsaTimer::getTimerFreq() + { + return (1000000000 / snd_timer_info_get_resolution(info)) / snd_timer_params_get_ticks(params); + } + + bool AlsaTimer::startTimer() + { + if(TIMER_DEBUG) + printf("AlsaTimer::startTimer(this=%p): handle=%p\n",this,handle); + int err; + if ((err = snd_timer_start(handle)) < 0) { + fprintf(stderr, "AlsaTimer::startTimer(): timer start %i (%s)\n", err, snd_strerror(err)); + return false; + } + return true; + } + + bool AlsaTimer::stopTimer() + { + int err; + if(TIMER_DEBUG) + printf("AlsaTimer::stopTimer(this=%p): handle=%p\n",this,handle); + if ((err = snd_timer_stop(handle)) < 0) { + fprintf(stderr, "AlsaTimer::stopTimer(): timer stop %i (%s)\n", err, snd_strerror(err)); + return false; + } + return true; + } + + unsigned int AlsaTimer::getTimerTicks(bool printTicks) + { + //if(TIMER_DEBUG) + // printf("AlsaTimer::getTimerTicks\n"); + snd_timer_read_t tr; + tr.ticks = 0; + while (snd_timer_read(handle, &tr, sizeof(tr)) == sizeof(tr)) { + if (printTicks) { + printf("TIMER: resolution = %uns, ticks = %u\n", + tr.resolution, tr.ticks); + } + } + return tr.ticks; + } diff --git a/muse2/muse/driver/alsatimer.h b/muse2/muse/driver/alsatimer.h new file mode 100644 index 00000000..211ba5ec --- /dev/null +++ b/muse2/muse/driver/alsatimer.h @@ -0,0 +1,52 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: alsatimer.h,v 1.1.2.4 2009/03/09 02:05:18 terminator356 Exp $ +// +// Plenty of code borrowed from timer.c example in +// alsalib 1.0.7 +// +// (C) Copyright 2004 Robert Jonsson (rj@spamatica.se) +//========================================================= + +#ifndef __ALSATIMER_H__ +#define __ALSATIMER_H__ + +#include "alsa/asoundlib.h" +#include "timerdev.h" + + +//--------------------------------------------------------- +// AlsaTimer +//--------------------------------------------------------- + +class AlsaTimer : public Timer{ + + snd_timer_t *handle; + snd_timer_id_t *id; + snd_timer_info_t *info; + snd_timer_params_t *params; + struct pollfd *fds; + char timername[64]; + signed int count; + unsigned int ticks; + bool findBest; + + public: + AlsaTimer(); + virtual ~AlsaTimer(); + + virtual signed int initTimer(); + virtual unsigned int setTimerResolution(unsigned int resolution); + virtual unsigned int getTimerResolution(); + virtual unsigned int setTimerFreq(unsigned int freq); + virtual unsigned int getTimerFreq(); + + virtual bool startTimer(); + virtual bool stopTimer(); + virtual unsigned int getTimerTicks(bool printTicks=false); + + void setFindBestTimer(bool b) { findBest = b; } +}; + +#endif //__ALSATIMER_H__ diff --git a/muse2/muse/driver/audiodev.h b/muse2/muse/driver/audiodev.h new file mode 100644 index 00000000..39ff822e --- /dev/null +++ b/muse2/muse/driver/audiodev.h @@ -0,0 +1,74 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: audiodev.h,v 1.5.2.2 2009/12/20 05:00:35 terminator356 Exp $ +// +// (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) +//========================================================= + +#ifndef __AUDIODEV_H__ +#define __AUDIODEV_H__ + +#include <qstring.h> +#include <list> + +class MidiPlayEvent; + +//--------------------------------------------------------- +// AudioDevice +//--------------------------------------------------------- + +class AudioDevice { + + public: + enum { DUMMY_AUDIO=0, JACK_AUDIO=1 }; // p3.3.52 + + AudioDevice() {} + virtual ~AudioDevice() {} + + virtual int deviceType() = 0; // p3.3.52 + + //virtual void start() = 0; + virtual void start(int priority) = 0; + + virtual void stop () = 0; + virtual int framePos() const = 0; + virtual unsigned frameTime() const = 0; + + virtual float* getBuffer(void* port, unsigned long nframes) = 0; + + virtual std::list<QString> outputPorts(bool midi = false, int aliases = -1) = 0; + virtual std::list<QString> inputPorts(bool midi = false, int aliases = -1) = 0; + + virtual void registerClient() = 0; + + virtual const char* clientName() = 0; + + //virtual void* registerOutPort(const char* name) = 0; + //virtual void* registerInPort(const char* name) = 0; + virtual void* registerOutPort(const char* /*name*/, bool /*midi*/) = 0; + virtual void* registerInPort(const char* /*name*/, bool /*midi*/) = 0; + + virtual void unregisterPort(void*) = 0; + virtual void connect(void*, void*) = 0; + virtual void disconnect(void*, void*) = 0; + virtual int connections(void* /*clientPort*/) = 0; + virtual void setPortName(void* p, const char* n) = 0; + virtual void* findPort(const char* name) = 0; + virtual QString portName(void* port) = 0; + virtual int getState() = 0; + virtual unsigned getCurFrame() = 0; + virtual bool isRealtime() = 0; + virtual int realtimePriority() const = 0; // return zero if not realtime + virtual void startTransport() = 0; + virtual void stopTransport() = 0; + virtual void seekTransport(unsigned frame) = 0; + virtual void seekTransport(const Pos &p) = 0; + virtual void setFreewheel(bool f) = 0; + virtual void graphChanged() {} + virtual void registrationChanged() {} + virtual int setMaster(bool f) = 0; + }; + +#endif + diff --git a/muse2/muse/driver/dummyaudio.cpp b/muse2/muse/driver/dummyaudio.cpp new file mode 100644 index 00000000..a17a99e7 --- /dev/null +++ b/muse2/muse/driver/dummyaudio.cpp @@ -0,0 +1,454 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: dummyaudio.cpp,v 1.3.2.16 2009/12/20 05:00:35 terminator356 Exp $ +// (C) Copyright 2002-2003 Werner Schweer (ws@seh.de) +//========================================================= + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <errno.h> +#include <stdarg.h> +#include <pthread.h> +#include <sys/poll.h> +#include <qmessagebox.h> + +#include "config.h" +#include "audio.h" +#include "audiodev.h" +#include "globals.h" +#include "song.h" +#include "driver/alsatimer.h" +#include "pos.h" +#include "gconfig.h" +#include "utils.h" + +class MidiPlayEvent; + +#define DEBUG_DUMMY 0 +//--------------------------------------------------------- +// DummyAudioDevice +//--------------------------------------------------------- + +//static const unsigned dummyFrames = 1024; + +enum Cmd { +trSeek, +trStart, +trStop +}; + +struct Msg { + enum Cmd cmd; + int arg; +}; + + +class DummyAudioDevice : public AudioDevice { + pthread_t dummyThread; + // Changed by Tim. p3.3.15 + //float buffer[1024]; + float* buffer; + int _realTimePriority; + + public: + std::list<Msg> cmdQueue; + Audio::State state; + int _framePos; + int playPos; + bool realtimeFlag; + + DummyAudioDevice(); + virtual ~DummyAudioDevice() + { + // Added by Tim. p3.3.15 + free(buffer); + } + + virtual inline int deviceType() { return DUMMY_AUDIO; } // p3.3.52 + + //virtual void start(); + virtual void start(int); + + virtual void stop (); + virtual int framePos() const { + if(DEBUG_DUMMY) + printf("DummyAudioDevice::framePos %d\n", _framePos); + return _framePos; + } + + virtual float* getBuffer(void* /*port*/, unsigned long nframes) + { + // p3.3.30 + //if (nframes > dummyFrames) { + //printf("error: segment size > 1024\n"); + if (nframes > segmentSize) { + printf("DummyAudioDevice::getBuffer nframes > segment size\n"); + + exit(-1); + } + return buffer; + } + + virtual std::list<QString> outputPorts(bool midi = false, int aliases = -1); + virtual std::list<QString> inputPorts(bool midi = false, int aliases = -1); + + virtual void registerClient() {} + + virtual const char* clientName() { return "MusE"; } + + //virtual void* registerOutPort(const char*) { + virtual void* registerOutPort(const char*, bool) { + return (void*)1; + } + //virtual void* registerInPort(const char*) { + virtual void* registerInPort(const char*, bool) { + return (void*)2; + } + virtual void unregisterPort(void*) {} + virtual void connect(void*, void*) {} + virtual void disconnect(void*, void*) {} + virtual int connections(void* /*clientPort*/) { return 0; } + virtual void setPortName(void*, const char*) {} + virtual void* findPort(const char*) { return 0;} + virtual QString portName(void*) { + return QString("mops"); + } + virtual int getState() { +// if(DEBUG_DUMMY) +// printf("DummyAudioDevice::getState %d\n", state); + + return state; } + virtual unsigned getCurFrame() { + if(DEBUG_DUMMY) + printf("DummyAudioDevice::getCurFrame %d\n", _framePos); + + return _framePos; } + virtual unsigned frameTime() const { + return lrint(curTime() * sampleRate); + } + virtual bool isRealtime() { return realtimeFlag; } + //virtual int realtimePriority() const { return 40; } + virtual int realtimePriority() const { return _realTimePriority; } + virtual void startTransport() { + if(DEBUG_DUMMY) + printf("DummyAudioDevice::startTransport playPos=%d\n", playPos); + Msg trcmd; + trcmd.cmd = trStart; + trcmd.arg = playPos; + cmdQueue.push_front(trcmd); +/* state = Audio::START_PLAY; + audio->sync(state, playPos); + state = Audio::PLAY;*/ + } + virtual void stopTransport() { + if(DEBUG_DUMMY) + printf("DummyAudioDevice::stopTransport, playPos=%d\n", playPos); + state = Audio::STOP; + } + virtual int setMaster(bool) { return 1; } + + virtual void seekTransport(const Pos &p) + { + if(DEBUG_DUMMY) + printf("DummyAudioDevice::seekTransport frame=%d topos=%d\n",playPos, p.frame()); + Msg trcmd; + trcmd.cmd = trSeek; + trcmd.arg = p.frame(); + cmdQueue.push_front(trcmd); + playPos = p.frame(); + } + virtual void seekTransport(unsigned pos) { + if(DEBUG_DUMMY) + printf("DummyAudioDevice::seekTransport frame=%d topos=%d\n",playPos,pos); + Msg trcmd; + trcmd.cmd = trSeek; + trcmd.arg = pos; + cmdQueue.push_front(trcmd); + playPos = pos; +/* + Audio::State tempState = state; + state = Audio::START_PLAY; + audio->sync(state, playPos); + state = tempState;*/ + } + virtual void setFreewheel(bool) {} + void setRealTime() { realtimeFlag = true; } + }; + +DummyAudioDevice* dummyAudio = 0; + +DummyAudioDevice::DummyAudioDevice() + { + // Added by Tim. p3.3.15 + // p3.3.30 + //posix_memalign((void**)&buffer, 16, sizeof(float) * dummyFrames); + posix_memalign((void**)&buffer, 16, sizeof(float) * config.dummyAudioBufSize); + + realtimeFlag = false; + state = Audio::STOP; + _framePos = 0; + playPos = 0; + cmdQueue.clear(); + } + +//--------------------------------------------------------- +// exitDummyAudio +//--------------------------------------------------------- + +void exitDummyAudio() +{ + if(dummyAudio) + delete dummyAudio; + dummyAudio = NULL; + audioDevice = NULL; +} + +//--------------------------------------------------------- +// initDummyAudio +//--------------------------------------------------------- + +bool initDummyAudio() + { + dummyAudio = new DummyAudioDevice(); + audioDevice = dummyAudio; + return false; + } + +//--------------------------------------------------------- +// outputPorts +//--------------------------------------------------------- + +std::list<QString> DummyAudioDevice::outputPorts(bool midi, int /*aliases*/) + { + std::list<QString> clientList; + if(!midi) + { + clientList.push_back(QString("output1")); + clientList.push_back(QString("output2")); + } + return clientList; + } + +//--------------------------------------------------------- +// inputPorts +//--------------------------------------------------------- + +std::list<QString> DummyAudioDevice::inputPorts(bool midi, int /*aliases*/) + { + std::list<QString> clientList; + if(!midi) + { + clientList.push_back(QString("input1")); + clientList.push_back(QString("input2")); + } + return clientList; + } + +//--------------------------------------------------------- +// dummyLoop +//--------------------------------------------------------- + +static void* dummyLoop(void* ptr) + { + //unsigned int tickRate = 25; + + // p3.3.30 + //sampleRate = 25600; + sampleRate = config.dummyAudioSampleRate; + //segmentSize = dummyFrames; + segmentSize = config.dummyAudioBufSize; + //unsigned int tickRate = sampleRate / dummyFrames; + unsigned int tickRate = sampleRate / segmentSize; + + AlsaTimer timer; + fprintf(stderr, "Get alsa timer for dummy driver:\n"); + timer.setFindBestTimer(false); + int fd = timer.initTimer(); + if (fd==-1) { + // QMessageBox::critical( 0, /*tr*/(QString("Failed to start timer for dummy audio driver!")), + // /*tr*/(QString("No functional timer was available.\n" + // "Alsa timer not available, check if module snd_timer is available and /dev/snd/timer is available"))); + fprintf(stderr, "Failed to start timer for dummy audio driver! No functional timer was available.\n" + "Alsa timer not available, check if module snd_timer is available and /dev/snd/timer is available\n"); + pthread_exit(0); + } + + /* Depending on nature of the timer, the requested tickRate might not + * be available. The return value is the nearest available frequency, + * so use this to reset our dummpy sampleRate to keep everything + * consistent. + */ + tickRate = timer.setTimerFreq( /*250*/ tickRate ); + + // p3.3.31 + // If it didn't work, get the actual rate. + if(tickRate == 0) + tickRate = timer.getTimerFreq(); + + sampleRate = tickRate * segmentSize; + timer.startTimer(); + + DummyAudioDevice *drvPtr = (DummyAudioDevice *)ptr; + + pollfd myPollFd; + + myPollFd.fd = fd; + myPollFd.events = POLLIN; + + /* + doSetuid(); + struct sched_param rt_param; + int rv; + memset(&rt_param, 0, sizeof(sched_param)); + int type; + rv = pthread_getschedparam(pthread_self(), &type, &rt_param); + if (rv != 0) + perror("get scheduler parameter"); + if (type != SCHED_FIFO) { + fprintf(stderr, "Driver thread not running SCHED_FIFO, trying to set...\n"); + + memset(&rt_param, 0, sizeof(sched_param)); + //rt_param.sched_priority = 1; + rt_param.sched_priority = realtimePriority(); + rv = pthread_setschedparam(pthread_self(), SCHED_FIFO, &rt_param); + if (rv != 0) + perror("set realtime scheduler"); + memset(&rt_param, 0, sizeof(sched_param)); + rv = pthread_getschedparam(pthread_self(), &type, &rt_param); + if (rv != 0) + perror("get scheduler parameter"); + if (type == SCHED_FIFO) { + drvPtr->setRealTime(); + fprintf(stderr, "Thread succesfully set to SCHED_FIFO\n"); + } + else { + fprintf(stderr, "Unable to set thread to SCHED_FIFO\n"); + } + } + undoSetuid(); + */ + +#ifndef __APPLE__ + doSetuid(); + //if (realTimePriority) { + if (realTimeScheduling) { + // + // check if we really got realtime priviledges + // + int policy; + if ((policy = sched_getscheduler (0)) < 0) { + printf("cannot get current client scheduler for audio dummy thread: %s!\n", strerror(errno)); + } + else + { + if (policy != SCHED_FIFO) + printf("audio dummy thread _NOT_ running SCHED_FIFO\n"); + else if (debugMsg) { + struct sched_param rt_param; + memset(&rt_param, 0, sizeof(sched_param)); + int type; + int rv = pthread_getschedparam(pthread_self(), &type, &rt_param); + if (rv == -1) + perror("get scheduler parameter"); + printf("audio dummy thread running SCHED_FIFO priority %d\n", + rt_param.sched_priority); + } + } + } + undoSetuid(); +#endif + + /* unsigned long tick = 0;*/ // prevent compiler warning: unused variable + for (;;) { + int _pollWait = 10; // ms + unsigned long count = 0; + while (count < 1 /*250/tickRate*/) // will loop until the next tick occurs + { + /*int n = */ poll(&myPollFd, 1 /* npfd */, _pollWait); + count += timer.getTimerTicks(); + while (drvPtr->cmdQueue.size()) + { + Msg &msg = drvPtr->cmdQueue.back(); + drvPtr->cmdQueue.pop_back(); + switch(msg.cmd) { + case trSeek: + { + //printf("trSeek\n"); + drvPtr->playPos = msg.arg; + Audio::State tempState = drvPtr->state; + drvPtr->state = Audio::START_PLAY; + audio->sync(drvPtr->state, msg.arg); + drvPtr->state = tempState; + } + break; + case trStart: + { + //printf("trStart\n"); + drvPtr->state = Audio::START_PLAY; + audio->sync(drvPtr->state, msg.arg); + drvPtr->state = Audio::PLAY; + } + break; + case trStop: + break; + default: + printf("dummyLoop: Unknown command!\n"); + } + } + } + audio->process(segmentSize); + int increment = segmentSize; // 1 //tickRate / sampleRate * segmentSize; + drvPtr->_framePos+=increment; + if (drvPtr->state == Audio::PLAY) + { + drvPtr->playPos+=increment; + } + } + timer.stopTimer(); + pthread_exit(0); + } + +//void DummyAudioDevice::start() +void DummyAudioDevice::start(int priority) + { + //realTimePriority = priority; + _realTimePriority = priority; + pthread_attr_t* attributes = 0; + + //if (priority) { + if (realTimeScheduling && priority > 0) { + attributes = (pthread_attr_t*) malloc(sizeof(pthread_attr_t)); + pthread_attr_init(attributes); + + if (pthread_attr_setschedpolicy(attributes, SCHED_FIFO)) { + printf("cannot set FIFO scheduling class for RT thread\n"); + } + if (pthread_attr_setscope (attributes, PTHREAD_SCOPE_SYSTEM)) { + printf("Cannot set scheduling scope for RT thread\n"); + } + struct sched_param rt_param; + memset(&rt_param, 0, sizeof(rt_param)); + rt_param.sched_priority = priority; + if (pthread_attr_setschedparam (attributes, &rt_param)) { + printf("Cannot set scheduling priority %d for RT thread (%s)\n", + priority, strerror(errno)); + } + } + + //pthread_attr_t* attributes = (pthread_attr_t*) malloc(sizeof(pthread_attr_t)); + //pthread_attr_init(attributes); + if (pthread_create(&dummyThread, attributes, ::dummyLoop, this)) + perror("creating thread failed:"); + if (priority) + pthread_attr_destroy(attributes); + } + +void DummyAudioDevice::stop () + { + pthread_cancel(dummyThread); + pthread_join(dummyThread, 0); + dummyThread = 0; + } + diff --git a/muse2/muse/driver/jack.cpp b/muse2/muse/driver/jack.cpp new file mode 100644 index 00000000..2c5081fc --- /dev/null +++ b/muse2/muse/driver/jack.cpp @@ -0,0 +1,2173 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: jack.cpp,v 1.30.2.17 2009/12/20 05:00:35 terminator356 Exp $ +// (C) Copyright 2002 Werner Schweer (ws@seh.de) +//========================================================= + +#include "config.h" +#include <string> +#include <set> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <stdarg.h> +//#include <time.h> +#include <unistd.h> +#include <jack/midiport.h> +#include <string.h> + +#include "audio.h" +#include "globals.h" +#include "song.h" +#include "jackaudio.h" +#include "track.h" +#include "pos.h" +#include "tempo.h" +#include "sync.h" +#include "utils.h" + +#include "midi.h" +#include "mididev.h" +#include "mpevent.h" + +#include "jackmidi.h" + + +#define JACK_DEBUG 0 + +//#include "errorhandler.h" + +#ifndef RTCAP +extern void doSetuid(); +extern void undoSetuid(); +#endif + +#ifdef VST_SUPPORT +#include <fst.h> +#endif + +//extern int jackmidi_pi[2]; +//extern int jackmidi_po[2]; + +//jack_port_t *midi_port_in[JACK_MIDI_CHANNELS]; +//jack_port_t *midi_port_out[JACK_MIDI_CHANNELS]; + +//muse_jack_midi_buffer jack_midi_out_data[JACK_MIDI_CHANNELS]; +//muse_jack_midi_buffer jack_midi_in_data[JACK_MIDI_CHANNELS]; + +JackAudioDevice* jackAudio; + +//--------------------------------------------------------- +// checkJackClient - make sure client is valid +//--------------------------------------------------------- +inline bool checkJackClient(jack_client_t* _client) + { + if (_client == NULL) { + printf("Panic! no _client!\n"); + return false; + } + return true; + } +//--------------------------------------------------------- +// checkAudioDevice - make sure audioDevice exists +//--------------------------------------------------------- +bool checkAudioDevice() + { + if (audioDevice == NULL) { + printf("Muse:checkAudioDevice: no audioDevice\n"); + return false; + } + return true; + } + + +//--------------------------------------------------------- +// jack_thread_init +//--------------------------------------------------------- + +static void jack_thread_init (void* ) // data + { + doSetuid(); + /* + if (jackAudio->isRealtime()) { + struct sched_param rt_param; + int rv; + memset(&rt_param, 0, sizeof(sched_param)); + int type; + rv = pthread_getschedparam(pthread_self(), &type, &rt_param); + if (rv != 0) + perror("get scheduler parameter"); + if (type != SCHED_FIFO) { + fprintf(stderr, "JACK thread not running SCHED_FIFO, try to set...\n"); + + memset(&rt_param, 0, sizeof(sched_param)); + rt_param.sched_priority = 1; + rv = pthread_setschedparam(pthread_self(), SCHED_FIFO, &rt_param); + if (rv != 0) + perror("set realtime scheduler"); + memset(&rt_param, 0, sizeof(sched_param)); + rv = pthread_getschedparam(pthread_self(), &type, &rt_param); + if (rv != 0) + perror("get scheduler parameter"); + if (type != SCHED_FIFO) + fprintf(stderr, "JACK still not running FIFO !?!\n" + "======reliable RT operation not possible!!======\n"); + else + fprintf(stderr, "JACK thread succesfully set to SCHED_FIFO\n"); + } + } + */ +#ifdef VST_SUPPORT + if (loadVST) + fst_adopt_thread(); +#endif + undoSetuid(); + } + +/* +//--------------------------------------------------------- +// processAudio + Midi +// JACK callback +//--------------------------------------------------------- +void +print_triplet(unsigned char *data) +{ + int a,b,c; + a = b = c = 0; + memcpy(&a, data, 1); + memcpy(&b, data+1, 1); + memcpy(&c, data+2, 1); + fprintf(stderr, "%x,%x,%x", a, b, c); +} +*/ + +/* +void handle_jack_midi_in_events(jack_nframes_t frames) +{ + char buf = 0; + int i,j; + jack_midi_event_t midi_event; + unsigned char t,n,v; + + for(j = 0; j < JACK_MIDI_CHANNELS; j++){ + void *midi_buffer_in = jack_port_get_buffer(midi_port_in[j], frames); + int event_count = jack_midi_get_event_count(midi_buffer_in); + + for(i = 0; i < event_count; i++){ + jack_midi_event_get(&midi_event, midi_buffer_in, i); + t = midi_event.buffer[0]; + n = midi_event.buffer[1]; + v = midi_event.buffer[2]; + if(((*(midi_event.buffer) & 0xf0)) == 0x90){ + fprintf(stderr, "jack-midi-in-event: ON_ time=%d %u ", midi_event.time, + midi_event.size); + print_triplet(midi_event.buffer); + fprintf(stderr, "\n"); + }else if(((*(midi_event.buffer)) & 0xf0) == 0x80){ + fprintf(stderr, "jack-midi-in-event: OFF time=%d %u ", midi_event.time, + midi_event.size); + print_triplet(midi_event.buffer); + fprintf(stderr, "\n"); + }else{ + fprintf(stderr, "jack-midi-in-event: ??? time=%d %u ", midi_event.time, + midi_event.size); + print_triplet(midi_event.buffer); + fprintf(stderr, "\n"); + } + jack_midi_in_data[j].buffer[0] = t; + jack_midi_in_data[j].buffer[1] = n; + jack_midi_in_data[j].buffer[2] = v; + jack_midi_in_data[j].buffer[3] = 1; + fprintf(stderr, "handle_jack_midi_in_events() w\n"); + write(jackmidi_pi[1], &buf, 1); + fprintf(stderr, "handle_jack_midi_in_events() wd\n"); + } + } +} + +void handle_jack_midi_out_events(jack_nframes_t frames) +{ + unsigned char *data; + void *port_buf; + int i,j,n,x; + + //for(i = 0; i < JACK_MIDI_CHANNELS; i++){ + for(i = 0; i < JACK_MIDI_CHANNELS; ++i){ + // jack-midi-clear any old events + while(jack_midi_out_data[i].buffer[jack_midi_out_data[i].take*4+3] == 2){ + port_buf = jack_port_get_buffer(midi_port_out[i], frames); + jack_midi_clear_buffer(port_buf); + jack_midi_out_data[i].buffer[jack_midi_out_data[i].take*4+3] = 0; + // point the take to the next slot + jack_midi_out_data[i].take++; + if(jack_midi_out_data[i].take >= JACK_MIDI_BUFFER_SIZE){ + jack_midi_out_data[i].take = 0; + } + } + // check if any incoming midi-events from muse + if(jack_midi_out_data[i].give != jack_midi_out_data[i].take){ + + if(jack_midi_out_data[i].give > jack_midi_out_data[i].take){ + n = jack_midi_out_data[i].give - jack_midi_out_data[i].take; + }else{ + n = jack_midi_out_data[i].give + + (JACK_MIDI_BUFFER_SIZE - jack_midi_out_data[i].take); + } + port_buf = jack_port_get_buffer(midi_port_out[i], frames); + jack_midi_clear_buffer(port_buf); + // FIX: midi events has different sizes, compare note-on to + // program-change. We should first walk over the events + // counting the size. + //data = jack_midi_event_reserve(port_buf, 0, n*3); + //x = jack_midi_out_data[i].take; + //for(j = 0; j < n; j++){ + // data[j*3+0] = jack_midi_out_data[i].buffer[x*4+0]; + // data[j*3+1] = jack_midi_out_data[i].buffer[x*4+1]; + // data[j*3+2] = jack_midi_out_data[i].buffer[x*4+2]; + // after having copied the buffer over to the jack-buffer, + // mark the muses midi-out buffer as 'need-cleaning' + // jack_midi_out_data[i].buffer[x*4+3] = 2; + // x++; + // if(x >= JACK_MIDI_BUFFER_SIZE){ + // x = 0; + // } + //} + + x = jack_midi_out_data[i].take; + for(j = 0; j < n; ++j) + { + data = jack_midi_event_reserve(port_buf, 0, 3); + if(data == 0) + { + fprintf(stderr, "handle_jack_midi_out_events: buffer overflow, event lost\n"); + // Can do no more processing. Just return. + return; + } + data[0] = jack_midi_out_data[i].buffer[x*4+0]; + data[1] = jack_midi_out_data[i].buffer[x*4+1]; + data[2] = jack_midi_out_data[i].buffer[x*4+2]; + // after having copied the buffer over to the jack-buffer, + // mark the muses midi-out buffer as 'need-cleaning' + jack_midi_out_data[i].buffer[x*4+3] = 2; + x++; + if(x >= JACK_MIDI_BUFFER_SIZE){ + x = 0; + } + } + + } + } +} +*/ + +//static int processAudio(jack_nframes_t frames, void*) +int JackAudioDevice::processAudio(jack_nframes_t frames, void*) +{ + jackAudio->_frameCounter += frames; + +/// handle_jack_midi_in_events(frames); +/// handle_jack_midi_out_events(frames); + +// if (JACK_DEBUG) +// printf("processAudio - >>>>\n"); + segmentSize = frames; + if (audio->isRunning()) + audio->process((unsigned long)frames); + else { + if (debugMsg) + puts("jack calling when audio is disconnected!\n"); + } +// if (JACK_DEBUG) +// printf("processAudio - <<<<\n"); + return 0; +} + +//--------------------------------------------------------- +// processSync +// return TRUE (non-zero) when ready to roll. +//--------------------------------------------------------- + +static int processSync(jack_transport_state_t state, jack_position_t* pos, void*) + { + if (JACK_DEBUG) + printf("processSync()\n"); + + if(!useJackTransport.value()) + return 1; + + int audioState = Audio::STOP; + switch (state) { + case JackTransportStopped: + audioState = Audio::STOP; + break; + case JackTransportLooping: + case JackTransportRolling: + audioState = Audio::PLAY; + break; + case JackTransportStarting: + //printf("processSync JackTransportStarting\n"); + + audioState = Audio::START_PLAY; + break; + //case JackTransportNetStarting: + // FIXME: Quick and dirty hack to support both Jack-1 and Jack-2 + // Really need a config check of version... + case 4: + //printf("processSync JackTransportNetStarting\n"); + + audioState = Audio::START_PLAY; + break; + } + + unsigned frame = pos->frame; + //printf("processSync valid:%d frame:%d\n", pos->valid, frame); + + // p3.3.23 + //printf("Jack processSync() before audio->sync frame:%d\n", frame); + //return audio->sync(audioState, frame); + int rv = audio->sync(audioState, frame); + //printf("Jack processSync() after audio->sync frame:%d\n", frame); + return rv; + } + +//--------------------------------------------------------- +// timebase_callback +//--------------------------------------------------------- + +static void timebase_callback(jack_transport_state_t /* state */, + jack_nframes_t /* nframes */, + jack_position_t* pos, + int /* new_pos */, + void*) + { + //printf("Jack timebase_callback pos->frame:%u audio->tickPos:%d song->cpos:%d\n", pos->frame, audio->tickPos(), song->cpos()); + + // p3.3.27 + //Pos p(pos->frame, false); + Pos p(extSyncFlag.value() ? audio->tickPos() : pos->frame, extSyncFlag.value() ? true : false); + // Can't use song pos - it is only updated every (slow) GUI heartbeat ! + //Pos p(extSyncFlag.value() ? song->cpos() : pos->frame, extSyncFlag.value() ? true : false); + + pos->valid = JackPositionBBT; + p.mbt(&pos->bar, &pos->beat, &pos->tick); + pos->bar++; + pos->beat++; + pos->bar_start_tick = Pos(pos->bar, 0, 0).tick(); + + // + // dummy: + // + + // p3.3.26 + //pos->beats_per_bar = 4; + //pos->beat_type = 4; + //pos->ticks_per_beat = 384; + // + /* // From example client transport.c : + float time_beats_per_bar = 4.0; + float time_beat_type = 0.25; // Huh? Inverted? From docs: "Time signature 'denominator'" + double time_ticks_per_beat = 1920.0; // Huh? Ticks per beat should be 24 etc. not 384 or 1920 etc. Otherwise it would be called 'frames_per_beat'. + double time_beats_per_minute = 120.0; + */ + // + int z, n; + sigmap.timesig(p.tick(), z, n); + pos->beats_per_bar = z; + pos->beat_type = n; + //pos->ticks_per_beat = config.division; + pos->ticks_per_beat = 24; + + int tempo = tempomap.tempo(p.tick()); + pos->beats_per_minute = (60000000.0 / tempo) * tempomap.globalTempo()/100.0; + } + +//--------------------------------------------------------- +// processShutdown +//--------------------------------------------------------- + +static void processShutdown(void*) + { + if (JACK_DEBUG) + printf("processShutdown()\n"); + //printf("processShutdown\n"); + jackAudio->nullify_client(); + audio->shutdown(); + + int c=0; + while(midiSeqRunning == true) { + if(c++ >10) { + fprintf(stderr, "sequencer still running, something is very wrong.\n"); + break; + } + sleep(1); + } + delete jackAudio; + jackAudio=0; + audioDevice=0; + } + +//--------------------------------------------------------- +// jackError +//--------------------------------------------------------- + +static void jackError(const char *s) + { + //error->logError( "JACK ERROR: %s\n", s); + fprintf(stderr,"JACK ERROR: %s\n", s); + } + +//--------------------------------------------------------- +// noJackError +//--------------------------------------------------------- + +static void noJackError(const char* /* s */) + { + } + +//--------------------------------------------------------- +// JackAudioDevice +//--------------------------------------------------------- + +JackAudioDevice::JackAudioDevice(jack_client_t* cl, char* name) + : AudioDevice() + { + _frameCounter = 0; + //JackAudioDevice::jackStarted=false; + strcpy(jackRegisteredName, name); + _client = cl; + dummyState = Audio::STOP; + dummyPos = 0; + } + +//--------------------------------------------------------- +// ~JackAudioDevice +//--------------------------------------------------------- + +JackAudioDevice::~JackAudioDevice() + { + if (JACK_DEBUG) + printf("~JackAudioDevice()\n"); + if (_client) { + + /* + // p3.3.35 + for(int i = 0; i < JACK_MIDI_CHANNELS; i++) + { + if(midi_port_in[i]) + jack_port_unregister(_client, midi_port_in[i]); + if(midi_port_out[i]) + jack_port_unregister(_client, midi_port_out[i]); + } + */ + + if (jack_client_close(_client)) { + //error->logError("jack_client_close() failed: %s\n", strerror(errno)); + fprintf(stderr,"jack_client_close() failed: %s\n", strerror(errno)); + } + } + if (JACK_DEBUG) + printf("~JackAudioDevice() after jack_client_close()\n"); + } + +//--------------------------------------------------------- +// realtimePriority +// return zero if not running realtime +// can only be called if JACK client thread is already +// running +//--------------------------------------------------------- + +int JackAudioDevice::realtimePriority() const + { + pthread_t t = jack_client_thread_id(_client); + int policy; + struct sched_param param; + memset(¶m, 0, sizeof(param)); + int rv = pthread_getschedparam(t, &policy, ¶m); + if (rv) { + perror("MusE: JackAudioDevice::realtimePriority: Error: Get jack schedule parameter"); + return 0; + } + if (policy != SCHED_FIFO) { + printf("MusE: JackAudioDevice::realtimePriority: JACK is not running realtime\n"); + return 0; + } + return param.sched_priority; + } + +/* +//--------------------------------------------------------- +// getJackName() +//--------------------------------------------------------- + +char* JackAudioDevice::getJackName() + { + return jackRegisteredName; + } +*/ + +/* +//--------------------------------------------------------- +// clientName() +//--------------------------------------------------------- + +const char* JackAudioDevice::clientName() +{ + //if(_client) + // return jack_get_client_name(_client); + //else + // return "MusE"; + return jackRegisteredName; +} +*/ + +//--------------------------------------------------------- +// initJackAudio +// return true if JACK not found +//--------------------------------------------------------- + +bool initJackAudio() + { + /* + // p3.3.35 + for(int i = 0; i < JACK_MIDI_CHANNELS; i++) + { + midi_port_in[i] = 0; + midi_port_out[i] = 0; + } + */ + + if (JACK_DEBUG) + printf("initJackAudio()\n"); + if (debugMsg) { + fprintf(stderr,"initJackAudio()\n"); + jack_set_error_function(jackError); + } + else + jack_set_error_function(noJackError); + doSetuid(); + + //jack_client_t* client = 0; + //int i = 0; + //char jackIdString[8]; + //for (i = 0; i < 5; ++i) { + // sprintf(jackIdString, "MusE-%d", i+1); + //client = jack_client_new(jackIdString); + // client = jack_client_open(jackIdString, JackNoStartServer, 0); + // if (client) + // break; + // } + //if (i == 5) + // return true; + jack_status_t status; + jack_client_t* client = jack_client_open("MusE", JackNoStartServer, &status); + if (!client) { + if (status & JackServerStarted) + printf("jack server started...\n"); + if (status & JackServerFailed) + printf("cannot connect to jack server\n"); + if (status & JackServerError) + printf("communication with jack server failed\n"); + if (status & JackShmFailure) + printf("jack cannot access shared memory\n"); + if (status & JackVersionError) + printf("jack server has wrong version\n"); + printf("cannot create jack client\n"); + undoSetuid(); // p3.3.51 + return true; + } + + if (debugMsg) + fprintf(stderr, "initJackAudio(): client %s opened.\n", jack_get_client_name(client)); + if (client) { + jack_set_error_function(jackError); + //jackAudio = new JackAudioDevice(client, jackIdString); + jackAudio = new JackAudioDevice(client, jack_get_client_name(client)); + if (debugMsg) + fprintf(stderr, "initJackAudio(): registering client...\n"); + jackAudio->registerClient(); + sampleRate = jack_get_sample_rate(client); + segmentSize = jack_get_buffer_size(client); + jack_set_thread_init_callback(client, (JackThreadInitCallback) jack_thread_init, 0); + //jack_set_timebase_callback(client, 0, (JackTimebaseCallback) timebase_callback, 0); + } + undoSetuid(); + + /* + // setup midi input/output + //memset(jack_midi_out_data, 0, JACK_MIDI_CHANNELS * sizeof(muse_jack_midi_buffer)); + //memset(jack_midi_in_data, 0, JACK_MIDI_CHANNELS * sizeof(muse_jack_midi_buffer)); + if(client){ + for(i = 0; i < JACK_MIDI_CHANNELS; i++) + { + char buf[80]; + snprintf(buf, 80, "muse-jack-midi-in-%d", i+1); + midi_port_in[i] = jack_port_register(client, buf, + JACK_DEFAULT_MIDI_TYPE, + JackPortIsInput, 0); + if(midi_port_in[i] == NULL){ + fprintf(stderr, "failed to register jack-midi-in\n"); + exit(-1); + } + snprintf(buf, 80, "muse-jack-midi-out-%d", i+1); + midi_port_out[i] = jack_port_register(client, buf, + JACK_DEFAULT_MIDI_TYPE, + JackPortIsOutput, 0); + if(midi_port_out == NULL) + { + fprintf(stderr, "failed to register jack-midi-out\n"); + exit(-1); + } + } + } + else + { + fprintf(stderr, "WARNING NO muse-jack midi connection\n"); + } + */ + + if (client) { + audioDevice = jackAudio; + jackAudio->scanMidiPorts(); + return false; + } + return true; + } + +static int bufsize_callback(jack_nframes_t n, void*) + { + printf("JACK: buffersize changed %d\n", n); + return 0; + } + +//--------------------------------------------------------- +// freewheel_callback +//--------------------------------------------------------- + +static void freewheel_callback(int starting, void*) + { + if (debugMsg || JACK_DEBUG) + printf("JACK: freewheel_callback: starting%d\n", starting); + audio->setFreewheel(starting); + } + +static int srate_callback(jack_nframes_t n, void*) + { + if (debugMsg || JACK_DEBUG) + printf("JACK: sample rate changed: %d\n", n); + return 0; + } + +//--------------------------------------------------------- +// registration_callback +//--------------------------------------------------------- + +static void registration_callback(jack_port_id_t, int, void*) +{ + if(debugMsg || JACK_DEBUG) + printf("JACK: registration changed\n"); + + audio->sendMsgToGui('R'); +} + +//--------------------------------------------------------- +// JackAudioDevice::registrationChanged +// this is called from song in gui context triggered +// by registration_callback() +//--------------------------------------------------------- + +void JackAudioDevice::registrationChanged() +{ + if(JACK_DEBUG) + printf("JackAudioDevice::registrationChanged()\n"); + + // Rescan. + scanMidiPorts(); + // Connect the Jack midi client ports to the device ports. + //connectJackMidiPorts(); +} + +//--------------------------------------------------------- +// JackAudioDevice::connectJackMidiPorts +//--------------------------------------------------------- + +void JackAudioDevice::connectJackMidiPorts() +{ + if(JACK_DEBUG) + printf("JackAudioDevice::connectJackMidiPorts()\n"); + + for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) + { + //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(*i); + //if(!mjd) + MidiDevice* md = *i; + if(md->deviceType() != MidiDevice::JACK_MIDI) + continue; + + void* port = md->clientPort(); + if(md->rwFlags() & 1) + { + RouteList* rl = md->outRoutes(); + for (iRoute r = rl->begin(); r != rl->end(); ++r) + connect(port, r->jackPort); + } + else + if(md->rwFlags() & 2) + { + RouteList* rl = md->inRoutes(); + for (iRoute r = rl->begin(); r != rl->end(); ++r) + connect(r->jackPort, port); + } + } + + + /* + const char* type = JACK_DEFAULT_MIDI_TYPE; + const char** ports = jack_get_ports(_client, 0, type, 0); + for (const char** p = ports; p && *p; ++p) + { + jack_port_t* port = jack_port_by_name(_client, *p); + if(!port) + continue; + // Ignore our own client ports. + if(jack_port_is_mine(_client, port)) + { + if(debugMsg) + printf(" ignoring own port: %s\n", *p); + continue; + } + int nsz = jack_port_name_size(); + char buffer[nsz]; + strncpy(buffer, *p, nsz); + // Ignore the MusE Jack port. + //if(strncmp(buffer, "MusE", 4) == 0) + // continue; + + if(debugMsg) + printf(" found port: %s ", buffer); + + // If there are aliases for this port, use the first one - much better for identifying. + //char a1[nsz]; + char a2[nsz]; + char* aliases[2]; + //aliases[0] = a1; + aliases[0] = buffer; + aliases[1] = a2; + // To disable aliases, just rem this line. + jack_port_get_aliases(port, aliases); + //int na = jack_port_get_aliases(port, aliases); + //char* namep = (na >= 1) ? aliases[0] : buffer; + char* namep = aliases[0]; + + if(debugMsg) + printf("alias: %s\n", aliases[0]); + + //int flags = 0; + int pf = jack_port_flags(port); + // If Jack port can send data to us... + //if(pf & JackPortIsOutput) + // Mark as input capable. + // flags |= 2; + // If Jack port can receive data from us... + //if(pf & JackPortIsInput) + // Mark as output capable. + // flags |= 1; + + //JackPort jp(0, QString(buffer), flags); + //portList.append(jp); + + QString name(namep); + + if(JACK_DEBUG) + printf("JackAudioDevice::graphChanged %s\n", name.latin1()); + + for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) + { + // Is it a Jack midi device? + MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(*imd); + if(!mjd) + continue; + + //if(dev->name() != name) + // continue; + + // Is this port the one created for the Jack midi device? + if(!mjd->clientJackPort() || (mjd->clientJackPort() != port)) + continue; + + jack_port_t* devport = jack_port_by_name(_client, mjd->name().latin1()); + if(!devport) + continue; + + int ofl = mjd->openFlags(); + + if(JACK_DEBUG) + printf("JackAudioDevice::graphChanged found MidiJackDevice:%s\n", mjd->name().latin1()); + + // Note docs say it can't be both input and output. src, dest + // If Jack port can receive data from us and we actually want to... + if((pf & JackPortIsOutput) && (ofl & 1)) + { + if(JACK_DEBUG) + printf("JackAudioDevice::graphChanged connecting MusE output\n"); + audioDevice->connect(port, devport); + } + else + // If Jack port can send data to us and we actually want it... + if((pf & JackPortIsInput) && (ofl & 2)) + { + if(JACK_DEBUG) + printf("JackAudioDevice::graphChanged connecting MusE input\n"); + audioDevice->connect(devport, port); + } + + break; + } + } + + if(ports) + free(ports); + + */ +} +//--------------------------------------------------------- +// client_registration_callback +//--------------------------------------------------------- + +static void client_registration_callback(const char *name, int isRegister, void*) + { + if (debugMsg || JACK_DEBUG) + printf("JACK: client registration changed:%s register:%d\n", name, isRegister); + } + +//--------------------------------------------------------- +// port_connect_callback +//--------------------------------------------------------- + +static void port_connect_callback(jack_port_id_t a, jack_port_id_t b, int isConnect, void*) + { + if (debugMsg || JACK_DEBUG) + { + //jack_port_t* ap = jack_port_by_id(_client, a); + //jack_port_t* bp = jack_port_by_id(_client, b); + //printf("JACK: port connections changed: A:%d:%s B:%d:%s isConnect:%d\n", a, jack_port_name(ap), b, jack_port_name(bp), isConnect); + printf("JACK: port connections changed: A:%d B:%d isConnect:%d\n", a, b, isConnect); + } + } + +//--------------------------------------------------------- +// graph_callback +// this is called from jack when the connections +// changed +//--------------------------------------------------------- + +static int graph_callback(void*) + { + if (JACK_DEBUG) + printf("graph_callback()\n"); + // we cannot call JackAudioDevice::graphChanged() from this + // context, so we send a message to the gui thread which in turn + // calls graphChanged() + audio->sendMsgToGui('C'); + if (debugMsg) + printf("JACK: graph changed\n"); + return 0; + } + +//--------------------------------------------------------- +// JackAudioDevice::graphChanged +// this is called from song in gui context triggered +// by graph_callback() +//--------------------------------------------------------- + +void JackAudioDevice::graphChanged() +{ + if (JACK_DEBUG) + printf("graphChanged()\n"); + if(!checkJackClient(_client)) return; + InputList* il = song->inputs(); + for (iAudioInput ii = il->begin(); ii != il->end(); ++ii) { + AudioInput* it = *ii; + int channels = it->channels(); + for (int channel = 0; channel < channels; ++channel) { + jack_port_t* port = (jack_port_t*)(it->jackPort(channel)); + if (port == 0) + continue; + const char** ports = jack_port_get_all_connections(_client, port); + RouteList* rl = it->inRoutes(); + + //--------------------------------------- + // check for disconnects + //--------------------------------------- + + bool erased; + // limit set to 20 iterations for disconnects, don't know how to make it go + // the "right" amount + for (int i = 0;i < 20;i++) { + erased = false; + for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + if (irl->channel != channel) + continue; + QString name = irl->name(); + const char* portName = name.latin1(); + //printf("portname=%s\n", portName); + bool found = false; + const char** pn = ports; + while (pn && *pn) { + if (strcmp(*pn, portName) == 0) { + found = true; + break; + } + ++pn; + } + if (!found) { + audio->msgRemoveRoute1( + //Route(portName, false, channel), + Route(portName, false, channel, Route::JACK_ROUTE), + Route(it, channel) + ); + erased = true; + break; + } + } + if (!erased) + break; + } + + //--------------------------------------- + // check for connects + //--------------------------------------- + + if (ports) { + const char** pn = ports; + while (*pn) { + bool found = false; + for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + if (irl->channel != channel) + continue; + QString name = irl->name(); + const char* portName = name.latin1(); + if (strcmp(*pn, portName) == 0) { + found = true; + break; + } + } + if (!found) { + audio->msgAddRoute1( + //Route(*pn, false, channel), + Route(*pn, false, channel, Route::JACK_ROUTE), + Route(it, channel) + ); + } + ++pn; + } + + // p3.3.37 + //delete ports; + free(ports); + + ports = NULL; + } + } + } + OutputList* ol = song->outputs(); + for (iAudioOutput ii = ol->begin(); ii != ol->end(); ++ii) { + AudioOutput* it = *ii; + int channels = it->channels(); + for (int channel = 0; channel < channels; ++channel) { + jack_port_t* port = (jack_port_t*)(it->jackPort(channel)); + if (port == 0) + continue; + const char** ports = jack_port_get_all_connections(_client, port); + RouteList* rl = it->outRoutes(); + + //--------------------------------------- + // check for disconnects + //--------------------------------------- + + bool erased; + // limit set to 20 iterations for disconnects, don't know how to make it go + // the "right" amount + for (int i = 0; i < 20 ; i++) { + erased = false; + for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + if (irl->channel != channel) + continue; + QString name = irl->name(); + const char* portName = name.latin1(); + bool found = false; + const char** pn = ports; + while (pn && *pn) { + if (strcmp(*pn, portName) == 0) { + found = true; + break; + } + ++pn; + } + if (!found) { + audio->msgRemoveRoute1( + Route(it, channel), + //Route(portName, false, channel) + Route(portName, false, channel, Route::JACK_ROUTE) + ); + erased = true; + break; + } + } + if (!erased) + break; + } + + //--------------------------------------- + // check for connects + //--------------------------------------- + + if (ports) { + const char** pn = ports; + while (*pn) { + bool found = false; + for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + if (irl->channel != channel) + continue; + QString name = irl->name(); + const char* portName = name.latin1(); + if (strcmp(*pn, portName) == 0) { + found = true; + break; + } + } + if (!found) { + audio->msgAddRoute1( + Route(it, channel), + //Route(*pn, false, channel) + Route(*pn, false, channel, Route::JACK_ROUTE) + ); + } + ++pn; + } + + // p3.3.37 + //delete ports; + free(ports); + + ports = NULL; + } + } + } + + for (iMidiDevice ii = midiDevices.begin(); ii != midiDevices.end(); ++ii) + { + MidiDevice* md = *ii; + if(md->deviceType() != MidiDevice::JACK_MIDI) + continue; + + //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(*ii); + //if(!mjd) + // continue; + //for (int channel = 0; channel < channels; ++channel) + //{ + jack_port_t* port = (jack_port_t*)md->clientPort(); + if (port == 0) + continue; + const char** ports = jack_port_get_all_connections(_client, port); + + //--------------------------------------- + // outputs + //--------------------------------------- + + if(md->rwFlags() & 1) // Writable + { + RouteList* rl = md->outRoutes(); + + //--------------------------------------- + // check for disconnects + //--------------------------------------- + + bool erased; + // limit set to 20 iterations for disconnects, don't know how to make it go + // the "right" amount + for (int i = 0; i < 20 ; i++) + { + erased = false; + for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + //if (irl->channel != channel) + // continue; + QString name = irl->name(); + const char* portName = name.latin1(); + bool found = false; + const char** pn = ports; + while (pn && *pn) { + if (strcmp(*pn, portName) == 0) { + found = true; + break; + } + ++pn; + } + if (!found) { + audio->msgRemoveRoute1( + //Route(it, channel), + //Route(mjd), + Route(md, -1), + //Route(portName, false, channel) + //Route(portName, false, -1) + Route(portName, false, -1, Route::JACK_ROUTE) + ); + erased = true; + break; + } + } + if (!erased) + break; + } + + //--------------------------------------- + // check for connects + //--------------------------------------- + + if (ports) + { + const char** pn = ports; + while (*pn) { + bool found = false; + for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + //if (irl->channel != channel) + // continue; + QString name = irl->name(); + const char* portName = name.latin1(); + if (strcmp(*pn, portName) == 0) { + found = true; + break; + } + } + if (!found) { + audio->msgAddRoute1( + //Route(it, channel), + //Route(mjd), + Route(md, -1), + //Route(*pn, false, channel) + //Route(*pn, false, -1) + Route(*pn, false, -1, Route::JACK_ROUTE) + ); + } + ++pn; + } + + // p3.3.37 + //delete ports; + //free(ports); + + //ports = NULL; + } + } + + + //------------------------ + // Inputs + //------------------------ + + if(md->rwFlags() & 2) // Readable + { + RouteList* rl = md->inRoutes(); + + //--------------------------------------- + // check for disconnects + //--------------------------------------- + + bool erased; + // limit set to 20 iterations for disconnects, don't know how to make it go + // the "right" amount + for (int i = 0; i < 20 ; i++) + { + erased = false; + for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + //if (irl->channel != channel) + // continue; + QString name = irl->name(); + const char* portName = name.latin1(); + bool found = false; + const char** pn = ports; + while (pn && *pn) { + if (strcmp(*pn, portName) == 0) { + found = true; + break; + } + ++pn; + } + if (!found) { + audio->msgRemoveRoute1( + //Route(portName, false, channel), + //Route(portName, false, -1), + Route(portName, false, -1, Route::JACK_ROUTE), + //Route(it, channel) + //Route(mjd) + Route(md, -1) + ); + erased = true; + break; + } + } + if (!erased) + break; + } + + //--------------------------------------- + // check for connects + //--------------------------------------- + + if (ports) + { + const char** pn = ports; + while (*pn) { + bool found = false; + for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + //if (irl->channel != channel) + // continue; + QString name = irl->name(); + const char* portName = name.latin1(); + if (strcmp(*pn, portName) == 0) { + found = true; + break; + } + } + if (!found) { + audio->msgAddRoute1( + //Route(*pn, false, channel), + //Route(*pn, false, -1), + Route(*pn, false, -1, Route::JACK_ROUTE), + //Route(it, channel) + //Route(mjd) + Route(md, -1) + ); + } + ++pn; + } + } + } + if(ports) + // Done with ports. Free them. + //delete ports; + free(ports); + + ports = NULL; + } +} + +//static int xrun_callback(void*) +// { +// printf("JACK: xrun\n"); +// return 0; +// } + +//--------------------------------------------------------- +// register +//--------------------------------------------------------- + +void JackAudioDevice::registerClient() + { + if (JACK_DEBUG) + printf("registerClient()\n"); + if(!checkJackClient(_client)) return; + jack_set_process_callback(_client, processAudio, 0); + jack_set_sync_callback(_client, processSync, 0); + // FIXME: FIXME: + // Added by Tim. p3.3.20 + // Did not help. Seek during play: Jack keeps switching to STOP state after about 1-2 seconds timeout if sync is holding it up. + // Nothing in MusE seems to be telling it to stop. + jack_set_sync_timeout(_client, 5000000); // Change default 2 to 5 second sync timeout because prefetch may be very slow esp. with resampling ! + + jack_on_shutdown(_client, processShutdown, 0); + jack_set_buffer_size_callback(_client, bufsize_callback, 0); + jack_set_sample_rate_callback(_client, srate_callback, 0); + jack_set_port_registration_callback(_client, registration_callback, 0); + // p3.3.37 + jack_set_client_registration_callback(_client, client_registration_callback, 0); + jack_set_port_connect_callback(_client, port_connect_callback, 0); + + jack_set_graph_order_callback(_client, graph_callback, 0); +// jack_set_xrun_callback(client, xrun_callback, 0); + jack_set_freewheel_callback (_client, freewheel_callback, 0); + } + +//--------------------------------------------------------- +// registerInPort +//--------------------------------------------------------- + +//void* JackAudioDevice::registerInPort(const char* name) +void* JackAudioDevice::registerInPort(const char* name, bool midi) + { + if (JACK_DEBUG) + printf("registerInPort()\n"); + if(!checkJackClient(_client)) return NULL; + const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; + //void* p = jack_port_register(_client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + void* p = jack_port_register(_client, name, type, JackPortIsInput, 0); +// printf("JACK: registerInPort: <%s> %p\n", name, p); + return p; + } + +//--------------------------------------------------------- +// registerOutPort +//--------------------------------------------------------- + +//void* JackAudioDevice::registerOutPort(const char* name) +void* JackAudioDevice::registerOutPort(const char* name, bool midi) + { + if (JACK_DEBUG) + printf("registerOutPort()\n"); + if(!checkJackClient(_client)) return NULL; + const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; + //void* p = jack_port_register(_client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + void* p = jack_port_register(_client, name, type, JackPortIsOutput, 0); +// printf("JACK: registerOutPort: <%s> %p\n", name, p); + return p; + } + +//--------------------------------------------------------- +// exitJackAudio +//--------------------------------------------------------- + +void exitJackAudio() + { + if (JACK_DEBUG) + printf("exitJackAudio()\n"); + if (jackAudio) + delete jackAudio; + + if (JACK_DEBUG) + printf("exitJackAudio() after delete jackAudio\n"); + + // Added by Tim. p3.3.14 + audioDevice = NULL; + + } + +//--------------------------------------------------------- +// connect +//--------------------------------------------------------- + +void JackAudioDevice::connect(void* src, void* dst) +{ + if (JACK_DEBUG) + printf("JackAudioDevice::connect()\n"); + if(!checkJackClient(_client)) return; + const char* sn = jack_port_name((jack_port_t*) src); + const char* dn = jack_port_name((jack_port_t*) dst); + if (sn == 0 || dn == 0) { + fprintf(stderr, "JackAudio::connect: unknown jack ports\n"); + return; + } + int err = jack_connect(_client, sn, dn); + //if (jack_connect(_client, sn, dn)) { + if (err) { + fprintf(stderr, "jack connect <%s>%p - <%s>%p failed with err:%d\n", + sn, src, dn, dst, err); + } + else + if (JACK_DEBUG) + { + fprintf(stderr, "jack connect <%s>%p - <%s>%p succeeded\n", + sn, src, dn, dst); + } +} + +//--------------------------------------------------------- +// disconnect +//--------------------------------------------------------- + +void JackAudioDevice::disconnect(void* src, void* dst) +{ + if (JACK_DEBUG) + printf("JackAudioDevice::disconnect()\n"); + if(!checkJackClient(_client)) return; + const char* sn = jack_port_name((jack_port_t*) src); + const char* dn = jack_port_name((jack_port_t*) dst); + if (sn == 0 || dn == 0) { + fprintf(stderr, "JackAudio::disconnect: unknown jack ports\n"); + return; + } + int err = jack_disconnect(_client, sn, dn); + //if (jack_disconnect(_client, sn, dn)) { + if (err) { + fprintf(stderr, "jack disconnect <%s> - <%s> failed with err:%d\n", + sn, dn, err); + } + else + if (JACK_DEBUG) + { + fprintf(stderr, "jack disconnect <%s> - <%s> succeeded\n", + sn, dn); + } +} + +//--------------------------------------------------------- +// start +//--------------------------------------------------------- + +//void JackAudioDevice::start() +void JackAudioDevice::start(int /*priority*/) + { + if (JACK_DEBUG) + printf("JackAudioDevice::start()\n"); + if(!checkJackClient(_client)) return; + + doSetuid(); + + if (jack_activate(_client)) { + undoSetuid(); // p3.3.51 + fprintf (stderr, "JACK: cannot activate client\n"); + exit(-1); + } + /* connect the ports. Note: you can't do this before + the client is activated, because we can't allow + connections to be made to clients that aren't + running. + */ + + InputList* il = song->inputs(); + for (iAudioInput i = il->begin(); i != il->end(); ++i) { + AudioInput* ai = *i; + int channel = ai->channels(); + for (int ch = 0; ch < channel; ++ch) { + RouteList* rl = ai->inRoutes(); + void* port = ai->jackPort(ch); + for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { + if (ir->channel == ch) + connect(ir->jackPort, port); + } + } + } + OutputList* ol = song->outputs(); + for (iAudioOutput i = ol->begin(); i != ol->end(); ++i) { + AudioOutput* ai = *i; + int channel = ai->channels(); + for (int ch = 0; ch < channel; ++ch) { + RouteList* rl = ai->outRoutes(); + void* port = ai->jackPort(ch); + for (iRoute r = rl->begin(); r != rl->end(); ++r) { + if (r->channel == ch) { + connect(port, r->jackPort); + } + } + } + } + + // p3.3.37 + // Connect the Jack midi client ports to device ports. + connectJackMidiPorts(); + + undoSetuid(); + + //MUSE_DEBUG("JackAudioDevice::start()\n"); + fflush(stdin); + //JackAudioDevice::jackStarted=true; + } + +//--------------------------------------------------------- +// stop +//--------------------------------------------------------- + +void JackAudioDevice::stop() + { + if (JACK_DEBUG) + printf("JackAudioDevice::stop()\n"); + if(!checkJackClient(_client)) return; + if (jack_deactivate(_client)) { + fprintf (stderr, "cannot deactivate client\n"); + } + //JackAudioDevice::jackStarted=false; + } + +//--------------------------------------------------------- +// transportQuery +//--------------------------------------------------------- + +jack_transport_state_t JackAudioDevice::transportQuery(jack_position_t* pos) +{ + if (JACK_DEBUG) + printf("JackAudioDevice::transportQuery pos:%d\n", (unsigned int)pos->frame); + + // TODO: Compose and return a state if MusE is disengaged from Jack transport. + + return jack_transport_query(_client, pos); +} + +//--------------------------------------------------------- +// getCurFrame +//--------------------------------------------------------- + +unsigned int JackAudioDevice::getCurFrame() +{ + if (JACK_DEBUG) + printf("JackAudioDevice::getCurFrame pos.frame:%d\n", pos.frame); + + if(!useJackTransport.value()) + return (unsigned int)dummyPos; + + return pos.frame; +} + +//--------------------------------------------------------- +// framePos +//--------------------------------------------------------- + +int JackAudioDevice::framePos() const + { + //if(!useJackTransport.value()) + //{ + // if (JACK_DEBUG) + // printf("JackAudioDevice::framePos dummyPos:%d\n", dummyPos); + // return dummyPos; + //} + + if(!checkJackClient(_client)) return 0; + jack_nframes_t n = jack_frame_time(_client); + + //if (JACK_DEBUG) + // printf("JackAudioDevice::framePos jack frame:%d\n", (int)n); + + return (int)n; + } + +#if 0 +//--------------------------------------------------------- +// framesSinceCycleStart +//--------------------------------------------------------- + +int JackAudioDevice::framesSinceCycleStart() const + { + jack_nframes_t n = jack_frames_since_cycle_start(client); + return (int)n; + } + +//--------------------------------------------------------- +// framesDelay +// TODO +//--------------------------------------------------------- + +int JackAudioDevice::frameDelay() const + { + jack_nframes_t n = (segmentSize * (segmentCount-1)) - jack_frames_since_cycle_start(client); + return (int)n; + } +#endif + +//--------------------------------------------------------- +// outputPorts +//--------------------------------------------------------- + +std::list<QString> JackAudioDevice::outputPorts(bool midi, int aliases) + { + if (JACK_DEBUG) + printf("JackAudioDevice::outputPorts()\n"); + std::list<QString> clientList; + if(!checkJackClient(_client)) return clientList; + QString qname; + const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; + const char** ports = jack_get_ports(_client, 0, type, JackPortIsOutput); + for (const char** p = ports; p && *p; ++p) { + jack_port_t* port = jack_port_by_name(_client, *p); + //int flags = jack_port_flags(port); + //if (!(flags & JackPortIsOutput)) + // continue; + //char buffer[128]; + + int nsz = jack_port_name_size(); + char buffer[nsz]; + + strncpy(buffer, *p, nsz); + //if (strncmp(buffer, "MusE", 4) == 0) + //{ + // if(debugMsg) + // printf("JackAudioDevice::outputPorts ignoring own MusE port: %s\n", *p); + // continue; + //} + + // Ignore our own client ports. + if(jack_port_is_mine(_client, port)) + { + if(debugMsg) + printf("JackAudioDevice::outputPorts ignoring own port: %s\n", *p); + continue; + } + + // p3.3.38 + if((aliases == 0) || (aliases == 1)) + { + //char a1[nsz]; + char a2[nsz]; + char* al[2]; + //aliases[0] = a1; + al[0] = buffer; + al[1] = a2; + int na = jack_port_get_aliases(port, al); + int a = aliases; + if(a >= na) + { + a = na; + if(a > 0) + a--; + } + qname = QString(al[a]); + } + else + qname = QString(buffer); + + //clientList.push_back(QString(buffer)); + clientList.push_back(qname); + } + + // p3.3.37 + if(ports) + free(ports); + + return clientList; + } + +//--------------------------------------------------------- +// inputPorts +//--------------------------------------------------------- + +std::list<QString> JackAudioDevice::inputPorts(bool midi, int aliases) + { + if (JACK_DEBUG) + printf("JackAudioDevice::inputPorts()\n"); + std::list<QString> clientList; + if(!checkJackClient(_client)) return clientList; + QString qname; + const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; + const char** ports = jack_get_ports(_client, 0, type, JackPortIsInput); + for (const char** p = ports; p && *p; ++p) { + jack_port_t* port = jack_port_by_name(_client, *p); + //int flags = jack_port_flags(port); + //if (!(flags & JackPortIsInput)) + // continue; + //char buffer[128]; + + int nsz = jack_port_name_size(); + char buffer[nsz]; + + strncpy(buffer, *p, nsz); + //if (strncmp(buffer, "MusE", 4) == 0) + //{ + // if(debugMsg) + // printf("JackAudioDevice::inputPorts ignoring own MusE port: %s\n", *p); + // continue; + //} + + // Ignore our own client ports. + if(jack_port_is_mine(_client, port)) + { + if(debugMsg) + printf("JackAudioDevice::inputPorts ignoring own port: %s\n", *p); + continue; + } + + // p3.3.38 + if((aliases == 0) || (aliases == 1)) + { + //char a1[nsz]; + char a2[nsz]; + char* al[2]; + //aliases[0] = a1; + al[0] = buffer; + al[1] = a2; + int na = jack_port_get_aliases(port, al); + int a = aliases; + if(a >= na) + { + a = na; + if(a > 0) + a--; + } + qname = QString(al[a]); + } + else + qname = QString(buffer); + + //clientList.push_back(QString(buffer)); + clientList.push_back(qname); + } + + // p3.3.37 + if(ports) + free(ports); + + return clientList; + } + +//--------------------------------------------------------- +// portName +//--------------------------------------------------------- + +QString JackAudioDevice::portName(void* port) + { + if (JACK_DEBUG) + printf("JackAudioDevice::portName(\n"); + if(!checkJackClient(_client)) return ""; + if (!port) + return ""; + + QString s(jack_port_name((jack_port_t*)port)); + //printf("Jack::portName %p %s\n", port, s.latin1()); + return s; + } + +//--------------------------------------------------------- +// unregisterPort +//--------------------------------------------------------- + +void JackAudioDevice::unregisterPort(void* p) + { + if (JACK_DEBUG) + printf("JackAudioDevice::unregisterPort(\n"); + if(!checkJackClient(_client)) return; +// printf("JACK: unregister Port\n"); + jack_port_unregister(_client, (jack_port_t*)p); + } + +//--------------------------------------------------------- +// getState +//--------------------------------------------------------- + +int JackAudioDevice::getState() + { + // If we're not using Jack's transport, just return current state. + if(!useJackTransport.value()) + { + //pos.valid = jack_position_bits_t(0); + //pos.frame = audio->pos().frame(); + //return audio->getState(); + //if (JACK_DEBUG) + // printf("JackAudioDevice::getState dummyState:%d\n", dummyState); + return dummyState; + } + + //if (JACK_DEBUG) + // printf("JackAudioDevice::getState ()\n"); + if(!checkJackClient(_client)) return 0; + transportState = jack_transport_query(_client, &pos); + //if (JACK_DEBUG) + // printf("JackAudioDevice::getState transportState:%d\n", transportState); + + switch (transportState) { + case JackTransportStopped: + return Audio::STOP; + case JackTransportLooping: + case JackTransportRolling: + return Audio::PLAY; + case JackTransportStarting: + //printf("JackAudioDevice::getState JackTransportStarting\n"); + + return Audio::START_PLAY; + //case JackTransportNetStarting: + // FIXME: Quick and dirty hack to support both Jack-1 and Jack-2 + // Really need a config check of version... + case 4: + //printf("JackAudioDevice::getState JackTransportNetStarting\n"); + + return Audio::START_PLAY; + break; + default: + return Audio::STOP; + } + } + +//--------------------------------------------------------- +// setFreewheel +//--------------------------------------------------------- + +void JackAudioDevice::setFreewheel(bool f) + { + if (JACK_DEBUG) + printf("JackAudioDevice::setFreewheel(\n"); + if(!checkJackClient(_client)) return; +// printf("JACK: setFreewheel %d\n", f); + jack_set_freewheel(_client, f); + } + +//--------------------------------------------------------- +// dummySync +//--------------------------------------------------------- + +bool JackAudioDevice::dummySync(int state) +{ + // Roughly segment time length. + //timespec ts = { 0, (1000000000 * segmentSize) / sampleRate }; // In nanoseconds. + unsigned int sl = (1000000 * segmentSize) / sampleRate; // In microseconds. + + double ct = curTime(); + // Wait for a default maximum of 5 seconds. + // Similar to how Jack is supposed to wait a default of 2 seconds for slow clients. + // TODO: Make this timeout a 'settings' option so it can be applied both to Jack and here. + while((curTime() - ct) < 5.0) + { + // Is MusE audio ready to roll? + if(audio->sync(state, dummyPos)) + return true; + + // Not ready. Wait a 'segment', try again... + //nanosleep(&ts, NULL); + usleep(sl); // usleep is supposed to be obsolete! + } + + //if(JACK_DEBUG) + printf("JackAudioDevice::dummySync Sync timeout - audio not ready!\n"); + + return false; +} + +//--------------------------------------------------------- +// startTransport +//--------------------------------------------------------- + +void JackAudioDevice::startTransport() + { + if (JACK_DEBUG) + printf("JackAudioDevice::startTransport()\n"); + + // If we're not using Jack's transport, just pass PLAY and current frame along + // as if processSync was called. + if(!useJackTransport.value()) + { + //dummyState = Audio::START_PLAY; + + // Is MusE audio ready to roll? + //if(dummySync(dummyState)) + if(dummySync(Audio::START_PLAY)) + { + // MusE audio is ready to roll. Let's play. + dummyState = Audio::PLAY; + return; + } + + // Ready or not, we gotta roll. Similar to how Jack is supposed to roll anyway. + dummyState = Audio::PLAY; + return; + } + + if(!checkJackClient(_client)) return; +// printf("JACK: startTransport\n"); + jack_transport_start(_client); + } + +//--------------------------------------------------------- +// stopTransport +//--------------------------------------------------------- + +void JackAudioDevice::stopTransport() + { + if (JACK_DEBUG) + printf("JackAudioDevice::stopTransport()\n"); + + dummyState = Audio::STOP; + + if(!useJackTransport.value()) + { + //dummyState = Audio::STOP; + return; + } + + if(!checkJackClient(_client)) return; + if (transportState != JackTransportStopped) { + // printf("JACK: stopTransport\n"); + jack_transport_stop(_client); + transportState=JackTransportStopped; + } + } + +//--------------------------------------------------------- +// seekTransport +//--------------------------------------------------------- + +void JackAudioDevice::seekTransport(unsigned frame) + { + if (JACK_DEBUG) + printf("JackAudioDevice::seekTransport() frame:%d\n", frame); + + dummyPos = frame; + if(!useJackTransport.value()) + { + // If we're not using Jack's transport, just pass the current state and new frame along + // as if processSync was called. + //dummyPos = frame; + int tempState = dummyState; + //dummyState = Audio::START_PLAY; + + // Is MusE audio ready yet? + //audio->sync(dummyState, dummyPos); + //if(dummySync(dummyState)) + if(dummySync(Audio::START_PLAY)) + { + dummyState = tempState; + return; + } + + // Not ready, resume previous state anyway. + // FIXME: Observed: Seek during play: Jack transport STOPs on timeout. + // Docs say when starting play, transport will roll anyway, ready or not (observed), + // but don't mention what should happen on seek during play. + // And setting the slow-sync timeout doesn't seem to do anything! + //dummyState = tempState; + dummyState = Audio::STOP; + return; + } + + if(!checkJackClient(_client)) return; +// printf("JACK: seekTransport %d\n", frame); + jack_transport_locate(_client, frame); + } + +//--------------------------------------------------------- +// seekTransport +//--------------------------------------------------------- + +void JackAudioDevice::seekTransport(const Pos &p) + { + if (JACK_DEBUG) + printf("JackAudioDevice::seekTransport() frame:%d\n", p.frame()); + + dummyPos = p.frame(); + if(!useJackTransport.value()) + { + // If we're not using Jack's transport, just pass the current state and new frame along + // as if processSync was called. + //dummyPos = p.frame(); + int tempState = dummyState; + //dummyState = Audio::START_PLAY; + + // Is MusE audio ready yet? + //audio->sync(dummyState, dummyPos); + //if(dummySync(dummyState)) + if(dummySync(Audio::START_PLAY)) + { + dummyState = tempState; + return; + } + + // Not ready, resume previous state anyway. + // FIXME: See fixme in other seekTransport... + //dummyState = tempState; + dummyState = Audio::STOP; + return; + } + + if(!checkJackClient(_client)) return; + + /* + jack_position_t jp; + jp.valid = JackPositionBBT; + p.mbt(&jp.bar, &jp.beat, &jp.tick); + jp.bar++; + jp.beat++; + jp.bar_start_tick = Pos(jp.bar, 0, 0).tick(); + // + // dummy: + // + jp.beats_per_bar = 4; + jp.beat_type = 4; + jp.ticks_per_beat = 384; + int tempo = tempomap.tempo(p.tick()); + jp.beats_per_minute = (60000000.0 / tempo) * tempomap.globalTempo()/100.0; + + jack_transport_reposition(_client, &jp); + */ + jack_transport_locate(_client, p.frame()); + } + +//--------------------------------------------------------- +// findPort +//--------------------------------------------------------- + +void* JackAudioDevice::findPort(const char* name) + { + if (JACK_DEBUG) + printf("JackAudioDevice::findPort(\n"); + if(!checkJackClient(_client)) return NULL; + void* p = jack_port_by_name(_client, name); +// printf("Jack::findPort <%s>, %p\n", name, p); + return p; + } + +//--------------------------------------------------------- +// setMaster +//--------------------------------------------------------- + +int JackAudioDevice::setMaster(bool f) +{ + if (JACK_DEBUG) + printf("JackAudioDevice::setMaster val:%d\n", f); + if(!checkJackClient(_client)) + return 0; + + int r = 0; + if(f) + { + if(useJackTransport.value()) + { + // Make Muse the Jack timebase master. Do it unconditionally (second param = 0). + r = jack_set_timebase_callback(_client, 0, (JackTimebaseCallback) timebase_callback, 0); + if(debugMsg || JACK_DEBUG) + { + if(r) + printf("JackAudioDevice::setMaster jack_set_timebase_callback failed: result:%d\n", r); + } + } + else + { + r = 1; + printf("JackAudioDevice::setMaster cannot set master because useJackTransport is false\n"); + } + } + else + { + r = jack_release_timebase(_client); + if(debugMsg || JACK_DEBUG) + { + if(r) + printf("JackAudioDevice::setMaster jack_release_timebase failed: result:%d\n", r); + } + } + return r; +} + +//--------------------------------------------------------- +// scanMidiPorts +//--------------------------------------------------------- + +void JackAudioDevice::scanMidiPorts() +{ + if(debugMsg) + printf("JackAudioDevice::scanMidiPorts:\n"); + +/* + const char* type = JACK_DEFAULT_MIDI_TYPE; + const char** ports = jack_get_ports(_client, 0, type, 0); + + std::set<std::string> names; + for (const char** p = ports; p && *p; ++p) + { + jack_port_t* port = jack_port_by_name(_client, *p); + if(!port) + continue; + // Ignore our own client ports. + if(jack_port_is_mine(_client, port)) + { + if(debugMsg) + printf(" ignoring own port: %s\n", *p); + continue; + } + + int nsz = jack_port_name_size(); + char buffer[nsz]; + strncpy(buffer, *p, nsz); + // Ignore the MusE Jack port. + //if(strncmp(buffer, "MusE", 4) == 0) + // continue; + + if(debugMsg) + printf(" found port: %s ", buffer); + + // If there are aliases for this port, use the first one - much better for identifying. + //char a1[nsz]; + char a2[nsz]; + char* aliases[2]; + //aliases[0] = a1; + aliases[0] = buffer; + aliases[1] = a2; + // To disable aliases, just rem this line. + jack_port_get_aliases(port, aliases); + //int na = jack_port_get_aliases(port, aliases); + //char* namep = (na >= 1) ? aliases[0] : buffer; + //char* namep = aliases[0]; + //names.insert(std::string(*p)); + if(debugMsg) + printf("alias: %s\n", aliases[0]); + + names.insert(std::string(aliases[0])); + } + if(ports) + free(ports); + + std::list<MidiDevice*> to_del; + for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) + { + // Only Jack midi devices. + if(dynamic_cast<MidiJackDevice*>(*imd) == 0) + continue; + if(names.find(std::string((*imd)->name().latin1())) == names.end()) + to_del.push_back(*imd); + } + + for(std::list<MidiDevice*>::iterator imd = to_del.begin(); imd != to_del.end(); ++imd) + { + if(debugMsg) + printf(" removing port device:%s\n", (*imd)->name().latin1()); + midiDevices.remove(*imd); + // This will close (and unregister) the client port. + delete (*imd); + } + + //for (const char** p = ports; p && *p; ++p) + for(std::set<std::string>::iterator is = names.begin(); is != names.end(); ++is) + { + //jack_port_t* port = jack_port_by_name(_client, *p); + jack_port_t* port = jack_port_by_name(_client, is->c_str()); + if(!port) + continue; +*/ + + /* + int nsz = jack_port_name_size(); + char buffer[nsz]; + //strncpy(buffer, *p, nsz); + strncpy(buffer, is->c_str(), nsz); + // Ignore the MusE Jack port. + //if(strncmp(buffer, "MusE", 4) == 0) + // continue; + + // If there are aliases for this port, use the first one - much better for identifying. + //char a1[nsz]; + char a2[nsz]; + char* aliases[2]; + //aliases[0] = a1; + aliases[0] = buffer; + aliases[1] = a2; + // To disable aliases, just rem this line. + jack_port_get_aliases(port, aliases); + //int na = jack_port_get_aliases(port, aliases); + //char* namep = (na >= 1) ? aliases[0] : buffer; + char* namep = aliases[0]; + QString qname(namep); + */ + +/* + QString qname(is->c_str()); + + // Port already exists? + if(midiDevices.find(qname)) + continue; + + int flags = 0; + int pf = jack_port_flags(port); + // If Jack port can send data to us... + if(pf & JackPortIsOutput) + // Mark as input capable. + flags |= 2; + // If Jack port can receive data from us... + if(pf & JackPortIsInput) + // Mark as output capable. + flags |= 1; + + //JackPort jp(0, QString(buffer), flags); + //portList.append(jp); + + if(debugMsg) + printf(" adding port device:%s\n", qname.latin1()); + + MidiJackDevice* dev = new MidiJackDevice(0, qname); + dev->setrwFlags(flags); + midiDevices.add(dev); + } +*/ +} + diff --git a/muse2/muse/driver/jackaudio.h b/muse2/muse/driver/jackaudio.h new file mode 100644 index 00000000..d3132efe --- /dev/null +++ b/muse2/muse/driver/jackaudio.h @@ -0,0 +1,97 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: jackaudio.h,v 1.20.2.4 2009/12/20 05:00:35 terminator356 Exp $ +// (C) Copyright 2002 Werner Schweer (ws@seh.de) +//========================================================= + +#ifndef __JACKAUDIO_H__ +#define __JACKAUDIO_H__ + +#include <jack/jack.h> +#include "audiodev.h" + +class MidiPlayEvent; + +//--------------------------------------------------------- +// JackAudioDevice +//--------------------------------------------------------- +bool checkAudioDevice(); + +class JackAudioDevice : public AudioDevice { + + jack_client_t* _client; + double sampleTime; + int samplePos; + jack_transport_state_t transportState; + jack_position_t pos; + char jackRegisteredName[16]; + int dummyState; + int dummyPos; + // Free-running frame counter incremented always in process. + jack_nframes_t _frameCounter; + + static int processAudio(jack_nframes_t frames, void*); + + public: + JackAudioDevice(jack_client_t* cl, char * jack_id_string); + virtual ~JackAudioDevice(); + virtual void nullify_client() { _client = 0; } + + virtual inline int deviceType() { return JACK_AUDIO; } // p3.3.52 + + void scanMidiPorts(); + + //virtual void start(); + virtual void start(int); + virtual void stop (); + virtual bool dummySync(int state); // Artificial sync when not using Jack transport. + + virtual int framePos() const; + virtual unsigned frameTime() const { return _frameCounter; } + + virtual float* getBuffer(void* port, unsigned long nframes) { + return (float*)jack_port_get_buffer((jack_port_t*)port, nframes); + } + + virtual std::list<QString> outputPorts(bool midi = false, int aliases = -1); + virtual std::list<QString> inputPorts(bool midi = false, int aliases = -1); + + virtual void registerClient(); + virtual const char* clientName() { return jackRegisteredName; } + + //virtual void* registerOutPort(const char* name); + //virtual void* registerInPort(const char* name); + virtual void* registerOutPort(const char* /*name*/, bool /*midi*/); + virtual void* registerInPort(const char* /*name*/, bool /*midi*/); + + //virtual char* getJackName(); + + virtual void unregisterPort(void*); + virtual void connect(void*, void*); + virtual void disconnect(void*, void*); + virtual int connections(void* clientPort) { return jack_port_connected((jack_port_t*)clientPort); } + virtual void setPortName(void* p, const char* n) { jack_port_set_name((jack_port_t*)p, n); } + virtual void* findPort(const char* name); + virtual QString portName(void* port); + virtual int getState(); + virtual unsigned int getCurFrame(); + virtual bool isRealtime() { return jack_is_realtime(_client); } + virtual int realtimePriority() const; + virtual void startTransport(); + virtual void stopTransport(); + virtual void seekTransport(unsigned frame); + virtual void seekTransport(const Pos &p); + virtual void setFreewheel(bool f); + jack_transport_state_t transportQuery(jack_position_t* pos); + void graphChanged(); + void registrationChanged(); + void connectJackMidiPorts(); + + virtual int setMaster(bool f); + + //static bool jackStarted; + }; + +#endif + diff --git a/muse2/muse/driver/jackmidi.cpp b/muse2/muse/driver/jackmidi.cpp new file mode 100644 index 00000000..4e871a2f --- /dev/null +++ b/muse2/muse/driver/jackmidi.cpp @@ -0,0 +1,1563 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: jackmidi.cpp,v 1.1.1.1 2010/01/27 09:06:43 terminator356 Exp $ +// (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) +//========================================================= + +//#include <qt.h> +#include <qstring.h> +#include <stdio.h> + +#include <jack/jack.h> +//#include <jack/midiport.h> + +#include "jackmidi.h" +#include "song.h" +#include "globals.h" +#include "midi.h" +#include "mididev.h" +#include "../midiport.h" +#include "../midiseq.h" +#include "../midictrl.h" +#include "../audio.h" +#include "mpevent.h" +//#include "sync.h" +#include "audiodev.h" +#include "../mplugins/midiitransform.h" +#include "../mplugins/mitplugin.h" +#include "xml.h" + +// Turn on debug messages. +//#define JACK_MIDI_DEBUG + +extern unsigned int volatile lastExtMidiSyncTick; + +///int jackmidi_pi[2]; +///int jackmidi_po[2]; + +//extern muse_jack_midi_buffer jack_midi_out_data[JACK_MIDI_CHANNELS]; +//extern muse_jack_midi_buffer jack_midi_in_data[JACK_MIDI_CHANNELS]; +///extern jack_port_t *midi_port_in[JACK_MIDI_CHANNELS]; +///extern jack_port_t *midi_port_out[JACK_MIDI_CHANNELS]; + +///MidiJackDevice* gmdev = NULL; + +///int* jackSeq; +//static snd_seq_addr_t musePort; + +//int MidiJackDevice::_nextOutIdNum = 0; +//int MidiJackDevice::_nextInIdNum = 0; + +//int JackMidiPortList::_nextOutIdNum = 0; +//int JackMidiPortList::_nextInIdNum = 0; + +//JackMidiPortList jackMidiClientPorts; + + +/* +//--------------------------------------------------------- +// JackMidiPortList +//--------------------------------------------------------- + +JackMidiPortList::JackMidiPortList() +{ + +} + +JackMidiPortList::~JackMidiPortList() +{ + +} + +iJackMidiPort JackMidiPortList::createClientPort(int flags) // 1 = writable, 2 = readable - do not mix +{ + if(flags & 1) + { + char buf[80]; + snprintf(buf, 80, "muse-jack-midi-out-%d", _nextOutIdNum); + jack_port_t* _client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); + if(_client_jackport == NULL) + { + fprintf(stderr, "JackMidiPortList::createClientPort failed to register jack-midi-out\n"); + //return QString("Could not register jack-midi-out client port"); + return end(); + } + else + { + JackMidiPort jmp(_client_jackport, QString(buf), flags); + _nextOutIdNum++; + return insert(begin(), std::pair<jack_port_t*, JackMidiPort>(_client_jackport, jmp)); + } + } + else + if(flags & 2) + { + char buf[80]; + snprintf(buf, 80, "muse-jack-midi-in-%d", _nextInIdNum); + jack_port_t* _client_jackport = (jack_port_t*)audioDevice->registerInPort(buf, true); + if(_client_jackport == NULL) + { + fprintf(stderr, "JackMidiPortList::createClientPort failed to register jack-midi-in\n"); + return end(); + } + else + { + JackMidiPort jmp(_client_jackport, QString(buf), flags); + _nextInIdNum++; + return insert(begin(), std::pair<jack_port_t*, JackMidiPort>(_client_jackport, jmp)); + } + } + return end(); +} + +// Return true if removed. +bool JackMidiPortList::removeClientPort(jack_port_t* port) +{ + iJackMidiPort ijp = find(port); + if(ijp == end()) + return false; + + // Is output? + if(ijp->second._flags & 1) + _nextOutIdNum--; + // Is input? + if(ijp->second._flags & 2) + _nextInIdNum--; + + erase(ijp); + + audioDevice->unregisterPort(port); + + return true; +} +*/ + +//--------------------------------------------------------- +// MidiJackDevice +//--------------------------------------------------------- + +//MidiJackDevice::MidiJackDevice(const int& a, const QString& n) +MidiJackDevice::MidiJackDevice(jack_port_t* jack_port, const QString& n) + : MidiDevice(n) +{ + //_client_jackport = 0; + _client_jackport = jack_port; + //adr = a; + init(); +} + +MidiJackDevice::~MidiJackDevice() +{ + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::~MidiJackDevice()\n"); + #endif + if(_client_jackport) + audioDevice->unregisterPort(_client_jackport); + //close(); +} + +/* +//--------------------------------------------------------- +// select[RW]fd +//--------------------------------------------------------- + +int MidiJackDevice::selectRfd() +{ + return jackmidi_pi[0]; +} + +int MidiJackDevice::selectWfd() +{ + return jackmidi_po[0]; +} +*/ + +//--------------------------------------------------------- +// createJackMidiDevice +// If name parameter is blank, creates a new (locally) unique one. +//--------------------------------------------------------- + +//QString MidiJackDevice::createJackMidiDevice(int rwflags) // 1:Writable 2: Readable. Do not mix. +MidiDevice* MidiJackDevice::createJackMidiDevice(QString name, int rwflags) // 1:Writable 2: Readable. Do not mix. +{ +/// _openFlags &= _rwFlags; // restrict to available bits + +/// #ifdef JACK_MIDI_DEBUG +/// printf("MidiJackDevice::open %s\n", name.latin1()); +/// #endif + + //jack_port_t* jp = jack_port_by_name(_client, name().latin1()); +/// jack_port_t* jp = (jack_port_t*)audioDevice->findPort(name().latin1()); + +/// if(!jp) +/// { +/// printf("MidiJackDevice::open: Jack midi port %s not found!\n", name().latin1()); +/// _writeEnable = false; +/// _readEnable = false; +/// return QString("Jack midi port not found"); +/// } + +/// int pf = jack_port_flags(jp); + + //if(!name.isEmpty()) + //{ + // Does not work. + // if(audioDevice->findPort(name.latin1())) + // { + // fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Given port name %s already exists!\n", name.latin1()); + // return 0; + // } + //} + + jack_port_t* client_jackport = NULL; + //char buf[80]; + + + // If Jack port can receive data from us and we actually want to... + //if((pf & JackPortIsInput) && (_openFlags & 1)) + if(rwflags & 1) + { + if(name.isEmpty()) + { + //snprintf(buf, 80, "muse-jack-midi-out-%d", _nextOutIdNum); + for(int i = 0; ; ++i) + { + //snprintf(buf, 80, "midi-out-%d", i); + name.sprintf("midi-out-%d", i); + + if(!midiDevices.find(name)) + { + // Does not work. + //if(!audioDevice->findPort(buf)) + // break; + //client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); + if(audioDevice->deviceType() == AudioDevice::JACK_AUDIO) // p3.3.52 + { + client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.latin1(), true); + if(client_jackport) + break; + } + else + break; + } + + if(i == 65535) + { + fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Can't find unused output port name!\n"); + return 0; + } + } + //name = QString(buf); + } + else + { + if(audioDevice->deviceType() == AudioDevice::JACK_AUDIO) // p3.3.52 + { + client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.latin1(), true); + if(!client_jackport) + { + fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed creating output port name %s\n", name.latin1()); + return 0; + } + } + } + /* + else + { + client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.latin1(), true); + if(!client_jackport) + { + for(int i = 0; ; ++i) + { + snprintf(buf, 80, "midi-out-%d", i); + // Does not work! + //if(!audioDevice->findPort(buf)) + // break; + client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); + if(client_jackport) + break; + + if(i == 65535) + { + fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Can't find unused output port name!\n"); + return 0; + } + } + name = QString(buf); + } + } + */ + + //client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.latin1(), true); + //if(client_jackport == NULL) + //{ + // fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed to register jack midi client output port %s\n", name.latin1()); + // return 0; + //} + //else + // _nextOutIdNum++; + + } + else // Note docs say it can't be both input and output. + // If Jack port can send data to us and we actually want it... + //if((pf & JackPortIsOutput) && (_openFlags & 2)) + if(rwflags & 2) + { + if(name.isEmpty()) + { + //snprintf(buf, 80, "muse-jack-midi-in-%d", _nextInIdNum); + for(int i = 0; ; ++i) + { + //snprintf(buf, 80, "midi-in-%d", i); + name.sprintf("midi-in-%d", i); + + if(!midiDevices.find(name)) + { + // Does not work. + //if(!audioDevice->findPort(buf)) + // break; + //client_jackport = (jack_port_t*)audioDevice->registerInPort(buf, true); + if(audioDevice->deviceType() == AudioDevice::JACK_AUDIO) // p3.3.52 + { + client_jackport = (jack_port_t*)audioDevice->registerInPort(name.latin1(), true); + if(client_jackport) + break; + } + else + break; + } + + if(i == 65535) + { + fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Can't find unused input port name!\n"); + return 0; + } + } + //name = QString(buf); + } + else + { + if(audioDevice->deviceType() == AudioDevice::JACK_AUDIO) // p3.3.52 + { + client_jackport = (jack_port_t*)audioDevice->registerInPort(name.latin1(), true); + if(!client_jackport) + { + fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed creating input port name %s\n", name.latin1()); + return 0; + } + } + } + + //client_jackport = (jack_port_t*)audioDevice->registerInPort(name.latin1(), true); + + //if(client_jackport == NULL) + //{ + // fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed to register jack midi client input port %s\n", name.latin1()); + //_readEnable = false; + //return QString("Could not register jack-midi-in client port"); + // return 0; + //} + //else + // _nextInIdNum++; + + } + + //if(client_jackport == NULL) // p3.3.52 Removed. Allow the device to be created even if Jack isn't running. + // return 0; + + MidiJackDevice* dev = new MidiJackDevice(client_jackport, name); + dev->setrwFlags(rwflags); + midiDevices.add(dev); + return dev; +} + +//--------------------------------------------------------- +// setName +//--------------------------------------------------------- + +void MidiJackDevice::setName(const QString& s) +{ + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::setName %s new name:%s\n", name().latin1(), s.latin1()); + #endif + _name = s; + if(clientPort()) // p3.3.52 Added check. + audioDevice->setPortName(clientPort(), s.latin1()); +} + +//--------------------------------------------------------- +// open +//--------------------------------------------------------- + +QString MidiJackDevice::open() +{ + _openFlags &= _rwFlags; // restrict to available bits + + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::open %s\n", name().latin1()); + #endif + + /* + //jack_port_t* jp = jack_port_by_name(_client, name().latin1()); + jack_port_t* jp = (jack_port_t*)audioDevice->findPort(name().latin1()); + + if(!jp) + { + printf("MidiJackDevice::open: Jack midi port %s not found!\n", name().latin1()); + _writeEnable = false; + _readEnable = false; + return QString("Jack midi port not found"); + } + + int pf = jack_port_flags(jp); + + // If Jack port can receive data from us and we actually want to... + if((pf & JackPortIsInput) && (_openFlags & 1)) + { + char buf[80]; + snprintf(buf, 80, "muse-jack-midi-out-%d", _nextOutIdNum); + _client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); + if(_client_jackport == NULL) + { + fprintf(stderr, "MidiJackDevice::open failed to register jack-midi-out\n"); + _writeEnable = false; + return QString("Could not register jack-midi-out client port"); + } + else + { + _nextOutIdNum++; + // src, dest + ///audioDevice->connect(_client_jackport, jp); + _writeEnable = true; + } + } + else // Note docs say it can't be both input and output. + // If Jack port can send data to us and we actually want it... + if((pf & JackPortIsOutput) && (_openFlags & 2)) + { + char buf[80]; + snprintf(buf, 80, "muse-jack-midi-in-%d", _nextInIdNum); + _client_jackport = (jack_port_t*)audioDevice->registerInPort(buf, true); + if(_client_jackport == NULL) + { + fprintf(stderr, "MidiJackDevice::open failed to register jack-midi-in\n"); + _readEnable = false; + return QString("Could not register jack-midi-in client port"); + } + else + { + _nextInIdNum++; + ///audioDevice->connect(jp, _client_jackport); + _readEnable = true; + } + } + */ + + _writeEnable = bool(_openFlags & 1); + _readEnable = bool(_openFlags & 2); + + return QString("OK"); +} + +//--------------------------------------------------------- +// close +//--------------------------------------------------------- + +void MidiJackDevice::close() +{ + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::close %s\n", name().latin1()); + #endif + + /* + if(_client_jackport) + { + int pf = jack_port_flags(_client_jackport); + + if(pf & JackPortIsOutput) + _nextOutIdNum--; + else + if(pf & JackPortIsInput) + _nextInIdNum--; + audioDevice->unregisterPort(_client_jackport); + _client_jackport = 0; + _writeEnable = false; + _readEnable = false; + return; + } + */ + + _writeEnable = false; + _readEnable = false; + + /* + //jack_port_t* jp = jack_port_by_name(_client, name().latin1()); + jack_port_t* jp = (jack_port_t*)audioDevice->findPort(name().latin1()); + + if(!jp) + { + printf("MidiJackDevice::close: Jack midi port %s not found!\n", name().latin1()); + _writeEnable = false; + _readEnable = false; + return; + } + + //int pf = jack_port_flags(jp); + + // If Jack port can receive data from us and we actually want to... + //if((pf & JackPortIsInput) && (_openFlags & 1)) + if(jack_port_connected_to(midi_port_out[0], name().latin1())) + { + // src, dest +/// audioDevice->disconnect(midi_port_out[0], jp); + _writeEnable = false; + } + else // Note docs say it can't be both input and output. + // If Jack port can send data to us and we actually want it... + //if((pf & JackPortIsOutput) && (_openFlags & 2)) + if(jack_port_connected_to(midi_port_in[0], name().latin1())) + { +/// audioDevice->disconnect(jp, midi_port_in[0]); + _readEnable = false; + } + */ +} + +//--------------------------------------------------------- +// writeRouting +//--------------------------------------------------------- + +void MidiJackDevice::writeRouting(int level, Xml& xml) const +{ + // p3.3.45 + // If this device is not actually in use by the song, do not write any routes. + // This prevents bogus routes from being saved and propagated in the med file. + if(midiPort() == -1) + return; + + QString s; + if(rwFlags() & 2) // Readable + { + //RouteList* rl = _inRoutes; + //for (ciRoute r = rl->begin(); r != rl->end(); ++r) + for (ciRoute r = _inRoutes.begin(); r != _inRoutes.end(); ++r) + { + if(!r->name().isEmpty()) + { + xml.tag(level++, "Route"); + + //xml.strTag(level, "srcNode", r->name()); + //xml.tag(level, "source type=\"%d\" name=\"%s\"/", r->type, r->name().latin1()); + s = QT_TR_NOOP("source"); + if(r->type != Route::TRACK_ROUTE) + s += QString(QT_TR_NOOP(" type=\"%1\"")).arg(r->type); + + //s += QString(QT_TR_NOOP(" name=\"%1\"/")).arg(r->name()); + s += QString(QT_TR_NOOP(" name=\"%1\"/")).arg(Xml::xmlString(r->name())); + xml.tag(level, s); + + //xml.strTag(level, "dstNode", name()); + //xml.tag(level, "dest type=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, name().latin1()); + //xml.tag(level, "dest type=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, name().latin1()); + //xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, name().latin1()); + xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, Xml::xmlString(name()).latin1()); + + xml.etag(level--, "Route"); + } + } + } + + for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r) + { + if(!r->name().isEmpty()) + { + s = QT_TR_NOOP("Route"); + if(r->channel != -1) + s += QString(QT_TR_NOOP(" channel=\"%1\"")).arg(r->channel); + + //xml.tag(level++, "Route"); + xml.tag(level++, s); + + /* + //xml.strTag(level, "srcNode", name()); + if(r->channel != -1) + //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, r->channel, name().latin1()); + //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, r->channel, name().latin1()); + xml.tag(level, "source devtype=\"%d\" channel=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, r->channel, name().latin1()); + else + //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, name().latin1()); + //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, name().latin1()); + */ + //xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, name().latin1()); + xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, Xml::xmlString(name()).latin1()); + + /* + //xml.strTag(level, "dstNode", r->name()); + if(r->channel != -1) + { + if(r->type == Route::MIDI_DEVICE_ROUTE) + xml.tag(level, "dest devtype=\"%d\" channel=\"%d\" name=\"%s\"/", r->device->deviceType(), r->channel, r->name().latin1()); + else + xml.tag(level, "dest type=\"%d\" channel=\"%d\" name=\"%s\"/", r->type, r->channel, r->name().latin1()); + } + else + { + if(r->type == Route::MIDI_DEVICE_ROUTE) + xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", r->device->deviceType(), r->name().latin1()); + else + xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().latin1()); + } + */ + + s = QT_TR_NOOP("dest"); + if(r->type == Route::MIDI_DEVICE_ROUTE) + s += QString(QT_TR_NOOP(" devtype=\"%1\"")).arg(r->device->deviceType()); + else + if(r->type != Route::TRACK_ROUTE) + s += QString(QT_TR_NOOP(" type=\"%1\"")).arg(r->type); + + //s += QString(QT_TR_NOOP(" name=\"%1\"/")).arg(r->name()); + s += QString(QT_TR_NOOP(" name=\"%1\"/")).arg(Xml::xmlString(r->name())); + xml.tag(level, s); + + + xml.etag(level--, "Route"); + } + } + + /* + else + if(rwFlags() & 1) // Writable + { + //RouteList* rl = _outRoutes; + //for (ciRoute r = rl->begin(); r != rl->end(); ++r) + for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r) + { + if(!r->name().isEmpty()) + { + xml.tag(level++, "Route"); + + //xml.strTag(level, "srcNode", name()); + //if(r->channel != -1) + // xml.tag(level, "srcNode type=\"%d\" channel=\"%d\" name=\"%s\"", Route::JACK_MIDI_ROUTE, r->channel, name().latin1()); + //else + xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, name().latin1()); + + //xml.strTag(level, "dstNode", r->name()); + xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().latin1()); + + xml.etag(level--, "Route"); + } + } + } + */ +} + +//--------------------------------------------------------- +// putEvent +//--------------------------------------------------------- + +/* FIX: if we fail to transmit the event, + * we return false (indicating OK). Otherwise + * it seems muse will retry forever + */ +bool MidiJackDevice::putMidiEvent(const MidiPlayEvent& /*event*/) +{ + /* + int give, channel = event.channel(); + int x; + + if(channel >= JACK_MIDI_CHANNELS) return false; + + // buffer up events, because jack eats them in chunks, if + // the buffer is full, there isn't so much to do, than + // drop the event + + give = jack_midi_out_data[channel].give; + if(jack_midi_out_data[channel].buffer[give*4+3]){ + fprintf(stderr, "WARNING: muse-to-jack midi-buffer is full, channel=%u\n", channel); + return false; + } + // copy event(note-on etc..), pitch and volume + // see http://www.midi.org/techspecs/midimessages.php + switch(event.type()){ + case ME_NOTEOFF: + jack_midi_out_data[channel].buffer[give*4+0] = 0x80; + jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f; + jack_midi_out_data[channel].buffer[give*4+2] = event.dataB() & 0x7f; + break; + case ME_NOTEON: + jack_midi_out_data[channel].buffer[give*4+0] = 0x90; + jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f; + jack_midi_out_data[channel].buffer[give*4+2] = event.dataB() & 0x7f; + break; + case ME_CONTROLLER: + jack_midi_out_data[channel].buffer[give*4+0] = 0xb0; + jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f; + jack_midi_out_data[channel].buffer[give*4+2] = event.dataB() & 0x7f; + break; + case ME_PROGRAM: + jack_midi_out_data[channel].buffer[give*4+0] = 0xc0; + jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f; + jack_midi_out_data[channel].buffer[give*4+2] = 0; + break; + case ME_PITCHBEND: + jack_midi_out_data[channel].buffer[give*4+0] = 0xE0; + // convert muse pitch-bend to midi standard + x = 0x2000 + event.dataA(); + jack_midi_out_data[channel].buffer[give*4+1] = x & 0x7f; + jack_midi_out_data[channel].buffer[give*4+2] = (x >> 8) & 0x7f; + break; + default: + fprintf(stderr, "jack-midi-out %u WARNING: unknown event %x\n", channel, event.type()); + return false; + } + jack_midi_out_data[channel].buffer[give*4+3] = 1; // mark state of this slot + // finally increase give position + give++; + if(give >= JACK_MIDI_BUFFER_SIZE){ + give = 0; + } + jack_midi_out_data[channel].give = give; + return false; + */ + + return false; +} + +/* +//--------------------------------------------------------- +// putEvent +// return false if event is delivered +//--------------------------------------------------------- + +bool MidiJackDevice::putEvent(int* event) +{ + int *y; y = event; + return false; +} +*/ + +//--------------------------------------------------------- +// recordEvent +//--------------------------------------------------------- + +void MidiJackDevice::recordEvent(MidiRecordEvent& event) + { + // Set the loop number which the event came in at. + //if(audio->isRecording()) + if(audio->isPlaying()) + event.setLoopNum(audio->loopCount()); + + if (midiInputTrace) { + printf("Jack MidiInput: "); + event.dump(); + } + + int typ = event.type(); + + if(_port != -1) + { + int idin = midiPorts[_port].syncInfo().idIn(); + + //--------------------------------------------------- + // filter some SYSEX events + //--------------------------------------------------- + + if (typ == ME_SYSEX) { + const unsigned char* p = event.data(); + int n = event.len(); + if (n >= 4) { + if ((p[0] == 0x7f) + //&& ((p[1] == 0x7f) || (p[1] == rxDeviceId))) { + && ((p[1] == 0x7f) || (idin == 0x7f) || (p[1] == idin))) { + if (p[2] == 0x06) { + //mmcInput(p, n); + midiSeq->mmcInput(_port, p, n); + return; + } + if (p[2] == 0x01) { + //mtcInputFull(p, n); + midiSeq->mtcInputFull(_port, p, n); + return; + } + } + else if (p[0] == 0x7e) { + //nonRealtimeSystemSysex(p, n); + midiSeq->nonRealtimeSystemSysex(_port, p, n); + return; + } + } + } + else + // Trigger general activity indicator detector. Sysex has no channel, don't trigger. + midiPorts[_port].syncInfo().trigActDetect(event.channel()); + } + + // + // process midi event input filtering and + // transformation + // + + processMidiInputTransformPlugins(event); + + if (filterEvent(event, midiRecordType, false)) + return; + + if (!applyMidiInputTransformation(event)) { + if (midiInputTrace) + printf(" midi input transformation: event filtered\n"); + return; + } + + // + // transfer noteOn events to gui for step recording and keyboard + // remote control + // + if (typ == ME_NOTEON) { + int pv = ((event.dataA() & 0xff)<<8) + (event.dataB() & 0xff); + song->putEvent(pv); + } + + //if(_recordFifo.put(MidiPlayEvent(event))) + // printf("MidiJackDevice::recordEvent: fifo overflow\n"); + + // p3.3.38 + // Do not bother recording if it is NOT actually being used by a port. + // Because from this point on, process handles things, by selected port. + if(_port == -1) + return; + + // Split the events up into channel fifos. Special 'channel' number 17 for sysex events. + unsigned int ch = (typ == ME_SYSEX)? MIDI_CHANNELS : event.channel(); + if(_recordFifo[ch].put(MidiPlayEvent(event))) + printf("MidiJackDevice::recordEvent: fifo channel %d overflow\n", ch); + } + +//--------------------------------------------------------- +// midiReceived +//--------------------------------------------------------- + +void MidiJackDevice::eventReceived(jack_midi_event_t* ev) + { + MidiRecordEvent event; + event.setB(0); + + // NOTE: From MusE-2. Not done here in Muse-1 (yet). + // move all events 2*segmentSize into the future to get + // jitterfree playback + // + // cycle n-1 n n+1 + // -+----------+----------+----------+- + // ^ ^ ^ + // catch process play + // +// const SeqTime* st = audio->seqTime(); + + //unsigned curFrame = st->startFrame() + segmentSize; +// unsigned curFrame = st->lastFrameTime; + //int frameOffset = audio->getFrameOffset(); + unsigned pos = audio->pos().frame(); + + //event.setTime(pos + ev->time); + event.setTime(extSyncFlag.value() ? lastExtMidiSyncTick : (pos + ev->time)); + + event.setChannel(*(ev->buffer) & 0xf); + int type = *(ev->buffer) & 0xf0; + int a = *(ev->buffer + 1) & 0x7f; + int b = *(ev->buffer + 2) & 0x7f; + event.setType(type); + switch(type) { + case ME_NOTEON: + case ME_NOTEOFF: + case ME_CONTROLLER: + event.setA(*(ev->buffer + 1)); + event.setB(*(ev->buffer + 2)); + break; + case ME_PROGRAM: + case ME_AFTERTOUCH: + event.setA(*(ev->buffer + 1)); + break; + + case ME_PITCHBEND: + event.setA(((b << 7) + a) - 8192); + break; + + case ME_SYSEX: + { + int type = *(ev->buffer) & 0xff; + switch(type) + { + case ME_SYSEX: + + // TODO: Deal with large sysex, which are broken up into chunks! + // For now, do not accept if the last byte is not EOX, meaning it's a chunk with more chunks to follow. + if(*(((unsigned char*)ev->buffer) + ev->size - 1) != ME_SYSEX_END) + { + printf("MidiJackDevice::eventReceived sysex chunks not supported!\n"); + return; + } + + //event.setTime(0); // mark as used + event.setType(ME_SYSEX); + event.setData((unsigned char*)(ev->buffer + 1), ev->size - 2); + break; + case ME_MTC_QUARTER: + if(_port != -1) + midiSeq->mtcInputQuarter(_port, *(ev->buffer + 1)); + return; + case ME_SONGPOS: + if(_port != -1) + midiSeq->setSongPosition(_port, *(ev->buffer + 1) | (*(ev->buffer + 2) >> 2 )); // LSB then MSB + return; + //case ME_SONGSEL: + //case ME_TUNE_REQ: + //case ME_SENSE: + case ME_CLOCK: + case ME_TICK: + case ME_START: + case ME_CONTINUE: + case ME_STOP: + if(_port != -1) + midiSeq->realtimeSystemInput(_port, type); + return; + //case ME_SYSEX_END: + //break; + // return; + default: + printf("MidiJackDevice::eventReceived unsupported system event 0x%02x\n", type); + return; + } + } + //return; + break; + default: + printf("MidiJackDevice::eventReceived unknown event 0x%02x\n", type); + //printf("MidiJackDevice::eventReceived unknown event 0x%02x size:%d buf:0x%02x 0x%02x 0x%02x ...0x%02x\n", type, ev->size, *(ev->buffer), *(ev->buffer + 1), *(ev->buffer + 2), *(ev->buffer + (ev->size - 1))); + return; + } + + if (midiInputTrace) { + printf("MidiInput<%s>: ", name().latin1()); + event.dump(); + } + + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::eventReceived time:%d type:%d ch:%d A:%d B:%d\n", event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); + #endif + + // Let recordEvent handle it from here, with timestamps, filtering, gui triggering etc. + recordEvent(event); + } + +//--------------------------------------------------------- +// collectMidiEvents +//--------------------------------------------------------- + +void MidiJackDevice::collectMidiEvents() +{ + if(!_readEnable) + return; + + if(!_client_jackport) + return; + void* port_buf = jack_port_get_buffer(_client_jackport, segmentSize); + + jack_midi_event_t event; + jack_nframes_t eventCount = jack_midi_get_event_count(port_buf); + for (jack_nframes_t i = 0; i < eventCount; ++i) + { + jack_midi_event_get(&event, port_buf, i); + + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::collectMidiEvents number:%d time:%d\n", i, event.time); + #endif + + eventReceived(&event); + } +} + +//--------------------------------------------------------- +// putEvent +// return true if event cannot be delivered +//--------------------------------------------------------- + +bool MidiJackDevice::putEvent(const MidiPlayEvent& ev) +{ + if(!_writeEnable) + //return true; + return false; + + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::putEvent time:%d type:%d ch:%d A:%d B:%d\n", ev.time(), ev.type(), ev.channel(), ev.dataA(), ev.dataB()); + #endif + + bool rv = eventFifo.put(ev); + if(rv) + printf("MidiJackDevice::putEvent: port overflow\n"); + + return rv; +} + +//--------------------------------------------------------- +// queueEvent +// return true if successful +//--------------------------------------------------------- + +//void JackAudioDevice::putEvent(Port port, const MidiEvent& e) +bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) +//bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) +{ + // Perhaps we can find use for this value later, together with the Jack midi MusE port(s). + // No big deal if not. Not used for now. + //int port = e.port(); + + //if(port >= JACK_MIDI_CHANNELS) + // return false; + + //if (midiOutputTrace) { + // printf("MidiOut<%s>: jackMidi: ", portName(port).toLatin1().data()); + // e.dump(); + // } + + //if(debugMsg) + // printf("MidiJackDevice::queueEvent\n"); + + if(!_client_jackport) + return false; + void* pb = jack_port_get_buffer(_client_jackport, segmentSize); + + //unsigned frameCounter = ->frameTime(); + int frameOffset = audio->getFrameOffset(); + unsigned pos = audio->pos().frame(); + int ft = e.time() - frameOffset - pos; + + if (ft < 0) + ft = 0; + if (ft >= (int)segmentSize) { + printf("MidiJackDevice::queueEvent: Event time:%d out of range. offset:%d ft:%d (seg=%d)\n", e.time(), frameOffset, ft, segmentSize); + if (ft > (int)segmentSize) + ft = segmentSize - 1; + } + + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::queueEvent time:%d type:%d ch:%d A:%d B:%d\n", e.time(), e.type(), e.channel(), e.dataA(), e.dataB()); + #endif + + switch(e.type()) { + case ME_NOTEON: + case ME_NOTEOFF: + case ME_POLYAFTER: + case ME_CONTROLLER: + case ME_PITCHBEND: + { + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::queueEvent note on/off polyafter controller or pitch\n"); + #endif + + unsigned char* p = jack_midi_event_reserve(pb, ft, 3); + if (p == 0) { + fprintf(stderr, "MidiJackDevice::queueEvent #1: buffer overflow, event lost\n"); + return false; + } + p[0] = e.type() | e.channel(); + p[1] = e.dataA(); + p[2] = e.dataB(); + } + break; + + case ME_PROGRAM: + case ME_AFTERTOUCH: + { + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::queueEvent program or aftertouch\n"); + #endif + + unsigned char* p = jack_midi_event_reserve(pb, ft, 2); + if (p == 0) { + fprintf(stderr, "MidiJackDevice::queueEvent #2: buffer overflow, event lost\n"); + return false; + } + p[0] = e.type() | e.channel(); + p[1] = e.dataA(); + } + break; + case ME_SYSEX: + { + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::queueEvent sysex\n"); + #endif + + const unsigned char* data = e.data(); + int len = e.len(); + unsigned char* p = jack_midi_event_reserve(pb, ft, len+2); + if (p == 0) { + fprintf(stderr, "MidiJackDevice::queueEvent #3: buffer overflow, event lost\n"); + return false; + } + p[0] = 0xf0; + p[len+1] = 0xf7; + memcpy(p+1, data, len); + } + break; + case ME_SONGPOS: + case ME_CLOCK: + case ME_START: + case ME_CONTINUE: + case ME_STOP: + printf("MidiJackDevice::queueEvent: event type %x not supported\n", e.type()); + return false; + break; + } + + return true; +} + +//--------------------------------------------------------- +// processEvent +//--------------------------------------------------------- + +void MidiJackDevice::processEvent(const MidiPlayEvent& event) +{ + //int frameOffset = audio->getFrameOffset(); + //unsigned pos = audio->pos().frame(); + + int chn = event.channel(); + unsigned t = event.time(); + int a = event.dataA(); + int b = event.dataB(); + // Perhaps we can find use for this value later, together with the Jack midi MusE port(s). + // No big deal if not. Not used for now. + int port = event.port(); + + // TODO: No sub-tick playback resolution yet, with external sync. + // Just do this 'standard midi 64T timing thing' for now until we figure out more precise external timings. + // Does require relatively short audio buffers, in order to catch the resolution, but buffer <= 256 should be OK... + // Tested OK so far with 128. + if(extSyncFlag.value()) + t = audio->getFrameOffset() + audio->pos().frame(); + //t = frameOffset + pos; + + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::processEvent time:%d type:%d ch:%d A:%d B:%d\n", event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); + #endif + + if(event.type() == ME_PROGRAM) + { + // don't output program changes for GM drum channel + //if (!(song->mtype() == MT_GM && chn == 9)) { + int hb = (a >> 16) & 0xff; + int lb = (a >> 8) & 0xff; + int pr = a & 0x7f; + + // p3.3.44 + //printf("MidiJackDevice::processEvent ME_PROGRAM time:%d type:%d ch:%d A:%d B:%d hb:%d lb:%d pr:%d\n", + // event.time(), event.type(), event.channel(), event.dataA(), event.dataB(), hb, lb, pr); + + if (hb != 0xff) + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb)); + if (lb != 0xff) + queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LBANK, lb)); + queueEvent(MidiPlayEvent(t+2, port, chn, ME_PROGRAM, pr, 0)); + // } + } + else + if(event.type() == ME_PITCHBEND) + { + int v = a + 8192; + // p3.3.44 + //printf("MidiJackDevice::processEvent ME_PITCHBEND v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); + + queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f)); + } + else + if(event.type() == ME_CONTROLLER) + { + //int a = event.dataA(); + //int b = event.dataB(); + // Perhaps we can find use for this value later, together with the Jack midi MusE port(s). + // No big deal if not. Not used for now. + //int port = event.port(); + + int nvh = 0xff; + int nvl = 0xff; + if(_port != -1) + { + int nv = midiPorts[_port].nullSendValue(); + if(nv != -1) + { + nvh = (nv >> 8) & 0xff; + nvl = nv & 0xff; + } + } + + if(a == CTRL_PITCH) + { + int v = b + 8192; + // p3.3.44 + //printf("MidiJackDevice::processEvent CTRL_PITCH v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); + + queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f)); + } + else if (a == CTRL_PROGRAM) + { + // don't output program changes for GM drum channel + //if (!(song->mtype() == MT_GM && chn == 9)) { + int hb = (b >> 16) & 0xff; + int lb = (b >> 8) & 0xff; + int pr = b & 0x7f; + + // p3.3.44 + //printf("MidiJackDevice::processEvent CTRL_PROGRAM time:%d type:%d ch:%d A:%d B:%d hb:%d lb:%d pr:%d\n", + // event.time(), event.type(), event.channel(), event.dataA(), event.dataB(), hb, lb, pr); + + if (hb != 0xff) + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb)); + if (lb != 0xff) + queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LBANK, lb)); + queueEvent(MidiPlayEvent(t+2, port, chn, ME_PROGRAM, pr, 0)); + // } + } + /* + else if (a == CTRL_MASTER_VOLUME) + { + unsigned char sysex[] = { + 0x7f, 0x7f, 0x04, 0x01, 0x00, 0x00 + }; + sysex[1] = deviceId(); + sysex[4] = b & 0x7f; + sysex[5] = (b >> 7) & 0x7f; + queueEvent(MidiPlayEvent(t, port, ME_SYSEX, sysex, 6)); + } + */ + else if (a < CTRL_14_OFFSET) + { // 7 Bit Controller + queueEvent(event); + //queueEvent(museport, MidiPlayEvent(t, port, chn, event)); + } + else if (a < CTRL_RPN_OFFSET) + { // 14 bit high resolution controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + int dataH = (b >> 7) & 0x7f; + int dataL = b & 0x7f; + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, ctrlH, dataH)); + queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, ctrlL, dataL)); + } + else if (a < CTRL_NRPN_OFFSET) + { // RPN 7-Bit Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH)); + queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL)); + queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, b)); + + t += 3; + // Select null parameters so that subsequent data controller events do not upset the last *RPN controller. + //sendNullRPNParams(chn, false); + if(nvh != 0xff) + { + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, nvh & 0x7f)); + t += 1; + } + if(nvl != 0xff) + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, nvl & 0x7f)); + } + //else if (a < CTRL_RPN14_OFFSET) + else if (a < CTRL_INTERNAL_OFFSET) + { // NRPN 7-Bit Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH)); + queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL)); + queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, b)); + + t += 3; + //sendNullRPNParams(chn, true); + if(nvh != 0xff) + { + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, nvh & 0x7f)); + t += 1; + } + if(nvl != 0xff) + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, nvl & 0x7f)); + } + else if (a < CTRL_NRPN14_OFFSET) + { // RPN14 Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + int dataH = (b >> 7) & 0x7f; + int dataL = b & 0x7f; + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH)); + queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL)); + queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH)); + queueEvent(MidiPlayEvent(t+3, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL)); + + t += 4; + //sendNullRPNParams(chn, false); + if(nvh != 0xff) + { + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, nvh & 0x7f)); + t += 1; + } + if(nvl != 0xff) + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, nvl & 0x7f)); + } + else if (a < CTRL_NONE_OFFSET) + { // NRPN14 Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + int dataH = (b >> 7) & 0x7f; + int dataL = b & 0x7f; + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH)); + queueEvent(MidiPlayEvent(t+1, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL)); + queueEvent(MidiPlayEvent(t+2, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH)); + queueEvent(MidiPlayEvent(t+3, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL)); + + t += 4; + //sendNullRPNParams(chn, true); + if(nvh != 0xff) + { + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, nvh & 0x7f)); + t += 1; + } + if(nvl != 0xff) + queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, nvl & 0x7f)); + } + else + { + printf("MidiJackDevice::processEvent: unknown controller type 0x%x\n", a); + } + } + else + { + queueEvent(event); + //queueEvent(MidiPlayEvent(t, port, chn, event)); + } +} + +//--------------------------------------------------------- +// processMidi called from audio process only. +//--------------------------------------------------------- + +void MidiJackDevice::processMidi() +{ + if(!_client_jackport) + return; + void* port_buf = jack_port_get_buffer(_client_jackport, segmentSize); + jack_midi_clear_buffer(port_buf); + + while(!eventFifo.isEmpty()) + { + MidiPlayEvent e(eventFifo.get()); + int evTime = e.time(); + // Is event marked to be played immediately? + if(evTime == 0) + { + // Nothing to do but stamp the event to be queued for frame 0+. + //e.setTime(frameOffset + pos); + e.setTime(audio->getFrameOffset() + audio->pos().frame()); + } + + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::processMidi eventFifo time:%d type:%d ch:%d A:%d B:%d\n", e.time(), e.type(), e.channel(), e.dataA(), e.dataB()); + #endif + + //el->insert(eventFifo.get()); + //el->insert(e); + processEvent(e); + } + + MPEventList* el = playEvents(); + if(el->empty()) + return; + + iMPEvent i = nextPlayEvent(); + for(; i != el->end(); ++i) + { + // p3.3.39 Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. + // Same code as in MidiPort::sendEvent() + if(_port != -1) + { + MidiPort* mp = &midiPorts[_port]; + if(i->type() == ME_CONTROLLER) + { + int da = i->dataA(); + int db = i->dataB(); + db = mp->limitValToInstrCtlRange(da, db); + if(!mp->setHwCtrlState(i->channel(), da, db)) + continue; + //mp->setHwCtrlState(i->channel(), da, db); + } + else + if(i->type() == ME_PITCHBEND) + { + // p3.3.44 + //printf("MidiJackDevice::processMidi playEvents ME_PITCHBEND time:%d type:%d ch:%d A:%d B:%d\n", (*i).time(), (*i).type(), (*i).channel(), (*i).dataA(), (*i).dataB()); + + int da = mp->limitValToInstrCtlRange(CTRL_PITCH, i->dataA()); + if(!mp->setHwCtrlState(i->channel(), CTRL_PITCH, da)) + continue; + //mp->setHwCtrlState(i->channel(), CTRL_PITCH, da); + + //(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f)); + } + else + if(i->type() == ME_PROGRAM) + { + if(!mp->setHwCtrlState(i->channel(), CTRL_PROGRAM, i->dataA())) + continue; + //mp->setHwCtrlState(i->channel(), CTRL_PROGRAM, i->dataA()); + } + } + + processEvent(*i); + } + + setNextPlayEvent(i); +} + +//--------------------------------------------------------- +// initMidiJack +// return true on error +//--------------------------------------------------------- + +bool initMidiJack() +{ + /* + int adr = 0; + + memset(jack_midi_out_data, 0, JACK_MIDI_CHANNELS * sizeof(muse_jack_midi_buffer)); + memset(jack_midi_in_data, 0, JACK_MIDI_CHANNELS * sizeof(muse_jack_midi_buffer)); + + MidiJackDevice* dev = new MidiJackDevice(adr, QString("jack-midi")); + dev->setrwFlags(3); // set read and write flags + + if(pipe(jackmidi_pi) < 0){ + fprintf(stderr, "cant create midi-jack input pipe\n"); + } + if(pipe(jackmidi_po) < 0){ + fprintf(stderr, "cant create midi-jack output pipe\n"); + } + + midiDevices.add(dev); + + gmdev = dev; // proclaim the global jack-midi instance + + //jackScanMidiPorts(); + */ + + return false; +} + +/* +struct JackPort { + int adr; + //char* name; + QString name; + int flags; + //JackPort(int a, const char* s, int f) { + JackPort(int a, const QString& s, int f) { + adr = a; + //name = strdup(s); + name = QString(s); + flags = f; + } + }; + + +static std::list<JackPort> portList; + +//--------------------------------------------------------- +// jackScanMidiPorts +//--------------------------------------------------------- + +void jackScanMidiPorts() +{ + int adr; + const char* name; + + portList.clear(); + adr = 0; + name = strdup("namex"); + portList.push_back(JackPort(adr, name, 0)); + // + // check for devices to add + // + for (std::list<JackPort>::iterator k = portList.begin(); k != portList.end(); ++k) { + iMidiDevice i = midiDevices.begin(); + for (;i != midiDevices.end(); ++i) { + //MidiJackDevice* d = dynamic_cast<MidiJackDevice*>(*i); + break; + //if (d == 0) continue; + //if ((k->adr.client == d->adr.client) && (k->adr.port == d->adr.port)) { + // break; + //} + } + if (i == midiDevices.end()) { + // add device + MidiJackDevice* dev = new MidiJackDevice(k->adr, QString(k->name)); + dev->setrwFlags(k->flags); + midiDevices.add(dev); + } + } +} +*/ + +/* +//--------------------------------------------------------- +// processInput +//--------------------------------------------------------- +static void handle_jack_midi_in(int channel) +{ + MidiRecordEvent event; + int t,n,v; + t = jack_midi_in_data[channel].buffer[0]; + n = jack_midi_in_data[channel].buffer[1]; + v = jack_midi_in_data[channel].buffer[2]; + + event.setType(0); // mark as unused + event.setPort(gmdev->midiPort()); + event.setB(0); + + if(t == 0x90){ // note on + fprintf(stderr, "jackProcessMidiInput note-on\n"); + event.setChannel(channel); + event.setType(ME_NOTEON); + event.setA(n); + event.setB(v); + }else if (t == 0x80){ // note off + fprintf(stderr, "jackProcessMidiInput note-off\n"); + event.setChannel(channel); + event.setType(ME_NOTEOFF); + event.setA(n); + event.setB(v); + }else{ + fprintf(stderr, "WARNING: unknown midi-in on channel %d: %x,%x,%x\n", + channel, t, n, v); + return; + } + if(event.type()){ + gmdev->recordEvent(event); + midiPorts[gmdev->midiPort()].syncInfo().trigActDetect(event.channel()); + } +} + +void MidiJackDevice::processInput() +{ + char buf; + int i,s; + read(gmdev->selectRfd(), &buf, 1); + + s = 1; + for(i = 0; i < JACK_MIDI_CHANNELS; i++){ + if(jack_midi_in_data[i].buffer[3]){ + s = 0; + handle_jack_midi_in(i); + jack_midi_in_data[i].buffer[3] = 0; + } + } +} + +*/ diff --git a/muse2/muse/driver/jackmidi.h b/muse2/muse/driver/jackmidi.h new file mode 100644 index 00000000..12b967a9 --- /dev/null +++ b/muse2/muse/driver/jackmidi.h @@ -0,0 +1,156 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: jackmidi.h,v 1.1.1.1 2010/01/27 09:06:43 terminator356 Exp $ +// (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) +//========================================================= + +#ifndef __JACKMIDI_H__ +#define __JACKMIDI_H__ + +//#include <config.h> + +#include <map> + +#include <jack/jack.h> +#include <jack/midiport.h> + +#include "mididev.h" +#include "route.h" + +class QString; +class MidiFifo; +class MidiRecordEvent; +class MidiPlayEvent; +//class RouteList; +class Xml; + +// Turn on to show multiple devices, work in progress, +// not working fully yet, can't seem to connect... +#define JACK_MIDI_SHOW_MULTIPLE_DEVICES + +// It appears one client port per remote port will be necessary. +// Jack doesn't seem to like manipulation of non-local ports buffers. +#define JACK_MIDI_USE_MULTIPLE_CLIENT_PORTS + +/* jack-midi channels */ +//#define JACK_MIDI_CHANNELS 32 + +/* jack-midi buffer size */ +//#define JACK_MIDI_BUFFER_SIZE 32 + +/* +typedef struct { + int give; + int take; + // 32 parallel midi events, where each event contains three + // midi-bytes and one busy-byte + char buffer[4 * JACK_MIDI_BUFFER_SIZE]; +} muse_jack_midi_buffer; +*/ + +/* +struct JackMidiPort +{ + jack_port_t* _jackPort; + QString _name; + int _flags; // 1 = writable, 2 = readable - do not mix + JackMidiPort(jack_port_t* jp, const QString& s, int f) + { + _jackPort = jp; + _name = QString(s); + _flags = f; + } +}; + +typedef std::map<jack_port_t*, JackMidiPort, std::less<jack_port_t*> >::iterator iJackMidiPort; +typedef std::map<jack_port_t*, JackMidiPort, std::less<jack_port_t*> >::const_iterator ciJackMidiPort; + +class JackMidiPortList : public std::map<jack_port_t*, JackMidiPort, std::less<jack_port_t*> > +{ + private: + static int _nextOutIdNum; + static int _nextInIdNum; + + public: + JackMidiPortList(); + ~JackMidiPortList(); + iJackMidiPort createClientPort(int flags); + bool removeClientPort(jack_port_t* port); +}; + +extern JackMidiPortList jackMidiClientPorts; +*/ + +//--------------------------------------------------------- +// MidiJackDevice +//--------------------------------------------------------- + +class MidiJackDevice : public MidiDevice { + public: + //int adr; + + private: + // fifo for midi events sent from gui + // direct to midi port: + MidiFifo eventFifo; + + //static int _nextOutIdNum; + //static int _nextInIdNum; + + jack_port_t* _client_jackport; + //RouteList _routes; + + virtual QString open(); + virtual void close(); + //bool putEvent(int*); + + void processEvent(const MidiPlayEvent&); + // Port is not midi port, it is the port(s) created for MusE. + bool queueEvent(const MidiPlayEvent&); + + virtual bool putMidiEvent(const MidiPlayEvent&); + //bool sendEvent(const MidiPlayEvent&); + + void eventReceived(jack_midi_event_t*); + + public: + MidiJackDevice() {} + //MidiJackDevice(const int&, const QString& name); + MidiJackDevice(jack_port_t* jack_port, const QString& name); + + static MidiDevice* createJackMidiDevice(QString /*name*/, int /*rwflags*/); // 1:Writable 2: Readable. Do not mix. + + virtual inline int deviceType() { return JACK_MIDI; } + + virtual void setName(const QString&); + + virtual void processMidi(); + virtual ~MidiJackDevice(); + //virtual int selectRfd(); + //virtual int selectWfd(); + //virtual void processInput(); + + virtual void recordEvent(MidiRecordEvent&); + + virtual bool putEvent(const MidiPlayEvent&); + virtual void collectMidiEvents(); + + //virtual jack_port_t* jackPort() { return _jackport; } + //virtual jack_port_t* clientJackPort() { return _client_jackport; } + virtual void* clientPort() { return (void*)_client_jackport; } + + //RouteList* routes() { return &_routes; } + //bool noRoute() const { return _routes.empty(); } + virtual void writeRouting(int, Xml&) const; + }; + +extern bool initMidiJack(); +//extern int jackSelectRfd(); +//extern int jackSelectWfd(); +//extern void jackProcessMidiInput(); +//extern void jackScanMidiPorts(); + +#endif + + diff --git a/muse2/muse/driver/rtctimer.cpp b/muse2/muse/driver/rtctimer.cpp new file mode 100644 index 00000000..1a3cefa6 --- /dev/null +++ b/muse2/muse/driver/rtctimer.cpp @@ -0,0 +1,155 @@ + //========================================================= + // MusE + // Linux Music Editor + // $Id: rtctimer.cpp,v 1.1.2.11 2009/03/09 02:05:18 terminator356 Exp $ + // + // Most code moved from midiseq.cpp by Werner Schweer. + // + // (C) Copyright 2004 Robert Jonsson (rj@spamatica.se) + // (C) Copyright -2004 Werner Schweer (werner@seh.de) + //========================================================= + +#include <linux/version.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) +#include <linux/spinlock.h> +#include <linux/mc146818rtc.h> +#else +#include <linux/rtc.h> +#endif +#include <stdio.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <poll.h> + + +#include "rtctimer.h" +#include "globals.h" +#include "gconfig.h" + + +RtcTimer::RtcTimer() + { + timerFd = -1; + } + +RtcTimer::~RtcTimer() + { + if (timerFd != -1) + close(timerFd); + } + +signed int RtcTimer::initTimer() + { + if(TIMER_DEBUG) + printf("RtcTimer::initTimer()\n"); + if (timerFd != -1) { + fprintf(stderr,"RtcTimer::initTimer(): called on initialised timer!\n"); + return -1; + } + doSetuid(); + + timerFd = ::open("/dev/rtc", O_RDONLY); + if (timerFd == -1) { + fprintf(stderr, "fatal error: open /dev/rtc failed: %s\n", strerror(errno)); + fprintf(stderr, "hint: check if 'rtc' kernel module is loaded, or used by something else\n"); + undoSetuid(); + return timerFd; + } + if (!setTimerFreq(config.rtcTicks)) { + // unable to set timer frequency + return -1; + } + // check if timer really works, start and stop it once. + if (!startTimer()) { + return -1; + } + if (!stopTimer()) { + return -1; + } + return timerFd; + } + +unsigned int RtcTimer::setTimerResolution(unsigned int resolution) + { + if(TIMER_DEBUG) + printf("RtcTimer::setTimerResolution(%d)\n",resolution); + /* The RTC can take power-of-two frequencies from 2 to 8196 Hz. + * It doesn't really have a resolution as such. + */ + return 0; + } + +unsigned int RtcTimer::setTimerFreq(unsigned int freq) + { + int rc = ioctl(timerFd, RTC_IRQP_SET, freq); + if (rc == -1) { + fprintf(stderr, "RtcTimer::setTimerFreq(): cannot set tick on /dev/rtc: %s\n", + strerror(errno)); + fprintf(stderr, " precise timer not available\n"); + return 0; + } + return freq; + } + +unsigned int RtcTimer::getTimerResolution() + { + /* The RTC doesn't really work with a set resolution as such. + * Not sure how this fits into things yet. + */ + return 0; + } + +unsigned int RtcTimer::getTimerFreq() + { + unsigned int freq; + int rv = ioctl(timerFd, RTC_IRQP_READ, &freq); + if (rv < 1) + return 0; + return freq; + } + +bool RtcTimer::startTimer() + { + if(TIMER_DEBUG) + printf("RtcTimer::startTimer()\n"); + if (timerFd == -1) { + fprintf(stderr, "RtcTimer::startTimer(): no timer open to start!\n"); + return false; + } + if (ioctl(timerFd, RTC_PIE_ON, 0) == -1) { + perror("MidiThread: start: RTC_PIE_ON failed"); + undoSetuid(); + return false; + } + return true; + } + +bool RtcTimer::stopTimer() + { + if(TIMER_DEBUG) + printf("RtcTimer::stopTimer\n"); + if (timerFd != -1) { + ioctl(timerFd, RTC_PIE_OFF, 0); + } + else { + fprintf(stderr,"RtcTimer::stopTimer(): no RTC to stop!\n"); + return false; + } + return true; + } + +unsigned int RtcTimer::getTimerTicks(bool /*printTicks*/)// prevent compiler warning: unused parameter + { + if(TIMER_DEBUG) + printf("getTimerTicks()\n"); + unsigned long int nn; + if (timerFd==-1) { + fprintf(stderr,"RtcTimer::getTimerTicks(): no RTC open to read!\n"); + return 0; + } + if (read(timerFd, &nn, sizeof(unsigned long)) != sizeof(unsigned long)) { + fprintf(stderr,"RtcTimer::getTimerTicks(): error reading RTC\n"); + return 0; + } + return nn; + } diff --git a/muse2/muse/driver/rtctimer.h b/muse2/muse/driver/rtctimer.h new file mode 100644 index 00000000..fa58b032 --- /dev/null +++ b/muse2/muse/driver/rtctimer.h @@ -0,0 +1,44 @@ + //========================================================= + // MusE + // Linux Music Editor + // $Id: rtctimer.h,v 1.1.2.3 2005/08/21 18:11:28 spamatica Exp $ + // + // Most code moved from midiseq.cpp + // + // (C) Copyright 2004 Robert Jonsson (rj@spamatica.se) + // (C) Copyright -2004 Werner Schweer (werner@seh.de) + //========================================================= + +#ifndef __RTCTIMER_H__ +#define __RTCTIMER_H__ + +#include "timerdev.h" + + +//--------------------------------------------------------- +// AlsaTimer +//--------------------------------------------------------- + +class RtcTimer : public Timer{ + + + public: + RtcTimer(); + virtual ~RtcTimer(); + + virtual signed int initTimer(); + virtual unsigned int setTimerResolution(unsigned int resolution); + virtual unsigned int getTimerResolution(); + virtual unsigned int setTimerFreq(unsigned int tick); + virtual unsigned int getTimerFreq(); + + virtual bool startTimer(); + virtual bool stopTimer(); + virtual unsigned int getTimerTicks(bool printTicks=false); + + private: + int timerFd; + +}; + +#endif //__ALSATIMER_H__ diff --git a/muse2/muse/driver/timerdev.h b/muse2/muse/driver/timerdev.h new file mode 100644 index 00000000..944bc213 --- /dev/null +++ b/muse2/muse/driver/timerdev.h @@ -0,0 +1,41 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: timerdev.h,v 1.1.2.3 2005/08/21 18:11:28 spamatica Exp $ +// +// Plenty of code borrowed from timer.c example in +// alsalib 1.0.7 +// +// (C) Copyright 2004 Robert Jonsson (rj@spamatica.se) +//========================================================= + +#ifndef __TIMERDEV_H__ +#define __TIMERDEV_H__ + +#include "alsa/asoundlib.h" + +#define TIMER_DEBUG 0 + +//--------------------------------------------------------- +// AlsaTimer +//--------------------------------------------------------- + +class Timer { + + public: + Timer() {}; + virtual ~Timer() {}; + + virtual signed int initTimer() = 0; + virtual unsigned int setTimerResolution(unsigned int resolution) = 0; + virtual unsigned int getTimerResolution() = 0; + virtual unsigned int setTimerFreq(unsigned int freq) = 0; + virtual unsigned int getTimerFreq() = 0; + + virtual bool startTimer() = 0; + virtual bool stopTimer() = 0; + virtual unsigned int getTimerTicks(bool printTicks = false) = 0; + +}; + +#endif //__ALSATIMER_H__ |