summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPepper <pepper@scannerjammer.com>2015-01-16 15:38:48 -0500
committerPepper <pepper@scannerjammer.com>2015-01-16 15:38:48 -0500
commit131832f418372c0654aa4169c4283f863cb96226 (patch)
treec86492441c3de3d13ebe2c2e2d9bc5dcf04aeaf0 /src
first commit
Diffstat (limited to 'src')
-rw-r--r--src/.deps/cli-dssi-host.Po179
-rw-r--r--src/Makefile561
-rw-r--r--src/Makefile.am12
-rw-r--r--src/Makefile.in561
-rwxr-xr-xsrc/cli-dssi-hostbin0 -> 59514 bytes
-rw-r--r--src/cli-dssi-host.c624
-rw-r--r--src/cli-dssi-host.h293
-rw-r--r--src/cli-dssi-host.obin0 -> 113264 bytes
-rw-r--r--src/common.c1386
-rw-r--r--src/common.h102
-rw-r--r--src/lts.prs1
-rw-r--r--src/memb.c286
-rw-r--r--src/memb.h53
-rw-r--r--src/midi/Makefile.am2
-rw-r--r--src/midi/NOTES84
-rw-r--r--src/midi/custom.c325
-rw-r--r--src/midi/custom.h20
-rw-r--r--src/midi/example.midbin0 -> 93565 bytes
-rw-r--r--src/midi/example1.midbin0 -> 9285 bytes
-rw-r--r--src/midi/example2.midbin0 -> 21504 bytes
-rw-r--r--src/midi/fluid_list.c269
-rw-r--r--src/midi/fluid_list.h62
-rw-r--r--src/midi/fluid_midi.c1939
-rw-r--r--src/midi/fluid_midi.h389
-rw-r--r--src/midi/fluidsynth_priv.h280
-rw-r--r--src/midi/old/fluid_conv.c320
-rw-r--r--src/midi/old/fluid_conv.h63
-rw-r--r--src/midi/old/fluid_hash.c1308
-rw-r--r--src/midi/old/fluid_hash.h131
-rw-r--r--src/midi/old/fluid_ringbuffer.c89
-rw-r--r--src/midi/old/fluid_ringbuffer.h128
-rw-r--r--src/midi/old/fluid_settings.c1600
-rw-r--r--src/midi/old/fluid_settings.h56
-rw-r--r--src/midi/old/fluid_sys.c1297
-rw-r--r--src/midi/old/fluid_sys.h448
-rw-r--r--src/output.wavbin0 -> 88620 bytes
-rw-r--r--src/readmidi.c6294
-rw-r--r--src/readmidi.h191
-rw-r--r--src/strtab.c109
-rw-r--r--src/tables.c1685
-rw-r--r--src/tables.h114
41 files changed, 21261 insertions, 0 deletions
diff --git a/src/.deps/cli-dssi-host.Po b/src/.deps/cli-dssi-host.Po
new file mode 100644
index 0000000..1e0896b
--- /dev/null
+++ b/src/.deps/cli-dssi-host.Po
@@ -0,0 +1,179 @@
+cli-dssi-host.o: cli-dssi-host.c /usr/include/stdc-predef.h \
+ cli-dssi-host.h /usr/include/sndfile.h /usr/include/stdio.h \
+ /usr/include/features.h /usr/include/sys/cdefs.h \
+ /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
+ /usr/include/gnu/stubs-64.h \
+ /usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.0/include/stddef.h \
+ /usr/include/bits/types.h /usr/include/bits/typesizes.h \
+ /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
+ /usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.0/include/stdarg.h \
+ /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
+ /usr/include/bits/stdio.h /usr/include/sys/types.h /usr/include/time.h \
+ /usr/include/endian.h /usr/include/bits/endian.h \
+ /usr/include/bits/byteswap.h /usr/include/bits/byteswap-16.h \
+ /usr/include/sys/select.h /usr/include/bits/select.h \
+ /usr/include/bits/sigset.h /usr/include/bits/time.h \
+ /usr/include/sys/sysmacros.h /usr/include/ladspa.h /usr/include/dssi.h \
+ /usr/include/alsa/seq_event.h /usr/include/stdlib.h \
+ /usr/include/alloca.h /usr/include/bits/stdlib-bsearch.h \
+ /usr/include/bits/stdlib-float.h /usr/include/string.h \
+ /usr/include/bits/string.h /usr/include/bits/string2.h \
+ /usr/include/assert.h /usr/include/dlfcn.h /usr/include/bits/dlfcn.h \
+ /usr/include/unistd.h /usr/include/bits/posix_opt.h \
+ /usr/include/bits/confname.h /usr/include/math.h \
+ /usr/include/bits/huge_val.h /usr/include/bits/huge_valf.h \
+ /usr/include/bits/huge_vall.h /usr/include/bits/inf.h \
+ /usr/include/bits/nan.h /usr/include/bits/mathdef.h \
+ /usr/include/bits/mathcalls.h /usr/include/bits/mathinline.h \
+ /usr/include/sys/stat.h /usr/include/bits/stat.h /usr/include/sys/time.h \
+ /usr/include/sys/wait.h /usr/include/signal.h /usr/include/bits/signum.h \
+ /usr/include/bits/sigcontext.h /usr/include/bits/sigstack.h \
+ /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
+ /usr/include/bits/siginfo.h /usr/include/dirent.h \
+ /usr/include/bits/dirent.h /usr/include/bits/posix1_lim.h \
+ /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
+ /usr/include/libgen.h
+
+/usr/include/stdc-predef.h:
+
+cli-dssi-host.h:
+
+/usr/include/sndfile.h:
+
+/usr/include/stdio.h:
+
+/usr/include/features.h:
+
+/usr/include/sys/cdefs.h:
+
+/usr/include/bits/wordsize.h:
+
+/usr/include/gnu/stubs.h:
+
+/usr/include/gnu/stubs-64.h:
+
+/usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.0/include/stddef.h:
+
+/usr/include/bits/types.h:
+
+/usr/include/bits/typesizes.h:
+
+/usr/include/libio.h:
+
+/usr/include/_G_config.h:
+
+/usr/include/wchar.h:
+
+/usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.0/include/stdarg.h:
+
+/usr/include/bits/stdio_lim.h:
+
+/usr/include/bits/sys_errlist.h:
+
+/usr/include/bits/stdio.h:
+
+/usr/include/sys/types.h:
+
+/usr/include/time.h:
+
+/usr/include/endian.h:
+
+/usr/include/bits/endian.h:
+
+/usr/include/bits/byteswap.h:
+
+/usr/include/bits/byteswap-16.h:
+
+/usr/include/sys/select.h:
+
+/usr/include/bits/select.h:
+
+/usr/include/bits/sigset.h:
+
+/usr/include/bits/time.h:
+
+/usr/include/sys/sysmacros.h:
+
+/usr/include/ladspa.h:
+
+/usr/include/dssi.h:
+
+/usr/include/alsa/seq_event.h:
+
+/usr/include/stdlib.h:
+
+/usr/include/alloca.h:
+
+/usr/include/bits/stdlib-bsearch.h:
+
+/usr/include/bits/stdlib-float.h:
+
+/usr/include/string.h:
+
+/usr/include/bits/string.h:
+
+/usr/include/bits/string2.h:
+
+/usr/include/assert.h:
+
+/usr/include/dlfcn.h:
+
+/usr/include/bits/dlfcn.h:
+
+/usr/include/unistd.h:
+
+/usr/include/bits/posix_opt.h:
+
+/usr/include/bits/confname.h:
+
+/usr/include/math.h:
+
+/usr/include/bits/huge_val.h:
+
+/usr/include/bits/huge_valf.h:
+
+/usr/include/bits/huge_vall.h:
+
+/usr/include/bits/inf.h:
+
+/usr/include/bits/nan.h:
+
+/usr/include/bits/mathdef.h:
+
+/usr/include/bits/mathcalls.h:
+
+/usr/include/bits/mathinline.h:
+
+/usr/include/sys/stat.h:
+
+/usr/include/bits/stat.h:
+
+/usr/include/sys/time.h:
+
+/usr/include/sys/wait.h:
+
+/usr/include/signal.h:
+
+/usr/include/bits/signum.h:
+
+/usr/include/bits/sigcontext.h:
+
+/usr/include/bits/sigstack.h:
+
+/usr/include/bits/waitflags.h:
+
+/usr/include/bits/waitstatus.h:
+
+/usr/include/bits/siginfo.h:
+
+/usr/include/dirent.h:
+
+/usr/include/bits/dirent.h:
+
+/usr/include/bits/posix1_lim.h:
+
+/usr/include/bits/local_lim.h:
+
+/usr/include/linux/limits.h:
+
+/usr/include/libgen.h:
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..c163de0
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,561 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# src/Makefile. Generated from Makefile.in by configure.
+
+# Copyright (C) 1994-2013 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.
+
+
+
+
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/cli-dssi-host
+pkgincludedir = $(includedir)/cli-dssi-host
+pkglibdir = $(libdir)/cli-dssi-host
+pkglibexecdir = $(libexecdir)/cli-dssi-host
+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 = :
+bin_PROGRAMS = cli-dssi-host$(EXEEXT)
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/mkinstalldirs $(top_srcdir)/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_cli_dssi_host_OBJECTS = cli-dssi-host.$(OBJEXT)
+cli_dssi_host_OBJECTS = $(am_cli_dssi_host_OBJECTS)
+am__DEPENDENCIES_1 =
+am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+cli_dssi_host_DEPENDENCIES = $(am__DEPENDENCIES_2)
+AM_V_P = $(am__v_P_$(V))
+am__v_P_ = $(am__v_P_$(AM_DEFAULT_VERBOSITY))
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(cli_dssi_host_SOURCES)
+DIST_SOURCES = $(cli_dssi_host_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = aclocal-1.14
+ALSA_CFLAGS = -I/usr/include/alsa
+ALSA_LIBS = -lasound
+AMTAR = $${TAR-tar}
+AM_DEFAULT_VERBOSITY = 1
+AUTOCONF = autoconf
+AUTOHEADER = autoheader
+AUTOMAKE = automake-1.14
+AWK = gawk
+CC = gcc
+CCDEPMODE = depmode=gcc3
+CFLAGS = -g -O2
+CPPFLAGS =
+CYGPATH_W = echo
+DEFS = -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE_URL=\"\" -DPACKAGE=\"cli-dssi-host\" -DVERSION=\"0.1.3\"
+DEPDIR = .deps
+DSSI_CFLAGS =
+DSSI_LIBS =
+ECHO_C =
+ECHO_N = -n
+ECHO_T =
+EXEEXT =
+INSTALL = /usr/bin/install -c
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_SCRIPT = ${INSTALL}
+INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
+LDFLAGS =
+LIBOBJS =
+LIBS =
+LTLIBOBJS =
+MAKEINFO = makeinfo
+MKDIR_P = /usr/bin/mkdir -p
+OBJEXT = o
+PACKAGE = cli-dssi-host
+PACKAGE_BUGREPORT =
+PACKAGE_NAME =
+PACKAGE_STRING =
+PACKAGE_TARNAME =
+PACKAGE_URL =
+PACKAGE_VERSION =
+PATH_SEPARATOR = :
+PKG_CONFIG = /usr/bin/pkg-config
+PKG_CONFIG_LIBDIR =
+PKG_CONFIG_PATH =
+SET_MAKE =
+SHELL = /bin/sh
+SNDFILE_CFLAGS =
+SNDFILE_LIBS = -lsndfile
+STRIP =
+VERSION = 0.1.3
+abs_builddir = /home/pepper/DSSI/dssi-render/cli-dssi-host/src
+abs_srcdir = /home/pepper/DSSI/dssi-render/cli-dssi-host/src
+abs_top_builddir = /home/pepper/DSSI/dssi-render/cli-dssi-host
+abs_top_srcdir = /home/pepper/DSSI/dssi-render/cli-dssi-host
+ac_ct_CC = gcc
+am__include = include
+am__leading_dot = .
+am__quote =
+am__tar = $${TAR-tar} chof - "$$tardir"
+am__untar = $${TAR-tar} xf -
+bindir = ${exec_prefix}/bin
+build_alias =
+builddir = .
+datadir = ${datarootdir}
+datarootdir = ${prefix}/share
+docdir = ${datarootdir}/doc/${PACKAGE}
+dvidir = ${docdir}
+exec_prefix = ${prefix}
+host_alias =
+htmldir = ${docdir}
+includedir = ${prefix}/include
+infodir = ${datarootdir}/info
+install_sh = ${SHELL} /home/pepper/DSSI/dssi-render/cli-dssi-host/install-sh
+libdir = ${exec_prefix}/lib
+libexecdir = ${exec_prefix}/libexec
+localedir = ${datarootdir}/locale
+localstatedir = ${prefix}/var
+mandir = ${datarootdir}/man
+mkdir_p = $(MKDIR_P)
+oldincludedir = /usr/include
+pdfdir = ${docdir}
+prefix = /usr/local
+program_transform_name = s,x,x,
+psdir = ${docdir}
+sbindir = ${exec_prefix}/sbin
+sharedstatedir = ${prefix}/com
+srcdir = .
+sysconfdir = ${prefix}/etc
+target_alias =
+top_build_prefix = ../
+top_builddir = ..
+top_srcdir = ..
+AM_CFLAGS = -Wall -std=c99 $(DSSI_CFLAGS) $(SNDFILE_CFLAGS) $(ALSA_CFLAGS)
+AM_LIBS = $(DSSI_LIBS) $(SNDFILE_LIBS) $(ALSA_LIBS)
+cli_dssi_host_SOURCES = cli-dssi-host.c cli-dssi-host.h
+cli_dssi_host_LDADD = $(AM_LIBS) -lm -ldl
+EXTRA_DIST = lts.prs
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/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):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+cli-dssi-host$(EXEEXT): $(cli_dssi_host_OBJECTS) $(cli_dssi_host_DEPENDENCIES) $(EXTRA_cli_dssi_host_DEPENDENCIES)
+ @rm -f cli-dssi-host$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(cli_dssi_host_OBJECTS) $(cli_dssi_host_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+include ./$(DEPDIR)/cli-dssi-host.Po
+
+.c.o:
+ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+# $(AM_V_CC)source='$<' object='$@' libtool=no \
+# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
+# $(AM_V_CC_no)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+# $(AM_V_CC)source='$<' object='$@' libtool=no \
+# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
+# $(AM_V_CC_no)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ 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-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ 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"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic 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-binPROGRAMS
+
+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
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \
+ clean-binPROGRAMS clean-generic cscopelist-am ctags ctags-am \
+ distclean distclean-compile distclean-generic distclean-tags \
+ distdir dvi dvi-am html html-am info info-am install \
+ install-am install-binPROGRAMS 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 pdf pdf-am \
+ ps ps-am tags tags-am uninstall uninstall-am \
+ uninstall-binPROGRAMS
+
+
+# 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/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..c53180c
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,12 @@
+SUBDIRS = midi
+
+bin_PROGRAMS = cli-dssi-host
+
+AM_CFLAGS = -Wall -std=c99 $(DSSI_CFLAGS) $(SNDFILE_CFLAGS) $(ALSA_CFLAGS)
+AM_LIBS = $(DSSI_LIBS) $(SNDFILE_LIBS) $(ALSA_LIBS)
+
+cli_dssi_host_SOURCES = cli-dssi-host.c cli-dssi-host.h
+cli_dssi_host_LDADD = $(AM_LIBS) -lm -ldl
+cli_dssi_host_LIBADD = midi/libfluidmidi.la
+
+EXTRA_DIST = lts.prs
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..919c67c
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,561 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+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 = :
+bin_PROGRAMS = cli-dssi-host$(EXEEXT)
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/mkinstalldirs $(top_srcdir)/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_cli_dssi_host_OBJECTS = cli-dssi-host.$(OBJEXT)
+cli_dssi_host_OBJECTS = $(am_cli_dssi_host_OBJECTS)
+am__DEPENDENCIES_1 =
+am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+cli_dssi_host_DEPENDENCIES = $(am__DEPENDENCIES_2)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(cli_dssi_host_SOURCES)
+DIST_SOURCES = $(cli_dssi_host_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSSI_CFLAGS = @DSSI_CFLAGS@
+DSSI_LIBS = @DSSI_LIBS@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EXEEXT = @EXEEXT@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+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@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SNDFILE_CFLAGS = @SNDFILE_CFLAGS@
+SNDFILE_LIBS = @SNDFILE_LIBS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+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_alias = @build_alias@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CFLAGS = -Wall -std=c99 $(DSSI_CFLAGS) $(SNDFILE_CFLAGS) $(ALSA_CFLAGS)
+AM_LIBS = $(DSSI_LIBS) $(SNDFILE_LIBS) $(ALSA_LIBS)
+cli_dssi_host_SOURCES = cli-dssi-host.c cli-dssi-host.h
+cli_dssi_host_LDADD = $(AM_LIBS) -lm -ldl
+EXTRA_DIST = lts.prs
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/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):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+cli-dssi-host$(EXEEXT): $(cli_dssi_host_OBJECTS) $(cli_dssi_host_DEPENDENCIES) $(EXTRA_cli_dssi_host_DEPENDENCIES)
+ @rm -f cli-dssi-host$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(cli_dssi_host_OBJECTS) $(cli_dssi_host_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cli-dssi-host.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ 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-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ 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"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic 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-binPROGRAMS
+
+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
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \
+ clean-binPROGRAMS clean-generic cscopelist-am ctags ctags-am \
+ distclean distclean-compile distclean-generic distclean-tags \
+ distdir dvi dvi-am html html-am info info-am install \
+ install-am install-binPROGRAMS 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 pdf pdf-am \
+ ps ps-am tags tags-am uninstall uninstall-am \
+ uninstall-binPROGRAMS
+
+
+# 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/src/cli-dssi-host b/src/cli-dssi-host
new file mode 100755
index 0000000..ff38cba
--- /dev/null
+++ b/src/cli-dssi-host
Binary files differ
diff --git a/src/cli-dssi-host.c b/src/cli-dssi-host.c
new file mode 100644
index 0000000..1f3ac30
--- /dev/null
+++ b/src/cli-dssi-host.c
@@ -0,0 +1,624 @@
+/* cli-dssi-host.c
+ * Copyright (C) 2005 James McDermott
+ * jamesmichaelmcdermott@gmail.com
+ *
+ * This program is derived from jack-dssi-host (Copyright 2004 Chris
+ * Cannam, Steve Harris and Sean Bolton).
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307
+ * USA
+ */
+
+#include "cli-dssi-host.h"
+
+void
+print_usage(void) {
+ fprintf(stderr, "A command-line DSSI host.\n");
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, "$ %s <dssi_plugin.so>[%c<label>]\n", my_name, LABEL_SEP);
+ fprintf(stderr, " [-p [<bank>%c]<preset>] "
+ "(use -p -1 for default port values;\n "
+ "-p -2 for random values; omit -p to read port values from stdin)\n",
+ BANK_SEP);
+ fprintf(stderr, " [-l <length>] (in seconds, between note-on and note-off; default is 1s)\n");
+ fprintf(stderr, " [-r <release_tail>] (in seconds: amount of data to allow after note-off;\n default waits until silence (up to a maximum of 15s))\n");
+ fprintf(stderr, " [-f <output_file.wav>] (default == \"output.wav\")\n");
+ fprintf(stderr, " [-c <no_channels>] (default == 1; use -c -1 to use plugin's channel count)\n");
+ fprintf(stderr, " [-n <midi_note_no>] (default == 60)\n");
+ fprintf(stderr, " [-v <midi_velocity>] (default == 127)\n");
+ fprintf(stderr, " [-d <project_directory>]\n");
+ fprintf(stderr, " [-k <configure_key>%c<value>] ...\n", KEYVAL_SEP);
+ fprintf(stderr, " [-b] (clip out-of-bounds values, including Inf and NaN, to within bounds\n (calls exit()) if -b is omitted)\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv) {
+
+ my_name = basename(argv[0]);
+
+ DSSI_Descriptor_Function descfn;
+ const DSSI_Descriptor *descriptor;
+ LADSPA_Handle instanceHandle;
+ void *pluginObject;
+
+ SNDFILE *outfile;
+ SF_INFO outsfinfo;
+
+ char **configure_key = NULL;
+ char **configure_val = NULL;
+ char *directory = NULL;
+ char *dllName = NULL;
+ char *label;
+ char *output_file = "output.wav";
+ char *projectDirectory = NULL;
+
+ int in, out, controlIn, controlOut;
+ int ins, outs, controlIns, controlOuts;
+ port_vals_source_t src = from_stdin;
+ int nchannels = 1;
+ int midi_velocity = 127;
+ int midi_note = 60;
+ int bank = 0;
+ int program_no = 0;
+ int nkeys = 0;
+ int clip = 0;
+ int have_warned = 0;
+
+
+ size_t length = SAMPLE_RATE;
+ size_t release_tail = -1;
+ size_t nframes = 256;
+ size_t total_written = 0;
+ size_t items_written = 0;
+
+ float **pluginInputBuffers, **pluginOutputBuffers;
+ float *pluginControlIns, *pluginControlOuts;
+
+ sample_rate = SAMPLE_RATE;
+
+ /* Probably an unorthodox srandom() technique... */
+ struct timeval tv;
+ struct timezone tz;
+ gettimeofday(&tv, &tz);
+ srandom(tv.tv_sec + tv.tv_usec);
+
+ if (argc < 2) {
+ print_usage();
+ }
+
+ /* dll name is argv[1]: parse dll name, plus a label if supplied */
+ parse_keyval(argv[1], LABEL_SEP, &dllName, &label);
+
+ for (int i = 2; i < argc; i++) {
+ if (DEBUG) {
+ fprintf(stderr, "%s: processing options: argv[%d] = %s\n",
+ my_name, i, argv[i]);
+ }
+
+ /* Deal with flags */
+ if (!strcmp(argv[i], "-b")) {
+ clip = 1;
+ continue;
+ } else {
+ /* It's not a flag, so expect option + argument */
+ if (argc <= i + 1) print_usage();
+ }
+
+ if (!strcmp(argv[i], "-c")) {
+ nchannels = strtol(argv[++i], NULL, 0);
+ } else if (!strcmp(argv[i], "-f")) {
+ output_file = argv[++i];
+ } else if (!strcmp(argv[i], "-n")) {
+ midi_note = strtol(argv[++i], NULL, 0);
+ } else if (!strcmp(argv[i], "-v")) {
+ midi_velocity = strtol(argv[++i], NULL, 0);
+ } else if (!strcmp(argv[i], "-d")) {
+ projectDirectory = argv[++i];
+ } else if (!strcmp(argv[i], "-l")) {
+ length = sample_rate * strtof(argv[++i], NULL);
+ } else if (!strcmp(argv[i], "-r")) {
+ release_tail = sample_rate * strtof(argv[++i], NULL);
+ } else if (!strcmp(argv[i], "-k")) {
+ configure_key = realloc(configure_key, (nkeys + 1) * sizeof(char *));
+ configure_val = realloc(configure_val, (nkeys + 1) * sizeof(char *));
+ parse_keyval(argv[++i], KEYVAL_SEP, &configure_key[nkeys],
+ &configure_val[nkeys]);
+ nkeys++;
+
+ } else if (!strcmp(argv[i], "-p")) {
+ char *first_str;
+ char *second_str;
+ parse_keyval(argv[++i], BANK_SEP, &first_str, &second_str);
+ if (second_str) {
+ bank = strtol(first_str, NULL, 0);
+ program_no = strtol(second_str, NULL, 0);
+ } else {
+ program_no = strtol(first_str, NULL, 0);
+ bank = 0;
+ }
+ if (program_no == -1) {
+ src = from_defaults;
+ } else if (program_no == -2) {
+ src = from_random;
+ } else {
+ src = from_preset;
+ }
+ } else {
+ fprintf(stderr, "%s: Error: Unknown option: %s\n", my_name, argv[i]);
+ print_usage();
+ }
+ }
+
+ if (DEBUG) {
+ for (int i = 0; i < nkeys; i++) {
+ printf("key %d: %s; value: %s\n", i, configure_key[i],
+ configure_val[i]);
+ }
+ }
+
+ if (DEBUG) {
+ fprintf(stderr, "%s: Cmd-line args ok\n", my_name);
+ }
+
+ directory = load(dllName, &pluginObject, 0);
+ if (!directory || !pluginObject) {
+ fprintf(stderr, "\n%s: Error: Failed to load plugin library \"%s\"\n",
+ my_name, dllName);
+ return 1;
+ }
+
+ descfn = (DSSI_Descriptor_Function)dlsym(pluginObject,
+ "dssi_descriptor");
+
+ if (!descfn) {
+ fprintf(stderr, "%s: Error: Not a DSSI plugin\n", my_name);
+ exit(1);
+ }
+
+
+ /* Get the plugin descriptor and check the run_synth*() function
+ * exists */
+ int j = 0;
+ descriptor = NULL;
+ const DSSI_Descriptor *desc;
+
+ while ((desc = descfn(j++))) {
+ if (!label ||
+ !strcmp(desc->LADSPA_Plugin->Label, label)) {
+ descriptor = desc;
+ break;
+ }
+ }
+
+ if (!descriptor) {
+ fprintf(stderr,
+ "\n%s: Error: Plugin label \"%s\" not found in library \"%s\"\n",
+ my_name, label ? label : "(none)", dllName);
+ return 1;
+ }
+
+ if (!descriptor->run_synth
+ && !descriptor->run_multiple_synths) {
+ fprintf(stderr, "%s: Error: No run_synth() or run_multiple_synths() method in plugin\n", my_name);
+ exit(1);
+ }
+
+ if (!label) {
+ label = strdup(descriptor->LADSPA_Plugin->Label);
+ }
+
+ /* Count number of i/o buffers and ports required */
+ ins = 0;
+ outs = 0;
+ controlIns = 0;
+ controlOuts = 0;
+
+ for (int j = 0; j < descriptor->LADSPA_Plugin->PortCount; j++) {
+ LADSPA_PortDescriptor pod =
+ descriptor->LADSPA_Plugin->PortDescriptors[j];
+
+ if (LADSPA_IS_PORT_AUDIO(pod)) {
+
+ if (LADSPA_IS_PORT_INPUT(pod)) ++ins;
+ else if (LADSPA_IS_PORT_OUTPUT(pod)) ++outs;
+
+ } else if (LADSPA_IS_PORT_CONTROL(pod)) {
+
+ if (LADSPA_IS_PORT_INPUT(pod)) ++controlIns;
+ else if (LADSPA_IS_PORT_OUTPUT(pod)) ++controlOuts;
+ }
+ }
+
+ if (!outs) {
+ fprintf(stderr, "%s: Error: no audio output ports\n", my_name);
+ exit(1);
+ }
+ if (nchannels == -1) {
+ nchannels = outs;
+ }
+
+ /* Create buffers */
+
+ pluginInputBuffers = (float **)malloc(ins * sizeof(float *));
+ pluginControlIns = (float *)calloc(controlIns, sizeof(float));
+
+ pluginOutputBuffers = (float **)malloc(outs * sizeof(float *));
+ pluginControlOuts = (float *)calloc(controlOuts, sizeof(float));
+
+ for (int i = 0; i < outs; i++) {
+ pluginOutputBuffers[i] = (float *)calloc(nframes, sizeof(float));
+ }
+
+
+
+ /* Instantiate plugin */
+
+ instanceHandle = descriptor->LADSPA_Plugin->instantiate
+ (descriptor->LADSPA_Plugin, sample_rate);
+ if (!instanceHandle) {
+ fprintf(stderr,
+ "\n%s: Error: Failed to instantiate instance %d!, plugin \"%s\"\n",
+ my_name, 0, label);
+ return 1;
+ }
+
+ /* Connect ports */
+
+ in = out = controlIn = controlOut = 0;
+ for (int j = 0; j < descriptor->LADSPA_Plugin->PortCount; j++) {
+ /* j is LADSPA port number */
+
+ LADSPA_PortDescriptor pod =
+ descriptor->LADSPA_Plugin->PortDescriptors[j];
+
+ if (LADSPA_IS_PORT_AUDIO(pod)) {
+
+ if (LADSPA_IS_PORT_INPUT(pod)) {
+ descriptor->LADSPA_Plugin->connect_port
+ (instanceHandle, j, pluginInputBuffers[in++]);
+
+ } else if (LADSPA_IS_PORT_OUTPUT(pod)) {
+ descriptor->LADSPA_Plugin->connect_port
+ (instanceHandle, j, pluginOutputBuffers[out++]);
+ }
+
+ } else if (LADSPA_IS_PORT_CONTROL(pod)) {
+
+ if (LADSPA_IS_PORT_INPUT(pod)) {
+
+ descriptor->LADSPA_Plugin->connect_port
+ (instanceHandle, j, &pluginControlIns[controlIn++]);
+
+ } else if (LADSPA_IS_PORT_OUTPUT(pod)) {
+ descriptor->LADSPA_Plugin->connect_port
+ (instanceHandle, j, &pluginControlOuts[controlOut++]);
+ }
+ }
+ } /* 'for (j...' LADSPA port number */
+
+
+
+ /* Set the control port values */
+
+ if (src == from_preset) {
+ /* Set the ports according to a preset */
+ if (descriptor->select_program) {
+ descriptor->select_program(instanceHandle, bank, program_no);
+ }
+ } else {
+ /* Assign values to control ports: defaults, random, or from stdin */
+ controlIn = 0;
+ for (int j = 0; j < descriptor->LADSPA_Plugin->PortCount; j++) {
+ /* j is LADSPA port number */
+
+ LADSPA_PortDescriptor pod =
+ descriptor->LADSPA_Plugin->PortDescriptors[j];
+
+ if (LADSPA_IS_PORT_CONTROL(pod) && LADSPA_IS_PORT_INPUT(pod)) {
+ LADSPA_Data val;
+ if (src == from_defaults) {
+ val = get_port_default(descriptor->LADSPA_Plugin, j);
+ } else if (src == from_stdin) {
+ scanf("%f", &val);
+ } else if (src == from_random) {
+ val = get_port_random(descriptor->LADSPA_Plugin, j);
+ }
+ pluginControlIns[controlIn] = val;
+ controlIn++;
+ }
+ }
+ }
+
+
+ /* It can happen that a control port is set wrongly after
+ * select_program(): for example xsynth-dssi does not set its tuning
+ * port in the select_program() call (which makes sense: xsynth
+ * users might want to be able to keep their current tuning while
+ * changing presets). Here, if we call select_program() we'll get
+ * tuning = 0.0, and we don't get any sound. There might be other
+ * bad effects in other cases. One solution is to read all the
+ * control-in values and if they're not in range, reset them using
+ * get_default().
+ */
+
+ controlIn = 0;
+ for (int j = 0; j < descriptor->LADSPA_Plugin->PortCount; j++) {
+ /* j is LADSPA port number */
+
+ LADSPA_PortDescriptor pod =
+ descriptor->LADSPA_Plugin->PortDescriptors[j];
+
+ if (LADSPA_IS_PORT_CONTROL(pod) && LADSPA_IS_PORT_INPUT(pod)) {
+
+ LADSPA_PortRangeHintDescriptor prhd =
+ descriptor->LADSPA_Plugin->PortRangeHints[j].HintDescriptor;
+ const char * pname = descriptor->LADSPA_Plugin->PortNames[j];
+ LADSPA_Data lb = descriptor->LADSPA_Plugin->
+ PortRangeHints[j].LowerBound;
+ LADSPA_Data ub = descriptor->LADSPA_Plugin->
+ PortRangeHints[j].UpperBound;
+ LADSPA_Data val = pluginControlIns[controlIn];
+ LADSPA_Data def = get_port_default(descriptor->LADSPA_Plugin, j);
+
+ if ((LADSPA_IS_HINT_BOUNDED_BELOW(prhd) && val < lb) ||
+ (LADSPA_IS_HINT_BOUNDED_ABOVE(prhd) && val > ub)) {
+ fprintf(stderr,
+ "%s: Warning: port %d (%s) was %.3f, overriding to %.3f\n",
+ my_name, j, pname, val, def);
+ pluginControlIns[controlIn] = def;
+ }
+ if (DEBUG) {
+ fprintf(stderr,
+ "port %3d; prhd %3d; lb %6.2f; ub %6.2f; val %6.2f (%s)\n",
+ j, prhd, lb, ub, val, pname);
+ }
+ controlIn++;
+ }
+ }
+
+ /* Activate */
+
+ if (descriptor->LADSPA_Plugin->activate) {
+ descriptor->LADSPA_Plugin->activate(instanceHandle);
+ }
+
+
+ /* Configure */
+
+ if (projectDirectory && descriptor->configure) {
+ char *rv = descriptor->configure(instanceHandle,
+ DSSI_PROJECT_DIRECTORY_KEY,
+ projectDirectory);
+ if (rv) {
+ fprintf(stderr,
+ "%s: Warning: plugin doesn't like project directory: \"%s\"\n",
+ my_name, rv);
+ }
+ }
+ if (nkeys && descriptor->configure) {
+ for (int i = 0; i < nkeys; i++) {
+ char *rv = descriptor->configure(instanceHandle,
+ configure_key[i],
+ configure_val[i]);
+ if (rv) {
+ fprintf(stderr,
+ "%s: Warning: plugin doesn't like "
+ "configure key-value pair: \"%s\"\n",
+ my_name, rv);
+ }
+ }
+ }
+
+
+ /* Open sndfile */
+
+ outsfinfo.samplerate = sample_rate;
+ outsfinfo.channels = nchannels;
+ outsfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+ outsfinfo.frames = length;
+
+ outfile = sf_open(output_file, SFM_WRITE, &outsfinfo);
+ if (!outfile) {
+ fprintf(stderr, "%s: Error: Not able to open output file %s.\n",
+ my_name, output_file);
+ fprintf(stderr, "%s: %s\n", my_name, sf_strerror(outfile));
+ return 1;
+ }
+
+
+ /* Instead of creating an alsa midi input, we fill in two events
+ * note-on and note-off */
+
+ snd_seq_event_t on_event, off_event, *current_event; // snd_seq_event_t's so we just need to fill array of those for curently pressed notes and send them. and we can be sure it's a simple array, not some linked list or something? yes because if it were the linked list, it wouldn't have the length passed with it? yep got it
+ on_event.type = SND_SEQ_EVENT_NOTEON;
+ on_event.data.note.channel = 0;
+ on_event.data.note.note = midi_note;
+ on_event.data.note.velocity = midi_velocity;
+ on_event.time.tick = 0;
+
+ off_event.type = SND_SEQ_EVENT_NOTEOFF;
+ off_event.data.note.channel = 0;
+ off_event.data.note.note = midi_note;
+ off_event.data.note.off_velocity = midi_velocity;
+ off_event.time.tick = 0;
+
+ /* Generate the data: send an on-event, wait, send an off-event,
+ wait for release tail to die */
+ total_written = 0;
+ int finished = 0;
+ unsigned long nevents;
+ while (!finished) {
+ if (total_written == 0) {
+ current_event = &on_event; //so this is just putting one in, but that's because it's the first maybe? yes so if there is only one element you can just create a pointer from it and tha't it. but if you have two or more you have to arrange them sequentually in memory, like array. I see what threw me off...ohh because this thing only makes a one note recording...got it
+//well should we check dssi.h just to be sure, and to look at how current_event array needs to be made? no need array is of
+ nevents = 1;
+ } else if (total_written >= length && total_written < length + nframes) {// this condition
+ current_event = &off_event;
+ nevents = 1;
+ } else {
+ current_event = NULL;
+ nevents = 0;
+ }
+
+/* if (DEBUG) { */
+/* fprintf(stderr, "about to call run_synth() or run_multiple_synths() with %ld events\n", nevents); */
+/* } */
+//ok so here it does all of the action...I might be wrong, but it seems to me that it calls run_synth on both the note_on event and the note_off
+//almost so it loops over time, and if note is pressed it set note_on as current_event, and so on. what do you mean by note is pressed?
+//it generates the event from the args I thought? so it looks like then it starts it set event to note_on, then it loops while there is that
+//condition, and it stops then. right...so what we don't know from this source is how to deal with the situation when there are two
+//note_on events overlapping? right, so ideally if plugin can work such way, it can just receive notes and generate complete soundwave with all samples playing at the same time, but if not - most likely you will have to load several such dssi hosts and have it sort of on different channels and then merge output from each plugin into one wave. I doubt the second would be necessary seems much more likely that the plugins are ready to received
+//multiple events simultaneously...I guess what is unclear to me is what should be used for the nframes variable, ohhhhh I see,
+//so it just is going through the midi file, and each event it sees it sends to plugin + nframes for the difference in frames between the two events? maybe the plugin will have to keep a state to know how many note_on and note_off events are currently active? yeah so in midi it's note_on\off pairs which should be processed. as for plugin - it should, but i doubt it does that, need to check plugins code, i think they all so simple they supposed to work in single note mode. well it's a bit confusing because I know for a fact that soundfonts are that simple, they are basically just a sample and little more data, and that fluidsynth+timidity have to create a virtual synth around all of that, but I think the dssi plugins are a bit more sophisticated...should we look at the plugin code? xsynth or something? yep
+//
+//ohh i see it now, it's not event actually to plugin, just a state which is sent every time we call run_synth, so plugin knows with notes
+//are currently pressed. that's good, right? yeah looks So
+//whew, ok so we send a new state every time there's a new event? no we keep iterating over time and every time we need to know which midi notes are pressed, then we construct this "current_event" buffer with events and send them all together to this plugin. right...so when is it that we need to know which notes are pressed? is that only when there's a new event? or is that every set duration of time yes every time we call run_synth, at it's called for every sound pressure sample. 44100 times per second. run synth is called 44100 times per second? well it's executed 44100 times to generate one second of audio output. wow ok
+//what seems to be the easiest way to proceed from here? so need to parse midi file to construct list of events, and then write a loop over time so we could know which notes are pressed, so can generate events for dssi. do you think we need C to parse the midi file? well unless thre is for example perl dssi host it has to be C because need to execute plugin. do you think it might be easier to alter this code and create perl handles for it with something like swig? not really, C code will be easier to write, also you have several projects to choose code from for midi parsing. right...so before we look at that, should we look one last time at dssi to see exactly what the run_synth function takes for args? sure
+
+
+
+ if (descriptor->run_synth) {
+ descriptor->run_synth(instanceHandle,
+ nframes,
+ current_event,
+ nevents);
+ } else if (descriptor->run_multiple_synths) {
+ descriptor->run_multiple_synths(1,
+ &instanceHandle,
+ nframes,
+ &current_event,
+ &nevents);
+ }
+
+ /* Interleaving for libsndfile. */
+ float sf_output[nchannels * nframes];
+ for (int i = 0; i < nframes; i++) {
+ /* First, write all the obvious channels */
+ for (int j = 0; j < min(outs, nchannels); j++) {
+ /* If outs > nchannels, we *could* do mixing - but don't. */
+ sf_output[i * nchannels + j] = pluginOutputBuffers[j][i];
+ }
+ /* Then, if user wants *more* output channels than there are
+ * audio output ports (ie outs < nchannels), copy the last audio
+ * out to all the remaining channels. If outs >= nchannels, this
+ * loop is never entered. */
+ for (int j = outs; j < nchannels; j++) {
+ sf_output[i * nchannels + j] = pluginOutputBuffers[outs - 1][i];
+ }
+ }
+
+ if (clip) {
+ for (int i = 0; i < nframes * nchannels; i++) {
+ if (!finite(sf_output[i])) {
+ if (!have_warned) {
+ have_warned = 1;
+ fprintf(stderr,
+ "%s: Warning: clipping NaN or Inf in synthesized data\n",
+ my_name);
+ }
+ if (sf_output[i] < 0.0f) {
+ sf_output[i] = -1.0f;
+ } else {
+ sf_output[i] = 1.0f;
+ }
+ } else {
+ if (sf_output[i] < -1.0f) {
+ if (!have_warned) {
+ have_warned = 1;
+ fprintf(stderr,
+ "%s: Warning: clipping out-of-bounds value in synthesized data\n",
+ my_name);
+ }
+ sf_output[i] = -1.0f;
+ } else if (sf_output[i] > 1.0f) {
+ if (!have_warned) {
+ have_warned = 1;
+ fprintf(stderr,
+ "%s: Warning: clipping out-of-bounds value in synthesized data\n",
+ my_name);
+ }
+ sf_output[i] = 1.0f;
+ }
+ }
+ }
+ } else {
+ for (int i = 0; i < nframes * nchannels; i++) {
+ if (!finite(sf_output[i])) {
+ fprintf(stderr, "%s: Error: NaN or Inf in synthesized data\n",
+ my_name);
+ exit(1);
+ }
+ if (sf_output[i] > 1.0f
+ || sf_output[i] < -1.0f) {
+ fprintf(stderr, "%s: Error: sample data out of bounds\n",
+ my_name);
+ exit(1);
+ }
+ }
+ }
+
+ /* Write the audio */
+ if ((items_written = sf_writef_float(outfile,
+ sf_output,
+ nframes)) != nframes) {
+ fprintf(stderr, "%s: Error: can't write data to output file %s\n",
+ my_name, output_file);
+ fprintf(stderr, "%s: %s\n", my_name, sf_strerror(outfile));
+ return 1;
+ }
+
+ total_written += items_written;
+ if (release_tail >= 0) {
+ if (total_written > length + release_tail) {
+ finished = 1;
+ }
+ } else {
+ if (total_written > length
+ && is_silent(sf_output, nframes * nchannels)) {
+ finished = 1;
+ } else if (total_written > MAX_LENGTH * sample_rate) {
+ /* The default sineshaper patch never releases, after a note-off,
+ * to silence. So truncate. This is sineshaper 0.3.0 (so maybe it's
+ * different in the new version) and here I mean the default
+ * patch as returned by the get_port_default() function, not the
+ * default set by the sineshaper UI.
+ */
+ finished = 1;
+ fprintf(stderr, "%s: Warning: truncating after writing %d frames\n",
+ my_name, total_written);
+ }
+ }
+ }
+
+ fprintf(stdout, "%s: Wrote %d frames to %s\n",
+ my_name, total_written, output_file);
+
+ sf_close(outfile);
+
+ /* Clean up */
+
+ if (descriptor->LADSPA_Plugin->deactivate) {
+ descriptor->LADSPA_Plugin->deactivate(instanceHandle);
+ }
+
+ if (descriptor->LADSPA_Plugin->cleanup) {
+ descriptor->LADSPA_Plugin->cleanup(instanceHandle);
+ }
+
+ return 0;
+}
+
diff --git a/src/cli-dssi-host.h b/src/cli-dssi-host.h
new file mode 100644
index 0000000..d8a7a15
--- /dev/null
+++ b/src/cli-dssi-host.h
@@ -0,0 +1,293 @@
+//it's most likely defined in one of alsa libs, just copy headers include from here to get it. right but it's
+//a pointer right? it's a structure. do we need to know what's in it? yes we need to fill it porperly for run_synth
+/* cli-dssi-host.h
+ * Copyright (C) 2005 James McDermott
+ * jamesmichaelmcdermott@gmail.com
+ *
+ * This program is derived from jack-dssi-host
+ * (Copyright 2004 Chris Cannam, Steve Harris and Sean Bolton).
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307
+ * USA
+ */
+
+#ifndef _CLI_DSSI_HOST_H
+#define _CLI_DSSI_HOST_H
+
+#define _BSD_SOURCE 1
+#define _SVID_SOURCE 1
+#define _ISOC99_SOURCE 1
+
+
+#define DEBUG 0
+#define MAX_LENGTH (15.0f)
+#define SAMPLE_RATE 44100
+/* character used to separate SO names from plugin labels on command line */
+#define LABEL_SEP ':'
+#define KEYVAL_SEP '='
+#define BANK_SEP ':'
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sndfile.h>
+#include <ladspa.h>
+#include <dssi.h>
+//#include <alsa/asoundlib.h> these two, conflicts with sndfile.h, so if i comment out then souce compiles on my gentoo. is sndfile part of alsa?
+//#include <alsa/seq.h> i guess not. what i found it there is asoundlib.h which include a lot of files from alsa, like alsa/seq.h and others, and
+//looks like we don't need to include it here, it's included from somewhere else too. ok no worries
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <time.h>
+#include <libgen.h>
+
+
+static float sample_rate;
+static int verbose = 0;
+char *my_name;
+
+LADSPA_Data get_port_random(const LADSPA_Descriptor *plugin, int port)
+{
+ LADSPA_PortRangeHint hint = plugin->PortRangeHints[port];
+ float lower = hint.LowerBound *
+ (LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor) ? sample_rate : 1.0f);
+ float upper = hint.UpperBound *
+ (LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor) ? sample_rate : 1.0f);
+
+ /* FIXME: here we assume that ports are bounded, and we do not take the
+ * logarithmic hint into account. */
+
+ float x = random() / (float) RAND_MAX;
+
+ return lower + x * (upper - lower);
+}
+
+LADSPA_Data get_port_default(const LADSPA_Descriptor *plugin, int port)
+{
+ LADSPA_PortRangeHint hint = plugin->PortRangeHints[port];
+ float lower = hint.LowerBound *
+ (LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor) ? sample_rate : 1.0f);
+ float upper = hint.UpperBound *
+ (LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor) ? sample_rate : 1.0f);
+
+ if (!LADSPA_IS_HINT_HAS_DEFAULT(hint.HintDescriptor)) {
+ if (!LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor) ||
+ !LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) {
+ /* No hint, its not bounded, wild guess */
+ return 0.0f;
+ }
+
+ if (lower <= 0.0f && upper >= 0.0f) {
+ /* It spans 0.0, 0.0 is often a good guess */
+ return 0.0f;
+ }
+
+ /* No clues, return minimum */
+ return lower;
+ }
+
+ /* Try all the easy ones */
+
+ if (LADSPA_IS_HINT_DEFAULT_0(hint.HintDescriptor)) {
+ return 0.0f;
+ } else if (LADSPA_IS_HINT_DEFAULT_1(hint.HintDescriptor)) {
+ return 1.0f;
+ } else if (LADSPA_IS_HINT_DEFAULT_100(hint.HintDescriptor)) {
+ return 100.0f;
+ } else if (LADSPA_IS_HINT_DEFAULT_440(hint.HintDescriptor)) {
+ return 440.0f;
+ }
+
+ /* All the others require some bounds */
+
+ if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) {
+ if (LADSPA_IS_HINT_DEFAULT_MINIMUM(hint.HintDescriptor)) {
+ return lower;
+ }
+ }
+ if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) {
+ if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(hint.HintDescriptor)) {
+ return upper;
+ }
+ if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) {
+ if (LADSPA_IS_HINT_LOGARITHMIC(hint.HintDescriptor) &&
+ lower > 0.0f && upper > 0.0f) {
+ if (LADSPA_IS_HINT_DEFAULT_LOW(hint.HintDescriptor)) {
+ return expf(logf(lower) * 0.75f + logf(upper) * 0.25f);
+ } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(hint.HintDescriptor)) {
+ return expf(logf(lower) * 0.5f + logf(upper) * 0.5f);
+ } else if (LADSPA_IS_HINT_DEFAULT_HIGH(hint.HintDescriptor)) {
+ return expf(logf(lower) * 0.25f + logf(upper) * 0.75f);
+ }
+ } else {
+ if (LADSPA_IS_HINT_DEFAULT_LOW(hint.HintDescriptor)) {
+ return lower * 0.75f + upper * 0.25f;
+ } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(hint.HintDescriptor)) {
+ return lower * 0.5f + upper * 0.5f;
+ } else if (LADSPA_IS_HINT_DEFAULT_HIGH(hint.HintDescriptor)) {
+ return lower * 0.25f + upper * 0.75f;
+ }
+ }
+ }
+ }
+
+ /* fallback */
+ return 0.0f;
+}
+
+
+
+
+char *
+load(const char *dllName, void **dll, int quiet) /* returns directory where dll found */
+{
+ static char *defaultDssiPath = 0;
+ const char *dssiPath = getenv("DSSI_PATH");
+ char *path, *origPath, *element;
+ const char *message;
+ void *handle = 0;
+
+ /* If the dllName is an absolute path */
+ if (*dllName == '/') {
+ if ((handle = dlopen(dllName, RTLD_NOW))) { /* real-time programs should not use RTLD_LAZY */
+ *dll = handle;
+ path = strdup(dllName);
+ return dirname(path);
+ } else {
+ if (!quiet) {
+ fprintf(stderr, "%s: Error: Cannot find DSSI or LADSPA plugin at '%s'\n", my_name, dllName);
+ }
+ return NULL;
+ }
+ }
+
+ if (!dssiPath) {
+ if (!defaultDssiPath) {
+ const char *home = getenv("HOME");
+ if (home) {
+ defaultDssiPath = malloc(strlen(home) + 60);
+ sprintf(defaultDssiPath, "/usr/local/lib/dssi:/usr/lib/dssi:%s/.dssi", home);
+ } else {
+ defaultDssiPath = strdup("/usr/local/lib/dssi:/usr/lib/dssi");
+ }
+ }
+ dssiPath = defaultDssiPath;
+ if (!quiet) {
+ fprintf(stderr, "\n%s: Warning: DSSI path not set\n%s: Defaulting to \"%s\"\n\n", my_name, my_name, dssiPath);
+ }
+ }
+
+ path = strdup(dssiPath);
+ origPath = path;
+ *dll = 0;
+
+ while ((element = strtok(path, ":")) != 0) {
+
+ char *filePath;
+
+ path = 0;
+
+ if (element[0] != '/') {
+ if (!quiet) {
+ fprintf(stderr, "%s: Warning: Ignoring relative element \"%s\" in path\n", my_name, element);
+ }
+ continue;
+ }
+
+ if (!quiet && verbose) {
+ fprintf(stderr, "%s: Looking for library \"%s\" in %s... ", my_name, dllName, element);
+ }
+
+ filePath = (char *)malloc(strlen(element) + strlen(dllName) + 2);
+ sprintf(filePath, "%s/%s", element, dllName);
+
+ if ((handle = dlopen(filePath, RTLD_NOW))) { /* real-time programs should not use RTLD_LAZY */
+ if (!quiet && verbose) {
+ fprintf(stderr, "found\n");
+ }
+ *dll = handle;
+ free(filePath);
+ path = strdup(element);
+ free(origPath);
+ return path;
+ }
+
+ if (!quiet && verbose) {
+ message = dlerror();
+ if (message) {
+ fprintf(stderr, "not found: %s\n", message);
+ } else {
+ fprintf(stderr, "not found\n");
+ }
+ }
+
+ free(filePath);
+ }
+
+ free(origPath);
+ return 0;
+}
+
+int
+is_silent(LADSPA_Data *data, size_t length) {
+ float epsilon = 0.01f;
+ float sum = 0.0f;
+
+ for (size_t i = 0; i < length; i++) {
+ sum += fabs(data[i]);
+ }
+ return (sum < epsilon);
+}
+
+inline int
+min(int x, int y) {
+ return (x < y) ? x : y;
+}
+
+
+void
+parse_keyval(char *input, char sep, char **key, char **val) {
+
+ char *tmp = strchr(input, sep);
+ if (tmp) {
+ *key = calloc(1, tmp - input + 1);
+ strncpy(*key, input, tmp - input);
+ *val = strdup(tmp + 1);
+ } else {
+ *key = strdup(input);
+ *val = NULL;
+ }
+}
+
+typedef enum {
+ from_stdin,
+ from_defaults,
+ from_preset,
+ from_random
+} port_vals_source_t;
+
+#endif /* _CLI_DSSI_HOST_H */
+
diff --git a/src/cli-dssi-host.o b/src/cli-dssi-host.o
new file mode 100644
index 0000000..43e65a1
--- /dev/null
+++ b/src/cli-dssi-host.o
Binary files differ
diff --git a/src/common.c b/src/common.c
new file mode 100644
index 0000000..f24dd9a
--- /dev/null
+++ b/src/common.c
@@ -0,0 +1,1386 @@
+/*
+ TiMidity++ -- MIDI to WAVE converter and player
+ Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
+ Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ common.c
+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif /* TIME_WITH_SYS_TIME */
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif /* HAVE_SYS_STAT_H */
+#include <fcntl.h>
+
+#ifndef NO_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+#include <ctype.h>
+#ifndef __W32__
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#else
+#include <process.h>
+#include <io.h>
+#endif /* __W32__ */
+#include "timidity.h"
+#include "common.h"
+#include "output.h"
+#include "controls.h"
+#include "arc.h"
+#include "nkflib.h"
+#include "wrd.h"
+#include "strtab.h"
+#include "support.h"
+
+/* RAND_MAX must defined in stdlib.h
+ * Why RAND_MAX is not defined at SunOS?
+ */
+#if defined(sun) && !defined(SOLARIS) && !defined(RAND_MAX)
+#define RAND_MAX ((1<<15)-1)
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/* #define MIME_CONVERSION */
+
+char *program_name, current_filename[1024];
+MBlockList tmpbuffer;
+char *output_text_code = NULL;
+int open_file_noise_mode = OF_NORMAL;
+
+#ifdef DEFAULT_PATH
+ /* The paths in this list will be tried whenever we're reading a file */
+ static PathList defaultpathlist={DEFAULT_PATH,0};
+ static PathList *pathlist=&defaultpathlist; /* This is a linked list */
+#else
+ static PathList *pathlist=0;
+#endif
+
+const char *note_name[] =
+{
+ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
+};
+
+
+#ifndef TMP_MAX
+#define TMP_MAX 238328
+#endif
+
+int
+tmdy_mkstemp(char *tmpl)
+{
+ char *XXXXXX;
+ static uint32 value;
+ uint32 random_time_bits;
+ int count, fd = -1;
+ int save_errno = errno;
+
+ /* These are the characters used in temporary filenames. */
+ static const char letters[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+ /* This is where the Xs start. */
+ XXXXXX = strstr(tmpl, "XXXXXX");
+ if (XXXXXX == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Get some more or less random data. */
+#if HAVE_GETTIMEOFDAY
+ {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ random_time_bits = (uint32)((tv.tv_usec << 16) ^ tv.tv_sec);
+ }
+#else
+ random_time_bits = (uint32)time(NULL);
+#endif
+
+ value += random_time_bits ^ getpid();
+
+ for (count = 0; count < TMP_MAX; value += 7777, ++count) {
+ uint32 v = value;
+
+ /* Fill in the random bits. */
+ XXXXXX[0] = letters[v % 62];
+ v /= 62;
+ XXXXXX[1] = letters[v % 62];
+ v /= 62;
+ XXXXXX[2] = letters[v % 62];
+
+ v = (v << 16) ^ value;
+ XXXXXX[3] = letters[v % 62];
+ v /= 62;
+ XXXXXX[4] = letters[v % 62];
+ v /= 62;
+ XXXXXX[5] = letters[v % 62];
+
+#if defined(_MSC_VER) || defined(__DMC__)
+#define S_IRUSR 0
+#define S_IWUSR 0
+#endif
+
+ fd = open(tmpl, O_RDWR | O_CREAT | O_EXCL | O_BINARY, S_IRUSR | S_IWUSR);
+
+ if (fd >= 0) {
+ errno = save_errno;
+ return fd;
+ }
+ if (errno != EEXIST)
+ return -1;
+ }
+
+ /* We got out of the loop because we ran out of combinations to try. */
+ errno = EEXIST;
+ return -1;
+}
+
+
+static char *
+url_dumpfile(URL url, const char *ext)
+{
+ char filename[1024];
+ char *tmpdir;
+ int fd;
+ FILE *fp;
+ int n;
+ char buff[BUFSIZ];
+
+#ifdef TMPDIR
+ tmpdir = TMPDIR;
+#else
+ tmpdir = getenv("TMPDIR");
+#endif
+ if(tmpdir == NULL || strlen(tmpdir) == 0)
+ tmpdir = PATH_STRING "tmp" PATH_STRING;
+ if(IS_PATH_SEP(tmpdir[strlen(tmpdir) - 1]))
+ snprintf(filename, sizeof(filename), "%sXXXXXX.%s", tmpdir, ext);
+ else
+ snprintf(filename, sizeof(filename), "%s" PATH_STRING "XXXXXX.%s",
+ tmpdir, ext);
+
+ fd = tmdy_mkstemp(filename);
+
+ if (fd == -1)
+ return NULL;
+
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ close(fd);
+ unlink(filename);
+ return NULL;
+ }
+
+ while((n = url_read(url, buff, sizeof(buff))) > 0) {
+ size_t dummy = fwrite(buff, 1, n, fp); ++dummy;
+ }
+ fclose(fp);
+ return safe_strdup(filename);
+}
+
+
+/* Try to open a file for reading. If the filename ends in one of the
+ defined compressor extensions, pipe the file through the decompressor */
+struct timidity_file *try_to_open(char *name, int decompress)
+{
+ struct timidity_file *tf;
+ URL url;
+ int len;
+
+ if((url = url_arc_open(name)) == NULL)
+ if((url = url_open(name)) == NULL)
+ return NULL;
+
+ tf = (struct timidity_file *)safe_malloc(sizeof(struct timidity_file));
+ tf->url = url;
+ tf->tmpname = NULL;
+
+ len = strlen(name);
+ if(decompress && len >= 3 && strcasecmp(name + len - 3, ".gz") == 0)
+ {
+ int method;
+
+ if(!IS_URL_SEEK_SAFE(tf->url))
+ {
+ if((tf->url = url_cache_open(tf->url, 1)) == NULL)
+ {
+ close_file(tf);
+ return NULL;
+ }
+ }
+
+ method = skip_gzip_header(tf->url);
+ if(method == ARCHIVEC_DEFLATED)
+ {
+ url_cache_disable(tf->url);
+ if((tf->url = url_inflate_open(tf->url, -1, 1)) == NULL)
+ {
+ close_file(tf);
+ return NULL;
+ }
+
+ /* success */
+ return tf;
+ }
+ /* fail */
+ url_rewind(tf->url);
+ url_cache_disable(tf->url);
+ }
+
+#ifdef __W32__
+ /* Sorry, DECOMPRESSOR_LIST and PATCH_CONVERTERS are not worked yet. */
+ return tf;
+#endif /* __W32__ */
+
+#if defined(DECOMPRESSOR_LIST)
+ if(decompress)
+ {
+ static char *decompressor_list[] = DECOMPRESSOR_LIST, **dec;
+ char tmp[1024];
+
+ /* Check if it's a compressed file */
+ for(dec = decompressor_list; *dec; dec += 2)
+ {
+ if(!check_file_extension(name, *dec, 0))
+ continue;
+
+ tf->tmpname = url_dumpfile(tf->url, *dec);
+ if (tf->tmpname == NULL) {
+ close_file(tf);
+ return NULL;
+ }
+
+ url_close(tf->url);
+ snprintf(tmp, sizeof(tmp), *(dec+1), tf->tmpname);
+ if((tf->url = url_pipe_open(tmp)) == NULL)
+ {
+ close_file(tf);
+ return NULL;
+ }
+
+ break;
+ }
+ }
+#endif /* DECOMPRESSOR_LIST */
+
+#if defined(PATCH_CONVERTERS)
+ if(decompress == 2)
+ {
+ static char *decompressor_list[] = PATCH_CONVERTERS, **dec;
+ char tmp[1024];
+
+ /* Check if it's a compressed file */
+ for(dec = decompressor_list; *dec; dec += 2)
+ {
+ if(!check_file_extension(name, *dec, 0))
+ continue;
+
+ tf->tmpname = url_dumpfile(tf->url, *dec);
+ if (tf->tmpname == NULL) {
+ close_file(tf);
+ return NULL;
+ }
+
+ url_close(tf->url);
+ sprintf(tmp, *(dec+1), tf->tmpname);
+ if((tf->url = url_pipe_open(tmp)) == NULL)
+ {
+ close_file(tf);
+ return NULL;
+ }
+
+ break;
+ }
+ }
+#endif /* PATCH_CONVERTERS */
+
+ return tf;
+}
+
+int is_url_prefix(const char *name)
+{
+ int i;
+
+ static char *url_proto_names[] =
+ {
+ "file:",
+#ifdef SUPPORT_SOCKET
+ "http://",
+ "ftp://",
+ "news://",
+#endif /* SUPPORT_SOCKET */
+ "mime:",
+ NULL
+ };
+ for(i = 0; url_proto_names[i]; i++)
+ if(strncmp(name, url_proto_names[i], strlen(url_proto_names[i])) == 0)
+ return 1;
+ return 0;
+}
+
+static int is_abs_path(const char *name)
+{
+#ifndef __MACOS__
+ if (IS_PATH_SEP(name[0]))
+ return 1;
+#else
+ if (!IS_PATH_SEP(name[0]) && strchr(name, PATH_SEP) != NULL)
+ return 1;
+#endif /* __MACOS__ */
+#ifdef __W32__
+ /* [A-Za-z]: (for Windows) */
+ if (isalpha(name[0]) && name[1] == ':')
+ return 1;
+#endif /* __W32__ */
+ if (is_url_prefix(name))
+ return 1; /* assuming relative notation is excluded */
+ return 0;
+}
+
+struct timidity_file *open_with_mem(char *mem, int32 memlen, int noise_mode)
+{
+ URL url;
+ struct timidity_file *tf;
+
+ errno = 0;
+ if((url = url_mem_open(mem, memlen, 0)) == NULL)
+ {
+ if(noise_mode >= 2)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Can't open.");
+ return NULL;
+ }
+ tf = (struct timidity_file *)safe_malloc(sizeof(struct timidity_file));
+ tf->url = url;
+ tf->tmpname = NULL;
+ return tf;
+}
+
+/*
+ * This is meant to find and open files for reading, possibly piping
+ * them through a decompressor.
+ */
+struct timidity_file *open_file(char *name, int decompress, int noise_mode)
+{
+ struct timidity_file *tf;
+ PathList *plp = pathlist;
+ int l;
+
+ open_file_noise_mode = noise_mode;
+ if (!name || !(*name)) {
+ if (noise_mode)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "Attempted to open nameless file.");
+ return 0;
+ }
+ /* First try the given name */
+ strncpy(current_filename, url_unexpand_home_dir(name), 1023);
+ current_filename[1023] = '\0';
+ if (noise_mode)
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Trying to open %s",
+ current_filename);
+ if ((tf = try_to_open(current_filename, decompress)))
+ return tf;
+#ifdef __MACOS__
+ if (errno) {
+#else
+ if (errno && errno != ENOENT) {
+#endif
+ if (noise_mode)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
+ current_filename, strerror(errno));
+ return 0;
+ }
+ if (!is_abs_path(name))
+ while (plp) { /* Try along the path then */
+ *current_filename = 0;
+ if((l = strlen(plp->path))) {
+ strncpy(current_filename, plp->path,
+ sizeof(current_filename));
+ if (!IS_PATH_SEP(current_filename[l - 1])
+ && current_filename[l - 1] != '#'
+ && name[0] != '#')
+ strncat(current_filename, PATH_STRING,
+ sizeof(current_filename)
+ - strlen(current_filename) - 1);
+ }
+ strncat(current_filename, name, sizeof(current_filename)
+ - strlen(current_filename) - 1);
+ if (noise_mode)
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "Trying to open %s", current_filename);
+ if ((tf = try_to_open(current_filename, decompress)))
+ return tf;
+#ifdef __MACOS__
+ if(errno) {
+#else
+ if(errno && errno != ENOENT) {
+#endif
+ if (noise_mode)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
+ current_filename, strerror(errno));
+ return 0;
+ }
+ plp = plp->next;
+ }
+ /* Nothing could be opened. */
+ *current_filename = 0;
+ if (noise_mode >= 2)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s", name,
+ (errno) ? strerror(errno) : "Can't open file");
+ return 0;
+}
+
+/*
+ * This is meant to find and open regular files for reading, possibly
+ * piping them through a decompressor.
+ */
+struct timidity_file *open_file_r(char *name, int decompress, int noise_mode)
+{
+ struct stat st;
+ struct timidity_file *tf;
+ PathList *plp = pathlist;
+ int l;
+
+ open_file_noise_mode = noise_mode;
+ if (!name || !(*name)) {
+ if (noise_mode)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "Attempted to open nameless file.");
+ return 0;
+ }
+ /* First try the given name */
+ strncpy(current_filename, url_unexpand_home_dir(name), 1023);
+ current_filename[1023] = '\0';
+ if (noise_mode)
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Trying to open %s",
+ current_filename);
+ stat(current_filename, &st);
+ if (!S_ISDIR(st.st_mode))
+ if ((tf = try_to_open(current_filename, decompress)))
+ return tf;
+#ifdef __MACOS__
+ if (errno) {
+#else
+ if (errno && errno != ENOENT) {
+#endif
+ if (noise_mode)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
+ current_filename, strerror(errno));
+ return 0;
+ }
+ if (!is_abs_path(name))
+ while (plp) { /* Try along the path then */
+ *current_filename = 0;
+ if((l = strlen(plp->path))) {
+ strncpy(current_filename, plp->path,
+ sizeof(current_filename));
+ if (!IS_PATH_SEP(current_filename[l - 1])
+ && current_filename[l - 1] != '#'
+ && name[0] != '#')
+ strncat(current_filename, PATH_STRING,
+ sizeof(current_filename)
+ - strlen(current_filename) - 1);
+ }
+ strncat(current_filename, name, sizeof(current_filename)
+ - strlen(current_filename) - 1);
+ if (noise_mode)
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "Trying to open %s", current_filename);
+ stat(current_filename, &st);
+ if (!S_ISDIR(st.st_mode))
+ if ((tf = try_to_open(current_filename, decompress)))
+ return tf;
+#ifdef __MACOS__
+ if(errno) {
+#else
+ if(errno && errno != ENOENT) {
+#endif
+ if (noise_mode)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
+ current_filename, strerror(errno));
+ return 0;
+ }
+ plp = plp->next;
+ }
+ /* Nothing could be opened. */
+ *current_filename = 0;
+ if (noise_mode >= 2)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s", name,
+ (errno) ? strerror(errno) : "Can't open file");
+ return 0;
+}
+
+/* This closes files opened with open_file */
+void close_file(struct timidity_file *tf)
+{
+ int save_errno = errno;
+ if(tf->url != NULL)
+ {
+#ifndef __W32__
+ if(tf->tmpname != NULL)
+ {
+ int i;
+ /* dispose the pipe garbage */
+ for(i = 0; tf_getc(tf) != EOF && i < 0xFFFF; i++)
+ ;
+ }
+#endif /* __W32__ */
+ url_close(tf->url);
+ }
+ if(tf->tmpname != NULL)
+ {
+ unlink(tf->tmpname); /* remove temporary file */
+ free(tf->tmpname);
+ }
+ free(tf);
+ errno = save_errno;
+}
+
+/* This is meant for skipping a few bytes. */
+void skip(struct timidity_file *tf, size_t len)
+{
+ url_skip(tf->url, (long)len);
+}
+
+char *tf_gets(char *buff, int n, struct timidity_file *tf)
+{
+ return url_gets(tf->url, buff, n);
+}
+
+long tf_read(void *buff, int32 size, int32 nitems, struct timidity_file *tf)
+{
+ return url_nread(tf->url, buff, size * nitems) / size;
+}
+
+long tf_seek(struct timidity_file *tf, long offset, int whence)
+{
+ long prevpos;
+
+ prevpos = url_seek(tf->url, offset, whence);
+ if(prevpos == -1)
+ ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+ "Warning: Can't seek file position");
+ return prevpos;
+}
+
+long tf_tell(struct timidity_file *tf)
+{
+ long pos;
+
+ pos = url_tell(tf->url);
+ if(pos == -1)
+ {
+ ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+ "Warning: Can't get current file position");
+ return (long)tf->url->nread;
+ }
+
+ return pos;
+}
+
+void safe_exit(int status)
+{
+ if(play_mode->fd != -1)
+ {
+ play_mode->acntl(PM_REQ_DISCARD, NULL);
+ play_mode->close_output();
+ }
+ ctl->close();
+ wrdt->close();
+ exit(status);
+ /*NOTREACHED*/
+}
+
+/* This'll allocate memory or die. */
+void *safe_malloc(size_t count)
+{
+ void *p;
+ static int errflag = 0;
+
+ if(errflag)
+ safe_exit(10);
+ if(count > MAX_SAFE_MALLOC_SIZE)
+ {
+ errflag = 1;
+ ctl->cmsg(CMSG_FATAL, VERB_NORMAL,
+ "Strange, I feel like allocating %d bytes. "
+ "This must be a bug.", count);
+ }
+ else {
+ if(count == 0)
+ /* Some malloc routine return NULL if count is zero, such as
+ * malloc routine from libmalloc.a of Solaris.
+ * But TiMidity doesn't want to return NULL even if count is zero.
+ */
+ count = 1;
+ if((p = (void *)malloc(count)) != NULL)
+ return p;
+ errflag = 1;
+ ctl->cmsg(CMSG_FATAL, VERB_NORMAL,
+ "Sorry. Couldn't malloc %d bytes.", count);
+ }
+#ifdef ABORT_AT_FATAL
+ abort();
+#endif /* ABORT_AT_FATAL */
+ safe_exit(10);
+ /*NOTREACHED*/
+ return 0;
+}
+
+void *safe_large_malloc(size_t count)
+{
+ void *p;
+ static int errflag = 0;
+
+ if(errflag)
+ safe_exit(10);
+ if(count == 0)
+ /* Some malloc routine return NULL if count is zero, such as
+ * malloc routine from libmalloc.a of Solaris.
+ * But TiMidity doesn't want to return NULL even if count is zero.
+ */
+ count = 1;
+ if((p = (void *)malloc(count)) != NULL)
+ return p;
+ errflag = 1;
+ ctl->cmsg(CMSG_FATAL, VERB_NORMAL,
+ "Sorry. Couldn't malloc %d bytes.", count);
+
+#ifdef ABORT_AT_FATAL
+ abort();
+#endif /* ABORT_AT_FATAL */
+ safe_exit(10);
+ /*NOTREACHED*/
+ return 0;
+}
+
+void *safe_realloc(void *ptr, size_t count)
+{
+ void *p;
+ static int errflag = 0;
+
+ if(errflag)
+ safe_exit(10);
+ if(count > MAX_SAFE_MALLOC_SIZE)
+ {
+ errflag = 1;
+ ctl->cmsg(CMSG_FATAL, VERB_NORMAL,
+ "Strange, I feel like allocating %d bytes. "
+ "This must be a bug.", count);
+ }
+ else {
+ if (ptr == NULL)
+ return safe_malloc(count);
+ if(count == 0)
+ /* Some malloc routine return NULL if count is zero, such as
+ * malloc routine from libmalloc.a of Solaris.
+ * But TiMidity doesn't want to return NULL even if count is zero.
+ */
+ count = 1;
+ if((p = (void *)realloc(ptr, count)) != NULL)
+ return p;
+ errflag = 1;
+ ctl->cmsg(CMSG_FATAL, VERB_NORMAL,
+ "Sorry. Couldn't malloc %d bytes.", count);
+ }
+#ifdef ABORT_AT_FATAL
+ abort();
+#endif /* ABORT_AT_FATAL */
+ safe_exit(10);
+ /*NOTREACHED*/
+ return 0;
+}
+
+/* This'll allocate memory or die. */
+char *safe_strdup(const char *s)
+{
+ char *p;
+ static int errflag = 0;
+
+ if(errflag)
+ safe_exit(10);
+
+ if(s == NULL)
+ p = strdup("");
+ else
+ p = strdup(s);
+ if(p != NULL)
+ return p;
+ errflag = 1;
+ ctl->cmsg(CMSG_FATAL, VERB_NORMAL, "Sorry. Couldn't alloc memory.");
+#ifdef ABORT_AT_FATAL
+ abort();
+#endif /* ABORT_AT_FATAL */
+ safe_exit(10);
+ /*NOTREACHED*/
+ return 0;
+}
+
+/* free ((void **)ptr_list)[0..count-1] and ptr_list itself */
+void free_ptr_list(void *ptr_list, int count)
+{
+ int i;
+ for(i = 0; i < count; i++)
+ free(((void **)ptr_list)[i]);
+ free(ptr_list);
+}
+
+static int atoi_limited(const char *string, int v_min, int v_max)
+{
+ int value = atoi(string);
+
+ if (value <= v_min)
+ value = v_min;
+ else if (value > v_max)
+ value = v_max;
+ return value;
+}
+
+int string_to_7bit_range(const char *string_, int *start, int *end)
+{
+ const char *string = string_;
+
+ if(isdigit(*string)) {
+ *start = atoi_limited(string, 0, 127);
+ while(isdigit(*++string)) ;
+ } else
+ *start = 0;
+ if (*string == '-') {
+ string++;
+ *end = isdigit(*string) ? atoi_limited(string, 0, 127) : 127;
+ if(*start > *end)
+ *end = *start;
+ } else
+ *end = *start;
+ return string != string_;
+}
+
+/* This adds a directory to the path list */
+void add_to_pathlist(char *s)
+{
+ PathList *cur, *prev, *plp;
+
+ /* Check duplicated path in the pathlist. */
+ plp = prev = NULL;
+ for(cur = pathlist; cur; prev = cur, cur = cur->next)
+ if(pathcmp(s, cur->path, 0) == 0)
+ {
+ plp = cur;
+ break;
+ }
+
+ if(plp) /* found */
+ {
+ if(prev == NULL) /* first */
+ pathlist = pathlist->next;
+ else
+ prev->next = plp->next;
+ }
+ else
+ {
+ /* Allocate new path */
+ plp = safe_malloc(sizeof(PathList));
+ plp->path = safe_strdup(s);
+ }
+
+ plp->next = pathlist;
+ pathlist = plp;
+}
+
+void clean_up_pathlist(void)
+{
+ PathList *cur, *next;
+
+ cur = pathlist;
+ while (cur) {
+ next = cur->next;
+#ifdef DEFAULT_PATH
+ if (cur == &defaultpathlist) {
+ cur = next;
+ continue;
+ }
+#endif
+ free(cur->path);
+ free(cur);
+ cur = next;
+ }
+
+#ifdef DEFAULT_PATH
+ pathlist = &defaultpathlist;
+#else
+ pathlist = NULL;
+#endif
+}
+
+#ifndef HAVE_VOLATILE
+/*ARGSUSED*/
+int volatile_touch(void *dmy) {return 1;}
+#endif /* HAVE_VOLATILE */
+
+/* code converters */
+static unsigned char
+ w2k[] = {128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
+ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
+ 225,226,247,231,228,229,246,250,233,234,235,236,237,238,239,240,
+ 242,243,244,245,230,232,227,254,251,253,255,249,248,252,224,241,
+ 193,194,215,199,196,197,214,218,201,202,203,204,205,206,207,208,
+ 210,211,212,213,198,200,195,222,219,221,223,217,216,220,192,209};
+
+static void code_convert_cp1251(char *in, char *out, int maxlen)
+{
+ int i;
+ if(out == NULL)
+ out = in;
+ for(i = 0; i < maxlen && in[i]; i++)
+ {
+ if(in[i] & 0200)
+ out[i] = w2k[in[i] & 0177];
+ else
+ out[i] = in[i];
+ }
+ out[i]='\0';
+}
+
+static void code_convert_dump(char *in, char *out, int maxlen, char *ocode)
+{
+ if(ocode == NULL)
+ ocode = output_text_code;
+
+ if(ocode != NULL && ocode != (char *)-1
+ && (strstr(ocode, "ASCII") || strstr(ocode, "ascii")))
+ {
+ int i;
+
+ if(out == NULL)
+ out = in;
+ for(i = 0; i < maxlen && in[i]; i++)
+ if(in[i] < ' ' || in[i] >= 127)
+ out[i] = '.';
+ else
+ out[i] = in[i];
+ out[i]='\0';
+ }
+ else /* "NOCNV" */
+ {
+ if(out == NULL)
+ return;
+ strncpy(out, in, maxlen);
+ out[maxlen] = '\0';
+ }
+}
+
+#ifdef JAPANESE
+static void code_convert_japan(char *in, char *out, int maxlen,
+ char *icode, char *ocode)
+{
+ static char *mode = NULL, *wrd_mode = NULL;
+
+ if(ocode != NULL && ocode != (char *)-1)
+ {
+ nkf_convert(in, out, maxlen, icode, ocode);
+ if(out != NULL)
+ out[maxlen] = '\0';
+ return;
+ }
+
+ if(mode == NULL || wrd_mode == NULL)
+ {
+ mode = output_text_code;
+ if(mode == NULL || strstr(mode, "AUTO"))
+ {
+#ifndef __W32__
+ mode = getenv("LANG");
+#else
+ mode = "SJIS";
+ wrd_mode = "SJISK";
+#endif
+ if(mode == NULL || *mode == '\0')
+ {
+ mode = "ASCII";
+ wrd_mode = mode;
+ }
+ }
+
+ if(strstr(mode, "ASCII") ||
+ strstr(mode, "ascii"))
+ {
+ mode = "ASCII";
+ wrd_mode = mode;
+ }
+ else if(strstr(mode, "NOCNV") ||
+ strstr(mode, "nocnv"))
+ {
+ mode = "NOCNV";
+ wrd_mode = mode;
+ }
+#ifndef HPUX
+ else if(strstr(mode, "EUC") ||
+ strstr(mode, "euc") ||
+ strstr(mode, "ujis") ||
+ strcmp(mode, "japanese") == 0)
+ {
+ mode = "EUC";
+ wrd_mode = "EUCK";
+ }
+ else if(strstr(mode, "SJIS") ||
+ strstr(mode, "sjis"))
+ {
+ mode = "SJIS";
+ wrd_mode = "SJISK";
+ }
+#else
+ else if(strstr(mode, "EUC") ||
+ strstr(mode, "euc") ||
+ strstr(mode, "ujis"))
+ {
+ mode = "EUC";
+ wrd_mode = "EUCK";
+ }
+ else if(strstr(mode, "SJIS") ||
+ strstr(mode, "sjis") ||
+ strcmp(mode, "japanese") == 0)
+ {
+ mode = "SJIS";
+ wrd_mode = "SJISK";
+ }
+#endif /* HPUX */
+ else if(strstr(mode,"JISk")||
+ strstr(mode,"jisk"))
+ {
+ mode = "JISK";
+ wrd_mode = mode;
+ }
+ else if(strstr(mode, "JIS") ||
+ strstr(mode, "jis"))
+ {
+ mode = "JIS";
+ wrd_mode = "JISK";
+ }
+ else if(strcmp(mode, "ja") == 0)
+ {
+ mode = "EUC";
+ wrd_mode = "EUCK";
+ }
+ else
+ {
+ mode = "NOCNV";
+ wrd_mode = mode;
+ }
+ }
+
+ if(ocode == NULL)
+ {
+ if(strcmp(mode, "NOCNV") == 0)
+ {
+ if(out == NULL)
+ return;
+ strncpy(out, in, maxlen);
+ out[maxlen] = '\0';
+ }
+ else if(strcmp(mode, "ASCII") == 0)
+ code_convert_dump(in, out, maxlen, "ASCII");
+ else
+ {
+ nkf_convert(in, out, maxlen, icode, mode);
+ if(out != NULL)
+ out[maxlen] = '\0';
+ }
+ }
+ else if(ocode == (char *)-1)
+ {
+ if(strcmp(wrd_mode, "NOCNV") == 0)
+ {
+ if(out == NULL)
+ return;
+ strncpy(out, in, maxlen);
+ out[maxlen] = '\0';
+ }
+ else if(strcmp(wrd_mode, "ASCII") == 0)
+ code_convert_dump(in, out, maxlen, "ASCII");
+ else
+ {
+ nkf_convert(in, out, maxlen, icode, wrd_mode);
+ if(out != NULL)
+ out[maxlen] = '\0';
+ }
+ }
+}
+#endif /* JAPANESE */
+
+void code_convert(char *in, char *out, int outsiz, char *icode, char *ocode)
+{
+#if !defined(MIME_CONVERSION) && defined(JAPANESE)
+ int i;
+ /* check ASCII string */
+ for(i = 0; in[i]; i++)
+ if(in[i] < ' ' || in[i] >= 127)
+ break;
+ if(!in[i])
+ {
+ if(out == NULL)
+ return;
+ strncpy(out, in, outsiz - 1);
+ out[outsiz - 1] = '\0';
+ return; /* All ASCII string */
+ }
+#endif /* MIME_CONVERSION */
+
+ if(ocode != NULL && ocode != (char *)-1)
+ {
+ if(strcasecmp(ocode, "nocnv") == 0)
+ {
+ if(out == NULL)
+ return;
+ outsiz--;
+ strncpy(out, in, outsiz);
+ out[outsiz] = '\0';
+ return;
+ }
+
+ if(strcasecmp(ocode, "ascii") == 0)
+ {
+ code_convert_dump(in, out, outsiz - 1, "ASCII");
+ return;
+ }
+
+ if(strcasecmp(ocode, "1251") == 0)
+ {
+ code_convert_cp1251(in, out, outsiz - 1);
+ return;
+ }
+ }
+
+#if defined(JAPANESE)
+ code_convert_japan(in, out, outsiz - 1, icode, ocode);
+#else
+ code_convert_dump(in, out, outsiz - 1, ocode);
+#endif
+}
+
+/* EAW -- insert stuff from playlist files
+ *
+ * Tue Apr 6 1999: Modified by Masanao Izumo <mo@goice.co.jp>
+ * One pass implemented.
+ */
+static char **expand_file_lists(char **files, int *nfiles_in_out)
+{
+ int nfiles;
+ int i;
+ char input_line[256];
+ char *pfile;
+ static const char *testext = ".m3u.pls.asx.M3U.PLS.ASX.tpl";
+ struct timidity_file *list_file;
+ char *one_file[1];
+ int one;
+
+ /* Recusive global */
+ static StringTable st;
+ static int error_outflag = 0;
+ static int depth = 0;
+
+ if(depth >= 16)
+ {
+ if(!error_outflag)
+ {
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "Probable loop in playlist files");
+ error_outflag = 1;
+ }
+ return NULL;
+ }
+
+ if(depth == 0)
+ {
+ error_outflag = 0;
+ init_string_table(&st);
+ }
+ nfiles = *nfiles_in_out;
+
+ /* Expand playlist recursively */
+ for(i = 0; i < nfiles; i++)
+ {
+ /* extract the file extension */
+ pfile = strrchr(files[i], '.');
+
+ if(*files[i] == '@' || (pfile != NULL && strstr(testext, pfile)))
+ {
+ /* Playlist file */
+ if(*files[i] == '@')
+ list_file = open_file(files[i] + 1, 1, 1);
+ else
+ list_file = open_file(files[i], 1, 1);
+ if(list_file)
+ {
+ while(tf_gets(input_line, sizeof(input_line), list_file)
+ != NULL) {
+ if(*input_line == '\n' || *input_line == '\r')
+ continue;
+ if((pfile = strchr(input_line, '\r')))
+ *pfile = '\0';
+ if((pfile = strchr(input_line, '\n')))
+ *pfile = '\0';
+ one_file[0] = input_line;
+ one = 1;
+ depth++;
+ expand_file_lists(one_file, &one);
+ depth--;
+ }
+ close_file(list_file);
+ }
+ }
+ else /* Other file */
+ put_string_table(&st, files[i], strlen(files[i]));
+ }
+
+ if(depth)
+ return NULL;
+ *nfiles_in_out = st.nstring;
+ return make_string_array(&st);
+}
+
+char **expand_file_archives(char **files, int *nfiles_in_out)
+{
+ int nfiles;
+ char **new_files;
+ int new_nfiles;
+
+ /* First, expand playlist files */
+ nfiles = *nfiles_in_out;
+ files = expand_file_lists(files, &nfiles);
+ if(files == NULL)
+ {
+ *nfiles_in_out = 0;
+ return NULL;
+ }
+
+ /* Second, expand archive files */
+ new_nfiles = nfiles;
+ open_file_noise_mode = OF_NORMAL;
+ new_files = expand_archive_names(&new_nfiles, files);
+ free(files[0]);
+ free(files);
+
+ *nfiles_in_out = new_nfiles;
+ return new_files;
+}
+
+#ifdef RAND_MAX
+int int_rand(int n)
+{
+ if(n < 0)
+ {
+ if(n == -1)
+ srand(time(NULL));
+ else
+ srand(-n);
+ return n;
+ }
+ return (int)(n * (double)rand() * (1.0 / (RAND_MAX + 1.0)));
+}
+#else
+int int_rand(int n)
+{
+ static unsigned long rnd_seed = 0xabcd0123;
+
+ if(n < 0)
+ {
+ if(n == -1)
+ rnd_seed = time(NULL);
+ else
+ rnd_seed = -n;
+ return n;
+ }
+
+ rnd_seed *= 69069UL;
+ return (int)(n * (double)(rnd_seed & 0xffffffff) *
+ (1.0 / (0xffffffff + 1.0)));
+}
+#endif /* RAND_MAX */
+
+int check_file_extension(char *filename, char *ext, int decompress)
+{
+ int len, elen, i;
+#if defined(DECOMPRESSOR_LIST)
+ char *dlist[] = DECOMPRESSOR_LIST;
+#endif /* DECOMPRESSOR_LIST */
+
+ len = strlen(filename);
+ elen = strlen(ext);
+ if(len > elen && strncasecmp(filename + len - elen, ext, elen) == 0)
+ return 1;
+
+ if(decompress)
+ {
+ /* Check gzip'ed file name */
+
+ if(len > 3 + elen &&
+ strncasecmp(filename + len - elen - 3 , ext, elen) == 0 &&
+ strncasecmp(filename + len - 3, ".gz", 3) == 0)
+ return 1;
+
+#if defined(DECOMPRESSOR_LIST)
+ for(i = 0; dlist[i]; i += 2)
+ {
+ int dlen;
+
+ dlen = strlen(dlist[i]);
+ if(len > dlen + elen &&
+ strncasecmp(filename + len - elen - dlen , ext, elen) == 0 &&
+ strncasecmp(filename + len - dlen, dlist[i], dlen) == 0)
+ return 1;
+ }
+#endif /* DECOMPRESSOR_LIST */
+ }
+ return 0;
+}
+
+void randomize_string_list(char **strlist, int n)
+{
+ int i, j;
+ char *tmp;
+ for(i = 0; i < n; i++)
+ {
+ j = int_rand(n - i);
+ tmp = strlist[j];
+ strlist[j] = strlist[n - i - 1];
+ strlist[n - i - 1] = tmp;
+ }
+}
+
+int pathcmp(const char *p1, const char *p2, int ignore_case)
+{
+ int c1, c2;
+
+#ifdef __W32__
+ ignore_case = 1; /* Always ignore the case */
+#endif
+
+ do {
+ c1 = *p1++ & 0xff;
+ c2 = *p2++ & 0xff;
+ if(ignore_case)
+ {
+ c1 = tolower(c1);
+ c2 = tolower(c2);
+ }
+ if(IS_PATH_SEP(c1)) c1 = *p1 ? 0x100 : 0;
+ if(IS_PATH_SEP(c2)) c2 = *p2 ? 0x100 : 0;
+ } while(c1 == c2 && c1 /* && c2 */);
+
+ return c1 - c2;
+}
+
+static int pathcmp_qsort(const char **p1,
+ const char **p2)
+{
+ return pathcmp(*(const char **)p1, *(const char **)p2, 1);
+}
+
+void sort_pathname(char **files, int nfiles)
+{
+ qsort(files, nfiles, sizeof(char *),
+ (int (*)(const void *, const void *))pathcmp_qsort);
+}
+
+char *pathsep_strchr(const char *path)
+{
+#ifdef PATH_SEP2
+ while(*path)
+ {
+ if(*path == PATH_SEP || *path == PATH_SEP2)
+ return (char *)path;
+ path++;
+ }
+ return NULL;
+#else
+ return strchr(path, PATH_SEP);
+#endif
+}
+
+char *pathsep_strrchr(const char *path)
+{
+#ifdef PATH_SEP2
+ char *last_sep = NULL;
+ while(*path)
+ {
+ if(*path == PATH_SEP || *path == PATH_SEP2)
+ last_sep = (char *)path;
+ path++;
+ }
+ return last_sep;
+#else
+ return strrchr(path, PATH_SEP);
+#endif
+}
+
+int str2mID(char *str)
+{
+ int val;
+
+ if(strncasecmp(str, "gs", 2) == 0)
+ val = 0x41;
+ else if(strncasecmp(str, "xg", 2) == 0)
+ val = 0x43;
+ else if(strncasecmp(str, "gm", 2) == 0)
+ val = 0x7e;
+ else
+ {
+ int i, v;
+ val = 0;
+ for(i = 0; i < 2; i++)
+ {
+ v = str[i];
+ if('0' <= v && v <= '9')
+ v = v - '0';
+ else if('A' <= v && v <= 'F')
+ v = v - 'A' + 10;
+ else if('a' <= v && v <= 'f')
+ v = v - 'a' + 10;
+ else
+ return 0;
+ val = (val << 4 | v);
+ }
+ }
+ return val;
+}
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..d5ed02d
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,102 @@
+/*
+ TiMidity++ -- MIDI to WAVE converter and player
+ Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
+ Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ common.h
+*/
+
+#ifndef ___COMMON_H_
+#define ___COMMON_H_
+
+#include "sysdep.h"
+#include "url.h"
+#include "mblock.h"
+
+extern char *program_name, current_filename[];
+extern const char *note_name[];
+
+typedef struct {
+ char *path;
+ void *next;
+} PathList;
+
+struct timidity_file
+{
+ URL url;
+ char *tmpname;
+};
+
+/* Noise modes for open_file */
+#define OF_SILENT 0
+#define OF_NORMAL 1
+#define OF_VERBOSE 2
+
+
+extern void add_to_pathlist(char *s);
+extern void clean_up_pathlist(void);
+extern int is_url_prefix(const char *name);
+extern struct timidity_file *open_file(char *name, int decompress,
+ int noise_mode);
+extern struct timidity_file *open_file_r(char *name, int decompress,
+ int noise_mode);
+extern struct timidity_file *open_with_mem(char *mem, int32 memlen,
+ int noise_mode);
+extern void close_file(struct timidity_file *tf);
+extern void skip(struct timidity_file *tf, size_t len);
+extern char *tf_gets(char *buff, int n, struct timidity_file *tf);
+#define tf_getc(tf) (url_getc((tf)->url))
+extern long tf_read(void *buff, int32 size, int32 nitems,
+ struct timidity_file *tf);
+extern long tf_seek(struct timidity_file *tf, long offset, int whence);
+extern long tf_tell(struct timidity_file *tf);
+extern int int_rand(int n); /* random [0..n-1] */
+extern int check_file_extension(char *filename, char *ext, int decompress);
+
+extern void *safe_malloc(size_t count);
+extern void *safe_realloc(void *old_ptr, size_t new_size);
+extern void *safe_large_malloc(size_t count);
+extern char *safe_strdup(const char *s);
+extern void free_ptr_list(void *ptr_list, int count);
+extern int string_to_7bit_range(const char *s, int *start, int *end);
+extern char **expand_file_archives(char **files, int *nfiles_in_out);
+extern void randomize_string_list(char **strlist, int nstr);
+extern int pathcmp(const char *path1, const char *path2, int ignore_case);
+extern void sort_pathname(char **files, int nfiles);
+extern int load_table(char *file);
+extern char *pathsep_strrchr(const char *path);
+extern char *pathsep_strchr(const char *path);
+extern int str2mID(char *str);
+
+
+/* code:
+ * "EUC" - Extended Unix Code
+ * NULL - Auto conversion
+ * "JIS" - Japanese Industrial Standard code
+ * "SJIS" - shift-JIS code
+ */
+extern void code_convert(char *in, char *out, int outsiz,
+ char *in_code, char *out_code);
+
+extern void safe_exit(int status);
+
+extern char *timidity_version;
+extern MBlockList tmpbuffer;
+extern char *output_text_code;
+
+#endif /* ___COMMON_H_ */
diff --git a/src/lts.prs b/src/lts.prs
new file mode 100644
index 0000000..b22fb23
--- /dev/null
+++ b/src/lts.prs
@@ -0,0 +1 @@
+440.0 0.1 0.3 0.3 0.3 0.9
diff --git a/src/memb.c b/src/memb.c
new file mode 100644
index 0000000..4b2ad61
--- /dev/null
+++ b/src/memb.c
@@ -0,0 +1,286 @@
+/*
+ TiMidity++ -- MIDI to WAVE converter and player
+ Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
+ Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef NO_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+#include "timidity.h"
+#include "memb.h"
+
+void init_memb(MemBuffer *b)
+{
+ memset(b, 0, sizeof(MemBuffer));
+}
+
+void delete_memb(MemBuffer *b)
+{
+ reuse_mblock(&b->pool);
+ memset(b, 0, sizeof(MemBuffer));
+}
+
+void rewind_memb(MemBuffer *b)
+{
+ if(b->head != NULL)
+ {
+ b->cur = b->head;
+ b->cur->pos = 0;
+ }
+}
+
+void push_memb(MemBuffer *b, char *buff, long buff_size)
+{
+ b->total_size += buff_size;
+ if(b->head == NULL)
+ {
+ b->head = b->tail = b->cur =
+ (MemBufferNode *)new_segment(&b->pool, MIN_MBLOCK_SIZE);
+ b->head->next = NULL;
+ b->head->size = b->head->pos = 0;
+ }
+ while(buff_size > 0)
+ {
+ long n;
+ MemBufferNode *p;
+
+ p = b->tail;
+ n = (long)(MEMBASESIZE - p->size);
+ if(n == 0)
+ {
+ p = (MemBufferNode *)new_segment(&b->pool, MIN_MBLOCK_SIZE);
+ b->tail->next = p;
+ b->tail = p;
+ p->next = NULL;
+ p->size = p->pos = 0;
+ n = MEMBASESIZE;
+ }
+ if(n > buff_size)
+ n = buff_size;
+ memcpy(p->base + p->size, buff, n);
+ p->size += n;
+ buff_size -= n;
+ buff += n;
+ }
+}
+
+long read_memb(MemBuffer *b, char *buff, long buff_size)
+{
+ long n;
+
+ if(b->head == NULL)
+ return 0;
+ if(b->cur == NULL)
+ rewind_memb(b);
+ if(b->cur->next == NULL && b->cur->pos == b->cur->size)
+ return 0;
+
+ n = 0;
+ while(n < buff_size)
+ {
+ long i;
+ MemBufferNode *p;
+
+ p = b->cur;
+ if(p->pos == p->size)
+ {
+ if(p->next == NULL)
+ break;
+ b->cur = p->next;
+ b->cur->pos = 0;
+ continue;
+ }
+
+ i = p->size - p->pos;
+ if(i > buff_size - n)
+ i = buff_size - n;
+ memcpy(buff + n, p->base + p->pos, i);
+ n += i;
+ p->pos += i;
+ }
+ return n;
+}
+
+long skip_read_memb(MemBuffer *b, long size)
+{
+ long n;
+
+ if(size <= 0 || b->head == NULL)
+ return 0;
+ if(b->cur == NULL)
+ rewind_memb(b);
+ if(b->cur->next == NULL && b->cur->pos == b->cur->size)
+ return 0;
+
+ n = 0;
+ while(n < size)
+ {
+ long i;
+ MemBufferNode *p;
+
+ p = b->cur;
+ if(p->pos == p->size)
+ {
+ if(p->next == NULL)
+ break;
+ b->cur = p->next;
+ b->cur->pos = 0;
+ continue;
+ }
+
+ i = p->size - p->pos;
+ if(i > size - n)
+ i = size - n;
+ n += i;
+ p->pos += i;
+ }
+ return n;
+}
+
+typedef struct _URL_memb
+{
+ char common[sizeof(struct _URL)];
+ MemBuffer *b;
+ long pos;
+ int autodelete;
+} URL_memb;
+
+static long url_memb_read(URL url, void *buff, long n);
+static int url_memb_fgetc(URL url);
+static long url_memb_seek(URL url, long offset, int whence);
+static long url_memb_tell(URL url);
+static void url_memb_close(URL url);
+
+URL memb_open_stream(MemBuffer *b, int autodelete)
+{
+ URL_memb *url;
+
+ url = (URL_memb *)alloc_url(sizeof(URL_memb));
+ if(url == NULL)
+ {
+ if(autodelete)
+ delete_memb(b);
+ url_errno = errno;
+ return NULL;
+ }
+
+ /* common members */
+ URLm(url, type) = URL_extension_t;
+ URLm(url, url_read) = url_memb_read;
+ URLm(url, url_gets) = NULL;
+ URLm(url, url_fgetc) = url_memb_fgetc;
+ URLm(url, url_seek) = url_memb_seek;
+ URLm(url, url_tell) = url_memb_tell;
+ URLm(url, url_close) = url_memb_close;
+
+ /* private members */
+ url->b = b;
+ url->pos = 0;
+ url->autodelete = autodelete;
+
+ rewind_memb(b);
+ return (URL)url;
+}
+
+static long url_memb_read(URL url, void *buff, long n)
+{
+ URL_memb *urlp = (URL_memb *)url;
+ if((n = read_memb(urlp->b, buff, n)) > 0)
+ urlp->pos += n;
+ return n;
+}
+
+static int url_memb_fgetc(URL url)
+{
+ URL_memb *urlp = (URL_memb *)url;
+ MemBuffer *b = urlp->b;
+ MemBufferNode *p;
+
+ p = b->cur;
+ if(p == NULL)
+ return EOF;
+ while(p->pos == p->size)
+ {
+ if(p->next == NULL)
+ return EOF;
+ p = b->cur = p->next;
+ p->pos = 0;
+ }
+ urlp->pos++;
+ return (int)((unsigned char *)p->base)[p->pos++];
+}
+
+static long url_memb_seek(URL url, long offset, int whence)
+{
+ URL_memb *urlp = (URL_memb *)url;
+ MemBuffer *b = urlp->b;
+ long ret, newpos = 0, n;
+
+ ret = urlp->pos;
+ switch(whence)
+ {
+ case SEEK_SET:
+ newpos = offset;
+ break;
+ case SEEK_CUR:
+ newpos = ret + offset;
+ break;
+ case SEEK_END:
+ newpos = b->total_size + offset;
+ break;
+ }
+ if(newpos < 0)
+ newpos = 0;
+ else if(newpos > b->total_size)
+ newpos = b->total_size;
+ n = newpos - ret;
+ if(n < 0)
+ {
+ rewind_memb(b);
+ n = newpos;
+ urlp->pos = 0;
+ }
+
+ urlp->pos += skip_read_memb(b, n);
+ return ret;
+}
+
+static long url_memb_tell(URL url)
+{
+ return ((URL_memb *)url)->pos;
+}
+
+static void url_memb_close(URL url)
+{
+ URL_memb *urlp = (URL_memb *)url;
+ if(urlp->autodelete)
+ {
+ delete_memb(urlp->b);
+ free(urlp->b);
+ }
+ free(url);
+}
diff --git a/src/memb.h b/src/memb.h
new file mode 100644
index 0000000..58cace7
--- /dev/null
+++ b/src/memb.h
@@ -0,0 +1,53 @@
+/*
+ TiMidity++ -- MIDI to WAVE converter and player
+ Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
+ Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#ifndef ___MEMB_H_
+#define ___MEMB_H_
+
+#include "mblock.h"
+#include "url.h"
+typedef struct _MemBufferNode
+{
+ struct _MemBufferNode *next; /* link to next buffer node */
+ int size; /* size of base */
+ int pos; /* current read position */
+ char base[1];
+} MemBufferNode;
+#define MEMBASESIZE (MIN_MBLOCK_SIZE - sizeof(MemBufferNode))
+
+typedef struct _MemBuffer
+{
+ MemBufferNode *head; /* start buffer node pointer */
+ MemBufferNode *tail; /* last buffer node pointer */
+ MemBufferNode *cur; /* current buffer node pointer */
+ long total_size;
+ MBlockList pool;
+} MemBuffer;
+
+extern void init_memb(MemBuffer *b);
+extern void push_memb(MemBuffer *b, char *buff, long buff_size);
+extern long read_memb(MemBuffer *b, char *buff, long buff_size);
+extern long skip_read_memb(MemBuffer *b, long size);
+extern void rewind_memb(MemBuffer *b);
+extern void delete_memb(MemBuffer *b);
+extern URL memb_open_stream(MemBuffer *b, int autodelete);
+
+#endif /* ___MEMB_H_ */
diff --git a/src/midi/Makefile.am b/src/midi/Makefile.am
new file mode 100644
index 0000000..6473df4
--- /dev/null
+++ b/src/midi/Makefile.am
@@ -0,0 +1,2 @@
+noinst_LTLIBRARIES = libfluidmidi.la
+libfluidmidi_la_SOURCES = fluid_midi.c fluid_list.c custom.c
diff --git a/src/midi/NOTES b/src/midi/NOTES
new file mode 100644
index 0000000..4bd7b67
--- /dev/null
+++ b/src/midi/NOTES
@@ -0,0 +1,84 @@
+
+ //unsigned long nevents; weird...so the plugin must know about previous notes that are pressed? yes for each call run_synth it should know about note which are currently pressed. oh but there isn't any place to indicate that? seems like we just need
+ //to keep track of how many notes are currently running and pass it an unsigned long that has that count, but aside from that,
+ //it just takes note_on and note_off structs, which refer to only one note at a time? not really,
+ //so in C to pass an array we have to use two variables onw is a pointer to data, another is number of elements in that array. so run_synth is one of such functions, and if you have two notes pressed you send array of size 2, storing two elements nearby in memory and setting nevents to 2. got it, so current_event isn't a pointer to a struct, it's a pointer to an array of event structs, most likely? yes
+//
+//so what do we do next? we need to construct this array of currently pressed notes, so need to check events and try to construc this.
+
+
+
+
+//ok moving on...I did a bit of research about midi types...basically the reason mrswatson only supports type0 midi is that
+//the idea behind type0 midi is that it is single-channel. Here channel is a bit of a confusing word, because there is also such a thing as an audio channel, obviously
+//but the two are completely unrelated. Channel is like a track in multitrack recording, type0 midi only has one of these tracks, but potentially has program
+//changes within it (program change is just a change of voice). so what we're working on here only handles single channel midi as well, and that's good.
+//Only thing to think about is, if we abstract our loop to process the note events in the channel, rather than the note events in the entire file (unfortunately confusing
+//terminology again, because in midifiles, the entire file is called a "track"). But this is basically the situation.
+//
+//Midi events come in a few different types, but basically there are three types that are important
+//
+//meta type events
+//
+//channel events (events particular to a single channel)
+//
+//track events (events that apply to every channel)
+//
+//so meta type events are sort of special events,
+//
+//channel events and track events are more like "regular" events....
+//
+//do you follow so far? yep
+//
+//
+//
+//So the main question to ask you is, since we are iterating through all events, how do we handle multiple channels? we have the option to do what mrswatson has done
+//which is to read the midi file type from the header and end in error if it is anything but type0, or we can sort of process the channels individually in the sense that we send appropriate info to the plugin, telling it which channel it is intended for. Most plugins though don't care about multiple channels
+//
+//another option, more related to the first is to write a simple script (or find one) that separates out multi-channel midi into individual type0 files.
+//
+//so with multi channel files, if events are sorted by time in file, then we can just send them all at once as it was one channel, so plugin will produce resulting file.
+//if that's the plan, I guess we should check to make sure that dssi doesn't support multiple channels, because if it does, maybe we should tell the plugin which channel each event is intended for? i think snd_seq_event_t has that filed for channel, so we just need to copy that info there.
+//
+//
+//
+//ok do we break now? seems a bit late yeah, so basically what is left to do is to write it from pseudo code to real code of this function, add pritngin to see how it works and if there is any bugs to fix them.
+//ok then make this into a module, add it to cli-dssi-host and change cli-dssi-host source to deal with this loop? yep. got it. maybe we can look into this tomorrow morning? sure ok perfect
+
+
+
+ //ok so what were you thinking here?
+ //ok and finally that run_synth need array which is not sparse, where elements are without spaces, so either you rebuild
+ //new array every time you call run_synth, or keep elements together and use find function to locate a note. find function
+ //sounds best, either way, do we need two arrays? no need
+
+ //with this, can I assign sparsely to it, if I want to put the notes[100] = 4; , will that cause an error?
+ //so first is that you need to put size of memory you use in malloc, not e lemtns, like that, right? yep
+ //second is that we need snd_seq_event_t intead of ints. I was just thinking that if the note is pressed, it wouldn
+ //have a value , if not it wouldn't so it would be an easy way to keep track of which are pressed or not? hmm sort of short lookup arraly, might work if built properly. ok well I don't know if that's right I'll make another array
+//question is, if we're sending a list, and a NOTE_ON and NOTE_OFF must be removed from the list once it has been already processed, how do we access those elements? by note name somehow? yeah need to track each note on\off pair.
+//no clue how to do this in C, easy in any other language, just use an associative array
+//right in C it turns into function which finds an element in array. how big should our array be? do we use the heap here? ideally we need to use it, but guess we can limit number of notes which are pressed at same time, like 100 or so should be plently there are only so many midi notes, I'll get the count 128 that should be the limit good, try write with heap functions, might be useful to see how to work with it. well could I just
+
+
+
+
+ //so what does this return exactly? so then event arrives we need to try find note if it was pressed or not.
+ //so basically whole logic of get_events function is:
+ //
+ //snd_seq_event_t event = convert_event(fluid_midi_event_t);
+ //event_in_table = find_event(&event);
+ //switch(event->type ){
+ // case NOTE_ON:
+ // if(event_in_table) return; // skip if pressed while still was pressed, shouldn't happen...I think it just gets pressed again, but both are deleted on the note_off, does that make sense? hmm might be, but that change a logic a bit
+ // yeah something like this. so basic idea is clear or not really? yeah it's clear, just too advanced for me
+ // to come up with on my own right now given my current skills with C, it's coming along fast though well it's simple then written in perl or somethng else, just showing how c does the same things. so now need to implement those functions ok
+ // insert_event(event);
+ // run_synth();
+ //
+ // case NOTE_OFF:
+ // replace_events(event_in_table,event)
+ // run_synth()
+ // delete_events(event);
+ //
+ //}
diff --git a/src/midi/custom.c b/src/midi/custom.c
new file mode 100644
index 0000000..9cb9613
--- /dev/null
+++ b/src/midi/custom.c
@@ -0,0 +1,325 @@
+#include "fluidsynth_priv.h"
+#include "fluid_midi.h"
+#include <sndfile.h>
+#include <string.h>
+#include <ladspa.h>
+#include <dssi.h>
+
+typedef struct event_table_t{
+ snd_seq_event_t *events;
+ int length;
+ size_t last_nframe;
+} event_table_t;
+typedef struct read_midi_ctx_t {
+ fluid_player_t *player;
+ fluid_track_t *track;
+} read_midi_ctx_t;
+
+event_table_t *event_table;
+
+int
+fluid_log(int level, const char* fmt, ...)
+{
+
+}
+//{{{OLD
+//fluid_midi_event_t *
+//new_fluid_midi_event ()
+//{
+// fluid_midi_event_t* evt; //oh it's just the name
+// evt = FLUID_NEW(fluid_midi_event_t);
+// if (evt == NULL) {
+// FLUID_LOG(FLUID_ERR, "Out of memory");
+// return NULL;
+// }
+// evt->dtime = 0;
+// evt->type = 0;
+// evt->channel = 0;
+// evt->param1 = 0;
+// evt->param2 = 0;
+// evt->next = NULL; //so this looks like it's set up to be a linked list, right? each event contains pointer to the next yes
+// evt->paramptr = NULL;
+// return evt;
+//}
+//}}}
+
+void insert_event(event_table_t *event_table, snd_seq_event_t *event){
+ event_table->events = realloc(event_table->events, (event_table->length + 1) * sizeof(snd_seq_event_t));
+ memcpy(&event_table->events[event_table->length], event, sizeof(snd_seq_event_t));
+ event_table->length += 1;
+}
+void delete_event(event_table_t *event_table, snd_seq_event_t *event){
+ size_t i;
+ for (i=0; i< event_table->length; i++){
+ if(compare_events(&event_table->events[i], event)){
+ memcpy(&event_table->events[i], &event_table->events[i+1], sizeof(snd_seq_event_t)*(event_table->length - i -1));
+ event_table->events = realloc(event_table->events, event_table->length * sizeof(snd_seq_event_t));
+ event_table->length--;
+ i--;
+ }
+ }
+}
+
+int compare_events(snd_seq_event_t *event1, snd_seq_event_t *event2){
+ return (
+ (event1->data.note.note == event2->data.note.note) &&
+ (event1->data.note.channel == event2->data.note.channel)
+ ) ? 1 : 0;
+}
+
+
+void replace_events(event_table_t *event_table, snd_seq_event_t *event){
+ size_t i;
+ for (i=0; i< event_table->length; i++){
+ if(compare_events(&event_table->events[i], event)){
+ memcpy(&event_table->events[i], event, sizeof(snd_seq_event_t));
+ }
+ }
+}
+
+//{{{ type conversion stuff
+//struct _fluid_midi_event_t {
+// fluid_midi_event_t* next; /* Link to next event */
+// void *paramptr; /* Pointer parameter (for SYSEX data), size is stored to param1, param2 indicates if pointer should be freed (dynamic if TRUE) */
+// unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */
+// unsigned int param1; /* First parameter */ DONE
+// unsigned int param2; /* Second parameter */ DONE
+// unsigned char type; /* MIDI event type */ DONE
+// unsigned char channel; /* MIDI channel */ DONE
+//};
+// on_event.type = SND_SEQ_EVENT_NOTEON;
+// on_event.data.note.channel = 0; // this one oh ok, so we save this data, and don't worry about it until we get to run_synth? looks so ok perfect
+// on_event.data.note.note = midi_note;
+// on_event.data.note.velocity = midi_velocity;
+// on_event.time.tick = 0;
+// }}}
+void convert_event_format(fluid_midi_event_t *from, snd_seq_event_t *to){
+//{{{ from->type
+ switch(from->type){
+
+ case NOTE_ON:
+ to->type = SND_SEQ_EVENT_NOTEON;
+ to->data.note.note = from->param1;
+ to->data.note.velocity = from->param2;
+ break;
+ case NOTE_OFF:
+ to->type = SND_SEQ_EVENT_NOTEOFF;
+ to->data.note.note = from->param1;
+ to->data.note.off_velocity = from->param2;
+ break;
+ /*case FLUID_NONE: to->type = SND_SEQ_EVENT_SYSTEM; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_RESULT; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_NOTE; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_KEYPRESS; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CONTROLLER; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_PGMCHANGE; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CHANPRESS; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_PITCHBEND; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CONTROL14; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_NONREGPARAM; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_REGPARAM; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_SONGPOS; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_SONGSEL; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_QFRAME; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_TIMESIGN; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_KEYSIGN; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_START; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CONTINUE; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_STOP; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_SETPOS_TICK; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_SETPOS_TIME; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_TEMPO; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CLOCK; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_TICK; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_QUEUE_SKEW; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_SYNC_POS; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_TUNE_REQUEST; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_RESET; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_SENSING; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_ECHO; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_OSS; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CLIENT_START; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CLIENT_EXIT; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CLIENT_CHANGE; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_PORT_START; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_PORT_EXIT; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_PORT_CHANGE; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_PORT_SUBSCRIBED; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_PORT_UNSUBSCRIBED; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR0; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR1; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR2; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR3; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR4; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR5; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR6; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR7; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR8; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR9; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_SYSEX; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_BOUNCE; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR_VAR0; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR_VAR1; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR_VAR2; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR_VAR3; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR_VAR4; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_NONE; break; one of those constants, we can have a switch in print function to clearly show what event is it*/
+ }
+//}}}
+ to->data.note.channel = from->channel;
+}
+
+
+
+void print_snd_seq_event(snd_seq_event_t *event){
+ char note_event[20];
+ switch(event->type){
+
+ case SND_SEQ_EVENT_NOTEON:
+ strcpy(note_event,"NOTE_ON");
+ break;
+ case SND_SEQ_EVENT_NOTEOFF:
+ strcpy(note_event,"NOTE_OFF");
+ break;
+ break;
+ }
+ printf("event_type: %s", note_event);
+ printf("channel: %d ", event->data.note.channel);
+ printf("note: %d ", event->data.note.note);
+ printf("velocity: %d ", event->data.note.velocity);
+ printf("\n");
+}
+
+
+void print_event_table (event_table_t *event_table){
+ unsigned int i;
+ for(i=0; i< event_table->length; i++){
+ printf(" - %d: ", i + 1);
+ print_snd_seq_event(&event_table->events[i]);
+
+ }
+ printf("--\n");
+}
+
+
+
+
+int get_events(void *data, fluid_midi_event_t *event){
+ read_midi_ctx_t *ctx = (read_midi_ctx_t *)data;
+ fluid_player_t *player = ctx->player;
+ fluid_track_t *track = ctx->track;
+ snd_seq_event_t seq_event;
+
+ size_t last_nframe = event_table->last_nframe;
+ event_table->last_nframe = (player->deltatime * track->ticks) * 44100 / 1000;
+ size_t nframes = event_table->last_nframe - last_nframe;
+ //ok so I have no idea how to get the linker to work when we put this code into cli-dssi-host, should we look at that next? sure
+ //FIXME msec
+ /*printf("track=%02d msec=%05d ticks=%05u dtime=%05u next=%05u type=Ox%x\n",
+ track->num,
+ track->ticks * player->deltatime,
+ track->ticks,
+ event->dtime,
+ track->ticks + event->dtime,
+ event->type); */
+
+ convert_event_format(event, &seq_event);
+// print_snd_seq_event(&seq_event);
+
+
+ switch(event->type){
+ case NOTE_ON:
+ insert_event(event_table, &seq_event);
+ break;
+ case NOTE_OFF:
+ delete_event(event_table, &seq_event);
+ break;
+ default:
+ break;
+ }
+ printf("event table last nframe: %u\n", event_table->last_nframe);
+ printf("run_synth(instancehandle, %u,\n", nframes);
+ print_event_table(event_table);
+ printf(", %u)\n", event_table->length);
+//weird, negative numbers type problem...is it because we need to print as %u? or something similar? might be
+//ok so it's the event table that we pass in, right? yes
+// descriptor->run_synth(instanceHandle, //instance handle is taken care of for us, current_event is our event_table, right? event_table->events
+// player->msec, //like this? not really
+// event_table->events,
+// event_table->length);
+//
+// so with nfraes, we need to track how many samples we should generate from last get_events call. so it should be something like:
+// ok something like this wow, ok, do we need print this now too, or should we try hooking it up? let's print it first
+}
+
+
+//{{{ mrswatson time calculation
+// case TIME_DIVISION_TYPE_TICKS_PER_BEAT: {
+// double ticksPerSecond = (double)timeDivision * getTempo() / 60.0;
+// double sampleFramesPerTick = getSampleRate() / ticksPerSecond;
+// currentTimeInSampleFrames += (long)(unpackedVariableLength * sampleFramesPerTick);
+// }
+//}}}
+
+
+void read_midi_file2(char *filename){
+ int i;
+ fluid_player_t *player;
+ fluid_playlist_item playlist_item;
+ read_midi_ctx_t ctx;
+
+ event_table = malloc(sizeof (event_table_t));
+ event_table->events = NULL;
+ event_table->length = 0;
+ event_table->last_nframe = 0;
+
+ playlist_item.filename = filename;
+ //
+ // player = (fluid_player_t *)new_fluid_player();
+ player = FLUID_NEW(fluid_player_t);
+ player->playback_callback = &get_events; //how do I pass it to the callback? no way to do that, we can only pass one variable as "data"
+ player->playback_userdata = (void *)&ctx; // here. so i guess need to make a structure and put player, track pointers into it.
+ ctx.player = player;
+ fluid_player_load(player, &playlist_item);
+
+//
+//
+ for(i = 0; i < player->ntracks; i++){
+ ctx.track = player->track[i];// here. so i guess need to make a structure and put player, track pointers into it.
+ fluid_track_send_events(player->track[i], player->synth, player, (unsigned int)-1);
+ }
+
+ delete_fluid_player(player);
+}
+//
+//should I grab the run_synth call from the other program just so that we know what it requires? yep
+//
+// something like this, need to fix it a bit ok I'll load example.mid, should we try it before taking a break from this? well it won't compile i think, get_events should be consistent with callback prototype
+//
+//EXPLANATION:
+//LADSPA_Handle instanceHandle;
+//instanceHandle = descriptor->LADSPA_Plugin->instantiate
+//
+//
+//
+//int nframes (number of frames)
+
+// current_event is pointer to on/off event
+// snd_seq_event_t on_event, off_event, *current_event;
+// on_event.type = SND_SEQ_EVENT_NOTEON;
+// on_event.data.note.channel = 0; // this one oh ok, so we save this data, and don't worry about it until we get to run_synth? looks so ok perfect
+// on_event.data.note.note = midi_note;
+// on_event.data.note.velocity = midi_velocity;
+// on_event.time.tick = 0;
+//
+// off_event.type = SND_SEQ_EVENT_NOTEOFF;
+// off_event.data.note.channel = 0;
+// off_event.data.note.note = midi_note;
+// off_event.data.note.off_velocity = midi_velocity; // yeah off_velocity instead
+// off_event.time.tick = 0;
+
+
+int main(void){
+ char *filename = "example.mid";
+ read_midi_file2(filename);
+}
diff --git a/src/midi/custom.h b/src/midi/custom.h
new file mode 100644
index 0000000..8a2fb14
--- /dev/null
+++ b/src/midi/custom.h
@@ -0,0 +1,20 @@
+typedef struct _fluid_midi_event_t fluid_midi_event_t;
+typedef struct _fluid_player_t fluid_player_t;
+typedef struct _fluid_synth_t fluid_synth_t;
+typedef struct _fluid_timer_t fluid_timer_t;
+typedef struct _fluid_sample_timer_t fluid_sample_timer_t;
+typedef struct _fluid_settings_t fluid_settings_t;
+
+
+#define FLUID_DBG 0
+#define FLUID_OK 1
+#define FLUID_ERR 0
+#define FLUID_FAILED 2
+#define FLUID_PANIC 3
+#define FLUID_PLAYER_DONE 2
+#define FLUID_PLAYER_PLAYING 1
+#define FLUID_PLAYER_READY 3
+#define FLUID_HINT_TOGGLED 0x01
+#define TRUE 1
+#define FALSE 0
+typedef int (*handle_midi_event_func_t)(void* data, fluid_midi_event_t* event);
diff --git a/src/midi/example.mid b/src/midi/example.mid
new file mode 100644
index 0000000..328d6ef
--- /dev/null
+++ b/src/midi/example.mid
Binary files differ
diff --git a/src/midi/example1.mid b/src/midi/example1.mid
new file mode 100644
index 0000000..2fa1b91
--- /dev/null
+++ b/src/midi/example1.mid
Binary files differ
diff --git a/src/midi/example2.mid b/src/midi/example2.mid
new file mode 100644
index 0000000..bf3bfc0
--- /dev/null
+++ b/src/midi/example2.mid
Binary files differ
diff --git a/src/midi/fluid_list.c b/src/midi/fluid_list.c
new file mode 100644
index 0000000..1b14146
--- /dev/null
+++ b/src/midi/fluid_list.c
@@ -0,0 +1,269 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-1999. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+
+
+#include "fluid_list.h"
+#include <stdlib.h>
+
+
+fluid_list_t*
+new_fluid_list(void)
+{
+ fluid_list_t* list;
+ list = (fluid_list_t*) FLUID_MALLOC(sizeof(fluid_list_t));
+ list->data = NULL;
+ list->next = NULL;
+ return list;
+}
+
+void
+delete_fluid_list(fluid_list_t *list)
+{
+ fluid_list_t *next;
+ while (list) {
+ next = list->next;
+ FLUID_FREE(list);
+ list = next;
+ }
+}
+
+void
+delete1_fluid_list(fluid_list_t *list)
+{
+ if (list) {
+ FLUID_FREE(list);
+ }
+}
+
+fluid_list_t*
+fluid_list_append(fluid_list_t *list, void* data)
+{
+ fluid_list_t *new_list;
+ fluid_list_t *last;
+
+ new_list = new_fluid_list();
+ new_list->data = data;
+
+ if (list)
+ {
+ last = fluid_list_last(list);
+ /* g_assert (last != NULL); */
+ last->next = new_list;
+
+ return list;
+ }
+ else
+ return new_list;
+}
+
+fluid_list_t*
+fluid_list_prepend(fluid_list_t *list, void* data)
+{
+ fluid_list_t *new_list;
+
+ new_list = new_fluid_list();
+ new_list->data = data;
+ new_list->next = list;
+
+ return new_list;
+}
+
+fluid_list_t*
+fluid_list_nth(fluid_list_t *list, int n)
+{
+ while ((n-- > 0) && list) {
+ list = list->next;
+ }
+
+ return list;
+}
+
+fluid_list_t*
+fluid_list_remove(fluid_list_t *list, void* data)
+{
+ fluid_list_t *tmp;
+ fluid_list_t *prev;
+
+ prev = NULL;
+ tmp = list;
+
+ while (tmp) {
+ if (tmp->data == data) {
+ if (prev) {
+ prev->next = tmp->next;
+ }
+ if (list == tmp) {
+ list = list->next;
+ }
+ tmp->next = NULL;
+ delete_fluid_list(tmp);
+
+ break;
+ }
+
+ prev = tmp;
+ tmp = tmp->next;
+ }
+
+ return list;
+}
+
+fluid_list_t*
+fluid_list_remove_link(fluid_list_t *list, fluid_list_t *link)
+{
+ fluid_list_t *tmp;
+ fluid_list_t *prev;
+
+ prev = NULL;
+ tmp = list;
+
+ while (tmp) {
+ if (tmp == link) {
+ if (prev) {
+ prev->next = tmp->next;
+ }
+ if (list == tmp) {
+ list = list->next;
+ }
+ tmp->next = NULL;
+ break;
+ }
+
+ prev = tmp;
+ tmp = tmp->next;
+ }
+
+ return list;
+}
+
+static fluid_list_t*
+fluid_list_sort_merge(fluid_list_t *l1, fluid_list_t *l2, fluid_compare_func_t compare_func)
+{
+ fluid_list_t list, *l;
+
+ l = &list;
+
+ while (l1 && l2) {
+ if (compare_func(l1->data,l2->data) < 0) {
+ l = l->next = l1;
+ l1 = l1->next;
+ } else {
+ l = l->next = l2;
+ l2 = l2->next;
+ }
+ }
+ l->next= l1 ? l1 : l2;
+
+ return list.next;
+}
+
+fluid_list_t*
+fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func)
+{
+ fluid_list_t *l1, *l2;
+
+ if (!list) {
+ return NULL;
+ }
+ if (!list->next) {
+ return list;
+ }
+
+ l1 = list;
+ l2 = list->next;
+
+ while ((l2 = l2->next) != NULL) {
+ if ((l2 = l2->next) == NULL)
+ break;
+ l1=l1->next;
+ }
+ l2 = l1->next;
+ l1->next = NULL;
+
+ return fluid_list_sort_merge(fluid_list_sort(list, compare_func),
+ fluid_list_sort(l2, compare_func),
+ compare_func);
+}
+
+
+fluid_list_t*
+fluid_list_last(fluid_list_t *list)
+{
+ if (list) {
+ while (list->next)
+ list = list->next;
+ }
+
+ return list;
+}
+
+int
+fluid_list_size(fluid_list_t *list)
+{
+ int n = 0;
+ while (list) {
+ n++;
+ list = list->next;
+ }
+ return n;
+}
+
+fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data)
+{
+ fluid_list_t *new_list;
+ fluid_list_t *cur;
+ fluid_list_t *prev = NULL;
+
+ new_list = new_fluid_list();
+ new_list->data = data;
+
+ cur = list;
+ while ((n-- > 0) && cur) {
+ prev = cur;
+ cur = cur->next;
+ }
+
+ new_list->next = cur;
+
+ if (prev) {
+ prev->next = new_list;
+ return list;
+ } else {
+ return new_list;
+ }
+}
+
+/* Compare function to sort strings alphabetically,
+ * for use with fluid_list_sort(). */
+int
+fluid_list_str_compare_func (void *a, void *b)
+{
+ if (a && b) return FLUID_STRCMP ((char *)a, (char *)b);
+ if (!a && !b) return 0;
+ if (a) return -1;
+ return 1;
+}
diff --git a/src/midi/fluid_list.h b/src/midi/fluid_list.h
new file mode 100644
index 0000000..bdc3291
--- /dev/null
+++ b/src/midi/fluid_list.h
@@ -0,0 +1,62 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _FLUID_LIST_H
+#define _FLUID_LIST_H
+
+#include "fluidsynth_priv.h"
+
+/*
+ *
+ * Lists
+ *
+ * A sound font loader has to pack the data from the .SF2 file into
+ * list structures of this type.
+ *
+ */
+
+typedef struct _fluid_list_t fluid_list_t;
+
+typedef int (*fluid_compare_func_t)(void* a, void* b);
+
+struct _fluid_list_t
+{
+ void* data;
+ fluid_list_t *next;
+};
+
+fluid_list_t* new_fluid_list(void);
+void delete_fluid_list(fluid_list_t *list);
+void delete1_fluid_list(fluid_list_t *list);
+fluid_list_t* fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func);
+fluid_list_t* fluid_list_append(fluid_list_t *list, void* data);
+fluid_list_t* fluid_list_prepend(fluid_list_t *list, void* data);
+fluid_list_t* fluid_list_remove(fluid_list_t *list, void* data);
+fluid_list_t* fluid_list_remove_link(fluid_list_t *list, fluid_list_t *llink);
+fluid_list_t* fluid_list_nth(fluid_list_t *list, int n);
+fluid_list_t* fluid_list_last(fluid_list_t *list);
+fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data);
+int fluid_list_size(fluid_list_t *list);
+
+#define fluid_list_next(slist) ((slist) ? (((fluid_list_t *)(slist))->next) : NULL)
+#define fluid_list_get(slist) ((slist) ? ((slist)->data) : NULL)
+
+int fluid_list_str_compare_func (void *a, void *b);
+
+#endif /* _FLUID_LIST_H */
diff --git a/src/midi/fluid_midi.c b/src/midi/fluid_midi.c
new file mode 100644
index 0000000..558f579
--- /dev/null
+++ b/src/midi/fluid_midi.c
@@ -0,0 +1,1939 @@
+
+#include "fluid_midi.h"
+#include <math.h>
+//#include "fluid_sys.h"
+//#include "fluid_synth.h"
+//#include "fluid_settings.h"
+
+
+static int fluid_midi_event_length(unsigned char event);
+
+/* Read the entire contents of a file into memory, allocating enough memory
+ * for the file, and returning the length and the buffer.
+ * Note: This rewinds the file to the start before reading.
+ * Returns NULL if there was an error reading or allocating memory.
+ */
+static char* fluid_file_read_full(fluid_file fp, size_t* length);
+#define READ_FULL_INITIAL_BUFLEN 1024
+
+
+/***************************************************************
+ * FILE READ HERE
+ *
+ * MIDIFILE
+ */
+
+/**
+ * Return a new MIDI file handle for parsing an already-loaded MIDI file.
+ * @internal
+ * @param buffer Pointer to full contents of MIDI file (borrows the pointer).
+ * The caller must not free buffer until after the fluid_midi_file is deleted.
+ * @param length Size of the buffer in bytes.
+ * @return New MIDI file handle or NULL on error.
+ */
+fluid_midi_file *
+new_fluid_midi_file(const char* buffer, size_t length)
+{
+ fluid_midi_file *mf;
+
+ mf = FLUID_NEW(fluid_midi_file);
+ if (mf == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ FLUID_MEMSET(mf, 0, sizeof(fluid_midi_file));
+
+ mf->c = -1;
+ mf->running_status = -1;
+
+ mf->buffer = buffer;
+ mf->buf_len = length;
+ mf->buf_pos = 0;
+ mf->eof = FALSE;
+
+ if (fluid_midi_file_read_mthd(mf) != FLUID_OK) { // here it calls read file
+ FLUID_FREE(mf);
+ return NULL;
+ }
+ return mf;
+}
+//ok how do you usually create a file_pointer in this case? with fopen FILE READ do you think we can just try this function directly with fopen? yes, fluid_file i think is just FILE * returned by fopen, so code will be fopen, fluid_file_read_full and then new_fluid_midi_file.
+
+static char*
+fluid_file_read_full(fluid_file fp, size_t* length)
+{
+ size_t buflen;
+ char* buffer;
+ size_t n;
+ /* Work out the length of the file in advance */
+ if (FLUID_FSEEK(fp, 0, SEEK_END) != 0)
+ {
+ FLUID_LOG(FLUID_ERR, "File load: Could not seek within file");
+ return NULL;
+ }
+ buflen = ftell(fp);
+ if (FLUID_FSEEK(fp, 0, SEEK_SET) != 0)
+ {
+ FLUID_LOG(FLUID_ERR, "File load: Could not seek within file");
+ return NULL;
+ }
+ FLUID_LOG(FLUID_DBG, "File load: Allocating %d bytes", buflen);
+ buffer = FLUID_MALLOC(buflen);
+ if (buffer == NULL) {
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return NULL;
+ }
+ n = FLUID_FREAD(buffer, 1, buflen, fp);
+ if (n != buflen) {
+ FLUID_LOG(FLUID_ERR, "Only read %d bytes; expected %d", n,
+ buflen);
+ FLUID_FREE(buffer);
+ return NULL;
+ };
+ *length = n;
+ return buffer;
+}
+
+/**
+ * Delete a MIDI file handle.
+ * @internal
+ * @param mf MIDI file handle to close and free.
+ */
+void
+delete_fluid_midi_file (fluid_midi_file *mf)
+{
+ if (mf == NULL) {
+ return;
+ }
+ FLUID_FREE(mf);
+ return;
+}
+
+/*
+ * Gets the next byte in a MIDI file, taking into account previous running status.
+ *
+ * returns FLUID_FAILED if EOF or read error
+ */
+int
+fluid_midi_file_getc (fluid_midi_file *mf)
+{
+ unsigned char c;
+ if (mf->c >= 0) {
+ c = mf->c;
+ mf->c = -1;
+ } else {
+ if (mf->buf_pos >= mf->buf_len) {
+ mf->eof = TRUE;
+ return FLUID_FAILED;
+ }
+ c = mf->buffer[mf->buf_pos++];
+ mf->trackpos++;
+ }
+ return (int) c;
+}
+
+/*
+ * Saves a byte to be returned the next time fluid_midi_file_getc() is called,
+ * when it is necessary according to running status.
+ */
+int
+fluid_midi_file_push(fluid_midi_file *mf, int c)
+{
+ mf->c = c;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_read
+ */
+int
+fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len)
+{
+ int num = len < mf->buf_len - mf->buf_pos
+ ? len : mf->buf_len - mf->buf_pos;
+ if (num != len) {
+ mf->eof = TRUE;
+ }
+ if (num < 0) {
+ num = 0;
+ }
+ /* Note: Read bytes, even if there aren't enough, but only increment
+ * trackpos if successful (emulates old behaviour of fluid_midi_file_read)
+ */
+ FLUID_MEMCPY(buf, mf->buffer+mf->buf_pos, num);
+ mf->buf_pos += num;
+ if (num == len)
+ mf->trackpos += num;
+#if DEBUG
+ else
+ FLUID_LOG(FLUID_DBG, "Could not read the requested number of bytes");
+#endif
+ return (num != len) ? FLUID_FAILED : FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_skip
+ */
+int
+fluid_midi_file_skip (fluid_midi_file *mf, int skip)
+{
+ int new_pos = mf->buf_pos + skip;
+ /* Mimic the behaviour of fseek: Error to seek past the start of file, but
+ * OK to seek past end (this just puts it into the EOF state). */
+ if (new_pos < 0) {
+ FLUID_LOG(FLUID_ERR, "Failed to seek position in file");
+ return FLUID_FAILED;
+ }
+ /* Clear the EOF flag, even if moved past the end of the file (this is
+ * consistent with the behaviour of fseek). */
+ mf->eof = FALSE;
+ mf->buf_pos = new_pos;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_eof
+ */
+int fluid_midi_file_eof(fluid_midi_file* mf)
+{
+ /* Note: This does not simply test whether the file read pointer is past
+ * the end of the file. It mimics the behaviour of feof by actually
+ * testing the stateful EOF condition, which is set to TRUE if getc or
+ * fread have attempted to read past the end (but not if they have
+ * precisely reached the end), but reset to FALSE upon a successful seek.
+ */
+ return mf->eof;
+}
+
+/*
+ * fluid_midi_file_read_mthd
+ */
+//it's actually does all the work inside new_fluid_midi_file function to read file and get all events allocated and set, so now we just need to print those.
+int
+fluid_midi_file_read_mthd(fluid_midi_file *mf)
+{
+ char mthd[15];
+ if (fluid_midi_file_read(mf, mthd, 14) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ if ((FLUID_STRNCMP(mthd, "MThd", 4) != 0) || (mthd[7] != 6)
+ || (mthd[9] > 2)) {
+ FLUID_LOG(FLUID_ERR,
+ "Doesn't look like a MIDI file: invalid MThd header");
+ return FLUID_FAILED;
+ }
+ mf->type = mthd[9];
+ mf->ntracks = (unsigned) mthd[11];
+ mf->ntracks += (unsigned int) (mthd[10]) << 16;
+ if ((mthd[12]) < 0) {
+ mf->uses_smpte = 1;
+ mf->smpte_fps = -mthd[12];
+ mf->smpte_res = (unsigned) mthd[13];
+ FLUID_LOG(FLUID_ERR, "File uses SMPTE timing -- Not implemented yet");
+ return FLUID_FAILED;
+ } else {
+ mf->uses_smpte = 0;
+ mf->division = (mthd[12] << 8) | (mthd[13] & 0xff); //division is in the header for the midi file, he gets the value here. do we have access to it in custom.c? looks like player have also current time in milliseconds too, in addition to ticks, we can try to just use those. ok good. so the player is doing the parsing, we're just
+
+ FLUID_LOG(FLUID_DBG, "Division=%d", mf->division);
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_load_tracks
+ * so now we load tracks, right? yeah but looks like we already have such code in here:
+ */
+int
+fluid_midi_file_load_tracks(fluid_midi_file *mf, fluid_player_t *player)
+{
+ int i;
+ for (i = 0; i < mf->ntracks; i++) {
+ if (fluid_midi_file_read_track(mf, player, i) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_isasciistring
+ */
+int
+fluid_isasciistring(char *s)
+{
+ int i;
+ int len = (int) FLUID_STRLEN(s);
+ for (i = 0; i < len; i++) {
+ if (!fluid_isascii(s[i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * fluid_getlength
+ */
+long
+fluid_getlength(unsigned char *s)
+{
+ long i = 0;
+ i = s[3] | (s[2] << 8) | (s[1] << 16) | (s[0] << 24);
+ return i;
+}
+
+/*
+ * fluid_midi_file_read_tracklen
+ */
+int
+fluid_midi_file_read_tracklen(fluid_midi_file *mf)
+{
+ unsigned char length[5];
+ if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ mf->tracklen = fluid_getlength(length);
+ mf->trackpos = 0;
+ mf->eot = 0;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_eot
+ */
+int
+fluid_midi_file_eot(fluid_midi_file *mf)
+{
+#if DEBUG
+ if (mf->trackpos > mf->tracklen) {
+ printf("track overrun: %d > %d\n", mf->trackpos, mf->tracklen);
+ }
+#endif
+ return mf->eot || (mf->trackpos >= mf->tracklen);
+}
+
+/*
+ * fluid_midi_file_read_track
+ */
+int
+fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num)
+{
+ fluid_track_t *track;
+ unsigned char id[5], length[5];
+ int found_track = 0;
+ int skip;
+
+ if (fluid_midi_file_read(mf, id, 4) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ id[4] = '\0';
+ mf->dtime = 0;
+
+ while (!found_track) {
+
+ if (fluid_isasciistring((char *) id) == 0) {
+ FLUID_LOG(FLUID_ERR,
+ "An non-ascii track header found, corrupt file");
+ return FLUID_FAILED;
+
+ } else if (strcmp((char *) id, "MTrk") == 0) {
+
+ found_track = 1;
+
+ if (fluid_midi_file_read_tracklen(mf) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ track = new_fluid_track(num);
+ if (track == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+
+ while (!fluid_midi_file_eot(mf)) {
+ if (fluid_midi_file_read_event(mf, track) != FLUID_OK) {
+ delete_fluid_track(track);
+ return FLUID_FAILED;
+ }
+ }
+
+ /* Skip remaining track data, if any */
+ if (mf->trackpos < mf->tracklen)
+ fluid_midi_file_skip(mf, mf->tracklen - mf->trackpos);
+
+ fluid_player_add_track(player, track);
+
+ } else {
+ found_track = 0;
+ if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ skip = fluid_getlength(length);
+ /* fseek(mf->fp, skip, SEEK_CUR); */
+ if (fluid_midi_file_skip(mf, skip) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ }
+ }
+ if (fluid_midi_file_eof(mf)) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_read_varlen
+ */
+int
+fluid_midi_file_read_varlen(fluid_midi_file *mf)
+{
+ int i;
+ int c;
+ mf->varlen = 0;
+ for (i = 0;; i++) {
+ if (i == 4) {
+ FLUID_LOG(FLUID_ERR, "Invalid variable length number");
+ return FLUID_FAILED;
+ }
+ c = fluid_midi_file_getc(mf);
+ if (c < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ if (c & 0x80) {
+ mf->varlen |= (int) (c & 0x7F);
+ mf->varlen <<= 7;
+ } else {
+ mf->varlen += c;
+ break;
+ }
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_read_event
+ */
+//could be this, right...it only takes the file and the track as args
+int
+fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track)
+{
+ int status;
+ int type;
+ int tempo;
+ unsigned char *metadata = NULL;
+ unsigned char *dyn_buf = NULL;
+ unsigned char static_buf[256];
+ int nominator, denominator, clocks, notes;
+ fluid_midi_event_t *evt; //do you know what evt is? is that the event struct?
+ int channel = 0;
+ int param1 = 0;
+ int param2 = 0;
+ int size;
+
+ /* read the delta-time of the event */
+ if (fluid_midi_file_read_varlen(mf) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ mf->dtime += mf->varlen;
+
+ /* read the status byte */
+ status = fluid_midi_file_getc(mf);
+ if (status < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+
+ /* not a valid status byte: use the running status instead */
+ if ((status & 0x80) == 0) {
+ if ((mf->running_status & 0x80) == 0) {
+ FLUID_LOG(FLUID_ERR, "Undefined status and invalid running status");
+ return FLUID_FAILED;
+ }
+ fluid_midi_file_push(mf, status);
+ status = mf->running_status;
+ }
+
+ /* check what message we have */
+
+ mf->running_status = status;
+
+ if ((status == MIDI_SYSEX)) { /* system exclusif */
+ /* read the length of the message */
+ if (fluid_midi_file_read_varlen(mf) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ if (mf->varlen) {
+ FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__,
+ __LINE__, mf->varlen);
+ metadata = FLUID_MALLOC(mf->varlen + 1);
+
+ if (metadata == NULL) {
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return FLUID_FAILED;
+ }
+
+ /* read the data of the message */
+ if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) {
+ FLUID_FREE (metadata);
+ return FLUID_FAILED;
+ }
+
+ evt = new_fluid_midi_event();
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ FLUID_FREE (metadata);
+ return FLUID_FAILED;
+ }
+
+ evt->dtime = mf->dtime;
+ size = mf->varlen;
+
+ if (metadata[mf->varlen - 1] == MIDI_EOX)
+ size--;
+
+ /* Add SYSEX event and indicate that its dynamically allocated and should be freed with event */
+ fluid_midi_event_set_sysex(evt, metadata, size, TRUE);
+ fluid_track_add_event(track, evt);
+ mf->dtime = 0;
+ }
+
+ return FLUID_OK;
+
+ } else if (status == MIDI_META_EVENT) { /* meta events */
+
+ int result = FLUID_OK;
+
+ /* get the type of the meta message */
+ type = fluid_midi_file_getc(mf);
+ if (type < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+
+ /* get the length of the data part */
+ if (fluid_midi_file_read_varlen(mf) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ if (mf->varlen < 255) {
+ metadata = &static_buf[0];
+ } else {
+ FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__,
+ __LINE__, mf->varlen);
+ dyn_buf = FLUID_MALLOC(mf->varlen + 1);
+ if (dyn_buf == NULL) {
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return FLUID_FAILED;
+ }
+ metadata = dyn_buf;
+ }
+
+ /* read the data */
+ if (mf->varlen) {
+ if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) {
+ if (dyn_buf) {
+ FLUID_FREE(dyn_buf);
+ }
+ return FLUID_FAILED;
+ }
+ }
+
+ /* handle meta data */
+ switch (type) {
+
+ case MIDI_COPYRIGHT:
+ metadata[mf->varlen] = 0;
+ break;
+
+ case MIDI_TRACK_NAME:
+ metadata[mf->varlen] = 0;
+ fluid_track_set_name(track, (char *) metadata);
+ break;
+
+ case MIDI_INST_NAME:
+ metadata[mf->varlen] = 0;
+ break;
+
+ case MIDI_LYRIC:
+ break;
+
+ case MIDI_MARKER:
+ break;
+
+ case MIDI_CUE_POINT:
+ break; /* don't care much for text events */
+
+ case MIDI_EOT:
+ if (mf->varlen != 0) {
+ FLUID_LOG(FLUID_ERR, "Invalid length for EndOfTrack event");
+ result = FLUID_FAILED;
+ break;
+ }
+ mf->eot = 1;
+ evt = new_fluid_midi_event();
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ result = FLUID_FAILED;
+ break;
+ }
+ evt->dtime = mf->dtime;
+ evt->type = MIDI_EOT;
+ fluid_track_add_event(track, evt);
+ mf->dtime = 0;
+ break;
+
+ case MIDI_SET_TEMPO:
+ if (mf->varlen != 3) {
+ FLUID_LOG(FLUID_ERR,
+ "Invalid length for SetTempo meta event");
+ result = FLUID_FAILED;
+ break;
+ }
+ tempo = (metadata[0] << 16) + (metadata[1] << 8) + metadata[2];
+ evt = new_fluid_midi_event();
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ result = FLUID_FAILED;
+ break;
+ }
+ evt->dtime = mf->dtime;
+ evt->type = MIDI_SET_TEMPO;
+ evt->channel = 0;
+ evt->param1 = tempo;
+ evt->param2 = 0;
+ fluid_track_add_event(track, evt);
+ mf->dtime = 0;
+ break;
+
+ case MIDI_SMPTE_OFFSET:
+ if (mf->varlen != 5) {
+ FLUID_LOG(FLUID_ERR,
+ "Invalid length for SMPTE Offset meta event");
+ result = FLUID_FAILED;
+ break;
+ }
+ break; /* we don't use smtp */
+
+ case MIDI_TIME_SIGNATURE:
+ if (mf->varlen != 4) {
+ FLUID_LOG(FLUID_ERR,
+ "Invalid length for TimeSignature meta event");
+ result = FLUID_FAILED;
+ break;
+ }
+ nominator = metadata[0];
+ denominator = pow(2.0, (double) metadata[1]);
+ clocks = metadata[2];
+ notes = metadata[3];
+
+ FLUID_LOG(FLUID_DBG,
+ "signature=%d/%d, metronome=%d, 32nd-notes=%d",
+ nominator, denominator, clocks, notes);
+
+ break;
+
+ case MIDI_KEY_SIGNATURE:
+ if (mf->varlen != 2) {
+ FLUID_LOG(FLUID_ERR,
+ "Invalid length for KeySignature meta event");
+ result = FLUID_FAILED;
+ break;
+ }
+ /* We don't care about key signatures anyway */
+ /* sf = metadata[0];
+ mi = metadata[1]; */
+ break;
+
+ case MIDI_SEQUENCER_EVENT:
+ break;
+
+ default:
+ break;
+ }
+
+ if (dyn_buf) {
+ FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__);
+ FLUID_FREE(dyn_buf);
+ }
+
+ return result;
+
+ } else { /* channel messages */
+
+ type = status & 0xf0;
+ channel = status & 0x0f;
+
+ /* all channel message have at least 1 byte of associated data */
+ if ((param1 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+
+ switch (type) {
+
+ case NOTE_ON:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ break;
+
+ case NOTE_OFF:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ break;
+
+ case KEY_PRESSURE:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ break;
+
+ case CONTROL_CHANGE:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ break;
+
+ case PROGRAM_CHANGE:
+ break;
+
+ case CHANNEL_PRESSURE:
+ break;
+
+ case PITCH_BEND:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+
+ param1 = ((param2 & 0x7f) << 7) | (param1 & 0x7f);
+ param2 = 0;
+ break;
+
+ default:
+ /* Can't possibly happen !? */
+ FLUID_LOG(FLUID_ERR, "Unrecognized MIDI event");
+ return FLUID_FAILED;
+ }
+ evt = new_fluid_midi_event();
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ evt->dtime = mf->dtime;
+ evt->type = type;
+ evt->channel = channel;
+ evt->param1 = param1;
+ evt->param2 = param2;
+ fluid_track_add_event(track, evt);
+ mf->dtime = 0;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_get_division
+ */
+int
+fluid_midi_file_get_division(fluid_midi_file *midifile)
+{
+ return midifile->division;
+}
+
+/******************************************************
+ *
+ * fluid_track_t
+ */
+
+/**
+ * MIDI EVENT DEFINED HERE...do we need to figure out exactly how this ties in to cli-dssi-host next? not yet, for now we need working midi loading from file and listing events according to each time sample. should we write a print event function? yeah in main() custom.c
+ * Create a MIDI event structure.
+ * @return New MIDI event structure or NULL when out of memory.
+ */
+fluid_midi_event_t *
+new_fluid_midi_event ()
+{
+ fluid_midi_event_t* evt; //oh it's just the name
+ evt = FLUID_NEW(fluid_midi_event_t);
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ evt->dtime = 0;
+ evt->type = 0;
+ evt->channel = 0;
+ evt->param1 = 0;
+ evt->param2 = 0;
+ evt->next = NULL;
+ evt->paramptr = NULL;
+ return evt;
+}
+
+/**
+ * Delete MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Always returns #FLUID_OK
+ */
+int
+delete_fluid_midi_event(fluid_midi_event_t *evt)
+{
+ fluid_midi_event_t *temp;
+
+ while (evt) {
+ temp = evt->next;
+
+ /* Dynamic SYSEX event? - free (param2 indicates if dynamic) */
+ if (evt->type == MIDI_SYSEX && evt->paramptr && evt->param2)
+ FLUID_FREE (evt->paramptr);
+
+ FLUID_FREE(evt);
+ evt = temp;
+ }
+ return FLUID_OK;
+}
+
+/**
+ * Get the event type field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Event type field (MIDI status byte without channel)
+ */
+int
+fluid_midi_event_get_type(fluid_midi_event_t *evt)
+{
+ return evt->type;
+}
+
+/**
+ * Set the event type field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param type Event type field (MIDI status byte without channel)
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_type(fluid_midi_event_t *evt, int type)
+{
+ evt->type = type;
+ return FLUID_OK;
+}
+
+/**
+ * Get the channel field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Channel field
+ */
+int
+fluid_midi_event_get_channel(fluid_midi_event_t *evt)
+{
+ return evt->channel;
+}
+
+/**
+ * Set the channel field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param chan MIDI channel field
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_channel(fluid_midi_event_t *evt, int chan)
+{
+ evt->channel = chan;
+ return FLUID_OK;
+}
+
+/**
+ * Get the key field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return MIDI note number (0-127)
+ */
+int
+fluid_midi_event_get_key(fluid_midi_event_t *evt)
+{
+ return evt->param1;
+}
+
+/**
+ * Set the key field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param v MIDI note number (0-127)
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_key(fluid_midi_event_t *evt, int v)
+{
+ evt->param1 = v;
+ return FLUID_OK;
+}
+
+/**
+ * Get the velocity field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return MIDI velocity number (0-127)
+ */
+int
+fluid_midi_event_get_velocity(fluid_midi_event_t *evt)
+{
+ return evt->param2;
+}
+
+/**
+ * Set the velocity field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param v MIDI velocity value
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_velocity(fluid_midi_event_t *evt, int v)
+{
+ evt->param2 = v;
+ return FLUID_OK;
+}
+
+/**
+ * Get the control number of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return MIDI control number
+ */
+int
+fluid_midi_event_get_control(fluid_midi_event_t *evt)
+{
+ return evt->param1;
+}
+
+/**
+ * Set the control field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param v MIDI control number
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_control(fluid_midi_event_t *evt, int v)
+{
+ evt->param1 = v;
+ return FLUID_OK;
+}
+
+/**
+ * Get the value field from a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Value field
+ */
+int
+fluid_midi_event_get_value(fluid_midi_event_t *evt)
+{
+ return evt->param2;
+}
+
+/**
+ * Set the value field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param v Value to assign
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_value(fluid_midi_event_t *evt, int v)
+{
+ evt->param2 = v;
+ return FLUID_OK;
+}
+
+/**
+ * Get the program field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return MIDI program number (0-127)
+ */
+int
+fluid_midi_event_get_program(fluid_midi_event_t *evt)
+{
+ return evt->param1;
+}
+
+/**
+ * Set the program field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param val MIDI program number (0-127)
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_program(fluid_midi_event_t *evt, int val)
+{
+ evt->param1 = val;
+ return FLUID_OK;
+}
+
+/**
+ * Get the pitch field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Pitch value (14 bit value, 0-16383, 8192 is center)
+ */
+int
+fluid_midi_event_get_pitch(fluid_midi_event_t *evt)
+{
+ return evt->param1;
+}
+
+/**
+ * Set the pitch field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param val Pitch value (14 bit value, 0-16383, 8192 is center)
+ * @return Always returns FLUID_OK
+ */
+int
+fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val)
+{
+ evt->param1 = val;
+ return FLUID_OK;
+}
+
+/**
+ * Assign sysex data to a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param data Pointer to SYSEX data
+ * @param size Size of SYSEX data
+ * @param dynamic TRUE if the SYSEX data has been dynamically allocated and
+ * should be freed when the event is freed (only applies if event gets destroyed
+ * with delete_fluid_midi_event())
+ * @return Always returns #FLUID_OK
+ *
+ * NOTE: Unlike the other event assignment functions, this one sets evt->type.
+ */
+int
+fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dynamic)
+{
+ evt->type = MIDI_SYSEX;
+ evt->paramptr = data;
+ evt->param1 = size;
+ evt->param2 = dynamic;
+ return FLUID_OK;
+}
+
+/******************************************************
+ *
+ * fluid_track_t
+ */
+
+/*
+ * new_fluid_track
+ */
+fluid_track_t *
+new_fluid_track(int num)
+{
+ fluid_track_t *track;
+ track = FLUID_NEW(fluid_track_t);
+ if (track == NULL) {
+ return NULL;
+ }
+ track->name = NULL;
+ track->num = num;
+ track->first = NULL;
+ track->cur = NULL;
+ track->last = NULL;
+ track->ticks = 0;
+ return track;
+}
+
+/*
+ * delete_fluid_track
+ */
+int
+delete_fluid_track(fluid_track_t *track)
+{
+ if (track->name != NULL) {
+ FLUID_FREE(track->name);
+ }
+ if (track->first != NULL) {
+ delete_fluid_midi_event(track->first);
+ }
+ FLUID_FREE(track);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_track_set_name
+ */
+int
+fluid_track_set_name(fluid_track_t *track, char *name)
+{
+ int len;
+ if (track->name != NULL) {
+ FLUID_FREE(track->name);
+ }
+ if (name == NULL) {
+ track->name = NULL;
+ return FLUID_OK;
+ }
+ len = FLUID_STRLEN(name);
+ track->name = FLUID_MALLOC(len + 1);
+ if (track->name == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ FLUID_STRCPY(track->name, name);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_track_get_name
+ */
+char *
+fluid_track_get_name(fluid_track_t *track)
+{
+ return track->name;
+}
+
+/*
+ * fluid_track_get_duration
+ */
+int
+fluid_track_get_duration(fluid_track_t *track)
+{
+ int time = 0;
+ fluid_midi_event_t *evt = track->first;
+ while (evt != NULL) {
+ time += evt->dtime;
+ evt = evt->next;
+ }
+ return time;
+}
+
+/*
+ * fluid_track_count_events
+ */
+int
+fluid_track_count_events(fluid_track_t *track, int *on, int *off)
+{
+ fluid_midi_event_t *evt = track->first;
+ while (evt != NULL) {
+ if (evt->type == NOTE_ON) {
+ (*on)++;
+ } else if (evt->type == NOTE_OFF) {
+ (*off)++;
+ }
+ evt = evt->next;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_track_add_event
+ */
+int
+fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt)
+{
+ evt->next = NULL;
+ if (track->first == NULL) {
+ track->first = evt;
+ track->cur = evt;
+ track->last = evt;
+ } else {
+ track->last->next = evt;
+ track->last = evt;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_track_first_event
+ */
+fluid_midi_event_t *
+fluid_track_first_event(fluid_track_t *track)
+{
+ track->cur = track->first;
+ return track->cur;
+}
+
+/*
+ * fluid_track_next_event
+ */
+fluid_midi_event_t *
+fluid_track_next_event(fluid_track_t *track)
+{
+ if (track->cur != NULL) {
+ track->cur = track->cur->next;
+ }
+ return track->cur;
+}
+
+/*
+ * fluid_track_reset
+ */
+int
+fluid_track_reset(fluid_track_t *track)
+{
+ track->ticks = 0;
+ track->cur = track->first;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_track_send_events
+ */
+int
+fluid_track_send_events(fluid_track_t *track,
+ fluid_synth_t *synth, //will the null pointer cause an error here? nope, will work //doesn't use it? here... I guess not
+ fluid_player_t *player,
+ unsigned int ticks)
+{
+ int status = FLUID_OK;
+ fluid_midi_event_t *event;
+
+ while (1) {
+
+ event = track->cur;
+ if (event == NULL) {
+ return status;
+ }
+
+//ok it should be printing here, right? well need to check flow again, starting from load_tracks if it reach this function or not
+//do we decide now to just listen for note on and note off, and keep track of the two? yep
+//run_synth also takes velocity, so maybe we should add that as well? i guess so
+
+ if (track->ticks + event->dtime > ticks) {
+ return status;
+ }
+
+ track->ticks += event->dtime;
+
+ if (!player || event->type == MIDI_EOT) {
+ }
+ else if (event->type == MIDI_SET_TEMPO) {
+ fluid_player_set_midi_tempo(player, event->param1);
+ }
+ else {
+ if (player->playback_callback)
+ player->playback_callback(player->playback_userdata, event);
+ }
+
+ fluid_track_next_event(track);
+
+ }
+ return status;
+}
+
+/******************************************************
+ *
+ * fluid_player
+ */
+
+/**
+ * create a new midi player.
+ * @param synth fluid synthesizer instance to create player for
+ * @return New MIDI player instance or NULL on error (out of memory)
+ */
+//ok how do we call new_fluid_player with the filename of the midifile? wmayeb call fluid_player_load() with file name
+fluid_player_t *
+new_fluid_player(void)
+{
+ int i;
+ fluid_player_t *player;
+ player = FLUID_NEW(fluid_player_t);
+ if (player == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ player->status = FLUID_PLAYER_READY;
+ player->loop = 1;
+ player->ntracks = 0;
+ for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) {
+ player->track[i] = NULL;
+ }
+ player->synth = NULL;
+// player->system_timer = NULL;
+// player->sample_timer = NULL;
+ player->playlist = NULL;
+ player->currentfile = NULL;
+ player->division = 0;
+ player->send_program_change = 1;
+ player->miditempo = 480000;
+ player->deltatime = 4.0;
+ player->cur_msec = 0;
+ player->cur_ticks = 0;
+ // FIXME fluid_player_set_playback_callback(player, fluid_synth_handle_midi_event, synth);
+
+ // player->use_system_timer = fluid_settings_str_equal(synth->settings,
+ // "player.timing-source", "system");
+
+ //fluid_settings_getint(synth->settings, "player.reset-synth", &i);
+ player->reset_synth_between_songs = i;
+
+ return player;
+}
+
+/**
+ * Delete a MIDI player instance.
+ * @param player MIDI player instance
+ * @return Always returns #FLUID_OK
+ */
+int
+delete_fluid_player(fluid_player_t *player)
+{
+ fluid_list_t *q;
+ fluid_playlist_item* pi;
+
+ if (player == NULL) {
+ return FLUID_OK;
+ }
+ fluid_player_stop(player);
+ fluid_player_reset(player);
+
+ while (player->playlist != NULL) {
+ q = player->playlist->next;
+ pi = (fluid_playlist_item*) player->playlist->data;
+ FLUID_FREE(pi->filename);
+ FLUID_FREE(pi->buffer);
+ FLUID_FREE(pi);
+ delete1_fluid_list(player->playlist);
+ player->playlist = q;
+ }
+
+ FLUID_FREE(player);
+ return FLUID_OK;
+}
+
+/**
+ * Registers settings related to the MIDI player
+ */
+//void
+//fluid_player_settings(fluid_settings_t *settings)
+//{
+ /* player.timing-source can be either "system" (use system timer)
+ or "sample" (use timer based on number of written samples) */
+ /*
+ fluid_settings_register_str(settings, "player.timing-source", "sample", 0,
+ NULL, NULL);
+ fluid_settings_add_option(settings, "player.timing-source", "sample");
+ fluid_settings_add_option(settings, "player.timing-source", "system");
+
+ Selects whether the player should reset the synth between songs, or not.
+ fluid_settings_register_int(settings, "player.reset-synth", 1, 0, 1,
+ FLUID_HINT_TOGGLED, NULL, NULL);
+*/
+//}
+
+
+int
+fluid_player_reset(fluid_player_t *player)
+{
+ int i;
+
+ for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) {
+ if (player->track[i] != NULL) {
+ delete_fluid_track(player->track[i]);
+ player->track[i] = NULL;
+ }
+ }
+ /* player->current_file = NULL; */
+ /* player->status = FLUID_PLAYER_READY; */
+ /* player->loop = 1; */
+ player->ntracks = 0;
+ player->division = 0;
+ player->send_program_change = 1;
+ player->miditempo = 480000;
+ player->deltatime = 4.0;
+ return 0;
+}
+
+/*
+ * fluid_player_add_track
+ */
+int
+fluid_player_add_track(fluid_player_t *player, fluid_track_t *track)
+{
+ if (player->ntracks < MAX_NUMBER_OF_TRACKS) {
+ player->track[player->ntracks++] = track;
+ return FLUID_OK;
+ } else {
+ return FLUID_FAILED;
+ }
+}
+
+/*
+ * fluid_player_count_tracks
+ */
+int
+fluid_player_count_tracks(fluid_player_t *player)
+{
+ return player->ntracks;
+}
+
+/*
+ * fluid_player_get_track
+ */
+fluid_track_t *
+fluid_player_get_track(fluid_player_t *player, int i)
+{
+ if ((i >= 0) && (i < MAX_NUMBER_OF_TRACKS)) {
+ return player->track[i];
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * Change the MIDI callback function. This is usually set to
+ * fluid_synth_handle_midi_event, but can optionally be changed
+ * to a user-defined function instead, for intercepting all MIDI
+ * messages sent to the synth. You can also use a midi router as
+ * the callback function to modify the MIDI messages before sending
+ * them to the synth.
+ * @param player MIDI player instance
+ * @param handler Pointer to callback function
+ * @param handler_data Parameter sent to the callback function
+ * @returns FLUID_OK
+ * @since 1.1.4
+ */
+int
+fluid_player_set_playback_callback(fluid_player_t* player,
+ handle_midi_event_func_t handler, void* handler_data)
+{
+ player->playback_callback = handler;
+ player->playback_userdata = handler_data;
+ return FLUID_OK;
+}
+
+/**
+ * Add a MIDI file to a player queue.
+ * @param player MIDI player instance
+ * @param midifile File name of the MIDI file to add
+ * @return #FLUID_OK or #FLUID_FAILED
+ */
+int
+fluid_player_add(fluid_player_t *player, const char *midifile)
+{
+ fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item));
+ char* f = FLUID_STRDUP(midifile);
+ if (!pi || !f) {
+ FLUID_FREE(pi);
+ FLUID_FREE(f);
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return FLUID_FAILED;
+ }
+
+ pi->filename = f;
+ pi->buffer = NULL;
+ pi->buffer_len = 0;
+ player->playlist = fluid_list_append(player->playlist, pi);
+ return FLUID_OK;
+}
+
+/**
+ * Add a MIDI file to a player queue, from a buffer in memory.
+ * @param player MIDI player instance
+ * @param buffer Pointer to memory containing the bytes of a complete MIDI
+ * file. The data is copied, so the caller may free or modify it immediately
+ * without affecting the playlist.
+ * @param len Length of the buffer, in bytes.
+ * @return #FLUID_OK or #FLUID_FAILED
+ */
+int
+fluid_player_add_mem(fluid_player_t* player, const void *buffer, size_t len)
+{
+ /* Take a copy of the buffer, so the caller can free immediately. */
+ fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item));
+ void *buf_copy = FLUID_MALLOC(len);
+ if (!pi || !buf_copy) {
+ FLUID_FREE(pi);
+ FLUID_FREE(buf_copy);
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return FLUID_FAILED;
+ }
+
+ FLUID_MEMCPY(buf_copy, buffer, len);
+ pi->filename = NULL;
+ pi->buffer = buf_copy;
+ pi->buffer_len = len;
+ player->playlist = fluid_list_append(player->playlist, pi);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_player_load
+ */
+int
+fluid_player_load(fluid_player_t *player, fluid_playlist_item *item)
+{
+ puts("inside fluid player load");
+ //ok do you think this is the function we need to check? well i found it, this long printf i uncommented wasn't used, we need to use fluid_track_send_events inside loop over loaded tracks to actually send events into our callback. ok
+ fluid_midi_file *midifile;
+ char* buffer;
+ size_t buffer_length;
+ int buffer_owned;
+ if (item->filename != NULL)
+ {
+ fluid_file fp;
+ /* This file is specified by filename; load the file from disk */
+ FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile %s", __FILE__, __LINE__,
+ item->filename);
+ /* Read the entire contents of the file into the buffer */
+ fp = FLUID_FOPEN(item->filename, "rb");
+ if (fp == NULL) {
+ FLUID_LOG(FLUID_ERR, "Couldn't open the MIDI file");
+ return FLUID_FAILED;
+ }
+ buffer = fluid_file_read_full(fp, &buffer_length);
+ if (buffer == NULL)
+ {
+ FLUID_FCLOSE(fp);
+ return FLUID_FAILED;
+ }
+ buffer_owned = 1;
+ FLUID_FCLOSE(fp);
+ }
+ else
+ {
+ /* This file is specified by a pre-loaded buffer; load from memory */
+ FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile from memory (%p)",
+ __FILE__, __LINE__, item->buffer);
+ buffer = (char *) item->buffer;
+ buffer_length = item->buffer_len;
+ /* Do not free the buffer (it is owned by the playlist) */
+ buffer_owned = 0;
+ }
+// here file
+ midifile = new_fluid_midi_file(buffer, buffer_length);
+ if (midifile == NULL) {
+ if (buffer_owned) {
+ FLUID_FREE(buffer);
+ }
+ return FLUID_FAILED;
+ }
+ player->division = fluid_midi_file_get_division(midifile);
+ //DIVISION SET HERE
+ fluid_player_set_midi_tempo(player, player->miditempo); // Update deltatime
+ /*FLUID_LOG(FLUID_DBG, "quarter note division=%d\n", player->division); */
+// here it load tracks
+ if (fluid_midi_file_load_tracks(midifile, player) != FLUID_OK) {
+ if (buffer_owned) {
+ FLUID_FREE(buffer);
+ }
+ delete_fluid_midi_file(midifile);
+ return FLUID_FAILED;
+ }
+ delete_fluid_midi_file(midifile);
+ if (buffer_owned) {
+ FLUID_FREE(buffer);
+ }
+ return FLUID_OK;
+}
+
+void
+fluid_player_advancefile(fluid_player_t *player)
+{
+ if (player->playlist == NULL) {
+ return; /* No files to play */
+ }
+ if (player->currentfile != NULL) {
+ player->currentfile = fluid_list_next(player->currentfile);
+ }
+ if (player->currentfile == NULL) {
+ if (player->loop == 0) {
+ return; /* We're done playing */
+ }
+ if (player->loop > 0) {
+ player->loop--;
+ }
+ player->currentfile = player->playlist;
+ }
+}
+
+void
+fluid_player_playlist_load(fluid_player_t *player, unsigned int msec)
+{
+ fluid_playlist_item* current_playitem;
+ int i;
+
+ do {
+ fluid_player_advancefile(player);
+ if (player->currentfile == NULL) {
+ /* Failed to find next song, probably since we're finished */
+ player->status = FLUID_PLAYER_DONE;
+ return;
+ }
+
+ fluid_player_reset(player);
+ current_playitem = (fluid_playlist_item *) player->currentfile->data;
+ } while (fluid_player_load(player, current_playitem) != FLUID_OK);
+
+ /* Successfully loaded midi file */
+
+ player->begin_msec = msec;
+ player->start_msec = msec;
+ player->start_ticks = 0;
+ player->cur_ticks = 0;
+
+// if (player->reset_synth_between_songs) {
+// fluid_synth_system_reset(player->synth);
+// }
+
+ for (i = 0; i < player->ntracks; i++) {
+ if (player->track[i] != NULL) {
+ fluid_track_reset(player->track[i]);
+ }
+ }
+}
+
+
+/*
+ * fluid_player_callback
+ */
+//ok so I guess it all starts here? not sure, new_fluid_player starts a new player struct, then we need fluid_player_load(player, playlist_item)
+//and playlist_item have filename of file to load got it
+int
+fluid_player_callback(void *data, unsigned int msec)
+{
+ int i;
+ int loadnextfile;
+ int status = FLUID_PLAYER_DONE;
+ fluid_player_t *player;
+ fluid_synth_t *synth;
+ player = (fluid_player_t *) data;
+ synth = player->synth;
+
+ loadnextfile = player->currentfile == NULL ? 1 : 0;
+ do {
+ if (loadnextfile) {
+ loadnextfile = 0;
+ fluid_player_playlist_load(player, msec);
+ if (player->currentfile == NULL) {
+ return 0;
+ }
+ }
+
+ player->cur_msec = msec;
+ player->cur_ticks = (player->start_ticks
+ + (int) ((double) (player->cur_msec - player->start_msec)
+ / player->deltatime));
+
+ for (i = 0; i < player->ntracks; i++) {
+ if (!fluid_track_eot(player->track[i])) {
+ status = FLUID_PLAYER_PLAYING;
+ if (fluid_track_send_events(player->track[i], synth, player,
+ player->cur_ticks) != FLUID_OK) {
+ /* */
+ }
+ }
+ }
+
+ if (status == FLUID_PLAYER_DONE) {
+ FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec", __FILE__,
+ __LINE__, (msec - player->begin_msec) / 1000.0);
+ loadnextfile = 1;
+ }
+ } while (loadnextfile);
+
+ player->status = status;
+
+ return 1;
+}
+
+/**
+ * Activates play mode for a MIDI player if not already playing.
+ * @param player MIDI player instance
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int
+fluid_player_play(fluid_player_t *player)
+{
+ if (player->status == FLUID_PLAYER_PLAYING) {
+ return FLUID_OK;
+ }
+
+ if (player->playlist == NULL) {
+ return FLUID_OK;
+ }
+
+ player->status = FLUID_PLAYER_PLAYING;
+
+/*
+ if (player->use_system_timer) {
+ player->system_timer = new_fluid_timer((int) player->deltatime,
+ fluid_player_callback, (void *) player, TRUE, FALSE, TRUE);
+ if (player->system_timer == NULL) {
+ return FLUID_FAILED;
+ }
+ } else {
+ player->sample_timer = new_fluid_sample_timer(player->synth,
+ fluid_player_callback, (void *) player);
+
+ if (player->sample_timer == NULL) {
+ return FLUID_FAILED;
+ }
+ }
+ */
+ return FLUID_OK;
+}
+
+/**
+ * Stops a MIDI player.
+ * @param player MIDI player instance
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_player_stop(fluid_player_t *player)
+{
+/* if (player->system_timer != NULL) {
+ delete_fluid_timer(player->system_timer);
+ }
+ if (player->sample_timer != NULL) {
+ delete_fluid_sample_timer(player->synth, player->sample_timer);
+ }*/
+ player->status = FLUID_PLAYER_DONE;
+// player->sample_timer = NULL;
+ // player->system_timer = NULL;
+ return FLUID_OK;
+}
+
+/**
+ * Get MIDI player status.
+ * @param player MIDI player instance
+ * @return Player status (#fluid_player_status)
+ * @since 1.1.0
+ */
+int
+fluid_player_get_status(fluid_player_t *player)
+{
+ return player->status;
+}
+
+/**
+ * Enable looping of a MIDI player
+ * @param player MIDI player instance
+ * @param loop Times left to loop the playlist. -1 means loop infinitely.
+ * @return Always returns #FLUID_OK
+ * @since 1.1.0
+ *
+ * For example, if you want to loop the playlist twice, set loop to 2
+ * and call this function before you start the player.
+ */
+int fluid_player_set_loop(fluid_player_t *player, int loop)
+{
+ player->loop = loop;
+ return FLUID_OK;
+}
+
+/**
+ * Set the tempo of a MIDI player.
+ * @param player MIDI player instance
+ * @param tempo Tempo to set playback speed to (in microseconds per quarter note, as per MIDI file spec)
+ * @return Always returns #FLUID_OK
+ */
+int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo)
+{
+ player->miditempo = tempo;
+ //DIVISION CALCULATION MADE HERE
+ //USE THIS TO FIX MRS WATSON
+ player->deltatime = (double) tempo / player->division / 1000.0; /* in milliseconds */
+ player->start_msec = player->cur_msec;
+ player->start_ticks = player->cur_ticks;
+
+ FLUID_LOG(FLUID_DBG,
+ "tempo=%d, tick time=%f msec, cur time=%d msec, cur tick=%d",
+ tempo, player->deltatime, player->cur_msec, player->cur_ticks);
+//so this would be player->cur_msec? yes
+ return FLUID_OK;
+}
+
+/**
+ * Set the tempo of a MIDI player in beats per minute.
+ * @param player MIDI player instance
+ * @param bpm Tempo in beats per minute
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_player_set_bpm(fluid_player_t *player, int bpm)
+{
+ return fluid_player_set_midi_tempo(player, (int) ((double) 60 * 1e6 / bpm));
+}
+
+/**
+ * Wait for a MIDI player to terminate (when done playing).
+ * @param player MIDI player instance
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+/*
+int
+fluid_player_join(fluid_player_t *player)
+{
+ if (player->system_timer) {
+ return fluid_timer_join(player->system_timer);
+ } else if (player->sample_timer) {
+ while (player->status != FLUID_PLAYER_DONE) {
+#if defined(WIN32)
+ Sleep(10);
+#else
+ usleep(10000);
+#endif
+ }
+ }
+ return FLUID_OK;
+}
+*/
+/************************************************************************
+ * MIDI PARSER
+ *
+ */
+
+/*
+ * new_fluid_midi_parser
+ */
+fluid_midi_parser_t *
+new_fluid_midi_parser ()
+{
+ fluid_midi_parser_t *parser;
+ parser = FLUID_NEW(fluid_midi_parser_t);
+ if (parser == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ parser->status = 0; /* As long as the status is 0, the parser won't do anything -> no need to initialize all the fields. */
+ return parser;
+}
+
+/*
+ * delete_fluid_midi_parser
+ */
+int
+delete_fluid_midi_parser(fluid_midi_parser_t *parser)
+{
+ FLUID_FREE(parser);
+ return FLUID_OK;
+}
+
+/**
+ * Parse a MIDI stream one character at a time.
+ * @param parser Parser instance
+ * @param c Next character in MIDI stream
+ * @return A parsed MIDI event or NULL if none. Event is internal and should
+ * not be modified or freed and is only valid until next call to this function.
+ */
+fluid_midi_event_t *
+fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c)
+{
+ fluid_midi_event_t *event;
+
+ /* Real-time messages (0xF8-0xFF) can occur anywhere, even in the middle
+ * of another message. */
+ if (c >= 0xF8) {
+ if (c == MIDI_SYSTEM_RESET) {
+ parser->event.type = c;
+ parser->status = 0; /* clear the status */
+ return &parser->event;
+ }
+
+ return NULL;
+ }
+
+ /* Status byte? - If previous message not yet complete, it is discarded (re-sync). */
+ if (c & 0x80) {
+ /* Any status byte terminates SYSEX messages (not just 0xF7) */
+ if (parser->status == MIDI_SYSEX && parser->nr_bytes > 0) {
+ event = &parser->event;
+ fluid_midi_event_set_sysex(event, parser->data, parser->nr_bytes,
+ FALSE);
+ } else
+ event = NULL;
+
+ if (c < 0xF0) /* Voice category message? */
+ {
+ parser->channel = c & 0x0F;
+ parser->status = c & 0xF0;
+
+ /* The event consumes x bytes of data... (subtract 1 for the status byte) */
+ parser->nr_bytes_total = fluid_midi_event_length(parser->status)
+ - 1;
+
+ parser->nr_bytes = 0; /* 0 bytes read so far */
+ } else if (c == MIDI_SYSEX) {
+ parser->status = MIDI_SYSEX;
+ parser->nr_bytes = 0;
+ } else
+ parser->status = 0; /* Discard other system messages (0xF1-0xF7) */
+
+ return event; /* Return SYSEX event or NULL */
+ }
+
+ /* Data/parameter byte */
+
+ /* Discard data bytes for events we don't care about */
+ if (parser->status == 0)
+ return NULL;
+
+ /* Max data size exceeded? (SYSEX messages only really) */
+ if (parser->nr_bytes == FLUID_MIDI_PARSER_MAX_DATA_SIZE) {
+ parser->status = 0; /* Discard the rest of the message */
+ return NULL;
+ }
+
+ /* Store next byte */
+ parser->data[parser->nr_bytes++] = c;
+
+ /* Do we still need more data to get this event complete? */
+ if (parser->nr_bytes < parser->nr_bytes_total)
+ return NULL;
+
+ /* Event is complete, return it.
+ * Running status byte MIDI feature is also handled here. */
+ parser->event.type = parser->status;
+ parser->event.channel = parser->channel;
+ parser->nr_bytes = 0; /* Reset data size, in case there are additional running status messages */
+
+ switch (parser->status) {
+ case NOTE_OFF:
+ case NOTE_ON:
+ case KEY_PRESSURE:
+ case CONTROL_CHANGE:
+ case PROGRAM_CHANGE:
+ case CHANNEL_PRESSURE:
+ parser->event.param1 = parser->data[0]; /* For example key number */
+ parser->event.param2 = parser->data[1]; /* For example velocity */
+ break;
+ case PITCH_BEND:
+ /* Pitch-bend is transmitted with 14-bit precision. */
+ parser->event.param1 = (parser->data[1] << 7) | parser->data[0];
+ break;
+ default: /* Unlikely */
+ return NULL;
+ }
+
+ return &parser->event;
+}
+
+/* Purpose:
+ * Returns the length of a MIDI message. */
+static int
+fluid_midi_event_length(unsigned char event)
+{
+ switch (event & 0xF0) {
+ case NOTE_OFF:
+ case NOTE_ON:
+ case KEY_PRESSURE:
+ case CONTROL_CHANGE:
+ case PITCH_BEND:
+ return 3;
+ case PROGRAM_CHANGE:
+ case CHANNEL_PRESSURE:
+ return 2;
+ }
+ switch (event) {
+ case MIDI_TIME_CODE:
+ case MIDI_SONG_SELECT:
+ case 0xF4:
+ case 0xF5:
+ return 2;
+ case MIDI_TUNE_REQUEST:
+ return 1;
+ case MIDI_SONG_POSITION:
+ return 3;
+ }
+ return 1;
+}
+
+
diff --git a/src/midi/fluid_midi.h b/src/midi/fluid_midi.h
new file mode 100644
index 0000000..a54f91c
--- /dev/null
+++ b/src/midi/fluid_midi.h
@@ -0,0 +1,389 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUID_MIDI_H
+#define _FLUID_MIDI_H
+
+#include "fluidsynth_priv.h"
+//#include "fluid_sys.h"
+//#include "custom.h"
+#include "fluid_list.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+typedef struct _fluid_midi_parser_t fluid_midi_parser_t;
+
+fluid_midi_parser_t* new_fluid_midi_parser(void);
+int delete_fluid_midi_parser(fluid_midi_parser_t* parser);
+fluid_midi_event_t* fluid_midi_parser_parse(fluid_midi_parser_t* parser, unsigned char c);
+
+fluid_midi_event_t *new_fluid_midi_event (void);
+
+
+/***************************************************************
+ *
+ * CONSTANTS & ENUM
+ */
+
+
+#define MAX_NUMBER_OF_TRACKS 128
+
+enum fluid_midi_event_type {
+ /* channel messages */
+ NOTE_OFF = 0x80, //what are these codes, they're in hex? yes so it's an int, in hex? well just an int, written here in hex. why hex? easier to read
+ NOTE_ON = 0x90, //oh so 90 is actually note on ok should we try printing as ints and inspecting more carefully?
+ KEY_PRESSURE = 0xa0, //so I guess there's a correlation between KEY_PRESSURE and velocity...maybe CHANNEL_PRESSURE and volume? but we should ignore this for now? yeah nure how all it works, you can play with it later then we caould actually hear something generated from this thing.
+ CONTROL_CHANGE = 0xb0,
+ PROGRAM_CHANGE = 0xc0,
+ CHANNEL_PRESSURE = 0xd0,
+ PITCH_BEND = 0xe0,
+ /* system exclusive */
+ MIDI_SYSEX = 0xf0,
+ /* system common - never in midi files */
+ MIDI_TIME_CODE = 0xf1,
+ MIDI_SONG_POSITION = 0xf2,
+ MIDI_SONG_SELECT = 0xf3,
+ MIDI_TUNE_REQUEST = 0xf6,
+ MIDI_EOX = 0xf7,
+ /* system real-time - never in midi files */
+ MIDI_SYNC = 0xf8,
+ MIDI_TICK = 0xf9,
+ MIDI_START = 0xfa,
+ MIDI_CONTINUE = 0xfb,
+ MIDI_STOP = 0xfc,
+ MIDI_ACTIVE_SENSING = 0xfe,
+ MIDI_SYSTEM_RESET = 0xff,
+ /* meta event - for midi files only */
+ MIDI_META_EVENT = 0xff
+};
+
+enum fluid_midi_control_change {
+ BANK_SELECT_MSB = 0x00,
+ MODULATION_MSB = 0x01,
+ BREATH_MSB = 0x02,
+ FOOT_MSB = 0x04,
+ PORTAMENTO_TIME_MSB = 0x05,
+ DATA_ENTRY_MSB = 0x06,
+ VOLUME_MSB = 0x07,
+ BALANCE_MSB = 0x08,
+ PAN_MSB = 0x0A,
+ EXPRESSION_MSB = 0x0B,
+ EFFECTS1_MSB = 0x0C,
+ EFFECTS2_MSB = 0x0D,
+ GPC1_MSB = 0x10, /* general purpose controller */
+ GPC2_MSB = 0x11,
+ GPC3_MSB = 0x12,
+ GPC4_MSB = 0x13,
+ BANK_SELECT_LSB = 0x20,
+ MODULATION_WHEEL_LSB = 0x21,
+ BREATH_LSB = 0x22,
+ FOOT_LSB = 0x24,
+ PORTAMENTO_TIME_LSB = 0x25,
+ DATA_ENTRY_LSB = 0x26,
+ VOLUME_LSB = 0x27,
+ BALANCE_LSB = 0x28,
+ PAN_LSB = 0x2A,
+ EXPRESSION_LSB = 0x2B,
+ EFFECTS1_LSB = 0x2C,
+ EFFECTS2_LSB = 0x2D,
+ GPC1_LSB = 0x30,
+ GPC2_LSB = 0x31,
+ GPC3_LSB = 0x32,
+ GPC4_LSB = 0x33,
+ SUSTAIN_SWITCH = 0x40,
+ PORTAMENTO_SWITCH = 0x41,
+ SOSTENUTO_SWITCH = 0x42,
+ SOFT_PEDAL_SWITCH = 0x43,
+ LEGATO_SWITCH = 0x45,
+ HOLD2_SWITCH = 0x45,
+ SOUND_CTRL1 = 0x46,
+ SOUND_CTRL2 = 0x47,
+ SOUND_CTRL3 = 0x48,
+ SOUND_CTRL4 = 0x49,
+ SOUND_CTRL5 = 0x4A,
+ SOUND_CTRL6 = 0x4B,
+ SOUND_CTRL7 = 0x4C,
+ SOUND_CTRL8 = 0x4D,
+ SOUND_CTRL9 = 0x4E,
+ SOUND_CTRL10 = 0x4F,
+ GPC5 = 0x50,
+ GPC6 = 0x51,
+ GPC7 = 0x52,
+ GPC8 = 0x53,
+ PORTAMENTO_CTRL = 0x54,
+ EFFECTS_DEPTH1 = 0x5B,
+ EFFECTS_DEPTH2 = 0x5C,
+ EFFECTS_DEPTH3 = 0x5D,
+ EFFECTS_DEPTH4 = 0x5E,
+ EFFECTS_DEPTH5 = 0x5F,
+ DATA_ENTRY_INCR = 0x60,
+ DATA_ENTRY_DECR = 0x61,
+ NRPN_LSB = 0x62,
+ NRPN_MSB = 0x63,
+ RPN_LSB = 0x64,
+ RPN_MSB = 0x65,
+ ALL_SOUND_OFF = 0x78,
+ ALL_CTRL_OFF = 0x79,
+ LOCAL_CONTROL = 0x7A,
+ ALL_NOTES_OFF = 0x7B,
+ OMNI_OFF = 0x7C,
+ OMNI_ON = 0x7D,
+ POLY_OFF = 0x7E,
+ POLY_ON = 0x7F
+};
+
+/* General MIDI RPN event numbers (LSB, MSB = 0) */
+enum midi_rpn_event {
+ RPN_PITCH_BEND_RANGE = 0x00,
+ RPN_CHANNEL_FINE_TUNE = 0x01,
+ RPN_CHANNEL_COARSE_TUNE = 0x02,
+ RPN_TUNING_PROGRAM_CHANGE = 0x03,
+ RPN_TUNING_BANK_SELECT = 0x04,
+ RPN_MODULATION_DEPTH_RANGE = 0x05
+};
+
+enum midi_meta_event {
+ MIDI_COPYRIGHT = 0x02,
+ MIDI_TRACK_NAME = 0x03,
+ MIDI_INST_NAME = 0x04,
+ MIDI_LYRIC = 0x05,
+ MIDI_MARKER = 0x06,
+ MIDI_CUE_POINT = 0x07,
+ MIDI_EOT = 0x2f,
+ MIDI_SET_TEMPO = 0x51,
+ MIDI_SMPTE_OFFSET = 0x54,
+ MIDI_TIME_SIGNATURE = 0x58,
+ MIDI_KEY_SIGNATURE = 0x59,
+ MIDI_SEQUENCER_EVENT = 0x7f
+};
+
+/* MIDI SYSEX useful manufacturer values */
+enum midi_sysex_manuf {
+ MIDI_SYSEX_MANUF_ROLAND = 0x41, /**< Roland manufacturer ID */
+ MIDI_SYSEX_UNIV_NON_REALTIME = 0x7E, /**< Universal non realtime message */
+ MIDI_SYSEX_UNIV_REALTIME = 0x7F /**< Universal realtime message */
+};
+
+#define MIDI_SYSEX_DEVICE_ID_ALL 0x7F /**< Device ID used in SYSEX messages to indicate all devices */
+
+/* SYSEX sub-ID #1 which follows device ID */
+#define MIDI_SYSEX_MIDI_TUNING_ID 0x08 /**< Sysex sub-ID #1 for MIDI tuning messages */
+#define MIDI_SYSEX_GM_ID 0x09 /**< Sysex sub-ID #1 for General MIDI messages */
+
+/**
+ * SYSEX tuning message IDs.
+ */
+enum midi_sysex_tuning_msg_id {
+ MIDI_SYSEX_TUNING_BULK_DUMP_REQ = 0x00, /**< Bulk tuning dump request (non-realtime) */
+ MIDI_SYSEX_TUNING_BULK_DUMP = 0x01, /**< Bulk tuning dump response (non-realtime) */
+ MIDI_SYSEX_TUNING_NOTE_TUNE = 0x02, /**< Tuning note change message (realtime) */
+ MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK = 0x03, /**< Bulk tuning dump request (with bank, non-realtime) */
+ MIDI_SYSEX_TUNING_BULK_DUMP_BANK = 0x04, /**< Bulk tuning dump resonse (with bank, non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_DUMP_1BYTE = 0x05, /**< Octave tuning dump using 1 byte values (non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_DUMP_2BYTE = 0x06, /**< Octave tuning dump using 2 byte values (non-realtime) */
+ MIDI_SYSEX_TUNING_NOTE_TUNE_BANK = 0x07, /**< Tuning note change message (with bank, realtime/non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE = 0x08, /**< Octave tuning message using 1 byte values (realtime/non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE = 0x09 /**< Octave tuning message using 2 byte values (realtime/non-realtime) */
+};
+
+/* General MIDI sub-ID #2 */
+#define MIDI_SYSEX_GM_ON 0x01 /**< Enable GM mode */
+#define MIDI_SYSEX_GM_OFF 0x02 /**< Disable GM mode */
+
+enum fluid_driver_status
+{
+ FLUID_MIDI_READY,
+ FLUID_MIDI_LISTENING,
+ FLUID_MIDI_DONE
+};
+
+/***************************************************************
+ *
+ * TYPE DEFINITIONS & FUNCTION DECLARATIONS
+ */
+
+/* From ctype.h */
+#define fluid_isascii(c) (((c) & ~0x7f) == 0)
+
+
+
+/*
+ * fluid_midi_event_t
+ */
+struct _fluid_midi_event_t {
+ fluid_midi_event_t* next; /* Link to next event */
+ void *paramptr; /* Pointer parameter (for SYSEX data), size is stored to param1, param2 indicates if pointer should be freed (dynamic if TRUE) */
+ unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */
+ unsigned int param1; /* First parameter */
+ unsigned int param2; /* Second parameter */
+ unsigned char type; /* MIDI event type */
+ unsigned char channel; /* MIDI channel */
+};
+
+
+/*
+ * fluid_track_t
+ */
+struct _fluid_track_t {
+ char* name;
+ int num;
+ fluid_midi_event_t *first;
+ fluid_midi_event_t *cur;
+ fluid_midi_event_t *last;
+ unsigned int ticks;
+};
+
+typedef struct _fluid_track_t fluid_track_t;
+
+fluid_track_t* new_fluid_track(int num);
+int delete_fluid_track(fluid_track_t* track);
+int fluid_track_set_name(fluid_track_t* track, char* name);
+char* fluid_track_get_name(fluid_track_t* track);
+int fluid_track_add_event(fluid_track_t* track, fluid_midi_event_t* evt);
+fluid_midi_event_t* fluid_track_first_event(fluid_track_t* track);
+fluid_midi_event_t* fluid_track_next_event(fluid_track_t* track);
+int fluid_track_get_duration(fluid_track_t* track);
+int fluid_track_reset(fluid_track_t* track);
+
+int fluid_track_send_events(fluid_track_t* track,
+ fluid_synth_t* synth,
+ fluid_player_t* player,
+ unsigned int ticks);
+
+#define fluid_track_eot(track) ((track)->cur == NULL)
+
+
+/**
+ * fluid_playlist_item
+ * Used as the `data' elements of the fluid_player.playlist.
+ * Represents either a filename or a pre-loaded memory buffer.
+ * Exactly one of `filename' and `buffer' is non-NULL.
+ */
+typedef struct
+{
+ char* filename; /** Name of file (owned); NULL if data pre-loaded */
+ void* buffer; /** The MIDI file data (owned); NULL if filename */
+ size_t buffer_len; /** Number of bytes in buffer; 0 if filename */
+} fluid_playlist_item;
+
+/*
+ * fluid_player
+ */
+struct _fluid_player_t {
+ int status;
+ int ntracks;
+ fluid_track_t *track[MAX_NUMBER_OF_TRACKS];
+ fluid_synth_t* synth;
+ fluid_timer_t* system_timer;
+ fluid_sample_timer_t* sample_timer;
+
+ int loop; /* -1 = loop infinitely, otherwise times left to loop the playlist */
+ fluid_list_t* playlist; /* List of fluid_playlist_item* objects */
+ fluid_list_t* currentfile; /* points to an item in files, or NULL if not playing */
+
+ char send_program_change; /* should we ignore the program changes? */
+ char use_system_timer; /* if zero, use sample timers, otherwise use system clock timer */
+ char reset_synth_between_songs; /* 1 if system reset should be sent to the synth between songs. */
+ int start_ticks; /* the number of tempo ticks passed at the last tempo change */
+ int cur_ticks; /* the number of tempo ticks passed */
+ int begin_msec; /* the time (msec) of the beginning of the file */
+ int start_msec; /* the start time of the last tempo change */
+ int cur_msec; /* the current time */
+ int miditempo; /* as indicated by MIDI SetTempo: n 24th of a usec per midi-clock. bravo! */
+ double deltatime; /* milliseconds per midi tick. depends on set-tempo */
+ unsigned int division;
+
+ handle_midi_event_func_t playback_callback; /* function fired on each midi event as it is played */
+ void* playback_userdata; /* pointer to user-defined data passed to playback_callback function */
+};
+
+int fluid_player_add_track(fluid_player_t* player, fluid_track_t* track);
+int fluid_player_callback(void* data, unsigned int msec);
+int fluid_player_count_tracks(fluid_player_t* player);
+fluid_track_t* fluid_player_get_track(fluid_player_t* player, int i);
+int fluid_player_reset(fluid_player_t* player);
+int fluid_player_load(fluid_player_t* player, fluid_playlist_item *item);
+
+//void fluid_player_settings(fluid_settings_t* settings);
+
+
+/*
+ * fluid_midi_file
+ */
+//hmm no events though here, right? looks so
+typedef struct {
+ const char* buffer; /* Entire contents of MIDI file (borrowed) */
+ int buf_len; /* Length of buffer, in bytes */
+ int buf_pos; /* Current read position in contents buffer */
+ int eof; /* The "end of file" condition */
+ int running_status;
+ int c;
+ int type;
+ int ntracks;
+ int uses_smpte;
+ unsigned int smpte_fps;
+ unsigned int smpte_res;
+ unsigned int division; /* If uses_SMPTE == 0 then division is
+ ticks per beat (quarter-note) */
+ double tempo; /* Beats per second (SI rules =) */
+ int tracklen;
+ int trackpos;
+ int eot;
+ int varlen;
+ int dtime;
+} fluid_midi_file;
+
+fluid_midi_file* new_fluid_midi_file(const char* buffer, size_t length);
+void delete_fluid_midi_file(fluid_midi_file* mf);
+int fluid_midi_file_read_mthd(fluid_midi_file* midifile);
+int fluid_midi_file_load_tracks(fluid_midi_file* midifile, fluid_player_t* player);
+int fluid_midi_file_read_track(fluid_midi_file* mf, fluid_player_t* player, int num);
+int fluid_midi_file_read_event(fluid_midi_file* mf, fluid_track_t* track);
+int fluid_midi_file_read_varlen(fluid_midi_file* mf);
+int fluid_midi_file_getc(fluid_midi_file* mf);
+int fluid_midi_file_push(fluid_midi_file* mf, int c);
+int fluid_midi_file_read(fluid_midi_file* mf, void* buf, int len);
+int fluid_midi_file_skip(fluid_midi_file* mf, int len);
+int fluid_midi_file_eof(fluid_midi_file* mf);
+int fluid_midi_file_read_tracklen(fluid_midi_file* mf);
+int fluid_midi_file_eot(fluid_midi_file* mf);
+int fluid_midi_file_get_division(fluid_midi_file* midifile);
+
+
+#define FLUID_MIDI_PARSER_MAX_DATA_SIZE 1024 /**< Maximum size of MIDI parameters/data (largest is SYSEX data) */
+
+/*
+ * fluid_midi_parser_t
+ */
+struct _fluid_midi_parser_t {
+ unsigned char status; /* Identifies the type of event, that is currently received ('Noteon', 'Pitch Bend' etc). */
+ unsigned char channel; /* The channel of the event that is received (in case of a channel event) */
+ unsigned int nr_bytes; /* How many bytes have been read for the current event? */
+ unsigned int nr_bytes_total; /* How many bytes does the current event type include? */
+ unsigned char data[FLUID_MIDI_PARSER_MAX_DATA_SIZE]; /* The parameters or SYSEX data */
+ fluid_midi_event_t event; /* The event, that is returned to the MIDI driver. */
+};
+
+int fluid_isasciistring(char* s);
+long fluid_getlength(unsigned char *s);
+
+
+#endif /* _FLUID_MIDI_H */
diff --git a/src/midi/fluidsynth_priv.h b/src/midi/fluidsynth_priv.h
new file mode 100644
index 0000000..04c1ee7
--- /dev/null
+++ b/src/midi/fluidsynth_priv.h
@@ -0,0 +1,280 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUIDSYNTH_PRIV_H
+#define _FLUIDSYNTH_PRIV_H
+
+#include "custom.h"
+//#include <glib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined(__POWERPC__) && !(defined(__APPLE__) && defined(__MACH__))
+#include "config_maxmsp43.h"
+#endif
+
+#if defined(WIN32) && !defined(MINGW32)
+#include "config_win32.h"
+#endif
+
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#if HAVE_MATH_H
+#include <math.h>
+#endif
+
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#if HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#if HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#if HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#if HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#if HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+#if HAVE_IO_H
+#include <io.h>
+#endif
+
+#if HAVE_WINDOWS_H
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#endif
+
+/* MinGW32 special defines */
+#ifdef MINGW32
+
+#include <stdint.h>
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+
+#define DSOUND_SUPPORT 1
+#define WINMIDI_SUPPORT 1
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+
+#endif
+
+/* Darwin special defines (taken from config_macosx.h) */
+#ifdef DARWIN
+#define MACINTOSH
+#define __Types__
+#define WITHOUT_SERVER 1
+#endif
+
+
+//#include "fluidsynth.h"
+
+
+/***************************************************************
+ *
+ * BASIC TYPES
+ */
+
+#if defined(WITH_FLOAT)
+typedef float fluid_real_t;
+#else
+typedef double fluid_real_t;
+#endif
+
+
+#if defined(WIN32)
+typedef SOCKET fluid_socket_t;
+#else
+typedef int fluid_socket_t;
+#define INVALID_SOCKET -1
+#endif
+
+#if defined(SUPPORTS_VLA)
+# define FLUID_DECLARE_VLA(_type, _name, _len) \
+ _type _name[_len]
+#else
+# define FLUID_DECLARE_VLA(_type, _name, _len) \
+ _type* _name = g_newa(_type, (_len))
+#endif
+
+
+/** Integer types */
+//typedef int8_t sint8_t;
+typedef uint8_t uint8_t;
+//typedef int16 sint16;
+//typedef uint16 uint16;
+typedef int32_t sint32_t;
+typedef uint32_t uint32_t;
+//typedef int64 sint64;
+//typedef uint64 uint64;
+
+
+/***************************************************************
+ *
+ * FORWARD DECLARATIONS
+ */
+typedef struct _fluid_env_data_t fluid_env_data_t;
+typedef struct _fluid_adriver_definition_t fluid_adriver_definition_t;
+typedef struct _fluid_channel_t fluid_channel_t;
+typedef struct _fluid_tuning_t fluid_tuning_t;
+typedef struct _fluid_hashtable_t fluid_hashtable_t;
+typedef struct _fluid_client_t fluid_client_t;
+typedef struct _fluid_server_socket_t fluid_server_socket_t;
+typedef struct _fluid_sample_timer_t fluid_sample_timer_t;
+
+/***************************************************************
+ *
+ * CONSTANTS
+ */
+
+#define FLUID_BUFSIZE 64 /**< FluidSynth internal buffer size (in samples) */
+#define FLUID_MAX_EVENTS_PER_BUFSIZE 1024 /**< Maximum queued MIDI events per #FLUID_BUFSIZE */
+#define FLUID_MAX_RETURN_EVENTS 1024 /**< Maximum queued synthesis thread return events */
+#define FLUID_MAX_EVENT_QUEUES 16 /**< Maximum number of unique threads queuing events */
+#define FLUID_DEFAULT_AUDIO_RT_PRIO 60 /**< Default setting for audio.realtime-prio */
+#define FLUID_DEFAULT_MIDI_RT_PRIO 50 /**< Default setting for midi.realtime-prio */
+
+#ifndef PI
+#define PI 3.141592654
+#endif
+
+/***************************************************************
+ *
+ * SYSTEM INTERFACE
+ */
+typedef FILE* fluid_file;
+
+#define FLUID_MALLOC(_n) malloc(_n)
+#define FLUID_REALLOC(_p,_n) realloc(_p,_n)
+#define FLUID_NEW(_t) (_t*)malloc(sizeof(_t))
+#define FLUID_ARRAY(_t,_n) (_t*)malloc((_n)*sizeof(_t))
+#define FLUID_FREE(_p) free(_p)
+#define FLUID_FOPEN(_f,_m) fopen(_f,_m)
+#define FLUID_FCLOSE(_f) fclose(_f)
+#define FLUID_FREAD(_p,_s,_n,_f) fread(_p,_s,_n,_f)
+#define FLUID_FSEEK(_f,_n,_set) fseek(_f,_n,_set)
+#define FLUID_MEMCPY(_dst,_src,_n) memcpy(_dst,_src,_n)
+#define FLUID_MEMSET(_s,_c,_n) memset(_s,_c,_n)
+#define FLUID_STRLEN(_s) strlen(_s)
+#define FLUID_STRCMP(_s,_t) strcmp(_s,_t)
+#define FLUID_STRNCMP(_s,_t,_n) strncmp(_s,_t,_n)
+#define FLUID_STRCPY(_dst,_src) strcpy(_dst,_src)
+#define FLUID_STRNCPY(_dst,_src,_n) strncpy(_dst,_src,_n)
+#define FLUID_STRCHR(_s,_c) strchr(_s,_c)
+#define FLUID_STRRCHR(_s,_c) strrchr(_s,_c)
+#ifdef strdup
+#define FLUID_STRDUP(s) strdup(s)
+#else
+#define FLUID_STRDUP(s) FLUID_STRCPY(FLUID_MALLOC(FLUID_STRLEN(s) + 1), s)
+#endif
+#define FLUID_SPRINTF sprintf
+#define FLUID_FPRINTF fprintf
+
+#define fluid_clip(_val, _min, _max) \
+{ (_val) = ((_val) < (_min))? (_min) : (((_val) > (_max))? (_max) : (_val)); }
+
+#if WITH_FTS
+#define FLUID_PRINTF post
+#define FLUID_FLUSH()
+#else
+#define FLUID_PRINTF printf
+#define FLUID_FLUSH() fflush(stdout)
+#endif
+
+#define FLUID_LOG fluid_log
+
+#ifndef M_PI
+#define M_PI 3.1415926535897932384626433832795
+#endif
+
+
+#define FLUID_ASSERT(a,b)
+#define FLUID_ASSERT_P(a,b)
+
+char* fluid_error(void);
+
+
+/* Internationalization */
+#define _(s) s
+
+
+#endif /* _FLUIDSYNTH_PRIV_H */
diff --git a/src/midi/old/fluid_conv.c b/src/midi/old/fluid_conv.c
new file mode 100644
index 0000000..1a790cf
--- /dev/null
+++ b/src/midi/old/fluid_conv.c
@@ -0,0 +1,320 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluid_conv.h"
+
+
+/* conversion tables */
+fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE];
+fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE];
+fluid_real_t fluid_atten2amp_tab[FLUID_ATTEN_AMP_SIZE];
+fluid_real_t fluid_posbp_tab[128];
+fluid_real_t fluid_concave_tab[128];
+fluid_real_t fluid_convex_tab[128];
+fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE];
+
+/*
+ * void fluid_synth_init
+ *
+ * Does all the initialization for this module.
+ */
+void
+fluid_conversion_config(void)
+{
+ int i;
+ double x;
+
+ for (i = 0; i < FLUID_CENTS_HZ_SIZE; i++) {
+ fluid_ct2hz_tab[i] = (fluid_real_t) pow(2.0, (double) i / 1200.0);
+ }
+
+ /* centibels to amplitude conversion
+ * Note: SF2.01 section 8.1.3: Initial attenuation range is
+ * between 0 and 144 dB. Therefore a negative attenuation is
+ * not allowed.
+ */
+ for (i = 0; i < FLUID_CB_AMP_SIZE; i++) {
+ fluid_cb2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / -200.0);
+ }
+
+ /* NOTE: EMU8k and EMU10k devices don't conform to the SoundFont
+ * specification in regards to volume attenuation. The below calculation
+ * is an approx. equation for generating a table equivelant to the
+ * cb_to_amp_table[] in tables.c of the TiMidity++ source, which I'm told
+ * was generated from device testing. By the spec this should be centibels.
+ */
+ for (i = 0; i < FLUID_ATTEN_AMP_SIZE; i++) {
+ fluid_atten2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / FLUID_ATTEN_POWER_FACTOR);
+ }
+
+ /* initialize the conversion tables (see fluid_mod.c
+ fluid_mod_get_value cases 4 and 8) */
+
+ /* concave unipolar positive transform curve */
+ fluid_concave_tab[0] = 0.0;
+ fluid_concave_tab[127] = 1.0;
+
+ /* convex unipolar positive transform curve */
+ fluid_convex_tab[0] = 0;
+ fluid_convex_tab[127] = 1.0;
+ x = log10(128.0 / 127.0);
+
+ /* There seems to be an error in the specs. The equations are
+ implemented according to the pictures on SF2.01 page 73. */
+
+ for (i = 1; i < 127; i++) {
+ x = -20.0 / 96.0 * log((i * i) / (127.0 * 127.0)) / log(10.0);
+ fluid_convex_tab[i] = (fluid_real_t) (1.0 - x);
+ fluid_concave_tab[127 - i] = (fluid_real_t) x;
+ }
+
+ /* initialize the pan conversion table */
+ x = PI / 2.0 / (FLUID_PAN_SIZE - 1.0);
+ for (i = 0; i < FLUID_PAN_SIZE; i++) {
+ fluid_pan_tab[i] = (fluid_real_t) sin(i * x);
+ }
+}
+
+/*
+ * fluid_ct2hz
+ */
+fluid_real_t
+fluid_ct2hz_real(fluid_real_t cents)
+{
+ if (cents < 0)
+ return (fluid_real_t) 1.0;
+ else if (cents < 900) {
+ return (fluid_real_t) 6.875 * fluid_ct2hz_tab[(int) (cents + 300)];
+ } else if (cents < 2100) {
+ return (fluid_real_t) 13.75 * fluid_ct2hz_tab[(int) (cents - 900)];
+ } else if (cents < 3300) {
+ return (fluid_real_t) 27.5 * fluid_ct2hz_tab[(int) (cents - 2100)];
+ } else if (cents < 4500) {
+ return (fluid_real_t) 55.0 * fluid_ct2hz_tab[(int) (cents - 3300)];
+ } else if (cents < 5700) {
+ return (fluid_real_t) 110.0 * fluid_ct2hz_tab[(int) (cents - 4500)];
+ } else if (cents < 6900) {
+ return (fluid_real_t) 220.0 * fluid_ct2hz_tab[(int) (cents - 5700)];
+ } else if (cents < 8100) {
+ return (fluid_real_t) 440.0 * fluid_ct2hz_tab[(int) (cents - 6900)];
+ } else if (cents < 9300) {
+ return (fluid_real_t) 880.0 * fluid_ct2hz_tab[(int) (cents - 8100)];
+ } else if (cents < 10500) {
+ return (fluid_real_t) 1760.0 * fluid_ct2hz_tab[(int) (cents - 9300)];
+ } else if (cents < 11700) {
+ return (fluid_real_t) 3520.0 * fluid_ct2hz_tab[(int) (cents - 10500)];
+ } else if (cents < 12900) {
+ return (fluid_real_t) 7040.0 * fluid_ct2hz_tab[(int) (cents - 11700)];
+ } else if (cents < 14100) {
+ return (fluid_real_t) 14080.0 * fluid_ct2hz_tab[(int) (cents - 12900)];
+ } else {
+ return (fluid_real_t) 1.0; /* some loony trying to make you deaf */
+ }
+}
+
+/*
+ * fluid_ct2hz
+ */
+fluid_real_t
+fluid_ct2hz(fluid_real_t cents)
+{
+ /* Filter fc limit: SF2.01 page 48 # 8 */
+ if (cents >= 13500){
+ cents = 13500; /* 20 kHz */
+ } else if (cents < 1500){
+ cents = 1500; /* 20 Hz */
+ }
+ return fluid_ct2hz_real(cents);
+}
+
+/*
+ * fluid_cb2amp
+ *
+ * in: a value between 0 and 960, 0 is no attenuation
+ * out: a value between 1 and 0
+ */
+fluid_real_t
+fluid_cb2amp(fluid_real_t cb)
+{
+ /*
+ * cb: an attenuation in 'centibels' (1/10 dB)
+ * SF2.01 page 49 # 48 limits it to 144 dB.
+ * 96 dB is reasonable for 16 bit systems, 144 would make sense for 24 bit.
+ */
+
+ /* minimum attenuation: 0 dB */
+ if (cb < 0) {
+ return 1.0;
+ }
+ if (cb >= FLUID_CB_AMP_SIZE) {
+ return 0.0;
+ }
+ return fluid_cb2amp_tab[(int) cb];
+}
+
+/*
+ * fluid_atten2amp
+ *
+ * in: a value between 0 and 1440, 0 is no attenuation
+ * out: a value between 1 and 0
+ *
+ * Note: Volume attenuation is supposed to be centibels but EMU8k/10k don't
+ * follow this. Thats the reason for separate fluid_cb2amp and fluid_atten2amp.
+ */
+fluid_real_t
+fluid_atten2amp(fluid_real_t atten)
+{
+ if (atten < 0) return 1.0;
+ else if (atten >= FLUID_ATTEN_AMP_SIZE) return 0.0;
+ else return fluid_atten2amp_tab[(int) atten];
+}
+
+/*
+ * fluid_tc2sec_delay
+ */
+fluid_real_t
+fluid_tc2sec_delay(fluid_real_t tc)
+{
+ /* SF2.01 section 8.1.2 items 21, 23, 25, 33
+ * SF2.01 section 8.1.3 items 21, 23, 25, 33
+ *
+ * The most negative number indicates a delay of 0. Range is limited
+ * from -12000 to 5000 */
+ if (tc <= -32768.0f) {
+ return (fluid_real_t) 0.0f;
+ };
+ if (tc < -12000.) {
+ tc = (fluid_real_t) -12000.0f;
+ }
+ if (tc > 5000.0f) {
+ tc = (fluid_real_t) 5000.0f;
+ }
+ return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
+}
+
+/*
+ * fluid_tc2sec_attack
+ */
+fluid_real_t
+fluid_tc2sec_attack(fluid_real_t tc)
+{
+ /* SF2.01 section 8.1.2 items 26, 34
+ * SF2.01 section 8.1.3 items 26, 34
+ * The most negative number indicates a delay of 0
+ * Range is limited from -12000 to 8000 */
+ if (tc<=-32768.){return (fluid_real_t) 0.0;};
+ if (tc<-12000.){tc=(fluid_real_t) -12000.0;};
+ if (tc>8000.){tc=(fluid_real_t) 8000.0;};
+ return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
+}
+
+/*
+ * fluid_tc2sec
+ */
+fluid_real_t
+fluid_tc2sec(fluid_real_t tc)
+{
+ /* No range checking here! */
+ return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
+}
+
+/*
+ * fluid_tc2sec_release
+ */
+fluid_real_t
+fluid_tc2sec_release(fluid_real_t tc)
+{
+ /* SF2.01 section 8.1.2 items 30, 38
+ * SF2.01 section 8.1.3 items 30, 38
+ * No 'most negative number' rule here!
+ * Range is limited from -12000 to 8000 */
+ if (tc<=-32768.){return (fluid_real_t) 0.0;};
+ if (tc<-12000.){tc=(fluid_real_t) -12000.0;};
+ if (tc>8000.){tc=(fluid_real_t) 8000.0;};
+ return (fluid_real_t) pow(2.0, (double) tc / 1200.0);
+}
+
+/*
+ * fluid_act2hz
+ *
+ * Convert from absolute cents to Hertz
+ */
+fluid_real_t
+fluid_act2hz(fluid_real_t c)
+{
+ return (fluid_real_t) (8.176 * pow(2.0, (double) c / 1200.0));
+}
+
+/*
+ * fluid_hz2ct
+ *
+ * Convert from Hertz to cents
+ */
+fluid_real_t
+fluid_hz2ct(fluid_real_t f)
+{
+ return (fluid_real_t) (6900 + 1200 * log(f / 440.0) / log(2.0));
+}
+
+/*
+ * fluid_pan
+ */
+fluid_real_t
+fluid_pan(fluid_real_t c, int left)
+{
+ if (left) {
+ c = -c;
+ }
+ if (c < -500) {
+ return (fluid_real_t) 0.0;
+ } else if (c > 500) {
+ return (fluid_real_t) 1.0;
+ } else {
+ return fluid_pan_tab[(int) (c + 500)];
+ }
+}
+
+/*
+ * fluid_concave
+ */
+fluid_real_t
+fluid_concave(fluid_real_t val)
+{
+ if (val < 0) {
+ return 0;
+ } else if (val > 127) {
+ return 1;
+ }
+ return fluid_concave_tab[(int) val];
+}
+
+/*
+ * fluid_convex
+ */
+fluid_real_t
+fluid_convex(fluid_real_t val)
+{
+ if (val < 0) {
+ return 0;
+ } else if (val > 127) {
+ return 1;
+ }
+ return fluid_convex_tab[(int) val];
+}
diff --git a/src/midi/old/fluid_conv.h b/src/midi/old/fluid_conv.h
new file mode 100644
index 0000000..29793c3
--- /dev/null
+++ b/src/midi/old/fluid_conv.h
@@ -0,0 +1,63 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUID_CONV_H
+#define _FLUID_CONV_H
+
+#include "fluidsynth_priv.h"
+
+#define FLUID_CENTS_HZ_SIZE 1200
+#define FLUID_VEL_CB_SIZE 128
+#define FLUID_CB_AMP_SIZE 961
+#define FLUID_ATTEN_AMP_SIZE 1441
+#define FLUID_PAN_SIZE 1002
+
+/* EMU 8k/10k don't follow spec in regards to volume attenuation.
+ * This factor is used in the equation pow (10.0, cb / FLUID_ATTEN_POWER_FACTOR).
+ * By the standard this should be -200.0. */
+/* 07/11/2008 modified by S. Christian Collins for increased velocity sensitivity. Now it equals the response of EMU10K1 programming.*/
+#define FLUID_ATTEN_POWER_FACTOR (-200.0) /* was (-531.509)*/
+
+void fluid_conversion_config(void);
+
+fluid_real_t fluid_ct2hz_real(fluid_real_t cents);
+fluid_real_t fluid_ct2hz(fluid_real_t cents);
+fluid_real_t fluid_cb2amp(fluid_real_t cb);
+fluid_real_t fluid_atten2amp(fluid_real_t atten);
+fluid_real_t fluid_tc2sec(fluid_real_t tc);
+fluid_real_t fluid_tc2sec_delay(fluid_real_t tc);
+fluid_real_t fluid_tc2sec_attack(fluid_real_t tc);
+fluid_real_t fluid_tc2sec_release(fluid_real_t tc);
+fluid_real_t fluid_act2hz(fluid_real_t c);
+fluid_real_t fluid_hz2ct(fluid_real_t c);
+fluid_real_t fluid_pan(fluid_real_t c, int left);
+fluid_real_t fluid_concave(fluid_real_t val);
+fluid_real_t fluid_convex(fluid_real_t val);
+
+extern fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE];
+extern fluid_real_t fluid_vel2cb_tab[FLUID_VEL_CB_SIZE];
+extern fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE];
+extern fluid_real_t fluid_posbp_tab[128];
+extern fluid_real_t fluid_concave_tab[128];
+extern fluid_real_t fluid_convex_tab[128];
+extern fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE];
+
+
+#endif /* _FLUID_CONV_H */
diff --git a/src/midi/old/fluid_hash.c b/src/midi/old/fluid_hash.c
new file mode 100644
index 0000000..cd6ca4c
--- /dev/null
+++ b/src/midi/old/fluid_hash.c
@@ -0,0 +1,1308 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ *
+ * Adapted for FluidSynth use by Josh Green <jgreen@users.sourceforge.net>
+ * September 8, 2009 from glib 2.18.4
+ */
+
+/*
+ * MT safe
+ */
+
+#include "fluidsynth_priv.h"
+#include "fluid_hash.h"
+#include "fluid_list.h"
+
+
+#define HASH_TABLE_MIN_SIZE 11
+#define HASH_TABLE_MAX_SIZE 13845163
+
+
+typedef struct
+{
+ fluid_hashtable_t *hashtable;
+ fluid_hashnode_t *prev_node;
+ fluid_hashnode_t *node;
+ int position;
+ int pre_advanced; // Boolean
+ int version;
+} RealIter;
+
+
+/* Excerpt from glib gprimes.c */
+
+static const uint primes[] =
+{
+ 11,
+ 19,
+ 37,
+ 73,
+ 109,
+ 163,
+ 251,
+ 367,
+ 557,
+ 823,
+ 1237,
+ 1861,
+ 2777,
+ 4177,
+ 6247,
+ 9371,
+ 14057,
+ 21089,
+ 31627,
+ 47431,
+ 71143,
+ 106721,
+ 160073,
+ 240101,
+ 360163,
+ 540217,
+ 810343,
+ 1215497,
+ 1823231,
+ 2734867,
+ 4102283,
+ 6153409,
+ 9230113,
+ 13845163,
+};
+
+static const unsigned int nprimes = sizeof (primes) / sizeof (primes[0]);
+
+unsigned int
+spaced_primes_closest (unsigned int num)
+{
+ unsigned int i;
+
+ for (i = 0; i < nprimes; i++)
+ if (primes[i] > num)
+ return primes[i];
+
+ return primes[nprimes - 1];
+}
+
+/* End excerpt from glib gprimes.c */
+
+
+/*
+ * @hashtable: our #fluid_hashtable_t
+ * @key: the key to lookup against
+ * @hash_return: optional key hash return location
+ * Return value: a pointer to the described #fluid_hashnode_t pointer
+ *
+ * Performs a lookup in the hash table. Virtually all hash operations
+ * will use this function internally.
+ *
+ * This function first computes the hash value of the key using the
+ * user's hash function.
+ *
+ * If an entry in the table matching @key is found then this function
+ * returns a pointer to the pointer to that entry in the table. In
+ * the case that the entry is at the head of a chain, this pointer
+ * will be an item in the nodes[] array. In the case that the entry
+ * is not at the head of a chain, this pointer will be the ->next
+ * pointer on the node that preceeds it.
+ *
+ * In the case that no matching entry exists in the table, a pointer
+ * to a %NULL pointer will be returned. To insert a item, this %NULL
+ * pointer should be updated to point to the new #fluid_hashnode_t.
+ *
+ * If @hash_return is a pass-by-reference parameter. If it is
+ * non-%NULL then the computed hash value is returned. This is to
+ * save insertions from having to compute the hash record again for
+ * the new record.
+ */
+static inline fluid_hashnode_t **
+fluid_hashtable_lookup_node (fluid_hashtable_t *hashtable, const void *key,
+ unsigned int *hash_return)
+{
+ fluid_hashnode_t **node_ptr, *node;
+ unsigned int hash_value;
+
+ hash_value = (* hashtable->hash_func)(key);
+ node_ptr = &hashtable->nodes[hash_value % hashtable->size];
+
+ if (hash_return)
+ *hash_return = hash_value;
+
+ /* Hash table lookup needs to be fast.
+ * We therefore remove the extra conditional of testing
+ * whether to call the key_equal_func or not from
+ * the inner loop.
+ *
+ * Additional optimisation: first check if our full hash
+ * values are equal so we can avoid calling the full-blown
+ * key equality function in most cases.
+ */
+ if (hashtable->key_equal_func)
+ {
+ while ((node = *node_ptr))
+ {
+ if (node->key_hash == hash_value &&
+ hashtable->key_equal_func (node->key, key))
+ break;
+
+ node_ptr = &(*node_ptr)->next;
+ }
+ }
+ else
+ {
+ while ((node = *node_ptr))
+ {
+ if (node->key == key)
+ break;
+
+ node_ptr = &(*node_ptr)->next;
+ }
+ }
+
+ return node_ptr;
+}
+
+/*
+ * @hashtable: our #fluid_hashtable_t
+ * @node_ptr_ptr: a pointer to the return value from
+ * fluid_hashtable_lookup_node()
+ * @notify: %TRUE if the destroy notify handlers are to be called
+ *
+ * Removes a node from the hash table and updates the node count. The
+ * node is freed. No table resize is performed.
+ *
+ * If @notify is %TRUE then the destroy notify functions are called
+ * for the key and value of the hash node.
+ *
+ * @node_ptr_ptr is a pass-by-reference in/out parameter. When the
+ * function is called, it should point to the pointer to the node to
+ * remove. This level of indirection is required so that the pointer
+ * may be updated appropriately once the node has been removed.
+ *
+ * Before the function returns, the pointer at @node_ptr_ptr will be
+ * updated to point to the position in the table that contains the
+ * pointer to the "next" node in the chain. This makes this function
+ * convenient to use from functions that iterate over the entire
+ * table. If there is no further item in the chain then the
+ * #fluid_hashnode_t pointer will be %NULL (ie: **node_ptr_ptr == %NULL).
+ *
+ * Since the pointer in the table to the removed node is replaced with
+ * either a pointer to the next node or a %NULL pointer as
+ * appropriate, the pointer at the end of @node_ptr_ptr will never be
+ * modified at all. Stay tuned. :)
+ */
+static void
+fluid_hashtable_remove_node (fluid_hashtable_t *hashtable,
+ fluid_hashnode_t ***node_ptr_ptr, int notify)
+{
+ fluid_hashnode_t **node_ptr, *node;
+
+ node_ptr = *node_ptr_ptr;
+ node = *node_ptr;
+
+ *node_ptr = node->next;
+
+ if (notify && hashtable->key_destroy_func)
+ hashtable->key_destroy_func (node->key);
+
+ if (notify && hashtable->value_destroy_func)
+ hashtable->value_destroy_func (node->value);
+
+ FLUID_FREE (node);
+
+ hashtable->nnodes--;
+}
+
+/*
+ * fluid_hashtable_remove_all_nodes:
+ * @hashtable: our #fluid_hashtable_t
+ * @notify: %TRUE if the destroy notify handlers are to be called
+ *
+ * Removes all nodes from the table. Since this may be a precursor to
+ * freeing the table entirely, no resize is performed.
+ *
+ * If @notify is %TRUE then the destroy notify functions are called
+ * for the key and value of the hash node.
+ */
+static void
+fluid_hashtable_remove_all_nodes (fluid_hashtable_t *hashtable, int notify)
+{
+ fluid_hashnode_t **node_ptr;
+ int i;
+
+ for (i = 0; i < hashtable->size; i++)
+ for (node_ptr = &hashtable->nodes[i]; *node_ptr != NULL;)
+ fluid_hashtable_remove_node (hashtable, &node_ptr, notify);
+
+ hashtable->nnodes = 0;
+}
+
+/*
+ * fluid_hashtable_resize:
+ * @hashtable: our #fluid_hashtable_t
+ *
+ * Resizes the hash table to the optimal size based on the number of
+ * nodes currently held. If you call this function then a resize will
+ * occur, even if one does not need to occur. Use
+ * fluid_hashtable_maybe_resize() instead.
+ */
+static void
+fluid_hashtable_resize (fluid_hashtable_t *hashtable)
+{
+ fluid_hashnode_t **new_nodes;
+ fluid_hashnode_t *node;
+ fluid_hashnode_t *next;
+ unsigned int hash_val;
+ int new_size;
+ int i;
+
+ new_size = spaced_primes_closest (hashtable->nnodes);
+ new_size = (new_size < HASH_TABLE_MIN_SIZE) ? HASH_TABLE_MIN_SIZE :
+ ((new_size > HASH_TABLE_MAX_SIZE) ? HASH_TABLE_MAX_SIZE : new_size);
+
+ new_nodes = FLUID_ARRAY (fluid_hashnode_t *, new_size);
+
+ if (!new_nodes)
+ {
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ return;
+ }
+
+ FLUID_MEMSET (new_nodes, 0, new_size * sizeof (fluid_hashnode_t *));
+
+ for (i = 0; i < hashtable->size; i++)
+ for (node = hashtable->nodes[i]; node; node = next)
+ {
+ next = node->next;
+
+ hash_val = node->key_hash % new_size;
+
+ node->next = new_nodes[hash_val];
+ new_nodes[hash_val] = node;
+ }
+
+ FLUID_FREE (hashtable->nodes);
+ hashtable->nodes = new_nodes;
+ hashtable->size = new_size;
+}
+
+/*
+ * fluid_hashtable_maybe_resize:
+ * @hashtable: our #fluid_hashtable_t
+ *
+ * Resizes the hash table, if needed.
+ *
+ * Essentially, calls fluid_hashtable_resize() if the table has strayed
+ * too far from its ideal size for its number of nodes.
+ */
+static inline void
+fluid_hashtable_maybe_resize (fluid_hashtable_t *hashtable)
+{
+ int nnodes = hashtable->nnodes;
+ int size = hashtable->size;
+
+ if ((size >= 3 * nnodes && size > HASH_TABLE_MIN_SIZE) ||
+ (3 * size <= nnodes && size < HASH_TABLE_MAX_SIZE))
+ fluid_hashtable_resize (hashtable);
+}
+
+/**
+ * new_fluid_hashtable:
+ * @hash_func: a function to create a hash value from a key.
+ * Hash values are used to determine where keys are stored within the
+ * #fluid_hashtable_t data structure. The fluid_direct_hash(), fluid_int_hash() and
+ * fluid_str_hash() functions are provided for some common types of keys.
+ * If hash_func is %NULL, fluid_direct_hash() is used.
+ * @key_equal_func: a function to check two keys for equality. This is
+ * used when looking up keys in the #fluid_hashtable_t. The fluid_direct_equal(),
+ * fluid_int_equal() and fluid_str_equal() functions are provided for the most
+ * common types of keys. If @key_equal_func is %NULL, keys are compared
+ * directly in a similar fashion to fluid_direct_equal(), but without the
+ * overhead of a function call.
+ *
+ * Creates a new #fluid_hashtable_t with a reference count of 1.
+ *
+ * Return value: a new #fluid_hashtable_t.
+ **/
+fluid_hashtable_t*
+new_fluid_hashtable (fluid_hash_func_t hash_func, fluid_equal_func_t key_equal_func)
+{
+ return new_fluid_hashtable_full (hash_func, key_equal_func, NULL, NULL);
+}
+
+
+/**
+ * new_fluid_hashtable_full:
+ * @hash_func: a function to create a hash value from a key.
+ * @key_equal_func: a function to check two keys for equality.
+ * @key_destroy_func: a function to free the memory allocated for the key
+ * used when removing the entry from the #fluid_hashtable_t or %NULL if you
+ * don't want to supply such a function.
+ * @value_destroy_func: a function to free the memory allocated for the
+ * value used when removing the entry from the #fluid_hashtable_t or %NULL if
+ * you don't want to supply such a function.
+ *
+ * Creates a new #fluid_hashtable_t like fluid_hashtable_new() with a reference count
+ * of 1 and allows to specify functions to free the memory allocated for the
+ * key and value that get called when removing the entry from the #fluid_hashtable_t.
+ *
+ * Return value: a new #fluid_hashtable_t.
+ **/
+fluid_hashtable_t*
+new_fluid_hashtable_full (fluid_hash_func_t hash_func,
+ fluid_equal_func_t key_equal_func,
+ fluid_destroy_notify_t key_destroy_func,
+ fluid_destroy_notify_t value_destroy_func)
+{
+ fluid_hashtable_t *hashtable;
+
+ hashtable = FLUID_NEW (fluid_hashtable_t);
+
+ if (!hashtable)
+ {
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ hashtable->size = HASH_TABLE_MIN_SIZE;
+ hashtable->nnodes = 0;
+ hashtable->hash_func = hash_func ? hash_func : fluid_direct_hash;
+ hashtable->key_equal_func = key_equal_func;
+ hashtable->ref_count = 1;
+ hashtable->key_destroy_func = key_destroy_func;
+ hashtable->value_destroy_func = value_destroy_func;
+ hashtable->nodes = FLUID_ARRAY (fluid_hashnode_t*, hashtable->size);
+ FLUID_MEMSET (hashtable->nodes, 0, hashtable->size * sizeof (fluid_hashnode_t *));
+
+ return hashtable;
+}
+
+/**
+ * fluid_hashtable_iter_init:
+ * @iter: an uninitialized #fluid_hashtable_iter_t.
+ * @hashtable: a #fluid_hashtable_t.
+ *
+ * Initializes a key/value pair iterator and associates it with
+ * @hashtable. Modifying the hash table after calling this function
+ * invalidates the returned iterator.
+ * |[
+ * fluid_hashtable_iter_t iter;
+ * gpointer key, value;
+ *
+ * fluid_hashtable_iter_init (&iter, hashtable);
+ * while (fluid_hashtable_iter_next (&iter, &key, &value))
+ * {
+ * /&ast; do something with key and value &ast;/
+ * }
+ * ]|
+ *
+ * Since: 2.16
+ **/
+void
+fluid_hashtable_iter_init (fluid_hashtable_iter_t *iter,
+ fluid_hashtable_t *hashtable)
+{
+ RealIter *ri = (RealIter *) iter;
+
+ fluid_return_if_fail (iter != NULL);
+ fluid_return_if_fail (hashtable != NULL);
+
+ ri->hashtable = hashtable;
+ ri->prev_node = NULL;
+ ri->node = NULL;
+ ri->position = -1;
+ ri->pre_advanced = FALSE;
+}
+
+/**
+ * fluid_hashtable_iter_next:
+ * @iter: an initialized #fluid_hashtable_iter_t.
+ * @key: a location to store the key, or %NULL.
+ * @value: a location to store the value, or %NULL.
+ *
+ * Advances @iter and retrieves the key and/or value that are now
+ * pointed to as a result of this advancement. If %FALSE is returned,
+ * @key and @value are not set, and the iterator becomes invalid.
+ *
+ * Return value: %FALSE if the end of the #fluid_hashtable_t has been reached.
+ *
+ * Since: 2.16
+ **/
+int
+fluid_hashtable_iter_next (fluid_hashtable_iter_t *iter, void **key,
+ void **value)
+{
+ RealIter *ri = (RealIter *) iter;
+
+ fluid_return_val_if_fail (iter != NULL, FALSE);
+
+ if (ri->pre_advanced)
+ {
+ ri->pre_advanced = FALSE;
+
+ if (ri->node == NULL)
+ return FALSE;
+ }
+ else
+ {
+ if (ri->node != NULL)
+ {
+ ri->prev_node = ri->node;
+ ri->node = ri->node->next;
+ }
+
+ while (ri->node == NULL)
+ {
+ ri->position++;
+ if (ri->position >= ri->hashtable->size)
+ return FALSE;
+
+ ri->prev_node = NULL;
+ ri->node = ri->hashtable->nodes[ri->position];
+ }
+ }
+
+ if (key != NULL)
+ *key = ri->node->key;
+ if (value != NULL)
+ *value = ri->node->value;
+
+ return TRUE;
+}
+
+/**
+ * fluid_hashtable_iter_get_hash_table:
+ * @iter: an initialized #fluid_hashtable_iter_t.
+ *
+ * Returns the #fluid_hashtable_t associated with @iter.
+ *
+ * Return value: the #fluid_hashtable_t associated with @iter.
+ *
+ * Since: 2.16
+ **/
+fluid_hashtable_t *
+fluid_hashtable_iter_get_hash_table (fluid_hashtable_iter_t *iter)
+{
+ fluid_return_val_if_fail (iter != NULL, NULL);
+
+ return ((RealIter *) iter)->hashtable;
+}
+
+static void
+iter_remove_or_steal (RealIter *ri, int notify)
+{
+ fluid_hashnode_t *prev;
+ fluid_hashnode_t *node;
+ int position;
+
+ fluid_return_if_fail (ri != NULL);
+ fluid_return_if_fail (ri->node != NULL);
+
+ prev = ri->prev_node;
+ node = ri->node;
+ position = ri->position;
+
+ /* pre-advance the iterator since we will remove the node */
+
+ ri->node = ri->node->next;
+ /* ri->prev_node is still the correct previous node */
+
+ while (ri->node == NULL)
+ {
+ ri->position++;
+ if (ri->position >= ri->hashtable->size)
+ break;
+
+ ri->prev_node = NULL;
+ ri->node = ri->hashtable->nodes[ri->position];
+ }
+
+ ri->pre_advanced = TRUE;
+
+ /* remove the node */
+
+ if (prev != NULL)
+ prev->next = node->next;
+ else
+ ri->hashtable->nodes[position] = node->next;
+
+ if (notify)
+ {
+ if (ri->hashtable->key_destroy_func)
+ ri->hashtable->key_destroy_func(node->key);
+ if (ri->hashtable->value_destroy_func)
+ ri->hashtable->value_destroy_func(node->value);
+ }
+
+ FLUID_FREE (node);
+
+ ri->hashtable->nnodes--;
+}
+
+/**
+ * fluid_hashtable_iter_remove():
+ * @iter: an initialized #fluid_hashtable_iter_t.
+ *
+ * Removes the key/value pair currently pointed to by the iterator
+ * from its associated #fluid_hashtable_t. Can only be called after
+ * fluid_hashtable_iter_next() returned %TRUE, and cannot be called more
+ * than once for the same key/value pair.
+ *
+ * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the
+ * key and value are freed using the supplied destroy functions, otherwise
+ * you have to make sure that any dynamically allocated values are freed
+ * yourself.
+ *
+ * Since: 2.16
+ **/
+void
+fluid_hashtable_iter_remove (fluid_hashtable_iter_t *iter)
+{
+ iter_remove_or_steal ((RealIter *) iter, TRUE);
+}
+
+/**
+ * fluid_hashtable_iter_steal():
+ * @iter: an initialized #fluid_hashtable_iter_t.
+ *
+ * Removes the key/value pair currently pointed to by the iterator
+ * from its associated #fluid_hashtable_t, without calling the key and value
+ * destroy functions. Can only be called after
+ * fluid_hashtable_iter_next() returned %TRUE, and cannot be called more
+ * than once for the same key/value pair.
+ *
+ * Since: 2.16
+ **/
+void
+fluid_hashtable_iter_steal (fluid_hashtable_iter_t *iter)
+{
+ iter_remove_or_steal ((RealIter *) iter, FALSE);
+}
+
+
+/**
+ * fluid_hashtable_ref:
+ * @hashtable: a valid #fluid_hashtable_t.
+ *
+ * Atomically increments the reference count of @hashtable by one.
+ * This function is MT-safe and may be called from any thread.
+ *
+ * Return value: the passed in #fluid_hashtable_t.
+ *
+ * Since: 2.10
+ **/
+fluid_hashtable_t*
+fluid_hashtable_ref (fluid_hashtable_t *hashtable)
+{
+ fluid_return_val_if_fail (hashtable != NULL, NULL);
+ fluid_return_val_if_fail (hashtable->ref_count > 0, hashtable);
+
+ fluid_atomic_int_add (&hashtable->ref_count, 1);
+ return hashtable;
+}
+
+/**
+ * fluid_hashtable_unref:
+ * @hashtable: a valid #fluid_hashtable_t.
+ *
+ * Atomically decrements the reference count of @hashtable by one.
+ * If the reference count drops to 0, all keys and values will be
+ * destroyed, and all memory allocated by the hash table is released.
+ * This function is MT-safe and may be called from any thread.
+ *
+ * Since: 2.10
+ **/
+void
+fluid_hashtable_unref (fluid_hashtable_t *hashtable)
+{
+ fluid_return_if_fail (hashtable != NULL);
+ fluid_return_if_fail (hashtable->ref_count > 0);
+
+ if (fluid_atomic_int_exchange_and_add (&hashtable->ref_count, -1) - 1 == 0)
+ {
+ fluid_hashtable_remove_all_nodes (hashtable, TRUE);
+ FLUID_FREE (hashtable->nodes);
+ FLUID_FREE (hashtable);
+ }
+}
+
+/**
+ * delete_fluid_hashtable:
+ * @hashtable: a #fluid_hashtable_t.
+ *
+ * Destroys all keys and values in the #fluid_hashtable_t and decrements its
+ * reference count by 1. If keys and/or values are dynamically allocated,
+ * you should either free them first or create the #fluid_hashtable_t with destroy
+ * notifiers using fluid_hashtable_new_full(). In the latter case the destroy
+ * functions you supplied will be called on all keys and values during the
+ * destruction phase.
+ **/
+void
+delete_fluid_hashtable (fluid_hashtable_t *hashtable)
+{
+ fluid_return_if_fail (hashtable != NULL);
+ fluid_return_if_fail (hashtable->ref_count > 0);
+
+ fluid_hashtable_remove_all (hashtable);
+ fluid_hashtable_unref (hashtable);
+}
+
+/**
+ * fluid_hashtable_lookup:
+ * @hashtable: a #fluid_hashtable_t.
+ * @key: the key to look up.
+ *
+ * Looks up a key in a #fluid_hashtable_t. Note that this function cannot
+ * distinguish between a key that is not present and one which is present
+ * and has the value %NULL. If you need this distinction, use
+ * fluid_hashtable_lookup_extended().
+ *
+ * Return value: the associated value, or %NULL if the key is not found.
+ **/
+void *
+fluid_hashtable_lookup (fluid_hashtable_t *hashtable, const void *key)
+{
+ fluid_hashnode_t *node;
+
+ fluid_return_val_if_fail (hashtable != NULL, NULL);
+
+ node = *fluid_hashtable_lookup_node (hashtable, key, NULL);
+
+ return node ? node->value : NULL;
+}
+
+/**
+ * fluid_hashtable_lookup_extended:
+ * @hashtable: a #fluid_hashtable_t.
+ * @lookup_key: the key to look up.
+ * @orig_key: returns the original key.
+ * @value: returns the value associated with the key.
+ *
+ * Looks up a key in the #fluid_hashtable_t, returning the original key and the
+ * associated value and a #gboolean which is %TRUE if the key was found. This
+ * is useful if you need to free the memory allocated for the original key,
+ * for example before calling fluid_hashtable_remove().
+ *
+ * Return value: %TRUE if the key was found in the #fluid_hashtable_t.
+ **/
+int
+fluid_hashtable_lookup_extended (fluid_hashtable_t *hashtable,
+ const void *lookup_key,
+ void **orig_key, void **value)
+{
+ fluid_hashnode_t *node;
+
+ fluid_return_val_if_fail (hashtable != NULL, FALSE);
+
+ node = *fluid_hashtable_lookup_node (hashtable, lookup_key, NULL);
+
+ if (node == NULL)
+ return FALSE;
+
+ if (orig_key)
+ *orig_key = node->key;
+
+ if (value)
+ *value = node->value;
+
+ return TRUE;
+}
+
+/*
+ * fluid_hashtable_insert_internal:
+ * @hashtable: our #fluid_hashtable_t
+ * @key: the key to insert
+ * @value: the value to insert
+ * @keep_new_key: if %TRUE and this key already exists in the table
+ * then call the destroy notify function on the old key. If %FALSE
+ * then call the destroy notify function on the new key.
+ *
+ * Implements the common logic for the fluid_hashtable_insert() and
+ * fluid_hashtable_replace() functions.
+ *
+ * Do a lookup of @key. If it is found, replace it with the new
+ * @value (and perhaps the new @key). If it is not found, create a
+ * new node.
+ */
+static void
+fluid_hashtable_insert_internal (fluid_hashtable_t *hashtable, void *key,
+ void *value, int keep_new_key)
+{
+ fluid_hashnode_t **node_ptr, *node;
+ unsigned int key_hash;
+
+ fluid_return_if_fail (hashtable != NULL);
+ fluid_return_if_fail (hashtable->ref_count > 0);
+
+ node_ptr = fluid_hashtable_lookup_node (hashtable, key, &key_hash);
+
+ if ((node = *node_ptr))
+ {
+ if (keep_new_key)
+ {
+ if (hashtable->key_destroy_func)
+ hashtable->key_destroy_func (node->key);
+ node->key = key;
+ }
+ else
+ {
+ if (hashtable->key_destroy_func)
+ hashtable->key_destroy_func (key);
+ }
+
+ if (hashtable->value_destroy_func)
+ hashtable->value_destroy_func (node->value);
+
+ node->value = value;
+ }
+ else
+ {
+ node = FLUID_NEW (fluid_hashnode_t);
+
+ if (!node)
+ {
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ return;
+ }
+
+ node->key = key;
+ node->value = value;
+ node->key_hash = key_hash;
+ node->next = NULL;
+
+ *node_ptr = node;
+ hashtable->nnodes++;
+ fluid_hashtable_maybe_resize (hashtable);
+ }
+}
+
+/**
+ * fluid_hashtable_insert:
+ * @hashtable: a #fluid_hashtable_t.
+ * @key: a key to insert.
+ * @value: the value to associate with the key.
+ *
+ * Inserts a new key and value into a #fluid_hashtable_t.
+ *
+ * If the key already exists in the #fluid_hashtable_t its current value is replaced
+ * with the new value. If you supplied a @value_destroy_func when creating the
+ * #fluid_hashtable_t, the old value is freed using that function. If you supplied
+ * a @key_destroy_func when creating the #fluid_hashtable_t, the passed key is freed
+ * using that function.
+ **/
+void
+fluid_hashtable_insert (fluid_hashtable_t *hashtable, void *key, void *value)
+{
+ fluid_hashtable_insert_internal (hashtable, key, value, FALSE);
+}
+
+/**
+ * fluid_hashtable_replace:
+ * @hashtable: a #fluid_hashtable_t.
+ * @key: a key to insert.
+ * @value: the value to associate with the key.
+ *
+ * Inserts a new key and value into a #fluid_hashtable_t similar to
+ * fluid_hashtable_insert(). The difference is that if the key already exists
+ * in the #fluid_hashtable_t, it gets replaced by the new key. If you supplied a
+ * @value_destroy_func when creating the #fluid_hashtable_t, the old value is freed
+ * using that function. If you supplied a @key_destroy_func when creating the
+ * #fluid_hashtable_t, the old key is freed using that function.
+ **/
+void
+fluid_hashtable_replace (fluid_hashtable_t *hashtable, void *key, void *value)
+{
+ fluid_hashtable_insert_internal (hashtable, key, value, TRUE);
+}
+
+/*
+ * fluid_hashtable_remove_internal:
+ * @hashtable: our #fluid_hashtable_t
+ * @key: the key to remove
+ * @notify: %TRUE if the destroy notify handlers are to be called
+ * Return value: %TRUE if a node was found and removed, else %FALSE
+ *
+ * Implements the common logic for the fluid_hashtable_remove() and
+ * fluid_hashtable_steal() functions.
+ *
+ * Do a lookup of @key and remove it if it is found, calling the
+ * destroy notify handlers only if @notify is %TRUE.
+ */
+static int
+fluid_hashtable_remove_internal (fluid_hashtable_t *hashtable, const void *key,
+ int notify)
+{
+ fluid_hashnode_t **node_ptr;
+
+ fluid_return_val_if_fail (hashtable != NULL, FALSE);
+
+ node_ptr = fluid_hashtable_lookup_node (hashtable, key, NULL);
+ if (*node_ptr == NULL)
+ return FALSE;
+
+ fluid_hashtable_remove_node (hashtable, &node_ptr, notify);
+ fluid_hashtable_maybe_resize (hashtable);
+
+ return TRUE;
+}
+
+/**
+ * fluid_hashtable_remove:
+ * @hashtable: a #fluid_hashtable_t.
+ * @key: the key to remove.
+ *
+ * Removes a key and its associated value from a #fluid_hashtable_t.
+ *
+ * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the
+ * key and value are freed using the supplied destroy functions, otherwise
+ * you have to make sure that any dynamically allocated values are freed
+ * yourself.
+ *
+ * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t.
+ **/
+int
+fluid_hashtable_remove (fluid_hashtable_t *hashtable, const void *key)
+{
+ return fluid_hashtable_remove_internal (hashtable, key, TRUE);
+}
+
+/**
+ * fluid_hashtable_steal:
+ * @hashtable: a #fluid_hashtable_t.
+ * @key: the key to remove.
+ *
+ * Removes a key and its associated value from a #fluid_hashtable_t without
+ * calling the key and value destroy functions.
+ *
+ * Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t.
+ **/
+int
+fluid_hashtable_steal (fluid_hashtable_t *hashtable, const void *key)
+{
+ return fluid_hashtable_remove_internal (hashtable, key, FALSE);
+}
+
+/**
+ * fluid_hashtable_remove_all:
+ * @hashtable: a #fluid_hashtable_t
+ *
+ * Removes all keys and their associated values from a #fluid_hashtable_t.
+ *
+ * If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the keys
+ * and values are freed using the supplied destroy functions, otherwise you
+ * have to make sure that any dynamically allocated values are freed
+ * yourself.
+ *
+ * Since: 2.12
+ **/
+void
+fluid_hashtable_remove_all (fluid_hashtable_t *hashtable)
+{
+ fluid_return_if_fail (hashtable != NULL);
+
+ fluid_hashtable_remove_all_nodes (hashtable, TRUE);
+ fluid_hashtable_maybe_resize (hashtable);
+}
+
+/**
+ * fluid_hashtable_steal_all:
+ * @hashtable: a #fluid_hashtable_t.
+ *
+ * Removes all keys and their associated values from a #fluid_hashtable_t
+ * without calling the key and value destroy functions.
+ *
+ * Since: 2.12
+ **/
+void
+fluid_hashtable_steal_all (fluid_hashtable_t *hashtable)
+{
+ fluid_return_if_fail (hashtable != NULL);
+
+ fluid_hashtable_remove_all_nodes (hashtable, FALSE);
+ fluid_hashtable_maybe_resize (hashtable);
+}
+
+/*
+ * fluid_hashtable_foreach_remove_or_steal:
+ * @hashtable: our #fluid_hashtable_t
+ * @func: the user's callback function
+ * @user_data: data for @func
+ * @notify: %TRUE if the destroy notify handlers are to be called
+ *
+ * Implements the common logic for fluid_hashtable_foreach_remove() and
+ * fluid_hashtable_foreach_steal().
+ *
+ * Iterates over every node in the table, calling @func with the key
+ * and value of the node (and @user_data). If @func returns %TRUE the
+ * node is removed from the table.
+ *
+ * If @notify is true then the destroy notify handlers will be called
+ * for each removed node.
+ */
+static unsigned int
+fluid_hashtable_foreach_remove_or_steal (fluid_hashtable_t *hashtable,
+ fluid_hr_func_t func, void *user_data,
+ int notify)
+{
+ fluid_hashnode_t *node, **node_ptr;
+ unsigned int deleted = 0;
+ int i;
+
+ for (i = 0; i < hashtable->size; i++)
+ for (node_ptr = &hashtable->nodes[i]; (node = *node_ptr) != NULL;)
+ if ((* func) (node->key, node->value, user_data))
+ {
+ fluid_hashtable_remove_node (hashtable, &node_ptr, notify);
+ deleted++;
+ }
+ else
+ node_ptr = &node->next;
+
+ fluid_hashtable_maybe_resize (hashtable);
+
+ return deleted;
+}
+
+/**
+ * fluid_hashtable_foreach_remove:
+ * @hashtable: a #fluid_hashtable_t.
+ * @func: the function to call for each key/value pair.
+ * @user_data: user data to pass to the function.
+ *
+ * Calls the given function for each key/value pair in the #fluid_hashtable_t.
+ * If the function returns %TRUE, then the key/value pair is removed from the
+ * #fluid_hashtable_t. If you supplied key or value destroy functions when creating
+ * the #fluid_hashtable_t, they are used to free the memory allocated for the removed
+ * keys and values.
+ *
+ * See #fluid_hashtable_iter_t for an alternative way to loop over the
+ * key/value pairs in the hash table.
+ *
+ * Return value: the number of key/value pairs removed.
+ **/
+unsigned int
+fluid_hashtable_foreach_remove (fluid_hashtable_t *hashtable,
+ fluid_hr_func_t func, void *user_data)
+{
+ fluid_return_val_if_fail (hashtable != NULL, 0);
+ fluid_return_val_if_fail (func != NULL, 0);
+
+ return fluid_hashtable_foreach_remove_or_steal (hashtable, func, user_data, TRUE);
+}
+
+/**
+ * fluid_hashtable_foreach_steal:
+ * @hashtable: a #fluid_hashtable_t.
+ * @func: the function to call for each key/value pair.
+ * @user_data: user data to pass to the function.
+ *
+ * Calls the given function for each key/value pair in the #fluid_hashtable_t.
+ * If the function returns %TRUE, then the key/value pair is removed from the
+ * #fluid_hashtable_t, but no key or value destroy functions are called.
+ *
+ * See #fluid_hashtable_iter_t for an alternative way to loop over the
+ * key/value pairs in the hash table.
+ *
+ * Return value: the number of key/value pairs removed.
+ **/
+unsigned int
+fluid_hashtable_foreach_steal (fluid_hashtable_t *hashtable,
+ fluid_hr_func_t func, void *user_data)
+{
+ fluid_return_val_if_fail (hashtable != NULL, 0);
+ fluid_return_val_if_fail (func != NULL, 0);
+
+ return fluid_hashtable_foreach_remove_or_steal (hashtable, func, user_data, FALSE);
+}
+
+/**
+ * fluid_hashtable_foreach:
+ * @hashtable: a #fluid_hashtable_t.
+ * @func: the function to call for each key/value pair.
+ * @user_data: user data to pass to the function.
+ *
+ * Calls the given function for each of the key/value pairs in the
+ * #fluid_hashtable_t. The function is passed the key and value of each
+ * pair, and the given @user_data parameter. The hash table may not
+ * be modified while iterating over it (you can't add/remove
+ * items). To remove all items matching a predicate, use
+ * fluid_hashtable_foreach_remove().
+ *
+ * See fluid_hashtable_find() for performance caveats for linear
+ * order searches in contrast to fluid_hashtable_lookup().
+ **/
+void
+fluid_hashtable_foreach (fluid_hashtable_t *hashtable, fluid_hr_func_t func,
+ void *user_data)
+{
+ fluid_hashnode_t *node;
+ int i;
+
+ fluid_return_if_fail (hashtable != NULL);
+ fluid_return_if_fail (func != NULL);
+
+ for (i = 0; i < hashtable->size; i++)
+ for (node = hashtable->nodes[i]; node; node = node->next)
+ (* func) (node->key, node->value, user_data);
+}
+
+/**
+ * fluid_hashtable_find:
+ * @hashtable: a #fluid_hashtable_t.
+ * @predicate: function to test the key/value pairs for a certain property.
+ * @user_data: user data to pass to the function.
+ *
+ * Calls the given function for key/value pairs in the #fluid_hashtable_t until
+ * @predicate returns %TRUE. The function is passed the key and value of
+ * each pair, and the given @user_data parameter. The hash table may not
+ * be modified while iterating over it (you can't add/remove items).
+ *
+ * Note, that hash tables are really only optimized for forward lookups,
+ * i.e. fluid_hashtable_lookup().
+ * So code that frequently issues fluid_hashtable_find() or
+ * fluid_hashtable_foreach() (e.g. in the order of once per every entry in a
+ * hash table) should probably be reworked to use additional or different
+ * data structures for reverse lookups (keep in mind that an O(n) find/foreach
+ * operation issued for all n values in a hash table ends up needing O(n*n)
+ * operations).
+ *
+ * Return value: The value of the first key/value pair is returned, for which
+ * func evaluates to %TRUE. If no pair with the requested property is found,
+ * %NULL is returned.
+ *
+ * Since: 2.4
+ **/
+void *
+fluid_hashtable_find (fluid_hashtable_t *hashtable, fluid_hr_func_t predicate,
+ void *user_data)
+{
+ fluid_hashnode_t *node;
+ int i;
+
+ fluid_return_val_if_fail (hashtable != NULL, NULL);
+ fluid_return_val_if_fail (predicate != NULL, NULL);
+
+ for (i = 0; i < hashtable->size; i++)
+ for (node = hashtable->nodes[i]; node; node = node->next)
+ if (predicate (node->key, node->value, user_data))
+ return node->value;
+ return NULL;
+}
+
+/**
+ * fluid_hashtable_size:
+ * @hashtable: a #fluid_hashtable_t.
+ *
+ * Returns the number of elements contained in the #fluid_hashtable_t.
+ *
+ * Return value: the number of key/value pairs in the #fluid_hashtable_t.
+ **/
+unsigned int
+fluid_hashtable_size (fluid_hashtable_t *hashtable)
+{
+ fluid_return_val_if_fail (hashtable != NULL, 0);
+
+ return hashtable->nnodes;
+}
+
+/**
+ * fluid_hashtable_get_keys:
+ * @hashtable: a #fluid_hashtable_t
+ *
+ * Retrieves every key inside @hashtable. The returned data is valid
+ * until @hashtable is modified.
+ *
+ * Return value: a #GList containing all the keys inside the hash
+ * table. The content of the list is owned by the hash table and
+ * should not be modified or freed. Use delete_fluid_list() when done
+ * using the list.
+ *
+ * Since: 2.14
+ */
+fluid_list_t *
+fluid_hashtable_get_keys (fluid_hashtable_t *hashtable)
+{
+ fluid_hashnode_t *node;
+ int i;
+ fluid_list_t *retval;
+
+ fluid_return_val_if_fail (hashtable != NULL, NULL);
+
+ retval = NULL;
+ for (i = 0; i < hashtable->size; i++)
+ for (node = hashtable->nodes[i]; node; node = node->next)
+ retval = fluid_list_prepend (retval, node->key);
+
+ return retval;
+}
+
+/**
+ * fluid_hashtable_get_values:
+ * @hashtable: a #fluid_hashtable_t
+ *
+ * Retrieves every value inside @hashtable. The returned data is
+ * valid until @hashtable is modified.
+ *
+ * Return value: a #GList containing all the values inside the hash
+ * table. The content of the list is owned by the hash table and
+ * should not be modified or freed. Use delete_fluid_list() when done
+ * using the list.
+ *
+ * Since: 2.14
+ */
+fluid_list_t *
+fluid_hashtable_get_values (fluid_hashtable_t *hashtable)
+{
+ fluid_hashnode_t *node;
+ int i;
+ fluid_list_t *retval;
+
+ fluid_return_val_if_fail (hashtable != NULL, NULL);
+
+ retval = NULL;
+ for (i = 0; i < hashtable->size; i++)
+ for (node = hashtable->nodes[i]; node; node = node->next)
+ retval = fluid_list_prepend (retval, node->value);
+
+ return retval;
+}
+
+
+/* Extracted from glib/gstring.c */
+
+
+/**
+ * fluid_str_equal:
+ * @v1: a key
+ * @v2: a key to compare with @v1
+ *
+ * Compares two strings for byte-by-byte equality and returns %TRUE
+ * if they are equal. It can be passed to new_fluid_hashtable() as the
+ * @key_equal_func parameter, when using strings as keys in a #Ghashtable.
+ *
+ * Returns: %TRUE if the two keys match
+ */
+int
+fluid_str_equal (const void *v1, const void *v2)
+{
+ const char *string1 = v1;
+ const char *string2 = v2;
+
+ return strcmp (string1, string2) == 0;
+}
+
+/**
+ * fluid_str_hash:
+ * @v: a string key
+ *
+ * Converts a string to a hash value.
+ * It can be passed to new_fluid_hashtable() as the @hash_func
+ * parameter, when using strings as keys in a #fluid_hashtable_t.
+ *
+ * Returns: a hash value corresponding to the key
+ */
+unsigned int
+fluid_str_hash (const void *v)
+{
+ /* 31 bit hash function */
+ const signed char *p = v;
+ uint32_t h = *p;
+
+ if (h)
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + *p;
+
+ return h;
+}
+
+
+/* Extracted from glib/gutils.c */
+
+
+/**
+ * fluid_direct_equal:
+ * @v1: a key.
+ * @v2: a key to compare with @v1.
+ *
+ * Compares two #gpointer arguments and returns %TRUE if they are equal.
+ * It can be passed to new_fluid_hashtable() as the @key_equal_func
+ * parameter, when using pointers as keys in a #fluid_hashtable_t.
+ *
+ * Returns: %TRUE if the two keys match.
+ */
+int
+fluid_direct_equal (const void *v1, const void *v2)
+{
+ return v1 == v2;
+}
+
+/**
+ * fluid_direct_hash:
+ * @v: a void * key
+ *
+ * Converts a gpointer to a hash value.
+ * It can be passed to g_hashtable_new() as the @hash_func parameter,
+ * when using pointers as keys in a #fluid_hashtable_t.
+ *
+ * Returns: a hash value corresponding to the key.
+ */
+unsigned int
+fluid_direct_hash (const void *v)
+{
+ return FLUID_POINTER_TO_UINT (v);
+}
+
+/**
+ * fluid_int_equal:
+ * @v1: a pointer to a int key.
+ * @v2: a pointer to a int key to compare with @v1.
+ *
+ * Compares the two #int values being pointed to and returns
+ * %TRUE if they are equal.
+ * It can be passed to g_hashtable_new() as the @key_equal_func
+ * parameter, when using pointers to integers as keys in a #fluid_hashtable_t.
+ *
+ * Returns: %TRUE if the two keys match.
+ */
+int
+fluid_int_equal (const void *v1, const void *v2)
+{
+ return *((const int*) v1) == *((const int*) v2);
+}
+
+/**
+ * fluid_int_hash:
+ * @v: a pointer to a int key
+ *
+ * Converts a pointer to a #int to a hash value.
+ * It can be passed to g_hashtable_new() as the @hash_func parameter,
+ * when using pointers to integers values as keys in a #fluid_hashtable_t.
+ *
+ * Returns: a hash value corresponding to the key.
+ */
+unsigned int
+fluid_int_hash (const void *v)
+{
+ return *(const int*) v;
+}
diff --git a/src/midi/old/fluid_hash.h b/src/midi/old/fluid_hash.h
new file mode 100644
index 0000000..3beff06
--- /dev/null
+++ b/src/midi/old/fluid_hash.h
@@ -0,0 +1,131 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * Adapted for FluidSynth use by Josh Green <jgreen@users.sourceforge.net>
+ * September 8, 2009 from glib 2.18.4
+ *
+ * - Self contained (no dependencies on glib)
+ * - changed names to fluid_hashtable_...
+ */
+
+#ifndef _FLUID_HASH_H
+#define _FLUID_HASH_H
+
+#include "fluidsynth_priv.h"
+#include "fluid_list.h"
+#include "fluid_sys.h"
+
+/* Extracted from gtypes.h */
+typedef void (*fluid_destroy_notify_t)(void *data);
+typedef unsigned int (*fluid_hash_func_t)(const void *key);
+typedef int (*fluid_equal_func_t)(const void *a, const void *b);
+/* End gtypes.h extraction */
+
+typedef int (*fluid_hr_func_t)(void *key, void *value, void *user_data);
+typedef struct _fluid_hashtable_iter_t fluid_hashtable_iter_t;
+
+typedef struct _fluid_hashnode_t fluid_hashnode_t;
+
+struct _fluid_hashnode_t
+{
+ void *key;
+ void *value;
+ fluid_hashnode_t *next;
+ unsigned int key_hash;
+};
+
+struct _fluid_hashtable_t
+{
+ int size;
+ int nnodes;
+ fluid_hashnode_t **nodes;
+ fluid_hash_func_t hash_func;
+ fluid_equal_func_t key_equal_func;
+ volatile int ref_count;
+ fluid_destroy_notify_t key_destroy_func;
+ fluid_destroy_notify_t value_destroy_func;
+ fluid_rec_mutex_t mutex; // Optionally used in other modules (fluid_settings.c for example)
+};
+
+struct _fluid_hashtable_iter_t
+{
+ /*< private >*/
+ void * dummy1;
+ void * dummy2;
+ void * dummy3;
+ int dummy4;
+ int dummy5; // Bool
+ void * dummy6;
+};
+
+fluid_hashtable_t* new_fluid_hashtable (fluid_hash_func_t hash_func,
+ fluid_equal_func_t key_equal_func);
+fluid_hashtable_t* new_fluid_hashtable_full (fluid_hash_func_t hash_func,
+ fluid_equal_func_t key_equal_func,
+ fluid_destroy_notify_t key_destroy_func,
+ fluid_destroy_notify_t value_destroy_func);
+void delete_fluid_hashtable(fluid_hashtable_t *hashtable);
+
+void fluid_hashtable_iter_init (fluid_hashtable_iter_t *iter, fluid_hashtable_t *hashtable);
+int fluid_hashtable_iter_next (fluid_hashtable_iter_t *iter, void **key, void **value);
+fluid_hashtable_t *fluid_hashtable_iter_get_hash_table (fluid_hashtable_iter_t *iter);
+void fluid_hashtable_iter_remove (fluid_hashtable_iter_t *iter);
+void fluid_hashtable_iter_steal (fluid_hashtable_iter_t *iter);
+
+fluid_hashtable_t* fluid_hashtable_ref (fluid_hashtable_t *hashtable);
+void fluid_hashtable_unref (fluid_hashtable_t *hashtable);
+
+void *fluid_hashtable_lookup (fluid_hashtable_t *hashtable, const void *key);
+int fluid_hashtable_lookup_extended (fluid_hashtable_t *hashtable, const void *lookup_key,
+ void **orig_key, void **value);
+
+void fluid_hashtable_insert (fluid_hashtable_t *hashtable, void *key, void *value);
+void fluid_hashtable_replace (fluid_hashtable_t *hashtable, void *key, void *value);
+
+int fluid_hashtable_remove (fluid_hashtable_t *hashtable, const void *key);
+int fluid_hashtable_steal (fluid_hashtable_t *hashtable, const void *key);
+void fluid_hashtable_remove_all (fluid_hashtable_t *hashtable);
+void fluid_hashtable_steal_all (fluid_hashtable_t *hashtable);
+unsigned int fluid_hashtable_foreach_steal (fluid_hashtable_t *hashtable,
+ fluid_hr_func_t func, void *user_data);
+void fluid_hashtable_foreach (fluid_hashtable_t *hashtable, fluid_hr_func_t func,
+ void *user_data);
+void *fluid_hashtable_find (fluid_hashtable_t *hashtable, fluid_hr_func_t predicate,
+ void *user_data);
+unsigned int fluid_hashtable_size (fluid_hashtable_t *hashtable);
+fluid_list_t *fluid_hashtable_get_keys (fluid_hashtable_t *hashtable);
+fluid_list_t *fluid_hashtable_get_values (fluid_hashtable_t *hashtable);
+
+int fluid_str_equal (const void *v1, const void *v2);
+unsigned int fluid_str_hash (const void *v);
+int fluid_direct_equal (const void *v1, const void *v2);
+unsigned int fluid_direct_hash (const void *v);
+int fluid_int_equal (const void *v1, const void *v2);
+unsigned int fluid_int_hash (const void *v);
+
+#endif /* _FLUID_HASH_H */
+
diff --git a/src/midi/old/fluid_ringbuffer.c b/src/midi/old/fluid_ringbuffer.c
new file mode 100644
index 0000000..f6c06dd
--- /dev/null
+++ b/src/midi/old/fluid_ringbuffer.c
@@ -0,0 +1,89 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+/*
+ * Josh Green <josh@resonance.org>
+ * 2009-05-28
+ */
+
+#include "fluid_ringbuffer.h"
+#include "fluidsynth_priv.h"
+
+
+/**
+ * Create a lock free queue with a fixed maximum count and size of elements.
+ * @param count Count of elements in queue (fixed max number of queued elements)
+ * @return New lock free queue or NULL if out of memory (error message logged)
+ *
+ * Lockless FIFO queues don't use any locking mechanisms and can therefore be
+ * advantageous in certain situations, such as passing data between a lower
+ * priority thread and a higher "real time" thread, without potential lock
+ * contention which could stall the high priority thread. Note that there may
+ * only be one producer thread and one consumer thread.
+ */
+fluid_ringbuffer_t *
+new_fluid_ringbuffer (int count, int elementsize)
+{
+ fluid_ringbuffer_t *queue;
+
+ fluid_return_val_if_fail (count > 0, NULL);
+
+ queue = FLUID_NEW (fluid_ringbuffer_t);
+
+ if (!queue)
+ {
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ queue->array = FLUID_MALLOC (elementsize * count);
+
+ if (!queue->array)
+ {
+ FLUID_FREE (queue);
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ /* Clear array, in case dynamic pointer reclaiming is being done */
+ FLUID_MEMSET (queue->array, 0, elementsize * count);
+
+ queue->totalcount = count;
+ queue->elementsize = elementsize;
+ queue->count = 0;
+ queue->in = 0;
+ queue->out = 0;
+
+ return (queue);
+}
+
+/**
+ * Free an event queue.
+ * @param queue Lockless queue instance
+ *
+ * Care must be taken when freeing a queue, to ensure that the consumer and
+ * producer threads will no longer access it.
+ */
+void
+delete_fluid_ringbuffer (fluid_ringbuffer_t *queue)
+{
+ FLUID_FREE (queue->array);
+ FLUID_FREE (queue);
+}
diff --git a/src/midi/old/fluid_ringbuffer.h b/src/midi/old/fluid_ringbuffer.h
new file mode 100644
index 0000000..bd43f8a
--- /dev/null
+++ b/src/midi/old/fluid_ringbuffer.h
@@ -0,0 +1,128 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUID_RINGBUFFER_H
+#define _FLUID_RINGBUFFER_H
+
+#include "fluid_sys.h"
+
+/**
+ * Lockless event queue instance.
+ */
+struct _fluid_ringbuffer_t
+{
+ char *array; /**< Queue array of arbitrary size elements */
+ int totalcount; /**< Total count of elements in array */
+ int count; /**< Current count of elements */
+ int in; /**< Index in queue to store next pushed element */
+ int out; /**< Index in queue of next popped element */
+ int elementsize; /**< Size of each element */
+ void* userdata;
+};
+
+typedef struct _fluid_ringbuffer_t fluid_ringbuffer_t;
+
+
+fluid_ringbuffer_t *new_fluid_ringbuffer (int count, int elementsize);
+void delete_fluid_ringbuffer (fluid_ringbuffer_t *queue);
+
+/**
+ * Get pointer to next input array element in queue.
+ * @param queue Lockless queue instance
+ * @param count Normally zero, or more if you need to push several items at once
+ * @return Pointer to array element in queue to store data to or NULL if queue is full
+ *
+ * This function along with fluid_ringbuffer_next_inptr() form a queue "push"
+ * operation and is split into 2 functions to avoid an element copy. Note that
+ * the returned array element pointer may contain the data of a previous element
+ * if the queue has wrapped around. This can be used to reclaim pointers to
+ * allocated memory, etc.
+ */
+static FLUID_INLINE void*
+fluid_ringbuffer_get_inptr (fluid_ringbuffer_t *queue, int offset)
+{
+ return fluid_atomic_int_get (&queue->count) + offset >= queue->totalcount ? NULL
+ : queue->array + queue->elementsize * ((queue->in + offset) % queue->totalcount);
+}
+
+/**
+ * Advance the input queue index to complete a "push" operation.
+ * @param queue Lockless queue instance
+ * @param count Normally one, or more if you need to push several items at once
+ *
+ * This function along with fluid_ringbuffer_get_inptr() form a queue "push"
+ * operation and is split into 2 functions to avoid element copy.
+ */
+static FLUID_INLINE void
+fluid_ringbuffer_next_inptr (fluid_ringbuffer_t *queue, int count)
+{
+ fluid_atomic_int_add (&queue->count, count);
+
+ queue->in += count;
+ if (queue->in >= queue->totalcount)
+ queue->in -= queue->totalcount;
+}
+
+/**
+ * Get amount of items currently in queue
+ * @param queue Lockless queue instance
+ * @return amount of items currently in queue
+ */
+static FLUID_INLINE int
+fluid_ringbuffer_get_count (fluid_ringbuffer_t *queue)
+{
+ return fluid_atomic_int_get (&queue->count);
+}
+
+
+/**
+ * Get pointer to next output array element in queue.
+ * @param queue Lockless queue instance
+ * @return Pointer to array element data in the queue or NULL if empty, can only
+ * be used up until fluid_ringbuffer_next_outptr() is called.
+ *
+ * This function along with fluid_ringbuffer_next_outptr() form a queue "pop"
+ * operation and is split into 2 functions to avoid an element copy.
+ */
+static FLUID_INLINE void*
+fluid_ringbuffer_get_outptr (fluid_ringbuffer_t *queue)
+{
+ return fluid_ringbuffer_get_count(queue) == 0 ? NULL
+ : queue->array + queue->elementsize * queue->out;
+}
+
+
+/**
+ * Advance the output queue index to complete a "pop" operation.
+ * @param queue Lockless queue instance
+ *
+ * This function along with fluid_ringbuffer_get_outptr() form a queue "pop"
+ * operation and is split into 2 functions to avoid an element copy.
+ */
+static FLUID_INLINE void
+fluid_ringbuffer_next_outptr (fluid_ringbuffer_t *queue)
+{
+ fluid_atomic_int_add (&queue->count, -1);
+
+ if (++queue->out == queue->totalcount)
+ queue->out = 0;
+}
+
+#endif /* _FLUID_ringbuffer_H */
diff --git a/src/midi/old/fluid_settings.c b/src/midi/old/fluid_settings.c
new file mode 100644
index 0000000..78725fb
--- /dev/null
+++ b/src/midi/old/fluid_settings.c
@@ -0,0 +1,1600 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluidsynth_priv.h"
+#include "fluid_sys.h"
+#include "fluid_hash.h"
+#include "fluid_synth.h"
+#include "fluid_cmd.h"
+#include "fluid_adriver.h"
+#include "fluid_mdriver.h"
+#include "fluid_settings.h"
+#include "fluid_midi.h"
+
+/* Defined in fluid_filerenderer.c */
+extern void fluid_file_renderer_settings (fluid_settings_t* settings);
+
+/* maximum allowed components of a settings variable (separated by '.') */
+#define MAX_SETTINGS_TOKENS 8 /* currently only a max of 3 are used */
+#define MAX_SETTINGS_LABEL 256 /* max length of a settings variable label */
+
+static void fluid_settings_init(fluid_settings_t* settings);
+static void fluid_settings_key_destroy_func(void* value);
+static void fluid_settings_value_destroy_func(void* value);
+static int fluid_settings_tokenize(const char *s, char *buf, char **ptr);
+
+/* Common structure to all settings nodes */
+typedef struct {
+ int type; /**< fluid_types_enum */
+} fluid_setting_node_t;
+
+typedef struct {
+ fluid_setting_node_t node;
+ char* value;
+ char* def;
+ int hints;
+ fluid_list_t* options;
+ fluid_str_update_t update;
+ void* data;
+} fluid_str_setting_t;
+
+typedef struct {
+ fluid_setting_node_t node;
+ double value;
+ double def;
+ double min;
+ double max;
+ int hints;
+ fluid_num_update_t update;
+ void* data;
+} fluid_num_setting_t;
+
+typedef struct {
+ fluid_setting_node_t node;
+ int value;
+ int def;
+ int min;
+ int max;
+ int hints;
+ fluid_int_update_t update;
+ void* data;
+} fluid_int_setting_t;
+
+typedef struct {
+ fluid_setting_node_t node;
+ fluid_hashtable_t *hashtable;
+} fluid_set_setting_t;
+
+
+static fluid_str_setting_t*
+new_fluid_str_setting(const char* value, char* def, int hints, fluid_str_update_t fun, void* data)
+{
+ fluid_str_setting_t* str;
+
+ str = FLUID_NEW(fluid_str_setting_t);
+
+ if (!str)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ str->node.type = FLUID_STR_TYPE;
+ str->value = value? FLUID_STRDUP(value) : NULL;
+ str->def = def? FLUID_STRDUP(def) : NULL;
+ str->hints = hints;
+ str->options = NULL;
+ str->update = fun;
+ str->data = data;
+ return str;
+}
+
+static void
+delete_fluid_str_setting(fluid_str_setting_t* str)
+{
+ if (!str) return;
+
+ if (str->value) FLUID_FREE(str->value);
+ if (str->def) FLUID_FREE(str->def);
+
+ if (str->options) {
+ fluid_list_t* list = str->options;
+
+ while (list) {
+ FLUID_FREE (list->data);
+ list = fluid_list_next(list);
+ }
+
+ delete_fluid_list(str->options);
+ }
+
+ FLUID_FREE(str);
+}
+
+
+static fluid_num_setting_t*
+new_fluid_num_setting(double min, double max, double def,
+ int hints, fluid_num_update_t fun, void* data)
+{
+ fluid_num_setting_t* setting;
+
+ setting = FLUID_NEW(fluid_num_setting_t);
+
+ if (!setting)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ setting->node.type = FLUID_NUM_TYPE;
+ setting->value = def;
+ setting->def = def;
+ setting->min = min;
+ setting->max = max;
+ setting->hints = hints;
+ setting->update = fun;
+ setting->data = data;
+ return setting;
+}
+
+static void
+delete_fluid_num_setting(fluid_num_setting_t* setting)
+{
+ if (setting) FLUID_FREE(setting);
+}
+
+static fluid_int_setting_t*
+new_fluid_int_setting(int min, int max, int def,
+ int hints, fluid_int_update_t fun, void* data)
+{
+ fluid_int_setting_t* setting;
+
+ setting = FLUID_NEW(fluid_int_setting_t);
+
+ if (!setting)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ setting->node.type = FLUID_INT_TYPE;
+ setting->value = def;
+ setting->def = def;
+ setting->min = min;
+ setting->max = max;
+ setting->hints = hints;
+ setting->update = fun;
+ setting->data = data;
+ return setting;
+}
+
+static void
+delete_fluid_int_setting(fluid_int_setting_t* setting)
+{
+ if (setting) FLUID_FREE(setting);
+}
+
+static fluid_set_setting_t*
+new_fluid_set_setting(void)
+{
+ fluid_set_setting_t* setting;
+
+ setting = FLUID_NEW(fluid_set_setting_t);
+
+ if (!setting)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ setting->node.type = FLUID_SET_TYPE;
+ setting->hashtable = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal,
+ fluid_settings_key_destroy_func,
+ fluid_settings_value_destroy_func);
+ if (!setting->hashtable)
+ {
+ FLUID_FREE (setting);
+ return NULL;
+ }
+
+ return setting;
+}
+
+static void
+delete_fluid_set_setting(fluid_set_setting_t* setting)
+{
+ if (setting)
+ {
+ delete_fluid_hashtable(setting->hashtable);
+ FLUID_FREE(setting);
+ }
+}
+
+/**
+ * Create a new settings object
+ * @return the pointer to the settings object
+ */
+fluid_settings_t *
+new_fluid_settings(void)
+{
+ fluid_settings_t* settings;
+
+ settings = new_fluid_hashtable_full(fluid_str_hash, fluid_str_equal,
+ fluid_settings_key_destroy_func,
+ fluid_settings_value_destroy_func);
+ if (settings == NULL) return NULL;
+
+ fluid_rec_mutex_init (settings->mutex);
+ fluid_settings_init(settings);
+ return settings;
+}
+
+/**
+ * Delete the provided settings object
+ * @param settings a settings object
+ */
+void
+delete_fluid_settings(fluid_settings_t* settings)
+{
+ fluid_return_if_fail (settings != NULL);
+
+ fluid_rec_mutex_destroy (settings->mutex);
+ delete_fluid_hashtable(settings);
+}
+
+/* Settings hash key destroy function */
+static void
+fluid_settings_key_destroy_func(void* value)
+{
+ FLUID_FREE (value); /* Free the string key value */
+}
+
+/* Settings hash value destroy function */
+static void
+fluid_settings_value_destroy_func(void* value)
+{
+ fluid_setting_node_t *node = value;
+
+ switch (node->type) {
+ case FLUID_NUM_TYPE:
+ delete_fluid_num_setting((fluid_num_setting_t*) value);
+ break;
+ case FLUID_INT_TYPE:
+ delete_fluid_int_setting((fluid_int_setting_t*) value);
+ break;
+ case FLUID_STR_TYPE:
+ delete_fluid_str_setting((fluid_str_setting_t*) value);
+ break;
+ case FLUID_SET_TYPE:
+ delete_fluid_set_setting((fluid_set_setting_t*) value);
+ break;
+ }
+}
+
+void
+fluid_settings_init(fluid_settings_t* settings)
+{
+ fluid_return_if_fail (settings != NULL);
+
+ fluid_synth_settings(settings);
+ fluid_shell_settings(settings);
+ fluid_player_settings(settings);
+ fluid_file_renderer_settings(settings);
+ fluid_audio_driver_settings(settings);
+ fluid_midi_driver_settings(settings);
+}
+
+static int
+fluid_settings_tokenize(const char *s, char *buf, char **ptr)
+{
+ char *tokstr, *tok;
+ int n = 0;
+
+ if (strlen (s) > MAX_SETTINGS_LABEL)
+ {
+ FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max length of %d chars",
+ MAX_SETTINGS_LABEL);
+ return 0;
+ }
+
+ FLUID_STRCPY(buf, s); /* copy string to buffer, since it gets modified */
+ tokstr = buf;
+
+ while ((tok = fluid_strtok (&tokstr, ".")))
+ {
+ if (n >= MAX_SETTINGS_TOKENS)
+ {
+ FLUID_LOG(FLUID_ERR, "Setting variable name exceeded max token count of %d",
+ MAX_SETTINGS_TOKENS);
+ return 0;
+ } else
+ ptr[n++] = tok;
+ }
+
+ return n;
+}
+
+/**
+ * Get a setting name, value and type
+ *
+ * @param settings a settings object
+ * @param name Settings name
+ * @param value Location to store setting node if found
+ * @return 1 if the node exists, 0 otherwise
+ */
+static int
+fluid_settings_get(fluid_settings_t* settings, const char *name,
+ fluid_setting_node_t **value)
+{
+ fluid_hashtable_t* table = settings;
+ fluid_setting_node_t *node = NULL;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int ntokens;
+ int n;
+
+ ntokens = fluid_settings_tokenize (name, buf, tokens);
+
+ if (table == NULL || ntokens <= 0) return 0;
+
+ for (n = 0; n < ntokens; n++) {
+
+ node = fluid_hashtable_lookup(table, tokens[n]);
+ if (!node) return 0;
+
+ table = (node->type == FLUID_SET_TYPE) ? ((fluid_set_setting_t *)node)->hashtable : NULL;
+ }
+
+ if (value) *value = node;
+
+ return 1;
+}
+
+/**
+ * Set a setting name, value and type, replacing it if already exists
+ *
+ * @param settings a settings object
+ * @param name Settings name
+ * @param value Node instance to assign (used directly)
+ * @return 1 if the value has been set, zero otherwise
+ */
+static int
+fluid_settings_set(fluid_settings_t* settings, const char *name, void* value)
+{
+ fluid_hashtable_t* table = settings;
+ fluid_setting_node_t *node;
+ char* tokens[MAX_SETTINGS_TOKENS];
+ char buf[MAX_SETTINGS_LABEL+1];
+ int n, num;
+ char *dupname;
+
+ num = fluid_settings_tokenize (name, buf, tokens) - 1;
+ if (num == 0)
+ return 0;
+
+ for (n = 0; n < num; n++) {
+
+ node = fluid_hashtable_lookup(table, tokens[n]);
+
+ if (node) {
+
+ if (node->type == FLUID_SET_TYPE) {
+ table = ((fluid_set_setting_t *)node)->hashtable;
+ } else {
+ /* path ends prematurely */
+ FLUID_LOG(FLUID_WARN, "'%s' is not a node", name[n]);
+ return 0;
+ }
+
+ } else {
+ /* create a new node */
+ fluid_set_setting_t* setnode;
+
+ dupname = FLUID_STRDUP (tokens[n]);
+ setnode = new_fluid_set_setting ();
+
+ if (!dupname || !setnode)
+ {
+ if (dupname) FLUID_FREE (dupname);
+ else FLUID_LOG(FLUID_ERR, "Out of memory");
+
+ if (setnode) delete_fluid_set_setting (setnode);
+
+ return 0;
+ }
+
+ fluid_hashtable_insert(table, dupname, setnode);
+ table = setnode->hashtable;
+ }
+ }
+
+ dupname = FLUID_STRDUP (tokens[num]);
+
+ if (!dupname)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return 0;
+ }
+
+ fluid_hashtable_insert(table, dupname, value);
+
+ return 1;
+}
+
+/** returns 1 if the value has been registered correctly, 0
+ otherwise */
+int
+fluid_settings_register_str(fluid_settings_t* settings, char* name, char* def, int hints,
+ fluid_str_update_t fun, void* data)
+{
+ fluid_setting_node_t *node;
+ fluid_str_setting_t* setting;
+ int retval;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (!fluid_settings_get(settings, name, &node)) {
+ setting = new_fluid_str_setting(def, def, hints, fun, data);
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_str_setting (setting);
+ } else {
+ /* if variable already exists, don't change its value. */
+ if (node->type == FLUID_STR_TYPE) {
+ setting = (fluid_str_setting_t*) node;
+ setting->update = fun;
+ setting->data = data;
+ setting->def = def? FLUID_STRDUP(def) : NULL;
+ setting->hints = hints;
+ retval = 1;
+ } else {
+ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+ retval = 0;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/** returns 1 if the value has been register correctly, zero
+ otherwise */
+int
+fluid_settings_register_num(fluid_settings_t* settings, char* name, double def,
+ double min, double max, int hints,
+ fluid_num_update_t fun, void* data)
+{
+ fluid_setting_node_t *node;
+ int retval;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ /* For now, all floating point settings are bounded below and above */
+ hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE;
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (!fluid_settings_get(settings, name, &node)) {
+ /* insert a new setting */
+ fluid_num_setting_t* setting;
+ setting = new_fluid_num_setting(min, max, def, hints, fun, data);
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_num_setting (setting);
+ } else {
+ if (node->type == FLUID_NUM_TYPE) {
+ /* update the existing setting but don't change its value */
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ setting->update = fun;
+ setting->data = data;
+ setting->min = min;
+ setting->max = max;
+ setting->def = def;
+ setting->hints = hints;
+ retval = 1;
+ } else {
+ /* type mismatch */
+ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+ retval = 0;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/** returns 1 if the value has been register correctly, zero
+ otherwise. */
+int
+fluid_settings_register_int(fluid_settings_t* settings, char* name, int def,
+ int min, int max, int hints,
+ fluid_int_update_t fun, void* data)
+{
+ fluid_setting_node_t *node;
+ int retval;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ /* For now, all integer settings are bounded below and above */
+ hints |= FLUID_HINT_BOUNDED_BELOW | FLUID_HINT_BOUNDED_ABOVE;
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (!fluid_settings_get(settings, name, &node)) {
+ /* insert a new setting */
+ fluid_int_setting_t* setting;
+ setting = new_fluid_int_setting(min, max, def, hints, fun, data);
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_int_setting (setting);
+ } else {
+ if (node->type == FLUID_INT_TYPE) {
+ /* update the existing setting but don't change its value */
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ setting->update = fun;
+ setting->data = data;
+ setting->min = min;
+ setting->max = max;
+ setting->def = def;
+ setting->hints = hints;
+ retval = 1;
+ } else {
+ /* type mismatch */
+ FLUID_LOG(FLUID_WARN, "Type mismatch on setting '%s'", name);
+ retval = 0;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the type of the setting with the given name
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the type for the named setting, or #FLUID_NO_TYPE when it does not exist
+ */
+int
+fluid_settings_get_type(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int type;
+
+ fluid_return_val_if_fail (settings != NULL, FLUID_NO_TYPE);
+ fluid_return_val_if_fail (name != NULL, FLUID_NO_TYPE);
+ fluid_return_val_if_fail (name[0] != '\0', FLUID_NO_TYPE);
+
+ fluid_rec_mutex_lock (settings->mutex);
+ type = fluid_settings_get (settings, name, &node) ? node->type : FLUID_NO_TYPE;
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return (type);
+}
+
+/**
+ * Get the hints for the named setting as an integer bitmap
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the hints associated to the named setting if it exists, zero otherwise
+ */
+int
+fluid_settings_get_hints(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int hints = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)) {
+ if (node->type == FLUID_NUM_TYPE) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ hints = setting->hints;
+ } else if (node->type == FLUID_STR_TYPE) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ hints = setting->hints;
+ } else if (node->type == FLUID_INT_TYPE) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ hints = setting->hints;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return hints;
+}
+
+/**
+ * Ask whether the setting is changeable in real-time.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return non zero if the setting is changeable in real-time
+ */
+int
+fluid_settings_is_realtime(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int isrealtime = FALSE;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)) {
+ if (node->type == FLUID_NUM_TYPE) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ isrealtime = setting->update != NULL;
+ } else if (node->type == FLUID_STR_TYPE) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ isrealtime = setting->update != NULL;
+ } else if (node->type == FLUID_INT_TYPE) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ isrealtime = setting->update != NULL;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return isrealtime;
+}
+
+/**
+ * Set a string value for a named setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param str new string value
+ * @return 1 if the value has been set, 0 otherwise
+ */
+int
+fluid_settings_setstr(fluid_settings_t* settings, const char *name, const char *str)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get (settings, name, &node)) {
+ if (node->type == FLUID_STR_TYPE) {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+
+ if (setting->value) FLUID_FREE (setting->value);
+ setting->value = str ? FLUID_STRDUP (str) : NULL;
+
+ /* Call under lock to keep update() synchronized with the current value */
+ if (setting->update) (*setting->update)(setting->data, name, str);
+ retval = 1;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle yes/no for boolean values for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ {
+ if (FLUID_STRCMP (str, "yes") == 0)
+ {
+ setting->value = TRUE;
+ if (setting->update) (*setting->update)(setting->data, name, TRUE);
+ }
+ else if (FLUID_STRCMP (str, "no") == 0)
+ {
+ setting->value = FALSE;
+ if (setting->update) (*setting->update)(setting->data, name, FALSE);
+ }
+ }
+ }
+ } else {
+ /* insert a new setting */
+ fluid_str_setting_t* setting;
+ setting = new_fluid_str_setting(str, NULL, 0, NULL, NULL);
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_str_setting (setting);
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Copy the value of a string setting
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param str Caller supplied buffer to copy string value to
+ * @param len Size of 'str' buffer (no more than len bytes will be written, which
+ * will always include a zero terminator)
+ * @return 1 if the value exists, 0 otherwise
+ * @since 1.1.0
+ *
+ * Like fluid_settings_getstr() but is thread safe. A size of 256 should be
+ * more than sufficient for the string buffer.
+ */
+int
+fluid_settings_copystr(fluid_settings_t* settings, const char *name,
+ char *str, int len)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (str != NULL, 0);
+ fluid_return_val_if_fail (len > 0, 0);
+
+ str[0] = 0;
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get (settings, name, &node))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+
+ if (setting->value)
+ {
+ FLUID_STRNCPY (str, setting->value, len);
+ str[len - 1] = 0; /* Force terminate, in case of truncation */
+ }
+
+ retval = 1;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ {
+ FLUID_STRNCPY (str, setting->value ? "yes" : "no", len);
+ str[len - 1] = 0; /* Force terminate, in case of truncation */
+
+ retval = 1;
+ }
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Duplicate the value of a string setting
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param str Location to store pointer to allocated duplicate string
+ * @return 1 if the value exists and was successfully duplicated, 0 otherwise
+ * @since 1.1.0
+ *
+ * Like fluid_settings_copystr() but allocates a new copy of the string. Caller
+ * owns the string and should free it with free() when done using it.
+ */
+int
+fluid_settings_dupstr(fluid_settings_t* settings, const char *name, char** str)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (str != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+
+ if (setting->value)
+ {
+ *str = FLUID_STRDUP (setting->value);
+ if (!*str) FLUID_LOG (FLUID_ERR, "Out of memory");
+ }
+
+ if (!setting->value || *str) retval = 1; /* Don't set to 1 if out of memory */
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ {
+ *str = FLUID_STRDUP (setting->value ? "yes" : "no");
+ if (!*str) FLUID_LOG (FLUID_ERR, "Out of memory");
+
+ if (!setting->value || *str) retval = 1; /* Don't set to 1 if out of memory */
+ }
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the value of a string setting
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param str Location to store pointer to the settings string value
+ * @return 1 if the value exists, 0 otherwise
+ * @deprecated
+ *
+ * If the value does not exists, 'str' is set to NULL. Otherwise, 'str' will
+ * point to the value. The application does not own the returned value and it
+ * is valid only until a new value is assigned to the setting of the given name.
+ *
+ * NOTE: In a multi-threaded environment, caller must ensure that the setting
+ * being read by fluid_settings_getstr() is not assigned during the
+ * duration of callers use of the setting's value. Use fluid_settings_copystr()
+ * or fluid_settings_dupstr() which does not have this restriction.
+ */
+int
+fluid_settings_getstr(fluid_settings_t* settings, const char *name, char** str)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (str != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+ *str = setting->value;
+ retval = 1;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ {
+ *str = setting->value ? "yes" : "no";
+ retval = 1;
+ }
+ }
+ }
+ else *str = NULL;
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Test a string setting for some value.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param s a string to be tested
+ * @return 1 if the value exists and is equal to 's', 0 otherwise
+ */
+int
+fluid_settings_str_equal (fluid_settings_t* settings, const char *name, const char *s)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (s != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get (settings, name, &node))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t *setting = (fluid_str_setting_t *)node;
+ if (setting->value) retval = FLUID_STRCMP (setting->value, s) == 0;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ retval = FLUID_STRCMP (setting->value ? "yes" : "no", s) == 0;
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the default value of a string setting. Note that the returned string is
+ * not owned by the caller and should not be modified or freed.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the default string value of the setting if it exists, NULL otherwise
+ */
+char*
+fluid_settings_getstr_default(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ char *retval = NULL;
+
+ fluid_return_val_if_fail (settings != NULL, NULL);
+ fluid_return_val_if_fail (name != NULL, NULL);
+ fluid_return_val_if_fail (name[0] != '\0', NULL);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get (settings, name, &node))
+ {
+ if (node->type == FLUID_STR_TYPE)
+ {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ retval = setting->def;
+ }
+ else if (node->type == FLUID_INT_TYPE) /* Handle boolean integers for backwards compatibility */
+ {
+ fluid_int_setting_t *setting = (fluid_int_setting_t *)node;
+
+ if (setting->hints & FLUID_HINT_TOGGLED)
+ retval = setting->def ? "yes" : "no";
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Add an option to a string setting (like an enumeration value).
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param s option string to add
+ * @return 1 if the setting exists and option was added, 0 otherwise
+ *
+ * Causes the setting's #FLUID_HINT_OPTIONLIST hint to be set.
+ */
+int
+fluid_settings_add_option(fluid_settings_t* settings, const char *name, const char *s)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (s != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_STR_TYPE)) {
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ char* copy = FLUID_STRDUP(s);
+ setting->options = fluid_list_append(setting->options, copy);
+ setting->hints |= FLUID_HINT_OPTIONLIST;
+ retval = 1;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Remove an option previously assigned by fluid_settings_add_option().
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param s option string to remove
+ * @return 1 if the setting exists and option was removed, 0 otherwise
+ */
+int
+fluid_settings_remove_option(fluid_settings_t* settings, const char *name, const char* s)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (s != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_STR_TYPE)) {
+
+ fluid_str_setting_t* setting = (fluid_str_setting_t*) node;
+ fluid_list_t* list = setting->options;
+
+ while (list) {
+ char* option = (char*) fluid_list_get(list);
+ if (FLUID_STRCMP(s, option) == 0) {
+ FLUID_FREE (option);
+ setting->options = fluid_list_remove_link(setting->options, list);
+ retval = 1;
+ break;
+ }
+ list = fluid_list_next(list);
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Set a numeric value for a named setting.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param val new setting's value
+ * @return 1 if the value has been set, 0 otherwise
+ */
+int
+fluid_settings_setnum(fluid_settings_t* settings, const char *name, double val)
+{
+ fluid_setting_node_t *node;
+ fluid_num_setting_t* setting;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)) {
+ if (node->type == FLUID_NUM_TYPE) {
+ setting = (fluid_num_setting_t*) node;
+
+ if (val < setting->min) val = setting->min;
+ else if (val > setting->max) val = setting->max;
+
+ setting->value = val;
+
+ /* Call under lock to keep update() synchronized with the current value */
+ if (setting->update) (*setting->update)(setting->data, name, val);
+ retval = 1;
+ }
+ } else {
+ /* insert a new setting */
+ fluid_num_setting_t* setting;
+ setting = new_fluid_num_setting(-1e10, 1e10, 0.0f, 0, NULL, NULL);
+ setting->value = val;
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_num_setting (setting);
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the numeric value of a named setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param val variable pointer to receive the setting's numeric value
+ * @return 1 if the value exists, 0 otherwise
+ */
+int
+fluid_settings_getnum(fluid_settings_t* settings, const char *name, double* val)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (val != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_NUM_TYPE)) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ *val = setting->value;
+ retval = 1;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the range of values of a numeric setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param min setting's range lower limit
+ * @param max setting's range upper limit
+ */
+void
+fluid_settings_getnum_range(fluid_settings_t* settings, const char *name,
+ double* min, double* max)
+{
+ fluid_setting_node_t *node;
+
+ fluid_return_if_fail (settings != NULL);
+ fluid_return_if_fail (name != NULL);
+ fluid_return_if_fail (name[0] != '\0');
+ fluid_return_if_fail (min != NULL);
+ fluid_return_if_fail (max != NULL);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_NUM_TYPE)) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ *min = setting->min;
+ *max = setting->max;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+}
+
+/**
+ * Get the default value of a named numeric (double) setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the default value if the named setting exists, 0.0f otherwise
+ */
+double
+fluid_settings_getnum_default(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ double retval = 0.0;
+
+ fluid_return_val_if_fail (settings != NULL, 0.0);
+ fluid_return_val_if_fail (name != NULL, 0.0);
+ fluid_return_val_if_fail (name[0] != '\0', 0.0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_NUM_TYPE)) {
+ fluid_num_setting_t* setting = (fluid_num_setting_t*) node;
+ retval = setting->def;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Set an integer value for a setting
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param val new setting's integer value
+ * @return 1 if the value has been set, 0 otherwise
+ */
+int
+fluid_settings_setint(fluid_settings_t* settings, const char *name, int val)
+{
+ fluid_setting_node_t *node;
+ fluid_int_setting_t* setting;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)) {
+ if (node->type == FLUID_INT_TYPE) {
+ setting = (fluid_int_setting_t*) node;
+
+ if (val < setting->min) val = setting->min;
+ else if (val > setting->max) val = setting->max;
+
+ setting->value = val;
+
+ /* Call under lock to keep update() synchronized with the current value */
+ if (setting->update) (*setting->update)(setting->data, name, val);
+ retval = 1;
+ }
+ } else {
+ /* insert a new setting */
+ fluid_int_setting_t* setting;
+ setting = new_fluid_int_setting(INT_MIN, INT_MAX, 0, 0, NULL, NULL);
+ setting->value = val;
+ retval = fluid_settings_set(settings, name, setting);
+ if (retval != 1) delete_fluid_int_setting (setting);
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get an integer value setting.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param val pointer to a variable to receive the setting's integer value
+ * @return 1 if the value exists, 0 otherwise
+ */
+int
+fluid_settings_getint(fluid_settings_t* settings, const char *name, int* val)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+ fluid_return_val_if_fail (val != NULL, 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_INT_TYPE)) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ *val = setting->value;
+ retval = 1;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Get the range of values of an integer setting
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param min setting's range lower limit
+ * @param max setting's range upper limit
+ */
+void
+fluid_settings_getint_range(fluid_settings_t* settings, const char *name,
+ int* min, int* max)
+{
+ fluid_setting_node_t *node;
+
+ fluid_return_if_fail (settings != NULL);
+ fluid_return_if_fail (name != NULL);
+ fluid_return_if_fail (name[0] != '\0');
+ fluid_return_if_fail (min != NULL);
+ fluid_return_if_fail (max != NULL);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_INT_TYPE)) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ *min = setting->min;
+ *max = setting->max;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+}
+
+/**
+ * Get the default value of an integer setting.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @return the setting's default integer value it it exists, zero otherwise
+ */
+int
+fluid_settings_getint_default(fluid_settings_t* settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int retval = 0;
+
+ fluid_return_val_if_fail (settings != NULL, 0);
+ fluid_return_val_if_fail (name != NULL, 0);
+ fluid_return_val_if_fail (name[0] != '\0', 0);
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ if (fluid_settings_get(settings, name, &node)
+ && (node->type == FLUID_INT_TYPE)) {
+ fluid_int_setting_t* setting = (fluid_int_setting_t*) node;
+ retval = setting->def;
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return retval;
+}
+
+/**
+ * Iterate the available options for a named string setting, calling the provided
+ * callback function for each existing option.
+ *
+ * @param settings a settings object
+ * @param name a setting's name
+ * @param data any user provided pointer
+ * @param func callback function to be called on each iteration
+ *
+ * NOTE: Starting with FluidSynth 1.1.0 the \a func callback is called for each
+ * option in alphabetical order. Sort order was undefined in previous versions.
+ */
+void
+fluid_settings_foreach_option (fluid_settings_t* settings, const char *name,
+ void* data, fluid_settings_foreach_option_t func)
+{
+ fluid_setting_node_t *node;
+ fluid_str_setting_t *setting;
+ fluid_list_t *p, *newlist = NULL;
+
+ fluid_return_if_fail (settings != NULL);
+ fluid_return_if_fail (name != NULL);
+ fluid_return_if_fail (name[0] != '\0');
+ fluid_return_if_fail (func != NULL);
+
+ fluid_rec_mutex_lock (settings->mutex); /* ++ lock */
+
+ if (!fluid_settings_get (settings, name, &node) || node->type != FLUID_STR_TYPE)
+ {
+ fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */
+ return;
+ }
+
+ setting = (fluid_str_setting_t*)node;
+
+ /* Duplicate option list */
+ for (p = setting->options; p; p = p->next)
+ newlist = fluid_list_append (newlist, fluid_list_get (p));
+
+ /* Sort by name */
+ newlist = fluid_list_sort (newlist, fluid_list_str_compare_func);
+
+ for (p = newlist; p; p = p->next)
+ (*func)(data, (char *)name, (char *)fluid_list_get (p));
+
+ fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */
+
+ delete_fluid_list (newlist);
+}
+
+/**
+ * Count option string values for a string setting.
+ * @param settings a settings object
+ * @param name Name of setting
+ * @return Count of options for this string setting (0 if none, -1 if not found
+ * or not a string setting)
+ * @since 1.1.0
+ */
+int
+fluid_settings_option_count (fluid_settings_t *settings, const char *name)
+{
+ fluid_setting_node_t *node;
+ int count = -1;
+
+ fluid_return_val_if_fail (settings != NULL, -1);
+ fluid_return_val_if_fail (name != NULL, -1);
+ fluid_return_val_if_fail (name[0] != '\0', -1);
+
+ fluid_rec_mutex_lock (settings->mutex);
+ if (fluid_settings_get(settings, name, &node) && node->type == FLUID_STR_TYPE)
+ count = fluid_list_size (((fluid_str_setting_t *)node)->options);
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ return (count);
+}
+
+/**
+ * Concatenate options for a string setting together with a separator between.
+ * @param settings Settings object
+ * @param name Settings name
+ * @param separator String to use between options (NULL to use ", ")
+ * @return Newly allocated string or NULL on error (out of memory, not a valid
+ * setting \a name or not a string setting). Free the string when finished with it.
+ * @since 1.1.0
+ */
+char *
+fluid_settings_option_concat (fluid_settings_t *settings, const char *name,
+ const char *separator)
+{
+ fluid_setting_node_t *node;
+ fluid_str_setting_t *setting;
+ fluid_list_t *p, *newlist = NULL;
+ int count, len;
+ char *str, *option;
+
+ fluid_return_val_if_fail (settings != NULL, NULL);
+ fluid_return_val_if_fail (name != NULL, NULL);
+ fluid_return_val_if_fail (name[0] != '\0', NULL);
+
+ if (!separator) separator = ", ";
+
+ fluid_rec_mutex_lock (settings->mutex); /* ++ lock */
+
+ if (!fluid_settings_get (settings, name, &node) || node->type != FLUID_STR_TYPE)
+ {
+ fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */
+ return (NULL);
+ }
+
+ setting = (fluid_str_setting_t*)node;
+
+ /* Duplicate option list, count options and get total string length */
+ for (p = setting->options, count = 0, len = 0; p; p = p->next, count++)
+ {
+ option = fluid_list_get (p);
+
+ if (option)
+ {
+ newlist = fluid_list_append (newlist, option);
+ len += strlen (option);
+ }
+ }
+
+ if (count > 1) len += (count - 1) * strlen (separator);
+ len++; /* For terminator */
+
+ /* Sort by name */
+ newlist = fluid_list_sort (newlist, fluid_list_str_compare_func);
+
+ str = FLUID_MALLOC (len);
+
+ if (str)
+ {
+ str[0] = 0;
+ for (p = newlist; p; p = p->next)
+ {
+ option = fluid_list_get (p);
+ strcat (str, option);
+ if (p->next) strcat (str, separator);
+ }
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex); /* -- unlock */
+
+ delete_fluid_list (newlist);
+
+ if (!str) FLUID_LOG (FLUID_ERR, "Out of memory");
+
+ return (str);
+}
+
+/* Structure passed to fluid_settings_foreach_iter recursive function */
+typedef struct
+{
+ char path[MAX_SETTINGS_LABEL+1]; /* Maximum settings label length */
+ fluid_list_t *names; /* For fluid_settings_foreach() */
+} fluid_settings_foreach_bag_t;
+
+static int
+fluid_settings_foreach_iter (void* key, void* value, void* data)
+{
+ fluid_settings_foreach_bag_t *bag = data;
+ char *keystr = key;
+ fluid_setting_node_t *node = value;
+ int pathlen;
+ char *s;
+
+ pathlen = strlen (bag->path);
+
+ if (pathlen > 0)
+ {
+ bag->path[pathlen] = '.';
+ bag->path[pathlen + 1] = 0;
+ }
+
+ strcat (bag->path, keystr);
+
+ switch (node->type) {
+ case FLUID_NUM_TYPE:
+ case FLUID_INT_TYPE:
+ case FLUID_STR_TYPE:
+ s = FLUID_STRDUP (bag->path);
+ if (s) bag->names = fluid_list_append (bag->names, s);
+ break;
+ case FLUID_SET_TYPE:
+ fluid_hashtable_foreach(((fluid_set_setting_t *)value)->hashtable,
+ fluid_settings_foreach_iter, bag);
+ break;
+ }
+
+ bag->path[pathlen] = 0;
+
+ return 0;
+}
+
+/**
+ * Iterate the existing settings defined in a settings object, calling the
+ * provided callback function for each setting.
+ *
+ * @param settings a settings object
+ * @param data any user provided pointer
+ * @param func callback function to be called on each iteration
+ *
+ * NOTE: Starting with FluidSynth 1.1.0 the \a func callback is called for each
+ * setting in alphabetical order. Sort order was undefined in previous versions.
+ */
+void
+fluid_settings_foreach (fluid_settings_t* settings, void* data,
+ fluid_settings_foreach_t func)
+{
+ fluid_settings_foreach_bag_t bag;
+ fluid_setting_node_t *node;
+ fluid_list_t *p;
+ int r;
+
+ fluid_return_if_fail (settings != NULL);
+ fluid_return_if_fail (func != NULL);
+
+ bag.path[0] = 0;
+ bag.names = NULL;
+
+ fluid_rec_mutex_lock (settings->mutex);
+
+ /* Add all node names to the bag.names list */
+ fluid_hashtable_foreach (settings, fluid_settings_foreach_iter, &bag);
+
+ /* Sort names */
+ bag.names = fluid_list_sort (bag.names, fluid_list_str_compare_func);
+
+ /* Loop over names and call the callback */
+ for (p = bag.names; p; p = p->next)
+ {
+ r = fluid_settings_get (settings, (char *)(p->data), &node);
+ if (r && node) (*func) (data, (char *)(p->data), node->type);
+ FLUID_FREE (p->data); /* -- Free name */
+ }
+
+ fluid_rec_mutex_unlock (settings->mutex);
+
+ delete_fluid_list (bag.names); /* -- Free names list */
+}
diff --git a/src/midi/old/fluid_settings.h b/src/midi/old/fluid_settings.h
new file mode 100644
index 0000000..0eb1f97
--- /dev/null
+++ b/src/midi/old/fluid_settings.h
@@ -0,0 +1,56 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUID_SETTINGS_H
+#define _FLUID_SETTINGS_H
+
+
+
+/** returns 1 if the option was added, 0 otherwise */
+int fluid_settings_add_option(fluid_settings_t* settings, const char* name, const char* s);
+
+/** returns 1 if the option was added, 0 otherwise */
+int fluid_settings_remove_option(fluid_settings_t* settings, const char* name, const char* s);
+
+
+typedef int (*fluid_num_update_t)(void* data, const char* name, double value);
+typedef int (*fluid_str_update_t)(void* data, const char* name, const char* value);
+typedef int (*fluid_int_update_t)(void* data, const char* name, int value);
+
+/** returns 0 if the value has been registered correctly, non-zero
+ otherwise */
+int fluid_settings_register_str(fluid_settings_t* settings, char* name, char* def, int hints,
+ fluid_str_update_t fun, void* data);
+
+/** returns 0 if the value has been registered correctly, non-zero
+ otherwise */
+int fluid_settings_register_num(fluid_settings_t* settings, char* name, double def,
+ double min, double max, int hints,
+ fluid_num_update_t fun, void* data);
+
+/** returns 0 if the value has been registered correctly, non-zero
+ otherwise */
+int fluid_settings_register_int(fluid_settings_t* settings, char* name, int def,
+ int min, int max, int hints,
+ fluid_int_update_t fun, void* data);
+
+
+#endif /* _FLUID_SETTINGS_H */
diff --git a/src/midi/old/fluid_sys.c b/src/midi/old/fluid_sys.c
new file mode 100644
index 0000000..0e208a8
--- /dev/null
+++ b/src/midi/old/fluid_sys.c
@@ -0,0 +1,1297 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "custom.h"
+#include "fluid_sys.h"
+
+
+#if WITH_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+#ifdef DBUS_SUPPORT
+#include "fluid_rtkit.h"
+#endif
+
+/* WIN32 HACK - Flag used to differentiate between a file descriptor and a socket.
+ * Should work, so long as no SOCKET or file descriptor ends up with this bit set. - JG */
+#define WIN32_SOCKET_FLAG 0x40000000
+
+/* SCHED_FIFO priority for high priority timer threads */
+#define FLUID_SYS_TIMER_HIGH_PRIO_LEVEL 10
+
+
+typedef struct
+{
+ fluid_thread_func_t func;
+ void *data;
+ int prio_level;
+} fluid_thread_info_t;
+
+struct _fluid_timer_t
+{
+ long msec;
+ fluid_timer_callback_t callback;
+ void *data;
+ fluid_thread_t *thread;
+ int cont;
+ int auto_destroy;
+};
+
+struct _fluid_server_socket_t
+{
+ fluid_socket_t socket;
+ fluid_thread_t *thread;
+ int cont;
+ fluid_server_func_t func;
+ void *data;
+};
+
+
+static int fluid_istream_gets(fluid_istream_t in, char* buf, int len);
+
+
+static char fluid_errbuf[512]; /* buffer for error message */
+
+static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL];
+static void* fluid_log_user_data[LAST_LOG_LEVEL];
+static int fluid_log_initialized = 0;
+
+static char* fluid_libname = "fluidsynth";
+
+
+void fluid_sys_config()
+{
+ fluid_log_config();
+}
+
+
+unsigned int fluid_debug_flags = 0;
+
+#if DEBUG
+/*
+ * fluid_debug
+ */
+int fluid_debug(int level, char * fmt, ...)
+{
+ if (fluid_debug_flags & level) {
+ fluid_log_function_t fun;
+ va_list args;
+
+ va_start (args, fmt);
+ vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args);
+ va_end (args);
+
+ fun = fluid_log_function[FLUID_DBG];
+ if (fun != NULL) {
+ (*fun)(level, fluid_errbuf, fluid_log_user_data[FLUID_DBG]);
+ }
+ }
+ return 0;
+}
+#endif
+
+/**
+ * Installs a new log function for a specified log level.
+ * @param level Log level to install handler for.
+ * @param fun Callback function handler to call for logged messages
+ * @param data User supplied data pointer to pass to log function
+ * @return The previously installed function.
+ */
+fluid_log_function_t
+fluid_set_log_function(int level, fluid_log_function_t fun, void* data)
+{
+ fluid_log_function_t old = NULL;
+
+ if ((level >= 0) && (level < LAST_LOG_LEVEL)) {
+ old = fluid_log_function[level];
+ fluid_log_function[level] = fun;
+ fluid_log_user_data[level] = data;
+ }
+ return old;
+}
+
+/**
+ * Default log function which prints to the stderr.
+ * @param level Log level
+ * @param message Log message
+ * @param data User supplied data (not used)
+ */
+void
+fluid_default_log_function(int level, char* message, void* data)
+{
+ FILE* out;
+
+#if defined(WIN32)
+ out = stdout;
+#else
+ out = stderr;
+#endif
+
+ if (fluid_log_initialized == 0) {
+ fluid_log_config();
+ }
+
+ switch (level) {
+ case FLUID_PANIC:
+ FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message);
+ break;
+ case FLUID_ERR:
+ FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message);
+ break;
+ case FLUID_WARN:
+ FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message);
+ break;
+ case FLUID_INFO:
+ FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
+ break;
+ case FLUID_DBG:
+#if DEBUG
+ FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message);
+#endif
+ break;
+ default:
+ FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
+ break;
+ }
+ fflush(out);
+}
+
+/*
+ * fluid_init_log
+ */
+void
+fluid_log_config(void)
+{
+ if (fluid_log_initialized == 0) {
+
+ fluid_log_initialized = 1;
+
+ if (fluid_log_function[FLUID_PANIC] == NULL) {
+ fluid_set_log_function(FLUID_PANIC, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_ERR] == NULL) {
+ fluid_set_log_function(FLUID_ERR, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_WARN] == NULL) {
+ fluid_set_log_function(FLUID_WARN, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_INFO] == NULL) {
+ fluid_set_log_function(FLUID_INFO, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_DBG] == NULL) {
+ fluid_set_log_function(FLUID_DBG, fluid_default_log_function, NULL);
+ }
+ }
+}
+
+/**
+ * Print a message to the log.
+ * @param level Log level (#fluid_log_level).
+ * @param fmt Printf style format string for log message
+ * @param ... Arguments for printf 'fmt' message string
+ * @return Always returns #FLUID_FAILED
+ */
+int
+fluid_log(int level, const char* fmt, ...)
+{
+ fluid_log_function_t fun = NULL;
+
+ va_list args;
+ va_start (args, fmt);
+ vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args);
+ va_end (args);
+
+ if ((level >= 0) && (level < LAST_LOG_LEVEL)) {
+ fun = fluid_log_function[level];
+ if (fun != NULL) {
+ (*fun)(level, fluid_errbuf, fluid_log_user_data[level]);
+ }
+ }
+ return FLUID_FAILED;
+}
+
+/**
+ * An improved strtok, still trashes the input string, but is portable and
+ * thread safe. Also skips token chars at beginning of token string and never
+ * returns an empty token (will return NULL if source ends in token chars though).
+ * NOTE: NOT part of public API
+ * @internal
+ * @param str Pointer to a string pointer of source to tokenize. Pointer gets
+ * updated on each invocation to point to beginning of next token. Note that
+ * token char get's overwritten with a 0 byte. String pointer is set to NULL
+ * when final token is returned.
+ * @param delim String of delimiter chars.
+ * @return Pointer to the next token or NULL if no more tokens.
+ */
+char *fluid_strtok (char **str, char *delim)
+{
+ char *s, *d, *token;
+ char c;
+
+ if (str == NULL || delim == NULL || !*delim)
+ {
+ FLUID_LOG(FLUID_ERR, "Null pointer");
+ return NULL;
+ }
+
+ s = *str;
+ if (!s) return NULL; /* str points to a NULL pointer? (tokenize already ended) */
+
+ /* skip delimiter chars at beginning of token */
+ do
+ {
+ c = *s;
+ if (!c) /* end of source string? */
+ {
+ *str = NULL;
+ return NULL;
+ }
+
+ for (d = delim; *d; d++) /* is source char a token char? */
+ {
+ if (c == *d) /* token char match? */
+ {
+ s++; /* advance to next source char */
+ break;
+ }
+ }
+ } while (*d); /* while token char match */
+
+ token = s; /* start of token found */
+
+ /* search for next token char or end of source string */
+ for (s = s+1; *s; s++)
+ {
+ c = *s;
+
+ for (d = delim; *d; d++) /* is source char a token char? */
+ {
+ if (c == *d) /* token char match? */
+ {
+ *s = '\0'; /* overwrite token char with zero byte to terminate token */
+ *str = s+1; /* update str to point to beginning of next token */
+ return token;
+ }
+ }
+ }
+
+ /* we get here only if source string ended */
+ *str = NULL;
+ return token;
+}
+
+/*
+ * fluid_error
+ */
+char*
+fluid_error()
+{
+ return fluid_errbuf;
+}
+
+/**
+ * Check if a file is a MIDI file.
+ * @param filename Path to the file to check
+ * @return TRUE if it could be a MIDI file, FALSE otherwise
+ *
+ * The current implementation only checks for the "MThd" header in the file.
+ * It is useful only to distinguish between SoundFont and MIDI files.
+ */
+int
+fluid_is_midifile(const char *filename)
+{
+ FILE* fp = fopen(filename, "rb");
+ char id[4];
+
+ if (fp == NULL) {
+ return 0;
+ }
+ if (fread((void*) id, 1, 4, fp) != 4) {
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+
+ return strncmp(id, "MThd", 4) == 0;
+}
+
+/**
+ * Check if a file is a SoundFont file.
+ * @param filename Path to the file to check
+ * @return TRUE if it could be a SoundFont, FALSE otherwise
+ *
+ * The current implementation only checks for the "RIFF" header in the file.
+ * It is useful only to distinguish between SoundFont and MIDI files.
+ */
+int
+fluid_is_soundfont(const char *filename)
+{
+ FILE* fp = fopen(filename, "rb");
+ char id[4];
+
+ if (fp == NULL) {
+ return 0;
+ }
+ if (fread((void*) id, 1, 4, fp) != 4) {
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+
+ return strncmp(id, "RIFF", 4) == 0;
+}
+
+/**
+ * Get time in milliseconds to be used in relative timing operations.
+ * @return Unix time in milliseconds.
+ */
+unsigned int fluid_curtime(void)
+{
+ static glong initial_seconds = 0;
+ GTimeVal timeval;
+
+ if (initial_seconds == 0) {
+ g_get_current_time (&timeval);
+ initial_seconds = timeval.tv_sec;
+ }
+
+ g_get_current_time (&timeval);
+
+ return (unsigned int)((timeval.tv_sec - initial_seconds) * 1000.0 + timeval.tv_usec / 1000.0);
+}
+
+/**
+ * Get time in microseconds to be used in relative timing operations.
+ * @return Unix time in microseconds.
+ */
+double
+fluid_utime (void)
+{
+ GTimeVal timeval;
+
+ g_get_current_time (&timeval);
+
+ return (timeval.tv_sec * 1000000.0 + timeval.tv_usec);
+}
+
+
+#if defined(WIN32) /* Windoze specific stuff */
+
+void
+fluid_thread_self_set_prio (int prio_level)
+{
+ if (prio_level > 0)
+ SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+}
+
+
+#elif defined(__OS2__) /* OS/2 specific stuff */
+
+void
+fluid_thread_self_set_prio (int prio_level)
+{
+ if (prio_level > 0)
+ DosSetPriority (PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0);
+}
+
+#else /* POSIX stuff.. Nice POSIX.. Good POSIX. */
+
+void
+fluid_thread_self_set_prio (int prio_level)
+{
+ struct sched_param priority;
+
+ if (prio_level > 0)
+ {
+
+ memset(&priority, 0, sizeof(priority));
+ priority.sched_priority = prio_level;
+
+ if (pthread_setschedparam (pthread_self (), SCHED_FIFO, &priority) == 0) {
+ return;
+ }
+#ifdef DBUS_SUPPORT
+/* Try to gain high priority via rtkit */
+
+ if (fluid_rtkit_make_realtime(0, prio_level) == 0) {
+ return;
+ }
+#endif
+ FLUID_LOG(FLUID_WARN, "Failed to set thread to high priority");
+ }
+}
+
+#ifdef FPE_CHECK
+
+/***************************************************************
+ *
+ * Floating point exceptions
+ *
+ * The floating point exception functions were taken from Ircam's
+ * jMax source code. http://www.ircam.fr/jmax
+ *
+ * FIXME: check in config for i386 machine
+ *
+ * Currently not used. I leave the code here in case we want to pick
+ * this up again some time later.
+ */
+
+/* Exception flags */
+#define _FPU_STATUS_IE 0x001 /* Invalid Operation */
+#define _FPU_STATUS_DE 0x002 /* Denormalized Operand */
+#define _FPU_STATUS_ZE 0x004 /* Zero Divide */
+#define _FPU_STATUS_OE 0x008 /* Overflow */
+#define _FPU_STATUS_UE 0x010 /* Underflow */
+#define _FPU_STATUS_PE 0x020 /* Precision */
+#define _FPU_STATUS_SF 0x040 /* Stack Fault */
+#define _FPU_STATUS_ES 0x080 /* Error Summary Status */
+
+/* Macros for accessing the FPU status word. */
+
+/* get the FPU status */
+#define _FPU_GET_SW(sw) __asm__ ("fnstsw %0" : "=m" (*&sw))
+
+/* clear the FPU status */
+#define _FPU_CLR_SW() __asm__ ("fnclex" : : )
+
+/* Purpose:
+ * Checks, if the floating point unit has produced an exception, print a message
+ * if so and clear the exception.
+ */
+unsigned int fluid_check_fpe_i386(char* explanation)
+{
+ unsigned int s;
+
+ _FPU_GET_SW(s);
+ _FPU_CLR_SW();
+
+ s &= _FPU_STATUS_IE | _FPU_STATUS_DE | _FPU_STATUS_ZE | _FPU_STATUS_OE | _FPU_STATUS_UE;
+
+ if (s)
+ {
+ FLUID_LOG(FLUID_WARN, "FPE exception (before or in %s): %s%s%s%s%s", explanation,
+ (s & _FPU_STATUS_IE) ? "Invalid operation " : "",
+ (s & _FPU_STATUS_DE) ? "Denormal number " : "",
+ (s & _FPU_STATUS_ZE) ? "Zero divide " : "",
+ (s & _FPU_STATUS_OE) ? "Overflow " : "",
+ (s & _FPU_STATUS_UE) ? "Underflow " : "");
+ }
+
+ return s;
+}
+
+/* Purpose:
+ * Clear floating point exception.
+ */
+void fluid_clear_fpe_i386 (void)
+{
+ _FPU_CLR_SW();
+}
+
+#endif // ifdef FPE_CHECK
+
+
+#endif // #else (its POSIX)
+
+
+/***************************************************************
+ *
+ * Profiling (Linux, i586 only)
+ *
+ */
+
+#if WITH_PROFILING
+
+fluid_profile_data_t fluid_profile_data[] =
+{
+ { FLUID_PROF_WRITE, "fluid_synth_write_* ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK, "fluid_synth_one_block ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_CLEAR, "fluid_synth_one_block:clear ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_VOICE, "fluid_synth_one_block:one voice ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_VOICES, "fluid_synth_one_block:all voices", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_REVERB, "fluid_synth_one_block:reverb ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_CHORUS, "fluid_synth_one_block:chorus ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_VOICE_NOTE, "fluid_voice:note ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_VOICE_RELEASE, "fluid_voice:release ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_LAST, "last", 1e100, 0.0, 0.0, 0}
+};
+
+
+void fluid_profiling_print(void)
+{
+ int i;
+
+ printf("fluid_profiling_print\n");
+
+ FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)");
+
+ for (i = 0; i < FLUID_PROF_LAST; i++) {
+ if (fluid_profile_data[i].count > 0) {
+ FLUID_LOG(FLUID_INFO, "%s: %.3f/%.3f/%.3f",
+ fluid_profile_data[i].description,
+ fluid_profile_data[i].min,
+ fluid_profile_data[i].total / fluid_profile_data[i].count,
+ fluid_profile_data[i].max);
+ } else {
+ FLUID_LOG(FLUID_DBG, "%s: no profiling available", fluid_profile_data[i].description);
+ }
+ }
+}
+
+
+#endif /* WITH_PROFILING */
+
+
+
+/***************************************************************
+ *
+ * Threads
+ *
+ */
+
+#if OLD_GLIB_THREAD_API
+
+/* Rather than inline this one, we just declare it as a function, to prevent
+ * GCC warning about inline failure. */
+fluid_cond_t *
+new_fluid_cond (void)
+{
+ if (!g_thread_supported ()) g_thread_init (NULL);
+ return g_cond_new ();
+}
+
+#endif
+
+static gpointer
+fluid_thread_high_prio (gpointer data)
+{
+ fluid_thread_info_t *info = data;
+
+ fluid_thread_self_set_prio (info->prio_level);
+
+ info->func (info->data);
+ FLUID_FREE (info);
+
+ return NULL;
+}
+
+/**
+ * Create a new thread.
+ * @param func Function to execute in new thread context
+ * @param data User defined data to pass to func
+ * @param prio_level Priority level. If greater than 0 then high priority scheduling will
+ * be used, with the given priority level (used by pthreads only). 0 uses normal scheduling.
+ * @param detach If TRUE, 'join' does not work and the thread destroys itself when finished.
+ * @return New thread pointer or NULL on error
+ */
+fluid_thread_t *
+new_fluid_thread (const char *name, fluid_thread_func_t func, void *data, int prio_level, int detach)
+{
+ GThread *thread;
+ fluid_thread_info_t *info;
+ GError *err = NULL;
+
+ g_return_val_if_fail (func != NULL, NULL);
+
+#if OLD_GLIB_THREAD_API
+ /* Make sure g_thread_init has been called.
+ * FIXME - Probably not a good idea in a shared library,
+ * but what can we do *and* remain backwards compatible? */
+ if (!g_thread_supported ()) g_thread_init (NULL);
+#endif
+
+ if (prio_level > 0)
+ {
+ info = FLUID_NEW (fluid_thread_info_t);
+
+ if (!info)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ info->func = func;
+ info->data = data;
+ info->prio_level = prio_level;
+#if NEW_GLIB_THREAD_API
+ thread = g_thread_try_new (name, fluid_thread_high_prio, info, &err);
+#else
+ thread = g_thread_create (fluid_thread_high_prio, info, detach == FALSE, &err);
+#endif
+ }
+#if NEW_GLIB_THREAD_API
+ else thread = g_thread_try_new (name, (GThreadFunc)func, data, &err);
+#else
+ else thread = g_thread_create ((GThreadFunc)func, data, detach == FALSE, &err);
+#endif
+
+ if (!thread)
+ {
+ FLUID_LOG(FLUID_ERR, "Failed to create the thread: %s",
+ fluid_gerror_message (err));
+ g_clear_error (&err);
+ return NULL;
+ }
+
+#if NEW_GLIB_THREAD_API
+ if (detach) g_thread_unref (thread); // Release thread reference, if caller wants to detach
+#endif
+
+ return thread;
+}
+
+/**
+ * Frees data associated with a thread (does not actually stop thread).
+ * @param thread Thread to free
+ */
+void
+delete_fluid_thread(fluid_thread_t* thread)
+{
+ /* Threads free themselves when they quit, nothing to do */
+}
+
+/**
+ * Join a thread (wait for it to terminate).
+ * @param thread Thread to join
+ * @return FLUID_OK
+ */
+int
+fluid_thread_join(fluid_thread_t* thread)
+{
+ g_thread_join (thread);
+
+ return FLUID_OK;
+}
+
+
+void
+fluid_timer_run (void *data)
+{
+ fluid_timer_t *timer;
+ int count = 0;
+ int cont;
+ long start;
+ long delay;
+
+ timer = (fluid_timer_t *)data;
+
+ /* keep track of the start time for absolute positioning */
+ start = fluid_curtime ();
+
+ while (timer->cont)
+ {
+ cont = (*timer->callback)(timer->data, fluid_curtime() - start);
+
+ count++;
+ if (!cont) break;
+
+ /* to avoid incremental time errors, calculate the delay between
+ two callbacks bringing in the "absolute" time (count *
+ timer->msec) */
+ delay = (count * timer->msec) - (fluid_curtime() - start);
+ if (delay > 0) g_usleep (delay * 1000);
+ }
+
+ FLUID_LOG (FLUID_DBG, "Timer thread finished");
+
+ if (timer->auto_destroy)
+ FLUID_FREE (timer);
+
+ return;
+}
+
+fluid_timer_t*
+new_fluid_timer (int msec, fluid_timer_callback_t callback, void* data,
+ int new_thread, int auto_destroy, int high_priority)
+{
+ fluid_timer_t *timer;
+
+ timer = FLUID_NEW (fluid_timer_t);
+
+ if (timer == NULL)
+ {
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ timer->msec = msec;
+ timer->callback = callback;
+ timer->data = data;
+ timer->cont = TRUE ;
+ timer->thread = NULL;
+ timer->auto_destroy = auto_destroy;
+
+ if (new_thread)
+ {
+ timer->thread = new_fluid_thread ("timer", fluid_timer_run, timer, high_priority
+ ? FLUID_SYS_TIMER_HIGH_PRIO_LEVEL : 0, FALSE);
+ if (!timer->thread)
+ {
+ FLUID_FREE (timer);
+ return NULL;
+ }
+ }
+ else fluid_timer_run (timer); /* Run directly, instead of as a separate thread */
+
+ return timer;
+}
+
+int
+delete_fluid_timer (fluid_timer_t *timer)
+{
+ int auto_destroy = timer->auto_destroy;
+
+ timer->cont = 0;
+ fluid_timer_join (timer);
+
+ /* Shouldn't access timer now if auto_destroy enabled, since it has been destroyed */
+
+ if (!auto_destroy) FLUID_FREE (timer);
+
+ return FLUID_OK;
+}
+
+int
+fluid_timer_join (fluid_timer_t *timer)
+{
+ int auto_destroy;
+
+ if (timer->thread)
+ {
+ auto_destroy = timer->auto_destroy;
+ fluid_thread_join (timer->thread);
+
+ if (!auto_destroy) timer->thread = NULL;
+ }
+
+ return FLUID_OK;
+}
+
+
+/***************************************************************
+ *
+ * Sockets and I/O
+ *
+ */
+
+/**
+ * Get standard in stream handle.
+ * @return Standard in stream.
+ */
+fluid_istream_t
+fluid_get_stdin (void)
+{
+ return STDIN_FILENO;
+}
+
+/**
+ * Get standard output stream handle.
+ * @return Standard out stream.
+ */
+fluid_ostream_t
+fluid_get_stdout (void)
+{
+ return STDOUT_FILENO;
+}
+
+/**
+ * Read a line from an input stream.
+ * @return 0 if end-of-stream, -1 if error, non zero otherwise
+ */
+int
+fluid_istream_readline (fluid_istream_t in, fluid_ostream_t out, char* prompt,
+ char* buf, int len)
+{
+#if WITH_READLINE
+ if (in == fluid_get_stdin ())
+ {
+ char *line;
+
+ line = readline (prompt);
+
+ if (line == NULL)
+ return -1;
+
+ snprintf(buf, len, "%s", line);
+ buf[len - 1] = 0;
+
+ free(line);
+ return 1;
+ }
+ else
+#endif
+ {
+ fluid_ostream_printf (out, "%s", prompt);
+ return fluid_istream_gets (in, buf, len);
+ }
+}
+
+/**
+ * Reads a line from an input stream (socket).
+ * @param in The input socket
+ * @param buf Buffer to store data to
+ * @param len Maximum length to store to buf
+ * @return 1 if a line was read, 0 on end of stream, -1 on error
+ */
+static int
+fluid_istream_gets (fluid_istream_t in, char* buf, int len)
+{
+ char c;
+ int n;
+
+ buf[len - 1] = 0;
+
+ while (--len > 0)
+ {
+#ifndef WIN32
+ n = read(in, &c, 1);
+ if (n == -1) return -1;
+#else
+ /* Handle read differently depending on if its a socket or file descriptor */
+ if (!(in & WIN32_SOCKET_FLAG))
+ {
+ n = read(in, &c, 1);
+ if (n == -1) return -1;
+ }
+ else
+ {
+ n = recv(in & ~WIN32_SOCKET_FLAG, &c, 1, 0);
+ if (n == SOCKET_ERROR) return -1;
+ }
+#endif
+
+ if (n == 0)
+ {
+ *buf++ = 0;
+ return 0;
+ }
+
+ if ((c == '\n'))
+ {
+ *buf++ = 0;
+ return 1;
+ }
+
+ /* Store all characters excluding CR */
+ if (c != '\r') *buf++ = c;
+ }
+
+ return -1;
+}
+
+/**
+ * Send a printf style string with arguments to an output stream (socket).
+ * @param out Output stream
+ * @param format printf style format string
+ * @param ... Arguments for the printf format string
+ * @return Number of bytes written or -1 on error
+ */
+int
+fluid_ostream_printf (fluid_ostream_t out, char* format, ...)
+{
+ char buf[4096];
+ va_list args;
+ int len;
+
+ va_start (args, format);
+ len = vsnprintf (buf, 4095, format, args);
+ va_end (args);
+
+ if (len == 0)
+ {
+ return 0;
+ }
+
+ if (len < 0)
+ {
+ printf("fluid_ostream_printf: buffer overflow");
+ return -1;
+ }
+
+ buf[4095] = 0;
+
+#ifndef WIN32
+ return write (out, buf, strlen (buf));
+#else
+ {
+ int retval;
+
+ /* Handle write differently depending on if its a socket or file descriptor */
+ if (!(out & WIN32_SOCKET_FLAG))
+ return write(out, buf, strlen (buf));
+
+ /* Socket */
+ retval = send (out & ~WIN32_SOCKET_FLAG, buf, strlen (buf), 0);
+
+ return retval != SOCKET_ERROR ? retval : -1;
+ }
+#endif
+}
+
+int fluid_server_socket_join(fluid_server_socket_t *server_socket)
+{
+ return fluid_thread_join (server_socket->thread);
+}
+
+
+#ifndef WIN32 // Not win32?
+
+#define SOCKET_ERROR -1
+
+fluid_istream_t fluid_socket_get_istream (fluid_socket_t sock)
+{
+ return sock;
+}
+
+fluid_ostream_t fluid_socket_get_ostream (fluid_socket_t sock)
+{
+ return sock;
+}
+
+void fluid_socket_close(fluid_socket_t sock)
+{
+ if (sock != INVALID_SOCKET)
+ close (sock);
+}
+/*
+static void
+fluid_server_socket_run (void *data)
+{
+ fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data;
+ fluid_socket_t client_socket;
+#ifdef IPV6
+ struct sockaddr_in6 addr;
+ char straddr[INET6_ADDRSTRLEN];
+#else
+ struct sockaddr_in addr;
+ char straddr[INET_ADDRSTRLEN];
+#endif
+ socklen_t addrlen = sizeof (addr);
+ int retval;
+ FLUID_MEMSET((char *)&addr, 0, sizeof(addr));
+
+ FLUID_LOG (FLUID_DBG, "Server listening for connections");
+
+ while (server_socket->cont)
+ {
+ client_socket = accept (server_socket->socket, (struct sockaddr *)&addr, &addrlen);
+
+ FLUID_LOG (FLUID_DBG, "New client connection");
+
+ if (client_socket == INVALID_SOCKET)
+ {
+ if (server_socket->cont)
+ FLUID_LOG(FLUID_ERR, "Failed to accept connection");
+
+ server_socket->cont = 0;
+ return;
+ } else {
+#ifdef IPV6
+ inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr));
+#else
+ inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr));
+#endif
+ retval = server_socket->func (server_socket->data, client_socket,
+ straddr);
+
+ if (retval != 0)
+ fluid_socket_close(client_socket);
+ }
+ }
+
+ FLUID_LOG(FLUID_DBG, "Server closing");
+}
+/*
+fluid_server_socket_t*
+new_fluid_server_socket(int port, fluid_server_func_t func, void* data)
+{
+ fluid_server_socket_t* server_socket;
+#ifdef IPV6
+ struct sockaddr_in6 addr;
+#else
+ struct sockaddr_in addr;
+#endif
+ fluid_socket_t sock;
+
+ g_return_val_if_fail (func != NULL, NULL);
+#ifdef IPV6
+ sock = socket(AF_INET6, SOCK_STREAM, 0);
+ if (sock == INVALID_SOCKET) {
+ FLUID_LOG(FLUID_ERR, "Failed to create server socket");
+ return NULL;
+ }
+
+ FLUID_MEMSET((char *)&addr, 0, sizeof(struct sockaddr_in6));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_addr = in6addr_any;
+ addr.sin6_port = htons(port);
+#else
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock == INVALID_SOCKET) {
+ FLUID_LOG(FLUID_ERR, "Failed to create server socket");
+ return NULL;
+ }
+
+ FLUID_MEMSET((char *)&addr, 0, sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(port);
+#endif
+ if (bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+ FLUID_LOG(FLUID_ERR, "Failed to bind server socket");
+ fluid_socket_close(sock);
+ return NULL;
+ }
+
+ if (listen(sock, 10) == SOCKET_ERROR) {
+ FLUID_LOG(FLUID_ERR, "Failed listen on server socket");
+ fluid_socket_close(sock);
+ return NULL;
+ }
+
+ server_socket = FLUID_NEW(fluid_server_socket_t);
+ if (server_socket == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ fluid_socket_close(sock);
+ return NULL;
+ }
+
+ server_socket->socket = sock;
+ server_socket->func = func;
+ server_socket->data = data;
+ server_socket->cont = 1;
+
+ server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket,
+ 0, FALSE);
+ if (server_socket->thread == NULL) {
+ FLUID_FREE(server_socket);
+ fluid_socket_close(sock);
+ return NULL;
+ }
+
+ return server_socket;
+}
+
+int delete_fluid_server_socket(fluid_server_socket_t* server_socket)
+{
+ server_socket->cont = 0;
+ if (server_socket->socket != INVALID_SOCKET) {
+ fluid_socket_close(server_socket->socket);
+ }
+ if (server_socket->thread) {
+ delete_fluid_thread(server_socket->thread);
+ }
+ FLUID_FREE(server_socket);
+ return FLUID_OK;
+}
+*/
+
+#else // Win32 is "special"
+
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+fluid_istream_t fluid_socket_get_istream (fluid_socket_t sock)
+{
+ return sock | WIN32_SOCKET_FLAG;
+}
+
+fluid_ostream_t fluid_socket_get_ostream (fluid_socket_t sock)
+{
+ return sock | WIN32_SOCKET_FLAG;
+}
+
+void fluid_socket_close (fluid_socket_t sock)
+{
+ if (sock != INVALID_SOCKET)
+ closesocket (sock);
+}
+
+static void fluid_server_socket_run (void *data)
+{
+ fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data;
+ fluid_socket_t client_socket;
+#ifdef IPV6
+ struct sockaddr_in6 addr;
+ char straddr[INET6_ADDRSTRLEN];
+#else
+ struct sockaddr_in addr;
+ char straddr[INET_ADDRSTRLEN];
+#endif
+ socklen_t addrlen = sizeof (addr);
+ int r;
+ FLUID_MEMSET((char *)&addr, 0, sizeof(addr));
+
+ FLUID_LOG(FLUID_DBG, "Server listening for connections");
+
+ while (server_socket->cont)
+ {
+ client_socket = accept (server_socket->socket, (struct sockaddr *)&addr, &addrlen);
+
+ FLUID_LOG (FLUID_DBG, "New client connection");
+
+ if (client_socket == INVALID_SOCKET)
+ {
+ if (server_socket->cont)
+ FLUID_LOG (FLUID_ERR, "Failed to accept connection: %ld", WSAGetLastError ());
+
+ server_socket->cont = 0;
+ return;
+ }
+ else
+ {
+#ifdef IPV6
+ inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr));
+#else
+ inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr));
+#endif
+ r = server_socket->func (server_socket->data, client_socket,
+ straddr);
+ if (r != 0)
+ fluid_socket_close (client_socket);
+ }
+ }
+
+ FLUID_LOG (FLUID_DBG, "Server closing");
+}
+
+fluid_server_socket_t*
+new_fluid_server_socket(int port, fluid_server_func_t func, void* data)
+{
+ fluid_server_socket_t* server_socket;
+#ifdef IPV6
+ struct sockaddr_in6 addr;
+#else
+ struct sockaddr_in addr;
+#endif
+
+ fluid_socket_t sock;
+ WSADATA wsaData;
+ int retval;
+
+ g_return_val_if_fail (func != NULL, NULL);
+
+ // Win32 requires initialization of winsock
+ retval = WSAStartup (MAKEWORD (2,2), &wsaData);
+
+ if (retval != 0)
+ {
+ FLUID_LOG(FLUID_ERR, "Server socket creation error: WSAStartup failed: %d", retval);
+ return NULL;
+ }
+#ifdef IPV6
+ sock = socket (AF_INET6, SOCK_STREAM, 0);
+ if (sock == INVALID_SOCKET)
+ {
+ FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", WSAGetLastError ());
+ WSACleanup ();
+ return NULL;
+ }
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons (port);
+ addr.sin6_addr = in6addr_any;
+#else
+
+ sock = socket (AF_INET, SOCK_STREAM, 0);
+
+ if (sock == INVALID_SOCKET)
+ {
+ FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", WSAGetLastError ());
+ WSACleanup ();
+ return NULL;
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons (port);
+ addr.sin_addr.s_addr = htonl (INADDR_ANY);
+#endif
+ retval = bind (sock, (struct sockaddr *)&addr, sizeof (addr));
+
+ if (retval == SOCKET_ERROR)
+ {
+ FLUID_LOG (FLUID_ERR, "Failed to bind server socket: %ld", WSAGetLastError ());
+ fluid_socket_close (sock);
+ WSACleanup ();
+ return NULL;
+ }
+
+ if (listen (sock, SOMAXCONN) == SOCKET_ERROR)
+ {
+ FLUID_LOG (FLUID_ERR, "Failed to listen on server socket: %ld", WSAGetLastError ());
+ fluid_socket_close (sock);
+ WSACleanup ();
+ return NULL;
+ }
+
+ server_socket = FLUID_NEW (fluid_server_socket_t);
+
+ if (server_socket == NULL)
+ {
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ fluid_socket_close (sock);
+ WSACleanup ();
+ return NULL;
+ }
+
+ server_socket->socket = sock;
+ server_socket->func = func;
+ server_socket->data = data;
+ server_socket->cont = 1;
+
+ server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket,
+ 0, FALSE);
+ if (server_socket->thread == NULL)
+ {
+ FLUID_FREE (server_socket);
+ fluid_socket_close (sock);
+ WSACleanup ();
+ return NULL;
+ }
+
+ return server_socket;
+}
+
+int delete_fluid_server_socket(fluid_server_socket_t *server_socket)
+{
+ server_socket->cont = 0;
+
+ if (server_socket->socket != INVALID_SOCKET)
+ fluid_socket_close (server_socket->socket);
+
+ if (server_socket->thread)
+ delete_fluid_thread (server_socket->thread);
+
+ FLUID_FREE (server_socket);
+
+ WSACleanup (); // Should be called the same number of times as WSAStartup
+
+ return FLUID_OK;
+}
+
+#endif
diff --git a/src/midi/old/fluid_sys.h b/src/midi/old/fluid_sys.h
new file mode 100644
index 0000000..de31cc4
--- /dev/null
+++ b/src/midi/old/fluid_sys.h
@@ -0,0 +1,448 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+/**
+
+ This header contains a bunch of (mostly) system and machine
+ dependent functions:
+
+ - timers
+ - current time in milliseconds and microseconds
+ - debug logging
+ - profiling
+ - memory locking
+ - checking for floating point exceptions
+
+ */
+
+#ifndef _FLUID_SYS_H
+#define _FLUID_SYS_H
+
+//#include <glib.h>
+#include "fluidsynth_priv.h"
+
+
+/**
+ * Macro used for safely accessing a message from a GError and using a default
+ * message if it is NULL.
+ * @param err Pointer to a GError to access the message field of.
+ * @return Message string
+ */
+#define fluid_gerror_message(err) ((err) ? err->message : "No error details")
+
+
+void fluid_sys_config(void);
+void fluid_log_config(void);
+void fluid_time_config(void);
+
+
+/* Misc */
+
+#define fluid_return_val_if_fail g_return_val_if_fail
+#define fluid_return_if_fail g_return_if_fail
+#define FLUID_INLINE inline
+#define FLUID_POINTER_TO_UINT GPOINTER_TO_UINT
+#define FLUID_UINT_TO_POINTER GUINT_TO_POINTER
+#define FLUID_POINTER_TO_INT GPOINTER_TO_INT
+#define FLUID_INT_TO_POINTER GINT_TO_POINTER
+#define FLUID_N_ELEMENTS(struct) (sizeof (struct) / sizeof (struct[0]))
+
+#define FLUID_IS_BIG_ENDIAN (G_BYTE_ORDER == G_BIG_ENDIAN)
+
+/*
+ * Utility functions
+ */
+char *fluid_strtok (char **str, char *delim);
+
+
+/**
+
+ Additional debugging system, separate from the log system. This
+ allows to print selected debug messages of a specific subsystem.
+ */
+
+extern unsigned int fluid_debug_flags;
+
+#if DEBUG
+
+enum fluid_debug_level {
+ FLUID_DBG_DRIVER = 1
+};
+
+int fluid_debug(int level, char * fmt, ...);
+
+#else
+#define fluid_debug
+#endif
+
+
+#if defined(__OS2__)
+#define INCL_DOS
+#include <os2.h>
+
+typedef int socklen_t;
+#endif
+
+unsigned int fluid_curtime(void);
+double fluid_utime(void);
+
+
+/**
+ Timers
+
+ */
+
+/* if the callback function returns 1 the timer will continue; if it
+ returns 0 it will stop */
+typedef int (*fluid_timer_callback_t)(void* data, unsigned int msec);
+
+typedef struct _fluid_timer_t fluid_timer_t;
+
+fluid_timer_t* new_fluid_timer(int msec, fluid_timer_callback_t callback,
+ void* data, int new_thread, int auto_destroy,
+ int high_priority);
+
+int delete_fluid_timer(fluid_timer_t* timer);
+int fluid_timer_join(fluid_timer_t* timer);
+int fluid_timer_stop(fluid_timer_t* timer);
+
+// Macros to use for pre-processor if statements to test which Glib thread API we have (pre or post 2.32)
+#define NEW_GLIB_THREAD_API (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 32))
+#define OLD_GLIB_THREAD_API (GLIB_MAJOR_VERSION < 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32))
+
+/* Muteces */
+
+#if NEW_GLIB_THREAD_API
+
+/* glib 2.32 and newer */
+
+/* Regular mutex */
+typedef GMutex fluid_mutex_t;
+#define FLUID_MUTEX_INIT { 0 }
+#define fluid_mutex_init(_m) g_mutex_init (&(_m))
+#define fluid_mutex_destroy(_m) g_mutex_clear (&(_m))
+#define fluid_mutex_lock(_m) g_mutex_lock(&(_m))
+#define fluid_mutex_unlock(_m) g_mutex_unlock(&(_m))
+
+/* Recursive lock capable mutex */
+typedef GRecMutex fluid_rec_mutex_t;
+#define fluid_rec_mutex_init(_m) g_rec_mutex_init(&(_m))
+#define fluid_rec_mutex_destroy(_m) g_rec_mutex_clear(&(_m))
+#define fluid_rec_mutex_lock(_m) g_rec_mutex_lock(&(_m))
+#define fluid_rec_mutex_unlock(_m) g_rec_mutex_unlock(&(_m))
+
+/* Dynamically allocated mutex suitable for fluid_cond_t use */
+typedef GMutex fluid_cond_mutex_t;
+#define fluid_cond_mutex_lock(m) g_mutex_lock(m)
+#define fluid_cond_mutex_unlock(m) g_mutex_unlock(m)
+
+static FLUID_INLINE fluid_cond_mutex_t *
+new_fluid_cond_mutex (void)
+{
+ GMutex *mutex;
+ mutex = g_new (GMutex, 1);
+ g_mutex_init (mutex);
+ return (mutex);
+}
+
+static FLUID_INLINE void
+delete_fluid_cond_mutex (fluid_cond_mutex_t *m)
+{
+ g_mutex_clear (m);
+ g_free (m);
+}
+
+/* Thread condition signaling */
+typedef GCond fluid_cond_t;
+#define fluid_cond_signal(cond) g_cond_signal(cond)
+#define fluid_cond_broadcast(cond) g_cond_broadcast(cond)
+#define fluid_cond_wait(cond, mutex) g_cond_wait(cond, mutex)
+
+static FLUID_INLINE fluid_cond_t *
+new_fluid_cond (void)
+{
+ GCond *cond;
+ cond = g_new (GCond, 1);
+ g_cond_init (cond);
+ return (cond);
+}
+
+static FLUID_INLINE void
+delete_fluid_cond (fluid_cond_t *cond)
+{
+ g_cond_clear (cond);
+ g_free (cond);
+}
+
+/* Thread private data */
+
+typedef GPrivate fluid_private_t;
+#define fluid_private_init(_priv) memset (&_priv, 0, sizeof (_priv))
+#define fluid_private_free(_priv)
+#define fluid_private_get(_priv) g_private_get(&(_priv))
+#define fluid_private_set(_priv, _data) g_private_set(&(_priv), _data)
+
+#else
+
+/* glib prior to 2.32 */
+
+/* Regular mutex */
+typedef GStaticMutex fluid_mutex_t;
+#define FLUID_MUTEX_INIT G_STATIC_MUTEX_INIT
+#define fluid_mutex_destroy(_m) g_static_mutex_free(&(_m))
+#define fluid_mutex_lock(_m) g_static_mutex_lock(&(_m))
+#define fluid_mutex_unlock(_m) g_static_mutex_unlock(&(_m))
+
+#define fluid_mutex_init(_m) G_STMT_START { \
+ if (!g_thread_supported ()) g_thread_init (NULL); \
+ g_static_mutex_init (&(_m)); \
+} G_STMT_END;
+
+/* Recursive lock capable mutex */
+typedef GStaticRecMutex fluid_rec_mutex_t;
+#define fluid_rec_mutex_destroy(_m) g_static_rec_mutex_free(&(_m))
+#define fluid_rec_mutex_lock(_m) g_static_rec_mutex_lock(&(_m))
+#define fluid_rec_mutex_unlock(_m) g_static_rec_mutex_unlock(&(_m))
+
+#define fluid_rec_mutex_init(_m) G_STMT_START { \
+ if (!g_thread_supported ()) g_thread_init (NULL); \
+ g_static_rec_mutex_init (&(_m)); \
+} G_STMT_END;
+
+/* Dynamically allocated mutex suitable for fluid_cond_t use */
+typedef GMutex fluid_cond_mutex_t;
+#define delete_fluid_cond_mutex(m) g_mutex_free(m)
+#define fluid_cond_mutex_lock(m) g_mutex_lock(m)
+#define fluid_cond_mutex_unlock(m) g_mutex_unlock(m)
+
+static FLUID_INLINE fluid_cond_mutex_t *
+new_fluid_cond_mutex (void)
+{
+ if (!g_thread_supported ()) g_thread_init (NULL);
+ return g_mutex_new ();
+}
+
+/* Thread condition signaling */
+typedef GCond fluid_cond_t;
+fluid_cond_t *new_fluid_cond (void);
+#define delete_fluid_cond(cond) g_cond_free(cond)
+#define fluid_cond_signal(cond) g_cond_signal(cond)
+#define fluid_cond_broadcast(cond) g_cond_broadcast(cond)
+#define fluid_cond_wait(cond, mutex) g_cond_wait(cond, mutex)
+
+/* Thread private data */
+typedef GStaticPrivate fluid_private_t;
+#define fluid_private_get(_priv) g_static_private_get(&(_priv))
+#define fluid_private_set(_priv, _data) g_static_private_set(&(_priv), _data, NULL)
+#define fluid_private_free(_priv) g_static_private_free(&(_priv))
+
+#define fluid_private_init(_priv) G_STMT_START { \
+ if (!g_thread_supported ()) g_thread_init (NULL); \
+ g_static_private_init (&(_priv)); \
+} G_STMT_END;
+
+#endif
+
+
+/* Atomic operations */
+
+#define fluid_atomic_int_inc(_pi) g_atomic_int_inc(_pi)
+#define fluid_atomic_int_add(_pi, _val) g_atomic_int_add(_pi, _val)
+#define fluid_atomic_int_get(_pi) g_atomic_int_get(_pi)
+#define fluid_atomic_int_set(_pi, _val) g_atomic_int_set(_pi, _val)
+#define fluid_atomic_int_dec_and_test(_pi) g_atomic_int_dec_and_test(_pi)
+#define fluid_atomic_int_compare_and_exchange(_pi, _old, _new) \
+ g_atomic_int_compare_and_exchange(_pi, _old, _new)
+
+#if GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 30)
+#define fluid_atomic_int_exchange_and_add(_pi, _add) \
+ g_atomic_int_add(_pi, _add)
+#else
+#define fluid_atomic_int_exchange_and_add(_pi, _add) \
+ g_atomic_int_exchange_and_add(_pi, _add)
+#endif
+
+#define fluid_atomic_pointer_get(_pp) g_atomic_pointer_get(_pp)
+#define fluid_atomic_pointer_set(_pp, val) g_atomic_pointer_set(_pp, val)
+#define fluid_atomic_pointer_compare_and_exchange(_pp, _old, _new) \
+ g_atomic_pointer_compare_and_exchange(_pp, _old, _new)
+
+static FLUID_INLINE void
+fluid_atomic_float_set(volatile float *fptr, float val)
+{
+ sint32_t ival;
+ memcpy (&ival, &val, 4);
+ fluid_atomic_int_set ((volatile int *)fptr, ival);
+}
+
+static FLUID_INLINE float
+fluid_atomic_float_get(volatile float *fptr)
+{
+ sint32_t ival;
+ float fval;
+ ival = fluid_atomic_int_get ((volatile int *)fptr);
+ memcpy (&fval, &ival, 4);
+ return fval;
+}
+
+
+/* Threads */
+
+typedef GThread fluid_thread_t;
+typedef void (*fluid_thread_func_t)(void* data);
+
+#define FLUID_THREAD_ID_NULL NULL /* A NULL "ID" value */
+#define fluid_thread_id_t GThread * /* Data type for a thread ID */
+#define fluid_thread_get_id() g_thread_self() /* Get unique "ID" for current thread */
+
+fluid_thread_t* new_fluid_thread(const char *name, fluid_thread_func_t func, void *data,
+ int prio_level, int detach);
+void delete_fluid_thread(fluid_thread_t* thread);
+void fluid_thread_self_set_prio (int prio_level);
+int fluid_thread_join(fluid_thread_t* thread);
+
+/* Sockets and I/O */
+
+fluid_istream_t fluid_get_stdin (void);
+fluid_ostream_t fluid_get_stdout (void);
+int fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char* prompt, char* buf, int len);
+int fluid_ostream_printf (fluid_ostream_t out, char* format, ...);
+
+/* The function should return 0 if no error occured, non-zero
+ otherwise. If the function return non-zero, the socket will be
+ closed by the server. */
+typedef int (*fluid_server_func_t)(void* data, fluid_socket_t client_socket, char* addr);
+
+fluid_server_socket_t* new_fluid_server_socket(int port, fluid_server_func_t func, void* data);
+int delete_fluid_server_socket(fluid_server_socket_t* sock);
+int fluid_server_socket_join(fluid_server_socket_t* sock);
+void fluid_socket_close(fluid_socket_t sock);
+fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock);
+fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock);
+
+
+
+/* Profiling */
+
+
+/**
+ * Profile numbers. List all the pieces of code you want to profile
+ * here. Be sure to add an entry in the fluid_profile_data table in
+ * fluid_sys.c
+ */
+enum {
+ FLUID_PROF_WRITE,
+ FLUID_PROF_ONE_BLOCK,
+ FLUID_PROF_ONE_BLOCK_CLEAR,
+ FLUID_PROF_ONE_BLOCK_VOICE,
+ FLUID_PROF_ONE_BLOCK_VOICES,
+ FLUID_PROF_ONE_BLOCK_REVERB,
+ FLUID_PROF_ONE_BLOCK_CHORUS,
+ FLUID_PROF_VOICE_NOTE,
+ FLUID_PROF_VOICE_RELEASE,
+ FLUID_PROF_LAST
+};
+
+
+#if WITH_PROFILING
+
+void fluid_profiling_print(void);
+
+
+/** Profiling data. Keep track of min/avg/max values to execute a
+ piece of code. */
+typedef struct _fluid_profile_data_t {
+ int num;
+ char* description;
+ double min, max, total;
+ unsigned int count;
+} fluid_profile_data_t;
+
+extern fluid_profile_data_t fluid_profile_data[];
+
+/** Macro to obtain a time refence used for the profiling */
+#define fluid_profile_ref() fluid_utime()
+
+/** Macro to create a variable and assign the current reference time for profiling.
+ * So we don't get unused variable warnings when profiling is disabled. */
+#define fluid_profile_ref_var(name) double name = fluid_utime()
+
+/** Macro to calculate the min/avg/max. Needs a time refence and a
+ profile number. */
+#define fluid_profile(_num,_ref) { \
+ double _now = fluid_utime(); \
+ double _delta = _now - _ref; \
+ fluid_profile_data[_num].min = _delta < fluid_profile_data[_num].min ? _delta : fluid_profile_data[_num].min; \
+ fluid_profile_data[_num].max = _delta > fluid_profile_data[_num].max ? _delta : fluid_profile_data[_num].max; \
+ fluid_profile_data[_num].total += _delta; \
+ fluid_profile_data[_num].count++; \
+ _ref = _now; \
+}
+
+
+#else
+
+/* No profiling */
+#define fluid_profiling_print()
+#define fluid_profile_ref() 0
+#define fluid_profile_ref_var(name)
+#define fluid_profile(_num,_ref)
+
+#endif
+
+
+
+/**
+
+ Memory locking
+
+ Memory locking is used to avoid swapping of the large block of
+ sample data.
+ */
+
+#if defined(HAVE_SYS_MMAN_H) && !defined(__OS2__)
+#define fluid_mlock(_p,_n) mlock(_p, _n)
+#define fluid_munlock(_p,_n) munlock(_p,_n)
+#else
+#define fluid_mlock(_p,_n) 0
+#define fluid_munlock(_p,_n)
+#endif
+
+
+/**
+
+ Floating point exceptions
+
+ fluid_check_fpe() checks for "unnormalized numbers" and other
+ exceptions of the floating point processsor.
+*/
+#ifdef FPE_CHECK
+#define fluid_check_fpe(expl) fluid_check_fpe_i386(expl)
+#define fluid_clear_fpe() fluid_clear_fpe_i386()
+#else
+#define fluid_check_fpe(expl)
+#define fluid_clear_fpe()
+#endif
+
+unsigned int fluid_check_fpe_i386(char * explanation_in_case_of_fpe);
+void fluid_clear_fpe_i386(void);
+
+#endif /* _FLUID_SYS_H */
diff --git a/src/output.wav b/src/output.wav
new file mode 100644
index 0000000..203d64e
--- /dev/null
+++ b/src/output.wav
Binary files differ
diff --git a/src/readmidi.c b/src/readmidi.c
new file mode 100644
index 0000000..125d80d
--- /dev/null
+++ b/src/readmidi.c
@@ -0,0 +1,6294 @@
+/*
+ TiMidity++ -- MIDI to WAVE converter and player
+ Copyright (C) 1999-2009 Masanao Izumo <iz@onicos.co.jp>
+ Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+#ifdef __POCC__
+#include <sys/types.h>
+#endif //for off_t
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef NO_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+//#include "timidity.h"
+#include "common.h"
+//#include "instrum.h"
+//#include "playmidi.h"
+#include "readmidi.h"
+#include "output.h"
+//#include "controls.h"
+#include "strtab.h"//string table
+#include "memb.h" //utils
+//#include "mod.h"
+//#include "wrd.h"
+#include "tables.h"
+//#include "reverb.h"
+#include <math.h>
+
+/* rcp.c */
+//any idea what rcp might stand for? not sure, might be some other lib man...I don't know whether this is going to be
+//possible, there are so many linked files here well need to dig this thourgh
+//well in meantime i checked fluidsynth and half way made it to compiling it, so it compiles but doesn't link yet because depend on log functions, which can be probably replaced with something else ok I guess we should use that
+int read_rcp_file(struct timidity_file *tf, char *magic0, char *fn);
+
+/* mld.c */
+extern int read_mfi_file(struct timidity_file *tf);
+extern char *get_mfi_file_title(struct timidity_file *tf);
+
+#define MAX_MIDI_EVENT ((MAX_SAFE_MALLOC_SIZE / sizeof(MidiEvent)) - 1)
+#define MARKER_START_CHAR '('
+#define MARKER_END_CHAR ')'
+
+static uint8 rhythm_part[2]; /* for GS */
+static uint8 drum_setup_xg[16] = { 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9 }; /* for XG */
+
+enum
+{
+ CHORUS_ST_NOT_OK = 0,
+ CHORUS_ST_OK
+};
+
+#ifdef ALWAYS_TRACE_TEXT_META_EVENT
+int opt_trace_text_meta_event = 1;
+#else
+int opt_trace_text_meta_event = 0;
+#endif /* ALWAYS_TRACE_TEXT_META_EVENT */
+
+int opt_default_mid = 0;
+int opt_system_mid = 0;
+int ignore_midi_error = 1;
+ChannelBitMask quietchannels;
+struct midi_file_info *current_file_info = NULL;
+int readmidi_error_flag = 0;
+int readmidi_wrd_mode = 0;
+int play_system_mode = DEFAULT_SYSTEM_MODE;
+
+/* Mingw gcc3 and Borland C hack */
+/* If these are not NULL initialized cause Hang up */
+/* why ? I dont know. (Keishi Suenaga) */
+static MidiEventList *evlist=NULL, *current_midi_point=NULL;
+static int32 event_count;
+static MBlockList mempool;
+static StringTable string_event_strtab = { 0 };
+static int current_read_track;
+static int karaoke_format, karaoke_title_flag;
+static struct midi_file_info *midi_file_info = NULL;
+static char **string_event_table = NULL;
+static int string_event_table_size = 0;
+int default_channel_program[256];
+static MidiEvent timesig[256];
+
+void init_delay_status_gs(void);
+void init_chorus_status_gs(void);
+void init_reverb_status_gs(void);
+void init_eq_status_gs(void);
+void init_insertion_effect_gs(void);
+void init_multi_eq_xg(void);
+static void init_all_effect_xg(void);
+
+/* MIDI ports will be merged in several channels in the future. */
+int midi_port_number;
+
+/* These would both fit into 32 bits, but they are often added in
+ large multiples, so it's simpler to have two roomy ints */
+static int32 sample_increment, sample_correction; /*samples per MIDI delta-t*/
+
+#define SETMIDIEVENT(e, at, t, ch, pa, pb) \
+ { (e).time = (at); (e).type = (t); \
+ (e).channel = (uint8)(ch); (e).a = (uint8)(pa); (e).b = (uint8)(pb); }
+
+#define MIDIEVENT(at, t, ch, pa, pb) \
+ { MidiEvent event; SETMIDIEVENT(event, at, t, ch, pa, pb); \
+ readmidi_add_event(&event); }
+
+#if MAX_CHANNELS <= 16
+#define MERGE_CHANNEL_PORT(ch) ((int)(ch))
+#define MERGE_CHANNEL_PORT2(ch, port) ((int)(ch))
+#else
+#define MERGE_CHANNEL_PORT(ch) ((int)(ch) | (midi_port_number << 4))
+#define MERGE_CHANNEL_PORT2(ch, port) ((int)(ch) | ((int)port << 4))
+#endif
+
+#define alloc_midi_event() \
+ (MidiEventList *)new_segment(&mempool, sizeof(MidiEventList))
+
+typedef struct _UserDrumset {
+ int8 bank;
+ int8 prog;
+ int8 play_note;
+ int8 level;
+ int8 assign_group;
+ int8 pan;
+ int8 reverb_send_level;
+ int8 chorus_send_level;
+ int8 rx_note_off;
+ int8 rx_note_on;
+ int8 delay_send_level;
+ int8 source_map;
+ int8 source_prog;
+ int8 source_note;
+ struct _UserDrumset *next;
+} UserDrumset;
+
+UserDrumset *userdrum_first = (UserDrumset *)NULL;
+UserDrumset *userdrum_last = (UserDrumset *)NULL;
+
+void init_userdrum();
+UserDrumset *get_userdrum(int bank, int prog);
+void recompute_userdrum_altassign(int bank,int group);
+
+typedef struct _UserInstrument {
+ int8 bank;
+ int8 prog;
+ int8 source_map;
+ int8 source_bank;
+ int8 source_prog;
+ int8 vibrato_rate;
+ int8 vibrato_depth;
+ int8 cutoff_freq;
+ int8 resonance;
+ int8 env_attack;
+ int8 env_decay;
+ int8 env_release;
+ int8 vibrato_delay;
+ struct _UserInstrument *next;
+} UserInstrument;
+
+UserInstrument *userinst_first = (UserInstrument *)NULL;
+UserInstrument *userinst_last = (UserInstrument *)NULL;
+
+void init_userinst();
+UserInstrument *get_userinst(int bank, int prog);
+void recompute_userinst(int bank, int prog);
+void recompute_userinst_altassign(int bank,int group);
+
+int32 readmidi_set_track(int trackno, int rewindp)
+{
+ current_read_track = trackno;
+ memset(&chorus_status_gs.text, 0, sizeof(struct chorus_text_gs_t));
+ if(karaoke_format == 1 && current_read_track == 2)
+ karaoke_format = 2; /* Start karaoke lyric */
+ else if(karaoke_format == 2 && current_read_track == 3)
+ karaoke_format = 3; /* End karaoke lyric */
+ midi_port_number = 0;
+
+ if(evlist == NULL)
+ return 0;
+ if(rewindp)
+ current_midi_point = evlist;
+ else
+ {
+ /* find the last event in the list */
+ while(current_midi_point->next != NULL)
+ current_midi_point = current_midi_point->next;
+ }
+ return current_midi_point->event.time;
+}
+
+void readmidi_add_event(MidiEvent *a_event)
+{
+ MidiEventList *newev;
+ int32 at;
+
+ if(event_count == MAX_MIDI_EVENT)
+ {
+ if(!readmidi_error_flag)
+ {
+ readmidi_error_flag = 1;
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "Maxmum number of events is exceeded");
+ }
+ return;
+ }
+ event_count++;
+
+ at = a_event->time;
+ newev = alloc_midi_event();
+ newev->event = *a_event; /* assign by value!!! */
+ if(at < 0) /* for safety */
+ at = newev->event.time = 0;
+
+ if(at >= current_midi_point->event.time)
+ {
+ /* Forward scan */
+ MidiEventList *next = current_midi_point->next;
+ while (next && (next->event.time <= at))
+ {
+ current_midi_point = next;
+ next = current_midi_point->next;
+ }
+ newev->prev = current_midi_point;
+ newev->next = next;
+ current_midi_point->next = newev;
+ if (next)
+ next->prev = newev;
+ }
+ else
+ {
+ /* Backward scan -- symmetrical to the one above */
+ MidiEventList *prev = current_midi_point->prev;
+ while (prev && (prev->event.time > at)) {
+ current_midi_point = prev;
+ prev = current_midi_point->prev;
+ }
+ newev->prev = prev;
+ newev->next = current_midi_point;
+ current_midi_point->prev = newev;
+ if (prev)
+ prev->next = newev;
+ }
+ current_midi_point = newev;
+}
+
+void readmidi_add_ctl_event(int32 at, int ch, int a, int b)
+{
+ MidiEvent ev;
+
+ if(convert_midi_control_change(ch, a, b, &ev))
+ {
+ ev.time = at;
+ readmidi_add_event(&ev);
+ }
+ else
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG, "(Control ch=%d %d: %d)", ch, a, b);
+}
+
+char *readmidi_make_string_event(int type, char *string, MidiEvent *ev,
+ int cnv)
+{
+ char *text;
+ int len;
+ StringTableNode *st;
+ int a, b;
+
+ if(string_event_strtab.nstring == 0)
+ put_string_table(&string_event_strtab, "", 0);
+ else if(string_event_strtab.nstring == 0x7FFE)
+ {
+ SETMIDIEVENT(*ev, 0, type, 0, 0, 0);
+ return NULL; /* Over flow */
+ }
+ a = (string_event_strtab.nstring & 0xff);
+ b = ((string_event_strtab.nstring >> 8) & 0xff);
+
+ len = strlen(string);
+ if(cnv)
+ {
+ text = (char *)new_segment(&tmpbuffer, SAFE_CONVERT_LENGTH(len) + 1);
+ code_convert(string, text + 1, SAFE_CONVERT_LENGTH(len), NULL, NULL);
+ }
+ else
+ {
+ text = (char *)new_segment(&tmpbuffer, len + 1);
+ memcpy(text + 1, string, len);
+ text[len + 1] = '\0';
+ }
+
+ st = put_string_table(&string_event_strtab, text, strlen(text + 1) + 1);
+ reuse_mblock(&tmpbuffer);
+
+ text = st->string;
+ *text = type;
+ SETMIDIEVENT(*ev, 0, type, 0, a, b);
+ return text;
+}
+
+static char *readmidi_make_lcd_event(int type, const uint8 *data, MidiEvent *ev)
+{
+ char *text;
+ int len;
+ StringTableNode *st;
+ int a, b, i;
+
+ if(string_event_strtab.nstring == 0)
+ put_string_table(&string_event_strtab, "", 0);
+ else if(string_event_strtab.nstring == 0x7FFE)
+ {
+ SETMIDIEVENT(*ev, 0, type, 0, 0, 0);
+ return NULL; /* Over flow */
+ }
+ a = (string_event_strtab.nstring & 0xff);
+ b = ((string_event_strtab.nstring >> 8) & 0xff);
+
+ len = 128;
+
+ text = (char *)new_segment(&tmpbuffer, len + 2);
+
+ for( i=0; i<64; i++){
+ const char tbl[]= "0123456789ABCDEF";
+ text[1+i*2 ]=tbl[data[i]>>4];
+ text[1+i*2+1]=tbl[data[i]&0xF];
+ }
+ text[len + 1] = '\0';
+
+
+ st = put_string_table(&string_event_strtab, text, strlen(text + 1) + 1);
+ reuse_mblock(&tmpbuffer);
+
+ text = st->string;
+ *text = type;
+ SETMIDIEVENT(*ev, 0, type, 0, a, b);
+ return text;
+}
+
+/* Computes how many (fractional) samples one MIDI delta-time unit contains */
+static void compute_sample_increment(int32 tempo, int32 divisions)
+{
+ double a;
+ a = (double) (tempo) * (double) (play_mode->rate) * (65536.0/1000000.0) /
+ (double)(divisions);
+
+ sample_correction = (int32)(a) & 0xFFFF;
+ sample_increment = (int32)(a) >> 16;
+
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Samples per delta-t: %d (correction %d)",
+ sample_increment, sample_correction);
+}
+
+/* Read variable-length number (7 bits per byte, MSB first) */
+static int32 getvl(struct timidity_file *tf)
+{
+ int32 l;
+ int c;
+
+ errno = 0;
+ l = 0;
+
+ /* 1 */
+ if((c = tf_getc(tf)) == EOF)
+ goto eof;
+ if(!(c & 0x80)) return l | c;
+ l = (l | (c & 0x7f)) << 7;
+
+ /* 2 */
+ if((c = tf_getc(tf)) == EOF)
+ goto eof;
+ if(!(c & 0x80)) return l | c;
+ l = (l | (c & 0x7f)) << 7;
+
+ /* 3 */
+ if((c = tf_getc(tf)) == EOF)
+ goto eof;
+ if(!(c & 0x80)) return l | c;
+ l = (l | (c & 0x7f)) << 7;
+
+ /* 4 */
+ if((c = tf_getc(tf)) == EOF)
+ goto eof;
+ if(!(c & 0x80)) return l | c;
+
+ /* Error */
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "%s: Illigal Variable-length quantity format.",
+ current_filename);
+ return -2;
+
+ eof:
+ if(errno)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "%s: read_midi_event: %s",
+ current_filename, strerror(errno));
+ else
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "Warning: %s: Too shorten midi file.",
+ current_filename);
+ return -1;
+}
+
+static char *add_karaoke_title(char *s1, char *s2)
+{
+ char *ks;
+ int k1, k2;
+
+ if(s1 == NULL)
+ return safe_strdup(s2);
+
+ k1 = strlen(s1);
+ k2 = strlen(s2);
+ if(k2 == 0)
+ return s1;
+ ks = (char *)safe_malloc(k1 + k2 + 2);
+ memcpy(ks, s1, k1);
+ ks[k1++] = ' ';
+ memcpy(ks + k1, s2, k2 + 1);
+ free(s1);
+ s1 = NULL;
+
+ return ks;
+}
+
+
+/* Print a string from the file, followed by a newline. Any non-ASCII
+ or unprintable characters will be converted to periods. */
+static char *dumpstring(int type, int32 len, char *label, int allocp,
+ struct timidity_file *tf)
+{
+ char *si, *so;
+ int s_maxlen = SAFE_CONVERT_LENGTH(len);
+ int llen, solen;
+
+ if(len <= 0)
+ {
+ ctl->cmsg(CMSG_TEXT, VERB_VERBOSE, "%s", label);
+ return NULL;
+ }
+
+ si = (char *)new_segment(&tmpbuffer, len + 1);
+ so = (char *)new_segment(&tmpbuffer, s_maxlen);
+
+ if(len != tf_read(si, 1, len, tf))
+ {
+ reuse_mblock(&tmpbuffer);
+ return NULL;
+ }
+ si[len]='\0';
+
+ if(type == 1 &&
+ current_file_info->format == 1 &&
+ (strncmp(si, "@K", 2) == 0))
+/* Karaoke string should be "@KMIDI KARAOKE FILE" */
+ karaoke_format = 1;
+
+ code_convert(si, so, s_maxlen, NULL, NULL);
+
+ llen = strlen(label);
+ solen = strlen(so);
+ if(llen + solen >= MIN_MBLOCK_SIZE)
+ so[MIN_MBLOCK_SIZE - llen - 1] = '\0';
+
+ ctl->cmsg(CMSG_TEXT, VERB_VERBOSE, "%s%s", label, so);
+
+ if(allocp)
+ {
+ so = safe_strdup(so);
+ reuse_mblock(&tmpbuffer);
+ return so;
+ }
+ reuse_mblock(&tmpbuffer);
+ return NULL;
+}
+
+static uint16 gs_convert_master_vol(int vol)
+{
+ double v;
+
+ if(vol >= 0x7f)
+ return 0xffff;
+ v = (double)vol * (0xffff/127.0);
+ if(v >= 0xffff)
+ return 0xffff;
+ return (uint16)v;
+}
+
+static uint16 gm_convert_master_vol(uint16 v1, uint16 v2)
+{
+ return (((v1 & 0x7f) | ((v2 & 0x7f) << 7)) << 2) | 3;
+}
+
+static void check_chorus_text_start(void)
+{
+ struct chorus_text_gs_t *p = &(chorus_status_gs.text);
+ if(p->status != CHORUS_ST_OK && p->voice_reserve[17] &&
+ p->macro[2] && p->pre_lpf[2] && p->level[2] &&
+ p->feed_back[2] && p->delay[2] && p->rate[2] &&
+ p->depth[2] && p->send_level[2])
+ {
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Chorus text start");
+ p->status = CHORUS_ST_OK;
+ }
+}
+
+struct ctl_chg_types {
+ unsigned char mtype;
+ int ttype;
+} ctl_chg_list[] = {
+ { 0, ME_TONE_BANK_MSB },
+ { 1, ME_MODULATION_WHEEL },
+ { 2, ME_BREATH },
+ { 4, ME_FOOT },
+ { 5, ME_PORTAMENTO_TIME_MSB },
+ { 6, ME_DATA_ENTRY_MSB },
+ { 7, ME_MAINVOLUME },
+ { 8, ME_BALANCE },
+ { 10, ME_PAN },
+ { 11, ME_EXPRESSION },
+ { 32, ME_TONE_BANK_LSB },
+ { 37, ME_PORTAMENTO_TIME_LSB },
+ { 38, ME_DATA_ENTRY_LSB },
+ { 64, ME_SUSTAIN },
+ { 65, ME_PORTAMENTO },
+ { 66, ME_SOSTENUTO },
+ { 67, ME_SOFT_PEDAL },
+ { 68, ME_LEGATO_FOOTSWITCH },
+ { 69, ME_HOLD2 },
+ { 71, ME_HARMONIC_CONTENT },
+ { 72, ME_RELEASE_TIME },
+ { 73, ME_ATTACK_TIME },
+ { 74, ME_BRIGHTNESS },
+ { 84, ME_PORTAMENTO_CONTROL },
+ { 91, ME_REVERB_EFFECT },
+ { 92, ME_TREMOLO_EFFECT },
+ { 93, ME_CHORUS_EFFECT },
+ { 94, ME_CELESTE_EFFECT },
+ { 95, ME_PHASER_EFFECT },
+ { 96, ME_RPN_INC },
+ { 97, ME_RPN_DEC },
+ { 98, ME_NRPN_LSB },
+ { 99, ME_NRPN_MSB },
+ { 100, ME_RPN_LSB },
+ { 101, ME_RPN_MSB },
+ { 120, ME_ALL_SOUNDS_OFF },
+ { 121, ME_RESET_CONTROLLERS },
+ { 123, ME_ALL_NOTES_OFF },
+ { 126, ME_MONO },
+ { 127, ME_POLY },
+};
+
+int convert_midi_control_change(int chn, int type, int val, MidiEvent *ev_ret)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(ctl_chg_list); i++) {
+ if (ctl_chg_list[i].mtype == type) {
+ type = ctl_chg_list[i].ttype;
+ break;
+ }
+ }
+ if (i >= ARRAY_SIZE(ctl_chg_list))
+ type = -1;
+
+ if(type != -1)
+ {
+ if(val > 127)
+ val = 127;
+ ev_ret->type = type;
+ ev_ret->channel = chn;
+ ev_ret->a = val;
+ ev_ret->b = 0;
+ return 1;
+ }
+ return 0;
+}
+
+int unconvert_midi_control_change(MidiEvent *ev)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(ctl_chg_list); i++) {
+ if (ctl_chg_list[i].ttype == ev->type)
+ break;
+ }
+ if (i >= ARRAY_SIZE(ctl_chg_list))
+ return -1;
+
+ return ctl_chg_list[i].mtype;
+}
+
+static int block_to_part(int block, int port)
+{
+ int p;
+ p = block & 0x0F;
+ if(p == 0) {p = 9;}
+ else if(p <= 9) {p--;}
+ return MERGE_CHANNEL_PORT2(p, port);
+}
+
+/* Map XG types onto GS types. XG should eventually have its own tables */
+static int set_xg_reverb_type(int msb, int lsb)
+{
+ int type = 4;
+
+ if ((msb == 0x00) ||
+ (msb >= 0x05 && msb <= 0x0F) ||
+ (msb >= 0x14)) /* NO EFFECT */
+ {
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"XG Set Reverb Type (NO EFFECT %d %d)", msb, lsb);
+ return -1;
+ }
+
+ switch(msb)
+ {
+ case 0x01:
+ type = 3; /* Hall 1 */
+ break;
+ case 0x02:
+ type = 0; /* Room 1 */
+ break;
+ case 0x03:
+ type = 3; /* Stage 1 -> Hall 1 */
+ break;
+ case 0x04:
+ type = 5; /* Plate */
+ break;
+ default:
+ type = 4; /* unsupported -> Hall 2 */
+ break;
+ }
+ if (lsb == 0x01)
+ {
+ switch(msb)
+ {
+ case 0x01:
+ type = 4; /* Hall 2 */
+ break;
+ case 0x02:
+ type = 1; /* Room 2 */
+ break;
+ case 0x03:
+ type = 4; /* Stage 2 -> Hall 2 */
+ break;
+ default:
+ break;
+ }
+ }
+ if (lsb == 0x02 && msb == 0x02)
+ type = 2; /* Room 3 */
+
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"XG Set Reverb Type (%d)", type);
+ return type;
+}
+
+/* Map XG types onto GS types. XG should eventually have its own tables */
+static int set_xg_chorus_type(int msb, int lsb)
+{
+ int type = 2;
+
+ if ((msb >= 0x00 && msb <= 0x40) ||
+ (msb >= 0x45 && msb <= 0x47) ||
+ (msb >= 0x49)) /* NO EFFECT */
+ {
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"XG Set Chorus Type (NO EFFECT %d %d)", msb, lsb);
+ return -1;
+ }
+
+ switch(msb)
+ {
+ case 0x41:
+ type = 0; /* Chorus 1 */
+ break;
+ case 0x42:
+ type = 0; /* Celeste 1 -> Chorus 1 */
+ break;
+ case 0x43:
+ type = 5;
+ break;
+ default:
+ type = 2; /* unsupported -> Chorus 3 */
+ break;
+ }
+ if (lsb == 0x01)
+ {
+ switch(msb)
+ {
+ case 0x41:
+ type = 1; /* Chorus 2 */
+ break;
+ case 0x42:
+ type = 1; /* Celeste 2 -> Chorus 2 */
+ break;
+ default:
+ break;
+ }
+ }
+ else if (lsb == 0x02)
+ {
+ switch(msb)
+ {
+ case 0x41:
+ type = 2; /* Chorus 3 */
+ break;
+ case 0x42:
+ type = 2; /* Celeste 3 -> Chorus 3 */
+ break;
+ default:
+ break;
+ }
+ }
+ else if (lsb == 0x08)
+ {
+ switch(msb)
+ {
+ case 0x41:
+ type = 3; /* Chorus 4 */
+ break;
+ case 0x42:
+ type = 3; /* Celeste 4 -> Chorus 4 */
+ break;
+ default:
+ break;
+ }
+ }
+
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"XG Set Chorus Type (%d)", type);
+ return type;
+}
+
+/* XG SysEx parsing function by Eric A. Welsh
+ * Also handles GS patch+bank changes
+ *
+ * This function provides basic support for XG Bulk Dump and Parameter
+ * Change SysEx events
+ */
+int parse_sysex_event_multi(uint8 *val, int32 len, MidiEvent *evm)
+{
+ int num_events = 0; /* Number of events added */
+ uint32 channel_tt;
+ int i, j;
+ static uint8 xg_reverb_type_msb = 0x01, xg_reverb_type_lsb = 0x00;
+ static uint8 xg_chorus_type_msb = 0x41, xg_chorus_type_lsb = 0x00;
+
+ if(current_file_info->mid == 0 || current_file_info->mid >= 0x7e)
+ current_file_info->mid = val[0];
+
+ /* Effect 1 or Multi EQ */
+ if(len >= 8 &&
+ val[0] == 0x43 && /* Yamaha ID */
+ val[2] == 0x4C && /* XG Model ID */
+ ((val[1] < 0x10 && val[5] == 0x02) || /* Bulk Dump*/
+ (val[1] >= 0x10 && val[3] == 0x02))) /* Parameter Change */
+ {
+ uint8 addhigh, addmid, addlow; /* Addresses */
+ uint8 *body; /* SysEx body */
+ int ent, v; /* Entry # of sub-event */
+ uint8 *body_end; /* End of SysEx body */
+
+ if (val[1] < 0x10) /* Bulk Dump */
+ {
+ addhigh = val[5];
+ addmid = val[6];
+ addlow = val[7];
+ body = val + 8;
+ body_end = val + len - 3;
+ }
+ else /* Parameter Change */
+ {
+ addhigh = val[3];
+ addmid = val[4];
+ addlow = val[5];
+ body = val + 6;
+ body_end = val + len - 2;
+ }
+
+ /* set the SYSEX_XG_MSB info */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_MSB, 0, addhigh, addmid);
+ num_events++;
+
+ for (ent = addlow; body <= body_end; body++, ent++) {
+ if(addmid == 0x01) { /* Effect 1 */
+ switch(ent) {
+ case 0x00: /* Reverb Type MSB */
+ xg_reverb_type_msb = *body;
+#if 0 /* XG specific reverb is not supported yet, use GS instead */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+#endif
+ break;
+
+ case 0x01: /* Reverb Type LSB */
+ xg_reverb_type_lsb = *body;
+#if 0 /* XG specific reverb is not supported yet, use GS instead */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+#else
+ v = set_xg_reverb_type(xg_reverb_type_msb, xg_reverb_type_lsb);
+ if (v >= 0) {
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, v, 0x05);
+ num_events++;
+ }
+#endif
+ break;
+
+ case 0x0C: /* Reverb Return */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x20: /* Chorus Type MSB */
+ xg_chorus_type_msb = *body;
+#if 0 /* XG specific chorus is not supported yet, use GS instead */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+#endif
+ break;
+
+ case 0x21: /* Chorus Type LSB */
+ xg_chorus_type_lsb = *body;
+#if 0 /* XG specific chorus is not supported yet, use GS instead */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+#else
+ v = set_xg_chorus_type(xg_chorus_type_msb, xg_chorus_type_lsb);
+ if (v >= 0) {
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, v, 0x0D);
+ num_events++;
+ }
+#endif
+ break;
+
+ case 0x2C: /* Chorus Return */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ default:
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+ }
+ }
+ else if(addmid == 0x40) { /* Multi EQ */
+ switch(ent) {
+ case 0x00: /* EQ type */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x01: /* EQ gain1 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x02: /* EQ frequency1 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x03: /* EQ Q1 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x04: /* EQ shape1 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x05: /* EQ gain2 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x06: /* EQ frequency2 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x07: /* EQ Q2 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x09: /* EQ gain3 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x0A: /* EQ frequency3 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x0B: /* EQ Q3 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x0D: /* EQ gain4 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x0E: /* EQ frequency4 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x0F: /* EQ Q4 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x11: /* EQ gain5 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x12: /* EQ frequency5 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x13: /* EQ Q5 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ case 0x14: /* EQ shape5 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ /* Effect 2 (Insertion Effects) */
+ else if(len >= 8 &&
+ val[0] == 0x43 && /* Yamaha ID */
+ val[2] == 0x4C && /* XG Model ID */
+ ((val[1] < 0x10 && val[5] == 0x03) || /* Bulk Dump*/
+ (val[1] >= 0x10 && val[3] == 0x03))) /* Parameter Change */
+ {
+ uint8 addhigh, addmid, addlow; /* Addresses */
+ uint8 *body; /* SysEx body */
+ int ent; /* Entry # of sub-event */
+ uint8 *body_end; /* End of SysEx body */
+
+ if (val[1] < 0x10) /* Bulk Dump */
+ {
+ addhigh = val[5];
+ addmid = val[6];
+ addlow = val[7];
+ body = val + 8;
+ body_end = val + len - 3;
+ }
+ else /* Parameter Change */
+ {
+ addhigh = val[3];
+ addmid = val[4];
+ addlow = val[5];
+ body = val + 6;
+ body_end = val + len - 2;
+ }
+
+ /* set the SYSEX_XG_MSB info */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_MSB, 0, addhigh, addmid);
+ num_events++;
+
+ for (ent = addlow; body <= body_end; body++, ent++) {
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, 0, *body, ent);
+ num_events++;
+ }
+ }
+
+ /* XG Multi Part Data parameter change */
+ else if(len >= 10 &&
+ val[0] == 0x43 && /* Yamaha ID */
+ val[2] == 0x4C && /* XG Model ID */
+ ((val[1] < 0x10 && val[5] == 0x08 && /* Bulk Dump */
+ (val[4] == 0x29 || val[4] == 0x3F)) || /* Blocks 1 or 2 */
+ (val[1] >= 0x10 && val[3] == 0x08))) /* Parameter Change */
+ {
+ uint8 addhigh, addmid, addlow; /* Addresses */
+ uint8 *body; /* SysEx body */
+ uint8 p; /* Channel part number [0..15] */
+ int ent; /* Entry # of sub-event */
+ uint8 *body_end; /* End of SysEx body */
+
+ if (val[1] < 0x10) /* Bulk Dump */
+ {
+ addhigh = val[5];
+ addmid = val[6];
+ addlow = val[7];
+ body = val + 8;
+ p = addmid;
+ body_end = val + len - 3;
+ }
+ else /* Parameter Change */
+ {
+ addhigh = val[3];
+ addmid = val[4];
+ addlow = val[5];
+ body = val + 6;
+ p = addmid;
+ body_end = val + len - 2;
+ }
+
+ /* set the SYSEX_XG_MSB info */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_MSB, p, addhigh, addmid);
+ num_events++;
+
+ for (ent = addlow; body <= body_end; body++, ent++) {
+ switch(ent) {
+ case 0x00: /* Element Reserve */
+/* ctl->cmsg(CMSG_INFO, VERB_NOISY, "Element Reserve is not supported. (CH:%d VAL:%d)", p, *body); */
+ break;
+
+ case 0x01: /* bank select MSB */
+ SETMIDIEVENT(evm[num_events], 0, ME_TONE_BANK_MSB, p, *body, SYSEX_TAG);
+ num_events++;
+ break;
+
+ case 0x02: /* bank select LSB */
+ SETMIDIEVENT(evm[num_events], 0, ME_TONE_BANK_LSB, p, *body, SYSEX_TAG);
+ num_events++;
+ break;
+
+ case 0x03: /* program number */
+ SETMIDIEVENT(evm[num_events], 0, ME_PROGRAM, p, *body, SYSEX_TAG);
+ num_events++;
+ break;
+
+ case 0x04: /* Rcv CHANNEL */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, p, *body, 0x99);
+ num_events++;
+ break;
+
+ case 0x05: /* mono/poly mode */
+ if(*body == 0) {SETMIDIEVENT(evm[num_events], 0, ME_MONO, p, 0, SYSEX_TAG);}
+ else {SETMIDIEVENT(evm[num_events], 0, ME_POLY, p, 0, SYSEX_TAG);}
+ num_events++;
+ break;
+
+ case 0x06: /* Same Note Number Key On Assign */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, p, *body, ent);
+ num_events++;
+ break;
+
+ case 0x07: /* Part Mode */
+ drum_setup_xg[*body] = p;
+ SETMIDIEVENT(evm[num_events], 0, ME_DRUMPART, p, *body, SYSEX_TAG);
+ num_events++;
+ break;
+
+ case 0x08: /* note shift */
+ SETMIDIEVENT(evm[num_events], 0, ME_KEYSHIFT, p, *body, SYSEX_TAG);
+ num_events++;
+ break;
+
+ case 0x09: /* Detune 1st bit */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "Detune 1st bit is not supported. (CH:%d VAL:%d)", p, *body);
+ break;
+
+ case 0x0A: /* Detune 2nd bit */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "Detune 2nd bit is not supported. (CH:%d VAL:%d)", p, *body);
+ break;
+
+ case 0x0B: /* volume */
+ SETMIDIEVENT(evm[num_events], 0, ME_MAINVOLUME, p, *body, SYSEX_TAG);
+ num_events++;
+ break;
+
+ case 0x0C: /* Velocity Sense Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, p, *body, 0x21);
+ num_events++;
+ break;
+
+ case 0x0D: /* Velocity Sense Offset */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, p, *body, 0x22);
+ num_events++;
+ break;
+
+ case 0x0E: /* pan */
+ if(*body == 0) {
+ SETMIDIEVENT(evm[num_events], 0, ME_RANDOM_PAN, p, 0, SYSEX_TAG);
+ }
+ else {
+ SETMIDIEVENT(evm[num_events], 0, ME_PAN, p, *body, SYSEX_TAG);
+ }
+ num_events++;
+ break;
+
+ case 0x0F: /* Note Limit Low */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x42);
+ num_events++;
+ break;
+
+ case 0x10: /* Note Limit High */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x43);
+ num_events++;
+ break;
+
+ case 0x11: /* Dry Level */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, p, *body, ent);
+ num_events++;
+ break;
+
+ case 0x12: /* chorus send */
+ SETMIDIEVENT(evm[num_events], 0, ME_CHORUS_EFFECT, p, *body, SYSEX_TAG);
+ num_events++;
+ break;
+
+ case 0x13: /* reverb send */
+ SETMIDIEVENT(evm[num_events], 0, ME_REVERB_EFFECT, p, *body, SYSEX_TAG);
+ num_events++;
+ break;
+
+ case 0x14: /* Variation Send */
+ SETMIDIEVENT(evm[num_events], 0, ME_CELESTE_EFFECT, p, *body, SYSEX_TAG);
+ num_events++;
+ break;
+
+ case 0x15: /* Vibrato Rate */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x08, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+
+ case 0x16: /* Vibrato Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x09, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+
+ case 0x17: /* Vibrato Delay */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x0A, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+
+ case 0x18: /* Filter Cutoff Frequency */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x20, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+
+ case 0x19: /* Filter Resonance */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x21, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+
+ case 0x1A: /* EG Attack Time */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x63, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+
+ case 0x1B: /* EG Decay Time */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x64, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+
+ case 0x1C: /* EG Release Time */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, p, 0x66, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+
+ case 0x1D: /* MW Pitch Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x16);
+ num_events++;
+ break;
+
+ case 0x1E: /* MW Filter Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x17);
+ num_events++;
+ break;
+
+ case 0x1F: /* MW Amplitude Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x18);
+ num_events++;
+ break;
+
+ case 0x20: /* MW LFO PMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x1A);
+ num_events++;
+ break;
+
+ case 0x21: /* MW LFO FMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x1B);
+ num_events++;
+ break;
+
+ case 0x22: /* MW LFO AMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x1C);
+ num_events++;
+ break;
+
+ case 0x23: /* bend pitch control */
+ SETMIDIEVENT(evm[num_events], 0, ME_RPN_MSB, p, 0, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_RPN_LSB, p, 0, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, p, (*body - 0x40) & 0x7F, SYSEX_TAG);
+ num_events += 3;
+ break;
+
+ case 0x24: /* Bend Filter Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x22);
+ num_events++;
+ break;
+
+ case 0x25: /* Bend Amplitude Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x23);
+ num_events++;
+ break;
+
+ case 0x26: /* Bend LFO PMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x25);
+ num_events++;
+ break;
+
+ case 0x27: /* Bend LFO FMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x26);
+ num_events++;
+ break;
+
+ case 0x28: /* Bend LFO AMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x27);
+ num_events++;
+ break;
+
+ case 0x30: /* Rcv Pitch Bend */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x48);
+ num_events++;
+ break;
+
+ case 0x31: /* Rcv Channel Pressure */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x49);
+ num_events++;
+ break;
+
+ case 0x32: /* Rcv Program Change */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4A);
+ num_events++;
+ break;
+
+ case 0x33: /* Rcv Control Change */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4B);
+ num_events++;
+ break;
+
+ case 0x34: /* Rcv Poly Pressure */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4C);
+ num_events++;
+ break;
+
+ case 0x35: /* Rcv Note Message */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4D);
+ num_events++;
+ break;
+
+ case 0x36: /* Rcv RPN */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4E);
+ num_events++;
+ break;
+
+ case 0x37: /* Rcv NRPN */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x4F);
+ num_events++;
+ break;
+
+ case 0x38: /* Rcv Modulation */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x50);
+ num_events++;
+ break;
+
+ case 0x39: /* Rcv Volume */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x51);
+ num_events++;
+ break;
+
+ case 0x3A: /* Rcv Pan */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x52);
+ num_events++;
+ break;
+
+ case 0x3B: /* Rcv Expression */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x53);
+ num_events++;
+ break;
+
+ case 0x3C: /* Rcv Hold1 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x54);
+ num_events++;
+ break;
+
+ case 0x3D: /* Rcv Portamento */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x55);
+ num_events++;
+ break;
+
+ case 0x3E: /* Rcv Sostenuto */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x56);
+ num_events++;
+ break;
+
+ case 0x3F: /* Rcv Soft */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x57);
+ num_events++;
+ break;
+
+ case 0x40: /* Rcv Bank Select */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x58);
+ num_events++;
+ break;
+
+ case 0x41: /* scale tuning */
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4a:
+ case 0x4b:
+ case 0x4c:
+ SETMIDIEVENT(evm[num_events], 0, ME_SCALE_TUNING, p, ent - 0x41, *body - 64);
+ num_events++;
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "Scale Tuning %s (CH:%d %d cent)",
+ note_name[ent - 0x41], p, *body - 64);
+ break;
+
+ case 0x4D: /* CAT Pitch Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x00);
+ num_events++;
+ break;
+
+ case 0x4E: /* CAT Filter Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x01);
+ num_events++;
+ break;
+
+ case 0x4F: /* CAT Amplitude Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x02);
+ num_events++;
+ break;
+
+ case 0x50: /* CAT LFO PMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x04);
+ num_events++;
+ break;
+
+ case 0x51: /* CAT LFO FMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x05);
+ num_events++;
+ break;
+
+ case 0x52: /* CAT LFO AMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x06);
+ num_events++;
+ break;
+
+ case 0x53: /* PAT Pitch Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x0B);
+ num_events++;
+ break;
+
+ case 0x54: /* PAT Filter Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x0C);
+ num_events++;
+ break;
+
+ case 0x55: /* PAT Amplitude Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x0D);
+ num_events++;
+ break;
+
+ case 0x56: /* PAT LFO PMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x0F);
+ num_events++;
+ break;
+
+ case 0x57: /* PAT LFO FMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x10);
+ num_events++;
+ break;
+
+ case 0x58: /* PAT LFO AMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x11);
+ num_events++;
+ break;
+
+ case 0x59: /* AC1 Controller Number */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "AC1 Controller Number is not supported. (CH:%d VAL:%d)", p, *body);
+ break;
+
+ case 0x5A: /* AC1 Pitch Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x2C);
+ num_events++;
+ break;
+
+ case 0x5B: /* AC1 Filter Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x2D);
+ num_events++;
+ break;
+
+ case 0x5C: /* AC1 Amplitude Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x2E);
+ num_events++;
+ break;
+
+ case 0x5D: /* AC1 LFO PMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x30);
+ num_events++;
+ break;
+
+ case 0x5E: /* AC1 LFO FMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x31);
+ num_events++;
+ break;
+
+ case 0x5F: /* AC1 LFO AMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x32);
+ num_events++;
+ break;
+
+ case 0x60: /* AC2 Controller Number */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "AC2 Controller Number is not supported. (CH:%d VAL:%d)", p, *body);
+ break;
+
+ case 0x61: /* AC2 Pitch Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x37);
+ num_events++;
+ break;
+
+ case 0x62: /* AC2 Filter Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x38);
+ num_events++;
+ break;
+
+ case 0x63: /* AC2 Amplitude Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x39);
+ num_events++;
+ break;
+
+ case 0x64: /* AC2 LFO PMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x3B);
+ num_events++;
+ break;
+
+ case 0x65: /* AC2 LFO FMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x3C);
+ num_events++;
+ break;
+
+ case 0x66: /* AC2 LFO AMod Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x3D);
+ num_events++;
+ break;
+
+ case 0x67: /* Portamento Switch */
+ SETMIDIEVENT(evm[num_events], 0, ME_PORTAMENTO, p, *body, SYSEX_TAG);
+ num_events++;
+
+ case 0x68: /* Portamento Time */
+ SETMIDIEVENT(evm[num_events], 0, ME_PORTAMENTO_TIME_MSB, p, *body, SYSEX_TAG);
+ num_events++;
+
+ case 0x69: /* Pitch EG Initial Level */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "Pitch EG Initial Level is not supported. (CH:%d VAL:%d)", p, *body);
+ break;
+
+ case 0x6A: /* Pitch EG Attack Time */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "Pitch EG Attack Time is not supported. (CH:%d VAL:%d)", p, *body);
+ break;
+
+ case 0x6B: /* Pitch EG Release Level */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "Pitch EG Release Level is not supported. (CH:%d VAL:%d)", p, *body);
+ break;
+
+ case 0x6C: /* Pitch EG Release Time */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "Pitch EG Release Time is not supported. (CH:%d VAL:%d)", p, *body);
+ break;
+
+ case 0x6D: /* Velocity Limit Low */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x44);
+ num_events++;
+ break;
+
+ case 0x6E: /* Velocity Limit High */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, p, *body, 0x45);
+ num_events++;
+ break;
+
+ case 0x70: /* Bend Pitch Low Control */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "Bend Pitch Low Control is not supported. (CH:%d VAL:%d)", p, *body);
+ break;
+
+ case 0x71: /* Filter EG Depth */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "Filter EG Depth is not supported. (CH:%d VAL:%d)", p, *body);
+ break;
+
+ case 0x72: /* EQ BASS */
+ SETMIDIEVENT(evm[num_events], 0,ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0,ME_NRPN_LSB, p, 0x30, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0,ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+
+ case 0x73: /* EQ TREBLE */
+ SETMIDIEVENT(evm[num_events], 0,ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0,ME_NRPN_LSB, p, 0x31, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0,ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+
+ case 0x76: /* EQ BASS frequency */
+ SETMIDIEVENT(evm[num_events], 0,ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0,ME_NRPN_LSB, p, 0x34, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0,ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+
+ case 0x77: /* EQ TREBLE frequency */
+ SETMIDIEVENT(evm[num_events], 0,ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0,ME_NRPN_LSB, p, 0x35, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0,ME_DATA_ENTRY_MSB, p, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+
+ default:
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"Unsupported XG Bulk Dump SysEx. (ADDR:%02X %02X %02X VAL:%02X)",addhigh,addlow,ent,*body);
+ continue;
+ break;
+ }
+ }
+ }
+
+ /* XG Drum Setup */
+ else if(len >= 10 &&
+ val[0] == 0x43 && /* Yamaha ID */
+ val[2] == 0x4C && /* XG Model ID */
+ ((val[1] < 0x10 && (val[5] & 0xF0) == 0x30) || /* Bulk Dump*/
+ (val[1] >= 0x10 && (val[3] & 0xF0) == 0x30))) /* Parameter Change */
+ {
+ uint8 addhigh, addmid, addlow; /* Addresses */
+ uint8 *body; /* SysEx body */
+ uint8 dp, note; /* Channel part number [0..15] */
+ int ent; /* Entry # of sub-event */
+ uint8 *body_end; /* End of SysEx body */
+
+ if (val[1] < 0x10) /* Bulk Dump */
+ {
+ addhigh = val[5];
+ addmid = val[6];
+ addlow = val[7];
+ body = val + 8;
+ body_end = val + len - 3;
+ }
+ else /* Parameter Change */
+ {
+ addhigh = val[3];
+ addmid = val[4];
+ addlow = val[5];
+ body = val + 6;
+ body_end = val + len - 2;
+ }
+
+ dp = drum_setup_xg[(addhigh & 0x0F) + 1];
+ note = addmid;
+
+ /* set the SYSEX_XG_MSB info */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_MSB, dp, addhigh, addmid);
+ num_events++;
+
+ for (ent = addlow; body <= body_end; body++, ent++) {
+ switch(ent) {
+ case 0x00: /* Pitch Coarse */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x18, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x01: /* Pitch Fine */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x19, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x02: /* Level */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1A, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x03: /* Alternate Group */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "Alternate Group is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body);
+ break;
+ case 0x04: /* Pan */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1C, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x05: /* Reverb Send */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1D, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x06: /* Chorus Send */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1E, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x07: /* Variation Send */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x1F, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x08: /* Key Assign */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "Key Assign is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body);
+ break;
+ case 0x09: /* Rcv Note Off */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_MSB, dp, note, 0);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_SYSEX_LSB, dp, *body, 0x46);
+ num_events += 2;
+ break;
+ case 0x0A: /* Rcv Note On */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_MSB, dp, note, 0);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_SYSEX_LSB, dp, *body, 0x47);
+ num_events += 2;
+ break;
+ case 0x0B: /* Filter Cutoff Frequency */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x14, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x0C: /* Filter Resonance */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x15, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x0D: /* EG Attack */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x16, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x0E: /* EG Decay1 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, dp, *body, ent);
+ num_events++;
+ break;
+ case 0x0F: /* EG Decay2 */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_XG_LSB, dp, *body, ent);
+ num_events++;
+ break;
+ case 0x20: /* EQ BASS */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x30, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x21: /* EQ TREBLE */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x31, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x24: /* EQ BASS frequency */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x34, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x25: /* EQ TREBLE frequency */
+ SETMIDIEVENT(evm[num_events], 0, ME_NRPN_MSB, dp, 0x35, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 1], 0, ME_NRPN_LSB, dp, note, SYSEX_TAG);
+ SETMIDIEVENT(evm[num_events + 2], 0, ME_DATA_ENTRY_MSB, dp, *body, SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x50: /* High Pass Filter Cutoff Frequency */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "High Pass Filter Cutoff Frequency is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body);
+ break;
+ case 0x60: /* Velocity Pitch Sense */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "Velocity Pitch Sense is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body);
+ break;
+ case 0x61: /* Velocity LPF Cutoff Sense */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "Velocity LPF Cutoff Sense is not supported. (CH:%d NOTE:%d VAL:%d)", dp, note, *body);
+ break;
+ default:
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"Unsupported XG Bulk Dump SysEx. (ADDR:%02X %02X %02X VAL:%02X)",addhigh,addmid,ent,*body);
+ break;
+ }
+ }
+ }
+
+ /* parsing GS System Exclusive Message...
+ *
+ * val[4] == Parameter Address(High)
+ * val[5] == Parameter Address(Middle)
+ * val[6] == Parameter Address(Low)
+ * val[7]... == Data...
+ * val[last] == Checksum(== 128 - (sum of addresses&data bytes % 128))
+ */
+ else if(len >= 9 &&
+ val[0] == 0x41 && /* Roland ID */
+ val[1] == 0x10 && /* Device ID */
+ val[2] == 0x42 && /* GS Model ID */
+ val[3] == 0x12) /* Data Set Command */
+ {
+ uint8 p, dp, udn, gslen, port = 0;
+ int i, addr, addr_h, addr_m, addr_l, checksum;
+ p = block_to_part(val[5], midi_port_number);
+
+ /* calculate checksum */
+ checksum = 0;
+ for(gslen = 9; gslen < len; gslen++)
+ if(val[gslen] == 0xF7)
+ break;
+ for(i=4;i<gslen-1;i++) {
+ checksum += val[i];
+ }
+ if(((128 - (checksum & 0x7F)) & 0x7F) != val[gslen-1]) {
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"GS SysEx: Checksum Error.");
+ return num_events;
+ }
+
+ /* drum channel */
+ dp = rhythm_part[(val[5] & 0xF0) >> 4];
+
+ /* calculate user drumset number */
+ udn = (val[5] & 0xF0) >> 4;
+
+ addr_h = val[4];
+ addr_m = val[5];
+ addr_l = val[6];
+ if(addr_h == 0x50) { /* for double module mode */
+ port = 1;
+ p = block_to_part(val[5], port);
+ addr_h = 0x40;
+ } else if(addr_h == 0x51) {
+ port = 1;
+ p = block_to_part(val[5], port);
+ addr_h = 0x41;
+ }
+ addr = (((int32)addr_h)<<16 | ((int32)addr_m)<<8 | (int32)addr_l);
+
+ switch(addr_h) {
+ case 0x40:
+ if((addr & 0xFFF000) == 0x401000) {
+ switch(addr & 0xFF) {
+ case 0x00: /* Tone Number */
+ SETMIDIEVENT(evm[0], 0, ME_TONE_BANK_MSB, p,val[7], SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_PROGRAM, p, val[8], SYSEX_TAG);
+ num_events += 2;
+ break;
+ case 0x02: /* Rx. Channel */
+ if (val[7] == 0x10) {
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB,
+ block_to_part(val[5],
+ midi_port_number ^ port), 0x80, 0x45);
+ } else {
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB,
+ block_to_part(val[5],
+ midi_port_number ^ port),
+ MERGE_CHANNEL_PORT2(val[7],
+ midi_port_number ^ port), 0x45);
+ }
+ num_events++;
+ break;
+ case 0x03: /* Rx. Pitch Bend */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x48);
+ num_events++;
+ break;
+ case 0x04: /* Rx. Channel Pressure */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x49);
+ num_events++;
+ break;
+ case 0x05: /* Rx. Program Change */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4A);
+ num_events++;
+ break;
+ case 0x06: /* Rx. Control Change */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4B);
+ num_events++;
+ break;
+ case 0x07: /* Rx. Poly Pressure */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4C);
+ num_events++;
+ break;
+ case 0x08: /* Rx. Note Message */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4D);
+ num_events++;
+ break;
+ case 0x09: /* Rx. RPN */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4E);
+ num_events++;
+ break;
+ case 0x0A: /* Rx. NRPN */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x4F);
+ num_events++;
+ break;
+ case 0x0B: /* Rx. Modulation */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x50);
+ num_events++;
+ break;
+ case 0x0C: /* Rx. Volume */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x51);
+ num_events++;
+ break;
+ case 0x0D: /* Rx. Panpot */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x52);
+ num_events++;
+ break;
+ case 0x0E: /* Rx. Expression */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x53);
+ num_events++;
+ break;
+ case 0x0F: /* Rx. Hold1 */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x54);
+ num_events++;
+ break;
+ case 0x10: /* Rx. Portamento */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x55);
+ num_events++;
+ break;
+ case 0x11: /* Rx. Sostenuto */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x56);
+ num_events++;
+ break;
+ case 0x12: /* Rx. Soft */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x57);
+ num_events++;
+ break;
+ case 0x13: /* MONO/POLY Mode */
+ if(val[7] == 0) {SETMIDIEVENT(evm[0], 0, ME_MONO, p, val[7], SYSEX_TAG);}
+ else {SETMIDIEVENT(evm[0], 0, ME_POLY, p, val[7], SYSEX_TAG);}
+ num_events++;
+ break;
+ case 0x14: /* Assign Mode */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x24);
+ num_events++;
+ break;
+ case 0x15: /* Use for Rhythm Part */
+ if(val[7]) {
+ rhythm_part[val[7] - 1] = p;
+ }
+ break;
+ case 0x16: /* Pitch Key Shift (dummy. see parse_sysex_event()) */
+ break;
+ case 0x17: /* Pitch Offset Fine */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x26);
+ num_events++;
+ break;
+ case 0x19: /* Part Level */
+ SETMIDIEVENT(evm[0], 0, ME_MAINVOLUME, p, val[7], SYSEX_TAG);
+ num_events++;
+ break;
+ case 0x1A: /* Velocity Sense Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x21);
+ num_events++;
+ break;
+ case 0x1B: /* Velocity Sense Offset */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x22);
+ num_events++;
+ break;
+ case 0x1C: /* Part Panpot */
+ if (val[7] == 0) {
+ SETMIDIEVENT(evm[0], 0, ME_RANDOM_PAN, p, 0, SYSEX_TAG);
+ } else {
+ SETMIDIEVENT(evm[0], 0, ME_PAN, p, val[7], SYSEX_TAG);
+ }
+ num_events++;
+ break;
+ case 0x1D: /* Keyboard Range Low */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x42);
+ num_events++;
+ break;
+ case 0x1E: /* Keyboard Range High */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x43);
+ num_events++;
+ break;
+ case 0x1F: /* CC1 Controller Number */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "CC1 Controller Number is not supported. (CH:%d VAL:%d)", p, val[7]);
+ break;
+ case 0x20: /* CC2 Controller Number */
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "CC2 Controller Number is not supported. (CH:%d VAL:%d)", p, val[7]);
+ break;
+ case 0x21: /* Chorus Send Level */
+ SETMIDIEVENT(evm[0], 0, ME_CHORUS_EFFECT, p, val[7], SYSEX_TAG);
+ num_events++;
+ break;
+ case 0x22: /* Reverb Send Level */
+ SETMIDIEVENT(evm[0], 0, ME_REVERB_EFFECT, p, val[7], SYSEX_TAG);
+ num_events++;
+ break;
+ case 0x23: /* Rx. Bank Select */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x58);
+ num_events++;
+ break;
+ case 0x24: /* Rx. Bank Select LSB */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x59);
+ num_events++;
+ break;
+ case 0x2C: /* Delay Send Level */
+ SETMIDIEVENT(evm[0], 0, ME_CELESTE_EFFECT, p, val[7], SYSEX_TAG);
+ num_events++;
+ break;
+ case 0x2A: /* Pitch Fine Tune */
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x00, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG);
+ SETMIDIEVENT(evm[3], 0, ME_DATA_ENTRY_LSB, p, val[8], SYSEX_TAG);
+ num_events += 4;
+ break;
+ case 0x30: /* TONE MODIFY1: Vibrato Rate */
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x08, SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x31: /* TONE MODIFY2: Vibrato Depth */
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x09, SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x32: /* TONE MODIFY3: TVF Cutoff Freq */
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x20, SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x33: /* TONE MODIFY4: TVF Resonance */
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x21, SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x34: /* TONE MODIFY5: TVF&TVA Env.attack */
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x63, SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x35: /* TONE MODIFY6: TVF&TVA Env.decay */
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x64, SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x36: /* TONE MODIFY7: TVF&TVA Env.release */
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x66, SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x37: /* TONE MODIFY8: Vibrato Delay */
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, p, 0x01, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, p, 0x0A, SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x40: /* Scale Tuning */
+ for (i = 0; i < 12; i++) {
+ SETMIDIEVENT(evm[i],
+ 0, ME_SCALE_TUNING, p, i, val[i + 7] - 64);
+ ctl->cmsg(CMSG_INFO, VERB_NOISY,
+ "Scale Tuning %s (CH:%d %d cent)",
+ note_name[i], p, val[i + 7] - 64);
+ }
+ num_events += 12;
+ break;
+ default:
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)",addr_h,addr_m,addr_l,val[7],val[8]);
+ break;
+ }
+ } else if((addr & 0xFFF000) == 0x402000) {
+ switch(addr & 0xFF) {
+ case 0x00: /* MOD Pitch Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x16);
+ num_events++;
+ break;
+ case 0x01: /* MOD TVF Cutoff Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x17);
+ num_events++;
+ break;
+ case 0x02: /* MOD Amplitude Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x18);
+ num_events++;
+ break;
+ case 0x03: /* MOD LFO1 Rate Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x19);
+ num_events++;
+ break;
+ case 0x04: /* MOD LFO1 Pitch Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1A);
+ num_events++;
+ break;
+ case 0x05: /* MOD LFO1 TVF Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1B);
+ num_events++;
+ break;
+ case 0x06: /* MOD LFO1 TVA Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1C);
+ num_events++;
+ break;
+ case 0x07: /* MOD LFO2 Rate Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1D);
+ num_events++;
+ break;
+ case 0x08: /* MOD LFO2 Pitch Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1E);
+ num_events++;
+ break;
+ case 0x09: /* MOD LFO2 TVF Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x1F);
+ num_events++;
+ break;
+ case 0x0A: /* MOD LFO2 TVA Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x20);
+ num_events++;
+ break;
+ case 0x10: /* !!!FIXME!!! Bend Pitch Control */
+ SETMIDIEVENT(evm[0], 0, ME_RPN_MSB, p, 0, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_RPN_LSB, p, 0, SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, p, (val[7] - 0x40) & 0x7F, SYSEX_TAG);
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x21);
+ num_events += 4;
+ break;
+ case 0x11: /* Bend TVF Cutoff Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x22);
+ num_events++;
+ break;
+ case 0x12: /* Bend Amplitude Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x23);
+ num_events++;
+ break;
+ case 0x13: /* Bend LFO1 Rate Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x24);
+ num_events++;
+ break;
+ case 0x14: /* Bend LFO1 Pitch Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x25);
+ num_events++;
+ break;
+ case 0x15: /* Bend LFO1 TVF Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x26);
+ num_events++;
+ break;
+ case 0x16: /* Bend LFO1 TVA Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x27);
+ num_events++;
+ break;
+ case 0x17: /* Bend LFO2 Rate Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x28);
+ num_events++;
+ break;
+ case 0x18: /* Bend LFO2 Pitch Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x29);
+ num_events++;
+ break;
+ case 0x19: /* Bend LFO2 TVF Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2A);
+ num_events++;
+ break;
+ case 0x1A: /* Bend LFO2 TVA Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2B);
+ num_events++;
+ break;
+ case 0x20: /* CAf Pitch Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x00);
+ num_events++;
+ break;
+ case 0x21: /* CAf TVF Cutoff Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x01);
+ num_events++;
+ break;
+ case 0x22: /* CAf Amplitude Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x02);
+ num_events++;
+ break;
+ case 0x23: /* CAf LFO1 Rate Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x03);
+ num_events++;
+ break;
+ case 0x24: /* CAf LFO1 Pitch Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x04);
+ num_events++;
+ break;
+ case 0x25: /* CAf LFO1 TVF Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x05);
+ num_events++;
+ break;
+ case 0x26: /* CAf LFO1 TVA Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x06);
+ num_events++;
+ break;
+ case 0x27: /* CAf LFO2 Rate Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x07);
+ num_events++;
+ break;
+ case 0x28: /* CAf LFO2 Pitch Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x08);
+ num_events++;
+ break;
+ case 0x29: /* CAf LFO2 TVF Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x09);
+ num_events++;
+ break;
+ case 0x2A: /* CAf LFO2 TVA Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0A);
+ num_events++;
+ break;
+ case 0x30: /* PAf Pitch Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0B);
+ num_events++;
+ break;
+ case 0x31: /* PAf TVF Cutoff Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0C);
+ num_events++;
+ break;
+ case 0x32: /* PAf Amplitude Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0D);
+ num_events++;
+ break;
+ case 0x33: /* PAf LFO1 Rate Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0E);
+ num_events++;
+ break;
+ case 0x34: /* PAf LFO1 Pitch Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x0F);
+ num_events++;
+ break;
+ case 0x35: /* PAf LFO1 TVF Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x10);
+ num_events++;
+ break;
+ case 0x36: /* PAf LFO1 TVA Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x11);
+ num_events++;
+ break;
+ case 0x37: /* PAf LFO2 Rate Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x12);
+ num_events++;
+ break;
+ case 0x38: /* PAf LFO2 Pitch Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x13);
+ num_events++;
+ break;
+ case 0x39: /* PAf LFO2 TVF Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x14);
+ num_events++;
+ break;
+ case 0x3A: /* PAf LFO2 TVA Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x15);
+ num_events++;
+ break;
+ case 0x40: /* CC1 Pitch Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2C);
+ num_events++;
+ break;
+ case 0x41: /* CC1 TVF Cutoff Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2D);
+ num_events++;
+ break;
+ case 0x42: /* CC1 Amplitude Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2E);
+ num_events++;
+ break;
+ case 0x43: /* CC1 LFO1 Rate Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x2F);
+ num_events++;
+ break;
+ case 0x44: /* CC1 LFO1 Pitch Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x30);
+ num_events++;
+ break;
+ case 0x45: /* CC1 LFO1 TVF Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x31);
+ num_events++;
+ break;
+ case 0x46: /* CC1 LFO1 TVA Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x32);
+ num_events++;
+ break;
+ case 0x47: /* CC1 LFO2 Rate Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x33);
+ num_events++;
+ break;
+ case 0x48: /* CC1 LFO2 Pitch Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x34);
+ num_events++;
+ break;
+ case 0x49: /* CC1 LFO2 TVF Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x35);
+ num_events++;
+ break;
+ case 0x4A: /* CC1 LFO2 TVA Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x36);
+ num_events++;
+ break;
+ case 0x50: /* CC2 Pitch Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x37);
+ num_events++;
+ break;
+ case 0x51: /* CC2 TVF Cutoff Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x38);
+ num_events++;
+ break;
+ case 0x52: /* CC2 Amplitude Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x39);
+ num_events++;
+ break;
+ case 0x53: /* CC2 LFO1 Rate Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3A);
+ num_events++;
+ break;
+ case 0x54: /* CC2 LFO1 Pitch Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3B);
+ num_events++;
+ break;
+ case 0x55: /* CC2 LFO1 TVF Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3C);
+ num_events++;
+ break;
+ case 0x56: /* CC2 LFO1 TVA Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3D);
+ num_events++;
+ break;
+ case 0x57: /* CC2 LFO2 Rate Control */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3E);
+ num_events++;
+ break;
+ case 0x58: /* CC2 LFO2 Pitch Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x3F);
+ num_events++;
+ break;
+ case 0x59: /* CC2 LFO2 TVF Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x40);
+ num_events++;
+ break;
+ case 0x5A: /* CC2 LFO2 TVA Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_LSB, p, val[7], 0x41);
+ num_events++;
+ break;
+ default:
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)",addr_h,addr_m,addr_l,val[7],val[8]);
+ break;
+ }
+ } else if((addr & 0xFFFF00) == 0x400100) {
+ switch(addr & 0xFF) {
+ case 0x30: /* Reverb Macro */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x05);
+ num_events++;
+ break;
+ case 0x31: /* Reverb Character */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x06);
+ num_events++;
+ break;
+ case 0x32: /* Reverb Pre-LPF */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x07);
+ num_events++;
+ break;
+ case 0x33: /* Reverb Level */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x08);
+ num_events++;
+ break;
+ case 0x34: /* Reverb Time */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x09);
+ num_events++;
+ break;
+ case 0x35: /* Reverb Delay Feedback */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0A);
+ num_events++;
+ break;
+ case 0x36: /* Unknown Reverb Parameter */
+ break;
+ case 0x37: /* Reverb Predelay Time */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0C);
+ num_events++;
+ break;
+ case 0x38: /* Chorus Macro */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0D);
+ num_events++;
+ break;
+ case 0x39: /* Chorus Pre-LPF */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0E);
+ num_events++;
+ break;
+ case 0x3A: /* Chorus Level */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x0F);
+ num_events++;
+ break;
+ case 0x3B: /* Chorus Feedback */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x10);
+ num_events++;
+ break;
+ case 0x3C: /* Chorus Delay */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x11);
+ num_events++;
+ break;
+ case 0x3D: /* Chorus Rate */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x12);
+ num_events++;
+ break;
+ case 0x3E: /* Chorus Depth */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x13);
+ num_events++;
+ break;
+ case 0x3F: /* Chorus Send Level to Reverb */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x14);
+ num_events++;
+ break;
+ case 0x40: /* Chorus Send Level to Delay */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x15);
+ num_events++;
+ break;
+ case 0x50: /* Delay Macro */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x16);
+ num_events++;
+ break;
+ case 0x51: /* Delay Pre-LPF */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x17);
+ num_events++;
+ break;
+ case 0x52: /* Delay Time Center */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x18);
+ num_events++;
+ break;
+ case 0x53: /* Delay Time Ratio Left */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x19);
+ num_events++;
+ break;
+ case 0x54: /* Delay Time Ratio Right */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1A);
+ num_events++;
+ break;
+ case 0x55: /* Delay Level Center */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1B);
+ num_events++;
+ break;
+ case 0x56: /* Delay Level Left */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1C);
+ num_events++;
+ break;
+ case 0x57: /* Delay Level Right */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1D);
+ num_events++;
+ break;
+ case 0x58: /* Delay Level */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1E);
+ num_events++;
+ break;
+ case 0x59: /* Delay Feedback */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x1F);
+ num_events++;
+ break;
+ case 0x5A: /* Delay Send Level to Reverb */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x20);
+ num_events++;
+ break;
+ default:
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)",addr_h,addr_m,addr_l,val[7],val[8]);
+ break;
+ }
+ } else if((addr & 0xFFFF00) == 0x400200) {
+ switch(addr & 0xFF) { /* EQ Parameter */
+ case 0x00: /* EQ LOW FREQ */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x01);
+ num_events++;
+ break;
+ case 0x01: /* EQ LOW GAIN */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x02);
+ num_events++;
+ break;
+ case 0x02: /* EQ HIGH FREQ */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x03);
+ num_events++;
+ break;
+ case 0x03: /* EQ HIGH GAIN */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x04);
+ num_events++;
+ break;
+ default:
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)",addr_h,addr_m,addr_l,val[7],val[8]);
+ break;
+ }
+ } else if((addr & 0xFFFF00) == 0x400300) {
+ switch(addr & 0xFF) { /* Insertion Effect Parameter */
+ case 0x00:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x27);
+ SETMIDIEVENT(evm[1], 0, ME_SYSEX_GS_LSB, p, val[8], 0x28);
+ num_events += 2;
+ break;
+ case 0x03:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x29);
+ num_events++;
+ break;
+ case 0x04:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2A);
+ num_events++;
+ break;
+ case 0x05:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2B);
+ num_events++;
+ break;
+ case 0x06:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2C);
+ num_events++;
+ break;
+ case 0x07:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2D);
+ num_events++;
+ break;
+ case 0x08:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2E);
+ num_events++;
+ break;
+ case 0x09:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x2F);
+ num_events++;
+ break;
+ case 0x0A:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x30);
+ num_events++;
+ break;
+ case 0x0B:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x31);
+ num_events++;
+ break;
+ case 0x0C:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x32);
+ num_events++;
+ break;
+ case 0x0D:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x33);
+ num_events++;
+ break;
+ case 0x0E:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x34);
+ num_events++;
+ break;
+ case 0x0F:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x35);
+ num_events++;
+ break;
+ case 0x10:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x36);
+ num_events++;
+ break;
+ case 0x11:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x37);
+ num_events++;
+ break;
+ case 0x12:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x38);
+ num_events++;
+ break;
+ case 0x13:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x39);
+ num_events++;
+ break;
+ case 0x14:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3A);
+ num_events++;
+ break;
+ case 0x15:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3B);
+ num_events++;
+ break;
+ case 0x16:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3C);
+ num_events++;
+ break;
+ case 0x17:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3D);
+ num_events++;
+ break;
+ case 0x18:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3E);
+ num_events++;
+ break;
+ case 0x19:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x3F);
+ num_events++;
+ break;
+ case 0x1B:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x40);
+ num_events++;
+ break;
+ case 0x1C:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x41);
+ num_events++;
+ break;
+ case 0x1D:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x42);
+ num_events++;
+ break;
+ case 0x1E:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x43);
+ num_events++;
+ break;
+ case 0x1F:
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x44);
+ num_events++;
+ break;
+ default:
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)",addr_h,addr_m,addr_l,val[7],val[8]);
+ break;
+ }
+ } else if((addr & 0xFFF000) == 0x404000) {
+ switch(addr & 0xFF) {
+ case 0x00: /* TONE MAP NUMBER */
+ SETMIDIEVENT(evm[0], 0, ME_TONE_BANK_LSB, p, val[7], SYSEX_TAG);
+ num_events++;
+ break;
+ case 0x01: /* TONE MAP-0 NUMBER */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x25);
+ num_events++;
+ break;
+ case 0x20: /* EQ ON/OFF */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x00);
+ num_events++;
+ break;
+ case 0x22: /* EFX ON/OFF */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB, p, val[7], 0x23);
+ num_events++;
+ break;
+ default:
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)",addr_h,addr_m,addr_l,val[7],val[8]);
+ break;
+ }
+ }
+ break;
+ case 0x41:
+ switch(addr & 0xF00) {
+ case 0x100: /* Play Note Number */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_MSB, dp, val[6], 0);
+ SETMIDIEVENT(evm[1], 0, ME_SYSEX_GS_LSB, dp, val[7], 0x47);
+ num_events += 2;
+ break;
+ case 0x200:
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1A, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x400:
+ SETMIDIEVENT(evm[0], 0,ME_NRPN_MSB, dp, 0x1C, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0,ME_NRPN_LSB, dp, val[6], SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0,ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x500:
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1D, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x600:
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1E, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x700: /* Rx. Note Off */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_MSB, dp, val[6], 0);
+ SETMIDIEVENT(evm[1], 0, ME_SYSEX_LSB, dp, val[7], 0x46);
+ num_events += 2;
+ break;
+ case 0x800: /* Rx. Note On */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_MSB, dp, val[6], 0);
+ SETMIDIEVENT(evm[1], 0, ME_SYSEX_LSB, dp, val[7], 0x47);
+ num_events += 2;
+ break;
+ case 0x900:
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1F, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ default:
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)",addr_h,addr_m,addr_l,val[7],val[8]);
+ break;
+ }
+ break;
+#if 0
+ case 0x20: /* User Instrument */
+ switch(addr & 0xF00) {
+ case 0x000: /* Source Map */
+ get_userinst(64 + udn, val[6])->source_map = val[7];
+ break;
+ case 0x100: /* Source Bank */
+ get_userinst(64 + udn, val[6])->source_bank = val[7];
+ break;
+#if !defined(TIMIDITY_TOOLS)
+ case 0x200: /* Source Prog */
+ get_userinst(64 + udn, val[6])->source_prog = val[7];
+ break;
+#endif
+ default:
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)",addr_h,addr_m,addr_l,val[7],val[8]);
+ break;
+ }
+ break;
+#endif
+ case 0x21: /* User Drumset */
+ switch(addr & 0xF00) {
+ case 0x100: /* Play Note */
+ get_userdrum(64 + udn, val[6])->play_note = val[7];
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_MSB, dp, val[6], 0);
+ SETMIDIEVENT(evm[1], 0, ME_SYSEX_GS_LSB, dp, val[7], 0x47);
+ num_events += 2;
+ break;
+ case 0x200: /* Level */
+ get_userdrum(64 + udn, val[6])->level = val[7];
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1A, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x300: /* Assign Group */
+ get_userdrum(64 + udn, val[6])->assign_group = val[7];
+ if(val[7] != 0) {recompute_userdrum_altassign(udn + 64, val[7]);}
+ break;
+ case 0x400: /* Panpot */
+ get_userdrum(64 + udn, val[6])->pan = val[7];
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1C, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x500: /* Reverb Send Level */
+ get_userdrum(64 + udn, val[6])->reverb_send_level = val[7];
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1D, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x600: /* Chorus Send Level */
+ get_userdrum(64 + udn, val[6])->chorus_send_level = val[7];
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1E, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0x700: /* Rx. Note Off */
+ get_userdrum(64 + udn, val[6])->rx_note_off = val[7];
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_MSB, dp, val[6], 0);
+ SETMIDIEVENT(evm[1], 0, ME_SYSEX_LSB, dp, val[7], 0x46);
+ num_events += 2;
+ break;
+ case 0x800: /* Rx. Note On */
+ get_userdrum(64 + udn, val[6])->rx_note_on = val[7];
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_MSB, dp, val[6], 0);
+ SETMIDIEVENT(evm[1], 0, ME_SYSEX_LSB, dp, val[7], 0x47);
+ num_events += 2;
+ break;
+ case 0x900: /* Delay Send Level */
+ get_userdrum(64 + udn, val[6])->delay_send_level = val[7];
+ SETMIDIEVENT(evm[0], 0, ME_NRPN_MSB, dp, 0x1F, SYSEX_TAG);
+ SETMIDIEVENT(evm[1], 0, ME_NRPN_LSB, dp, val[6], SYSEX_TAG);
+ SETMIDIEVENT(evm[2], 0, ME_DATA_ENTRY_MSB, dp, val[7], SYSEX_TAG);
+ num_events += 3;
+ break;
+ case 0xA00: /* Source Map */
+ get_userdrum(64 + udn, val[6])->source_map = val[7];
+ break;
+ case 0xB00: /* Source Prog */
+ get_userdrum(64 + udn, val[6])->source_prog = val[7];
+ break;
+#if !defined(TIMIDITY_TOOLS)
+ case 0xC00: /* Source Note */
+ get_userdrum(64 + udn, val[6])->source_note = val[7];
+ break;
+#endif
+ default:
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"Unsupported GS SysEx. (ADDR:%02X %02X %02X VAL:%02X %02X)",addr_h,addr_m,addr_l,val[7],val[8]);
+ break;
+ }
+ break;
+ case 0x00: /* System */
+ switch (addr & 0xfff0) {
+ case 0x0100: /* Channel Msg Rx Port (A) */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB,
+ block_to_part(addr & 0xf, 0), val[7], 0x46);
+ num_events++;
+ break;
+ case 0x0110: /* Channel Msg Rx Port (B) */
+ SETMIDIEVENT(evm[0], 0, ME_SYSEX_GS_LSB,
+ block_to_part(addr & 0xf, 1), val[7], 0x46);
+ num_events++;
+ break;
+ default:
+ /* ctl->cmsg(CMSG_INFO,VERB_NOISY, "Unsupported GS SysEx. "
+ "(ADDR:%02X %02X %02X VAL:%02X %02X)",
+ addr_h, addr_m, addr_l, val[7], val[8]);*/
+ break;
+ }
+ break;
+ }
+ }
+
+ /* Non-RealTime / RealTime Universal SysEx messages
+ * 0 0x7e(Non-RealTime) / 0x7f(RealTime)
+ * 1 SysEx device ID. Could be from 0x00 to 0x7f.
+ * 0x7f means disregard device.
+ * 2 Sub ID
+ * ...
+ * E 0xf7
+ */
+ else if (len > 4 && val[0] >= 0x7e)
+ switch (val[2]) {
+ case 0x01: /* Sample Dump header */
+ case 0x02: /* Sample Dump packet */
+ case 0x03: /* Dump Request */
+ case 0x04: /* Device Control */
+ if (val[3] == 0x05) { /* Global Parameter Control */
+ if (val[7] == 0x01 && val[8] == 0x01) { /* Reverb */
+ for (i = 9; i < len && val[i] != 0xf7; i+= 2) {
+ switch(val[i]) {
+ case 0x00: /* Reverb Type */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, 0, val[i + 1], 0x60);
+ num_events++;
+ break;
+ case 0x01: /* Reverb Time */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x09);
+ num_events++;
+ break;
+ }
+ }
+ } else if (val[7] == 0x01 && val[8] == 0x02) { /* Chorus */
+ for (i = 9; i < len && val[i] != 0xf7; i+= 2) {
+ switch(val[i]) {
+ case 0x00: /* Chorus Type */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, 0, val[i + 1], 0x61);
+ num_events++;
+ break;
+ case 0x01: /* Modulation Rate */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x12);
+ num_events++;
+ break;
+ case 0x02: /* Modulation Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x13);
+ num_events++;
+ break;
+ case 0x03: /* Feedback */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x10);
+ num_events++;
+ break;
+ case 0x04: /* Send To Reverb */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_GS_LSB, 0, val[i + 1], 0x14);
+ num_events++;
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case 0x05: /* Sample Dump extensions */
+ case 0x06: /* Inquiry Message */
+ case 0x07: /* File Dump */
+ break;
+ case 0x08: /* MIDI Tuning Standard */
+ switch (val[3]) {
+ case 0x01:
+ SETMIDIEVENT(evm[0], 0, ME_BULK_TUNING_DUMP, 0, val[4], 0);
+ for (i = 0; i < 128; i++) {
+ SETMIDIEVENT(evm[i * 2 + 1], 0, ME_BULK_TUNING_DUMP,
+ 1, i, val[i * 3 + 21]);
+ SETMIDIEVENT(evm[i * 2 + 2], 0, ME_BULK_TUNING_DUMP,
+ 2, val[i * 3 + 22], val[i * 3 + 23]);
+ }
+ num_events += 257;
+ break;
+ case 0x02:
+ SETMIDIEVENT(evm[0], 0, ME_SINGLE_NOTE_TUNING,
+ 0, val[4], 0);
+ for (i = 0; i < val[5]; i++) {
+ SETMIDIEVENT(evm[i * 2 + 1], 0, ME_SINGLE_NOTE_TUNING,
+ 1, val[i * 4 + 6], val[i * 4 + 7]);
+ SETMIDIEVENT(evm[i * 2 + 2], 0, ME_SINGLE_NOTE_TUNING,
+ 2, val[i * 4 + 8], val[i * 4 + 9]);
+ }
+ num_events += val[5] * 2 + 1;
+ break;
+ case 0x0b:
+ channel_tt = ((val[4] & 0x03) << 14 | val[5] << 7 | val[6])
+ << ((val[4] >> 2) * 16);
+ if (val[1] == 0x7f) {
+ SETMIDIEVENT(evm[0], 0, ME_MASTER_TEMPER_TYPE,
+ 0, val[7], (val[0] == 0x7f));
+ num_events++;
+ } else {
+ for (i = j = 0; i < 32; i++)
+ if (channel_tt & 1 << i) {
+ SETMIDIEVENT(evm[j], 0, ME_TEMPER_TYPE,
+ MERGE_CHANNEL_PORT(i),
+ val[7], (val[0] == 0x7f));
+ j++;
+ }
+ num_events += j;
+ }
+ break;
+ case 0x0c:
+ SETMIDIEVENT(evm[0], 0, ME_USER_TEMPER_ENTRY,
+ 0, val[4], val[21]);
+ for (i = 0; i < val[21]; i++) {
+ SETMIDIEVENT(evm[i * 5 + 1], 0, ME_USER_TEMPER_ENTRY,
+ 1, val[i * 10 + 22], val[i * 10 + 23]);
+ SETMIDIEVENT(evm[i * 5 + 2], 0, ME_USER_TEMPER_ENTRY,
+ 2, val[i * 10 + 24], val[i * 10 + 25]);
+ SETMIDIEVENT(evm[i * 5 + 3], 0, ME_USER_TEMPER_ENTRY,
+ 3, val[i * 10 + 26], val[i * 10 + 27]);
+ SETMIDIEVENT(evm[i * 5 + 4], 0, ME_USER_TEMPER_ENTRY,
+ 4, val[i * 10 + 28], val[i * 10 + 29]);
+ SETMIDIEVENT(evm[i * 5 + 5], 0, ME_USER_TEMPER_ENTRY,
+ 5, val[i * 10 + 30], val[i * 10 + 31]);
+ }
+ num_events += val[21] * 5 + 1;
+ break;
+ }
+ break;
+ case 0x09: /* General MIDI Message */
+ switch(val[3]) {
+ case 0x01: /* Channel Pressure */
+ for (i = 5; i < len && val[i] != 0xf7; i+= 2) {
+ switch(val[i]) {
+ case 0x00: /* Pitch Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x00);
+ num_events++;
+ break;
+ case 0x01: /* Filter Cutoff Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x01);
+ num_events++;
+ break;
+ case 0x02: /* Amplitude Control */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x02);
+ num_events++;
+ break;
+ case 0x03: /* LFO Pitch Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x04);
+ num_events++;
+ break;
+ case 0x04: /* LFO Filter Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x05);
+ num_events++;
+ break;
+ case 0x05: /* LFO Amplitude Depth */
+ SETMIDIEVENT(evm[num_events], 0, ME_SYSEX_LSB, val[4], val[i + 1], 0x06);
+ num_events++;
+ break;
+ }
+ }
+ break;
+ }
+ break;
+ case 0x7b: /* End of File */
+ case 0x7c: /* Handshaking Message: Wait */
+ case 0x7d: /* Handshaking Message: Cancel */
+ case 0x7e: /* Handshaking Message: NAK */
+ case 0x7f: /* Handshaking Message: ACK */
+ break;
+ }
+
+ return(num_events);
+}
+
+int parse_sysex_event(uint8 *val, int32 len, MidiEvent *ev)
+{
+ uint16 vol;
+
+ if(current_file_info->mid == 0 || current_file_info->mid >= 0x7e)
+ current_file_info->mid = val[0];
+
+ if(len >= 10 &&
+ val[0] == 0x41 && /* Roland ID */
+ val[1] == 0x10 && /* Device ID */
+ val[2] == 0x42 && /* GS Model ID */
+ val[3] == 0x12) /* Data Set Command */
+ {
+ /* Roland GS-Based Synthesizers.
+ * val[4..6] is address, val[7..len-2] is body.
+ *
+ * GS Channel part number
+ * 0 10
+ * 1-9 1-9
+ * 10-15 11-16
+ */
+
+ int32 addr,checksum,i; /* SysEx address */
+ uint8 *body; /* SysEx body */
+ uint8 p,gslen; /* Channel part number [0..15] */
+
+ /* check Checksum */
+ checksum = 0;
+ for(gslen = 9; gslen < len; gslen++)
+ if(val[gslen] == 0xF7)
+ break;
+ for(i=4;i<gslen-1;i++) {
+ checksum += val[i];
+ }
+ if(((128 - (checksum & 0x7F)) & 0x7F) != val[gslen-1]) {
+ return 0;
+ }
+
+ addr = (((int32)val[4])<<16 |
+ ((int32)val[5])<<8 |
+ (int32)val[6]);
+ body = val + 7;
+ p = (uint8)((addr >> 8) & 0xF);
+ if(p == 0)
+ p = 9;
+ else if(p <= 9)
+ p--;
+ p = MERGE_CHANNEL_PORT(p);
+
+ if(val[4] == 0x50) { /* for double module mode */
+ p += 16;
+ addr = (((int32)0x40)<<16 |
+ ((int32)val[5])<<8 |
+ (int32)val[6]);
+ } else { /* single module mode */
+ addr = (((int32)val[4])<<16 |
+ ((int32)val[5])<<8 |
+ (int32)val[6]);
+ }
+
+ if((addr & 0xFFF0FF) == 0x401015) /* Rhythm Parts */
+ {
+#ifdef GS_DRUMPART
+/* GS drum part check from Masaaki Koyanagi's patch (GS_Drum_Part_Check()) */
+/* Modified by Masanao Izumo */
+ SETMIDIEVENT(*ev, 0, ME_DRUMPART, p, *body, SYSEX_TAG);
+ return 1;
+#else
+ return 0;
+#endif /* GS_DRUMPART */
+ }
+
+ if((addr & 0xFFF0FF) == 0x401016) /* Key Shift */
+ {
+ SETMIDIEVENT(*ev, 0, ME_KEYSHIFT, p, *body, SYSEX_TAG);
+ return 1;
+ }
+
+ if(addr == 0x400000) /* Master Tune, not for SMF */
+ {
+ uint16 tune = ((body[1] & 0xF) << 8) | ((body[2] & 0xF) << 4) | (body[3] & 0xF);
+
+ if (tune < 0x18)
+ tune = 0x18;
+ else if (tune > 0x7E8)
+ tune = 0x7E8;
+ SETMIDIEVENT(*ev, 0, ME_MASTER_TUNING, 0, tune & 0xFF, (tune >> 8) & 0x7F);
+ return 1;
+ }
+
+ if(addr == 0x400004) /* Master Volume */
+ {
+ vol = gs_convert_master_vol(*body);
+ SETMIDIEVENT(*ev, 0, ME_MASTER_VOLUME,
+ 0, vol & 0xFF, (vol >> 8) & 0xFF);
+ return 1;
+ }
+
+ if((addr & 0xFFF0FF) == 0x401019) /* Volume on/off */
+ {
+#if 0
+ SETMIDIEVENT(*ev, 0, ME_VOLUME_ONOFF, p, *body >= 64, SYSEX_TAG);
+#endif
+ return 0;
+ }
+
+ if((addr & 0xFFF0FF) == 0x401002) /* Receive channel on/off */
+ {
+#if 0
+ SETMIDIEVENT(*ev, 0, ME_RECEIVE_CHANNEL, (uint8)p, *body >= 64, SYSEX_TAG);
+#endif
+ return 0;
+ }
+
+ if(0x402000 <= addr && addr <= 0x402F5A) /* Controller Routing */
+ return 0;
+
+ if((addr & 0xFFF0FF) == 0x401040) /* Alternate Scale Tunings */
+ return 0;
+
+ if((addr & 0xFFFFF0) == 0x400130) /* Changing Effects */
+ {
+ struct chorus_text_gs_t *chorus_text = &(chorus_status_gs.text);
+ switch(addr & 0xF)
+ {
+ case 0x8: /* macro */
+ memcpy(chorus_text->macro, body, 3);
+ break;
+ case 0x9: /* PRE-LPF */
+ memcpy(chorus_text->pre_lpf, body, 3);
+ break;
+ case 0xa: /* level */
+ memcpy(chorus_text->level, body, 3);
+ break;
+ case 0xb: /* feed back */
+ memcpy(chorus_text->feed_back, body, 3);
+ break;
+ case 0xc: /* delay */
+ memcpy(chorus_text->delay, body, 3);
+ break;
+ case 0xd: /* rate */
+ memcpy(chorus_text->rate, body, 3);
+ break;
+ case 0xe: /* depth */
+ memcpy(chorus_text->depth, body, 3);
+ break;
+ case 0xf: /* send level */
+ memcpy(chorus_text->send_level, body, 3);
+ break;
+ default: break;
+ }
+
+ check_chorus_text_start();
+ return 0;
+ }
+
+ if((addr & 0xFFF0FF) == 0x401003) /* Rx Pitch-Bend */
+ return 0;
+
+ if(addr == 0x400110) /* Voice Reserve */
+ {
+ if(len >= 25)
+ memcpy(chorus_status_gs.text.voice_reserve, body, 18);
+ check_chorus_text_start();
+ return 0;
+ }
+
+ if(addr == 0x40007F || /* GS Reset */
+ addr == 0x00007F) /* SC-88 Single Module */
+ {
+ SETMIDIEVENT(*ev, 0, ME_RESET, 0, GS_SYSTEM_MODE, SYSEX_TAG);
+ return 1;
+ }
+ return 0;
+ }
+
+ if(len > 9 &&
+ val[0] == 0x41 && /* Roland ID */
+ val[1] == 0x10 && /* Device ID */
+ val[2] == 0x45 &&
+ val[3] == 0x12 &&
+ val[4] == 0x10 &&
+ val[5] == 0x00 &&
+ val[6] == 0x00)
+ {
+ /* Text Insert for SC */
+ uint8 save;
+
+ len -= 2;
+ save = val[len];
+ val[len] = '\0';
+ if(readmidi_make_string_event(ME_INSERT_TEXT, (char *)val + 7, ev, 1))
+ {
+ val[len] = save;
+ return 1;
+ }
+ val[len] = save;
+ return 0;
+ }
+
+ if(len > 9 && /* GS lcd event. by T.Nogami*/
+ val[0] == 0x41 && /* Roland ID */
+ val[1] == 0x10 && /* Device ID */
+ val[2] == 0x45 &&
+ val[3] == 0x12 &&
+ val[4] == 0x10 &&
+ val[5] == 0x01 &&
+ val[6] == 0x00)
+ {
+ /* Text Insert for SC */
+ uint8 save;
+
+ len -= 2;
+ save = val[len];
+ val[len] = '\0';
+ if(readmidi_make_lcd_event(ME_GSLCD, (uint8 *)val + 7, ev))
+ {
+ val[len] = save;
+ return 1;
+ }
+ val[len] = save;
+ return 0;
+ }
+
+ /* val[1] can have values other than 0x10 for the XG ON event, which
+ * work on real XG hardware. I have several midi that use 0x1f instead
+ * of 0x10. playmidi.h lists 0x10 - 0x13 as MU50/80/90/100. I don't
+ * know what real world Device Number 0x1f would correspond to, but the
+ * XG spec says the entire 0x1n range is valid, and 0x1f works on real
+ * hardware, so I have modified the check below to accept the entire
+ * 0x1n range.
+ *
+ * I think there are/were some hacks somewhere in playmidi.c (?) to work
+ * around non- 0x10 values, but this fixes the root of the problem, which
+ * allows the server mode to handle XG initialization properly as well.
+ */
+ if(len >= 8 &&
+ val[0] == 0x43 &&
+ (val[1] >= 0x10 && val[1] <= 0x1f) &&
+ val[2] == 0x4C)
+ {
+ int addr = (val[3] << 16) | (val[4] << 8) | val[5];
+
+ if (addr == 0x00007E) /* XG SYSTEM ON */
+ {
+ SETMIDIEVENT(*ev, 0, ME_RESET, 0, XG_SYSTEM_MODE, SYSEX_TAG);
+ return 1;
+ }
+ else if (addr == 0x000000 && len >= 12) /* XG Master Tune */
+ {
+ uint16 tune = ((val[7] & 0xF) << 8) | ((val[8] & 0xF) << 4) | (val[9] & 0xF);
+
+ if (tune > 0x7FF)
+ tune = 0x7FF;
+ SETMIDIEVENT(*ev, 0, ME_MASTER_TUNING, 0, tune & 0xFF, (tune >> 8) & 0x7F);
+ return 1;
+ }
+ }
+
+ if (len >= 7 && val[0] == 0x7F && val[1] == 0x7F)
+ {
+ if (val[2] == 0x04 && val[3] == 0x03) /* GM2 Master Fine Tune */
+ {
+ uint16 tune = (val[4] & 0x7F) | (val[5] << 7) | 0x4000;
+
+ SETMIDIEVENT(*ev, 0, ME_MASTER_TUNING, 0, tune & 0xFF, (tune >> 8) & 0x7F);
+ return 1;
+ }
+ if (val[2] == 0x04 && val[3] == 0x04) /* GM2 Master Coarse Tune */
+ {
+ uint8 tune = val[5];
+
+ if (tune < 0x28)
+ tune = 0x28;
+ else if (tune > 0x58)
+ tune = 0x58;
+ SETMIDIEVENT(*ev, 0, ME_MASTER_TUNING, 0, tune, 0x80);
+ return 1;
+ }
+ }
+
+ /* Non-RealTime / RealTime Universal SysEx messages
+ * 0 0x7e(Non-RealTime) / 0x7f(RealTime)
+ * 1 SysEx device ID. Could be from 0x00 to 0x7f.
+ * 0x7f means disregard device.
+ * 2 Sub ID
+ * ...
+ * E 0xf7
+ */
+ if (len > 4 && val[0] >= 0x7e)
+ switch (val[2]) {
+ case 0x01: /* Sample Dump header */
+ case 0x02: /* Sample Dump packet */
+ case 0x03: /* Dump Request */
+ break;
+ case 0x04: /* MIDI Time Code Setup/Device Control */
+ switch (val[3]) {
+ case 0x01: /* Master Volume */
+ vol = gm_convert_master_vol(val[4], val[5]);
+ if (val[1] == 0x7f) {
+ SETMIDIEVENT(*ev, 0, ME_MASTER_VOLUME, 0,
+ vol & 0xff, vol >> 8 & 0xff);
+ } else {
+ SETMIDIEVENT(*ev, 0, ME_MAINVOLUME,
+ MERGE_CHANNEL_PORT(val[1]),
+ vol >> 8 & 0xff, 0);
+ }
+ return 1;
+ }
+ break;
+ case 0x05: /* Sample Dump extensions */
+ case 0x06: /* Inquiry Message */
+ case 0x07: /* File Dump */
+ break;
+ case 0x08: /* MIDI Tuning Standard */
+ switch (val[3]) {
+ case 0x0a:
+ SETMIDIEVENT(*ev, 0, ME_TEMPER_KEYSIG, 0,
+ val[4] - 0x40 + val[5] * 16, (val[0] == 0x7f));
+ return 1;
+ }
+ break;
+ case 0x09: /* General MIDI Message */
+ /* GM System Enable/Disable */
+ if(val[3] == 1) {
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG, "SysEx: GM System On");
+ SETMIDIEVENT(*ev, 0, ME_RESET, 0, GM_SYSTEM_MODE, 0);
+ } else if(val[3] == 3) {
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG, "SysEx: GM2 System On");
+ SETMIDIEVENT(*ev, 0, ME_RESET, 0, GM2_SYSTEM_MODE, 0);
+ } else {
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG, "SysEx: GM System Off");
+ SETMIDIEVENT(*ev, 0, ME_RESET, 0, DEFAULT_SYSTEM_MODE, 0);
+ }
+ return 1;
+ case 0x7b: /* End of File */
+ case 0x7c: /* Handshaking Message: Wait */
+ case 0x7d: /* Handshaking Message: Cancel */
+ case 0x7e: /* Handshaking Message: NAK */
+ case 0x7f: /* Handshaking Message: ACK */
+ break;
+ }
+
+ return 0;
+}
+
+static int read_sysex_event(int32 at, int me, int32 len,
+ struct timidity_file *tf)
+{
+ uint8 *val;
+ MidiEvent ev, evm[260]; /* maximum number of XG bulk dump events */
+ int ne, i;
+
+ if(len == 0)
+ return 0;
+ if(me != 0xF0)
+ {
+ skip(tf, len);
+ return 0;
+ }
+
+ val = (uint8 *)new_segment(&tmpbuffer, len);
+ if(tf_read(val, 1, len, tf) != len)
+ {
+ reuse_mblock(&tmpbuffer);
+ return -1;
+ }
+ if(parse_sysex_event(val, len, &ev))
+ {
+ ev.time = at;
+ readmidi_add_event(&ev);
+ }
+ if ((ne = parse_sysex_event_multi(val, len, evm)))
+ {
+ for (i = 0; i < ne; i++) {
+ evm[i].time = at;
+ readmidi_add_event(&evm[i]);
+ }
+ }
+
+ reuse_mblock(&tmpbuffer);
+
+ return 0;
+}
+
+static char *fix_string(char *s)
+{
+ int i, j, w;
+ char c;
+
+ if(s == NULL)
+ return NULL;
+ while(*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
+ s++;
+
+ /* s =~ tr/ \t\r\n/ /s; */
+ w = 0;
+ for(i = j = 0; (c = s[i]) != '\0'; i++)
+ {
+ if(c == '\t' || c == '\r' || c == '\n')
+ c = ' ';
+ if(w)
+ w = (c == ' ');
+ if(!w)
+ {
+ s[j++] = c;
+ w = (c == ' ');
+ }
+ }
+
+ /* s =~ s/ $//; */
+ if(j > 0 && s[j - 1] == ' ')
+ j--;
+
+ s[j] = '\0';
+ return s;
+}
+
+static void smf_time_signature(int32 at, struct timidity_file *tf, int len)
+{
+ int n, d, c, b;
+
+ /* Time Signature (nn dd cc bb)
+ * [0]: numerator
+ * [1]: denominator
+ * [2]: number of MIDI clocks in a metronome click
+ * [3]: number of notated 32nd-notes in a MIDI
+ * quarter-note (24 MIDI Clocks).
+ */
+
+ if(len != 4)
+ {
+ ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "Invalid time signature");
+ skip(tf, len);
+ return;
+ }
+
+ n = tf_getc(tf);
+ d = (1<<tf_getc(tf));
+ c = tf_getc(tf);
+ b = tf_getc(tf);
+
+ if(n == 0 || d == 0)
+ {
+ ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "Invalid time signature");
+ return;
+ }
+
+ MIDIEVENT(at, ME_TIMESIG, 0, n, d);
+ MIDIEVENT(at, ME_TIMESIG, 1, c, b);
+ ctl->cmsg(CMSG_INFO, VERB_NOISY,
+ "Time signature: %d/%d %d clock %d q.n.", n, d, c, b);
+ if(current_file_info->time_sig_n == -1)
+ {
+ current_file_info->time_sig_n = n;
+ current_file_info->time_sig_d = d;
+ current_file_info->time_sig_c = c;
+ current_file_info->time_sig_b = b;
+ }
+}
+
+static void smf_key_signature(int32 at, struct timidity_file *tf, int len)
+{
+ int8 sf, mi;
+ /* Key Signature (sf mi)
+ * sf = -7: 7 flats
+ * sf = -1: 1 flat
+ * sf = 0: key of C
+ * sf = 1: 1 sharp
+ * sf = 7: 7 sharps
+ * mi = 0: major key
+ * mi = 1: minor key
+ */
+
+ if (len != 2) {
+ ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "Invalid key signature");
+ skip(tf, len);
+ return;
+ }
+ sf = tf_getc(tf);
+ mi = tf_getc(tf);
+ if (sf < -7 || sf > 7) {
+ ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "Invalid key signature");
+ return;
+ }
+ if (mi != 0 && mi != 1) {
+ ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "Invalid key signature");
+ return;
+ }
+ MIDIEVENT(at, ME_KEYSIG, 0, sf, mi);
+ ctl->cmsg(CMSG_INFO, VERB_NOISY,
+ "Key signature: %d %s %s", abs(sf),
+ (sf < 0) ? "flat(s)" : "sharp(s)", (mi) ? "minor" : "major");
+}
+
+/* Used for WRD reader */
+int dump_current_timesig(MidiEvent *codes, int maxlen)
+{
+ int i, n;
+ MidiEventList *e;
+
+ if(maxlen <= 0 || evlist == NULL)
+ return 0;
+ n = 0;
+ for(i = 0, e = evlist; i < event_count; i++, e = e->next)
+ if(e->event.type == ME_TIMESIG && e->event.channel == 0)
+ {
+ if(n == 0 && e->event.time > 0)
+ {
+ /* 4/4 is default */
+ SETMIDIEVENT(codes[0], 0, ME_TIMESIG, 0, 4, 4);
+ n++;
+ if(maxlen == 1)
+ return 1;
+ }
+
+ if(n > 0)
+ {
+ if(e->event.a == codes[n - 1].a &&
+ e->event.b == codes[n - 1].b)
+ continue; /* Unchanged */
+ if(e->event.time == codes[n - 1].time)
+ n--; /* overwrite previous code */
+ }
+ codes[n++] = e->event;
+ if(n == maxlen)
+ return n;
+ }
+ return n;
+}
+
+/* Read a SMF track */
+static int read_smf_track(struct timidity_file *tf, int trackno, int rewindp)
+{
+ int32 len, next_pos, pos;
+ char tmp[4];
+ int lastchan, laststatus;
+ int me, type, a, b, c;
+ int i;
+ int32 smf_at_time;
+ int note_seen = (! opt_preserve_silence);
+
+ smf_at_time = readmidi_set_track(trackno, rewindp);
+
+ /* Check the formalities */
+ if((tf_read(tmp, 1, 4, tf) != 4) || (tf_read(&len, 4, 1, tf) != 1))
+ {
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "%s: Can't read track header.", current_filename);
+ return -1;
+ }
+ len = BE_LONG(len);
+ next_pos = tf_tell(tf) + len;
+ if(strncmp(tmp, "MTrk", 4))
+ {
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "%s: Corrupt MIDI file.", current_filename);
+ return -2;
+ }
+
+ lastchan = laststatus = 0;
+
+ for(;;)
+ {
+ if(readmidi_error_flag)
+ return -1;
+ if((len = getvl(tf)) < 0)
+ return -1;
+ smf_at_time += len;
+ errno = 0;
+ if((i = tf_getc(tf)) == EOF)
+ {
+ if(errno)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "%s: read_midi_event: %s",
+ current_filename, strerror(errno));
+ else
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "Warning: %s: Too shorten midi file.",
+ current_filename);
+ return -1;
+ }
+
+ me = (uint8)i;
+ if(me == 0xF0 || me == 0xF7) /* SysEx event */
+ {
+ if((len = getvl(tf)) < 0)
+ return -1;
+ if((i = read_sysex_event(smf_at_time, me, len, tf)) != 0)
+ return i;
+ }
+ else if(me == 0xFF) /* Meta event */
+ {
+ type = tf_getc(tf);
+ if((len = getvl(tf)) < 0)
+ return -1;
+ if(type > 0 && type < 16)
+ {
+ static char *label[] =
+ {
+ "Text event: ", "Text: ", "Copyright: ", "Track name: ",
+ "Instrument: ", "Lyric: ", "Marker: ", "Cue point: "
+ };
+
+ if(type == 5 || /* Lyric */
+ (type == 1 && (opt_trace_text_meta_event ||
+ karaoke_format == 2 ||
+ chorus_status_gs.text.status == CHORUS_ST_OK)) ||
+ (type == 6 && (current_file_info->format == 0 ||
+ (current_file_info->format == 1 &&
+ current_read_track == 0))))
+ {
+ char *str, *text;
+ MidiEvent ev;
+
+ str = (char *)new_segment(&tmpbuffer, len + 3);
+ if(type != 6)
+ {
+ i = tf_read(str, 1, len, tf);
+ str[len] = '\0';
+ }
+ else
+ {
+ i = tf_read(str + 1, 1, len, tf);
+ str[0] = MARKER_START_CHAR;
+ str[len + 1] = MARKER_END_CHAR;
+ str[len + 2] = '\0';
+ }
+
+ if(i != len)
+ {
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "Warning: %s: Too shorten midi file.",
+ current_filename);
+ reuse_mblock(&tmpbuffer);
+ return -1;
+ }
+
+ if((text = readmidi_make_string_event(1, str, &ev, 1))
+ == NULL)
+ {
+ reuse_mblock(&tmpbuffer);
+ continue;
+ }
+ ev.time = smf_at_time;
+
+ if(type == 6)
+ {
+ if(strlen(fix_string(text + 1)) == 2)
+ {
+ reuse_mblock(&tmpbuffer);
+ continue; /* Empty Marker */
+ }
+ }
+
+ switch(type)
+ {
+ case 1:
+ if(karaoke_format == 2)
+ {
+ *text = ME_KARAOKE_LYRIC;
+ if(karaoke_title_flag == 0 &&
+ strncmp(str, "@T", 2) == 0)
+ current_file_info->karaoke_title =
+ add_karaoke_title(current_file_info->
+ karaoke_title, str + 2);
+ ev.type = ME_KARAOKE_LYRIC;
+ readmidi_add_event(&ev);
+ continue;
+ }
+ if(chorus_status_gs.text.status == CHORUS_ST_OK)
+ {
+ *text = ME_CHORUS_TEXT;
+ ev.type = ME_CHORUS_TEXT;
+ readmidi_add_event(&ev);
+ continue;
+ }
+ *text = ME_TEXT;
+ ev.type = ME_TEXT;
+ readmidi_add_event(&ev);
+ continue;
+ case 5:
+ *text = ME_LYRIC;
+ ev.type = ME_LYRIC;
+ readmidi_add_event(&ev);
+ continue;
+ case 6:
+ *text = ME_MARKER;
+ ev.type = ME_MARKER;
+ readmidi_add_event(&ev);
+ continue;
+ }
+ }
+
+ if(type == 3 && /* Sequence or Track Name */
+ (current_file_info->format == 0 ||
+ (current_file_info->format == 1 &&
+ current_read_track == 0)))
+ {
+ if(current_file_info->seq_name == NULL) {
+ char *name = dumpstring(3, len, "Sequence: ", 1, tf);
+ current_file_info->seq_name = safe_strdup(fix_string(name));
+ free(name);
+ }
+ else
+ dumpstring(3, len, "Sequence: ", 0, tf);
+ }
+ else if(type == 1 &&
+ current_file_info->first_text == NULL &&
+ (current_file_info->format == 0 ||
+ (current_file_info->format == 1 &&
+ current_read_track == 0))) {
+ char *name = dumpstring(1, len, "Text: ", 1, tf);
+ current_file_info->first_text = safe_strdup(fix_string(name));
+ free(name);
+ }
+ else
+ dumpstring(type, len, label[(type>7) ? 0 : type], 0, tf);
+ }
+ else
+ {
+ switch(type)
+ {
+ case 0x00:
+ if(len == 2)
+ {
+ a = tf_getc(tf);
+ b = tf_getc(tf);
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(Sequence Number %02x %02x)", a, b);
+ }
+ else
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(Sequence Number len=%d)", len);
+ break;
+
+ case 0x2F: /* End of Track */
+ pos = tf_tell(tf);
+ if(pos < next_pos)
+ tf_seek(tf, next_pos - pos, SEEK_CUR);
+ return 0;
+
+ case 0x51: /* Tempo */
+ a = tf_getc(tf);
+ b = tf_getc(tf);
+ c = tf_getc(tf);
+ MIDIEVENT(smf_at_time, ME_TEMPO, c, a, b);
+ break;
+
+ case 0x54:
+ /* SMPTE Offset (hr mn se fr ff)
+ * hr: hours&type
+ * 0 1 2 3 4 5 6 7 bits
+ * 0 |<--type -->|<---- hours [0..23]---->|
+ * type: 00: 24 frames/second
+ * 01: 25 frames/second
+ * 10: 30 frames/second (drop frame)
+ * 11: 30 frames/second (non-drop frame)
+ * mn: minis [0..59]
+ * se: seconds [0..59]
+ * fr: frames [0..29]
+ * ff: fractional frames [0..99]
+ */
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(SMPTE Offset meta event)");
+ skip(tf, len);
+ break;
+
+ case 0x58: /* Time Signature */
+ smf_time_signature(smf_at_time, tf, len);
+ break;
+
+ case 0x59: /* Key Signature */
+ smf_key_signature(smf_at_time, tf, len);
+ break;
+
+ case 0x7f: /* Sequencer-Specific Meta-Event */
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(Sequencer-Specific meta event, length %ld)",
+ len);
+ skip(tf, len);
+ break;
+
+ case 0x20: /* MIDI channel prefix (SMF v1.0) */
+ if(len == 1)
+ {
+ int midi_channel_prefix = tf_getc(tf);
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(MIDI channel prefix %d)",
+ midi_channel_prefix);
+ }
+ else
+ skip(tf, len);
+ break;
+
+ case 0x21: /* MIDI port number */
+ if(len == 1)
+ {
+ if((midi_port_number = tf_getc(tf))
+ == EOF)
+ {
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "Warning: %s: Too shorten midi file.",
+ current_filename);
+ return -1;
+ }
+ midi_port_number &= 0xF;
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(MIDI port number %d)", midi_port_number);
+ }
+ else
+ skip(tf, len);
+ break;
+
+ default:
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(Meta event type 0x%02x, length %ld)",
+ type, len);
+ skip(tf, len);
+ break;
+ }
+ }
+ }
+ else /* MIDI event */
+ {
+ a = me;
+ if(a & 0x80) /* status byte */
+ {
+ lastchan = MERGE_CHANNEL_PORT(a & 0x0F);
+ laststatus = (a >> 4) & 0x07;
+ if(laststatus != 7)
+ a = tf_getc(tf) & 0x7F;
+ }
+ switch(laststatus)
+ {
+ case 0: /* Note off */
+ b = tf_getc(tf) & 0x7F;
+ MIDIEVENT(smf_at_time, ME_NOTEOFF, lastchan, a,b);
+ break;
+
+ case 1: /* Note on */
+ b = tf_getc(tf) & 0x7F;
+ if(b)
+ {
+ if (! note_seen && smf_at_time > 0)
+ {
+ MIDIEVENT(0, ME_NOTEON, lastchan, a, 0);
+ MIDIEVENT(0, ME_NOTEOFF, lastchan, a, 0);
+ note_seen = 1;
+ }
+ MIDIEVENT(smf_at_time, ME_NOTEON, lastchan, a,b);
+ }
+ else /* b == 0 means Note Off */
+ {
+ MIDIEVENT(smf_at_time, ME_NOTEOFF, lastchan, a, 0);
+ }
+ break;
+
+ case 2: /* Key Pressure */
+ b = tf_getc(tf) & 0x7F;
+ MIDIEVENT(smf_at_time, ME_KEYPRESSURE, lastchan, a, b);
+ break;
+
+ case 3: /* Control change */
+ b = tf_getc(tf);
+ readmidi_add_ctl_event(smf_at_time, lastchan, a, b);
+ break;
+
+ case 4: /* Program change */
+ MIDIEVENT(smf_at_time, ME_PROGRAM, lastchan, a, 0);
+ break;
+
+ case 5: /* Channel pressure */
+ MIDIEVENT(smf_at_time, ME_CHANNEL_PRESSURE, lastchan, a, 0);
+ break;
+
+ case 6: /* Pitch wheel */
+ b = tf_getc(tf) & 0x7F;
+ MIDIEVENT(smf_at_time, ME_PITCHWHEEL, lastchan, a, b);
+ break;
+
+ default: /* case 7: */
+ /* Ignore this event */
+ switch(lastchan & 0xF)
+ {
+ case 2: /* Sys Com Song Position Pntr */
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(Sys Com Song Position Pntr)");
+ tf_getc(tf);
+ tf_getc(tf);
+ break;
+
+ case 3: /* Sys Com Song Select(Song #) */
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(Sys Com Song Select(Song #))");
+ tf_getc(tf);
+ break;
+
+ case 6: /* Sys Com tune request */
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(Sys Com tune request)");
+ break;
+ case 8: /* Sys real time timing clock */
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(Sys real time timing clock)");
+ break;
+ case 10: /* Sys real time start */
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(Sys real time start)");
+ break;
+ case 11: /* Sys real time continue */
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(Sys real time continue)");
+ break;
+ case 12: /* Sys real time stop */
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(Sys real time stop)");
+ break;
+ case 14: /* Sys real time active sensing */
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(Sys real time active sensing)");
+ break;
+#if 0
+ case 15: /* Meta */
+ case 0: /* SysEx */
+ case 7: /* SysEx */
+#endif
+ default: /* 1, 4, 5, 9, 13 */
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "*** Can't happen: status 0x%02X channel 0x%02X",
+ laststatus, lastchan & 0xF);
+ break;
+ }
+ }
+ }
+ }
+ /*NOTREACHED*/
+}
+
+/* Free the linked event list from memory. */
+static void free_midi_list(void)
+{
+ if(evlist != NULL)
+ {
+ reuse_mblock(&mempool);
+ evlist = NULL;
+ }
+}
+
+static void move_channels(int *chidx)
+{
+ int i, ch, maxch, newch;
+ MidiEventList *e;
+
+ for (i = 0; i < 256; i++)
+ chidx[i] = -1;
+ /* check channels */
+ for (i = maxch = 0, e = evlist; i < event_count; i++, e = e->next)
+ if (! GLOBAL_CHANNEL_EVENT_TYPE(e->event.type)) {
+ if ((ch = e->event.channel) < REDUCE_CHANNELS)
+ chidx[ch] = ch;
+ if (maxch < ch)
+ maxch = ch;
+ }
+ if (maxch >= REDUCE_CHANNELS)
+ /* Move channel if enable */
+ for (i = maxch = 0, e = evlist; i < event_count; i++, e = e->next)
+ if (! GLOBAL_CHANNEL_EVENT_TYPE(e->event.type)) {
+ if (chidx[ch = e->event.channel] != -1)
+ ch = e->event.channel = chidx[ch];
+ else { /* -1 */
+ if (ch >= MAX_CHANNELS) {
+ newch = ch % REDUCE_CHANNELS;
+ while (newch < ch && newch < MAX_CHANNELS) {
+ if (chidx[newch] == -1) {
+ ctl->cmsg(CMSG_INFO, VERB_VERBOSE,
+ "channel %d => %d", ch, newch);
+ ch = e->event.channel = chidx[ch] = newch;
+ break;
+ }
+ newch += REDUCE_CHANNELS;
+ }
+ }
+ if (chidx[ch] == -1) {
+ if (ch < MAX_CHANNELS)
+ chidx[ch] = ch;
+ else {
+ newch = ch % MAX_CHANNELS;
+ ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
+ "channel %d => %d (mixed)", ch, newch);
+ ch = e->event.channel = chidx[ch] = newch;
+ }
+ }
+ }
+ if (maxch < ch)
+ maxch = ch;
+ }
+ for (i = 0, e = evlist; i < event_count; i++, e = e->next)
+ if (e->event.type == ME_SYSEX_GS_LSB) {
+ if (e->event.b == 0x45 || e->event.b == 0x46)
+ if (maxch < e->event.channel)
+ maxch = e->event.channel;
+ } else if (e->event.type == ME_SYSEX_XG_LSB) {
+ if (e->event.b == 0x99)
+ if (maxch < e->event.channel)
+ maxch = e->event.channel;
+ }
+ current_file_info->max_channel = maxch;
+}
+
+void change_system_mode(int mode)
+{
+ int mid;
+
+ if(opt_system_mid)
+ {
+ mid = opt_system_mid;
+ mode = -1; /* Always use opt_system_mid */
+ }
+ else
+ mid = current_file_info->mid;
+ pan_table = sc_pan_table;
+ switch(mode)
+ {
+ case GM_SYSTEM_MODE:
+ if(play_system_mode == DEFAULT_SYSTEM_MODE)
+ {
+ play_system_mode = GM_SYSTEM_MODE;
+ vol_table = def_vol_table;
+ }
+ break;
+ case GM2_SYSTEM_MODE:
+ play_system_mode = GM2_SYSTEM_MODE;
+ vol_table = def_vol_table;
+ pan_table = gm2_pan_table;
+ break;
+ case GS_SYSTEM_MODE:
+ play_system_mode = GS_SYSTEM_MODE;
+ vol_table = gs_vol_table;
+ break;
+ case XG_SYSTEM_MODE:
+ if (play_system_mode != XG_SYSTEM_MODE) {init_all_effect_xg();}
+ play_system_mode = XG_SYSTEM_MODE;
+ vol_table = xg_vol_table;
+ break;
+ default:
+ /* --module option */
+ if (is_gs_module()) {
+ play_system_mode = GS_SYSTEM_MODE;
+ break;
+ } else if (is_xg_module()) {
+ if (play_system_mode != XG_SYSTEM_MODE) {init_all_effect_xg();}
+ play_system_mode = XG_SYSTEM_MODE;
+ break;
+ }
+ switch(mid)
+ {
+ case 0x41:
+ play_system_mode = GS_SYSTEM_MODE;
+ vol_table = gs_vol_table;
+ break;
+ case 0x43:
+ if (play_system_mode != XG_SYSTEM_MODE) {init_all_effect_xg();}
+ play_system_mode = XG_SYSTEM_MODE;
+ vol_table = xg_vol_table;
+ break;
+ case 0x7e:
+ play_system_mode = GM_SYSTEM_MODE;
+ vol_table = def_vol_table;
+ break;
+ default:
+ play_system_mode = DEFAULT_SYSTEM_MODE;
+ vol_table = def_vol_table;
+ break;
+ }
+ break;
+ }
+}
+
+int get_default_mapID(int ch)
+{
+ if(play_system_mode == XG_SYSTEM_MODE)
+ return ISDRUMCHANNEL(ch) ? XG_DRUM_MAP : XG_NORMAL_MAP;
+ return INST_NO_MAP;
+}
+
+/* Allocate an array of MidiEvents and fill it from the linked list of
+ events, marking used instruments for loading. Convert event times to
+ samples: handle tempo changes. Strip unnecessary events from the list.
+ Free the linked list. */
+static MidiEvent *groom_list(int32 divisions, int32 *eventsp, int32 *samplesp)
+{
+ MidiEvent *groomed_list, *lp;
+ MidiEventList *meep;
+ int32 i, j, our_event_count, tempo, skip_this_event;
+ int32 sample_cum, samples_to_do, at, st, dt, counting_time;
+ int ch, gch;
+ uint8 current_set[MAX_CHANNELS],
+ warn_tonebank[128 + MAP_BANK_COUNT], warn_drumset[128 + MAP_BANK_COUNT];
+ int8 bank_lsb[MAX_CHANNELS], bank_msb[MAX_CHANNELS], mapID[MAX_CHANNELS];
+ int current_program[MAX_CHANNELS];
+ int wrd_args[WRD_MAXPARAM];
+ int wrd_argc;
+ int chidx[256];
+ int newbank, newprog;
+
+ move_channels(chidx);
+
+ COPY_CHANNELMASK(drumchannels, current_file_info->drumchannels);
+ COPY_CHANNELMASK(drumchannel_mask, current_file_info->drumchannel_mask);
+
+ /* Move drumchannels */
+ for(ch = REDUCE_CHANNELS; ch < MAX_CHANNELS; ch++)
+ {
+ i = chidx[ch];
+ if(i != -1 && i != ch && !IS_SET_CHANNELMASK(drumchannel_mask, i))
+ {
+ if(IS_SET_CHANNELMASK(drumchannels, ch))
+ SET_CHANNELMASK(drumchannels, i);
+ else
+ UNSET_CHANNELMASK(drumchannels, i);
+ }
+ }
+
+ memset(warn_tonebank, 0, sizeof(warn_tonebank));
+ if (special_tonebank >= 0)
+ newbank = special_tonebank;
+ else
+ newbank = default_tonebank;
+ for(j = 0; j < MAX_CHANNELS; j++)
+ {
+ if(ISDRUMCHANNEL(j))
+ current_set[j] = 0;
+ else
+ {
+ if (tonebank[newbank] == NULL)
+ {
+ if (warn_tonebank[newbank] == 0)
+ {
+ ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
+ "Tone bank %d is undefined", newbank);
+ warn_tonebank[newbank] = 1;
+ }
+ newbank = 0;
+ }
+ current_set[j] = newbank;
+ }
+ bank_lsb[j] = bank_msb[j] = 0;
+ if(play_system_mode == XG_SYSTEM_MODE && j % 16 == 9)
+ bank_msb[j] = 127; /* Use MSB=127 for XG */
+ current_program[j] = default_program[j];
+ }
+
+ memset(warn_drumset, 0, sizeof(warn_drumset));
+ tempo = 500000;
+ compute_sample_increment(tempo, divisions);
+
+ /* This may allocate a bit more than we need */
+ groomed_list = lp =
+ (MidiEvent *)safe_malloc(sizeof(MidiEvent) * (event_count + 1));
+ meep = evlist;
+
+ our_event_count = 0;
+ st = at = sample_cum = 0;
+ counting_time = 2; /* We strip any silence before the first NOTE ON. */
+ wrd_argc = 0;
+ change_system_mode(DEFAULT_SYSTEM_MODE);
+
+ for(j = 0; j < MAX_CHANNELS; j++)
+ mapID[j] = get_default_mapID(j);
+
+ for(i = 0; i < event_count; i++)
+ {
+ skip_this_event = 0;
+ ch = meep->event.channel;
+ gch = GLOBAL_CHANNEL_EVENT_TYPE(meep->event.type);
+ if(!gch && ch >= MAX_CHANNELS) /* For safety */
+ meep->event.channel = ch = ch % MAX_CHANNELS;
+
+ if(!gch && IS_SET_CHANNELMASK(quietchannels, ch))
+ skip_this_event = 1;
+ else switch(meep->event.type)
+ {
+ case ME_NONE:
+ skip_this_event = 1;
+ break;
+ case ME_RESET:
+ change_system_mode(meep->event.a);
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "MIDI reset at %d sec",
+ (int)((double)st / play_mode->rate + 0.5));
+ for(j = 0; j < MAX_CHANNELS; j++)
+ {
+ if(play_system_mode == XG_SYSTEM_MODE && j % 16 == 9)
+ mapID[j] = XG_DRUM_MAP;
+ else
+ mapID[j] = get_default_mapID(j);
+ if(ISDRUMCHANNEL(j))
+ current_set[j] = 0;
+ else
+ {
+ if(special_tonebank >= 0)
+ current_set[j] = special_tonebank;
+ else
+ current_set[j] = default_tonebank;
+ if(tonebank[current_set[j]] == NULL)
+ current_set[j] = 0;
+ }
+ bank_lsb[j] = bank_msb[j] = 0;
+ if(play_system_mode == XG_SYSTEM_MODE && j % 16 == 9)
+ bank_msb[j] = 127; /* Use MSB=127 for XG */
+ current_program[j] = default_program[j];
+ }
+ break;
+
+ case ME_PROGRAM:
+ if (ISDRUMCHANNEL(ch))
+ newbank = current_program[ch];
+ else
+ newbank = current_set[ch];
+ newprog = meep->event.a;
+ switch (play_system_mode) {
+ case GS_SYSTEM_MODE: /* GS */
+ switch (bank_lsb[ch]) {
+ case 0: /* No change */
+ break;
+ case 1:
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(GS ch=%d SC-55 MAP)", ch);
+ mapID[ch] = (ISDRUMCHANNEL(ch)) ? SC_55_DRUM_MAP
+ : SC_55_TONE_MAP;
+ break;
+ case 2:
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(GS ch=%d SC-88 MAP)", ch);
+ mapID[ch] = (ISDRUMCHANNEL(ch)) ? SC_88_DRUM_MAP
+ : SC_88_TONE_MAP;
+ break;
+ case 3:
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(GS ch=%d SC-88Pro MAP)", ch);
+ mapID[ch] = (ISDRUMCHANNEL(ch)) ? SC_88PRO_DRUM_MAP
+ : SC_88PRO_TONE_MAP;
+ break;
+ case 4:
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(GS ch=%d SC-8820/SC-8850 MAP)", ch);
+ mapID[ch] = (ISDRUMCHANNEL(ch)) ? SC_8850_DRUM_MAP
+ : SC_8850_TONE_MAP;
+ break;
+ default:
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(GS: ch=%d Strange bank LSB %d)",
+ ch, bank_lsb[ch]);
+ break;
+ }
+ newbank = bank_msb[ch];
+ break;
+ case XG_SYSTEM_MODE: /* XG */
+ switch (bank_msb[ch]) {
+ case 0: /* Normal */
+ if (ch == 9 && bank_lsb[ch] == 127
+ && mapID[ch] == XG_DRUM_MAP)
+ /* FIXME: Why this part is drum? Is this correct? */
+ ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+ "Warning: XG bank 0/127 is found. "
+ "It may be not correctly played.");
+ else {
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(XG ch=%d Normal voice)", ch);
+ midi_drumpart_change(ch, 0);
+ mapID[ch] = XG_NORMAL_MAP;
+ }
+ break;
+ case 64: /* SFX voice */
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(XG ch=%d SFX voice)", ch);
+ midi_drumpart_change(ch, 0);
+ mapID[ch] = XG_SFX64_MAP;
+ break;
+ case 126: /* SFX kit */
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(XG ch=%d SFX kit)", ch);
+ midi_drumpart_change(ch, 1);
+ mapID[ch] = XG_SFX126_MAP;
+ break;
+ case 127: /* Drum kit */
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(XG ch=%d Drum kit)", ch);
+ midi_drumpart_change(ch, 1);
+ mapID[ch] = XG_DRUM_MAP;
+ break;
+ default:
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "(XG: ch=%d Strange bank MSB %d)",
+ ch, bank_msb[ch]);
+ break;
+ }
+ newbank = bank_lsb[ch];
+ break;
+ case GM2_SYSTEM_MODE: /* GM2 */
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG, "(GM2 ch=%d)", ch);
+ if ((bank_msb[ch] & 0xfe) == 0x78) /* 0x78/0x79 */
+ midi_drumpart_change(ch, bank_msb[ch] == 0x78);
+ mapID[ch] = (ISDRUMCHANNEL(ch)) ? GM2_DRUM_MAP
+ : GM2_TONE_MAP;
+ newbank = bank_lsb[ch];
+ break;
+ default:
+ newbank = bank_msb[ch];
+ break;
+ }
+ if (ISDRUMCHANNEL(ch))
+ current_set[ch] = newprog;
+ else {
+ if (special_tonebank >= 0)
+ newbank = special_tonebank;
+ if (current_program[ch] == SPECIAL_PROGRAM)
+ skip_this_event = 1;
+ current_set[ch] = newbank;
+ }
+ current_program[ch] = newprog;
+ break;
+
+ case ME_NOTEON:
+ if(counting_time)
+ counting_time = 1;
+ if(ISDRUMCHANNEL(ch))
+ {
+ newbank = current_set[ch];
+ newprog = meep->event.a;
+ instrument_map(mapID[ch], &newbank, &newprog);
+
+ if(!drumset[newbank]) /* Is this a defined drumset? */
+ {
+ if(warn_drumset[newbank] == 0)
+ {
+ ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
+ "Drum set %d is undefined", newbank);
+ warn_drumset[newbank] = 1;
+ }
+ newbank = 0;
+ }
+
+ /* Mark this instrument to be loaded */
+ if(!(drumset[newbank]->tone[newprog].instrument))
+ drumset[newbank]->tone[newprog].instrument =
+ MAGIC_LOAD_INSTRUMENT;
+ }
+ else
+ {
+ if(current_program[ch] == SPECIAL_PROGRAM)
+ break;
+ newbank = current_set[ch];
+ newprog = current_program[ch];
+ instrument_map(mapID[ch], &newbank, &newprog);
+ if(tonebank[newbank] == NULL)
+ {
+ if(warn_tonebank[newbank] == 0)
+ {
+ ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
+ "Tone bank %d is undefined", newbank);
+ warn_tonebank[newbank] = 1;
+ }
+ newbank = 0;
+ }
+
+ /* Mark this instrument to be loaded */
+ if(!(tonebank[newbank]->tone[newprog].instrument))
+ tonebank[newbank]->tone[newprog].instrument =
+ MAGIC_LOAD_INSTRUMENT;
+ }
+ break;
+
+ case ME_TONE_BANK_MSB:
+ bank_msb[ch] = meep->event.a;
+ break;
+
+ case ME_TONE_BANK_LSB:
+ bank_lsb[ch] = meep->event.a;
+ break;
+
+ case ME_CHORUS_TEXT:
+ case ME_LYRIC:
+ case ME_MARKER:
+ case ME_INSERT_TEXT:
+ case ME_TEXT:
+ case ME_KARAOKE_LYRIC:
+ if((meep->event.a | meep->event.b) == 0)
+ skip_this_event = 1;
+ else if(counting_time && ctl->trace_playing)
+ counting_time = 1;
+ break;
+
+ case ME_GSLCD:
+ if (counting_time && ctl->trace_playing)
+ counting_time = 1;
+ skip_this_event = !ctl->trace_playing;
+ break;
+
+ case ME_DRUMPART:
+ midi_drumpart_change(ch, meep->event.a);
+ break;
+
+ case ME_WRD:
+ if(readmidi_wrd_mode == WRD_TRACE_MIMPI)
+ {
+ wrd_args[wrd_argc++] = meep->event.a | 256 * meep->event.b;
+ if(ch != WRD_ARG)
+ {
+ if(ch == WRD_MAG) {
+ wrdt->apply(WRD_MAGPRELOAD, wrd_argc, wrd_args);
+ }
+ else if(ch == WRD_PLOAD)
+ wrdt->apply(WRD_PHOPRELOAD, wrd_argc, wrd_args);
+ else if(ch == WRD_PATH)
+ wrdt->apply(WRD_PATH, wrd_argc, wrd_args);
+ wrd_argc = 0;
+ }
+ }
+ if(counting_time == 2 && readmidi_wrd_mode != WRD_TRACE_NOTHING)
+ counting_time = 1;
+ break;
+
+ case ME_SHERRY:
+ if(counting_time == 2)
+ counting_time = 1;
+ break;
+
+ case ME_NOTE_STEP:
+ if(counting_time == 2)
+ skip_this_event = 1;
+ break;
+ }
+
+ /* Recompute time in samples*/
+ if((dt = meep->event.time - at) && !counting_time)
+ {
+ samples_to_do = sample_increment * dt;
+ sample_cum += sample_correction * dt;
+ if(sample_cum & 0xFFFF0000)
+ {
+ samples_to_do += ((sample_cum >> 16) & 0xFFFF);
+ sample_cum &= 0x0000FFFF;
+ }
+ st += samples_to_do;
+ if(st < 0)
+ {
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "Overflow the sample counter");
+ free(groomed_list);
+ return NULL;
+ }
+ }
+ else if(counting_time == 1)
+ counting_time = 0;
+
+ if(meep->event.type == ME_TEMPO)
+ {
+ tempo = ch + meep->event.b * 256 + meep->event.a * 65536;
+ compute_sample_increment(tempo, divisions);
+ }
+
+ if(!skip_this_event)
+ {
+ /* Add the event to the list */
+ *lp = meep->event;
+ lp->time = st;
+ lp++;
+ our_event_count++;
+ }
+ at = meep->event.time;
+ meep = meep->next;
+ }
+ /* Add an End-of-Track event */
+ lp->time = st;
+ lp->type = ME_EOT;
+ our_event_count++;
+ free_midi_list();
+
+ *eventsp = our_event_count;
+ *samplesp = st;
+ return groomed_list;
+}
+
+static int read_smf_file(struct timidity_file *tf)
+{
+ int32 len, divisions;
+ int16 format, tracks, divisions_tmp;
+ int i;
+
+ if(current_file_info->file_type == IS_OTHER_FILE)
+ current_file_info->file_type = IS_SMF_FILE;
+
+ if(current_file_info->karaoke_title == NULL)
+ karaoke_title_flag = 0;
+ else
+ karaoke_title_flag = 1;
+
+ errno = 0;
+ if(tf_read(&len, 4, 1, tf) != 1)
+ {
+ if(errno)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s", current_filename,
+ strerror(errno));
+ else
+ ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+ "%s: Not a MIDI file!", current_filename);
+ return 1;
+ }
+ len = BE_LONG(len);
+
+ tf_read(&format, 2, 1, tf);
+ tf_read(&tracks, 2, 1, tf);
+ tf_read(&divisions_tmp, 2, 1, tf);
+ format = BE_SHORT(format);
+ tracks = BE_SHORT(tracks);
+ divisions_tmp = BE_SHORT(divisions_tmp);
+
+ if(divisions_tmp < 0)
+ {
+ /* SMPTE time -- totally untested. Got a MIDI file that uses this? */
+ divisions=
+ (int32)(-(divisions_tmp / 256)) * (int32)(divisions_tmp & 0xFF);
+ }
+ else
+ divisions = (int32)divisions_tmp;
+
+ if(play_mode->flag & PF_MIDI_EVENT)
+ play_mode->acntl(PM_REQ_DIVISIONS, &divisions);
+
+ if(len > 6)
+ {
+ ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+ "%s: MIDI file header size %ld bytes",
+ current_filename, len);
+ skip(tf, len - 6); /* skip the excess */
+ }
+ if(format < 0 || format > 2)
+ {
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "%s: Unknown MIDI file format %d", current_filename, format);
+ return 1;
+ }
+ ctl->cmsg(CMSG_INFO, VERB_VERBOSE, "Format: %d Tracks: %d Divisions: %d",
+ format, tracks, divisions);
+
+ current_file_info->format = format;
+ current_file_info->tracks = tracks;
+ current_file_info->divisions = divisions;
+ if(tf->url->url_tell != NULL)
+ current_file_info->hdrsiz = (int16)tf_tell(tf);
+ else
+ current_file_info->hdrsiz = -1;
+
+ switch(format)
+ {
+ case 0:
+ if(read_smf_track(tf, 0, 1))
+ {
+ if(ignore_midi_error)
+ break;
+ return 1;
+ }
+ break;
+
+ case 1:
+ for(i = 0; i < tracks; i++)
+ {
+ if(read_smf_track(tf, i, 1))
+ {
+ if(ignore_midi_error)
+ break;
+ return 1;
+ }
+ }
+ break;
+
+ case 2: /* We simply play the tracks sequentially */
+ for(i = 0; i < tracks; i++)
+ {
+ if(read_smf_track(tf, i, 0))
+ {
+ if(ignore_midi_error)
+ break;
+ return 1;
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+void readmidi_read_init(void)
+{
+ int i;
+ static int first = 1;
+
+ /* initialize effect status */
+ for (i = 0; i < MAX_CHANNELS; i++)
+ init_channel_layer(i);
+ free_effect_buffers();
+ init_reverb_status_gs();
+ init_delay_status_gs();
+ init_chorus_status_gs();
+ init_eq_status_gs();
+ init_insertion_effect_gs();
+ init_multi_eq_xg();
+ if (play_system_mode == XG_SYSTEM_MODE) {init_all_effect_xg();}
+ init_userdrum();
+ init_userinst();
+ rhythm_part[0] = rhythm_part[1] = 9;
+ for(i = 0; i < 6; i++) {drum_setup_xg[i] = 9;}
+
+ /* Put a do-nothing event first in the list for easier processing */
+ evlist = current_midi_point = alloc_midi_event();
+ evlist->event.time = 0;
+ evlist->event.type = ME_NONE;
+ evlist->event.channel = 0;
+ evlist->event.a = 0;
+ evlist->event.b = 0;
+ evlist->prev = NULL;
+ evlist->next = NULL;
+ readmidi_error_flag = 0;
+ event_count = 1;
+
+ if(string_event_table != NULL)
+ {
+ free(string_event_table[0]);
+ free(string_event_table);
+ string_event_table = NULL;
+ string_event_table_size = 0;
+ }
+ if (first != 1)
+ if (string_event_strtab.nstring > 0)
+ delete_string_table(&string_event_strtab);
+ init_string_table(&string_event_strtab);
+ karaoke_format = 0;
+
+ for(i = 0; i < 256; i++)
+ default_channel_program[i] = -1;
+ readmidi_wrd_mode = WRD_TRACE_NOTHING;
+ first = 0;
+}
+
+static void insert_note_steps(void)
+{
+ MidiEventList *e;
+ int32 i, n, at, lasttime, meas, beat;
+ uint8 num = 0, denom = 1, a, b;
+
+ e = evlist;
+ for (i = n = 0; i < event_count - 1 && n < 256 - 1; i++, e = e->next)
+ if (e->event.type == ME_TIMESIG && e->event.channel == 0) {
+ if (n == 0 && e->event.time > 0) { /* 4/4 is default */
+ SETMIDIEVENT(timesig[n], 0, ME_TIMESIG, 0, 4, 4);
+ n++;
+ }
+ if (n > 0 && e->event.a == timesig[n - 1].a
+ && e->event.b == timesig[n - 1].b)
+ continue; /* unchanged */
+ if (n > 0 && e->event.time == timesig[n - 1].time)
+ n--; /* overwrite previous timesig */
+ timesig[n++] = e->event;
+ }
+ if (n == 0) {
+ SETMIDIEVENT(timesig[n], 0, ME_TIMESIG, 0, 4, 4);
+ n++;
+ }
+ timesig[n] = timesig[n - 1];
+ timesig[n].time = 0x7fffffff; /* stopper */
+ lasttime = e->event.time;
+ readmidi_set_track(0, 1);
+ at = n = meas = beat = 0;
+ while (at < lasttime && ! readmidi_error_flag) {
+ if (at >= timesig[n].time) {
+ if (beat != 0)
+ meas++, beat = 0;
+ num = timesig[n].a, denom = timesig[n].b, n++;
+ }
+ a = (meas + 1) & 0xff;
+ b = (((meas + 1) >> 8) & 0x0f) + ((beat + 1) << 4);
+ MIDIEVENT(at, ME_NOTE_STEP, 0, a, b);
+ if (++beat == num)
+ meas++, beat = 0;
+ at += current_file_info->divisions * 4 / denom;
+ }
+}
+
+MidiEvent *read_midi_file(struct timidity_file *tf, int32 *count, int32 *sp,
+ char *fn)
+{
+ char magic[4];
+ MidiEvent *ev;
+ int err, macbin_check, mtype, i;
+
+ macbin_check = 1;
+ current_file_info = get_midi_file_info(current_filename, 1);
+ COPY_CHANNELMASK(drumchannels, current_file_info->drumchannels);
+ COPY_CHANNELMASK(drumchannel_mask, current_file_info->drumchannel_mask);
+
+ errno = 0;
+
+ if((mtype = get_module_type(fn)) > 0)
+ {
+ readmidi_read_init();
+ if(!IS_URL_SEEK_SAFE(tf->url))
+ tf->url = url_cache_open(tf->url, 1);
+ err = load_module_file(tf, mtype);
+ if(!err)
+ {
+ current_file_info->format = 0;
+ memset(&drumchannels, 0, sizeof(drumchannels));
+ goto grooming;
+ }
+ free_midi_list();
+
+ if(err == 2)
+ return NULL;
+ url_rewind(tf->url);
+ url_cache_disable(tf->url);
+ }
+
+#if MAX_CHANNELS > 16
+ for(i = 16; i < MAX_CHANNELS; i++)
+ {
+ if(!IS_SET_CHANNELMASK(drumchannel_mask, i))
+ {
+ if(IS_SET_CHANNELMASK(drumchannels, i & 0xF))
+ SET_CHANNELMASK(drumchannels, i);
+ else
+ UNSET_CHANNELMASK(drumchannels, i);
+ }
+ }
+#endif
+
+ if(opt_default_mid &&
+ (current_file_info->mid == 0 || current_file_info->mid >= 0x7e))
+ current_file_info->mid = opt_default_mid;
+
+ retry_read:
+ if(tf_read(magic, 1, 4, tf) != 4)
+ {
+ if(errno)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s", current_filename,
+ strerror(errno));
+ else
+ ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+ "%s: Not a MIDI file!", current_filename);
+ return NULL;
+ }
+
+ if(memcmp(magic, "MThd", 4) == 0)
+ {
+ readmidi_read_init();
+ err = read_smf_file(tf);
+ }
+ else if(memcmp(magic, "RCM-", 4) == 0 || memcmp(magic, "COME", 4) == 0)
+ {
+ readmidi_read_init();
+ err = read_rcp_file(tf, magic, fn);
+ }
+ else if (strncmp(magic, "RIFF", 4) == 0) {
+ if (tf_read(magic, 1, 4, tf) == 4 &&
+ tf_read(magic, 1, 4, tf) == 4 &&
+ strncmp(magic, "RMID", 4) == 0 &&
+ tf_read(magic, 1, 4, tf) == 4 &&
+ strncmp(magic, "data", 4) == 0 &&
+ tf_read(magic, 1, 4, tf) == 4) {
+ goto retry_read;
+ } else {
+ err = 1;
+ ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+ "%s: Not a MIDI file!", current_filename);
+ }
+ }
+ else if(memcmp(magic, "melo", 4) == 0)
+ {
+ readmidi_read_init();
+ err = read_mfi_file(tf);
+ }
+ else
+ {
+ if(macbin_check && magic[0] == 0)
+ {
+ /* Mac Binary */
+ macbin_check = 0;
+ skip(tf, 128 - 4);
+ goto retry_read;
+ }
+ else if(memcmp(magic, "RIFF", 4) == 0)
+ {
+ /* RIFF MIDI file */
+ skip(tf, 20 - 4);
+ goto retry_read;
+ }
+ err = 1;
+ ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+ "%s: Not a MIDI file!", current_filename);
+ }
+
+ if(err)
+ {
+ free_midi_list();
+ if(string_event_strtab.nstring > 0)
+ delete_string_table(&string_event_strtab);
+ return NULL;
+ }
+
+ /* Read WRD file */
+ if(!(play_mode->flag&PF_CAN_TRACE))
+ {
+ if(wrdt->start != NULL)
+ wrdt->start(WRD_TRACE_NOTHING);
+ readmidi_wrd_mode = WRD_TRACE_NOTHING;
+ }
+ else if(wrdt->id != '-' && wrdt->opened)
+ {
+ readmidi_wrd_mode = import_wrd_file(fn);
+ if(wrdt->start != NULL)
+ if(wrdt->start(readmidi_wrd_mode) == -1)
+ {
+ /* strip all WRD events */
+ MidiEventList *e;
+ int32 i;
+ for(i = 0, e = evlist; i < event_count; i++, e = e->next)
+ if (e->event.type == ME_WRD || e->event.type == ME_SHERRY)
+ e->event.type = ME_NONE;
+ }
+ }
+ else
+ readmidi_wrd_mode = WRD_TRACE_NOTHING;
+
+ /* make lyric table */
+ if(string_event_strtab.nstring > 0)
+ {
+ string_event_table_size = string_event_strtab.nstring;
+ string_event_table = make_string_array(&string_event_strtab);
+ if(string_event_table == NULL)
+ {
+ delete_string_table(&string_event_strtab);
+ string_event_table_size = 0;
+ }
+ }
+
+ grooming:
+ insert_note_steps();
+ ev = groom_list(current_file_info->divisions, count, sp);
+ if(ev == NULL)
+ {
+ free_midi_list();
+ if(string_event_strtab.nstring > 0)
+ delete_string_table(&string_event_strtab);
+ return NULL;
+ }
+ current_file_info->samples = *sp;
+ if(current_file_info->first_text == NULL)
+ current_file_info->first_text = safe_strdup("");
+ current_file_info->readflag = 1;
+ return ev;
+}
+
+
+struct midi_file_info *new_midi_file_info(const char *filename)
+{
+ struct midi_file_info *p;
+ p = (struct midi_file_info *)safe_malloc(sizeof(struct midi_file_info));
+
+ /* Initialize default members */
+ memset(p, 0, sizeof(struct midi_file_info));
+ p->hdrsiz = -1;
+ p->format = -1;
+ p->tracks = -1;
+ p->divisions = -1;
+ p->time_sig_n = p->time_sig_d = -1;
+ p->samples = -1;
+ p->max_channel = -1;
+ p->file_type = IS_OTHER_FILE;
+ if(filename != NULL)
+ p->filename = safe_strdup(filename);
+ COPY_CHANNELMASK(p->drumchannels, default_drumchannels);
+ COPY_CHANNELMASK(p->drumchannel_mask, default_drumchannel_mask);
+
+ /* Append to midi_file_info */
+ p->next = midi_file_info;
+ midi_file_info = p;
+
+ return p;
+}
+
+void free_all_midi_file_info(void)
+{
+ struct midi_file_info *info, *next;
+
+ info = midi_file_info;
+ while (info) {
+ next = info->next;
+ free(info->filename);
+ if (info->seq_name)
+ free(info->seq_name);
+ if (info->karaoke_title != NULL && info->karaoke_title == info->first_text)
+ free(info->karaoke_title);
+ else {
+ if (info->karaoke_title)
+ free(info->karaoke_title);
+ if (info->first_text)
+ free(info->first_text);
+ if (info->midi_data)
+ free(info->midi_data);
+ if (info->pcm_filename)
+ free(info->pcm_filename); /* Note: this memory is freed in playmidi.c*/
+ }
+ free(info);
+ info = next;
+ }
+ midi_file_info = NULL;
+ current_file_info = NULL;
+}
+
+struct midi_file_info *get_midi_file_info(char *filename, int newp)
+{
+ struct midi_file_info *p;
+
+ filename = url_expand_home_dir(filename);
+ /* Linear search */
+ for(p = midi_file_info; p; p = p->next)
+ if(!strcmp(filename, p->filename))
+ return p;
+ if(newp)
+ return new_midi_file_info(filename);
+ return NULL;
+}
+
+struct timidity_file *open_midi_file(char *fn,
+ int decompress, int noise_mode)
+{
+ struct midi_file_info *infop;
+ struct timidity_file *tf;
+#if defined(SMFCONV) && defined(__W32__)
+ extern int opt_rcpcv_dll;
+#endif
+
+ infop = get_midi_file_info(fn, 0);
+ if(infop == NULL || infop->midi_data == NULL)
+ tf = open_file(fn, decompress, noise_mode);
+ else
+ {
+ tf = open_with_mem(infop->midi_data, infop->midi_data_size,
+ noise_mode);
+ if(infop->compressed)
+ {
+ if((tf->url = url_inflate_open(tf->url, infop->midi_data_size, 1))
+ == NULL)
+ {
+ close_file(tf);
+ return NULL;
+ }
+ }
+ }
+
+#if defined(SMFCONV) && defined(__W32__)
+ /* smf convert */
+ if(tf != NULL && opt_rcpcv_dll)
+ {
+ if(smfconv_w32(tf, fn))
+ {
+ close_file(tf);
+ return NULL;
+ }
+ }
+#endif
+
+ return tf;
+}
+
+#ifndef NO_MIDI_CACHE
+static long deflate_url_reader(char *buf, long size, void *user_val)
+{
+ return url_nread((URL)user_val, buf, size);
+}
+
+/*
+ * URL data into deflated buffer.
+ */
+static void url_make_file_data(URL url, struct midi_file_info *infop)
+{
+ char buff[BUFSIZ];
+ MemBuffer b;
+ long n;
+ DeflateHandler compressor;
+
+ init_memb(&b);
+
+ /* url => b */
+ if((compressor = open_deflate_handler(deflate_url_reader, url,
+ ARC_DEFLATE_LEVEL)) == NULL)
+ return;
+ while((n = zip_deflate(compressor, buff, sizeof(buff))) > 0)
+ push_memb(&b, buff, n);
+ close_deflate_handler(compressor);
+ infop->compressed = 1;
+
+ /* b => mem */
+ infop->midi_data_size = b.total_size;
+ rewind_memb(&b);
+ infop->midi_data = (void *)safe_malloc(infop->midi_data_size);
+ read_memb(&b, infop->midi_data, infop->midi_data_size);
+ delete_memb(&b);
+}
+
+static int check_need_cache(URL url, char *filename)
+{
+ int t1, t2;
+ t1 = url_check_type(filename);
+ t2 = url->type;
+ return (t1 == URL_http_t || t1 == URL_ftp_t || t1 == URL_news_t)
+ && t2 != URL_arc_t;
+}
+#else
+/*ARGSUSED*/
+static void url_make_file_data(URL url, struct midi_file_info *infop)
+{
+}
+/*ARGSUSED*/
+static int check_need_cache(URL url, char *filename)
+{
+ return 0;
+}
+#endif /* NO_MIDI_CACHE */
+
+int check_midi_file(char *filename)
+{
+ struct midi_file_info *p;
+ struct timidity_file *tf;
+ char tmp[4];
+ int32 len;
+ int16 format;
+ int check_cache;
+
+ if(filename == NULL)
+ {
+ if(current_file_info == NULL)
+ return -1;
+ filename = current_file_info->filename;
+ }
+
+ p = get_midi_file_info(filename, 0);
+ if(p != NULL)
+ return p->format;
+ p = get_midi_file_info(filename, 1);
+
+ if(get_module_type(filename) > 0)
+ {
+ p->format = 0;
+ return 0;
+ }
+
+ tf = open_file(filename, 1, OF_SILENT);
+ if(tf == NULL)
+ return -1;
+
+ check_cache = check_need_cache(tf->url, filename);
+ if(check_cache)
+ {
+ if(!IS_URL_SEEK_SAFE(tf->url))
+ {
+ if((tf->url = url_cache_open(tf->url, 1)) == NULL)
+ {
+ close_file(tf);
+ return -1;
+ }
+ }
+ }
+
+ /* Parse MIDI header */
+ if(tf_read(tmp, 1, 4, tf) != 4)
+ {
+ close_file(tf);
+ return -1;
+ }
+
+ if(tmp[0] == 0)
+ {
+ skip(tf, 128 - 4);
+ if(tf_read(tmp, 1, 4, tf) != 4)
+ {
+ close_file(tf);
+ return -1;
+ }
+ }
+
+ if(strncmp(tmp, "RCM-", 4) == 0 ||
+ strncmp(tmp, "COME", 4) == 0 ||
+ strncmp(tmp, "RIFF", 4) == 0 ||
+ strncmp(tmp, "melo", 4) == 0 ||
+ strncmp(tmp, "M1", 2) == 0)
+ {
+ p->format = format = 1;
+ goto end_of_header;
+ }
+
+ if(strncmp(tmp, "MThd", 4) != 0)
+ {
+ close_file(tf);
+ return -1;
+ }
+
+ if(tf_read(&len, 4, 1, tf) != 1)
+ {
+ close_file(tf);
+ return -1;
+ }
+ len = BE_LONG(len);
+
+ tf_read(&format, 2, 1, tf);
+ format = BE_SHORT(format);
+ if(format < 0 || format > 2)
+ {
+ close_file(tf);
+ return -1;
+ }
+ skip(tf, len - 2);
+
+ p->format = format;
+ p->hdrsiz = (int16)tf_tell(tf);
+
+ end_of_header:
+ if(check_cache)
+ {
+ url_rewind(tf->url);
+ url_cache_disable(tf->url);
+ url_make_file_data(tf->url, p);
+ }
+ close_file(tf);
+ return format;
+}
+
+static char *get_midi_title1(struct midi_file_info *p)
+{
+ char *s;
+
+ if(p->format != 0 && p->format != 1)
+ return NULL;
+
+ if((s = p->seq_name) == NULL)
+ if((s = p->karaoke_title) == NULL)
+ s = p->first_text;
+ if(s != NULL)
+ {
+ int all_space, i;
+
+ all_space = 1;
+ for(i = 0; s[i]; i++)
+ if(s[i] != ' ')
+ {
+ all_space = 0;
+ break;
+ }
+ if(all_space)
+ s = NULL;
+ }
+ return s;
+}
+
+char *get_midi_title(char *filename)
+{
+ struct midi_file_info *p;
+ struct timidity_file *tf;
+ char tmp[4];
+ int32 len;
+ int16 format, tracks, trk;
+ int laststatus, check_cache;
+ int mtype;
+
+ if(filename == NULL)
+ {
+ if(current_file_info == NULL)
+ return NULL;
+ filename = current_file_info->filename;
+ }
+
+ p = get_midi_file_info(filename, 0);
+ if(p == NULL)
+ p = get_midi_file_info(filename, 1);
+ else
+ {
+ if(p->seq_name != NULL || p->first_text != NULL || p->format < 0)
+ return get_midi_title1(p);
+ }
+
+ tf = open_file(filename, 1, OF_SILENT);
+ if(tf == NULL)
+ return NULL;
+
+ mtype = get_module_type(filename);
+ check_cache = check_need_cache(tf->url, filename);
+ if(check_cache || mtype > 0)
+ {
+ if(!IS_URL_SEEK_SAFE(tf->url))
+ {
+ if((tf->url = url_cache_open(tf->url, 1)) == NULL)
+ {
+ close_file(tf);
+ return NULL;
+ }
+ }
+ }
+
+ if(mtype > 0)
+ {
+ char *title, *str;
+
+ title = get_module_title(tf, mtype);
+ if(title == NULL)
+ {
+ /* No title */
+ p->seq_name = NULL;
+ p->format = 0;
+ goto end_of_parse;
+ }
+
+ len = (int32)strlen(title);
+ len = SAFE_CONVERT_LENGTH(len);
+ str = (char *)new_segment(&tmpbuffer, len);
+ code_convert(title, str, len, NULL, NULL);
+ p->seq_name = (char *)safe_strdup(str);
+ reuse_mblock(&tmpbuffer);
+ p->format = 0;
+ free (title);
+ goto end_of_parse;
+ }
+
+ /* Parse MIDI header */
+ if(tf_read(tmp, 1, 4, tf) != 4)
+ {
+ close_file(tf);
+ return NULL;
+ }
+
+ if(tmp[0] == 0)
+ {
+ skip(tf, 128 - 4);
+ if(tf_read(tmp, 1, 4, tf) != 4)
+ {
+ close_file(tf);
+ return NULL;
+ }
+ }
+
+ if(memcmp(tmp, "RCM-", 4) == 0 || memcmp(tmp, "COME", 4) == 0)
+ {
+ int i;
+ char local[0x40 + 1];
+ char *str;
+
+ p->format = 1;
+ skip(tf, 0x20 - 4);
+ tf_read(local, 1, 0x40, tf);
+ local[0x40]='\0';
+
+ for(i = 0x40 - 1; i >= 0; i--)
+ {
+ if(local[i] == 0x20)
+ local[i] = '\0';
+ else if(local[i] != '\0')
+ break;
+ }
+
+ i = SAFE_CONVERT_LENGTH(i + 1);
+ str = (char *)new_segment(&tmpbuffer, i);
+ code_convert(local, str, i, NULL, NULL);
+ p->seq_name = (char *)safe_strdup(str);
+ reuse_mblock(&tmpbuffer);
+ p->format = 1;
+ goto end_of_parse;
+ }
+ if(memcmp(tmp, "melo", 4) == 0)
+ {
+ int i;
+ char *master, *converted;
+
+ master = get_mfi_file_title(tf);
+ if (master != NULL)
+ {
+ i = SAFE_CONVERT_LENGTH(strlen(master) + 1);
+ converted = (char *)new_segment(&tmpbuffer, i);
+ code_convert(master, converted, i, NULL, NULL);
+ p->seq_name = (char *)safe_strdup(converted);
+ reuse_mblock(&tmpbuffer);
+ }
+ else
+ {
+ p->seq_name = (char *)safe_malloc(1);
+ p->seq_name[0] = '\0';
+ }
+ p->format = 0;
+ goto end_of_parse;
+ }
+
+ if(strncmp(tmp, "M1", 2) == 0)
+ {
+ /* I don't know MPC file format */
+ p->format = 1;
+ goto end_of_parse;
+ }
+
+ if(strncmp(tmp, "RIFF", 4) == 0)
+ {
+ /* RIFF MIDI file */
+ skip(tf, 20 - 4);
+ if(tf_read(tmp, 1, 4, tf) != 4)
+ {
+ close_file(tf);
+ return NULL;
+ }
+ }
+
+ if(strncmp(tmp, "MThd", 4) != 0)
+ {
+ close_file(tf);
+ return NULL;
+ }
+
+ if(tf_read(&len, 4, 1, tf) != 1)
+ {
+ close_file(tf);
+ return NULL;
+ }
+
+ len = BE_LONG(len);
+
+ tf_read(&format, 2, 1, tf);
+ tf_read(&tracks, 2, 1, tf);
+ format = BE_SHORT(format);
+ tracks = BE_SHORT(tracks);
+ p->format = format;
+ p->tracks = tracks;
+ if(format < 0 || format > 2)
+ {
+ p->format = -1;
+ close_file(tf);
+ return NULL;
+ }
+
+ skip(tf, len - 4);
+ p->hdrsiz = (int16)tf_tell(tf);
+
+ if(format == 2)
+ goto end_of_parse;
+
+ if(tracks >= 3)
+ {
+ tracks = 3;
+ karaoke_format = 0;
+ }
+ else
+ {
+ tracks = 1;
+ karaoke_format = -1;
+ }
+
+ for(trk = 0; trk < tracks; trk++)
+ {
+ int32 next_pos, pos;
+
+ if(trk >= 1 && karaoke_format == -1)
+ break;
+
+ if((tf_read(tmp,1,4,tf) != 4) || (tf_read(&len,4,1,tf) != 1))
+ break;
+
+ if(memcmp(tmp, "MTrk", 4))
+ break;
+
+ next_pos = tf_tell(tf) + len;
+ laststatus = -1;
+ for(;;)
+ {
+ int i, me, type;
+
+ /* skip Variable-length quantity */
+ do
+ {
+ if((i = tf_getc(tf)) == EOF)
+ goto end_of_parse;
+ } while (i & 0x80);
+
+ if((me = tf_getc(tf)) == EOF)
+ goto end_of_parse;
+
+ if(me == 0xF0 || me == 0xF7) /* SysEx */
+ {
+ if((len = getvl(tf)) < 0)
+ goto end_of_parse;
+ if((p->mid == 0 || p->mid >= 0x7e) && len > 0 && me == 0xF0)
+ {
+ p->mid = tf_getc(tf);
+ len--;
+ }
+ skip(tf, len);
+ }
+ else if(me == 0xFF) /* Meta */
+ {
+ type = tf_getc(tf);
+ if((len = getvl(tf)) < 0)
+ goto end_of_parse;
+ if((type == 1 || type == 3) && len > 0 &&
+ (trk == 0 || karaoke_format != -1))
+ {
+ char *si, *so;
+ int s_maxlen = SAFE_CONVERT_LENGTH(len);
+
+ si = (char *)new_segment(&tmpbuffer, len + 1);
+ so = (char *)new_segment(&tmpbuffer, s_maxlen);
+
+ if(len != tf_read(si, 1, len, tf))
+ {
+ reuse_mblock(&tmpbuffer);
+ goto end_of_parse;
+ }
+
+ si[len]='\0';
+ code_convert(si, so, s_maxlen, NULL, NULL);
+ if(trk == 0 && type == 3)
+ {
+ if(p->seq_name == NULL) {
+ char *name = safe_strdup(so);
+ p->seq_name = safe_strdup(fix_string(name));
+ free(name);
+ }
+ reuse_mblock(&tmpbuffer);
+ if(karaoke_format == -1)
+ goto end_of_parse;
+ }
+ if(p->first_text == NULL) {
+ char *name;
+ name = safe_strdup(so);
+ p->first_text = safe_strdup(fix_string(name));
+ free(name);
+ }
+ if(karaoke_format != -1)
+ {
+ if(trk == 1 && strncmp(si, "@K", 2) == 0)
+ karaoke_format = 1;
+ else if(karaoke_format == 1 && trk == 2)
+ karaoke_format = 2;
+ }
+ if(type == 1 && karaoke_format == 2)
+ {
+ if(strncmp(si, "@T", 2) == 0)
+ p->karaoke_title =
+ add_karaoke_title(p->karaoke_title, si + 2);
+ else if(si[0] == '\\')
+ goto end_of_parse;
+ }
+ reuse_mblock(&tmpbuffer);
+ }
+ else if(type == 0x2F)
+ {
+ pos = tf_tell(tf);
+ if(pos < next_pos)
+ tf_seek(tf, next_pos - pos, SEEK_CUR);
+ break; /* End of track */
+ }
+ else
+ skip(tf, len);
+ }
+ else /* MIDI event */
+ {
+ /* skip MIDI event */
+ karaoke_format = -1;
+ if(trk != 0)
+ goto end_of_parse;
+
+ if(me & 0x80) /* status byte */
+ {
+ laststatus = (me >> 4) & 0x07;
+ if(laststatus != 7)
+ tf_getc(tf);
+ }
+
+ switch(laststatus)
+ {
+ case 0: case 1: case 2: case 3: case 6:
+ tf_getc(tf);
+ break;
+ case 7:
+ if(!(me & 0x80))
+ break;
+ switch(me & 0x0F)
+ {
+ case 2:
+ tf_getc(tf);
+ tf_getc(tf);
+ break;
+ case 3:
+ tf_getc(tf);
+ break;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ end_of_parse:
+ if(check_cache)
+ {
+ url_rewind(tf->url);
+ url_cache_disable(tf->url);
+ url_make_file_data(tf->url, p);
+ }
+ close_file(tf);
+ if(p->first_text == NULL)
+ p->first_text = safe_strdup("");
+ return get_midi_title1(p);
+}
+
+int midi_file_save_as(char *in_name, char *out_name)
+{
+ struct timidity_file *tf;
+ FILE* ofp;
+ char buff[BUFSIZ];
+ long n;
+
+ if(in_name == NULL)
+ {
+ if(current_file_info == NULL)
+ return 0;
+ in_name = current_file_info->filename;
+ }
+ out_name = (char *)url_expand_home_dir(out_name);
+
+ ctl->cmsg(CMSG_INFO, VERB_NORMAL, "Save as %s...", out_name);
+
+ errno = 0;
+ if((tf = open_midi_file(in_name, 1, 0)) == NULL)
+ {
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "%s: %s", out_name,
+ errno ? strerror(errno) : "Can't save file");
+ return -1;
+ }
+
+ errno = 0;
+ if((ofp = fopen(out_name, "wb")) == NULL)
+ {
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "%s: %s", out_name,
+ errno ? strerror(errno) : "Can't save file");
+ close_file(tf);
+ return -1;
+ }
+
+ while((n = tf_read(buff, 1, sizeof(buff), tf)) > 0) {
+ size_t dummy = fwrite(buff, 1, n, ofp); ++dummy;
+ }
+ ctl->cmsg(CMSG_INFO, VERB_NORMAL, "Save as %s...Done", out_name);
+
+ fclose(ofp);
+ close_file(tf);
+ return 0;
+}
+
+char *event2string(int id)
+{
+ if(id == 0)
+ return "";
+#ifdef ABORT_AT_FATAL
+ if(id >= string_event_table_size)
+ abort();
+#endif /* ABORT_AT_FATAL */
+ if(string_event_table == NULL || id < 0 || id >= string_event_table_size)
+ return NULL;
+ return string_event_table[id];
+}
+
+/*! initialize Delay Effect (GS) */
+void init_delay_status_gs(void)
+{
+ struct delay_status_gs_t *p = &delay_status_gs;
+ p->type = 0;
+ p->level = 0x40;
+ p->level_center = 0x7F;
+ p->level_left = 0;
+ p->level_right = 0;
+ p->time_c = 0x61;
+ p->time_l = 0x01;
+ p->time_r = 0x01;
+ p->feedback = 0x50;
+ p->pre_lpf = 0;
+ recompute_delay_status_gs();
+}
+
+/*! recompute Delay Effect (GS) */
+void recompute_delay_status_gs(void)
+{
+ struct delay_status_gs_t *p = &delay_status_gs;
+ p->time_center = delay_time_center_table[p->time_c > 0x73 ? 0x73 : p->time_c];
+ p->time_ratio_left = (double)p->time_l / 24;
+ p->time_ratio_right = (double)p->time_r / 24;
+ p->sample[0] = p->time_center * play_mode->rate / 1000.0f;
+ p->sample[1] = p->sample[0] * p->time_ratio_left;
+ p->sample[2] = p->sample[0] * p->time_ratio_right;
+ p->level_ratio[0] = p->level * p->level_center / (127.0f * 127.0f);
+ p->level_ratio[1] = p->level * p->level_left / (127.0f * 127.0f);
+ p->level_ratio[2] = p->level * p->level_right / (127.0f * 127.0f);
+ p->feedback_ratio = (double)(p->feedback - 64) * (0.763f * 2.0f / 100.0f);
+ p->send_reverb_ratio = (double)p->send_reverb * (0.787f / 100.0f);
+
+ if(p->level_left != 0 || (p->level_right != 0 && p->type == 0)) {
+ p->type = 1; /* it needs 3-tap delay effect. */
+ }
+
+ if(p->pre_lpf) {
+ p->lpf.a = 2.0 * ((double)(7 - p->pre_lpf) / 7.0f * 16000.0f + 200.0f) / play_mode->rate;
+ init_filter_lowpass1(&(p->lpf));
+ }
+}
+
+/*! Delay Macro (GS) */
+void set_delay_macro_gs(int macro)
+{
+ struct delay_status_gs_t *p = &delay_status_gs;
+ if(macro >= 4) {p->type = 2;} /* cross delay */
+ macro *= 10;
+ p->time_center = delay_time_center_table[delay_macro_presets[macro + 1]];
+ p->time_ratio_left = (double)delay_macro_presets[macro + 2] / 24;
+ p->time_ratio_right = (double)delay_macro_presets[macro + 3] / 24;
+ p->level_center = delay_macro_presets[macro + 4];
+ p->level_left = delay_macro_presets[macro + 5];
+ p->level_right = delay_macro_presets[macro + 6];
+ p->level = delay_macro_presets[macro + 7];
+ p->feedback = delay_macro_presets[macro + 8];
+}
+
+/*! initialize Reverb Effect (GS) */
+void init_reverb_status_gs(void)
+{
+ struct reverb_status_gs_t *p = &reverb_status_gs;
+ p->character = 0x04;
+ p->pre_lpf = 0;
+ p->level = 0x40;
+ p->time = 0x40;
+ p->delay_feedback = 0;
+ p->pre_delay_time = 0;
+ recompute_reverb_status_gs();
+ init_reverb();
+}
+
+/*! recompute Reverb Effect (GS) */
+void recompute_reverb_status_gs(void)
+{
+ struct reverb_status_gs_t *p = &reverb_status_gs;
+
+ if(p->pre_lpf) {
+ p->lpf.a = 2.0 * ((double)(7 - p->pre_lpf) / 7.0f * 16000.0f + 200.0f) / play_mode->rate;
+ init_filter_lowpass1(&(p->lpf));
+ }
+}
+
+/*! Reverb Type (GM2) */
+void set_reverb_macro_gm2(int macro)
+{
+ struct reverb_status_gs_t *p = &reverb_status_gs;
+ int type = macro;
+ if (macro == 8) {macro = 5;}
+ macro *= 6;
+ p->character = reverb_macro_presets[macro];
+ p->pre_lpf = reverb_macro_presets[macro + 1];
+ p->level = reverb_macro_presets[macro + 2];
+ p->time = reverb_macro_presets[macro + 3];
+ p->delay_feedback = reverb_macro_presets[macro + 4];
+ p->pre_delay_time = reverb_macro_presets[macro + 5];
+
+ switch(type) { /* override GS macro's parameter */
+ case 0: /* Small Room */
+ p->time = 44;
+ break;
+ case 1: /* Medium Room */
+ case 8: /* Plate */
+ p->time = 50;
+ break;
+ case 2: /* Large Room */
+ p->time = 56;
+ break;
+ case 3: /* Medium Hall */
+ case 4: /* Large Hall */
+ p->time = 64;
+ break;
+ }
+}
+
+/*! Reverb Macro (GS) */
+void set_reverb_macro_gs(int macro)
+{
+ struct reverb_status_gs_t *p = &reverb_status_gs;
+ macro *= 6;
+ p->character = reverb_macro_presets[macro];
+ p->pre_lpf = reverb_macro_presets[macro + 1];
+ p->level = reverb_macro_presets[macro + 2];
+ p->time = reverb_macro_presets[macro + 3];
+ p->delay_feedback = reverb_macro_presets[macro + 4];
+ p->pre_delay_time = reverb_macro_presets[macro + 5];
+}
+
+/*! initialize Chorus Effect (GS) */
+void init_chorus_status_gs(void)
+{
+ struct chorus_status_gs_t *p = &chorus_status_gs;
+ p->macro = 0;
+ p->pre_lpf = 0;
+ p->level = 0x40;
+ p->feedback = 0x08;
+ p->delay = 0x50;
+ p->rate = 0x03;
+ p->depth = 0x13;
+ p->send_reverb = 0;
+ p->send_delay = 0;
+ recompute_chorus_status_gs();
+}
+
+/*! recompute Chorus Effect (GS) */
+void recompute_chorus_status_gs()
+{
+ struct chorus_status_gs_t *p = &chorus_status_gs;
+
+ if(p->pre_lpf) {
+ p->lpf.a = 2.0 * ((double)(7 - p->pre_lpf) / 7.0f * 16000.0f + 200.0f) / play_mode->rate;
+ init_filter_lowpass1(&(p->lpf));
+ }
+}
+
+/*! Chorus Macro (GS), Chorus Type (GM2) */
+void set_chorus_macro_gs(int macro)
+{
+ struct chorus_status_gs_t *p = &chorus_status_gs;
+ macro *= 8;
+ p->pre_lpf = chorus_macro_presets[macro];
+ p->level = chorus_macro_presets[macro + 1];
+ p->feedback = chorus_macro_presets[macro + 2];
+ p->delay = chorus_macro_presets[macro + 3];
+ p->rate = chorus_macro_presets[macro + 4];
+ p->depth = chorus_macro_presets[macro + 5];
+ p->send_reverb = chorus_macro_presets[macro + 6];
+ p->send_delay = chorus_macro_presets[macro + 7];
+}
+
+/*! initialize EQ (GS) */
+void init_eq_status_gs(void)
+{
+ struct eq_status_gs_t *p = &eq_status_gs;
+ p->low_freq = 0;
+ p->low_gain = 0x40;
+ p->high_freq = 0;
+ p->high_gain = 0x40;
+ recompute_eq_status_gs();
+}
+
+/*! recompute EQ (GS) */
+void recompute_eq_status_gs(void)
+{
+ double freq, dbGain;
+ struct eq_status_gs_t *p = &eq_status_gs;
+
+ /* Lowpass Shelving Filter */
+ if(p->low_freq == 0) {freq = 200;}
+ else {freq = 400;}
+ dbGain = p->low_gain - 0x40;
+ if(freq < play_mode->rate / 2) {
+ p->lsf.q = 0;
+ p->lsf.freq = freq;
+ p->lsf.gain = dbGain;
+ calc_filter_shelving_low(&(p->lsf));
+ }
+
+ /* Highpass Shelving Filter */
+ if(p->high_freq == 0) {freq = 3000;}
+ else {freq = 6000;}
+ dbGain = p->high_gain - 0x40;
+ if(freq < play_mode->rate / 2) {
+ p->hsf.q = 0;
+ p->hsf.freq = freq;
+ p->hsf.gain = dbGain;
+ calc_filter_shelving_high(&(p->hsf));
+ }
+}
+
+/*! initialize Multi EQ (XG) */
+void init_multi_eq_xg(void)
+{
+ multi_eq_xg.valid = 0;
+ set_multi_eq_type_xg(0);
+ recompute_multi_eq_xg();
+}
+
+/*! set Multi EQ type (XG) */
+void set_multi_eq_type_xg(int type)
+{
+ struct multi_eq_xg_t *p = &multi_eq_xg;
+ type *= 20;
+ p->gain1 = multi_eq_block_table_xg[type];
+ p->freq1 = multi_eq_block_table_xg[type + 1];
+ p->q1 = multi_eq_block_table_xg[type + 2];
+ p->shape1 = multi_eq_block_table_xg[type + 3];
+ p->gain2 = multi_eq_block_table_xg[type + 4];
+ p->freq2 = multi_eq_block_table_xg[type + 5];
+ p->q2 = multi_eq_block_table_xg[type + 6];
+ p->gain3 = multi_eq_block_table_xg[type + 8];
+ p->freq3 = multi_eq_block_table_xg[type + 9];
+ p->q3 = multi_eq_block_table_xg[type + 10];
+ p->gain4 = multi_eq_block_table_xg[type + 12];
+ p->freq4 = multi_eq_block_table_xg[type + 13];
+ p->q4 = multi_eq_block_table_xg[type + 14];
+ p->gain5 = multi_eq_block_table_xg[type + 16];
+ p->freq5 = multi_eq_block_table_xg[type + 17];
+ p->q5 = multi_eq_block_table_xg[type + 18];
+ p->shape5 = multi_eq_block_table_xg[type + 19];
+}
+
+/*! recompute Multi EQ (XG) */
+void recompute_multi_eq_xg(void)
+{
+ struct multi_eq_xg_t *p = &multi_eq_xg;
+
+ if(p->freq1 != 0 && p->freq1 < 60 && p->gain1 != 0x40) {
+ p->valid1 = 1;
+ if(p->shape1) { /* peaking */
+ p->eq1p.q = (double)p->q1 / 10.0;
+ p->eq1p.freq = eq_freq_table_xg[p->freq1];
+ p->eq1p.gain = p->gain1 - 0x40;
+ calc_filter_peaking(&(p->eq1p));
+ } else { /* shelving */
+ p->eq1s.q = (double)p->q1 / 10.0;
+ p->eq1s.freq = eq_freq_table_xg[p->freq1];
+ p->eq1s.gain = p->gain1 - 0x40;
+ calc_filter_shelving_low(&(p->eq1s));
+ }
+ } else {p->valid1 = 0;}
+ if(p->freq2 != 0 && p->freq2 < 60 && p->gain2 != 0x40) {
+ p->valid2 = 1;
+ p->eq2p.q = (double)p->q2 / 10.0;
+ p->eq2p.freq = eq_freq_table_xg[p->freq2];
+ p->eq2p.gain = p->gain2 - 0x40;
+ calc_filter_peaking(&(p->eq2p));
+ } else {p->valid2 = 0;}
+ if(p->freq3 != 0 && p->freq3 < 60 && p->gain3 != 0x40) {
+ p->valid3 = 1;
+ p->eq3p.q = (double)p->q3 / 10.0;
+ p->eq4p.freq = eq_freq_table_xg[p->freq3];
+ p->eq4p.gain = p->gain3 - 0x40;
+ calc_filter_peaking(&(p->eq3p));
+ } else {p->valid3 = 0;}
+ if(p->freq4 != 0 && p->freq4 < 60 && p->gain4 != 0x40) {
+ p->valid4 = 1;
+ p->eq4p.q = (double)p->q4 / 10.0;
+ p->eq4p.freq = eq_freq_table_xg[p->freq4];
+ p->eq4p.gain = p->gain4 - 0x40;
+ calc_filter_peaking(&(p->eq4p));
+ } else {p->valid4 = 0;}
+ if(p->freq5 != 0 && p->freq5 < 60 && p->gain5 != 0x40) {
+ p->valid5 = 1;
+ if(p->shape5) { /* peaking */
+ p->eq5p.q = (double)p->q5 / 10.0;
+ p->eq5p.freq = eq_freq_table_xg[p->freq5];
+ p->eq5p.gain = p->gain5 - 0x40;
+ calc_filter_peaking(&(p->eq5p));
+ } else { /* shelving */
+ p->eq5s.q = (double)p->q5 / 10.0;
+ p->eq5s.freq = eq_freq_table_xg[p->freq5];
+ p->eq5s.gain = p->gain5 - 0x40;
+ calc_filter_shelving_high(&(p->eq5s));
+ }
+ } else {p->valid5 = 0;}
+ p->valid = p->valid1 || p->valid2 || p->valid3 || p->valid4 || p->valid5;
+}
+
+/*! convert GS user drumset assign groups to internal "alternate assign". */
+void recompute_userdrum_altassign(int bank, int group)
+{
+ int number = 0, i;
+ char *params[131], param[10];
+ ToneBank *bk;
+ UserDrumset *p;
+
+ for(p = userdrum_first; p != NULL; p = p->next) {
+ if(p->assign_group == group) {
+ sprintf(param, "%d", p->prog);
+ params[number] = safe_strdup(param);
+ number++;
+ }
+ }
+ params[number] = NULL;
+
+ alloc_instrument_bank(1, bank);
+ bk = drumset[bank];
+ bk->alt = add_altassign_string(bk->alt, params, number);
+ for (i = number - 1; i >= 0; i--)
+ free(params[i]);
+}
+
+/*! initialize GS user drumset. */
+void init_userdrum()
+{
+ int i;
+ AlternateAssign *alt;
+
+ free_userdrum();
+
+ for(i=0;i<2;i++) { /* allocate alternative assign */
+ alt = (AlternateAssign *)safe_malloc(sizeof(AlternateAssign));
+ memset(alt, 0, sizeof(AlternateAssign));
+ alloc_instrument_bank(1, 64 + i);
+ drumset[64 + i]->alt = alt;
+ }
+}
+
+/*! recompute GS user drumset. */
+Instrument *recompute_userdrum(int bank, int prog)
+{
+ UserDrumset *p;
+ Instrument *ip = NULL;
+
+ p = get_userdrum(bank, prog);
+
+ free_tone_bank_element(&drumset[bank]->tone[prog]);
+ if(drumset[p->source_prog]) {
+ ToneBankElement *source_tone = &drumset[p->source_prog]->tone[p->source_note];
+
+ if(source_tone->name == NULL /* NULL if "soundfont" directive is used */
+ && source_tone->instrument == NULL) {
+ if((ip = load_instrument(1, p->source_prog, p->source_note)) == NULL) {
+ ip = MAGIC_ERROR_INSTRUMENT;
+ }
+ source_tone->instrument = ip;
+ }
+ if(source_tone->name) {
+ copy_tone_bank_element(&drumset[bank]->tone[prog], source_tone);
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"User Drumset (%d %d -> %d %d)", p->source_prog, p->source_note, bank, prog);
+ } else if(drumset[0]->tone[p->source_note].name) {
+ copy_tone_bank_element(&drumset[bank]->tone[prog], &drumset[0]->tone[p->source_note]);
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"User Drumset (%d %d -> %d %d)", 0, p->source_note, bank, prog);
+ } else {
+ ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "Referring user drum set %d, note %d not found - this instrument will not be heard as expected", bank, prog);
+ }
+ }
+ return ip;
+}
+
+/*! get pointer to requested GS user drumset.
+ if it's not found, allocate a new item first. */
+UserDrumset *get_userdrum(int bank, int prog)
+{
+ UserDrumset *p;
+
+ for(p = userdrum_first; p != NULL; p = p->next) {
+ if(p->bank == bank && p->prog == prog) {return p;}
+ }
+
+ p = (UserDrumset *)safe_malloc(sizeof(UserDrumset));
+ memset(p, 0, sizeof(UserDrumset));
+ p->next = NULL;
+ if(userdrum_first == NULL) {
+ userdrum_first = p;
+ userdrum_last = p;
+ } else {
+ userdrum_last->next = p;
+ userdrum_last = p;
+ }
+ p->bank = bank;
+ p->prog = prog;
+
+ return p;
+}
+
+/*! free GS user drumset. */
+void free_userdrum()
+{
+ UserDrumset *p, *next;
+
+ for(p = userdrum_first; p != NULL; p = next){
+ next = p->next;
+ free(p);
+ }
+ userdrum_first = userdrum_last = NULL;
+}
+
+/*! initialize GS user instrument. */
+void init_userinst()
+{
+ free_userinst();
+}
+
+/*! recompute GS user instrument. */
+void recompute_userinst(int bank, int prog)
+{
+ UserInstrument *p;
+
+ p = get_userinst(bank, prog);
+
+ free_tone_bank_element(&tonebank[bank]->tone[prog]);
+ if(tonebank[p->source_bank]) {
+ if(tonebank[p->source_bank]->tone[p->source_prog].name) {
+ copy_tone_bank_element(&tonebank[bank]->tone[prog], &tonebank[p->source_bank]->tone[p->source_prog]);
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"User Instrument (%d %d -> %d %d)", p->source_bank, p->source_prog, bank, prog);
+ } else if(tonebank[0]->tone[p->source_prog].name) {
+ copy_tone_bank_element(&tonebank[bank]->tone[prog], &tonebank[0]->tone[p->source_prog]);
+ ctl->cmsg(CMSG_INFO,VERB_NOISY,"User Instrument (%d %d -> %d %d)", 0, p->source_prog, bank, prog);
+ }
+ }
+}
+
+/*! get pointer to requested GS user instrument.
+ if it's not found, allocate a new item first. */
+UserInstrument *get_userinst(int bank, int prog)
+{
+ UserInstrument *p;
+
+ for(p = userinst_first; p != NULL; p = p->next) {
+ if(p->bank == bank && p->prog == prog) {return p;}
+ }
+
+ p = (UserInstrument *)safe_malloc(sizeof(UserInstrument));
+ memset(p, 0, sizeof(UserInstrument));
+ p->next = NULL;
+ if(userinst_first == NULL) {
+ userinst_first = p;
+ userinst_last = p;
+ } else {
+ userinst_last->next = p;
+ userinst_last = p;
+ }
+ p->bank = bank;
+ p->prog = prog;
+
+ return p;
+}
+
+/*! free GS user instrument. */
+void free_userinst()
+{
+ UserInstrument *p, *next;
+
+ for(p = userinst_first; p != NULL; p = next){
+ next = p->next;
+ free(p);
+ }
+ userinst_first = userinst_last = NULL;
+}
+
+static void set_effect_param_xg(struct effect_xg_t *st, int type_msb, int type_lsb)
+{
+ int i, j;
+ for (i = 0; effect_parameter_xg[i].type_msb != -1
+ && effect_parameter_xg[i].type_lsb != -1; i++) {
+ if (type_msb == effect_parameter_xg[i].type_msb
+ && type_lsb == effect_parameter_xg[i].type_lsb) {
+ for (j = 0; j < 16; j++) {
+ st->param_lsb[j] = effect_parameter_xg[i].param_lsb[j];
+ }
+ for (j = 0; j < 10; j++) {
+ st->param_msb[j] = effect_parameter_xg[i].param_msb[j];
+ }
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "XG EFX: %s", effect_parameter_xg[i].name);
+ return;
+ }
+ }
+ if (type_msb != 0) {
+ for (i = 0; effect_parameter_xg[i].type_msb != -1
+ && effect_parameter_xg[i].type_lsb != -1; i++) {
+ if (type_lsb == effect_parameter_xg[i].type_lsb) {
+ for (j = 0; j < 16; j++) {
+ st->param_lsb[j] = effect_parameter_xg[i].param_lsb[j];
+ }
+ for (j = 0; j < 10; j++) {
+ st->param_msb[j] = effect_parameter_xg[i].param_msb[j];
+ }
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "XG EFX: %s", effect_parameter_xg[i].name);
+ return;
+ }
+ }
+ }
+}
+
+/*! recompute XG effect parameters. */
+void recompute_effect_xg(struct effect_xg_t *st)
+{
+ EffectList *efc = st->ef;
+
+ if (efc == NULL) {return;}
+ while (efc != NULL && efc->info != NULL)
+ {
+ (*efc->engine->conv_xg)(st, efc);
+ (*efc->engine->do_effect)(NULL, MAGIC_INIT_EFFECT_INFO, efc);
+ efc = efc->next_ef;
+ }
+}
+
+void realloc_effect_xg(struct effect_xg_t *st)
+{
+ int type_msb = st->type_msb, type_lsb = st->type_lsb;
+
+ free_effect_list(st->ef);
+ st->ef = NULL;
+ st->use_msb = 0;
+
+ switch(type_msb) {
+ case 0x05:
+ st->use_msb = 1;
+ st->ef = push_effect(st->ef, EFFECT_DELAY_LCR);
+ st->ef = push_effect(st->ef, EFFECT_DELAY_EQ2);
+ break;
+ case 0x06:
+ st->use_msb = 1;
+ st->ef = push_effect(st->ef, EFFECT_DELAY_LR);
+ st->ef = push_effect(st->ef, EFFECT_DELAY_EQ2);
+ break;
+ case 0x07:
+ st->use_msb = 1;
+ st->ef = push_effect(st->ef, EFFECT_ECHO);
+ st->ef = push_effect(st->ef, EFFECT_DELAY_EQ2);
+ break;
+ case 0x08:
+ st->use_msb = 1;
+ st->ef = push_effect(st->ef, EFFECT_CROSS_DELAY);
+ st->ef = push_effect(st->ef, EFFECT_DELAY_EQ2);
+ break;
+ case 0x41:
+ case 0x42:
+ st->ef = push_effect(st->ef, EFFECT_CHORUS);
+ st->ef = push_effect(st->ef, EFFECT_CHORUS_EQ3);
+ break;
+ case 0x43:
+ st->ef = push_effect(st->ef, EFFECT_FLANGER);
+ st->ef = push_effect(st->ef, EFFECT_CHORUS_EQ3);
+ break;
+ case 0x44:
+ st->ef = push_effect(st->ef, EFFECT_SYMPHONIC);
+ st->ef = push_effect(st->ef, EFFECT_CHORUS_EQ3);
+ break;
+ case 0x49:
+ st->ef = push_effect(st->ef, EFFECT_STEREO_DISTORTION);
+ st->ef = push_effect(st->ef, EFFECT_OD_EQ3);
+ break;
+ case 0x4A:
+ st->ef = push_effect(st->ef, EFFECT_STEREO_OVERDRIVE);
+ st->ef = push_effect(st->ef, EFFECT_OD_EQ3);
+ break;
+ case 0x4B:
+ st->ef = push_effect(st->ef, EFFECT_STEREO_AMP_SIMULATOR);
+ break;
+ case 0x4C:
+ st->ef = push_effect(st->ef, EFFECT_EQ3);
+ break;
+ case 0x4D:
+ st->ef = push_effect(st->ef, EFFECT_EQ2);
+ break;
+ case 0x4E:
+ if (type_lsb == 0x01 || type_lsb == 0x02) {
+ st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH);
+ st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH_EQ2);
+ st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH_OD);
+ st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH_OD_EQ3);
+ } else {
+ st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH);
+ st->ef = push_effect(st->ef, EFFECT_XG_AUTO_WAH_EQ2);
+ }
+ break;
+ case 0x5E:
+ st->ef = push_effect(st->ef, EFFECT_LOFI);
+ break;
+ default: /* Not Supported */
+ type_msb = type_lsb = 0;
+ break;
+ }
+ set_effect_param_xg(st, type_msb, type_lsb);
+ recompute_effect_xg(st);
+}
+
+static void init_effect_xg(struct effect_xg_t *st)
+{
+ int i;
+
+ free_effect_list(st->ef);
+ st->ef = NULL;
+
+ st->use_msb = 0;
+ st->type_msb = st->type_lsb = st->connection =
+ st->send_reverb = st->send_chorus = 0;
+ st->part = 0x7f;
+ st->ret = st->pan = st->mw_depth = st->bend_depth = st->cat_depth =
+ st->ac1_depth = st->ac2_depth = st->cbc1_depth = st->cbc2_depth = 0x40;
+ for (i = 0; i < 16; i++) {st->param_lsb[i] = 0;}
+ for (i = 0; i < 10; i++) {st->param_msb[i] = 0;}
+}
+
+/*! initialize XG effect parameters */
+static void init_all_effect_xg(void)
+{
+ int i;
+ init_effect_xg(&reverb_status_xg);
+ reverb_status_xg.type_msb = 0x01;
+ reverb_status_xg.connection = XG_CONN_SYSTEM_REVERB;
+ realloc_effect_xg(&reverb_status_xg);
+ init_effect_xg(&chorus_status_xg);
+ chorus_status_xg.type_msb = 0x41;
+ chorus_status_xg.connection = XG_CONN_SYSTEM_CHORUS;
+ realloc_effect_xg(&chorus_status_xg);
+ for (i = 0; i < XG_VARIATION_EFFECT_NUM; i++) {
+ init_effect_xg(&variation_effect_xg[i]);
+ variation_effect_xg[i].type_msb = 0x05;
+ realloc_effect_xg(&variation_effect_xg[i]);
+ }
+ for (i = 0; i < XG_INSERTION_EFFECT_NUM; i++) {
+ init_effect_xg(&insertion_effect_xg[i]);
+ insertion_effect_xg[i].type_msb = 0x49;
+ realloc_effect_xg(&insertion_effect_xg[i]);
+ }
+ init_ch_effect_xg();
+}
+
+/*! initialize GS insertion effect parameters */
+void init_insertion_effect_gs(void)
+{
+ int i;
+ struct insertion_effect_gs_t *st = &insertion_effect_gs;
+
+ free_effect_list(st->ef);
+ st->ef = NULL;
+
+ for(i = 0; i < 20; i++) {st->parameter[i] = 0;}
+
+ st->type = 0;
+ st->type_lsb = 0;
+ st->type_msb = 0;
+ st->send_reverb = 0x28;
+ st->send_chorus = 0;
+ st->send_delay = 0;
+ st->control_source1 = 0;
+ st->control_depth1 = 0x40;
+ st->control_source2 = 0;
+ st->control_depth2 = 0x40;
+ st->send_eq_switch = 0x01;
+}
+
+static void set_effect_param_gs(struct insertion_effect_gs_t *st, int msb, int lsb)
+{
+ int i, j;
+ for (i = 0; effect_parameter_gs[i].type_msb != -1
+ && effect_parameter_gs[i].type_lsb != -1; i++) {
+ if (msb == effect_parameter_gs[i].type_msb
+ && lsb == effect_parameter_gs[i].type_lsb) {
+ for (j = 0; j < 20; j++) {
+ st->parameter[j] = effect_parameter_gs[i].param[j];
+ }
+ ctl->cmsg(CMSG_INFO, VERB_NOISY, "GS EFX: %s", effect_parameter_gs[i].name);
+ break;
+ }
+ }
+}
+
+/*! recompute GS insertion effect parameters. */
+void recompute_insertion_effect_gs(void)
+{
+ struct insertion_effect_gs_t *st = &insertion_effect_gs;
+ EffectList *efc = st->ef;
+
+ if (st->ef == NULL) {return;}
+ while(efc != NULL && efc->info != NULL)
+ {
+ (*efc->engine->conv_gs)(st, efc);
+ (*efc->engine->do_effect)(NULL, MAGIC_INIT_EFFECT_INFO, efc);
+ efc = efc->next_ef;
+ }
+}
+
+/*! re-allocate GS insertion effect parameters. */
+void realloc_insertion_effect_gs(void)
+{
+ struct insertion_effect_gs_t *st = &insertion_effect_gs;
+ int type_msb = st->type_msb, type_lsb = st->type_lsb;
+
+ free_effect_list(st->ef);
+ st->ef = NULL;
+
+ switch(type_msb) {
+ case 0x01:
+ switch(type_lsb) {
+ case 0x00: /* Stereo-EQ */
+ st->ef = push_effect(st->ef, EFFECT_STEREO_EQ);
+ break;
+ case 0x10: /* Overdrive */
+ st->ef = push_effect(st->ef, EFFECT_EQ2);
+ st->ef = push_effect(st->ef, EFFECT_OVERDRIVE1);
+ break;
+ case 0x11: /* Distortion */
+ st->ef = push_effect(st->ef, EFFECT_EQ2);
+ st->ef = push_effect(st->ef, EFFECT_DISTORTION1);
+ break;
+ case 0x40: /* Hexa Chorus */
+ st->ef = push_effect(st->ef, EFFECT_EQ2);
+ st->ef = push_effect(st->ef, EFFECT_HEXA_CHORUS);
+ break;
+ case 0x72: /* Lo-Fi 1 */
+ st->ef = push_effect(st->ef, EFFECT_EQ2);
+ st->ef = push_effect(st->ef, EFFECT_LOFI1);
+ break;
+ case 0x73: /* Lo-Fi 2 */
+ st->ef = push_effect(st->ef, EFFECT_EQ2);
+ st->ef = push_effect(st->ef, EFFECT_LOFI2);
+ break;
+ default: break;
+ }
+ break;
+ case 0x11:
+ switch(type_lsb) {
+ case 0x03: /* OD1 / OD2 */
+ st->ef = push_effect(st->ef, EFFECT_OD1OD2);
+ break;
+ default: break;
+ }
+ break;
+ default: break;
+ }
+
+ set_effect_param_gs(st, type_msb, type_lsb);
+
+ recompute_insertion_effect_gs();
+}
+
+/*! initialize channel layers. */
+void init_channel_layer(int ch)
+{
+ if (ch >= MAX_CHANNELS)
+ return;
+ CLEAR_CHANNELMASK(channel[ch].channel_layer);
+ SET_CHANNELMASK(channel[ch].channel_layer, ch);
+ channel[ch].port_select = ch >> 4;
+}
+
+/*! add a new layer. */
+void add_channel_layer(int to_ch, int from_ch)
+{
+ if (to_ch >= MAX_CHANNELS || from_ch >= MAX_CHANNELS)
+ return;
+ /* add a channel layer */
+ UNSET_CHANNELMASK(channel[to_ch].channel_layer, to_ch);
+ SET_CHANNELMASK(channel[to_ch].channel_layer, from_ch);
+ ctl->cmsg(CMSG_INFO, VERB_NOISY,
+ "Channel Layer (CH:%d -> CH:%d)", from_ch, to_ch);
+}
+
+/*! remove all layers for this channel. */
+void remove_channel_layer(int ch)
+{
+ int i, offset;
+
+ if (ch >= MAX_CHANNELS)
+ return;
+ /* remove channel layers */
+ offset = ch & ~0xf;
+ for (i = offset; i < offset + REDUCE_CHANNELS; i++)
+ UNSET_CHANNELMASK(channel[i].channel_layer, ch);
+ SET_CHANNELMASK(channel[ch].channel_layer, ch);
+}
+
+void free_readmidi(void)
+{
+ reuse_mblock(&mempool);
+ free_all_midi_file_info();
+ free_userdrum();
+ free_userinst();
+ if (string_event_strtab.nstring > 0)
+ delete_string_table(&string_event_strtab);
+ if (string_event_table != NULL) {
+ free(string_event_table[0]);
+ free(string_event_table);
+ string_event_table = NULL;
+ string_event_table_size = 0;
+ }
+}
+
+int main(){
+ return 0; //well obviously this won't compile because of all the libs it asks for, should we just copy those to this directory too? well need to figure out which to compy, overwise we will end up with whole timidity here.
+}
diff --git a/src/readmidi.h b/src/readmidi.h
new file mode 100644
index 0000000..2d8a843
--- /dev/null
+++ b/src/readmidi.h
@@ -0,0 +1,191 @@
+/*
+ TiMidity++ -- MIDI to WAVE converter and player
+ Copyright (C) 1999-2004 Masanao Izumo <iz@onicos.co.jp>
+ Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ readmidi.h
+*/
+
+#ifndef ___READMIDI_H_
+#define ___READMIDI_H_
+#include "reverb.h"
+
+/* MIDI file types */
+#define IS_ERROR_FILE -1 /* Error file */
+#define IS_OTHER_FILE 0 /* Not a MIDI file */
+#define IS_SMF_FILE 101 /* Standard MIDI File */ //here wow...well we don't care about any of this, right? yep
+#define IS_MCP_FILE 201 /* MCP */
+#define IS_RCP_FILE 202 /* Recomposer */
+#define IS_R36_FILE 203 /* Recomposer */
+#define IS_G18_FILE 204 /* Recomposer */
+#define IS_G36_FILE 205 /* Recomposer */
+#define IS_SNG_FILE 301
+#define IS_MM2_FILE 401
+#define IS_MML_FILE 501
+#define IS_FM_FILE 601
+#define IS_FPD_FILE 602
+#define IS_MOD_FILE 701 /* Pro/Fast/Star/Noise Tracker */
+#define IS_669_FILE 702 /* Composer 669, UNIS669 */
+#define IS_MTM_FILE 703 /* MultiModuleEdit */
+#define IS_STM_FILE 704 /* ScreamTracker 2 */
+#define IS_S3M_FILE 705 /* ScreamTracker 3 */
+#define IS_ULT_FILE 706 /* UltraTracker */
+#define IS_XM_FILE 707 /* FastTracker 2 */
+#define IS_FAR_FILE 708 /* Farandole Composer */
+#define IS_WOW_FILE 709 /* Grave Composer */
+#define IS_OKT_FILE 710 /* Oktalyzer */
+#define IS_DMF_FILE 711 /* X-Tracker */
+#define IS_MED_FILE 712 /* MED/OctaMED */
+#define IS_IT_FILE 713 /* Impulse Tracker */
+#define IS_PTM_FILE 714 /* Poly Tracker */
+#define IS_MFI_FILE 800 /* Melody Format for i-mode */
+
+#define REDUCE_CHANNELS 16
+
+//so basically what is needed to make this file stand alone? we need to separate it from its libs, but there is
+//no main function here, so how much removal actually needs to be done? well for start there is no need to remove anything, just need pull this file and header plus anything it depend on, like file
+
+
+enum play_system_modes
+{
+ DEFAULT_SYSTEM_MODE,
+ GM_SYSTEM_MODE,
+ GM2_SYSTEM_MODE,
+ GS_SYSTEM_MODE,
+ XG_SYSTEM_MODE
+};
+
+enum {
+ PCM_MODE_NON = 0,
+ PCM_MODE_WAV,
+ PCM_MODE_AIFF,
+ PCM_MODE_AU,
+ PCM_MODE_MP3
+};
+
+#define IS_CURRENT_MOD_FILE \
+ (current_file_info && \
+ current_file_info->file_type >= 700 && \
+ current_file_info->file_type < 800)
+
+typedef struct {
+ MidiEvent event;
+ void *next;
+ void *prev;
+} MidiEventList;
+
+struct midi_file_info
+{
+ int readflag;
+ char *filename;
+ char *seq_name;
+ char *karaoke_title;
+ char *first_text;
+ uint8 mid; /* Manufacture ID (0x41/Roland, 0x43/Yamaha, etc...) */
+ int16 hdrsiz;
+ int16 format;
+ int16 tracks;
+ int32 divisions;
+ int time_sig_n, time_sig_d, time_sig_c, time_sig_b; /* Time signature */
+ int drumchannels_isset;
+ ChannelBitMask drumchannels;
+ ChannelBitMask drumchannel_mask;
+ int32 samples;
+ int max_channel;
+ struct midi_file_info *next;
+ int compressed; /* True if midi_data is compressed */
+ char *midi_data;
+ int32 midi_data_size;
+ int file_type;
+
+ int pcm_mode;
+ char *pcm_filename;
+ struct timidity_file *pcm_tf;
+};
+// those funcs looks like does all parsing and above is structs too
+//ok so in terms of compilation, do we want to start another script, with a main function that includes this header file? or
+//do we want to try to compile this into something like a .so?
+//jus a file with main function and this .c file compiled. i guess no need to integrate with cli-dssi-host yet, to avoid additional bugs early. right
+extern int32 readmidi_set_track(int trackno, int rewindp);
+extern void readmidi_add_event(MidiEvent *newev);
+extern void readmidi_add_ctl_event(int32 at, int ch, int control, int val);
+extern int parse_sysex_event(uint8 *data, int32 datalen, MidiEvent *ev_ret);
+extern int parse_sysex_event_multi(uint8 *data, int32 datalen, MidiEvent *ev_ret);
+extern int convert_midi_control_change(int chn, int type, int val,
+ MidiEvent *ev_ret);
+extern int unconvert_midi_control_change(MidiEvent *ev);
+extern char *readmidi_make_string_event(int type, char *string, MidiEvent *ev,
+ int cnv);
+extern MidiEvent *read_midi_file(struct timidity_file *mtf, // this, timidity file struct.
+// basically need to try to compile it, and check evey warning or error and fix that, a bit iterative process.
+ int32 *count, int32 *sp, char *file_name); // here good so should we take the code and header out and see if we can get it to read out_name
+//events? yep
+//ok so some of this stuff we want to remove, right? yeah like get_midi_file_info from filename most likely is not needed, unless used by read_midi_file or something.
+extern struct midi_file_info *get_midi_file_info(char *filename,int newp);
+extern struct midi_file_info *new_midi_file_info(const char *filename);
+extern void free_all_midi_file_info(void);
+extern int check_midi_file(char *filename);
+extern char *get_midi_title(char *filename);
+extern struct timidity_file *open_midi_file(char *name,
+ int decompress, int noise_mode);
+extern int midi_file_save_as(char *in_name, char *out_name);
+extern char *event2string(int id);
+extern void change_system_mode(int mode);
+extern int get_default_mapID(int ch);
+extern int dump_current_timesig(MidiEvent *codes, int maxlen);
+
+extern ChannelBitMask quietchannels;
+extern struct midi_file_info *current_file_info;
+extern int opt_trace_text_meta_event;
+extern int opt_default_mid;
+extern int opt_system_mid;
+extern int ignore_midi_error;
+extern int readmidi_error_flag;
+extern int readmidi_wrd_mode;
+extern int play_system_mode;
+
+//I guess I'm talking more about this stuff below, seems like maybe this is timidity-specific, or maybe meant for general midi? looks like there is a lot of formats of midi, i've noticed above list of defines and there is only on standard midi format, and a lot of intrument specific.
+extern void recompute_delay_status_gs(void);
+extern void set_delay_macro_gs(int);
+extern void recompute_chorus_status_gs(void);
+extern void set_chorus_macro_gs(int);
+extern void recompute_reverb_status_gs(void);
+extern void set_reverb_macro_gs(int);
+extern void set_reverb_macro_gm2(int);
+extern void recompute_eq_status_gs(void);
+extern void realloc_insertion_effect_gs(void);
+extern void recompute_insertion_effect_gs(void);
+extern void recompute_multi_eq_xg(void);
+extern void set_multi_eq_type_xg(int);
+extern void realloc_effect_xg(struct effect_xg_t *st);
+extern void recompute_effect_xg(struct effect_xg_t *st);
+
+extern Instrument *recompute_userdrum(int bank, int prog);
+extern void free_userdrum();
+
+extern void recompute_userinst(int bank, int prog);
+extern void free_userinst();
+
+extern void init_channel_layer(int);
+extern void add_channel_layer(int, int);
+extern void remove_channel_layer(int);
+
+extern void free_readmidi(void);
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#endif /* ___READMIDI_H_ */
diff --git a/src/strtab.c b/src/strtab.c
new file mode 100644
index 0000000..24ebfc7
--- /dev/null
+++ b/src/strtab.c
@@ -0,0 +1,109 @@
+/*
+ TiMidity++ -- MIDI to WAVE converter and player
+ Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
+ Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef NO_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+#include "timidity.h"
+#include "common.h"
+#include "mblock.h"
+#include "strtab.h"
+
+void init_string_table(StringTable *stab)
+{
+ memset(stab, 0, sizeof(StringTable));
+}
+
+StringTableNode *put_string_table(StringTable *stab, char *str, int len)
+{
+ StringTableNode *p;
+
+ p = new_segment(&stab->pool, sizeof(StringTableNode) + len + 1);
+ if(p == NULL)
+ return NULL;
+ p->next = NULL;
+ if(str != NULL)
+ {
+ memcpy(p->string, str, len);
+ p->string[len] = '\0';
+ }
+
+ if(stab->head == NULL)
+ {
+ stab->head = stab->tail = p;
+ stab->nstring = 1;
+ }
+ else
+ {
+ stab->nstring++;
+ stab->tail = stab->tail->next = p;
+ }
+ return p;
+}
+
+char **make_string_array(StringTable *stab)
+{
+ char **table, *u;
+ int i, n, s;
+ StringTableNode *p;
+
+ n = stab->nstring;
+ if(n == 0)
+ return NULL;
+ if((table = (char **)safe_malloc((n + 1) * sizeof(char *))) == NULL)
+ return NULL;
+
+ s = 0;
+ for(p = stab->head; p; p = p->next)
+ s += strlen(p->string) + 1;
+
+ if((u = (char *)safe_malloc(s)) == NULL)
+ {
+ free(table);
+ return NULL;
+ }
+
+ for(i = 0, p = stab->head; p; i++, p = p->next)
+ {
+ int len;
+
+ len = strlen(p->string) + 1;
+ table[i] = u;
+ memcpy(u, p->string, len);
+ u += len;
+ }
+ table[i] = NULL;
+ delete_string_table(stab);
+ return table;
+}
+
+void delete_string_table(StringTable *stab)
+{
+ reuse_mblock(&stab->pool);
+ init_string_table(stab);
+}
diff --git a/src/tables.c b/src/tables.c
new file mode 100644
index 0000000..9f55f54
--- /dev/null
+++ b/src/tables.c
@@ -0,0 +1,1685 @@
+/*
+ TiMidity++ -- MIDI to WAVE converter and player
+ Copyright (C) 1999-2004 Masanao Izumo <iz@onicos.co.jp>
+ Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ The 8-bit uLaw to 16-bit PCM and the 13-bit-PCM to 8-bit uLaw
+ tables were lifted from the rsynth-2.0 sources. The README says:
+
+ This is a text to speech system produced by integrating various pieces
+ of code and tables of data, which are all (I believe) in the public domain.
+
+ The bulk of the intergration was done by myself, that is Nick Ing-Simmons.
+ I can be reached via my employer at nik@tiuk.ti.com.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+#include <stdio.h>
+#include <math.h>
+#include "timidity.h"
+#include "common.h"
+#include "mt19937ar.h"
+#include "tables.h"
+
+int32 freq_table[128];
+int32 freq_table_zapped[128];
+int32 freq_table_tuning[128][128];
+int32 freq_table_pytha[24][128];
+int32 freq_table_meantone[48][128];
+int32 freq_table_pureint[48][128];
+int32 freq_table_user[4][48][128];
+
+void init_freq_table(void)
+{
+ int i;
+
+ for (i = 0; i < 128; i++) {
+ freq_table[i] = 440 * pow(2.0, (i - 69) / 12.0) * 1000 + 0.5;
+ freq_table_zapped[i] = freq_table[i];
+ }
+}
+
+void init_freq_table_tuning(void)
+{
+ int p, i;
+ double f;
+
+ for (i = 0; i < 128; i++)
+ freq_table_tuning[0][i] = freq_table_zapped[i];
+ for (i = 0; i < 128; i++) {
+ f = 440 * pow(2.0, (i - 69) / 12.0);
+ for (p = 1; p < 128; p++)
+ freq_table_tuning[p][i] = f * 1000 + 0.5;
+ }
+}
+
+void init_freq_table_pytha(void)
+{
+ int i, j, k, l;
+ double f;
+ static const double major_ratio[] = {
+ 1.0 / 1, 256.0 / 243, 9.0 / 8, 32.0 / 27,
+ 81.0 / 64, 4.0 / 3, 729.0 / 512, 3.0 / 2,
+ 128.0 / 81, 27.0 / 16, 16.0 / 9, 243.0 / 128
+ };
+ static const double minor_ratio[] = {
+ 1.0 / 1, 2187.0 / 2048, 9.0 / 8, 19683.0 / 16384,
+ 81.0 / 64, 4.0 / 3, 729.0 / 512, 3.0 / 2,
+ 6561.0 / 4096, 27.0 / 16, 16.0 / 9, 243.0 / 128
+ };
+
+ for (i = 0; i < 12; i++)
+ for (j = -1; j < 11; j++) {
+ f = 440 * pow(2.0, (i - 9) / 12.0 + j - 5);
+ for (k = 0; k < 12; k++) {
+ l = i + j * 12 + k;
+ if (l < 0 || l >= 128)
+ continue;
+ freq_table_pytha[i][l] = f * major_ratio[k] * 1000 + 0.5;
+ freq_table_pytha[i + 12][l] = f * minor_ratio[k] * 1000 + 0.5;
+ }
+ }
+}
+
+void init_freq_table_meantone(void)
+{
+ int i, j, k, l;
+ double f;
+ static double major_ratio[12], minor_ratio[12];
+ static const double sc = 81.0 / 80;
+
+ major_ratio[0] = 1;
+ major_ratio[1] = 8 / pow(5.0, 5.0 / 4);
+ major_ratio[2] = pow(5.0, 1.0 / 2) / 2;
+ major_ratio[3] = 4 / pow(5.0, 3.0 / 4);
+ major_ratio[4] = 5.0 / 4;
+ major_ratio[5] = 2 / pow(5.0, 1.0 / 4);
+ major_ratio[6] = pow(5.0, 3.0 / 2) / 8;
+ major_ratio[7] = pow(5.0, 1.0 / 4);
+ major_ratio[8] = 8.0 / 5;
+ major_ratio[9] = pow(5.0, 3.0 / 4) / 2;
+ major_ratio[10] = 4 / pow(5.0, 1.0 / 2);
+ major_ratio[11] = pow(5.0, 5.0 / 4) / 4;
+ minor_ratio[0] = 1;
+ minor_ratio[1] = pow(10.0 / 3, 7.0 / 3) / 16;
+ minor_ratio[2] = pow(10.0 / 3, 2.0 / 3) / 2;
+ minor_ratio[3] = 125.0 / 108;
+ minor_ratio[4] = pow(10.0 / 3, 4.0 / 3) / 4;
+ minor_ratio[5] = 2 / pow(10.0 / 3, 1.0 / 3);
+ minor_ratio[6] = 25.0 / 18;
+ minor_ratio[7] = pow(10.0 / 3, 1.0 / 3);
+ minor_ratio[8] = pow(10.0 / 3, 8.0 / 3) / 16;
+ minor_ratio[9] = 5.0 / 3;
+ minor_ratio[10] = 4 / pow(10.0 / 3, 2.0 / 3);
+ minor_ratio[11] = pow(10.0 / 3, 5.0 / 3) / 4;
+ for (i = 0; i < 12; i++)
+ for (j = -1; j < 11; j++) {
+ f = 440 * pow(2.0, (i - 9) / 12.0 + j - 5);
+ for (k = 0; k < 12; k++) {
+ l = i + j * 12 + k;
+ if (l < 0 || l >= 128)
+ continue;
+ freq_table_meantone[i][l] =
+ f * major_ratio[k] * 1000 + 0.5;
+ freq_table_meantone[i + 12][l] =
+ f * minor_ratio[k] * sc * 1000 + 0.5;
+ freq_table_meantone[i + 24][l] =
+ f * minor_ratio[k] * 1000 + 0.5;
+ freq_table_meantone[i + 36][l] =
+ f * major_ratio[k] * sc * 1000 + 0.5;
+ }
+ }
+}
+
+void init_freq_table_pureint(void)
+{
+ int i, j, k, l;
+ double f;
+ static const double major_ratio[] = {
+ 1.0 / 1, 16.0 / 15, 9.0 / 8, 6.0 / 5, 5.0 / 4, 4.0 / 3,
+ 45.0 / 32, 3.0 / 2, 8.0 / 5, 5.0 / 3, 9.0 / 5, 15.0 / 8
+ };
+ static const double minor_ratio[] = {
+ 1.0 / 1, 25.0 / 24, 10.0 / 9, 75.0 / 64, 5.0 / 4, 4.0 / 3,
+ 25.0 / 18, 3.0 / 2, 25.0 / 16, 5.0 / 3, 16.0 / 9, 15.0 / 8
+ };
+ static const double sc = 81.0 / 80;
+
+ for (i = 0; i < 12; i++)
+ for (j = -1; j < 11; j++) {
+ f = 440 * pow(2.0, (i - 9) / 12.0 + j - 5);
+ for (k = 0; k < 12; k++) {
+ l = i + j * 12 + k;
+ if (l < 0 || l >= 128)
+ continue;
+ freq_table_pureint[i][l] =
+ f * major_ratio[k] * 1000 + 0.5;
+ freq_table_pureint[i + 12][l] =
+ f * minor_ratio[k] * sc * 1000 + 0.5;
+ freq_table_pureint[i + 24][l] =
+ f * minor_ratio[k] * 1000 + 0.5;
+ freq_table_pureint[i + 36][l] =
+ f * major_ratio[k] * sc * 1000 + 0.5;
+ }
+ }
+}
+
+void init_freq_table_user(void)
+{
+ int p, i, j, k, l;
+ double f;
+
+ for (p = 0; p < 4; p++)
+ for (i = 0; i < 12; i++)
+ for (j = -1; j < 11; j++) {
+ f = 440 * pow(2.0, (i - 9) / 12.0 + j - 5);
+ for (k = 0; k < 12; k++) {
+ l = i + j * 12 + k;
+ if (l < 0 || l >= 128)
+ continue;
+ freq_table_user[p][i][l] = f * 1000 + 0.5;
+ freq_table_user[p][i + 12][l] = f * 1000 + 0.5;
+ freq_table_user[p][i + 24][l] = f * 1000 + 0.5;
+ freq_table_user[p][i + 36][l] = f * 1000 + 0.5;
+ }
+ }
+}
+
+/* v=2.^((x/127-1) * 6) */
+FLOAT_T def_vol_table[1024];
+
+void init_def_vol_table(void)
+{
+ int i;
+
+ for (i = 0; i < 1024; i++)
+ def_vol_table[i] = pow(2.0f,((double)i / 1023.0 - 1) * 6);
+}
+
+/* v=2.^((x/127-1) * 8) */
+FLOAT_T gs_vol_table[1024];
+
+void init_gs_vol_table(void)
+{
+ int i;
+
+ for (i = 0; i < 1024; i++)
+ gs_vol_table[i] = pow(2.0f,((double)i / 1023.0 - 1) * 8);
+}
+
+FLOAT_T *xg_vol_table = gs_vol_table;
+FLOAT_T *vol_table = def_vol_table;
+
+FLOAT_T bend_fine[256];
+FLOAT_T bend_coarse[128];
+
+void init_bend_fine(void)
+{
+ int i;
+
+ for (i = 0; i < 256; i++)
+ bend_fine[i] = pow(2.0, i / 12.0 / 256);
+}
+
+void init_bend_coarse(void)
+{
+ int i;
+
+ for (i = 0; i < 128; i++)
+ bend_coarse[i] = pow(2.0, i / 12.0);
+}
+
+/*
+ * midi_time_table(x + 16y) = midi_time_table(x) * (2^y)
+ * midi_time_table(64) = 1
+ * then,
+ * midi_time_table(x) := (2^(x/16))/16
+ */
+FLOAT_T midi_time_table[128] =
+{
+ 0.06250, 0.06527, 0.06816, 0.07117, 0.07433, 0.07762, 0.08105, 0.08464,
+ 0.08839, 0.09230, 0.09639, 0.10066, 0.10511, 0.10977, 0.11463, 0.11970,
+ 0.12500, 0.13053, 0.13631, 0.14235, 0.14865, 0.15523, 0.16210, 0.16928,
+ 0.17678, 0.18460, 0.19278, 0.20131, 0.21022, 0.21953, 0.22925, 0.23940,
+ 0.25000, 0.26107, 0.27263, 0.28470, 0.29730, 0.31046, 0.32421, 0.33856,
+ 0.35355, 0.36921, 0.38555, 0.40262, 0.42045, 0.43906, 0.45850, 0.47880,
+ 0.50000, 0.52214, 0.54525, 0.56939, 0.59460, 0.62093, 0.64842, 0.67713,
+ 0.70711, 0.73841, 0.77111, 0.80525, 0.84090, 0.87813, 0.91700, 0.95760,
+ 1.00000, 1.04427, 1.09051, 1.13879, 1.18921, 1.24186, 1.29684, 1.35426,
+ 1.41421, 1.47683, 1.54221, 1.61049, 1.68179, 1.75625, 1.83401, 1.91521,
+ 2.00000, 2.08855, 2.18102, 2.27758, 2.37841, 2.48372, 2.59368, 2.70851,
+ 2.82843, 2.95365, 3.08442, 3.22098, 3.36359, 3.51250, 3.66802, 3.83041,
+ 4.00000, 4.17710, 4.36203, 4.55515, 4.75683, 4.96743, 5.18736, 5.41702,
+ 5.65685, 5.90730, 6.16884, 6.44196, 6.72717, 7.02501, 7.33603, 7.66083,
+ 8.00000, 8.35419, 8.72406, 9.11031, 9.51366, 9.93486,10.37472,10.83404,
+ 11.31371,11.81461,12.33769,12.88392,13.45434,14.05002,14.67206,15.32165
+};
+/*
+ * midi_time_table2(x) := 2^(x/16/128) (for lsb tunning)
+ */
+FLOAT_T midi_time_table2[128] =
+{
+ 1.00000, 1.00034, 1.00068, 1.00102, 1.00135, 1.00169, 1.00203, 1.00237,
+ 1.00271, 1.00305, 1.00339, 1.00373, 1.00407, 1.00441, 1.00475, 1.00509,
+ 1.00543, 1.00577, 1.00611, 1.00645, 1.00679, 1.00713, 1.00747, 1.00781,
+ 1.00816, 1.00850, 1.00884, 1.00918, 1.00952, 1.00986, 1.01021, 1.01055,
+ 1.01089, 1.01123, 1.01157, 1.01192, 1.01226, 1.01260, 1.01294, 1.01329,
+ 1.01363, 1.01397, 1.01432, 1.01466, 1.01500, 1.01535, 1.01569, 1.01603,
+ 1.01638, 1.01672, 1.01707, 1.01741, 1.01776, 1.01810, 1.01844, 1.01879,
+ 1.01913, 1.01948, 1.01982, 1.02017, 1.02051, 1.02086, 1.02121, 1.02155,
+ 1.02190, 1.02224, 1.02259, 1.02294, 1.02328, 1.02363, 1.02397, 1.02432,
+ 1.02467, 1.02501, 1.02536, 1.02571, 1.02606, 1.02640, 1.02675, 1.02710,
+ 1.02745, 1.02779, 1.02814, 1.02849, 1.02884, 1.02919, 1.02953, 1.02988,
+ 1.03023, 1.03058, 1.03093, 1.03128, 1.03163, 1.03198, 1.03233, 1.03268,
+ 1.03302, 1.03337, 1.03372, 1.03407, 1.03442, 1.03477, 1.03512, 1.03548,
+ 1.03583, 1.03618, 1.03653, 1.03688, 1.03723, 1.03758, 1.03793, 1.03828,
+ 1.03863, 1.03899, 1.03934, 1.03969, 1.04004, 1.04039, 1.04075, 1.04110,
+ 1.04145, 1.04180, 1.04216, 1.04251, 1.04286, 1.04321, 1.04357, 1.04392
+};
+
+#ifdef LOOKUP_SINE
+static FLOAT_T sine_table[257]=
+{
+ 0.0, 0.0061358846491544753, 0.012271538285719925, 0.01840672990580482,
+ 0.024541228522912288, 0.030674803176636626, 0.036807222941358832, 0.04293825693494082,
+ 0.049067674327418015, 0.055195244349689934, 0.061320736302208578, 0.067443919563664051,
+ 0.073564563599667426, 0.079682437971430126, 0.085797312344439894, 0.091908956497132724,
+ 0.098017140329560604, 0.10412163387205459, 0.11022220729388306, 0.11631863091190475,
+ 0.1224106751992162, 0.12849811079379317, 0.13458070850712617, 0.14065823933284921,
+ 0.14673047445536175, 0.15279718525844344, 0.15885814333386145, 0.16491312048996989,
+ 0.17096188876030122, 0.17700422041214875, 0.18303988795514095, 0.18906866414980619,
+ 0.19509032201612825, 0.2011046348420919, 0.20711137619221856, 0.21311031991609136,
+ 0.2191012401568698, 0.22508391135979283, 0.23105810828067111, 0.2370236059943672,
+ 0.24298017990326387, 0.24892760574572015, 0.25486565960451457, 0.26079411791527551,
+ 0.26671275747489837, 0.27262135544994898, 0.27851968938505306, 0.28440753721127188,
+ 0.29028467725446233, 0.29615088824362379, 0.30200594931922808, 0.30784964004153487,
+ 0.31368174039889152, 0.31950203081601569, 0.32531029216226293, 0.33110630575987643,
+ 0.33688985339222005, 0.34266071731199438, 0.34841868024943456, 0.35416352542049034,
+ 0.35989503653498811, 0.36561299780477385, 0.37131719395183754, 0.37700741021641826,
+ 0.38268343236508978, 0.38834504669882625, 0.3939920400610481, 0.39962419984564679,
+ 0.40524131400498986, 0.41084317105790391, 0.41642956009763715, 0.42200027079979968,
+ 0.42755509343028208, 0.43309381885315196, 0.43861623853852766, 0.4441221445704292,
+ 0.44961132965460654, 0.45508358712634384, 0.46053871095824001, 0.46597649576796618,
+ 0.47139673682599764, 0.47679923006332209, 0.48218377207912272, 0.487550160148436,
+ 0.49289819222978404, 0.49822766697278187, 0.50353838372571758, 0.50883014254310699,
+ 0.51410274419322166, 0.51935599016558964, 0.52458968267846895, 0.52980362468629461,
+ 0.53499761988709715, 0.54017147272989285, 0.54532498842204646, 0.55045797293660481,
+ 0.55557023301960218, 0.56066157619733603, 0.56573181078361312, 0.57078074588696726,
+ 0.57580819141784534, 0.58081395809576453, 0.58579785745643886, 0.59075970185887416,
+ 0.59569930449243336, 0.60061647938386897, 0.60551104140432555, 0.61038280627630948,
+ 0.61523159058062682, 0.6200572117632891, 0.62485948814238634, 0.62963823891492698,
+ 0.63439328416364549, 0.63912444486377573, 0.64383154288979139, 0.64851440102211244,
+ 0.65317284295377676, 0.65780669329707864, 0.66241577759017178, 0.66699992230363747,
+ 0.67155895484701833, 0.67609270357531592, 0.68060099779545302, 0.68508366777270036,
+ 0.68954054473706683, 0.693971460889654, 0.69837624940897292, 0.7027547444572253,
+ 0.70710678118654746, 0.71143219574521643, 0.71573082528381859, 0.72000250796138165,
+ 0.72424708295146689, 0.7284643904482252, 0.73265427167241282, 0.73681656887736979,
+ 0.74095112535495911, 0.74505778544146595, 0.74913639452345926, 0.75318679904361241,
+ 0.75720884650648446, 0.76120238548426178, 0.76516726562245896, 0.76910333764557959,
+ 0.77301045336273699, 0.77688846567323244, 0.78073722857209438, 0.78455659715557524,
+ 0.78834642762660623, 0.79210657730021239, 0.79583690460888346, 0.79953726910790501,
+ 0.80320753148064483, 0.80684755354379922, 0.81045719825259477, 0.8140363297059483,
+ 0.81758481315158371, 0.82110251499110465, 0.82458930278502529, 0.8280450452577558,
+ 0.83146961230254524, 0.83486287498638001, 0.83822470555483797, 0.84155497743689833,
+ 0.84485356524970701, 0.84812034480329712, 0.8513551931052652, 0.85455798836540053,
+ 0.85772861000027212, 0.86086693863776731, 0.8639728561215867, 0.86704624551569265,
+ 0.87008699110871135, 0.87309497841829009, 0.8760700941954066, 0.87901222642863341,
+ 0.88192126434835494, 0.88479709843093779, 0.88763962040285393, 0.89044872324475788,
+ 0.89322430119551532, 0.89596624975618511, 0.89867446569395382, 0.90134884704602203,
+ 0.90398929312344334, 0.90659570451491533, 0.90916798309052227, 0.91170603200542988,
+ 0.91420975570353069, 0.9166790599210427, 0.91911385169005777, 0.9215140393420419,
+ 0.92387953251128674, 0.92621024213831127, 0.92850608047321548, 0.93076696107898371,
+ 0.93299279883473885, 0.9351835099389475, 0.93733901191257496, 0.93945922360218992,
+ 0.94154406518302081, 0.94359345816196039, 0.94560732538052128, 0.94758559101774109,
+ 0.94952818059303667, 0.95143502096900834, 0.95330604035419375, 0.95514116830577067,
+ 0.95694033573220894, 0.9587034748958716, 0.96043051941556579, 0.96212140426904158,
+ 0.96377606579543984, 0.9653944416976894, 0.96697647104485207, 0.96852209427441727,
+ 0.97003125319454397, 0.97150389098625178, 0.97293995220556007, 0.97433938278557586,
+ 0.97570213003852857, 0.97702814265775439, 0.97831737071962765, 0.97956976568544052,
+ 0.98078528040323043, 0.98196386910955524, 0.98310548743121629, 0.98421009238692903,
+ 0.98527764238894122, 0.98630809724459867, 0.98730141815785843, 0.98825756773074946,
+ 0.98917650996478101, 0.99005821026229712, 0.99090263542778001, 0.99170975366909953,
+ 0.99247953459870997, 0.9932119492347945, 0.99390697000235606, 0.99456457073425542,
+ 0.99518472667219682, 0.99576741446765982, 0.996312612182778, 0.99682029929116567,
+ 0.99729045667869021, 0.99772306664419164, 0.99811811290014918, 0.99847558057329477,
+ 0.99879545620517241, 0.99907772775264536, 0.99932238458834954, 0.99952941750109314,
+ 0.99969881869620425, 0.9998305817958234, 0.9999247018391445, 0.99998117528260111,
+ 1.0
+};
+
+/*
+ looks up sin(2 * Pi * x / 1024)
+*/
+FLOAT_T lookup_sine(int x)
+{
+ int xx = x & 0xFF;
+ switch ((x>>8) & 0x03)
+ {
+ default: /* just to shut gcc up. */
+ case 0:
+ return sine_table[xx];
+ case 1:
+ return sine_table[0x100 - xx];
+ case 2:
+ return -sine_table[xx];
+ case 3:
+ return -sine_table[0x100 - xx];
+ }
+}
+#endif /* LOOKUP_SINE */
+
+static FLOAT_T triangular_table[257];
+
+void init_triangular_table(void)
+{
+ int i;
+ unsigned long init[4]={0x123, 0x234, 0x345, 0x456}, length=4;
+ init_by_array(init, length);
+
+ for (i = 0; i < 257; i++) {
+ triangular_table[i] = (double)(i/* - (genrand_int32() % 1)*/) / 256.0;
+ if(triangular_table[i] < 0) {triangular_table[i] = 0;}
+ else if(triangular_table[i] > 1.0) {triangular_table[i] = 1.0;}
+ }
+ triangular_table[0] = 0.0;
+ triangular_table[256] = 1.0;
+}
+
+FLOAT_T lookup_triangular(int x)
+{
+ int xx = x & 0xFF;
+ switch ((x>>8) & 0x03)
+ {
+ default:
+ case 0:
+ return triangular_table[xx];
+ case 1:
+ return triangular_table[0x100 - xx];
+ case 2:
+ return -triangular_table[xx];
+ case 3:
+ return -triangular_table[0x100 - xx];
+ }
+}
+
+#ifdef LOOKUP_HACK
+int16 _u2l[] =
+{
+ -32256, -31228, -30200, -29172, -28143, -27115, -26087, -25059,
+ -24031, -23002, -21974, -20946, -19918, -18889, -17861, -16833,
+ -16062, -15548, -15033, -14519, -14005, -13491, -12977, -12463,
+ -11949, -11435, -10920, -10406, -9892, -9378, -8864, -8350,
+ -7964, -7707, -7450, -7193, -6936, -6679, -6422, -6165,
+ -5908, -5651, -5394, -5137, -4880, -4623, -4365, -4108,
+ -3916, -3787, -3659, -3530, -3402, -3273, -3144, -3016,
+ -2887, -2759, -2630, -2502, -2373, -2245, -2116, -1988,
+ -1891, -1827, -1763, -1698, -1634, -1570, -1506, -1441,
+ -1377, -1313, -1249, -1184, -1120, -1056, -992, -927,
+ -879, -847, -815, -783, -751, -718, -686, -654,
+ -622, -590, -558, -526, -494, -461, -429, -397,
+ -373, -357, -341, -325, -309, -293, -277, -261,
+ -245, -228, -212, -196, -180, -164, -148, -132,
+ -120, -112, -104, -96, -88, -80, -72, -64,
+ -56, -48, -40, -32, -24, -16, -8, 0,
+ 32256, 31228, 30200, 29172, 28143, 27115, 26087, 25059,
+ 24031, 23002, 21974, 20946, 19918, 18889, 17861, 16833,
+ 16062, 15548, 15033, 14519, 14005, 13491, 12977, 12463,
+ 11949, 11435, 10920, 10406, 9892, 9378, 8864, 8350,
+ 7964, 7707, 7450, 7193, 6936, 6679, 6422, 6165,
+ 5908, 5651, 5394, 5137, 4880, 4623, 4365, 4108,
+ 3916, 3787, 3659, 3530, 3402, 3273, 3144, 3016,
+ 2887, 2759, 2630, 2502, 2373, 2245, 2116, 1988,
+ 1891, 1827, 1763, 1698, 1634, 1570, 1506, 1441,
+ 1377, 1313, 1249, 1184, 1120, 1056, 992, 927,
+ 879, 847, 815, 783, 751, 718, 686, 654,
+ 622, 590, 558, 526, 494, 461, 429, 397,
+ 373, 357, 341, 325, 309, 293, 277, 261,
+ 245, 228, 212, 196, 180, 164, 148, 132,
+ 120, 112, 104, 96, 88, 80, 72, 64,
+ 56, 48, 40, 32, 24, 16, 8, 0
+};
+
+int32 *mixup;
+#ifdef LOOKUP_INTERPOLATION
+int8 *iplookup;
+#endif
+
+#endif
+
+void init_tables(void)
+{
+#ifdef LOOKUP_HACK
+ int i,j,v;
+ mixup = (int32 *)safe_malloc(1<<(7+8+2)); /* Give your cache a workout! */
+
+ for (i=0; i<128; i++)
+ {
+ v=_u2l[255-i];
+ for (j=-128; j<128; j++)
+ {
+ mixup[ ((i & 0x7F)<<8) | (j & 0xFF)] =
+ (v * j) << MIXUP_SHIFT;
+ }
+ }
+
+#ifdef LOOKUP_INTERPOLATION
+ iplookup = (int8 *)safe_malloc(1<<(9+5));
+ for (i=-256; i<256; i++)
+ for(j=0; j<32; j++)
+ iplookup[((i<<5) & 0x3FE0) | j] = (i * j)>>5;
+ /* I don't know. Quantum bits? Magick? */
+#endif
+
+#endif
+ init_triangular_table();
+}
+
+#ifdef LOOKUP_HACK
+uint8 _l2u_[] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
+ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
+ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
+ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
+ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
+ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
+ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
+ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
+ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E,
+ 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E,
+ 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E,
+ 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E,
+ 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E,
+ 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E,
+ 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E,
+ 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E,
+ 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+ 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
+ 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
+ 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
+ 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A,
+ 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B,
+ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B,
+ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B,
+ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B,
+ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+ 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+ 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+ 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+ 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1D, 0x1D, 0x1D, 0x1D,
+ 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D,
+ 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D,
+ 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D,
+ 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1E, 0x1E, 0x1E, 0x1E,
+ 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
+ 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
+ 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
+ 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F,
+ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
+ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
+ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
+ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
+ 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25,
+ 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27,
+ 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2A, 0x2A,
+ 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
+ 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2B, 0x2B,
+ 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B,
+ 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2D, 0x2D,
+ 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D,
+ 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2E, 0x2E,
+ 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+ 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2F, 0x2F,
+ 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F,
+ 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34,
+ 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35,
+ 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37,
+ 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3A,
+ 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3B,
+ 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3B, 0x3C,
+ 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3D,
+ 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D,
+ 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43,
+ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
+ 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47,
+ 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x4A, 0x4A, 0x4A, 0x4A, 0x4A, 0x4A, 0x4A, 0x4A, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
+ 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D,
+ 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F,
+ 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x53,
+ 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 0x57,
+ 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x5A, 0x5A, 0x5A, 0x5A, 0x5B, 0x5B, 0x5B, 0x5B,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5D, 0x5D, 0x5D, 0x5D, 0x5E, 0x5E, 0x5E, 0x5E, 0x5F, 0x5F, 0x5F, 0x5F,
+ 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67,
+ 0x68, 0x68, 0x68, 0x69, 0x69, 0x6A, 0x6A, 0x6B, 0x6B, 0x6C, 0x6C, 0x6D, 0x6D, 0x6E, 0x6E, 0x6F,
+ 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E,
+ 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2, 0xF1, 0xF0,
+ 0xEF, 0xEF, 0xEE, 0xEE, 0xED, 0xED, 0xEC, 0xEC, 0xEB, 0xEB, 0xEA, 0xEA, 0xE9, 0xE9, 0xE8, 0xE8,
+ 0xE7, 0xE7, 0xE6, 0xE6, 0xE5, 0xE5, 0xE4, 0xE4, 0xE3, 0xE3, 0xE2, 0xE2, 0xE1, 0xE1, 0xE0, 0xE0,
+ 0xDF, 0xDF, 0xDF, 0xDF, 0xDE, 0xDE, 0xDE, 0xDE, 0xDD, 0xDD, 0xDD, 0xDD, 0xDC, 0xDC, 0xDC, 0xDC,
+ 0xDB, 0xDB, 0xDB, 0xDB, 0xDA, 0xDA, 0xDA, 0xDA, 0xD9, 0xD9, 0xD9, 0xD9, 0xD8, 0xD8, 0xD8, 0xD8,
+ 0xD7, 0xD7, 0xD7, 0xD7, 0xD6, 0xD6, 0xD6, 0xD6, 0xD5, 0xD5, 0xD5, 0xD5, 0xD4, 0xD4, 0xD4, 0xD4,
+ 0xD3, 0xD3, 0xD3, 0xD3, 0xD2, 0xD2, 0xD2, 0xD2, 0xD1, 0xD1, 0xD1, 0xD1, 0xD0, 0xD0, 0xD0, 0xD0,
+ 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE,
+ 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
+ 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA,
+ 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+ 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
+ 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4,
+ 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2,
+ 0xC2, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
+ 0xC0, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF,
+ 0xBF, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE,
+ 0xBE, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD,
+ 0xBD, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC,
+ 0xBC, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
+ 0xBB, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA,
+ 0xBA, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9,
+ 0xB9, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8,
+ 0xB8, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7,
+ 0xB7, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6,
+ 0xB6, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5,
+ 0xB5, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4,
+ 0xB4, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3,
+ 0xB3, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2,
+ 0xB2, 0xB2, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1,
+ 0xB1, 0xB1, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0,
+ 0xB0, 0xB0, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF,
+ 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF,
+ 0xAF, 0xAF, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+ 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE,
+ 0xAE, 0xAE, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+ 0xAD, 0xAD, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC,
+ 0xAC, 0xAC, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
+ 0xAB, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xAA, 0xAA, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9,
+ 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9,
+ 0xA9, 0xA9, 0xA9, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+ 0xA8, 0xA8, 0xA8, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+ 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+ 0xA7, 0xA7, 0xA7, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+ 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+ 0xA6, 0xA6, 0xA6, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+ 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
+ 0xA5, 0xA5, 0xA5, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+ 0xA4, 0xA4, 0xA4, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+ 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3,
+ 0xA3, 0xA3, 0xA3, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2,
+ 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2,
+ 0xA2, 0xA2, 0xA2, 0xA2, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
+ 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0,
+ 0xA0, 0xA0, 0xA0, 0xA0, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+ 0x9F, 0x9F, 0x9F, 0x9F, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E,
+ 0x9E, 0x9E, 0x9E, 0x9E, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D,
+ 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+ 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B,
+ 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A,
+ 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,
+ 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F,
+ 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+ 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D,
+ 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C,
+ 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B,
+ 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A,
+ 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,
+ 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+ 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,
+ 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
+ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
+};
+uint8 *_l2u = _l2u_ + 4096;
+#endif /* LOOKUP_HACK */
+
+uint8 reverb_macro_presets[] =
+{ /* CHARACTER,PRE-LPF,LEVEL,TIME,DELAY FEEDBACK,PREDELAY TIME */
+ 0,3,64,80,0,0, /* 00: Room1 */
+ 1,4,64,56,0,0, /* 01: Room2 */
+ 2,0,64,64,0,0, /* 02: Room3 */
+ 3,4,64,72,0,0, /* 03: Hall1 */
+ 4,0,64,64,0,0, /* 04: Hall2 */
+ 5,0,64,88,0,0, /* 05: Plate */
+ 6,0,64,32,40,0, /* 06: Delay */
+ 7,0,64,64,32,0, /* 07: Panning Delay */
+};
+
+uint8 chorus_macro_presets[] =
+{ /* PRE-LPF,LEVEL,FEEDBACK,DELAY,RATE,DEPTH,SEND TO REVERB,SEND TO DELAY */
+ 0,64,0,112,3,5,0,0, /* 00: Chorus1 */
+ 0,64,5,80,9,19,0,0, /* 01: Chorus2 */
+ 0,64,8,80,3,19,0,0, /* 02: Chorus3 */
+ 0,64,16,64,9,16,0,0, /* 03: Chorus4 */
+ 0,64,64,127,2,24,0,0, /* 04: Feedback Chorus */
+ 0,64,112,127,1,5,0,0, /* 05: Flanger */
+ 0,64,0,127,0,127,0,0, /* 06: Short Delay */
+ 0,64,80,127,0,127,0,0, /* 07: Short Delay(Feedback) */
+};
+
+uint8 delay_macro_presets[] =
+{ /* PRE-LPF,TIME(C),RATIO(L),RATIO(R),LEVEL(C),LEVEL(L),LEVEL(R),LEVEL,FEEDBACK,LEVEL TO REVERB */
+ 0,97,1,1,127,0,0,64,79,0, /* 00: Delay1 */
+ 0,106,1,1,127,0,0,64,79,0, /* 01: Delay2 */
+ 0,115,1,1,127,0,0,64,63,0, /* 02: Delay3 */
+ 0,83,1,1,127,0,0,64,71,0, /* 03: Delay4 */
+ 0,90,12,24,0,125,60,64,73,0, /* 04: Pan Delay1 */
+ 0,109,12,24,0,125,60,64,70,0, /* 05: Pan Delay2 */
+ 0,115,12,24,0,120,64,64,72,0, /* 06: Pan Delay3 */
+ 0,93,12,24,0,120,64,64,63,0, /* 07: Pan Delay4 */
+ 0,109,12,24,0,114,60,64,60,36, /* 08: Delay to Reverb */
+ 0,110,21,31,97,127,67,64,39,0, /* 09: Pan Repeat */
+};
+
+float delay_time_center_table[] =
+{ /* 0x00~0x73, 0.1ms~1000ms */
+ 0.1, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9,
+ 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.2, 4.4, 4.6, 4.8,
+ 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
+ 50, 55, 60, 65, 70, 75, 80, 85, 90, 95,
+ 100, 110, 120, 130, 140, 150, 160, 170, 180, 190,
+ 200, 220, 240, 260, 280, 300, 320, 340, 360, 380, 400, 420, 440, 460, 480,
+ 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000,
+};
+
+float pre_delay_time_table[] =
+{
+ 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9,
+ 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9,
+ 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9,
+ 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88,
+ 90, 92, 94, 96, 98, 100, 100, 100,
+};
+
+float chorus_delay_time_table[] =
+{
+ 0.000000, 0.078740, 0.157480, 0.236220, 0.314961, 0.393701, 0.472441, 0.551181,
+ 0.629921, 0.708661, 0.787402, 0.866142, 0.944882, 1.023622, 1.102362, 1.181102,
+ 1.259843, 1.338583, 1.417323, 1.496063, 1.574803, 1.653543, 1.732283, 1.811024,
+ 1.889764, 1.968504, 2.047244, 2.125984, 2.204724, 2.283465, 2.362205, 2.440945,
+ 2.519685, 2.598425, 2.677165, 2.755906, 2.834646, 2.913386, 2.992126, 3.070866,
+ 3.149606, 3.228346, 3.307087, 3.385827, 3.464567, 3.543307, 3.622047, 3.700787,
+ 3.779528, 3.858268, 3.937008, 4.015748, 4.094488, 4.173228, 4.251969, 4.330709,
+ 4.409449, 4.488189, 4.566929, 4.645669, 4.724409, 4.803150, 4.881890, 4.960630,
+ 5.039370, 5.118110, 5.196850, 5.275591, 5.354331, 5.433071, 5.511811, 5.590551,
+ 5.669291, 5.748031, 5.826772, 5.905512, 5.984252, 6.062992, 6.141732, 6.220472,
+ 6.299213, 6.377953, 6.456693, 6.535433, 6.614173, 6.692913, 6.771654, 6.850394,
+ 6.929134, 7.007874, 7.086614, 7.165354, 7.244094, 7.322835, 7.401575, 7.480315,
+ 7.559055, 7.637795, 7.716535, 7.795276, 10.000000, 10.555556, 11.111111, 11.666667,
+ 12.222222, 12.777778, 13.333333, 13.888889, 14.444444, 15.000000, 15.555556, 16.111111,
+ 16.666667, 17.222222, 17.777778, 18.333333, 18.888889, 19.444444, 20.000000, 20.555556,
+ 21.111111, 21.666667, 22.222222, 22.777778, 23.333333, 23.888889, 24.444444, 25.000000,
+};
+
+float rate1_table[] =
+{
+ 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80,
+ 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15, 1.20, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50, 1.55, 1.60,
+ 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00, 2.05, 2.10, 2.15, 2.20, 2.25, 2.30, 2.35, 2.40,
+ 2.45, 2.50, 2.55, 2.60, 2.65, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20,
+ 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00,
+ 4.05, 4.10, 4.15, 4.20, 4.25, 4.30, 4.35, 4.40, 4.45, 4.50, 4.55, 4.60, 4.65, 4.70, 4.75, 4.80,
+ 4.85, 4.90, 4.95, 5.00, 5.10, 5.20, 5.30, 5.40, 5.50, 5.60, 5.70, 5.80, 5.90, 6.00, 6.10, 6.20,
+ 6.30, 6.40, 6.50, 6.60, 6.70, 6.80, 6.90, 7.00, 7.50, 8.00, 8.50, 9.00, 9.50, 10.00, 10.00, 10.00,
+};
+
+/* Derivation of Perceived Volume Curve Equation:
+ *
+ * Given: delta dB = 20 * log10(amplitude_new / amplitude_old)
+ * delta dB of 10 == perceived volume change of 2x
+ *
+ * 10 = 20 * log10(?)
+ * 0.5 = log10(?)
+ * 10^0.5 = ?
+ *
+ * therefore: 2x perceived volume == ~3.16x amplitude
+ * 4x perceived volume == 10x amplitude
+ *
+ * Volume Amplitude
+ * ------------ ---------------
+ * 1 1
+ * 0.25 0.1
+ * 0.0625 0.01
+ * 0.015625 0.001
+ * 0.00390625 0.0001
+ * 0.0009765625 0.00001
+ * 0 0
+ *
+ * Fit curve to table:
+ *
+ * amplification = pow(volume, 1.66096404744)
+ */
+FLOAT_T perceived_vol_table[128];
+
+void init_perceived_vol_table(void)
+{
+ int i;
+
+ for (i = 0; i < 128; i++)
+ perceived_vol_table[i] =
+ 127.0 * pow((double)i / 127.0, 1.66096404744);
+}
+
+FLOAT_T gm2_vol_table[128];
+
+void init_gm2_vol_table(void)
+{
+ int i;
+
+ for(i = 0; i < 128; i++)
+ gm2_vol_table[i] = (i * i) / 127.0;
+}
+
+FLOAT_T user_vol_table[128];
+
+void init_user_vol_table(FLOAT_T power)
+{
+ int i;
+
+ for (i = 0; i < 128; i++)
+ user_vol_table[i] = 127.0 * pow((double)i / 127.0, power);
+}
+
+/* measured value from SC-88STPro.
+ approximate expression: y = (-0.3768x6 + 0.9528x5 - 0.8253x4 + 0.2665x3 + 0.9892x2 - 0.0059x + 0.001) * 127 */
+FLOAT_T sc_vol_table[128] =
+{
+ 0.000000, 0.128905, 0.146482, 0.179815, 0.228982, 0.294049, 0.375078, 0.472120,
+ 0.585221, 0.714419, 0.859746, 1.021229, 1.198887, 1.392736, 1.602785, 1.829039,
+ 2.071501, 2.330166, 2.605028, 2.896078, 3.203301, 3.526682, 3.866202, 4.221841,
+ 4.593575, 4.981382, 5.385233, 5.805103, 6.240963, 6.692783, 7.160536, 7.644189,
+ 8.143714, 8.659080, 9.190256, 9.737215, 10.299925, 10.878360, 11.472491, 12.082292,
+ 12.707738, 13.348803, 14.005465, 14.677704, 15.365497, 16.068829, 16.787681, 17.522039,
+ 18.271890, 19.037223, 19.818029, 20.614301, 21.426034, 22.253225, 23.095873, 23.953980,
+ 24.827548, 25.716584, 26.621094, 27.541088, 28.476578, 29.427576, 30.394100, 31.376165,
+ 32.373791, 33.386998, 34.415810, 35.460249, 36.520342, 37.596115, 38.687597, 39.794815,
+ 40.917801, 42.056586, 43.211200, 44.381677, 45.568048, 46.770346, 47.988605, 49.222856,
+ 50.473131, 51.739464, 53.021883, 54.320420, 55.635102, 56.965957, 58.313010, 59.676284,
+ 61.055802, 62.451580, 63.863636, 65.291982, 66.736627, 68.197578, 69.674835, 71.168397,
+ 72.678255, 74.204399, 75.746811, 77.305466, 78.880337, 80.471388, 82.078576, 83.701851,
+ 85.341158, 86.996429, 88.667594, 90.354568, 92.057260, 93.775571, 95.509387, 97.258589,
+ 99.023042, 100.802603, 102.597116, 104.406412, 106.230309, 108.068613, 109.921115, 111.787592,
+ 113.667805, 115.561500, 117.468408, 119.388243, 121.320699, 123.265458, 125.222177, 127.000000,
+};
+
+/* measured value from SC-88STPro.
+ approximate expression: y = (-1.5374x6 + 4.4002x5 - 4.8309x4 + 2.572x3 + 0.1487x2 + 0.2412x + 0.0044) * 127 */
+FLOAT_T sc_vel_table[128] =
+{
+ 0.000000, 0.801328, 1.047122, 1.297056, 1.551953, 1.812583, 2.079668, 2.353885,
+ 2.635863, 2.926190, 3.225412, 3.534034, 3.852525, 4.181317, 4.520806, 4.871357,
+ 5.233303, 5.606946, 5.992560, 6.390392, 6.800666, 7.223577, 7.659301, 8.107993,
+ 8.569785, 9.044792, 9.533111, 10.034824, 10.549997, 11.078680, 11.620912, 12.176722,
+ 12.746124, 13.329126, 13.925725, 14.535911, 15.159666, 15.796968, 16.447787, 17.112090,
+ 17.789842, 18.481003, 19.185529, 19.903380, 20.634509, 21.378873, 22.136426, 22.907127,
+ 23.690932, 24.487801, 25.297696, 26.120582, 26.956425, 27.805197, 28.666872, 29.541428,
+ 30.428848, 31.329119, 32.242232, 33.168184, 34.106974, 35.058609, 36.023099, 37.000459,
+ 37.990710, 38.993877, 40.009989, 41.039080, 42.081190, 43.136361, 44.204639, 45.286075,
+ 46.380723, 47.488640, 48.609885, 49.744520, 50.892609, 52.054217, 53.229410, 54.418254,
+ 55.620816, 56.837160, 58.067351, 59.311450, 60.569517, 61.841607, 63.127770, 64.428053,
+ 65.742497, 67.071133, 68.413988, 69.771079, 71.142412, 72.527985, 73.927782, 75.341776,
+ 76.769925, 78.212172, 79.668444, 81.138652, 82.622687, 84.120420, 85.631702, 87.156361,
+ 88.694200, 90.244998, 91.808507, 93.384452, 94.972526, 96.572391, 98.183679, 99.805985,
+ 101.438869, 103.081852, 104.734417, 106.396007, 108.066018, 109.743805, 111.428675, 113.119886,
+ 114.816648, 116.518116, 118.223392, 119.931522, 121.641492, 123.352230, 125.062599, 127.000000,
+};
+
+FLOAT_T sc_pan_table[129] =
+{
+ 0.000000, 0.000000, 0.999479, 2.011744, 3.036530, 4.073569, 5.122593, 6.183332,
+ 7.255517, 8.338874, 9.433131, 10.538014, 11.653247, 12.778552, 13.913653, 15.058271,
+ 16.212123, 17.374930, 18.546409, 19.726275, 20.914243, 22.110027, 23.313339, 24.523890,
+ 25.741391, 26.965550, 28.196074, 29.432671, 30.675045, 31.922900, 33.175939, 34.433863,
+ 35.696373, 36.963168, 38.233946, 39.508403, 40.786235, 42.067137, 43.350800, 44.636918,
+ 45.925181, 47.215278, 48.506897, 49.799726, 51.093451, 52.387755, 53.682323, 54.976837,
+ 56.270977, 57.564424, 58.856855, 60.147950, 61.437382, 62.724829, 64.009963, 65.292456,
+ 66.571981, 67.848208, 69.120804, 70.389439, 71.653778, 72.913487, 74.168230, 75.417670,
+ 76.661468, 77.899286, 79.130781, 80.355614, 81.573439, 82.783913, 83.986691, 85.181425,
+ 86.367767, 87.545369, 88.713880, 89.872949, 91.022222, 92.161346, 93.289965, 94.407723,
+ 95.514263, 96.609225, 97.692249, 98.762975, 99.821039, 100.866079, 101.897729, 102.915623,
+ 103.919394, 104.908673, 105.883091, 106.842276, 107.785858, 108.713461, 109.624713, 110.519236,
+ 111.396655, 112.256590, 113.098663, 113.922493, 114.727699, 115.513896, 116.280702, 117.027730,
+ 117.754595, 118.460908, 119.146280, 119.810321, 120.452639, 121.072843, 121.670538, 122.245328,
+ 122.796819, 123.324612, 123.828308, 124.307509, 124.761812, 125.190815, 125.594115, 125.971307,
+ 126.321986, 126.645744, 126.942172, 127.210862, 127.451402, 127.663381, 127.846385, 128.000000,
+ 128.000000,
+};
+
+FLOAT_T gm2_pan_table[129];
+FLOAT_T *pan_table = sc_pan_table;
+
+void init_gm2_pan_table(void)
+{
+ int i;
+
+ gm2_pan_table[0] = 0;
+ for(i = 0; i < 127; i++)
+ gm2_pan_table[i + 1] = sin(M_PI / 2 * i / 126) * 128;
+ /* lookup_sine(i * SINE_CYCLE_LENGTH / 4 / 126) */
+ gm2_pan_table[128] = 128.0;
+}
+
+FLOAT_T sc_drum_level_table[128] =
+{
+ 0.007874, 0.007874, 0.031496, 0.070866, 0.125984, 0.196850, 0.283465, 0.385827,
+ 0.503937, 0.637795, 0.787402, 0.952756, 1.133858, 1.330709, 1.543307, 1.771654,
+ 2.015748, 2.275591, 2.551181, 2.842520, 3.149606, 3.472441, 3.811024, 4.165354,
+ 4.535433, 4.921260, 5.322835, 5.740157, 6.173228, 6.622047, 7.086614, 7.566929,
+ 8.062992, 8.574803, 9.102362, 9.645669, 10.204724, 10.779528, 11.370079, 11.976378,
+ 12.598425, 13.236220, 13.889764, 14.559055, 15.244094, 15.944882, 16.661417, 17.393701,
+ 18.141732, 18.905512, 19.685039, 20.480315, 21.291339, 22.118110, 22.960630, 23.818898,
+ 24.692913, 25.582677, 26.488189, 27.409449, 28.346457, 29.299213, 30.267717, 31.251969,
+ 32.251969, 33.267717, 34.299213, 35.346457, 36.409449, 37.488189, 38.582677, 39.692913,
+ 40.818898, 41.960630, 43.118110, 44.291339, 45.480315, 46.685039, 47.905512, 49.141732,
+ 50.393701, 51.661417, 52.944882, 54.244094, 55.559055, 56.889764, 58.236220, 59.598425,
+ 60.976378, 62.370079, 63.779528, 65.204724, 66.645669, 68.102362, 69.574803, 71.062992,
+ 72.566929, 74.086614, 75.622047, 77.173228, 78.740157, 80.322835, 81.921260, 83.535433,
+ 85.165354, 86.811024, 88.472441, 90.149606, 91.842520, 93.551181, 95.275591, 97.015748,
+ 98.771654, 100.543307, 102.330709, 104.133858, 105.952756, 107.787402, 109.637795, 111.503937,
+ 113.385827, 115.283465, 117.196850, 119.125984, 121.070866, 123.031496, 125.007874, 127.000000,
+};
+
+FLOAT_T attack_vol_table[1024];
+
+void init_attack_vol_table(void)
+{
+ int i;
+
+ for (i = 0; i < 1024; i++)
+ attack_vol_table[i] = i / 1023.0;
+}
+
+float sc_eg_decay_table[128] =
+{
+ 81.841218, 81.841218, 81.841218, 81.841218, 81.841218, 81.841218, 81.841218, 81.841218,
+ 81.841218, 81.841218, 81.841218, 81.841218, 81.841218, 81.841218, 81.841218, 81.841218,
+ 81.841218, 81.841218, 81.841218, 81.841218, 81.841218, 81.841218, 74.928977, 68.580327,
+ 62.750584, 57.398521, 52.486110, 47.978275, 43.842671, 40.049475, 36.571192, 33.382477,
+ 30.459975, 27.782161, 25.329207, 23.082845, 21.026252, 19.143935, 17.421629, 15.846202,
+ 14.405568, 13.088604, 11.885077, 10.785570, 9.781425, 8.864676, 8.028000, 7.264663,
+ 6.568475, 5.933745, 5.355241, 4.828153, 4.348058, 3.910885, 3.512890, 3.150618,
+ 2.820877, 2.520709, 2.247348, 1.998183, 1.770681, 1.562261, 1.369978, 1.189386,
+ 1.000000, 0.838459, 0.726301, 0.635581, 0.559656, 0.494986, 0.439286, 0.390934,
+ 0.348712, 0.311669, 0.279045, 0.250221, 0.224684, 0.202006, 0.181825, 0.163831,
+ 0.147761, 0.133387, 0.120513, 0.108967, 0.098600, 0.089282, 0.080897, 0.073346,
+ 0.066540, 0.060399, 0.054854, 0.049845, 0.045315, 0.041217, 0.037506, 0.034144,
+ 0.031097, 0.028333, 0.025826, 0.023549, 0.021480, 0.019601, 0.017892, 0.016337,
+ 0.014923, 0.013635, 0.012462, 0.011394, 0.011394, 0.011394, 0.011394, 0.011394,
+ 0.011394, 0.011394, 0.011394, 0.011394, 0.011394, 0.011394, 0.011394, 0.011394,
+ 0.011394, 0.011394, 0.011394, 0.011394, 0.011394, 0.011394, 0.011394, 0.011394,
+};
+
+float sc_eg_release_table[128] =
+{
+ 27.322002, 27.322002, 27.322002, 27.322002, 27.322002, 27.322002, 27.322002, 27.322002,
+ 27.322002, 27.322002, 27.322002, 27.322002, 27.322002, 27.322002, 27.322002, 27.322002,
+ 27.322002, 27.322002, 27.322002, 27.322002, 27.322002, 27.322002, 25.299110, 23.425992,
+ 21.691556, 20.085537, 18.598425, 17.221418, 15.946363, 14.765711, 13.672474, 12.660179,
+ 11.722833, 10.854887, 10.051203, 9.307023, 8.617941, 7.979878, 7.389056, 6.841978,
+ 6.335406, 5.866339, 5.432002, 5.029822, 4.657419, 4.312589, 3.993290, 3.697631,
+ 3.423862, 3.170363, 2.935633, 2.718282, 2.517023, 2.330665, 2.158106, 1.998322,
+ 1.850368, 1.713369, 1.586513, 1.469049, 1.360282, 1.259569, 1.166311, 1.079959,
+ 1.000000, 0.925961, 0.857404, 0.793923, 0.735141, 0.680712, 0.630313, 0.583645,
+ 0.540433, 0.500420, 0.463369, 0.429062, 0.397295, 0.367879, 0.340642, 0.315421,
+ 0.292068, 0.270443, 0.250420, 0.231879, 0.214711, 0.198814, 0.184094, 0.170464,
+ 0.157843, 0.146157, 0.135335, 0.125315, 0.116037, 0.107446, 0.099491, 0.092124,
+ 0.085304, 0.078988, 0.073140, 0.067724, 0.062710, 0.058067, 0.053768, 0.049787,
+ 0.046101, 0.042688, 0.039527, 0.036601, 0.036601, 0.036601, 0.036601, 0.036601,
+ 0.036601, 0.036601, 0.036601, 0.036601, 0.036601, 0.036601, 0.036601, 0.036601,
+ 0.036601, 0.036601, 0.036601, 0.036601, 0.036601, 0.036601, 0.036601, 0.036601,
+};
+
+float sc_eg_attack_table[128] =
+{
+ 82.756924, 82.756924, 82.756924, 82.756924, 82.756924, 82.756924, 82.756924, 82.756924,
+ 82.756924, 82.756924, 82.756924, 82.756924, 82.756924, 82.756924, 82.756924, 82.756924,
+ 82.756924, 82.756924, 82.756924, 82.756924, 82.756924, 82.756924, 75.473398, 68.815182,
+ 62.729632, 57.168464, 52.087395, 47.445819, 43.206507, 39.335325, 35.800987, 32.574817,
+ 29.630534, 26.944060, 24.493331, 22.258137, 20.219967, 18.361866, 16.668311, 15.125088,
+ 13.719184, 12.438688, 11.272700, 10.211246, 9.245197, 8.366205, 7.566631, 6.839489,
+ 6.178391, 5.577493, 5.031451, 4.535378, 4.084805, 3.675641, 3.304143, 2.966879,
+ 2.660703, 2.382715, 2.130237, 1.900768, 1.691929, 1.501374, 1.326560, 1.163993,
+ 1.000000, 0.859112, 0.753830, 0.666057, 0.591041, 0.526103, 0.469431, 0.419689,
+ 0.375841, 0.337054, 0.302650, 0.272061, 0.244810, 0.220489, 0.198750, 0.179292,
+ 0.161854, 0.146210, 0.132159, 0.119529, 0.108164, 0.097931, 0.088710, 0.080394,
+ 0.072891, 0.066115, 0.059994, 0.054461, 0.049456, 0.044927, 0.040827, 0.037114,
+ 0.033749, 0.030699, 0.027932, 0.025422, 0.023145, 0.021077, 0.019199, 0.017492,
+ 0.015941, 0.014532, 0.013250, 0.012084, 0.012084, 0.012084, 0.012084, 0.012084,
+ 0.012084, 0.012084, 0.012084, 0.012084, 0.012084, 0.012084, 0.012084, 0.012084,
+ 0.012084, 0.012084, 0.012084, 0.012084, 0.012084, 0.012084, 0.012084, 0.012084,
+};
+
+FLOAT_T sb_vol_table[1024];
+
+void init_sb_vol_table(void)
+{
+ int i;
+
+ for (i = 0; i < 1024; i++)
+ sb_vol_table[i] = pow(10.0, (double)(1023 - i) * 960.0 / (1023.0 * -200.0));
+}
+
+FLOAT_T modenv_vol_table[1024];
+
+void init_modenv_vol_table(void)
+{
+ int i;
+ double x;
+
+ modenv_vol_table[0] = (float)0;
+ for (i = 1; i < 1023; i++) {
+ x = (1.0 - (-20.0 / 96.0 * log(((double)i * (double)i) / (1023.0 * 1023.0)) / log(10.0)));
+ if (x < 0) {x = 0;}
+ modenv_vol_table[i] = log(x + 1) / log(2);
+ }
+ modenv_vol_table[1023] = (float)1.0;
+}
+
+float cb_to_amp_table[961] =
+{
+ 1.000000, 0.995677, 0.991373, 0.987088, 0.982821, 0.978572, 0.974342, 0.970130,
+ 0.965936, 0.961761, 0.957603, 0.953464, 0.949342, 0.945238, 0.941152, 0.937084,
+ 0.933033, 0.929000, 0.924984, 0.920985, 0.917004, 0.913040, 0.909093, 0.905163,
+ 0.901250, 0.897355, 0.893475, 0.889613, 0.885768, 0.881939, 0.878126, 0.874330,
+ 0.870551, 0.866787, 0.863040, 0.859310, 0.855595, 0.851896, 0.848214, 0.844547,
+ 0.840896, 0.837261, 0.833642, 0.830038, 0.826450, 0.822878, 0.819321, 0.815779,
+ 0.812252, 0.808741, 0.805245, 0.801764, 0.798298, 0.794848, 0.791412, 0.787990,
+ 0.784584, 0.781192, 0.777816, 0.774453, 0.771105, 0.767772, 0.764453, 0.761149,
+ 0.757858, 0.754582, 0.751320, 0.748072, 0.744839, 0.741619, 0.738413, 0.735221,
+ 0.732043, 0.728878, 0.725728, 0.722590, 0.719467, 0.716357, 0.713260, 0.710177,
+ 0.707107, 0.704050, 0.701007, 0.697976, 0.694959, 0.691955, 0.688964, 0.685986,
+ 0.683020, 0.680068, 0.677128, 0.674201, 0.671286, 0.668384, 0.665495, 0.662618,
+ 0.659754, 0.656902, 0.654062, 0.651235, 0.648420, 0.645617, 0.642826, 0.640047,
+ 0.637280, 0.634525, 0.631783, 0.629051, 0.626332, 0.623625, 0.620929, 0.618245,
+ 0.615572, 0.612911, 0.610262, 0.607624, 0.604997, 0.602382, 0.599778, 0.597185,
+ 0.594604, 0.592033, 0.589474, 0.586926, 0.584389, 0.581862, 0.579347, 0.576843,
+ 0.574349, 0.571866, 0.569394, 0.566933, 0.564482, 0.562042, 0.559612, 0.557193,
+ 0.554785, 0.552387, 0.549999, 0.547621, 0.545254, 0.542897, 0.540550, 0.538213,
+ 0.535887, 0.533570, 0.531264, 0.528967, 0.526681, 0.524404, 0.522137, 0.519880,
+ 0.517632, 0.515395, 0.513167, 0.510949, 0.508740, 0.506541, 0.504351, 0.502171,
+ 0.500000, 0.497839, 0.495687, 0.493544, 0.491410, 0.489286, 0.487171, 0.485065,
+ 0.482968, 0.480880, 0.478802, 0.476732, 0.474671, 0.472619, 0.470576, 0.468542,
+ 0.466516, 0.464500, 0.462492, 0.460493, 0.458502, 0.456520, 0.454547, 0.452582,
+ 0.450625, 0.448677, 0.446738, 0.444807, 0.442884, 0.440969, 0.439063, 0.437165,
+ 0.435275, 0.433394, 0.431520, 0.429655, 0.427798, 0.425948, 0.424107, 0.422274,
+ 0.420448, 0.418631, 0.416821, 0.415019, 0.413225, 0.411439, 0.409660, 0.407889,
+ 0.406126, 0.404371, 0.402623, 0.400882, 0.399149, 0.397424, 0.395706, 0.393995,
+ 0.392292, 0.390596, 0.388908, 0.387227, 0.385553, 0.383886, 0.382227, 0.380574,
+ 0.378929, 0.377291, 0.375660, 0.374036, 0.372419, 0.370809, 0.369207, 0.367611,
+ 0.366021, 0.364439, 0.362864, 0.361295, 0.359733, 0.358178, 0.356630, 0.355088,
+ 0.353553, 0.352025, 0.350503, 0.348988, 0.347480, 0.345977, 0.344482, 0.342993,
+ 0.341510, 0.340034, 0.338564, 0.337100, 0.335643, 0.334192, 0.332748, 0.331309,
+ 0.329877, 0.328451, 0.327031, 0.325617, 0.324210, 0.322808, 0.321413, 0.320024,
+ 0.318640, 0.317263, 0.315891, 0.314526, 0.313166, 0.311812, 0.310464, 0.309122,
+ 0.307786, 0.306456, 0.305131, 0.303812, 0.302499, 0.301191, 0.299889, 0.298593,
+ 0.297302, 0.296017, 0.294737, 0.293463, 0.292194, 0.290931, 0.289674, 0.288421,
+ 0.287175, 0.285933, 0.284697, 0.283466, 0.282241, 0.281021, 0.279806, 0.278597,
+ 0.277392, 0.276193, 0.274999, 0.273811, 0.272627, 0.271448, 0.270275, 0.269107,
+ 0.267943, 0.266785, 0.265632, 0.264484, 0.263340, 0.262202, 0.261068, 0.259940,
+ 0.258816, 0.257697, 0.256583, 0.255474, 0.254370, 0.253270, 0.252175, 0.251085,
+ 0.250000, 0.248919, 0.247843, 0.246772, 0.245705, 0.244643, 0.243585, 0.242533,
+ 0.241484, 0.240440, 0.239401, 0.238366, 0.237336, 0.236310, 0.235288, 0.234271,
+ 0.233258, 0.232250, 0.231246, 0.230246, 0.229251, 0.228260, 0.227273, 0.226291,
+ 0.225313, 0.224339, 0.223369, 0.222403, 0.221442, 0.220485, 0.219532, 0.218583,
+ 0.217638, 0.216697, 0.215760, 0.214827, 0.213899, 0.212974, 0.212053, 0.211137,
+ 0.210224, 0.209315, 0.208411, 0.207510, 0.206613, 0.205719, 0.204830, 0.203945,
+ 0.203063, 0.202185, 0.201311, 0.200441, 0.199575, 0.198712, 0.197853, 0.196998,
+ 0.196146, 0.195298, 0.194454, 0.193613, 0.192776, 0.191943, 0.191113, 0.190287,
+ 0.189465, 0.188646, 0.187830, 0.187018, 0.186210, 0.185405, 0.184603, 0.183805,
+ 0.183011, 0.182220, 0.181432, 0.180648, 0.179867, 0.179089, 0.178315, 0.177544,
+ 0.176777, 0.176013, 0.175252, 0.174494, 0.173740, 0.172989, 0.172241, 0.171496,
+ 0.170755, 0.170017, 0.169282, 0.168550, 0.167822, 0.167096, 0.166374, 0.165655,
+ 0.164938, 0.164225, 0.163516, 0.162809, 0.162105, 0.161404, 0.160706, 0.160012,
+ 0.159320, 0.158631, 0.157946, 0.157263, 0.156583, 0.155906, 0.155232, 0.154561,
+ 0.153893, 0.153228, 0.152565, 0.151906, 0.151249, 0.150595, 0.149944, 0.149296,
+ 0.148651, 0.148008, 0.147368, 0.146731, 0.146097, 0.145466, 0.144837, 0.144211,
+ 0.143587, 0.142967, 0.142349, 0.141733, 0.141121, 0.140511, 0.139903, 0.139298,
+ 0.138696, 0.138097, 0.137500, 0.136905, 0.136313, 0.135724, 0.135138, 0.134553,
+ 0.133972, 0.133393, 0.132816, 0.132242, 0.131670, 0.131101, 0.130534, 0.129970,
+ 0.129408, 0.128849, 0.128292, 0.127737, 0.127185, 0.126635, 0.126088, 0.125543,
+ 0.125000, 0.124460, 0.123922, 0.123386, 0.122853, 0.122322, 0.121793, 0.121266,
+ 0.120742, 0.120220, 0.119700, 0.119183, 0.118668, 0.118155, 0.117644, 0.117135,
+ 0.116629, 0.116125, 0.115623, 0.115123, 0.114626, 0.114130, 0.113637, 0.113145,
+ 0.112656, 0.112169, 0.111684, 0.111202, 0.110721, 0.110242, 0.109766, 0.109291,
+ 0.108819, 0.108348, 0.107880, 0.107414, 0.106949, 0.106487, 0.106027, 0.105568,
+ 0.105112, 0.104658, 0.104205, 0.103755, 0.103306, 0.102860, 0.102415, 0.101972,
+ 0.101532, 0.101093, 0.100656, 0.100221, 0.099787, 0.099356, 0.098926, 0.098499,
+ 0.098073, 0.097649, 0.097227, 0.096807, 0.096388, 0.095972, 0.095557, 0.095144,
+ 0.094732, 0.094323, 0.093915, 0.093509, 0.093105, 0.092702, 0.092302, 0.091903,
+ 0.091505, 0.091110, 0.090716, 0.090324, 0.089933, 0.089545, 0.089158, 0.088772,
+ 0.088388, 0.088006, 0.087626, 0.087247, 0.086870, 0.086494, 0.086120, 0.085748,
+ 0.085378, 0.085008, 0.084641, 0.084275, 0.083911, 0.083548, 0.083187, 0.082827,
+ 0.082469, 0.082113, 0.081758, 0.081404, 0.081052, 0.080702, 0.080353, 0.080006,
+ 0.079660, 0.079316, 0.078973, 0.078631, 0.078292, 0.077953, 0.077616, 0.077281,
+ 0.076947, 0.076614, 0.076283, 0.075953, 0.075625, 0.075298, 0.074972, 0.074648,
+ 0.074325, 0.074004, 0.073684, 0.073366, 0.073049, 0.072733, 0.072418, 0.072105,
+ 0.071794, 0.071483, 0.071174, 0.070867, 0.070560, 0.070255, 0.069952, 0.069649,
+ 0.069348, 0.069048, 0.068750, 0.068453, 0.068157, 0.067862, 0.067569, 0.067277,
+ 0.066986, 0.066696, 0.066408, 0.066121, 0.065835, 0.065550, 0.065267, 0.064985,
+ 0.064704, 0.064424, 0.064146, 0.063869, 0.063592, 0.063318, 0.063044, 0.062771,
+ 0.062500, 0.062230, 0.061961, 0.061693, 0.061426, 0.061161, 0.060896, 0.060633,
+ 0.060371, 0.060110, 0.059850, 0.059591, 0.059334, 0.059077, 0.058822, 0.058568,
+ 0.058315, 0.058062, 0.057811, 0.057562, 0.057313, 0.057065, 0.056818, 0.056573,
+ 0.056328, 0.056085, 0.055842, 0.055601, 0.055360, 0.055121, 0.054883, 0.054646,
+ 0.054409, 0.054174, 0.053940, 0.053707, 0.053475, 0.053244, 0.053013, 0.052784,
+ 0.052556, 0.052329, 0.052103, 0.051877, 0.051653, 0.051430, 0.051208, 0.050986,
+ 0.050766, 0.050546, 0.050328, 0.050110, 0.049894, 0.049678, 0.049463, 0.049249,
+ 0.049037, 0.048825, 0.048613, 0.048403, 0.048194, 0.047986, 0.047778, 0.047572,
+ 0.047366, 0.047161, 0.046958, 0.046755, 0.046552, 0.046351, 0.046151, 0.045951,
+ 0.045753, 0.045555, 0.045358, 0.045162, 0.044967, 0.044772, 0.044579, 0.044386,
+ 0.044194, 0.044003, 0.043813, 0.043624, 0.043435, 0.043247, 0.043060, 0.042874,
+ 0.042689, 0.042504, 0.042320, 0.042138, 0.041955, 0.041774, 0.041593, 0.041414,
+ 0.041235, 0.041056, 0.040879, 0.040702, 0.040526, 0.040351, 0.040177, 0.040003,
+ 0.039830, 0.039658, 0.039486, 0.039316, 0.039146, 0.038977, 0.038808, 0.038640,
+ 0.038473, 0.038307, 0.038141, 0.037976, 0.037812, 0.037649, 0.037486, 0.037324,
+ 0.037163, 0.037002, 0.036842, 0.036683, 0.036524, 0.036366, 0.036209, 0.036053,
+ 0.035897, 0.035742, 0.035587, 0.035433, 0.035280, 0.035128, 0.034976, 0.034825,
+ 0.034674, 0.034524, 0.034375, 0.034226, 0.034078, 0.033931, 0.033784, 0.033638,
+ 0.033493, 0.033348, 0.033204, 0.033060, 0.032918, 0.032775, 0.032634, 0.032492,
+ 0.032352, 0.032212, 0.032073, 0.031934, 0.031796, 0.031659, 0.031522, 0.031386,
+ 0.031250, 0.031115, 0.030980, 0.030846, 0.030713, 0.030580, 0.030448, 0.030317,
+ 0.030186, 0.030055, 0.029925, 0.029796, 0.029667, 0.029539, 0.029411, 0.029284,
+ 0.029157, 0.029031, 0.028906, 0.028781, 0.028656, 0.028533, 0.028409, 0.028286,
+ 0.028164, 0.028042, 0.027921, 0.027800, 0.027680, 0.027561, 0.027441, 0.027323,
+ 0.027205, 0.027087, 0.026970, 0.026853, 0.026737, 0.026622, 0.026507, 0.026392,
+ 0.026278, 0.026164, 0.026051, 0.025939, 0.025827, 0.025715, 0.025604, 0.025493,
+ 0.025383, 0.025273, 0.025164, 0.025055, 0.024947, 0.024839, 0.024732, 0.024625,
+ 0.024518, 0.024412, 0.024307, 0.024202, 0.024097, 0.023993, 0.023889, 0.023786,
+ 0.023683, 0.023581, 0.023479, 0.023377, 0.023276, 0.023176, 0.023075, 0.022976,
+ 0.022876, 0.022777, 0.022679, 0.022581, 0.022483, 0.022386, 0.022289, 0.022193,
+ 0.022097, 0.022002, 0.021906, 0.021812, 0.021717, 0.021624, 0.021530, 0.021437,
+ 0.021344, 0.021252, 0.021160, 0.021069, 0.020978, 0.020887, 0.020797, 0.020707,
+ 0.020617, 0.020528, 0.020439, 0.020351, 0.020263, 0.020176, 0.020088, 0.020001,
+ 0.019915, 0.019829, 0.019743, 0.019658, 0.019573, 0.019488, 0.019404, 0.019320,
+ 0.019237, 0.019153, 0.019071, 0.018988, 0.018906, 0.018824, 0.018743, 0.018662,
+ 0.018581, 0.018501, 0.018421, 0.018341, 0.018262, 0.018183, 0.018105, 0.018026,
+ 0.017948, 0.017871, 0.017794, 0.017717, 0.017640, 0.017564, 0.017488, 0.017412,
+ 0.017337, 0.017262, 0.017187, 0.017113, 0.017039, 0.016966, 0.016892, 0.016819,
+ 0.016746, 0.016674, 0.016602, 0.016530, 0.016459, 0.016388, 0.016317, 0.016246,
+ 0.016176, 0.016106, 0.016036, 0.015967, 0.015898, 0.015829, 0.015761, 0.015693,
+ 0.015625,
+};
+
+/* Reverb Time in sec */
+float reverb_time_table[128] =
+{
+ 0.410349, 0.440872, 0.468882, 0.494640, 0.518394, 0.540373, 0.560793, 0.579854,
+ 0.597743, 0.614635, 0.630688, 0.646053, 0.660866, 0.675251, 0.689325, 0.703192,
+ 0.716947, 0.730676, 0.744456, 0.758358, 0.772441, 0.786761, 0.801365, 0.816293,
+ 0.831583, 0.847262, 0.863356, 0.879886, 0.896866, 0.914308, 0.932223, 0.950614,
+ 0.969484, 0.988835, 1.008663, 1.028967, 1.049741, 1.070980, 1.092677, 1.114826,
+ 1.137419, 1.160450, 1.183914, 1.207803, 1.232115, 1.256845, 1.281992, 1.307556,
+ 1.333540, 1.359947, 1.386784, 1.414061, 1.441788, 1.469982, 1.498661, 1.527845,
+ 1.557561, 1.587836, 1.618703, 1.650199, 1.682363, 1.715240, 1.748879, 1.783333,
+ 1.818659, 1.854921, 1.892183, 1.930517, 1.970001, 2.010713, 2.052741, 2.096173,
+ 2.141107, 2.187641, 2.235880, 2.285935, 2.337920, 2.391955, 2.448163, 2.506674,
+ 2.567622, 2.631144, 2.697384, 2.766490, 2.838612, 2.913907, 2.992536, 3.074662,
+ 3.160454, 3.250085, 3.343730, 3.441570, 3.543786, 3.650566, 3.762098, 3.878575,
+ 4.000192, 4.127146, 4.259638, 4.397868, 4.542042, 4.692364, 4.849041, 5.012281,
+ 5.182294, 5.359289, 5.543476, 5.735064, 5.934264, 6.141286, 6.356336, 6.356336,
+ 6.356336, 6.356336, 6.356336, 6.356336, 6.356336, 6.356336, 6.356336, 6.356336,
+ 6.356336, 6.356336, 6.356336, 6.356336, 6.356336, 6.356336, 6.356336, 6.356336,
+};
+
+/* phase lag between left and right ear. (in ms) */
+float pan_delay_table[128] =
+{
+ 0.000000, 0.006136, 0.012271, 0.018404, 0.024534, 0.030660, 0.036782, 0.042899,
+ 0.049009, 0.055111, 0.061205, 0.067290, 0.073365, 0.079429, 0.085481, 0.091520,
+ 0.097545, 0.103556, 0.109551, 0.115529, 0.121490, 0.127433, 0.133356, 0.139260,
+ 0.145142, 0.151003, 0.156841, 0.162655, 0.168445, 0.174209, 0.179948, 0.185659,
+ 0.191342, 0.196996, 0.202621, 0.208215, 0.213778, 0.219308, 0.224806, 0.230269,
+ 0.235698, 0.241092, 0.246449, 0.251769, 0.257051, 0.262295, 0.267499, 0.272662,
+ 0.277785, 0.282866, 0.287904, 0.292899, 0.297850, 0.302756, 0.307616, 0.312430,
+ 0.317197, 0.321916, 0.326586, 0.331208, 0.335779, 0.340300, 0.344770, 0.349188,
+ 0.353553, 0.357865, 0.362124, 0.366327, 0.370476, 0.374568, 0.378604, 0.382584,
+ 0.386505, 0.390369, 0.394173, 0.397918, 0.401604, 0.405229, 0.408792, 0.412295,
+ 0.415735, 0.419112, 0.422427, 0.425678, 0.428864, 0.431986, 0.435043, 0.438035,
+ 0.440961, 0.443820, 0.446612, 0.449337, 0.451995, 0.454584, 0.457105, 0.459557,
+ 0.461940, 0.464253, 0.466496, 0.468670, 0.470772, 0.472804, 0.474764, 0.476653,
+ 0.478470, 0.480215, 0.481888, 0.483488, 0.485016, 0.486470, 0.487851, 0.489159,
+ 0.490393, 0.491553, 0.492639, 0.493651, 0.494588, 0.495451, 0.496240, 0.496953,
+ 0.497592, 0.498156, 0.498645, 0.499059, 0.499398, 0.499661, 0.499849, 0.500000,
+};
+
+/* for 0dB, 0.25dB, 0.5dB,..., 24dB. */
+float chamberlin_filter_db_to_q_table[97] =
+{
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.029207, 1.113701, 1.205132,
+ 1.304068, 1.411127, 1.526975, 1.652334, 1.787984, 1.934771, 2.093608, 2.265485,
+ 2.451472, 2.652729, 2.870507, 3.106165, 3.361169, 3.637108, 3.935700, 4.258806,
+ 4.608437, 4.986772, 5.396167, 5.839171, 6.318544, 6.837272, 7.398585, 8.005980,
+ 8.663240, 9.374459, 10.144065, 10.976853, 11.878010, 12.853149, 13.908342, 15.050163,
+ 16.285723, 17.622717, 19.069474, 20.635003, 22.329057, 24.162185, 26.145807, 28.292276,
+ 30.614961, 33.128330, 35.848037, 38.791022, 41.975614, 45.421648, 49.150589, 53.185661,
+ 57.551996, 62.276791, 67.389473, 72.921887, 78.908490, 85.386569, 92.396474, 99.981865,
+ 108.189987, 117.071964, 126.683116, 137.083307, 148.337313, 160.515229, 173.692904, 187.952416,
+ 203.382577, 220.079495, 238.147165, 257.698120, 278.854132, 301.746971, 326.519223, 353.325180,
+ 382.331802,
+};
+
+uint8 multi_eq_block_table_xg[] =
+{ /* Gain1, Freq1, Q1, Shape1, Gain2, Freq2, Q2, Not Used, Gain3, Freq3, Q3, Not Used,
+ Gain4, Freq4, Q4, Not Used, Gain5, Freq5, Shape5 */
+ 64, 12, 7, 0, 64, 28, 7, 0, 64, 34, 7, 0, 64, 46, 7, 0, 64, 52, 7, 0, /* Flat */
+ 58, 8, 7, 0, 66, 16, 3, 0, 68, 33, 3, 0, 60, 44, 5, 0, 58, 50, 7, 0, /* Jazz */
+ 68, 16, 7, 0, 60, 24, 20, 0, 67, 34, 7, 0, 60, 40, 20, 0, 70, 48, 7, 0, /* Pops */
+ 71, 16, 7, 0, 68, 20, 7, 0, 60, 36, 5, 0, 68, 41, 10, 0, 66, 50, 7, 0, /* Rock */
+ 67, 12, 7, 0, 68, 24, 7, 0, 64, 34, 5, 0, 66, 50, 7, 0, 61, 52, 7, 0, /* Concert */
+};
+
+float eq_freq_table_xg[] =
+{
+ 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, 63, 70, 80, 90, 100, 110,
+ 125, 140, 160, 180, 200, 225, 250, 280, 315, 355, 400, 450, 500, 560, 630,
+ 700, 800, 900, 1000, 1100, 1200, 1400, 1600, 1800, 2000, 2200, 2500, 2800, 3200, 3600,
+ 4000, 4500, 5000, 5600, 6300, 7000, 8000, 9000, 10000, 11000, 12000, 14000, 16000, 18000, 20000,
+};
+
+float lfo_freq_table_xg[] =
+{
+ 0.00, 0.04, 0.08, 0.13, 0.17, 0.21, 0.25, 0.29, 0.34, 0.38, 0.42, 0.46, 0.51, 0.55, 0.59, 0.63,
+ 0.67, 0.72, 0.76, 0.80, 0.84, 0.88, 0.93, 0.97, 1.01, 1.05, 1.09, 1.14, 1.18, 1.22, 1.26, 1.30,
+ 1.35, 1.39, 1.43, 1.47, 1.51, 1.56, 1.60, 1.64, 1.68, 1.72, 1.77, 1.81, 1.85, 1.89, 1.94, 1.98,
+ 2.02, 2.06, 2.10, 2.15, 2.19, 2.23, 2.27, 2.31, 2.36, 2.40, 2.44, 2.48, 2.52, 2.57, 2.61, 2.65,
+ 2.69, 2.78, 2.86, 2.94, 3.03, 3.11, 3.20, 2.28, 3.37, 3.45, 3.53, 3.62, 3.70, 3.87, 4.04, 4.21,
+ 4.37, 4.54, 4.71, 4.88, 5.05, 5.22, 5.38, 5.55, 5.72, 6.06, 6.39, 6.73, 7.07, 7.40, 7.74, 8.08,
+ 8.41, 8.75, 9.08, 9.42, 9.76, 10.1, 10.8, 11.4, 12.1, 12.8, 13.5, 14.1, 14.8, 15.5, 16.2, 16.8,
+ 17.5, 18.2, 19.5, 20.9, 22.2, 23.6, 24.9, 26.2, 27.6, 28.9, 30.3, 31.6, 33.0, 34.3, 37.0, 39.7,
+};
+
+float mod_delay_offset_table_xg[] =
+{
+ 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5,
+ 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1,
+ 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7,
+ 4.8, 4.9, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9, 6.0, 6.1, 6.2, 6.3,
+ 6.4, 6.5, 6.6, 6.7, 6.8, 6.9, 7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9,
+ 8.0, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, 9.2, 9.3, 9.4, 9.5,
+ 9.6, 9.7, 9.8, 9.9, 10.0, 11.1, 12.2, 13.3, 14.4, 15.5, 17.1, 18.6, 20.2, 21.8, 23.3, 24.9,
+ 26.5, 28.0, 29.6, 31.2, 32.8, 34.3, 35.9, 37.5, 39.0, 40.6, 42.2, 43.7, 45.3, 46.9, 48.4, 50.0,
+};
+
+float reverb_time_table_xg[] =
+{
+ 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8,
+ 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.4,
+ 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 5.0,
+ 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
+ 17.0, 18.0, 19.0, 20.0, 25.0, 30.0,
+};
+
+float delay_time_table_xg[] =
+{
+ 0.1, 1.7, 3.2, 4.8, 6.4, 8.0, 9.5, 11.1, 12.7, 14.3, 15.8, 17.4, 19.0, 20.6, 22.1, 23.7,
+ 25.3, 26.9, 28.4, 30.0, 31.6, 33.2, 34.7, 36.3, 37.9, 39.5, 41.0, 42.6, 44.2, 45.7, 47.3, 48.9,
+ 50.5, 52.0, 53.6, 55.2, 56.8, 58.3, 59.9, 61.5, 63.1, 64.6, 66.2, 67.8, 69.4, 70.9, 72.5, 74.1,
+ 75.7, 77.2, 78.8, 80.4, 81.9, 83.5, 85.1, 86.7, 88.2, 89.8, 91.4, 93.0, 94.5, 96.1, 97.7, 99.3,
+ 100.8, 102.4, 104.0, 105.6, 107.1, 108.7, 110.3, 111.9, 113.4, 115.0, 116.6, 118.2, 119.7, 121.3, 122.9, 124.4,
+ 126.0, 127.6, 129.2, 130.7, 132.3, 133.9, 135.5, 137.0, 138.6, 140.2, 141.8, 143.3, 144.9, 146.5, 148.1, 149.6,
+ 151.2, 152.8, 154.4, 155.9, 157.5, 159.1, 160.6, 162.2, 163.8, 165.4, 166.9, 168.5, 170.1, 171.7, 173.2, 174.8,
+ 176.4, 178.0, 179.5, 181.1, 182.7, 184.3, 185.8, 187.4, 189.0, 190.6, 192.1, 193.7, 195.3, 196.9, 198.4, 200.0,
+};
+
+int16 cutoff_freq_table_gs[] =
+{
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 315, 315, 315, 315, 315, 315, 315, 315,
+ 400, 400, 400, 400, 400, 400, 400, 400,
+ 500, 500, 500, 500, 500, 500, 500, 500,
+ 630, 630, 630, 630, 630, 630, 630, 630,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000,
+ 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250,
+ 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600,
+ 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000,
+ 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500,
+ 3150, 3150, 3150, 3150, 3150, 3150, 3150, 3150,
+ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000,
+ 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000,
+ 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300,
+ 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000,
+};
+
+int16 lpf_table_gs[] =
+{
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 315, 315, 315, 315, 315, 315, 315, 315,
+ 400, 400, 400, 400, 400, 400, 400, 400,
+ 500, 500, 500, 500, 500, 500, 500, 500,
+ 630, 630, 630, 630, 630, 630, 630, 630,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000,
+ 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250,
+ 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600,
+ 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000,
+ 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500,
+ 3150, 3150, 3150, 3150, 3150, 3150, 3150, 3150,
+ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000,
+ 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000,
+ 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+int16 eq_freq_table_gs[] =
+{
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ 250, 250, 250, 250, 250, 250, 250, 250,
+ 315, 315, 315, 315, 315, 315, 315, 315,
+ 400, 400, 400, 400, 400, 400, 400, 400,
+ 500, 500, 500, 500, 500, 500, 500, 500,
+ 630, 630, 630, 630, 630, 630, 630, 630,
+ 800, 800, 800, 800, 800, 800, 800, 800,
+ 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000,
+ 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250,
+ 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600,
+ 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000,
+ 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500,
+ 3150, 3150, 3150, 3150, 3150, 3150, 3150, 3150,
+ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000,
+ 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000,
+ 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300,
+};
+
+float lofi_sampling_freq_table_xg[] =
+{
+ 44100.0, 22100.0, 14700.0, 11000.0, 8800.0, 7400.0, 6300.0, 5500.0,
+ 4900.0, 4400.0, 4000.0, 3700.0, 3400.0, 3200.0, 2900.0, 2800.0,
+ 2600.0, 2500.0, 2300.0, 2200.0, 2100.0, 2000.0, 1920.0, 1840.0,
+ 1760.0, 1700.0, 1630.0, 1580.0, 1520.0, 1470.0, 1420.0, 1380.0,
+ 1340.0, 1300.0, 1260.0, 1230.0, 1190.0, 1160.0, 1130.0, 1110.0,
+ 1080.0, 1050.0, 1030.0, 1000.0, 980.0, 959.0, 938.0, 919.0,
+ 900.0, 882.0, 865.0, 848.0, 832.0, 817.0, 802.0, 788.0,
+ 774.0, 760.0, 747.0, 735.0, 723.0, 711.0, 700.0, 689.0,
+ 678.0, 668.0, 658.0, 649.0, 639.0, 630.0, 621.0, 613.0,
+ 604.0, 596.0, 588.0, 580.0, 573.0, 565.0, 558.0, 551.0,
+ 544.0, 538.0, 531.0, 525.0, 519.0, 513.0, 507.0, 501.0,
+ 496.0, 490.0, 485.0, 479.0, 474.0, 469.0, 464.0, 459.0,
+ 455.0, 450.0, 445.0, 441.0, 437.0, 432.0, 428.0, 424.0,
+ 420.0, 416.0, 412.0, 408.0, 405.0, 401.0, 397.0, 394.0,
+ 390.0, 387.0, 383.0, 380.0, 377.0, 374.0, 371.0, 368.0,
+ 364.0, 361.0, 359.0, 356.0, 353.0, 350.0, 347.0, 345.0,
+};
diff --git a/src/tables.h b/src/tables.h
new file mode 100644
index 0000000..82439ea
--- /dev/null
+++ b/src/tables.h
@@ -0,0 +1,114 @@
+/*
+ TiMidity++ -- MIDI to WAVE converter and player
+ Copyright (C) 1999-2004 Masanao Izumo <iz@onicos.co.jp>
+ Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ tables.h
+*/
+
+#ifndef ___TABLES_H_
+#define ___TABLES_H_
+
+#ifdef LOOKUP_SINE
+extern FLOAT_T lookup_sine(int x);
+#else
+#include <math.h>
+#define lookup_sine(x) (sin((2*M_PI/1024.0) * (x)))
+#endif
+extern FLOAT_T lookup_triangular(int x);
+extern FLOAT_T lookup_log(int x);
+
+#define SINE_CYCLE_LENGTH 1024
+extern int32 freq_table[];
+extern int32 freq_table_zapped[];
+extern int32 freq_table_tuning[][128];
+extern int32 freq_table_pytha[][128];
+extern int32 freq_table_meantone[][128];
+extern int32 freq_table_pureint[][128];
+extern int32 freq_table_user[][48][128];
+extern FLOAT_T *vol_table;
+extern FLOAT_T def_vol_table[];
+extern FLOAT_T gs_vol_table[];
+extern FLOAT_T *xg_vol_table; /* == gs_vol_table */
+extern FLOAT_T *pan_table;
+extern FLOAT_T bend_fine[];
+extern FLOAT_T bend_coarse[];
+extern FLOAT_T midi_time_table[], midi_time_table2[];
+#ifdef LOOKUP_HACK
+extern uint8 *_l2u; /* 13-bit PCM to 8-bit u-law */
+extern uint8 _l2u_[]; /* used in LOOKUP_HACK */
+extern int16 _u2l[];
+extern int32 *mixup;
+#ifdef LOOKUP_INTERPOLATION
+extern int8 *iplookup;
+#endif
+#endif
+extern uint8 reverb_macro_presets[];
+extern uint8 chorus_macro_presets[];
+extern uint8 delay_macro_presets[];
+extern float delay_time_center_table[];
+extern float pre_delay_time_table[];
+extern float chorus_delay_time_table[];
+extern float rate1_table[];
+extern FLOAT_T attack_vol_table[];
+extern FLOAT_T perceived_vol_table[];
+extern FLOAT_T gm2_vol_table[];
+extern FLOAT_T user_vol_table[];
+extern float sc_eg_attack_table[];
+extern float sc_eg_decay_table[];
+extern float sc_eg_release_table[];
+extern FLOAT_T sc_vel_table[];
+extern FLOAT_T sc_vol_table[];
+extern FLOAT_T sc_pan_table[], gm2_pan_table[];
+extern FLOAT_T sc_drum_level_table[];
+extern FLOAT_T sb_vol_table[];
+extern FLOAT_T modenv_vol_table[];
+extern float cb_to_amp_table[];
+extern float reverb_time_table[];
+extern float pan_delay_table[];
+extern float chamberlin_filter_db_to_q_table[];
+extern uint8 multi_eq_block_table_xg[];
+extern float eq_freq_table_xg[];
+extern float lfo_freq_table_xg[];
+extern float mod_delay_offset_table_xg[];
+extern float reverb_time_table_xg[];
+extern float delay_time_table_xg[];
+extern int16 cutoff_freq_table_gs[];
+extern int16 lpf_table_gs[];
+extern int16 eq_freq_table_gs[];
+extern float lofi_sampling_freq_table_xg[];
+
+extern void init_freq_table(void);
+extern void init_freq_table_tuning(void);
+extern void init_freq_table_pytha(void);
+extern void init_freq_table_meantone(void);
+extern void init_freq_table_pureint(void);
+extern void init_freq_table_user(void);
+extern void init_bend_fine(void);
+extern void init_bend_coarse(void);
+extern void init_tables(void);
+extern void init_gm2_pan_table(void);
+extern void init_attack_vol_table(void);
+extern void init_sb_vol_table(void);
+extern void init_modenv_vol_table(void);
+extern void init_def_vol_table(void);
+extern void init_gs_vol_table(void);
+extern void init_perceived_vol_table(void);
+extern void init_gm2_vol_table(void);
+extern void init_user_vol_table(FLOAT_T power);
+
+#endif /* ___TABLES_H_ */