diff options
Diffstat (limited to 'lib/libmodplug/src')
43 files changed, 31010 insertions, 0 deletions
diff --git a/lib/libmodplug/src/Makefile.am b/lib/libmodplug/src/Makefile.am new file mode 100644 index 0000000000..5be33d4257 --- /dev/null +++ b/lib/libmodplug/src/Makefile.am @@ -0,0 +1,48 @@ +AM_CPPFLAGS = -I$(top_srcdir)/src/libmodplug + +if MINGW32 + AM_LDFLAGS = -no-undefined +endif +lib_LTLIBRARIES = libmodplug.la +libmodplug_la_LIBADD = -lm +libmodplug_la_SOURCES = tables.h \ + sndmix.cpp \ + sndfile.cpp \ + snd_fx.cpp \ + snd_flt.cpp \ + snd_dsp.cpp \ + fastmix.cpp \ + mmcmp.cpp \ + load_xm.cpp \ + load_wav.cpp \ + load_umx.cpp \ + load_ult.cpp \ + load_stm.cpp \ + load_s3m.cpp \ + load_ptm.cpp \ + load_okt.cpp \ + load_mtm.cpp \ + load_mod.cpp \ + load_med.cpp \ + load_mdl.cpp \ + load_it.cpp \ + load_far.cpp \ + load_dsm.cpp \ + load_dmf.cpp \ + load_dbm.cpp \ + load_ams.cpp \ + load_amf.cpp \ + load_669.cpp \ + load_j2b.cpp \ + load_mt2.cpp \ + load_psm.cpp \ + load_abc.cpp \ + load_mid.cpp \ + load_pat.cpp \ + modplug.cpp + +libmodplugincludedir = $(includedir)/libmodplug + + +libmodpluginclude_HEADERS = libmodplug/stdafx.h libmodplug/sndfile.h libmodplug/it_defs.h modplug.h +noinst_HEADERS = load_pat.h diff --git a/lib/libmodplug/src/Makefile.in b/lib/libmodplug/src/Makefile.in new file mode 100644 index 0000000000..351e997feb --- /dev/null +++ b/lib/libmodplug/src/Makefile.in @@ -0,0 +1,652 @@ +# Makefile.in generated by automake 1.11 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src +DIST_COMMON = $(libmodpluginclude_HEADERS) $(noinst_HEADERS) \ + $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/config.h.in +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 = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__installdirs = "$(DESTDIR)$(libdir)" \ + "$(DESTDIR)$(libmodplugincludedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libmodplug_la_DEPENDENCIES = +am_libmodplug_la_OBJECTS = sndmix.lo sndfile.lo snd_fx.lo snd_flt.lo \ + snd_dsp.lo fastmix.lo mmcmp.lo load_xm.lo load_wav.lo \ + load_umx.lo load_ult.lo load_stm.lo load_s3m.lo load_ptm.lo \ + load_okt.lo load_mtm.lo load_mod.lo load_med.lo load_mdl.lo \ + load_it.lo load_far.lo load_dsm.lo load_dmf.lo load_dbm.lo \ + load_ams.lo load_amf.lo load_669.lo load_j2b.lo load_mt2.lo \ + load_psm.lo load_abc.lo load_mid.lo load_pat.lo modplug.lo +libmodplug_la_OBJECTS = $(am_libmodplug_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libmodplug_la_SOURCES) +DIST_SOURCES = $(libmodplug_la_SOURCES) +HEADERS = $(libmodpluginclude_HEADERS) $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +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_CPPFLAGS = -I$(top_srcdir)/src/libmodplug +@MINGW32_TRUE@AM_LDFLAGS = -no-undefined +lib_LTLIBRARIES = libmodplug.la +libmodplug_la_LIBADD = -lm +libmodplug_la_SOURCES = tables.h \ + sndmix.cpp \ + sndfile.cpp \ + snd_fx.cpp \ + snd_flt.cpp \ + snd_dsp.cpp \ + fastmix.cpp \ + mmcmp.cpp \ + load_xm.cpp \ + load_wav.cpp \ + load_umx.cpp \ + load_ult.cpp \ + load_stm.cpp \ + load_s3m.cpp \ + load_ptm.cpp \ + load_okt.cpp \ + load_mtm.cpp \ + load_mod.cpp \ + load_med.cpp \ + load_mdl.cpp \ + load_it.cpp \ + load_far.cpp \ + load_dsm.cpp \ + load_dmf.cpp \ + load_dbm.cpp \ + load_ams.cpp \ + load_amf.cpp \ + load_669.cpp \ + load_j2b.cpp \ + load_mt2.cpp \ + load_psm.cpp \ + load_abc.cpp \ + load_mid.cpp \ + load_pat.cpp \ + modplug.cpp + +libmodplugincludedir = $(includedir)/libmodplug +libmodpluginclude_HEADERS = libmodplug/stdafx.h libmodplug/sndfile.h libmodplug/it_defs.h modplug.h +noinst_HEADERS = load_pat.h +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --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): + +config.h: stamp-h1 + @if test ! -f $@; then \ + rm -f stamp-h1; \ + $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \ + else :; fi + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status src/config.h +$(srcdir)/config.h.in: $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libmodplug.la: $(libmodplug_la_OBJECTS) $(libmodplug_la_DEPENDENCIES) + $(CXXLINK) -rpath $(libdir) $(libmodplug_la_OBJECTS) $(libmodplug_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fastmix.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_669.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_abc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_amf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_ams.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_dbm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_dmf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_dsm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_far.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_it.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_j2b.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_mdl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_med.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_mid.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_mod.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_mt2.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_mtm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_okt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_pat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_psm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_ptm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_s3m.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_stm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_ult.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_umx.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_wav.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_xm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmcmp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/modplug.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snd_dsp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snd_flt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snd_fx.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sndfile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sndmix.Plo@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-libmodplugincludeHEADERS: $(libmodpluginclude_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(libmodplugincludedir)" || $(MKDIR_P) "$(DESTDIR)$(libmodplugincludedir)" + @list='$(libmodpluginclude_HEADERS)'; test -n "$(libmodplugincludedir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libmodplugincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(libmodplugincludedir)" || exit $$?; \ + done + +uninstall-libmodplugincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(libmodpluginclude_HEADERS)'; test -n "$(libmodplugincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(libmodplugincludedir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(libmodplugincludedir)" && rm -f $$files + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) config.h +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libmodplugincludedir)"; 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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-libmodplugincludeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES \ + uninstall-libmodplugincludeHEADERS + +.MAKE: all install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool ctags distclean \ + distclean-compile distclean-generic distclean-hdr \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES \ + install-libmodplugincludeHEADERS install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-libLTLIBRARIES \ + uninstall-libmodplugincludeHEADERS + + +# 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/lib/libmodplug/src/config.h.in b/lib/libmodplug/src/config.h.in new file mode 100644 index 0000000000..e4aac90f3a --- /dev/null +++ b/lib/libmodplug/src/config.h.in @@ -0,0 +1,130 @@ +/* src/config.h.in. Generated from configure.in by autoheader. */ + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the <malloc.h> header file. */ +#undef HAVE_MALLOC_H + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `setenv' function. */ +#undef HAVE_SETENV + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif + +/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>, + <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT32_T + +/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>, + <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT64_T + +/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>, + <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT8_T + +/* Define to the type of a signed integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +#undef int16_t + +/* Define to the type of a signed integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef int32_t + +/* Define to the type of a signed integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +#undef int64_t + +/* Define to the type of a signed integer type of width exactly 8 bits if such + a type exists and the standard includes do not define it. */ +#undef int8_t + +/* Define to the type of an unsigned integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +#undef uint16_t + +/* Define to the type of an unsigned integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef uint32_t + +/* Define to the type of an unsigned integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +#undef uint64_t + +/* Define to the type of an unsigned integer type of width exactly 8 bits if + such a type exists and the standard includes do not define it. */ +#undef uint8_t diff --git a/lib/libmodplug/src/fastmix.cpp b/lib/libmodplug/src/fastmix.cpp new file mode 100644 index 0000000000..69570df084 --- /dev/null +++ b/lib/libmodplug/src/fastmix.cpp @@ -0,0 +1,2388 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> + * Markus Fick <webmaster@mark-f.de> spline + fir-resampler +*/ + +#include "stdafx.h" +#include "sndfile.h" +#include <math.h> + +#ifdef MSC_VER +#pragma bss_seg(".modplug") +#endif + +// Front Mix Buffer (Also room for interleaved rear mix) +int MixSoundBuffer[MIXBUFFERSIZE*4]; + +// Reverb Mix Buffer +#ifndef MODPLUG_NO_REVERB +int MixReverbBuffer[MIXBUFFERSIZE*2]; +extern UINT gnReverbSend; +#endif + +#ifndef MODPLUG_FASTSOUNDLIB +int MixRearBuffer[MIXBUFFERSIZE*2]; +float MixFloatBuffer[MIXBUFFERSIZE*2]; +#endif + +#ifdef MSC_VER +#pragma bss_seg() +#endif + + +extern LONG gnDryROfsVol; +extern LONG gnDryLOfsVol; +extern LONG gnRvbROfsVol; +extern LONG gnRvbLOfsVol; + +// 4x256 taps polyphase FIR resampling filter +extern short int gFastSinc[]; +extern short int gKaiserSinc[]; // 8-taps polyphase +/* + *----------------------------------------------------------------------------- + cubic spline interpolation doc, + (derived from "digital image warping", g. wolberg) + + interpolation polynomial: f(x) = A3*(x-floor(x))**3 + A2*(x-floor(x))**2 + + A1*(x-floor(x)) + A0 + + with Y = equispaced data points (dist=1), YD = first derivates of data points and IP = floor(x) + the A[0..3] can be found by solving + A0 = Y[IP] + A1 = YD[IP] + A2 = 3*(Y[IP+1]-Y[IP])-2.0*YD[IP]-YD[IP+1] + A3 = -2.0 * (Y[IP+1]-Y[IP]) + YD[IP] - YD[IP+1] + + with the first derivates as + YD[IP] = 0.5 * (Y[IP+1] - Y[IP-1]); + YD[IP+1] = 0.5 * (Y[IP+2] - Y[IP]) + + the coefs becomes + A0 = Y[IP] + A1 = YD[IP] + = 0.5*(Y[IP+1] - Y[IP-1]); + A2 = 3.0*(Y[IP+1]-Y[IP])-2.0*YD[IP]-YD[IP+1] + = 3.0*(Y[IP+1]-Y[IP]) - 0.5*2.0*(Y[IP+1]-Y[IP-1]) - 0.5*(Y[IP+2]-Y[IP]) + = 3.0*Y[IP+1] - 3.0*Y[IP] - Y[IP+1] + Y[IP-1] - 0.5*Y[IP+2] + 0.5*Y[IP] + = -0.5*Y[IP+2] + 2.0 * Y[IP+1] - 2.5*Y[IP] + Y[IP-1] + = Y[IP-1] + 2 * Y[IP+1] - 0.5 * (5.0 * Y[IP] + Y[IP+2]) + A3 = -2.0*(Y[IP+1]-Y[IP]) + YD[IP] + YD[IP+1] + = -2.0*Y[IP+1] + 2.0*Y[IP] + 0.5*(Y[IP+1]-Y[IP-1]) + 0.5*(Y[IP+2]-Y[IP]) + = -2.0*Y[IP+1] + 2.0*Y[IP] + 0.5*Y[IP+1] - 0.5*Y[IP-1] + 0.5*Y[IP+2] - 0.5*Y[IP] + = 0.5 * Y[IP+2] - 1.5 * Y[IP+1] + 1.5 * Y[IP] - 0.5 * Y[IP-1] + = 0.5 * (3.0 * (Y[IP] - Y[IP+1]) - Y[IP-1] + YP[IP+2]) + + then interpolated data value is (horner rule) + out = (((A3*x)+A2)*x+A1)*x+A0 + + this gives parts of data points Y[IP-1] to Y[IP+2] of + part x**3 x**2 x**1 x**0 + Y[IP-1] -0.5 1 -0.5 0 + Y[IP] 1.5 -2.5 0 1 + Y[IP+1] -1.5 2 0.5 0 + Y[IP+2] 0.5 -0.5 0 0 + *--------------------------------------------------------------------------- + */ +// number of bits used to scale spline coefs +#define SPLINE_QUANTBITS 14 +#define SPLINE_QUANTSCALE (1L<<SPLINE_QUANTBITS) +#define SPLINE_8SHIFT (SPLINE_QUANTBITS-8) +#define SPLINE_16SHIFT (SPLINE_QUANTBITS) +// forces coefsset to unity gain +#define SPLINE_CLAMPFORUNITY +// log2(number) of precalculated splines (range is [4..14]) +#define SPLINE_FRACBITS 10 +#define SPLINE_LUTLEN (1L<<SPLINE_FRACBITS) + +class CzCUBICSPLINE +{ public: + CzCUBICSPLINE( ); + ~CzCUBICSPLINE( ); + static signed short lut[4*(1L<<SPLINE_FRACBITS)]; +}; + +signed short CzCUBICSPLINE::lut[4*(1L<<SPLINE_FRACBITS)]; + +CzCUBICSPLINE::CzCUBICSPLINE( ) +{ int _LIi; + int _LLen = (1L<<SPLINE_FRACBITS); + float _LFlen = 1.0f / (float)_LLen; + float _LScale = (float)SPLINE_QUANTSCALE; + for(_LIi=0;_LIi<_LLen;_LIi++) + { float _LCm1, _LC0, _LC1, _LC2; + float _LX = ((float)_LIi)*_LFlen; + int _LSum,_LIdx = _LIi<<2; + _LCm1 = (float)floor( 0.5 + _LScale*(-0.5*_LX*_LX*_LX + 1.0*_LX*_LX - 0.5*_LX ) ); + _LC0 = (float)floor( 0.5 + _LScale*( 1.5*_LX*_LX*_LX - 2.5*_LX*_LX + 1.0 ) ); + _LC1 = (float)floor( 0.5 + _LScale*(-1.5*_LX*_LX*_LX + 2.0*_LX*_LX + 0.5*_LX ) ); + _LC2 = (float)floor( 0.5 + _LScale*( 0.5*_LX*_LX*_LX - 0.5*_LX*_LX) ); + lut[_LIdx+0] = (signed short)( (_LCm1 < -_LScale) ? -_LScale : ((_LCm1 > _LScale) ? _LScale : _LCm1) ); + lut[_LIdx+1] = (signed short)( (_LC0 < -_LScale) ? -_LScale : ((_LC0 > _LScale) ? _LScale : _LC0 ) ); + lut[_LIdx+2] = (signed short)( (_LC1 < -_LScale) ? -_LScale : ((_LC1 > _LScale) ? _LScale : _LC1 ) ); + lut[_LIdx+3] = (signed short)( (_LC2 < -_LScale) ? -_LScale : ((_LC2 > _LScale) ? _LScale : _LC2 ) ); +#ifdef SPLINE_CLAMPFORUNITY + _LSum = lut[_LIdx+0]+lut[_LIdx+1]+lut[_LIdx+2]+lut[_LIdx+3]; + if( _LSum != SPLINE_QUANTSCALE ) + { int _LMax = _LIdx; + if( lut[_LIdx+1]>lut[_LMax] ) _LMax = _LIdx+1; + if( lut[_LIdx+2]>lut[_LMax] ) _LMax = _LIdx+2; + if( lut[_LIdx+3]>lut[_LMax] ) _LMax = _LIdx+3; + lut[_LMax] += (SPLINE_QUANTSCALE-_LSum); + } +#endif + } +} + +CzCUBICSPLINE::~CzCUBICSPLINE( ) +{ // nothing todo +} + +CzCUBICSPLINE sspline; + +/* + ------------------------------------------------------------------------------ + fir interpolation doc, + (derived from "an engineer's guide to fir digital filters", n.j. loy) + + calculate coefficients for ideal lowpass filter (with cutoff = fc in + 0..1 (mapped to 0..nyquist)) + c[-N..N] = (i==0) ? fc : sin(fc*pi*i)/(pi*i) + + then apply selected window to coefficients + c[-N..N] *= w(0..N) + with n in 2*N and w(n) being a window function (see loy) + + then calculate gain and scale filter coefs to have unity gain. + ------------------------------------------------------------------------------ +*/ +// quantizer scale of window coefs +#define WFIR_QUANTBITS 15 +#define WFIR_QUANTSCALE (1L<<WFIR_QUANTBITS) +#define WFIR_8SHIFT (WFIR_QUANTBITS-8) +#define WFIR_16BITSHIFT (WFIR_QUANTBITS) +// log2(number)-1 of precalculated taps range is [4..12] +#define WFIR_FRACBITS 10 +#define WFIR_LUTLEN ((1L<<(WFIR_FRACBITS+1))+1) +// number of samples in window +#define WFIR_LOG2WIDTH 3 +#define WFIR_WIDTH (1L<<WFIR_LOG2WIDTH) +#define WFIR_SMPSPERWING ((WFIR_WIDTH-1)>>1) +// cutoff (1.0 == pi/2) +#define WFIR_CUTOFF 0.90f +// wfir type +#define WFIR_HANN 0 +#define WFIR_HAMMING 1 +#define WFIR_BLACKMANEXACT 2 +#define WFIR_BLACKMAN3T61 3 +#define WFIR_BLACKMAN3T67 4 +#define WFIR_BLACKMAN4T92 5 +#define WFIR_BLACKMAN4T74 6 +#define WFIR_KAISER4T 7 +#define WFIR_TYPE WFIR_BLACKMANEXACT +// wfir help +#ifndef M_zPI +#define M_zPI 3.1415926535897932384626433832795 +#endif +#define M_zEPS 1e-8 +#define M_zBESSELEPS 1e-21 + +class CzWINDOWEDFIR +{ +public: + CzWINDOWEDFIR( ); + ~CzWINDOWEDFIR( ); + float coef( int _PCnr, float _POfs, float _PCut, int _PWidth, int _PType ) +//OLD args to coef: float _PPos, float _PFc, int _PLen ) + { + double _LWidthM1 = _PWidth-1; + double _LWidthM1Half = 0.5*_LWidthM1; + double _LPosU = ((double)_PCnr - _POfs); + double _LPos = _LPosU-_LWidthM1Half; + double _LPIdl = 2.0*M_zPI/_LWidthM1; + double _LWc,_LSi; + if( fabs(_LPos)<M_zEPS ) { + _LWc = 1.0; + _LSi = _PCut; + } else { + switch( _PType ) + { + case WFIR_HANN: + _LWc = 0.50 - 0.50 * cos(_LPIdl*_LPosU); + break; + case WFIR_HAMMING: + _LWc = 0.54 - 0.46 * cos(_LPIdl*_LPosU); + break; + case WFIR_BLACKMANEXACT: + _LWc = 0.42 - 0.50 * cos(_LPIdl*_LPosU) + + 0.08 * cos(2.0*_LPIdl*_LPosU); + break; + case WFIR_BLACKMAN3T61: + _LWc = 0.44959 - 0.49364 * cos(_LPIdl*_LPosU) + + 0.05677 * cos(2.0*_LPIdl*_LPosU); + break; + case WFIR_BLACKMAN3T67: + _LWc = 0.42323 - 0.49755 * cos(_LPIdl*_LPosU) + + 0.07922 * cos(2.0*_LPIdl*_LPosU); + break; + case WFIR_BLACKMAN4T92: + _LWc = 0.35875 - 0.48829 * cos(_LPIdl*_LPosU) + + 0.14128 * cos(2.0*_LPIdl*_LPosU) - + 0.01168 * cos(3.0*_LPIdl*_LPosU); + break; + case WFIR_BLACKMAN4T74: + _LWc = 0.40217 - 0.49703 * cos(_LPIdl*_LPosU) + + 0.09392 * cos(2.0*_LPIdl*_LPosU) - + 0.00183 * cos(3.0*_LPIdl*_LPosU); + break; + case WFIR_KAISER4T: + _LWc = 0.40243 - 0.49804 * cos(_LPIdl*_LPosU) + + 0.09831 * cos(2.0*_LPIdl*_LPosU) - + 0.00122 * cos(3.0*_LPIdl*_LPosU); + break; + default: + _LWc = 1.0; + break; + } + _LPos *= M_zPI; + _LSi = sin(_PCut*_LPos)/_LPos; + } + return (float)(_LWc*_LSi); + } + static signed short lut[WFIR_LUTLEN*WFIR_WIDTH]; +}; + +signed short CzWINDOWEDFIR::lut[WFIR_LUTLEN*WFIR_WIDTH]; + +CzWINDOWEDFIR::CzWINDOWEDFIR() +{ + int _LPcl; + float _LPcllen = (float)(1L<<WFIR_FRACBITS); // number of precalculated lines for 0..1 (-1..0) + float _LNorm = 1.0f / (float)(2.0f * _LPcllen); + float _LCut = WFIR_CUTOFF; + float _LScale = (float)WFIR_QUANTSCALE; + for( _LPcl=0;_LPcl<WFIR_LUTLEN;_LPcl++ ) + { + float _LGain,_LCoefs[WFIR_WIDTH]; + float _LOfs = ((float)_LPcl-_LPcllen)*_LNorm; + int _LCc,_LIdx = _LPcl<<WFIR_LOG2WIDTH; + for( _LCc=0,_LGain=0.0f;_LCc<WFIR_WIDTH;_LCc++ ) + { _LGain += (_LCoefs[_LCc] = coef( _LCc, _LOfs, _LCut, WFIR_WIDTH, WFIR_TYPE )); + } + _LGain = 1.0f/_LGain; + for( _LCc=0;_LCc<WFIR_WIDTH;_LCc++ ) + { float _LCoef = (float)floor( 0.5 + _LScale*_LCoefs[_LCc]*_LGain ); + lut[_LIdx+_LCc] = (signed short)( (_LCoef<-_LScale)?-_LScale:((_LCoef>_LScale)?_LScale:_LCoef) ); + } + } +} + +CzWINDOWEDFIR::~CzWINDOWEDFIR() +{ // nothing todo +} + +CzWINDOWEDFIR sfir; + +// ---------------------------------------------------------------------------- +// MIXING MACROS +// ---------------------------------------------------------------------------- +#define SNDMIX_BEGINSAMPLELOOP8\ + register MODCHANNEL * const pChn = pChannel;\ + nPos = pChn->nPosLo;\ + const signed char *p = (signed char *)(pChn->pCurrentSample+pChn->nPos);\ + if (pChn->dwFlags & CHN_STEREO) p += pChn->nPos;\ + int *pvol = pbuffer;\ + do { + +#define SNDMIX_BEGINSAMPLELOOP16\ + register MODCHANNEL * const pChn = pChannel;\ + nPos = pChn->nPosLo;\ + const signed short *p = (signed short *)(pChn->pCurrentSample+(pChn->nPos*2));\ + if (pChn->dwFlags & CHN_STEREO) p += pChn->nPos;\ + int *pvol = pbuffer;\ + do { + +#define SNDMIX_ENDSAMPLELOOP\ + nPos += pChn->nInc;\ + } while (pvol < pbufmax);\ + pChn->nPos += nPos >> 16;\ + pChn->nPosLo = nPos & 0xFFFF; + +#define SNDMIX_ENDSAMPLELOOP8 SNDMIX_ENDSAMPLELOOP +#define SNDMIX_ENDSAMPLELOOP16 SNDMIX_ENDSAMPLELOOP + +////////////////////////////////////////////////////////////////////////////// +// Mono + +// No interpolation +#define SNDMIX_GETMONOVOL8NOIDO\ + int vol = p[nPos >> 16] << 8; + +#define SNDMIX_GETMONOVOL16NOIDO\ + int vol = p[nPos >> 16]; + +// Linear Interpolation +#define SNDMIX_GETMONOVOL8LINEAR\ + int poshi = nPos >> 16;\ + int poslo = (nPos >> 8) & 0xFF;\ + int srcvol = p[poshi];\ + int destvol = p[poshi+1];\ + int vol = (srcvol<<8) + ((int)(poslo * (destvol - srcvol))); + +#define SNDMIX_GETMONOVOL16LINEAR\ + int poshi = nPos >> 16;\ + int poslo = (nPos >> 8) & 0xFF;\ + int srcvol = p[poshi];\ + int destvol = p[poshi+1];\ + int vol = srcvol + ((int)(poslo * (destvol - srcvol)) >> 8); + +// spline interpolation (2 guard bits should be enough???) +#define SPLINE_FRACSHIFT ((16-SPLINE_FRACBITS)-2) +#define SPLINE_FRACMASK (((1L<<(16-SPLINE_FRACSHIFT))-1)&~3) + +#define SNDMIX_GETMONOVOL8SPLINE \ + int poshi = nPos >> 16; \ + int poslo = (nPos >> SPLINE_FRACSHIFT) & SPLINE_FRACMASK; \ + int vol = (CzCUBICSPLINE::lut[poslo ]*(int)p[poshi-1] + \ + CzCUBICSPLINE::lut[poslo+1]*(int)p[poshi ] + \ + CzCUBICSPLINE::lut[poslo+3]*(int)p[poshi+2] + \ + CzCUBICSPLINE::lut[poslo+2]*(int)p[poshi+1]) >> SPLINE_8SHIFT; + +#define SNDMIX_GETMONOVOL16SPLINE \ + int poshi = nPos >> 16; \ + int poslo = (nPos >> SPLINE_FRACSHIFT) & SPLINE_FRACMASK; \ + int vol = (CzCUBICSPLINE::lut[poslo ]*(int)p[poshi-1] + \ + CzCUBICSPLINE::lut[poslo+1]*(int)p[poshi ] + \ + CzCUBICSPLINE::lut[poslo+3]*(int)p[poshi+2] + \ + CzCUBICSPLINE::lut[poslo+2]*(int)p[poshi+1]) >> SPLINE_16SHIFT; + + +// fir interpolation +#define WFIR_FRACSHIFT (16-(WFIR_FRACBITS+1+WFIR_LOG2WIDTH)) +#define WFIR_FRACMASK ((((1L<<(17-WFIR_FRACSHIFT))-1)&~((1L<<WFIR_LOG2WIDTH)-1))) +#define WFIR_FRACHALVE (1L<<(16-(WFIR_FRACBITS+2))) + +#define SNDMIX_GETMONOVOL8FIRFILTER \ + int poshi = nPos >> 16;\ + int poslo = (nPos & 0xFFFF);\ + int firidx = ((poslo+WFIR_FRACHALVE)>>WFIR_FRACSHIFT) & WFIR_FRACMASK; \ + int vol = (CzWINDOWEDFIR::lut[firidx+0]*(int)p[poshi+1-4]); \ + vol += (CzWINDOWEDFIR::lut[firidx+1]*(int)p[poshi+2-4]); \ + vol += (CzWINDOWEDFIR::lut[firidx+2]*(int)p[poshi+3-4]); \ + vol += (CzWINDOWEDFIR::lut[firidx+3]*(int)p[poshi+4-4]); \ + vol += (CzWINDOWEDFIR::lut[firidx+4]*(int)p[poshi+5-4]); \ + vol += (CzWINDOWEDFIR::lut[firidx+5]*(int)p[poshi+6-4]); \ + vol += (CzWINDOWEDFIR::lut[firidx+6]*(int)p[poshi+7-4]); \ + vol += (CzWINDOWEDFIR::lut[firidx+7]*(int)p[poshi+8-4]); \ + vol >>= WFIR_8SHIFT; + +#define SNDMIX_GETMONOVOL16FIRFILTER \ + int poshi = nPos >> 16;\ + int poslo = (nPos & 0xFFFF);\ + int firidx = ((poslo+WFIR_FRACHALVE)>>WFIR_FRACSHIFT) & WFIR_FRACMASK; \ + int vol1 = (CzWINDOWEDFIR::lut[firidx+0]*(int)p[poshi+1-4]); \ + vol1 += (CzWINDOWEDFIR::lut[firidx+1]*(int)p[poshi+2-4]); \ + vol1 += (CzWINDOWEDFIR::lut[firidx+2]*(int)p[poshi+3-4]); \ + vol1 += (CzWINDOWEDFIR::lut[firidx+3]*(int)p[poshi+4-4]); \ + int vol2 = (CzWINDOWEDFIR::lut[firidx+4]*(int)p[poshi+5-4]); \ + vol2 += (CzWINDOWEDFIR::lut[firidx+5]*(int)p[poshi+6-4]); \ + vol2 += (CzWINDOWEDFIR::lut[firidx+6]*(int)p[poshi+7-4]); \ + vol2 += (CzWINDOWEDFIR::lut[firidx+7]*(int)p[poshi+8-4]); \ + int vol = ((vol1>>1)+(vol2>>1)) >> (WFIR_16BITSHIFT-1); + +///////////////////////////////////////////////////////////////////////////// +// Stereo + +// No interpolation +#define SNDMIX_GETSTEREOVOL8NOIDO\ + int vol_l = p[(nPos>>16)*2] << 8;\ + int vol_r = p[(nPos>>16)*2+1] << 8; + +#define SNDMIX_GETSTEREOVOL16NOIDO\ + int vol_l = p[(nPos>>16)*2];\ + int vol_r = p[(nPos>>16)*2+1]; + +// Linear Interpolation +#define SNDMIX_GETSTEREOVOL8LINEAR\ + int poshi = nPos >> 16;\ + int poslo = (nPos >> 8) & 0xFF;\ + int srcvol_l = p[poshi*2];\ + int vol_l = (srcvol_l<<8) + ((int)(poslo * (p[poshi*2+2] - srcvol_l)));\ + int srcvol_r = p[poshi*2+1];\ + int vol_r = (srcvol_r<<8) + ((int)(poslo * (p[poshi*2+3] - srcvol_r))); + +#define SNDMIX_GETSTEREOVOL16LINEAR\ + int poshi = nPos >> 16;\ + int poslo = (nPos >> 8) & 0xFF;\ + int srcvol_l = p[poshi*2];\ + int vol_l = srcvol_l + ((int)(poslo * (p[poshi*2+2] - srcvol_l)) >> 8);\ + int srcvol_r = p[poshi*2+1];\ + int vol_r = srcvol_r + ((int)(poslo * (p[poshi*2+3] - srcvol_r)) >> 8);\ + +// Spline Interpolation +#define SNDMIX_GETSTEREOVOL8SPLINE \ + int poshi = nPos >> 16; \ + int poslo = (nPos >> SPLINE_FRACSHIFT) & SPLINE_FRACMASK; \ + int vol_l = (CzCUBICSPLINE::lut[poslo ]*(int)p[(poshi-1)*2 ] + \ + CzCUBICSPLINE::lut[poslo+1]*(int)p[(poshi )*2 ] + \ + CzCUBICSPLINE::lut[poslo+2]*(int)p[(poshi+1)*2 ] + \ + CzCUBICSPLINE::lut[poslo+3]*(int)p[(poshi+2)*2 ]) >> SPLINE_8SHIFT; \ + int vol_r = (CzCUBICSPLINE::lut[poslo ]*(int)p[(poshi-1)*2+1] + \ + CzCUBICSPLINE::lut[poslo+1]*(int)p[(poshi )*2+1] + \ + CzCUBICSPLINE::lut[poslo+2]*(int)p[(poshi+1)*2+1] + \ + CzCUBICSPLINE::lut[poslo+3]*(int)p[(poshi+2)*2+1]) >> SPLINE_8SHIFT; + +#define SNDMIX_GETSTEREOVOL16SPLINE \ + int poshi = nPos >> 16; \ + int poslo = (nPos >> SPLINE_FRACSHIFT) & SPLINE_FRACMASK; \ + int vol_l = (CzCUBICSPLINE::lut[poslo ]*(int)p[(poshi-1)*2 ] + \ + CzCUBICSPLINE::lut[poslo+1]*(int)p[(poshi )*2 ] + \ + CzCUBICSPLINE::lut[poslo+2]*(int)p[(poshi+1)*2 ] + \ + CzCUBICSPLINE::lut[poslo+3]*(int)p[(poshi+2)*2 ]) >> SPLINE_16SHIFT; \ + int vol_r = (CzCUBICSPLINE::lut[poslo ]*(int)p[(poshi-1)*2+1] + \ + CzCUBICSPLINE::lut[poslo+1]*(int)p[(poshi )*2+1] + \ + CzCUBICSPLINE::lut[poslo+2]*(int)p[(poshi+1)*2+1] + \ + CzCUBICSPLINE::lut[poslo+3]*(int)p[(poshi+2)*2+1]) >> SPLINE_16SHIFT; + +// fir interpolation +#define SNDMIX_GETSTEREOVOL8FIRFILTER \ + int poshi = nPos >> 16;\ + int poslo = (nPos & 0xFFFF);\ + int firidx = ((poslo+WFIR_FRACHALVE)>>WFIR_FRACSHIFT) & WFIR_FRACMASK; \ + int vol_l = (CzWINDOWEDFIR::lut[firidx+0]*(int)p[(poshi+1-4)*2 ]); \ + vol_l += (CzWINDOWEDFIR::lut[firidx+1]*(int)p[(poshi+2-4)*2 ]); \ + vol_l += (CzWINDOWEDFIR::lut[firidx+2]*(int)p[(poshi+3-4)*2 ]); \ + vol_l += (CzWINDOWEDFIR::lut[firidx+3]*(int)p[(poshi+4-4)*2 ]); \ + vol_l += (CzWINDOWEDFIR::lut[firidx+4]*(int)p[(poshi+5-4)*2 ]); \ + vol_l += (CzWINDOWEDFIR::lut[firidx+5]*(int)p[(poshi+6-4)*2 ]); \ + vol_l += (CzWINDOWEDFIR::lut[firidx+6]*(int)p[(poshi+7-4)*2 ]); \ + vol_l += (CzWINDOWEDFIR::lut[firidx+7]*(int)p[(poshi+8-4)*2 ]); \ + vol_l >>= WFIR_8SHIFT; \ + int vol_r = (CzWINDOWEDFIR::lut[firidx+0]*(int)p[(poshi+1-4)*2+1]); \ + vol_r += (CzWINDOWEDFIR::lut[firidx+1]*(int)p[(poshi+2-4)*2+1]); \ + vol_r += (CzWINDOWEDFIR::lut[firidx+2]*(int)p[(poshi+3-4)*2+1]); \ + vol_r += (CzWINDOWEDFIR::lut[firidx+3]*(int)p[(poshi+4-4)*2+1]); \ + vol_r += (CzWINDOWEDFIR::lut[firidx+4]*(int)p[(poshi+5-4)*2+1]); \ + vol_r += (CzWINDOWEDFIR::lut[firidx+5]*(int)p[(poshi+6-4)*2+1]); \ + vol_r += (CzWINDOWEDFIR::lut[firidx+6]*(int)p[(poshi+7-4)*2+1]); \ + vol_r += (CzWINDOWEDFIR::lut[firidx+7]*(int)p[(poshi+8-4)*2+1]); \ + vol_r >>= WFIR_8SHIFT; + +#define SNDMIX_GETSTEREOVOL16FIRFILTER \ + int poshi = nPos >> 16;\ + int poslo = (nPos & 0xFFFF);\ + int firidx = ((poslo+WFIR_FRACHALVE)>>WFIR_FRACSHIFT) & WFIR_FRACMASK; \ + int vol1_l = (CzWINDOWEDFIR::lut[firidx+0]*(int)p[(poshi+1-4)*2 ]); \ + vol1_l += (CzWINDOWEDFIR::lut[firidx+1]*(int)p[(poshi+2-4)*2 ]); \ + vol1_l += (CzWINDOWEDFIR::lut[firidx+2]*(int)p[(poshi+3-4)*2 ]); \ + vol1_l += (CzWINDOWEDFIR::lut[firidx+3]*(int)p[(poshi+4-4)*2 ]); \ + int vol2_l = (CzWINDOWEDFIR::lut[firidx+4]*(int)p[(poshi+5-4)*2 ]); \ + vol2_l += (CzWINDOWEDFIR::lut[firidx+5]*(int)p[(poshi+6-4)*2 ]); \ + vol2_l += (CzWINDOWEDFIR::lut[firidx+6]*(int)p[(poshi+7-4)*2 ]); \ + vol2_l += (CzWINDOWEDFIR::lut[firidx+7]*(int)p[(poshi+8-4)*2 ]); \ + int vol_l = ((vol1_l>>1)+(vol2_l>>1)) >> (WFIR_16BITSHIFT-1); \ + int vol1_r = (CzWINDOWEDFIR::lut[firidx+0]*(int)p[(poshi+1-4)*2+1]); \ + vol1_r += (CzWINDOWEDFIR::lut[firidx+1]*(int)p[(poshi+2-4)*2+1]); \ + vol1_r += (CzWINDOWEDFIR::lut[firidx+2]*(int)p[(poshi+3-4)*2+1]); \ + vol1_r += (CzWINDOWEDFIR::lut[firidx+3]*(int)p[(poshi+4-4)*2+1]); \ + int vol2_r = (CzWINDOWEDFIR::lut[firidx+4]*(int)p[(poshi+5-4)*2+1]); \ + vol2_r += (CzWINDOWEDFIR::lut[firidx+5]*(int)p[(poshi+6-4)*2+1]); \ + vol2_r += (CzWINDOWEDFIR::lut[firidx+6]*(int)p[(poshi+7-4)*2+1]); \ + vol2_r += (CzWINDOWEDFIR::lut[firidx+7]*(int)p[(poshi+8-4)*2+1]); \ + int vol_r = ((vol1_r>>1)+(vol2_r>>1)) >> (WFIR_16BITSHIFT-1); + +///////////////////////////////////////////////////////////////////////////// + +#define SNDMIX_STOREMONOVOL\ + pvol[0] += vol * pChn->nRightVol;\ + pvol[1] += vol * pChn->nLeftVol;\ + pvol += 2; + +#define SNDMIX_STORESTEREOVOL\ + pvol[0] += vol_l * pChn->nRightVol;\ + pvol[1] += vol_r * pChn->nLeftVol;\ + pvol += 2; + +#define SNDMIX_STOREFASTMONOVOL\ + int v = vol * pChn->nRightVol;\ + pvol[0] += v;\ + pvol[1] += v;\ + pvol += 2; + +#define SNDMIX_RAMPMONOVOL\ + nRampLeftVol += pChn->nLeftRamp;\ + nRampRightVol += pChn->nRightRamp;\ + pvol[0] += vol * (nRampRightVol >> VOLUMERAMPPRECISION);\ + pvol[1] += vol * (nRampLeftVol >> VOLUMERAMPPRECISION);\ + pvol += 2; + +#define SNDMIX_RAMPFASTMONOVOL\ + nRampRightVol += pChn->nRightRamp;\ + int fastvol = vol * (nRampRightVol >> VOLUMERAMPPRECISION);\ + pvol[0] += fastvol;\ + pvol[1] += fastvol;\ + pvol += 2; + +#define SNDMIX_RAMPSTEREOVOL\ + nRampLeftVol += pChn->nLeftRamp;\ + nRampRightVol += pChn->nRightRamp;\ + pvol[0] += vol_l * (nRampRightVol >> VOLUMERAMPPRECISION);\ + pvol[1] += vol_r * (nRampLeftVol >> VOLUMERAMPPRECISION);\ + pvol += 2; + + +/////////////////////////////////////////////////// +// Resonant Filters + +// Mono +#define MIX_BEGIN_FILTER\ + int fy1 = pChannel->nFilter_Y1;\ + int fy2 = pChannel->nFilter_Y2;\ + +#define MIX_END_FILTER\ + pChannel->nFilter_Y1 = fy1;\ + pChannel->nFilter_Y2 = fy2; + +#define SNDMIX_PROCESSFILTER\ + vol = (vol * pChn->nFilter_A0 + fy1 * pChn->nFilter_B0 + fy2 * pChn->nFilter_B1 + 4096) >> 13;\ + fy2 = fy1;\ + fy1 = vol;\ + +// Stereo +#define MIX_BEGIN_STEREO_FILTER\ + int fy1 = pChannel->nFilter_Y1;\ + int fy2 = pChannel->nFilter_Y2;\ + int fy3 = pChannel->nFilter_Y3;\ + int fy4 = pChannel->nFilter_Y4;\ + +#define MIX_END_STEREO_FILTER\ + pChannel->nFilter_Y1 = fy1;\ + pChannel->nFilter_Y2 = fy2;\ + pChannel->nFilter_Y3 = fy3;\ + pChannel->nFilter_Y4 = fy4;\ + +#define SNDMIX_PROCESSSTEREOFILTER\ + vol_l = (vol_l * pChn->nFilter_A0 + fy1 * pChn->nFilter_B0 + fy2 * pChn->nFilter_B1 + 4096) >> 13;\ + vol_r = (vol_r * pChn->nFilter_A0 + fy3 * pChn->nFilter_B0 + fy4 * pChn->nFilter_B1 + 4096) >> 13;\ + fy2 = fy1; fy1 = vol_l;\ + fy4 = fy3; fy3 = vol_r;\ + +////////////////////////////////////////////////////////// +// Interfaces + +typedef VOID (MPPASMCALL * LPMIXINTERFACE)(MODCHANNEL *, int *, int *); + +#define BEGIN_MIX_INTERFACE(func)\ + VOID MPPASMCALL func(MODCHANNEL *pChannel, int *pbuffer, int *pbufmax)\ + {\ + LONG nPos; + +#define END_MIX_INTERFACE()\ + SNDMIX_ENDSAMPLELOOP\ + } + +// Volume Ramps +#define BEGIN_RAMPMIX_INTERFACE(func)\ + BEGIN_MIX_INTERFACE(func)\ + LONG nRampRightVol = pChannel->nRampRightVol;\ + LONG nRampLeftVol = pChannel->nRampLeftVol; + +#define END_RAMPMIX_INTERFACE()\ + SNDMIX_ENDSAMPLELOOP\ + pChannel->nRampRightVol = nRampRightVol;\ + pChannel->nRightVol = nRampRightVol >> VOLUMERAMPPRECISION;\ + pChannel->nRampLeftVol = nRampLeftVol;\ + pChannel->nLeftVol = nRampLeftVol >> VOLUMERAMPPRECISION;\ + } + +#define BEGIN_FASTRAMPMIX_INTERFACE(func)\ + BEGIN_MIX_INTERFACE(func)\ + LONG nRampRightVol = pChannel->nRampRightVol; + +#define END_FASTRAMPMIX_INTERFACE()\ + SNDMIX_ENDSAMPLELOOP\ + pChannel->nRampRightVol = nRampRightVol;\ + pChannel->nRampLeftVol = nRampRightVol;\ + pChannel->nRightVol = nRampRightVol >> VOLUMERAMPPRECISION;\ + pChannel->nLeftVol = pChannel->nRightVol;\ + } + + +// Mono Resonant Filters +#define BEGIN_MIX_FLT_INTERFACE(func)\ + BEGIN_MIX_INTERFACE(func)\ + MIX_BEGIN_FILTER + + +#define END_MIX_FLT_INTERFACE()\ + SNDMIX_ENDSAMPLELOOP\ + MIX_END_FILTER\ + } + +#define BEGIN_RAMPMIX_FLT_INTERFACE(func)\ + BEGIN_MIX_INTERFACE(func)\ + LONG nRampRightVol = pChannel->nRampRightVol;\ + LONG nRampLeftVol = pChannel->nRampLeftVol;\ + MIX_BEGIN_FILTER + +#define END_RAMPMIX_FLT_INTERFACE()\ + SNDMIX_ENDSAMPLELOOP\ + MIX_END_FILTER\ + pChannel->nRampRightVol = nRampRightVol;\ + pChannel->nRightVol = nRampRightVol >> VOLUMERAMPPRECISION;\ + pChannel->nRampLeftVol = nRampLeftVol;\ + pChannel->nLeftVol = nRampLeftVol >> VOLUMERAMPPRECISION;\ + } + +// Stereo Resonant Filters +#define BEGIN_MIX_STFLT_INTERFACE(func)\ + BEGIN_MIX_INTERFACE(func)\ + MIX_BEGIN_STEREO_FILTER + + +#define END_MIX_STFLT_INTERFACE()\ + SNDMIX_ENDSAMPLELOOP\ + MIX_END_STEREO_FILTER\ + } + +#define BEGIN_RAMPMIX_STFLT_INTERFACE(func)\ + BEGIN_MIX_INTERFACE(func)\ + LONG nRampRightVol = pChannel->nRampRightVol;\ + LONG nRampLeftVol = pChannel->nRampLeftVol;\ + MIX_BEGIN_STEREO_FILTER + +#define END_RAMPMIX_STFLT_INTERFACE()\ + SNDMIX_ENDSAMPLELOOP\ + MIX_END_STEREO_FILTER\ + pChannel->nRampRightVol = nRampRightVol;\ + pChannel->nRightVol = nRampRightVol >> VOLUMERAMPPRECISION;\ + pChannel->nRampLeftVol = nRampLeftVol;\ + pChannel->nLeftVol = nRampLeftVol >> VOLUMERAMPPRECISION;\ + } + + +///////////////////////////////////////////////////// +// + +void MPPASMCALL X86_InitMixBuffer(int *pBuffer, UINT nSamples); +void MPPASMCALL X86_EndChannelOfs(MODCHANNEL *pChannel, int *pBuffer, UINT nSamples); +void MPPASMCALL X86_StereoFill(int *pBuffer, UINT nSamples, LPLONG lpROfs, LPLONG lpLOfs); +void X86_StereoMixToFloat(const int *, float *, float *, UINT nCount); +void X86_FloatToStereoMix(const float *pIn1, const float *pIn2, int *pOut, UINT nCount); + +///////////////////////////////////////////////////// +// Mono samples functions + +BEGIN_MIX_INTERFACE(Mono8BitMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8NOIDO + SNDMIX_STOREMONOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(Mono16BitMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16NOIDO + SNDMIX_STOREMONOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(Mono8BitLinearMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8LINEAR + SNDMIX_STOREMONOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(Mono16BitLinearMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16LINEAR + SNDMIX_STOREMONOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(Mono8BitSplineMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8SPLINE + SNDMIX_STOREMONOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(Mono16BitSplineMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16SPLINE + SNDMIX_STOREMONOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(Mono8BitFirFilterMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8FIRFILTER + SNDMIX_STOREMONOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(Mono16BitFirFilterMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16FIRFILTER + SNDMIX_STOREMONOVOL +END_MIX_INTERFACE() + + +// Volume Ramps +BEGIN_RAMPMIX_INTERFACE(Mono8BitRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8NOIDO + SNDMIX_RAMPMONOVOL +END_RAMPMIX_INTERFACE() + +BEGIN_RAMPMIX_INTERFACE(Mono16BitRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16NOIDO + SNDMIX_RAMPMONOVOL +END_RAMPMIX_INTERFACE() + +BEGIN_RAMPMIX_INTERFACE(Mono8BitLinearRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8LINEAR + SNDMIX_RAMPMONOVOL +END_RAMPMIX_INTERFACE() + +BEGIN_RAMPMIX_INTERFACE(Mono16BitLinearRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16LINEAR + SNDMIX_RAMPMONOVOL +END_RAMPMIX_INTERFACE() + +BEGIN_RAMPMIX_INTERFACE(Mono8BitSplineRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8SPLINE + SNDMIX_RAMPMONOVOL +END_RAMPMIX_INTERFACE() + +BEGIN_RAMPMIX_INTERFACE(Mono16BitSplineRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16SPLINE + SNDMIX_RAMPMONOVOL +END_RAMPMIX_INTERFACE() + +BEGIN_RAMPMIX_INTERFACE(Mono8BitFirFilterRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8FIRFILTER + SNDMIX_RAMPMONOVOL +END_RAMPMIX_INTERFACE() + +BEGIN_RAMPMIX_INTERFACE(Mono16BitFirFilterRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16FIRFILTER + SNDMIX_RAMPMONOVOL +END_RAMPMIX_INTERFACE() + + +////////////////////////////////////////////////////// +// Fast mono mix for leftvol=rightvol (1 less imul) + +BEGIN_MIX_INTERFACE(FastMono8BitMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8NOIDO + SNDMIX_STOREFASTMONOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(FastMono16BitMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16NOIDO + SNDMIX_STOREFASTMONOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(FastMono8BitLinearMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8LINEAR + SNDMIX_STOREFASTMONOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(FastMono16BitLinearMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16LINEAR + SNDMIX_STOREFASTMONOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(FastMono8BitSplineMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8SPLINE + SNDMIX_STOREFASTMONOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(FastMono16BitSplineMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16SPLINE + SNDMIX_STOREFASTMONOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(FastMono8BitFirFilterMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8FIRFILTER + SNDMIX_STOREFASTMONOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(FastMono16BitFirFilterMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16FIRFILTER + SNDMIX_STOREFASTMONOVOL +END_MIX_INTERFACE() + + +// Fast Ramps +BEGIN_FASTRAMPMIX_INTERFACE(FastMono8BitRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8NOIDO + SNDMIX_RAMPFASTMONOVOL +END_FASTRAMPMIX_INTERFACE() + +BEGIN_FASTRAMPMIX_INTERFACE(FastMono16BitRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16NOIDO + SNDMIX_RAMPFASTMONOVOL +END_FASTRAMPMIX_INTERFACE() + +BEGIN_FASTRAMPMIX_INTERFACE(FastMono8BitLinearRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8LINEAR + SNDMIX_RAMPFASTMONOVOL +END_FASTRAMPMIX_INTERFACE() + +BEGIN_FASTRAMPMIX_INTERFACE(FastMono16BitLinearRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16LINEAR + SNDMIX_RAMPFASTMONOVOL +END_FASTRAMPMIX_INTERFACE() + +BEGIN_FASTRAMPMIX_INTERFACE(FastMono8BitSplineRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8SPLINE + SNDMIX_RAMPFASTMONOVOL +END_FASTRAMPMIX_INTERFACE() + +BEGIN_FASTRAMPMIX_INTERFACE(FastMono16BitSplineRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16SPLINE + SNDMIX_RAMPFASTMONOVOL +END_FASTRAMPMIX_INTERFACE() + +BEGIN_FASTRAMPMIX_INTERFACE(FastMono8BitFirFilterRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8FIRFILTER + SNDMIX_RAMPFASTMONOVOL +END_FASTRAMPMIX_INTERFACE() + +BEGIN_FASTRAMPMIX_INTERFACE(FastMono16BitFirFilterRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16FIRFILTER + SNDMIX_RAMPFASTMONOVOL +END_FASTRAMPMIX_INTERFACE() + + +////////////////////////////////////////////////////// +// Stereo samples + +BEGIN_MIX_INTERFACE(Stereo8BitMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8NOIDO + SNDMIX_STORESTEREOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(Stereo16BitMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16NOIDO + SNDMIX_STORESTEREOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(Stereo8BitLinearMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8LINEAR + SNDMIX_STORESTEREOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(Stereo16BitLinearMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16LINEAR + SNDMIX_STORESTEREOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(Stereo8BitSplineMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8SPLINE + SNDMIX_STORESTEREOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(Stereo16BitSplineMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16SPLINE + SNDMIX_STORESTEREOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(Stereo8BitFirFilterMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8FIRFILTER + SNDMIX_STORESTEREOVOL +END_MIX_INTERFACE() + +BEGIN_MIX_INTERFACE(Stereo16BitFirFilterMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16FIRFILTER + SNDMIX_STORESTEREOVOL +END_MIX_INTERFACE() + + +// Volume Ramps +BEGIN_RAMPMIX_INTERFACE(Stereo8BitRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8NOIDO + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_INTERFACE() + +BEGIN_RAMPMIX_INTERFACE(Stereo16BitRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16NOIDO + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_INTERFACE() + +BEGIN_RAMPMIX_INTERFACE(Stereo8BitLinearRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8LINEAR + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_INTERFACE() + +BEGIN_RAMPMIX_INTERFACE(Stereo16BitLinearRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16LINEAR + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_INTERFACE() + +BEGIN_RAMPMIX_INTERFACE(Stereo8BitSplineRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8SPLINE + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_INTERFACE() + +BEGIN_RAMPMIX_INTERFACE(Stereo16BitSplineRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16SPLINE + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_INTERFACE() + +BEGIN_RAMPMIX_INTERFACE(Stereo8BitFirFilterRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8FIRFILTER + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_INTERFACE() + +BEGIN_RAMPMIX_INTERFACE(Stereo16BitFirFilterRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16FIRFILTER + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_INTERFACE() + + + +////////////////////////////////////////////////////// +// Resonant Filter Mix + +#ifndef NO_FILTER + +// Mono Filter Mix +BEGIN_MIX_FLT_INTERFACE(FilterMono8BitMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8NOIDO + SNDMIX_PROCESSFILTER + SNDMIX_STOREMONOVOL +END_MIX_FLT_INTERFACE() + +BEGIN_MIX_FLT_INTERFACE(FilterMono16BitMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16NOIDO + SNDMIX_PROCESSFILTER + SNDMIX_STOREMONOVOL +END_MIX_FLT_INTERFACE() + +BEGIN_MIX_FLT_INTERFACE(FilterMono8BitLinearMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8LINEAR + SNDMIX_PROCESSFILTER + SNDMIX_STOREMONOVOL +END_MIX_FLT_INTERFACE() + +BEGIN_MIX_FLT_INTERFACE(FilterMono16BitLinearMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16LINEAR + SNDMIX_PROCESSFILTER + SNDMIX_STOREMONOVOL +END_MIX_FLT_INTERFACE() + +BEGIN_MIX_FLT_INTERFACE(FilterMono8BitSplineMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8SPLINE + SNDMIX_PROCESSFILTER + SNDMIX_STOREMONOVOL +END_MIX_FLT_INTERFACE() + +BEGIN_MIX_FLT_INTERFACE(FilterMono16BitSplineMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16SPLINE + SNDMIX_PROCESSFILTER + SNDMIX_STOREMONOVOL +END_MIX_FLT_INTERFACE() + +BEGIN_MIX_FLT_INTERFACE(FilterMono8BitFirFilterMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8FIRFILTER + SNDMIX_PROCESSFILTER + SNDMIX_STOREMONOVOL +END_MIX_FLT_INTERFACE() + +BEGIN_MIX_FLT_INTERFACE(FilterMono16BitFirFilterMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16FIRFILTER + SNDMIX_PROCESSFILTER + SNDMIX_STOREMONOVOL +END_MIX_FLT_INTERFACE() + +// Filter + Ramp +BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono8BitRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8NOIDO + SNDMIX_PROCESSFILTER + SNDMIX_RAMPMONOVOL +END_RAMPMIX_FLT_INTERFACE() + +BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono16BitRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16NOIDO + SNDMIX_PROCESSFILTER + SNDMIX_RAMPMONOVOL +END_RAMPMIX_FLT_INTERFACE() + +BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono8BitLinearRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8LINEAR + SNDMIX_PROCESSFILTER + SNDMIX_RAMPMONOVOL +END_RAMPMIX_FLT_INTERFACE() + +BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono16BitLinearRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16LINEAR + SNDMIX_PROCESSFILTER + SNDMIX_RAMPMONOVOL +END_RAMPMIX_FLT_INTERFACE() + +BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono8BitSplineRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8SPLINE + SNDMIX_PROCESSFILTER + SNDMIX_RAMPMONOVOL +END_RAMPMIX_FLT_INTERFACE() + +BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono16BitSplineRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16SPLINE + SNDMIX_PROCESSFILTER + SNDMIX_RAMPMONOVOL +END_RAMPMIX_FLT_INTERFACE() + +BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono8BitFirFilterRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETMONOVOL8FIRFILTER + SNDMIX_PROCESSFILTER + SNDMIX_RAMPMONOVOL +END_RAMPMIX_FLT_INTERFACE() + +BEGIN_RAMPMIX_FLT_INTERFACE(FilterMono16BitFirFilterRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETMONOVOL16FIRFILTER + SNDMIX_PROCESSFILTER + SNDMIX_RAMPMONOVOL +END_RAMPMIX_FLT_INTERFACE() + + +// Stereo Filter Mix +BEGIN_MIX_STFLT_INTERFACE(FilterStereo8BitMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8NOIDO + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_STORESTEREOVOL +END_MIX_STFLT_INTERFACE() + +BEGIN_MIX_STFLT_INTERFACE(FilterStereo16BitMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16NOIDO + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_STORESTEREOVOL +END_MIX_STFLT_INTERFACE() + +BEGIN_MIX_STFLT_INTERFACE(FilterStereo8BitLinearMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8LINEAR + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_STORESTEREOVOL +END_MIX_STFLT_INTERFACE() + +BEGIN_MIX_STFLT_INTERFACE(FilterStereo16BitLinearMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16LINEAR + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_STORESTEREOVOL +END_MIX_STFLT_INTERFACE() + +BEGIN_MIX_STFLT_INTERFACE(FilterStereo8BitSplineMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8SPLINE + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_STORESTEREOVOL +END_MIX_STFLT_INTERFACE() + +BEGIN_MIX_STFLT_INTERFACE(FilterStereo16BitSplineMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16SPLINE + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_STORESTEREOVOL +END_MIX_STFLT_INTERFACE() + +BEGIN_MIX_STFLT_INTERFACE(FilterStereo8BitFirFilterMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8FIRFILTER + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_STORESTEREOVOL +END_MIX_STFLT_INTERFACE() + +BEGIN_MIX_STFLT_INTERFACE(FilterStereo16BitFirFilterMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16FIRFILTER + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_STORESTEREOVOL +END_MIX_STFLT_INTERFACE() + +// Stereo Filter + Ramp +BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo8BitRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8NOIDO + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_STFLT_INTERFACE() + +BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo16BitRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16NOIDO + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_STFLT_INTERFACE() + +BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo8BitLinearRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8LINEAR + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_STFLT_INTERFACE() + +BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo16BitLinearRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16LINEAR + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_STFLT_INTERFACE() + +BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo8BitSplineRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8SPLINE + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_STFLT_INTERFACE() + +BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo16BitSplineRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16SPLINE + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_STFLT_INTERFACE() + +BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo8BitFirFilterRampMix) + SNDMIX_BEGINSAMPLELOOP8 + SNDMIX_GETSTEREOVOL8FIRFILTER + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_STFLT_INTERFACE() + +BEGIN_RAMPMIX_STFLT_INTERFACE(FilterStereo16BitFirFilterRampMix) + SNDMIX_BEGINSAMPLELOOP16 + SNDMIX_GETSTEREOVOL16FIRFILTER + SNDMIX_PROCESSSTEREOFILTER + SNDMIX_RAMPSTEREOVOL +END_RAMPMIX_STFLT_INTERFACE() + + +#else +// Mono +#define FilterMono8BitMix Mono8BitMix +#define FilterMono16BitMix Mono16BitMix +#define FilterMono8BitLinearMix Mono8BitLinearMix +#define FilterMono16BitLinearMix Mono16BitLinearMix +#define FilterMono8BitSplineMix Mono8BitSplineMix +#define FilterMono16BitSplineMix Mono16BitSplineMix +#define FilterMono8BitFirFilterMix Mono8BitFirFilterMix +#define FilterMono16BitFirFilterMix Mono16BitFirFilterMix +#define FilterMono8BitRampMix Mono8BitRampMix +#define FilterMono16BitRampMix Mono16BitRampMix +#define FilterMono8BitLinearRampMix Mono8BitLinearRampMix +#define FilterMono16BitLinearRampMix Mono16BitLinearRampMix +#define FilterMono8BitSplineRampMix Mono8BitSplineRampMix +#define FilterMono16BitSplineRampMix Mono16BitSplineRampMix +#define FilterMono8BitFirFilterRampMix Mono8BitFirFilterRampMix +#define FilterMono16BitFirFilterRampMix Mono16BitFirFilterRampMix +// Stereo +#define FilterStereo8BitMix Stereo8BitMix +#define FilterStereo16BitMix Stereo16BitMix +#define FilterStereo8BitLinearMix Stereo8BitLinearMix +#define FilterStereo16BitLinearMix Stereo16BitLinearMix +#define FilterStereo8BitSplineMix Stereo8BitSplineMix +#define FilterStereo16BitSplineMix Stereo16BitSplineMix +#define FilterStereo8BitFirFilterMix Stereo8BitFirFilterMix +#define FilterStereo16BitFirFilterMix Stereo16BitFirFilterMix +#define FilterStereo8BitRampMix Stereo8BitRampMix +#define FilterStereo16BitRampMix Stereo16BitRampMix +#define FilterStereo8BitLinearRampMix Stereo8BitLinearRampMix +#define FilterStereo16BitLinearRampMix Stereo16BitLinearRampMix +#define FilterStereo8BitSplineRampMix Stereo8BitSplineRampMix +#define FilterStereo16BitSplineRampMix Stereo16BitSplineRampMix +#define FilterStereo8BitFirFilterRampMix Stereo8BitFirFilterRampMix +#define FilterStereo16BitFirFilterRampMix Stereo16BitFirFilterRampMix + +#endif + +/////////////////////////////////////////////////////////////////////////////// +// +// Mix function tables +// +// +// Index is as follow: +// [b1-b0] format (8-bit-mono, 16-bit-mono, 8-bit-stereo, 16-bit-stereo) +// [b2] ramp +// [b3] filter +// [b5-b4] src type +// + +#define MIXNDX_16BIT 0x01 +#define MIXNDX_STEREO 0x02 +#define MIXNDX_RAMP 0x04 +#define MIXNDX_FILTER 0x08 +#define MIXNDX_LINEARSRC 0x10 +#define MIXNDX_SPLINESRC 0x20 +#define MIXNDX_FIRSRC 0x30 + +const LPMIXINTERFACE gpMixFunctionTable[2*2*16] = +{ + // No SRC + Mono8BitMix, Mono16BitMix, Stereo8BitMix, Stereo16BitMix, + Mono8BitRampMix, Mono16BitRampMix, Stereo8BitRampMix, + Stereo16BitRampMix, + // No SRC, Filter + FilterMono8BitMix, FilterMono16BitMix, FilterStereo8BitMix, + FilterStereo16BitMix, FilterMono8BitRampMix, FilterMono16BitRampMix, + FilterStereo8BitRampMix, FilterStereo16BitRampMix, + // Linear SRC + Mono8BitLinearMix, Mono16BitLinearMix, Stereo8BitLinearMix, + Stereo16BitLinearMix, Mono8BitLinearRampMix, Mono16BitLinearRampMix, + Stereo8BitLinearRampMix,Stereo16BitLinearRampMix, + // Linear SRC, Filter + FilterMono8BitLinearMix, FilterMono16BitLinearMix, + FilterStereo8BitLinearMix, FilterStereo16BitLinearMix, + FilterMono8BitLinearRampMix, FilterMono16BitLinearRampMix, + FilterStereo8BitLinearRampMix, FilterStereo16BitLinearRampMix, + + // FirFilter SRC + Mono8BitSplineMix, Mono16BitSplineMix, Stereo8BitSplineMix, + Stereo16BitSplineMix, Mono8BitSplineRampMix, Mono16BitSplineRampMix, + Stereo8BitSplineRampMix,Stereo16BitSplineRampMix, + // Spline SRC, Filter + FilterMono8BitSplineMix, FilterMono16BitSplineMix, + FilterStereo8BitSplineMix, FilterStereo16BitSplineMix, + FilterMono8BitSplineRampMix, FilterMono16BitSplineRampMix, + FilterStereo8BitSplineRampMix, FilterStereo16BitSplineRampMix, + + // FirFilter SRC + Mono8BitFirFilterMix, Mono16BitFirFilterMix, Stereo8BitFirFilterMix, + Stereo16BitFirFilterMix, Mono8BitFirFilterRampMix, + Mono16BitFirFilterRampMix, Stereo8BitFirFilterRampMix, + Stereo16BitFirFilterRampMix, + // FirFilter SRC, Filter + FilterMono8BitFirFilterMix, FilterMono16BitFirFilterMix, + FilterStereo8BitFirFilterMix, FilterStereo16BitFirFilterMix, + FilterMono8BitFirFilterRampMix, FilterMono16BitFirFilterRampMix, + FilterStereo8BitFirFilterRampMix, FilterStereo16BitFirFilterRampMix +}; + +const LPMIXINTERFACE gpFastMixFunctionTable[2*2*16] = +{ + // No SRC + FastMono8BitMix, FastMono16BitMix, Stereo8BitMix, Stereo16BitMix, + FastMono8BitRampMix, FastMono16BitRampMix, Stereo8BitRampMix, + Stereo16BitRampMix, + // No SRC, Filter + FilterMono8BitMix, FilterMono16BitMix, FilterStereo8BitMix, + FilterStereo16BitMix, FilterMono8BitRampMix, FilterMono16BitRampMix, + FilterStereo8BitRampMix, FilterStereo16BitRampMix, + // Linear SRC + FastMono8BitLinearMix, FastMono16BitLinearMix, Stereo8BitLinearMix, + Stereo16BitLinearMix, FastMono8BitLinearRampMix, + FastMono16BitLinearRampMix, Stereo8BitLinearRampMix, + Stereo16BitLinearRampMix, + // Linear SRC, Filter + FilterMono8BitLinearMix, FilterMono16BitLinearMix, + FilterStereo8BitLinearMix, FilterStereo16BitLinearMix, + FilterMono8BitLinearRampMix, FilterMono16BitLinearRampMix, + FilterStereo8BitLinearRampMix, FilterStereo16BitLinearRampMix, + + // Spline SRC + Mono8BitSplineMix, Mono16BitSplineMix, Stereo8BitSplineMix, + Stereo16BitSplineMix, Mono8BitSplineRampMix, Mono16BitSplineRampMix, + Stereo8BitSplineRampMix, Stereo16BitSplineRampMix, + // Spline SRC, Filter + FilterMono8BitSplineMix, FilterMono16BitSplineMix, + FilterStereo8BitSplineMix, FilterStereo16BitSplineMix, + FilterMono8BitSplineRampMix, FilterMono16BitSplineRampMix, + FilterStereo8BitSplineRampMix, FilterStereo16BitSplineRampMix, + + // FirFilter SRC + Mono8BitFirFilterMix, Mono16BitFirFilterMix, Stereo8BitFirFilterMix, + Stereo16BitFirFilterMix, Mono8BitFirFilterRampMix, + Mono16BitFirFilterRampMix, Stereo8BitFirFilterRampMix, + Stereo16BitFirFilterRampMix, + // FirFilter SRC, Filter + FilterMono8BitFirFilterMix, FilterMono16BitFirFilterMix, + FilterStereo8BitFirFilterMix, FilterStereo16BitFirFilterMix, + FilterMono8BitFirFilterRampMix, FilterMono16BitFirFilterRampMix, + FilterStereo8BitFirFilterRampMix, FilterStereo16BitFirFilterRampMix, +}; + + +///////////////////////////////////////////////////////////////////////// + +static LONG MPPFASTCALL GetSampleCount(MODCHANNEL *pChn, LONG nSamples) +//--------------------------------------------------------------------- +{ + LONG nLoopStart = (pChn->dwFlags & CHN_LOOP) ? pChn->nLoopStart : 0; + LONG nInc = pChn->nInc; + + if ((nSamples <= 0) || (!nInc) || (!pChn->nLength)) return 0; + // Under zero ? + if ((LONG)pChn->nPos < nLoopStart) + { + if (nInc < 0) + { + // Invert loop for bidi loops + LONG nDelta = ((nLoopStart - pChn->nPos) << 16) - (pChn->nPosLo & 0xffff); + pChn->nPos = nLoopStart | (nDelta>>16); + pChn->nPosLo = nDelta & 0xffff; + if (((LONG)pChn->nPos < nLoopStart) || + (pChn->nPos >= (nLoopStart+pChn->nLength)/2)) + { + pChn->nPos = nLoopStart; pChn->nPosLo = 0; + } + nInc = -nInc; + pChn->nInc = nInc; + pChn->dwFlags &= ~(CHN_PINGPONGFLAG); // go forward + if ((!(pChn->dwFlags & CHN_LOOP)) || (pChn->nPos >= pChn->nLength)) + { + pChn->nPos = pChn->nLength; + pChn->nPosLo = 0; + return 0; + } + } else + { + // We probably didn't hit the loop end yet + // (first loop), so we do nothing + if ((LONG)pChn->nPos < 0) pChn->nPos = 0; + } + } else + // Past the end + if (pChn->nPos >= pChn->nLength) + { + if (!(pChn->dwFlags & CHN_LOOP)) return 0; // not looping -> stop this channel + if (pChn->dwFlags & CHN_PINGPONGLOOP) + { + // Invert loop + if (nInc > 0) + { + nInc = -nInc; + pChn->nInc = nInc; + } + pChn->dwFlags |= CHN_PINGPONGFLAG; + // adjust loop position + LONG nDeltaHi = (pChn->nPos - pChn->nLength); + LONG nDeltaLo = 0x10000 - (pChn->nPosLo & 0xffff); + pChn->nPos = pChn->nLength - nDeltaHi - (nDeltaLo>>16); + pChn->nPosLo = nDeltaLo & 0xffff; + if ((pChn->nPos <= pChn->nLoopStart) || + (pChn->nPos >= pChn->nLength)) + pChn->nPos = pChn->nLength-1; + } else + { + if (nInc < 0) // This is a bug + { + nInc = -nInc; + pChn->nInc = nInc; + } + // Restart at loop start + pChn->nPos += nLoopStart - pChn->nLength; + if ((LONG)pChn->nPos < nLoopStart) + pChn->nPos = pChn->nLoopStart; + } + } + LONG nPos = pChn->nPos; + // too big increment, and/or too small loop length + if (nPos < nLoopStart) + { + if ((nPos < 0) || (nInc < 0)) return 0; + } + if ((nPos < 0) || (nPos >= (LONG)pChn->nLength)) return 0; + LONG nPosLo = (USHORT)pChn->nPosLo, nSmpCount = nSamples; + if (nInc < 0) + { + LONG nInv = -nInc; + LONG maxsamples = 16384 / ((nInv>>16)+1); + if (maxsamples < 2) maxsamples = 2; + if (nSamples > maxsamples) nSamples = maxsamples; + LONG nDeltaHi = (nInv>>16) * (nSamples - 1); + LONG nDeltaLo = (nInv&0xffff) * (nSamples - 1); + LONG nPosDest = nPos - nDeltaHi + ((nPosLo - nDeltaLo) >> 16); + if (nPosDest < nLoopStart) + { + nSmpCount = (ULONG)(((((LONGLONG)nPos - nLoopStart) << 16) + nPosLo - 1) / nInv) + 1; + } + } else + { + LONG maxsamples = 16384 / ((nInc>>16)+1); + if (maxsamples < 2) maxsamples = 2; + if (nSamples > maxsamples) nSamples = maxsamples; + LONG nDeltaHi = (nInc>>16) * (nSamples - 1); + LONG nDeltaLo = (nInc&0xffff) * (nSamples - 1); + LONG nPosDest = nPos + nDeltaHi + ((nPosLo + nDeltaLo)>>16); + if (nPosDest >= (LONG)pChn->nLength) + { + nSmpCount = (ULONG)(((((LONGLONG)pChn->nLength - nPos) << 16) - nPosLo - 1) / nInc) + 1; + } + } + if (nSmpCount <= 1) return 1; + if (nSmpCount > nSamples) return nSamples; + return nSmpCount; +} + + +UINT CSoundFile::CreateStereoMix(int count) +//----------------------------------------- +{ + LPLONG pOfsL, pOfsR; + DWORD nchused, nchmixed; + + if (!count) return 0; +#ifndef MODPLUG_FASTSOUNDLIB + if (gnChannels > 2) X86_InitMixBuffer(MixRearBuffer, count*2); +#endif + nchused = nchmixed = 0; + for (UINT nChn=0; nChn<m_nMixChannels; nChn++) + { + const LPMIXINTERFACE *pMixFuncTable; + MODCHANNEL * const pChannel = &Chn[ChnMix[nChn]]; + UINT nFlags, nMasterCh; + LONG nSmpCount; + int nsamples; + int *pbuffer; + + if (!pChannel->pCurrentSample) continue; + nMasterCh = (ChnMix[nChn] < m_nChannels) ? ChnMix[nChn]+1 : pChannel->nMasterChn; + pOfsR = &gnDryROfsVol; + pOfsL = &gnDryLOfsVol; + nFlags = 0; + if (pChannel->dwFlags & CHN_16BIT) nFlags |= MIXNDX_16BIT; + if (pChannel->dwFlags & CHN_STEREO) nFlags |= MIXNDX_STEREO; + #ifndef NO_FILTER + if (pChannel->dwFlags & CHN_FILTER) nFlags |= MIXNDX_FILTER; + #endif + if (!(pChannel->dwFlags & CHN_NOIDO)) + { + // use hq-fir mixer? + if( (gdwSoundSetup & (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE)) == + (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE) ) + nFlags += MIXNDX_FIRSRC; + else if( (gdwSoundSetup & (SNDMIX_HQRESAMPLER)) == SNDMIX_HQRESAMPLER ) + nFlags += MIXNDX_SPLINESRC; + else + nFlags += MIXNDX_LINEARSRC; // use + } + if ((nFlags < 0x40) && (pChannel->nLeftVol == pChannel->nRightVol) + && ((!pChannel->nRampLength) || (pChannel->nLeftRamp == pChannel->nRightRamp))) + { + pMixFuncTable = gpFastMixFunctionTable; + } else + { + pMixFuncTable = gpMixFunctionTable; + } + nsamples = count; +#ifndef MODPLUG_NO_REVERB + pbuffer = (gdwSoundSetup & SNDMIX_REVERB) ? MixReverbBuffer : MixSoundBuffer; + if (pChannel->dwFlags & CHN_NOREVERB) pbuffer = MixSoundBuffer; + if (pChannel->dwFlags & CHN_REVERB) pbuffer = MixReverbBuffer; + if (pbuffer == MixReverbBuffer) + { + if (!gnReverbSend) memset(MixReverbBuffer, 0, count * 8); + gnReverbSend += count; + } +#else + pbuffer = MixSoundBuffer; +#endif + nchused++; + //////////////////////////////////////////////////// + SampleLooping: + UINT nrampsamples = nsamples; + if (pChannel->nRampLength > 0) + { + if ((LONG)nrampsamples > pChannel->nRampLength) nrampsamples = pChannel->nRampLength; + } + if ((nSmpCount = GetSampleCount(pChannel, nrampsamples)) <= 0) + { + // Stopping the channel + pChannel->pCurrentSample = NULL; + pChannel->nLength = 0; + pChannel->nPos = 0; + pChannel->nPosLo = 0; + pChannel->nRampLength = 0; + X86_EndChannelOfs(pChannel, pbuffer, nsamples); + *pOfsR += pChannel->nROfs; + *pOfsL += pChannel->nLOfs; + pChannel->nROfs = pChannel->nLOfs = 0; + pChannel->dwFlags &= ~CHN_PINGPONGFLAG; + continue; + } + // Should we mix this channel ? + UINT naddmix; + if (((nchmixed >= m_nMaxMixChannels) && (!(gdwSoundSetup & SNDMIX_DIRECTTODISK))) + || ((!pChannel->nRampLength) && (!(pChannel->nLeftVol|pChannel->nRightVol)))) + { + LONG delta = (pChannel->nInc * (LONG)nSmpCount) + (LONG)pChannel->nPosLo; + pChannel->nPosLo = delta & 0xFFFF; + pChannel->nPos += (delta >> 16); + pChannel->nROfs = pChannel->nLOfs = 0; + pbuffer += nSmpCount*2; + naddmix = 0; + } else + // Do mixing + { + // Choose function for mixing + LPMIXINTERFACE pMixFunc; + pMixFunc = (pChannel->nRampLength) ? pMixFuncTable[nFlags|MIXNDX_RAMP] : pMixFuncTable[nFlags]; + int *pbufmax = pbuffer + (nSmpCount*2); + pChannel->nROfs = - *(pbufmax-2); + pChannel->nLOfs = - *(pbufmax-1); + pMixFunc(pChannel, pbuffer, pbufmax); + pChannel->nROfs += *(pbufmax-2); + pChannel->nLOfs += *(pbufmax-1); + pbuffer = pbufmax; + naddmix = 1; + + } + nsamples -= nSmpCount; + if (pChannel->nRampLength) + { + pChannel->nRampLength -= nSmpCount; + if (pChannel->nRampLength <= 0) + { + pChannel->nRampLength = 0; + pChannel->nRightVol = pChannel->nNewRightVol; + pChannel->nLeftVol = pChannel->nNewLeftVol; + pChannel->nRightRamp = pChannel->nLeftRamp = 0; + if ((pChannel->dwFlags & CHN_NOTEFADE) && (!(pChannel->nFadeOutVol))) + { + pChannel->nLength = 0; + pChannel->pCurrentSample = NULL; + } + } + } + if (nsamples > 0) goto SampleLooping; + nchmixed += naddmix; + } + return nchused; +} + + +#ifdef MSC_VER +#pragma warning (disable:4100) +#endif + +// Clip and convert to 8 bit +#ifdef MSC_VER +__declspec(naked) DWORD MPPASMCALL X86_Convert32To8(LPVOID lp16, int *pBuffer, DWORD lSampleCount, LPLONG lpMin, LPLONG lpMax) +//------------------------------------------------------------------------------ +{ + _asm { + push ebx + push esi + push edi + mov ebx, 16[esp] // ebx = 8-bit buffer + mov esi, 20[esp] // esi = pBuffer + mov edi, 24[esp] // edi = lSampleCount + mov eax, 28[esp] + mov ecx, dword ptr [eax] // ecx = clipmin + mov eax, 32[esp] + mov edx, dword ptr [eax] // edx = clipmax +cliploop: + mov eax, dword ptr [esi] + inc ebx + cdq + and edx, (1 << (24-MIXING_ATTENUATION)) - 1 + add eax, edx + cmp eax, MIXING_CLIPMIN + jl cliplow + cmp eax, MIXING_CLIPMAX + jg cliphigh + cmp eax, ecx + jl updatemin + cmp eax, edx + jg updatemax +cliprecover: + add esi, 4 + sar eax, 24-MIXING_ATTENUATION + xor eax, 0x80 + dec edi + mov byte ptr [ebx-1], al + jnz cliploop + mov eax, 28[esp] + mov dword ptr [eax], ecx + mov eax, 32[esp] + mov dword ptr [eax], edx + mov eax, 24[esp] + pop edi + pop esi + pop ebx + ret +updatemin: + mov ecx, eax + jmp cliprecover +updatemax: + mov edx, eax + jmp cliprecover +cliplow: + mov ecx, MIXING_CLIPMIN + mov edx, MIXING_CLIPMAX + mov eax, MIXING_CLIPMIN + jmp cliprecover +cliphigh: + mov ecx, MIXING_CLIPMIN + mov edx, MIXING_CLIPMAX + mov eax, MIXING_CLIPMAX + jmp cliprecover + } +} +#else //MSC_VER +//---GCCFIX: Asm replaced with C function +// The C version was written by Rani Assaf <rani@magic.metawire.com>, I believe +DWORD MPPASMCALL X86_Convert32To8(LPVOID lp8, int *pBuffer, DWORD lSampleCount, LPLONG lpMin, LPLONG lpMax) +{ + int vumin = *lpMin, vumax = *lpMax; + unsigned char *p = (unsigned char *)lp8; + for (UINT i=0; i<lSampleCount; i++) + { + int n = pBuffer[i]; + if (n < MIXING_CLIPMIN) + n = MIXING_CLIPMIN; + else if (n > MIXING_CLIPMAX) + n = MIXING_CLIPMAX; + if (n < vumin) + vumin = n; + else if (n > vumax) + vumax = n; + p[i] = (n >> (24-MIXING_ATTENUATION)) ^ 0x80; // 8-bit unsigned + } + *lpMin = vumin; + *lpMax = vumax; + return lSampleCount; +} +#endif //MSC_VER, else + + +#ifdef MSC_VER +// Clip and convert to 16 bit +__declspec(naked) DWORD MPPASMCALL X86_Convert32To16(LPVOID lp16, int *pBuffer, DWORD lSampleCount, LPLONG lpMin, LPLONG lpMax) +//------------------------------------------------------------------------------ +{ + _asm { + push ebx + push esi + push edi + mov ebx, 16[esp] // ebx = 16-bit buffer + mov eax, 28[esp] + mov esi, 20[esp] // esi = pBuffer + mov ecx, dword ptr [eax] // ecx = clipmin + mov edi, 24[esp] // edi = lSampleCount + mov eax, 32[esp] + push ebp + mov ebp, dword ptr [eax] // edx = clipmax +cliploop: + mov eax, dword ptr [esi] + add ebx, 2 + cdq + and edx, (1 << (16-MIXING_ATTENUATION)) - 1 + add esi, 4 + add eax, edx + cmp eax, MIXING_CLIPMIN + jl cliplow + cmp eax, MIXING_CLIPMAX + jg cliphigh + cmp eax, ecx + jl updatemin + cmp eax, ebp + jg updatemax +cliprecover: + sar eax, 16-MIXING_ATTENUATION + dec edi + mov word ptr [ebx-2], ax + jnz cliploop + mov edx, ebp + pop ebp + mov eax, 28[esp] + mov dword ptr [eax], ecx + mov eax, 32[esp] + mov dword ptr [eax], edx + mov eax, 24[esp] + pop edi + shl eax, 1 + pop esi + pop ebx + ret +updatemin: + mov ecx, eax + jmp cliprecover +updatemax: + mov ebp, eax + jmp cliprecover +cliplow: + mov ecx, MIXING_CLIPMIN + mov ebp, MIXING_CLIPMAX + mov eax, MIXING_CLIPMIN + jmp cliprecover +cliphigh: + mov ecx, MIXING_CLIPMIN + mov ebp, MIXING_CLIPMAX + mov eax, MIXING_CLIPMAX + jmp cliprecover + } +} +#else //MSC_VER +//---GCCFIX: Asm replaced with C function +// The C version was written by Rani Assaf <rani@magic.metawire.com>, I believe +DWORD MPPASMCALL X86_Convert32To16(LPVOID lp16, int *pBuffer, DWORD lSampleCount, LPLONG lpMin, LPLONG lpMax) +{ + int vumin = *lpMin, vumax = *lpMax; + signed short *p = (signed short *)lp16; + for (UINT i=0; i<lSampleCount; i++) + { + int n = pBuffer[i]; + if (n < MIXING_CLIPMIN) + n = MIXING_CLIPMIN; + else if (n > MIXING_CLIPMAX) + n = MIXING_CLIPMAX; + if (n < vumin) + vumin = n; + else if (n > vumax) + vumax = n; + p[i] = n >> (16-MIXING_ATTENUATION); // 16-bit signed + } + *lpMin = vumin; + *lpMax = vumax; + return lSampleCount * 2; +} +#endif //MSC_VER, else + +#ifdef MSC_VER +// Clip and convert to 24 bit +__declspec(naked) DWORD MPPASMCALL X86_Convert32To24(LPVOID lp16, int *pBuffer, DWORD lSampleCount, LPLONG lpMin, LPLONG lpMax) +//------------------------------------------------------------------------------ +{ + _asm { + push ebx + push esi + push edi + mov ebx, 16[esp] // ebx = 8-bit buffer + mov esi, 20[esp] // esi = pBuffer + mov edi, 24[esp] // edi = lSampleCount + mov eax, 28[esp] + mov ecx, dword ptr [eax] // ecx = clipmin + mov eax, 32[esp] + push ebp + mov edx, dword ptr [eax] // edx = clipmax +cliploop: + mov eax, dword ptr [esi] + mov ebp, eax + sar ebp, 31 + and ebp, (1 << (8-MIXING_ATTENUATION)) - 1 + add eax, ebp + cmp eax, MIXING_CLIPMIN + jl cliplow + cmp eax, MIXING_CLIPMAX + jg cliphigh + cmp eax, ecx + jl updatemin + cmp eax, edx + jg updatemax +cliprecover: + add ebx, 3 + sar eax, 8-MIXING_ATTENUATION + add esi, 4 + mov word ptr [ebx-3], ax + shr eax, 16 + dec edi + mov byte ptr [ebx-1], al + jnz cliploop + pop ebp + mov eax, 28[esp] + mov dword ptr [eax], ecx + mov eax, 32[esp] + mov dword ptr [eax], edx + mov edx, 24[esp] + mov eax, edx + pop edi + shl eax, 1 + pop esi + add eax, edx + pop ebx + ret +updatemin: + mov ecx, eax + jmp cliprecover +updatemax: + mov edx, eax + jmp cliprecover +cliplow: + mov ecx, MIXING_CLIPMIN + mov edx, MIXING_CLIPMAX + mov eax, MIXING_CLIPMIN + jmp cliprecover +cliphigh: + mov ecx, MIXING_CLIPMIN + mov edx, MIXING_CLIPMAX + mov eax, MIXING_CLIPMAX + jmp cliprecover + } +} +#else //MSC_VER +//---GCCFIX: Asm replaced with C function +DWORD MPPASMCALL X86_Convert32To24(LPVOID lp16, int *pBuffer, DWORD lSampleCount, LPLONG lpMin, LPLONG lpMax) +{ + UINT i ; + int vumin = *lpMin, vumax = *lpMax; + int n,p ; + unsigned char* buf = (unsigned char*)lp16 ; + + for ( i=0; i<lSampleCount; i++) + { + n = pBuffer[i]; + if (n < MIXING_CLIPMIN) + n = MIXING_CLIPMIN; + else if (n > MIXING_CLIPMAX) + n = MIXING_CLIPMAX; + if (n < vumin) + vumin = n; + else if (n > vumax) + vumax = n; + p = n >> (8-MIXING_ATTENUATION) ; // 24-bit signed +#ifdef WORDS_BIGENDIAN + buf[i*3+0] = p & 0xFF0000 >> 24; + buf[i*3+1] = p & 0x00FF00 >> 16 ; + buf[i*3+2] = p & 0x0000FF ; +#else + buf[i*3+0] = p & 0x0000FF ; + buf[i*3+1] = p & 0x00FF00 >> 16; + buf[i*3+2] = p & 0xFF0000 >> 24; +#endif + } + *lpMin = vumin; + *lpMax = vumax; + return lSampleCount * 3; +} +#endif + +#ifdef MSC_VER +// Clip and convert to 32 bit +__declspec(naked) DWORD MPPASMCALL X86_Convert32To32(LPVOID lp16, int *pBuffer, DWORD lSampleCount, LPLONG lpMin, LPLONG lpMax) +//------------------------------------------------------------------------------ +{ + _asm { + push ebx + push esi + push edi + mov ebx, 16[esp] // ebx = 32-bit buffer + mov esi, 20[esp] // esi = pBuffer + mov edi, 24[esp] // edi = lSampleCount + mov eax, 28[esp] + mov ecx, dword ptr [eax] // ecx = clipmin + mov eax, 32[esp] + mov edx, dword ptr [eax] // edx = clipmax +cliploop: + mov eax, dword ptr [esi] + add ebx, 4 + add esi, 4 + cmp eax, MIXING_CLIPMIN + jl cliplow + cmp eax, MIXING_CLIPMAX + jg cliphigh + cmp eax, ecx + jl updatemin + cmp eax, edx + jg updatemax +cliprecover: + shl eax, MIXING_ATTENUATION + dec edi + mov dword ptr [ebx-4], eax + jnz cliploop + mov eax, 28[esp] + mov dword ptr [eax], ecx + mov eax, 32[esp] + mov dword ptr [eax], edx + mov edx, 24[esp] + pop edi + mov eax, edx + pop esi + shl eax, 2 + pop ebx + ret +updatemin: + mov ecx, eax + jmp cliprecover +updatemax: + mov edx, eax + jmp cliprecover +cliplow: + mov ecx, MIXING_CLIPMIN + mov edx, MIXING_CLIPMAX + mov eax, MIXING_CLIPMIN + jmp cliprecover +cliphigh: + mov ecx, MIXING_CLIPMIN + mov edx, MIXING_CLIPMAX + mov eax, MIXING_CLIPMAX + jmp cliprecover + } +} +#else +//---GCCFIX: Asm replaced with C function +DWORD MPPASMCALL X86_Convert32To32(LPVOID lp16, int *pBuffer, DWORD lSampleCount, LPLONG lpMin, LPLONG lpMax) +{ + UINT i ; + int vumin = *lpMin, vumax = *lpMax; + signed long *p = (signed long *)lp16; + + for ( i=0; i<lSampleCount; i++) + { + int n = pBuffer[i]; + if (n < MIXING_CLIPMIN) + n = MIXING_CLIPMIN; + else if (n > MIXING_CLIPMAX) + n = MIXING_CLIPMAX; + if (n < vumin) + vumin = n; + else if (n > vumax) + vumax = n; + p[i] = n << MIXING_ATTENUATION; // 32-bit signed + } + *lpMin = vumin; + *lpMax = vumax; + return lSampleCount * 4; +} +#endif + + +#ifdef MSC_VER +void MPPASMCALL X86_InitMixBuffer(int *pBuffer, UINT nSamples) +//------------------------------------------------------------ +{ + _asm { + mov ecx, nSamples + mov esi, pBuffer + xor eax, eax + mov edx, ecx + shr ecx, 2 + and edx, 3 + jz unroll4x +loop1x: + add esi, 4 + dec edx + mov dword ptr [esi-4], eax + jnz loop1x +unroll4x: + or ecx, ecx + jnz loop4x + jmp done +loop4x: + add esi, 16 + dec ecx + mov dword ptr [esi-16], eax + mov dword ptr [esi-12], eax + mov dword ptr [esi-8], eax + mov dword ptr [esi-4], eax + jnz loop4x +done:; + } +} +#else +//---GCCFIX: Asm replaced with C function +// Will fill in later. +void MPPASMCALL X86_InitMixBuffer(int *pBuffer, UINT nSamples) +{ + memset(pBuffer, 0, nSamples * sizeof(int)); +} +#endif + + +#ifdef MSC_VER +__declspec(naked) void MPPASMCALL X86_InterleaveFrontRear(int *pFrontBuf, int *pRearBuf, DWORD nSamples) +//------------------------------------------------------------------------------ +{ + _asm { + push ebx + push ebp + push esi + push edi + mov ecx, 28[esp] // ecx = samplecount + mov esi, 20[esp] // esi = front buffer + mov edi, 24[esp] // edi = rear buffer + lea esi, [esi+ecx*4] // esi = &front[N] + lea edi, [edi+ecx*4] // edi = &rear[N] + lea ebx, [esi+ecx*4] // ebx = &front[N*2] +interleaveloop: + mov eax, dword ptr [esi-8] + mov edx, dword ptr [esi-4] + sub ebx, 16 + mov ebp, dword ptr [edi-8] + mov dword ptr [ebx], eax + mov dword ptr [ebx+4], edx + mov eax, dword ptr [edi-4] + sub esi, 8 + sub edi, 8 + dec ecx + mov dword ptr [ebx+8], ebp + mov dword ptr [ebx+12], eax + jnz interleaveloop + pop edi + pop esi + pop ebp + pop ebx + ret + } +} +#else +//---GCCFIX: Asm replaced with C function +// Multichannel not supported. +void MPPASMCALL X86_InterleaveFrontRear(int *pFrontBuf, int *pRearBuf, DWORD nSamples) +{ +} +#endif + + +#ifdef MSC_VER +VOID MPPASMCALL X86_MonoFromStereo(int *pMixBuf, UINT nSamples) +//------------------------------------------------------------- +{ + _asm { + mov ecx, nSamples + mov esi, pMixBuf + mov edi, esi +stloop: + mov eax, dword ptr [esi] + mov edx, dword ptr [esi+4] + add edi, 4 + add esi, 8 + add eax, edx + sar eax, 1 + dec ecx + mov dword ptr [edi-4], eax + jnz stloop + } +} +#else +//---GCCFIX: Asm replaced with C function +VOID MPPASMCALL X86_MonoFromStereo(int *pMixBuf, UINT nSamples) +{ + UINT j; + for(UINT i = 0; i < nSamples; i++) + { + j = i << 1; + pMixBuf[i] = (pMixBuf[j] + pMixBuf[j + 1]) >> 1; + } +} +#endif + +#define OFSDECAYSHIFT 8 +#define OFSDECAYMASK 0xFF + + +#ifdef MSC_VER +void MPPASMCALL X86_StereoFill(int *pBuffer, UINT nSamples, LPLONG lpROfs, LPLONG lpLOfs) +//------------------------------------------------------------------------------ +{ + _asm { + mov edi, pBuffer + mov ecx, nSamples + mov eax, lpROfs + mov edx, lpLOfs + mov eax, [eax] + mov edx, [edx] + or ecx, ecx + jz fill_loop + mov ebx, eax + or ebx, edx + jz fill_loop +ofsloop: + mov ebx, eax + mov esi, edx + neg ebx + neg esi + sar ebx, 31 + sar esi, 31 + and ebx, OFSDECAYMASK + and esi, OFSDECAYMASK + add ebx, eax + add esi, edx + sar ebx, OFSDECAYSHIFT + sar esi, OFSDECAYSHIFT + sub eax, ebx + sub edx, esi + mov ebx, eax + or ebx, edx + jz fill_loop + add edi, 8 + dec ecx + mov [edi-8], eax + mov [edi-4], edx + jnz ofsloop +fill_loop: + mov ebx, ecx + and ebx, 3 + jz fill4x +fill1x: + mov [edi], eax + mov [edi+4], edx + add edi, 8 + dec ebx + jnz fill1x +fill4x: + shr ecx, 2 + or ecx, ecx + jz done +fill4xloop: + mov [edi], eax + mov [edi+4], edx + mov [edi+8], eax + mov [edi+12], edx + add edi, 8*4 + dec ecx + mov [edi-16], eax + mov [edi-12], edx + mov [edi-8], eax + mov [edi-4], edx + jnz fill4xloop +done: + mov esi, lpROfs + mov edi, lpLOfs + mov [esi], eax + mov [edi], edx + } +} +#else +//---GCCFIX: Asm replaced with C function +#define OFSDECAYSHIFT 8 +#define OFSDECAYMASK 0xFF +void MPPASMCALL X86_StereoFill(int *pBuffer, UINT nSamples, LPLONG lpROfs, LPLONG lpLOfs) +//---------------------------------------------------------------------------- +{ + int rofs = *lpROfs; + int lofs = *lpLOfs; + + if ((!rofs) && (!lofs)) + { + X86_InitMixBuffer(pBuffer, nSamples*2); + return; + } + for (UINT i=0; i<nSamples; i++) + { + int x_r = (rofs + (((-rofs)>>31) & OFSDECAYMASK)) >> OFSDECAYSHIFT; + int x_l = (lofs + (((-lofs)>>31) & OFSDECAYMASK)) >> OFSDECAYSHIFT; + rofs -= x_r; + lofs -= x_l; + pBuffer[i*2] = x_r; + pBuffer[i*2+1] = x_l; + } + *lpROfs = rofs; + *lpLOfs = lofs; +} +#endif + +#ifdef MSC_VER +void MPPASMCALL X86_EndChannelOfs(MODCHANNEL *pChannel, int *pBuffer, UINT nSamples) +//------------------------------------------------------------------------------ +{ + _asm { + mov esi, pChannel + mov edi, pBuffer + mov ecx, nSamples + mov eax, dword ptr [esi+MODCHANNEL.nROfs] + mov edx, dword ptr [esi+MODCHANNEL.nLOfs] + or ecx, ecx + jz brkloop +ofsloop: + mov ebx, eax + mov esi, edx + neg ebx + neg esi + sar ebx, 31 + sar esi, 31 + and ebx, OFSDECAYMASK + and esi, OFSDECAYMASK + add ebx, eax + add esi, edx + sar ebx, OFSDECAYSHIFT + sar esi, OFSDECAYSHIFT + sub eax, ebx + sub edx, esi + mov ebx, eax + add dword ptr [edi], eax + add dword ptr [edi+4], edx + or ebx, edx + jz brkloop + add edi, 8 + dec ecx + jnz ofsloop +brkloop: + mov esi, pChannel + mov dword ptr [esi+MODCHANNEL.nROfs], eax + mov dword ptr [esi+MODCHANNEL.nLOfs], edx + } +} +#else +//---GCCFIX: Asm replaced with C function +// Will fill in later. +void MPPASMCALL X86_EndChannelOfs(MODCHANNEL *pChannel, int *pBuffer, UINT nSamples) +{ + int rofs = pChannel->nROfs; + int lofs = pChannel->nLOfs; + + if ((!rofs) && (!lofs)) return; + for (UINT i=0; i<nSamples; i++) + { + int x_r = (rofs + (((-rofs)>>31) & OFSDECAYMASK)) >> OFSDECAYSHIFT; + int x_l = (lofs + (((-lofs)>>31) & OFSDECAYMASK)) >> OFSDECAYSHIFT; + rofs -= x_r; + lofs -= x_l; + pBuffer[i*2] += x_r; + pBuffer[i*2+1] += x_l; + } + pChannel->nROfs = rofs; + pChannel->nLOfs = lofs; +} +#endif + + +////////////////////////////////////////////////////////////////////////////////// +// Automatic Gain Control + +#ifndef NO_AGC + +// Limiter +#define MIXING_LIMITMAX (0x08100000) +#define MIXING_LIMITMIN (-MIXING_LIMITMAX) + +#ifdef MSC_VER +__declspec(naked) UINT MPPASMCALL X86_AGC(int *pBuffer, UINT nSamples, UINT nAGC) +//------------------------------------------------------------------------------ +{ + __asm { + push ebx + push ebp + push esi + push edi + mov esi, 20[esp] // esi = pBuffer+i + mov ecx, 24[esp] // ecx = i + mov edi, 28[esp] // edi = AGC (0..256) +agcloop: + mov eax, dword ptr [esi] + imul edi + shrd eax, edx, AGC_PRECISION + add esi, 4 + cmp eax, MIXING_LIMITMIN + jl agcupdate + cmp eax, MIXING_LIMITMAX + jg agcupdate +agcrecover: + dec ecx + mov dword ptr [esi-4], eax + jnz agcloop + mov eax, edi + pop edi + pop esi + pop ebp + pop ebx + ret +agcupdate: + dec edi + jmp agcrecover + } +} + +#pragma warning (default:4100) +#else +// Version for GCC +UINT MPPASMCALL X86_AGC(int *pBuffer, UINT nSamples, UINT nAGC) +{ + int x; + + while(nSamples) + { + x = ((long long int)(*pBuffer) * nAGC) >> AGC_PRECISION; + + if((x < MIXING_LIMITMIN) || (x > MIXING_LIMITMAX)) + nAGC--; + + *pBuffer = x; + + pBuffer++; + nSamples--; + } + + return nAGC; +} +#endif + +void CSoundFile::ProcessAGC(int count) +//------------------------------------ +{ + static DWORD gAGCRecoverCount = 0; + UINT agc = X86_AGC(MixSoundBuffer, count, gnAGC); + // Some kind custom law, so that the AGC stays quite stable, but slowly + // goes back up if the sound level stays below a level inversely + // proportional to the AGC level. (J'me comprends) + if ((agc >= gnAGC) && (gnAGC < AGC_UNITY) && (gnVUMeter < (0xFF - (gnAGC >> (AGC_PRECISION-7))) )) + { + gAGCRecoverCount += count; + UINT agctimeout = gdwMixingFreq + gnAGC; + if (gnChannels >= 2) agctimeout <<= 1; + if (gAGCRecoverCount >= agctimeout) + { + gAGCRecoverCount = 0; + gnAGC++; + } + } else + { + gnAGC = agc; + gAGCRecoverCount = 0; + } +} + + + +void CSoundFile::ResetAGC() +//------------------------- +{ + gnAGC = AGC_UNITY; +} + +#endif // NO_AGC diff --git a/lib/libmodplug/src/libmodplug/it_defs.h b/lib/libmodplug/src/libmodplug/it_defs.h new file mode 100644 index 0000000000..89cb456515 --- /dev/null +++ b/lib/libmodplug/src/libmodplug/it_defs.h @@ -0,0 +1,134 @@ +#ifndef _ITDEFS_H_ +#define _ITDEFS_H_ + +#pragma pack(1) + +typedef struct tagITFILEHEADER +{ + DWORD id; // 0x4D504D49 + CHAR songname[26]; + WORD reserved1; // 0x1004 + WORD ordnum; + WORD insnum; + WORD smpnum; + WORD patnum; + WORD cwtv; + WORD cmwt; + WORD flags; + WORD special; + BYTE globalvol; + BYTE mv; + BYTE speed; + BYTE tempo; + BYTE sep; + BYTE zero; + WORD msglength; + DWORD msgoffset; + DWORD reserved2; + BYTE chnpan[64]; + BYTE chnvol[64]; +} ITFILEHEADER; + + +typedef struct tagITENVELOPE +{ + BYTE flags; + BYTE num; + BYTE lpb; + BYTE lpe; + BYTE slb; + BYTE sle; + BYTE data[25*3]; + BYTE reserved; +} ITENVELOPE; + +// Old Impulse Instrument Format (cmwt < 0x200) +typedef struct tagITOLDINSTRUMENT +{ + DWORD id; // IMPI = 0x49504D49 + CHAR filename[12]; // DOS file name + BYTE zero; + BYTE flags; + BYTE vls; + BYTE vle; + BYTE sls; + BYTE sle; + WORD reserved1; + WORD fadeout; + BYTE nna; + BYTE dnc; + WORD trkvers; + BYTE nos; + BYTE reserved2; + CHAR name[26]; + WORD reserved3[3]; + BYTE keyboard[240]; + BYTE volenv[200]; + BYTE nodes[50]; +} ITOLDINSTRUMENT; + + +// Impulse Instrument Format +typedef struct tagITINSTRUMENT +{ + DWORD id; + CHAR filename[12]; + BYTE zero; + BYTE nna; + BYTE dct; + BYTE dca; + WORD fadeout; + signed char pps; + BYTE ppc; + BYTE gbv; + BYTE dfp; + BYTE rv; + BYTE rp; + WORD trkvers; + BYTE nos; + BYTE reserved1; + CHAR name[26]; + BYTE ifc; + BYTE ifr; + BYTE mch; + BYTE mpr; + WORD mbank; + BYTE keyboard[240]; + ITENVELOPE volenv; + ITENVELOPE panenv; + ITENVELOPE pitchenv; + BYTE dummy[4]; // was 7, but IT v2.17 saves 554 bytes +} ITINSTRUMENT; + + +// IT Sample Format +typedef struct ITSAMPLESTRUCT +{ + DWORD id; // 0x53504D49 + CHAR filename[12]; + BYTE zero; + BYTE gvl; + BYTE flags; + BYTE vol; + CHAR name[26]; + BYTE cvt; + BYTE dfp; + DWORD length; + DWORD loopbegin; + DWORD loopend; + DWORD C5Speed; + DWORD susloopbegin; + DWORD susloopend; + DWORD samplepointer; + BYTE vis; + BYTE vid; + BYTE vir; + BYTE vit; +} ITSAMPLESTRUCT; + +#pragma pack() + +extern BYTE autovibit2xm[8]; +extern BYTE autovibxm2it[8]; + +#endif diff --git a/lib/libmodplug/src/libmodplug/sndfile.h b/lib/libmodplug/src/libmodplug/sndfile.h new file mode 100644 index 0000000000..a6663205b9 --- /dev/null +++ b/lib/libmodplug/src/libmodplug/sndfile.h @@ -0,0 +1,1016 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net>, + * Adam Goode <adam@evdebs.org> (endian and char fixes for PPC) +*/ + +#if defined(HAVE_CONFIG_H) && !defined(CONFIG_H_INCLUDED) +#include "config.h" +#define CONFIG_H_INCLUDED 1 +#endif + +#ifndef __SNDFILE_H +#define __SNDFILE_H + +#ifdef UNDER_CE +int _strnicmp(const char *str1,const char *str2, int n); +#endif + +#ifndef LPCBYTE +typedef const BYTE * LPCBYTE; +#endif + +#define MOD_AMIGAC2 0x1AB +#define MAX_SAMPLE_LENGTH 16000000 +#define MAX_SAMPLE_RATE 192000 +#define MAX_ORDERS 256 +#define MAX_PATTERNS 240 +#define MAX_SAMPLES 240 +#define MAX_INSTRUMENTS MAX_SAMPLES +#ifdef MODPLUG_FASTSOUNDLIB +#define MAX_CHANNELS 80 +#else +#define MAX_CHANNELS 128 +#endif +#define MAX_BASECHANNELS 64 +#define MAX_ENVPOINTS 32 +#define MIN_PERIOD 0x0020 +#define MAX_PERIOD 0xFFFF +#define MAX_PATTERNNAME 32 +#define MAX_CHANNELNAME 20 +#define MAX_INFONAME 80 +#define MAX_EQ_BANDS 6 +#define MAX_MIXPLUGINS 8 + + +#define MOD_TYPE_NONE 0x00 +#define MOD_TYPE_MOD 0x01 +#define MOD_TYPE_S3M 0x02 +#define MOD_TYPE_XM 0x04 +#define MOD_TYPE_MED 0x08 +#define MOD_TYPE_MTM 0x10 +#define MOD_TYPE_IT 0x20 +#define MOD_TYPE_669 0x40 +#define MOD_TYPE_ULT 0x80 +#define MOD_TYPE_STM 0x100 +#define MOD_TYPE_FAR 0x200 +#define MOD_TYPE_WAV 0x400 +#define MOD_TYPE_AMF 0x800 +#define MOD_TYPE_AMS 0x1000 +#define MOD_TYPE_DSM 0x2000 +#define MOD_TYPE_MDL 0x4000 +#define MOD_TYPE_OKT 0x8000 +#define MOD_TYPE_MID 0x10000 +#define MOD_TYPE_DMF 0x20000 +#define MOD_TYPE_PTM 0x40000 +#define MOD_TYPE_DBM 0x80000 +#define MOD_TYPE_MT2 0x100000 +#define MOD_TYPE_AMF0 0x200000 +#define MOD_TYPE_PSM 0x400000 +#define MOD_TYPE_J2B 0x800000 +#define MOD_TYPE_ABC 0x1000000 +#define MOD_TYPE_PAT 0x2000000 +#define MOD_TYPE_UMX 0x80000000 // Fake type +#define MAX_MODTYPE 24 + + + +// Channel flags: +// Bits 0-7: Sample Flags +#define CHN_16BIT 0x01 +#define CHN_LOOP 0x02 +#define CHN_PINGPONGLOOP 0x04 +#define CHN_SUSTAINLOOP 0x08 +#define CHN_PINGPONGSUSTAIN 0x10 +#define CHN_PANNING 0x20 +#define CHN_STEREO 0x40 +#define CHN_PINGPONGFLAG 0x80 +// Bits 8-31: Channel Flags +#define CHN_MUTE 0x100 +#define CHN_KEYOFF 0x200 +#define CHN_NOTEFADE 0x400 +#define CHN_SURROUND 0x800 +#define CHN_NOIDO 0x1000 +#define CHN_HQSRC 0x2000 +#define CHN_FILTER 0x4000 +#define CHN_VOLUMERAMP 0x8000 +#define CHN_VIBRATO 0x10000 +#define CHN_TREMOLO 0x20000 +#define CHN_PANBRELLO 0x40000 +#define CHN_PORTAMENTO 0x80000 +#define CHN_GLISSANDO 0x100000 +#define CHN_VOLENV 0x200000 +#define CHN_PANENV 0x400000 +#define CHN_PITCHENV 0x800000 +#define CHN_FASTVOLRAMP 0x1000000 +#define CHN_EXTRALOUD 0x2000000 +#define CHN_REVERB 0x4000000 +#define CHN_NOREVERB 0x8000000 + + +#define ENV_VOLUME 0x0001 +#define ENV_VOLSUSTAIN 0x0002 +#define ENV_VOLLOOP 0x0004 +#define ENV_PANNING 0x0008 +#define ENV_PANSUSTAIN 0x0010 +#define ENV_PANLOOP 0x0020 +#define ENV_PITCH 0x0040 +#define ENV_PITCHSUSTAIN 0x0080 +#define ENV_PITCHLOOP 0x0100 +#define ENV_SETPANNING 0x0200 +#define ENV_FILTER 0x0400 +#define ENV_VOLCARRY 0x0800 +#define ENV_PANCARRY 0x1000 +#define ENV_PITCHCARRY 0x2000 + +#define CMD_NONE 0 +#define CMD_ARPEGGIO 1 +#define CMD_PORTAMENTOUP 2 +#define CMD_PORTAMENTODOWN 3 +#define CMD_TONEPORTAMENTO 4 +#define CMD_VIBRATO 5 +#define CMD_TONEPORTAVOL 6 +#define CMD_VIBRATOVOL 7 +#define CMD_TREMOLO 8 +#define CMD_PANNING8 9 +#define CMD_OFFSET 10 +#define CMD_VOLUMESLIDE 11 +#define CMD_POSITIONJUMP 12 +#define CMD_VOLUME 13 +#define CMD_PATTERNBREAK 14 +#define CMD_RETRIG 15 +#define CMD_SPEED 16 +#define CMD_TEMPO 17 +#define CMD_TREMOR 18 +#define CMD_MODCMDEX 19 +#define CMD_S3MCMDEX 20 +#define CMD_CHANNELVOLUME 21 +#define CMD_CHANNELVOLSLIDE 22 +#define CMD_GLOBALVOLUME 23 +#define CMD_GLOBALVOLSLIDE 24 +#define CMD_KEYOFF 25 +#define CMD_FINEVIBRATO 26 +#define CMD_PANBRELLO 27 +#define CMD_XFINEPORTAUPDOWN 28 +#define CMD_PANNINGSLIDE 29 +#define CMD_SETENVPOSITION 30 +#define CMD_MIDI 31 + + +// Volume Column commands +#define VOLCMD_VOLUME 1 +#define VOLCMD_PANNING 2 +#define VOLCMD_VOLSLIDEUP 3 +#define VOLCMD_VOLSLIDEDOWN 4 +#define VOLCMD_FINEVOLUP 5 +#define VOLCMD_FINEVOLDOWN 6 +#define VOLCMD_VIBRATOSPEED 7 +#define VOLCMD_VIBRATO 8 +#define VOLCMD_PANSLIDELEFT 9 +#define VOLCMD_PANSLIDERIGHT 10 +#define VOLCMD_TONEPORTAMENTO 11 +#define VOLCMD_PORTAUP 12 +#define VOLCMD_PORTADOWN 13 + +#define RSF_16BIT 0x04 +#define RSF_STEREO 0x08 + +#define RS_PCM8S 0 // 8-bit signed +#define RS_PCM8U 1 // 8-bit unsigned +#define RS_PCM8D 2 // 8-bit delta values +#define RS_ADPCM4 3 // 4-bit ADPCM-packed +#define RS_PCM16D 4 // 16-bit delta values +#define RS_PCM16S 5 // 16-bit signed +#define RS_PCM16U 6 // 16-bit unsigned +#define RS_PCM16M 7 // 16-bit motorola order +#define RS_STPCM8S (RS_PCM8S|RSF_STEREO) // stereo 8-bit signed +#define RS_STPCM8U (RS_PCM8U|RSF_STEREO) // stereo 8-bit unsigned +#define RS_STPCM8D (RS_PCM8D|RSF_STEREO) // stereo 8-bit delta values +#define RS_STPCM16S (RS_PCM16S|RSF_STEREO) // stereo 16-bit signed +#define RS_STPCM16U (RS_PCM16U|RSF_STEREO) // stereo 16-bit unsigned +#define RS_STPCM16D (RS_PCM16D|RSF_STEREO) // stereo 16-bit delta values +#define RS_STPCM16M (RS_PCM16M|RSF_STEREO) // stereo 16-bit signed big endian +// IT 2.14 compressed samples +#define RS_IT2148 0x10 +#define RS_IT21416 0x14 +#define RS_IT2158 0x12 +#define RS_IT21516 0x16 +// AMS Packed Samples +#define RS_AMS8 0x11 +#define RS_AMS16 0x15 +// DMF Huffman compression +#define RS_DMF8 0x13 +#define RS_DMF16 0x17 +// MDL Huffman compression +#define RS_MDL8 0x20 +#define RS_MDL16 0x24 +#define RS_PTM8DTO16 0x25 +// Stereo Interleaved Samples +#define RS_STIPCM8S (RS_PCM8S|0x40|RSF_STEREO) // stereo 8-bit signed +#define RS_STIPCM8U (RS_PCM8U|0x40|RSF_STEREO) // stereo 8-bit unsigned +#define RS_STIPCM16S (RS_PCM16S|0x40|RSF_STEREO) // stereo 16-bit signed +#define RS_STIPCM16U (RS_PCM16U|0x40|RSF_STEREO) // stereo 16-bit unsigned +#define RS_STIPCM16M (RS_PCM16M|0x40|RSF_STEREO) // stereo 16-bit signed big endian +// 24-bit signed +#define RS_PCM24S (RS_PCM16S|0x80) // mono 24-bit signed +#define RS_STIPCM24S (RS_PCM16S|0x80|RSF_STEREO) // stereo 24-bit signed +#define RS_PCM32S (RS_PCM16S|0xC0) // mono 24-bit signed +#define RS_STIPCM32S (RS_PCM16S|0xC0|RSF_STEREO) // stereo 24-bit signed + +// NNA types +#define NNA_NOTECUT 0 +#define NNA_CONTINUE 1 +#define NNA_NOTEOFF 2 +#define NNA_NOTEFADE 3 + +// DCT types +#define DCT_NONE 0 +#define DCT_NOTE 1 +#define DCT_SAMPLE 2 +#define DCT_INSTRUMENT 3 + +// DNA types +#define DNA_NOTECUT 0 +#define DNA_NOTEOFF 1 +#define DNA_NOTEFADE 2 + +// Mixer Hardware-Dependent features +#define SYSMIX_ENABLEMMX 0x01 +#define SYSMIX_WINDOWSNT 0x02 +#define SYSMIX_SLOWCPU 0x04 +#define SYSMIX_FASTCPU 0x08 + +// Module flags +#define SONG_EMBEDMIDICFG 0x0001 +#define SONG_FASTVOLSLIDES 0x0002 +#define SONG_ITOLDEFFECTS 0x0004 +#define SONG_ITCOMPATMODE 0x0008 +#define SONG_LINEARSLIDES 0x0010 +#define SONG_PATTERNLOOP 0x0020 +#define SONG_STEP 0x0040 +#define SONG_PAUSED 0x0080 +#define SONG_FADINGSONG 0x0100 +#define SONG_ENDREACHED 0x0200 +#define SONG_GLOBALFADE 0x0400 +#define SONG_CPUVERYHIGH 0x0800 +#define SONG_FIRSTTICK 0x1000 +#define SONG_MPTFILTERMODE 0x2000 +#define SONG_SURROUNDPAN 0x4000 +#define SONG_EXFILTERRANGE 0x8000 +#define SONG_AMIGALIMITS 0x10000 + +// Global Options (Renderer) +#define SNDMIX_REVERSESTEREO 0x0001 +#define SNDMIX_NOISEREDUCTION 0x0002 +#define SNDMIX_AGC 0x0004 +#define SNDMIX_NORESAMPLING 0x0008 +#define SNDMIX_HQRESAMPLER 0x0010 +#define SNDMIX_MEGABASS 0x0020 +#define SNDMIX_SURROUND 0x0040 +#define SNDMIX_REVERB 0x0080 +#define SNDMIX_EQ 0x0100 +#define SNDMIX_SOFTPANNING 0x0200 +#define SNDMIX_ULTRAHQSRCMODE 0x0400 +// Misc Flags (can safely be turned on or off) +#define SNDMIX_DIRECTTODISK 0x10000 +#define SNDMIX_ENABLEMMX 0x20000 +#define SNDMIX_NOBACKWARDJUMPS 0x40000 +#define SNDMIX_MAXDEFAULTPAN 0x80000 // Used by the MOD loader + + +// Reverb Types (GM2 Presets) +enum { + REVERBTYPE_SMALLROOM, + REVERBTYPE_MEDIUMROOM, + REVERBTYPE_LARGEROOM, + REVERBTYPE_SMALLHALL, + REVERBTYPE_MEDIUMHALL, + REVERBTYPE_LARGEHALL, + NUM_REVERBTYPES +}; + + +enum { + SRCMODE_NEAREST, + SRCMODE_LINEAR, + SRCMODE_SPLINE, + SRCMODE_POLYPHASE, + NUM_SRC_MODES +}; + + +// Sample Struct +typedef struct _MODINSTRUMENT +{ + UINT nLength,nLoopStart,nLoopEnd; + UINT nSustainStart, nSustainEnd; + signed char *pSample; + UINT nC4Speed; + WORD nPan; + WORD nVolume; + WORD nGlobalVol; + WORD uFlags; + signed char RelativeTone; + signed char nFineTune; + BYTE nVibType; + BYTE nVibSweep; + BYTE nVibDepth; + BYTE nVibRate; + CHAR name[22]; +} MODINSTRUMENT; + + +// Instrument Struct +typedef struct _INSTRUMENTHEADER +{ + UINT nFadeOut; + DWORD dwFlags; + WORD nGlobalVol; + WORD nPan; + WORD VolPoints[MAX_ENVPOINTS]; + WORD PanPoints[MAX_ENVPOINTS]; + WORD PitchPoints[MAX_ENVPOINTS]; + BYTE VolEnv[MAX_ENVPOINTS]; + BYTE PanEnv[MAX_ENVPOINTS]; + BYTE PitchEnv[MAX_ENVPOINTS]; + BYTE Keyboard[128]; + BYTE NoteMap[128]; + + BYTE nVolEnv; + BYTE nPanEnv; + BYTE nPitchEnv; + BYTE nVolLoopStart; + BYTE nVolLoopEnd; + BYTE nVolSustainBegin; + BYTE nVolSustainEnd; + BYTE nPanLoopStart; + BYTE nPanLoopEnd; + BYTE nPanSustainBegin; + BYTE nPanSustainEnd; + BYTE nPitchLoopStart; + BYTE nPitchLoopEnd; + BYTE nPitchSustainBegin; + BYTE nPitchSustainEnd; + BYTE nNNA; + BYTE nDCT; + BYTE nDNA; + BYTE nPanSwing; + BYTE nVolSwing; + BYTE nIFC; + BYTE nIFR; + WORD wMidiBank; + BYTE nMidiProgram; + BYTE nMidiChannel; + BYTE nMidiDrumKey; + signed char nPPS; + unsigned char nPPC; + CHAR name[32]; + CHAR filename[12]; +} INSTRUMENTHEADER; + + +// Channel Struct +typedef struct _MODCHANNEL +{ + // First 32-bytes: Most used mixing information: don't change it + signed char * pCurrentSample; + DWORD nPos; + DWORD nPosLo; // actually 16-bit + LONG nInc; // 16.16 + LONG nRightVol; + LONG nLeftVol; + LONG nRightRamp; + LONG nLeftRamp; + // 2nd cache line + DWORD nLength; + DWORD dwFlags; + DWORD nLoopStart; + DWORD nLoopEnd; + LONG nRampRightVol; + LONG nRampLeftVol; + LONG nFilter_Y1, nFilter_Y2, nFilter_Y3, nFilter_Y4; + LONG nFilter_A0, nFilter_B0, nFilter_B1; + LONG nROfs, nLOfs; + LONG nRampLength; + // Information not used in the mixer + signed char * pSample; + LONG nNewRightVol, nNewLeftVol; + LONG nRealVolume, nRealPan; + LONG nVolume, nPan, nFadeOutVol; + LONG nPeriod, nC4Speed, nPortamentoDest; + INSTRUMENTHEADER *pHeader; + MODINSTRUMENT *pInstrument; + DWORD nVolEnvPosition, nPanEnvPosition, nPitchEnvPosition; + DWORD nMasterChn, nVUMeter; + LONG nGlobalVol, nInsVol; + LONG nFineTune, nTranspose; + LONG nPortamentoSlide, nAutoVibDepth; + UINT nAutoVibPos, nVibratoPos, nTremoloPos, nPanbrelloPos; + // 16-bit members + signed short nVolSwing, nPanSwing; + // 8-bit members + BYTE nNote, nNNA; + BYTE nNewNote, nNewIns, nCommand, nArpeggio; + BYTE nOldVolumeSlide, nOldFineVolUpDown; + BYTE nOldPortaUpDown, nOldFinePortaUpDown; + BYTE nOldPanSlide, nOldChnVolSlide; + BYTE nVibratoType, nVibratoSpeed, nVibratoDepth; + BYTE nTremoloType, nTremoloSpeed, nTremoloDepth; + BYTE nPanbrelloType, nPanbrelloSpeed, nPanbrelloDepth; + BYTE nOldCmdEx, nOldVolParam, nOldTempo; + BYTE nOldOffset, nOldHiOffset; + BYTE nCutOff, nResonance; + BYTE nRetrigCount, nRetrigParam; + BYTE nTremorCount, nTremorParam; + BYTE nPatternLoop, nPatternLoopCount; + BYTE nRowNote, nRowInstr; + BYTE nRowVolCmd, nRowVolume; + BYTE nRowCommand, nRowParam; + BYTE nLeftVU, nRightVU; + BYTE nActiveMacro, nPadding; +} MODCHANNEL; + + +typedef struct _MODCHANNELSETTINGS +{ + UINT nPan; + UINT nVolume; + DWORD dwFlags; + UINT nMixPlugin; + char szName[MAX_CHANNELNAME]; // changed from CHAR +} MODCHANNELSETTINGS; + + +typedef struct _MODCOMMAND +{ + BYTE note; + BYTE instr; + BYTE volcmd; + BYTE command; + BYTE vol; + BYTE param; +} MODCOMMAND, *LPMODCOMMAND; + +//////////////////////////////////////////////////////////////////// +// Mix Plugins +#define MIXPLUG_MIXREADY 0x01 // Set when cleared + +class IMixPlugin +{ +public: + virtual ~IMixPlugin(); + virtual int AddRef() = 0; + virtual int Release() = 0; + virtual void SaveAllParameters() = 0; + virtual void RestoreAllParameters() = 0; + virtual void Process(float *pOutL, float *pOutR, unsigned long nSamples) = 0; + virtual void Init(unsigned long nFreq, int bReset) = 0; + virtual void MidiSend(DWORD dwMidiCode) = 0; + virtual void MidiCommand(UINT nMidiCh, UINT nMidiProg, UINT note, UINT vol) = 0; +}; + + +#define MIXPLUG_INPUTF_MASTEREFFECT 0x01 // Apply to master mix +#define MIXPLUG_INPUTF_BYPASS 0x02 // Bypass effect +#define MIXPLUG_INPUTF_WETMIX 0x04 // Wet Mix (dry added) + +typedef struct _SNDMIXPLUGINSTATE +{ + DWORD dwFlags; // MIXPLUG_XXXX + LONG nVolDecayL, nVolDecayR; // Buffer click removal + int *pMixBuffer; // Stereo effect send buffer + float *pOutBufferL; // Temp storage for int -> float conversion + float *pOutBufferR; +} SNDMIXPLUGINSTATE, *PSNDMIXPLUGINSTATE; + +typedef struct _SNDMIXPLUGININFO +{ + DWORD dwPluginId1; + DWORD dwPluginId2; + DWORD dwInputRouting; // MIXPLUG_INPUTF_XXXX + DWORD dwOutputRouting; // 0=mix 0x80+=fx + DWORD dwReserved[4]; // Reserved for routing info + CHAR szName[32]; + CHAR szLibraryName[64]; // original DLL name +} SNDMIXPLUGININFO, *PSNDMIXPLUGININFO; // Size should be 128 + +typedef struct _SNDMIXPLUGIN +{ + IMixPlugin *pMixPlugin; + PSNDMIXPLUGINSTATE pMixState; + ULONG nPluginDataSize; + PVOID pPluginData; + SNDMIXPLUGININFO Info; +} SNDMIXPLUGIN, *PSNDMIXPLUGIN; + +typedef BOOL (*PMIXPLUGINCREATEPROC)(PSNDMIXPLUGIN); + +//////////////////////////////////////////////////////////////////// + +enum { + MIDIOUT_START=0, + MIDIOUT_STOP, + MIDIOUT_TICK, + MIDIOUT_NOTEON, + MIDIOUT_NOTEOFF, + MIDIOUT_VOLUME, + MIDIOUT_PAN, + MIDIOUT_BANKSEL, + MIDIOUT_PROGRAM, +}; + + +typedef struct MODMIDICFG +{ + char szMidiGlb[9*32]; // changed from CHAR + char szMidiSFXExt[16*32]; // changed from CHAR + char szMidiZXXExt[128*32]; // changed from CHAR +} MODMIDICFG, *LPMODMIDICFG; + +#define NOTE_MAX 120 //Defines maximum notevalue as well as maximum number of notes. + +typedef VOID (* LPSNDMIXHOOKPROC)(int *, unsigned long, unsigned long); // buffer, samples, channels + + + +//============== +class CSoundFile +//============== +{ +public: // Static Members + static UINT m_nXBassDepth, m_nXBassRange; + static UINT m_nReverbDepth, m_nReverbDelay, gnReverbType; + static UINT m_nProLogicDepth, m_nProLogicDelay; + static UINT m_nStereoSeparation; + static UINT m_nMaxMixChannels; + static LONG m_nStreamVolume; + static DWORD gdwSysInfo, gdwSoundSetup, gdwMixingFreq, gnBitsPerSample, gnChannels; + static UINT gnAGC, gnVolumeRampSamples, gnVUMeter, gnCPUUsage; + static LPSNDMIXHOOKPROC gpSndMixHook; + static PMIXPLUGINCREATEPROC gpMixPluginCreateProc; + +public: // for Editing + MODCHANNEL Chn[MAX_CHANNELS]; // Channels + UINT ChnMix[MAX_CHANNELS]; // Channels to be mixed + MODINSTRUMENT Ins[MAX_SAMPLES]; // Instruments + INSTRUMENTHEADER *Headers[MAX_INSTRUMENTS]; // Instrument Headers + MODCHANNELSETTINGS ChnSettings[MAX_BASECHANNELS]; // Channels settings + MODCOMMAND *Patterns[MAX_PATTERNS]; // Patterns + WORD PatternSize[MAX_PATTERNS]; // Patterns Lengths + BYTE Order[MAX_ORDERS]; // Pattern Orders + MODMIDICFG m_MidiCfg; // Midi macro config table + SNDMIXPLUGIN m_MixPlugins[MAX_MIXPLUGINS]; // Mix plugins + UINT m_nDefaultSpeed, m_nDefaultTempo, m_nDefaultGlobalVolume; + DWORD m_dwSongFlags; // Song flags SONG_XXXX + UINT m_nChannels, m_nMixChannels, m_nMixStat, m_nBufferCount; + UINT m_nType, m_nSamples, m_nInstruments; + UINT m_nTickCount, m_nTotalCount, m_nPatternDelay, m_nFrameDelay; + UINT m_nMusicSpeed, m_nMusicTempo; + UINT m_nNextRow, m_nRow; + UINT m_nPattern,m_nCurrentPattern,m_nNextPattern,m_nRestartPos; + UINT m_nMasterVolume, m_nGlobalVolume, m_nSongPreAmp; + UINT m_nFreqFactor, m_nTempoFactor, m_nOldGlbVolSlide; + LONG m_nMinPeriod, m_nMaxPeriod, m_nRepeatCount, m_nInitialRepeatCount; + DWORD m_nGlobalFadeSamples, m_nGlobalFadeMaxSamples; + UINT m_nMaxOrderPosition; + UINT m_nPatternNames; + LPSTR m_lpszSongComments, m_lpszPatternNames; + char m_szNames[MAX_INSTRUMENTS][32]; // changed from CHAR + CHAR CompressionTable[16]; + +public: + CSoundFile(); + ~CSoundFile(); + +public: + BOOL Create(LPCBYTE lpStream, DWORD dwMemLength=0); + BOOL Destroy(); + UINT GetType() const { return m_nType; } + UINT GetNumChannels() const; + UINT GetLogicalChannels() const { return m_nChannels; } + BOOL SetMasterVolume(UINT vol, BOOL bAdjustAGC=FALSE); + UINT GetMasterVolume() const { return m_nMasterVolume; } + UINT GetNumPatterns() const; + UINT GetNumInstruments() const; + UINT GetNumSamples() const { return m_nSamples; } + UINT GetCurrentPos() const; + UINT GetCurrentPattern() const { return m_nPattern; } + UINT GetCurrentOrder() const { return m_nCurrentPattern; } + UINT GetSongComments(LPSTR s, UINT cbsize, UINT linesize=32); + UINT GetRawSongComments(LPSTR s, UINT cbsize, UINT linesize=32); + UINT GetMaxPosition() const; + void SetCurrentPos(UINT nPos); + void SetCurrentOrder(UINT nOrder); + void GetTitle(LPSTR s) const { lstrcpyn(s,m_szNames[0],32); } + LPCSTR GetTitle() const { return m_szNames[0]; } + UINT GetSampleName(UINT nSample,LPSTR s=NULL) const; + UINT GetInstrumentName(UINT nInstr,LPSTR s=NULL) const; + UINT GetMusicSpeed() const { return m_nMusicSpeed; } + UINT GetMusicTempo() const { return m_nMusicTempo; } + DWORD GetLength(BOOL bAdjust, BOOL bTotal=FALSE); + DWORD GetSongTime() { return GetLength(FALSE, TRUE); } + void SetRepeatCount(int n) { m_nRepeatCount = n; m_nInitialRepeatCount = n; } + int GetRepeatCount() const { return m_nRepeatCount; } + BOOL IsPaused() const { return (m_dwSongFlags & SONG_PAUSED) ? TRUE : FALSE; } + void LoopPattern(int nPat, int nRow=0); + void CheckCPUUsage(UINT nCPU); + BOOL SetPatternName(UINT nPat, LPCSTR lpszName); + BOOL GetPatternName(UINT nPat, LPSTR lpszName, UINT cbSize=MAX_PATTERNNAME) const; + // Module Loaders + BOOL ReadXM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadS3M(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMod(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMed(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadSTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadIT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL Read669(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadUlt(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadWav(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDSM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadFAR(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMS(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMDL(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadOKT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDMF(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPTM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadDBM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadAMF(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMT2(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPSM(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadJ2B(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadUMX(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadABC(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestABC(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadMID(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestMID(LPCBYTE lpStream, DWORD dwMemLength); + BOOL ReadPAT(LPCBYTE lpStream, DWORD dwMemLength); + BOOL TestPAT(LPCBYTE lpStream, DWORD dwMemLength); + // Save Functions +#ifndef MODPLUG_NO_FILESAVE + UINT WriteSample(FILE *f, MODINSTRUMENT *pins, UINT nFlags, UINT nMaxLen=0); + BOOL SaveXM(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveS3M(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveMod(LPCSTR lpszFileName, UINT nPacking=0); + BOOL SaveIT(LPCSTR lpszFileName, UINT nPacking=0); +#endif // MODPLUG_NO_FILESAVE + // MOD Convert function + UINT GetBestSaveFormat() const; + UINT GetSaveFormats() const; + void ConvertModCommand(MODCOMMAND *) const; + void S3MConvert(MODCOMMAND *m, BOOL bIT) const; + void S3MSaveConvert(UINT *pcmd, UINT *pprm, BOOL bIT) const; + WORD ModSaveCommand(const MODCOMMAND *m, BOOL bXM) const; + +public: + // Real-time sound functions + VOID ResetChannels(); + + UINT Read(LPVOID lpBuffer, UINT cbBuffer); + UINT CreateStereoMix(int count); + BOOL FadeSong(UINT msec); + BOOL GlobalFadeSong(UINT msec); + UINT GetTotalTickCount() const { return m_nTotalCount; } + VOID ResetTotalTickCount() { m_nTotalCount = 0; } + +public: + // Mixer Config + static BOOL InitPlayer(BOOL bReset=FALSE); + static BOOL SetWaveConfig(UINT nRate,UINT nBits,UINT nChannels,BOOL bMMX=FALSE); + static BOOL SetResamplingMode(UINT nMode); // SRCMODE_XXXX + static BOOL IsStereo() { return (gnChannels > 1) ? TRUE : FALSE; } + static DWORD GetSampleRate() { return gdwMixingFreq; } + static DWORD GetBitsPerSample() { return gnBitsPerSample; } + static DWORD InitSysInfo(); + static DWORD GetSysInfo() { return gdwSysInfo; } + // AGC + static BOOL GetAGC() { return (gdwSoundSetup & SNDMIX_AGC) ? TRUE : FALSE; } + static void SetAGC(BOOL b); + static void ResetAGC(); + static void ProcessAGC(int count); + + //GCCFIX -- added these functions back in! + static BOOL SetWaveConfigEx(BOOL bSurround,BOOL bNoOverSampling,BOOL bReverb,BOOL hqido,BOOL bMegaBass,BOOL bNR,BOOL bEQ); + // DSP Effects + static void InitializeDSP(BOOL bReset); + static void ProcessStereoDSP(int count); + static void ProcessMonoDSP(int count); + // [Reverb level 0(quiet)-100(loud)], [delay in ms, usually 40-200ms] + static BOOL SetReverbParameters(UINT nDepth, UINT nDelay); + // [XBass level 0(quiet)-100(loud)], [cutoff in Hz 10-100] + static BOOL SetXBassParameters(UINT nDepth, UINT nRange); + // [Surround level 0(quiet)-100(heavy)] [delay in ms, usually 5-40ms] + static BOOL SetSurroundParameters(UINT nDepth, UINT nDelay); +public: + BOOL ReadNote(); + BOOL ProcessRow(); + BOOL ProcessEffects(); + UINT GetNNAChannel(UINT nChn) const; + void CheckNNA(UINT nChn, UINT instr, int note, BOOL bForceCut); + void NoteChange(UINT nChn, int note, BOOL bPorta=FALSE, BOOL bResetEnv=TRUE); + void InstrumentChange(MODCHANNEL *pChn, UINT instr, BOOL bPorta=FALSE,BOOL bUpdVol=TRUE,BOOL bResetEnv=TRUE); + // Channel Effects + void PortamentoUp(MODCHANNEL *pChn, UINT param); + void PortamentoDown(MODCHANNEL *pChn, UINT param); + void FinePortamentoUp(MODCHANNEL *pChn, UINT param); + void FinePortamentoDown(MODCHANNEL *pChn, UINT param); + void ExtraFinePortamentoUp(MODCHANNEL *pChn, UINT param); + void ExtraFinePortamentoDown(MODCHANNEL *pChn, UINT param); + void TonePortamento(MODCHANNEL *pChn, UINT param); + void Vibrato(MODCHANNEL *pChn, UINT param); + void FineVibrato(MODCHANNEL *pChn, UINT param); + void VolumeSlide(MODCHANNEL *pChn, UINT param); + void PanningSlide(MODCHANNEL *pChn, UINT param); + void ChannelVolSlide(MODCHANNEL *pChn, UINT param); + void FineVolumeUp(MODCHANNEL *pChn, UINT param); + void FineVolumeDown(MODCHANNEL *pChn, UINT param); + void Tremolo(MODCHANNEL *pChn, UINT param); + void Panbrello(MODCHANNEL *pChn, UINT param); + void RetrigNote(UINT nChn, UINT param); + void NoteCut(UINT nChn, UINT nTick); + void KeyOff(UINT nChn); + int PatternLoop(MODCHANNEL *, UINT param); + void ExtendedMODCommands(UINT nChn, UINT param); + void ExtendedS3MCommands(UINT nChn, UINT param); + void ExtendedChannelEffect(MODCHANNEL *, UINT param); + void ProcessMidiMacro(UINT nChn, LPCSTR pszMidiMacro, UINT param=0); + void SetupChannelFilter(MODCHANNEL *pChn, BOOL bReset, int flt_modifier=256) const; + // Low-Level effect processing + void DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide); + // Global Effects + void SetTempo(UINT param); + void SetSpeed(UINT param); + void GlobalVolSlide(UINT param); + DWORD IsSongFinished(UINT nOrder, UINT nRow) const; + BOOL IsValidBackwardJump(UINT nStartOrder, UINT nStartRow, UINT nJumpOrder, UINT nJumpRow) const; + // Read/Write sample functions + signed char GetDeltaValue(signed char prev, UINT n) const { return (signed char)(prev + CompressionTable[n & 0x0F]); } + UINT PackSample(int &sample, int next); + BOOL CanPackSample(LPSTR pSample, UINT nLen, UINT nPacking, BYTE *result=NULL); + UINT ReadSample(MODINSTRUMENT *pIns, UINT nFlags, LPCSTR pMemFile, DWORD dwMemLength); + BOOL DestroySample(UINT nSample); + BOOL DestroyInstrument(UINT nInstr); + BOOL IsSampleUsed(UINT nSample); + BOOL IsInstrumentUsed(UINT nInstr); + BOOL RemoveInstrumentSamples(UINT nInstr); + UINT DetectUnusedSamples(BOOL *); + BOOL RemoveSelectedSamples(BOOL *); + void AdjustSampleLoop(MODINSTRUMENT *pIns); + // I/O from another sound file + BOOL ReadInstrumentFromSong(UINT nInstr, CSoundFile *, UINT nSrcInstrument); + BOOL ReadSampleFromSong(UINT nSample, CSoundFile *, UINT nSrcSample); + // Period/Note functions + UINT GetNoteFromPeriod(UINT period) const; + UINT GetPeriodFromNote(UINT note, int nFineTune, UINT nC4Speed) const; + UINT GetFreqFromPeriod(UINT period, UINT nC4Speed, int nPeriodFrac=0) const; + // Misc functions + MODINSTRUMENT *GetSample(UINT n) { return Ins+n; } + void ResetMidiCfg(); + UINT MapMidiInstrument(DWORD dwProgram, UINT nChannel, UINT nNote); + BOOL ITInstrToMPT(const void *p, INSTRUMENTHEADER *penv, UINT trkvers); + UINT SaveMixPlugins(FILE *f=NULL, BOOL bUpdate=TRUE); + UINT LoadMixPlugins(const void *pData, UINT nLen); +#ifndef NO_FILTER + DWORD CutOffToFrequency(UINT nCutOff, int flt_modifier=256) const; // [0-255] => [1-10KHz] +#endif + + // Static helper functions +public: + static DWORD TransposeToFrequency(int transp, int ftune=0); + static int FrequencyToTranspose(DWORD freq); + static void FrequencyToTranspose(MODINSTRUMENT *psmp); + + // System-Dependant functions +public: + static MODCOMMAND *AllocatePattern(UINT rows, UINT nchns); + static signed char* AllocateSample(UINT nbytes); + static void FreePattern(LPVOID pat); + static void FreeSample(LPVOID p); + static UINT Normalize24BitBuffer(LPBYTE pbuffer, UINT cbsizebytes, DWORD lmax24, DWORD dwByteInc); +}; + + +// inline DWORD BigEndian(DWORD x) { return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | ((x & 0xFF000000) >> 24); } +// inline WORD BigEndianW(WORD x) { return (WORD)(((x >> 8) & 0xFF) | ((x << 8) & 0xFF00)); } + + +////////////////////////////////////////////////////////// +// WAVE format information + +#pragma pack(1) + +// Standard IFF chunks IDs +#define IFFID_FORM 0x4d524f46 +#define IFFID_RIFF 0x46464952 +#define IFFID_WAVE 0x45564157 +#define IFFID_LIST 0x5453494C +#define IFFID_INFO 0x4F464E49 + +// IFF Info fields +#define IFFID_ICOP 0x504F4349 +#define IFFID_IART 0x54524149 +#define IFFID_IPRD 0x44525049 +#define IFFID_INAM 0x4D414E49 +#define IFFID_ICMT 0x544D4349 +#define IFFID_IENG 0x474E4549 +#define IFFID_ISFT 0x54465349 +#define IFFID_ISBJ 0x4A425349 +#define IFFID_IGNR 0x524E4749 +#define IFFID_ICRD 0x44524349 + +// Wave IFF chunks IDs +#define IFFID_wave 0x65766177 +#define IFFID_fmt 0x20746D66 +#define IFFID_wsmp 0x706D7377 +#define IFFID_pcm 0x206d6370 +#define IFFID_data 0x61746164 +#define IFFID_smpl 0x6C706D73 +#define IFFID_xtra 0x61727478 + +typedef struct WAVEFILEHEADER +{ + DWORD id_RIFF; // "RIFF" + DWORD filesize; // file length-8 + DWORD id_WAVE; +} WAVEFILEHEADER; + + +typedef struct WAVEFORMATHEADER +{ + DWORD id_fmt; // "fmt " + DWORD hdrlen; // 16 + WORD format; // 1 + WORD channels; // 1:mono, 2:stereo + DWORD freqHz; // sampling freq + DWORD bytessec; // bytes/sec=freqHz*samplesize + WORD samplesize; // sizeof(sample) + WORD bitspersample; // bits per sample (8/16) +} WAVEFORMATHEADER; + + +typedef struct WAVEDATAHEADER +{ + DWORD id_data; // "data" + DWORD length; // length of data +} WAVEDATAHEADER; + + +typedef struct WAVESMPLHEADER +{ + // SMPL + DWORD smpl_id; // "smpl" -> 0x6C706D73 + DWORD smpl_len; // length of smpl: 3Ch (54h with sustain loop) + DWORD dwManufacturer; + DWORD dwProduct; + DWORD dwSamplePeriod; // 1000000000/freqHz + DWORD dwBaseNote; // 3Ch = C-4 -> 60 + RelativeTone + DWORD dwPitchFraction; + DWORD dwSMPTEFormat; + DWORD dwSMPTEOffset; + DWORD dwSampleLoops; // number of loops + DWORD cbSamplerData; +} WAVESMPLHEADER; + + +typedef struct SAMPLELOOPSTRUCT +{ + DWORD dwIdentifier; + DWORD dwLoopType; // 0=normal, 1=bidi + DWORD dwLoopStart; + DWORD dwLoopEnd; // Byte offset ? + DWORD dwFraction; + DWORD dwPlayCount; // Loop Count, 0=infinite +} SAMPLELOOPSTRUCT; + + +typedef struct WAVESAMPLERINFO +{ + WAVESMPLHEADER wsiHdr; + SAMPLELOOPSTRUCT wsiLoops[2]; +} WAVESAMPLERINFO; + + +typedef struct WAVELISTHEADER +{ + DWORD list_id; // "LIST" -> 0x5453494C + DWORD list_len; + DWORD info; // "INFO" +} WAVELISTHEADER; + + +typedef struct WAVEEXTRAHEADER +{ + DWORD xtra_id; // "xtra" -> 0x61727478 + DWORD xtra_len; + DWORD dwFlags; + WORD wPan; + WORD wVolume; + WORD wGlobalVol; + WORD wReserved; + BYTE nVibType; + BYTE nVibSweep; + BYTE nVibDepth; + BYTE nVibRate; +} WAVEEXTRAHEADER; + +#pragma pack() + +/////////////////////////////////////////////////////////// +// Low-level Mixing functions + +#define MIXBUFFERSIZE 512 +#define MIXING_ATTENUATION 4 +#define MIXING_CLIPMIN (-0x08000000) +#define MIXING_CLIPMAX (0x07FFFFFF) +#define VOLUMERAMPPRECISION 12 +#define FADESONGDELAY 100 +#define EQ_BUFFERSIZE (MIXBUFFERSIZE) +#define AGC_PRECISION 9 +#define AGC_UNITY (1 << AGC_PRECISION) + +// Calling conventions +#ifdef MSC_VER +#define MPPASMCALL __cdecl +#define MPPFASTCALL __fastcall +#else +#define MPPASMCALL +#define MPPFASTCALL +#endif + +#define MOD2XMFineTune(k) ((int)( (signed char)((k)<<4) )) +#define XM2MODFineTune(k) ((int)( (k>>4)&0x0f )) + +int _muldiv(long a, long b, long c); +int _muldivr(long a, long b, long c); + + +// Byte swapping functions from the GNU C Library and libsdl + +/* Swap bytes in 16 bit value. */ +#ifdef __GNUC__ +# define bswap_16(x) \ + (__extension__ \ + ({ unsigned short int __bsx = (x); \ + ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); })) +#else +static __inline unsigned short int +bswap_16 (unsigned short int __bsx) +{ + return ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); +} +#endif + +/* Swap bytes in 32 bit value. */ +#ifdef __GNUC__ +# define bswap_32(x) \ + (__extension__ \ + ({ unsigned int __bsx = (x); \ + ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | \ + (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); })) +#else +static __inline unsigned int +bswap_32 (unsigned int __bsx) +{ + return ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | + (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); +} +#endif + +#if (defined ARM) && (defined _WIN32_WCE) +static __inline unsigned short int +ARM_get16(const void *data) +{ + unsigned short int s; + memcpy(&s,data,sizeof(s)); + return s; +} + +static __inline unsigned int +ARM_get32(const void *data) +{ + unsigned int s; + memcpy(&s,data,sizeof(s)); + return s; +} + +#define bswapLE16(X) ARM_get16(&X) +#define bswapLE32(X) ARM_get32(&X) +#define bswapBE16(X) bswap_16(ARM_get16(&X)) +#define bswapBE32(X) bswap_32(ARM_get32(&X)) + +// From libsdl +#elif WORDS_BIGENDIAN +#define bswapLE16(X) bswap_16(X) +#define bswapLE32(X) bswap_32(X) +#define bswapBE16(X) (X) +#define bswapBE32(X) (X) +#else +#define bswapLE16(X) (X) +#define bswapLE32(X) (X) +#define bswapBE16(X) bswap_16(X) +#define bswapBE32(X) bswap_32(X) +#endif + +#endif diff --git a/lib/libmodplug/src/libmodplug/stdafx.h b/lib/libmodplug/src/libmodplug/stdafx.h new file mode 100644 index 0000000000..6631b8b0b6 --- /dev/null +++ b/lib/libmodplug/src/libmodplug/stdafx.h @@ -0,0 +1,121 @@ +/* + * This source code is public domain. + * + * Authors: Rani Assaf <rani@magic.metawire.com>, + * Olivier Lapicque <olivierl@jps.net>, + * Adam Goode <adam@evdebs.org> (endian and char fixes for PPC) + */ + +#ifndef _STDAFX_H_ +#define _STDAFX_H_ + +/* Autoconf detection of stdint/inttypes */ +#if defined(HAVE_CONFIG_H) && !defined(CONFIG_H_INCLUDED) +# include "config.h" +# define CONFIG_H_INCLUDED 1 +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif + + +#ifdef _WIN32 + +#ifdef MSC_VER +#pragma warning (disable:4201) +#pragma warning (disable:4514) +#endif + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <windowsx.h> +#include <mmsystem.h> +#include <stdio.h> +#include <malloc.h> + +#define srandom(_seed) srand(_seed) +#define random() rand() +#define sleep(_ms) Sleep(_ms) + +inline void ProcessPlugins(int n) {} + +#else + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif + +typedef int8_t CHAR; +typedef uint8_t UCHAR; +typedef uint8_t* PUCHAR; +typedef uint16_t USHORT; +typedef uint32_t ULONG; +typedef uint32_t UINT; +typedef uint32_t DWORD; +typedef int32_t LONG; +typedef int64_t LONGLONG; +typedef int32_t* LPLONG; +typedef uint32_t* LPDWORD; +typedef uint16_t WORD; +typedef uint8_t BYTE; +typedef uint8_t* LPBYTE; +typedef bool BOOL; +typedef char* LPSTR; +typedef void* LPVOID; +typedef uint16_t* LPWORD; +typedef const char* LPCSTR; +typedef void* PVOID; +typedef void VOID; + +inline LONG MulDiv (long a, long b, long c) +{ + // if (!c) return 0; + return ((uint64_t) a * (uint64_t) b ) / c; +} + +#define MODPLUG_NO_FILESAVE +#define NO_AGC +#define LPCTSTR LPCSTR +#define lstrcpyn strncpy +#define lstrcpy strcpy +#define lstrcmp strcmp +#define WAVE_FORMAT_PCM 1 +//#define ENABLE_EQ + +#define GHND 0 + +inline int8_t * GlobalAllocPtr(unsigned int, size_t size) +{ + int8_t * p = (int8_t *) malloc(size); + + if (p != NULL) memset(p, 0, size); + return p; +} + +inline void ProcessPlugins(int n) {} + +#define GlobalFreePtr(p) free((void *)(p)) + +#define strnicmp(a,b,c) strncasecmp(a,b,c) +#define wsprintf sprintf + +#ifndef FALSE +#define FALSE false +#endif + +#ifndef TRUE +#define TRUE true +#endif + +#endif // _WIN32 + +#endif + + + diff --git a/lib/libmodplug/src/load_669.cpp b/lib/libmodplug/src/load_669.cpp new file mode 100644 index 0000000000..a0dbfabd76 --- /dev/null +++ b/lib/libmodplug/src/load_669.cpp @@ -0,0 +1,186 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net>, + * Adam Goode <adam@evdebs.org> (endian and char fixes for PPC) +*/ + +//////////////////////////////////////////////////////////// +// 669 Composer / UNIS 669 module loader +//////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "sndfile.h" + +//#pragma warning(disable:4244) + +typedef struct tagFILEHEADER669 +{ + WORD sig; // 'if' or 'JN' + signed char songmessage[108]; // Song Message + BYTE samples; // number of samples (1-64) + BYTE patterns; // number of patterns (1-128) + BYTE restartpos; + BYTE orders[128]; + BYTE tempolist[128]; + BYTE breaks[128]; +} FILEHEADER669; + + +typedef struct tagSAMPLE669 +{ + BYTE filename[13]; + BYTE length[4]; // when will somebody think about DWORD align ??? + BYTE loopstart[4]; + BYTE loopend[4]; +} SAMPLE669; + + +BOOL CSoundFile::Read669(const BYTE *lpStream, DWORD dwMemLength) +//--------------------------------------------------------------- +{ + BOOL b669Ext; + const FILEHEADER669 *pfh = (const FILEHEADER669 *)lpStream; + const SAMPLE669 *psmp = (const SAMPLE669 *)(lpStream + 0x1F1); + DWORD dwMemPos = 0; + + if ((!lpStream) || (dwMemLength < sizeof(FILEHEADER669))) return FALSE; + if ((bswapLE16(pfh->sig) != 0x6669) && (bswapLE16(pfh->sig) != 0x4E4A)) return FALSE; + b669Ext = (bswapLE16(pfh->sig) == 0x4E4A) ? TRUE : FALSE; + if ((!pfh->samples) || (pfh->samples > 64) || (pfh->restartpos >= 128) + || (!pfh->patterns) || (pfh->patterns > 128)) return FALSE; + DWORD dontfuckwithme = 0x1F1 + pfh->samples * sizeof(SAMPLE669) + pfh->patterns * 0x600; + if (dontfuckwithme > dwMemLength) return FALSE; + for (UINT ichk=0; ichk<pfh->samples; ichk++) + { + DWORD len = bswapLE32(*((DWORD *)(&psmp[ichk].length))); + dontfuckwithme += len; + } + if (dontfuckwithme > dwMemLength) return FALSE; + // That should be enough checking: this must be a 669 module. + m_nType = MOD_TYPE_669; + m_dwSongFlags |= SONG_LINEARSLIDES; + m_nMinPeriod = 28 << 2; + m_nMaxPeriod = 1712 << 3; + m_nDefaultTempo = 125; + m_nDefaultSpeed = 6; + m_nChannels = 8; + memcpy(m_szNames[0], pfh->songmessage, 16); + m_nSamples = pfh->samples; + for (UINT nins=1; nins<=m_nSamples; nins++, psmp++) + { + DWORD len = bswapLE32(*((DWORD *)(&psmp->length))); + DWORD loopstart = bswapLE32(*((DWORD *)(&psmp->loopstart))); + DWORD loopend = bswapLE32(*((DWORD *)(&psmp->loopend))); + if (len > MAX_SAMPLE_LENGTH) len = MAX_SAMPLE_LENGTH; + if ((loopend > len) && (!loopstart)) loopend = 0; + if (loopend > len) loopend = len; + if (loopstart + 4 >= loopend) loopstart = loopend = 0; + Ins[nins].nLength = len; + Ins[nins].nLoopStart = loopstart; + Ins[nins].nLoopEnd = loopend; + if (loopend) Ins[nins].uFlags |= CHN_LOOP; + memcpy(m_szNames[nins], psmp->filename, 13); + Ins[nins].nVolume = 256; + Ins[nins].nGlobalVol = 64; + Ins[nins].nPan = 128; + } + // Song Message + m_lpszSongComments = new char[109]; + memcpy(m_lpszSongComments, pfh->songmessage, 108); + m_lpszSongComments[108] = 0; + // Reading Orders + memcpy(Order, pfh->orders, 128); + m_nRestartPos = pfh->restartpos; + if (Order[m_nRestartPos] >= pfh->patterns) m_nRestartPos = 0; + // Reading Pattern Break Locations + for (UINT npan=0; npan<8; npan++) + { + ChnSettings[npan].nPan = (npan & 1) ? 0x30 : 0xD0; + ChnSettings[npan].nVolume = 64; + } + // Reading Patterns + dwMemPos = 0x1F1 + pfh->samples * 25; + for (UINT npat=0; npat<pfh->patterns; npat++) + { + Patterns[npat] = AllocatePattern(64, m_nChannels); + if (!Patterns[npat]) break; + PatternSize[npat] = 64; + MODCOMMAND *m = Patterns[npat]; + const BYTE *p = lpStream + dwMemPos; + for (UINT row=0; row<64; row++) + { + MODCOMMAND *mspeed = m; + if ((row == pfh->breaks[npat]) && (row != 63)) + { + for (UINT i=0; i<8; i++) + { + m[i].command = CMD_PATTERNBREAK; + m[i].param = 0; + } + } + for (UINT n=0; n<8; n++, m++, p+=3) + { + UINT note = p[0] >> 2; + UINT instr = ((p[0] & 0x03) << 4) | (p[1] >> 4); + UINT vol = p[1] & 0x0F; + if (p[0] < 0xFE) + { + m->note = note + 37; + m->instr = instr + 1; + } + if (p[0] <= 0xFE) + { + m->volcmd = VOLCMD_VOLUME; + m->vol = (vol << 2) + 2; + } + if (p[2] != 0xFF) + { + UINT command = p[2] >> 4; + UINT param = p[2] & 0x0F; + switch(command) + { + case 0x00: command = CMD_PORTAMENTOUP; break; + case 0x01: command = CMD_PORTAMENTODOWN; break; + case 0x02: command = CMD_TONEPORTAMENTO; break; + case 0x03: command = CMD_MODCMDEX; param |= 0x50; break; + case 0x04: command = CMD_VIBRATO; param |= 0x40; break; + case 0x05: if (param) command = CMD_SPEED; else command = 0; param += 2; break; + case 0x06: if (param == 0) { command = CMD_PANNINGSLIDE; param = 0xFE; } else + if (param == 1) { command = CMD_PANNINGSLIDE; param = 0xEF; } else + command = 0; + break; + default: command = 0; + } + if (command) + { + if (command == CMD_SPEED) mspeed = NULL; + m->command = command; + m->param = param; + } + } + } + if ((!row) && (mspeed)) + { + for (UINT i=0; i<8; i++) if (!mspeed[i].command) + { + mspeed[i].command = CMD_SPEED; + mspeed[i].param = pfh->tempolist[npat] + 2; + break; + } + } + } + dwMemPos += 0x600; + } + // Reading Samples + for (UINT n=1; n<=m_nSamples; n++) + { + UINT len = Ins[n].nLength; + if (dwMemPos >= dwMemLength) break; + if (len > 4) ReadSample(&Ins[n], RS_PCM8U, (LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos); + dwMemPos += len; + } + return TRUE; +} + + diff --git a/lib/libmodplug/src/load_abc.cpp b/lib/libmodplug/src/load_abc.cpp new file mode 100644 index 0000000000..2fe8ea2bc9 --- /dev/null +++ b/lib/libmodplug/src/load_abc.cpp @@ -0,0 +1,5141 @@ +/* + + MikMod Sound System + + By Jake Stine of Divine Entertainment (1996-2000) + + Support: + If you find problems with this code, send mail to: + air@divent.org + + Distribution / Code rights: + Use this source code in any fashion you see fit. Giving me credit where + credit is due is optional, depending on your own levels of integrity and + honesty. + + ----------------------------------------- + Module: LOAD_ABC + + ABC module loader. + by Peter Grootswagers (2006) + <email:pgrootswagers@planet.nl> + + Portability: + All systems - all compilers (hopefully) +*/ + +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include <math.h> +#include <ctype.h> +#include <unistd.h> // for sleep + +#ifdef NEWMIKMOD +#include "mikmod.h" +#include "uniform.h" +typedef UBYTE BYTE; +typedef UWORD WORD; +#else +#include "stdafx.h" +#include "sndfile.h" +#endif + +#include "load_pat.h" + +#define MAXABCINCLUDES 8 +#define MAXCHORDNAMES 80 +#define ABC_ENV_DUMPTRACKS "MMABC_DUMPTRACKS" +#define ABC_ENV_NORANDOMPICK "MMABC_NO_RANDOM_PICK" + +// gchords use tracks with vpos 1 thru 7 +// drums use track with vpos 8 +// voice chords use vpos 0 and vpos from 11 up +#define GCHORDBPOS 1 +#define GCHORDFPOS 2 +#define GCHORDCPOS 3 +#define DRUMPOS 8 +#define DRONEPOS1 9 +#define DRONEPOS2 10 + +// in the patterns a whole note at unmodified tempo is 16 rows +#define ROWSPERNOTE 16 +// a 1/64-th note played in triool equals a 1/96-th note, to be able +// to play them and also to play the 1/64-th we need a resolution of 192 +// because 2/192 = 1/96 and 3/192 = 1/64 +#define RESOLUTION 192 + +#pragma pack(1) + +/************************************************************************** +**************************************************************************/ +#ifdef NEWMIKMOD +static char ABC_Version[] = "ABC+2.0 (draft IV)"; +#endif + +typedef enum { + note, + octave, + smpno, + volume, + effect, + effoper +} ABCEVENT_X_NOTE; + +typedef enum { + none, + trill, + bow, + accent +} ABCEVENT_X_EFFECT; + +typedef enum { + cmdflag, + command, + chordnum, + chordnote, + chordbase, + jumptype +} ABCEVENT_X_CMD; + +typedef enum { + cmdsegno = '$', + cmdcapo = 'B', + cmdchord = 'C', + cmdfine = 'F', + cmdhide = 'H', + cmdjump = 'J', + cmdloop = 'L', + cmdcoda = 'O', + cmdpartbrk = 'P', + cmdsync = 'S', + cmdtempo = 'T', + cmdvariant = 'V', + cmdtocoda = 'X' +} ABCEVENT_CMD; + +typedef enum { + jumpnormal, + jumpfade, + jumpdacapo, + jumpdcfade, + jumpdasegno, + jumpdsfade, + jumpfine, + jumptocoda, + jumpvariant, + jumpnot +} ABCEVENT_JUMPTYPE; + +typedef struct _ABCEVENT +{ + struct _ABCEVENT *next; + uint32_t tracktick; + union { + uint8_t par[6]; + struct { + uint8_t flg; + uint8_t cmd; + uint32_t lpar; // for variant selections, bit pattern + }; + }; + uint8_t part; + uint8_t tiednote; +} ABCEVENT; + +typedef struct _ABCTRACK +{ + struct _ABCTRACK *next; + ABCEVENT *head; + ABCEVENT *tail; + ABCEVENT *capostart; + ABCEVENT *tienote; + int transpose; + int octave_shift; + uint32_t slidevoltime; // for crescendo and diminuendo + int slidevol; // -2:fade away, -1:diminuendo, 0:none, +1:crescendo + uint8_t vno; // 0 is track is free for use, from previous song in multi-songbook + uint8_t vpos; // 0 is main voice, other is subtrack for gchords, gchords or drumnotes + uint8_t tiedvpos; + uint8_t mute; + uint8_t chan; // 10 is percussion channel, any other is melodic channel + uint8_t volume; + uint8_t instr; // current instrument for this track + uint8_t legato; + char v[22]; // first twenty characters are significant +} ABCTRACK; + +typedef struct _ABCMACRO +{ + struct _ABCMACRO *next; + char *name; + char *subst; + char *n; +} ABCMACRO; + +/************************************************************************** +**************************************************************************/ + +typedef struct _ABCHANDLE +{ +#ifdef NEWMIKMOD + MM_ALLOC *allochandle; + MM_ALLOC *macrohandle; + MM_ALLOC *trackhandle; + MM_ALLOC *ho; +#endif + ABCMACRO *macro; + ABCMACRO *umacro; + ABCTRACK *track; + long int pickrandom; + unsigned int len; + int speed; + char *line; + char *beatstring; + uint8_t beat[4]; // a:first note, b:strong notes, c:weak notes, n:strong note every n + char gchord[80]; // last setting for gchord + char drum[80]; // last setting for drum + char drumins[80]; // last setting for drum + char drumvol[80]; // last setting for drum + uint32_t barticks; + // parse variables, declared here to avoid parameter pollution + int abcchordvol, abcchordprog, abcbassvol, abcbassprog; + int ktrans; + int drumon, gchordon, droneon; + int dronegm, dronepitch[2], dronevol[2]; + ABCTRACK *tp, *tpc, *tpr; + uint32_t tracktime; +} ABCHANDLE; + +static int global_voiceno, global_octave_shift, global_tempo_factor, global_tempo_divider; +static char global_part; +static uint32_t global_songstart; +/* Named guitar chords */ +static char chordname[MAXCHORDNAMES][8]; +static int chordnotes[MAXCHORDNAMES][6]; +static int chordlen[MAXCHORDNAMES]; +static int chordsnamed = 0; + +static const char *sig[] = { + " C D EF G A Bc d ef g a b", // 7 sharps C# + " C D EF G AB c d ef g ab ", // 6 sharps F# + " C DE F G AB c de f g ab ", // 5 sharps B + " C DE F GA B c de f ga b ", // 4 sharps E + " CD E F GA B cd e f ga b ", // 3 sharps A + " CD E FG A B cd e fg a b ", // 2 sharps D + " C D E FG A Bc d e fg a b", // 1 sharps G + " C D EF G A Bc d ef g a b", // 0 sharps C + " C D EF G AB c d ef g ab ", // 1 flats F + " C DE F G AB c de f g ab ", // 2 flats Bb + " C DE F GA B c de f ga b ", // 3 flats Eb + " CD E F GA B cd e f ga b ", // 4 flats Ab + " CD E FG A B cd e fg a b ", // 5 flats Db + "C D E FG A Bc d e fg a b ", // 6 flats Gb + "C D EF G A Bc d ef g a b ", // 7 flats Cb +// 0123456789012345678901234 +}; + +static const char *keySigs[] = { +/* 0....:....1....:....2....:....3....:....4....:....5. */ + "7 sharps: C# A#m G#Mix D#Dor E#Phr F#Lyd B#Loc ", + "6 sharps: F# D#m C#Mix G#Dor A#Phr BLyd E#Loc ", + "5 sharps: B G#m F#Mix C#Dor D#Phr ELyd A#Loc ", + "4 sharps: E C#m BMix F#Dor G#Phr ALyd D#Loc ", + "3 sharps: A F#m EMix BDor C#Phr DLyd G#Loc ", + "2 sharps: D Bm AMix EDor F#Phr GLyd C#Loc ", + "1 sharp : G Em DMix ADor BPhr CLyd F#Loc ", + "0 sharps: C Am GMix DDor EPhr FLyd BLoc ", + "1 flat : F Dm CMix GDor APhr BbLyd ELoc ", + "2 flats : Bb Gm FMix CDor DPhr EbLyd ALoc ", + "3 flats : Eb Cm BbMix FDor GPhr AbLyd DLoc ", + "4 flats : Ab Fm EbMix BbDor CPhr DbLyd GLoc ", + "5 flats : Db Bbm AbMix EbDor FPhr GbLyd CLoc ", + "6 flats : Gb Ebm DbMix AbDor BbPhr CbLyd FLoc ", + "7 flats : Cb Abm GbMix DbDor EbPhr FbLyd BbLoc ", + 0 +}; + +// local prototypes +static int abc_getnumber(const char *p, int *number); +static ABCTRACK *abc_locate_track(ABCHANDLE *h, const char *voice, int pos); +static void abc_add_event(ABCHANDLE *h, ABCTRACK *tp, ABCEVENT *e); +static void abc_add_setloop(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime); +static void abc_add_setjumploop(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime, ABCEVENT_JUMPTYPE j); +static uint32_t abc_pattracktime(ABCHANDLE *h, uint32_t tracktime); +static int abc_patno(ABCHANDLE *h, uint32_t tracktime); + +#ifndef HAVE_SETENV +static void setenv(const char *name, const char *value, int overwrite) +{ + int len = strlen(name)+1+strlen(value)+1; + char *str = (char *)alloca(len); + sprintf(str, "%s=%s", name, value); + putenv(str); +} +#endif + + +static int abc_isvalidchar(char c) { + return(isalpha(c) || isdigit(c) || isspace(c) || c == '%' || c == ':'); +} + + +static void abc_message(const char *s1, const char *s2) +{ + char txt[256]; + if( strlen(s1) + strlen(s2) > 255 ) return; + sprintf(txt, s1, s2); +#ifdef NEWMIKMOD + _mmlog(txt); +#else + fprintf(stderr, "load_abc > %s\n", txt); +#endif +} + +static uint32_t modticks(uint32_t abcticks) +{ + return abcticks / RESOLUTION; +} + +static uint32_t abcticks(uint32_t modticks) +{ + return modticks * RESOLUTION; +} + +static uint32_t notelen_notediv_to_ticks(int speed, int len, int div) +{ + uint32_t u; + u = (ROWSPERNOTE * RESOLUTION * speed * len * global_tempo_factor) / (div * global_tempo_divider); + return u; +} + +static void abc_dumptracks(ABCHANDLE *h, const char *p) +{ + ABCTRACK *t; + ABCEVENT *e; + int n,pat,row,tck; + char nn[3]; + if( !h ) return; + for( t=h->track; t; t=t->next ) { + printf("track %d.%d chan=%d %s\n", (int)(t->vno), (int)(t->vpos), + (int)(t->chan), (char *)(t->v)); + if( strcmp(p,"nonotes") ) + n = 1; + else + n = 0; + for( e=t->head; e; e=e->next ) { + tck = modticks(e->tracktick); + row = tck / h->speed; + pat = row / 64; + tck = tck % h->speed; + row = row % 64; + nn[0] = ( e->tracktick % abcticks(h->speed * 64) ) ? ' ': '-'; + if( e->flg == 1 ) { + printf(" %6d.%02d.%d%c%c %d.%d %s ", + pat, row, tck, nn[0], (int)(e->part), (int)(t->vno), + (int)(t->vpos), (char *)(t->v)); + if( e->cmd == cmdchord ) { + nn[0] = "CCCDDEFFGGAABccddeffggaabb"[e->par[chordnote]]; + nn[1] = "b # # # # # # # # # # #"[e->par[chordnote]]; + nn[2] = '\0'; + if( isspace(nn[1]) ) nn[1] = '\0'; + printf("CMD %c: gchord %s%s", + (char)(e->cmd), nn, chordname[e->par[chordnum]]); + if( e->par[chordbase] != e->par[chordnote] ) { + nn[0] = "CCCDDEFFGGAABccddeffggaabb"[e->par[chordbase]]; + nn[1] = "b # # # # # # # # # # #"[e->par[chordbase]]; + nn[2] = '\0'; + printf("/%s", nn); + } + printf("\n"); + } + else + printf("CMD %c @%p 0x%08lX\n", + (char)(e->cmd), e, + (unsigned long)(e->lpar)); + if( strcmp(p,"nonotes") ) + n = 1; + else + n = 0; + } + else if( n ) { + printf(" %6d.%02d.%d%c%c %d.%d %s ", pat, row, tck, nn[0], e->part, t->vno, t->vpos, t->v); + if( e->par[note] ) { + nn[0] = "CCCDDEFFGGAABccddeffggaabb"[e->par[note]-23]; + nn[1] = "b # # # # # # # # # # #"[e->par[note]-23]; + nn[2] = '\0'; + } + else strcpy(nn,"--"); + printf("NOTE %s octave %d inst %s vol %03d\n", + nn, e->par[octave], pat_gm_name(pat_smptogm(e->par[smpno])),e->par[volume]); + if( strcmp(p,"all") ) + n = 0; + } + } + } +} + +#ifdef NEWMIKMOD + +#define MMFILE MMSTREAM +#define mmfgetc(x) _mm_read_SBYTE(x) +#define mmfeof(x) _mm_feof(x) +#define mmfgets(buf,sz,f) _mm_fgets(f,buf,sz) +#define mmftell(x) _mm_ftell(x) +#define mmfseek(f,p,w) _mm_fseek(f,p,w) +#define mmfopen(s,m) _mm_fopen(s,m) +#define mmfclose(f) _mm_fclose(f) + +#else + +#define MMSTREAM FILE +#define _mm_fopen(name,mode) fopen(name,mode) +#define _mm_fgets(f,buf,sz) fgets(buf,sz,f) +#define _mm_fseek(f,pos,whence) fseek(f,pos,whence) +#define _mm_ftell(f) ftell(f) +#define _mm_read_UBYTES(buf,sz,f) fread(buf,sz,1,f) +#define _mm_read_SBYTES(buf,sz,f) fread(buf,sz,1,f) +#define _mm_feof(f) feof(f) +#define _mm_fclose(f) fclose(f) +#define DupStr(h,buf,sz) strdup(buf) +#define _mm_calloc(h,n,sz) calloc(n,sz) +#define _mm_recalloc(h,buf,sz,elsz) realloc(buf,sz) +#define _mm_free(h,p) free(p) + +typedef struct { + char *mm; + int sz; + int pos; +} MMFILE; + +static MMFILE *mmfopen(const char *name, const char *mode) +{ + FILE *fp; + MMFILE *mmfile; + long len; + if( *mode != 'r' ) return NULL; + fp = fopen(name, mode); + if( !fp ) return NULL; + fseek(fp, 0, SEEK_END); + len = ftell(fp); + mmfile = (MMFILE *)malloc(len+sizeof(MMFILE)); + if( !mmfile ) return NULL; + fseek(fp, 0, SEEK_SET); + fread(&mmfile[1],1,len,fp); + fclose(fp); + mmfile->mm = (char *)&mmfile[1]; + mmfile->sz = len; + mmfile->pos = 0; + return mmfile; +} + +static void mmfclose(MMFILE *mmfile) +{ + free(mmfile); +} + +static bool mmfeof(MMFILE *mmfile) +{ + if( mmfile->pos < 0 ) return TRUE; + if( mmfile->pos < mmfile->sz ) return FALSE; + return TRUE; +} + +static int mmfgetc(MMFILE *mmfile) +{ + int b; + if( mmfeof(mmfile) ) return EOF; + b = mmfile->mm[mmfile->pos]; + mmfile->pos++; + if( b=='\r' && mmfile->mm[mmfile->pos] == '\n' ) { + b = '\n'; + mmfile->pos++; + } + return b; +} + +static void mmfgets(char buf[], unsigned int bufsz, MMFILE *mmfile) +{ + int i,b; + for( i=0; i<(int)bufsz-1; i++ ) { + b = mmfgetc(mmfile); + if( b==EOF ) break; + buf[i] = b; + if( b == '\n' ) break; + } + buf[i] = '\0'; +} + +static long mmftell(MMFILE *mmfile) +{ + return mmfile->pos; +} + +static void mmfseek(MMFILE *mmfile, long p, int whence) +{ + switch(whence) { + case SEEK_SET: + mmfile->pos = p; + break; + case SEEK_CUR: + mmfile->pos += p; + break; + case SEEK_END: + mmfile->pos = mmfile->sz + p; + break; + } +} +#endif + +// ===================================================================================== +static ABCEVENT *abc_new_event(ABCHANDLE *h, uint32_t abctick, const char data[]) +// ===================================================================================== +{ + ABCEVENT *retval; + int i; + + retval = (ABCEVENT *)_mm_calloc(h->trackhandle, 1,sizeof(ABCEVENT)); + retval->next = NULL; + retval->tracktick = abctick; + for( i=0; i<6; i++ ) + retval->par[i] = data[i]; + retval->part = global_part; + retval->tiednote = 0; + return retval; +} + +// ============================================================================= +static ABCEVENT *abc_copy_event(ABCHANDLE *h, ABCEVENT *se) +// ============================================================================= +{ + ABCEVENT *e; + e = (ABCEVENT *)_mm_calloc(h->trackhandle, 1,sizeof(ABCEVENT)); + e->next = NULL; + e->tracktick = se->tracktick; + e->flg = se->flg; + e->cmd = se->cmd; + e->lpar = se->lpar; + e->part = se->part; + return e; +} + +// ============================================================================= +static void abc_new_macro(ABCHANDLE *h, const char *m) +// ============================================================================= +{ + ABCMACRO *retval; + const char *p; + char buf[256],*q; + for( p=m; *p && isspace(*p); p++ ) ; + for( q=buf; *p && *p != '='; p++ ) + *q++ = *p; + if( q != buf ) + while( isspace(q[-1]) ) q--; + *q = '\0'; + retval = (ABCMACRO *)_mm_calloc(h->macrohandle, 1,sizeof(ABCTRACK)); + retval->name = DupStr(h->macrohandle, buf,strlen(buf)); + retval->n = strrchr(retval->name, 'n'); // for transposing macro's + for( p++; *p && isspace(*p); p++ ) ; + strncpy(buf,p,200); + for( q=&buf[strlen(buf)-1]; q!=buf && isspace(*q); q-- ) *q = '\0'; + retval->subst = DupStr(h->macrohandle, buf, strlen(buf)); + retval->next = h->macro; + h->macro = retval; +} + +// ============================================================================= +static void abc_new_umacro(ABCHANDLE *h, const char *m) +// ============================================================================= +{ + ABCMACRO *retval, *mp; + const char *p; + char buf[256], let[2], *q; + for( p=m; *p && isspace(*p); p++ ) ; + for( q=buf; *p && *p != '='; p++ ) + *q++ = *p; + if( q != buf ) + while( isspace(q[-1]) ) q--; + *q = '\0'; + if( strlen(buf) > 1 || strchr("~HIJKLMNOPQRSTUVWXY",toupper(buf[0])) == 0 || strchr("xy",buf[0]) ) return; + strcpy(let,buf); + for( p++; *p && isspace(*p); p++ ) ; + strncpy(buf,p,200); + for( q=&buf[strlen(buf)-1]; q!=buf && isspace(*q); q-- ) *q = '\0'; + for( q=buf; *q; q++ ) if( *q == '!' ) *q = '+'; // translate oldstyle to newstyle + if( !strcmp(buf,"+nil+") ) { // delete a macro + mp = NULL; + for( retval=h->umacro; retval; retval = retval->next ) { + if( retval->name[0] == let[0] ) { // delete this one + if( mp ) mp->next = retval->next; + else h->umacro = retval->next; + _mm_free(h->macrohandle, retval); + return; + } + mp = retval; + } + return; + } + retval = (ABCMACRO *)_mm_calloc(h->macrohandle, 1,sizeof(ABCTRACK)); + retval->name = DupStr(h->macrohandle, let,1); + retval->subst = DupStr(h->macrohandle, buf, strlen(buf)); + retval->n = 0; + retval->next = h->umacro; // by placing it up front we mask out the old macro until we +nil+ it + h->umacro = retval; +} + +// ============================================================================= +static ABCTRACK *abc_new_track(ABCHANDLE *h, const char *voice, int pos) +// ============================================================================= +{ + ABCTRACK *retval; + if( !pos ) global_voiceno++; + retval = (ABCTRACK *)_mm_calloc(h->trackhandle, 1,sizeof(ABCTRACK)); + retval->next = NULL; + retval->vno = global_voiceno; + retval->vpos = pos; + retval->tiedvpos = pos; + retval->instr = 1; + strncpy(retval->v, voice, 20); + retval->v[20] = '\0'; + retval->head = NULL; + retval->tail = NULL; + retval->capostart = NULL; + retval->tienote = NULL; + retval->mute = 0; + retval->chan = 0; + retval->transpose = 0; + retval->volume = h->track? h->track->volume: 120; + retval->slidevoltime = 0; + retval->slidevol = 0; + retval->legato = 0; + return retval; +} + +static int abc_numtracks(ABCHANDLE *h) +{ + int n; + ABCTRACK *t; + n=0; + for( t = h->track; t; t=t->next ) + n++; + return n; +} + +static int abc_interval(const char *s, const char *d) +{ + const char *p; + int i,j,k; + int n,oct,m[2]; + for( j=0; j<2; j++ ) { + if( j ) p = d; + else p = s; + switch(p[0]) { + case '^': + n = p[1]; + i = 2; + break; + case '_': + n = p[1]; + i = 2; + break; + case '=': + n = p[1]; + i = 2; + break; + default: + n = p[0]; + i = 1; + break; + } + for( k=0; k<25; k++ ) + if( n == sig[7][k] ) + break; + oct = 4; // ABC note pitch C is C4 and pitch c is C5 + if( k > 12 ) { + oct++; + k -= 12; + } + while( p[i] == ',' || p[i] == '\'' ) { + if( p[i] == ',' ) + oct--; + else + oct++; + i++; + } + m[j] = k + 12 * oct; + } + return m[0] - m[1]; +} + +static int abc_transpose(const char *v) +{ + int i,j,t; + const char *m = "B", *mv = ""; + t = 0; + global_octave_shift = 99; + for( ; *v && *v != ']'; v++ ) { + if( !strncasecmp(v,"t=",2) ) { + v+=2; + if( *v=='-' ) { + j = -1; + v++; + } + else j = 1; + v+=abc_getnumber(v,&i); + t += i * j; + global_octave_shift = 0; + } + if( !strncasecmp(v,"octave=",7) ) { + v+=7; + if( *v=='-' ) { + j = -1; + v++; + } + else j = 1; + v+=abc_getnumber(v,&i); + t += i * j * 12; + global_octave_shift = 0; + } + if( !strncasecmp(v,"transpose=",10) ) { + v+=10; + if( *v=='-' ) { + j = -1; + v++; + } + else j = 1; + v+=abc_getnumber(v,&i); + t += i * j; + global_octave_shift = 0; + } + if( !strncasecmp(v,"octave=",7) ) { // used in kv304*.abc + v+=7; + if( *v=='-' ) { + j = -1; + v++; + } + else j = 1; + v+=abc_getnumber(v,&i); + t += i * j * 12; + global_octave_shift = 0; + } + if( !strncasecmp(v,"m=",2) ) { + v += 2; + mv = v; // get the pitch for the middle staff line + while( *v && *v != ' ' && *v != ']' ) v++; + global_octave_shift = 0; + } + if( !strncasecmp(v,"middle=",7) ) { + v += 7; + mv = v; // get the pitch for the middle staff line + while( *v && *v != ' ' && *v != ']' ) v++; + global_octave_shift = 0; + } + if( !strncasecmp(v,"clef=",5) ) + v += 5; + j = 1; + if( !strncasecmp(v,"treble",6) ) { + j = 0; + v += 6; + switch( *v ) { + case '1': v++; m = "d"; break; + case '2': v++; + default: m = "B"; break; + case '3': v++; m = "G"; break; + case '4': v++; m = "E"; break; + case '5': v++; m = "C"; break; + } + global_octave_shift = 0; + } + if( j && !strncasecmp(v,"bass",4) ) { + m = "D,"; + j = 0; + v += 4; + switch( *v ) { + case '1': v++; m = "C"; break; + case '2': v++; m = "A,"; break; + case '3': v++; m = "F,"; break; + case '4': v++; + default: m = "D,"; break; + case '5': v++; m = "B,,"; break; + } + if( global_octave_shift == 99 ) + global_octave_shift = -2; + } + if( j && !strncasecmp(v,"tenor",5) ) { + j = 0; + v += 5; + switch( *v ) { + case '1': v++; m = "G"; break; + case '2': v++; m = "E"; break; + case '3': v++; m = "C"; break; + case '4': v++; + default: m = "A,"; break; + case '5': v++; m = "F,"; break; + } + if( global_octave_shift == 99 ) + global_octave_shift = 1; + } + if( j && !strncasecmp(v,"alto",4) ) { + j = 0; + v += 4; + switch( *v ) { + case '1': v++; m = "G"; break; + case '2': v++; m = "E"; break; + case '3': v++; + default: m = "C"; break; + case '4': v++; m = "A,"; break; + case '5': v++; m = "F,"; break; + } + if( global_octave_shift == 99 ) + global_octave_shift = 1; + } + if( j && strchr("+-",*v) && *v && v[1]=='8' ) { + switch(*v) { + case '+': + t += 12; + break; + case '-': + t -= 12; + break; + } + v += 2; + if( !strncasecmp(v,"va",2) ) v += 2; + global_octave_shift = 0; + j = 0; + } + if( j ) { + while( *v && *v != ' ' && *v != ']' ) v++; + } + } + if( strlen(mv) > 0 ) // someone set the middle note + t += abc_interval(mv, m); + if( global_octave_shift == 99 ) + global_octave_shift = 0; + return t; +} + +// ============================================================================= +static ABCTRACK *abc_locate_track(ABCHANDLE *h, const char *voice, int pos) +// ============================================================================= +{ + ABCTRACK *tr, *prev, *trunused; + char vc[21]; + int i, trans=0, voiceno=0, instrno = 1, channo = 0; + for( ; *voice == ' '; voice++ ) ; // skip leading spaces + for( i=0; *voice && *voice != ']' && *voice != '%' && !isspace(*voice); voice++ ) // can work with inline voice instructions + vc[i++] = *voice; + vc[i] = '\0'; + prev = NULL; + trunused = NULL; + if( !pos ) trans = abc_transpose(voice); + for( tr=h->track; tr; tr=tr->next ) { + if( tr->vno == 0 ) { + if( !trunused ) trunused = tr; // must reuse mastertrack (h->track) as first + } + else { + if( !strncasecmp(tr->v, vc, 20) ) { + if( tr->vpos == pos ) + return tr; + trans = tr->transpose; + global_octave_shift = tr->octave_shift; + voiceno = tr->vno; + instrno = tr->instr; + channo = tr->chan; + } + } + prev = tr; + } + if( trunused ) { + tr = trunused; + if( pos ) { + tr->vno = voiceno; + tr->instr = instrno; + tr->chan = channo; + } + else { + global_voiceno++; + tr->vno = global_voiceno; + tr->instr = 1; + tr->chan = 0; + } + tr->vpos = pos; + tr->tiedvpos = pos; + strncpy(tr->v, vc, 20); + tr->v[20] = '\0'; + tr->mute = 0; + tr->transpose = trans; + tr->octave_shift = global_octave_shift; + tr->volume = h->track->volume; + tr->tienote = NULL; + tr->legato = 0; + return tr; + } + tr = abc_new_track(h, vc, pos); + if( pos ) { + tr->vno = voiceno; + tr->instr = instrno; + tr->chan = channo; + } + tr->transpose = trans; + tr->octave_shift = global_octave_shift; + if( prev ) prev->next = tr; + else h->track = tr; + return tr; +} + +// ============================================================================= +static ABCTRACK *abc_check_track(ABCHANDLE *h, ABCTRACK *tp) +// ============================================================================= +{ + if( !tp ) { + tp = abc_locate_track(h, "", 0); // must work for voiceless abc too... + tp->transpose = h->ktrans; + } + return tp; +} + +static void abc_add_capo(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime) +{ + ABCEVENT *e; + char d[6]; + d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; + d[cmdflag] = 1; + d[command] = cmdcapo; + e = abc_new_event(h, tracktime, d); + tp->capostart = e; + abc_add_event(h, tp, e); // do this last (recursion danger) +} + +static void abc_add_segno(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime) +{ + ABCEVENT *e; + char d[6]; + d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; + d[cmdflag] = 1; + d[command] = cmdsegno; + e = abc_new_event(h, tracktime, d); + abc_add_event(h, tp, e); +} + +static void abc_add_coda(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime) +{ + ABCEVENT *e; + char d[6]; + d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; + d[cmdflag] = 1; + d[command] = cmdcoda; + e = abc_new_event(h, tracktime, d); + abc_add_event(h, tp, e); +} + +static void abc_add_fine(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime) +{ + ABCEVENT *e; + char d[6]; + d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; + d[cmdflag] = 1; + d[command] = cmdfine; + e = abc_new_event(h, tracktime, d); + abc_add_event(h, tp, e); +} + +static void abc_add_tocoda(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime) +{ + ABCEVENT *e; + char d[6]; + d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; + d[cmdflag] = 1; + d[command] = cmdtocoda; + e = abc_new_event(h, tracktime, d); + abc_add_event(h, tp, e); +} + +// first track is dirigent, remove all control events from other tracks +// to keep the information where the events should be relative to note events +// in the same tick the ticks are octated and four added for note events +// the control events that come before the note events get a decremented tick, +// those that come after get an incremented tick, for example: +// ctrl ctrl note ctrl ctrl note +// original: t t t t t+1 t+1 +// recoded: 8t+1 8t+2 8t+4 8t+5 8t+11 8t+12 +static void abc_remove_unnecessary_events(ABCHANDLE *h) +{ + ABCTRACK *tp,*ptp; + ABCEVENT *ep, *el; + uint32_t ct, et; + int d; + ptp = NULL; + for( tp=h->track; tp; tp=tp->next ) { + el = NULL; + ep = tp->head; + ct = 0; + d = -3; + while( ep ) { + et = ep->tracktick; + ep->tracktick <<= 3; + ep->tracktick += 4; + if( ep->flg == 1 ) { + ep->tracktick += d; + d++; + if( d == 0 ) d = -1; + if( d == 4 ) d = 3; + if( tp!=h->track ) ep->cmd = cmdhide; + switch( ep->cmd ) { + case cmdhide: + case cmdsync: + if( el ) { + el->next = ep->next; + _mm_free(h->trackhandle,ep); + ep = el->next; + } + else { + tp->head = ep->next; + _mm_free(h->trackhandle,ep); + ep = tp->head; + } + break; + default: + el = ep; + ep = ep->next; + break; + } + } + else { + el = ep; + ep = ep->next; + d = 1; + } + if( et > ct ) + d = -3; + ct = et; + } + if( !tp->head ) { // no need to keep empty tracks... + if( ptp ) { + ptp->next = tp->next; + _mm_free(h->trackhandle,tp); + tp = ptp; + } + else { + h->track = tp->next; + _mm_free(h->trackhandle,tp); + tp = h->track; + } + } + ptp = tp; // remember previous track + } +} + +// set ticks back, and handle partbreaks +static void abc_retick_events(ABCHANDLE *h) +{ + ABCTRACK *tp; + ABCEVENT *ep; + uint32_t et, tt=0, at = abcticks(64 * h->speed); + for( tp=h->track; tp; tp=tp->next ) { + // make ticks relative + tt = 0; + for( ep=tp->head; ep; ep=ep->next ) { + et = ep->tracktick >> 3; + ep->tracktick = et - tt; + tt = et; + } + // make ticks absolute again, skipping no-op partbreaks + tt = 0; + for( ep=tp->head; ep; ep=ep->next ) { + ep->tracktick += tt; + tt = ep->tracktick; + if( ep->flg == 1 && ep->cmd == cmdpartbrk ) { + if( tt % at ) { + tt += at; + tt /= at; + tt *= at; + ep->tracktick -= abcticks(h->speed); // break plays current row + } + else ep->cmd = cmdhide; + } + } + } +} + +// make sure every track has the control events it needs, this way it is not +// necessary to have redundant +segno+ +D.C.+ etc in the voices, the first voice +// is the master, it is pointed to by the member 'track' in the ABCHANDLE +static void abc_synchronise_tracks(ABCHANDLE *h) +{ + ABCTRACK *tp; + uint32_t tm; // tracktime in master + ABCEVENT *em, *es, *et, *ec; // events in master, slave, slave temporary and copied event + if( !h || !h->track ) return; + abc_remove_unnecessary_events(h); + for( tp = h->track->next; tp; tp = tp->next ) { + for( em=h->track->head; em; em=em->next ) { + if( em->flg == 1 ) { // some kind of control event + switch( em->cmd ) { + case cmdchord: + case cmdhide: + case cmdtempo: + case cmdsync: + break; + default: // check to see if copy is necessary + ec = abc_copy_event(h, em); + tm = em->tracktick; + es = tp->head; // allways search from the begin... + for( et=es; et && et->tracktick <= tm; et=et->next ) + es = et; + if( es == NULL || es->tracktick > tm ) { // special case: head of track + ec->next = es; + tp->head = ec; + } + else { + ec->next = es->next; + es->next = ec; + } + break; + } + } + } + } + abc_retick_events(h); +} + +static void abc_add_event(ABCHANDLE *h, ABCTRACK *tp, ABCEVENT *e) +{ + if( !tp->capostart ) abc_add_capo(h, tp, global_songstart); + if( tp->tail ) { + tp->tail->next = e; + tp->tail = e; + } + else { + tp->head = e; + tp->tail = e; + } +} + +static void abc_add_partbreak(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime) +{ + ABCEVENT *e; + char d[6]; + d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; + d[cmdflag] = 1; + d[command] = cmdpartbrk; + e = abc_new_event(h, tracktime, d); + abc_add_event(h, tp, e); +} + +static void abc_add_tempo_event(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime, int tempo) +{ + ABCEVENT *e; + char d[6]; + d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; + d[cmdflag] = 1; + d[command] = cmdtempo; + e = abc_new_event(h, tracktime, d); + e->lpar = tempo; + abc_add_event(h, tp, e); +} + +static void abc_add_noteoff(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime) +{ + ABCEVENT *e; + char d[6]; + d[note] = 0; + d[octave] = 0; + d[smpno] = pat_gmtosmp(tp->instr); + d[volume] = 0; + d[effect] = 0; + d[effoper] = 0; + e = abc_new_event(h, tracktime, d); + abc_add_event(h, tp, e); +} + +static int abc_dynamic_volume(ABCTRACK *tp, uint32_t tracktime, int vol) +{ + uint32_t slidetime; + int voldelta; + if( tp->mute ) return 0; + if( tp->slidevol == 0 ) return vol; + if( tracktime < tp->slidevoltime ) return vol; + slidetime = modticks(tracktime - tp->slidevoltime); + voldelta = (slidetime * 15) / 64 / 6; // slide from say mf up to f in one pattern's time + if( tp->slidevol > -2 && voldelta > 15 ) voldelta = 15; // never to much dynamics + if( tp->slidevol > 0 ) vol += voldelta; + else vol -= voldelta; + if( vol < 2 ) vol = 2; // xmms divides this by 2.... + if( vol > 127 ) vol = 127; + return vol; +} + +static void abc_track_untie_short_chordnotes(ABCHANDLE *h) +{ + ABCTRACK *tp; + int vn; + tp = h->tp; + vn = tp->vno; + for( tp = h->track; tp; tp = tp->next ) + if( tp != h->tp && tp->vno == vn && tp->tienote ) { + abc_message("short notes in chord can not be tied:\n%s", h->line); + tp->tienote = 0; + } +} + +static void abc_track_clear_tiednote(ABCHANDLE *h) +{ + ABCTRACK *tp; + int vn; + tp = h->tp; + vn = tp->vno; + for( tp = h->track; tp; tp = tp->next ) + if( tp->vno == vn ) tp->tienote = 0; +} + +static void abc_track_clear_tiedvpos(ABCHANDLE *h) +{ + ABCTRACK *tp; + int vn; + tp = h->tp; + vn = tp->vno; + for( tp = h->track; tp; tp = tp->next ) + if( tp->vno == vn ) tp->tiedvpos = tp->vpos; +} + +static ABCTRACK *abc_track_with_note_tied(ABCHANDLE *h, uint32_t tracktime, int n, int oct) +{ + int vn, vp; + ABCTRACK *tp; + ABCEVENT *e; + tp = h->tp; + vn = tp->vno; + vp = tp->vpos; + for( tp = h->track; tp; tp = tp->next ) { + if( tp->vno == vn ) { + e = tp->tienote; + if( e && e->tracktick < tracktime + && e->par[octave] == oct && abs(e->par[note] - n) < 3 ) { + if( tp->vpos != vp ) tp->tiedvpos = vp; + h->tp = tp; + return tp; + } + } + } + tp = h->tp; + vp = tp->tiedvpos; + if( tp->vpos != vp ) { + // chord note track allready returned in previous call + for( tp = h->track; tp; tp = tp->next ) { + if( tp->vno == vn && tp->vpos == vp ) { + tp->tiedvpos = h->tp->vpos; + h->tp = tp; + return tp; + } + } + } + return h->tp; +} + +static int abc_add_noteon(ABCHANDLE *h, int ch, const char *p, uint32_t tracktime, char *barkey, int vol, ABCEVENT_X_EFFECT fx, int fxop) +{ + ABCEVENT *e; + ABCTRACK *tp; + int i,j,k; + int n,oct; + char d[6]; + tp = h->tp; + switch(ch) { + case '^': + if( p[0] == '^' ) { + n = p[1]; + i = 2; + ch = 'x'; + } + else { + n = p[0]; + i = 1; + } + break; + case '_': + if( p[0] == '_' ) { + n = p[1]; + i = 2; + ch = 'b'; + } + else { + n = p[0]; + i = 1; + } + break; + case '=': + n = p[0]; + i = 1; + break; + default: + n = ch; + i = 0; + break; + } + for( k=0; k<51; k++ ) { + if( n == barkey[k] ) + break; + } + j = k; + if( k > 24 ) + k -= 25; // had something like A# over Bb key F signature.... + if( i ) { + // propagate accidentals if necessary + // DON'T do redundant accidentals they're always relative to C-scale + for( k=0; k<25; k++ ) { + if( n == sig[7][k] ) + break; + } + if( k < 25 ) { // only do real notes... + switch(ch) { + case 'x': + k++; + case '^': + k++; + break; + case 'b': + k--; + case '_': + k--; + break; + case '=': + break; + } + if( j < 25 ) // was it not A# over Bb? + barkey[j] = ' '; + barkey[k] = n; + } + } + oct = 3; // ABC note pitch C is C4 and pitch c is C5 + if( k < 25 ) { + k += tp->transpose; + while( k > 12 ) { + oct++; + k -= 12; + } + while( k < 0 ) { + oct--; + k += 12; + } + d[note] = 23 + k; // C0 is midi notenumber 24 + } + else + d[note] = 0; // someone has doen ^X3 or something like it... + while( p[i] && strchr(",'",p[i]) ) { + if( p[i]==',' ) oct--; + else oct++; + i++; + tp->octave_shift = 0; // forget we ever had to look at it + } + if( tp->octave_shift ) + tp->transpose += 12 * tp->octave_shift; + oct += tp->octave_shift; + tp->octave_shift = 0; // after the first note we never have to look at it again + if( oct < 0 ) oct = 0; + if( oct > 9 ) oct = 9; + d[octave] = oct; + d[smpno] = pat_gmtosmp(tp->instr); + d[volume] = abc_dynamic_volume(tp, tracktime, vol); + d[effect] = fx; // effect + d[effoper] = fxop; + tp = abc_track_with_note_tied(h, tracktime, d[note], oct); + if( tp->tienote ) { + if( tp->tienote->par[note] != d[note] ) { + if( abs(tp->tienote->par[note] - d[note]) < 3 ) { + // may be tied over bar symbol, recover local accidental to barkey + k = tp->tienote->par[note] - 23 - tp->transpose; + while( k < 0 ) k += 12; + while( k > 12 ) k -= 12; + if( (isupper(n) && barkey[k+12] == ' ') || (islower(n) && barkey[k] == ' ') ) { + barkey[j] = ' '; + if( isupper(n) ) + barkey[k] = n; + else + barkey[k+12] = n; + d[note] = tp->tienote->par[note]; + d[octave] = tp->tienote->par[octave]; + } + } + } + } + if( tp->tienote + && tp->tienote->par[note] == d[note] + && tp->tienote->par[octave] == d[octave] ) { + for( e = tp->tienote; e; e = e->next ) { + if( e->par[note] == 0 && e->par[octave] == 0 ) { // undo noteoff + e->flg = 1; + e->cmd = cmdhide; + e->lpar = 0; + break; + } + } + tp->tienote->tiednote = 1; // mark him for the pattern writers + for( j=i; isdigit(p[j]) || p[j]=='/'; j++ ) ; // look ahead to see if this one is tied too + if( p[j] != '-' ) // is this note tied too? + tp->tienote = NULL; // if not the tie ends here... + return i; + } + tp->tienote = NULL; + if( tp->tail + && tp->tail->tracktick == tracktime + && tp->tail->par[note] == 0 + && tp->tail->par[octave] == 0 ) { + for( j=0; j<6; j++ ) + tp->tail->par[j] = d[j]; + } + else { + e = abc_new_event(h, tracktime, d); + abc_add_event(h, tp, e); + } + if( i > 0 && p[i-1] == '"' ) { + i--; // someone coded a weird note like ^"E" + abc_message("strange note encountered scanning %s", h->line); + } + return i; +} + +static void abc_add_dronenote(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime, int nnum, int vol) +{ + ABCEVENT *e; + int j,k; + int oct; + char d[6]; + oct = -1; // ABC note pitch C is C4 and pitch c is C5 + k = nnum + 1; + while( k > 12 ) { + oct++; + k -= 12; + } + while( k < 0 ) { + oct--; + k += 12; + } + if( oct < 0 ) oct = 0; + d[note] = 23 + k; // C0 is midi notenumber 24 + d[octave] = oct; + d[smpno] = pat_gmtosmp(tp->instr); + d[volume] = abc_dynamic_volume(tp, tracktime, vol); + d[effect] = 0; // effect + d[effoper] = 0; + if( tp->tail + && tp->tail->tracktick == tracktime + && tp->tail->par[note] == 0 + && tp->tail->par[octave] == 0 ) { + for( j=0; j<6; j++ ) + tp->tail->par[j] = d[j]; + } + else { + e = abc_new_event(h, tracktime, d); + abc_add_event(h, tp, e); + } +} + +static void abc_add_chordnote(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime, int nnum, int vol) +{ + abc_add_dronenote(h, tp, tracktime, nnum + 23, tp->mute? 0: vol); +} + +static void abc_add_drumnote(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime, int nnum, int vol) +{ + abc_add_dronenote(h, tp, tracktime, nnum, tp->mute? 0: vol); +} + +static void abc_add_variant_start(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime, int n) +{ + ABCEVENT *e; + char d[6]; + d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; + d[cmdflag] = 1; + d[command] = cmdvariant; + e = abc_new_event(h, tracktime, d); + e->lpar = 1<<n; + abc_add_event(h, tp, e); +} + +static void abc_add_variant_choise(ABCTRACK *tp, int n) +{ + tp->tail->lpar |= 1<<n; +} + +static void abc_add_chord(const char *p, ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime) +{ + ABCEVENT *e; + char d[6]; + char s[8]; + int i; + const char *n = " C D EF G A Bc d ef g a b"; + d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; + d[cmdflag] = 1; + d[command] = cmdchord; + if( p[0] == '(' ) p++; // chord between parens like: (C) + for( i=0; n[i]; i++ ) + if( *p == n[i] ) { + d[chordnote] = i; + break; + } + p++; + switch(*p) { + case 'b': + d[chordnote]--; + p++; + break; + case '#': + d[chordnote]++; + p++; + break; + } + d[chordbase] = d[chordnote]; + for( i=0; p[i] && p[i] != '"' && p[i] != '/' && p[i] != '(' && p[i] != ')' && p[i] != ' '; i++ ) s[i] = p[i]; + s[i] = '\0'; + p = &p[i]; + if( *p=='/' ) { + p++; + for( i=0; n[i]; i++ ) + if( *p == n[i] ) { + d[chordbase] = i; + break; + } + p++; + switch(*p) { + case 'b': + d[chordbase]--; + p++; + break; + case '#': + d[chordbase]++; + p++; + break; + } + } + for( i=0; i<chordsnamed; i++ ) + if( !strcmp(s, chordname[i]) ) { + d[chordnum] = i; + break; + } + if( i==chordsnamed ) { + abc_message("Failure: unrecognized chordname %s",s); + return; + } + e = abc_new_event(h, tracktime, d); + abc_add_event(h, tp, e); +} + +static void abc_add_setloop(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime) +{ + ABCEVENT *e; + char d[6]; + d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; + d[cmdflag] = 1; + d[command] = cmdloop; + e = abc_new_event(h, tracktime, d); + abc_add_event(h, tp, e); +} + +static void abc_fade_track(ABCTRACK *tp, ABCEVENT *e) +{ + while(e) { + if( e->flg != 1 && e->par[note] != 0 ) + e->par[volume] = abc_dynamic_volume(tp, e->tracktick, e->par[volume]); + e = e->next; + } +} + +static void abc_add_setjumploop(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime, ABCEVENT_JUMPTYPE j) +{ + ABCEVENT *e; + char d[8]; + d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; + d[cmdflag] = 1; + d[command] = cmdjump; + d[jumptype] = j; + e = abc_new_event(h, tracktime, d); + abc_add_event(h, tp, e); +} + +static void abc_add_sync(ABCHANDLE *h, ABCTRACK *tp, uint32_t tracktime) +{ + ABCEVENT *e; + char d[6]; + e = tp->tail; + if( e && e->tracktick == tracktime ) return; + if( e && e->flg == 1 && e->cmd == cmdsync ) { + e->tracktick = tracktime; + return; + } + d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; + d[cmdflag] = 1; + d[command] = cmdsync; + e = abc_new_event(h, tracktime, d); + abc_add_event(h, tp, e); +} + +static void abc_add_gchord_syncs(ABCHANDLE *h, ABCTRACK *tpc, uint32_t tracktime) +{ + ABCTRACK *tp; + int i; + for( i = GCHORDBPOS; i < DRUMPOS; i++ ) { + tp = abc_locate_track(h, tpc->v, i); + abc_add_sync(h,tp,tracktime); + } +} + +static void abc_add_drum_sync(ABCHANDLE *h, ABCTRACK *tpr, uint32_t tracktime) +{ + ABCTRACK *tp; + tp = abc_locate_track(h, tpr->v, DRUMPOS); + abc_add_sync(h,tp,tracktime); +} + +static int abc_getnumber(const char *p, int *number) +{ + int i,h; + i = 0; + h = 0; + while( isdigit(p[i]) ) { + h = 10 * h + p[i] - '0'; + i++; + } + if( i==0 ) + *number = 1; + else + *number = h; + return i; +} + +static int abc_getexpr(const char *p, int *number) +{ + int i, term, total; + i = 0; + while( isspace(p[i]) ) + i++; + if( p[i] == '(' ) { + i += abc_getexpr(p+i+1, number); + while( p[i] && (p[i] != ')') ) + i++; + return i; + } + i += abc_getnumber(p+i, &total); + while( isspace(p[i]) ) + i++; + while( p[i] == '+' ) { + i += abc_getexpr(p+i+1, &term); + total += term; + while( isspace(p[i]) ) + i++; + } + *number = total; + return i; +} + +static int abc_notelen(const char *p, int *len, int *div) +{ + int i,h,k; + i = abc_getnumber(p,len); + h = 1; + while( p[i] == '/' ) { + h *= 2; + i++; + } + if( isdigit(p[i]) ) { + h /= 2; + i += abc_getnumber(p+i,&k); + } + else k = 1; + *div = h * k; + return i; +} + +static int abc_brokenrithm(const char *p, int *nl, int *nd, int *b, int hornpipe) +{ + switch( *b ) { + case '<': + *nl *= 3; + *nd *= 2; + hornpipe = 0; + break; + case '>': + *nd *= 2; + hornpipe = 0; + break; + } + *b = *p; + switch( *b ) { + case '>': + *nl *= 3; + *nd *= 2; + return 1; + case '<': + *nd *= 2; + return 1; + default: + *b = 0; + break; + } + if( hornpipe ) { // still true then make 1/8 notes broken rithme + if( *nl == 1 && *nd == 1 ) { + *b = '>'; + *nl = 3; + *nd = 2; + } + } + return 0; +} + +// put p notes in the time q for the next r notes +static int abc_tuplet(int *nl, int *nd, int p, int q, int r) +{ + if( !r ) return 0; + *nl *= q; + *nd *= p; + return r - 1; +} + +// evaluate [Q:"string" n1/m1 n2/m2 n3/m3 n4/m4=bpm "string"] +// minimal form [Q:"string"] +// most used form [Q: 1/4=120] +static int abc_extract_tempo(const char *p, int invoice) +{ + int nl, nd, ns, in, tempo; + int nl1=0, nd1, notes, state; + const char *q; + in = 0; + nl = 0; + nd = 1; + ns = 120; + notes = 0; + state = 0; + for( q=p; *q; q++ ) { + if( in ) { + if( *q=='"' ) + in = 0; + } + else { + if( *q == ']' ) break; + switch( *q ) { + case '"': + in = 1; + break; + case '/': + notes++; + state = 1; + nl1 = ns; + break; + case '=': + break; + default: + if( isdigit(*q) ) { + if( state ) { + q+=abc_getnumber(q,&nd1)-1; + state = 0; + nl = nl * nd1 + nl1 * nd; + nd = nd * nd1; + } + else + q+=abc_getnumber(q,&ns)-1; + } + break; + } + } + } + if( !notes ) { + nl = 1; + nd = 4; + } + if( !nd ) tempo = 120; + else tempo = ns * nl * 4 / nd; // mod tempo is really BPM where one B is equal to a quartnote + if( invoice ) { + nl = global_tempo_factor; + nd = global_tempo_divider; + } + global_tempo_factor = 1; + global_tempo_divider = 1; + while( tempo/global_tempo_divider > 255 ) + global_tempo_divider++; + tempo /= global_tempo_divider; + while( tempo * global_tempo_factor < 256 ) + global_tempo_factor++; + global_tempo_factor--; + tempo *= global_tempo_factor; + if( tempo * 3 < 512 ) { + global_tempo_factor *= 3; + global_tempo_divider *= 2; + tempo = (tempo * 3) / 2; + } + if( invoice ) { + if( nl != global_tempo_factor || nd != global_tempo_divider ) { + ns = (tempo * nl * global_tempo_divider) / (nd * global_tempo_factor); + if( ns > 31 && ns < 256 ) { + tempo = ns; + global_tempo_factor = nl; + global_tempo_divider = nd; + } + else + abc_message("Failure: inconvenient tempo change in middle of voice (%s)", p); + } + } + return tempo; +} + +static void abc_set_parts(char **d, char *p) +{ + int i,j,k,m,n; + char *q; +#ifdef NEWMIKMOD + static MM_ALLOC *h; + if( *d ) _mmalloc_close(h); +#else + if( *d ) free(*d); +#endif + *d = 0; + if( !p ) return; + for( i=0; p[i] && p[i] != '%'; i++ ) { + if( !strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ().0123456789 ",p[i]) ) { + abc_message("invalid characters in part string scanning P:%s", p); + return; + } + } +#ifdef NEWMIKMOD + h = _mmalloc_create("Load_ABC_parts", NULL); +#endif + // decode constructs like "((AB)2.(CD)2)3.(AB)E2" to "ABABCDCDABABCDCDABABCDCDABEE" + // first compute needed storage... + j=0; + k=0; + for( i=0; p[i] && p[i] != '%'; i++ ) { + if( isupper(p[i]) ) { + j++; + } + if( isdigit(p[i]) ) { + n=abc_getnumber(p+i,&k); + if( p[i-1] == ')' ) + j *= k; // never mind multiple parens, just take the worst case + else + j += k-1; + i += n-1; + } + } + q = (char *)_mm_calloc(h, j+1, sizeof(char)); // enough storage for the worst case + // now copy bytes from p to *d, taking parens and digits in account + j = 0; + for( i=0; p[i] && p[i] != '%'; i++ ) { + if( isdigit(p[i]) || isupper(p[i]) || p[i] == '(' || p[i] == ')' ) { + if( p[i] == ')' ) { + for( n=j; n > 0 && q[n-1] != '('; n-- ) ; // find open paren in q + // q[n+1] to q[j] contains the substring that must be repeated + if( n > 0 ) { + for( k = n; k<j; k++ ) q[k-1] = q[k]; // shift to the left... + j--; + } + else + abc_message("Warning: Unbalanced right parens in P: definition %s",p); + n = j - n + 1; // number of repeatable characters + i += abc_getnumber(p+i+1,&k); + while( k-- > 1 ) { + for( m=0; m<n; m++ ) { + q[j] = q[j-n]; + j++; + } + } + continue; + } + if( isdigit(p[i]) ) { + n = abc_getnumber(p+i,&k); + i += n - 1; + while( k-- > 1 ) { + q[j] = q[j-1]; + j++; + } + continue; + } + q[j] = p[i]; + j++; + } + } + q[j] = '\0'; + // remove any left over parens + for( i=0; i<j; i++ ) { + if( q[i] == '(' ) { + abc_message("Warning: Unbalanced left parens in P: definition %s",p); + for( k=i; k<j; k++ ) q[k] = q[k+1]; + j--; + } + } + *d = q; +} + +static void abc_appendpart(ABCHANDLE *h, ABCTRACK *tp, uint32_t pt1, uint32_t pt2) +{ + ABCEVENT *e, *ec; + uint32_t dt; + dt = tp->tail->tracktick - pt1; + for( e=tp->head; e && e->tracktick <= pt2; e=e->next ) { + if( e->tracktick >= pt1 ) { + if( e->flg != 1 || e->cmd == cmdsync || e->cmd == cmdchord ) { + if( e != tp->tail ) { + // copy this event at tail + ec = abc_copy_event(h,e); + ec->tracktick += dt; + ec->part = '*'; + tp->tail->next = ec; + tp->tail = ec; + } + } + } + } + abc_add_sync(h, tp, pt2 + dt); // make sure there is progression... +} + +static uint32_t abc_pattracktime(ABCHANDLE *h, uint32_t tracktime) +{ + ABCEVENT *e; + uint32_t dt,et,pt=abcticks(64 * h->speed); + if(!h || !h->track || !h->track->head ) return 0; + dt = 0; + for( e=h->track->head; e && e->tracktick <= tracktime; e=e->next ) { + if( e->flg == 1 && e->cmd == cmdpartbrk ) { + et = e->tracktick + dt; + if( et % pt ) { + et += pt; + et /= pt; + et *= pt; + dt = et - e->tracktick; + } + } + } + return (tracktime + dt); +} + +static int abc_patno(ABCHANDLE *h, uint32_t tracktime) +{ + return modticks(abc_pattracktime(h, tracktime)) / 64 / h->speed; +} + +static void abc_stripoff(ABCHANDLE *h, ABCTRACK *tp, uint32_t tt) +{ + ABCEVENT *e1, *e2; + e2 = NULL; + for( e1 = tp->head; e1 && e1->tracktick <= tt; e1=e1->next ) + e2 = e1; + if( e2 ) { + e1 = e2->next; + tp->tail = e2; + e2->next = NULL; + } + else { + e1 = tp->tail; + tp->head = NULL; + tp->tail = NULL; + } + while( e1 ) { + e2 = e1->next; + _mm_free(h->trackhandle,e1); + e1 = e2; + } +} + +static void abc_keeptiednotes(ABCHANDLE *h, uint32_t fromtime, uint32_t totime) { + ABCTRACK *tp; + ABCEVENT *e,*n,*f; + if( totime <= fromtime ) return; + for( tp=h->track; tp; tp=tp->next ) { + if( tp->vno ) { // if track is in use... + n = NULL; + for( e=tp->head; e && e->tracktick < fromtime; e = e->next ) + if( e->flg != 1 ) n = e; // remember it when it is a note event + if( n && n->tiednote ) { // we've a candidate to tie over the break + while( e && e->tracktick < totime ) e=e->next; // skip to other part + if( e && e->tracktick == totime ) { // if this is on begin row of this part + f = NULL; + while( !f && e && e->tracktick == totime ) { + if( e->flg != 1 ) f = e; + e = e->next; + } + if( f && f->par[note] ) { // pfoeie, we've found a candidate + if( abs(n->par[note] - f->par[note]) < 3 ) { // undo the note on + f->flg = 1; + f->cmd = cmdhide; + f->lpar = 0; + } + } + } + } + } + } +} + +static uint32_t abc_fade_tracks(ABCHANDLE *h, char *abcparts, uint32_t ptt[27]) +{ + ABCTRACK *tp; + ABCEVENT *e0; + char *p; + int vol; + uint32_t pt1, pt2; + uint32_t tt; + tt = h->track->tail->tracktick; + for( tp=h->track->next; tp; tp=tp->next ) { + if( !tp->tail ) abc_add_sync(h, tp, tt); // no empty tracks please... + if( tp->tail->tracktick > tt ) abc_stripoff(h, tp, tt); // should not happen.... + if( tp->tail->tracktick < tt ) abc_add_sync(h, tp, tt); + } + for( tp=h->track; tp; tp=tp->next ) { + vol = 127; + e0 = tp->tail; + if( tp->slidevol != -2 ) { + tp->slidevol = -2; + tp->slidevoltime = e0->tracktick; + } + tp->mute = 0; // unmute track for safety, notes in a muted track already have zero volume... + while( vol > 5 ) { + for( p=abcparts; *p && vol > 5; p++ ) { + pt1 = ptt[*p-'A']; + pt2 = ptt[*p-'A'+1]; + abc_appendpart(h, tp, pt1, pt2); + vol = abc_dynamic_volume(tp, tp->tail->tracktick, 127); + } + } + abc_fade_track(tp,e0); + } + return h->track->tail->tracktick; +} + +static void abc_song_to_parts(ABCHANDLE *h, char **abcparts, BYTE partp[27][2]) +{ + uint32_t starttick; + ABCEVENT *e; + int i, fading, loop, normal, partno, partsegno, partloop, partcoda, parttocoda, partfine, skip, x, y; + int vmask[27],nextp[27]; + uint32_t ptt[27]; + char buf[256]; // must be enough, mod's cannot handle more than 240 patterns + char *pfade; + if( !h || !h->track || !h->track->capostart ) return; + strcpy(buf,"A"); // initialize our temporary array + i = 1; + loop = 1; + partno = 0; + partsegno = 0; + partloop = 0; + partcoda = -1; + parttocoda = -1; + partfine = -1; + starttick = h->track->capostart->tracktick; + ptt[0] = starttick; + vmask[0] = -1; + nextp[0] = 1; + for( e=h->track->capostart; e; e=e->next ) { + if( e->flg == 1 ) { + switch( e->cmd ) { + case cmdpartbrk: + if( e->tracktick > starttick) { + starttick = e->tracktick; // do not make empty parts + if( partno < 26 ) { + partno++; + ptt[partno] = starttick; + } + if( i < 255 ) buf[i++] = partno+'A'; + vmask[partno] = -1; + nextp[partno] = partno+1; + } + break; + case cmdloop: + partloop = partno; + loop = 1; // start counting anew... + break; + case cmdvariant: + vmask[partno] = e->lpar; + break; + case cmdjump: + x = 0; + fading = 0; + normal = 0; + skip = 0; + pfade = &buf[i]; + switch( e->par[jumptype] ) { + case jumpfade: + fading = 1; + case jumpnormal: + normal = 1; + x = partloop; + loop++; + break; + case jumpdsfade: + fading = 1; + case jumpdasegno: + x = partsegno; + break; + case jumpdcfade: + fading = 1; + case jumpdacapo: + x = 0; + break; + default: + x = 0; + break; + } + if( vmask[partno] != -1 ) nextp[partno] = x; + if( partno < 26 ) ptt[partno+1] = e->tracktick; // for handling ties over breaks + while( x <= partno ) { + if( skip == 1 && x == partcoda ) skip = 0; + y = !skip; + if( y ) { + if( !normal ) { + if( x == partfine ) skip = 2; + if( x == parttocoda ) skip = 1; + y = !skip; + } + if( !(vmask[x] & (1<<loop)) ) y = 0; + } + if( y ) { + if( i < 255 ) buf[i++] = x+'A'; + if( nextp[x] != x + 1 ) loop++; + x = nextp[x]; + } + else + x++; + } + if( fading && partno < 26 && i < 255 ) { // add single part with fading tracks + partno++; + ptt[partno] = e->tracktick; + buf[i] = '\0'; // close up pfade with zero byte + starttick = abc_fade_tracks(h, pfade, ptt); + buf[i++] = partno+'A'; + partno++; + ptt[partno] = starttick; + buf[i++] = partno+'A'; // one extra to throw away... + e = h->track->tail; // this is the edge of the world captain... + } + break; + case cmdtocoda: + parttocoda = partno; + break; + case cmdcoda: + partcoda = partno; + break; + case cmdfine: + partfine = partno; + break; + case cmdsegno: + partsegno = partno; + break; + } + } + e->part = partno+'a'; // small caps for generated parts... + } + i--; // strip off last partno + if( partno > 0 ) partno--; + buf[i] = '\0'; + if( i > 1 ) { + for( i=1; buf[i]; i++ ) { + if( buf[i] != buf[i-1] + 1 ) { + x = buf[i-1] - 'A'; + y = buf[i] - 'A'; + abc_keeptiednotes(h, ptt[x+1], ptt[y]); + } + } + } + starttick = h->track->tail->tracktick; + ptt[partno+1] = starttick; + for( i=0; i<=partno; i++ ) { + partp[i][0] = abc_patno(h, ptt[i]); + partp[i][1] = abc_patno(h, ptt[i+1]); + } + // calculate end point of last part + starttick = abc_pattracktime(h, starttick); + if( starttick % abcticks(64 * h->speed) ) + partp[partno][1]++; + abc_set_parts(abcparts, buf); +} + +// ===================================================================================== +static char *abc_fgets(MMFILE *mmfile, char buf[], unsigned int bufsz) +// ===================================================================================== +{ + if( mmfeof(mmfile) ) return NULL; + mmfgets(buf,bufsz,mmfile); + return buf; +} + +// ===================================================================================== +static char *abc_fgetbytes(MMFILE *mmfile, char buf[], unsigned int bufsz) +// ===================================================================================== +{ + unsigned int i; + long pos; + if( mmfeof(mmfile) ) return NULL; + for( i=0; i<bufsz-2; i++ ) { + buf[i] = (char)mmfgetc(mmfile); + if( buf[i] == '\n' ) break; + if( buf[i] == '\r' ) { + pos = mmftell(mmfile); + if( mmfgetc(mmfile) != '\n' ) mmfseek(mmfile, pos, SEEK_SET); + buf[i] = '\n'; + break; + } + } + if( buf[i] == '\n' ) i++; + buf[i] = '\0'; + return buf; +} + +static void abc_substitute(ABCHANDLE *h, char *target, char *s) +{ + char *p, *q; + int i; + int l = strlen(target); + int n = strlen(s); + while( (p=strstr(h->line, target)) ) { + if( (i=strlen(h->line)) + n - l >= (int)h->len ) { + h->line = (char *)_mm_recalloc(h->allochandle, h->line, h->len<<1, sizeof(char)); + h->len <<= 1; + p=strstr(h->line, target); + } + if( n > l ) { + for( q=&h->line[i]; q>p; q-- ) q[n-l] = q[0]; + for( q=s; *q; q++ ) *p++ = *q; + } + else { + strcpy(p,s); + strcat(p,p+l); + } + } +} + +static void abc_preprocess(ABCHANDLE *h, ABCMACRO *m) +{ + int i, j, k, l, a, b; + char t[32]; + char s[200],*p; + if( m->n ) { + k = m->n - m->name; + for( i=0; i<14; i++ ) { + strncpy(t, m->name, 32); + t[k] = "CDEFGABcdefgab"[i]; + l = strlen(m->subst); + p = s; + for( j=0; j<l; j++ ) { + a = m->subst[j]; + if( a > 'g' && islower(a) ) { + b = a - 'n'; + a = "CDEFGABCDEFGABcdefgabcdefgab"[i+b+7]; + *p++ = a; + if( i+b < 0 ) + *p++ = ','; + if( i+b > 13 ) + *p++ = '\''; + } + else *p++ = a; + } + *p = '\0'; + abc_substitute(h, t, s); + } + } + else + abc_substitute(h, m->name, m->subst); +} + +static char *abc_gets(ABCHANDLE *h, MMFILE *mmfile) +{ + int i; + ABCMACRO *mp; + if( !h->len ) { + h->len = 64; // initial line size, adequate for most abc's + h->line = (char *)_mm_calloc(h->allochandle, h->len, sizeof(char)); + } + if( abc_fgetbytes(mmfile, h->line, h->len) ) { + while( (i=strlen(h->line)) > (int)(h->len - 3) ) { + // line too short, double it + h->line = (char *)_mm_recalloc(h->allochandle, h->line, h->len<<1, sizeof(char)); + if( h->line[i-1] != '\n' ) + abc_fgetbytes(mmfile, &h->line[i], h->len); + h->len <<= 1; + } + h->line[i-1] = '\0'; // strip off newline + for( mp=h->macro; mp; mp=mp->next ) + abc_preprocess(h,mp); + return h->line; + } + return NULL; +} + +static int abc_parse_decorations(ABCHANDLE *h, ABCTRACK *tp, const char *p) +{ + int vol=0; + if( !strncmp(p,"mp",2) ) vol = 75; + if( !strncmp(p,"mf",2) ) vol = 90; + if( !strncmp(p,"sfz",3) ) vol = 100; + if( *p == 'p' ) { + vol = 60; + while( *p++ == 'p' ) vol -= 15; + if( vol < 1 ) vol = 1; + } + if( *p == 'f' ) { + vol = 105; + while( *p++ == 'f' ) vol += 15; + if( vol > 135 ) vol = 127; // ffff + if( vol > 127 ) vol = 125; // fff + } + if( vol ) { + tp->volume = vol; + if( tp == h->track ) { // copy volume over to all voice tracks + for( ; tp; tp=tp->next ) { + if( tp->vpos == 0 || tp->vpos > DRONEPOS2 ) tp->volume = vol; + } + tp = h->track; + } + } + return tp->volume; +} + +// ===================================================================================== +#ifdef NEWMIKMOD +BOOL ABC_Test(MMSTREAM *mmfile) +#else +BOOL CSoundFile::TestABC(const BYTE *lpStream, DWORD dwMemLength) +#endif +// ===================================================================================== +{ + char id[128]; + bool hasK = false, hasX = false; + // scan file for first K: line (last in header) +#ifdef NEWMIKMOD + _mm_fseek(mmfile,0,SEEK_SET); + while(abc_fgets(mmfile,id,128)) { +#else + MMFILE mmfile; + mmfile.mm = (char *)lpStream; + mmfile.sz = dwMemLength; + mmfseek(&mmfile,0,SEEK_SET); + while(abc_fgets(&mmfile,id,128)) { +#endif + if (id[0] == 0) continue; // blank line. + if (id[0] == '%' && id[1] != '%') continue; // comment line. + + if (!abc_isvalidchar(id[0]) || !abc_isvalidchar(id[1])) { + return(0); // probably not an ABC. + } + if(id[0]=='K' + && id[1]==':' + && (isalpha(id[2]) || isspace(id[2])) ) hasK = true; + if (id[0]=='X' + && id[1]== ':' + && (abc_isvalidchar(id[2])) ) hasX = true; + if (hasK && hasX) { return 1; printf("valid\n"); } + } + return 0; +} + +// ===================================================================================== +static ABCHANDLE *ABC_Init(void) +{ + ABCHANDLE *retval; + char *p; + char buf[10]; +#ifdef NEWMIKMOD + MM_ALLOC *allochandle; + + allochandle = _mmalloc_create("Load_ABC", NULL); + retval = (ABCHANDLE *)_mm_calloc(allochandle, 1,sizeof(ABCHANDLE)); + if( !retval ) return NULL; + retval->allochandle = allochandle; + allochandle = _mmalloc_create("Load_ABC_macros", NULL); + retval->macrohandle = allochandle; + allochandle = _mmalloc_create("Load_ABC_tracks", NULL); + retval->trackhandle = allochandle; +#else + retval = (ABCHANDLE *)calloc(1,sizeof(ABCHANDLE)); + if( !retval ) return NULL; +#endif + retval->track = NULL; + retval->macro = NULL; + retval->umacro = NULL; + retval->beatstring = NULL; + retval->pickrandom = 0; + retval->len = 0; + retval->line = NULL; + strcpy(retval->gchord, ""); + retval->barticks = 0; + p = getenv(ABC_ENV_NORANDOMPICK); + if( p ) { + if( isdigit(*p) ) + retval->pickrandom = atoi(p); + if( *p == '-' ) { +#ifdef NEWMIKMOD + retval->pickrandom = atoi(p+1); + sprintf(buf,"-%ld",retval->pickrandom+1); +#else + retval->pickrandom = atoi(p+1)-1; // xmms preloads the file + sprintf(buf,"-%ld",retval->pickrandom+2); +#endif + setenv(ABC_ENV_NORANDOMPICK, buf, 1); + } + } + else { + srandom(time(0)); // initialize random generator with seed + retval->pickrandom = 1+(int)(10000.0*random()/(RAND_MAX+1.0)); + // can handle pickin' from songbooks with 10.000 songs +#ifdef NEWMIKMOD + sprintf(buf,"-%ld",retval->pickrandom+1); // next in sequence +#else + sprintf(buf,"-%ld",retval->pickrandom); // xmms preloads the file +#endif + setenv(ABC_ENV_NORANDOMPICK, buf, 1); + } + return retval; +} + +#ifndef NEWMIKMOD +static void ABC_CleanupTrack(ABCTRACK *tp) +{ + ABCEVENT *ep, *en; + if( tp ) { + for( ep=tp->head; ep; ep = en ) { + en=ep->next; + free(ep); + } + tp->head = NULL; + } +} + +static void ABC_CleanupMacro(ABCMACRO *m) +{ + if( m->name ) + free(m->name); + if( m->subst ) + free(m->subst); + free(m); +} +#endif + +// ===================================================================================== +static void ABC_CleanupTracks(ABCHANDLE *handle) +// ===================================================================================== +{ +#ifdef NEWMIKMOD + if(handle && handle->trackhandle) { + _mmalloc_close(handle->trackhandle); + handle->trackhandle = 0; + } +#else + ABCTRACK *tp, *tn; + if(handle) { + for( tp=handle->track; tp; tp = tn ) { + tn=tp->next; + ABC_CleanupTrack(tp); + } + handle->track = NULL; + } +#endif +} + +// ===================================================================================== +static void ABC_CleanupMacros(ABCHANDLE *handle) +// ===================================================================================== +{ +#ifdef NEWMIKMOD + if(handle && handle->macrohandle) { + _mmalloc_close(handle->macrohandle); + handle->macrohandle = 0; + } +#else + ABCMACRO *mp, *mn; + if(handle) { + for( mp=handle->macro; mp; mp = mn ) { + mn=mp->next; + ABC_CleanupMacro(mp); + } + for( mp=handle->umacro; mp; mp = mn ) { + mn=mp->next; + ABC_CleanupMacro(mp); + } + handle->macro = NULL; + handle->umacro = NULL; + } +#endif +} + +// ===================================================================================== +static void ABC_Cleanup(ABCHANDLE *handle) +// ===================================================================================== +{ +#ifdef NEWMIKMOD + if(handle && handle->allochandle) { +#else + if(handle) { +#endif + ABC_CleanupMacros(handle); + ABC_CleanupTracks(handle); +#ifdef NEWMIKMOD + _mmalloc_close(handle->allochandle); + handle->allochandle = 0; + handle->len = 0; +#else + if( handle->line ) + free(handle->line); + if( handle->beatstring ) + free(handle->beatstring); + free(handle); +#endif + } +} + +static int abc_is_global_event(ABCEVENT *e) +{ + return e->flg == 1 && (e->cmd == cmdtempo || e->cmd == cmdpartbrk); +} + +static ABCEVENT *abc_next_global(ABCEVENT *e) +{ + for( ; e && !abc_is_global_event(e); e=e->next ) ; + return e; +} + +static ABCEVENT *abc_next_note(ABCEVENT *e) +{ + for( ; e && e->flg == 1; e=e->next ) ; + return e; +} + +// ============================================================================= +#ifdef NEWMIKMOD +static void ABC_ReadPatterns(UNIMOD *of, ABCHANDLE *h, int numpat) +// ============================================================================= +{ + int pat,row,i,ch,trillbits; + BYTE n,ins,vol; + ABCTRACK *t; + ABCEVENT *e, *en, *ef, *el; + uint32_t tt1, tt2; + UNITRK_EFFECT eff; + + // initialize start points of event list in tracks + for( t = h->track; t; t = t->next ) t->capostart = t->head; + trillbits = 0; // trill effect admininstration: one bit per channel, max 32 channnels + for( pat = 0; pat < numpat; pat++ ) { + utrk_reset(of->ut); + for( row = 0; row < 64; row++ ) { + tt1 = abcticks((pat * 64 + row ) * h->speed); + tt2 = tt1 + abcticks(h->speed); + ch = 0; + for( e=abc_next_global(h->track->capostart); e && e->tracktick < tt2; e=abc_next_global(e->next) ) { + if( e && e->tracktick >= tt1 ) { // we have a tempo event in this row + switch( e->cmd ) { + case cmdtempo: + eff.effect = UNI_GLOB_TEMPO; + eff.param.u = e->lpar; + eff.framedly = UFD_RUNONCE; + utrk_write_global(of->ut, &eff, PTMEM_TEMPO); + break; + case cmdpartbrk: + eff.effect = UNI_GLOB_PATBREAK; + eff.param.u = 0; + eff.framedly = UFD_RUNONCE; + utrk_write_global(of->ut, &eff, UNIMEM_NONE); + break; + } + } + } + for( t = h->track; t; t = t->next ) { + for( e=abc_next_note(t->capostart); e && e->tracktick < tt1; e=abc_next_note(e->next) ) + t->capostart = e; + i = 0; + ef = NULL; + en = e; + el = e; + for( ; e && e->tracktick < tt2; e=abc_next_note(e->next) ) { // we have a note event in this row + t->capostart = e; + i++; + if( e->par[volume] ) { + if( !ef ) ef = e; + el = e; + } + } + if( i ) { + trillbits &= ~(1<<ch); + utrk_settrack(of->ut, ch); + if( i == 1 || ef == el || !ef ) { // only one event in this row + if( ef ) e = ef; + else e = en; + el = t->capostart; + i = e->par[note] + ((e->par[octave])*12); + if( t->chan == 10 ) { + n = pat_gm_drumnote(i) + 23; + ins = pat_gmtosmp(pat_gm_drumnr(i)); + } + else { + n = pat_modnote(i); + ins = e->par[smpno]; + } + eff.framedly = modticks(e->tracktick - tt1); + vol = e->par[volume]; + if( e->par[effect] == accent ) { + vol += vol / 10; + if( vol > 127 ) vol = 127; + } + if (vol <= 0) {} + else if( el->par[volume] == 0 ) { + eff.framedly = modticks(el->tracktick - tt1); + eff.param.u = 0; + eff.param.byte_a = n; + eff.param.byte_b = ins; + eff.effect = UNI_NOTEKILL; + utrk_write_local(of->ut, &eff, UNIMEM_NONE); + } + else { + switch( e->par[effect] ) { + case trill: + eff.effect = UNI_VIBRATO_DEPTH; + eff.param.u = 12; // depth 1.5 + utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_DEPTH); + eff.effect = UNI_VIBRATO_SPEED; + eff.param.u = 48; // speed 12 + utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_SPEED); + trillbits |= (1<<ch); + break; + case bow: + eff.effect = UNI_PITCHSLIDE; + eff.framedly = (h->speed/2)|UFD_RUNONCE; + eff.param.s = 2; + utrk_write_local(of->ut, &eff, (e->par[effoper])? PTMEM_PITCHSLIDEUP: PTMEM_PITCHSLIDEDN); + break; + default: + break; + } + if( eff.framedly ) { + eff.param.u = 0; + eff.param.byte_a = n; + eff.param.byte_b = ins; + eff.effect = UNI_NOTEDELAY; + utrk_write_local(of->ut, &eff, UNIMEM_NONE); + } + } + utrk_write_inst(of->ut, ins); + utrk_write_note(of->ut, n); // <- normal note + pt_write_effect(of->ut, 0xc, vol); + } + else { + // two notes in one row, use FINEPITCHSLIDE runonce effect + // start first note on first tick and framedly runonce on seconds note tick + // use volume and instrument of last note + if( t->chan == 10 ) { + i = el->par[note] + ((el->par[octave])*12); + n = pat_gm_drumnote(i) + 23; + ins = pat_gmtosmp(pat_gm_drumnr(i)); + i = n; // cannot change instrument here.. + } + else { + i = ef->par[note] + ((ef->par[octave])*12); + n = pat_modnote(i); + ins = el->par[smpno]; + i = pat_modnote(el->par[note] + ((el->par[octave])*12)); + } + vol = el->par[volume]; + eff.effect = UNI_PITCHSLIDE; + eff.framedly = modticks(el->tracktick - tt1)|UFD_RUNONCE; + eff.param.s = ((i > n)?i-n:n-i); + utrk_write_inst(of->ut, ins); + utrk_write_note(of->ut, n); // <- normal note + pt_write_effect(of->ut, 0xc, vol); + utrk_write_local(of->ut, &eff, (i > n)? PTMEM_PITCHSLIDEUP: PTMEM_PITCHSLIDEDN); + } + } + else { // no new notes, keep on trilling... + if( trillbits & (1<<ch) ) { + utrk_settrack(of->ut, ch); + eff.effect = UNI_VIBRATO_DEPTH; + eff.param.u = 12; // depth 1.5 + utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_DEPTH); + eff.effect = UNI_VIBRATO_SPEED; + eff.param.u = 60; // speed 15 + utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_SPEED); + } + } + ch++; + } + utrk_newline(of->ut); + } + if(!utrk_dup_pattern(of->ut,of)) return; + } +} + +#else + +static int ABC_ReadPatterns(MODCOMMAND *pattern[], WORD psize[], ABCHANDLE *h, int numpat, int channels) +// ===================================================================================== +{ + int pat,row,i,ch,trillbits; + BYTE n,ins,vol; + ABCTRACK *t; + ABCEVENT *e, *en, *ef, *el; + uint32_t tt1, tt2; + MODCOMMAND *m; + int patbrk, tempo; + if( numpat > MAX_PATTERNS ) numpat = MAX_PATTERNS; + + // initialize start points of event list in tracks + for( t = h->track; t; t = t->next ) t->capostart = t->head; + trillbits = 0; // trill effect admininstration: one bit per channel, max 32 channnels + for( pat = 0; pat < numpat; pat++ ) { + pattern[pat] = CSoundFile::AllocatePattern(64, channels); + if( !pattern[pat] ) return 0; + psize[pat] = 64; + for( row = 0; row < 64; row++ ) { + tt1 = abcticks((pat * 64 + row ) * h->speed); + tt2 = tt1 + abcticks(h->speed); + ch = 0; + tempo = 0; + patbrk = 0; + for( e=abc_next_global(h->track->capostart); e && e->tracktick < tt2; e=abc_next_global(e->next) ) { + if( e && e->tracktick >= tt1 ) { // we have a tempo event in this row + switch( e->cmd ) { + case cmdtempo: + tempo = e->lpar; + break; + case cmdpartbrk: + patbrk = 1; + break; + } + } + } + for( t = h->track; t; t = t->next ) { + for( e=abc_next_note(t->capostart); e && e->tracktick < tt1; e=abc_next_note(e->next) ) ; + i = 0; + ef = NULL; + en = e; + el = e; + for( ; e && e->tracktick < tt2; e=abc_next_note(e->next) ) { // we have a note event in this row + t->capostart = e; + i++; + if( e->par[volume] ) { + if( !ef ) ef = e; + el = e; + } + } + m = &pattern[pat][row * channels + ch]; + m->param = 0; + m->command = CMD_NONE; + if( i ) { + trillbits &= ~(1<<ch); + if( i == 1 || ef == el || !ef ) { // only one event in this row + if( ef ) e = ef; + else e = en; + el = t->capostart; + i = e->par[note] + ((e->par[octave])*12); + if( t->chan == 10 ) { + n = pat_gm_drumnote(i) + 23; + ins = pat_gmtosmp(pat_gm_drumnr(i)); + } + else { + n = pat_modnote(i); + ins = e->par[smpno]; + } + vol = e->par[volume]/2; + if( e->par[volume] > 0 ) { + if( e->par[effect] == accent ) vol += vol / 20; + if( vol > 64 ) vol = 64; + if( el->par[volume] == 0 ) { // note cut + m->param = el->tracktick - tt1; + m->command = CMD_S3MCMDEX; + m->param |= 0xC0; + } + else { + switch( e->par[effect] ) { + case trill: + m->command = CMD_VIBRATO; + m->param = 0xC2; // speed 12 depth 2 + trillbits |= (1<<ch); + break; + case bow: + m->command = CMD_XFINEPORTAUPDOWN; + m->param |= (e->par[effoper])? 0x12: 0x22; + break; + default: + m->param = modticks(e->tracktick - tt1); + if( m->param ) { // note delay + m->command = CMD_S3MCMDEX; + m->param |= 0xD0; + } + break; + } + } + } + m->instr = ins; + m->note = n; // <- normal note + m->volcmd = VOLCMD_VOLUME; + m->vol = vol; + } + else { + // two notes in one row, use FINEPITCHSLIDE runonce effect + // start first note on first tick and framedly runonce on seconds note tick + // use volume and instrument of last note + if( t->chan == 10 ) { + i = el->par[note] + ((el->par[octave])*12); + n = pat_gm_drumnote(i) + 23; + ins = pat_gmtosmp(pat_gm_drumnr(i)); + i = n; // cannot change instrument here.. + } + else { + i = ef->par[note] + ((ef->par[octave])*12); + n = pat_modnote(i); + ins = el->par[smpno]; + i = pat_modnote(el->par[note] + ((el->par[octave])*12)); + } + vol = el->par[volume]/2; + if( vol > 64 ) vol = 64; + m->instr = ins; + m->note = n; // <- normal note + m->volcmd = VOLCMD_VOLUME; + m->vol = vol; + m->param = ((i > n)?i-n:n-i); + if( m->param < 16 ) { + if( m->param ) { + m->command = CMD_XFINEPORTAUPDOWN; + m->param |= (i > n)? 0x10: 0x20; + } + else { // retrigger same note... + m->command = CMD_RETRIG; + m->param = modticks(el->tracktick - tt1); + } + } + else + m->command = (i > n)? CMD_PORTAMENTOUP: CMD_PORTAMENTODOWN; + } + } + else { // no new notes, keep on trilling... + if( trillbits & (1<<ch) ) { + m = &pattern[pat][row * channels + ch]; + m->command = CMD_VIBRATO; + m->param = 0; // inherited from first effect + m->instr = 0; + m->note = 0; + m->volcmd = 0; + m->vol = 0; + } + } + if( m->param == 0 && m->command == CMD_NONE ) { + if( tempo ) { + m->command = CMD_TEMPO; + m->param = tempo; + tempo = 0; + } + else { + if( patbrk ) { + m->command = CMD_PATTERNBREAK; + patbrk = 0; + } + } + } + ch++; + } + if( tempo || patbrk ) return 1; + } + } + return 0; +} + +#endif + +static int ABC_Key(const char *p) +{ + int i,j; + char c[8]; + const char *q; + while( isspace(*p) ) p++; + i = 0; + q = p; + for( i=0; i<8 && *p && *p != ']'; p++ ) { + if( isspace(*p) ) { + while( isspace(*p) ) p++; + if( strncasecmp(p, "min", 3) && strncasecmp(p, "maj", 3) ) + break; + } + c[i] = *p; + i++; + } + c[i] = '\0'; + if( !strcmp(c,"Hp") || !strcmp(c,"HP") ) // highland pipes + strcpy(c,"Bm"); // two sharps at c and f + if( !strcasecmp(c+1, "minor") ) i=2; + if( !strcasecmp(c+2, "minor") ) i=3; + if( !strcasecmp(c+1, "major") ) i=1; + if( !strcasecmp(c+2, "major") ) i=2; + if( !strcasecmp(c+1, "min") ) i=2; + if( !strcasecmp(c+2, "min") ) i=3; + if( !strcasecmp(c+1, "maj") ) i=1; + if( !strcasecmp(c+2, "maj") ) i=2; + for( ; i<6; i++ ) + c[i] = ' '; + c[i] = '\0'; + for( i=0; keySigs[i]; i++ ) { + for( j=10; j<46; j+=6 ) + if( !strncasecmp(keySigs[i]+j, c, 6) ) + return i; + } + abc_message("Failure: Unrecognised K: field %s", q); + return 7; +} + +static char *abc_skip_word(char *p) +{ + while( isspace(*p) ) p++; + while( *p && !isspace(*p) && *p != ']') p++; + while( isspace(*p) ) p++; + return p; +} + +static uint32_t abc_tracktime(ABCTRACK *tp) +{ + uint32_t tracktime; + if( tp->tail ) tracktime = tp->tail->tracktick; + else tracktime = 0; + if( tracktime < global_songstart ) + tracktime = global_songstart; + return tracktime; +} + +static void abc_addchordname(const char *s, int len, const int *notes) +// adds chord name and note set to list of known chords +{ + int i, j; + if(strlen(s) > 7) { + abc_message("Failure: Chord name cannot exceed 7 characters, %s", s); + return; + } + if(len > 6) { + abc_message("Failure: Named chord cannot have more than 6 notes, %s", s); + return; + } + for( i=0; i < chordsnamed; i++ ) { + if(strcmp(s, chordname[i]) == 0) { + /* change chord */ + chordlen[i] = len; + for(j = 0; j < len; j++) chordnotes[i][j] = notes[j]; + return; + } + } + if(chordsnamed > MAXCHORDNAMES - 1) + abc_message("Failure: Too many Guitar Chord Names used, %s", s); + else { + strcpy(chordname[chordsnamed], s); + chordlen[chordsnamed] = len; + for(j = 0; j < len; j++) chordnotes[chordsnamed][j] = notes[j]; + chordsnamed++; + } +} + +static void abc_setup_chordnames() +// set up named guitar chords +{ + static const int list_Maj[3] = { 0, 4, 7 }; + static const int list_m[3] = { 0, 3, 7 }; + static const int list_7[4] = { 0, 4, 7, 10 }; + static const int list_m7[4] = { 0, 3, 7, 10 }; + static const int list_maj7[4] = { 0, 4, 7, 11 }; + static const int list_M7[4] = { 0, 4, 7, 11 }; + static const int list_6[4] = { 0, 4, 7, 9 }; + static const int list_m6[4] = { 0, 3, 7, 9 }; + static const int list_aug[3] = { 0, 4, 8 }; + static const int list_plus[3] = { 0, 4, 8 }; + static const int list_aug7[4] = { 0, 4, 8, 10 }; + static const int list_dim[3] = { 0, 3, 6 }; + static const int list_dim7[4] = { 0, 3, 6, 9 }; + static const int list_9[5] = { 0, 4, 7, 10, 2 }; + static const int list_m9[5] = { 0, 3, 7, 10, 2 }; + static const int list_maj9[5] = { 0, 4, 7, 11, 2 }; + static const int list_M9[5] = { 0, 4, 7, 11, 2 }; + static const int list_11[6] = { 0, 4, 7, 10, 2, 5 }; + static const int list_dim9[5] = { 0, 4, 7, 10, 13 }; + static const int list_sus[3] = { 0, 5, 7 }; + static const int list_sus9[3] = { 0, 2, 7 }; + static const int list_7sus[4] = { 0, 5, 7, 10 }; + static const int list_7sus4[4] = { 0, 5, 7, 10 }; + static const int list_7sus9[4] = { 0, 2, 7, 10 }; + static const int list_9sus4[5] = { 0, 5, 10, 14, 19 }; + static const int list_5[2] = { 0, 7 }; + static const int list_13[6] = { 0, 4, 7, 10, 16, 21 }; + + chordsnamed = 0; + abc_addchordname("", 3, list_Maj); + abc_addchordname("m", 3, list_m); + abc_addchordname("7", 4, list_7); + abc_addchordname("m7", 4, list_m7); + abc_addchordname("maj7", 4, list_maj7); + abc_addchordname("M7", 4, list_M7); + abc_addchordname("6", 4, list_6); + abc_addchordname("m6", 4, list_m6); + abc_addchordname("aug", 3, list_aug); + abc_addchordname("+", 3, list_plus); + abc_addchordname("aug7", 4, list_aug7); + abc_addchordname("7+", 4, list_aug7); + abc_addchordname("dim", 3, list_dim); + abc_addchordname("dim7", 4, list_dim7); + abc_addchordname("9", 5, list_9); + abc_addchordname("m9", 5, list_m9); + abc_addchordname("maj9", 5, list_maj9); + abc_addchordname("M9", 5, list_M9); + abc_addchordname("11", 6, list_11); + abc_addchordname("dim9", 5, list_dim9); + abc_addchordname("sus", 3, list_sus); + abc_addchordname("sus9", 3, list_sus9); + abc_addchordname("7sus", 4, list_7sus); + abc_addchordname("7sus4", 4, list_7sus4); + abc_addchordname("7sus9", 4, list_7sus9); + abc_addchordname("9sus4", 5, list_9sus4); + abc_addchordname("5", 2, list_5); + abc_addchordname("13", 6, list_13); +} + +static int abc_MIDI_getnumber(const char *p) +{ + int n; + while( isspace(*p) ) p++; + abc_getnumber(p, &n); + if( n < 0 ) n = 0; + if( n > 127 ) n = 127; + return n; +} + +static int abc_MIDI_getprog(const char *p) +{ + int n; + while( isspace(*p) ) p++; + abc_getnumber(p, &n); + if( n < 1 ) n = 1; + if( n > 128 ) n = 128; + return n; +} + +// MIDI drone <instr0> <pitch1> <pitch2> <vel1> <vel2> +static void abc_MIDI_drone(const char *p, int *gm, int *ptch, int *vol) +{ + int i; + while( isspace(*p) ) p++; + p += abc_getnumber(p, &i); + i++; // adjust for 1..128 + if( i>0 && i < 129 ) + *gm = i; + else + *gm = 71; // bassoon + while( isspace(*p) ) p++; + p += abc_getnumber(p, &i); + if( i>0 && i < 127 ) + ptch[0] = i; + else + ptch[0] = 45; + while( isspace(*p) ) p++; + p += abc_getnumber(p, &i); + if( i>0 && i < 127 ) + ptch[1] = i; + else + ptch[1] = 33; + while( isspace(*p) ) p++; + p += abc_getnumber(p, &i); + if( i>0 && i < 127 ) + vol[0] = i; + else + vol[0] = 80; + while( isspace(*p) ) p++; + p += abc_getnumber(p, &i); + if( i>0 && i < 127 ) + vol[1] = i; + else + vol[1] = 80; +} + +static void abc_chan_to_tracks(ABCHANDLE *h, int tno, int ch) +{ + ABCTRACK *tp; + if( tno>0 && tno<33 ) { + for( tp=h->track; tp; tp=tp->next ) { + if( tp->vno == tno && (tp->vpos < GCHORDBPOS || tp->vpos > DRONEPOS2) ) + tp->chan = ch; + } + } +} + +// %%MIDI channel int1 +// channel numbers are 1-16 +static void abc_MIDI_channel(const char *p, ABCTRACK *tp, ABCHANDLE *h) +{ + int i1, i2; + i1 = tp? tp->vno: 1; + for( ; *p && isspace(*p); p++ ) ; + if( isdigit(*p) ) { + p += abc_getnumber(p, &i2); + if( i2 >= 1 && i2 <= 16 ) + abc_chan_to_tracks(h, i1, i2); // we start at 1 + } +} + +static void abc_instr_to_tracks(ABCHANDLE *h, int tno, int gm) +{ + ABCTRACK *tp; + if( tno>0 && tno<33 && gm>0 && gm<129 ) { + for( tp=h->track; tp; tp=tp->next ) { + if( tp->vno == tno && (tp->vpos < GCHORDBPOS || tp->vpos > DRONEPOS2) ) + tp->instr = gm; + } + } +} + +// %%MIDI program [int1] <int2> +// instrument numbers are 0-127 +static void abc_MIDI_program(const char *p, ABCTRACK *tp, ABCHANDLE *h) +{ + int i1, i2; + i1 = tp? tp->vno: 1; + for( ; *p && isspace(*p); p++ ) ; + if( isdigit(*p) ) { + p += abc_getnumber(p, &i2); + for( ; *p && isspace(*p); p++ ) ; + if( isdigit(*p) ) { + i1 = i2; + p += abc_getnumber(p, &i2); + } + abc_instr_to_tracks(h, i1, i2 + 1); // we start at 1 + } +} + +static void abc_mute_voice(ABCHANDLE *h, ABCTRACK *tp, int m) +{ + ABCTRACK *t; + for( t=h->track; t; t=t->next ) { + if( t->vno == tp->vno ) t->mute = m; + } +} + +// %%MIDI voice [<ID>] [instrument=<integer> [bank=<integer>]] [mute] +// instrument numbers are 1-128 +static void abc_MIDI_voice(const char *p, ABCTRACK *tp, ABCHANDLE *h) +{ + int i1, i2; + for( ; *p && isspace(*p); p++ ) ; + if( strncmp(p,"instrument=",11) && strncmp(p,"mute",4) ) { + tp = abc_locate_track(h, p, 0); + for( ; *p && !isspace(*p); p++ ) ; + for( ; *p && isspace(*p); p++ ) ; + } + i1 = tp? tp->vno: 1; + i2 = 0; + if( !strncmp(p,"instrument=",11) && isdigit(p[11]) ) { + p += 11; + p += abc_getnumber(p, &i2); + for( ; *p && isspace(*p); p++ ) ; + if( !strncmp(p,"bank=",5) && isdigit(p[5]) ) { + for( ; *p && !isspace(*p); p++ ) ; + for( ; *p && isspace(*p); p++ ) ; + } + } + if( tp ) abc_mute_voice(h,tp,0); + if( !strncmp(p,"mute",4) && (p[4]=='\0' || p[4]=='%' || isspace(p[4])) ) { + if( tp ) abc_mute_voice(h,tp,1); + } + abc_instr_to_tracks(h, i1, i2); // starts already at 1 (draft 4.0) +} + +// %%MIDI chordname <string> <int1> <int2> ... <int6> +static void abc_MIDI_chordname(const char *p) +{ + char name[20]; + int i, notes[6]; + + for( ; *p && isspace(*p); p++ ) ; + i = 0; + while ((i < 19) && (*p != ' ') && (*p != '\0')) { + name[i] = *p; + p = p + 1; + i = i + 1; + } + name[i] = '\0'; + if(*p != ' ') { + abc_message("Failure: Bad format for chordname command, %s", p); + } + else { + i = 0; + while ((i <= 6) && isspace(*p)) { + for( ; *p && isspace(*p); p++ ) ; + p += abc_getnumber(p, ¬es[i]); + i = i + 1; + } + abc_addchordname(name, i, notes); + } +} + +// %%MIDI drum <string> <inst 1> ... <inst n> <vol 1> ... <vol n> +// instrument numbers are 0-127 +static int abc_MIDI_drum(const char *p, ABCHANDLE *h) +{ + char *q; + int i,n,m; + while( isspace(*p) ) p++; + if( !strncmp(p,"on",2) && (isspace(p[2]) || p[2] == '\0') ) return 2; + if( !strncmp(p,"off",3) && (isspace(p[3]) || p[3] == '\0') ) return 1; + n = 0; + for( q = h->drum; *p && !isspace(*p); p++ ) { + if( !strchr("dz0123456789",*p) ) break; + *q++ = *p; + if( !isdigit(*p) ) { + if( !isdigit(p[1]) ) *q++ = '1'; + n++; // count the silences too.... + } + } + *q = '\0'; + q = h->drumins; + for( i = 0; i<n; i++ ) { + if( h->drum[i*2] == 'd' ) { + while( isspace(*p) ) p++; + if( !isdigit(*p) ) { + m = 0; + while( !isspace(*p) ) p++; + } + else + p += abc_getnumber(p,&m); + q[i] = m + 1; // we start at 1 + } + else q[i] = 0; + } + q = h->drumvol; + for( i = 0; i<n; i++ ) { + if( h->drum[i*2] == 'd' ) { + while( isspace(*p) ) p++; + if( !isdigit(*p) ) { + m = 0; + while( !isspace(*p) ) p++; + } + else + p += abc_getnumber(p,&m); + q[i] = m; + } + else q[i] = 0; + } + return 0; +} + +// %%MIDI gchord <string> +static int abc_MIDI_gchord(const char *p, ABCHANDLE *h) +{ + char *q; + while( isspace(*p) ) p++; + if( !strncmp(p,"on",2) && (isspace(p[2]) || p[2] == '\0') ) return 2; + if( !strncmp(p,"off",3) && (isspace(p[3]) || p[3] == '\0') ) return 1; + for( q = h->gchord; *p && !isspace(*p); p++ ) { + if( !strchr("fbcz0123456789ghijGHIJ",*p) ) break; + *q++ = *p; + if( !isdigit(*p) && !isdigit(p[1]) ) *q++ = '1'; + } + *q = '\0'; + return 0; +} + +static void abc_metric_gchord(ABCHANDLE *h, int mlen, int mdiv) +{ + switch( 16 * mlen + mdiv ) { + case 0x24: + case 0x44: + case 0x22: + abc_MIDI_gchord("fzczfzcz", h); + break; + case 0x64: + case 0x32: + abc_MIDI_gchord("fzczczfzczcz", h); + break; + case 0x34: + case 0x38: + abc_MIDI_gchord("fzczcz", h); + break; + case 0x68: + abc_MIDI_gchord("fzcfzc", h); + break; + case 0x98: + abc_MIDI_gchord("fzcfzcfzc", h); + break; + case 0xc8: + abc_MIDI_gchord("fzcfzcfzcfzc", h); + break; + default: + if( mlen % 3 == 0 ) + abc_MIDI_gchord("fzcfzcfzcfzcfzcfzcfzcfzcfzc", h); + else + abc_MIDI_gchord("fzczfzczfzczfzczfzczfzczfzcz", h); + if( mdiv == 8 ) h->gchord[mlen*2] = '\0'; + else h->gchord[mlen*4] = '\0'; + break; + } +} + +static void abc_MIDI_legato(const char *p, ABCTRACK *tp) +{ + for( ; *p && isspace(*p); p++ ) ; + if( !strncmp(p,"off",3) ) tp->legato = 0; + else tp->legato = 1; +} + +static void abc_M_field(const char *p, int *mlen, int *mdiv) +{ + if( !strncmp(p,"none",4) ) { + *mlen = 1; + *mdiv = 1; + return; + } + if( !strncmp(p,"C|",2) ) { + *mlen = 2; + *mdiv = 2; + return; + } + if( *p == 'C' ) { + *mlen = 4; + *mdiv = 4; + return; + } + p += abc_getexpr(p,mlen); + sscanf(p," / %d", mdiv); +} + +static int abc_drum_steps(const char *dch) +{ + const char *p; + int i=0; + for( p=dch; *p; p++ ) { + if( isdigit(*p) ) i += *p - '0';; + } + return i; +} + +static void abc_add_drum(ABCHANDLE *h, uint32_t tracktime, uint32_t bartime) +{ + ABCEVENT *e; + ABCTRACK *tp; + uint32_t etime, ctime , rtime, stime; + int i, g, steps, gnote, gsteps, nnum; + steps = abc_drum_steps(h->drum); + ctime = h->barticks; + // look up the last event in tpr drumtrack + tp = abc_locate_track(h, h->tpr->v, DRUMPOS); + e = tp->tail; + etime = e? e->tracktick: bartime; + if( etime > tracktime ) return; + if( etime < bartime ) rtime = h->barticks - ((bartime - etime) % h->barticks); + else rtime = (etime - bartime) % h->barticks; + stime = ctime*steps; + rtime *= steps; + rtime += stime; + gsteps = strlen(h->drum)/2; + g = 0; + while( rtime > stime ) { + rtime -= ctime*(h->drum[g*2+1] - '0'); + if( ++g == gsteps ) g = 0; + } + stime = (tracktime - etime) * steps; + rtime = 0; + while( rtime < stime ) { + gnote = h->drum[g*2]; + i = h->drum[g*2+1] - '0'; + if(gnote=='d') { + tp->instr = pat_gm_drumnr(h->drumins[g]-1); + nnum = pat_gm_drumnote(h->drumins[g]); + abc_add_drumnote(h, tp, etime + rtime/steps, nnum, h->drumvol[g]); + abc_add_noteoff(h,tp,etime + ( rtime + ctime * i )/steps); + } + if( ++g == gsteps ) g = 0; + rtime += ctime * i; + } +} + +static int abc_gchord_steps(const char *gch) +{ + const char *p; + int i=0; + for( p=gch; *p; p++ ) + if( isdigit(*p) ) i += *p - '0'; + return i; +} + +static void abc_add_gchord(ABCHANDLE *h, uint32_t tracktime, uint32_t bartime) +{ + ABCEVENT *e, *c; + ABCTRACK *tp; + uint32_t etime, ctime , rtime, stime; + int i, g, steps, gnote, gcnum, gsteps, nnum, glen; + // look up the last chord event in tpc + c = 0; + for( e = h->tpc->head; e; e = e->next ) + if( e->flg == 1 && e->cmd == cmdchord ) + c = e; + if( !c ) return; + gcnum = c->par[chordnum]; + steps = abc_gchord_steps(h->gchord); + ctime = h->barticks; + etime = 0; + for( i = GCHORDBPOS; i < DRUMPOS; i++ ) { + tp = abc_locate_track(h, h->tpc->v, i); + e = tp->tail; + if( !e ) e = c; + stime = e->tracktick; + if( stime > etime ) etime = stime; + } + if( etime > tracktime ) return; + if( etime < bartime ) rtime = h->barticks - ((bartime - etime) % h->barticks); + else rtime = (etime - bartime) % h->barticks; + stime = ctime * steps; + rtime *= steps; + rtime += stime; + gsteps = strlen(h->gchord); + g = 0; + while( rtime > stime ) { + glen = h->gchord[2*g+1] - '0'; + rtime -= ctime * glen; + if( ++g == gsteps ) g = 0; + } + stime = (tracktime - etime) * steps; + rtime = 0; + while( rtime < stime ) { + gnote = h->gchord[2*g]; + glen = h->gchord[2*g+1] - '0'; + if( ++g == gsteps ) g = 0; + nnum = 0; + switch(gnote) { + case 'b': + tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS); + tp->instr = h->abcbassprog; + nnum = c->par[chordnote]+chordnotes[gcnum][0]+24; + abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcbassvol); + abc_add_noteoff(h,tp,etime + ( rtime + ctime * glen )/steps); + case 'c': + for( i = 1; i < chordlen[gcnum]; i++ ) { + tp = abc_locate_track(h, h->tpc->v, i+GCHORDFPOS); + tp->instr = h->abcchordprog; + nnum = c->par[chordnote]+chordnotes[gcnum][i]+24; + abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcchordvol); + abc_add_noteoff(h,tp,etime + ( rtime + ctime * glen )/steps); + } + rtime += ctime * glen; + break; + case 'f': + tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS); + tp->instr = h->abcbassprog; + nnum = c->par[chordbase]+12; + abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcbassvol); + rtime += ctime * glen; + abc_add_noteoff(h,tp,etime + rtime/steps); + break; + case 'g': + case 'h': + case 'i': + case 'j': + case 'G': + case 'H': + case 'I': + case 'J': + i = toupper(gnote) - 'G'; + nnum = 0; + if( i < chordlen[gcnum] ) { + tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS+i+1); + tp->instr = h->abcchordprog; + nnum = c->par[chordnote]+chordnotes[gcnum][i]+24; + if( isupper(gnote) ) nnum -= 12; + abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcchordvol); + } + rtime += ctime * glen; + if( nnum ) abc_add_noteoff(h,tp,etime + rtime/steps); + break; + case 'z': + rtime += ctime * glen; + break; + } + } +} + +// %%MIDI beat a b c n +// +// controls the way note velocities are selected. The first note in a bar has +// velocity a. Other "strong" notes have velocity b and all the rest have velocity +// c. a, b and c must be in the range 0-128. The parameter n determines which +// notes are "strong". If the time signature is x/y, then each note is given +// a position number k = 0, 1, 2 .. x-1 within each bar. Note that the units for +// n are not the unit note length. If k is a multiple of n, then the note is +// "strong". The volume specifiers !ppp! to !fff! are equivalent to the +// following : +// +// !ppp! = %%MIDI beat 30 20 10 1 +// !pp! = %%MIDI beat 45 35 20 1 +// !p! = %%MIDI beat 60 50 35 1 +// !mp! = %%MIDI beat 75 65 50 1 +// !mf! = %%MIDI beat 90 80 65 1 +// !f! = %%MIDI beat 105 95 80 1 +// !ff! = %%MIDI beat 120 110 95 1 +// !fff! = %%MIDI beat 127 125 110 1 +static void abc_MIDI_beat(ABCHANDLE *h, const char *p) +{ + int i,j; + h->beat[0] = 127; + h->beat[1] = 125; + h->beat[2] = 110; + h->beat[3] = 1; + for( j=0; j<4; j++ ) { + while( isspace(*p) ) p++; + if( *p ) { + p += abc_getnumber(p, &i); + if( i < 0 ) i = 0; + if( i > 127 ) i = 127; + h->beat[j] = i; + } + } + if( h->beat[3] == 0 ) h->beat[3] = 1; // BB Ruud says: do not let you make mad +} + +// +// %%MIDI beatstring <string of f, m and p> +// +// This provides an alternative way of specifying where the strong and weak +// stresses fall within a bar. 'f' means velocity a (normally strong), 'm' +// means velocity b (medium velocity) and 'p' means velocity c (soft velocity). +// For example, if the time signature is 7/8 with stresses on the first, fourth +// and sixth notes in the bar, we could use the following +// +// %%MIDI beatstring fppmpmp +static void abc_MIDI_beatstring(ABCHANDLE *h, const char *p) +{ + while( isspace(*p) ) p++; + if( h->beatstring ) _mm_free(h->allochandle, h->beatstring); + if( strlen(p) ) + h->beatstring = DupStr(h->allochandle,p,strlen(p)+1); + else + h->beatstring = NULL; +} + +static int abc_beat_vol(ABCHANDLE *h, int abcvol, int barpos) +{ + int vol; + if( h->beatstring ) { + vol = (h->beat[2] * 9) / 10; + if( barpos < (int)strlen(h->beatstring) ) { + switch(h->beatstring[barpos]) { + case 'f': + vol = h->beat[0]; + break; + case 'm': + vol = h->beat[1]; + break; + case 'p': + vol = h->beat[2]; + break; + default: + break; + } + } + } + else { + if( (barpos % h->beat[3]) == 0 ) { + if( barpos ) + vol = h->beat[1]; + else + vol = h->beat[0]; + } + else + vol = h->beat[2]; + } + vol *= abcvol; + vol /= 128; + return vol; +} + +static void abc_init_partpat(BYTE partp[27][2]) +{ + int i; + for( i=0; i<27; i++ ) { + partp[i][0] = 0xff; + partp[i][1] = 0; + } +} + +static int abc_partpat_to_orderlist(BYTE partp[27][2], const char *abcparts, ABCHANDLE *h, BYTE **list, int orderlen) +{ + int t, partsused; + const char *p; + BYTE *orderlist = *list; + static int ordersize = 0; + if( *list == NULL ) { + ordersize = 128; + orderlist = (BYTE *)_mm_calloc(h->ho, ordersize, sizeof(BYTE)); + *list = orderlist; + } + if( abcparts ) { + partsused = 0; + for( p = abcparts; *p; p++ ) { + for( t = partp[*p - 'A'][0]; t < partp[*p - 'A'][1]; t++ ) { + if( orderlen == ordersize ) { + ordersize <<= 1; + orderlist = (BYTE *)_mm_recalloc(h->ho, orderlist, ordersize, sizeof(BYTE)); + *list = orderlist; + } + orderlist[orderlen] = t; + orderlen++; + partsused++; + } + } + if( partsused ) return orderlen; + } + // some fool wrote a P: string in the header but didn't use P: in the body + for( t = partp[26][0]; t < partp[26][1]; t++ ) { + if( orderlen == ordersize ) { + ordersize <<= 1; + orderlist = (BYTE *)_mm_recalloc(h->ho, orderlist, ordersize, sizeof(BYTE)); + *list = orderlist; + } + orderlist[orderlen] = t; + orderlen++; + } + return orderlen; +} + +static void abc_globalslide(ABCHANDLE *h, uint32_t tracktime, int slide) +{ + ABCTRACK *tp; + ABCEVENT *e; + int hslide; + hslide = h->track? h->track->slidevol: slide; + for( tp=h->track; tp; tp = tp->next ) { + if( slide ) { + tp->slidevoltime = tracktime; + if( slide == 2 ) + tp->slidevol = 0; + } + if( tp->slidevol > -2 && slide < 2 ) + tp->slidevol = slide; + } + if( h->track && h->track->tail + && hslide != slide && slide == -2 + && h->track->tail->tracktick >= tracktime ) { + // need to update jumptypes in mastertrack from tracktime on... + for( e=h->track->head; e; e=e->next ) { + if( e->flg == 1 && e->cmd == cmdjump && e->tracktick >= tracktime ) { + switch( e->par[jumptype] ) { + case jumpnormal: + case jumpfade: + e->par[jumptype] = jumpfade; + break; + case jumpdacapo: + case jumpdcfade: + e->par[jumptype] = jumpdcfade; + break; + case jumpdasegno: + case jumpdsfade: + e->par[jumptype] = jumpdsfade; + break; + } + } + } + } +} + +static void abc_recalculate_tracktime(ABCHANDLE *h) { + ABCTRACK *ttp; + h->tracktime = 0; + for( ttp=h->track; ttp; ttp=ttp->next ) + if( ttp->tail && ttp->tail->tracktick > h->tracktime ) + h->tracktime = ttp->tail->tracktick; +} + +static void abc_MIDI_command(ABCHANDLE *h, char *p, char delim) { + int t; + // interpret some of the possibilitys + if( !strncmp(p,"bassprog",8) && isspace(p[8]) ) h->abcbassprog = abc_MIDI_getprog(p+8)+1; + if( !strncmp(p,"bassvol",7) && isspace(p[7]) ) h->abcbassvol = abc_MIDI_getnumber(p+7); + if( !strncmp(p,"beat",4) && isspace(p[4]) ) abc_MIDI_beat(h, p+4); + if( !strncmp(p,"beatstring",10) && isspace(p[10]) ) abc_MIDI_beatstring(h, p+4); + if( !strncmp(p,"chordname",9) && isspace(p[9]) ) abc_MIDI_chordname(p+9); + if( !strncmp(p,"chordprog",9) && isspace(p[9]) ) h->abcchordprog = abc_MIDI_getprog(p+9)+1; + if( !strncmp(p,"chordvol",8) && isspace(p[8]) ) h->abcchordvol = abc_MIDI_getnumber(p+8); + if( !strncmp(p,"drone",5) && isspace(p[5]) ) abc_MIDI_drone(p+5, &h->dronegm, h->dronepitch, h->dronevol); + if( !strncmp(p,"droneoff",8) && (p[8]=='\0' || p[8]==delim || isspace(p[8])) ) h->droneon = 0; + if( !strncmp(p,"droneon",7) && (p[7]=='\0' || p[7]==delim || isspace(p[7])) ) h->droneon = 1; + t = h->drumon; + if( !strncmp(p,"drum",4) && isspace(p[4]) ) { + h->drumon = abc_MIDI_drum(p+4, h); + if( h->drumon ) --h->drumon; + else h->drumon = t; + } + if( !strncmp(p,"drumoff",7) && (p[7]=='\0' || p[7]==delim || isspace(p[7])) ) h->drumon = 0; + if( !strncmp(p,"drumon",6) && (p[6]=='\0' || p[6]==delim || isspace(p[6])) ) h->drumon = 1; + if( t != h->drumon ) { + if( h->drumon && !h->tpr ) h->tpr = h->track; + if( h->tpr ) abc_add_drum_sync(h, h->tpr, h->tracktime); // don't start drumming from the beginning of time! + if( h->tpr && !h->drumon ) h->tpr = NULL; + } + t = h->gchordon; + if( !strncmp(p,"gchord",6) && (p[6]=='\0' || p[6]==delim || isspace(p[6])) ) { + h->gchordon = abc_MIDI_gchord(p+6, h); + if( h->gchordon ) --h->gchordon; + else h->gchordon = t; + } + if( !strncmp(p,"gchordoff",9) && (p[9]=='\0' || p[9]==delim || isspace(p[9])) ) h->gchordon = 0; + if( !strncmp(p,"gchordon",8) && (p[8]=='\0' || p[8]==delim || isspace(p[8])) ) h->gchordon = 1; + if( t != h->gchordon ) { + if( h->tpc ) abc_add_gchord_syncs(h, h->tpc, h->tracktime); + } + if( !strncmp(p,"channel",7) && isspace(p[7]) ) + abc_MIDI_channel(p+8, h->tp = abc_check_track(h, h->tp), h); + if( !strncmp(p,"program",7) && isspace(p[7]) ) + abc_MIDI_program(p+8, h->tp = abc_check_track(h, h->tp), h); + if( !strncmp(p,"voice",5) && isspace(p[5]) ) + abc_MIDI_voice(p+6, h->tp = abc_check_track(h, h->tp), h); + if( !strncmp(p,"legato",6) && (p[6]=='\0' || p[6]==delim || isspace(p[6])) ) + abc_MIDI_legato(p+6, h->tp = abc_check_track(h, h->tp)); +} + +// continuate line that ends with a backslash, can't do this in abc_gets because voice lines +// can have comment lines in between that must be parsed properly, for example: +// [V:1] cdef gabc' |\ << continuation backslash +// %%MIDI program 25 +// c'bag fedc | +// informational lines can have this too, so it is rather convoluted code... +static char *abc_continuated(ABCHANDLE *h, MMFILE *mmf, char *p) { + char *pm, *p1, *p2 = 0; + int continued; + pm = p; + while( pm[strlen(pm)-1]=='\\' ) { + p1 = strdup(pm); + if( p2 ) free(p2); + continued = 1; + while( continued ) { + continued = 0; + pm = abc_gets(h, mmf); + if( !pm ) { + abc_message("line not properly continued\n%s", p1); + return p1; + } + while( *pm && isspace(*pm) ) ++pm; + if( !strncmp(pm,"%%",2) ) { + for( p2 = pm+2; *p2 && isspace(*p2); p2++ ) ; + if( !strncmp(p2,"MIDI",4) && (p2[4]=='=' || isspace(p2[4])) ) { + for( p2+=5; *p2 && isspace(*p2); p2++ ) ; + if( *p2 == '=' ) + for( p2+=1; *p2 && isspace(*p2); p2++ ) ; + abc_MIDI_command(h,p2,'%'); + } + continued = 1; + } + } + p2 = (char *)malloc(strlen(p1)+strlen(pm)); + if( !p2 ) { + abc_message("macro line too long\n%s", p1); + return p1; + } + p1[strlen(p1)-1] = '\0'; // strip off the backslash + strcpy(p2,p1); + strcat(p2,pm); + pm = p2; + free(p1); + } + return pm; +} + +// ===================================================================================== +#ifdef NEWMIKMOD +BOOL ABC_Load(ABCHANDLE *h, UNIMOD *of, MMSTREAM *mmfile) +#else +BOOL CSoundFile::ReadABC(const uint8_t *lpStream, DWORD dwMemLength) +#endif +{ + static int avoid_reentry = 0; +#ifdef NEWMIKMOD +#define m_nDefaultTempo of->inittempo +#else + ABCHANDLE *h; + uint32_t numpat; + MMFILE mm, *mmfile; +#endif + uint32_t t; + char *line, *p, *pp, ch, ch0=0; + char barsig[52]; // for propagated accidental key signature within bar + char *abcparts; + uint8_t partpat[27][2], *orderlist; + int orderlen; + enum { NOWHERE, INBETWEEN, INHEAD, INBODY, INSKIPFORX, INSKIPFORQUOTE } abcstate; + ABCEVENT_JUMPTYPE j; + ABCEVENT_X_EFFECT abceffect; + int abceffoper; + int abcxcount=0, abcxwanted=0, abcxnumber=1; + int abckey, abcrate, abcchord, abcvol, abcbeatvol, abcnoslurs, abcnolegato, abcfermata, abcarpeggio, abcto; + int abctempo; + int cnotelen=0, cnotediv=0, snotelen, snotediv, mnotelen, mnotediv, notelen, notediv; + // c for chords, s for standard L: setting, m for M: barlength + int abchornpipe, brokenrithm, tupletp, tupletq, tupletr; + int ktempo; + uint32_t abcgrace=0, bartime, thistime=0; + ABCTRACK *tpd, *ttp; + ABCMACRO *mp; + int mmsp; +#ifdef NEWMIKMOD + MMSTREAM *mmstack[MAXABCINCLUDES]; + h->ho = _mmalloc_create("Load_ABC_ORDERLIST", NULL); +#else + MMFILE *mmstack[MAXABCINCLUDES]; + if( !TestABC(lpStream, dwMemLength) ) return FALSE; + h = ABC_Init(); + if( !h ) return FALSE; + mmfile = &mm; + mm.mm = (char *)lpStream; + mm.sz = dwMemLength; + mm.pos = 0; +#endif + while( avoid_reentry ) sleep(1); + avoid_reentry = 1; + pat_resetsmp(); + pat_init_patnames(); + m_nDefaultTempo = 0; + global_voiceno = 0; + abckey = 0; + h->tracktime = 0; + global_songstart = 0; + h->speed = 6; + abcrate = 240; + global_tempo_factor = 2; + global_tempo_divider = 1; + abctempo = 0; + ktempo = 0; + abceffect = none; + abceffoper = 0; + abcvol = 120; + h->abcchordvol = abcvol; + h->abcbassvol = abcvol; + h->abcchordprog = 25; // acoustic guitar + h->abcbassprog = 33; // acoustic bass + abcparts = 0; + abcnoslurs = 1; + abcnolegato = 1; + abcfermata = 0; + abcarpeggio = 0; + abcto = 0; + snotelen = 0; + snotediv = 0; + mnotelen = 1; + mnotediv = 1; + abchornpipe = 0; + brokenrithm = 0; + tupletp = 0; + tupletq = 0; + tupletr = 0; + h->ktrans = 0; + h->drumon = 0; + h->gchordon = 1; + h->droneon = 0; + h->tracktime = 0; + bartime = 0; + h->tp = NULL; + h->tpc = NULL; + h->tpr = NULL; + tpd = NULL; + h->dronegm = 71; + h->dronepitch[0] = 45; + h->dronepitch[1] = 33; + h->dronevol[0] = 80; + h->dronevol[1] = 80; + abc_new_umacro(h, "v = +downbow+"); + abc_new_umacro(h, "u = +upbow+"); + abc_new_umacro(h, "O = +coda+"); + abc_new_umacro(h, "S = +segno+"); + abc_new_umacro(h, "P = +uppermordent+"); + abc_new_umacro(h, "M = +lowermordent+"); + abc_new_umacro(h, "L = +emphasis+"); + abc_new_umacro(h, "H = +fermata+"); + abc_new_umacro(h, "T = +trill+"); + abc_new_umacro(h, "~ = +roll+"); + abc_setup_chordnames(); + abc_init_partpat(partpat); + abc_MIDI_beat(h, ""); // reset beat array + abc_MIDI_beatstring(h, ""); // reset beatstring + orderlist = NULL; + orderlen = 0; + mmsp = 1; + mmstack[0] = mmfile; + mmfseek(mmfile,0,SEEK_SET); + abcstate = NOWHERE; + if( h->pickrandom ) { + abcstate = INSKIPFORX; + abcxcount = 0; + mmfseek(mmfile,0,SEEK_SET); + while( (line=abc_gets(h, mmfile)) ) { + for( p=line; isspace(*p); p++ ) ; + if( !strncmp(p,"X:",2) ) abcxcount++; + } + if( abcxcount == 0 ) + abcstate = NOWHERE; + else + abcxwanted = (h->pickrandom - 1) % abcxcount; + abcxcount = 0; + mmfseek(mmfile,0,SEEK_SET); + } + while( mmsp > 0 ) { + mmsp--; + while((line=abc_gets(h, mmstack[mmsp]))) { + for( p=line; isspace(*p); p++ ) ; + switch(abcstate) { + case INSKIPFORX: + if( !strncmp(p,"X:",2) ) { + if( abcxcount++ != abcxwanted ) + break; + } + // fall through + case INBETWEEN: + if( !strncmp(p,"X:",2) ) { + abcstate = INHEAD; +#ifdef NEWMIKMOD + of->songname = NULL; +#else + memset(m_szNames[0], 0, 32); +#endif + for( p+=2; isspace(*p); p++ ) ; + abcxnumber = atoi(p); + abchornpipe = 0; + h->droneon = 0; + h->dronegm = 71; + h->dronepitch[0] = 45; + h->dronepitch[1] = 33; + h->dronevol[0] = 80; + h->dronevol[1] = 80; + for( ttp = h->track; ttp; ttp=ttp->next ) { + ttp->vno = 0; // mark track unused + ttp->capostart = NULL; + } + h->tp = NULL; // forget old voices + h->tpc = NULL; + h->tpr = NULL; + global_voiceno = 0; + abc_set_parts(&abcparts, 0); + abcgrace = 0; + h->ktrans = 0; + ktempo = 0; + h->gchordon = 1; + h->drumon = 0; + global_songstart = h->tracktime; + abc_MIDI_beat(h, ""); // reset beat array + abc_MIDI_beatstring(h, ""); // reset beatstring + strcpy(h->gchord, ""); // reset gchord string + abcnolegato = 1; // reset legato switch + } + break; + case NOWHERE: + if( p[0] != '\0' && p[1] == ':' ) { + abcstate = INHEAD; + abc_set_parts(&abcparts, 0); + strcpy(h->gchord, ""); + if( h->drumon && h->tpr ) abc_add_drum_sync(h, h->tpr, h->tracktime); + if( h->tpc && !h->gchordon ) abc_add_gchord_syncs(h, h->tpc, h->tracktime); + h->gchordon = 1; + h->drumon = 0; + } + else + break; + case INHEAD: + if( !strncmp(p,"L:",2) ) { + sscanf(p+2," %d / %d", &snotelen, &snotediv); + break; + } + if( !strncmp(p,"M:",2) ) { + abc_M_field(p+2, &mnotelen, &mnotediv); + break; + } + if( !strncmp(p,"P:",2) ) { + abc_set_parts(&abcparts, p+2); + break; + } + if( !strncmp(p,"Q:",2) ) { + abctempo = abc_extract_tempo(p+2,0); + ktempo = 1; + if( h->track ) { + // make h->tracktime start of a new age... + abc_add_partbreak(h, h->track, h->tracktime); + abc_add_tempo_event(h, h->track, h->tracktime, abctempo); + } + if( m_nDefaultTempo == 0 ) m_nDefaultTempo = abctempo; + break; + } + if( !strncmp(p,"T:",2) ) { + char buf[200]; + if( strchr(p,'%') ) *strchr(p,'%') = '\0'; + for( t=strlen(p)-1; isspace(p[t]); t-- ) + p[t]='\0'; + for( t=2; isspace(p[t]); t++ ) ; +#ifdef NEWMIKMOD + if( of->songname ) + strcpy(buf,of->songname); + else + strcpy(buf,""); +#else + strcpy(buf,m_szNames[0]); +#endif + if( strlen(buf) + strlen(p+t) > 199 ) p[t+199-strlen(buf)] = '\0'; // chop it of + if( strlen(buf) ) strcat(buf," "); // add a space + strcat(buf, p+t); +#ifdef NEWMIKMOD + of->songname = DupStr(of->allochandle, buf, strlen(buf)); +#else + if( strlen(buf) > 31 ) buf[31] = '\0'; // chop it of + strcpy(m_szNames[0], buf); +#endif + break; + } + if( !strncmp(p,"R:",2) ) { + for( p+=2; isspace(*p); p++ ) ; + if( !strncmp(p,"hornpipe",8) && (isspace(p[8]) || p[8]=='\0') ) abchornpipe = 1; + else abchornpipe = 0; + break; + } + if( !strncmp(p,"V:",2) ) { + for( t=2; p[t]==' '; t++ ) ; + h->tp = abc_locate_track(h, p+t, 0); + abcvol = h->tp->volume; + abcnolegato = !h->tp->legato; + if( !abcnolegato ) abcnoslurs = 0; + break; + } + if( !strncmp(p,"K:",2) ) { + abcstate = INBODY; + abckey = ABC_Key(p+2); + sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature + p = abc_skip_word(p+2); + h->ktrans = abc_transpose(p); + *p = '%'; // force skip rest of line + if( snotelen == 0 ) { // calculate default notelen from meter M: + if( mnotediv == 0 ) mnotediv = mnotelen = 1; // do'nt get nuked + snotelen = 100 * mnotelen / mnotediv; + if( snotelen > 74 ) + snotediv = 8; + else + snotediv = 16; + snotelen = 1; + } + abceffect = none; + abceffoper = 0; + if( !(snotelen == 1 && snotediv == 8) ) abchornpipe = 0; // no matter what they said at R: + brokenrithm = 0; + global_part = ' '; + abcgrace = 0; + abcnoslurs = abcnolegato; + abcto = 0; + h->tpc = NULL; // reset chord track + tpd = NULL; // reset drone track + h->tpr = NULL; // reset drum track + if( !strlen(h->gchord) ) abc_metric_gchord(h, mnotelen, mnotediv); + h->barticks = notelen_notediv_to_ticks(h->speed, mnotelen, mnotediv); + if( abctempo && !ktempo ) { // did not set tempo in this songpiece so reset to abcrate + abctempo = 0; + global_tempo_factor = 2; + global_tempo_divider = 1; + if( h->track ) { + // make h->tracktime start of a new age... + abc_add_partbreak(h, h->track, h->tracktime); + abc_add_tempo_event(h, h->track, h->tracktime, abcrate); + } + if( m_nDefaultTempo == 0 ) m_nDefaultTempo = abcrate; + } + abc_init_partpat(partpat); + partpat[26][0] = abc_patno(h, h->tracktime); + partpat[26][1] = 0; + abc_globalslide(h, h->tracktime, 2); // reset all volumeslides + break; + } + if( !strlen(p) ) + abcstate = INBETWEEN; + break; + case INSKIPFORQUOTE: + while( (ch=*p++) && (ch != '"') ) + ; + if( !ch ) break; + abcstate = INBODY; + // fall through + case INBODY: + if( !strlen(p) && h->track ) { // end of this song + abcstate = h->pickrandom? INSKIPFORX: INBETWEEN; + // last but not least shut off all pending events + abc_recalculate_tracktime(h); + for( ttp=h->track; ttp; ttp=ttp->next ) + abc_add_noteoff(h,ttp,h->tracktime); + abc_add_partbreak(h, h->track, h->tracktime); + t = abc_patno(h, h->tracktime); + if( abc_pattracktime(h, h->tracktime) % abcticks(64 * h->speed) ) t++; + if( global_part == ' ' ) { + partpat[26][1] = t; + if( abcparts ) { + for( t=0; t<26; t++ ) + if( partpat[t][0] < partpat[t][1] ) break; + if( t == 26 ) { + abc_message("parts (%s) set but not used", abcparts); + abc_set_parts(&abcparts, 0); // forget the parts array + } + } + } + else + partpat[global_part - 'A'][1] = t; + if( !abcparts ) abc_song_to_parts(h, &abcparts, partpat); + orderlen = abc_partpat_to_orderlist(partpat, abcparts, h, &orderlist, orderlen); + } + if( !strncmp(p,"V:",2) ) { + for( t=2; p[t]==' '; t++ ) ; + h->tp = abc_locate_track(h, p+t, 0); + sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature + abcgrace = 0; + brokenrithm = 0; + h->tracktime = abc_tracktime(h->tp); + bartime = h->tracktime; // it is not friendly to break voices in the middle of a track... + abcnolegato = !h->tp->legato; + if( !abcnolegato ) abcnoslurs = 0; + *p = '%'; // make me skip the rest of the line.... + } + if( !strncmp(p,"K:",2) ) { + abckey = ABC_Key(p+2); + sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature + p = abc_skip_word(p+2); + h->ktrans = abc_transpose(p); + *p = '%'; // make me skip the rest of the line.... + } + if( !strncmp(p,"L:",2) ) { + sscanf(p+2," %d / %d", &snotelen, &snotediv); + *p = '%'; // make me skip the rest of the line.... + } + if( !strncmp(p,"M:",2) ) { + abc_M_field(p+2, &mnotelen, &mnotediv); + h->barticks = notelen_notediv_to_ticks(h->speed, mnotelen, mnotediv); + *p = '%'; // make me skip the rest of the line.... + } + if( !strncmp(p,"Q:",2) ) { + abctempo = abc_extract_tempo(p+2,ch0=='\\'); + if( !h->track ) { + h->tp = abc_check_track(h, h->track); + h->tp->vno = 0; // mark reuseable (temporarely, until first notes come up) + } + abc_add_tempo_event(h, h->track, h->tracktime, abctempo); + *p = '%'; // make me skip the rest of the line.... + } + if( !strncmp(p,"T:",2) ) { + char buf[200]; + if( strchr(p,'%') ) *strchr(p,'%') = '\0'; + for( t=strlen(p)-1; isspace(p[t]); t-- ) + p[t]='\0'; + for( t=2; isspace(p[t]); t++ ) ; +#ifdef NEWMIKMOD + if( of->songname ) + strcpy(buf,of->songname); + else + strcpy(buf,""); +#else + strcpy(buf,m_szNames[0]); +#endif + if( strlen(buf) + strlen(p+t) > 198 ) p[t+198-strlen(buf)] = '\0'; // chop it of + if( strlen(buf) ) strcat(buf," "); // add a space + strcat(buf, p+t); +#ifdef NEWMIKMOD + of->songname = DupStr(of->allochandle, buf, strlen(buf)); +#else + if( strlen(buf) > 31 ) buf[31] = '\0'; // chop it of + strcpy(m_szNames[0], buf); +#endif + *p = '%'; // make me skip the rest of the line.... + } + break; + } + if( !strncmp(p,"m:",2) ) { + if( abcstate != INSKIPFORX ) { + char *pm; + pm = abc_continuated(h, mmstack[mmsp], p); + abc_new_macro(h, pm+2); + if( pm != p ) { + free(pm); + if( h->tp ) abcnolegato = !h->tp->legato; + if( !abcnolegato ) abcnoslurs = 0; + } + } + *p = '%'; // skip rest of line + } + if( !strncmp(p,"U:",2) ) { + abc_new_umacro(h, p+2); + *p = '%'; // skip rest of line + } + if( !strncmp(p,"w:",2) ) { // inline lyrics + *p = '%'; // skip rest of line + } + if( !strncmp(p,"W:",2) ) { // lyrics at end of song body + *p = '%'; // skip rest of line + } + if( !strncmp(p,"d:",2) ) { // oldstyle decorations + abc_message("warning: old style decorations not handled\n%s", p); + *p = '%'; // skip rest of line + } + if( !strncmp(p,"s:",2) ) { // newstyle decorations (symbols) + abc_message("warning: new style decorations not handled\n%s", p); + *p = '%'; // skip rest of line + } + if( !strncmp(p,"I:",2) && abcstate != INSKIPFORX ) { // handle like oldstyle '%%command' lines + p[0]= '%'; + p[1]= '%'; + } + if( !strncmp(p,"%%",2) ) { + for( p+=2; *p && isspace(*p); p++ ) ; + if( !strncmp(p,"abc-include",11) && isspace(p[11]) ) { + for( t=12; isspace(p[t]); t++ ) ; + if( p[t] ) { + mmsp++; + if( mmsp == MAXABCINCLUDES ) { + mmsp--; + abc_message("failure: too many abc-include's, %s", &p[t]); + } else { + mmstack[mmsp] = mmfopen(&p[t], "r"); + if( !mmstack[mmsp] ) { + mmsp--; + abc_message("failure: abc-include file %s not found", &p[t]); + } + } + } + else abc_message("failure: abc-include missing file name, %s", p); + } + if( !strncmp(p,"MIDI",4) && (p[4]=='=' || isspace(p[4])) && abcstate != INSKIPFORX ) { + for( p+=5; *p && isspace(*p); p++ ) ; + if( *p == '=' ) + for( p+=1; *p && isspace(*p); p++ ) ; + abc_MIDI_command(h,p,'%'); + if( h->tp ) abcnolegato = !h->tp->legato; + if( !abcnolegato ) abcnoslurs = 0; + } + if(*p) *p = '%'; // skip rest of line + } + if( abcstate == INBODY ) { + if( *p == 'P' && p[1] == ':' ) { // a line with a part indication + if( abcparts != NULL ) { + // make h->tracktime start of a new age... + if( !h->track ) { + h->tp = abc_check_track(h, h->track); + h->tp->vno = 0; // mark reuseable (temporarely, until first notes come up) + } + h->tracktime = h->track? abc_tracktime(h->track): 0; // global parts are voice independent + abc_add_partbreak(h, h->track, h->tracktime); + t = abc_patno(h, h->tracktime); + if( global_part == ' ' ) { + partpat[26][1] = t; + if( abcparts ) { + for( t=0; t<26; t++ ) + if( partpat[t][0] < partpat[t][1] ) break; + if( t == 26 ) { + abc_message("parts (%s) set but not used", abcparts); + abc_set_parts(&abcparts, 0); // forget the parts array + } + } + } + else + partpat[global_part - 'A'][1] = t; + // give every new coming abcevent the desired part indication + while( p[2]==' ' || p[2]=='.' ) p++; // skip blancs and dots + if( isupper(p[2]) ) + global_part = p[2]; + else + global_part = ' '; + if( global_part == ' ' ) + partpat[26][0] = t; + else + partpat[global_part - 'A'][0] = t; + } + *p = '%'; // make me skip the rest of the line.... + } + if( h->droneon && !tpd ) { + tpd = h->track; + if( tpd ) { + tpd = abc_locate_track(h, tpd->v, DRONEPOS1); + tpd->instr = h->dronegm; + abc_add_dronenote(h, tpd, h->tracktime, h->dronepitch[0], h->dronevol[0]); + tpd = abc_locate_track(h, tpd->v, DRONEPOS2); + tpd->instr = h->dronegm; + abc_add_dronenote(h, tpd, h->tracktime, h->dronepitch[1], h->dronevol[1]); + } + } + if( tpd && !h->droneon ) { + tpd = abc_locate_track(h, tpd->v, DRONEPOS1); + abc_add_noteoff(h, tpd, h->tracktime); + tpd = abc_locate_track(h, tpd->v, DRONEPOS2); + abc_add_noteoff(h, tpd, h->tracktime); + tpd = NULL; + } + if( h->drumon && !h->tpr ) { + h->tpr = h->track; + if( h->tpr ) abc_add_drum_sync(h, h->tpr, h->tracktime); // don't start drumming from the beginning of time! + } + if( h->tpr && !h->drumon ) h->tpr = NULL; + if( *p != '%' ) { // skip uninteresting lines + // plough thru the songline gathering mos.... + ch0 = ' '; + pp = 0; + while( (ch = *p++) ) { + if( isalpha(ch) && *p != ':' ) { // maybe a macro + for( mp=h->umacro; mp; mp=mp->next ) { + if( ch == mp->name[0] ) { + pp = p; + p = mp->subst; + ch = *p++; + break; + } + } + } + switch(ch) { + case '%': + abcto = 0; + while( *p ) p++; + break; + case '[': // chord follows or some inline field + abcto = 0; + if( *p=='|' ) break; // [| a thick-thin bar line, loop around and let case '|' handle it + if( !strncmp(p,"V:",2) ) { // inline voice change + for( t=2; isspace(p[t]); t++ ) ; + h->tp = abc_locate_track(h, p+t, 0); + for( ; *p && *p != ']'; p++ ) ; + abcgrace = 0; + brokenrithm = 0; + sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature + h->tracktime = abc_tracktime(h->tp); + bartime = h->tracktime; // it is not wise to break voices in the middle of a track... + abcvol = h->tp->volume; + abcnolegato = !h->tp->legato; + if( !abcnolegato ) abcnoslurs = 0; + break; + } + if( !strncmp(p,"K:",2) ) { + abckey = ABC_Key(p+2); + sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature + p = abc_skip_word(p+2); + h->ktrans = abc_transpose(p); + for( ; *p && *p != ']'; p++ ) ; + break; + } + if( !strncmp(p,"M:",2) ) { + abc_M_field(p+2, &mnotelen, &mnotediv); + for( ; *p && *p != ']'; p++ ) ; + h->barticks = notelen_notediv_to_ticks(h->speed, mnotelen, mnotediv); + break; + } + if( !strncmp(p,"P:",2) ) { // a [P:X] field inline + if( abcparts != NULL ) { + // make h->tracktime start of a new age... + abc_add_partbreak(h, h->track, h->tracktime); + t = abc_patno(h, h->tracktime); + if( global_part == ' ' ) + partpat[26][1] = t; + else + partpat[global_part - 'A'][1] = t; + // give every new coming abcevent the desired part indication + while( isspace(p[2]) || p[2]=='.' ) p++; // skip blancs and dots + if( isupper(p[2]) ) + global_part = p[2]; + else + global_part = ' '; + if( global_part == ' ' ) + partpat[26][0] = t; + else + partpat[global_part - 'A'][0] = t; + } + for( ; *p && *p != ']'; p++ ) ; + break; + } + if( !strncmp(p,"Q:",2) ) { + abctempo = abc_extract_tempo(p+2,1); + for( ; *p && *p != ']'; p++ ) ; + abc_add_tempo_event(h, h->track, h->tracktime, abctempo); + break; + } + if( !strncmp(p,"I:",2) ) { // interpret some of the possibilitys + for( p += 2; isspace(*p); p++ ) ; + if( !strncmp(p,"MIDI",4) && (p[4]=='=' || isspace(p[4])) ) { // interpret some of the possibilitys + for( p += 4; isspace(*p); p++ ) ; + if( *p == '=' ) + for( p += 1; isspace(*p); p++ ) ; + abc_MIDI_command(h, p, ']'); + if( h->tp ) abcnolegato = !h->tp->legato; + if( !abcnolegato ) abcnoslurs = 0; + } + for( ; *p && *p != ']'; p++ ) ; // skip rest of inline field + } + if( *p && p[1] == ':' ) { // some other kind of inline field + for( ; *p && *p != ']'; p++ ) ; + break; + } + if( *p && strchr("abcdefgABCDEFG^_=",*p) ) { + int cnl[8],cnd[8],vnl,nl0=0,nd0=0; // for chords with notes of varying length + abcchord = 0; + vnl = 0; + h->tp = abc_check_track(h, h->tp); + abc_track_clear_tiedvpos(h); + abcbeatvol = abc_beat_vol(h, abcvol, (h->tracktime - bartime)/notelen_notediv_to_ticks(h->speed,1,mnotediv)); + while( (ch=*p++) && (ch != ']') ) { + h->tp = abc_locate_track(h, h->tp->v, abcchord? abcchord+DRONEPOS2: 0); + p += abc_add_noteon(h, ch, p, h->tracktime, barsig, abcbeatvol, abceffect, abceffoper); + p += abc_notelen(p, ¬elen, ¬ediv); + if( *p == '-' ) { + p++; + if( h->tp->tail->flg != 1 ) + h->tp->tienote = h->tp->tail; + } + if( abcchord<8 ) { + cnl[abcchord] = notelen; + cnd[abcchord] = notediv; + } + if( abcchord==0 ) { + cnotelen = notelen; + cnotediv = notediv; + nl0 = notelen; + nd0 = notediv; + } + else { + if( cnotelen != notelen || cnotediv != notediv ) { + vnl = 1; + // update to longest duration + if( cnotelen * notediv < notelen * cnotediv ) { + cnotelen = notelen; + cnotediv = notediv; + abc_track_untie_short_chordnotes(h); + } + if( cnotelen * notediv > notelen * cnotediv ) { + if( h->tp->tienote ) { + abc_message("short notes in chord can not be tied:\n%s", h->line); + h->tp->tienote = 0; // short chord notes cannot be tied... + } + } + // update to shortest duration + if( nl0 * notediv > notelen * nd0 ) { + nl0 = notelen; + nd0 = notediv; + } + } + } + abcchord++; + } + p += abc_notelen(p, ¬elen, ¬ediv); + if( (ch = *p) == '-' ) p++; // tied chord... + if( abcarpeggio ) { // update starttime in the noteon events... + thistime = notelen_notediv_to_ticks(h->speed, nl0*notelen*snotelen, nd0*notediv*snotediv)/abcchord; + if( thistime > abcticks(h->speed) ) thistime = abcticks(h->speed); + for( nl0=1; nl0<abcchord; nl0++ ) { + h->tp = abc_locate_track(h, h->tp->v, nl0+DRONEPOS2); + h->tp->tail->tracktick = h->tracktime + thistime * nl0; + } + } + notelen *= cnotelen; + notediv *= cnotediv; + tupletr = abc_tuplet(¬elen, ¬ediv, tupletp, tupletq, tupletr); + while( isspace(*p) ) p++; // allow spacing in broken rithm notation + p += abc_brokenrithm(p, ¬elen, ¬ediv, &brokenrithm, abchornpipe); + thistime = notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv); + if( abcfermata ) { + thistime <<= 1; + abcfermata = 0; + } + if( thistime > abcgrace ) { + thistime -= abcgrace; + abcgrace = 0; + } + else { + abcgrace -= thistime; + thistime = abcticks(h->speed); + abcgrace += abcticks(h->speed); + } + h->tracktime += thistime; + while( abcchord>0 ) { + abcchord--; + h->tp = abc_locate_track(h, h->tp->v, abcchord? abcchord+DRONEPOS2: 0); + if( vnl && (abcchord < 8) && (cnl[abcchord] != cnotelen || cnd[abcchord] != cnotediv) ) { + abc_add_noteoff(h, h->tp, + h->tracktime - thistime + + (thistime * cnl[abcchord] * cnotediv)/(cnd[abcchord] * cnotelen) ); + } + else { + if( ch=='-' && h->tp->tail->flg != 1 ) + h->tp->tienote = h->tp->tail; // copy noteon event to tienote in track + if( thistime > abcticks(h->speed) ) + abc_add_noteoff(h, h->tp, h->tracktime - abcnoslurs); + else + abc_add_noteoff(h, h->tp, h->tracktime); + } + } + if( h->gchordon && (h->tp == h->tpc) ) + abc_add_gchord(h, h->tracktime, bartime); + if( h->drumon && (h->tp == h->tpr) ) + abc_add_drum(h, h->tracktime, bartime); + abcarpeggio = 0; + if( abceffoper != 255 ) abceffect = none; + break; + } + if( isdigit(*p) ) { // different endings in repeats [i,j,n-r,s,... + h->tp = abc_check_track(h, h->tp); + abc_add_partbreak(h, h->tp, h->tracktime); + p += abc_getnumber(p, ¬elen); + abc_add_variant_start(h, h->tp, h->tracktime, notelen); + while( *p==',' || *p=='-' ) { + if( *p==',' ) { + p++; + p += abc_getnumber(p, ¬elen); + abc_add_variant_choise(h->tp, notelen); + } + else { + p++; + p += abc_getnumber(p, ¬ediv); + while( notelen < notediv ) { + notelen++; + abc_add_variant_choise(h->tp, notelen); + } + } + } + break; + } + // collect the notes in the chord + break; + case '(': // slurs follow or some tuplet (duplet, triplet etc.) + abcto = 0; + if( isdigit(*p) ) { + p += abc_getnumber(p,&tupletp); + tupletr = tupletp; // ABC draft 2.0 (4.13): if r is not given it defaults to p + switch( tupletp ) { // ABC draft 2.0 (4.13): q defaults depending on p and time signature + case 2: case 4: case 8: + tupletq = 3; + break; + case 3: case 6: + tupletq = 2; + break; + default: + if( snotediv == 8 ) + tupletq = 3; + else + tupletq = 2; + break; + } + if( *p==':' ) { + p++; + if( isdigit(*p) ) p += abc_getnumber(p,&tupletq); + if( *p==':' ) { + p++; + if( isdigit(*p) ) p += abc_getnumber(p,&tupletr); + } + } + } + else + abcnoslurs=0; + break; + case ')': // end of slurs + abcto = 0; + abcnoslurs = abcnolegato; + break; + case '{': // grace notes follow + abcto = 0; + h->tp = abc_check_track(h, h->tp); + abc_track_clear_tiedvpos(h); + abcgrace = 0; + abcbeatvol = abc_beat_vol(h, abcvol, (h->tracktime - bartime)/notelen_notediv_to_ticks(h->speed,1,mnotediv)); + while( (ch=*p++) && (ch != '}') ) { + p += abc_add_noteon(h, ch, p, h->tracktime+abcgrace, barsig, abcbeatvol, none, 0); + p += abc_notelen(p, ¬elen, ¬ediv); + if( *p=='-' ) { + p++; + if( h->tp->tail->flg != 1 ) + h->tp->tienote = h->tp->tail; + } + notediv *= 4; // grace notes factor 4 shorter (1/8 => 1/32) + abcgrace += notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv); + abc_add_noteoff(h, h->tp, h->tracktime + abcgrace); + } + h->tracktime += abcgrace; + abc_add_sync(h, h->tp, h->tracktime); + if( h->gchordon && (h->tp == h->tpc) ) + abc_add_gchord(h, h->tracktime, bartime); + if( h->drumon && (h->tp == h->tpr) ) + abc_add_drum(h, h->tracktime, bartime); + break; + case '|': // bar symbols + abcto = 0; + if( h->gchordon && h->tp && (h->tp == h->tpc) ) + abc_add_gchord(h, h->tracktime, bartime); + if( h->drumon && (h->tp == h->tpr) ) + abc_add_drum(h, h->tracktime, bartime); + sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature + bartime = h->tracktime; + if( h->tp && h->tp->vpos ) h->tp = abc_locate_track(h, h->tp->v, 0); // reset from voice overlay + if( isdigit(*p) ) { // different endings in repeats |i,j,n-r,s,... + h->tp = abc_check_track(h, h->tp); + abc_add_partbreak(h, h->tp, h->tracktime); + p += abc_getnumber(p, ¬elen); + abc_add_variant_start(h, h->tp, h->tracktime, notelen); + while( *p==',' || *p=='-' ) { + if( *p==',' ) { + p++; + p += abc_getnumber(p, ¬elen); + abc_add_variant_choise(h->tp, notelen); + } + else { + p++; + p += abc_getnumber(p, ¬ediv); + while( notelen < notediv ) { + notelen++; + abc_add_variant_choise(h->tp, notelen); + } + } + } + break; + } + if( *p==':' ) { // repeat start + p++; + h->tp = abc_check_track(h, h->tp); + abc_add_partbreak(h, h->tp, h->tracktime); + abc_add_setloop(h, h->tp, h->tracktime); + } + break; + case '&': // voice overlay + abcto = 0; + h->tracktime = bartime; + h->tp = abc_check_track(h, h->tp); + t = h->tp->vpos; + h->tp = abc_locate_track(h, h->tp->v, t? t+1: DRONEPOS2+1); + break; + case ']': // staff break, end of song + abcto = 0; + break; + case ':': // repeat jump + abcto = 0; + h->tp = abc_check_track(h, h->tp); + j = (h->tp->slidevol == -2)? jumpfade: jumpnormal; + abc_add_setjumploop(h, h->tp, h->tracktime, j); + abc_add_partbreak(h, h->tp, h->tracktime); + if( *p==':' ) { // repeat start without intermediate bar symbol + p++; + abc_add_setloop(h, h->tp, h->tracktime); + } + break; + case '"': // chord notation + if( !strchr("_^<>@", *p) && !isdigit(*p) ) { // if it's not a annotation string + h->tp = abc_check_track(h, h->tp); + if( !h->tpc ) h->tpc = abc_locate_track(h, h->tp->v, 0); + if( h->tp == h->tpc ) abc_add_chord(p, h, h->tpc, h->tracktime); // only do chords for one voice + } + abcto = 0; + while( (ch=*p++) && (ch != '"') ) { + if( !strncasecmp(p,"fade",4) && h->track && h->track->slidevol > -2 ) + abc_globalslide(h, h->tracktime, -2); // set volumeslide to fade away... + if( !strncasecmp(p,"to coda",7) ) { + h->tp = abc_check_track(h, h->tp); + abc_add_partbreak(h, h->tp, h->tracktime); + abc_add_tocoda(h, h->tp, h->tracktime); + p+=7; + abcto = -1; + } + else + if( !isspace(*p) ) abcto = 0; + if( !strncasecmp(p,"to",2) && (isspace(p[2]) || p[2] == '"') ) abcto = 1; + } + if( !ch ) abcstate = INSKIPFORQUOTE; + break; + case '\\': // skip the rest of this line, should be the end of the line anyway + while( (ch=*p++) ) + ; + ch = '\\'; // remember for invoice tempo changes.... + break; + case '!': // line break, or deprecated old style decoration + case '+': // decorations new style + if( !strncmp(p,"coda",4) && p[4] == ch ) { + h->tp = abc_check_track(h, h->tp); + if( abcto ) { + if( abcto > 0 ) { + abc_add_partbreak(h, h->tp, h->tracktime); + abc_add_tocoda(h, h->tp, h->tracktime); + } + } + else { + abc_add_partbreak(h, h->tp, h->tracktime); + abc_add_coda(h, h->tp, h->tracktime); + } + p += 5; + abcto = 0; + break; + } + abcto = 0; + if( !strncmp(p,"arpeggio",8) && p[8] == ch ) { + abcarpeggio = 1; + p += 9; + break; + } + if( !strncmp(p,"crescendo(",10) && p[10] == ch ) { + h->tp = abc_check_track(h, h->tp); + abc_globalslide(h, h->tracktime, 1); + p += 11; + break; + } + if( !strncmp(p,"crescendo)",10) && p[10] == ch ) { + h->tp = abc_check_track(h, h->tp); + abc_globalslide(h, h->tracktime, 0); + p += 11; + break; + } + if( !strncmp(p,"<(",2) && p[2] == ch ) { + h->tp = abc_check_track(h, h->tp); + abc_globalslide(h, h->tracktime, 1); + p += 3; + break; + } + if( !strncmp(p,"<)",2) && p[2] == ch ) { + h->tp = abc_check_track(h, h->tp); + abc_globalslide(h, h->tracktime, 0); + p += 3; + break; + } + if( !strncmp(p,"dimimuendo(",11) && p[11] == ch ) { + h->tp = abc_check_track(h, h->tp); + abc_globalslide(h, h->tracktime, -1); + p += 12; + break; + } + if( !strncmp(p,"diminuendo)",11) && p[11] == ch ) { + h->tp = abc_check_track(h, h->tp); + abc_globalslide(h, h->tracktime, 0); + p += 12; + break; + } + if( !strncmp(p,">(",2) && p[2] == ch ) { + h->tp = abc_check_track(h, h->tp); + abc_globalslide(h, h->tracktime, -1); + p += 3; + break; + } + if( !strncmp(p,">)",2) && p[2] == ch ) { + h->tp = abc_check_track(h, h->tp); + abc_globalslide(h, h->tracktime, 0); + p += 3; + break; + } + if( !strncmp(p,"upbow",5) && p[5] == ch ) { + abceffect = bow; + abceffoper = 1; + p += 6; + break; + } + if( !strncmp(p,"downbow",7) && p[7] == ch ) { + abceffect = bow; + abceffoper = 0; + p += 8; + break; + } + if( !strncmp(p,"trill",5) && p[5] == ch ) { + abceffect = trill; + abceffoper = 0; + p += 6; + break; + } + if( !strncmp(p,"trill(",6) && p[6] == ch ) { + abceffect = trill; + abceffoper = 255; + p += 7; + break; + } + if( !strncmp(p,"trill)",6) && p[6] == ch ) { + abceffect = none; + abceffoper = 0; + p += 7; + break; + } + if( !strncmp(p,"accent",6) && p[6] == ch ) { + abceffect = accent; + abceffoper = 0; + p += 7; + break; + } + if( !strncmp(p,"emphasis",8) && p[8] == ch ) { + abceffect = accent; + abceffoper = 0; + p += 9; + break; + } + if( !strncmp(p,">",1) && p[1] == ch ) { + abceffect = accent; + abceffoper = 0; + p += 2; + break; + } + if( !strncmp(p,"fermata",7) && p[7] == ch ) { + abcfermata = 1; + p += 8; + break; + } + if( !strncmp(p,"fine",4) && p[4] == ch ) { + h->tp = abc_check_track(h, h->tp); + abc_add_partbreak(h, h->tp, h->tracktime); + abc_add_fine(h, h->tp, h->tracktime); + p += 5; + break; + } + if( !strncmp(p,"segno",5) && p[5] == ch ) { + h->tp = abc_check_track(h, h->tp); + abc_add_partbreak(h, h->tp, h->tracktime); + abc_add_segno(h, h->tp, h->tracktime); + p += 6; + break; + } + if( !strncmp(p,"tocoda",6) && p[6] == ch ) { + h->tp = abc_check_track(h, h->tp); + abc_add_partbreak(h, h->tp, h->tracktime); + abc_add_tocoda(h, h->tp, h->tracktime); + p += 7; + break; + } + if( !strncmp(p,"D.C.",4) && p[4] == ch ) { + h->tp = abc_check_track(h, h->tp); + j = (h->tp->slidevol == -2)? jumpdcfade: jumpdacapo; + abc_add_setjumploop(h, h->tp, h->tracktime, j); + abc_add_partbreak(h, h->tp, h->tracktime); + p += 5; + break; + } + if( !strncmp(p,"D.S.",4) && p[4] == ch ) { + h->tp = abc_check_track(h, h->tp); + j = (h->tp->slidevol == -2)? jumpdsfade: jumpdasegno; + abc_add_setjumploop(h, h->tp, h->tracktime, j); + abc_add_partbreak(h, h->tp, h->tracktime); + p += 5; + break; + } + if( !strncmp(p,"dacapo",6) && p[6] == ch ) { + h->tp = abc_check_track(h, h->tp); + j = (h->tp->slidevol == -2)? jumpdcfade: jumpdacapo; + abc_add_setjumploop(h, h->tp, h->tracktime, j); + abc_add_partbreak(h, h->tp, h->tracktime); + p += 7; + break; + } + if( !strncmp(p,"dacoda",6) && p[6] == ch ) { + h->tp = abc_check_track(h, h->tp); + j = (h->tp->slidevol == -2)? jumpdcfade: jumpdacapo; + abc_add_setjumploop(h, h->tp, h->tracktime, j); + abc_add_partbreak(h, h->tp, h->tracktime); + p += 7; + break; + } + if( ch == '!' ) { + for( t=0; p[t] && strchr("|[:]!",p[t])==0 && !isspace(p[t]); t++ ) ; + if( p[t] == '!' ) { // volume and other decorations, deprecated + h->tp = abc_check_track(h, h->tp); + abcvol = abc_parse_decorations(h, h->tp, p); + p = &p[t+1]; + } + } + else { + h->tp = abc_check_track(h, h->tp); + abcvol = abc_parse_decorations(h, h->tp, p); + while( (ch=*p++) && (ch != '+') ) + ; + } + break; + case '`': // back quotes are for readability + break; + case '.': // staccato marks + break; + default: // some kinda note must follow + if( strchr("abcdefgABCDEFG^_=X",ch) ) { + h->tp = abc_check_track(h, h->tp); + abc_track_clear_tiedvpos(h); + abcbeatvol = abc_beat_vol(h, abcvol, (h->tracktime - bartime)/notelen_notediv_to_ticks(h->speed,1,mnotediv)); + p += abc_add_noteon(h, ch, p, h->tracktime, barsig, abcbeatvol, abceffect, abceffoper); + if( abceffoper != 255 ) abceffect = none; + p += abc_notelen(p, ¬elen, ¬ediv); + if( *p=='-' ) { + p++; + if( h->tp->tail->flg != 1 ) + h->tp->tienote = h->tp->tail; + } + tupletr = abc_tuplet(¬elen, ¬ediv, tupletp, tupletq, tupletr); + while( isspace(*p) ) p++; // allow spacing in broken rithm notation + p += abc_brokenrithm(p, ¬elen, ¬ediv, &brokenrithm, abchornpipe); + thistime = notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv); + if( abcfermata ) { + thistime <<= 1; + abcfermata = 0; + } + if( thistime > abcgrace ) { + thistime -= abcgrace; + abcgrace = 0; + } + else { + abcgrace -= thistime; + thistime = abcticks(h->speed); + abcgrace += abcticks(h->speed); + } + h->tracktime += thistime; + if( thistime > abcticks(h->speed) ) + abc_add_noteoff(h, h->tp, h->tracktime - abcnoslurs - (( ch0 == '.')? thistime / 2: 0)); + else + abc_add_noteoff(h, h->tp, h->tracktime); + abc_add_sync(h, h->tp, h->tracktime); + if( h->gchordon && (h->tp == h->tpc) ) + abc_add_gchord(h, h->tracktime, bartime); + if( h->drumon && (h->tp == h->tpr) ) + abc_add_drum(h, h->tracktime, bartime); + abcarpeggio = 0; + break; + } + if( strchr("zx",ch) ) { + h->tp = abc_check_track(h, h->tp); + abc_track_clear_tiednote(h); + p += abc_notelen(p, ¬elen, ¬ediv); + tupletr = abc_tuplet(¬elen, ¬ediv, tupletp, tupletq, tupletr); + while( isspace(*p) ) p++; // allow spacing in broken rithm notation + p += abc_brokenrithm(p, ¬elen, ¬ediv, &brokenrithm, abchornpipe); + thistime = notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv); + if( abcfermata ) { + thistime <<= 1; + abcfermata = 0; + } + if( thistime > abcgrace ) { + thistime -= abcgrace; + abcgrace = 0; + } + else { + abcgrace -= thistime; + thistime = abcticks(h->speed); + abcgrace += abcticks(h->speed); + } + h->tracktime += thistime; + abc_add_sync(h, h->tp, h->tracktime); + if( h->gchordon && (h->tp == h->tpc) ) + abc_add_gchord(h, h->tracktime, bartime); + if( h->drumon && (h->tp == h->tpr) ) + abc_add_drum(h, h->tracktime, bartime); + abcarpeggio = 0; + break; + } + if( strchr("Z",ch) ) { + h->tp = abc_check_track(h, h->tp); + abc_track_clear_tiednote(h); + p += abc_notelen(p, ¬elen, ¬ediv); + thistime = notelen_notediv_to_ticks(h->speed, notelen*mnotelen, notediv*mnotediv); + if( abcfermata ) { + thistime <<= 1; + abcfermata = 0; + } + if( thistime > abcgrace ) { + thistime -= abcgrace; + abcgrace = 0; + } + else { + abcgrace -= thistime; + thistime = abcticks(h->speed); + abcgrace += abcticks(h->speed); + } + h->tracktime += thistime; + sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature + abc_add_sync(h, h->tp, h->tracktime); + if( h->gchordon && (h->tp == h->tpc) ) + abc_add_gchord(h, h->tracktime, bartime); + if( h->drumon && (h->tp == h->tpr) ) + abc_add_drum(h, h->tracktime, bartime); + abcarpeggio = 0; + break; + } + if( isalpha(ch) && *p==':' ) { + // some unprocessed field line? + while( *p ) p++; // skip it + break; + } + break; + } + ch0 = ch; // remember previous char, can be staccato dot... + if( pp ) { // did we have a U: macro substitution? + if( !*p ) { + p = pp; + pp = 0; + } + } + } + } + } + } + if( mmsp ) mmfclose(mmstack[mmsp]); + } + ABC_CleanupMacros(h); // we dont need them anymore + if( !h->track ) { + char buf[10]; + sprintf(buf,"%d",abcxnumber); + abc_message("abc X:%s has no body", buf); + h->track = abc_check_track(h, h->track); // for sanity... + } + if( abcstate == INBODY ) { + // last but not least shut off all pending events + abc_recalculate_tracktime(h); + for( ttp=h->track; ttp; ttp=ttp->next ) + abc_add_noteoff(h,ttp,h->tracktime); + abc_add_partbreak(h, h->track, h->tracktime); + t = abc_patno(h, h->tracktime); + if( abc_pattracktime(h, h->tracktime) % abcticks(64 * h->speed) ) t++; + if( global_part == ' ' ) { + partpat[26][1] = t; + if( abcparts ) { + for( t=0; t<26; t++ ) + if( partpat[t][0] < partpat[t][1] ) break; + if( t == 26 ) { + abc_message("parts (%s) set but not used", abcparts); + abc_set_parts(&abcparts, 0); // forget the parts array + } + } + } + else + partpat[global_part - 'A'][1] = t; + if( !abcparts ) abc_song_to_parts(h, &abcparts, partpat); + orderlen = abc_partpat_to_orderlist(partpat, abcparts, h, &orderlist, orderlen); + } + abc_synchronise_tracks(h); // distribute all control events + abc_recalculate_tracktime(h); +/* + + abctrack: + tracktick long + note byte + octave byte + instrument byte + effects byte + + tick = tracktick modulo speed + row = (tracktick div speed) modulo 64 + pat = (tracktick div speed) div 64 + ord = calculated + +*/ + if( (p=getenv(ABC_ENV_DUMPTRACKS)) ) { + printf("P:%s\n",abcparts); + for( t=0; t<26; t++ ) + if( partpat[t][1] >= partpat[t][0] ) + printf(" %c ",t+'A'); + if( partpat[26][1] >= partpat[26][0] ) + printf("All"); + printf("\n"); + for( t=0; t<27; t++ ) + if( partpat[t][1] >= partpat[t][0] ) + printf("%3d ",partpat[t][0]); + printf("\n"); + for( t=0; t<27; t++ ) + if( partpat[t][1] >= partpat[t][0] ) + printf("%3d ",partpat[t][1]); + printf("\n"); + for( t=0; (int)t<orderlen; t++ ) + printf("%3d ",t); + printf("\n"); + for( t=0; (int)t<orderlen; t++ ) + printf("%3d ",orderlist[t]); + printf("\n"); + abc_dumptracks(h,p); + } + // set module variables + if( abctempo == 0 ) abctempo = abcrate; + if( m_nDefaultTempo == 0 ) m_nDefaultTempo = abctempo; +#ifdef NEWMIKMOD + of->memsize = PTMEM_LAST; // Number of memory slots to reserve! + of->modtype = _mm_strdup(of->allochandle, ABC_Version); + of->numpat = 1+(modticks(h->tracktime) / h->speed / 64); + of->numpos = orderlen; + of->reppos = 0; + of->initspeed = h->speed; + of->numchn = abc_numtracks(h); + of->numtrk = of->numpat * of->numchn; + of->initvolume = 64; + of->pansep = 128; + // orderlist + if(!AllocPositions(of, orderlen)) { + avoid_reentry = 0; + return FALSE; + } + for(t=0; t<orderlen; t++) + of->positions[t] = orderlist[t]; + _mmalloc_close(h->ho); // get rid of orderlist memory +#else + m_nType = MOD_TYPE_ABC; + numpat = 1+(modticks(h->tracktime) / h->speed / 64); + m_nDefaultSpeed = h->speed; + m_nChannels = abc_numtracks(h); + m_dwSongFlags = SONG_LINEARSLIDES; + m_nMinPeriod = 28 << 2; + m_nMaxPeriod = 1712 << 3; + // orderlist + for(t=0; t < (uint32_t)orderlen; t++) + Order[t] = orderlist[t]; + free(orderlist); // get rid of orderlist memory +#endif +#ifdef NEWMIKMOD + // ============================== + // Load the pattern info now! + if(!AllocTracks(of)) return 0; + if(!AllocPatterns(of)) return 0; + of->ut = utrk_init(of->numchn, h->allochandle); + utrk_memory_reset(of->ut); + utrk_local_memflag(of->ut, PTMEM_PORTAMENTO, TRUE, FALSE); + ABC_ReadPatterns(of, h, of->numpat); + // load instruments after building the patterns (chan == 10 track handling) + if( !PAT_Load_Instruments(of) ) { + avoid_reentry = 0; + return FALSE; + } + // ============================================================ + // set panning positions + for(t=0; t<of->numchn; t++) { + of->panning[t] = PAN_LEFT+((t+2)%5)*((PAN_RIGHT - PAN_LEFT)/5); // 0x30 = std s3m val + } +#else + // ============================== + // Load the pattern info now! + if( ABC_ReadPatterns(Patterns, PatternSize, h, numpat, m_nChannels) ) { + // :^( need one more channel to handle the global events ;^b + m_nChannels++; + h->tp = abc_locate_track(h, "", 99); + abc_add_sync(h, h->tp, h->tracktime); + for( t=0; t<numpat; t++ ) { + FreePattern(Patterns[t]); + Patterns[t] = NULL; + } + ABC_ReadPatterns(Patterns, PatternSize, h, numpat, m_nChannels); + } + // load instruments after building the patterns (chan == 10 track handling) + if( !PAT_Load_Instruments(this) ) { + avoid_reentry = 0; + return FALSE; + } + // ============================================================ + // set panning positions + for(t=0; t<m_nChannels; t++) { + ChnSettings[t].nPan = 0x30+((t+2)%5)*((0xD0 - 0x30)/5); // 0x30 = std s3m val + ChnSettings[t].nVolume = 64; + } +#endif + avoid_reentry = 0; // it is safe now, I'm finished + abc_set_parts(&abcparts, 0); // free the parts array +#ifndef NEWMIKMOD + ABC_Cleanup(h); // we dont need it anymore +#endif + return 1; +} + +#ifdef NEWMIKMOD +// ===================================================================================== +CHAR *ABC_LoadTitle(MMSTREAM *mmfile) +// ===================================================================================== +{ + char s[128]; + int i; + // get the first line with T:songtitle + _mm_fseek(mmfile,0,SEEK_SET); + while(abc_fgets(mmfile,s,128)) { + if( s[0]=='T' && s[1]==':' ) { + for( i=2; s[i]==' '; i++ ) ; + return(DupStr(NULL, s+i,strlen(s+i))); + } + } + return NULL; +} + +MLOADER load_abc = +{ + "ABC", + "ABC draft 2.0", + 0x30, + NULL, + ABC_Test, + (void *(*)(void))ABC_Init, + (void (*)(ML_HANDLE *))ABC_Cleanup, + /* Every single loader seems to need one of these! */ + (BOOL (*)(ML_HANDLE *, UNIMOD *, MMSTREAM *))ABC_Load, + ABC_LoadTitle +}; +#endif diff --git a/lib/libmodplug/src/load_amf.cpp b/lib/libmodplug/src/load_amf.cpp new file mode 100644 index 0000000000..dd806a0787 --- /dev/null +++ b/lib/libmodplug/src/load_amf.cpp @@ -0,0 +1,418 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +/////////////////////////////////////////////////// +// +// AMF module loader +// +// There is 2 types of AMF files: +// - ASYLUM Music Format +// - Advanced Music Format(DSM) +// +/////////////////////////////////////////////////// +#include "stdafx.h" +#include "sndfile.h" + +//#define AMFLOG + +//#pragma warning(disable:4244) + +#pragma pack(1) + +typedef struct _AMFFILEHEADER +{ + UCHAR szAMF[3]; + UCHAR version; + CHAR title[32]; + UCHAR numsamples; + UCHAR numorders; + USHORT numtracks; + UCHAR numchannels; +} AMFFILEHEADER; + +typedef struct _AMFSAMPLE +{ + UCHAR type; + CHAR samplename[32]; + CHAR filename[13]; + ULONG offset; + ULONG length; + USHORT c2spd; + UCHAR volume; +} AMFSAMPLE; + + +#pragma pack() + + +#ifdef AMFLOG +extern void Log(LPCSTR, ...); +#endif + +VOID AMF_Unpack(MODCOMMAND *pPat, const BYTE *pTrack, UINT nRows, UINT nChannels) +//------------------------------------------------------------------------------- +{ + UINT lastinstr = 0; + UINT nTrkSize = bswapLE16(*(USHORT *)pTrack); + nTrkSize += (UINT)pTrack[2] << 16; + pTrack += 3; + while (nTrkSize--) + { + UINT row = pTrack[0]; + UINT cmd = pTrack[1]; + UINT arg = pTrack[2]; + if (row >= nRows) break; + MODCOMMAND *m = pPat + row * nChannels; + if (cmd < 0x7F) // note+vol + { + m->note = cmd+1; + if (!m->instr) m->instr = lastinstr; + m->volcmd = VOLCMD_VOLUME; + m->vol = arg; + } else + if (cmd == 0x7F) // duplicate row + { + signed char rdelta = (signed char)arg; + int rowsrc = (int)row + (int)rdelta; + if ((rowsrc >= 0) && (rowsrc < (int)nRows)) memcpy(m, &pPat[rowsrc*nChannels],sizeof(pPat[rowsrc*nChannels])); + } else + if (cmd == 0x80) // instrument + { + m->instr = arg+1; + lastinstr = m->instr; + } else + if (cmd == 0x83) // volume + { + m->volcmd = VOLCMD_VOLUME; + m->vol = arg; + } else + // effect + { + UINT command = cmd & 0x7F; + UINT param = arg; + switch(command) + { + // 0x01: Set Speed + case 0x01: command = CMD_SPEED; break; + // 0x02: Volume Slide + // 0x0A: Tone Porta + Vol Slide + // 0x0B: Vibrato + Vol Slide + case 0x02: command = CMD_VOLUMESLIDE; + case 0x0A: if (command == 0x0A) command = CMD_TONEPORTAVOL; + case 0x0B: if (command == 0x0B) command = CMD_VIBRATOVOL; + if (param & 0x80) param = (-(signed char)param)&0x0F; + else param = (param&0x0F)<<4; + break; + // 0x04: Porta Up/Down + case 0x04: if (param & 0x80) { command = CMD_PORTAMENTOUP; param = (-(signed char)param)&0x7F; } + else { command = CMD_PORTAMENTODOWN; } break; + // 0x06: Tone Portamento + case 0x06: command = CMD_TONEPORTAMENTO; break; + // 0x07: Tremor + case 0x07: command = CMD_TREMOR; break; + // 0x08: Arpeggio + case 0x08: command = CMD_ARPEGGIO; break; + // 0x09: Vibrato + case 0x09: command = CMD_VIBRATO; break; + // 0x0C: Pattern Break + case 0x0C: command = CMD_PATTERNBREAK; break; + // 0x0D: Position Jump + case 0x0D: command = CMD_POSITIONJUMP; break; + // 0x0F: Retrig + case 0x0F: command = CMD_RETRIG; break; + // 0x10: Offset + case 0x10: command = CMD_OFFSET; break; + // 0x11: Fine Volume Slide + case 0x11: if (param) { command = CMD_VOLUMESLIDE; + if (param & 0x80) param = 0xF0|((-(signed char)param)&0x0F); + else param = 0x0F|((param&0x0F)<<4); + } else command = 0; break; + // 0x12: Fine Portamento + // 0x16: Extra Fine Portamento + case 0x12: + case 0x16: if (param) { int mask = (command == 0x16) ? 0xE0 : 0xF0; + command = (param & 0x80) ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN; + if (param & 0x80) param = mask|((-(signed char)param)&0x0F); + else param |= mask; + } else command = 0; break; + // 0x13: Note Delay + case 0x13: command = CMD_S3MCMDEX; param = 0xD0|(param & 0x0F); break; + // 0x14: Note Cut + case 0x14: command = CMD_S3MCMDEX; param = 0xC0|(param & 0x0F); break; + // 0x15: Set Tempo + case 0x15: command = CMD_TEMPO; break; + // 0x17: Panning + case 0x17: param = (param+64)&0x7F; + if (m->command) { if (!m->volcmd) { m->volcmd = VOLCMD_PANNING; m->vol = param/2; } command = 0; } + else { command = CMD_PANNING8; } + // Unknown effects + default: command = param = 0; + } + if (command) + { + m->command = command; + m->param = param; + } + } + pTrack += 3; + } +} + + + +BOOL CSoundFile::ReadAMF(LPCBYTE lpStream, const DWORD dwMemLength) +//----------------------------------------------------------- +{ + const AMFFILEHEADER *pfh = (AMFFILEHEADER *)lpStream; + DWORD dwMemPos; + + if ((!lpStream) || (dwMemLength < 2048)) return FALSE; + if ((!strncmp((LPCTSTR)lpStream, "ASYLUM Music Format V1.0", 25)) && (dwMemLength > 4096)) + { + UINT numorders, numpats, numsamples; + + dwMemPos = 32; + numpats = lpStream[dwMemPos+3]; + numorders = lpStream[dwMemPos+4]; + numsamples = 64; + dwMemPos += 6; + if ((!numpats) || (numpats > MAX_PATTERNS) || (!numorders) + || (numpats*64*32 + 294 + 37*64 >= dwMemLength)) return FALSE; + m_nType = MOD_TYPE_AMF0; + m_nChannels = 8; + m_nInstruments = 0; + m_nSamples = 31; + m_nDefaultTempo = 125; + m_nDefaultSpeed = 6; + for (UINT iOrd=0; iOrd<MAX_ORDERS; iOrd++) + { + Order[iOrd] = (iOrd < numorders) ? lpStream[dwMemPos+iOrd] : 0xFF; + } + dwMemPos = 294; // ??? + for (UINT iSmp=0; iSmp<numsamples; iSmp++) + { + MODINSTRUMENT *psmp = &Ins[iSmp+1]; + memcpy(m_szNames[iSmp+1], lpStream+dwMemPos, 22); + psmp->nFineTune = MOD2XMFineTune(lpStream[dwMemPos+22]); + psmp->nVolume = lpStream[dwMemPos+23]; + psmp->nGlobalVol = 64; + if (psmp->nVolume > 0x40) psmp->nVolume = 0x40; + psmp->nVolume <<= 2; + psmp->nLength = bswapLE32(*((LPDWORD)(lpStream+dwMemPos+25))); + psmp->nLoopStart = bswapLE32(*((LPDWORD)(lpStream+dwMemPos+29))); + psmp->nLoopEnd = psmp->nLoopStart + bswapLE32(*((LPDWORD)(lpStream+dwMemPos+33))); + if ((psmp->nLoopEnd > psmp->nLoopStart) && (psmp->nLoopEnd <= psmp->nLength)) + { + psmp->uFlags = CHN_LOOP; + } else + { + psmp->nLoopStart = psmp->nLoopEnd = 0; + } + if ((psmp->nLength) && (iSmp>31)) m_nSamples = iSmp+1; + dwMemPos += 37; + } + for (UINT iPat=0; iPat<numpats; iPat++) + { + MODCOMMAND *p = AllocatePattern(64, m_nChannels); + if (!p) break; + Patterns[iPat] = p; + PatternSize[iPat] = 64; + const UCHAR *pin = lpStream + dwMemPos; + for (UINT i=0; i<8*64; i++) + { + p->note = 0; + + if (pin[0]) + { + p->note = pin[0] + 13; + } + p->instr = pin[1]; + p->command = pin[2]; + p->param = pin[3]; + if (p->command > 0x0F) + { + #ifdef AMFLOG + Log("0x%02X.0x%02X ?", p->command, p->param); + #endif + p->command = 0; + } + ConvertModCommand(p); + pin += 4; + p++; + } + dwMemPos += 64*32; + } + // Read samples + for (UINT iData=0; iData<m_nSamples; iData++) + { + MODINSTRUMENT *psmp = &Ins[iData+1]; + if (psmp->nLength) + { + if (dwMemPos > dwMemLength) return FALSE; + dwMemPos += ReadSample(psmp, RS_PCM8S, (LPCSTR)(lpStream+dwMemPos), dwMemLength); + } + } + return TRUE; + } + //////////////////////////// + // DSM/AMF + USHORT *ptracks[MAX_PATTERNS]; + DWORD sampleseekpos[MAX_SAMPLES]; + + if ((pfh->szAMF[0] != 'A') || (pfh->szAMF[1] != 'M') || (pfh->szAMF[2] != 'F') + || (pfh->version < 10) || (pfh->version > 14) || (!bswapLE16(pfh->numtracks)) + || (!pfh->numorders) || (pfh->numorders > MAX_PATTERNS) + || (!pfh->numsamples) || (pfh->numsamples > MAX_SAMPLES) + || (pfh->numchannels < 4) || (pfh->numchannels > 32)) + return FALSE; + memcpy(m_szNames[0], pfh->title, 32); + dwMemPos = sizeof(AMFFILEHEADER); + m_nType = MOD_TYPE_AMF; + m_nChannels = pfh->numchannels; + m_nSamples = pfh->numsamples; + m_nInstruments = 0; + // Setup Channel Pan Positions + if (pfh->version >= 11) + { + signed char *panpos = (signed char *)(lpStream + dwMemPos); + UINT nchannels = (pfh->version >= 13) ? 32 : 16; + for (UINT i=0; i<nchannels; i++) + { + int pan = (panpos[i] + 64) * 2; + if (pan < 0) pan = 0; + if (pan > 256) { pan = 128; ChnSettings[i].dwFlags |= CHN_SURROUND; } + ChnSettings[i].nPan = pan; + } + dwMemPos += nchannels; + } else + { + for (UINT i=0; i<16; i++) + { + ChnSettings[i].nPan = (lpStream[dwMemPos+i] & 1) ? 0x30 : 0xD0; + } + dwMemPos += 16; + } + // Get Tempo/Speed + m_nDefaultTempo = 125; + m_nDefaultSpeed = 6; + if (pfh->version >= 13) + { + if (lpStream[dwMemPos] >= 32) m_nDefaultTempo = lpStream[dwMemPos]; + if (lpStream[dwMemPos+1] <= 32) m_nDefaultSpeed = lpStream[dwMemPos+1]; + dwMemPos += 2; + } + // Setup sequence list + for (UINT iOrd=0; iOrd<MAX_ORDERS; iOrd++) + { + Order[iOrd] = 0xFF; + if (iOrd < pfh->numorders) + { + Order[iOrd] = iOrd; + PatternSize[iOrd] = 64; + if (pfh->version >= 14) + { + PatternSize[iOrd] = bswapLE16(*(USHORT *)(lpStream+dwMemPos)); + dwMemPos += 2; + } + ptracks[iOrd] = (USHORT *)(lpStream+dwMemPos); + dwMemPos += m_nChannels * sizeof(USHORT); + } + } + if (dwMemPos + m_nSamples * (sizeof(AMFSAMPLE)+8) > dwMemLength) return TRUE; + // Read Samples + UINT maxsampleseekpos = 0; + for (UINT iIns=0; iIns<m_nSamples; iIns++) + { + MODINSTRUMENT *pins = &Ins[iIns+1]; + AMFSAMPLE *psh = (AMFSAMPLE *)(lpStream + dwMemPos); + + dwMemPos += sizeof(AMFSAMPLE); + memcpy(m_szNames[iIns+1], psh->samplename, 32); + memcpy(pins->name, psh->filename, 13); + pins->nLength = bswapLE32(psh->length); + pins->nC4Speed = bswapLE16(psh->c2spd); + pins->nGlobalVol = 64; + pins->nVolume = psh->volume * 4; + if (pfh->version >= 11) + { + pins->nLoopStart = bswapLE32(*(DWORD *)(lpStream+dwMemPos)); + pins->nLoopEnd = bswapLE32(*(DWORD *)(lpStream+dwMemPos+4)); + dwMemPos += 8; + } else + { + pins->nLoopStart = bswapLE16(*(WORD *)(lpStream+dwMemPos)); + pins->nLoopEnd = pins->nLength; + dwMemPos += 2; + } + sampleseekpos[iIns] = 0; + if ((psh->type) && (bswapLE32(psh->offset) < dwMemLength-1)) + { + sampleseekpos[iIns] = bswapLE32(psh->offset); + if (bswapLE32(psh->offset) > maxsampleseekpos) + maxsampleseekpos = bswapLE32(psh->offset); + if ((pins->nLoopEnd > pins->nLoopStart + 2) + && (pins->nLoopEnd <= pins->nLength)) pins->uFlags |= CHN_LOOP; + } + } + // Read Track Mapping Table + USHORT *pTrackMap = (USHORT *)(lpStream+dwMemPos); + UINT realtrackcnt = 0; + dwMemPos += pfh->numtracks * sizeof(USHORT); + for (UINT iTrkMap=0; iTrkMap<pfh->numtracks; iTrkMap++) + { + if (realtrackcnt < pTrackMap[iTrkMap]) realtrackcnt = pTrackMap[iTrkMap]; + } + // Store tracks positions + BYTE **pTrackData = new BYTE *[realtrackcnt]; + memset(pTrackData, 0, sizeof(pTrackData)); + for (UINT iTrack=0; iTrack<realtrackcnt; iTrack++) if (dwMemPos + 3 <= dwMemLength) + { + UINT nTrkSize = bswapLE16(*(USHORT *)(lpStream+dwMemPos)); + nTrkSize += (UINT)lpStream[dwMemPos+2] << 16; + if (dwMemPos + nTrkSize * 3 + 3 <= dwMemLength) + { + pTrackData[iTrack] = (BYTE *)(lpStream + dwMemPos); + } + dwMemPos += nTrkSize * 3 + 3; + } + // Create the patterns from the list of tracks + for (UINT iPat=0; iPat<pfh->numorders; iPat++) + { + MODCOMMAND *p = AllocatePattern(PatternSize[iPat], m_nChannels); + if (!p) break; + Patterns[iPat] = p; + for (UINT iChn=0; iChn<m_nChannels; iChn++) + { + UINT nTrack = bswapLE16(ptracks[iPat][iChn]); + if ((nTrack) && (nTrack <= pfh->numtracks)) + { + UINT realtrk = bswapLE16(pTrackMap[nTrack-1]); + if (realtrk) + { + realtrk--; + if ((realtrk < realtrackcnt) && (pTrackData[realtrk])) + { + AMF_Unpack(p+iChn, pTrackData[realtrk], PatternSize[iPat], m_nChannels); + } + } + } + } + } + delete[] pTrackData; + // Read Sample Data + for (UINT iSeek=1; iSeek<=maxsampleseekpos; iSeek++) + { + if (dwMemPos >= dwMemLength) break; + for (UINT iSmp=0; iSmp<m_nSamples; iSmp++) if (iSeek == sampleseekpos[iSmp]) + { + MODINSTRUMENT *pins = &Ins[iSmp+1]; + dwMemPos += ReadSample(pins, RS_PCM8U, (LPCSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos); + break; + } + } + return TRUE; +} + diff --git a/lib/libmodplug/src/load_ams.cpp b/lib/libmodplug/src/load_ams.cpp new file mode 100644 index 0000000000..c5f12dc99a --- /dev/null +++ b/lib/libmodplug/src/load_ams.cpp @@ -0,0 +1,628 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +////////////////////////////////////////////// +// AMS module loader // +////////////////////////////////////////////// +#include "stdafx.h" +#include "sndfile.h" + +//#pragma warning(disable:4244) + +#pragma pack(1) + +typedef struct AMSFILEHEADER +{ + char szHeader[7]; // "Extreme" // changed from CHAR + BYTE verlo, verhi; // 0x??,0x01 + BYTE chncfg; + BYTE samples; + WORD patterns; + WORD orders; + BYTE vmidi; + WORD extra; +} AMSFILEHEADER; + +typedef struct AMSSAMPLEHEADER +{ + DWORD length; + DWORD loopstart; + DWORD loopend; + BYTE finetune_and_pan; + WORD samplerate; // C-2 = 8363 + BYTE volume; // 0-127 + BYTE infobyte; +} AMSSAMPLEHEADER; + + +#pragma pack() + + + +BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength) +//----------------------------------------------------------- +{ + BYTE pkinf[MAX_SAMPLES]; + AMSFILEHEADER *pfh = (AMSFILEHEADER *)lpStream; + DWORD dwMemPos; + UINT tmp, tmp2; + + if ((!lpStream) || (dwMemLength < 1024)) return FALSE; + if ((pfh->verhi != 0x01) || (strncmp(pfh->szHeader, "Extreme", 7)) + || (!pfh->patterns) || (!pfh->orders) || (!pfh->samples) || (pfh->samples > MAX_SAMPLES) + || (pfh->patterns > MAX_PATTERNS) || (pfh->orders > MAX_ORDERS)) + { + return ReadAMS2(lpStream, dwMemLength); + } + dwMemPos = sizeof(AMSFILEHEADER) + pfh->extra; + if (dwMemPos + pfh->samples * sizeof(AMSSAMPLEHEADER) + 256 >= dwMemLength) return FALSE; + m_nType = MOD_TYPE_AMS; + m_nInstruments = 0; + m_nChannels = (pfh->chncfg & 0x1F) + 1; + m_nSamples = pfh->samples; + for (UINT nSmp=1; nSmp<=m_nSamples; nSmp++, dwMemPos += sizeof(AMSSAMPLEHEADER)) + { + AMSSAMPLEHEADER *psh = (AMSSAMPLEHEADER *)(lpStream + dwMemPos); + MODINSTRUMENT *pins = &Ins[nSmp]; + pins->nLength = psh->length; + pins->nLoopStart = psh->loopstart; + pins->nLoopEnd = psh->loopend; + pins->nGlobalVol = 64; + pins->nVolume = psh->volume << 1; + pins->nC4Speed = psh->samplerate; + pins->nPan = (psh->finetune_and_pan & 0xF0); + if (pins->nPan < 0x80) pins->nPan += 0x10; + pins->nFineTune = MOD2XMFineTune(psh->finetune_and_pan & 0x0F); + pins->uFlags = (psh->infobyte & 0x80) ? CHN_16BIT : 0; + if ((pins->nLoopEnd <= pins->nLength) && (pins->nLoopStart+4 <= pins->nLoopEnd)) pins->uFlags |= CHN_LOOP; + pkinf[nSmp] = psh->infobyte; + } + // Read Song Name + tmp = lpStream[dwMemPos++]; + if (dwMemPos + tmp + 1 >= dwMemLength) return TRUE; + tmp2 = (tmp < 32) ? tmp : 31; + if (tmp2) memcpy(m_szNames[0], lpStream+dwMemPos, tmp2); + m_szNames[0][tmp2] = 0; + dwMemPos += tmp; + // Read sample names + for (UINT sNam=1; sNam<=m_nSamples; sNam++) + { + if (dwMemPos + 32 >= dwMemLength) return TRUE; + tmp = lpStream[dwMemPos++]; + tmp2 = (tmp < 32) ? tmp : 31; + if (tmp2) memcpy(m_szNames[sNam], lpStream+dwMemPos, tmp2); + dwMemPos += tmp; + } + // Skip Channel names + for (UINT cNam=0; cNam<m_nChannels; cNam++) + { + if (dwMemPos + 32 >= dwMemLength) return TRUE; + tmp = lpStream[dwMemPos++]; + dwMemPos += tmp; + } + // Read Pattern Names + m_lpszPatternNames = new char[pfh->patterns * 32]; // changed from CHAR + if (!m_lpszPatternNames) return TRUE; + m_nPatternNames = pfh->patterns; + memset(m_lpszPatternNames, 0, m_nPatternNames * 32); + for (UINT pNam=0; pNam < m_nPatternNames; pNam++) + { + if (dwMemPos + 32 >= dwMemLength) return TRUE; + tmp = lpStream[dwMemPos++]; + tmp2 = (tmp < 32) ? tmp : 31; + if (tmp2) memcpy(m_lpszPatternNames+pNam*32, lpStream+dwMemPos, tmp2); + dwMemPos += tmp; + } + // Read Song Comments + tmp = *((WORD *)(lpStream+dwMemPos)); + dwMemPos += 2; + if (dwMemPos + tmp >= dwMemLength) return TRUE; + if (tmp) + { + m_lpszSongComments = new char[tmp+1]; // changed from CHAR + if (!m_lpszSongComments) return TRUE; + memset(m_lpszSongComments, 0, tmp+1); + memcpy(m_lpszSongComments, lpStream + dwMemPos, tmp); + dwMemPos += tmp; + } + // Read Order List + for (UINT iOrd=0; iOrd<pfh->orders; iOrd++, dwMemPos += 2) + { + UINT n = *((WORD *)(lpStream+dwMemPos)); + Order[iOrd] = (BYTE)n; + } + // Read Patterns + for (UINT iPat=0; iPat<pfh->patterns; iPat++) + { + if (dwMemPos + 4 >= dwMemLength) return TRUE; + UINT len = *((DWORD *)(lpStream + dwMemPos)); + dwMemPos += 4; + if ((len >= dwMemLength) || (dwMemPos + len > dwMemLength)) return TRUE; + PatternSize[iPat] = 64; + MODCOMMAND *m = AllocatePattern(PatternSize[iPat], m_nChannels); + if (!m) return TRUE; + Patterns[iPat] = m; + const BYTE *p = lpStream + dwMemPos; + UINT row = 0, i = 0; + while ((row < PatternSize[iPat]) && (i+2 < len)) + { + BYTE b0 = p[i++]; + BYTE b1 = p[i++]; + BYTE b2 = 0; + UINT ch = b0 & 0x3F; + // Note+Instr + if (!(b0 & 0x40)) + { + b2 = p[i++]; + if (ch < m_nChannels) + { + if (b1 & 0x7F) m[ch].note = (b1 & 0x7F) + 25; + m[ch].instr = b2; + } + if (b1 & 0x80) + { + b0 |= 0x40; + b1 = p[i++]; + } + } + // Effect + if (b0 & 0x40) + { + anothercommand: + if (b1 & 0x40) + { + if (ch < m_nChannels) + { + m[ch].volcmd = VOLCMD_VOLUME; + m[ch].vol = b1 & 0x3F; + } + } else + { + b2 = p[i++]; + if (ch < m_nChannels) + { + UINT cmd = b1 & 0x3F; + if (cmd == 0x0C) + { + m[ch].volcmd = VOLCMD_VOLUME; + m[ch].vol = b2 >> 1; + } else + if (cmd == 0x0E) + { + if (!m[ch].command) + { + UINT command = CMD_S3MCMDEX; + UINT param = b2; + switch(param & 0xF0) + { + case 0x00: if (param & 0x08) { param &= 0x07; param |= 0x90; } else {command=param=0;} break; + case 0x10: command = CMD_PORTAMENTOUP; param |= 0xF0; break; + case 0x20: command = CMD_PORTAMENTODOWN; param |= 0xF0; break; + case 0x30: param = (param & 0x0F) | 0x10; break; + case 0x40: param = (param & 0x0F) | 0x30; break; + case 0x50: param = (param & 0x0F) | 0x20; break; + case 0x60: param = (param & 0x0F) | 0xB0; break; + case 0x70: param = (param & 0x0F) | 0x40; break; + case 0x90: command = CMD_RETRIG; param &= 0x0F; break; + case 0xA0: if (param & 0x0F) { command = CMD_VOLUMESLIDE; param = (param << 4) | 0x0F; } else command=param=0; break; + case 0xB0: if (param & 0x0F) { command = CMD_VOLUMESLIDE; param |= 0xF0; } else command=param=0; break; + } + m[ch].command = command; + m[ch].param = param; + } + } else + { + m[ch].command = cmd; + m[ch].param = b2; + ConvertModCommand(&m[ch]); + } + } + } + if (b1 & 0x80) + { + b1 = p[i++]; + if (i <= len) goto anothercommand; + } + } + if (b0 & 0x80) + { + row++; + m += m_nChannels; + } + } + dwMemPos += len; + } + // Read Samples + for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++) if (Ins[iSmp].nLength) + { + if (dwMemPos >= dwMemLength - 9) return TRUE; + UINT flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_AMS16 : RS_AMS8; + dwMemPos += ReadSample(&Ins[iSmp], flags, (LPSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos); + } + return TRUE; +} + + +///////////////////////////////////////////////////////////////////// +// AMS 2.2 loader + +#pragma pack(1) + +typedef struct AMS2FILEHEADER +{ + DWORD dwHdr1; // AMShdr + WORD wHdr2; + BYTE b1A; // 0x1A + BYTE titlelen; // 30-bytes max + CHAR szTitle[30]; // [titlelen] +} AMS2FILEHEADER; + +typedef struct AMS2SONGHEADER +{ + WORD version; + BYTE instruments; + WORD patterns; + WORD orders; + WORD bpm; + BYTE speed; + BYTE channels; + BYTE commands; + BYTE rows; + WORD flags; +} AMS2SONGHEADER; + +typedef struct AMS2INSTRUMENT +{ + BYTE samples; + BYTE notemap[NOTE_MAX]; +} AMS2INSTRUMENT; + +typedef struct AMS2ENVELOPE +{ + BYTE speed; + BYTE sustain; + BYTE loopbegin; + BYTE loopend; + BYTE points; + BYTE info[3]; +} AMS2ENVELOPE; + +typedef struct AMS2SAMPLE +{ + DWORD length; + DWORD loopstart; + DWORD loopend; + WORD frequency; + BYTE finetune; + WORD c4speed; + CHAR transpose; + BYTE volume; + BYTE flags; +} AMS2SAMPLE; + + +#pragma pack() + + +BOOL CSoundFile::ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength) +//------------------------------------------------------------ +{ + AMS2FILEHEADER *pfh = (AMS2FILEHEADER *)lpStream; + AMS2SONGHEADER *psh; + DWORD dwMemPos; + BYTE smpmap[16]; + BYTE packedsamples[MAX_SAMPLES]; + + if ((pfh->dwHdr1 != 0x68534D41) || (pfh->wHdr2 != 0x7264) + || (pfh->b1A != 0x1A) || (pfh->titlelen > 30)) return FALSE; + dwMemPos = pfh->titlelen + 8; + psh = (AMS2SONGHEADER *)(lpStream + dwMemPos); + if (((psh->version & 0xFF00) != 0x0200) || (!psh->instruments) + || (psh->instruments > MAX_INSTRUMENTS) || (!psh->patterns) || (!psh->orders)) return FALSE; + dwMemPos += sizeof(AMS2SONGHEADER); + if (pfh->titlelen) + { + memcpy(m_szNames, pfh->szTitle, pfh->titlelen); + m_szNames[0][pfh->titlelen] = 0; + } + m_nType = MOD_TYPE_AMS; + m_nChannels = 32; + m_nDefaultTempo = psh->bpm >> 8; + m_nDefaultSpeed = psh->speed; + m_nInstruments = psh->instruments; + m_nSamples = 0; + if (psh->flags & 0x40) m_dwSongFlags |= SONG_LINEARSLIDES; + for (UINT nIns=1; nIns<=m_nInstruments; nIns++) + { + UINT insnamelen = lpStream[dwMemPos]; + CHAR *pinsname = (CHAR *)(lpStream+dwMemPos+1); + dwMemPos += insnamelen + 1; + AMS2INSTRUMENT *pins = (AMS2INSTRUMENT *)(lpStream + dwMemPos); + dwMemPos += sizeof(AMS2INSTRUMENT); + if (dwMemPos + 1024 >= dwMemLength) return TRUE; + AMS2ENVELOPE *volenv, *panenv, *pitchenv; + volenv = (AMS2ENVELOPE *)(lpStream+dwMemPos); + dwMemPos += 5 + volenv->points*3; + panenv = (AMS2ENVELOPE *)(lpStream+dwMemPos); + dwMemPos += 5 + panenv->points*3; + pitchenv = (AMS2ENVELOPE *)(lpStream+dwMemPos); + dwMemPos += 5 + pitchenv->points*3; + INSTRUMENTHEADER *penv = new INSTRUMENTHEADER; + if (!penv) return TRUE; + memset(smpmap, 0, sizeof(smpmap)); + memset(penv, 0, sizeof(INSTRUMENTHEADER)); + for (UINT ismpmap=0; ismpmap<pins->samples; ismpmap++) + { + if ((ismpmap >= 16) || (m_nSamples+1 >= MAX_SAMPLES)) break; + m_nSamples++; + smpmap[ismpmap] = m_nSamples; + } + penv->nGlobalVol = 64; + penv->nPan = 128; + penv->nPPC = 60; + Headers[nIns] = penv; + if (insnamelen) + { + if (insnamelen > 31) insnamelen = 31; + memcpy(penv->name, pinsname, insnamelen); + penv->name[insnamelen] = 0; + } + for (UINT inotemap=0; inotemap<NOTE_MAX; inotemap++) + { + penv->NoteMap[inotemap] = inotemap+1; + penv->Keyboard[inotemap] = smpmap[pins->notemap[inotemap] & 0x0F]; + } + // Volume Envelope + { + UINT pos = 0; + penv->nVolEnv = (volenv->points > 16) ? 16 : volenv->points; + penv->nVolSustainBegin = penv->nVolSustainEnd = volenv->sustain; + penv->nVolLoopStart = volenv->loopbegin; + penv->nVolLoopEnd = volenv->loopend; + for (UINT i=0; i<penv->nVolEnv; i++) + { + penv->VolEnv[i] = (BYTE)((volenv->info[i*3+2] & 0x7F) >> 1); + pos += volenv->info[i*3] + ((volenv->info[i*3+1] & 1) << 8); + penv->VolPoints[i] = (WORD)pos; + } + } + penv->nFadeOut = (((lpStream[dwMemPos+2] & 0x0F) << 8) | (lpStream[dwMemPos+1])) << 3; + UINT envflags = lpStream[dwMemPos+3]; + if (envflags & 0x01) penv->dwFlags |= ENV_VOLLOOP; + if (envflags & 0x02) penv->dwFlags |= ENV_VOLSUSTAIN; + if (envflags & 0x04) penv->dwFlags |= ENV_VOLUME; + dwMemPos += 5; + // Read Samples + for (UINT ismp=0; ismp<pins->samples; ismp++) + { + MODINSTRUMENT *psmp = ((ismp < 16) && (smpmap[ismp])) ? &Ins[smpmap[ismp]] : NULL; + UINT smpnamelen = lpStream[dwMemPos]; + if ((psmp) && (smpnamelen) && (smpnamelen <= 22)) + { + memcpy(m_szNames[smpmap[ismp]], lpStream+dwMemPos+1, smpnamelen); + } + dwMemPos += smpnamelen + 1; + if (psmp) + { + AMS2SAMPLE *pams = (AMS2SAMPLE *)(lpStream+dwMemPos); + psmp->nGlobalVol = 64; + psmp->nPan = 128; + psmp->nLength = pams->length; + psmp->nLoopStart = pams->loopstart; + psmp->nLoopEnd = pams->loopend; + psmp->nC4Speed = pams->c4speed; + psmp->RelativeTone = pams->transpose; + psmp->nVolume = pams->volume / 2; + packedsamples[smpmap[ismp]] = pams->flags; + if (pams->flags & 0x04) psmp->uFlags |= CHN_16BIT; + if (pams->flags & 0x08) psmp->uFlags |= CHN_LOOP; + if (pams->flags & 0x10) psmp->uFlags |= CHN_PINGPONGLOOP; + } + dwMemPos += sizeof(AMS2SAMPLE); + } + } + if (dwMemPos + 256 >= dwMemLength) return TRUE; + // Comments + { + UINT composernamelen = lpStream[dwMemPos]; + if (composernamelen) + { + m_lpszSongComments = new char[composernamelen+1]; // changed from CHAR + if (m_lpszSongComments) + { + memcpy(m_lpszSongComments, lpStream+dwMemPos+1, composernamelen); + m_lpszSongComments[composernamelen] = 0; + } + } + dwMemPos += composernamelen + 1; + // channel names + for (UINT i=0; i<32; i++) + { + UINT chnnamlen = lpStream[dwMemPos]; + if ((chnnamlen) && (chnnamlen < MAX_CHANNELNAME)) + { + memcpy(ChnSettings[i].szName, lpStream+dwMemPos+1, chnnamlen); + } + dwMemPos += chnnamlen + 1; + if (dwMemPos + chnnamlen + 256 >= dwMemLength) return TRUE; + } + // packed comments (ignored) + UINT songtextlen = *((LPDWORD)(lpStream+dwMemPos)); + dwMemPos += songtextlen; + if (dwMemPos + 256 >= dwMemLength) return TRUE; + } + // Order List + { + for (UINT i=0; i<MAX_ORDERS; i++) + { + Order[i] = 0xFF; + if (dwMemPos + 2 >= dwMemLength) return TRUE; + if (i < psh->orders) + { + Order[i] = lpStream[dwMemPos]; + dwMemPos += 2; + } + } + } + // Pattern Data + for (UINT ipat=0; ipat<psh->patterns; ipat++) + { + if (dwMemPos+8 >= dwMemLength) return TRUE; + UINT packedlen = *((LPDWORD)(lpStream+dwMemPos)); + UINT numrows = 1 + (UINT)(lpStream[dwMemPos+4]); + //UINT patchn = 1 + (UINT)(lpStream[dwMemPos+5] & 0x1F); + //UINT patcmds = 1 + (UINT)(lpStream[dwMemPos+5] >> 5); + UINT patnamlen = lpStream[dwMemPos+6]; + dwMemPos += 4; + if ((ipat < MAX_PATTERNS) && (packedlen < dwMemLength-dwMemPos) && (numrows >= 8)) + { + if ((patnamlen) && (patnamlen < MAX_PATTERNNAME)) + { + char s[MAX_PATTERNNAME]; // changed from CHAR + memcpy(s, lpStream+dwMemPos+3, patnamlen); + s[patnamlen] = 0; + SetPatternName(ipat, s); + } + PatternSize[ipat] = numrows; + Patterns[ipat] = AllocatePattern(numrows, m_nChannels); + if (!Patterns[ipat]) return TRUE; + // Unpack Pattern Data + LPCBYTE psrc = lpStream + dwMemPos; + UINT pos = 3 + patnamlen; + UINT row = 0; + while ((pos < packedlen) && (row < numrows)) + { + MODCOMMAND *m = Patterns[ipat] + row * m_nChannels; + UINT byte1 = psrc[pos++]; + UINT ch = byte1 & 0x1F; + // Read Note + Instr + if (!(byte1 & 0x40)) + { + UINT byte2 = psrc[pos++]; + UINT note = byte2 & 0x7F; + if (note) m[ch].note = (note > 1) ? (note-1) : 0xFF; + m[ch].instr = psrc[pos++]; + // Read Effect + while (byte2 & 0x80) + { + byte2 = psrc[pos++]; + if (byte2 & 0x40) + { + m[ch].volcmd = VOLCMD_VOLUME; + m[ch].vol = byte2 & 0x3F; + } else + { + UINT command = byte2 & 0x3F; + UINT param = psrc[pos++]; + if (command == 0x0C) + { + m[ch].volcmd = VOLCMD_VOLUME; + m[ch].vol = param / 2; + } else + if (command < 0x10) + { + m[ch].command = command; + m[ch].param = param; + ConvertModCommand(&m[ch]); + } else + { + // TODO: AMS effects + } + } + } + } + if (byte1 & 0x80) row++; + } + } + dwMemPos += packedlen; + } + // Read Samples + for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++) if (Ins[iSmp].nLength) + { + if (dwMemPos >= dwMemLength - 9) return TRUE; + UINT flags; + if (packedsamples[iSmp] & 0x03) + { + flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_AMS16 : RS_AMS8; + } else + { + flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S; + } + dwMemPos += ReadSample(&Ins[iSmp], flags, (LPSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos); + } + return TRUE; +} + + +///////////////////////////////////////////////////////////////////// +// AMS Sample unpacking + +void AMSUnpack(const char *psrc, UINT inputlen, char *pdest, UINT dmax, char packcharacter) +{ + UINT tmplen = dmax; + signed char *amstmp = new signed char[tmplen]; + + if (!amstmp) return; + // Unpack Loop + { + signed char *p = amstmp; + UINT i=0, j=0; + while ((i < inputlen) && (j < tmplen)) + { + signed char ch = psrc[i++]; + if (ch == packcharacter) + { + BYTE ch2 = psrc[i++]; + if (ch2) + { + ch = psrc[i++]; + while (ch2--) + { + p[j++] = ch; + if (j >= tmplen) break; + } + } else p[j++] = packcharacter; + } else p[j++] = ch; + } + } + // Bit Unpack Loop + { + signed char *p = amstmp; + UINT bitcount = 0x80, dh; + UINT k=0; + for (UINT i=0; i<dmax; i++) + { + BYTE al = *p++; + dh = 0; + for (UINT count=0; count<8; count++) + { + UINT bl = al & bitcount; + bl = ((bl|(bl<<8)) >> ((dh+8-count) & 7)) & 0xFF; + bitcount = ((bitcount|(bitcount<<8)) >> 1) & 0xFF; + pdest[k++] |= bl; + if (k >= dmax) + { + k = 0; + dh++; + } + } + bitcount = ((bitcount|(bitcount<<8)) >> dh) & 0xFF; + } + } + // Delta Unpack + { + signed char old = 0; + for (UINT i=0; i<dmax; i++) + { + int pos = ((LPBYTE)pdest)[i]; + if ((pos != 128) && (pos & 0x80)) pos = -(pos & 0x7F); + old -= (signed char)pos; + pdest[i] = old; + } + } + delete amstmp; +} + diff --git a/lib/libmodplug/src/load_dbm.cpp b/lib/libmodplug/src/load_dbm.cpp new file mode 100644 index 0000000000..363579d5e0 --- /dev/null +++ b/lib/libmodplug/src/load_dbm.cpp @@ -0,0 +1,368 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net>, + * Adam Goode <adam@evdebs.org> (endian and char fixes for PPC) +*/ + +/////////////////////////////////////////////////////////////// +// +// DigiBooster Pro Module Loader (*.dbm) +// +// Note: this loader doesn't handle multiple songs +// +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "sndfile.h" + +//#pragma warning(disable:4244) + +#define DBM_FILE_MAGIC 0x304d4244 +#define DBM_ID_NAME 0x454d414e +#define DBM_NAMELEN 0x2c000000 +#define DBM_ID_INFO 0x4f464e49 +#define DBM_INFOLEN 0x0a000000 +#define DBM_ID_SONG 0x474e4f53 +#define DBM_ID_INST 0x54534e49 +#define DBM_ID_VENV 0x564e4556 +#define DBM_ID_PATT 0x54544150 +#define DBM_ID_SMPL 0x4c504d53 + +#pragma pack(1) + +typedef struct DBMFILEHEADER +{ + DWORD dbm_id; // "DBM0" = 0x304d4244 + WORD trkver; // Tracker version: 02.15 + WORD reserved; + DWORD name_id; // "NAME" = 0x454d414e + DWORD name_len; // name length: always 44 + CHAR songname[44]; + DWORD info_id; // "INFO" = 0x4f464e49 + DWORD info_len; // 0x0a000000 + WORD instruments; + WORD samples; + WORD songs; + WORD patterns; + WORD channels; + DWORD song_id; // "SONG" = 0x474e4f53 + DWORD song_len; + CHAR songname2[44]; + WORD orders; +// WORD orderlist[0]; // orderlist[orders] in words +} DBMFILEHEADER; + +typedef struct DBMINSTRUMENT +{ + CHAR name[30]; + WORD sampleno; + WORD volume; + DWORD finetune; + DWORD loopstart; + DWORD looplen; + WORD panning; + WORD flags; +} DBMINSTRUMENT; + +typedef struct DBMENVELOPE +{ + WORD instrument; + BYTE flags; + BYTE numpoints; + BYTE sustain1; + BYTE loopbegin; + BYTE loopend; + BYTE sustain2; + WORD volenv[2*32]; +} DBMENVELOPE; + +typedef struct DBMPATTERN +{ + WORD rows; + DWORD packedsize; + BYTE patterndata[2]; // [packedsize] +} DBMPATTERN; + +typedef struct DBMSAMPLE +{ + DWORD flags; + DWORD samplesize; + BYTE sampledata[2]; // [samplesize] +} DBMSAMPLE; + +#pragma pack() + + +BOOL CSoundFile::ReadDBM(const BYTE *lpStream, DWORD dwMemLength) +//--------------------------------------------------------------- +{ + DBMFILEHEADER *pfh = (DBMFILEHEADER *)lpStream; + DWORD dwMemPos; + UINT nOrders, nSamples, nInstruments, nPatterns; + + if ((!lpStream) || (dwMemLength <= sizeof(DBMFILEHEADER)) || (!pfh->channels) + || (pfh->dbm_id != DBM_FILE_MAGIC) || (!pfh->songs) || (pfh->song_id != DBM_ID_SONG) + || (pfh->name_id != DBM_ID_NAME) || (pfh->name_len != DBM_NAMELEN) + || (pfh->info_id != DBM_ID_INFO) || (pfh->info_len != DBM_INFOLEN)) return FALSE; + dwMemPos = sizeof(DBMFILEHEADER); + nOrders = bswapBE16(pfh->orders); + if (dwMemPos + 2 * nOrders + 8*3 >= dwMemLength) return FALSE; + nInstruments = bswapBE16(pfh->instruments); + nSamples = bswapBE16(pfh->samples); + nPatterns = bswapBE16(pfh->patterns); + m_nType = MOD_TYPE_DBM; + m_nChannels = bswapBE16(pfh->channels); + if (m_nChannels < 4) m_nChannels = 4; + if (m_nChannels > 64) m_nChannels = 64; + memcpy(m_szNames[0], (pfh->songname[0]) ? pfh->songname : pfh->songname2, 32); + m_szNames[0][31] = 0; + for (UINT iOrd=0; iOrd < nOrders; iOrd++) + { + Order[iOrd] = lpStream[dwMemPos+iOrd*2+1]; + if (iOrd >= MAX_ORDERS-2) break; + } + dwMemPos += 2*nOrders; + while (dwMemPos + 10 < dwMemLength) + { + DWORD chunk_id = ((LPDWORD)(lpStream+dwMemPos))[0]; + DWORD chunk_size = bswapBE32(((LPDWORD)(lpStream+dwMemPos))[1]); + DWORD chunk_pos; + + dwMemPos += 8; + chunk_pos = dwMemPos; + if ((dwMemPos + chunk_size > dwMemLength) || (chunk_size > dwMemLength)) break; + dwMemPos += chunk_size; + // Instruments + if (chunk_id == DBM_ID_INST) + { + if (nInstruments >= MAX_INSTRUMENTS) nInstruments = MAX_INSTRUMENTS-1; + for (UINT iIns=0; iIns<nInstruments; iIns++) + { + MODINSTRUMENT *psmp; + INSTRUMENTHEADER *penv; + DBMINSTRUMENT *pih; + UINT nsmp; + + if (chunk_pos + sizeof(DBMINSTRUMENT) > dwMemPos) break; + if ((penv = new INSTRUMENTHEADER) == NULL) break; + pih = (DBMINSTRUMENT *)(lpStream+chunk_pos); + nsmp = bswapBE16(pih->sampleno); + psmp = ((nsmp) && (nsmp < MAX_SAMPLES)) ? &Ins[nsmp] : NULL; + memset(penv, 0, sizeof(INSTRUMENTHEADER)); + memcpy(penv->name, pih->name, 30); + if (psmp) + { + memcpy(m_szNames[nsmp], pih->name, 30); + m_szNames[nsmp][30] = 0; + } + Headers[iIns+1] = penv; + penv->nFadeOut = 1024; // ??? + penv->nGlobalVol = 64; + penv->nPan = bswapBE16(pih->panning); + if ((penv->nPan) && (penv->nPan < 256)) + penv->dwFlags = ENV_SETPANNING; + else + penv->nPan = 128; + penv->nPPC = 5*12; + for (UINT i=0; i<NOTE_MAX; i++) + { + penv->Keyboard[i] = nsmp; + penv->NoteMap[i] = i+1; + } + // Sample Info + if (psmp) + { + DWORD sflags = bswapBE16(pih->flags); + psmp->nVolume = bswapBE16(pih->volume) * 4; + if ((!psmp->nVolume) || (psmp->nVolume > 256)) psmp->nVolume = 256; + psmp->nGlobalVol = 64; + psmp->nC4Speed = bswapBE32(pih->finetune); + int f2t = FrequencyToTranspose(psmp->nC4Speed); + psmp->RelativeTone = f2t >> 7; + psmp->nFineTune = f2t & 0x7F; + if ((pih->looplen) && (sflags & 3)) + { + psmp->nLoopStart = bswapBE32(pih->loopstart); + psmp->nLoopEnd = psmp->nLoopStart + bswapBE32(pih->looplen); + psmp->uFlags |= CHN_LOOP; + psmp->uFlags &= ~CHN_PINGPONGLOOP; + if (sflags & 2) psmp->uFlags |= CHN_PINGPONGLOOP; + } + } + chunk_pos += sizeof(DBMINSTRUMENT); + m_nInstruments = iIns+1; + } + } else + // Volume Envelopes + if (chunk_id == DBM_ID_VENV) + { + UINT nEnvelopes = lpStream[chunk_pos+1]; + + chunk_pos += 2; + for (UINT iEnv=0; iEnv<nEnvelopes; iEnv++) + { + DBMENVELOPE *peh; + UINT nins; + + if (chunk_pos + sizeof(DBMENVELOPE) > dwMemPos) break; + peh = (DBMENVELOPE *)(lpStream+chunk_pos); + nins = bswapBE16(peh->instrument); + if ((nins) && (nins < MAX_INSTRUMENTS) && (Headers[nins]) && (peh->numpoints)) + { + INSTRUMENTHEADER *penv = Headers[nins]; + + if (peh->flags & 1) penv->dwFlags |= ENV_VOLUME; + if (peh->flags & 2) penv->dwFlags |= ENV_VOLSUSTAIN; + if (peh->flags & 4) penv->dwFlags |= ENV_VOLLOOP; + penv->nVolEnv = peh->numpoints + 1; + if (penv->nVolEnv > MAX_ENVPOINTS) penv->nVolEnv = MAX_ENVPOINTS; + penv->nVolLoopStart = peh->loopbegin; + penv->nVolLoopEnd = peh->loopend; + penv->nVolSustainBegin = penv->nVolSustainEnd = peh->sustain1; + for (UINT i=0; i<penv->nVolEnv; i++) + { + penv->VolPoints[i] = bswapBE16(peh->volenv[i*2]); + penv->VolEnv[i] = (BYTE)bswapBE16(peh->volenv[i*2+1]); + } + } + chunk_pos += sizeof(DBMENVELOPE); + } + } else + // Packed Pattern Data + if (chunk_id == DBM_ID_PATT) + { + if (nPatterns > MAX_PATTERNS) nPatterns = MAX_PATTERNS; + for (UINT iPat=0; iPat<nPatterns; iPat++) + { + DBMPATTERN *pph; + DWORD pksize; + UINT nRows; + + if (chunk_pos + sizeof(DBMPATTERN) > dwMemPos) break; + pph = (DBMPATTERN *)(lpStream+chunk_pos); + pksize = bswapBE32(pph->packedsize); + if ((chunk_pos + pksize + 6 > dwMemPos) || (pksize > dwMemPos)) break; + nRows = bswapBE16(pph->rows); + if ((nRows >= 4) && (nRows <= 256)) + { + MODCOMMAND *m = AllocatePattern(nRows, m_nChannels); + if (m) + { + LPBYTE pkdata = (LPBYTE)&pph->patterndata; + UINT row = 0; + UINT i = 0; + + PatternSize[iPat] = nRows; + Patterns[iPat] = m; + while ((i+3<pksize) && (row < nRows)) + { + UINT ch = pkdata[i++]; + + if (ch) + { + BYTE b = pkdata[i++]; + ch--; + if (ch < m_nChannels) + { + if (b & 0x01) + { + UINT note = pkdata[i++]; + + if (note == 0x1F) note = 0xFF; else + if ((note) && (note < 0xFE)) + { + note = ((note >> 4)*12) + (note & 0x0F) + 13; + } + m[ch].note = note; + } + if (b & 0x02) m[ch].instr = pkdata[i++]; + if (b & 0x3C) + { + UINT cmd1 = 0xFF, param1 = 0, cmd2 = 0xFF, param2 = 0; + if (b & 0x04) cmd1 = (UINT)pkdata[i++]; + if (b & 0x08) param1 = pkdata[i++]; + if (b & 0x10) cmd2 = (UINT)pkdata[i++]; + if (b & 0x20) param2 = pkdata[i++]; + if (cmd1 == 0x0C) + { + m[ch].volcmd = VOLCMD_VOLUME; + m[ch].vol = param1; + cmd1 = 0xFF; + } else + if (cmd2 == 0x0C) + { + m[ch].volcmd = VOLCMD_VOLUME; + m[ch].vol = param2; + cmd2 = 0xFF; + } + if ((cmd1 > 0x13) || ((cmd1 >= 0x10) && (cmd2 < 0x10))) + { + cmd1 = cmd2; + param1 = param2; + cmd2 = 0xFF; + } + if (cmd1 <= 0x13) + { + m[ch].command = cmd1; + m[ch].param = param1; + ConvertModCommand(&m[ch]); + } + } + } else + { + if (b & 0x01) i++; + if (b & 0x02) i++; + if (b & 0x04) i++; + if (b & 0x08) i++; + if (b & 0x10) i++; + if (b & 0x20) i++; + } + } else + { + row++; + m += m_nChannels; + } + } + } + } + chunk_pos += 6 + pksize; + } + } else + // Reading Sample Data + if (chunk_id == DBM_ID_SMPL) + { + if (nSamples >= MAX_SAMPLES) nSamples = MAX_SAMPLES-1; + m_nSamples = nSamples; + for (UINT iSmp=1; iSmp<=nSamples; iSmp++) + { + MODINSTRUMENT *pins; + DBMSAMPLE *psh; + DWORD samplesize; + DWORD sampleflags; + + if (chunk_pos + sizeof(DBMSAMPLE) >= dwMemPos) break; + psh = (DBMSAMPLE *)(lpStream+chunk_pos); + chunk_pos += 8; + samplesize = bswapBE32(psh->samplesize); + sampleflags = bswapBE32(psh->flags); + pins = &Ins[iSmp]; + pins->nLength = samplesize; + if (sampleflags & 2) + { + pins->uFlags |= CHN_16BIT; + samplesize <<= 1; + } + if ((chunk_pos+samplesize > dwMemPos) || (samplesize > dwMemLength)) break; + if (sampleflags & 3) + { + ReadSample(pins, (pins->uFlags & CHN_16BIT) ? RS_PCM16M : RS_PCM8S, + (LPSTR)(psh->sampledata), samplesize); + } + chunk_pos += samplesize; + } + } + } + return TRUE; +} + diff --git a/lib/libmodplug/src/load_dmf.cpp b/lib/libmodplug/src/load_dmf.cpp new file mode 100644 index 0000000000..f8b803b6a6 --- /dev/null +++ b/lib/libmodplug/src/load_dmf.cpp @@ -0,0 +1,606 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +/////////////////////////////////////////////////////// +// DMF DELUSION DIGITAL MUSIC FILEFORMAT (X-Tracker) // +/////////////////////////////////////////////////////// +#include "stdafx.h" +#include "sndfile.h" + +//#define DMFLOG + +//#pragma warning(disable:4244) + +#pragma pack(1) + +typedef struct DMFHEADER +{ + DWORD id; // "DDMF" = 0x464d4444 + BYTE version; // 4 + CHAR trackername[8]; // "XTRACKER" + CHAR songname[30]; + CHAR composer[20]; + BYTE date[3]; +} DMFHEADER; + +typedef struct DMFINFO +{ + DWORD id; // "INFO" + DWORD infosize; +} DMFINFO; + +typedef struct DMFSEQU +{ + DWORD id; // "SEQU" + DWORD seqsize; + WORD loopstart; + WORD loopend; + WORD sequ[2]; +} DMFSEQU; + +typedef struct DMFPATT +{ + DWORD id; // "PATT" + DWORD patsize; + WORD numpat; // 1-1024 + BYTE tracks; + BYTE firstpatinfo; +} DMFPATT; + +typedef struct DMFTRACK +{ + BYTE tracks; + BYTE beat; // [hi|lo] -> hi=ticks per beat, lo=beats per measure + WORD ticks; // max 512 + DWORD jmpsize; +} DMFTRACK; + +typedef struct DMFSMPI +{ + DWORD id; + DWORD size; + BYTE samples; +} DMFSMPI; + +typedef struct DMFSAMPLE +{ + DWORD len; + DWORD loopstart; + DWORD loopend; + WORD c3speed; + BYTE volume; + BYTE flags; +} DMFSAMPLE; + +#pragma pack() + + +#ifdef DMFLOG +extern void Log(LPCSTR s, ...); +#endif + + +BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength) +//--------------------------------------------------------------- +{ + DMFHEADER *pfh = (DMFHEADER *)lpStream; + DMFINFO *psi; + DMFSEQU *sequ; + DWORD dwMemPos; + BYTE infobyte[32]; + BYTE smplflags[MAX_SAMPLES]; + + if ((!lpStream) || (dwMemLength < 1024)) return FALSE; + if ((pfh->id != 0x464d4444) || (!pfh->version) || (pfh->version & 0xF0)) return FALSE; + dwMemPos = 66; + memcpy(m_szNames[0], pfh->songname, 30); + m_szNames[0][30] = 0; + m_nType = MOD_TYPE_DMF; + m_nChannels = 0; +#ifdef DMFLOG + Log("DMF version %d: \"%s\": %d bytes (0x%04X)\n", pfh->version, m_szNames[0], dwMemLength, dwMemLength); +#endif + while (dwMemPos + 7 < dwMemLength) + { + DWORD id = *((LPDWORD)(lpStream+dwMemPos)); + + switch(id) + { + // "INFO" + case 0x4f464e49: + // "CMSG" + case 0x47534d43: + psi = (DMFINFO *)(lpStream+dwMemPos); + if (id == 0x47534d43) dwMemPos++; + if ((psi->infosize > dwMemLength) || (psi->infosize + dwMemPos + 8 > dwMemLength)) goto dmfexit; + if ((psi->infosize >= 8) && (!m_lpszSongComments)) + { + m_lpszSongComments = new char[psi->infosize]; // changed from CHAR + if (m_lpszSongComments) + { + for (UINT i=0; i<psi->infosize-1; i++) + { + CHAR c = lpStream[dwMemPos+8+i]; + if ((i % 40) == 39) + m_lpszSongComments[i] = 0x0d; + else + m_lpszSongComments[i] = (c < ' ') ? ' ' : c; + } + m_lpszSongComments[psi->infosize-1] = 0; + } + } + dwMemPos += psi->infosize + 8 - 1; + break; + + // "SEQU" + case 0x55514553: + sequ = (DMFSEQU *)(lpStream+dwMemPos); + if ((sequ->seqsize >= dwMemLength) || (dwMemPos + sequ->seqsize + 12 > dwMemLength)) goto dmfexit; + { + UINT nseq = sequ->seqsize >> 1; + if (nseq >= MAX_ORDERS-1) nseq = MAX_ORDERS-1; + if (sequ->loopstart < nseq) m_nRestartPos = sequ->loopstart; + for (UINT i=0; i<nseq; i++) Order[i] = (BYTE)sequ->sequ[i]; + } + dwMemPos += sequ->seqsize + 8; + break; + + // "PATT" + case 0x54544150: + if (!m_nChannels) + { + DMFPATT *patt = (DMFPATT *)(lpStream+dwMemPos); + UINT numpat; + DWORD dwPos = dwMemPos + 11; + if ((patt->patsize >= dwMemLength) || (dwMemPos + patt->patsize + 8 > dwMemLength)) goto dmfexit; + numpat = patt->numpat; + if (numpat > MAX_PATTERNS) numpat = MAX_PATTERNS; + m_nChannels = patt->tracks; + if (m_nChannels < patt->firstpatinfo) m_nChannels = patt->firstpatinfo; + if (m_nChannels > 32) m_nChannels = 32; + if (m_nChannels < 4) m_nChannels = 4; + for (UINT npat=0; npat<numpat; npat++) + { + DMFTRACK *pt = (DMFTRACK *)(lpStream+dwPos); + #ifdef DMFLOG + Log("Pattern #%d: %d tracks, %d rows\n", npat, pt->tracks, pt->ticks); + #endif + UINT tracks = pt->tracks; + if (tracks > 32) tracks = 32; + UINT ticks = pt->ticks; + if (ticks > 256) ticks = 256; + if (ticks < 16) ticks = 16; + dwPos += 8; + if ((pt->jmpsize >= dwMemLength) || (dwPos + pt->jmpsize + 4 >= dwMemLength)) break; + PatternSize[npat] = (WORD)ticks; + MODCOMMAND *m = AllocatePattern(PatternSize[npat], m_nChannels); + if (!m) goto dmfexit; + Patterns[npat] = m; + DWORD d = dwPos; + dwPos += pt->jmpsize; + UINT ttype = 1; + UINT tempo = 125; + UINT glbinfobyte = 0; + UINT pbeat = (pt->beat & 0xf0) ? pt->beat>>4 : 8; + BOOL tempochange = (pt->beat & 0xf0) ? TRUE : FALSE; + memset(infobyte, 0, sizeof(infobyte)); + for (UINT row=0; row<ticks; row++) + { + MODCOMMAND *p = &m[row*m_nChannels]; + // Parse track global effects + if (!glbinfobyte) + { + BYTE info = lpStream[d++]; + BYTE infoval = 0; + if ((info & 0x80) && (d < dwPos)) glbinfobyte = lpStream[d++]; + info &= 0x7f; + if ((info) && (d < dwPos)) infoval = lpStream[d++]; + switch(info) + { + case 1: ttype = 0; tempo = infoval; tempochange = TRUE; break; + case 2: ttype = 1; tempo = infoval; tempochange = TRUE; break; + case 3: pbeat = infoval>>4; tempochange = ttype; break; + #ifdef DMFLOG + default: if (info) Log("GLB: %02X.%02X\n", info, infoval); + #endif + } + } else + { + glbinfobyte--; + } + // Parse channels + for (UINT i=0; i<tracks; i++) if (!infobyte[i]) + { + MODCOMMAND cmd = {0,0,0,0,0,0}; + BYTE info = lpStream[d++]; + if (info & 0x80) infobyte[i] = lpStream[d++]; + // Instrument + if (info & 0x40) + { + cmd.instr = lpStream[d++]; + } + // Note + if (info & 0x20) + { + cmd.note = lpStream[d++]; + if ((cmd.note) && (cmd.note < 0xfe)) cmd.note &= 0x7f; + if ((cmd.note) && (cmd.note < 128)) cmd.note += 24; + } + // Volume + if (info & 0x10) + { + cmd.volcmd = VOLCMD_VOLUME; + cmd.vol = (lpStream[d++]+3)>>2; + } + // Effect 1 + if (info & 0x08) + { + BYTE efx = lpStream[d++]; + BYTE eval = lpStream[d++]; + switch(efx) + { + // 1: Key Off + case 1: if (!cmd.note) cmd.note = 0xFE; break; + // 2: Set Loop + // 4: Sample Delay + case 4: if (eval&0xe0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>5)|0xD0; } break; + // 5: Retrig + case 5: if (eval&0xe0) { cmd.command = CMD_RETRIG; cmd.param = (eval>>5); } break; + // 6: Offset + case 6: cmd.command = CMD_OFFSET; cmd.param = eval; break; + #ifdef DMFLOG + default: Log("FX1: %02X.%02X\n", efx, eval); + #endif + } + } + // Effect 2 + if (info & 0x04) + { + BYTE efx = lpStream[d++]; + BYTE eval = lpStream[d++]; + switch(efx) + { + // 1: Finetune + case 1: if (eval&0xf0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>4)|0x20; } break; + // 2: Note Delay + case 2: if (eval&0xe0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>5)|0xD0; } break; + // 3: Arpeggio + case 3: if (eval) { cmd.command = CMD_ARPEGGIO; cmd.param = eval; } break; + // 4: Portamento Up + case 4: cmd.command = CMD_PORTAMENTOUP; cmd.param = (eval >= 0xe0) ? 0xdf : eval; break; + // 5: Portamento Down + case 5: cmd.command = CMD_PORTAMENTODOWN; cmd.param = (eval >= 0xe0) ? 0xdf : eval; break; + // 6: Tone Portamento + case 6: cmd.command = CMD_TONEPORTAMENTO; cmd.param = eval; break; + // 8: Vibrato + case 8: cmd.command = CMD_VIBRATO; cmd.param = eval; break; + // 12: Note cut + case 12: if (eval & 0xe0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>5)|0xc0; } + else if (!cmd.note) { cmd.note = 0xfe; } break; + #ifdef DMFLOG + default: Log("FX2: %02X.%02X\n", efx, eval); + #endif + } + } + // Effect 3 + if (info & 0x02) + { + BYTE efx = lpStream[d++]; + BYTE eval = lpStream[d++]; + switch(efx) + { + // 1: Vol Slide Up + case 1: if (eval == 0xff) break; + eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f; + cmd.command = CMD_VOLUMESLIDE; cmd.param = eval<<4; break; + // 2: Vol Slide Down + case 2: if (eval == 0xff) break; + eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f; + cmd.command = CMD_VOLUMESLIDE; cmd.param = eval; break; + // 7: Set Pan + case 7: if (!cmd.volcmd) { cmd.volcmd = VOLCMD_PANNING; cmd.vol = (eval+3)>>2; } + else { cmd.command = CMD_PANNING8; cmd.param = eval; } break; + // 8: Pan Slide Left + case 8: eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f; + cmd.command = CMD_PANNINGSLIDE; cmd.param = eval<<4; break; + // 9: Pan Slide Right + case 9: eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f; + cmd.command = CMD_PANNINGSLIDE; cmd.param = eval; break; + #ifdef DMFLOG + default: Log("FX3: %02X.%02X\n", efx, eval); + #endif + + } + } + // Store effect + if (i < m_nChannels) p[i] = cmd; + if (d > dwPos) + { + #ifdef DMFLOG + Log("Unexpected EOP: row=%d\n", row); + #endif + break; + } + } else + { + infobyte[i]--; + } + + // Find free channel for tempo change + if (tempochange) + { + tempochange = FALSE; + UINT speed=6, modtempo=tempo; + UINT rpm = ((ttype) && (pbeat)) ? tempo*pbeat : (tempo+1)*15; + for (speed=30; speed>1; speed--) + { + modtempo = rpm*speed/24; + if (modtempo <= 200) break; + if ((speed < 6) && (modtempo < 256)) break; + } + #ifdef DMFLOG + Log("Tempo change: ttype=%d pbeat=%d tempo=%3d -> speed=%d tempo=%d\n", + ttype, pbeat, tempo, speed, modtempo); + #endif + for (UINT ich=0; ich<m_nChannels; ich++) if (!p[ich].command) + { + if (speed) + { + p[ich].command = CMD_SPEED; + p[ich].param = (BYTE)speed; + speed = 0; + } else + if ((modtempo >= 32) && (modtempo < 256)) + { + p[ich].command = CMD_TEMPO; + p[ich].param = (BYTE)modtempo; + modtempo = 0; + } else + { + break; + } + } + } + if (d >= dwPos) break; + } + #ifdef DMFLOG + Log(" %d/%d bytes remaining\n", dwPos-d, pt->jmpsize); + #endif + if (dwPos + 8 >= dwMemLength) break; + } + dwMemPos += patt->patsize + 8; + } + break; + + // "SMPI": Sample Info + case 0x49504d53: + { + DMFSMPI *pds = (DMFSMPI *)(lpStream+dwMemPos); + if (pds->size <= dwMemLength - dwMemPos) + { + DWORD dwPos = dwMemPos + 9; + m_nSamples = pds->samples; + if (m_nSamples >= MAX_SAMPLES) m_nSamples = MAX_SAMPLES-1; + for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++) + { + UINT namelen = lpStream[dwPos]; + smplflags[iSmp] = 0; + if (dwPos+namelen+1+sizeof(DMFSAMPLE) > dwMemPos+pds->size+8) break; + if (namelen) + { + UINT rlen = (namelen < 32) ? namelen : 31; + memcpy(m_szNames[iSmp], lpStream+dwPos+1, rlen); + m_szNames[iSmp][rlen] = 0; + } + dwPos += namelen + 1; + DMFSAMPLE *psh = (DMFSAMPLE *)(lpStream+dwPos); + MODINSTRUMENT *psmp = &Ins[iSmp]; + psmp->nLength = psh->len; + psmp->nLoopStart = psh->loopstart; + psmp->nLoopEnd = psh->loopend; + psmp->nC4Speed = psh->c3speed; + psmp->nGlobalVol = 64; + psmp->nVolume = (psh->volume) ? ((WORD)psh->volume)+1 : (WORD)256; + psmp->uFlags = (psh->flags & 2) ? CHN_16BIT : 0; + if (psmp->uFlags & CHN_16BIT) psmp->nLength >>= 1; + if (psh->flags & 1) psmp->uFlags |= CHN_LOOP; + smplflags[iSmp] = psh->flags; + dwPos += (pfh->version < 8) ? 22 : 30; + #ifdef DMFLOG + Log("SMPI %d/%d: len=%d flags=0x%02X\n", iSmp, m_nSamples, psmp->nLength, psh->flags); + #endif + } + } + dwMemPos += pds->size + 8; + } + break; + + // "SMPD": Sample Data + case 0x44504d53: + { + DWORD dwPos = dwMemPos + 8; + UINT ismpd = 0; + for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++) + { + ismpd++; + DWORD pksize; + if (dwPos + 4 >= dwMemLength) + { + #ifdef DMFLOG + Log("Unexpected EOF at sample %d/%d! (pos=%d)\n", iSmp, m_nSamples, dwPos); + #endif + break; + } + pksize = *((LPDWORD)(lpStream+dwPos)); + #ifdef DMFLOG + Log("sample %d: pos=0x%X pksize=%d ", iSmp, dwPos, pksize); + Log("len=%d flags=0x%X [%08X]\n", Ins[iSmp].nLength, smplflags[ismpd], *((LPDWORD)(lpStream+dwPos+4))); + #endif + dwPos += 4; + if (pksize > dwMemLength - dwPos) + { + #ifdef DMFLOG + Log("WARNING: pksize=%d, but only %d bytes left\n", pksize, dwMemLength-dwPos); + #endif + pksize = dwMemLength - dwPos; + } + if ((pksize) && (iSmp <= m_nSamples)) + { + UINT flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S; + if (smplflags[ismpd] & 4) flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_DMF16 : RS_DMF8; + ReadSample(&Ins[iSmp], flags, (LPSTR)(lpStream+dwPos), pksize); + } + dwPos += pksize; + } + dwMemPos = dwPos; + } + break; + + // "ENDE": end of file + case 0x45444e45: + goto dmfexit; + + // Unrecognized id, or "ENDE" field + default: + dwMemPos += 4; + break; + } + } +dmfexit: + if (!m_nChannels) + { + if (!m_nSamples) + { + m_nType = MOD_TYPE_NONE; + return FALSE; + } + m_nChannels = 4; + } + return TRUE; +} + + +/////////////////////////////////////////////////////////////////////// +// DMF Compression + +#pragma pack(1) + +typedef struct DMF_HNODE +{ + short int left, right; + BYTE value; +} DMF_HNODE; + +typedef struct DMF_HTREE +{ + LPBYTE ibuf, ibufmax; + DWORD bitbuf; + UINT bitnum; + UINT lastnode, nodecount; + DMF_HNODE nodes[256]; +} DMF_HTREE; + +#pragma pack() + + +// DMF Huffman ReadBits +BYTE DMFReadBits(DMF_HTREE *tree, UINT nbits) +//------------------------------------------- +{ + BYTE x = 0, bitv = 1; + while (nbits--) + { + if (tree->bitnum) + { + tree->bitnum--; + } else + { + tree->bitbuf = (tree->ibuf < tree->ibufmax) ? *(tree->ibuf++) : 0; + tree->bitnum = 7; + } + if (tree->bitbuf & 1) x |= bitv; + bitv <<= 1; + tree->bitbuf >>= 1; + } + return x; +} + +// +// tree: [8-bit value][12-bit index][12-bit index] = 32-bit +// + +void DMFNewNode(DMF_HTREE *tree) +//------------------------------ +{ + BYTE isleft, isright; + UINT actnode; + + actnode = tree->nodecount; + if (actnode > 255) return; + tree->nodes[actnode].value = DMFReadBits(tree, 7); + isleft = DMFReadBits(tree, 1); + isright = DMFReadBits(tree, 1); + actnode = tree->lastnode; + if (actnode > 255) return; + tree->nodecount++; + tree->lastnode = tree->nodecount; + if (isleft) + { + tree->nodes[actnode].left = tree->lastnode; + DMFNewNode(tree); + } else + { + tree->nodes[actnode].left = -1; + } + tree->lastnode = tree->nodecount; + if (isright) + { + tree->nodes[actnode].right = tree->lastnode; + DMFNewNode(tree); + } else + { + tree->nodes[actnode].right = -1; + } +} + + +int DMFUnpack(LPBYTE psample, LPBYTE ibuf, LPBYTE ibufmax, UINT maxlen) +//---------------------------------------------------------------------- +{ + DMF_HTREE tree; + UINT actnode; + BYTE value, sign, delta = 0; + + memset(&tree, 0, sizeof(tree)); + tree.ibuf = ibuf; + tree.ibufmax = ibufmax; + DMFNewNode(&tree); + value = 0; + for (UINT i=0; i<maxlen; i++) + { + actnode = 0; + sign = DMFReadBits(&tree, 1); + do + { + if (DMFReadBits(&tree, 1)) + actnode = tree.nodes[actnode].right; + else + actnode = tree.nodes[actnode].left; + if (actnode > 255) break; + delta = tree.nodes[actnode].value; + if ((tree.ibuf >= tree.ibufmax) && (!tree.bitnum)) break; + } while ((tree.nodes[actnode].left >= 0) && (tree.nodes[actnode].right >= 0)); + if (sign) delta ^= 0xFF; + value += delta; + psample[i] = (i) ? value : 0; + } +#ifdef DMFLOG +// Log("DMFUnpack: %d remaining bytes\n", tree.ibufmax-tree.ibuf); +#endif + return tree.ibuf - ibuf; +} + + diff --git a/lib/libmodplug/src/load_dsm.cpp b/lib/libmodplug/src/load_dsm.cpp new file mode 100644 index 0000000000..4f51469f83 --- /dev/null +++ b/lib/libmodplug/src/load_dsm.cpp @@ -0,0 +1,236 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +////////////////////////////////////////////// +// DSIK Internal Format (DSM) module loader // +////////////////////////////////////////////// +#include "stdafx.h" +#include "sndfile.h" + +#pragma pack(1) + +#define DSMID_RIFF 0x46464952 // "RIFF" +#define DSMID_DSMF 0x464d5344 // "DSMF" +#define DSMID_SONG 0x474e4f53 // "SONG" +#define DSMID_INST 0x54534e49 // "INST" +#define DSMID_PATT 0x54544150 // "PATT" + + +typedef struct DSMNOTE +{ + BYTE note,ins,vol,cmd,inf; +} DSMNOTE; + + +typedef struct DSMINST +{ + DWORD id_INST; + DWORD inst_len; + CHAR filename[13]; + BYTE flags; + BYTE flags2; + BYTE volume; + DWORD length; + DWORD loopstart; + DWORD loopend; + DWORD reserved1; + WORD c2spd; + WORD reserved2; + CHAR samplename[28]; +} DSMINST; + + +typedef struct DSMFILEHEADER +{ + DWORD id_RIFF; // "RIFF" + DWORD riff_len; + DWORD id_DSMF; // "DSMF" + DWORD id_SONG; // "SONG" + DWORD song_len; +} DSMFILEHEADER; + + +typedef struct DSMSONG +{ + CHAR songname[28]; + WORD reserved1; + WORD flags; + DWORD reserved2; + WORD numord; + WORD numsmp; + WORD numpat; + WORD numtrk; + BYTE globalvol; + BYTE mastervol; + BYTE speed; + BYTE bpm; + BYTE panpos[16]; + BYTE orders[128]; +} DSMSONG; + +typedef struct DSMPATT +{ + DWORD id_PATT; + DWORD patt_len; + BYTE dummy1; + BYTE dummy2; +} DSMPATT; + +#pragma pack() + + +BOOL CSoundFile::ReadDSM(LPCBYTE lpStream, DWORD dwMemLength) +//----------------------------------------------------------- +{ + DSMFILEHEADER *pfh = (DSMFILEHEADER *)lpStream; + DSMSONG *psong; + DWORD dwMemPos; + UINT nPat, nSmp; + + if ((!lpStream) || (dwMemLength < 1024) || (pfh->id_RIFF != DSMID_RIFF) + || (pfh->riff_len + 8 > dwMemLength) || (pfh->riff_len < 1024) + || (pfh->id_DSMF != DSMID_DSMF) || (pfh->id_SONG != DSMID_SONG) + || (pfh->song_len > dwMemLength)) return FALSE; + psong = (DSMSONG *)(lpStream + sizeof(DSMFILEHEADER)); + dwMemPos = sizeof(DSMFILEHEADER) + pfh->song_len; + m_nType = MOD_TYPE_DSM; + m_nChannels = psong->numtrk; + if (m_nChannels < 4) m_nChannels = 4; + if (m_nChannels > 16) m_nChannels = 16; + m_nSamples = psong->numsmp; + if (m_nSamples > MAX_SAMPLES) m_nSamples = MAX_SAMPLES; + m_nDefaultSpeed = psong->speed; + m_nDefaultTempo = psong->bpm; + m_nDefaultGlobalVolume = psong->globalvol << 2; + if ((!m_nDefaultGlobalVolume) || (m_nDefaultGlobalVolume > 256)) m_nDefaultGlobalVolume = 256; + m_nSongPreAmp = psong->mastervol & 0x7F; + for (UINT iOrd=0; iOrd<MAX_ORDERS; iOrd++) + { + Order[iOrd] = (BYTE)((iOrd < psong->numord) ? psong->orders[iOrd] : 0xFF); + } + for (UINT iPan=0; iPan<16; iPan++) + { + ChnSettings[iPan].nPan = 0x80; + if (psong->panpos[iPan] <= 0x80) + { + ChnSettings[iPan].nPan = psong->panpos[iPan] << 1; + } + } + memcpy(m_szNames[0], psong->songname, 28); + nPat = 0; + nSmp = 1; + while (dwMemPos < dwMemLength - 8) + { + DSMPATT *ppatt = (DSMPATT *)(lpStream + dwMemPos); + DSMINST *pins = (DSMINST *)(lpStream+dwMemPos); + // Reading Patterns + if (ppatt->id_PATT == DSMID_PATT) + { + dwMemPos += 8; + if (dwMemPos + ppatt->patt_len >= dwMemLength) break; + DWORD dwPos = dwMemPos; + dwMemPos += ppatt->patt_len; + MODCOMMAND *m = AllocatePattern(64, m_nChannels); + if (!m) break; + PatternSize[nPat] = 64; + Patterns[nPat] = m; + UINT row = 0; + while ((row < 64) && (dwPos + 2 <= dwMemPos)) + { + UINT flag = lpStream[dwPos++]; + if (flag) + { + UINT ch = (flag & 0x0F) % m_nChannels; + if (flag & 0x80) + { + UINT note = lpStream[dwPos++]; + if (note) + { + if (note <= 12*9) note += 12; + m[ch].note = (BYTE)note; + } + } + if (flag & 0x40) + { + m[ch].instr = lpStream[dwPos++]; + } + if (flag & 0x20) + { + m[ch].volcmd = VOLCMD_VOLUME; + m[ch].vol = lpStream[dwPos++]; + } + if (flag & 0x10) + { + UINT command = lpStream[dwPos++]; + UINT param = lpStream[dwPos++]; + switch(command) + { + // 4-bit Panning + case 0x08: + switch(param & 0xF0) + { + case 0x00: param <<= 4; break; + case 0x10: command = 0x0A; param = (param & 0x0F) << 4; break; + case 0x20: command = 0x0E; param = (param & 0x0F) | 0xA0; break; + case 0x30: command = 0x0E; param = (param & 0x0F) | 0x10; break; + case 0x40: command = 0x0E; param = (param & 0x0F) | 0x20; break; + default: command = 0; + } + break; + // Portamentos + case 0x11: + case 0x12: + command &= 0x0F; + break; + // 3D Sound (?) + case 0x13: + command = 'X' - 55; + param = 0x91; + break; + default: + // Volume + Offset (?) + command = ((command & 0xF0) == 0x20) ? 0x09 : 0; + } + m[ch].command = (BYTE)command; + m[ch].param = (BYTE)param; + if (command) ConvertModCommand(&m[ch]); + } + } else + { + m += m_nChannels; + row++; + } + } + nPat++; + } else + // Reading Samples + if ((nSmp <= m_nSamples) && (pins->id_INST == DSMID_INST)) + { + if (dwMemPos + pins->inst_len >= dwMemLength - 8) break; + DWORD dwPos = dwMemPos + sizeof(DSMINST); + dwMemPos += 8 + pins->inst_len; + memcpy(m_szNames[nSmp], pins->samplename, 28); + MODINSTRUMENT *psmp = &Ins[nSmp]; + memcpy(psmp->name, pins->filename, 13); + psmp->nGlobalVol = 64; + psmp->nC4Speed = pins->c2spd; + psmp->uFlags = (WORD)((pins->flags & 1) ? CHN_LOOP : 0); + psmp->nLength = pins->length; + psmp->nLoopStart = pins->loopstart; + psmp->nLoopEnd = pins->loopend; + psmp->nVolume = (WORD)(pins->volume << 2); + if (psmp->nVolume > 256) psmp->nVolume = 256; + UINT smptype = (pins->flags & 2) ? RS_PCM8S : RS_PCM8U; + ReadSample(psmp, smptype, (LPCSTR)(lpStream+dwPos), dwMemLength - dwPos); + nSmp++; + } else + { + break; + } + } + return TRUE; +} + diff --git a/lib/libmodplug/src/load_far.cpp b/lib/libmodplug/src/load_far.cpp new file mode 100644 index 0000000000..4bc2d9590a --- /dev/null +++ b/lib/libmodplug/src/load_far.cpp @@ -0,0 +1,270 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +//////////////////////////////////////// +// Farandole (FAR) module loader // +//////////////////////////////////////// +#include "stdafx.h" +#include "sndfile.h" + +//#pragma warning(disable:4244) + +#define FARFILEMAGIC 0xFE524146 // "FAR" + +#pragma pack(1) + +typedef struct FARHEADER1 +{ + DWORD id; // file magic FAR= + CHAR songname[40]; // songname + CHAR magic2[3]; // 13,10,26 + WORD headerlen; // remaining length of header in bytes + BYTE version; // 0xD1 + BYTE onoff[16]; + BYTE edit1[9]; + BYTE speed; + BYTE panning[16]; + BYTE edit2[4]; + WORD stlen; +} FARHEADER1; + +typedef struct FARHEADER2 +{ + BYTE orders[256]; + BYTE numpat; + BYTE snglen; + BYTE loopto; + WORD patsiz[256]; +} FARHEADER2; + +typedef struct FARSAMPLE +{ + CHAR samplename[32]; + DWORD length; + BYTE finetune; + BYTE volume; + DWORD reppos; + DWORD repend; + BYTE type; + BYTE loop; +} FARSAMPLE; + +#pragma pack() + + +BOOL CSoundFile::ReadFAR(const BYTE *lpStream, DWORD dwMemLength) +//--------------------------------------------------------------- +{ + FARHEADER1 *pmh1 = (FARHEADER1 *)lpStream; + FARHEADER2 *pmh2; + DWORD dwMemPos = sizeof(FARHEADER1); + UINT headerlen; + BYTE samplemap[8]; + + if ((!lpStream) || (dwMemLength < 1024) || (bswapLE32(pmh1->id) != FARFILEMAGIC) + || (pmh1->magic2[0] != 13) || (pmh1->magic2[1] != 10) || (pmh1->magic2[2] != 26)) return FALSE; + headerlen = bswapLE16(pmh1->headerlen); + pmh1->stlen = bswapLE16( pmh1->stlen ); /* inplace byteswap -- Toad */ + if ((headerlen >= dwMemLength) || (dwMemPos + pmh1->stlen + sizeof(FARHEADER2) >= dwMemLength)) return FALSE; + // Globals + m_nType = MOD_TYPE_FAR; + m_nChannels = 16; + m_nInstruments = 0; + m_nSamples = 0; + m_nSongPreAmp = 0x20; + m_nDefaultSpeed = pmh1->speed; + m_nDefaultTempo = 80; + m_nDefaultGlobalVolume = 256; + + memcpy(m_szNames[0], pmh1->songname, 32); + // Channel Setting + for (UINT nchpan=0; nchpan<16; nchpan++) + { + ChnSettings[nchpan].dwFlags = 0; + ChnSettings[nchpan].nPan = ((pmh1->panning[nchpan] & 0x0F) << 4) + 8; + ChnSettings[nchpan].nVolume = 64; + } + // Reading comment + if (pmh1->stlen) + { + UINT szLen = pmh1->stlen; + if (szLen > dwMemLength - dwMemPos) szLen = dwMemLength - dwMemPos; + if ((m_lpszSongComments = new char[szLen + 1]) != NULL) + { + memcpy(m_lpszSongComments, lpStream+dwMemPos, szLen); + m_lpszSongComments[szLen] = 0; + } + dwMemPos += pmh1->stlen; + } + // Reading orders + pmh2 = (FARHEADER2 *)(lpStream + dwMemPos); + dwMemPos += sizeof(FARHEADER2); + if (dwMemPos >= dwMemLength) return TRUE; + for (UINT iorder=0; iorder<MAX_ORDERS; iorder++) + { + Order[iorder] = (iorder <= pmh2->snglen) ? pmh2->orders[iorder] : 0xFF; + } + m_nRestartPos = pmh2->loopto; + // Reading Patterns + dwMemPos += headerlen - (869 + pmh1->stlen); + if (dwMemPos >= dwMemLength) return TRUE; + + // byteswap pattern data -- Toad + UINT psfix = 0 ; + while( psfix++ < 256 ) + { + pmh2->patsiz[psfix] = bswapLE16( pmh2->patsiz[psfix] ) ; + } + // end byteswap of pattern data + + WORD *patsiz = (WORD *)pmh2->patsiz; + for (UINT ipat=0; ipat<256; ipat++) if (patsiz[ipat]) + { + UINT patlen = patsiz[ipat]; + if ((ipat >= MAX_PATTERNS) || (patsiz[ipat] < 2)) + { + dwMemPos += patlen; + continue; + } + if (dwMemPos + patlen >= dwMemLength) return TRUE; + UINT rows = (patlen - 2) >> 6; + if (!rows) + { + dwMemPos += patlen; + continue; + } + if (rows > 256) rows = 256; + if (rows < 16) rows = 16; + PatternSize[ipat] = rows; + if ((Patterns[ipat] = AllocatePattern(rows, m_nChannels)) == NULL) return TRUE; + MODCOMMAND *m = Patterns[ipat]; + UINT patbrk = lpStream[dwMemPos]; + const BYTE *p = lpStream + dwMemPos + 2; + UINT max = rows*16*4; + if (max > patlen-2) max = patlen-2; + for (UINT len=0; len<max; len += 4, m++) + { + BYTE note = p[len]; + BYTE ins = p[len+1]; + BYTE vol = p[len+2]; + BYTE eff = p[len+3]; + if (note) + { + m->instr = ins + 1; + m->note = note + 36; + } + if (vol & 0x0F) + { + m->volcmd = VOLCMD_VOLUME; + m->vol = (vol & 0x0F) << 2; + if (m->vol <= 4) m->vol = 0; + } + switch(eff & 0xF0) + { + // 1.x: Portamento Up + case 0x10: + m->command = CMD_PORTAMENTOUP; + m->param = eff & 0x0F; + break; + // 2.x: Portamento Down + case 0x20: + m->command = CMD_PORTAMENTODOWN; + m->param = eff & 0x0F; + break; + // 3.x: Tone-Portamento + case 0x30: + m->command = CMD_TONEPORTAMENTO; + m->param = (eff & 0x0F) << 2; + break; + // 4.x: Retrigger + case 0x40: + m->command = CMD_RETRIG; + m->param = 6 / (1+(eff&0x0F)) + 1; + break; + // 5.x: Set Vibrato Depth + case 0x50: + m->command = CMD_VIBRATO; + m->param = (eff & 0x0F); + break; + // 6.x: Set Vibrato Speed + case 0x60: + m->command = CMD_VIBRATO; + m->param = (eff & 0x0F) << 4; + break; + // 7.x: Vol Slide Up + case 0x70: + m->command = CMD_VOLUMESLIDE; + m->param = (eff & 0x0F) << 4; + break; + // 8.x: Vol Slide Down + case 0x80: + m->command = CMD_VOLUMESLIDE; + m->param = (eff & 0x0F); + break; + // A.x: Port to vol + case 0xA0: + m->volcmd = VOLCMD_VOLUME; + m->vol = ((eff & 0x0F) << 2) + 4; + break; + // B.x: Set Balance + case 0xB0: + m->command = CMD_PANNING8; + m->param = (eff & 0x0F) << 4; + break; + // F.x: Set Speed + case 0xF0: + m->command = CMD_SPEED; + m->param = eff & 0x0F; + break; + default: + if ((patbrk) && (patbrk+1 == (len >> 6)) && (patbrk+1 != rows-1)) + { + m->command = CMD_PATTERNBREAK; + patbrk = 0; + } + } + } + dwMemPos += patlen; + } + // Reading samples + if (dwMemPos + 8 >= dwMemLength) return TRUE; + memcpy(samplemap, lpStream+dwMemPos, 8); + dwMemPos += 8; + MODINSTRUMENT *pins = &Ins[1]; + for (UINT ismp=0; ismp<64; ismp++, pins++) if (samplemap[ismp >> 3] & (1 << (ismp & 7))) + { + if (dwMemPos + sizeof(FARSAMPLE) > dwMemLength) return TRUE; + FARSAMPLE *pfs = (FARSAMPLE *)(lpStream + dwMemPos); + dwMemPos += sizeof(FARSAMPLE); + m_nSamples = ismp + 1; + memcpy(m_szNames[ismp+1], pfs->samplename, 32); + pfs->length = bswapLE32( pfs->length ) ; /* endian fix - Toad */ + pins->nLength = pfs->length ; + pins->nLoopStart = bswapLE32(pfs->reppos) ; + pins->nLoopEnd = bswapLE32(pfs->repend) ; + pins->nFineTune = 0; + pins->nC4Speed = 8363*2; + pins->nGlobalVol = 64; + pins->nVolume = pfs->volume << 4; + pins->uFlags = 0; + if ((pins->nLength > 3) && (dwMemPos + 4 < dwMemLength)) + { + if (pfs->type & 1) + { + pins->uFlags |= CHN_16BIT; + pins->nLength >>= 1; + pins->nLoopStart >>= 1; + pins->nLoopEnd >>= 1; + } + if ((pfs->loop & 8) && (pins->nLoopEnd > 4)) pins->uFlags |= CHN_LOOP; + ReadSample(pins, (pins->uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S, + (LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos); + } + dwMemPos += pfs->length; + } + return TRUE; +} + diff --git a/lib/libmodplug/src/load_it.cpp b/lib/libmodplug/src/load_it.cpp new file mode 100644 index 0000000000..daa80a2a76 --- /dev/null +++ b/lib/libmodplug/src/load_it.cpp @@ -0,0 +1,1515 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net>, + * Adam Goode <adam@evdebs.org> (Endian and char fixes for PPC) + * Marco Trillo <toad@arsystel.com> (Endian fixes for SaveIT, XM->IT Sample Converter) + * +*/ + +#include "stdafx.h" +#include "sndfile.h" +#include "it_defs.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif + +BYTE autovibit2xm[8] = +{ 0, 3, 1, 4, 2, 0, 0, 0 }; + +BYTE autovibxm2it[8] = +{ 0, 2, 4, 1, 3, 0, 0, 0 }; + +////////////////////////////////////////////////////////// +// Impulse Tracker IT file support + +// for conversion of XM samples +extern WORD XMPeriodTable[96+8]; +extern UINT XMLinearTable[768]; + +static inline UINT ConvertVolParam(UINT value) +//-------------------------------------------- +{ + return (value > 9) ? 9 : value; +} + + +BOOL CSoundFile::ITInstrToMPT(const void *p, INSTRUMENTHEADER *penv, UINT trkvers) +//-------------------------------------------------------------------------------- +{ + if (trkvers < 0x0200) + { + const ITOLDINSTRUMENT *pis = (const ITOLDINSTRUMENT *)p; + memcpy(penv->name, pis->name, 26); + memcpy(penv->filename, pis->filename, 12); + penv->nFadeOut = bswapLE16(pis->fadeout) << 6; + penv->nGlobalVol = 64; + for (UINT j=0; j<NOTE_MAX; j++) + { + UINT note = pis->keyboard[j*2]; + UINT ins = pis->keyboard[j*2+1]; + if (ins < MAX_SAMPLES) penv->Keyboard[j] = ins; + if (note < 128) penv->NoteMap[j] = note+1; + else if (note >= 0xFE) penv->NoteMap[j] = note; + } + if (pis->flags & 0x01) penv->dwFlags |= ENV_VOLUME; + if (pis->flags & 0x02) penv->dwFlags |= ENV_VOLLOOP; + if (pis->flags & 0x04) penv->dwFlags |= ENV_VOLSUSTAIN; + penv->nVolLoopStart = pis->vls; + penv->nVolLoopEnd = pis->vle; + penv->nVolSustainBegin = pis->sls; + penv->nVolSustainEnd = pis->sle; + penv->nVolEnv = 25; + for (UINT ev=0; ev<25; ev++) + { + if ((penv->VolPoints[ev] = pis->nodes[ev*2]) == 0xFF) + { + penv->nVolEnv = ev; + break; + } + penv->VolEnv[ev] = pis->nodes[ev*2+1]; + } + penv->nNNA = pis->nna; + penv->nDCT = pis->dnc; + penv->nPan = 0x80; + } else + { + const ITINSTRUMENT *pis = (const ITINSTRUMENT *)p; + memcpy(penv->name, pis->name, 26); + memcpy(penv->filename, pis->filename, 12); + penv->nMidiProgram = pis->mpr; + penv->nMidiChannel = pis->mch; + penv->wMidiBank = bswapLE16(pis->mbank); + penv->nFadeOut = bswapLE16(pis->fadeout) << 5; + penv->nGlobalVol = pis->gbv >> 1; + if (penv->nGlobalVol > 64) penv->nGlobalVol = 64; + for (UINT j=0; j<NOTE_MAX; j++) + { + UINT note = pis->keyboard[j*2]; + UINT ins = pis->keyboard[j*2+1]; + if (ins < MAX_SAMPLES) penv->Keyboard[j] = ins; + if (note < 128) penv->NoteMap[j] = note+1; + else if (note >= 0xFE) penv->NoteMap[j] = note; + } + // Volume Envelope + if (pis->volenv.flags & 1) penv->dwFlags |= ENV_VOLUME; + if (pis->volenv.flags & 2) penv->dwFlags |= ENV_VOLLOOP; + if (pis->volenv.flags & 4) penv->dwFlags |= ENV_VOLSUSTAIN; + if (pis->volenv.flags & 8) penv->dwFlags |= ENV_VOLCARRY; + penv->nVolEnv = pis->volenv.num; + if (penv->nVolEnv > 25) penv->nVolEnv = 25; + + penv->nVolLoopStart = pis->volenv.lpb; + penv->nVolLoopEnd = pis->volenv.lpe; + penv->nVolSustainBegin = pis->volenv.slb; + penv->nVolSustainEnd = pis->volenv.sle; + // Panning Envelope + if (pis->panenv.flags & 1) penv->dwFlags |= ENV_PANNING; + if (pis->panenv.flags & 2) penv->dwFlags |= ENV_PANLOOP; + if (pis->panenv.flags & 4) penv->dwFlags |= ENV_PANSUSTAIN; + if (pis->panenv.flags & 8) penv->dwFlags |= ENV_PANCARRY; + penv->nPanEnv = pis->panenv.num; + if (penv->nPanEnv > 25) penv->nPanEnv = 25; + penv->nPanLoopStart = pis->panenv.lpb; + penv->nPanLoopEnd = pis->panenv.lpe; + penv->nPanSustainBegin = pis->panenv.slb; + penv->nPanSustainEnd = pis->panenv.sle; + // Pitch Envelope + if (pis->pitchenv.flags & 1) penv->dwFlags |= ENV_PITCH; + if (pis->pitchenv.flags & 2) penv->dwFlags |= ENV_PITCHLOOP; + if (pis->pitchenv.flags & 4) penv->dwFlags |= ENV_PITCHSUSTAIN; + if (pis->pitchenv.flags & 8) penv->dwFlags |= ENV_PITCHCARRY; + if (pis->pitchenv.flags & 0x80) penv->dwFlags |= ENV_FILTER; + penv->nPitchEnv = pis->pitchenv.num; + if (penv->nPitchEnv > 25) penv->nPitchEnv = 25; + penv->nPitchLoopStart = pis->pitchenv.lpb; + penv->nPitchLoopEnd = pis->pitchenv.lpe; + penv->nPitchSustainBegin = pis->pitchenv.slb; + penv->nPitchSustainEnd = pis->pitchenv.sle; + // Envelopes Data + for (UINT ev=0; ev<25; ev++) + { + penv->VolEnv[ev] = pis->volenv.data[ev*3]; + penv->VolPoints[ev] = (pis->volenv.data[ev*3+2] << 8) | (pis->volenv.data[ev*3+1]); + penv->PanEnv[ev] = pis->panenv.data[ev*3] + 32; + penv->PanPoints[ev] = (pis->panenv.data[ev*3+2] << 8) | (pis->panenv.data[ev*3+1]); + penv->PitchEnv[ev] = pis->pitchenv.data[ev*3] + 32; + penv->PitchPoints[ev] = (pis->pitchenv.data[ev*3+2] << 8) | (pis->pitchenv.data[ev*3+1]); + } + penv->nNNA = pis->nna; + penv->nDCT = pis->dct; + penv->nDNA = pis->dca; + penv->nPPS = pis->pps; + penv->nPPC = pis->ppc; + penv->nIFC = pis->ifc; + penv->nIFR = pis->ifr; + penv->nVolSwing = pis->rv; + penv->nPanSwing = pis->rp; + penv->nPan = (pis->dfp & 0x7F) << 2; + if (penv->nPan > 256) penv->nPan = 128; + if (pis->dfp < 0x80) penv->dwFlags |= ENV_SETPANNING; + } + if ((penv->nVolLoopStart >= 25) || (penv->nVolLoopEnd >= 25)) penv->dwFlags &= ~ENV_VOLLOOP; + if ((penv->nVolSustainBegin >= 25) || (penv->nVolSustainEnd >= 25)) penv->dwFlags &= ~ENV_VOLSUSTAIN; + return TRUE; +} + + +BOOL CSoundFile::ReadIT(const BYTE *lpStream, DWORD dwMemLength) +//-------------------------------------------------------------- +{ + ITFILEHEADER pifh = *(ITFILEHEADER *)lpStream; + DWORD dwMemPos = sizeof(ITFILEHEADER); + DWORD inspos[MAX_INSTRUMENTS]; + DWORD smppos[MAX_SAMPLES]; + DWORD patpos[MAX_PATTERNS]; + BYTE chnmask[64], channels_used[64]; + MODCOMMAND lastvalue[64]; + + pifh.id = bswapLE32(pifh.id); + pifh.reserved1 = bswapLE16(pifh.reserved1); + pifh.ordnum = bswapLE16(pifh.ordnum); + pifh.insnum = bswapLE16(pifh.insnum); + pifh.smpnum = bswapLE16(pifh.smpnum); + pifh.patnum = bswapLE16(pifh.patnum); + pifh.cwtv = bswapLE16(pifh.cwtv); + pifh.cmwt = bswapLE16(pifh.cmwt); + pifh.flags = bswapLE16(pifh.flags); + pifh.special = bswapLE16(pifh.special); + pifh.msglength = bswapLE16(pifh.msglength); + pifh.msgoffset = bswapLE32(pifh.msgoffset); + pifh.reserved2 = bswapLE32(pifh.reserved2); + + if ((!lpStream) || (dwMemLength < 0x100)) return FALSE; + if ((pifh.id != 0x4D504D49) || (pifh.insnum >= MAX_INSTRUMENTS) + || (!pifh.smpnum) || (pifh.smpnum >= MAX_INSTRUMENTS) || (!pifh.ordnum)) return FALSE; + if (dwMemPos + pifh.ordnum + pifh.insnum*4 + + pifh.smpnum*4 + pifh.patnum*4 > dwMemLength) return FALSE; + m_nType = MOD_TYPE_IT; + if (pifh.flags & 0x08) m_dwSongFlags |= SONG_LINEARSLIDES; + if (pifh.flags & 0x10) m_dwSongFlags |= SONG_ITOLDEFFECTS; + if (pifh.flags & 0x20) m_dwSongFlags |= SONG_ITCOMPATMODE; + if (pifh.flags & 0x80) m_dwSongFlags |= SONG_EMBEDMIDICFG; + if (pifh.flags & 0x1000) m_dwSongFlags |= SONG_EXFILTERRANGE; + memcpy(m_szNames[0], pifh.songname, 26); + m_szNames[0][26] = 0; + // Global Volume + if (pifh.globalvol) + { + m_nDefaultGlobalVolume = pifh.globalvol << 1; + if (!m_nDefaultGlobalVolume) m_nDefaultGlobalVolume = 256; + if (m_nDefaultGlobalVolume > 256) m_nDefaultGlobalVolume = 256; + } + if (pifh.speed) m_nDefaultSpeed = pifh.speed; + if (pifh.tempo) m_nDefaultTempo = pifh.tempo; + m_nSongPreAmp = pifh.mv & 0x7F; + // Reading Channels Pan Positions + for (int ipan=0; ipan<64; ipan++) if (pifh.chnpan[ipan] != 0xFF) + { + ChnSettings[ipan].nVolume = pifh.chnvol[ipan]; + ChnSettings[ipan].nPan = 128; + if (pifh.chnpan[ipan] & 0x80) ChnSettings[ipan].dwFlags |= CHN_MUTE; + UINT n = pifh.chnpan[ipan] & 0x7F; + if (n <= 64) ChnSettings[ipan].nPan = n << 2; + if (n == 100) ChnSettings[ipan].dwFlags |= CHN_SURROUND; + } + if (m_nChannels < 4) m_nChannels = 4; + // Reading Song Message + if ((pifh.special & 0x01) && (pifh.msglength) && (pifh.msgoffset + pifh.msglength < dwMemLength)) + { + m_lpszSongComments = new char[pifh.msglength+1]; + if (m_lpszSongComments) + { + memcpy(m_lpszSongComments, lpStream+pifh.msgoffset, pifh.msglength); + m_lpszSongComments[pifh.msglength] = 0; + } + } + // Reading orders + UINT nordsize = pifh.ordnum; + if (nordsize > MAX_ORDERS) nordsize = MAX_ORDERS; + memcpy(Order, lpStream+dwMemPos, nordsize); + dwMemPos += pifh.ordnum; + // Reading Instrument Offsets + memset(inspos, 0, sizeof(inspos)); + UINT inspossize = pifh.insnum; + if (inspossize > MAX_INSTRUMENTS) inspossize = MAX_INSTRUMENTS; + inspossize <<= 2; + memcpy(inspos, lpStream+dwMemPos, inspossize); + for (UINT j=0; j < (inspossize>>2); j++) + { + inspos[j] = bswapLE32(inspos[j]); + } + dwMemPos += pifh.insnum * 4; + // Reading Samples Offsets + memset(smppos, 0, sizeof(smppos)); + UINT smppossize = pifh.smpnum; + if (smppossize > MAX_SAMPLES) smppossize = MAX_SAMPLES; + smppossize <<= 2; + memcpy(smppos, lpStream+dwMemPos, smppossize); + for (UINT j=0; j < (smppossize>>2); j++) + { + smppos[j] = bswapLE32(smppos[j]); + } + dwMemPos += pifh.smpnum * 4; + // Reading Patterns Offsets + memset(patpos, 0, sizeof(patpos)); + UINT patpossize = pifh.patnum; + if (patpossize > MAX_PATTERNS) patpossize = MAX_PATTERNS; + patpossize <<= 2; + memcpy(patpos, lpStream+dwMemPos, patpossize); + for (UINT j=0; j < (patpossize>>2); j++) + { + patpos[j] = bswapLE32(patpos[j]); + } + dwMemPos += pifh.patnum * 4; + // Reading IT Extra Info + if (dwMemPos + 2 < dwMemLength) + { + UINT nflt = bswapLE16(*((WORD *)(lpStream + dwMemPos))); + dwMemPos += 2; + if (dwMemPos + nflt * 8 < dwMemLength) dwMemPos += nflt * 8; + } + // Reading Midi Output & Macros + if (m_dwSongFlags & SONG_EMBEDMIDICFG) + { + if (dwMemPos + sizeof(MODMIDICFG) < dwMemLength) + { + memcpy(&m_MidiCfg, lpStream+dwMemPos, sizeof(MODMIDICFG)); + dwMemPos += sizeof(MODMIDICFG); + } + } + // Read pattern names: "PNAM" + if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x4d414e50)) + { + UINT len = bswapLE32(*((DWORD *)(lpStream+dwMemPos+4))); + dwMemPos += 8; + if ((dwMemPos + len <= dwMemLength) && (len <= MAX_PATTERNS*MAX_PATTERNNAME) && (len >= MAX_PATTERNNAME)) + { + m_lpszPatternNames = new char[len]; + if (m_lpszPatternNames) + { + m_nPatternNames = len / MAX_PATTERNNAME; + memcpy(m_lpszPatternNames, lpStream+dwMemPos, len); + } + dwMemPos += len; + } + } + // 4-channels minimum + m_nChannels = 4; + // Read channel names: "CNAM" + if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x4d414e43)) + { + UINT len = bswapLE32(*((DWORD *)(lpStream+dwMemPos+4))); + dwMemPos += 8; + if ((dwMemPos + len <= dwMemLength) && (len <= 64*MAX_CHANNELNAME)) + { + UINT n = len / MAX_CHANNELNAME; + if (n > m_nChannels) m_nChannels = n; + for (UINT i=0; i<n; i++) + { + memcpy(ChnSettings[i].szName, (lpStream+dwMemPos+i*MAX_CHANNELNAME), MAX_CHANNELNAME); + ChnSettings[i].szName[MAX_CHANNELNAME-1] = 0; + } + dwMemPos += len; + } + } + // Read mix plugins information + if (dwMemPos + 8 < dwMemLength) + { + dwMemPos += LoadMixPlugins(lpStream+dwMemPos, dwMemLength-dwMemPos); + } + // Checking for unused channels + UINT npatterns = pifh.patnum; + if (npatterns > MAX_PATTERNS) npatterns = MAX_PATTERNS; + for (UINT patchk=0; patchk<npatterns; patchk++) + { + memset(chnmask, 0, sizeof(chnmask)); + if ((!patpos[patchk]) || ((DWORD)patpos[patchk] + 4 >= dwMemLength)) continue; + UINT len = bswapLE16(*((WORD *)(lpStream+patpos[patchk]))); + UINT rows = bswapLE16(*((WORD *)(lpStream+patpos[patchk]+2))); + if ((rows < 4) || (rows > 256)) continue; + if (patpos[patchk]+8+len > dwMemLength) continue; + UINT i = 0; + const BYTE *p = lpStream+patpos[patchk]+8; + UINT nrow = 0; + while (nrow<rows) + { + if (i >= len) break; + BYTE b = p[i++]; + if (!b) + { + nrow++; + continue; + } + UINT ch = b & 0x7F; + if (ch) ch = (ch - 1) & 0x3F; + if (b & 0x80) + { + if (i >= len) break; + chnmask[ch] = p[i++]; + } + // Channel used + if (chnmask[ch] & 0x0F) + { + if ((ch >= m_nChannels) && (ch < 64)) m_nChannels = ch+1; + } + // Note + if (chnmask[ch] & 1) i++; + // Instrument + if (chnmask[ch] & 2) i++; + // Volume + if (chnmask[ch] & 4) i++; + // Effect + if (chnmask[ch] & 8) i += 2; + if (i >= len) break; + } + } + // Reading Instruments + m_nInstruments = 0; + if (pifh.flags & 0x04) m_nInstruments = pifh.insnum; + if (m_nInstruments >= MAX_INSTRUMENTS) m_nInstruments = MAX_INSTRUMENTS-1; + for (UINT nins=0; nins<m_nInstruments; nins++) + { + if ((inspos[nins] > 0) && (inspos[nins] < dwMemLength - sizeof(ITOLDINSTRUMENT))) + { + INSTRUMENTHEADER *penv = new INSTRUMENTHEADER; + if (!penv) continue; + Headers[nins+1] = penv; + memset(penv, 0, sizeof(INSTRUMENTHEADER)); + ITInstrToMPT(lpStream + inspos[nins], penv, pifh.cmwt); + } + } + // Reading Samples + m_nSamples = pifh.smpnum; + if (m_nSamples >= MAX_SAMPLES) m_nSamples = MAX_SAMPLES-1; + for (UINT nsmp=0; nsmp<pifh.smpnum; nsmp++) if ((smppos[nsmp]) && (smppos[nsmp] + sizeof(ITSAMPLESTRUCT) <= dwMemLength)) + { + ITSAMPLESTRUCT pis = *(ITSAMPLESTRUCT *)(lpStream+smppos[nsmp]); + pis.id = bswapLE32(pis.id); + pis.length = bswapLE32(pis.length); + pis.loopbegin = bswapLE32(pis.loopbegin); + pis.loopend = bswapLE32(pis.loopend); + pis.C5Speed = bswapLE32(pis.C5Speed); + pis.susloopbegin = bswapLE32(pis.susloopbegin); + pis.susloopend = bswapLE32(pis.susloopend); + pis.samplepointer = bswapLE32(pis.samplepointer); + + if (pis.id == 0x53504D49) + { + MODINSTRUMENT *pins = &Ins[nsmp+1]; + memcpy(pins->name, pis.filename, 12); + pins->uFlags = 0; + pins->nLength = 0; + pins->nLoopStart = pis.loopbegin; + pins->nLoopEnd = pis.loopend; + pins->nSustainStart = pis.susloopbegin; + pins->nSustainEnd = pis.susloopend; + pins->nC4Speed = pis.C5Speed; + if (!pins->nC4Speed) pins->nC4Speed = 8363; + if (pis.C5Speed < 256) pins->nC4Speed = 256; + pins->nVolume = pis.vol << 2; + if (pins->nVolume > 256) pins->nVolume = 256; + pins->nGlobalVol = pis.gvl; + if (pins->nGlobalVol > 64) pins->nGlobalVol = 64; + if (pis.flags & 0x10) pins->uFlags |= CHN_LOOP; + if (pis.flags & 0x20) pins->uFlags |= CHN_SUSTAINLOOP; + if (pis.flags & 0x40) pins->uFlags |= CHN_PINGPONGLOOP; + if (pis.flags & 0x80) pins->uFlags |= CHN_PINGPONGSUSTAIN; + pins->nPan = (pis.dfp & 0x7F) << 2; + if (pins->nPan > 256) pins->nPan = 256; + if (pis.dfp & 0x80) pins->uFlags |= CHN_PANNING; + pins->nVibType = autovibit2xm[pis.vit & 7]; + pins->nVibRate = pis.vis; + pins->nVibDepth = pis.vid & 0x7F; + pins->nVibSweep = (pis.vir + 3) / 4; + if ((pis.samplepointer) && (pis.samplepointer < dwMemLength) && (pis.length)) + { + pins->nLength = pis.length; + if (pins->nLength > MAX_SAMPLE_LENGTH) pins->nLength = MAX_SAMPLE_LENGTH; + UINT flags = (pis.cvt & 1) ? RS_PCM8S : RS_PCM8U; + if (pis.flags & 2) + { + flags += 5; + if (pis.flags & 4) flags |= RSF_STEREO; + pins->uFlags |= CHN_16BIT; + // IT 2.14 16-bit packed sample ? + if (pis.flags & 8) flags = ((pifh.cmwt >= 0x215) && (pis.cvt & 4)) ? RS_IT21516 : RS_IT21416; + } else + { + if (pis.flags & 4) flags |= RSF_STEREO; + if (pis.cvt == 0xFF) flags = RS_ADPCM4; else + // IT 2.14 8-bit packed sample ? + if (pis.flags & 8) flags = ((pifh.cmwt >= 0x215) && (pis.cvt & 4)) ? RS_IT2158 : RS_IT2148; + } + ReadSample(&Ins[nsmp+1], flags, (LPSTR)(lpStream+pis.samplepointer), dwMemLength - pis.samplepointer); + } + } + memcpy(m_szNames[nsmp+1], pis.name, 26); + } + // Reading Patterns + for (UINT npat=0; npat<npatterns; npat++) + { + if ((!patpos[npat]) || ((DWORD)patpos[npat] + 4 >= dwMemLength)) + { + PatternSize[npat] = 64; + Patterns[npat] = AllocatePattern(64, m_nChannels); + continue; + } + + UINT len = bswapLE16(*((WORD *)(lpStream+patpos[npat]))); + UINT rows = bswapLE16(*((WORD *)(lpStream+patpos[npat]+2))); + if ((rows < 4) || (rows > 256)) continue; + if (patpos[npat]+8+len > dwMemLength) continue; + PatternSize[npat] = rows; + if ((Patterns[npat] = AllocatePattern(rows, m_nChannels)) == NULL) continue; + memset(lastvalue, 0, sizeof(lastvalue)); + memset(chnmask, 0, sizeof(chnmask)); + MODCOMMAND *m = Patterns[npat]; + UINT i = 0; + const BYTE *p = lpStream+patpos[npat]+8; + UINT nrow = 0; + while (nrow<rows) + { + if (i >= len) break; + BYTE b = p[i++]; + if (!b) + { + nrow++; + m+=m_nChannels; + continue; + } + UINT ch = b & 0x7F; + if (ch) ch = (ch - 1) & 0x3F; + if (b & 0x80) + { + if (i >= len) break; + chnmask[ch] = p[i++]; + } + if ((chnmask[ch] & 0x10) && (ch < m_nChannels)) + { + m[ch].note = lastvalue[ch].note; + } + if ((chnmask[ch] & 0x20) && (ch < m_nChannels)) + { + m[ch].instr = lastvalue[ch].instr; + } + if ((chnmask[ch] & 0x40) && (ch < m_nChannels)) + { + m[ch].volcmd = lastvalue[ch].volcmd; + m[ch].vol = lastvalue[ch].vol; + } + if ((chnmask[ch] & 0x80) && (ch < m_nChannels)) + { + m[ch].command = lastvalue[ch].command; + m[ch].param = lastvalue[ch].param; + } + if (chnmask[ch] & 1) // Note + { + if (i >= len) break; + UINT note = p[i++]; + if (ch < m_nChannels) + { + if (note < 0x80) note++; + m[ch].note = note; + lastvalue[ch].note = note; + channels_used[ch] = TRUE; + } + } + if (chnmask[ch] & 2) + { + if (i >= len) break; + UINT instr = p[i++]; + if (ch < m_nChannels) + { + m[ch].instr = instr; + lastvalue[ch].instr = instr; + } + } + if (chnmask[ch] & 4) + { + if (i >= len) break; + UINT vol = p[i++]; + if (ch < m_nChannels) + { + // 0-64: Set Volume + if (vol <= 64) { m[ch].volcmd = VOLCMD_VOLUME; m[ch].vol = vol; } else + // 128-192: Set Panning + if ((vol >= 128) && (vol <= 192)) { m[ch].volcmd = VOLCMD_PANNING; m[ch].vol = vol - 128; } else + // 65-74: Fine Volume Up + if (vol < 75) { m[ch].volcmd = VOLCMD_FINEVOLUP; m[ch].vol = vol - 65; } else + // 75-84: Fine Volume Down + if (vol < 85) { m[ch].volcmd = VOLCMD_FINEVOLDOWN; m[ch].vol = vol - 75; } else + // 85-94: Volume Slide Up + if (vol < 95) { m[ch].volcmd = VOLCMD_VOLSLIDEUP; m[ch].vol = vol - 85; } else + // 95-104: Volume Slide Down + if (vol < 105) { m[ch].volcmd = VOLCMD_VOLSLIDEDOWN; m[ch].vol = vol - 95; } else + // 105-114: Pitch Slide Up + if (vol < 115) { m[ch].volcmd = VOLCMD_PORTADOWN; m[ch].vol = vol - 105; } else + // 115-124: Pitch Slide Down + if (vol < 125) { m[ch].volcmd = VOLCMD_PORTAUP; m[ch].vol = vol - 115; } else + // 193-202: Portamento To + if ((vol >= 193) && (vol <= 202)) { m[ch].volcmd = VOLCMD_TONEPORTAMENTO; m[ch].vol = vol - 193; } else + // 203-212: Vibrato + if ((vol >= 203) && (vol <= 212)) { m[ch].volcmd = VOLCMD_VIBRATOSPEED; m[ch].vol = vol - 203; } + lastvalue[ch].volcmd = m[ch].volcmd; + lastvalue[ch].vol = m[ch].vol; + } + } + // Reading command/param + if (chnmask[ch] & 8) + { + if (i > len - 2) break; + UINT cmd = p[i++]; + UINT param = p[i++]; + if (ch < m_nChannels) + { + if (cmd) + { + m[ch].command = cmd; + m[ch].param = param; + S3MConvert(&m[ch], TRUE); + lastvalue[ch].command = m[ch].command; + lastvalue[ch].param = m[ch].param; + } + } + } + } + } + for (UINT ncu=0; ncu<MAX_BASECHANNELS; ncu++) + { + if (ncu>=m_nChannels) + { + ChnSettings[ncu].nVolume = 64; + ChnSettings[ncu].dwFlags &= ~CHN_MUTE; + } + } + m_nMinPeriod = 8; + m_nMaxPeriod = 0xF000; + return TRUE; +} + + +#ifndef MODPLUG_NO_FILESAVE +//#define SAVEITTIMESTAMP +#ifdef _MSC_VER +#pragma warning(disable:4100) +#endif + +BOOL CSoundFile::SaveIT(LPCSTR lpszFileName, UINT nPacking) +//--------------------------------------------------------- +{ + DWORD dwPatNamLen, dwChnNamLen; + ITFILEHEADER header, writeheader; + ITINSTRUMENT iti, writeiti; + ITSAMPLESTRUCT itss; + BYTE smpcount[MAX_SAMPLES]; + DWORD inspos[MAX_INSTRUMENTS]; + DWORD patpos[MAX_PATTERNS]; + DWORD smppos[MAX_SAMPLES]; + DWORD dwPos = 0, dwHdrPos = 0, dwExtra = 2; + WORD patinfo[4]; + BYTE chnmask[64]; + BYTE buf[512]; + MODCOMMAND lastvalue[64]; + FILE *f; + + + if ((!lpszFileName) || ((f = fopen(lpszFileName, "wb")) == NULL)) return FALSE; + memset(inspos, 0, sizeof(inspos)); + memset(patpos, 0, sizeof(patpos)); + memset(smppos, 0, sizeof(smppos)); + // Writing Header + memset(&header, 0, sizeof(header)); + dwPatNamLen = 0; + dwChnNamLen = 0; + header.id = 0x4D504D49; // IMPM + lstrcpyn((char *)header.songname, m_szNames[0], 27); + header.reserved1 = 0x1004; + header.ordnum = 0; + while ((header.ordnum < MAX_ORDERS) && (Order[header.ordnum] < 0xFF)) header.ordnum++; + if (header.ordnum < MAX_ORDERS) Order[header.ordnum++] = 0xFF; + header.insnum = m_nInstruments; + header.smpnum = m_nSamples; + header.patnum = MAX_PATTERNS; + while ((header.patnum > 0) && (!Patterns[header.patnum-1])) header.patnum--; + header.cwtv = 0x217; + header.cmwt = 0x200; + header.flags = 0x0001; + header.special = 0x0006; + if (m_nInstruments) header.flags |= 0x04; + if (m_dwSongFlags & SONG_LINEARSLIDES) header.flags |= 0x08; + if (m_dwSongFlags & SONG_ITOLDEFFECTS) header.flags |= 0x10; + if (m_dwSongFlags & SONG_ITCOMPATMODE) header.flags |= 0x20; + if (m_dwSongFlags & SONG_EXFILTERRANGE) header.flags |= 0x1000; + header.globalvol = m_nDefaultGlobalVolume >> 1; + header.mv = m_nSongPreAmp; + // clip song pre-amp values (between 0x20 and 0x7f) + if (header.mv < 0x20) header.mv = 0x20; + if (header.mv > 0x7F) header.mv = 0x7F; + header.speed = m_nDefaultSpeed; + header.tempo = m_nDefaultTempo; + header.sep = m_nStereoSeparation; + dwHdrPos = sizeof(header) + header.ordnum; + // Channel Pan and Volume + memset(header.chnpan, 0xFF, 64); + memset(header.chnvol, 64, 64); + for (UINT ich=0; ich<m_nChannels; ich++) + { + header.chnpan[ich] = ChnSettings[ich].nPan >> 2; + if (ChnSettings[ich].dwFlags & CHN_SURROUND) header.chnpan[ich] = 100; + header.chnvol[ich] = ChnSettings[ich].nVolume; + if (ChnSettings[ich].dwFlags & CHN_MUTE) header.chnpan[ich] |= 0x80; + if (ChnSettings[ich].szName[0]) + { + dwChnNamLen = (ich+1) * MAX_CHANNELNAME; + } + } + if (dwChnNamLen) dwExtra += dwChnNamLen + 8; +#ifdef SAVEITTIMESTAMP + dwExtra += 8; // Time Stamp +#endif + if (m_dwSongFlags & SONG_EMBEDMIDICFG) + { + header.flags |= 0x80; + header.special |= 0x08; + dwExtra += sizeof(MODMIDICFG); + } + // Pattern Names + if ((m_nPatternNames) && (m_lpszPatternNames)) + { + dwPatNamLen = m_nPatternNames * MAX_PATTERNNAME; + while ((dwPatNamLen >= MAX_PATTERNNAME) && (!m_lpszPatternNames[dwPatNamLen-MAX_PATTERNNAME])) dwPatNamLen -= MAX_PATTERNNAME; + if (dwPatNamLen < MAX_PATTERNNAME) dwPatNamLen = 0; + if (dwPatNamLen) dwExtra += dwPatNamLen + 8; + } + // Mix Plugins + dwExtra += SaveMixPlugins(NULL, TRUE); + // Comments + if (m_lpszSongComments) + { + header.special |= 1; + header.msglength = strlen(m_lpszSongComments)+1; + header.msgoffset = dwHdrPos + dwExtra + header.insnum*4 + header.patnum*4 + header.smpnum*4; + } + // Write file header + memcpy(&writeheader, &header, sizeof(header)); + + // Byteswap header information + writeheader.id = bswapLE32(writeheader.id); + writeheader.reserved1 = bswapLE16(writeheader.reserved1); + writeheader.ordnum = bswapLE16(writeheader.ordnum); + writeheader.insnum = bswapLE16(writeheader.insnum); + writeheader.smpnum = bswapLE16(writeheader.smpnum); + writeheader.patnum = bswapLE16(writeheader.patnum); + writeheader.cwtv = bswapLE16(writeheader.cwtv); + writeheader.cmwt = bswapLE16(writeheader.cmwt); + writeheader.flags = bswapLE16(writeheader.flags); + writeheader.special = bswapLE16(writeheader.special); + writeheader.msglength = bswapLE16(writeheader.msglength); + writeheader.msgoffset = bswapLE32(writeheader.msgoffset); + writeheader.reserved2 = bswapLE32(writeheader.reserved2); + + fwrite(&writeheader, 1, sizeof(writeheader), f); + + fwrite(Order, 1, header.ordnum, f); + if (header.insnum) fwrite(inspos, 4, header.insnum, f); + if (header.smpnum) fwrite(smppos, 4, header.smpnum, f); + if (header.patnum) fwrite(patpos, 4, header.patnum, f); + // Writing editor history information + { +#ifdef SAVEITTIMESTAMP + SYSTEMTIME systime; + FILETIME filetime; + WORD timestamp[4]; + WORD nInfoEx = 1; + memset(timestamp, 0, sizeof(timestamp)); + fwrite(&nInfoEx, 1, 2, f); + GetSystemTime(&systime); + SystemTimeToFileTime(&systime, &filetime); + FileTimeToDosDateTime(&filetime, ×tamp[0], ×tamp[1]); + fwrite(timestamp, 1, 8, f); +#else + WORD nInfoEx = 0; + fwrite(&nInfoEx, 1, 2, f); +#endif + } + // Writing midi cfg + if (header.flags & 0x80) + { + fwrite(&m_MidiCfg, 1, sizeof(MODMIDICFG), f); + } + // Writing pattern names + if (dwPatNamLen) + { + DWORD d = bswapLE32(0x4d414e50); + UINT len= bswapLE32(dwPatNamLen); + fwrite(&d, 1, 4, f); + fwrite(&len, 1, 4, f); + fwrite(m_lpszPatternNames, 1, dwPatNamLen, f); + } + // Writing channel Names + if (dwChnNamLen) + { + DWORD d = bswapLE32(0x4d414e43); + UINT len= bswapLE32(dwChnNamLen); + fwrite(&d, 1, 4, f); + fwrite(&len, 1, 4, f); + UINT nChnNames = dwChnNamLen / MAX_CHANNELNAME; + for (UINT inam=0; inam<nChnNames; inam++) + { + fwrite(ChnSettings[inam].szName, 1, MAX_CHANNELNAME, f); + } + } + // Writing mix plugins info + SaveMixPlugins(f, FALSE); + // Writing song message + dwPos = dwHdrPos + dwExtra + (header.insnum + header.smpnum + header.patnum) * 4; + if (header.special & 1) + { + dwPos += strlen(m_lpszSongComments) + 1; + fwrite(m_lpszSongComments, 1, strlen(m_lpszSongComments)+1, f); + } + // Writing instruments + for (UINT nins=1; nins<=header.insnum; nins++) + { + memset(&iti, 0, sizeof(iti)); + iti.id = 0x49504D49; // "IMPI" + iti.trkvers = 0x211; + if (Headers[nins]) + { + INSTRUMENTHEADER *penv = Headers[nins]; + memset(smpcount, 0, sizeof(smpcount)); + memcpy(iti.filename, penv->filename, 12); + memcpy(iti.name, penv->name, 26); + iti.mbank = penv->wMidiBank; + iti.mpr = penv->nMidiProgram; + iti.mch = penv->nMidiChannel; + iti.nna = penv->nNNA; + iti.dct = penv->nDCT; + iti.dca = penv->nDNA; + iti.fadeout = penv->nFadeOut >> 5; + iti.pps = penv->nPPS; + iti.ppc = penv->nPPC; + iti.gbv = (BYTE)(penv->nGlobalVol << 1); + iti.dfp = (BYTE)penv->nPan >> 2; + if (!(penv->dwFlags & ENV_SETPANNING)) iti.dfp |= 0x80; + iti.rv = penv->nVolSwing; + iti.rp = penv->nPanSwing; + iti.ifc = penv->nIFC; + iti.ifr = penv->nIFR; + iti.nos = 0; + for (UINT i=0; i<NOTE_MAX; i++) if (penv->Keyboard[i] < MAX_SAMPLES) + { + UINT smp = penv->Keyboard[i]; + if ((smp) && (!smpcount[smp])) + { + smpcount[smp] = 1; + iti.nos++; + } + iti.keyboard[i*2] = penv->NoteMap[i] - 1; + iti.keyboard[i*2+1] = smp; + } + // Writing Volume envelope + if (penv->dwFlags & ENV_VOLUME) iti.volenv.flags |= 0x01; + if (penv->dwFlags & ENV_VOLLOOP) iti.volenv.flags |= 0x02; + if (penv->dwFlags & ENV_VOLSUSTAIN) iti.volenv.flags |= 0x04; + if (penv->dwFlags & ENV_VOLCARRY) iti.volenv.flags |= 0x08; + iti.volenv.num = (BYTE)penv->nVolEnv; + iti.volenv.lpb = (BYTE)penv->nVolLoopStart; + iti.volenv.lpe = (BYTE)penv->nVolLoopEnd; + iti.volenv.slb = penv->nVolSustainBegin; + iti.volenv.sle = penv->nVolSustainEnd; + // Writing Panning envelope + if (penv->dwFlags & ENV_PANNING) iti.panenv.flags |= 0x01; + if (penv->dwFlags & ENV_PANLOOP) iti.panenv.flags |= 0x02; + if (penv->dwFlags & ENV_PANSUSTAIN) iti.panenv.flags |= 0x04; + if (penv->dwFlags & ENV_PANCARRY) iti.panenv.flags |= 0x08; + iti.panenv.num = (BYTE)penv->nPanEnv; + iti.panenv.lpb = (BYTE)penv->nPanLoopStart; + iti.panenv.lpe = (BYTE)penv->nPanLoopEnd; + iti.panenv.slb = penv->nPanSustainBegin; + iti.panenv.sle = penv->nPanSustainEnd; + // Writing Pitch Envelope + if (penv->dwFlags & ENV_PITCH) iti.pitchenv.flags |= 0x01; + if (penv->dwFlags & ENV_PITCHLOOP) iti.pitchenv.flags |= 0x02; + if (penv->dwFlags & ENV_PITCHSUSTAIN) iti.pitchenv.flags |= 0x04; + if (penv->dwFlags & ENV_PITCHCARRY) iti.pitchenv.flags |= 0x08; + if (penv->dwFlags & ENV_FILTER) iti.pitchenv.flags |= 0x80; + iti.pitchenv.num = (BYTE)penv->nPitchEnv; + iti.pitchenv.lpb = (BYTE)penv->nPitchLoopStart; + iti.pitchenv.lpe = (BYTE)penv->nPitchLoopEnd; + iti.pitchenv.slb = (BYTE)penv->nPitchSustainBegin; + iti.pitchenv.sle = (BYTE)penv->nPitchSustainEnd; + // Writing Envelopes data + for (UINT ev=0; ev<25; ev++) + { + iti.volenv.data[ev*3] = penv->VolEnv[ev]; + iti.volenv.data[ev*3+1] = penv->VolPoints[ev] & 0xFF; + iti.volenv.data[ev*3+2] = penv->VolPoints[ev] >> 8; + iti.panenv.data[ev*3] = penv->PanEnv[ev] - 32; + iti.panenv.data[ev*3+1] = penv->PanPoints[ev] & 0xFF; + iti.panenv.data[ev*3+2] = penv->PanPoints[ev] >> 8; + iti.pitchenv.data[ev*3] = penv->PitchEnv[ev] - 32; + iti.pitchenv.data[ev*3+1] = penv->PitchPoints[ev] & 0xFF; + iti.pitchenv.data[ev*3+2] = penv->PitchPoints[ev] >> 8; + } + } else + // Save Empty Instrument + { + for (UINT i=0; i<NOTE_MAX; i++) iti.keyboard[i*2] = i; + iti.ppc = 5*12; + iti.gbv = 128; + iti.dfp = 0x20; + iti.ifc = 0xFF; + } + if (!iti.nos) iti.trkvers = 0; + // Writing instrument + inspos[nins-1] = dwPos; + dwPos += sizeof(ITINSTRUMENT); + + memcpy(&writeiti, &iti, sizeof(ITINSTRUMENT)); + + writeiti.fadeout = bswapLE16(writeiti.fadeout); + writeiti.id = bswapLE32(writeiti.id); + writeiti.trkvers = bswapLE16(writeiti.trkvers); + writeiti.mbank = bswapLE16(writeiti.mbank); + + fwrite(&writeiti, 1, sizeof(ITINSTRUMENT), f); + } + // Writing sample headers + memset(&itss, 0, sizeof(itss)); + for (UINT hsmp=0; hsmp<header.smpnum; hsmp++) + { + smppos[hsmp] = dwPos; + dwPos += sizeof(ITSAMPLESTRUCT); + fwrite(&itss, 1, sizeof(ITSAMPLESTRUCT), f); + } + // Writing Patterns + for (UINT npat=0; npat<header.patnum; npat++) + { + DWORD dwPatPos = dwPos; + UINT len; + if (!Patterns[npat]) continue; + patpos[npat] = dwPos; + patinfo[0] = 0; + patinfo[1] = bswapLE16(PatternSize[npat]); + patinfo[2] = 0; + patinfo[3] = 0; + // Check for empty pattern + if (PatternSize[npat] == 64) + { + MODCOMMAND *pzc = Patterns[npat]; + UINT iz, nz = PatternSize[npat] * m_nChannels; + for (iz=0; iz<nz; iz++) + { + if ((pzc[iz].note) || (pzc[iz].instr) + || (pzc[iz].volcmd) || (pzc[iz].command)) break; + } + if (iz == nz) + { + patpos[npat] = 0; + continue; + } + } + fwrite(patinfo, 8, 1, f); + dwPos += 8; + memset(chnmask, 0xFF, sizeof(chnmask)); + memset(lastvalue, 0, sizeof(lastvalue)); + MODCOMMAND *m = Patterns[npat]; + for (UINT row=0; row<PatternSize[npat]; row++) + { + len = 0; + for (UINT ch=0; ch<m_nChannels; ch++, m++) + { + BYTE b = 0; + UINT command = m->command; + UINT param = m->param; + UINT vol = 0xFF; + UINT note = m->note; + if (note) b |= 1; + if ((note) && (note < 0x80)) note--; // 0xfe->0x80 --Toad + if (m->instr) b |= 2; + if (m->volcmd) + { + UINT volcmd = m->volcmd; + switch(volcmd) + { + case VOLCMD_VOLUME: vol = m->vol; if (vol > 64) vol = 64; break; + case VOLCMD_PANNING: vol = m->vol + 128; if (vol > 192) vol = 192; break; + case VOLCMD_VOLSLIDEUP: vol = 85 + ConvertVolParam(m->vol); break; + case VOLCMD_VOLSLIDEDOWN: vol = 95 + ConvertVolParam(m->vol); break; + case VOLCMD_FINEVOLUP: vol = 65 + ConvertVolParam(m->vol); break; + case VOLCMD_FINEVOLDOWN: vol = 75 + ConvertVolParam(m->vol); break; + case VOLCMD_VIBRATOSPEED: vol = 203 + ConvertVolParam(m->vol); break; + case VOLCMD_VIBRATO: vol = 203; break; + case VOLCMD_TONEPORTAMENTO: vol = 193 + ConvertVolParam(m->vol); break; + case VOLCMD_PORTADOWN: vol = 105 + ConvertVolParam(m->vol); break; + case VOLCMD_PORTAUP: vol = 115 + ConvertVolParam(m->vol); break; + default: vol = 0xFF; + } + } + if (vol != 0xFF) b |= 4; + if (command) + { + S3MSaveConvert(&command, ¶m, TRUE); + if (command) b |= 8; + } + // Packing information + if (b) + { + // Same note ? + if (b & 1) + { + if ((note == lastvalue[ch].note) && (lastvalue[ch].volcmd & 1)) + { + b &= ~1; + b |= 0x10; + } else + { + lastvalue[ch].note = note; + lastvalue[ch].volcmd |= 1; + } + } + // Same instrument ? + if (b & 2) + { + if ((m->instr == lastvalue[ch].instr) && (lastvalue[ch].volcmd & 2)) + { + b &= ~2; + b |= 0x20; + } else + { + lastvalue[ch].instr = m->instr; + lastvalue[ch].volcmd |= 2; + } + } + // Same volume column byte ? + if (b & 4) + { + if ((vol == lastvalue[ch].vol) && (lastvalue[ch].volcmd & 4)) + { + b &= ~4; + b |= 0x40; + } else + { + lastvalue[ch].vol = vol; + lastvalue[ch].volcmd |= 4; + } + } + // Same command / param ? + if (b & 8) + { + if ((command == lastvalue[ch].command) && (param == lastvalue[ch].param) && (lastvalue[ch].volcmd & 8)) + { + b &= ~8; + b |= 0x80; + } else + { + lastvalue[ch].command = command; + lastvalue[ch].param = param; + lastvalue[ch].volcmd |= 8; + } + } + if (b != chnmask[ch]) + { + chnmask[ch] = b; + buf[len++] = (ch+1) | 0x80; + buf[len++] = b; + } else + { + buf[len++] = ch+1; + } + if (b & 1) buf[len++] = note; + if (b & 2) buf[len++] = m->instr; + if (b & 4) buf[len++] = vol; + if (b & 8) + { + buf[len++] = command; + buf[len++] = param; + } + } + } + buf[len++] = 0; + dwPos += len; + patinfo[0] += len; + fwrite(buf, 1, len, f); + } + fseek(f, dwPatPos, SEEK_SET); + patinfo[0] = bswapLE16(patinfo[0]); // byteswap -- Toad + fwrite(patinfo, 8, 1, f); + fseek(f, dwPos, SEEK_SET); + } + // Writing Sample Data + for (UINT nsmp=1; nsmp<=header.smpnum; nsmp++) + { + MODINSTRUMENT *psmp = &Ins[nsmp]; + memset(&itss, 0, sizeof(itss)); + memcpy(itss.filename, psmp->name, 12); + memcpy(itss.name, m_szNames[nsmp], 26); + itss.id = 0x53504D49; + itss.gvl = (BYTE)psmp->nGlobalVol; + if (m_nInstruments) + { + for (UINT iu=1; iu<=m_nInstruments; iu++) if (Headers[iu]) + { + INSTRUMENTHEADER *penv = Headers[iu]; + for (UINT ju=0; ju<128; ju++) if (penv->Keyboard[ju] == nsmp) + { + itss.flags = 0x01; + break; + } + } + } else + { + itss.flags = 0x01; + } + if (psmp->uFlags & CHN_LOOP) itss.flags |= 0x10; + if (psmp->uFlags & CHN_SUSTAINLOOP) itss.flags |= 0x20; + if (psmp->uFlags & CHN_PINGPONGLOOP) itss.flags |= 0x40; + if (psmp->uFlags & CHN_PINGPONGSUSTAIN) itss.flags |= 0x80; + itss.C5Speed = psmp->nC4Speed; + if (!itss.C5Speed) // if no C5Speed assume it is XM Sample + { + UINT period; + + /** + * C5 note => number 61, but in XM samples: + * RealNote = Note + RelativeTone + */ + period = GetPeriodFromNote(61+psmp->RelativeTone, psmp->nFineTune, 0); + + if (period) + itss.C5Speed = GetFreqFromPeriod(period, 0, 0); + /** + * If it didn`t work, it may not be a XM file; + * so put the default C5Speed, 8363Hz. + */ + if (!itss.C5Speed) itss.C5Speed = 8363; + } + + itss.length = psmp->nLength; + itss.loopbegin = psmp->nLoopStart; + itss.loopend = psmp->nLoopEnd; + itss.susloopbegin = psmp->nSustainStart; + itss.susloopend = psmp->nSustainEnd; + itss.vol = psmp->nVolume >> 2; + itss.dfp = psmp->nPan >> 2; + itss.vit = autovibxm2it[psmp->nVibType & 7]; + itss.vis = psmp->nVibRate; + itss.vid = psmp->nVibDepth; + itss.vir = (psmp->nVibSweep < 64) ? psmp->nVibSweep * 4 : 255; + if (psmp->uFlags & CHN_PANNING) itss.dfp |= 0x80; + if ((psmp->pSample) && (psmp->nLength)) itss.cvt = 0x01; + UINT flags = RS_PCM8S; +#ifndef NO_PACKING + if (nPacking) + { + if ((!(psmp->uFlags & (CHN_16BIT|CHN_STEREO))) + && (CanPackSample((char *)psmp->pSample, psmp->nLength, nPacking))) + { + flags = RS_ADPCM4; + itss.cvt = 0xFF; + } + } else +#endif // NO_PACKING + { + if (psmp->uFlags & CHN_STEREO) + { + flags = RS_STPCM8S; + itss.flags |= 0x04; + } + if (psmp->uFlags & CHN_16BIT) + { + itss.flags |= 0x02; + flags = (psmp->uFlags & CHN_STEREO) ? RS_STPCM16S : RS_PCM16S; + } + } + itss.samplepointer = dwPos; + fseek(f, smppos[nsmp-1], SEEK_SET); + + itss.id = bswapLE32(itss.id); + itss.length = bswapLE32(itss.length); + itss.loopbegin = bswapLE32(itss.loopbegin); + itss.loopend = bswapLE32(itss.loopend); + itss.C5Speed = bswapLE32(itss.C5Speed); + itss.susloopbegin = bswapLE32(itss.susloopbegin); + itss.susloopend = bswapLE32(itss.susloopend); + itss.samplepointer = bswapLE32(itss.samplepointer); + + fwrite(&itss, 1, sizeof(ITSAMPLESTRUCT), f); + fseek(f, dwPos, SEEK_SET); + if ((psmp->pSample) && (psmp->nLength)) + { + dwPos += WriteSample(f, psmp, flags); + } + } + // Updating offsets + fseek(f, dwHdrPos, SEEK_SET); + + /* <Toad> Now we can byteswap them ;-) */ + UINT WW; + UINT WX; + WX = (UINT)header.insnum; + WX <<= 2; + for (WW=0; WW < (WX>>2); WW++) + inspos[WW] = bswapLE32(inspos[WW]); + + WX = (UINT)header.smpnum; + WX <<= 2; + for (WW=0; WW < (WX>>2); WW++) + smppos[WW] = bswapLE32(smppos[WW]); + + WX=(UINT)header.patnum; + WX <<= 2; + for (WW=0; WW < (WX>>2); WW++) + patpos[WW] = bswapLE32(patpos[WW]); + + if (header.insnum) fwrite(inspos, 4, header.insnum, f); + if (header.smpnum) fwrite(smppos, 4, header.smpnum, f); + if (header.patnum) fwrite(patpos, 4, header.patnum, f); + fclose(f); + return TRUE; +} + +#ifdef _MSC_VER +//#pragma warning(default:4100) +#endif +#endif // MODPLUG_NO_FILESAVE + +////////////////////////////////////////////////////////////////////////////// +// IT 2.14 compression + +DWORD ITReadBits(DWORD &bitbuf, UINT &bitnum, LPBYTE &ibuf, CHAR n) +//----------------------------------------------------------------- +{ + DWORD retval = 0; + UINT i = n; + + if (n > 0) + { + do + { + if (!bitnum) + { + bitbuf = *ibuf++; + bitnum = 8; + } + retval >>= 1; + retval |= bitbuf << 31; + bitbuf >>= 1; + bitnum--; + i--; + } while (i); + i = n; + } + return (retval >> (32-i)); +} + +#define IT215_SUPPORT +void ITUnpack8Bit(signed char *pSample, DWORD dwLen, LPBYTE lpMemFile, DWORD dwMemLength, BOOL b215) +//------------------------------------------------------------------------------------------- +{ + signed char *pDst = pSample; + LPBYTE pSrc = lpMemFile; + DWORD wHdr = 0; + DWORD wCount = 0; + DWORD bitbuf = 0; + UINT bitnum = 0; + BYTE bLeft = 0, bTemp = 0, bTemp2 = 0; + + while (dwLen) + { + if (!wCount) + { + wCount = 0x8000; + wHdr = bswapLE16(*((LPWORD)pSrc)); + pSrc += 2; + bLeft = 9; + bTemp = bTemp2 = 0; + bitbuf = bitnum = 0; + } + DWORD d = wCount; + if (d > dwLen) d = dwLen; + // Unpacking + DWORD dwPos = 0; + do + { + WORD wBits = (WORD)ITReadBits(bitbuf, bitnum, pSrc, bLeft); + if (bLeft < 7) + { + DWORD i = 1 << (bLeft-1); + DWORD j = wBits & 0xFFFF; + if (i != j) goto UnpackByte; + wBits = (WORD)(ITReadBits(bitbuf, bitnum, pSrc, 3) + 1) & 0xFF; + bLeft = ((BYTE)wBits < bLeft) ? (BYTE)wBits : (BYTE)((wBits+1) & 0xFF); + goto Next; + } + if (bLeft < 9) + { + WORD i = (0xFF >> (9 - bLeft)) + 4; + WORD j = i - 8; + if ((wBits <= j) || (wBits > i)) goto UnpackByte; + wBits -= j; + bLeft = ((BYTE)(wBits & 0xFF) < bLeft) ? (BYTE)(wBits & 0xFF) : (BYTE)((wBits+1) & 0xFF); + goto Next; + } + if (bLeft >= 10) goto SkipByte; + if (wBits >= 256) + { + bLeft = (BYTE)(wBits + 1) & 0xFF; + goto Next; + } + UnpackByte: + if (bLeft < 8) + { + BYTE shift = 8 - bLeft; + signed char c = (signed char)(wBits << shift); + c >>= shift; + wBits = (WORD)c; + } + wBits += bTemp; + bTemp = (BYTE)wBits; + bTemp2 += bTemp; +#ifdef IT215_SUPPORT + pDst[dwPos] = (b215) ? bTemp2 : bTemp; +#else + pDst[dwPos] = bTemp; +#endif + SkipByte: + dwPos++; + Next: + if (pSrc >= lpMemFile+dwMemLength+1) return; + } while (dwPos < d); + // Move On + wCount -= d; + dwLen -= d; + pDst += d; + } +} + + +void ITUnpack16Bit(signed char *pSample, DWORD dwLen, LPBYTE lpMemFile, DWORD dwMemLength, BOOL b215) +//-------------------------------------------------------------------------------------------- +{ + signed short *pDst = (signed short *)pSample; + LPBYTE pSrc = lpMemFile; + DWORD wHdr = 0; + DWORD wCount = 0; + DWORD bitbuf = 0; + UINT bitnum = 0; + BYTE bLeft = 0; + signed short wTemp = 0, wTemp2 = 0; + + while (dwLen) + { + if (!wCount) + { + wCount = 0x4000; + wHdr = bswapLE16(*((LPWORD)pSrc)); + pSrc += 2; + bLeft = 17; + wTemp = wTemp2 = 0; + bitbuf = bitnum = 0; + } + DWORD d = wCount; + if (d > dwLen) d = dwLen; + // Unpacking + DWORD dwPos = 0; + do + { + DWORD dwBits = ITReadBits(bitbuf, bitnum, pSrc, bLeft); + if (bLeft < 7) + { + DWORD i = 1 << (bLeft-1); + DWORD j = dwBits; + if (i != j) goto UnpackByte; + dwBits = ITReadBits(bitbuf, bitnum, pSrc, 4) + 1; + bLeft = ((BYTE)(dwBits & 0xFF) < bLeft) ? (BYTE)(dwBits & 0xFF) : (BYTE)((dwBits+1) & 0xFF); + goto Next; + } + if (bLeft < 17) + { + DWORD i = (0xFFFF >> (17 - bLeft)) + 8; + DWORD j = (i - 16) & 0xFFFF; + if ((dwBits <= j) || (dwBits > (i & 0xFFFF))) goto UnpackByte; + dwBits -= j; + bLeft = ((BYTE)(dwBits & 0xFF) < bLeft) ? (BYTE)(dwBits & 0xFF) : (BYTE)((dwBits+1) & 0xFF); + goto Next; + } + if (bLeft >= 18) goto SkipByte; + if (dwBits >= 0x10000) + { + bLeft = (BYTE)(dwBits + 1) & 0xFF; + goto Next; + } + UnpackByte: + if (bLeft < 16) + { + BYTE shift = 16 - bLeft; + signed short c = (signed short)(dwBits << shift); + c >>= shift; + dwBits = (DWORD)c; + } + dwBits += wTemp; + wTemp = (signed short)dwBits; + wTemp2 += wTemp; +#ifdef IT215_SUPPORT + pDst[dwPos] = (b215) ? wTemp2 : wTemp; +#else + pDst[dwPos] = wTemp; +#endif + SkipByte: + dwPos++; + Next: + if (pSrc >= lpMemFile+dwMemLength+1) return; + } while (dwPos < d); + // Move On + wCount -= d; + dwLen -= d; + pDst += d; + if (pSrc >= lpMemFile+dwMemLength) break; + } +} + + +UINT CSoundFile::SaveMixPlugins(FILE *f, BOOL bUpdate) +//---------------------------------------------------- +{ + DWORD chinfo[64]; + CHAR s[32]; + DWORD nPluginSize, writeSwapDWORD; + SNDMIXPLUGININFO writePluginInfo; + UINT nTotalSize = 0; + UINT nChInfo = 0; + + for (UINT i=0; i<MAX_MIXPLUGINS; i++) + { + PSNDMIXPLUGIN p = &m_MixPlugins[i]; + if ((p->Info.dwPluginId1) || (p->Info.dwPluginId2)) + { + nPluginSize = sizeof(SNDMIXPLUGININFO)+4; // plugininfo+4 (datalen) + if ((p->pMixPlugin) && (bUpdate)) + { + p->pMixPlugin->SaveAllParameters(); + } + if (p->pPluginData) + { + nPluginSize += p->nPluginDataSize; + } + if (f) + { + s[0] = 'F'; + s[1] = 'X'; + s[2] = '0' + (i/10); + s[3] = '0' + (i%10); + fwrite(s, 1, 4, f); + writeSwapDWORD = bswapLE32(nPluginSize); + fwrite(&writeSwapDWORD, 1, 4, f); + + // Copy Information To Be Written for ByteSwapping + memcpy(&writePluginInfo, &p->Info, sizeof(SNDMIXPLUGININFO)); + writePluginInfo.dwPluginId1 = bswapLE32(p->Info.dwPluginId1); + writePluginInfo.dwPluginId2 = bswapLE32(p->Info.dwPluginId2); + writePluginInfo.dwInputRouting = bswapLE32(p->Info.dwInputRouting); + writePluginInfo.dwOutputRouting = bswapLE32(p->Info.dwOutputRouting); + for (UINT j=0; j<4; j++) + { + writePluginInfo.dwReserved[j] = bswapLE32(p->Info.dwReserved[j]); + } + + fwrite(&writePluginInfo, 1, sizeof(SNDMIXPLUGININFO), f); + writeSwapDWORD = bswapLE32(m_MixPlugins[i].nPluginDataSize); + fwrite(&writeSwapDWORD, 1, 4, f); + if (m_MixPlugins[i].pPluginData) + { + fwrite(m_MixPlugins[i].pPluginData, 1, m_MixPlugins[i].nPluginDataSize, f); + } + } + nTotalSize += nPluginSize + 8; + } + } + for (UINT j=0; j<m_nChannels; j++) + { + if (j < 64) + { + if ((chinfo[j] = ChnSettings[j].nMixPlugin) != 0) + { + nChInfo = j+1; + chinfo[j] = bswapLE32(chinfo[j]); // inplace BS + } + } + } + if (nChInfo) + { + if (f) + { + nPluginSize = bswapLE32(0x58464843); + fwrite(&nPluginSize, 1, 4, f); + nPluginSize = nChInfo*4; + writeSwapDWORD = bswapLE32(nPluginSize); + fwrite(&writeSwapDWORD, 1, 4, f); + fwrite(chinfo, 1, nPluginSize, f); + } + nTotalSize += nChInfo*4 + 8; + } + return nTotalSize; +} + + +UINT CSoundFile::LoadMixPlugins(const void *pData, UINT nLen) +//----------------------------------------------------------- +{ + const BYTE *p = (const BYTE *)pData; + UINT nPos = 0; + + while (nPos+8 < nLen) + { + DWORD nPluginSize; + UINT nPlugin; + + nPluginSize = bswapLE32(*(DWORD *)(p+nPos+4)); + if (nPluginSize > nLen-nPos-8) break;; + if ((bswapLE32(*(DWORD *)(p+nPos))) == 0x58464843) + { + for (UINT ch=0; ch<64; ch++) if (ch*4 < nPluginSize) + { + ChnSettings[ch].nMixPlugin = bswapLE32(*(DWORD *)(p+nPos+8+ch*4)); + } + } else + { + if ((p[nPos] != 'F') || (p[nPos+1] != 'X') + || (p[nPos+2] < '0') || (p[nPos+3] < '0')) + { + break; + } + nPlugin = (p[nPos+2]-'0')*10 + (p[nPos+3]-'0'); + if ((nPlugin < MAX_MIXPLUGINS) && (nPluginSize >= sizeof(SNDMIXPLUGININFO)+4)) + { + DWORD dwExtra = bswapLE32(*(DWORD *)(p+nPos+8+sizeof(SNDMIXPLUGININFO))); + m_MixPlugins[nPlugin].Info = *(const SNDMIXPLUGININFO *)(p+nPos+8); + m_MixPlugins[nPlugin].Info.dwPluginId1 = bswapLE32(m_MixPlugins[nPlugin].Info.dwPluginId1); + m_MixPlugins[nPlugin].Info.dwPluginId2 = bswapLE32(m_MixPlugins[nPlugin].Info.dwPluginId2); + m_MixPlugins[nPlugin].Info.dwInputRouting = bswapLE32(m_MixPlugins[nPlugin].Info.dwInputRouting); + m_MixPlugins[nPlugin].Info.dwOutputRouting = bswapLE32(m_MixPlugins[nPlugin].Info.dwOutputRouting); + for (UINT j=0; j<4; j++) + { + m_MixPlugins[nPlugin].Info.dwReserved[j] = bswapLE32(m_MixPlugins[nPlugin].Info.dwReserved[j]); + } + if ((dwExtra) && (dwExtra <= nPluginSize-sizeof(SNDMIXPLUGININFO)-4)) + { + m_MixPlugins[nPlugin].nPluginDataSize = 0; + m_MixPlugins[nPlugin].pPluginData = new signed char [dwExtra]; + if (m_MixPlugins[nPlugin].pPluginData) + { + m_MixPlugins[nPlugin].nPluginDataSize = dwExtra; + memcpy(m_MixPlugins[nPlugin].pPluginData, p+nPos+8+sizeof(SNDMIXPLUGININFO)+4, dwExtra); + } + } + } + } + nPos += nPluginSize + 8; + } + return nPos; +} + diff --git a/lib/libmodplug/src/load_j2b.cpp b/lib/libmodplug/src/load_j2b.cpp new file mode 100644 index 0000000000..2d86ce823f --- /dev/null +++ b/lib/libmodplug/src/load_j2b.cpp @@ -0,0 +1,15 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + + +/////////////////////////////////////////////////// +// +// J2B module loader +// +/////////////////////////////////////////////////// +#include "stdafx.h" +#include "sndfile.h" + diff --git a/lib/libmodplug/src/load_mdl.cpp b/lib/libmodplug/src/load_mdl.cpp new file mode 100644 index 0000000000..bb0b4e6653 --- /dev/null +++ b/lib/libmodplug/src/load_mdl.cpp @@ -0,0 +1,503 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +////////////////////////////////////////////// +// DigiTracker (MDL) module loader // +////////////////////////////////////////////// +#include "stdafx.h" +#include "sndfile.h" + +//#pragma warning(disable:4244) + +typedef struct MDLSONGHEADER +{ + DWORD id; // "DMDL" = 0x4C444D44 + BYTE version; +} MDLSONGHEADER; + + +typedef struct MDLINFOBLOCK +{ + CHAR songname[32]; + CHAR composer[20]; + WORD norders; + WORD repeatpos; + BYTE globalvol; + BYTE speed; + BYTE tempo; + BYTE channelinfo[32]; + BYTE seq[256]; +} MDLINFOBLOCK; + + +typedef struct MDLPATTERNDATA +{ + BYTE channels; + BYTE lastrow; // nrows = lastrow+1 + CHAR name[16]; + WORD data[1]; +} MDLPATTERNDATA; + + +void ConvertMDLCommand(MODCOMMAND *m, UINT eff, UINT data) +//-------------------------------------------------------- +{ + UINT command = 0, param = data; + switch(eff) + { + case 0x01: command = CMD_PORTAMENTOUP; break; + case 0x02: command = CMD_PORTAMENTODOWN; break; + case 0x03: command = CMD_TONEPORTAMENTO; break; + case 0x04: command = CMD_VIBRATO; break; + case 0x05: command = CMD_ARPEGGIO; break; + case 0x07: command = (param < 0x20) ? CMD_SPEED : CMD_TEMPO; break; + case 0x08: command = CMD_PANNING8; param <<= 1; break; + case 0x0B: command = CMD_POSITIONJUMP; break; + case 0x0C: command = CMD_GLOBALVOLUME; break; + case 0x0D: command = CMD_PATTERNBREAK; param = (data & 0x0F) + (data>>4)*10; break; + case 0x0E: + command = CMD_S3MCMDEX; + switch(data & 0xF0) + { + case 0x00: command = 0; break; // What is E0x in MDL (there is a bunch) ? + case 0x10: if (param & 0x0F) { param |= 0xF0; command = CMD_PANNINGSLIDE; } else command = 0; break; + case 0x20: if (param & 0x0F) { param = (param << 4) | 0x0F; command = CMD_PANNINGSLIDE; } else command = 0; break; + case 0x30: param = (data & 0x0F) | 0x10; break; // glissando + case 0x40: param = (data & 0x0F) | 0x30; break; // vibrato waveform + case 0x60: param = (data & 0x0F) | 0xB0; break; + case 0x70: param = (data & 0x0F) | 0x40; break; // tremolo waveform + case 0x90: command = CMD_RETRIG; param &= 0x0F; break; + case 0xA0: param = (data & 0x0F) << 4; command = CMD_GLOBALVOLSLIDE; break; + case 0xB0: param = data & 0x0F; command = CMD_GLOBALVOLSLIDE; break; + case 0xF0: param = ((data >> 8) & 0x0F) | 0xA0; break; + } + break; + case 0x0F: command = CMD_SPEED; break; + case 0x10: if ((param & 0xF0) != 0xE0) { command = CMD_VOLUMESLIDE; if ((param & 0xF0) == 0xF0) param = ((param << 4) | 0x0F); else param >>= 2; } break; + case 0x20: if ((param & 0xF0) != 0xE0) { command = CMD_VOLUMESLIDE; if ((param & 0xF0) != 0xF0) param >>= 2; } break; + case 0x30: command = CMD_RETRIG; break; + case 0x40: command = CMD_TREMOLO; break; + case 0x50: command = CMD_TREMOR; break; + case 0xEF: if (param > 0xFF) param = 0xFF; command = CMD_OFFSET; break; + } + if (command) + { + m->command = command; + m->param = param; + } +} + + +void UnpackMDLTrack(MODCOMMAND *pat, UINT nChannels, UINT nRows, UINT nTrack, const BYTE *lpTracks) +//------------------------------------------------------------------------------------------------- +{ + MODCOMMAND cmd, *m = pat; + UINT len = *((WORD *)lpTracks); + UINT pos = 0, row = 0, i; + lpTracks += 2; + for (UINT ntrk=1; ntrk<nTrack; ntrk++) + { + lpTracks += len; + len = *((WORD *)lpTracks); + lpTracks += 2; + } + cmd.note = cmd.instr = 0; + cmd.volcmd = cmd.vol = 0; + cmd.command = cmd.param = 0; + while ((row < nRows) && (pos < len)) + { + UINT xx; + BYTE b = lpTracks[pos++]; + xx = b >> 2; + switch(b & 0x03) + { + case 0x01: + for (i=0; i<=xx; i++) + { + if (row) *m = *(m-nChannels); + m += nChannels; + row++; + if (row >= nRows) break; + } + break; + + case 0x02: + if (xx < row) *m = pat[nChannels*xx]; + m += nChannels; + row++; + break; + + case 0x03: + { + cmd.note = (xx & 0x01) ? lpTracks[pos++] : 0; + cmd.instr = (xx & 0x02) ? lpTracks[pos++] : 0; + cmd.volcmd = cmd.vol = 0; + cmd.command = cmd.param = 0; + if ((cmd.note < NOTE_MAX-12) && (cmd.note)) cmd.note += 12; + UINT volume = (xx & 0x04) ? lpTracks[pos++] : 0; + UINT commands = (xx & 0x08) ? lpTracks[pos++] : 0; + UINT command1 = commands & 0x0F; + UINT command2 = commands & 0xF0; + UINT param1 = (xx & 0x10) ? lpTracks[pos++] : 0; + UINT param2 = (xx & 0x20) ? lpTracks[pos++] : 0; + if ((command1 == 0x0E) && ((param1 & 0xF0) == 0xF0) && (!command2)) + { + param1 = ((param1 & 0x0F) << 8) | param2; + command1 = 0xEF; + command2 = param2 = 0; + } + if (volume) + { + cmd.volcmd = VOLCMD_VOLUME; + cmd.vol = (volume+1) >> 2; + } + ConvertMDLCommand(&cmd, command1, param1); + if ((cmd.command != CMD_SPEED) + && (cmd.command != CMD_TEMPO) + && (cmd.command != CMD_PATTERNBREAK)) + ConvertMDLCommand(&cmd, command2, param2); + *m = cmd; + m += nChannels; + row++; + } + break; + + // Empty Slots + default: + row += xx+1; + m += (xx+1)*nChannels; + if (row >= nRows) break; + } + } +} + + + +BOOL CSoundFile::ReadMDL(const BYTE *lpStream, DWORD dwMemLength) +//--------------------------------------------------------------- +{ + DWORD dwMemPos, dwPos, blocklen, dwTrackPos; + const MDLSONGHEADER *pmsh = (const MDLSONGHEADER *)lpStream; + MDLINFOBLOCK *pmib; + MDLPATTERNDATA *pmpd; + UINT i,j, norders = 0, npatterns = 0, ntracks = 0; + UINT ninstruments = 0, nsamples = 0; + WORD block; + WORD patterntracks[MAX_PATTERNS*32]; + BYTE smpinfo[MAX_SAMPLES]; + BYTE insvolenv[MAX_INSTRUMENTS]; + BYTE inspanenv[MAX_INSTRUMENTS]; + LPCBYTE pvolenv, ppanenv, ppitchenv; + UINT nvolenv, npanenv, npitchenv; + + if ((!lpStream) || (dwMemLength < 1024)) return FALSE; + if ((pmsh->id != 0x4C444D44) || ((pmsh->version & 0xF0) > 0x10)) return FALSE; + memset(patterntracks, 0, sizeof(patterntracks)); + memset(smpinfo, 0, sizeof(smpinfo)); + memset(insvolenv, 0, sizeof(insvolenv)); + memset(inspanenv, 0, sizeof(inspanenv)); + dwMemPos = 5; + dwTrackPos = 0; + pvolenv = ppanenv = ppitchenv = NULL; + nvolenv = npanenv = npitchenv = 0; + m_nSamples = m_nInstruments = 0; + while (dwMemPos+6 < dwMemLength) + { + block = *((WORD *)(lpStream+dwMemPos)); + blocklen = *((DWORD *)(lpStream+dwMemPos+2)); + dwMemPos += 6; + if (dwMemPos + blocklen > dwMemLength) + { + if (dwMemPos == 11) return FALSE; + break; + } + switch(block) + { + // IN: infoblock + case 0x4E49: + pmib = (MDLINFOBLOCK *)(lpStream+dwMemPos); + memcpy(m_szNames[0], pmib->songname, 32); + norders = pmib->norders; + if (norders > MAX_ORDERS) norders = MAX_ORDERS; + m_nRestartPos = pmib->repeatpos; + m_nDefaultGlobalVolume = pmib->globalvol; + m_nDefaultTempo = pmib->tempo; + m_nDefaultSpeed = pmib->speed; + m_nChannels = 4; + for (i=0; i<32; i++) + { + ChnSettings[i].nVolume = 64; + ChnSettings[i].nPan = (pmib->channelinfo[i] & 0x7F) << 1; + if (pmib->channelinfo[i] & 0x80) + ChnSettings[i].dwFlags |= CHN_MUTE; + else + m_nChannels = i+1; + } + for (j=0; j<norders; j++) Order[j] = pmib->seq[j]; + break; + // ME: song message + case 0x454D: + if (blocklen) + { + if (m_lpszSongComments) delete [] m_lpszSongComments; + m_lpszSongComments = new char[blocklen]; + if (m_lpszSongComments) + { + memcpy(m_lpszSongComments, lpStream+dwMemPos, blocklen); + m_lpszSongComments[blocklen-1] = 0; + } + } + break; + // PA: Pattern Data + case 0x4150: + npatterns = lpStream[dwMemPos]; + if (npatterns > MAX_PATTERNS) npatterns = MAX_PATTERNS; + dwPos = dwMemPos + 1; + for (i=0; i<npatterns; i++) + { + if (dwPos+18 >= dwMemLength) break; + pmpd = (MDLPATTERNDATA *)(lpStream + dwPos); + if (pmpd->channels > 32) break; + PatternSize[i] = pmpd->lastrow+1; + if (m_nChannels < pmpd->channels) m_nChannels = pmpd->channels; + dwPos += 18 + 2*pmpd->channels; + for (j=0; j<pmpd->channels; j++) + { + patterntracks[i*32+j] = pmpd->data[j]; + } + } + break; + // TR: Track Data + case 0x5254: + if (dwTrackPos) break; + ntracks = *((WORD *)(lpStream+dwMemPos)); + dwTrackPos = dwMemPos+2; + break; + // II: Instruments + case 0x4949: + ninstruments = lpStream[dwMemPos]; + dwPos = dwMemPos+1; + for (i=0; i<ninstruments; i++) + { + UINT nins = lpStream[dwPos]; + if ((nins >= MAX_INSTRUMENTS) || (!nins)) break; + if (m_nInstruments < nins) m_nInstruments = nins; + if (!Headers[nins]) + { + UINT note = 12; + if ((Headers[nins] = new INSTRUMENTHEADER) == NULL) break; + INSTRUMENTHEADER *penv = Headers[nins]; + memset(penv, 0, sizeof(INSTRUMENTHEADER)); + memcpy(penv->name, lpStream+dwPos+2, 32); + penv->nGlobalVol = 64; + penv->nPPC = 5*12; + for (j=0; j<lpStream[dwPos+1]; j++) + { + const BYTE *ps = lpStream+dwPos+34+14*j; + while ((note < (UINT)(ps[1]+12)) && (note < NOTE_MAX)) + { + penv->NoteMap[note] = note+1; + if (ps[0] < MAX_SAMPLES) + { + int ismp = ps[0]; + penv->Keyboard[note] = ps[0]; + Ins[ismp].nVolume = ps[2]; + Ins[ismp].nPan = ps[4] << 1; + Ins[ismp].nVibType = ps[11]; + Ins[ismp].nVibSweep = ps[10]; + Ins[ismp].nVibDepth = ps[9]; + Ins[ismp].nVibRate = ps[8]; + } + penv->nFadeOut = (ps[7] << 8) | ps[6]; + if (penv->nFadeOut == 0xFFFF) penv->nFadeOut = 0; + note++; + } + // Use volume envelope ? + if (ps[3] & 0x80) + { + penv->dwFlags |= ENV_VOLUME; + insvolenv[nins] = (ps[3] & 0x3F) + 1; + } + // Use panning envelope ? + if (ps[5] & 0x80) + { + penv->dwFlags |= ENV_PANNING; + inspanenv[nins] = (ps[5] & 0x3F) + 1; + } + } + } + dwPos += 34 + 14*lpStream[dwPos+1]; + } + for (j=1; j<=m_nInstruments; j++) if (!Headers[j]) + { + Headers[j] = new INSTRUMENTHEADER; + if (Headers[j]) memset(Headers[j], 0, sizeof(INSTRUMENTHEADER)); + } + break; + // VE: Volume Envelope + case 0x4556: + if ((nvolenv = lpStream[dwMemPos]) == 0) break; + if (dwMemPos + nvolenv*32 + 1 <= dwMemLength) pvolenv = lpStream + dwMemPos + 1; + break; + // PE: Panning Envelope + case 0x4550: + if ((npanenv = lpStream[dwMemPos]) == 0) break; + if (dwMemPos + npanenv*32 + 1 <= dwMemLength) ppanenv = lpStream + dwMemPos + 1; + break; + // FE: Pitch Envelope + case 0x4546: + if ((npitchenv = lpStream[dwMemPos]) == 0) break; + if (dwMemPos + npitchenv*32 + 1 <= dwMemLength) ppitchenv = lpStream + dwMemPos + 1; + break; + // IS: Sample Infoblock + case 0x5349: + nsamples = lpStream[dwMemPos]; + dwPos = dwMemPos+1; + for (i=0; i<nsamples; i++, dwPos += 59) + { + UINT nins = lpStream[dwPos]; + if ((nins >= MAX_SAMPLES) || (!nins)) continue; + if (m_nSamples < nins) m_nSamples = nins; + MODINSTRUMENT *pins = &Ins[nins]; + memcpy(m_szNames[nins], lpStream+dwPos+1, 32); + memcpy(pins->name, lpStream+dwPos+33, 8); + pins->nC4Speed = *((DWORD *)(lpStream+dwPos+41)); + pins->nLength = *((DWORD *)(lpStream+dwPos+45)); + pins->nLoopStart = *((DWORD *)(lpStream+dwPos+49)); + pins->nLoopEnd = pins->nLoopStart + *((DWORD *)(lpStream+dwPos+53)); + if (pins->nLoopEnd > pins->nLoopStart) pins->uFlags |= CHN_LOOP; + pins->nGlobalVol = 64; + if (lpStream[dwPos+58] & 0x01) + { + pins->uFlags |= CHN_16BIT; + pins->nLength >>= 1; + pins->nLoopStart >>= 1; + pins->nLoopEnd >>= 1; + } + if (lpStream[dwPos+58] & 0x02) pins->uFlags |= CHN_PINGPONGLOOP; + smpinfo[nins] = (lpStream[dwPos+58] >> 2) & 3; + } + break; + // SA: Sample Data + case 0x4153: + dwPos = dwMemPos; + for (i=1; i<=m_nSamples; i++) if ((Ins[i].nLength) && (!Ins[i].pSample) && (smpinfo[i] != 3) && (dwPos < dwMemLength)) + { + MODINSTRUMENT *pins = &Ins[i]; + UINT flags = (pins->uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S; + if (!smpinfo[i]) + { + dwPos += ReadSample(pins, flags, (LPSTR)(lpStream+dwPos), dwMemLength - dwPos); + } else + { + DWORD dwLen = *((DWORD *)(lpStream+dwPos)); + dwPos += 4; + if ((dwPos+dwLen <= dwMemLength) && (dwLen > 4)) + { + flags = (pins->uFlags & CHN_16BIT) ? RS_MDL16 : RS_MDL8; + ReadSample(pins, flags, (LPSTR)(lpStream+dwPos), dwLen); + } + dwPos += dwLen; + } + } + break; + } + dwMemPos += blocklen; + } + // Unpack Patterns + if ((dwTrackPos) && (npatterns) && (m_nChannels) && (ntracks)) + { + for (UINT ipat=0; ipat<npatterns; ipat++) + { + if ((Patterns[ipat] = AllocatePattern(PatternSize[ipat], m_nChannels)) == NULL) break; + for (UINT chn=0; chn<m_nChannels; chn++) if ((patterntracks[ipat*32+chn]) && (patterntracks[ipat*32+chn] <= ntracks)) + { + MODCOMMAND *m = Patterns[ipat] + chn; + UnpackMDLTrack(m, m_nChannels, PatternSize[ipat], patterntracks[ipat*32+chn], lpStream+dwTrackPos); + } + } + } + // Set up envelopes + for (UINT iIns=1; iIns<=m_nInstruments; iIns++) if (Headers[iIns]) + { + INSTRUMENTHEADER *penv = Headers[iIns]; + // Setup volume envelope + if ((nvolenv) && (pvolenv) && (insvolenv[iIns])) + { + LPCBYTE pve = pvolenv; + for (UINT nve=0; nve<nvolenv; nve++, pve+=33) if (pve[0]+1 == insvolenv[iIns]) + { + WORD vtick = 1; + penv->nVolEnv = 15; + for (UINT iv=0; iv<15; iv++) + { + if (iv) vtick += pve[iv*2+1]; + penv->VolPoints[iv] = vtick; + penv->VolEnv[iv] = pve[iv*2+2]; + if (!pve[iv*2+1]) + { + penv->nVolEnv = iv+1; + break; + } + } + penv->nVolSustainBegin = penv->nVolSustainEnd = pve[31] & 0x0F; + if (pve[31] & 0x10) penv->dwFlags |= ENV_VOLSUSTAIN; + if (pve[31] & 0x20) penv->dwFlags |= ENV_VOLLOOP; + penv->nVolLoopStart = pve[32] & 0x0F; + penv->nVolLoopEnd = pve[32] >> 4; + } + } + // Setup panning envelope + if ((npanenv) && (ppanenv) && (inspanenv[iIns])) + { + LPCBYTE ppe = ppanenv; + for (UINT npe=0; npe<npanenv; npe++, ppe+=33) if (ppe[0]+1 == inspanenv[iIns]) + { + WORD vtick = 1; + penv->nPanEnv = 15; + for (UINT iv=0; iv<15; iv++) + { + if (iv) vtick += ppe[iv*2+1]; + penv->PanPoints[iv] = vtick; + penv->PanEnv[iv] = ppe[iv*2+2]; + if (!ppe[iv*2+1]) + { + penv->nPanEnv = iv+1; + break; + } + } + if (ppe[31] & 0x10) penv->dwFlags |= ENV_PANSUSTAIN; + if (ppe[31] & 0x20) penv->dwFlags |= ENV_PANLOOP; + penv->nPanLoopStart = ppe[32] & 0x0F; + penv->nPanLoopEnd = ppe[32] >> 4; + } + } + } + m_dwSongFlags |= SONG_LINEARSLIDES; + m_nType = MOD_TYPE_MDL; + return TRUE; +} + + +///////////////////////////////////////////////////////////////////////// +// MDL Sample Unpacking + +// MDL Huffman ReadBits compression +WORD MDLReadBits(DWORD &bitbuf, UINT &bitnum, LPBYTE &ibuf, CHAR n) +//----------------------------------------------------------------- +{ + WORD v = (WORD)(bitbuf & ((1 << n) - 1) ); + bitbuf >>= n; + bitnum -= n; + if (bitnum <= 24) + { + bitbuf |= (((DWORD)(*ibuf++)) << bitnum); + bitnum += 8; + } + return v; +} + + diff --git a/lib/libmodplug/src/load_med.cpp b/lib/libmodplug/src/load_med.cpp new file mode 100644 index 0000000000..0caebee30e --- /dev/null +++ b/lib/libmodplug/src/load_med.cpp @@ -0,0 +1,924 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net>, + * Adam Goode <adam@evdebs.org> (endian and char fixes for PPC) +*/ + +#include "stdafx.h" +#include "sndfile.h" + +//#define MED_LOG + +#ifdef MED_LOG +extern void Log(LPCSTR s, ...); +#endif + +////////////////////////////////////////////////////////// +// OctaMed MED file support (import only) + +// flags +#define MMD_FLAG_FILTERON 0x1 +#define MMD_FLAG_JUMPINGON 0x2 +#define MMD_FLAG_JUMP8TH 0x4 +#define MMD_FLAG_INSTRSATT 0x8 // instruments are attached (this is a module) +#define MMD_FLAG_VOLHEX 0x10 +#define MMD_FLAG_STSLIDE 0x20 // SoundTracker mode for slides +#define MMD_FLAG_8CHANNEL 0x40 // OctaMED 8 channel song +#define MMD_FLAG_SLOWHQ 0x80 // HQ slows playing speed (V2-V4 compatibility) +// flags2 +#define MMD_FLAG2_BMASK 0x1F +#define MMD_FLAG2_BPM 0x20 +#define MMD_FLAG2_MIX 0x80 // uses Mixing (V7+) +// flags3: +#define MMD_FLAG3_STEREO 0x1 // mixing in Stereo mode +#define MMD_FLAG3_FREEPAN 0x2 // free panning +#define MMD_FLAG3_GM 0x4 // module designed for GM/XG compatibility + + +// generic MMD tags +#define MMDTAG_END 0 +#define MMDTAG_PTR 0x80000000 // data needs relocation +#define MMDTAG_MUSTKNOW 0x40000000 // loader must fail if this isn't recognized +#define MMDTAG_MUSTWARN 0x20000000 // loader must warn if this isn't recognized + +// ExpData tags +// # of effect groups, including the global group (will +// override settings in MMDSong struct), default = 1 +#define MMDTAG_EXP_NUMFXGROUPS 1 +#define MMDTAG_TRK_NAME (MMDTAG_PTR|1) // trackinfo tags +#define MMDTAG_TRK_NAMELEN 2 // namelen includes zero term. +#define MMDTAG_TRK_FXGROUP 3 +// effectinfo tags +#define MMDTAG_FX_ECHOTYPE 1 +#define MMDTAG_FX_ECHOLEN 2 +#define MMDTAG_FX_ECHODEPTH 3 +#define MMDTAG_FX_STEREOSEP 4 +#define MMDTAG_FX_GROUPNAME (MMDTAG_PTR|5) // the Global Effects group shouldn't have name saved! +#define MMDTAG_FX_GRPNAMELEN 6 // namelen includes zero term. + +#pragma pack(1) + +typedef struct tagMEDMODULEHEADER +{ + DWORD id; // MMD1-MMD3 + DWORD modlen; // Size of file + DWORD song; // Position in file for this song + WORD psecnum; + WORD pseq; + DWORD blockarr; // Position in file for blocks + DWORD mmdflags; + DWORD smplarr; // Position in file for samples + DWORD reserved; + DWORD expdata; // Absolute offset in file for ExpData (0 if not present) + DWORD reserved2; + WORD pstate; + WORD pblock; + WORD pline; + WORD pseqnum; + WORD actplayline; + BYTE counter; + BYTE extra_songs; // # of songs - 1 +} MEDMODULEHEADER; + + +typedef struct tagMMD0SAMPLE +{ + WORD rep, replen; + BYTE midich; + BYTE midipreset; + BYTE svol; + signed char strans; +} MMD0SAMPLE; + + +// Sample header is immediately followed by sample data... +typedef struct tagMMDSAMPLEHEADER +{ + DWORD length; // length of *one* *unpacked* channel in *bytes* + WORD type; + // if non-negative + // bits 0-3 reserved for multi-octave instruments, not supported on the PC + // 0x10: 16 bit (otherwise 8 bit) + // 0x20: Stereo (otherwise mono) + // 0x40: Uses DeltaCode + // 0x80: Packed data + // -1: Synth + // -2: Hybrid + // if type indicates packed data, these fields follow, otherwise we go right to the data + WORD packtype; // Only 1 = ADPCM is supported + WORD subtype; // Packing subtype + // ADPCM subtype + // 1: g723_40 + // 2: g721 + // 3: g723_24 + BYTE commonflags; // flags common to all packtypes (none defined so far) + BYTE packerflags; // flags for the specific packtype + ULONG leftchlen; // packed length of left channel in bytes + ULONG rightchlen; // packed length of right channel in bytes (ONLY PRESENT IN STEREO SAMPLES) + BYTE SampleData[1]; // Sample Data +} MMDSAMPLEHEADER; + + +// MMD0/MMD1 song header +typedef struct tagMMD0SONGHEADER +{ + MMD0SAMPLE sample[63]; + WORD numblocks; // # of blocks + WORD songlen; // # of entries used in playseq + BYTE playseq[256]; // Play sequence + WORD deftempo; // BPM tempo + signed char playtransp; // Play transpose + BYTE flags; // 0x10: Hex Volumes | 0x20: ST/NT/PT Slides | 0x40: 8 Channels song + BYTE flags2; // [b4-b0]+1: Tempo LPB, 0x20: tempo mode, 0x80: mix_conv=on + BYTE tempo2; // tempo TPL + BYTE trkvol[16]; // track volumes + BYTE mastervol; // master volume + BYTE numsamples; // # of samples (max=63) +} MMD0SONGHEADER; + + +// MMD2/MMD3 song header +typedef struct tagMMD2SONGHEADER +{ + MMD0SAMPLE sample[63]; + WORD numblocks; // # of blocks + WORD numsections; // # of sections + DWORD playseqtable; // filepos of play sequence + DWORD sectiontable; // filepos of sections table (WORD array) + DWORD trackvols; // filepos of tracks volume (BYTE array) + WORD numtracks; // # of tracks (max 64) + WORD numpseqs; // # of play sequences + DWORD trackpans; // filepos of tracks pan values (BYTE array) + LONG flags3; // 0x1:stereo_mix, 0x2:free_panning, 0x4:GM/XG compatibility + WORD voladj; // vol_adjust (set to 100 if 0) + WORD channels; // # of channels (4 if =0) + BYTE mix_echotype; // 1:normal,2:xecho + BYTE mix_echodepth; // 1..6 + WORD mix_echolen; // > 0 + signed char mix_stereosep; // -4..4 + BYTE pad0[223]; + WORD deftempo; // BPM tempo + signed char playtransp; // play transpose + BYTE flags; // 0x1:filteron, 0x2:jumpingon, 0x4:jump8th, 0x8:instr_attached, 0x10:hex_vol, 0x20:PT_slides, 0x40:8ch_conv,0x80:hq slows playing speed + BYTE flags2; // 0x80:mix_conv=on, [b4-b0]+1:tempo LPB, 0x20:tempo_mode + BYTE tempo2; // tempo TPL + BYTE pad1[16]; + BYTE mastervol; // master volume + BYTE numsamples; // # of samples (max 63) +} MMD2SONGHEADER; + +// For MMD0 the note information is held in 3 bytes, byte0, byte1, byte2. For reference we +// number the bits in each byte 0..7, where 0 is the low bit. +// The note is held as bits 5..0 of byte0 +// The instrument is encoded in 6 bits, bits 7 and 6 of byte0 and bits 7,6,5,4 of byte1 +// The command number is bits 3,2,1,0 of byte1, command data is in byte2: +// For command 0, byte2 represents the second data byte, otherwise byte2 +// represents the first data byte. +typedef struct tagMMD0BLOCK +{ + BYTE numtracks; + BYTE lines; // File value is 1 less than actual, so 0 -> 1 line +} MMD0BLOCK; // BYTE data[lines+1][tracks][3]; + + +// For MMD1,MMD2,MMD3 the note information is carried in 4 bytes, byte0, byte1, +// byte2 and byte3 +// The note is held as byte0 (values above 0x84 are ignored) +// The instrument is held as byte1 +// The command number is held as byte2, command data is in byte3 +// For commands 0 and 0x19 byte3 represents the second data byte, +// otherwise byte2 represents the first data byte. +typedef struct tagMMD1BLOCK +{ + WORD numtracks; // Number of tracks, may be > 64, but then that data is skipped. + WORD lines; // Stored value is 1 less than actual, so 0 -> 1 line + DWORD info; // Offset of BlockInfo (if 0, no block_info is present) +} MMD1BLOCK; + + +typedef struct tagMMD1BLOCKINFO +{ + DWORD hlmask; // Unimplemented - ignore + DWORD blockname; // file offset of block name + DWORD blocknamelen; // length of block name (including term. 0) + DWORD pagetable; // file offset of command page table + DWORD cmdexttable; // file offset of command extension table + DWORD reserved[4]; // future expansion +} MMD1BLOCKINFO; + + +// A set of play sequences is stored as an array of ULONG files offsets +// Each offset points to the play sequence itself. +typedef struct tagMMD2PLAYSEQ +{ + CHAR name[32]; + DWORD command_offs; // filepos of command table + DWORD reserved; + WORD length; + WORD seq[512]; // skip if > 0x8000 +} MMD2PLAYSEQ; + + +// A command table contains commands that effect a particular play sequence +// entry. The only commands read in are STOP or POSJUMP, all others are ignored +// POSJUMP is presumed to have extra bytes containing a WORD for the position +typedef struct tagMMDCOMMAND +{ + WORD offset; // Offset within current sequence entry + BYTE cmdnumber; // STOP (537) or POSJUMP (538) (others skipped) + BYTE extra_count; + BYTE extra_bytes[4];// [extra_count]; +} MMDCOMMAND; // Last entry has offset == 0xFFFF, cmd_number == 0 and 0 extrabytes + + +typedef struct tagMMD0EXP +{ + DWORD nextmod; // File offset of next Hdr + DWORD exp_smp; // Pointer to extra instrument data + WORD s_ext_entries; // Number of extra instrument entries + WORD s_ext_entrsz; // Size of extra instrument data + DWORD annotxt; + DWORD annolen; + DWORD iinfo; // Instrument names + WORD i_ext_entries; + WORD i_ext_entrsz; + DWORD jumpmask; + DWORD rgbtable; + BYTE channelsplit[4]; // Only used if 8ch_conv (extra channel for every nonzero entry) + DWORD n_info; + DWORD songname; // Song name + DWORD songnamelen; + DWORD dumps; + DWORD mmdinfo; + DWORD mmdrexx; + DWORD mmdcmd3x; + DWORD trackinfo_ofs; // ptr to song->numtracks ptrs to tag lists + DWORD effectinfo_ofs; // ptr to group ptrs + DWORD tag_end; +} MMD0EXP; + +#pragma pack() + + + +static void MedConvert(MODCOMMAND *p, const MMD0SONGHEADER *pmsh) +//--------------------------------------------------------------- +{ + const BYTE bpmvals[9] = { 179,164,152,141,131,123,116,110,104}; + + UINT command = p->command; + UINT param = p->param; + switch(command) + { + case 0x00: if (param) command = CMD_ARPEGGIO; else command = 0; break; + case 0x01: command = CMD_PORTAMENTOUP; break; + case 0x02: command = CMD_PORTAMENTODOWN; break; + case 0x03: command = CMD_TONEPORTAMENTO; break; + case 0x04: command = CMD_VIBRATO; break; + case 0x05: command = CMD_TONEPORTAVOL; break; + case 0x06: command = CMD_VIBRATOVOL; break; + case 0x07: command = CMD_TREMOLO; break; + case 0x0A: if (param & 0xF0) param &= 0xF0; command = CMD_VOLUMESLIDE; if (!param) command = 0; break; + case 0x0B: command = CMD_POSITIONJUMP; break; + case 0x0C: command = CMD_VOLUME; + if (pmsh->flags & MMD_FLAG_VOLHEX) + { + if (param < 0x80) + { + param = (param+1) / 2; + } else command = 0; + } else + { + if (param <= 0x99) + { + param = (param >> 4)*10+((param & 0x0F) % 10); + if (param > 64) param = 64; + } else command = 0; + } + break; + case 0x09: command = (param < 0x20) ? CMD_SPEED : CMD_TEMPO; break; + case 0x0D: if (param & 0xF0) param &= 0xF0; command = CMD_VOLUMESLIDE; if (!param) command = 0; break; + case 0x0F: // Set Tempo / Special + // F.00 = Pattern Break + if (!param) command = CMD_PATTERNBREAK; else + // F.01 - F.F0: Set tempo/speed + if (param <= 0xF0) + { + if (pmsh->flags & MMD_FLAG_8CHANNEL) + { + param = (param > 10) ? 99 : bpmvals[param-1]; + } else + // F.01 - F.0A: Set Speed + if (param <= 0x0A) + { + command = CMD_SPEED; + } else + // Old tempo + if (!(pmsh->flags2 & MMD_FLAG2_BPM)) + { + param = _muldiv(param, 5*715909, 2*474326); + } + // F.0B - F.F0: Set Tempo (assumes LPB=4) + if (param > 0x0A) + { + command = CMD_TEMPO; + if (param < 0x21) param = 0x21; + if (param > 240) param = 240; + } + } else + switch(param) + { + // F.F1: Retrig 2x + case 0xF1: + command = CMD_MODCMDEX; + param = 0x93; + break; + // F.F2: Note Delay 2x + case 0xF2: + command = CMD_MODCMDEX; + param = 0xD3; + break; + // F.F3: Retrig 3x + case 0xF3: + command = CMD_MODCMDEX; + param = 0x92; + break; + // F.F4: Note Delay 1/3 + case 0xF4: + command = CMD_MODCMDEX; + param = 0xD2; + break; + // F.F5: Note Delay 2/3 + case 0xF5: + command = CMD_MODCMDEX; + param = 0xD4; + break; + // F.F8: Filter Off + case 0xF8: + command = CMD_MODCMDEX; + param = 0x00; + break; + // F.F9: Filter On + case 0xF9: + command = CMD_MODCMDEX; + param = 0x01; + break; + // F.FD: Very fast tone-portamento + case 0xFD: + command = CMD_TONEPORTAMENTO; + param = 0xFF; + break; + // F.FE: End Song + case 0xFE: + command = CMD_SPEED; + param = 0; + break; + // F.FF: Note Cut + case 0xFF: + command = CMD_MODCMDEX; + param = 0xC0; + break; + default: +#ifdef MED_LOG + Log("Unknown Fxx command: cmd=0x%02X param=0x%02X\n", command, param); +#endif + param = command = 0; + } + break; + // 11.0x: Fine Slide Up + case 0x11: + command = CMD_MODCMDEX; + if (param > 0x0F) param = 0x0F; + param |= 0x10; + break; + // 12.0x: Fine Slide Down + case 0x12: + command = CMD_MODCMDEX; + if (param > 0x0F) param = 0x0F; + param |= 0x20; + break; + // 14.xx: Vibrato + case 0x14: + command = CMD_VIBRATO; + break; + // 15.xx: FineTune + case 0x15: + command = CMD_MODCMDEX; + param &= 0x0F; + param |= 0x50; + break; + // 16.xx: Pattern Loop + case 0x16: + command = CMD_MODCMDEX; + if (param > 0x0F) param = 0x0F; + param |= 0x60; + break; + // 18.xx: Note Cut + case 0x18: + command = CMD_MODCMDEX; + if (param > 0x0F) param = 0x0F; + param |= 0xC0; + break; + // 19.xx: Sample Offset + case 0x19: + command = CMD_OFFSET; + break; + // 1A.0x: Fine Volume Up + case 0x1A: + command = CMD_MODCMDEX; + if (param > 0x0F) param = 0x0F; + param |= 0xA0; + break; + // 1B.0x: Fine Volume Down + case 0x1B: + command = CMD_MODCMDEX; + if (param > 0x0F) param = 0x0F; + param |= 0xB0; + break; + // 1D.xx: Pattern Break + case 0x1D: + command = CMD_PATTERNBREAK; + break; + // 1E.0x: Pattern Delay + case 0x1E: + command = CMD_MODCMDEX; + if (param > 0x0F) param = 0x0F; + param |= 0xE0; + break; + // 1F.xy: Retrig + case 0x1F: + command = CMD_RETRIG; + param &= 0x0F; + break; + // 2E.xx: set panning + case 0x2E: + command = CMD_MODCMDEX; + param = ((param + 0x10) & 0xFF) >> 1; + if (param > 0x0F) param = 0x0F; + param |= 0x80; + break; + default: +#ifdef MED_LOG + // 0x2E ? + Log("Unknown command: cmd=0x%02X param=0x%02X\n", command, param); +#endif + command = param = 0; + } + p->command = command; + p->param = param; +} + + +BOOL CSoundFile::ReadMed(const BYTE *lpStream, DWORD dwMemLength) +//--------------------------------------------------------------- +{ + const MEDMODULEHEADER *pmmh; + const MMD0SONGHEADER *pmsh; + const MMD2SONGHEADER *pmsh2; + const MMD0EXP *pmex; + DWORD dwBlockArr, dwSmplArr, dwExpData, wNumBlocks; + LPDWORD pdwTable; + CHAR version; + UINT deftempo; + int playtransp = 0; + + if ((!lpStream) || (dwMemLength < 0x200)) return FALSE; + pmmh = (MEDMODULEHEADER *)lpStream; + if (((pmmh->id & 0x00FFFFFF) != 0x444D4D) || (!pmmh->song)) return FALSE; + // Check for 'MMDx' + DWORD dwSong = bswapBE32(pmmh->song); + if ((dwSong >= dwMemLength) || (dwSong + sizeof(MMD0SONGHEADER) >= dwMemLength)) return FALSE; + version = (signed char)((pmmh->id >> 24) & 0xFF); + if ((version < '0') || (version > '3')) return FALSE; +#ifdef MED_LOG + Log("\nLoading MMD%c module (flags=0x%02X)...\n", version, bswapBE32(pmmh->mmdflags)); + Log(" modlen = %d\n", bswapBE32(pmmh->modlen)); + Log(" song = 0x%08X\n", bswapBE32(pmmh->song)); + Log(" psecnum = %d\n", bswapBE16(pmmh->psecnum)); + Log(" pseq = %d\n", bswapBE16(pmmh->pseq)); + Log(" blockarr = 0x%08X\n", bswapBE32(pmmh->blockarr)); + Log(" mmdflags = 0x%08X\n", bswapBE32(pmmh->mmdflags)); + Log(" smplarr = 0x%08X\n", bswapBE32(pmmh->smplarr)); + Log(" reserved = 0x%08X\n", bswapBE32(pmmh->reserved)); + Log(" expdata = 0x%08X\n", bswapBE32(pmmh->expdata)); + Log(" reserved2= 0x%08X\n", bswapBE32(pmmh->reserved2)); + Log(" pstate = %d\n", bswapBE16(pmmh->pstate)); + Log(" pblock = %d\n", bswapBE16(pmmh->pblock)); + Log(" pline = %d\n", bswapBE16(pmmh->pline)); + Log(" pseqnum = %d\n", bswapBE16(pmmh->pseqnum)); + Log(" actplayline=%d\n", bswapBE16(pmmh->actplayline)); + Log(" counter = %d\n", pmmh->counter); + Log(" extra_songs = %d\n", pmmh->extra_songs); + Log("\n"); +#endif + m_nType = MOD_TYPE_MED; + m_nSongPreAmp = 0x20; + dwBlockArr = bswapBE32(pmmh->blockarr); + dwSmplArr = bswapBE32(pmmh->smplarr); + dwExpData = bswapBE32(pmmh->expdata); + if ((dwExpData) && (dwExpData+sizeof(MMD0EXP) < dwMemLength)) + pmex = (MMD0EXP *)(lpStream+dwExpData); + else + pmex = NULL; + pmsh = (MMD0SONGHEADER *)(lpStream + dwSong); + pmsh2 = (MMD2SONGHEADER *)pmsh; +#ifdef MED_LOG + if (version < '2') + { + Log("MMD0 Header:\n"); + Log(" numblocks = %d\n", bswapBE16(pmsh->numblocks)); + Log(" songlen = %d\n", bswapBE16(pmsh->songlen)); + Log(" playseq = "); + for (UINT idbg1=0; idbg1<16; idbg1++) Log("%2d, ", pmsh->playseq[idbg1]); + Log("...\n"); + Log(" deftempo = 0x%04X\n", bswapBE16(pmsh->deftempo)); + Log(" playtransp = %d\n", (signed char)pmsh->playtransp); + Log(" flags(1,2) = 0x%02X, 0x%02X\n", pmsh->flags, pmsh->flags2); + Log(" tempo2 = %d\n", pmsh->tempo2); + Log(" trkvol = "); + for (UINT idbg2=0; idbg2<16; idbg2++) Log("0x%02X, ", pmsh->trkvol[idbg2]); + Log("...\n"); + Log(" mastervol = 0x%02X\n", pmsh->mastervol); + Log(" numsamples = %d\n", pmsh->numsamples); + } else + { + Log("MMD2 Header:\n"); + Log(" numblocks = %d\n", bswapBE16(pmsh2->numblocks)); + Log(" numsections= %d\n", bswapBE16(pmsh2->numsections)); + Log(" playseqptr = 0x%04X\n", bswapBE32(pmsh2->playseqtable)); + Log(" sectionptr = 0x%04X\n", bswapBE32(pmsh2->sectiontable)); + Log(" trackvols = 0x%04X\n", bswapBE32(pmsh2->trackvols)); + Log(" numtracks = %d\n", bswapBE16(pmsh2->numtracks)); + Log(" numpseqs = %d\n", bswapBE16(pmsh2->numpseqs)); + Log(" trackpans = 0x%04X\n", bswapBE32(pmsh2->trackpans)); + Log(" flags3 = 0x%08X\n", bswapBE32(pmsh2->flags3)); + Log(" voladj = %d\n", bswapBE16(pmsh2->voladj)); + Log(" channels = %d\n", bswapBE16(pmsh2->channels)); + Log(" echotype = %d\n", pmsh2->mix_echotype); + Log(" echodepth = %d\n", pmsh2->mix_echodepth); + Log(" echolen = %d\n", bswapBE16(pmsh2->mix_echolen)); + Log(" stereosep = %d\n", (signed char)pmsh2->mix_stereosep); + Log(" deftempo = 0x%04X\n", bswapBE16(pmsh2->deftempo)); + Log(" playtransp = %d\n", (signed char)pmsh2->playtransp); + Log(" flags(1,2) = 0x%02X, 0x%02X\n", pmsh2->flags, pmsh2->flags2); + Log(" tempo2 = %d\n", pmsh2->tempo2); + Log(" mastervol = 0x%02X\n", pmsh2->mastervol); + Log(" numsamples = %d\n", pmsh->numsamples); + } + Log("\n"); +#endif + wNumBlocks = bswapBE16(pmsh->numblocks); + m_nChannels = 4; + m_nSamples = pmsh->numsamples; + if (m_nSamples > 63) m_nSamples = 63; + // Tempo + m_nDefaultTempo = 125; + deftempo = bswapBE16(pmsh->deftempo); + if (!deftempo) deftempo = 125; + if (pmsh->flags2 & MMD_FLAG2_BPM) + { + UINT tempo_tpl = (pmsh->flags2 & MMD_FLAG2_BMASK) + 1; + if (!tempo_tpl) tempo_tpl = 4; + deftempo *= tempo_tpl; + deftempo /= 4; + #ifdef MED_LOG + Log("newtempo: %3d bpm (bpm=%3d lpb=%2d)\n", deftempo, bswapBE16(pmsh->deftempo), (pmsh->flags2 & MMD_FLAG2_BMASK)+1); + #endif + } else + { + deftempo = _muldiv(deftempo, 5*715909, 2*474326); + #ifdef MED_LOG + Log("oldtempo: %3d bpm (bpm=%3d)\n", deftempo, bswapBE16(pmsh->deftempo)); + #endif + } + // Speed + m_nDefaultSpeed = pmsh->tempo2; + if (!m_nDefaultSpeed) m_nDefaultSpeed = 6; + if (deftempo < 0x21) deftempo = 0x21; + if (deftempo > 255) + { + while ((m_nDefaultSpeed > 3) && (deftempo > 260)) + { + deftempo = (deftempo * (m_nDefaultSpeed - 1)) / m_nDefaultSpeed; + m_nDefaultSpeed--; + } + if (deftempo > 255) deftempo = 255; + } + m_nDefaultTempo = deftempo; + // Reading Samples + for (UINT iSHdr=0; iSHdr<m_nSamples; iSHdr++) + { + MODINSTRUMENT *pins = &Ins[iSHdr+1]; + pins->nLoopStart = bswapBE16(pmsh->sample[iSHdr].rep) << 1; + pins->nLoopEnd = pins->nLoopStart + (bswapBE16(pmsh->sample[iSHdr].replen) << 1); + pins->nVolume = (pmsh->sample[iSHdr].svol << 2); + pins->nGlobalVol = 64; + if (pins->nVolume > 256) pins->nVolume = 256; + pins->RelativeTone = -12 * pmsh->sample[iSHdr].strans; + pins->nPan = 128; + if (pins->nLoopEnd) pins->uFlags |= CHN_LOOP; + } + // Common Flags + if (!(pmsh->flags & 0x20)) m_dwSongFlags |= SONG_FASTVOLSLIDES; + // Reading play sequence + if (version < '2') + { + UINT nbo = pmsh->songlen >> 8; + if (nbo >= MAX_ORDERS) nbo = MAX_ORDERS-1; + if (!nbo) nbo = 1; + memcpy(Order, pmsh->playseq, nbo); + playtransp = pmsh->playtransp; + } else + { + UINT nOrders, nSections; + UINT nTrks = bswapBE16(pmsh2->numtracks); + if ((nTrks >= 4) && (nTrks <= 32)) m_nChannels = nTrks; + DWORD playseqtable = bswapBE32(pmsh2->playseqtable); + UINT numplayseqs = bswapBE16(pmsh2->numpseqs); + if (!numplayseqs) numplayseqs = 1; + nOrders = 0; + nSections = bswapBE16(pmsh2->numsections); + DWORD sectiontable = bswapBE32(pmsh2->sectiontable); + if ((!nSections) || (!sectiontable) || (sectiontable >= dwMemLength-2)) nSections = 1; + nOrders = 0; + for (UINT iSection=0; iSection<nSections; iSection++) + { + UINT nplayseq = 0; + if ((sectiontable) && (sectiontable < dwMemLength-2)) + { + nplayseq = lpStream[sectiontable+1]; + sectiontable += 2; // WORDs + } else + { + nSections = 0; + } + UINT pseq = 0; + + if ((playseqtable) && (playseqtable + nplayseq*4 < dwMemLength)) + { + pseq = bswapBE32(((LPDWORD)(lpStream+playseqtable))[nplayseq]); + } + if ((pseq) && (pseq < dwMemLength - sizeof(MMD2PLAYSEQ))) + { + MMD2PLAYSEQ *pmps = (MMD2PLAYSEQ *)(lpStream + pseq); + if (!m_szNames[0][0]) memcpy(m_szNames[0], pmps->name, 31); + UINT n = bswapBE16(pmps->length); + if (pseq+n <= dwMemLength) + { + for (UINT i=0; i<n; i++) + { + UINT seqval = pmps->seq[i] >> 8; + if ((seqval < wNumBlocks) && (nOrders < MAX_ORDERS-1)) + { + Order[nOrders++] = seqval; + } + } + } + } + } + playtransp = pmsh2->playtransp; + while (nOrders < MAX_ORDERS) Order[nOrders++] = 0xFF; + } + // Reading Expansion structure + if (pmex) + { + // Channel Split + if ((m_nChannels == 4) && (pmsh->flags & 0x40)) + { + for (UINT i8ch=0; i8ch<4; i8ch++) + { + if (pmex->channelsplit[i8ch]) m_nChannels++; + } + } + // Song Comments + uint32_t annotxt = bswapBE32(pmex->annotxt); + uint32_t annolen = bswapBE32(pmex->annolen); + if ((annotxt) && (annolen) && (annotxt + annolen > annotxt) // overflow checks. + && (annotxt+annolen <= dwMemLength)) + { + m_lpszSongComments = new char[annolen+1]; + memcpy(m_lpszSongComments, lpStream+annotxt, annolen); + m_lpszSongComments[annolen] = 0; + } + // Song Name + uint32_t songname = bswapBE32(pmex->songname); + uint32_t songnamelen = bswapBE32(pmex->songnamelen); + if ((songname) && (songnamelen) && (songname+songnamelen > songname) + && (songname+songnamelen <= dwMemLength)) + { + if (songnamelen > 31) songnamelen = 31; + memcpy(m_szNames[0], lpStream+songname, songnamelen); + m_szNames[0][31] = '\0'; + } + // Sample Names + DWORD smpinfoex = bswapBE32(pmex->iinfo); + if (smpinfoex) + { + DWORD iinfoptr = bswapBE32(pmex->iinfo); + UINT ientries = bswapBE16(pmex->i_ext_entries); + UINT ientrysz = bswapBE16(pmex->i_ext_entrsz); + + if ((iinfoptr) && (ientrysz < 256) && + (ientries*ientrysz < dwMemLength) && + (iinfoptr < dwMemLength - (ientries*ientrysz))) + { + LPCSTR psznames = (LPCSTR)(lpStream + iinfoptr); + UINT maxnamelen = ientrysz; + // copy a max of 32 bytes. + if (maxnamelen > 32) maxnamelen = 32; + for (UINT i=0; i<ientries; i++) if (i < m_nSamples) + { + lstrcpyn(m_szNames[i+1], psznames + i*ientrysz, maxnamelen); + m_szNames[i+1][31] = '\0'; + } + } + } + // Track Names + DWORD trackinfo_ofs = bswapBE32(pmex->trackinfo_ofs); + if ((trackinfo_ofs) && (trackinfo_ofs + m_nChannels * 4 < dwMemLength)) + { + DWORD *ptrktags = (DWORD *)(lpStream + trackinfo_ofs); + for (UINT i=0; i<m_nChannels; i++) + { + DWORD trknameofs = 0, trknamelen = 0; + DWORD trktagofs = bswapBE32(ptrktags[i]); + if (trktagofs) + { + while (trktagofs+8 < dwMemLength) + { + DWORD ntag = bswapBE32(*(DWORD *)(lpStream + trktagofs)); + if (ntag == MMDTAG_END) break; + DWORD tagdata = bswapBE32(*(DWORD *)(lpStream + trktagofs + 4)); + switch(ntag) + { + case MMDTAG_TRK_NAMELEN: trknamelen = tagdata; break; + case MMDTAG_TRK_NAME: trknameofs = tagdata; break; + } + trktagofs += 8; + } + if (trknamelen > MAX_CHANNELNAME) trknamelen = MAX_CHANNELNAME; + if ((trknameofs) && (trknameofs + trknamelen < dwMemLength)) + { + lstrcpyn(ChnSettings[i].szName, (LPCSTR)(lpStream+trknameofs), MAX_CHANNELNAME); + ChnSettings[i].szName[MAX_CHANNELNAME-1] = '\0'; + } + } + } + } + } + // Reading samples + if (dwSmplArr > dwMemLength - 4*m_nSamples) return TRUE; + pdwTable = (LPDWORD)(lpStream + dwSmplArr); + for (UINT iSmp=0; iSmp<m_nSamples; iSmp++) if (pdwTable[iSmp]) + { + UINT dwPos = bswapBE32(pdwTable[iSmp]); + if ((dwPos >= dwMemLength) || (dwPos + sizeof(MMDSAMPLEHEADER) >= dwMemLength)) continue; + MMDSAMPLEHEADER *psdh = (MMDSAMPLEHEADER *)(lpStream + dwPos); + UINT len = bswapBE32(psdh->length); + #ifdef MED_LOG + Log("SampleData %d: stype=0x%02X len=%d\n", iSmp, bswapBE16(psdh->type), len); + #endif + if ((len > MAX_SAMPLE_LENGTH) || (dwPos + len + 6 > dwMemLength)) len = 0; + UINT flags = RS_PCM8S, stype = bswapBE16(psdh->type); + LPSTR psdata = (LPSTR)(lpStream + dwPos + 6); + if (stype & 0x80) + { + psdata += (stype & 0x20) ? 14 : 6; + } else + { + if (stype & 0x10) + { + Ins[iSmp+1].uFlags |= CHN_16BIT; + len /= 2; + flags = (stype & 0x20) ? RS_STPCM16M : RS_PCM16M; + } else + { + flags = (stype & 0x20) ? RS_STPCM8S : RS_PCM8S; + } + if (stype & 0x20) len /= 2; + } + Ins[iSmp+1].nLength = len; + ReadSample(&Ins[iSmp+1], flags, psdata, dwMemLength - dwPos - 6); + } + // Reading patterns (blocks) + if (wNumBlocks > MAX_PATTERNS) wNumBlocks = MAX_PATTERNS; + if ((!dwBlockArr) || (dwBlockArr > dwMemLength - 4*wNumBlocks)) return TRUE; + pdwTable = (LPDWORD)(lpStream + dwBlockArr); + playtransp += (version == '3') ? 24 : 48; + for (UINT iBlk=0; iBlk<wNumBlocks; iBlk++) + { + UINT dwPos = bswapBE32(pdwTable[iBlk]); + if ((!dwPos) || (dwPos >= dwMemLength) || (dwPos >= dwMemLength - 8)) continue; + UINT lines = 64, tracks = 4; + if (version == '0') + { + const MMD0BLOCK *pmb = (const MMD0BLOCK *)(lpStream + dwPos); + lines = pmb->lines + 1; + tracks = pmb->numtracks; + if (!tracks) tracks = m_nChannels; + if ((Patterns[iBlk] = AllocatePattern(lines, m_nChannels)) == NULL) continue; + PatternSize[iBlk] = lines; + MODCOMMAND *p = Patterns[iBlk]; + LPBYTE s = (LPBYTE)(lpStream + dwPos + 2); + UINT maxlen = tracks*lines*3; + if (maxlen + dwPos > dwMemLength - 2) break; + for (UINT y=0; y<lines; y++) + { + for (UINT x=0; x<tracks; x++, s+=3) if (x < m_nChannels) + { + BYTE note = s[0] & 0x3F; + BYTE instr = s[1] >> 4; + if (s[0] & 0x80) instr |= 0x10; + if (s[0] & 0x40) instr |= 0x20; + if ((note) && (note <= 132)) p->note = note + playtransp; + p->instr = instr; + p->command = s[1] & 0x0F; + p->param = s[2]; + // if (!iBlk) Log("%02X.%02X.%02X | ", s[0], s[1], s[2]); + MedConvert(p, pmsh); + p++; + } + //if (!iBlk) Log("\n"); + } + } else + { + MMD1BLOCK *pmb = (MMD1BLOCK *)(lpStream + dwPos); + #ifdef MED_LOG + Log("MMD1BLOCK: lines=%2d, tracks=%2d, offset=0x%04X\n", + bswapBE16(pmb->lines), bswapBE16(pmb->numtracks), bswapBE32(pmb->info)); + #endif + MMD1BLOCKINFO *pbi = NULL; + BYTE *pcmdext = NULL; + lines = (pmb->lines >> 8) + 1; + tracks = pmb->numtracks >> 8; + if (!tracks) tracks = m_nChannels; + if ((Patterns[iBlk] = AllocatePattern(lines, m_nChannels)) == NULL) continue; + PatternSize[iBlk] = (WORD)lines; + DWORD dwBlockInfo = bswapBE32(pmb->info); + if ((dwBlockInfo) && (dwBlockInfo < dwMemLength - sizeof(MMD1BLOCKINFO))) + { + pbi = (MMD1BLOCKINFO *)(lpStream + dwBlockInfo); + #ifdef MED_LOG + Log(" BLOCKINFO: blockname=0x%04X namelen=%d pagetable=0x%04X &cmdexttable=0x%04X\n", + bswapBE32(pbi->blockname), bswapBE32(pbi->blocknamelen), bswapBE32(pbi->pagetable), bswapBE32(pbi->cmdexttable)); + #endif + if ((pbi->blockname) && (pbi->blocknamelen)) + { + DWORD nameofs = bswapBE32(pbi->blockname); + UINT namelen = bswapBE32(pbi->blocknamelen); + if ((nameofs < dwMemLength) && (nameofs+namelen < dwMemLength)) + { + SetPatternName(iBlk, (LPCSTR)(lpStream+nameofs)); + } + } + if (pbi->cmdexttable) + { + DWORD cmdexttable = bswapBE32(pbi->cmdexttable); + if (cmdexttable < dwMemLength - 4) + { + cmdexttable = bswapBE32(*(DWORD *)(lpStream + cmdexttable)); + if ((cmdexttable) && (cmdexttable <= dwMemLength - lines*tracks)) + { + pcmdext = (BYTE *)(lpStream + cmdexttable); + } + } + } + } + MODCOMMAND *p = Patterns[iBlk]; + LPBYTE s = (LPBYTE)(lpStream + dwPos + 8); + UINT maxlen = tracks*lines*4; + if (maxlen + dwPos > dwMemLength - 8) break; + for (UINT y=0; y<lines; y++) + { + for (UINT x=0; x<tracks; x++, s+=4) if (x < m_nChannels) + { + BYTE note = s[0]; + if ((note) && (note <= 132)) + { + int rnote = note + playtransp; + if (rnote < 1) rnote = 1; + if (rnote > 120) rnote = 120; + p->note = (BYTE)rnote; + } + p->instr = s[1]; + p->command = s[2]; + p->param = s[3]; + if (pcmdext) p->vol = pcmdext[x]; + MedConvert(p, pmsh); + p++; + } + if (pcmdext) pcmdext += tracks; + } + } + } + // Setup channel pan positions + for (UINT iCh=0; iCh<m_nChannels; iCh++) + { + ChnSettings[iCh].nPan = (((iCh&3) == 1) || ((iCh&3) == 2)) ? 0xC0 : 0x40; + ChnSettings[iCh].nVolume = 64; + } + return TRUE; +} + + diff --git a/lib/libmodplug/src/load_mid.cpp b/lib/libmodplug/src/load_mid.cpp new file mode 100644 index 0000000000..1429af4baf --- /dev/null +++ b/lib/libmodplug/src/load_mid.cpp @@ -0,0 +1,2019 @@ +/* + + MikMod Sound System + + By Jake Stine of Divine Entertainment (1996-2000) + + Support: + If you find problems with this code, send mail to: + air@divent.org + + Distribution / Code rights: + Use this source code in any fashion you see fit. Giving me credit where + credit is due is optional, depending on your own levels of integrity and + honesty. + + ----------------------------------------- + Module: LOAD_MID + + MID module loader. + by Peter Grootswagers (2006) + <email:pgrootswagers@planet.nl> + + Portability: + All systems - all compilers (hopefully) +*/ + +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include <math.h> +#include <ctype.h> +#include <unistd.h> // for sleep + +#ifdef NEWMIKMOD +#include "mikmod.h" +#include "uniform.h" +#include "itshare.h" // for STMEM_PITCHSLIDE +typedef UBYTE BYTE; +typedef UWORD WORD; +#define MAX_POLYPHONY 12 // max notes in one midi channel +#define MAX_TRACKS 63 // max mod tracks +#define WHEELSHIFT 11 // how many bits the 13bit midi wheel value must shift right +#else +#include "stdafx.h" +#include "sndfile.h" +#define PAN_LEFT 0x30 +#define PAN_RIGHT 0xD0 +#define MAX_POLYPHONY 16 // max notes in one midi channel +#define MAX_TRACKS (MAX_BASECHANNELS-6) // max mod tracks +#define WHEELSHIFT 10 // how many bits the 13bit midi wheel value must shift right +#endif + +#include "load_pat.h" + +#define ROWSPERNOTE 16 +#define ENV_MMMID_SPEED "MMMID_SPEED" +#define ENV_MMMID_DEBUG "MMMID_DEBUG" +#define ENV_MMMID_VERBOSE "MMMID_VERBOSE" + +/************************************************************************** +**************************************************************************/ +#ifdef NEWMIKMOD +static char MID_Version[] = "Musical Instrument Digital Interface"; +#endif + +typedef enum { + none, + wheeldown, + wheelup, + fxbrk, + tmpo, + fxsync, + modwheel, + mainvol, + prog +} MIDEVENT_X_EFFECT; + +typedef struct _MIDEVENT +{ + struct _MIDEVENT *next; + ULONG tracktick; + BYTE flg; // 1 = note present + BYTE note; + BYTE volume; + BYTE smpno; + BYTE fx; + BYTE fxparam; +} MIDEVENT; + +typedef struct _MIDTRACK +{ + struct _MIDTRACK *next; + MIDEVENT *head; + MIDEVENT *tail; + MIDEVENT *workevent; // keeps track of events in track + int balance; // last balance on this track + ULONG vtracktick; // tracktick of last note event (on or off) + BYTE chan; + BYTE vpos; // 0xff is track is free for use, otherwise it's the note playing on this track + BYTE volume; // last note volume on this track + BYTE instr; // current instrument for this track +} MIDTRACK; + +#ifdef NEWMIKMOD + +#define MMFILE MMSTREAM +#define mmfseek(f,p,w) _mm_fseek(f,p,w) +#define mmftell(x) _mm_ftell(x) +#define mmreadUBYTE(f) _mm_read_UBYTE(f) +#define mmreadSBYTES(buf,sz,f) _mm_read_SBYTES(buf,sz,f) +#define mmreadUBYTES(buf,sz,f) _mm_read_UBYTES(buf,sz,f) + +#else + +#define MMSTREAM FILE +#define _mm_fseek(f,pos,whence) fseek(f,pos,whence) +#define _mm_read_UBYTES(buf,sz,f) fread(buf,sz,1,f) +#define _mm_read_SBYTES(buf,sz,f) fread(buf,sz,1,f) +#define DupStr(h,buf,sz) strdup(buf) +#define _mm_calloc(h,n,sz) calloc(n,sz) +#define _mm_recalloc(h,buf,sz,elsz) realloc(buf,sz) +#define _mm_free(h,p) free(p) + +typedef struct { + char *mm; + int sz; + int pos; +} MMFILE; + +static void mmfseek(MMFILE *mmfile, long p, int whence) +{ + switch(whence) { + case SEEK_SET: + mmfile->pos = p; + break; + case SEEK_CUR: + mmfile->pos += p; + break; + case SEEK_END: + mmfile->pos = mmfile->sz + p; + break; + } +} + +static long mmftell(MMFILE *mmfile) +{ + return mmfile->pos; +} + +static BYTE mmreadUBYTE(MMFILE *mmfile) +{ + BYTE b; + b = (BYTE)mmfile->mm[mmfile->pos]; + mmfile->pos++; + return b; +} + +static void mmreadUBYTES(BYTE *buf, long sz, MMFILE *mmfile) +{ + memcpy(buf, &mmfile->mm[mmfile->pos], sz); + mmfile->pos += sz; +} + +static void mmreadSBYTES(char *buf, long sz, MMFILE *mmfile) +{ + memcpy(buf, &mmfile->mm[mmfile->pos], sz); + mmfile->pos += sz; +} + +#endif + +/************************************************************************** +**************************************************************************/ + +typedef struct _MIDHANDLE +{ +#ifdef NEWMIKMOD + MM_ALLOC *allochandle; + MM_ALLOC *trackhandle; +#endif + MMFILE *mmf; + MIDTRACK *track; + MIDTRACK *tp; + ULONG tracktime; + const char *debug; + const char *verbose; + int speed; + int midispeed; + int midiformat; + int resolution; + int miditracks; + int divider; + int tempo; + int percussion; + long deltatime; +} MIDHANDLE; + +static void mid_dump_tracks(MIDHANDLE *h) +{ + MIDTRACK *tr; + MIDEVENT *e; + int t; + printf("tracktime = %ld\n", (long)(h->tracktime)); + printf("speed = %d\n", h->speed); + printf("midispeed = %d\n", h->midispeed); + printf("midiformat = %d\n", h->midiformat); + printf("resolution = %d\n", h->resolution); + printf("miditracks = %d\n", h->miditracks); + printf("divider = %d\n", h->divider); + printf("tempo = %d\n", h->tempo); + printf("percussion = %d\n", h->percussion); + printf("deltatime = %ld\n", h->deltatime); + t = 0; + for( tr=h->track; tr; tr = tr->next ) { + t++; + printf("TRACK %2d chan=%d note=0x%02x vol=%d pan=0x%02x instr=%d\n", t, tr->chan + 1, tr->vpos, tr->balance, tr->volume, tr->instr); + for( e=tr->head; e; e=e->next ) { + printf("%2d %6ld %s %3d %3d %3d ", + t, (long)(e->tracktick), + e->flg? "NOTE": "CTRL", e->note, e->volume, e->smpno); + switch( e->fx ) { + case fxbrk: printf("fxbrk\n");break; + case fxsync: printf("fxsync\n");break; + case prog: printf("prog %d\n", e->fxparam);break; + case mainvol: printf("mainvol %d\n", e->fxparam);break; + case modwheel: printf("modwheel %d\n", e->fxparam);break; + case wheeldown: printf("wheeldown %d\n", e->fxparam);break; + case wheelup: printf("wheelup %d\n", e->fxparam);break; + case tmpo: printf("tmpo %d\n", e->fxparam);break; + default: printf("\n");break; + } + } + } +} + +static void mid_message(const char *s1, const char *s2) +{ + char txt[256]; + if( strlen(s1) + strlen(s2) > 255 ) return; + sprintf(txt, s1, s2); +#ifdef NEWMIKMOD + _mmlog(txt); +#else + fprintf(stderr, "load_mid > %s\n", txt); +#endif +} + +static ULONG miditicks(MIDHANDLE *h, ULONG modtick) +{ + return modtick * h->divider / ROWSPERNOTE / h->speed; +} + +static ULONG modticks(MIDHANDLE *h, ULONG miditick) +{ + return miditick * ROWSPERNOTE * h->speed / h->divider; +} + +static void mid_adjust_for_optimal_tempo(MIDHANDLE *h, int maxtempo) +{ + // the tempo is adjusted so that the maximum tempo is 255 + // this way we have the biggest change that very short notes get played + // and we make sure the tempo doesn't become too large or too small + // if the piece in hand isn't so weird it changes tempo from 20 to 255, that is. + // tempo is only registered in first track (h->track) because it is a global event + MIDEVENT *e; + int d, t; + if( maxtempo < 1 ) return; + d = h->divider; + t = maxtempo; + h->divider = (t * d) / 255; + while( (h->midispeed = miditicks(h, h->speed)) < h->speed ) { + ++t; + h->divider = (t * d) / 255; + } + if( h->verbose && t > maxtempo ) + printf("Adjusted maximum tempo from %d to %d to get %d miditicks per patternrow\n", + maxtempo, 2 * maxtempo - t, h->midispeed); + if( h->track ) { + for( e=h->track->head; e; e=e->next ) { + if( e->fx == tmpo ) + e->fxparam = (255 * e->fxparam ) / t; + } + } +} + +// ===================================================================================== +static MIDEVENT *mid_new_event(MIDHANDLE *h) +// ===================================================================================== +{ + MIDEVENT *retval; + + retval = (MIDEVENT *)_mm_calloc(h->trackhandle, 1,sizeof(MIDEVENT)); + retval->next = NULL; + retval->tracktick = h->tracktime; + retval->flg = 0; + retval->note = 0; + retval->volume = 0; + retval->smpno = 0; + retval->fx = none; + retval->fxparam = 0; + return retval; +} + +// ===================================================================================== +static MIDTRACK *mid_new_track(MIDHANDLE *h, int mch, int pos) +// ===================================================================================== +{ + MIDTRACK *retval; + retval = (MIDTRACK *)_mm_calloc(h->trackhandle, 1,sizeof(MIDTRACK)); + retval->next = NULL; + retval->vpos = pos; + retval->instr = 1; + retval->chan = mch; + retval->head = NULL; + retval->tail = NULL; + retval->workevent = NULL; + retval->vtracktick = 0; + retval->volume = h->track? h->track->volume: 120; + retval->balance = 64; + return retval; +} + +static int mid_numtracks(MIDHANDLE *h) +{ + int n; + MIDTRACK *t; + n=0; + for( t = h->track; t; t=t->next ) + n++; + return n; +} + +// find out how many midichannel we have +static int mid_numchans(MIDHANDLE *h) +{ + int i,c,n; + MIDTRACK *t; + c = 0; + for( t = h->track; t; t=t->next ) + c |= (1<<t->chan); + n = 0; + for( i=0; i<16; i++ ) + if( c & (1<<i) ) n++; + return n; +} + +// find out which ordinal a midichannel has +static int mid_ordchan(MIDHANDLE *h, int mch) +{ + int i,c,n; + MIDTRACK *t; + c = 0; + for( t = h->track; t; t=t->next ) + c |= (1<<t->chan); + n = 0; + for( i=0; i<mch; i++ ) + if( c & (1<<i) ) n++; + return n; +} + +static void mid_rewind_tracks(MIDHANDLE *h) +{ + MIDTRACK *tr; + h->tracktime = 0; + for( tr = h->track; tr; tr = tr->next ) { + tr->vpos = 0xff; + tr->workevent = tr->head; + tr->vtracktick = 0; + } +} + +static void mid_update_track(MIDTRACK *tr) +{ + MIDEVENT *e; + e = tr->workevent; + if( e->flg ) { + if( e->volume ) tr->vpos = e->note; + else tr->vpos = 0xff; + tr->volume = e->volume; + tr->vtracktick = e->tracktick; + } + if( e->fx == prog ) tr->instr = e->fxparam; +} + +static void mid_sync_track(MIDTRACK *tr, ULONG tracktime) +{ + MIDEVENT *e; + e = tr->workevent; + if( e && e->tracktick > tracktime ) e = tr->head; // start again.... + for( ; e && e->tracktick <= tracktime; e=e->next ) { + tr->workevent = e; + mid_update_track(tr); + } +} + +// ===================================================================================== +static MIDTRACK *mid_find_track(MIDHANDLE *h, int mch, int pos) +// ===================================================================================== +{ + MIDTRACK *tr; + for( tr=h->track; tr; tr=tr->next ) { + mid_sync_track(tr, h->tracktime); + if( tr->chan == mch && tr->vpos == pos ) + return tr; + } + return NULL; +} + +// ===================================================================================== +static MIDTRACK *mid_locate_track(MIDHANDLE *h, int mch, int pos) +// ===================================================================================== +{ + MIDTRACK *tr, *prev, *trunused; + MIDEVENT *e; + int instrno = 1; + int polyphony; + int vol = 0, bal = 0; + int numtracks; + ULONG tmin; + prev = NULL; + trunused = NULL; + polyphony = 0; + numtracks = 0; + tmin = h->midispeed; // minimal distance between note events in track + // look up track with desired channel and pos (note) + for( tr=h->track; tr; tr=tr->next ) { + mid_sync_track(tr, h->tracktime); + if( tr->chan == mch ) { + if( tr->vpos == pos ) + return tr; + if( tr->vpos == 0xff ) { + // check if track with silence is quiet long enough + if( h->tracktime > tr->vtracktick + tmin ) trunused = tr; + } + else vol = tr->volume; + instrno = tr->instr; + bal = tr->balance; + polyphony++; + } + numtracks++; + prev = tr; + } + if( trunused ) { + trunused->vpos = pos; + return trunused; + } + if( polyphony > MAX_POLYPHONY || (polyphony > 0 && numtracks > MAX_TRACKS) ) { // do not use up too much channels + for( tr=h->track; tr; tr=tr->next ) { + if( tr->chan == mch ) { + e = tr->workevent; + if( h->tracktime > e->tracktick + tmin ) { + tmin = h->tracktime - e->tracktick; + trunused = tr; + } + } + } + if( trunused ) { + trunused->vpos = pos; + return trunused; + } + } + if( numtracks > MAX_TRACKS ) { // we can not allocate new tracks + tmin = 0; + for( tr=h->track; tr; tr=tr->next ) { + if( tr->chan == mch ) { + e = tr->workevent; + if( h->tracktime >= e->tracktick + tmin ) { + tmin = h->tracktime - e->tracktick; + trunused = tr; + } + } + } + if( trunused ) { + trunused->vpos = pos; + return trunused; + } + tmin = 0; + for( tr=h->track; tr; tr=tr->next ) { + e = tr->workevent; + if( h->tracktime >= e->tracktick + tmin ) { + tmin = h->tracktime - e->tracktick; + trunused = tr; + } + } + if( trunused ) { + trunused->vpos = pos; + trunused->chan = mch; + return trunused; + } + } + tr = mid_new_track(h, mch, pos); + tr->instr = instrno; + tr->volume = vol; + tr->balance = bal; + if( prev ) prev->next = tr; + else h->track = tr; + return tr; +} + +static void mid_add_event(MIDHANDLE *h, MIDTRACK *tp, MIDEVENT *e) +{ + MIDEVENT *ew, *ep; + ep = NULL; + ew = tp->workevent; + if( ew && ew->tracktick > e->tracktick ) ew = tp->head; // start again from the beginning... + for( ; ew && ew->tracktick <= e->tracktick; ew = ew->next ) { + ep = ew; + tp->workevent = ew; + mid_update_track(tp); + } + if( ep ) { + ep->next = e; + e->next = ew; + } + else { + e->next = tp->head; + tp->head = e; + } + if( !e->next ) + tp->tail = e; + tp->workevent = e; + mid_update_track(tp); +} + +static void mid_add_tempo_event(MIDHANDLE *h, int tempo) +{ + MIDEVENT *e; + e = mid_new_event(h); + e->flg = 0; + e->fx = tmpo; + e->fxparam = tempo; + mid_add_event(h, h->track, e); +} + +static void mid_add_partbreak(MIDHANDLE *h) +{ + MIDEVENT *e; + e = mid_new_event(h); + e->flg = 0; + e->fx = fxbrk; + mid_add_event(h, h->track, e); +} + +static void mid_add_noteoff(MIDHANDLE *h, MIDTRACK *tp) +{ + MIDEVENT *e; + e = mid_new_event(h); + e->flg = 1; + e->note = tp->vpos; + e->smpno = tp->instr; + mid_add_event(h, tp, e); +} + +static void mid_add_noteon(MIDHANDLE *h, MIDTRACK *tp, int n, int vol) +{ + MIDEVENT *e; + e = mid_new_event(h); + e->flg = 1; + e->note = n; + e->smpno = tp->instr; + e->volume = vol; + mid_add_event(h, tp, e); +} + +static BYTE modtremolo(int midimod) +{ + int m; + if( midimod == 0 ) return 0; + if( midimod > 63 ) { + m = (128 - midimod) / 4; + if( m==0 ) m = 1; + return m|0xf0; // find slide down + } + m = midimod / 4; + if( m==0 ) m = 1; + return (m<<4)|0x0f; // find slide up +} + +// ===================================================================================== +static void mid_mod_wheel(MIDHANDLE *h, int mch, int mod) +// ===================================================================================== +{ + MIDTRACK *tr; + MIDEVENT *e; + for( tr=h->track; tr; tr=tr->next ) { + if( tr->chan == mch ) { + mid_sync_track(tr, h->tracktime); + if( tr->vpos != 0xff ) { // only on tracks with notes on... + e = mid_new_event(h); + e->flg = 0; + e->fx = modwheel; + e->fxparam = modtremolo(mod); + mid_add_event(h, tr, e); + } + } + } +} + +// ===================================================================================== +static void mid_main_volume(MIDHANDLE *h, int mch, int vol) +// ===================================================================================== +{ + MIDTRACK *tr; + MIDEVENT *e; + for( tr=h->track; tr; tr=tr->next ) { + if( tr->chan == mch ) { + e = mid_new_event(h); + e->flg = 0; + e->fx = mainvol; + e->fxparam = vol; + mid_add_event(h, tr, e); + } + } +} + +// transform 0..63..127 to left..center..right in 2n+1 areas +static int modpan(int midipan, int n) +{ + int npan, area, x; + x = 2 * n + 1; + area = (midipan * x * (PAN_RIGHT - PAN_LEFT))>>7; + npan = (PAN_LEFT * x + area) / x; + return npan; +} + +// ===================================================================================== +static void mid_pan(MIDHANDLE *h, int mch, int pan) +// ===================================================================================== +{ + MIDTRACK *tr; + int hits; + hits = 0; + for( tr=h->track; tr; tr=tr->next ) { + if( tr->chan == mch ) { + hits++; + tr->balance = pan; + } + } + if( !hits ) { + tr = mid_locate_track(h, mch, 0xff); + tr->balance = pan; + } +} + +// ===================================================================================== +static void mid_add_program(MIDHANDLE *h, int mch, int pr) +// ===================================================================================== +{ + MIDTRACK *tr; + MIDEVENT *e; + int hits; + hits = 0; + for( tr=h->track; tr; tr=tr->next ) { + if( tr->chan == mch ) { + hits++; + e = mid_new_event(h); + e->flg = 0; + e->fx = prog; + e->fxparam = pat_gmtosmp(pr + 1); + mid_add_event(h, tr, e); + } + } + if( !hits ) { + tr = mid_locate_track(h, mch, 0xff); + e = mid_new_event(h); + e->flg = 0; + e->fx = prog; + e->fxparam = pat_gmtosmp(pr + 1); + mid_add_event(h, tr, e); + } +} + +// ===================================================================================== +static void mid_all_notes_off(MIDHANDLE *h, int mch) +// ===================================================================================== +{ + MIDTRACK *tr; + if( h->debug ) printf("%ld %d all notes off\n",(long)(h->tracktime), mch+1); + for( tr=h->track; tr; tr=tr->next ) { + if( tr->chan == mch || mch == -1 ) { + mid_sync_track(tr, h->tracktime); + if( tr->vpos != 0xff ) + mid_add_noteoff(h, tr); + } + } +} + +#ifndef NEWMIKMOD +static void mid_add_sync(MIDHANDLE *h, MIDTRACK *tp) +{ + MIDEVENT *e; + e = mid_new_event(h); + e->flg = 0; + e->fx = fxsync; + mid_add_event(h, tp, e); +} +#endif + +static BYTE mid_to_mod_wheel(unsigned int midwheel) +{ + unsigned int i; + if( midwheel == 0 ) return 0; + i = midwheel >> WHEELSHIFT; + return i+1; +} + +static void mid_add_wheel(MIDHANDLE *h, MIDTRACK *tp, int wheel) +{ + MIDEVENT *e; + e = mid_new_event(h); + e->flg = 0; + if( wheel < 0 ) { + e->fx = wheeldown; + e->fxparam = mid_to_mod_wheel(-wheel); + } + else { + e->fx = wheelup; + e->fxparam = mid_to_mod_wheel(wheel); + } + mid_add_event(h, tp, e); +} + +static void mid_add_pitchwheel(MIDHANDLE *h, int mch, int wheel) +{ + MIDTRACK *tr; + int hits; + hits = 0; + for( tr=h->track; tr; tr=tr->next ) { + if( tr->chan == mch ) { + hits++; + mid_sync_track(tr, h->tracktime); + if( tr->vpos != 0xff ) // only on tracks with notes on... + mid_add_wheel(h, tr, wheel); + } + } + if( !hits ) { // special case in midiformat 1 events in first track... + tr = mid_locate_track(h, mch, 0xff); + mid_add_wheel(h, tr, wheel); + } +} + +static long int mid_read_long(MIDHANDLE *h) +{ + BYTE buf[4]; + mmreadUBYTES(buf, 4, h->mmf); + return (buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|buf[3]; +} + +static short int mid_read_short(MIDHANDLE *h) +{ + BYTE buf[2]; + mmreadUBYTES(buf, 2, h->mmf); + return (buf[0]<<8)|buf[1]; +} + +static BYTE mid_read_byte(MIDHANDLE *h) +{ + return mmreadUBYTE(h->mmf); +} + +static int mid_read_delta(MIDHANDLE *h) +{ + BYTE bits; + int i, d; + d = 0; + for( i=0; i<4; ) { + bits = mid_read_byte(h); + i++; + d = (d<<7)|(bits&0x7f); + if( !(bits & 0x80) ) + break; + } + h->deltatime = d; + return i; +} + +// ===================================================================================== +#ifdef NEWMIKMOD +BOOL MID_Test(MMSTREAM *mmfile) +#else +BOOL CSoundFile::TestMID(const BYTE *lpStream, DWORD dwMemLength) +#endif +// ===================================================================================== +{ + char id[5]; + MIDHANDLE h; +#ifdef NEWMIKMOD + h.mmf = mmfile; +#else + MMFILE mm; + mm.mm = (char *)lpStream; + mm.sz = dwMemLength; + h.mmf = &mm; +#endif + mmfseek(h.mmf,0,SEEK_SET); + mmreadSBYTES(id, 4, h.mmf); + id[4] = '\0'; + return !strcmp(id,"MThd") && mid_read_long(&h) == 6; +} + +// ===================================================================================== +static MIDHANDLE *MID_Init(void) +{ + MIDHANDLE *retval; +#ifdef NEWMIKMOD + MM_ALLOC *allochandle; + + allochandle = _mmalloc_create("Load_MID", NULL); + retval = (MIDHANDLE *)_mm_calloc(allochandle, 1,sizeof(MIDHANDLE)); + if( !retval ) return NULL; + retval->allochandle = allochandle; + allochandle = _mmalloc_create("Load_ABC_tracks", NULL); + retval->trackhandle = allochandle; +#else + retval = (MIDHANDLE *)calloc(1,sizeof(MIDHANDLE)); + if( !retval ) return NULL; +#endif + retval->track = NULL; + retval->percussion = 0; + retval->debug = NULL; + return retval; +} + +#ifndef NEWMIKMOD +static void MID_CleanupTrack(MIDTRACK *tp) +{ + MIDEVENT *ep, *en; + if( tp ) { + for( ep=tp->head; ep; ep = en ) { + en=ep->next; + free(ep); + } + tp->head = NULL; + } +} +#endif + +// ===================================================================================== +static void MID_CleanupTracks(MIDHANDLE *handle) +// ===================================================================================== +{ +#ifdef NEWMIKMOD + if(handle && handle->trackhandle) { + _mmalloc_close(handle->trackhandle); + handle->trackhandle = 0; + } +#else + MIDTRACK *tp, *tn; + if(handle) { + for( tp=handle->track; tp; tp = tn ) { + tn=tp->next; + MID_CleanupTrack(tp); + } + handle->track = NULL; + } +#endif +} + +// ===================================================================================== +static void MID_Cleanup(MIDHANDLE *handle) +// ===================================================================================== +{ +#ifdef NEWMIKMOD + if(handle && handle->allochandle) { + MID_CleanupTracks(handle); + _mmalloc_close(handle->allochandle); + handle->allochandle = 0; + } +#else + if(handle) { + MID_CleanupTracks(handle); + free(handle); + handle = 0; + } +#endif +} + +static int mid_is_global_event(MIDEVENT *e) +{ + return (e->fx == tmpo || e->fx == fxbrk); +} + +static MIDEVENT *mid_next_global(MIDEVENT *e) +{ + for( ; e && !mid_is_global_event(e); e=e->next ) ; + return e; +} + +static MIDEVENT *mid_next_fx(MIDEVENT *e) +{ + for( ; e && e->fx == none; e=e->next ) ; + return e; +} + +static int mid_is_note_event(MIDEVENT *e) +{ +#ifdef LOOPED_NOTES_OFF + return (e->flg == 0); +#else + if( e->flg == 0 ) return 0; + if( e->volume ) return 1; + return pat_smplooped(e->smpno); // let non looping samples die out... +#endif +} + +static MIDEVENT *mid_next_note(MIDEVENT *e) +{ + for( ; e && !mid_is_note_event(e); e=e->next ) ; + return e; +} + +// ===================================================================================== +#ifdef NEWMIKMOD +static void MID_ReadPatterns(UNIMOD *of, MIDHANDLE *h, int numpat) +// ===================================================================================== +{ + int pat,row,i,ch,trkset; + BYTE n,ins,vol; + MIDTRACK *t; + MIDEVENT *e, *en, *ef, *el; + ULONG tt1, tt2; + UNITRK_EFFECT eff; + + // initialize start points of event list in tracks + for( t = h->track; t; t = t->next ) t->workevent = t->head; + for( pat = 0; pat < numpat; pat++ ) { + utrk_reset(of->ut); + for( row = 0; row < 64; row++ ) { + tt1 = miditicks(h, (pat * 64 + row ) * h->speed); + tt2 = tt1 + h->midispeed; + for( e=mid_next_global(h->track->workevent); e && e->tracktick < tt2; e=mid_next_global(e->next) ) { + if( e && e->tracktick >= tt1 ) { // we have a controller event in this row + switch( e->fx ) { + case tmpo: + eff.effect = UNI_GLOB_TEMPO; + eff.param.u = e->fxparam; + eff.framedly = UFD_RUNONCE; + utrk_write_global(of->ut, &eff, PTMEM_TEMPO); + break; + case fxbrk: + eff.effect = UNI_GLOB_PATBREAK; + eff.param.u = 0; + eff.framedly = UFD_RUNONCE; + utrk_write_global(of->ut, &eff, UNIMEM_NONE); + break; + } + } + } + ch = 0; + for( t = h->track; t; t = t->next ) { + trkset = 0; + e = NULL; + for( el=mid_next_fx(t->workevent); el && el->tracktick < tt2; el=mid_next_fx(el->next) ) { + if( el && el->tracktick >= tt1 ) { + switch( el->fx ) { + case modwheel: + case wheelup: + case wheeldown: + e = el; + default: + break; + } + } + } + if( e ) { // we have a controller event in this row + switch( e->fx ) { + case modwheel: + if( !trkset ) { + utrk_settrack(of->ut, ch); + trkset = 1; + } + eff.effect = UNI_VOLSLIDE; + eff.framedly = UFD_RUNONCE; + if( (e->fxparam & 0x0f) == 0x0f ) + eff.param.s = (e->fxparam >> 3)&0x1f; + else + eff.param.s = -((e->fxparam & 0x0f)*2); + utrk_write_local(of->ut, &eff, STMEM_VOLSLIDE); + break; + case wheelup: + if( !trkset ) { + utrk_settrack(of->ut, ch); + trkset = 1; + } + eff.effect = UNI_PITCHSLIDE; + eff.framedly = UFD_RUNONCE; + eff.param.s = e->fxparam; + utrk_write_local(of->ut, &eff, STMEM_PITCHSLIDE); + break; + case wheeldown: + if( !trkset ) { + utrk_settrack(of->ut, ch); + trkset = 1; + } + eff.effect = UNI_PITCHSLIDE; + eff.framedly = UFD_RUNONCE; + eff.param.s = -(int)(e->fxparam); + utrk_write_local(of->ut, &eff, STMEM_PITCHSLIDE); + break; + } + } + for( e=mid_next_note(t->workevent); e && e->tracktick < tt1; e=mid_next_note(e->next) ) + t->workevent = e; + i = 0; + ef = NULL; + en = e; + el = e; + for( ; e && e->tracktick < tt2; e=mid_next_note(e->next) ) { // we have a note event in this row + t->workevent = e; + i++; + if( e->volume ) { + if( !ef ) ef = e; + el = e; + } + } + if( i ) { + if( !trkset ) { + utrk_settrack(of->ut, ch); + trkset = 1; + } + if( i == 1 || ef == el || !ef ) { // only one event in this row + if( ef ) e = ef; + else e = en; + el = t->workevent; + n = pat_modnote(e->note); + ins = e->smpno; + eff.framedly = modticks(h, e->tracktick - tt1); + eff.param.u = 0; + eff.param.byte_a = n; + eff.param.byte_b = ins; + vol = e->volume; + if( vol == 0 ) { + eff.effect = UNI_NOTEKILL; + utrk_write_local(of->ut, &eff, UNIMEM_NONE); + } + else { + if( el->volume == 0 ) { + eff.framedly = modticks(h, el->tracktick - tt1); + eff.effect = UNI_NOTEKILL; + utrk_write_local(of->ut, &eff, UNIMEM_NONE); + } + else { + if( eff.framedly ) { + eff.effect = UNI_NOTEDELAY; + utrk_write_local(of->ut, &eff, UNIMEM_NONE); + } + } + } + utrk_write_inst(of->ut, ins); + utrk_write_note(of->ut, n); // <- normal note + pt_write_effect(of->ut, 0xc, vol); + } + else { + // two notes in one row, use FINEPITCHSLIDE runonce effect + // start first note on first tick and framedly runonce on seconds note tick + // use volume and instrument of last note + n = pat_modnote(ef->note); + i = pat_modnote(el->note); + ins = el->smpno; + vol = el->volume; + eff.effect = UNI_PITCHSLIDE; + eff.framedly = modticks(h, el->tracktick - tt1)|UFD_RUNONCE; + eff.param.s = ((i > n)?i-n:n-i); + utrk_write_inst(of->ut, ins); + utrk_write_note(of->ut, n); // <- normal note + pt_write_effect(of->ut, 0xc, vol); + utrk_write_local(of->ut, &eff, (i > n)? PTMEM_PITCHSLIDEUP: PTMEM_PITCHSLIDEDN); + } + } + ch++; + } + utrk_newline(of->ut); + } + if(!utrk_dup_pattern(of->ut,of)) return; + } +} + +#else + +static int MID_ReadPatterns(MODCOMMAND *pattern[], WORD psize[], MIDHANDLE *h, int numpat, int channels) +// ===================================================================================== +{ + int pat,row,i,ch; + BYTE n,ins,vol; + MIDTRACK *t; + MIDEVENT *e, *en, *ef, *el; + ULONG tt1, tt2; + MODCOMMAND *m; + int patbrk, tempo; + if( numpat > MAX_PATTERNS ) numpat = MAX_PATTERNS; + + // initialize start points of event list in tracks + for( t = h->track; t; t = t->next ) t->workevent = t->head; + for( pat = 0; pat < numpat; pat++ ) { + pattern[pat] = CSoundFile::AllocatePattern(64, channels); + if( !pattern[pat] ) return 0; + psize[pat] = 64; + for( row = 0; row < 64; row++ ) { + tt1 = miditicks(h, (pat * 64 + row ) * h->speed); + tt2 = tt1 + h->midispeed; + ch = 0; + tempo = 0; + patbrk = 0; + for( e=mid_next_global(h->track->workevent); e && e->tracktick < tt2; e=mid_next_global(e->next) ) { + if( e && e->tracktick >= tt1 ) { // we have a controller event in this row + switch( e->fx ) { + case tmpo: + tempo = e->fxparam; + break; + case fxbrk: + patbrk = 1; + break; + } + } + } + for( t = h->track; t; t = t->next ) { + m = &pattern[pat][row * channels + ch]; + m->param = 0; + m->command = CMD_NONE; + for( e=mid_next_fx(t->workevent); e && e->tracktick < tt2; e=mid_next_fx(e->next) ) { + if( e && e->tracktick >= tt1 ) { // we have a controller event in this row + switch( e->fx ) { + case modwheel: + m->param = e->fxparam; + m->command = CMD_VOLUMESLIDE; + break; + case wheelup: + m->param = e->fxparam|0x10; + m->command = CMD_XFINEPORTAUPDOWN; + break; + case wheeldown: + m->param = e->fxparam|0x20; + m->command = CMD_XFINEPORTAUPDOWN; + break; + } + } + } + for( e=mid_next_note(t->workevent); e && e->tracktick < tt1; e=mid_next_note(e->next) ) + t->workevent = e; + i = 0; + ef = NULL; + en = e; + el = e; + for( ; e && e->tracktick < tt2; e=mid_next_note(e->next) ) { // we have a note event in this row + t->workevent = e; + i++; + if( e->volume ) { + if( !ef ) ef = e; + el = e; + } + } + if( i ) { + if( i == 1 || ef == el || !ef ) { // only one event in this row or a note on with some note off + if( ef ) e = ef; + else e = en; + el = t->workevent; + n = pat_modnote(e->note); + ins = e->smpno; + if( e->volume == 0 ) { + m->param = modticks(h, e->tracktick - tt1); + if( m->param ) { // note cut + m->command = CMD_S3MCMDEX; + m->param |= 0xC0; + } + else { + m->param = 0; + m->command = CMD_KEYOFF; + } + vol = 0; + } + else { + vol = e->volume/2; + if( el->volume == 0 ) { + m->param = modticks(h, el->tracktick - tt1); + if( m->param ) { // note cut + m->command = CMD_S3MCMDEX; + m->param |= 0xC0; + } + } + else { + m->param = modticks(h, e->tracktick - tt1); + if( m->param ) { // note delay + m->command = CMD_S3MCMDEX; + m->param |= 0xD0; + } + } + } + m->instr = ins; + m->note = n; // <- normal note + m->volcmd = VOLCMD_VOLUME; + m->vol = vol; + } + else { + // two notes in one row, use FINEPITCHSLIDE runonce effect + // start first note on first tick and framedly runonce on seconds note tick + // use volume and instrument of last note + n = pat_modnote(ef->note); + i = pat_modnote(el->note); + ins = el->smpno; + vol = el->volume/2; + if( vol > 64 ) vol = 64; + m->instr = ins; + m->note = n; // <- normal note + m->volcmd = VOLCMD_VOLUME; + m->vol = vol; + m->param = ((i > n)?i-n:n-i); + if( m->param < 16 ) { + if( m->param ) { + m->command = CMD_XFINEPORTAUPDOWN; + m->param |= (i > n)? 0x10: 0x20; + } + else { // retrigger same note... + m->command = CMD_RETRIG; + m->param = modticks(h, el->tracktick - tt1); + } + } + else + m->command = (i > n)? CMD_PORTAMENTOUP: CMD_PORTAMENTODOWN; + } + } + if( m->param == 0 && m->command == CMD_NONE ) { + if( tempo ) { + m->command = CMD_TEMPO; + m->param = tempo; + tempo = 0; + } + else { + if( patbrk ) { + m->command = CMD_PATTERNBREAK; + patbrk = 0; + } + } + } + ch++; + } + if( tempo || patbrk ) return 1; + } + } + return 0; +} + +#endif + +static ULONG mid_next_tracktick(MIDEVENT *e) +{ + MIDEVENT *en; + en = e->next; + if( en ) return en->tracktick; + return 0x7fffffff; // practically indefinite +} + +// cut off alle events that follow the given event +static void mid_stripoff(MIDTRACK *tp, MIDEVENT *e) +{ +#ifndef NEWMIKMOD + MIDEVENT *ep, *en; + for( ep=e->next; ep; ep = en ) { + en=ep->next; + free(ep); + } +#endif + e->next = NULL; + tp->tail = e; + tp->workevent = tp->head; + mid_sync_track(tp, e->tracktick); +} + +static void mid_notes_to_percussion(MIDTRACK *tp, ULONG adjust, ULONG tmin) +{ + MIDEVENT *e, *lno = 0; + int n = 0,v; + ULONG ton, toff = 0, tnext; + v = 0x7f; // as loud as it gets + ton = 0; + for( e=tp->head; e; e=e->next ) { + if( e->tracktick < adjust ) e->tracktick = 0; + else e->tracktick -= adjust; + if( e->flg == 1 ) { + if( e->volume > 0 ) { + n = e->note; + e->smpno = pat_gmtosmp(pat_gm_drumnr(n)); + e->note = pat_gm_drumnote(n); + e->volume = (v * e->volume) / 128; + if( v && !e->volume ) e->volume = 1; + ton = e->tracktick; + } + else { + toff = ton + tmin; + if( toff > e->tracktick ) { + tnext = mid_next_tracktick(e); + if( toff + tmin < tnext ) e->tracktick = toff; + else { + if( toff < tnext ) e->tracktick = toff - 1; + else e->tracktick = tnext - 1; + } + } + toff = e->tracktick; + lno = e; + } + } + else { + if( e->fx == mainvol ) { + v = e->fxparam; + if( !v && ton > toff ) { + e->flg = 1; + e->volume = 0; + e->note = pat_gm_drumnote(n); + toff = e->tracktick; + lno = e; + } + } + } + } + if( ton > toff ) { + char info[32]; + sprintf(info,"%ld > %ld note %d", (long)ton, (long)toff, n); + mid_message("drum track ends with note on (%s)", info); + } + if( lno && lno->next ) mid_stripoff(tp, lno); +} + +static void mid_prog_to_notes(MIDTRACK *tp, ULONG adjust, ULONG tmin) +{ + MIDEVENT *e, *lno = 0; + int i = 0, n = 0, v = 0x7f; + ULONG ton, toff = 0, tnext; + ton = 0; + for( e=tp->head; e; e=e->next ) { + if( e->tracktick < adjust ) e->tracktick = 0; + else e->tracktick -= adjust; + if( e->flg == 1 ) { + if( !i ) i = pat_gmtosmp(1); // happens in eternal2.mid + e->smpno = i; + n = e->note; + if( e->volume > 0 ) { + e->volume = (v * e->volume) / 128; + if( v && !e->volume ) e->volume = 1; + ton = e->tracktick; + } + else { + toff = ton + tmin; + if( toff > e->tracktick ) { + tnext = mid_next_tracktick(e); + if( toff + tmin < tnext ) e->tracktick = toff; + else { + if( toff < tnext ) e->tracktick = toff - 1; + else e->tracktick = tnext - 1; + } + } + toff = e->tracktick; + lno = e; + } + } + else { + if( e->fx == prog ) i = e->fxparam; + if( e->fx == mainvol ) { + v = e->fxparam; + if( !v && ton > toff ) { + e->flg = 1; + e->volume = 0; + e->note = n; + toff = e->tracktick; + lno = e; + } + } + } + } + if( ton > toff ) { + char info[40]; + sprintf(info,"channel %d, %ld > %ld note %d", tp->chan + 1, (long)ton, (long)toff, n); + mid_message("melody track ends with note on (%s)", info); + } + if( lno && lno->next ) mid_stripoff(tp, lno); +} + +static int midiword(BYTE *b) +{ + int i; + i = (b[0]&0x7f)|((b[1]&0x7f)<<7); + return i; +} + +static int midishort(BYTE *b) +{ + return midiword(b) - 0x2000; +} + +ULONG mid_first_noteonevent_tick(MIDEVENT *e) +{ + while( e && (e->flg == 0 || e->volume == 0) ) e=e->next; + if( !e ) return 0x7fffffff; + return e->tracktick; +} + +// ===================================================================================== +#ifdef NEWMIKMOD +BOOL MID_Load(MIDHANDLE *h, UNIMOD *of, MMSTREAM *mmfile) +#else +BOOL CSoundFile::ReadMID(const BYTE *lpStream, DWORD dwMemLength) +#endif +{ + static int avoid_reentry = 0; +#ifdef NEWMIKMOD +#define m_nDefaultTempo of->inittempo +#else + MIDHANDLE *h; + MMFILE mm; +#endif + int ch, dmulti, maxtempo, panlow, panhigh, numchans, numtracks; + MIDTRACK *ttp; + uint32_t t, numpats; + char buf[256]; + long miditracklen; + BYTE runningstatus; + BYTE cmd; + BYTE midibyte[2]; + long metalen, delta; + BYTE *p; + while( avoid_reentry ) sleep(1); + avoid_reentry = 1; +#ifdef NEWMIKMOD + h->mmf = mmfile; +#else + if( !TestMID(lpStream, dwMemLength) ) { + avoid_reentry = 0; + return FALSE; + } + h = MID_Init(); + if( !h ) { + avoid_reentry = 0; + return FALSE; + } + h->mmf = &mm; + mm.mm = (char *)lpStream; + mm.sz = dwMemLength; + mm.pos = 0; +#endif + h->debug = getenv(ENV_MMMID_DEBUG); + h->verbose = getenv(ENV_MMMID_VERBOSE); + pat_resetsmp(); + pat_init_patnames(); + mmfseek(h->mmf,8,SEEK_SET); + h->midiformat = mid_read_short(h); + h->miditracks = mid_read_short(h); + h->resolution = mid_read_short(h); + // at this point the h->mmf is positioned at first miditrack + if( h->midiformat == 0 ) h->miditracks = 1; + if( h->resolution & 0x8000 ) + h->divider = ((h->resolution & 0x7f00)>>8)*(h->resolution & 0xff); + else + h->divider = h->resolution; + h->divider <<= 2; // ticks per quartnote ==> ticks per note + h->tempo = 122; + m_nDefaultTempo = 0; + h->tracktime = 0; + h->speed = 6; + p = (BYTE *)getenv(ENV_MMMID_SPEED); + if( p && isdigit(*p) && p[0] != '0' && p[1] == '\0' ) { + // transform speed + t = *p - '0'; + h->speed *= t; + h->divider *= t; + h->speed /= 6; + h->divider /= 6; + } + // calculate optimal delta multiplier dmulti keeping tempo adjustments + // from 10 to 255 in mind (hoping there will be no midi's with tempo's + // lower than 10, that is sooo sick...) + // this is necessary for the tracks to patterns routine + dmulti = 1; + maxtempo = h->divider; + while( (h->midispeed = miditicks(h, h->speed)) * 10 < 255 * h->speed ) { + ++dmulti; + h->divider = maxtempo * dmulti; + } + h->tp = NULL; + memset(buf,0,sizeof(buf)); +#ifdef NEWMIKMOD + of->songname = NULL; +#else + strcpy(m_szNames[0], ""); +#endif + maxtempo = 0; + panlow = 64; + panhigh = 64; + if( h->verbose ) { + printf("Scanning MIDI with format: %d resolution: %d tracks: %d\n", + h->midiformat, + h->resolution, + h->miditracks); + } + if( h->verbose && dmulti > 1 ) { + printf("Multiplying resolution and deltatimes by %d to get %d miditicks per patternrow\n", + dmulti, h->midispeed); + } + for( t=0; t<(uint32_t)h->miditracks; t++ ) { + if( h->verbose ) printf("Parsing track %d\n", t+1); + mmreadSBYTES(buf,4,h->mmf); + buf[4] = '\0'; + if( strcmp(buf,"MTrk") ) { + mid_message("invalid track-chunk '%s' is not 'MTrk'",buf); + avoid_reentry = 0; + return FALSE; + } + miditracklen = mid_read_long(h); + runningstatus = 0; + if( t && h->midiformat == 1 ) mid_rewind_tracks(h); // tracks sound simultaneously + while( miditracklen > 0 ) { + miditracklen -= mid_read_delta(h); + midibyte[0] = mid_read_byte(h); + miditracklen--; + if( midibyte[0] & 0x80 ) { + runningstatus = midibyte[0]; + switch( runningstatus ) { + case 0xf1: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + case 0xf8: + case 0xf9: + case 0xfa: + case 0xfb: + case 0xfc: + case 0xfd: + case 0xfe: + break; + default: + midibyte[0] = mid_read_byte(h); + miditracklen--; + break; + } + } + h->tracktime += dmulti * h->deltatime; + ch = runningstatus & 0x0f; + cmd = runningstatus & 0xf0; + switch( cmd ) { + case 0x80: // note off + midibyte[1] = mid_read_byte(h); + miditracklen--; + ttp = mid_find_track(h, ch, midibyte[0]); + if( ttp ) mid_add_noteoff(h, ttp); + if( h->debug ) + printf("%2d %08ld Note off: ch %d 0x%02x 0x%02x\n", + t, (long)(h->tracktime), + ch + 1, midibyte[0], midibyte[1]); + break; + case 0x90: // note on + midibyte[1] = mid_read_byte(h); + miditracklen--; + if( midibyte[1] ) { + ttp = mid_locate_track(h, ch, midibyte[0]); + mid_add_noteon(h, ttp, midibyte[0], midibyte[1]); + if( h->debug ) + printf("%2d %08ld Note on: ch %d 0x%02x 0x%02x\n", + t, (long)(h->tracktime), + ch + 1, midibyte[0], midibyte[1]); + } + else { + ttp = mid_find_track(h, ch, midibyte[0]); + if( ttp ) mid_add_noteoff(h, ttp); + if( h->debug ) + printf("%2d %08ld note off: ch %d 0x%02x\n", + t, (long)(h->tracktime), + ch + 1, midibyte[0]); + } + break; + case 0xa0: // polyphonic key pressure + midibyte[1] = mid_read_byte(h); + miditracklen--; + if( h->debug ) + printf("%2d %08ld polyphonic key pressure: ch %d 0x%02x 0x%02x\n", t, (long)(h->tracktime), ch + 1, midibyte[0], midibyte[1]); + break; + case 0xb0: // control change + midibyte[1] = mid_read_byte(h); + miditracklen--; + switch(midibyte[0]) { + case 0x01: // mod wheel + mid_mod_wheel(h, ch, midibyte[1]); + break; + case 0x07: // main volume + mid_main_volume(h, ch, midibyte[1]); + break; + case 0x0a: // pan + if( midibyte[1] < panlow ) panlow = midibyte[1]; + if( midibyte[1] > panhigh ) panhigh = midibyte[1]; + mid_pan(h, ch, midibyte[1]); + break; + case 0x0b: // expression + break; + case 0x7b: + if( midibyte[1] == 0x00 ) // all notes off + mid_all_notes_off(h, ch); + break; + default: + break; + } + if( h->debug ) + printf("%2d %08ld control change: ch %d 0x%02x 0x%02x\n", + t, (long)(h->tracktime), ch + 1, midibyte[0], midibyte[1]); + break; + case 0xc0: // program change + mid_add_program(h, ch, midibyte[0]); + if( h->debug ) + printf("%2d %08ld program change: ch %d %d\n", + t, (long)(h->tracktime), ch + 1, midibyte[0]); + break; + case 0xd0: // channel pressure + if( h->debug ) + printf("%2d %08ld channel pressure: ch %d 0x%02x\n", t, (long)(h->tracktime), ch + 1, midibyte[0]); + break; + case 0xe0: // pitch wheel change + midibyte[1] = mid_read_byte(h); + miditracklen--; + if( h->debug ) + printf("%2d %08ld pitch wheel change: ch %d %d\n", + t, (long)(h->tracktime), ch + 1, midishort(midibyte)); + mid_add_pitchwheel(h, ch, midishort(midibyte)); + break; + case 0xf0: // system & realtime + switch( runningstatus ) { + case 0xf0: // sysex + if( h->debug ) printf("%2d %08ld sysex: 0x%02x", + t, (long)(h->tracktime), midibyte[0]); + while( midibyte[0] != 0xf7 ) { + midibyte[0] = mid_read_byte(h); + miditracklen--; + if( h->debug ) printf(" %02X", midibyte[0]); + } + if( h->debug ) printf("\n"); + break; + case 0xf2: // song position pointer + midibyte[1] = mid_read_byte(h); + miditracklen--; + if( h->debug ) + printf("%2d %08ld song position pointer: %d", + t, (long)(h->tracktime), midishort(midibyte)); + break; + case 0xf7: + delta = h->deltatime; + miditracklen -= mid_read_delta(h); + metalen = h->deltatime; + if( h->debug ) + printf("%2d %08ld sysex continued: %ld", + t, (long)(h->tracktime), metalen); + while( metalen > 0 ) { + midibyte[1] = mid_read_byte(h); + metalen--; + miditracklen--; + if( h->debug ) printf(" %02X", midibyte[1]); + } + h->deltatime = delta; + break; + case 0xff: // meta event + delta = h->deltatime; + miditracklen -= mid_read_delta(h); + metalen = h->deltatime; + if( metalen > 31 ) metalen = 31; + if( metalen ) { + mmreadSBYTES(buf, metalen, h->mmf); + miditracklen -= metalen; + } + buf[metalen] = '\0'; + metalen = h->deltatime - metalen; + while( metalen > 0 ) { + midibyte[1] = mid_read_byte(h); + metalen--; + miditracklen--; + } + h->deltatime = delta; + switch( midibyte[0] ) { + case 0x03: // type: track name + if( h->debug ) + printf("%2d %08ld META trackname:%s\n", t, (long)(h->tracktime), buf); +#ifdef NEWMIKMOD + if( !of->songname ) + of->songname = DupStr(of->allochandle, buf, strlen(buf)); +#else + if( m_szNames[0][0] == '\0' ) + strcpy(m_szNames[0], buf); +#endif + break; + case 0x51: // type: tempo + p=(BYTE *)buf; + delta = (p[0]<<16)|(p[1]<<8)|p[2]; + if( delta ) + h->tempo = 60000000 / delta; + if( h->debug ) printf("%2d %08ld META tempo:%d\n", t, (long)(h->tracktime), h->tempo); + if( m_nDefaultTempo == 0 ) m_nDefaultTempo = h->tempo; + else { + ttp = h->track; + if( !ttp ) ttp = mid_locate_track(h, 0, 0xff); + mid_add_tempo_event(h,h->tempo); + } + if( h->tempo > maxtempo ) maxtempo = h->tempo; + break; + case 0x2f: // type: end of track + if( h->debug ) printf("%2d %08ld META end of track\n", t, (long)(h->tracktime)); + if( miditracklen > 0 ) { + sprintf(buf, "%ld", miditracklen); + mid_message("Meta event not at end of track, %s bytes left in track", buf); + miditracklen = 0; + } + break; + default: + if( h->debug ) printf("%2d %08ld META type 0x%02x\n", t, (long)(h->tracktime), midibyte[0]); + break; + } + break; + default: + if( h->debug ) printf("%2d %08ld System type 0x%02x\n", t, (long)(h->tracktime), midibyte[0]); + break; + } + break; + default: // no running status, just skip it... + if( h->debug ) printf("%2d %08ld unknown runningstatus: 0x%02x skipped:0x%02x\n", t, (long)(h->tracktime), runningstatus, midibyte[0]); + break; + } + if( miditracklen < 1 && (runningstatus != 0xff || midibyte[0] != 0x2f) ) { + delta = mmftell(h->mmf); + mmreadSBYTES(buf,4,h->mmf); + buf[4] = '\0'; + if( strcmp(buf,"MTrk") ) { + miditracklen = 0x7fffffff; + mid_message("Meta event not at end of track, %s bytes left in track", "superfluous"); + } + else + mid_message("Meta event not at end of track, %s bytes left in track", "no"); + mmfseek(h->mmf,delta,SEEK_SET); + } + } + } + if( h->verbose ) printf("Determining percussion channel\n"); + // get the lowest event time and the used channels + delta = 0x7fffffff; + metalen = 0; // use as bit bucket for used channels + for( ttp=h->track; ttp; ttp=ttp->next ) { + metalen |= (1<<ttp->chan); + if( ttp->head ) { + ULONG tt; + tt = mid_first_noteonevent_tick(ttp->head); + if( tt < (ULONG)delta ) + delta = tt; + } + } + if( metalen & 0x03ff ) { + if( (metalen & 0x0f00) == 0x0400 ) + h->percussion = 10; // buggy sng2mid uses channel 10 + else + h->percussion = 9; + } + else h->percussion = 15; + if( h->verbose ) + printf("Percussion channel is %d\nStripping off silences and other optimalisations\n", h->percussion + 1); + // last but not least shut off all pending events, transform drumnotes when appropriate + // strip off silences at begin and end and get the greatest tracktime + h->tracktime = 0; + metalen = h->midispeed; + for( ttp=h->track; ttp; ttp=ttp->next ) { + if( ttp->chan == h->percussion ) + mid_notes_to_percussion(ttp, delta, metalen); + else + mid_prog_to_notes(ttp, delta, metalen); + if( ttp->tail && ttp->tail->tracktick > h->tracktime ) + h->tracktime = ttp->tail->tracktick; + } + h->tracktime += h->divider >> 2; // add one quartnote to the song for silence + mid_add_partbreak(h); + if( h->debug ) + mid_dump_tracks(h); + numchans = mid_numchans(h); + if( panlow > 48 || panhigh < 80 ) { + for( ttp=h->track; ttp; ttp=ttp->next ) { + ttp->balance = ((0x40*numchans+0x80*mid_ordchan(h, ttp->chan))/numchans)&0x7f; + } + } + // set module variables + numtracks = mid_numtracks(h); + if( m_nDefaultTempo == 0 ) m_nDefaultTempo = h->tempo; + if( maxtempo == 0 ) maxtempo = h->tempo; + if( maxtempo != 255 ) { + if( h->verbose ) printf("Adjusting tempo %d to 255\n", maxtempo); + mid_adjust_for_optimal_tempo(h, maxtempo); + } + if( maxtempo > 0 ) m_nDefaultTempo = (255 * m_nDefaultTempo) / maxtempo; + numpats = 1 + (modticks(h, h->tracktime) / h->speed / 64 ); + if( h->verbose ) printf("Generating %d patterns with speed %d\n", numpats, h->speed); +#ifdef NEWMIKMOD + if( !of->songname ) of->songname = DupStr(of->allochandle, "Untitled", 8); + of->memsize = STMEM_LAST; // Number of memory slots to reserve! + of->modtype = _mm_strdup(of->allochandle, MID_Version); + of->numpat = numpats; + of->numpos = of->numpat; + of->reppos = 0; + of->initspeed = h->speed; + of->numchn = numtracks; + of->numtrk = of->numpat * of->numchn; + of->initvolume = 64; + of->pansep = 128; + // orderlist + if(!AllocPositions(of, of->numpos)) { + avoid_reentry = 0; + return FALSE; + } + for(t=0; t<of->numpos; t++) + of->positions[t] = t; + if( !PAT_Load_Instruments(of) ) { + avoid_reentry = 0; + return FALSE; + } + // ============================== + // Load the pattern info now! + if(!AllocTracks(of)) { + avoid_reentry = 0; + return FALSE; + } + if(!AllocPatterns(of)) { + avoid_reentry = 0; + return FALSE; + } + of->ut = utrk_init(of->numchn, h->allochandle); + utrk_memory_reset(of->ut); + utrk_local_memflag(of->ut, PTMEM_PORTAMENTO, TRUE, FALSE); + MID_ReadPatterns(of, h, numpats); + // ============================================================ + // set panning positions + t = 0; + for( ttp=h->track; ttp; ttp=ttp->next ) { + of->panning[t] = modpan(ttp->balance, numchans / 2); + t++; + } +#else + m_nType = MOD_TYPE_MID; + m_nDefaultSpeed = h->speed; + m_nChannels = numtracks; + m_dwSongFlags = SONG_LINEARSLIDES; + m_nMinPeriod = 28 << 2; + m_nMaxPeriod = 1712 << 3; + // orderlist + for(t=0; t < numpats; t++) + Order[t] = t; + if( !PAT_Load_Instruments(this) ) { + avoid_reentry = 0; + return FALSE; + } + // ============================== + // Load the pattern info now! + if( MID_ReadPatterns(Patterns, PatternSize, h, numpats, m_nChannels) ) { + // :^( need one more channel to handle the global events ;^b + m_nChannels++; + h->tp = mid_new_track(h, h->track->chan, 0xff); + for( ttp=h->track; ttp->next; ttp=ttp->next ) ; + ttp->next = h->tp; + mid_add_sync(h, h->tp); + for( t=0; t<numpats; t++ ) { + FreePattern(Patterns[t]); + Patterns[t] = NULL; + } + MID_ReadPatterns(Patterns, PatternSize, h, numpats, m_nChannels); + } + // ============================================================ + // set panning positions + t = 0; + for( ttp=h->track; ttp; ttp=ttp->next ) { + ChnSettings[t].nPan = modpan(ttp->balance, numchans / 2); + ChnSettings[t].nVolume = 64; + t++; + } + MID_Cleanup(h); // we dont need it anymore +#endif + if( h->verbose ) printf("Done\n"); + avoid_reentry = 0; // it is safe now, I'm finished + return TRUE; +} + +#ifdef NEWMIKMOD +// ===================================================================================== +CHAR *MID_LoadTitle(MMSTREAM *mmfile) +// ===================================================================================== +{ + int t; + char buf[24]; + long miditracklen; + BYTE runningstatus; + BYTE cmd; + BYTE midibyte[2]; + long metalen; + MIDHANDLE hh, *h; + h = &hh; + h->mmf = mmfile; + mmfseek(h->mmf,8,SEEK_SET); + h->midiformat = mid_read_short(h); + h->miditracks = mid_read_short(h); + h->resolution = mid_read_short(h); + // at this point the h->mmf is positioned at first miditrack + if( h->midiformat == 0 ) h->miditracks = 1; + h->tracktime = 0; + for( t=0; t<h->miditracks; t++ ) { + mmreadSBYTES(buf,4,h->mmf); + miditracklen = mid_read_long(h); + runningstatus = 0; + while( miditracklen > 0 ) { + miditracklen -= mid_read_delta(h); + midibyte[0] = mid_read_byte(h); + miditracklen--; + if( midibyte[0] & 0x80 ) { + runningstatus = midibyte[0]; + switch( runningstatus ) { + case 0xf1: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + case 0xf8: + case 0xf9: + case 0xfa: + case 0xfb: + case 0xfc: + case 0xfd: + case 0xfe: + break; + default: + midibyte[0] = mid_read_byte(h); + miditracklen--; + break; + } + } + cmd = runningstatus & 0xf0; + switch( cmd ) { + case 0x80: // note off + case 0x90: // note on + case 0xa0: // polyphonic key pressure + case 0xb0: // control change + case 0xe0: // pitch wheel change + midibyte[1] = mid_read_byte(h); + miditracklen--; + case 0xc0: // program change + case 0xd0: // channel pressure + break; + case 0xf0: // system & realtime + switch( runningstatus ) { + case 0xf0: // sysex + while( midibyte[0] != 0xf7 ) { + midibyte[0] = mid_read_byte(h); + miditracklen--; + } + break; + case 0xf2: // song position pointer + midibyte[1] = mid_read_byte(h); + miditracklen--; + break; + case 0xf7: + miditracklen -= mid_read_delta(h); + metalen = h->deltatime; + while( metalen > 0 ) { + midibyte[1] = mid_read_byte(h); + metalen--; + miditracklen--; + } + break; + case 0xff: // meta event + miditracklen -= mid_read_delta(h); + metalen = h->deltatime; + if( metalen > 21 ) metalen = 21; + if( metalen ) { + mmreadSBYTES(buf, metalen, h->mmf); + miditracklen -= metalen; + } + buf[metalen] = '\0'; + metalen = h->deltatime - metalen; + while( metalen > 0 ) { + midibyte[1] = mid_read_byte(h); + metalen--; + miditracklen--; + } + switch( midibyte[0] ) { + case 0x03: // type: track name + return DupStr(NULL, buf, strlen(buf)); + break; + case 0x2f: // type: end of track + miditracklen = 0; + break; + default: + break; + } + break; + default: + break; + } + break; + default: // no running status, just skip it... + break; + } + if( miditracklen < 1 && (runningstatus != 0xff || midibyte[0] != 0x2f) ) { + metalen = mmftell(h->mmf); + mmreadSBYTES(buf,4,h->mmf); + buf[4] = '\0'; + if( strcmp(buf,"MTrk") ) miditracklen = 0x7fffffff; + mmfseek(h->mmf,metalen,SEEK_SET); + } + } + } + return DupStr(NULL, "Untitled" ,8); +} + +MLOADER load_mid = +{ + "MID", + "Musical Instrument Digital Interface", + 0x30, + NULL, + MID_Test, + (void *(*)(void))MID_Init, + (void (*)(ML_HANDLE *))MID_Cleanup, + /* Every single loader seems to need one of these! */ + (BOOL (*)(ML_HANDLE *, UNIMOD *, MMSTREAM *))MID_Load, + MID_LoadTitle +}; +#endif diff --git a/lib/libmodplug/src/load_mod.cpp b/lib/libmodplug/src/load_mod.cpp new file mode 100644 index 0000000000..57304ac51e --- /dev/null +++ b/lib/libmodplug/src/load_mod.cpp @@ -0,0 +1,505 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net>, + * Adam Goode <adam@evdebs.org> (endian and char fixes for PPC) +*/ + +#include "stdafx.h" +#include "sndfile.h" +#include "tables.h" + +#ifdef _MSC_VER +//#pragma warning(disable:4244) +#endif + +////////////////////////////////////////////////////////// +// ProTracker / NoiseTracker MOD/NST file support + +void CSoundFile::ConvertModCommand(MODCOMMAND *m) const +//----------------------------------------------------- +{ + UINT command = m->command, param = m->param; + + switch(command) + { + case 0x00: if (param) command = CMD_ARPEGGIO; break; + case 0x01: command = CMD_PORTAMENTOUP; break; + case 0x02: command = CMD_PORTAMENTODOWN; break; + case 0x03: command = CMD_TONEPORTAMENTO; break; + case 0x04: command = CMD_VIBRATO; break; + case 0x05: command = CMD_TONEPORTAVOL; if (param & 0xF0) param &= 0xF0; break; + case 0x06: command = CMD_VIBRATOVOL; if (param & 0xF0) param &= 0xF0; break; + case 0x07: command = CMD_TREMOLO; break; + case 0x08: command = CMD_PANNING8; break; + case 0x09: command = CMD_OFFSET; break; + case 0x0A: command = CMD_VOLUMESLIDE; if (param & 0xF0) param &= 0xF0; break; + case 0x0B: command = CMD_POSITIONJUMP; break; + case 0x0C: command = CMD_VOLUME; break; + case 0x0D: command = CMD_PATTERNBREAK; param = ((param >> 4) * 10) + (param & 0x0F); break; + case 0x0E: command = CMD_MODCMDEX; break; + case 0x0F: command = (param <= (UINT)((m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) ? 0x1F : 0x20)) ? CMD_SPEED : CMD_TEMPO; + if ((param == 0xFF) && (m_nSamples == 15)) command = 0; break; + // Extension for XM extended effects + case 'G' - 55: command = CMD_GLOBALVOLUME; break; + case 'H' - 55: command = CMD_GLOBALVOLSLIDE; if (param & 0xF0) param &= 0xF0; break; + case 'K' - 55: command = CMD_KEYOFF; break; + case 'L' - 55: command = CMD_SETENVPOSITION; break; + case 'M' - 55: command = CMD_CHANNELVOLUME; break; + case 'N' - 55: command = CMD_CHANNELVOLSLIDE; break; + case 'P' - 55: command = CMD_PANNINGSLIDE; if (param & 0xF0) param &= 0xF0; break; + case 'R' - 55: command = CMD_RETRIG; break; + case 'T' - 55: command = CMD_TREMOR; break; + case 'X' - 55: command = CMD_XFINEPORTAUPDOWN; break; + case 'Y' - 55: command = CMD_PANBRELLO; break; + case 'Z' - 55: command = CMD_MIDI; break; + default: command = 0; + } + m->command = command; + m->param = param; +} + + +WORD CSoundFile::ModSaveCommand(const MODCOMMAND *m, BOOL bXM) const +//------------------------------------------------------------------ +{ + UINT command = m->command & 0x3F, param = m->param; + + switch(command) + { + case 0: command = param = 0; break; + case CMD_ARPEGGIO: command = 0; break; + case CMD_PORTAMENTOUP: + if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM)) + { + if ((param & 0xF0) == 0xE0) { command=0x0E; param=((param & 0x0F) >> 2)|0x10; break; } + else if ((param & 0xF0) == 0xF0) { command=0x0E; param &= 0x0F; param|=0x10; break; } + } + command = 0x01; + break; + case CMD_PORTAMENTODOWN: + if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM)) + { + if ((param & 0xF0) == 0xE0) { command=0x0E; param=((param & 0x0F) >> 2)|0x20; break; } + else if ((param & 0xF0) == 0xF0) { command=0x0E; param &= 0x0F; param|=0x20; break; } + } + command = 0x02; + break; + case CMD_TONEPORTAMENTO: command = 0x03; break; + case CMD_VIBRATO: command = 0x04; break; + case CMD_TONEPORTAVOL: command = 0x05; break; + case CMD_VIBRATOVOL: command = 0x06; break; + case CMD_TREMOLO: command = 0x07; break; + case CMD_PANNING8: + command = 0x08; + if (bXM) + { + if ((m_nType != MOD_TYPE_IT) && (m_nType != MOD_TYPE_XM) && (param <= 0x80)) + { + param <<= 1; + if (param > 255) param = 255; + } + } else + { + if ((m_nType == MOD_TYPE_IT) || (m_nType == MOD_TYPE_XM)) param >>= 1; + } + break; + case CMD_OFFSET: command = 0x09; break; + case CMD_VOLUMESLIDE: command = 0x0A; break; + case CMD_POSITIONJUMP: command = 0x0B; break; + case CMD_VOLUME: command = 0x0C; break; + case CMD_PATTERNBREAK: command = 0x0D; param = ((param / 10) << 4) | (param % 10); break; + case CMD_MODCMDEX: command = 0x0E; break; + case CMD_SPEED: command = 0x0F; if (param > 0x20) param = 0x20; break; + case CMD_TEMPO: if (param > 0x20) { command = 0x0F; break; } + case CMD_GLOBALVOLUME: command = 'G' - 55; break; + case CMD_GLOBALVOLSLIDE: command = 'H' - 55; break; + case CMD_KEYOFF: command = 'K' - 55; break; + case CMD_SETENVPOSITION: command = 'L' - 55; break; + case CMD_CHANNELVOLUME: command = 'M' - 55; break; + case CMD_CHANNELVOLSLIDE: command = 'N' - 55; break; + case CMD_PANNINGSLIDE: command = 'P' - 55; break; + case CMD_RETRIG: command = 'R' - 55; break; + case CMD_TREMOR: command = 'T' - 55; break; + case CMD_XFINEPORTAUPDOWN: command = 'X' - 55; break; + case CMD_PANBRELLO: command = 'Y' - 55; break; + case CMD_MIDI: command = 'Z' - 55; break; + case CMD_S3MCMDEX: + switch(param & 0xF0) + { + case 0x10: command = 0x0E; param = (param & 0x0F) | 0x30; break; + case 0x20: command = 0x0E; param = (param & 0x0F) | 0x50; break; + case 0x30: command = 0x0E; param = (param & 0x0F) | 0x40; break; + case 0x40: command = 0x0E; param = (param & 0x0F) | 0x70; break; + case 0x90: command = 'X' - 55; break; + case 0xB0: command = 0x0E; param = (param & 0x0F) | 0x60; break; + case 0xA0: + case 0x50: + case 0x70: + case 0x60: command = param = 0; break; + default: command = 0x0E; break; + } + break; + default: command = param = 0; + } + return (WORD)((command << 8) | (param)); +} + + +#pragma pack(1) + +typedef struct _MODSAMPLE +{ + CHAR name[22]; + WORD length; + BYTE finetune; + BYTE volume; + WORD loopstart; + WORD looplen; +} MODSAMPLE, *PMODSAMPLE; + +typedef struct _MODMAGIC +{ + BYTE nOrders; + BYTE nRestartPos; + BYTE Orders[128]; + char Magic[4]; // changed from CHAR +} MODMAGIC, *PMODMAGIC; + +#pragma pack() + +BOOL IsMagic(LPCSTR s1, LPCSTR s2) +{ + return ((*(DWORD *)s1) == (*(DWORD *)s2)) ? TRUE : FALSE; +} + + +BOOL CSoundFile::ReadMod(const BYTE *lpStream, DWORD dwMemLength) +//--------------------------------------------------------------- +{ + char s[1024]; // changed from CHAR + DWORD dwMemPos, dwTotalSampleLen; + PMODMAGIC pMagic; + UINT nErr; + + if ((!lpStream) || (dwMemLength < 0x600)) return FALSE; + dwMemPos = 20; + m_nSamples = 31; + m_nChannels = 4; + pMagic = (PMODMAGIC)(lpStream+dwMemPos+sizeof(MODSAMPLE)*31); + // Check Mod Magic + memcpy(s, pMagic->Magic, 4); + if ((IsMagic(s, "M.K.")) || (IsMagic(s, "M!K!")) + || (IsMagic(s, "M&K!")) || (IsMagic(s, "N.T."))) m_nChannels = 4; else + if ((IsMagic(s, "CD81")) || (IsMagic(s, "OKTA"))) m_nChannels = 8; else + if ((s[0]=='F') && (s[1]=='L') && (s[2]=='T') && (s[3]>='4') && (s[3]<='9')) m_nChannels = s[3] - '0'; else + if ((s[0]>='2') && (s[0]<='9') && (s[1]=='C') && (s[2]=='H') && (s[3]=='N')) m_nChannels = s[0] - '0'; else + if ((s[0]=='1') && (s[1]>='0') && (s[1]<='9') && (s[2]=='C') && (s[3]=='H')) m_nChannels = s[1] - '0' + 10; else + if ((s[0]=='2') && (s[1]>='0') && (s[1]<='9') && (s[2]=='C') && (s[3]=='H')) m_nChannels = s[1] - '0' + 20; else + if ((s[0]=='3') && (s[1]>='0') && (s[1]<='2') && (s[2]=='C') && (s[3]=='H')) m_nChannels = s[1] - '0' + 30; else + if ((s[0]=='T') && (s[1]=='D') && (s[2]=='Z') && (s[3]>='4') && (s[3]<='9')) m_nChannels = s[3] - '0'; else + if (IsMagic(s,"16CN")) m_nChannels = 16; else + if (IsMagic(s,"32CN")) m_nChannels = 32; else m_nSamples = 15; + // Load Samples + nErr = 0; + dwTotalSampleLen = 0; + for (UINT i=1; i<=m_nSamples; i++) + { + PMODSAMPLE pms = (PMODSAMPLE)(lpStream+dwMemPos); + MODINSTRUMENT *psmp = &Ins[i]; + UINT loopstart, looplen; + + memcpy(m_szNames[i], pms->name, 22); + m_szNames[i][22] = 0; + psmp->uFlags = 0; + psmp->nLength = bswapBE16(pms->length)*2; + dwTotalSampleLen += psmp->nLength; + psmp->nFineTune = MOD2XMFineTune(pms->finetune & 0x0F); + psmp->nVolume = 4*pms->volume; + if (psmp->nVolume > 256) { psmp->nVolume = 256; nErr++; } + psmp->nGlobalVol = 64; + psmp->nPan = 128; + loopstart = bswapBE16(pms->loopstart)*2; + looplen = bswapBE16(pms->looplen)*2; + // Fix loops + if ((looplen > 2) && (loopstart+looplen > psmp->nLength) + && (loopstart/2+looplen <= psmp->nLength)) + { + loopstart /= 2; + } + psmp->nLoopStart = loopstart; + psmp->nLoopEnd = loopstart + looplen; + if (psmp->nLength < 4) psmp->nLength = 0; + if (psmp->nLength) + { + UINT derr = 0; + if (psmp->nLoopStart >= psmp->nLength) { psmp->nLoopStart = psmp->nLength-1; derr|=1; } + if (psmp->nLoopEnd > psmp->nLength) { psmp->nLoopEnd = psmp->nLength; derr |= 1; } + if (psmp->nLoopStart > psmp->nLoopEnd) derr |= 1; + if ((psmp->nLoopStart > psmp->nLoopEnd) || (psmp->nLoopEnd <= 8) + || (psmp->nLoopEnd - psmp->nLoopStart <= 4)) + { + psmp->nLoopStart = 0; + psmp->nLoopEnd = 0; + } + if (psmp->nLoopEnd > psmp->nLoopStart) + { + psmp->uFlags |= CHN_LOOP; + } + } + dwMemPos += sizeof(MODSAMPLE); + } + if ((m_nSamples == 15) && (dwTotalSampleLen > dwMemLength * 4)) return FALSE; + pMagic = (PMODMAGIC)(lpStream+dwMemPos); + dwMemPos += sizeof(MODMAGIC); + if (m_nSamples == 15) dwMemPos -= 4; + memset(Order, 0,sizeof(Order)); + memcpy(Order, pMagic->Orders, 128); + + UINT nbp, nbpbuggy, nbpbuggy2, norders; + + norders = pMagic->nOrders; + if ((!norders) || (norders > 0x80)) + { + norders = 0x80; + while ((norders > 1) && (!Order[norders-1])) norders--; + } + nbpbuggy = 0; + nbpbuggy2 = 0; + nbp = 0; + for (UINT iord=0; iord<128; iord++) + { + UINT i = Order[iord]; + if ((i < 0x80) && (nbp <= i)) + { + nbp = i+1; + if (iord<norders) nbpbuggy = nbp; + } + if (i >= nbpbuggy2) nbpbuggy2 = i+1; + } + for (UINT iend=norders; iend<MAX_ORDERS; iend++) Order[iend] = 0xFF; + norders--; + m_nRestartPos = pMagic->nRestartPos; + if (m_nRestartPos >= 0x78) m_nRestartPos = 0; + if (m_nRestartPos + 1 >= (UINT)norders) m_nRestartPos = 0; + if (!nbp) return FALSE; + DWORD dwWowTest = dwTotalSampleLen+dwMemPos; + if ((IsMagic(pMagic->Magic, "M.K.")) && (dwWowTest + nbp*8*256 == dwMemLength)) m_nChannels = 8; + if ((nbp != nbpbuggy) && (dwWowTest + nbp*m_nChannels*256 != dwMemLength)) + { + if (dwWowTest + nbpbuggy*m_nChannels*256 == dwMemLength) nbp = nbpbuggy; + else nErr += 8; + } else + if ((nbpbuggy2 > nbp) && (dwWowTest + nbpbuggy2*m_nChannels*256 == dwMemLength)) + { + nbp = nbpbuggy2; + } + if ((dwWowTest < 0x600) || (dwWowTest > dwMemLength)) nErr += 8; + if ((m_nSamples == 15) && (nErr >= 16)) return FALSE; + // Default settings + m_nType = MOD_TYPE_MOD; + m_nDefaultSpeed = 6; + m_nDefaultTempo = 125; + m_nMinPeriod = 14 << 2; + m_nMaxPeriod = 3424 << 2; + memcpy(m_szNames, lpStream, 20); + // Setting channels pan + for (UINT ich=0; ich<m_nChannels; ich++) + { + ChnSettings[ich].nVolume = 64; + if (gdwSoundSetup & SNDMIX_MAXDEFAULTPAN) + ChnSettings[ich].nPan = (((ich&3)==1) || ((ich&3)==2)) ? 256 : 0; + else + ChnSettings[ich].nPan = (((ich&3)==1) || ((ich&3)==2)) ? 0xC0 : 0x40; + } + // Reading channels + for (UINT ipat=0; ipat<nbp; ipat++) + { + if (ipat < MAX_PATTERNS) + { + if ((Patterns[ipat] = AllocatePattern(64, m_nChannels)) == NULL) break; + PatternSize[ipat] = 64; + if (dwMemPos + m_nChannels*256 >= dwMemLength) break; + MODCOMMAND *m = Patterns[ipat]; + LPCBYTE p = lpStream + dwMemPos; + for (UINT j=m_nChannels*64; j; m++,p+=4,j--) + { + BYTE A0=p[0], A1=p[1], A2=p[2], A3=p[3]; + UINT n = ((((UINT)A0 & 0x0F) << 8) | (A1)); + if ((n) && (n != 0xFFF)) m->note = GetNoteFromPeriod(n << 2); + m->instr = ((UINT)A2 >> 4) | (A0 & 0x10); + m->command = A2 & 0x0F; + m->param = A3; + if ((m->command) || (m->param)) ConvertModCommand(m); + } + } + dwMemPos += m_nChannels*256; + } + // Reading instruments + DWORD dwErrCheck = 0; + for (UINT ismp=1; ismp<=m_nSamples; ismp++) if (Ins[ismp].nLength) + { + LPSTR p = (LPSTR)(lpStream+dwMemPos); + UINT flags = 0; + if (dwMemPos + 5 >= dwMemLength) break; + if (!strnicmp(p, "ADPCM", 5)) + { + flags = 3; + p += 5; + dwMemPos += 5; + } + DWORD dwSize = ReadSample(&Ins[ismp], flags, p, dwMemLength - dwMemPos); + if (dwSize) + { + dwMemPos += dwSize; + dwErrCheck++; + } + } +#ifdef MODPLUG_TRACKER + return TRUE; +#else + return (dwErrCheck) ? TRUE : FALSE; +#endif +} + + +#ifndef MODPLUG_NO_FILESAVE + +#ifdef _MSC_VER +#pragma warning(disable:4100) +#endif + +BOOL CSoundFile::SaveMod(LPCSTR lpszFileName, UINT nPacking) +//---------------------------------------------------------- +{ + BYTE insmap[32]; + UINT inslen[32]; + BYTE bTab[32]; + BYTE ord[128]; + FILE *f; + + if ((!m_nChannels) || (!lpszFileName)) return FALSE; + if ((f = fopen(lpszFileName, "wb")) == NULL) return FALSE; + memset(ord, 0, sizeof(ord)); + memset(inslen, 0, sizeof(inslen)); + if (m_nInstruments) + { + memset(insmap, 0, sizeof(insmap)); + for (UINT i=1; i<32; i++) if (Headers[i]) + { + for (UINT j=0; j<128; j++) if (Headers[i]->Keyboard[j]) + { + insmap[i] = Headers[i]->Keyboard[j]; + break; + } + } + } else + { + for (UINT i=0; i<32; i++) insmap[i] = (BYTE)i; + } + // Writing song name + fwrite(m_szNames, 20, 1, f); + // Writing instrument definition + for (UINT iins=1; iins<=31; iins++) + { + MODINSTRUMENT *pins = &Ins[insmap[iins]]; + memcpy(bTab, m_szNames[iins],22); + inslen[iins] = pins->nLength; + if (inslen[iins] > 0x1fff0) inslen[iins] = 0x1fff0; + bTab[22] = inslen[iins] >> 9; + bTab[23] = inslen[iins] >> 1; + if (pins->RelativeTone < 0) bTab[24] = 0x08; else + if (pins->RelativeTone > 0) bTab[24] = 0x07; else + bTab[24] = (BYTE)XM2MODFineTune(pins->nFineTune); + bTab[25] = pins->nVolume >> 2; + bTab[26] = pins->nLoopStart >> 9; + bTab[27] = pins->nLoopStart >> 1; + bTab[28] = (pins->nLoopEnd - pins->nLoopStart) >> 9; + bTab[29] = (pins->nLoopEnd - pins->nLoopStart) >> 1; + fwrite(bTab, 30, 1, f); + } + // Writing number of patterns + UINT nbp=0, norders=128; + for (UINT iord=0; iord<128; iord++) + { + if (Order[iord] == 0xFF) + { + norders = iord; + break; + } + if ((Order[iord] < 0x80) && (nbp<=Order[iord])) nbp = Order[iord]+1; + } + bTab[0] = norders; + bTab[1] = m_nRestartPos; + fwrite(bTab, 2, 1, f); + // Writing pattern list + if (norders) memcpy(ord, Order, norders); + fwrite(ord, 128, 1, f); + // Writing signature + if (m_nChannels == 4) + lstrcpy((LPSTR)&bTab, "M.K."); + else + wsprintf((LPSTR)&bTab, "%luCHN", m_nChannels); + fwrite(bTab, 4, 1, f); + // Writing patterns + for (UINT ipat=0; ipat<nbp; ipat++) if (Patterns[ipat]) + { + BYTE s[64*4]; + MODCOMMAND *m = Patterns[ipat]; + for (UINT i=0; i<64; i++) if (i < PatternSize[ipat]) + { + LPBYTE p=s; + for (UINT c=0; c<m_nChannels; c++,p+=4,m++) + { + UINT param = ModSaveCommand(m, FALSE); + UINT command = param >> 8; + param &= 0xFF; + if (command > 0x0F) command = param = 0; + if ((m->vol >= 0x10) && (m->vol <= 0x50) && (!command) && (!param)) { command = 0x0C; param = m->vol - 0x10; } + UINT period = m->note; + if (period) + { + if (period < 37) period = 37; + period -= 37; + if (period >= 6*12) period = 6*12-1; + period = ProTrackerPeriodTable[period]; + } + UINT instr = (m->instr > 31) ? 0 : m->instr; + p[0] = ((period >> 8) & 0x0F) | (instr & 0x10); + p[1] = period & 0xFF; + p[2] = ((instr & 0x0F) << 4) | (command & 0x0F); + p[3] = param; + } + fwrite(s, m_nChannels, 4, f); + } else + { + memset(s, 0, m_nChannels*4); + fwrite(s, m_nChannels, 4, f); + } + } + // Writing instruments + for (UINT ismpd=1; ismpd<=31; ismpd++) if (inslen[ismpd]) + { + MODINSTRUMENT *pins = &Ins[insmap[ismpd]]; + UINT flags = RS_PCM8S; +#ifndef NO_PACKING + if (!(pins->uFlags & (CHN_16BIT|CHN_STEREO))) + { + if ((nPacking) && (CanPackSample((char *)pins->pSample, inslen[ismpd], nPacking))) + { + fwrite("ADPCM", 1, 5, f); + flags = RS_ADPCM4; + } + } +#endif + WriteSample(f, pins, flags, inslen[ismpd]); + } + fclose(f); + return TRUE; +} + +#ifdef _MSC_VER +#pragma warning(default:4100) +#endif + +#endif // MODPLUG_NO_FILESAVE diff --git a/lib/libmodplug/src/load_mt2.cpp b/lib/libmodplug/src/load_mt2.cpp new file mode 100644 index 0000000000..208154f6e8 --- /dev/null +++ b/lib/libmodplug/src/load_mt2.cpp @@ -0,0 +1,635 @@ +#include "stdafx.h" +#include "sndfile.h" + +//#define MT2DEBUG + +#pragma pack(1) + +typedef struct _MT2FILEHEADER +{ + DWORD dwMT20; // 0x3032544D "MT20" + DWORD dwSpecial; + WORD wVersion; + CHAR szTrackerName[32]; // "MadTracker 2.0" + CHAR szSongName[64]; + WORD nOrders; + WORD wRestart; + WORD wPatterns; + WORD wChannels; + WORD wSamplesPerTick; + BYTE bTicksPerLine; + BYTE bLinesPerBeat; + DWORD fulFlags; // b0=packed patterns + WORD wInstruments; + WORD wSamples; + BYTE Orders[256]; +} MT2FILEHEADER; + +typedef struct _MT2PATTERN +{ + WORD wLines; + DWORD wDataLen; +} MT2PATTERN; + +typedef struct _MT2COMMAND +{ + BYTE note; // 0=nothing, 97=note off + BYTE instr; + BYTE vol; + BYTE pan; + BYTE fxcmd; + BYTE fxparam1; + BYTE fxparam2; +} MT2COMMAND; + +typedef struct _MT2DRUMSDATA +{ + WORD wDrumPatterns; + WORD wDrumSamples[8]; + BYTE DrumPatternOrder[256]; +} MT2DRUMSDATA; + +typedef struct _MT2AUTOMATION +{ + DWORD dwFlags; + DWORD dwEffectId; + DWORD nEnvPoints; +} MT2AUTOMATION; + +typedef struct _MT2INSTRUMENT +{ + CHAR szName[32]; + DWORD dwDataLen; + WORD wSamples; + BYTE GroupsMapping[96]; + BYTE bVibType; + BYTE bVibSweep; + BYTE bVibDepth; + BYTE bVibRate; + WORD wFadeOut; + WORD wNNA; + WORD wInstrFlags; + WORD wEnvFlags1; + WORD wEnvFlags2; +} MT2INSTRUMENT; + +typedef struct _MT2ENVELOPE +{ + BYTE nFlags; + BYTE nPoints; + BYTE nSustainPos; + BYTE nLoopStart; + BYTE nLoopEnd; + BYTE bReserved[3]; + BYTE EnvData[64]; +} MT2ENVELOPE; + +typedef struct _MT2SYNTH +{ + BYTE nSynthId; + BYTE nFxId; + WORD wCutOff; + BYTE nResonance; + BYTE nAttack; + BYTE nDecay; + BYTE bReserved[25]; +} MT2SYNTH; + +typedef struct _MT2SAMPLE +{ + CHAR szName[32]; + DWORD dwDataLen; + DWORD dwLength; + DWORD dwFrequency; + BYTE nQuality; + BYTE nChannels; + BYTE nFlags; + BYTE nLoop; + DWORD dwLoopStart; + DWORD dwLoopEnd; + WORD wVolume; + BYTE nPan; + BYTE nBaseNote; + WORD wSamplesPerBeat; +} MT2SAMPLE; + +typedef struct _MT2GROUP +{ + BYTE nSmpNo; + BYTE nVolume; // 0-128 + BYTE nFinePitch; + BYTE Reserved[5]; +} MT2GROUP; + +#pragma pack() + + +static VOID ConvertMT2Command(CSoundFile *that, MODCOMMAND *m, MT2COMMAND *p) +//--------------------------------------------------------------------------- +{ + // Note + m->note = 0; + if (p->note) m->note = (p->note > 96) ? 0xFF : p->note+12; + // Instrument + m->instr = p->instr; + // Volume Column + if ((p->vol >= 0x10) && (p->vol <= 0x90)) + { + m->volcmd = VOLCMD_VOLUME; + m->vol = (p->vol - 0x10) >> 1; + } else + if ((p->vol >= 0xA0) && (p->vol <= 0xAF)) + { + m->volcmd = VOLCMD_VOLSLIDEDOWN; + m->vol = (p->vol & 0x0f); + } else + if ((p->vol >= 0xB0) && (p->vol <= 0xBF)) + { + m->volcmd = VOLCMD_VOLSLIDEUP; + m->vol = (p->vol & 0x0f); + } else + if ((p->vol >= 0xC0) && (p->vol <= 0xCF)) + { + m->volcmd = VOLCMD_FINEVOLDOWN; + m->vol = (p->vol & 0x0f); + } else + if ((p->vol >= 0xD0) && (p->vol <= 0xDF)) + { + m->volcmd = VOLCMD_FINEVOLUP; + m->vol = (p->vol & 0x0f); + } else + { + m->volcmd = 0; + m->vol = 0; + } + // Effects + m->command = 0; + m->param = 0; + if ((p->fxcmd) || (p->fxparam1) || (p->fxparam2)) + { + if (!p->fxcmd) + { + m->command = p->fxparam2; + m->param = p->fxparam1; + that->ConvertModCommand(m); + } else + { + // TODO: MT2 Effects + } + } +} + + +BOOL CSoundFile::ReadMT2(LPCBYTE lpStream, DWORD dwMemLength) +//----------------------------------------------------------- +{ + MT2FILEHEADER *pfh = (MT2FILEHEADER *)lpStream; + DWORD dwMemPos, dwDrumDataPos, dwExtraDataPos; + UINT nDrumDataLen, nExtraDataLen; + MT2DRUMSDATA *pdd; + MT2INSTRUMENT *InstrMap[255]; + MT2SAMPLE *SampleMap[256]; + + if ((!lpStream) || (dwMemLength < sizeof(MT2FILEHEADER)) + || (pfh->dwMT20 != 0x3032544D) + || (pfh->wVersion < 0x0200) || (pfh->wVersion >= 0x0300) + || (pfh->wChannels < 4) || (pfh->wChannels > 64)) return FALSE; + pdd = NULL; + m_nType = MOD_TYPE_MT2; + m_nChannels = pfh->wChannels; + m_nRestartPos = pfh->wRestart; + m_nDefaultSpeed = pfh->bTicksPerLine; + m_nDefaultTempo = 125; + if ((pfh->wSamplesPerTick > 100) && (pfh->wSamplesPerTick < 5000)) + { + m_nDefaultTempo = 110250 / pfh->wSamplesPerTick; + } + for (UINT iOrd=0; iOrd<MAX_ORDERS; iOrd++) + { + Order[iOrd] = (BYTE)((iOrd < pfh->nOrders) ? pfh->Orders[iOrd] : 0xFF); + } + memcpy(m_szNames[0], pfh->szSongName, 32); + m_szNames[0][31] = 0; + dwMemPos = sizeof(MT2FILEHEADER); + nDrumDataLen = *(WORD *)(lpStream + dwMemPos); + dwDrumDataPos = dwMemPos + 2; + if (nDrumDataLen >= 2) pdd = (MT2DRUMSDATA *)(lpStream+dwDrumDataPos); + dwMemPos += 2 + nDrumDataLen; +#ifdef MT2DEBUG + + Log("MT2 v%03X: \"%s\" (flags=%04X)\n", pfh->wVersion, m_szNames[0], pfh->fulFlags); + Log("%d Channels, %d Patterns, %d Instruments, %d Samples\n", pfh->wChannels, pfh->wPatterns, pfh->wInstruments, pfh->wSamples); + Log("Drum Data: %d bytes @%04X\n", nDrumDataLen, dwDrumDataPos); +#endif + if (dwMemPos >= dwMemLength-12) return TRUE; + if (!*(DWORD *)(lpStream+dwMemPos)) dwMemPos += 4; + if (!*(DWORD *)(lpStream+dwMemPos)) dwMemPos += 4; + nExtraDataLen = *(DWORD *)(lpStream+dwMemPos); + dwExtraDataPos = dwMemPos + 4; + dwMemPos += 4; +#ifdef MT2DEBUG + Log("Extra Data: %d bytes @%04X\n", nExtraDataLen, dwExtraDataPos); +#endif + if (dwMemPos + nExtraDataLen >= dwMemLength) return TRUE; + while (dwMemPos+8 < dwExtraDataPos + nExtraDataLen) + { + DWORD dwId = *(DWORD *)(lpStream+dwMemPos); + DWORD dwLen = *(DWORD *)(lpStream+dwMemPos+4); + dwMemPos += 8; + if (dwMemPos + dwLen > dwMemLength) return TRUE; +#ifdef MT2DEBUG + CHAR s[5]; + memcpy(s, &dwId, 4); + s[4] = 0; + Log("pos=0x%04X: %s: %d bytes\n", dwMemPos-8, s, dwLen); +#endif + switch(dwId) + { + // MSG + case 0x0047534D: + if ((dwLen > 3) && (!m_lpszSongComments)) + { + DWORD nTxtLen = dwLen; + if (nTxtLen > 32000) nTxtLen = 32000; + m_lpszSongComments = new char[nTxtLen]; // changed from CHAR + if (m_lpszSongComments) + { + memcpy(m_lpszSongComments, lpStream+dwMemPos+1, nTxtLen-1); + m_lpszSongComments[nTxtLen-1] = 0; + } + } + break; + // SUM -> author name (or "Unregistered") + // TMAP + // TRKS + case 0x534b5254: + break; + } + dwMemPos += dwLen; + } + // Load Patterns + dwMemPos = dwExtraDataPos + nExtraDataLen; + for (UINT iPat=0; iPat<pfh->wPatterns; iPat++) if (dwMemPos < dwMemLength-6) + { + MT2PATTERN *pmp = (MT2PATTERN *)(lpStream+dwMemPos); + UINT wDataLen = (pmp->wDataLen + 1) & ~1; + dwMemPos += 6; + if (dwMemPos + wDataLen > dwMemLength) break; + UINT nLines = pmp->wLines; + if ((iPat < MAX_PATTERNS) && (nLines > 0) && (nLines <= 256)) + { + #ifdef MT2DEBUG + Log("Pattern #%d @%04X: %d lines, %d bytes\n", iPat, dwMemPos-6, nLines, pmp->wDataLen); + #endif + PatternSize[iPat] = nLines; + Patterns[iPat] = AllocatePattern(nLines, m_nChannels); + if (!Patterns[iPat]) return TRUE; + MODCOMMAND *m = Patterns[iPat]; + UINT len = wDataLen; + if (pfh->fulFlags & 1) // Packed Patterns + { + BYTE *p = (BYTE *)(lpStream+dwMemPos); + UINT pos = 0, row=0, ch=0; + while (pos < len) + { + MT2COMMAND cmd; + UINT infobyte = p[pos++]; + UINT rptcount = 0; + if (infobyte == 0xff) + { + rptcount = p[pos++]; + infobyte = p[pos++]; + #if 0 + Log("(%d.%d) FF(%02X).%02X\n", row, ch, rptcount, infobyte); + } else + { + Log("(%d.%d) %02X\n", row, ch, infobyte); + #endif + } + if (infobyte & 0x7f) + { + UINT patpos = row*m_nChannels+ch; + cmd.note = cmd.instr = cmd.vol = cmd.pan = cmd.fxcmd = cmd.fxparam1 = cmd.fxparam2 = 0; + if (infobyte & 1) cmd.note = p[pos++]; + if (infobyte & 2) cmd.instr = p[pos++]; + if (infobyte & 4) cmd.vol = p[pos++]; + if (infobyte & 8) cmd.pan = p[pos++]; + if (infobyte & 16) cmd.fxcmd = p[pos++]; + if (infobyte & 32) cmd.fxparam1 = p[pos++]; + if (infobyte & 64) cmd.fxparam2 = p[pos++]; + #ifdef MT2DEBUG + if (cmd.fxcmd) + { + Log("(%d.%d) MT2 FX=%02X.%02X.%02X\n", row, ch, cmd.fxcmd, cmd.fxparam1, cmd.fxparam2); + } + #endif + ConvertMT2Command(this, &m[patpos], &cmd); + } + row += rptcount+1; + while (row >= nLines) { row-=nLines; ch++; } + if (ch >= m_nChannels) break; + } + } else + { + MT2COMMAND *p = (MT2COMMAND *)(lpStream+dwMemPos); + UINT n = 0; + while ((len > sizeof(MT2COMMAND)) && (n < m_nChannels*nLines)) + { + ConvertMT2Command(this, m, p); + len -= sizeof(MT2COMMAND); + n++; + p++; + m++; + } + } + } + dwMemPos += wDataLen; + } + // Skip Drum Patterns + if (pdd) + { + #ifdef MT2DEBUG + Log("%d Drum Patterns at offset 0x%08X\n", pdd->wDrumPatterns, dwMemPos); + #endif + for (UINT iDrm=0; iDrm<pdd->wDrumPatterns; iDrm++) + { + if (dwMemPos > dwMemLength-2) return TRUE; + UINT nLines = *(WORD *)(lpStream+dwMemPos); + #ifdef MT2DEBUG + if (nLines != 64) Log("Drum Pattern %d: %d Lines @%04X\n", iDrm, nLines, dwMemPos); + #endif + dwMemPos += 2 + nLines * 32; + } + } + // Automation + if (pfh->fulFlags & 2) + { + #ifdef MT2DEBUG + Log("Automation at offset 0x%08X\n", dwMemPos); + #endif + UINT nAutoCount = m_nChannels; + if (pfh->fulFlags & 0x10) nAutoCount++; // Master Automation + if ((pfh->fulFlags & 0x08) && (pdd)) nAutoCount += 8; // Drums Automation + nAutoCount *= pfh->wPatterns; + for (UINT iAuto=0; iAuto<nAutoCount; iAuto++) + { + if (dwMemPos+12 >= dwMemLength) return TRUE; + MT2AUTOMATION *pma = (MT2AUTOMATION *)(lpStream+dwMemPos); + dwMemPos += (pfh->wVersion <= 0x201) ? 4 : 8; + for (UINT iEnv=0; iEnv<14; iEnv++) + { + if (pma->dwFlags & (1 << iEnv)) + { + #ifdef MT2DEBUG + UINT nPoints = *(DWORD *)(lpStream+dwMemPos); + Log(" Env[%d/%d] %04X @%04X: %d points\n", iAuto, nAutoCount, 1 << iEnv, dwMemPos-8, nPoints); + #endif + dwMemPos += 260; + } + } + } + } + // Load Instruments +#ifdef MT2DEBUG + Log("Loading instruments at offset 0x%08X\n", dwMemPos); +#endif + memset(InstrMap, 0, sizeof(InstrMap)); + m_nInstruments = (pfh->wInstruments < MAX_INSTRUMENTS) ? pfh->wInstruments : MAX_INSTRUMENTS-1; + for (UINT iIns=1; iIns<=255; iIns++) + { + if (dwMemPos+36 > dwMemLength) return TRUE; + MT2INSTRUMENT *pmi = (MT2INSTRUMENT *)(lpStream+dwMemPos); + INSTRUMENTHEADER *penv = NULL; + if (iIns <= m_nInstruments) + { + penv = new INSTRUMENTHEADER; + Headers[iIns] = penv; + if (penv) + { + memset(penv, 0, sizeof(INSTRUMENTHEADER)); + memcpy(penv->name, pmi->szName, 32); + penv->nGlobalVol = 64; + penv->nPan = 128; + for (UINT i=0; i<NOTE_MAX; i++) + { + penv->NoteMap[i] = i+1; + } + } + } + #ifdef MT2DEBUG + if (iIns <= pfh->wInstruments) Log(" Instrument #%d at offset %04X: %d bytes\n", iIns, dwMemPos, pmi->dwDataLen); + #endif + if (((LONG)pmi->dwDataLen > 0) && (dwMemPos + pmi->dwDataLen + 40 <= dwMemLength)) + { + InstrMap[iIns-1] = pmi; + if (penv) + { + penv->nFadeOut = pmi->wFadeOut; + penv->nNNA = pmi->wNNA & 3; + penv->nDCT = (pmi->wNNA>>8) & 3; + penv->nDNA = (pmi->wNNA>>12) & 3; + MT2ENVELOPE *pehdr[4]; + WORD *pedata[4]; + if (pfh->wVersion <= 0x201) + { + DWORD dwEnvPos = dwMemPos + sizeof(MT2INSTRUMENT) - 4; + pehdr[0] = (MT2ENVELOPE *)(lpStream+dwEnvPos); + pehdr[1] = (MT2ENVELOPE *)(lpStream+dwEnvPos+8); + pehdr[2] = pehdr[3] = NULL; + pedata[0] = (WORD *)(lpStream+dwEnvPos+16); + pedata[1] = (WORD *)(lpStream+dwEnvPos+16+64); + pedata[2] = pedata[3] = NULL; + } else + { + DWORD dwEnvPos = dwMemPos + sizeof(MT2INSTRUMENT); + for (UINT i=0; i<4; i++) + { + if (pmi->wEnvFlags1 & (1<<i)) + { + pehdr[i] = (MT2ENVELOPE *)(lpStream+dwEnvPos); + pedata[i] = (WORD *)pehdr[i]->EnvData; + dwEnvPos += sizeof(MT2ENVELOPE); + } else + { + pehdr[i] = NULL; + pedata[i] = NULL; + } + } + } + // Load envelopes + for (UINT iEnv=0; iEnv<4; iEnv++) if (pehdr[iEnv]) + { + MT2ENVELOPE *pme = pehdr[iEnv]; + WORD *pEnvPoints = NULL; + BYTE *pEnvData = NULL; + #ifdef MT2DEBUG + Log(" Env %d.%d @%04X: %d points\n", iIns, iEnv, (UINT)(((BYTE *)pme)-lpStream), pme->nPoints); + #endif + switch(iEnv) + { + // Volume Envelope + case 0: + if (pme->nFlags & 1) penv->dwFlags |= ENV_VOLUME; + if (pme->nFlags & 2) penv->dwFlags |= ENV_VOLSUSTAIN; + if (pme->nFlags & 4) penv->dwFlags |= ENV_VOLLOOP; + penv->nVolEnv = (pme->nPoints > 16) ? 16 : pme->nPoints; + penv->nVolSustainBegin = penv->nVolSustainEnd = pme->nSustainPos; + penv->nVolLoopStart = pme->nLoopStart; + penv->nVolLoopEnd = pme->nLoopEnd; + pEnvPoints = penv->VolPoints; + pEnvData = penv->VolEnv; + break; + + // Panning Envelope + case 1: + if (pme->nFlags & 1) penv->dwFlags |= ENV_PANNING; + if (pme->nFlags & 2) penv->dwFlags |= ENV_PANSUSTAIN; + if (pme->nFlags & 4) penv->dwFlags |= ENV_PANLOOP; + penv->nPanEnv = (pme->nPoints > 16) ? 16 : pme->nPoints; + penv->nPanSustainBegin = penv->nPanSustainEnd = pme->nSustainPos; + penv->nPanLoopStart = pme->nLoopStart; + penv->nPanLoopEnd = pme->nLoopEnd; + pEnvPoints = penv->PanPoints; + pEnvData = penv->PanEnv; + break; + + // Pitch/Filter envelope + default: + if (pme->nFlags & 1) penv->dwFlags |= (iEnv==3) ? (ENV_PITCH|ENV_FILTER) : ENV_PITCH; + if (pme->nFlags & 2) penv->dwFlags |= ENV_PITCHSUSTAIN; + if (pme->nFlags & 4) penv->dwFlags |= ENV_PITCHLOOP; + penv->nPitchEnv = (pme->nPoints > 16) ? 16 : pme->nPoints; + penv->nPitchSustainBegin = penv->nPitchSustainEnd = pme->nSustainPos; + penv->nPitchLoopStart = pme->nLoopStart; + penv->nPitchLoopEnd = pme->nLoopEnd; + pEnvPoints = penv->PitchPoints; + pEnvData = penv->PitchEnv; + } + // Envelope data + if ((pEnvPoints) && (pEnvData) && (pedata[iEnv])) + { + WORD *psrc = pedata[iEnv]; + for (UINT i=0; i<16; i++) + { + pEnvPoints[i] = psrc[i*2]; + pEnvData[i] = (BYTE)psrc[i*2+1]; + } + } + } + } + dwMemPos += pmi->dwDataLen + 36; + if (pfh->wVersion > 0x201) dwMemPos += 4; // ? + } else + { + dwMemPos += 36; + } + } +#ifdef MT2DEBUG + Log("Loading samples at offset 0x%08X\n", dwMemPos); +#endif + memset(SampleMap, 0, sizeof(SampleMap)); + m_nSamples = (pfh->wSamples < MAX_SAMPLES) ? pfh->wSamples : MAX_SAMPLES-1; + for (UINT iSmp=1; iSmp<=256; iSmp++) + { + if (dwMemPos+36 > dwMemLength) return TRUE; + MT2SAMPLE *pms = (MT2SAMPLE *)(lpStream+dwMemPos); + #ifdef MT2DEBUG + if (iSmp <= m_nSamples) Log(" Sample #%d at offset %04X: %d bytes\n", iSmp, dwMemPos, pms->dwDataLen); + #endif + if (iSmp < MAX_SAMPLES) + { + memcpy(m_szNames[iSmp], pms->szName, 32); + } + if (pms->dwDataLen > 0) + { + SampleMap[iSmp-1] = pms; + if (iSmp < MAX_SAMPLES) + { + MODINSTRUMENT *psmp = &Ins[iSmp]; + psmp->nGlobalVol = 64; + psmp->nVolume = (pms->wVolume >> 7); + psmp->nPan = (pms->nPan == 0x80) ? 128 : (pms->nPan^0x80); + psmp->nLength = pms->dwLength; + psmp->nC4Speed = pms->dwFrequency; + psmp->nLoopStart = pms->dwLoopStart; + psmp->nLoopEnd = pms->dwLoopEnd; + FrequencyToTranspose(psmp); + psmp->RelativeTone -= pms->nBaseNote - 49; + psmp->nC4Speed = TransposeToFrequency(psmp->RelativeTone, psmp->nFineTune); + if (pms->nQuality == 2) { psmp->uFlags |= CHN_16BIT; psmp->nLength >>= 1; } + if (pms->nChannels == 2) { psmp->nLength >>= 1; } + if (pms->nLoop == 1) psmp->uFlags |= CHN_LOOP; + if (pms->nLoop == 2) psmp->uFlags |= CHN_LOOP|CHN_PINGPONGLOOP; + } + dwMemPos += pms->dwDataLen + 36; + } else + { + dwMemPos += 36; + } + } +#ifdef MT2DEBUG + Log("Loading groups at offset 0x%08X\n", dwMemPos); +#endif + for (UINT iMap=0; iMap<255; iMap++) if (InstrMap[iMap]) + { + if (dwMemPos+8 > dwMemLength) return TRUE; + MT2INSTRUMENT *pmi = InstrMap[iMap]; + INSTRUMENTHEADER *penv = NULL; + if (iMap<m_nInstruments) penv = Headers[iMap+1]; + for (UINT iGrp=0; iGrp<pmi->wSamples; iGrp++) + { + if (penv) + { + MT2GROUP *pmg = (MT2GROUP *)(lpStream+dwMemPos); + for (UINT i=0; i<96; i++) + { + if (pmi->GroupsMapping[i] == iGrp) + { + UINT nSmp = pmg->nSmpNo+1; + penv->Keyboard[i+12] = (BYTE)nSmp; + if (nSmp <= m_nSamples) + { + Ins[nSmp].nVibType = pmi->bVibType; + Ins[nSmp].nVibSweep = pmi->bVibSweep; + Ins[nSmp].nVibDepth = pmi->bVibDepth; + Ins[nSmp].nVibRate = pmi->bVibRate; + } + } + } + } + dwMemPos += 8; + } + } +#ifdef MT2DEBUG + Log("Loading sample data at offset 0x%08X\n", dwMemPos); +#endif + for (UINT iData=0; iData<256; iData++) if ((iData < m_nSamples) && (SampleMap[iData])) + { + MT2SAMPLE *pms = SampleMap[iData]; + MODINSTRUMENT *psmp = &Ins[iData+1]; + if (!(pms->nFlags & 5)) + { + if (psmp->nLength > 0) + { + #ifdef MT2DEBUG + Log(" Reading sample #%d at offset 0x%04X (len=%d)\n", iData+1, dwMemPos, psmp->nLength); + #endif + UINT rsflags; + + if (pms->nChannels == 2) + rsflags = (psmp->uFlags & CHN_16BIT) ? RS_STPCM16D : RS_STPCM8D; + else + rsflags = (psmp->uFlags & CHN_16BIT) ? RS_PCM16D : RS_PCM8D; + + dwMemPos += ReadSample(psmp, rsflags, (LPCSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos); + } + } else + if (dwMemPos+4 < dwMemLength) + { + UINT nNameLen = *(DWORD *)(lpStream+dwMemPos); + dwMemPos += nNameLen + 16; + } + if (dwMemPos+4 >= dwMemLength) break; + } + return TRUE; +} diff --git a/lib/libmodplug/src/load_mtm.cpp b/lib/libmodplug/src/load_mtm.cpp new file mode 100644 index 0000000000..f5f0299e23 --- /dev/null +++ b/lib/libmodplug/src/load_mtm.cpp @@ -0,0 +1,168 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +#include "stdafx.h" +#include "sndfile.h" + +//#pragma warning(disable:4244) + +////////////////////////////////////////////////////////// +// MTM file support (import only) + +#pragma pack(1) + + +typedef struct tagMTMSAMPLE +{ + char samplename[22]; // changed from CHAR + DWORD length; + DWORD reppos; + DWORD repend; + CHAR finetune; + BYTE volume; + BYTE attribute; +} MTMSAMPLE; + + +typedef struct tagMTMHEADER +{ + char id[4]; // MTM file marker + version // changed from CHAR + char songname[20]; // ASCIIZ songname // changed from CHAR + WORD numtracks; // number of tracks saved + BYTE lastpattern; // last pattern number saved + BYTE lastorder; // last order number to play (songlength-1) + WORD commentsize; // length of comment field + BYTE numsamples; // number of samples saved + BYTE attribute; // attribute byte (unused) + BYTE beatspertrack; + BYTE numchannels; // number of channels used + BYTE panpos[32]; // voice pan positions +} MTMHEADER; + + +#pragma pack() + + +BOOL CSoundFile::ReadMTM(LPCBYTE lpStream, DWORD dwMemLength) +//----------------------------------------------------------- +{ + MTMHEADER *pmh = (MTMHEADER *)lpStream; + DWORD dwMemPos = 66; + + if ((!lpStream) || (dwMemLength < 0x100)) return FALSE; + if ((strncmp(pmh->id, "MTM", 3)) || (pmh->numchannels > 32) + || (pmh->numsamples >= MAX_SAMPLES) || (!pmh->numsamples) + || (!pmh->numtracks) || (!pmh->numchannels) + || (!pmh->lastpattern) || (pmh->lastpattern > MAX_PATTERNS)) + return FALSE; + strncpy(m_szNames[0], pmh->songname, 20); + m_szNames[0][20] = 0; + if (dwMemPos + 37*pmh->numsamples + 128 + 192*pmh->numtracks + + 64 * (pmh->lastpattern+1) + pmh->commentsize >= dwMemLength) + return FALSE; + m_nType = MOD_TYPE_MTM; + m_nSamples = pmh->numsamples; + m_nChannels = pmh->numchannels; + // Reading instruments + for (UINT i=1; i<=m_nSamples; i++) + { + MTMSAMPLE *pms = (MTMSAMPLE *)(lpStream + dwMemPos); + strncpy(m_szNames[i], pms->samplename, 22); + m_szNames[i][22] = 0; + Ins[i].nVolume = pms->volume << 2; + Ins[i].nGlobalVol = 64; + DWORD len = pms->length; + if ((len > 4) && (len <= MAX_SAMPLE_LENGTH)) + { + Ins[i].nLength = len; + Ins[i].nLoopStart = pms->reppos; + Ins[i].nLoopEnd = pms->repend; + if (Ins[i].nLoopEnd > Ins[i].nLength) + Ins[i].nLoopEnd = Ins[i].nLength; + if (Ins[i].nLoopStart + 4 >= Ins[i].nLoopEnd) + Ins[i].nLoopStart = Ins[i].nLoopEnd = 0; + if (Ins[i].nLoopEnd) Ins[i].uFlags |= CHN_LOOP; + Ins[i].nFineTune = MOD2XMFineTune(pms->finetune); + if (pms->attribute & 0x01) + { + Ins[i].uFlags |= CHN_16BIT; + Ins[i].nLength >>= 1; + Ins[i].nLoopStart >>= 1; + Ins[i].nLoopEnd >>= 1; + } + Ins[i].nPan = 128; + } + dwMemPos += 37; + } + // Setting Channel Pan Position + for (UINT ich=0; ich<m_nChannels; ich++) + { + ChnSettings[ich].nPan = ((pmh->panpos[ich] & 0x0F) << 4) + 8; + ChnSettings[ich].nVolume = 64; + } + // Reading pattern order + memcpy(Order, lpStream + dwMemPos, pmh->lastorder+1); + dwMemPos += 128; + // Reading Patterns + LPCBYTE pTracks = lpStream + dwMemPos; + dwMemPos += 192 * pmh->numtracks; + LPWORD pSeq = (LPWORD)(lpStream + dwMemPos); + for (UINT pat=0; pat<=pmh->lastpattern; pat++) + { + PatternSize[pat] = 64; + if ((Patterns[pat] = AllocatePattern(64, m_nChannels)) == NULL) break; + for (UINT n=0; n<32; n++) if ((pSeq[n]) && (pSeq[n] <= pmh->numtracks) && (n < m_nChannels)) + { + LPCBYTE p = pTracks + 192 * (pSeq[n]-1); + MODCOMMAND *m = Patterns[pat] + n; + for (UINT i=0; i<64; i++, m+=m_nChannels, p+=3) + { + if (p[0] & 0xFC) m->note = (p[0] >> 2) + 37; + m->instr = ((p[0] & 0x03) << 4) | (p[1] >> 4); + UINT cmd = p[1] & 0x0F; + UINT param = p[2]; + if (cmd == 0x0A) + { + if (param & 0xF0) param &= 0xF0; else param &= 0x0F; + } + m->command = cmd; + m->param = param; + if ((cmd) || (param)) ConvertModCommand(m); + } + } + pSeq += 32; + } + dwMemPos += 64*(pmh->lastpattern+1); + if ((pmh->commentsize) && (dwMemPos + pmh->commentsize < dwMemLength)) + { + UINT n = pmh->commentsize; + m_lpszSongComments = new char[n+1]; + if (m_lpszSongComments) + { + memcpy(m_lpszSongComments, lpStream+dwMemPos, n); + m_lpszSongComments[n] = 0; + for (UINT i=0; i<n; i++) + { + if (!m_lpszSongComments[i]) + { + m_lpszSongComments[i] = ((i+1) % 40) ? 0x20 : 0x0D; + } + } + } + } + dwMemPos += pmh->commentsize; + // Reading Samples + for (UINT ismp=1; ismp<=m_nSamples; ismp++) + { + if (dwMemPos >= dwMemLength) break; + dwMemPos += ReadSample(&Ins[ismp], (Ins[ismp].uFlags & CHN_16BIT) ? RS_PCM16U : RS_PCM8U, + (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos); + } + m_nMinPeriod = 64; + m_nMaxPeriod = 32767; + return TRUE; +} + diff --git a/lib/libmodplug/src/load_okt.cpp b/lib/libmodplug/src/load_okt.cpp new file mode 100644 index 0000000000..4c4e08a1fb --- /dev/null +++ b/lib/libmodplug/src/load_okt.cpp @@ -0,0 +1,197 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net>, + * Adam Goode <adam@evdebs.org> (endian and char fixes for PPC) +*/ + +////////////////////////////////////////////// +// Oktalyzer (OKT) module loader // +////////////////////////////////////////////// +#include "stdafx.h" +#include "sndfile.h" + +//#pragma warning(disable:4244) + +typedef struct OKTFILEHEADER +{ + DWORD okta; // "OKTA" + DWORD song; // "SONG" + DWORD cmod; // "CMOD" + DWORD fixed8; + BYTE chnsetup[8]; + DWORD samp; // "SAMP" + DWORD samplen; +} OKTFILEHEADER; + + +typedef struct OKTSAMPLE +{ + CHAR name[20]; + DWORD length; + WORD loopstart; + WORD looplen; + BYTE pad1; + BYTE volume; + BYTE pad2; + BYTE pad3; +} OKTSAMPLE; + + +BOOL CSoundFile::ReadOKT(const BYTE *lpStream, DWORD dwMemLength) +//--------------------------------------------------------------- +{ + OKTFILEHEADER *pfh = (OKTFILEHEADER *)lpStream; + DWORD dwMemPos = sizeof(OKTFILEHEADER); + UINT nsamples = 0, npatterns = 0, norders = 0; + + if ((!lpStream) || (dwMemLength < 1024)) return FALSE; + if ((pfh->okta != 0x41544B4F) || (pfh->song != 0x474E4F53) + || (pfh->cmod != 0x444F4D43) || (pfh->chnsetup[0]) || (pfh->chnsetup[2]) + || (pfh->chnsetup[4]) || (pfh->chnsetup[6]) || (pfh->fixed8 != 0x08000000) + || (pfh->samp != 0x504D4153)) return FALSE; + m_nType = MOD_TYPE_OKT; + m_nChannels = 4 + pfh->chnsetup[1] + pfh->chnsetup[3] + pfh->chnsetup[5] + pfh->chnsetup[7]; + if (m_nChannels > MAX_CHANNELS) m_nChannels = MAX_CHANNELS; + nsamples = bswapBE32(pfh->samplen) >> 5; + m_nSamples = nsamples; + if (m_nSamples >= MAX_SAMPLES) m_nSamples = MAX_SAMPLES-1; + // Reading samples + for (UINT smp=1; smp <= nsamples; smp++) + { + if (dwMemPos >= dwMemLength) return TRUE; + if (smp < MAX_SAMPLES) + { + OKTSAMPLE *psmp = (OKTSAMPLE *)(lpStream + dwMemPos); + MODINSTRUMENT *pins = &Ins[smp]; + + memcpy(m_szNames[smp], psmp->name, 20); + pins->uFlags = 0; + pins->nLength = bswapBE32(psmp->length) & ~1; + pins->nLoopStart = bswapBE16(psmp->loopstart); + pins->nLoopEnd = pins->nLoopStart + bswapBE16(psmp->looplen); + if (pins->nLoopStart + 2 < pins->nLoopEnd) pins->uFlags |= CHN_LOOP; + pins->nGlobalVol = 64; + pins->nVolume = psmp->volume << 2; + pins->nC4Speed = 8363; + } + dwMemPos += sizeof(OKTSAMPLE); + } + // SPEE + if (dwMemPos >= dwMemLength) return TRUE; + if (*((DWORD *)(lpStream + dwMemPos)) == 0x45455053) + { + m_nDefaultSpeed = lpStream[dwMemPos+9]; + dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8; + } + // SLEN + if (dwMemPos >= dwMemLength) return TRUE; + if (*((DWORD *)(lpStream + dwMemPos)) == 0x4E454C53) + { + npatterns = lpStream[dwMemPos+9]; + dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8; + } + // PLEN + if (dwMemPos >= dwMemLength) return TRUE; + if (*((DWORD *)(lpStream + dwMemPos)) == 0x4E454C50) + { + norders = lpStream[dwMemPos+9]; + dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8; + } + // PATT + if (dwMemPos >= dwMemLength) return TRUE; + if (*((DWORD *)(lpStream + dwMemPos)) == 0x54544150) + { + UINT orderlen = norders; + if (orderlen >= MAX_ORDERS) orderlen = MAX_ORDERS-1; + for (UINT i=0; i<orderlen; i++) Order[i] = lpStream[dwMemPos+10+i]; + for (UINT j=orderlen; j>1; j--) { if (Order[j-1]) break; Order[j-1] = 0xFF; } + dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8; + } + // PBOD + UINT npat = 0; + while ((dwMemPos+10 < dwMemLength) && (*((DWORD *)(lpStream + dwMemPos)) == 0x444F4250)) + { + DWORD dwPos = dwMemPos + 10; + UINT rows = lpStream[dwMemPos+9]; + if (!rows) rows = 64; + if (npat < MAX_PATTERNS) + { + if ((Patterns[npat] = AllocatePattern(rows, m_nChannels)) == NULL) return TRUE; + MODCOMMAND *m = Patterns[npat]; + PatternSize[npat] = rows; + UINT imax = m_nChannels*rows; + for (UINT i=0; i<imax; i++, m++, dwPos+=4) + { + if (dwPos+4 > dwMemLength) break; + const BYTE *p = lpStream+dwPos; + UINT note = p[0]; + if (note) + { + m->note = note + 48; + m->instr = p[1] + 1; + } + UINT command = p[2]; + UINT param = p[3]; + m->param = param; + switch(command) + { + // 0: no effect + case 0: + break; + // 1: Portamento Up + case 1: + case 17: + case 30: + if (param) m->command = CMD_PORTAMENTOUP; + break; + // 2: Portamento Down + case 2: + case 13: + case 21: + if (param) m->command = CMD_PORTAMENTODOWN; + break; + // 10: Arpeggio + case 10: + case 11: + case 12: + m->command = CMD_ARPEGGIO; + break; + // 15: Filter + case 15: + m->command = CMD_MODCMDEX; + m->param = param & 0x0F; + break; + // 25: Position Jump + case 25: + m->command = CMD_POSITIONJUMP; + break; + // 28: Set Speed + case 28: + m->command = CMD_SPEED; + break; + // 31: Volume Control + case 31: + if (param <= 0x40) m->command = CMD_VOLUME; else + if (param <= 0x50) { m->command = CMD_VOLUMESLIDE; m->param &= 0x0F; if (!m->param) m->param = 0x0F; } else + if (param <= 0x60) { m->command = CMD_VOLUMESLIDE; m->param = (param & 0x0F) << 4; if (!m->param) m->param = 0xF0; } else + if (param <= 0x70) { m->command = CMD_MODCMDEX; m->param = 0xB0 | (param & 0x0F); if (!(param & 0x0F)) m->param = 0xBF; } else + if (param <= 0x80) { m->command = CMD_MODCMDEX; m->param = 0xA0 | (param & 0x0F); if (!(param & 0x0F)) m->param = 0xAF; } + break; + } + } + } + npat++; + dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8; + } + // SBOD + UINT nsmp = 1; + while ((dwMemPos+10 < dwMemLength) && (*((DWORD *)(lpStream + dwMemPos)) == 0x444F4253)) + { + if (nsmp < MAX_SAMPLES) ReadSample(&Ins[nsmp], RS_PCM8S, (LPSTR)(lpStream+dwMemPos+8), dwMemLength-dwMemPos-8); + dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8; + nsmp++; + } + return TRUE; +} + diff --git a/lib/libmodplug/src/load_pat.cpp b/lib/libmodplug/src/load_pat.cpp new file mode 100644 index 0000000000..9f8a6d838c --- /dev/null +++ b/lib/libmodplug/src/load_pat.cpp @@ -0,0 +1,1576 @@ +/* + + MikMod Sound System + + By Jake Stine of Divine Entertainment (1996-2000) + + Support: + If you find problems with this code, send mail to: + air@divent.org + + Distribution / Code rights: + Use this source code in any fashion you see fit. Giving me credit where + credit is due is optional, depending on your own levels of integrity and + honesty. + + ----------------------------------------- + Module: LOAD_PAT + + PAT sample loader. + by Peter Grootswagers (2006) + <email:pgrootswagers@planet.nl> + + It's primary purpose is loading samples for the .abc and .mid modules + Can also be used stand alone, in that case a tune (frere Jacques) + is generated using al samples available in the .pat file + + Portability: + All systems - all compilers (hopefully) +*/ + +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include <math.h> +#include <ctype.h> +#include <unistd.h> // for sleep + +#ifdef NEWMIKMOD +#include "mikmod.h" +#include "uniform.h" +typedef UBYTE BYTE; +typedef UWORD WORD; +#else +#include "stdafx.h" +#include "sndfile.h" +#endif + +#include "load_pat.h" + +#ifdef MSC_VER +#define DIRDELIM '\\' +#define TIMIDITYCFG "C:\\TIMIDITY\\TIMIDITY.CFG" +#define PATHFORPAT "C:\\TIMIDITY\\INSTRUMENTS" +#else +#define DIRDELIM '/' +#define TIMIDITYCFG "/usr/local/share/timidity/timidity.cfg" +#define PATHFORPAT "/usr/local/share/timidity/instruments" +#endif + +#define PAT_ENV_PATH2CFG "MMPAT_PATH_TO_CFG" + +// 128 gm and 63 drum +#define MAXSMP 191 +static char midipat[MAXSMP][40]; +static char pathforpat[128]; +static char timiditycfg[128]; + +#pragma pack(1) + +typedef struct { + char header[12]; // ascizz GF1PATCH110 + char gravis_id[10]; // allways ID#000002 + char description[60]; + BYTE instruments; + BYTE voices; + BYTE channels; + WORD waveforms; + WORD master_volume; + DWORD data_size; + char reserved[36]; +} PatchHeader; + +typedef struct { + WORD instrument_id; + char instrument_name[16]; + DWORD instrument_size; + BYTE layers; + char reserved[40]; +} InstrumentHeader; + +typedef struct { + BYTE layer_dup; + BYTE layer_id; + DWORD layer_size; + BYTE samples; + char reserved[40]; +} LayerHeader; + +typedef struct { + char wave_name[7]; + BYTE fractions; + DWORD wave_size; + DWORD start_loop; + DWORD end_loop; + WORD sample_rate; + DWORD low_frequency ; + DWORD high_frequency; + DWORD root_frequency; + short int tune; + BYTE balance; + BYTE envelope_rate[6]; + BYTE envelope_offset[6]; + BYTE tremolo_sweep; + BYTE tremolo_rate; + BYTE tremolo_depth; + BYTE vibrato_sweep; + BYTE vibrato_rate; + BYTE vibrato_depth; + BYTE modes; + DWORD scale_frequency; + DWORD scale_factor; + char reserved[32]; +} WaveHeader; + +// WaveHeader.modes bits +#define PAT_16BIT 1 +#define PAT_UNSIGNED 2 +#define PAT_LOOP 4 +#define PAT_PINGPONG 8 +#define PAT_BACKWARD 16 +#define PAT_SUSTAIN 32 +#define PAT_ENVELOPE 64 +#define PAT_CLAMPED 128 + +#define C4SPD 8363 +#define C4mHz 523251 +#define C4 523.251 +#define PI 3.141592653589793 +#define OMEGA ((2.0 * PI * C4)/(float)C4SPD) + +/************************************************************************** +**************************************************************************/ +#ifdef NEWMIKMOD +static char PAT_Version[] = "Timidity GUS Patch v1.0"; +#endif +static BYTE pat_gm_used[MAXSMP]; +static BYTE pat_loops[MAXSMP]; + +/************************************************************************** +**************************************************************************/ + +typedef struct _PATHANDLE +{ +#ifdef NEWMIKMOD + MM_ALLOC *allochandle; +#endif + char patname[16]; + int samples; +} PATHANDLE; + +// local prototypes +static int pat_getopt(const char *s, const char *o, int dflt); + +static void pat_message(const char *s1, const char *s2) +{ + char txt[256]; + if( strlen(s1) + strlen(s2) > 255 ) return; + sprintf(txt, s1, s2); +#ifdef NEWMIKMOD + _mmlog(txt); +#else + fprintf(stderr, "load_pat > %s\n", txt); +#endif +} + +void pat_resetsmp(void) +{ + int i; + for( i=0; i<MAXSMP; i++ ) { + pat_loops[i] = 0; + pat_gm_used[i] = 0; + } +} + +int pat_numsmp() +{ + return strlen((const char *)pat_gm_used); +} + +int pat_numinstr(void) +{ + return strlen((const char *)pat_gm_used); +} + +int pat_smptogm(int smp) +{ + if( smp < MAXSMP ) + return pat_gm_used[smp - 1]; + return 1; +} + +int pat_gmtosmp(int gm) +{ + int smp; + for( smp=0; pat_gm_used[smp]; smp++ ) + if( pat_gm_used[smp] == gm ) + return smp+1; + if( smp < MAXSMP ) { + pat_gm_used[smp] = gm; + return smp+1; + } + return 1; +} + +int pat_smplooped(int smp) +{ + if( smp < MAXSMP ) return pat_loops[smp - 1]; + return 1; +} + +const char *pat_gm_name(int gm) +{ + static char buf[40]; + if( gm < 1 || gm > MAXSMP ) { + sprintf(buf, "invalid gm %d", gm); + return buf; + } + return midipat[gm - 1]; +} + +int pat_gm_drumnr(int n) +{ + if( n < 25 ) return 129; + if( n+129-25 < MAXSMP ) + return 129+n-25; // timidity.cfg drum patches start at 25 + return MAXSMP; +} + +int pat_gm_drumnote(int n) +{ + char *p; + p = strchr(midipat[pat_gm_drumnr(n)-1], ':'); + if( p ) return pat_getopt(p+1, "note", n); + return n; +} + +static float pat_sinus(int i) +{ + float res = sinf(OMEGA * (float)i); + return res; +} + +static float pat_square(int i) +{ + float res = 30.0 * sinf(OMEGA * (float)i); + if( res > 0.99 ) return 0.99; + if( res < -0.99 ) return -0.99; + return res; +} + +static float pat_sawtooth(int i) +{ + float res = OMEGA * (float)i; + while( res > 2 * PI ) + res -= 2 * PI; + i = 2; + if( res > PI ) { + res = PI - res; + i = -2; + } + res = (float)i * res / PI; + if( res > 0.9 ) return 1.0 - res; + if( res < -0.9 ) return 1.0 + res; + return res; +} + +typedef float (*PAT_SAMPLE_FUN)(int); + +static PAT_SAMPLE_FUN pat_fun[] = { pat_sinus, pat_square, pat_sawtooth }; + +#ifdef NEWMIKMOD + +#define MMFILE MMSTREAM +#define mmftell(x) _mm_ftell(x) +#define mmfseek(f,p,w) _mm_fseek(f,p,w) +#define mmreadUBYTES(buf,sz,f) _mm_read_UBYTES(buf,sz,f) + +#else + +#define MMSTREAM FILE +#define _mm_fopen(name,mode) fopen(name,mode) +#define _mm_fgets(f,buf,sz) fgets(buf,sz,f) +#define _mm_fseek(f,pos,whence) fseek(f,pos,whence) +#define _mm_ftell(f) ftell(f) +#define _mm_read_UBYTES(buf,sz,f) fread(buf,sz,1,f) +#define _mm_read_SBYTES(buf,sz,f) fread(buf,sz,1,f) +#define _mm_feof(f) feof(f) +#define _mm_fclose(f) fclose(f) +#define DupStr(h,buf,sz) strdup(buf) +#define _mm_calloc(h,n,sz) calloc(n,sz) +#define _mm_recalloc(h,buf,sz,elsz) realloc(buf,sz) +#define _mm_free(h,p) free(p) + +typedef struct { + char *mm; + int sz; + int pos; +} MMFILE; + +static long mmftell(MMFILE *mmfile) +{ + return mmfile->pos; +} + +static void mmfseek(MMFILE *mmfile, long p, int whence) +{ + switch(whence) { + case SEEK_SET: + mmfile->pos = p; + break; + case SEEK_CUR: + mmfile->pos += p; + break; + case SEEK_END: + mmfile->pos = mmfile->sz + p; + break; + } +} + +static void mmreadUBYTES(BYTE *buf, long sz, MMFILE *mmfile) +{ + memcpy(buf, &mmfile->mm[mmfile->pos], sz); + mmfile->pos += sz; +} + +static void mmreadSBYTES(char *buf, long sz, MMFILE *mmfile) +{ + memcpy(buf, &mmfile->mm[mmfile->pos], sz); + mmfile->pos += sz; +} + +#endif + +void pat_init_patnames(void) +{ + int i,j; + char *p, *q; + char line[80]; + MMSTREAM *mmcfg; + strcpy(pathforpat, PATHFORPAT); + strcpy(timiditycfg, TIMIDITYCFG); + p = getenv(PAT_ENV_PATH2CFG); + if( p ) { + strcpy(timiditycfg,p); + strcpy(pathforpat,p); + strcat(timiditycfg,"/timidity.cfg"); + strcat(pathforpat,"/instruments"); + } + mmcfg = _mm_fopen(timiditycfg,"r"); + for( i=0; i<MAXSMP; i++ ) midipat[i][0] = '\0'; + if( !mmcfg ) { + pat_message("can not open %s, use environment variable " PAT_ENV_PATH2CFG " for the directory", timiditycfg); + } + else { + // read in bank 0 and drum patches + j = 0; + _mm_fgets(mmcfg, line, 80); + while( !_mm_feof(mmcfg) ) { + if( isdigit(line[0]) ) { + i = atoi(line); + if( i < MAXSMP && i >= 0 ) { + p = strchr(line,'/')+1; + if(j) + q = midipat[pat_gm_drumnr(i)-1]; + else + q = midipat[i]; + while( *p && !isspace(*p) ) *q++ = *p++; + if( isspace(*p) ) { + *q++ = ':'; + while( isspace(*p) ) { + while( isspace(*p) ) p++; + while( *p && !isspace(*p) ) *q++ = *p++; + if( isspace(*p) ) *q++ = ' '; + } + } + *q++ = '\0'; + } + } + if( !strncmp(line,"drumset",7) ) j = 1; + _mm_fgets(mmcfg, line, 80); + } + _mm_fclose(mmcfg); + } + q = midipat[0]; + j = 0; + for( i=0; i<MAXSMP; i++ ) { + if( midipat[i][0] ) q = midipat[i]; + else { + strcpy(midipat[i],q); + if( midipat[i][0] == '\0' ) j++; + } + } + if( j ) { + for( i=MAXSMP; i-- > 0; ) { + if( midipat[i][0] ) q = midipat[i]; + else strcpy(midipat[i],q); + } + } +} + +static char *pat_build_path(char *fname, int pat) +{ + char *ps; + ps = strrchr(midipat[pat], ':'); + if( ps ) { + sprintf(fname, "%s%c%s", pathforpat, DIRDELIM, midipat[pat]); + strcpy(strrchr(fname, ':'), ".pat"); + return ps; + } + sprintf(fname, "%s%c%s.pat", pathforpat, DIRDELIM, midipat[pat]); + return 0; +} + +static void pat_read_patname(PATHANDLE *h, MMFILE *mmpat) { + InstrumentHeader ih; + mmfseek(mmpat,sizeof(PatchHeader), SEEK_SET); + mmreadUBYTES((BYTE *)&ih, sizeof(InstrumentHeader), mmpat); + strncpy(h->patname, ih.instrument_name, 16); + h->patname[15] = '\0'; +} + +static void pat_read_layerheader(MMSTREAM *mmpat, LayerHeader *hl) +{ + _mm_fseek(mmpat,sizeof(PatchHeader)+sizeof(InstrumentHeader), SEEK_SET); + _mm_read_UBYTES((BYTE *)hl, sizeof(LayerHeader), mmpat); +} + +static void pat_get_layerheader(MMFILE *mmpat, LayerHeader *hl) +{ + InstrumentHeader ih; + mmfseek(mmpat,sizeof(PatchHeader), SEEK_SET); + mmreadUBYTES((BYTE *)&ih, sizeof(InstrumentHeader), mmpat); + mmreadUBYTES((BYTE *)hl, sizeof(LayerHeader), mmpat); + strncpy(hl->reserved, ih.instrument_name, 40); +} + +static int pat_read_numsmp(MMFILE *mmpat) { + LayerHeader hl; + pat_get_layerheader(mmpat, &hl); + return hl.samples; +} + +static void pat_read_waveheader(MMSTREAM *mmpat, WaveHeader *hw, int layer) +{ + long int pos, bestpos=0; + LayerHeader hl; + ULONG bestfreq, freqdist; + int i; + // read the very first and maybe only sample + pat_read_layerheader(mmpat, &hl); + if( hl.samples > 1 ) { + if( layer ) { + if( layer > hl.samples ) layer = hl.samples; // you don't fool me.... + for( i=1; i<layer; i++ ) { + _mm_read_UBYTES((BYTE *)hw, sizeof(WaveHeader), mmpat); + _mm_fseek(mmpat, hw->wave_size, SEEK_CUR); + } + } + else { + bestfreq = C4mHz * 1000; // big enough + for( i=0; i<hl.samples; i++ ) { + pos = _mm_ftell(mmpat); + _mm_read_UBYTES((BYTE *)hw, sizeof(WaveHeader), mmpat); + if( hw->root_frequency > C4mHz ) + freqdist = hw->root_frequency - C4mHz; + else + freqdist = 2 * (C4mHz - hw->root_frequency); + if( freqdist < bestfreq ) { + bestfreq = freqdist; + bestpos = pos; + } + _mm_fseek(mmpat, hw->wave_size, SEEK_CUR); + } + _mm_fseek(mmpat, bestpos, SEEK_SET); + } + } + _mm_read_UBYTES((BYTE *)hw, sizeof(WaveHeader), mmpat); + strncpy(hw->reserved, hl.reserved, 36); + if( hw->start_loop >= hw->wave_size ) { + hw->start_loop = 0; + hw->end_loop = 0; + hw->modes &= ~PAT_LOOP; // mask off loop indicator + } + if( hw->end_loop > hw->wave_size ) + hw->end_loop = hw->wave_size; +} + +#ifndef NEWMIKMOD +static void pat_get_waveheader(MMFILE *mmpat, WaveHeader *hw, int layer) +{ + long int pos, bestpos=0; + LayerHeader hl; + ULONG bestfreq, freqdist; + int i; + // read the very first and maybe only sample + pat_get_layerheader(mmpat, &hl); + if( hl.samples > 1 ) { + if( layer ) { + if( layer > hl.samples ) layer = hl.samples; // you don't fool me.... + for( i=1; i<layer; i++ ) { + mmreadUBYTES((BYTE *)hw, sizeof(WaveHeader), mmpat); + mmfseek(mmpat, hw->wave_size, SEEK_CUR); + } + } + else { + bestfreq = C4mHz * 1000; // big enough + for( i=0; i<hl.samples; i++ ) { + pos = mmftell(mmpat); + mmreadUBYTES((BYTE *)hw, sizeof(WaveHeader), mmpat); + if( hw->root_frequency > C4mHz ) + freqdist = hw->root_frequency - C4mHz; + else + freqdist = 2 * (C4mHz - hw->root_frequency); + if( freqdist < bestfreq ) { + bestfreq = freqdist; + bestpos = pos; + } + mmfseek(mmpat, hw->wave_size, SEEK_CUR); + } + mmfseek(mmpat, bestpos, SEEK_SET); + } + } + mmreadUBYTES((BYTE *)hw, sizeof(WaveHeader), mmpat); + if( hw->start_loop >= hw->wave_size ) { + hw->start_loop = 0; + hw->end_loop = 0; + hw->modes &= ~PAT_LOOP; // mask off loop indicator + } + if( hw->end_loop > hw->wave_size ) + hw->end_loop = hw->wave_size; +} +#endif + +static int pat_readpat_attr(int pat, WaveHeader *hw, int layer) +{ + char fname[128]; + MMSTREAM *mmpat; + pat_build_path(fname, pat); + mmpat = _mm_fopen(fname, "r"); + if( !mmpat ) + return 0; + pat_read_waveheader(mmpat, hw, layer); + _mm_fclose(mmpat); + return 1; +} + +static void pat_amplify(char *b, int num, int amp, int m) +{ + char *pb; + BYTE *pu; + short int *pi; + WORD *pw; + int i,n,v; + n = num; + if( m & PAT_16BIT ) { // 16 bit + n >>= 1; + if( m & 2 ) { // unsigned + pw = (WORD *)b; + for( i=0; i<n; i++ ) { + v = (((int)(*pw) - 0x8000) * amp) / 100; + if( v < -0x8000 ) v = -0x8000; + if( v > 0x7fff ) v = 0x7fff; + *pw++ = v + 0x8000; + } + } + else { + pi = (short int *)b; + for( i=0; i<n; i++ ) { + v = ((*pi) * amp) / 100; + if( v < -0x8000 ) v = -0x8000; + if( v > 0x7fff ) v = 0x7fff; + *pi++ = v; + } + } + } + else { + if( m & 2 ) { // unsigned + pu = (BYTE *)b; + for( i=0; i<n; i++ ) { + v = (((int)(*pu) - 0x80) * amp) / 100; + if( v < -0x80 ) v = -0x80; + if( v > 0x7f ) v = 0x7f; + *pu++ = v + 0x80; + } + } + else { + pb = (char *)b; + for( i=0; i<n; i++ ) { + v = ((*pb) * amp) / 100; + if( v < -0x80 ) v = -0x80; + if( v > 0x7f ) v = 0x7f; + *pb++ = v; + } + } + } +} + +static int pat_getopt(const char *s, const char *o, int dflt) +{ + const char *p; + if( !s ) return dflt; + p = strstr(s,o); + if( !p ) return dflt; + return atoi(strchr(p,'=')+1); +} + +static void pat_readpat(int pat, char *dest, int num) +{ + static int readlasttime = 0, wavesize = 0; + static MMSTREAM *mmpat = 0; + static char *opt = 0; + int amp; + char fname[128]; + WaveHeader hw; +#ifdef NEWMIKMOD + static int patlast = MAXSMP; + if( !dest ) { // reset + if( mmpat ) _mm_fclose(mmpat); + readlasttime = 0; + wavesize = 0; + mmpat = 0; + patlast = MAXSMP; + return; + } + if( pat != patlast ) { // reset for other instrument + if( mmpat ) _mm_fclose(mmpat); + readlasttime = 0; + patlast = pat; + } +#endif + if( !readlasttime ) { + opt=pat_build_path(fname, pat); + mmpat = _mm_fopen(fname, "r"); + if( !mmpat ) + return; + pat_read_waveheader(mmpat, &hw, 0); + wavesize = hw.wave_size; + } + _mm_read_SBYTES(dest, num, mmpat); + amp = pat_getopt(opt,"amp",100); + if( amp != 100 ) pat_amplify(dest, num, amp, hw.modes); + readlasttime += num; + if( readlasttime < wavesize ) return; + readlasttime = 0; + _mm_fclose(mmpat); + mmpat = 0; +} + +#ifdef NEWMIKMOD +// next code pinched from dec_raw.c and rebuild to load bytes from different places +// ===================================================================================== +static void *dec_pat_Init(MMSTREAM *mmfp) +{ + pat_readpat(0,0,0); // initialize pat loader + return (void *)mmfp; +} + +static void dec_pat_Cleanup(void *raw) +{ +} + +static BOOL dec_pat_Decompress16Bit(void *raw, short int *dest, int cbcount, MMSTREAM *mmfp) +{ + long samplenum = _mm_ftell(mmfp) - 1; +#else +static BOOL dec_pat_Decompress16Bit(short int *dest, int cbcount, int samplenum) +{ +#endif + int i; + PAT_SAMPLE_FUN f; + if( samplenum < MAXSMP ) pat_readpat(samplenum, (char *)dest, cbcount*2); + else { + f = pat_fun[(samplenum - MAXSMP) % 3]; + for( i=0; i<cbcount; i++ ) + dest[i] = (short int)(32000.0*f(i)); + } + return cbcount; +} + +// convert 8 bit data to 16 bit! +// We do the conversion in reverse so that the data we're converting isn't overwritten +// by the result. +static void pat_blowup_to16bit(short int *dest, int cbcount) { + char *s; + short int *d; + int t; + s = (char *)dest; + d = dest; + s += cbcount; + d += cbcount; + for(t=0; t<cbcount; t++) + { s--; + d--; + *d = (*s) << 8; + } +} + +#ifdef NEWMIKMOD +static BOOL dec_pat_Decompress8Bit(void *raw, short int *dest, int cbcount, MMSTREAM *mmfp) +{ + long samplenum = _mm_ftell(mmfp) - 1; +#else +static BOOL dec_pat_Decompress8Bit(short int *dest, int cbcount, int samplenum) +{ +#endif + int i; + PAT_SAMPLE_FUN f; + if( samplenum < MAXSMP ) pat_readpat(samplenum, (char *)dest, cbcount); + else { + f = pat_fun[(samplenum - MAXSMP) % 3]; + for( i=0; i<cbcount; i++ ) + dest[i] = (char)(120.0*f(i)); + } + pat_blowup_to16bit(dest, cbcount); + return cbcount; +} + +#ifdef NEWMIKMOD +SL_DECOMPRESS_API dec_pat = +{ + NULL, + SL_COMPRESS_RAW, + dec_pat_Init, + dec_pat_Cleanup, + dec_pat_Decompress16Bit, + dec_pat_Decompress8Bit, +}; +#endif + +// ===================================================================================== +#ifdef NEWMIKMOD +BOOL PAT_Test(MMSTREAM *mmfile) +#else +BOOL CSoundFile::TestPAT(const BYTE *lpStream, DWORD dwMemLength) +#endif +// ===================================================================================== +{ + PatchHeader ph; +#ifdef NEWMIKMOD + _mm_fseek(mmfile,0,SEEK_SET); + _mm_read_UBYTES((BYTE *)&ph, sizeof(PatchHeader), mmfile); +#else + if( dwMemLength < sizeof(PatchHeader) ) return 0; + memcpy((BYTE *)&ph, lpStream, sizeof(PatchHeader)); +#endif + if( !strcmp(ph.header,"GF1PATCH110") && !strcmp(ph.gravis_id,"ID#000002") ) return 1; + return 0; +} + +// ===================================================================================== +static PATHANDLE *PAT_Init(void) +{ + PATHANDLE *retval; +#ifdef NEWMIKMOD + MM_ALLOC *allochandle; + + allochandle = _mmalloc_create("Load_PAT", NULL); + retval = (PATHANDLE *)_mm_calloc(allochandle, 1,sizeof(PATHANDLE)); + if( !retval ) return NULL; + SL_RegisterDecompressor(&dec_raw); // we can not get the samples out of our own routines...! +#else + retval = (PATHANDLE *)calloc(1,sizeof(PATHANDLE)); + if( !retval ) return NULL; +#endif + return retval; +} + +// ===================================================================================== +static void PAT_Cleanup(PATHANDLE *handle) +// ===================================================================================== +{ +#ifdef NEWMIKMOD + if(handle && handle->allochandle) { + _mmalloc_close(handle->allochandle); + handle->allochandle = 0; + } +#else + if(handle) { + free(handle); + } +#endif +} + +static char tune[] = "c d e c|c d e c|e f g..|e f g..|gagfe c|gagfe c|c G c..|c G c..|"; +static int pat_note(int abc) +{ + switch( abc ) { + case 'C': return 48; + case 'D': return 50; + case 'E': return 52; + case 'F': return 53; + case 'G': return 55; + case 'A': return 57; + case 'B': return 59; + case 'c': return 60; + case 'd': return 62; + case 'e': return 64; + case 'f': return 65; + case 'g': return 67; + case 'a': return 69; + case 'b': return 71; + default: + break; + } + return 0; +} + +int pat_modnote(int midinote) +{ + int n; + n = midinote; +#ifdef NEWMIKMOD + if( n < 12 ) n++; + else n-=11; +#else + n += 13; +#endif + return n; +} + +// ===================================================================================== +#ifdef NEWMIKMOD +static void PAT_ReadPatterns(UNIMOD *of, PATHANDLE *h, int numpat) +// ===================================================================================== +{ + int pat,row,i,ch; + BYTE n,ins,vol; + int t; + int tt1, tt2; + UNITRK_EFFECT eff; + + tt2 = (h->samples - 1) * 16 + 128; + for( pat = 0; pat < numpat; pat++ ) { + utrk_reset(of->ut); + for( row = 0; row < 64; row++ ) { + tt1 = (pat * 64 + row); + for( ch = 0; ch < h->samples; ch++ ) { + t = tt1 - ch * 16; + if( t >= 0 ) { + i = tt2 - 16 * ((h->samples - 1 - ch) & 3); + if( tt1 < i ) { + t = t % 64; + if( isalpha(tune[t]) ) { + utrk_settrack(of->ut, ch); + n = pat_modnote(pat_note(tune[t])); + ins = ch; + vol = 100; + if( (t % 16) == 0 ) { + vol += vol / 10; + if( vol > 127 ) vol = 127; + } + utrk_write_inst(of->ut, ins); + utrk_write_note(of->ut, n); // <- normal note + pt_write_effect(of->ut, 0xc, vol); + } + if( tt1 == i - 1 && ch == 0 && row < 63 ) { + eff.effect = UNI_GLOB_PATBREAK; + eff.param.u = 0; + eff.framedly = UFD_RUNONCE; + utrk_write_global(of->ut, &eff, UNIMEM_NONE); + } + } + else { + if( tt1 == i ) { + eff.param.u = 0; + eff.effect = UNI_NOTEKILL; + utrk_write_local(of->ut, &eff, UNIMEM_NONE); + } + } + } + } + utrk_newline(of->ut); + } + if(!utrk_dup_pattern(of->ut,of)) return; + } +} + +#else + +static void PAT_ReadPatterns(MODCOMMAND *pattern[], WORD psize[], PATHANDLE *h, int numpat) +// ===================================================================================== +{ + int pat,row,i,ch; + BYTE n,ins,vol; + int t; + int tt1, tt2; + MODCOMMAND *m; + if( numpat > MAX_PATTERNS ) numpat = MAX_PATTERNS; + + tt2 = (h->samples - 1) * 16 + 128; + for( pat = 0; pat < numpat; pat++ ) { + pattern[pat] = CSoundFile::AllocatePattern(64, h->samples); + if( !pattern[pat] ) return; + psize[pat] = 64; + for( row = 0; row < 64; row++ ) { + tt1 = (pat * 64 + row); + for( ch = 0; ch < h->samples; ch++ ) { + t = tt1 - ch * 16; + m = &pattern[pat][row * h->samples + ch]; + m->param = 0; + m->command = CMD_NONE; + if( t >= 0 ) { + i = tt2 - 16 * ((h->samples - 1 - ch) & 3); + if( tt1 < i ) { + t = t % 64; + if( isalpha(tune[t]) ) { + n = pat_modnote(pat_note(tune[t])); + ins = ch + 1; + vol = 40; + if( (t % 16) == 0 ) { + vol += vol / 10; + if( vol > 64 ) vol = 64; + } + m->instr = ins; + m->note = n; // <- normal note + m->volcmd = VOLCMD_VOLUME; + m->vol = vol; + } + if( tt1 == i - 1 && ch == 0 && row < 63 ) { + m->command = CMD_PATTERNBREAK; + } + } + else { + if( tt1 == i ) { + m->param = 0; + m->command = CMD_KEYOFF; + m->volcmd = VOLCMD_VOLUME; + m->vol = 0; + } + } + } + } + } + } +} + +#endif + +// calculate the best speed that approximates the pat root frequency as a C note +static ULONG pat_patrate_to_C4SPD(ULONG patRate , ULONG patMilliHz) +{ + ULONG u; + double x, y; + u = patMilliHz; + x = 0.1 * patRate; + x = x * C4mHz; + y = u * 0.4; + x = x / y; + u = (ULONG)(x+0.5); + return u; +} + +// return relative position in samples for the rate starting with offset start ending with offset end +static int pat_envelope_rpos(int rate, int start, int end) +{ + int r, p, t, s; + // rate byte is 3 bits exponent and 6 bits increment size + // eeiiiiii + // every 8 to the power ee the volume is incremented/decremented by iiiiii + // Thank you Gravis for this weirdness... + r = 3 - ((rate >> 6) & 3) * 3; + p = rate & 0x3f; + if( !p ) return 0; + t = end - start; + if( !t ) return 0; + if (t < 0) t = -t; + s = (t << r)/ p; + return s; +} + +static void pat_modenv(WaveHeader *hw, int mpos[6], int mvol[6]) +{ + int i, sum, s; + BYTE *prate = hw->envelope_rate, *poffset = hw->envelope_offset; + for( i=0; i<6; i++ ) { + mpos[i] = 0; + mvol[i] = 64; + } + if( !memcmp(prate, "??????", 6) || poffset[5] >= 100 ) return; // weird rates or high env end volume + if( !(hw->modes & PAT_SUSTAIN) ) return; // no sustain thus no need for envelope + s = hw->wave_size; + if (s == 0) return; + if( hw->modes & PAT_16BIT ) + s >>= 1; + // offsets 0 1 2 3 4 5 are distributed over 0 2 4 6 8 10, the odd numbers are set in between + sum = 0; + for( i=0; i<6; i++ ) { + mvol[i] = poffset[i]; + mpos[i] = pat_envelope_rpos(prate[i], i? poffset[i-1]: 0, poffset[i]); + sum += mpos[i]; + } + if( sum == 0 ) return; + if( sum > s ) { + for( i=0; i<6; i++ ) + mpos[i] = (s * mpos[i]) / sum; + } + for( i=1; i<6; i++ ) + mpos[i] += mpos[i-1]; + for( i=0; i<6 ; i++ ) { + mpos[i] = (256 * mpos[i]) / s; + mpos[i]++; + if( i > 0 && mpos[i] <= mpos[i-1] ) { + if( mvol[i] == mvol[i-1] ) mpos[i] = mpos[i-1]; + else mpos[i] = mpos[i-1] + 1; + } + if( mpos[i] > 256 ) mpos[i] = 256; + } + mvol[5] = 0; // kill Bill.... +} + +#ifdef NEWMIKMOD +static void pat_setpat_inst(WaveHeader *hw, INSTRUMENT *d, int smp) +{ + int u, inuse; + int envpoint[6], envvolume[6]; + for(u=0; u<120; u++) { + d->samplenumber[u] = smp; + d->samplenote[u] = smp; + } + d->globvol = 64; + d->volfade = 0; + d->volflg = EF_CARRY; + d->panflg = EF_CARRY; + if( hw->modes & PAT_ENVELOPE ) d->volflg |= EF_ON; + if( hw->modes & PAT_SUSTAIN ) d->volflg |= EF_SUSTAIN; + if( (hw->modes & PAT_LOOP) && (hw->start_loop != hw->end_loop) ) d->volflg |= EF_LOOP; + d->volsusbeg = 1; + d->volsusend = 1; + d->volbeg = 1; + d->volend = 2; + d->volpts = 6; + // scale volume envelope: + inuse = 0; + pat_modenv(hw, envpoint, envvolume); + for(u=0; u<6; u++) + { + if( envvolume[u] != 64 ) inuse = 1; + d->volenv[u].val = envvolume[u]<<2; + d->volenv[u].pos = envpoint[u]; + } + if(!inuse) d->volpts = 0; + d->pansusbeg = 0; + d->pansusend = 0; + d->panbeg = 0; + d->panend = 0; + d->panpts = 0; + // scale panning envelope: + for(u=0; u<12; u++) + { + d->panenv[u].val = 0; + d->panenv[u].pos = 0; + } + d->panpts = 0; +} +#else +static void pat_setpat_inst(WaveHeader *hw, INSTRUMENTHEADER *d, int smp) +{ + int u, inuse; + int envpoint[6], envvolume[6]; + d->nMidiProgram = 0; + d->nFadeOut = 0; + d->nPan = 128; + d->nPPC = 5*12; + d->dwFlags = 0; + if( hw->modes & PAT_ENVELOPE ) d->dwFlags |= ENV_VOLUME; + if( hw->modes & PAT_SUSTAIN ) d->dwFlags |= ENV_VOLSUSTAIN; + if( (hw->modes & PAT_LOOP) && (hw->start_loop != hw->end_loop) ) d->dwFlags |= ENV_VOLLOOP; + d->nVolEnv = 6; + //if (!d->nVolEnv) d->dwFlags &= ~ENV_VOLUME; + d->nPanEnv = 0; + d->nVolSustainBegin = 1; + d->nVolSustainEnd = 1; + d->nVolLoopStart = 1; + d->nVolLoopEnd = 2; + d->nPanSustainBegin = 0; + d->nPanSustainEnd = 0; + d->nPanLoopStart = 0; + d->nPanLoopEnd = 0; + d->nGlobalVol = 64; + pat_modenv(hw, envpoint, envvolume); + inuse = 0; + for( u=0; u<6; u++) + { + if( envvolume[u] != 64 ) inuse = 1; + d->VolPoints[u] = envpoint[u]; + d->VolEnv[u] = envvolume[u]; + d->PanPoints[u] = 0; + d->PanEnv[u] = 0; + if (u) + { + if (d->VolPoints[u] < d->VolPoints[u-1]) + { + d->VolPoints[u] &= 0xFF; + d->VolPoints[u] += d->VolPoints[u-1] & 0xFF00; + if (d->VolPoints[u] < d->VolPoints[u-1]) d->VolPoints[u] += 0x100; + } + } + } + if( !inuse ) d->nVolEnv = 0; + for( u=0; u<128; u++) + { + d->NoteMap[u] = u+1; + d->Keyboard[u] = smp; + } +} +#endif +#ifdef NEWMIKMOD +static void PATinst(UNIMOD *of, INSTRUMENT *d, int smp, int gm) +#else +static void PATinst(INSTRUMENTHEADER *d, int smp, int gm) +#endif +{ + WaveHeader hw; + char s[32]; + memset(s,0,32); + if( pat_readpat_attr(gm-1, &hw, 0) ) { + pat_setpat_inst(&hw, d, smp); + } + else { + hw.modes = PAT_16BIT|PAT_ENVELOPE|PAT_SUSTAIN|PAT_LOOP; + hw.start_loop = 0; + hw.end_loop = 30000; + hw.wave_size = 30000; +// envelope rates and offsets pinched from timidity's acpiano.pat sample no 1 + hw.envelope_rate[0] = 0x3f; + hw.envelope_rate[1] = 0x3f; + hw.envelope_rate[2] = 0x3f; + hw.envelope_rate[3] = 0x08|(3<<6); + hw.envelope_rate[4] = 0x3f; + hw.envelope_rate[5] = 0x3f; + hw.envelope_offset[0] = 246; + hw.envelope_offset[1] = 246; + hw.envelope_offset[2] = 246; + hw.envelope_offset[3] = 0; + hw.envelope_offset[4] = 0; + hw.envelope_offset[5] = 0; + strncpy(hw.reserved, midipat[gm-1], sizeof(hw.reserved)); + pat_setpat_inst(&hw, d, smp); + } + if( hw.reserved[0] ) + strncpy(s, hw.reserved, 32); + else + strncpy(s, midipat[gm-1], 32); +#ifdef NEWMIKMOD + d->insname = DupStr(of->allochandle, s,28); +#else + s[31] = '\0'; + memset(d->name, 0, 32); + strcpy((char *)d->name, s); + strncpy(s, midipat[gm-1], 12); + s[11] = '\0'; + memset(d->filename, 0, 12); + strcpy((char *)d->filename, s); +#endif +} + +#ifdef NEWMIKMOD +static void pat_setpat_attr(WaveHeader *hw, UNISAMPLE *q, int gm) +{ + q->seekpos = gm; // dec_pat expects the midi samplenumber in this + q->speed = pat_patrate_to_C4SPD(hw->sample_rate , hw->root_frequency); + q->length = hw->wave_size; + q->loopstart = hw->start_loop; + q->loopend = hw->end_loop; + q->volume = 0x40; + if( hw->modes & PAT_16BIT ) { + q->format |= SF_16BITS; + q->length >>= 1; + q->loopstart >>= 1; + q->loopend >>= 1; + q->speed <<= 1; + } + if( (hw->modes & PAT_UNSIGNED)==0 ) q->format |= SF_SIGNED; + if( hw->modes & PAT_LOOP ) { + q->flags |= SL_LOOP; + if( hw->modes & PAT_PINGPONG ) q->flags |= SL_SUSTAIN_BIDI; + if( hw->modes & PAT_SUSTAIN ) q->flags |= SL_SUSTAIN_LOOP; + } +} +#else +static void pat_setpat_attr(WaveHeader *hw, MODINSTRUMENT *q) +{ + q->nC4Speed = pat_patrate_to_C4SPD(hw->sample_rate , hw->root_frequency); + q->nLength = hw->wave_size; + q->nLoopStart = hw->start_loop; + q->nLoopEnd = hw->end_loop; + q->nVolume = 256; + if( hw->modes & PAT_16BIT ) { + q->nLength >>= 1; + q->nLoopStart >>= 1; + q->nLoopEnd >>= 1; + } + if( hw->modes & PAT_LOOP ) { + q->uFlags |= CHN_LOOP; + if( hw->modes & PAT_PINGPONG ) q->uFlags |= CHN_PINGPONGSUSTAIN; + if( hw->modes & PAT_SUSTAIN ) q->uFlags |= CHN_SUSTAINLOOP; + } +} +#endif + +// ========================== +// Load those darned Samples! +#ifdef NEWMIKMOD +static void PATsample(UNIMOD *of, UNISAMPLE *q, int smp, int gm) +#else +static void PATsample(CSoundFile *cs, MODINSTRUMENT *q, int smp, int gm) +#endif +{ + WaveHeader hw; + char s[32]; + sprintf(s, "%d:%s", smp-1, midipat[gm-1]); +#ifdef NEWMIKMOD + q->samplename = DupStr(of->allochandle, s,28); + if( pat_readpat_attr(gm-1, &hw, 0) ) { + pat_setpat_attr(&hw, q, gm); + pat_loops[smp-1] = (q->flags & (SL_LOOP | SL_SUSTAIN_LOOP))? 1: 0; + } + else { + q->seekpos = smp + MAXSMP + 1; // dec_pat expects the samplenumber in this + q->speed = C4SPD; + q->length = 30000; + q->loopstart = 0; + q->loopend = 30000; + q->volume = 0x40; + + // Enable aggressive declicking for songs that do not loop and that + // are long enough that they won't be adversely affected. + + q->flags |= SL_LOOP; + q->format |= SF_16BITS; + q->format |= SF_SIGNED; + } + if(!(q->flags & (SL_LOOP | SL_SUSTAIN_LOOP)) && (q->length > 5000)) + q->flags |= SL_DECLICK; +#else + s[31] = '\0'; + memset(cs->m_szNames[smp], 0, 32); + strcpy(cs->m_szNames[smp], s); + q->nGlobalVol = 64; + q->nPan = 128; + q->uFlags = CHN_16BIT; + if( pat_readpat_attr(gm-1, &hw, 0) ) { + char *p; + pat_setpat_attr(&hw, q); + pat_loops[smp-1] = (q->uFlags & CHN_LOOP)? 1: 0; + if( hw.modes & PAT_16BIT ) p = (char *)malloc(hw.wave_size); + else p = (char *)malloc(hw.wave_size * sizeof(short int)); + if( p ) { + if( hw.modes & PAT_16BIT ) { + dec_pat_Decompress16Bit((short int *)p, hw.wave_size>>1, gm - 1); + cs->ReadSample(q, (hw.modes&PAT_UNSIGNED)?RS_PCM16U:RS_PCM16S, (LPSTR)p, hw.wave_size); + } + else { + dec_pat_Decompress8Bit((short int *)p, hw.wave_size, gm - 1); + cs->ReadSample(q, (hw.modes&PAT_UNSIGNED)?RS_PCM16U:RS_PCM16S, (LPSTR)p, hw.wave_size * sizeof(short int)); + } + free(p); + } + } + else { + char *p; + q->nC4Speed = C4SPD; + q->nLength = 30000; + q->nLoopStart = 0; + q->nLoopEnd = 30000; + q->nVolume = 256; + q->uFlags |= CHN_LOOP; + q->uFlags |= CHN_16BIT; + p = (char *)malloc(q->nLength*sizeof(short int)); + if( p ) { + dec_pat_Decompress8Bit((short int *)p, q->nLength, smp + MAXSMP - 1); + cs->ReadSample(q, RS_PCM16S, (LPSTR)p, q->nLength*2); + free(p); + } + } +#endif +} + +// ===================================================================================== +BOOL PAT_Load_Instruments(void *c) +{ + uint32_t t; +#ifdef NEWMIKMOD + UNIMOD *of = (UNIMOD *)c; + INSTRUMENT *d; + UNISAMPLE *q; + if( !pat_numsmp() ) pat_gmtosmp(1); // make sure there is a sample + of->numsmp = pat_numsmp(); + of->numins = pat_numinstr(); + if(!AllocInstruments(of)) return FALSE; + if(!AllocSamples(of, 0)) return FALSE; + d = of->instruments; + for(t=1; t<=of->numins; t++) { + PATinst(of, d, t, pat_smptogm(t)); + d++; + } + q = of->samples; + for(t=1; t<=of->numsmp; t++) { + PATsample(of, q, t, pat_smptogm(t)); + q++; + } + SL_RegisterDecompressor(&dec_pat); // fool him to generate samples +#else + CSoundFile *of=(CSoundFile *)c; + if( !pat_numsmp() ) pat_gmtosmp(1); // make sure there is a sample + of->m_nSamples = pat_numsmp() + 1; // xmms modplug does not use slot zero + of->m_nInstruments = pat_numinstr() + 1; + for(t=1; t<of->m_nInstruments; t++) { // xmms modplug doesn't use slot zero + if( (of->Headers[t] = new INSTRUMENTHEADER) == NULL ) return FALSE; + memset(of->Headers[t], 0, sizeof(INSTRUMENTHEADER)); + PATinst(of->Headers[t], t, pat_smptogm(t)); + } + for(t=1; t<of->m_nSamples; t++) { // xmms modplug doesn't use slot zero + PATsample(of, &of->Ins[t], t, pat_smptogm(t)); + } + // copy last of the mohicans to entry 0 for XMMS modinfo to work.... + t = of->m_nInstruments - 1; + if( (of->Headers[0] = new INSTRUMENTHEADER) == NULL ) return FALSE; + memcpy(of->Headers[0], of->Headers[t], sizeof(INSTRUMENTHEADER)); + memset(of->Headers[0]->name, 0, 32); + strncpy((char *)of->Headers[0]->name, "Timidity GM patches", 32); + t = of->m_nSamples - 1; + memcpy(&of->Ins[0], &of->Ins[t], sizeof(MODINSTRUMENT)); +#endif + return TRUE; +} +// ===================================================================================== +#ifdef NEWMIKMOD +BOOL PAT_Load(PATHANDLE *h, UNIMOD *of, MMSTREAM *mmfile) +#else +BOOL CSoundFile::ReadPAT(const BYTE *lpStream, DWORD dwMemLength) +#endif +{ + static int avoid_reentry = 0; + char buf[60]; + int t; +#ifdef NEWMIKMOD + UNISAMPLE *q; + INSTRUMENT *d; +#define m_nDefaultTempo of->inittempo +#else + PATHANDLE *h; + int numpat; + MMFILE mm, *mmfile; + MODINSTRUMENT *q; + INSTRUMENTHEADER *d; + if( !TestPAT(lpStream, dwMemLength) ) return FALSE; + h = PAT_Init(); + if( !h ) return FALSE; + mmfile = &mm; + mm.mm = (char *)lpStream; + mm.sz = dwMemLength; + mm.pos = 0; +#endif + while( avoid_reentry ) sleep(1); + avoid_reentry = 1; + pat_read_patname(h, mmfile); + h->samples = pat_read_numsmp(mmfile); + if( strlen(h->patname) ) + sprintf(buf,"%s canon %d-v (Fr. Jacques)", h->patname, h->samples); + else + sprintf(buf,"%d-voice canon (Fr. Jacques)", h->samples); +#ifdef NEWMIKMOD + of->songname = DupStr(of->allochandle, buf, strlen(buf)); +#else + if( strlen(buf) > 31 ) buf[31] = '\0'; // chop it of + strcpy(m_szNames[0], buf); +#endif + m_nDefaultTempo = 60; // 120 / 2 + t = (h->samples - 1) * 16 + 128; + if( t % 64 ) t += 64; + t = t / 64; +#ifdef NEWMIKMOD + of->memsize = PTMEM_LAST; // Number of memory slots to reserve! + of->modtype = _mm_strdup(of->allochandle, PAT_Version); + of->reppos = 0; + of->numins = h->samples; + of->numsmp = h->samples; + of->initspeed = 6; + of->numchn = h->samples; + of->numpat = t; + of->numpos = of->numpat; // one repeating pattern + of->numtrk = of->numpat * of->numchn; + of->initvolume = 64; + of->pansep=128; + // allocate resources + if(!AllocPositions(of, of->numpos)) { + avoid_reentry = 0; + return FALSE; + } + if(!AllocInstruments(of)) { + avoid_reentry = 0; + return FALSE; + } + if(!AllocSamples(of, 0)) { + avoid_reentry = 0; + return FALSE; + } + // orderlist + for(t=0; t<of->numpos; t++) + of->positions[t] = t; + d = of->instruments; + for(t=1; t<=of->numins; t++) { + WaveHeader hw; + char s[32]; + sprintf(s, "%s", h->patname); + d->insname = DupStr(of->allochandle, s,28); + pat_read_waveheader(mmfile, &hw, t); + pat_setpat_inst(&hw, d, t); + } + q = of->samples; + for(t=1; t<=of->numsmp; t++) { + WaveHeader hw; + char s[28]; + pat_read_waveheader(mmfile, &hw, t); + pat_setpat_attr(&hw, q, _mm_ftell(mmfile)); + memset(s,0,28); + if( hw.wave_name[0] ) + sprintf(s, "%d:%s", t, hw.wave_name); + else + sprintf(s, "%d:%s", t, h->patname); + q->samplename = DupStr(of->allochandle, s,28); + if(!(q->flags & (SL_LOOP | SL_SUSTAIN_LOOP)) && (q->length > 5000)) + q->flags |= SL_DECLICK; + q++; + } +#else + m_nType = MOD_TYPE_PAT; + m_nInstruments = h->samples + 1; // we know better but use each sample in the pat... + m_nSamples = h->samples + 1; // xmms modplug does not use slot zero + m_nDefaultSpeed = 6; + m_nChannels = h->samples; + numpat = t; + + m_dwSongFlags = SONG_LINEARSLIDES; + m_nMinPeriod = 28 << 2; + m_nMaxPeriod = 1712 << 3; + // orderlist + for(t=0; t < numpat; t++) + Order[t] = t; + for(t=1; t<(int)m_nInstruments; t++) { // xmms modplug doesn't use slot zero + WaveHeader hw; + char s[32]; + if( (d = new INSTRUMENTHEADER) == NULL ) { + avoid_reentry = 0; + return FALSE; + } + memset(d, 0, sizeof(INSTRUMENTHEADER)); + Headers[t] = d; + sprintf(s, "%s", h->patname); + s[31] = '\0'; + memset(d->name, 0, 32); + strcpy((char *)d->name, s); + s[11] = '\0'; + memset(d->filename, 0, 12); + strcpy((char *)d->filename, s); + pat_get_waveheader(mmfile, &hw, t); + pat_setpat_inst(&hw, d, t); + } + for(t=1; t<(int)m_nSamples; t++) { // xmms modplug doesn't use slot zero + WaveHeader hw; + char s[32]; + char *p; + q = &Ins[t]; // we do not use slot zero + q->nGlobalVol = 64; + q->nPan = 128; + q->uFlags = CHN_16BIT; + pat_get_waveheader(mmfile, &hw, t); + pat_setpat_attr(&hw, q); + memset(s,0,32); + if( hw.wave_name[0] ) + sprintf(s, "%d:%s", t, hw.wave_name); + else { + if( h->patname[0] ) + sprintf(s, "%d:%s", t, h->patname); + else + sprintf(s, "%d:Untitled GM patch", t); + } + s[31] = '\0'; + memset(m_szNames[t], 0, 32); + strcpy(m_szNames[t], s); + if( hw.modes & PAT_16BIT ) p = (char *)malloc(hw.wave_size); + else p = (char *)malloc(hw.wave_size * sizeof(short int)); + if( p ) { + mmreadSBYTES(p, hw.wave_size, mmfile); + if( hw.modes & PAT_16BIT ) { + ReadSample(q, (hw.modes&PAT_UNSIGNED)?RS_PCM16U:RS_PCM16S, (LPSTR)p, hw.wave_size); + } + else { + pat_blowup_to16bit((short int *)p, hw.wave_size); + ReadSample(q, (hw.modes&PAT_UNSIGNED)?RS_PCM16U:RS_PCM16S, (LPSTR)p, hw.wave_size * sizeof(short int)); + } + free(p); + } + } + // copy last of the mohicans to entry 0 for XMMS modinfo to work.... + t = m_nInstruments - 1; + if( (Headers[0] = new INSTRUMENTHEADER) == NULL ) { + avoid_reentry = 0; + return FALSE; + } + memcpy(Headers[0], Headers[t], sizeof(INSTRUMENTHEADER)); + memset(Headers[0]->name, 0, 32); + if( h->patname[0] ) + strncpy((char *)Headers[0]->name, h->patname, 32); + else + strncpy((char *)Headers[0]->name, "Timidity GM patch", 32); + t = m_nSamples - 1; + memcpy(&Ins[0], &Ins[t], sizeof(MODINSTRUMENT)); +#endif +#ifdef NEWMIKMOD + // ============================== + // Load the pattern info now! + if(!AllocTracks(of)) return 0; + if(!AllocPatterns(of)) return 0; + of->ut = utrk_init(of->numchn, h->allochandle); + utrk_memory_reset(of->ut); + utrk_local_memflag(of->ut, PTMEM_PORTAMENTO, TRUE, FALSE); + PAT_ReadPatterns(of, h, of->numpat); + // ============================================================ + // set panning positions + for(t=0; t<of->numchn; t++) { + of->panning[t] = PAN_LEFT+((t+2)%5)*((PAN_RIGHT - PAN_LEFT)/5); // 0x30 = std s3m val + } +#else + // ============================== + // Load the pattern info now! + PAT_ReadPatterns(Patterns, PatternSize, h, numpat); + // ============================================================ + // set panning positions + for(t=0; t<(int)m_nChannels; t++) { + ChnSettings[t].nPan = 0x30+((t+2)%5)*((0xD0 - 0x30)/5); // 0x30 = std s3m val + ChnSettings[t].nVolume = 64; + } +#endif + avoid_reentry = 0; // it is safe now, I'm finished +#ifndef NEWMIKMOD + PAT_Cleanup(h); // we dont need it anymore +#endif + return 1; +} + +#ifdef NEWMIKMOD +// ===================================================================================== +CHAR *PAT_LoadTitle(MMSTREAM *mmfile) +// ===================================================================================== +{ + PATHANDLE dummy; + pat_read_patname(&dummy, mmfile); + return(DupStr(NULL, dummy.patname,strlen(dummy.patname))); +} + +MLOADER load_pat = +{ + "PAT", + "PAT loader 1.0", + 0x30, + NULL, + PAT_Test, + (void *(*)(void))PAT_Init, + (void (*)(ML_HANDLE *))PAT_Cleanup, + /* Every single loader seems to need one of these! */ + (BOOL (*)(ML_HANDLE *, UNIMOD *, MMSTREAM *))PAT_Load, + PAT_LoadTitle +}; +#endif diff --git a/lib/libmodplug/src/load_pat.h b/lib/libmodplug/src/load_pat.h new file mode 100644 index 0000000000..21db1f7ba8 --- /dev/null +++ b/lib/libmodplug/src/load_pat.h @@ -0,0 +1,27 @@ +#ifndef LOAD_PAT_H +#define LOAD_PAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +void pat_init_patnames(void); +void pat_resetsmp(void); +int pat_numinstr(void); +int pat_numsmp(void); +int pat_smptogm(int smp); +int pat_gmtosmp(int gm); +int pat_gm_drumnr(int n); +int pat_gm_drumnote(int n); +const char *pat_gm_name(int gm); +int pat_modnote(int midinote); +int pat_smplooped(int smp); +//#ifdef NEWMIKMOD +BOOL PAT_Load_Instruments(void *c); +//#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/libmodplug/src/load_psm.cpp b/lib/libmodplug/src/load_psm.cpp new file mode 100644 index 0000000000..19966a680a --- /dev/null +++ b/lib/libmodplug/src/load_psm.cpp @@ -0,0 +1,839 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + + +/////////////////////////////////////////////////// +// +// PSM module loader +// +/////////////////////////////////////////////////// +#include "stdafx.h" +#include "sndfile.h" + +//#define PSM_LOG + +#define PSM_ID_NEW 0x204d5350 +#define PSM_ID_OLD 0xfe4d5350 +#define IFFID_FILE 0x454c4946 +#define IFFID_TITL 0x4c544954 +#define IFFID_SDFT 0x54464453 +#define IFFID_PBOD 0x444f4250 +#define IFFID_SONG 0x474e4f53 +#define IFFID_PATT 0x54544150 +#define IFFID_DSMP 0x504d5344 +#define IFFID_OPLH 0x484c504f + +#pragma pack(1) + +typedef struct _PSMCHUNK +{ + DWORD id; + DWORD len; + DWORD listid; +} PSMCHUNK; + +typedef struct _PSMSONGHDR +{ + CHAR songname[8]; // "MAINSONG" + BYTE reserved1; + BYTE reserved2; + BYTE channels; +} PSMSONGHDR; + +typedef struct _PSMPATTERN +{ + DWORD size; + DWORD name; + WORD rows; + WORD reserved1; + BYTE data[4]; +} PSMPATTERN; + +typedef struct _PSMSAMPLE +{ + BYTE flags; + CHAR songname[8]; + DWORD smpid; + CHAR samplename[34]; + DWORD reserved1; + BYTE reserved2; + BYTE insno; + BYTE reserved3; + DWORD length; + DWORD loopstart; + DWORD loopend; + WORD reserved4; + BYTE defvol; + DWORD reserved5; + DWORD samplerate; + BYTE reserved6[19]; +} PSMSAMPLE; + +#pragma pack() + + +BOOL CSoundFile::ReadPSM(LPCBYTE lpStream, DWORD dwMemLength) +//----------------------------------------------------------- +{ + PSMCHUNK *pfh = (PSMCHUNK *)lpStream; + DWORD dwMemPos, dwSongPos; + DWORD smpnames[MAX_SAMPLES]; + DWORD patptrs[MAX_PATTERNS]; + BYTE samplemap[MAX_SAMPLES]; + UINT nPatterns; + + // Chunk0: "PSM ",filesize,"FILE" + if (dwMemLength < 256) return FALSE; + if (pfh->id == PSM_ID_OLD) + { + #ifdef PSM_LOG + Log("Old PSM format not supported\n"); + #endif + return FALSE; + } + if ((pfh->id != PSM_ID_NEW) || (pfh->len+12 > dwMemLength) || (pfh->listid != IFFID_FILE)) return FALSE; + m_nType = MOD_TYPE_PSM; + m_nChannels = 16; + m_nSamples = 0; + nPatterns = 0; + dwMemPos = 12; + dwSongPos = 0; + for (UINT iChPan=0; iChPan<16; iChPan++) + { + UINT pan = (((iChPan & 3) == 1) || ((iChPan&3)==2)) ? 0xC0 : 0x40; + ChnSettings[iChPan].nPan = pan; + } + while (dwMemPos+8 < dwMemLength) + { + PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos); + if ((pchunk->len >= dwMemLength - 8) || (dwMemPos + pchunk->len + 8 > dwMemLength)) break; + dwMemPos += 8; + PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos); + ULONG len = pchunk->len; + if (len) switch(pchunk->id) + { + // "TITL": Song title + case IFFID_TITL: + if (!pdata[0]) { pdata++; len--; } + memcpy(m_szNames[0], pdata, (len>31) ? 31 : len); + m_szNames[0][31] = 0; + break; + // "PBOD": Pattern + case IFFID_PBOD: + if ((len >= 12) && (nPatterns < MAX_PATTERNS)) + { + patptrs[nPatterns++] = dwMemPos-8; + } + break; + // "SONG": Song description + case IFFID_SONG: + if ((len >= sizeof(PSMSONGHDR)+8) && (!dwSongPos)) + { + dwSongPos = dwMemPos - 8; + } + break; + // "DSMP": Sample Data + case IFFID_DSMP: + if ((len >= sizeof(PSMSAMPLE)) && (m_nSamples+1 < MAX_SAMPLES)) + { + m_nSamples++; + MODINSTRUMENT *pins = &Ins[m_nSamples]; + PSMSAMPLE *psmp = (PSMSAMPLE *)pdata; + smpnames[m_nSamples] = psmp->smpid; + memcpy(m_szNames[m_nSamples], psmp->samplename, 31); + m_szNames[m_nSamples][31] = 0; + samplemap[m_nSamples-1] = (BYTE)m_nSamples; + // Init sample + pins->nGlobalVol = 0x40; + pins->nC4Speed = psmp->samplerate; + pins->nLength = psmp->length; + pins->nLoopStart = psmp->loopstart; + pins->nLoopEnd = psmp->loopend; + pins->nPan = 128; + pins->nVolume = (psmp->defvol+1) * 2; + pins->uFlags = (psmp->flags & 0x80) ? CHN_LOOP : 0; + if (pins->nLoopStart > 0) pins->nLoopStart--; + // Point to sample data + pdata += 0x60; + len -= 0x60; + // Load sample data + if ((pins->nLength > 3) && (len > 3)) + { + ReadSample(pins, RS_PCM8D, (LPCSTR)pdata, len); + } else + { + pins->nLength = 0; + } + } + break; + #if 0 + default: + { + CHAR s[8], s2[64]; + *(DWORD *)s = pchunk->id; + s[4] = 0; + wsprintf(s2, "%s: %4d bytes @ %4d\n", s, pchunk->len, dwMemPos); + OutputDebugString(s2); + } + #endif + } + dwMemPos += pchunk->len; + } + // Step #1: convert song structure + PSMSONGHDR *pSong = (PSMSONGHDR *)(lpStream+dwSongPos+8); + if ((!dwSongPos) || (pSong->channels < 2) || (pSong->channels > 32)) return TRUE; + m_nChannels = pSong->channels; + // Valid song header -> convert attached chunks + { + DWORD dwSongEnd = dwSongPos + 8 + *(DWORD *)(lpStream+dwSongPos+4); + dwMemPos = dwSongPos + 8 + 11; // sizeof(PSMCHUNK)+sizeof(PSMSONGHDR) + while (dwMemPos + 8 < dwSongEnd) + { + PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos); + dwMemPos += 8; + if ((pchunk->len > dwSongEnd) || (dwMemPos + pchunk->len > dwSongEnd)) break; + PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos); + ULONG len = pchunk->len; + switch(pchunk->id) + { + case IFFID_OPLH: + if (len >= 0x20) + { + UINT pos = len - 3; + while (pos > 5) + { + BOOL bFound = FALSE; + pos -= 5; + DWORD dwName = *(DWORD *)(pdata+pos); + for (UINT i=0; i<nPatterns; i++) + { + DWORD dwPatName = ((PSMPATTERN *)(lpStream+patptrs[i]+8))->name; + if (dwName == dwPatName) + { + bFound = TRUE; + break; + } + } + if ((!bFound) && (pdata[pos+1] > 0) && (pdata[pos+1] <= 0x10) + && (pdata[pos+3] > 0x40) && (pdata[pos+3] < 0xC0)) + { + m_nDefaultSpeed = pdata[pos+1]; + m_nDefaultTempo = pdata[pos+3]; + break; + } + } + UINT iOrd = 0; + while ((pos+5<len) && (iOrd < MAX_ORDERS)) + { + DWORD dwName = *(DWORD *)(pdata+pos); + for (UINT i=0; i<nPatterns; i++) + { + DWORD dwPatName = ((PSMPATTERN *)(lpStream+patptrs[i]+8))->name; + if (dwName == dwPatName) + { + Order[iOrd++] = i; + break; + } + } + pos += 5; + } + } + break; + } + dwMemPos += pchunk->len; + } + } + + // Step #2: convert patterns + for (UINT nPat=0; nPat<nPatterns; nPat++) + { + PSMPATTERN *pPsmPat = (PSMPATTERN *)(lpStream+patptrs[nPat]+8); + ULONG len = *(DWORD *)(lpStream+patptrs[nPat]+4) - 12; + UINT nRows = pPsmPat->rows; + if (len > pPsmPat->size) len = pPsmPat->size; + if ((nRows < 64) || (nRows > 256)) nRows = 64; + PatternSize[nPat] = nRows; + if ((Patterns[nPat] = AllocatePattern(nRows, m_nChannels)) == NULL) break; + MODCOMMAND *m = Patterns[nPat]; + BYTE *p = pPsmPat->data; + UINT pos = 0; + UINT row = 0; + UINT oldch = 0; + BOOL bNewRow = FALSE; + #ifdef PSM_LOG + Log("Pattern %d at offset 0x%04X\n", nPat, (DWORD)(p - (BYTE *)lpStream)); + #endif + while ((row < nRows) && (pos+1 < len)) + { + UINT flags = p[pos++]; + UINT ch = p[pos++]; + + #ifdef PSM_LOG + //Log("flags+ch: %02X.%02X\n", flags, ch); + #endif + if (((flags & 0xf0) == 0x10) && (ch <= oldch) /*&& (!bNewRow)*/) + { + if ((pos+1<len) && (!(p[pos] & 0x0f)) && (p[pos+1] < m_nChannels)) + { + #ifdef PSM_LOG + //if (!nPat) Log("Continuing on new row\n"); + #endif + row++; + m += m_nChannels; + oldch = ch; + continue; + } + } + if ((pos >= len) || (row >= nRows)) break; + if (!(flags & 0xf0)) + { + #ifdef PSM_LOG + //if (!nPat) Log("EOR(%d): %02X.%02X\n", row, p[pos], p[pos+1]); + #endif + row++; + m += m_nChannels; + bNewRow = TRUE; + oldch = ch; + continue; + } + bNewRow = FALSE; + if (ch >= m_nChannels) + { + #ifdef PSM_LOG + if (!nPat) Log("Invalid channel row=%d (0x%02X.0x%02X)\n", row, flags, ch); + #endif + ch = 0; + } + // Note + Instr + if ((flags & 0x40) && (pos+1 < len)) + { + UINT note = p[pos++]; + UINT nins = p[pos++]; + #ifdef PSM_LOG + //if (!nPat) Log("note+ins: %02X.%02X\n", note, nins); + if ((!nPat) && (nins >= m_nSamples)) Log("WARNING: invalid instrument number (%d)\n", nins); + #endif + if ((note) && (note < 0x80)) note = (note>>4)*12+(note&0x0f)+12+1; + m[ch].instr = samplemap[nins]; + m[ch].note = note; + } + // Volume + if ((flags & 0x20) && (pos < len)) + { + m[ch].volcmd = VOLCMD_VOLUME; + m[ch].vol = p[pos++] / 2; + } + // Effect + if ((flags & 0x10) && (pos+1 < len)) + { + UINT command = p[pos++]; + UINT param = p[pos++]; + // Convert effects + switch(command) + { + // 01: fine volslide up + case 0x01: command = CMD_VOLUMESLIDE; param |= 0x0f; break; + // 04: fine volslide down + case 0x04: command = CMD_VOLUMESLIDE; param>>=4; param |= 0xf0; break; + // 0C: portamento up + case 0x0C: command = CMD_PORTAMENTOUP; param = (param+1)/2; break; + // 0E: portamento down + case 0x0E: command = CMD_PORTAMENTODOWN; param = (param+1)/2; break; + // 33: Position Jump + case 0x33: command = CMD_POSITIONJUMP; break; + // 34: Pattern break + case 0x34: command = CMD_PATTERNBREAK; break; + // 3D: speed + case 0x3D: command = CMD_SPEED; break; + // 3E: tempo + case 0x3E: command = CMD_TEMPO; break; + // Unknown + default: + #ifdef PSM_LOG + Log("Unknown PSM effect pat=%d row=%d ch=%d: %02X.%02X\n", nPat, row, ch, command, param); + #endif + command = param = 0; + } + m[ch].command = (BYTE)command; + m[ch].param = (BYTE)param; + } + oldch = ch; + } + #ifdef PSM_LOG + if (pos < len) + { + Log("Pattern %d: %d/%d[%d] rows (%d bytes) -> %d bytes left\n", nPat, row, nRows, pPsmPat->rows, pPsmPat->size, len-pos); + } + #endif + } + + // Done (finally!) + return TRUE; +} + + +////////////////////////////////////////////////////////////// +// +// PSM Old Format +// + +/* + +CONST + c_PSM_MaxOrder = $FF; + c_PSM_MaxSample = $FF; + c_PSM_MaxChannel = $0F; + + TYPE + PPSM_Header = ^TPSM_Header; + TPSM_Header = RECORD + PSM_Sign : ARRAY[01..04] OF CHAR; { PSM + #254 } + PSM_SongName : ARRAY[01..58] OF CHAR; + PSM_Byte00 : BYTE; + PSM_Byte1A : BYTE; + PSM_Unknown00 : BYTE; + PSM_Unknown01 : BYTE; + PSM_Unknown02 : BYTE; + PSM_Speed : BYTE; + PSM_Tempo : BYTE; + PSM_Unknown03 : BYTE; + PSM_Unknown04 : WORD; + PSM_OrderLength : WORD; + PSM_PatternNumber : WORD; + PSM_SampleNumber : WORD; + PSM_ChannelNumber : WORD; + PSM_ChannelUsed : WORD; + PSM_OrderPosition : LONGINT; + PSM_ChannelSettingPosition : LONGINT; + PSM_PatternPosition : LONGINT; + PSM_SamplePosition : LONGINT; + { *** perhaps there are some more infos in a larger header, + but i have not decoded it and so it apears here NOT } + END; + + PPSM_Sample = ^TPSM_Sample; + TPSM_Sample = RECORD + PSM_SampleFileName : ARRAY[01..12] OF CHAR; + PSM_SampleByte00 : BYTE; + PSM_SampleName : ARRAY[01..22] OF CHAR; + PSM_SampleUnknown00 : ARRAY[01..02] OF BYTE; + PSM_SamplePosition : LONGINT; + PSM_SampleUnknown01 : ARRAY[01..04] OF BYTE; + PSM_SampleNumber : BYTE; + PSM_SampleFlags : WORD; + PSM_SampleLength : LONGINT; + PSM_SampleLoopBegin : LONGINT; + PSM_SampleLoopEnd : LONGINT; + PSM_Unknown03 : BYTE; + PSM_SampleVolume : BYTE; + PSM_SampleC5Speed : WORD; + END; + + PPSM_SampleList = ^TPSM_SampleList; + TPSM_SampleList = ARRAY[01..c_PSM_MaxSample] OF TPSM_Sample; + + PPSM_Order = ^TPSM_Order; + TPSM_Order = ARRAY[00..c_PSM_MaxOrder] OF BYTE; + + PPSM_ChannelSettings = ^TPSM_ChannelSettings; + TPSM_ChannelSettings = ARRAY[00..c_PSM_MaxChannel] OF BYTE; + + CONST + PSM_NotesInPattern : BYTE = $00; + PSM_ChannelInPattern : BYTE = $00; + + CONST + c_PSM_SetSpeed = 60; + + FUNCTION PSM_Size(FileName : STRING;FilePosition : LONGINT) : LONGINT; + BEGIN + END; + + PROCEDURE PSM_UnpackPattern(VAR Source,Destination;PatternLength : WORD); + VAR + Witz : ARRAY[00..04] OF WORD; + I1,I2 : WORD; + I3,I4 : WORD; + TopicalByte : ^BYTE; + Pattern : PUnpackedPattern; + ChannelP : BYTE; + NoteP : BYTE; + InfoByte : BYTE; + CodeByte : BYTE; + InfoWord : WORD; + Effect : BYTE; + Opperand : BYTE; + Panning : BYTE; + Volume : BYTE; + PrevInfo : BYTE; + InfoIndex : BYTE; + BEGIN + Pattern := @Destination; + TopicalByte := @Source; + { *** Initialize patttern } + FOR I2 := 0 TO c_Maximum_NoteIndex DO + FOR I3 := 0 TO c_Maximum_ChannelIndex DO + BEGIN + Pattern^[I2,I3,c_Pattern_NoteIndex] := $FF; + Pattern^[I2,I3,c_Pattern_SampleIndex] := $00; + Pattern^[I2,I3,c_Pattern_VolumeIndex] := $FF; + Pattern^[I2,I3,c_Pattern_PanningIndex] := $FF; + Pattern^[I2,I3,c_Pattern_EffectIndex] := $00; + Pattern^[I2,I3,c_Pattern_OpperandIndex] := $00; + END; + { *** Byte-pointer on first pattern-entry } + ChannelP := $00; + NoteP := $00; + InfoByte := $00; + PrevInfo := $00; + InfoIndex := $02; + { *** read notes in pattern } + PSM_NotesInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); + PSM_ChannelInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); + { *** unpack pattern } + WHILE (INTEGER(PatternLength) > 0) AND (NoteP < c_Maximum_NoteIndex) DO + BEGIN + { *** Read info-byte } + InfoByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); + IF InfoByte <> $00 THEN + BEGIN + ChannelP := InfoByte AND $0F; + IF InfoByte AND 128 = 128 THEN { note and sample } + BEGIN + { *** read note } + CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); + DEC(CodeByte); + CodeByte := CodeByte MOD 12 * 16 + CodeByte DIV 12 + 2; + Pattern^[NoteP,ChannelP,c_Pattern_NoteIndex] := CodeByte; + { *** read sample } + CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); + Pattern^[NoteP,ChannelP,c_Pattern_SampleIndex] := CodeByte; + END; + IF InfoByte AND 64 = 64 THEN { Volume } + BEGIN + CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); + Pattern^[NoteP,ChannelP,c_Pattern_VolumeIndex] := CodeByte; + END; + IF InfoByte AND 32 = 32 THEN { effect AND opperand } + BEGIN + Effect := TopicalByte^; INC(TopicalByte); DEC(PatternLength); + Opperand := TopicalByte^; INC(TopicalByte); DEC(PatternLength); + CASE Effect OF + c_PSM_SetSpeed: + BEGIN + Effect := c_I_Set_Speed; + END; + ELSE + BEGIN + Effect := c_I_NoEffect; + Opperand := $00; + END; + END; + Pattern^[NoteP,ChannelP,c_Pattern_EffectIndex] := Effect; + Pattern^[NoteP,ChannelP,c_Pattern_OpperandIndex] := Opperand; + END; + END ELSE INC(NoteP); + END; + END; + + PROCEDURE PSM_Load(FileName : STRING;FilePosition : LONGINT;VAR Module : PModule;VAR ErrorCode : WORD); + { *** caution : Module has to be inited before!!!! } + VAR + Header : PPSM_Header; + Sample : PPSM_SampleList; + Order : PPSM_Order; + ChannelSettings : PPSM_ChannelSettings; + MultiPurposeBuffer : PByteArray; + PatternBuffer : PUnpackedPattern; + TopicalParaPointer : WORD; + + InFile : FILE; + I1,I2 : WORD; + I3,I4 : WORD; + TempW : WORD; + TempB : BYTE; + TempP : PByteArray; + TempI : INTEGER; + { *** copy-vars for loop-extension } + CopySource : LONGINT; + CopyDestination : LONGINT; + CopyLength : LONGINT; + BEGIN + { *** try to open file } + ASSIGN(InFile,FileName); +{$I-} + RESET(InFile,1); +{$I+} + IF IORESULT <> $00 THEN + BEGIN + EXIT; + END; +{$I-} + { *** seek start of module } + IF FILESIZE(InFile) < FilePosition THEN + BEGIN + EXIT; + END; + SEEK(InFile,FilePosition); + { *** look for enough memory for temporary variables } + IF MEMAVAIL < SIZEOF(TPSM_Header) + SIZEOF(TPSM_SampleList) + + SIZEOF(TPSM_Order) + SIZEOF(TPSM_ChannelSettings) + + SIZEOF(TByteArray) + SIZEOF(TUnpackedPattern) + THEN + BEGIN + EXIT; + END; + { *** init dynamic variables } + NEW(Header); + NEW(Sample); + NEW(Order); + NEW(ChannelSettings); + NEW(MultiPurposeBuffer); + NEW(PatternBuffer); + { *** read header } + BLOCKREAD(InFile,Header^,SIZEOF(TPSM_Header)); + { *** test if this is a DSM-file } + IF NOT ((Header^.PSM_Sign[1] = 'P') AND (Header^.PSM_Sign[2] = 'S') AND + (Header^.PSM_Sign[3] = 'M') AND (Header^.PSM_Sign[4] = #254)) THEN + BEGIN + ErrorCode := c_NoValidFileFormat; + CLOSE(InFile); + EXIT; + END; + { *** read order } + SEEK(InFile,FilePosition + Header^.PSM_OrderPosition); + BLOCKREAD(InFile,Order^,Header^.PSM_OrderLength); + { *** read channelsettings } + SEEK(InFile,FilePosition + Header^.PSM_ChannelSettingPosition); + BLOCKREAD(InFile,ChannelSettings^,SIZEOF(TPSM_ChannelSettings)); + { *** read samplelist } + SEEK(InFile,FilePosition + Header^.PSM_SamplePosition); + BLOCKREAD(InFile,Sample^,Header^.PSM_SampleNumber * SIZEOF(TPSM_Sample)); + { *** copy header to intern NTMIK-structure } + Module^.Module_Sign := 'MF'; + Module^.Module_FileFormatVersion := $0100; + Module^.Module_SampleNumber := Header^.PSM_SampleNumber; + Module^.Module_PatternNumber := Header^.PSM_PatternNumber; + Module^.Module_OrderLength := Header^.PSM_OrderLength; + Module^.Module_ChannelNumber := Header^.PSM_ChannelNumber+1; + Module^.Module_Initial_GlobalVolume := 64; + Module^.Module_Initial_MasterVolume := $C0; + Module^.Module_Initial_Speed := Header^.PSM_Speed; + Module^.Module_Initial_Tempo := Header^.PSM_Tempo; +{ *** paragraph 01 start } + Module^.Module_Flags := c_Module_Flags_ZeroVolume * BYTE(1) + + c_Module_Flags_Stereo * BYTE(1) + + c_Module_Flags_ForceAmigaLimits * BYTE(0) + + c_Module_Flags_Panning * BYTE(1) + + c_Module_Flags_Surround * BYTE(1) + + c_Module_Flags_QualityMixing * BYTE(1) + + c_Module_Flags_FastVolumeSlides * BYTE(0) + + c_Module_Flags_SpecialCustomData * BYTE(0) + + c_Module_Flags_SongName * BYTE(1); + I1 := $01; + WHILE (Header^.PSM_SongName[I1] > #00) AND (I1 < c_Module_SongNameLength) DO + BEGIN + Module^.Module_Name[I1] := Header^.PSM_SongName[I1]; + INC(I1); + END; + Module^.Module_Name[c_Module_SongNameLength] := #00; + { *** Init channelsettings } + FOR I1 := 0 TO c_Maximum_ChannelIndex DO + BEGIN + IF I1 < Header^.PSM_ChannelUsed THEN + BEGIN + { *** channel enabled } + Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := 64; + Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := (ChannelSettings^[I1]) * $08; + Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := I1 + $10 * BYTE(ChannelSettings^[I1] > $08) + + c_ChannelSettings_Code_ChannelEnabled * BYTE(1) + + c_ChannelSettings_Code_ChannelDigital * BYTE(1); + Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls := + c_ChannelSettings_Controls_EnhancedMode * BYTE(1) + + c_ChannelSettings_Controls_SurroundMode * BYTE(0); + END + ELSE + BEGIN + { *** channel disabled } + Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := $00; + Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := $00; + Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := $00; + Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls := $00; + END; + END; + { *** init and copy order } + FILLCHAR(Module^.Module_OrderPointer^,c_Maximum_OrderIndex+1,$FF); + MOVE(Order^,Module^.Module_OrderPointer^,Header^.PSM_OrderLength); + { *** read pattern } + SEEK(InFile,FilePosition + Header^.PSM_PatternPosition); + NTMIK_LoaderPatternNumber := Header^.PSM_PatternNumber-1; + FOR I1 := 0 TO Header^.PSM_PatternNumber-1 DO + BEGIN + NTMIK_LoadPatternProcedure; + { *** read length } + BLOCKREAD(InFile,TempW,2); + { *** read pattern } + BLOCKREAD(InFile,MultiPurposeBuffer^,TempW-2); + { *** unpack pattern and set notes per channel to 64 } + PSM_UnpackPattern(MultiPurposeBuffer^,PatternBuffer^,TempW); + NTMIK_PackPattern(MultiPurposeBuffer^,PatternBuffer^,PSM_NotesInPattern); + TempW := WORD(256) * MultiPurposeBuffer^[01] + MultiPurposeBuffer^[00]; + GETMEM(Module^.Module_PatternPointer^[I1],TempW); + MOVE(MultiPurposeBuffer^,Module^.Module_PatternPointer^[I1]^,TempW); + { *** next pattern } + END; + { *** read samples } + NTMIK_LoaderSampleNumber := Header^.PSM_SampleNumber; + FOR I1 := 1 TO Header^.PSM_SampleNumber DO + BEGIN + NTMIK_LoadSampleProcedure; + { *** get index for sample } + I3 := Sample^[I1].PSM_SampleNumber; + { *** clip PSM-sample } + IF Sample^[I1].PSM_SampleLoopEnd > Sample^[I1].PSM_SampleLength + THEN Sample^[I1].PSM_SampleLoopEnd := Sample^[I1].PSM_SampleLength; + { *** init intern sample } + NEW(Module^.Module_SamplePointer^[I3]); + FILLCHAR(Module^.Module_SamplePointer^[I3]^,SIZEOF(TSample),$00); + FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_SampleName,c_Sample_SampleNameLength,#32); + FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_FileName,c_Sample_FileNameLength,#32); + { *** copy informations to intern sample } + I2 := $01; + WHILE (Sample^[I1].PSM_SampleName[I2] > #00) AND (I2 < c_Sample_SampleNameLength) DO + BEGIN + Module^.Module_SamplePointer^[I3]^.Sample_SampleName[I2] := Sample^[I1].PSM_SampleName[I2]; + INC(I2); + END; + Module^.Module_SamplePointer^[I3]^.Sample_Sign := 'DF'; + Module^.Module_SamplePointer^[I3]^.Sample_FileFormatVersion := $00100; + Module^.Module_SamplePointer^[I3]^.Sample_Position := $00000000; + Module^.Module_SamplePointer^[I3]^.Sample_Selector := $0000; + Module^.Module_SamplePointer^[I3]^.Sample_Volume := Sample^[I1].PSM_SampleVolume; + Module^.Module_SamplePointer^[I3]^.Sample_LoopCounter := $00; + Module^.Module_SamplePointer^[I3]^.Sample_C5Speed := Sample^[I1].PSM_SampleC5Speed; + Module^.Module_SamplePointer^[I3]^.Sample_Length := Sample^[I1].PSM_SampleLength; + Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin := Sample^[I1].PSM_SampleLoopBegin; + Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd := Sample^[I1].PSM_SampleLoopEnd; + { *** now it's time for the flags } + Module^.Module_SamplePointer^[I3]^.Sample_Flags := + c_Sample_Flags_DigitalSample * BYTE(1) + + c_Sample_Flags_8BitSample * BYTE(1) + + c_Sample_Flags_UnsignedSampleData * BYTE(1) + + c_Sample_Flags_Packed * BYTE(0) + + c_Sample_Flags_LoopCounter * BYTE(0) + + c_Sample_Flags_SampleName * BYTE(1) + + c_Sample_Flags_LoopActive * + BYTE(Sample^[I1].PSM_SampleFlags AND (LONGINT(1) SHL 15) = (LONGINT(1) SHL 15)); + { *** alloc memory for sample-data } + E_Getmem(Module^.Module_SamplePointer^[I3]^.Sample_Selector, + Module^.Module_SamplePointer^[I3]^.Sample_Position, + Module^.Module_SamplePointer^[I3]^.Sample_Length + c_LoopExtensionSize); + { *** read out data } + EPT(TempP).p_Selector := Module^.Module_SamplePointer^[I3]^.Sample_Selector; + EPT(TempP).p_Offset := $0000; + SEEK(InFile,Sample^[I1].PSM_SamplePosition); + E_BLOCKREAD(InFile,TempP^,Module^.Module_SamplePointer^[I3]^.Sample_Length); + { *** 'coz the samples are signed in a DSM-file -> PC-fy them } + IF Module^.Module_SamplePointer^[I3]^.Sample_Length > 4 THEN + BEGIN + CopyLength := Module^.Module_SamplePointer^[I3]^.Sample_Length; + { *** decode sample } + ASM + DB 066h; MOV CX,WORD PTR CopyLength + { *** load sample selector } + MOV ES,WORD PTR TempP[00002h] + DB 066h; XOR SI,SI + DB 066h; XOR DI,DI + XOR AH,AH + { *** conert all bytes } + @@MainLoop: + DB 026h; DB 067h; LODSB + ADD AL,AH + MOV AH,AL + DB 067h; STOSB + DB 066h; LOOP @@MainLoop + END; + { *** make samples unsigned } + ASM + DB 066h; MOV CX,WORD PTR CopyLength + { *** load sample selector } + MOV ES,WORD PTR TempP[00002h] + DB 066h; XOR SI,SI + DB 066h; XOR DI,DI + { *** conert all bytes } + @@MainLoop: + DB 026h; DB 067h; LODSB + SUB AL,080h + DB 067h; STOSB + DB 066h; LOOP @@MainLoop + END; + { *** Create Loop-Extension } + IF Module^.Module_SamplePointer^[I3]^.Sample_Flags AND c_Sample_Flags_LoopActive = c_Sample_Flags_LoopActive THEN + BEGIN + CopySource := Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin; + CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd; + CopyLength := CopyDestination - CopySource; + ASM + { *** load sample-selector } + MOV ES,WORD PTR TempP[00002h] + DB 066h; MOV DI,WORD PTR CopyDestination + { *** calculate number of full sample-loops to copy } + XOR DX,DX + MOV AX,c_LoopExtensionSize + MOV BX,WORD PTR CopyLength + DIV BX + OR AX,AX + JE @@NoFullLoop + { *** copy some full-loops (size=bx) } + MOV CX,AX + @@InnerLoop: + PUSH CX + DB 066h; MOV SI,WORD PTR CopySource + MOV CX,BX + DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] } + POP CX + LOOP @@InnerLoop + @@NoFullLoop: + { *** calculate number of rest-bytes to copy } + DB 066h; MOV SI,WORD PTR CopySource + MOV CX,DX + DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] } + END; + END + ELSE + BEGIN + CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_Length; + ASM + { *** load sample-selector } + MOV ES,WORD PTR TempP[00002h] + DB 066h; MOV DI,WORD PTR CopyDestination + { *** clear extension } + MOV CX,c_LoopExtensionSize + MOV AL,080h + DB 0F3h; DB 067h,0AAh { REP STOS BYTE PTR ES:[EDI] } + END; + END; + END; + { *** next sample } + END; + { *** init period-ranges } + NTMIK_MaximumPeriod := $0000D600 SHR 1; + NTMIK_MinimumPeriod := $0000D600 SHR 8; + { *** close file } + CLOSE(InFile); + { *** dispose all dynamic variables } + DISPOSE(Header); + DISPOSE(Sample); + DISPOSE(Order); + DISPOSE(ChannelSettings); + DISPOSE(MultiPurposeBuffer); + DISPOSE(PatternBuffer); + { *** set errorcode to noerror } + ErrorCode := c_NoError; + END; + +*/ + diff --git a/lib/libmodplug/src/load_ptm.cpp b/lib/libmodplug/src/load_ptm.cpp new file mode 100644 index 0000000000..3acc5596fc --- /dev/null +++ b/lib/libmodplug/src/load_ptm.cpp @@ -0,0 +1,208 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net>, + * Adam Goode <adam@evdebs.org> (endian and char fixes for PPC) +*/ + +////////////////////////////////////////////// +// PTM PolyTracker module loader // +////////////////////////////////////////////// +#include "stdafx.h" +#include "sndfile.h" + +//#pragma warning(disable:4244) + +#pragma pack(1) + +typedef struct PTMFILEHEADER +{ + CHAR songname[28]; // name of song, asciiz string + CHAR eof; // 26 + BYTE version_lo; // 03 version of file, currently 0203h + BYTE version_hi; // 02 + BYTE reserved1; // reserved, set to 0 + WORD norders; // number of orders (0..256) + WORD nsamples; // number of instruments (1..255) + WORD npatterns; // number of patterns (1..128) + WORD nchannels; // number of channels (voices) used (1..32) + WORD fileflags; // set to 0 + WORD reserved2; // reserved, set to 0 + DWORD ptmf_id; // song identification, 'PTMF' or 0x464d5450 + BYTE reserved3[16]; // reserved, set to 0 + BYTE chnpan[32]; // channel panning settings, 0..15, 0 = left, 7 = middle, 15 = right + BYTE orders[256]; // order list, valid entries 0..nOrders-1 + WORD patseg[128]; // pattern offsets (*16) +} PTMFILEHEADER, *LPPTMFILEHEADER; + +#define SIZEOF_PTMFILEHEADER 608 + + +typedef struct PTMSAMPLE +{ + BYTE sampletype; // sample type (bit array) + CHAR filename[12]; // name of external sample file + BYTE volume; // default volume + WORD nC4Spd; // C4 speed + WORD sampleseg; // sample segment (used internally) + WORD fileofs[2]; // offset of sample data + WORD length[2]; // sample size (in bytes) + WORD loopbeg[2]; // start of loop + WORD loopend[2]; // end of loop + WORD gusdata[8]; + char samplename[28]; // name of sample, asciiz // changed from CHAR + DWORD ptms_id; // sample identification, 'PTMS' or 0x534d5450 +} PTMSAMPLE; + +#define SIZEOF_PTMSAMPLE 80 + +#pragma pack() + + +BOOL CSoundFile::ReadPTM(const BYTE *lpStream, DWORD dwMemLength) +//--------------------------------------------------------------- +{ + PTMFILEHEADER pfh = *(LPPTMFILEHEADER)lpStream; + DWORD dwMemPos; + UINT nOrders; + + pfh.norders = bswapLE16(pfh.norders); + pfh.nsamples = bswapLE16(pfh.nsamples); + pfh.npatterns = bswapLE16(pfh.npatterns); + pfh.nchannels = bswapLE16(pfh.nchannels); + pfh.fileflags = bswapLE16(pfh.fileflags); + pfh.reserved2 = bswapLE16(pfh.reserved2); + pfh.ptmf_id = bswapLE32(pfh.ptmf_id); + for (UINT j=0; j<128; j++) + { + pfh.patseg[j] = bswapLE16(pfh.patseg[j]); + } + + if ((!lpStream) || (dwMemLength < 1024)) return FALSE; + if ((pfh.ptmf_id != 0x464d5450) || (!pfh.nchannels) + || (pfh.nchannels > 32) + || (pfh.norders > 256) || (!pfh.norders) + || (!pfh.nsamples) || (pfh.nsamples > 255) + || (!pfh.npatterns) || (pfh.npatterns > 128) + || (SIZEOF_PTMFILEHEADER+pfh.nsamples*SIZEOF_PTMSAMPLE >= (int)dwMemLength)) return FALSE; + memcpy(m_szNames[0], pfh.songname, 28); + m_szNames[0][28] = 0; + m_nType = MOD_TYPE_PTM; + m_nChannels = pfh.nchannels; + m_nSamples = (pfh.nsamples < MAX_SAMPLES) ? pfh.nsamples : MAX_SAMPLES-1; + dwMemPos = SIZEOF_PTMFILEHEADER; + nOrders = (pfh.norders < MAX_ORDERS) ? pfh.norders : MAX_ORDERS-1; + memcpy(Order, pfh.orders, nOrders); + for (UINT ipan=0; ipan<m_nChannels; ipan++) + { + ChnSettings[ipan].nVolume = 64; + ChnSettings[ipan].nPan = ((pfh.chnpan[ipan] & 0x0F) << 4) + 4; + } + for (UINT ismp=0; ismp<m_nSamples; ismp++, dwMemPos += SIZEOF_PTMSAMPLE) + { + MODINSTRUMENT *pins = &Ins[ismp+1]; + PTMSAMPLE *psmp = (PTMSAMPLE *)(lpStream+dwMemPos); + + lstrcpyn(m_szNames[ismp+1], psmp->samplename, 28); + memcpy(pins->name, psmp->filename, 12); + pins->name[12] = 0; + pins->nGlobalVol = 64; + pins->nPan = 128; + pins->nVolume = psmp->volume << 2; + pins->nC4Speed = bswapLE16(psmp->nC4Spd) << 1; + pins->uFlags = 0; + if ((psmp->sampletype & 3) == 1) + { + UINT smpflg = RS_PCM8D; + DWORD samplepos = psmp->fileofs[1] << 16 + + psmp->fileofs[0]; + pins->nLength = bswapLE32(*(LPDWORD)(psmp->length)); + pins->nLoopStart = bswapLE32(*(LPDWORD)(psmp->loopbeg)); + pins->nLoopEnd = bswapLE32(*(LPDWORD)(psmp->loopend)); + samplepos = bswapLE32(samplepos); + if (psmp->sampletype & 4) pins->uFlags |= CHN_LOOP; + if (psmp->sampletype & 8) pins->uFlags |= CHN_PINGPONGLOOP; + if (psmp->sampletype & 16) + { + pins->uFlags |= CHN_16BIT; + pins->nLength >>= 1; + pins->nLoopStart >>= 1; + pins->nLoopEnd >>= 1; + smpflg = RS_PTM8DTO16; + } + if ((pins->nLength) && (samplepos) && (samplepos < dwMemLength)) + { + ReadSample(pins, smpflg, (LPSTR)(lpStream+samplepos), dwMemLength-samplepos); + } + } + } + // Reading Patterns + for (UINT ipat=0; ipat<pfh.npatterns; ipat++) + { + dwMemPos = ((UINT)pfh.patseg[ipat]) << 4; + if ((!dwMemPos) || (dwMemPos >= dwMemLength)) continue; + PatternSize[ipat] = 64; + if ((Patterns[ipat] = AllocatePattern(64, m_nChannels)) == NULL) break; + // + MODCOMMAND *m = Patterns[ipat]; + for (UINT row=0; ((row < 64) && (dwMemPos < dwMemLength)); ) + { + UINT b = lpStream[dwMemPos++]; + + if (dwMemPos >= dwMemLength) break; + if (b) + { + UINT nChn = b & 0x1F; + + if (b & 0x20) + { + if (dwMemPos + 2 > dwMemLength) break; + m[nChn].note = lpStream[dwMemPos++]; + m[nChn].instr = lpStream[dwMemPos++]; + } + if (b & 0x40) + { + if (dwMemPos + 2 > dwMemLength) break; + m[nChn].command = lpStream[dwMemPos++]; + m[nChn].param = lpStream[dwMemPos++]; + if ((m[nChn].command == 0x0E) && ((m[nChn].param & 0xF0) == 0x80)) + { + m[nChn].command = CMD_S3MCMDEX; + } else + if (m[nChn].command < 0x10) + { + ConvertModCommand(&m[nChn]); + } else + { + switch(m[nChn].command) + { + case 16: + m[nChn].command = CMD_GLOBALVOLUME; + break; + case 17: + m[nChn].command = CMD_RETRIG; + break; + case 18: + m[nChn].command = CMD_FINEVIBRATO; + break; + default: + m[nChn].command = 0; + } + } + } + if (b & 0x80) + { + if (dwMemPos >= dwMemLength) break; + m[nChn].volcmd = VOLCMD_VOLUME; + m[nChn].vol = lpStream[dwMemPos++]; + } + } else + { + row++; + m += m_nChannels; + } + } + } + return TRUE; +} + diff --git a/lib/libmodplug/src/load_s3m.cpp b/lib/libmodplug/src/load_s3m.cpp new file mode 100644 index 0000000000..f994f90923 --- /dev/null +++ b/lib/libmodplug/src/load_s3m.cpp @@ -0,0 +1,657 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net>, + * Adam Goode <adam@evdebs.org> (endian and char fixes for PPC) +*/ + +#include "stdafx.h" +#include "sndfile.h" +#include "tables.h" + +#ifdef _MSC_VER +//#pragma warning(disable:4244) +#endif + +////////////////////////////////////////////////////// +// ScreamTracker S3M file support + +typedef struct tagS3MSAMPLESTRUCT +{ + BYTE type; + CHAR dosname[12]; + BYTE hmem; + WORD memseg; + DWORD length; + DWORD loopbegin; + DWORD loopend; + BYTE vol; + BYTE bReserved; + BYTE pack; + BYTE flags; + DWORD finetune; + DWORD dwReserved; + WORD intgp; + WORD int512; + DWORD lastused; + CHAR name[28]; + CHAR scrs[4]; +} S3MSAMPLESTRUCT; + + +typedef struct tagS3MFILEHEADER +{ + CHAR name[28]; + BYTE b1A; + BYTE type; + WORD reserved1; + WORD ordnum; + WORD insnum; + WORD patnum; + WORD flags; + WORD cwtv; + WORD version; + DWORD scrm; // "SCRM" = 0x4D524353 + BYTE globalvol; + BYTE speed; + BYTE tempo; + BYTE mastervol; + BYTE ultraclicks; + BYTE panning_present; + BYTE reserved2[8]; + WORD special; + BYTE channels[32]; +} S3MFILEHEADER; + + +void CSoundFile::S3MConvert(MODCOMMAND *m, BOOL bIT) const +//-------------------------------------------------------- +{ + UINT command = m->command; + UINT param = m->param; + switch (command + 0x40) + { + case 'A': command = CMD_SPEED; break; + case 'B': command = CMD_POSITIONJUMP; break; + case 'C': command = CMD_PATTERNBREAK; if (!bIT) param = (param >> 4) * 10 + (param & 0x0F); break; + case 'D': command = CMD_VOLUMESLIDE; break; + case 'E': command = CMD_PORTAMENTODOWN; break; + case 'F': command = CMD_PORTAMENTOUP; break; + case 'G': command = CMD_TONEPORTAMENTO; break; + case 'H': command = CMD_VIBRATO; break; + case 'I': command = CMD_TREMOR; break; + case 'J': command = CMD_ARPEGGIO; break; + case 'K': command = CMD_VIBRATOVOL; break; + case 'L': command = CMD_TONEPORTAVOL; break; + case 'M': command = CMD_CHANNELVOLUME; break; + case 'N': command = CMD_CHANNELVOLSLIDE; break; + case 'O': command = CMD_OFFSET; break; + case 'P': command = CMD_PANNINGSLIDE; break; + case 'Q': command = CMD_RETRIG; break; + case 'R': command = CMD_TREMOLO; break; + case 'S': command = CMD_S3MCMDEX; break; + case 'T': command = CMD_TEMPO; break; + case 'U': command = CMD_FINEVIBRATO; break; + case 'V': command = CMD_GLOBALVOLUME; break; + case 'W': command = CMD_GLOBALVOLSLIDE; break; + case 'X': command = CMD_PANNING8; break; + case 'Y': command = CMD_PANBRELLO; break; + case 'Z': command = CMD_MIDI; break; + default: command = 0; + } + m->command = command; + m->param = param; +} + + +void CSoundFile::S3MSaveConvert(UINT *pcmd, UINT *pprm, BOOL bIT) const +//--------------------------------------------------------------------- +{ + UINT command = *pcmd; + UINT param = *pprm; + switch(command) + { + case CMD_SPEED: command = 'A'; break; + case CMD_POSITIONJUMP: command = 'B'; break; + case CMD_PATTERNBREAK: command = 'C'; if (!bIT) param = ((param / 10) << 4) + (param % 10); break; + case CMD_VOLUMESLIDE: command = 'D'; break; + case CMD_PORTAMENTODOWN: command = 'E'; if ((param >= 0xE0) && (m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM))) param = 0xDF; break; + case CMD_PORTAMENTOUP: command = 'F'; if ((param >= 0xE0) && (m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM))) param = 0xDF; break; + case CMD_TONEPORTAMENTO: command = 'G'; break; + case CMD_VIBRATO: command = 'H'; break; + case CMD_TREMOR: command = 'I'; break; + case CMD_ARPEGGIO: command = 'J'; break; + case CMD_VIBRATOVOL: command = 'K'; break; + case CMD_TONEPORTAVOL: command = 'L'; break; + case CMD_CHANNELVOLUME: command = 'M'; break; + case CMD_CHANNELVOLSLIDE: command = 'N'; break; + case CMD_OFFSET: command = 'O'; break; + case CMD_PANNINGSLIDE: command = 'P'; break; + case CMD_RETRIG: command = 'Q'; break; + case CMD_TREMOLO: command = 'R'; break; + case CMD_S3MCMDEX: command = 'S'; break; + case CMD_TEMPO: command = 'T'; break; + case CMD_FINEVIBRATO: command = 'U'; break; + case CMD_GLOBALVOLUME: command = 'V'; break; + case CMD_GLOBALVOLSLIDE: command = 'W'; break; + case CMD_PANNING8: + command = 'X'; + if ((bIT) && (m_nType != MOD_TYPE_IT) && (m_nType != MOD_TYPE_XM)) + { + if (param == 0xA4) { command = 'S'; param = 0x91; } else + if (param <= 0x80) { param <<= 1; if (param > 255) param = 255; } else + command = param = 0; + } else + if ((!bIT) && ((m_nType == MOD_TYPE_IT) || (m_nType == MOD_TYPE_XM))) + { + param >>= 1; + } + break; + case CMD_PANBRELLO: command = 'Y'; break; + case CMD_MIDI: command = 'Z'; break; + case CMD_XFINEPORTAUPDOWN: + if (param & 0x0F) switch(param & 0xF0) + { + case 0x10: command = 'F'; param = (param & 0x0F) | 0xE0; break; + case 0x20: command = 'E'; param = (param & 0x0F) | 0xE0; break; + case 0x90: command = 'S'; break; + default: command = param = 0; + } else command = param = 0; + break; + case CMD_MODCMDEX: + command = 'S'; + switch(param & 0xF0) + { + case 0x00: command = param = 0; break; + case 0x10: command = 'F'; param |= 0xF0; break; + case 0x20: command = 'E'; param |= 0xF0; break; + case 0x30: param = (param & 0x0F) | 0x10; break; + case 0x40: param = (param & 0x0F) | 0x30; break; + case 0x50: param = (param & 0x0F) | 0x20; break; + case 0x60: param = (param & 0x0F) | 0xB0; break; + case 0x70: param = (param & 0x0F) | 0x40; break; + case 0x90: command = 'Q'; param &= 0x0F; break; + case 0xA0: if (param & 0x0F) { command = 'D'; param = (param << 4) | 0x0F; } else command=param=0; break; + case 0xB0: if (param & 0x0F) { command = 'D'; param |= 0xF0; } else command=param=0; break; + } + break; + default: command = param = 0; + } + command &= ~0x40; + *pcmd = command; + *pprm = param; +} + + +BOOL CSoundFile::ReadS3M(const BYTE *lpStream, DWORD dwMemLength) +//--------------------------------------------------------------- +{ + UINT insnum,patnum,nins,npat; + DWORD insfile[128]; + WORD ptr[256]; + BYTE s[1024]; + DWORD dwMemPos; + BYTE insflags[128], inspack[128]; + S3MFILEHEADER psfh = *(S3MFILEHEADER *)lpStream; + + psfh.reserved1 = bswapLE16(psfh.reserved1); + psfh.ordnum = bswapLE16(psfh.ordnum); + psfh.insnum = bswapLE16(psfh.insnum); + psfh.patnum = bswapLE16(psfh.patnum); + psfh.flags = bswapLE16(psfh.flags); + psfh.cwtv = bswapLE16(psfh.cwtv); + psfh.version = bswapLE16(psfh.version); + psfh.scrm = bswapLE32(psfh.scrm); + psfh.special = bswapLE16(psfh.special); + + if ((!lpStream) || (dwMemLength <= sizeof(S3MFILEHEADER)+sizeof(S3MSAMPLESTRUCT)+64)) return FALSE; + if (psfh.scrm != 0x4D524353) return FALSE; + dwMemPos = 0x60; + m_nType = MOD_TYPE_S3M; + memset(m_szNames,0,sizeof(m_szNames)); + memcpy(m_szNames[0], psfh.name, 28); + // Speed + m_nDefaultSpeed = psfh.speed; + if (m_nDefaultSpeed < 1) m_nDefaultSpeed = 6; + if (m_nDefaultSpeed > 0x1F) m_nDefaultSpeed = 0x1F; + // Tempo + m_nDefaultTempo = psfh.tempo; + if (m_nDefaultTempo < 40) m_nDefaultTempo = 40; + if (m_nDefaultTempo > 240) m_nDefaultTempo = 240; + // Global Volume + m_nDefaultGlobalVolume = psfh.globalvol << 2; + if ((!m_nDefaultGlobalVolume) || (m_nDefaultGlobalVolume > 256)) m_nDefaultGlobalVolume = 256; + m_nSongPreAmp = psfh.mastervol & 0x7F; + // Channels + m_nChannels = 4; + for (UINT ich=0; ich<32; ich++) + { + ChnSettings[ich].nPan = 128; + ChnSettings[ich].nVolume = 64; + + ChnSettings[ich].dwFlags = CHN_MUTE; + if (psfh.channels[ich] != 0xFF) + { + m_nChannels = ich+1; + UINT b = psfh.channels[ich] & 0x0F; + ChnSettings[ich].nPan = (b & 8) ? 0xC0 : 0x40; + ChnSettings[ich].dwFlags = 0; + } + } + if (m_nChannels < 4) m_nChannels = 4; + if ((psfh.cwtv < 0x1320) || (psfh.flags & 0x40)) m_dwSongFlags |= SONG_FASTVOLSLIDES; + // Reading pattern order + UINT iord = psfh.ordnum; + if (iord<1) iord = 1; + if (iord > MAX_ORDERS) iord = MAX_ORDERS; + if (iord) + { + memcpy(Order, lpStream+dwMemPos, iord); + dwMemPos += iord; + } + if ((iord & 1) && (lpStream[dwMemPos] == 0xFF)) dwMemPos++; + // Reading file pointers + insnum = nins = psfh.insnum; + if (insnum >= MAX_SAMPLES) insnum = MAX_SAMPLES-1; + m_nSamples = insnum; + patnum = npat = psfh.patnum; + if (patnum > MAX_PATTERNS) patnum = MAX_PATTERNS; + memset(ptr, 0, sizeof(ptr)); + if (nins+npat) + { + memcpy(ptr, lpStream+dwMemPos, 2*(nins+npat)); + dwMemPos += 2*(nins+npat); + for (UINT j = 0; j < (nins+npat); ++j) { + ptr[j] = bswapLE16(ptr[j]); + } + if (psfh.panning_present == 252) + { + const BYTE *chnpan = lpStream+dwMemPos; + for (UINT i=0; i<32; i++) if (chnpan[i] & 0x20) + { + ChnSettings[i].nPan = ((chnpan[i] & 0x0F) << 4) + 8; + } + } + } + if (!m_nChannels) return TRUE; + // Reading instrument headers + memset(insfile, 0, sizeof(insfile)); + for (UINT iSmp=1; iSmp<=insnum; iSmp++) + { + UINT nInd = ((DWORD)ptr[iSmp-1])*16; + if ((!nInd) || (nInd + 0x50 > dwMemLength)) continue; + memcpy(s, lpStream+nInd, 0x50); + memcpy(Ins[iSmp].name, s+1, 12); + insflags[iSmp-1] = s[0x1F]; + inspack[iSmp-1] = s[0x1E]; + s[0x4C] = 0; + lstrcpy(m_szNames[iSmp], (LPCSTR)&s[0x30]); + if ((s[0]==1) && (s[0x4E]=='R') && (s[0x4F]=='S')) + { + UINT j = bswapLE32(*((LPDWORD)(s+0x10))); + if (j > MAX_SAMPLE_LENGTH) j = MAX_SAMPLE_LENGTH; + if (j < 4) j = 0; + Ins[iSmp].nLength = j; + j = bswapLE32(*((LPDWORD)(s+0x14))); + if (j >= Ins[iSmp].nLength) j = Ins[iSmp].nLength - 1; + Ins[iSmp].nLoopStart = j; + j = bswapLE32(*((LPDWORD)(s+0x18))); + if (j > MAX_SAMPLE_LENGTH) j = MAX_SAMPLE_LENGTH; + if (j < 4) j = 0; + if (j > Ins[iSmp].nLength) j = Ins[iSmp].nLength; + Ins[iSmp].nLoopEnd = j; + j = s[0x1C]; + if (j > 64) j = 64; + Ins[iSmp].nVolume = j << 2; + Ins[iSmp].nGlobalVol = 64; + if (s[0x1F]&1) Ins[iSmp].uFlags |= CHN_LOOP; + j = bswapLE32(*((LPDWORD)(s+0x20))); + if (!j) j = 8363; + if (j < 1024) j = 1024; + Ins[iSmp].nC4Speed = j; + insfile[iSmp] = ((DWORD)bswapLE16(*((LPWORD)(s+0x0E)))) << 4; + insfile[iSmp] += ((DWORD)(BYTE)s[0x0D]) << 20; + if (insfile[iSmp] > dwMemLength) insfile[iSmp] &= 0xFFFF; + if ((Ins[iSmp].nLoopStart >= Ins[iSmp].nLoopEnd) || (Ins[iSmp].nLoopEnd - Ins[iSmp].nLoopStart < 8)) + Ins[iSmp].nLoopStart = Ins[iSmp].nLoopEnd = 0; + Ins[iSmp].nPan = 0x80; + } + } + // Reading patterns + for (UINT iPat=0; iPat<patnum; iPat++) + { + UINT nInd = ((DWORD)ptr[nins+iPat]) << 4; + if (nInd + 0x40 > dwMemLength) continue; + WORD len = bswapLE16(*((WORD *)(lpStream+nInd))); + nInd += 2; + PatternSize[iPat] = 64; + if ((!len) || (nInd + len > dwMemLength - 6) + || ((Patterns[iPat] = AllocatePattern(64, m_nChannels)) == NULL)) continue; + LPBYTE src = (LPBYTE)(lpStream+nInd); + // Unpacking pattern + MODCOMMAND *p = Patterns[iPat]; + UINT row = 0; + UINT j = 0; + while (j < len) + { + BYTE b = src[j++]; + if (!b) + { + if (++row >= 64) break; + } else + { + UINT chn = b & 0x1F; + if (chn < m_nChannels) + { + MODCOMMAND *m = &p[row*m_nChannels+chn]; + if (b & 0x20) + { + m->note = src[j++]; + if (m->note < 0xF0) m->note = (m->note & 0x0F) + 12*(m->note >> 4) + 13; + else if (m->note == 0xFF) m->note = 0; + m->instr = src[j++]; + } + if (b & 0x40) + { + UINT vol = src[j++]; + if ((vol >= 128) && (vol <= 192)) + { + vol -= 128; + m->volcmd = VOLCMD_PANNING; + } else + { + if (vol > 64) vol = 64; + m->volcmd = VOLCMD_VOLUME; + } + m->vol = vol; + } + if (b & 0x80) + { + m->command = src[j++]; + m->param = src[j++]; + if (m->command) S3MConvert(m, FALSE); + } + } else + { + if (b & 0x20) j += 2; + if (b & 0x40) j++; + if (b & 0x80) j += 2; + } + if (j >= len) break; + } + } + } + // Reading samples + for (UINT iRaw=1; iRaw<=insnum; iRaw++) if ((Ins[iRaw].nLength) && (insfile[iRaw])) + { + UINT flags = (psfh.version == 1) ? RS_PCM8S : RS_PCM8U; + if (insflags[iRaw-1] & 4) flags += 5; + if (insflags[iRaw-1] & 2) flags |= RSF_STEREO; + if (inspack[iRaw-1] == 4) flags = RS_ADPCM4; + dwMemPos = insfile[iRaw]; + dwMemPos += ReadSample(&Ins[iRaw], flags, (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos); + } + m_nMinPeriod = 64; + m_nMaxPeriod = 32767; + if (psfh.flags & 0x10) m_dwSongFlags |= SONG_AMIGALIMITS; + return TRUE; +} + + +#ifndef MODPLUG_NO_FILESAVE + +#ifdef _MSC_VER +#pragma warning(disable:4100) +#endif + +static BYTE S3MFiller[16] = +{ + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 +}; + + +BOOL CSoundFile::SaveS3M(LPCSTR lpszFileName, UINT nPacking) +//---------------------------------------------------------- +{ + FILE *f; + BYTE header[0x60]; + UINT nbo,nbi,nbp,i; + WORD patptr[128]; + WORD insptr[128]; + BYTE buffer[5*1024]; + S3MSAMPLESTRUCT insex[128]; + + if ((!m_nChannels) || (!lpszFileName)) return FALSE; + if ((f = fopen(lpszFileName, "wb")) == NULL) return FALSE; + // Writing S3M header + memset(header, 0, sizeof(header)); + memset(insex, 0, sizeof(insex)); + memcpy(header, m_szNames[0], 0x1C); + header[0x1B] = 0; + header[0x1C] = 0x1A; + header[0x1D] = 0x10; + nbo = (GetNumPatterns() + 15) & 0xF0; + if (!nbo) nbo = 16; + header[0x20] = nbo & 0xFF; + header[0x21] = nbo >> 8; + nbi = m_nInstruments; + if (!nbi) nbi = m_nSamples; + if (nbi > 99) nbi = 99; + header[0x22] = nbi & 0xFF; + header[0x23] = nbi >> 8; + nbp = 0; + for (i=0; Patterns[i]; i++) { nbp = i+1; if (nbp >= MAX_PATTERNS) break; } + for (i=0; i<MAX_ORDERS; i++) if ((Order[i] < MAX_PATTERNS) && (Order[i] >= nbp)) nbp = Order[i] + 1; + header[0x24] = nbp & 0xFF; + header[0x25] = nbp >> 8; + if (m_dwSongFlags & SONG_FASTVOLSLIDES) header[0x26] |= 0x40; + if ((m_nMaxPeriod < 20000) || (m_dwSongFlags & SONG_AMIGALIMITS)) header[0x26] |= 0x10; + header[0x28] = 0x20; + header[0x29] = 0x13; + header[0x2A] = 0x02; // Version = 1 => Signed samples + header[0x2B] = 0x00; + header[0x2C] = 'S'; + header[0x2D] = 'C'; + header[0x2E] = 'R'; + header[0x2F] = 'M'; + header[0x30] = m_nDefaultGlobalVolume >> 2; + header[0x31] = m_nDefaultSpeed; + header[0x32] = m_nDefaultTempo; + header[0x33] = ((m_nSongPreAmp < 0x20) ? 0x20 : m_nSongPreAmp) | 0x80; // Stereo + header[0x35] = 0xFC; + for (i=0; i<32; i++) + { + if (i < m_nChannels) + { + UINT tmp = (i & 0x0F) >> 1; + header[0x40+i] = (i & 0x10) | ((i & 1) ? 8+tmp : tmp); + } else header[0x40+i] = 0xFF; + } + fwrite(header, 0x60, 1, f); + fwrite(Order, nbo, 1, f); + memset(patptr, 0, sizeof(patptr)); + memset(insptr, 0, sizeof(insptr)); + UINT ofs0 = 0x60 + nbo; + UINT ofs1 = ((0x60 + nbo + nbi*2 + nbp*2 + 15) & 0xFFF0) + 0x20; + UINT ofs = ofs1; + + for (i=0; i<nbi; i++) insptr[i] = (WORD)((ofs + i*0x50) / 16); + for (i=0; i<nbp; i++) patptr[i] = (WORD)((ofs + nbi*0x50) / 16); + fwrite(insptr, nbi, 2, f); + fwrite(patptr, nbp, 2, f); + if (header[0x35] == 0xFC) + { + BYTE chnpan[32]; + for (i=0; i<32; i++) + { + chnpan[i] = 0x20 | (ChnSettings[i].nPan >> 4); + } + fwrite(chnpan, 0x20, 1, f); + } + if ((nbi*2+nbp*2) & 0x0F) + { + fwrite(S3MFiller, 0x10 - ((nbi*2+nbp*2) & 0x0F), 1, f); + } + ofs1 = ftell(f); + fwrite(insex, nbi, 0x50, f); + // Packing patterns + ofs += nbi*0x50; + for (i=0; i<nbp; i++) + { + WORD len = 64; + memset(buffer, 0, sizeof(buffer)); + patptr[i] = ofs / 16; + if (Patterns[i]) + { + len = 2; + MODCOMMAND *p = Patterns[i]; + for (int row=0; row<64; row++) if (row < PatternSize[i]) + { + for (UINT j=0; j<m_nChannels; j++) + { + UINT b = j; + MODCOMMAND *m = &p[row*m_nChannels+j]; + UINT note = m->note; + UINT volcmd = m->volcmd; + UINT vol = m->vol; + UINT command = m->command; + UINT param = m->param; + + if ((note) || (m->instr)) b |= 0x20; + if (!note) note = 0xFF; else + if (note >= 0xFE) note = 0xFE; else + if (note < 13) note = 0; else note -= 13; + if (note < 0xFE) note = (note % 12) + ((note / 12) << 4); + if (command == CMD_VOLUME) + { + command = 0; + if (param > 64) param = 64; + volcmd = VOLCMD_VOLUME; + vol = param; + } + if (volcmd == VOLCMD_VOLUME) b |= 0x40; else + if (volcmd == VOLCMD_PANNING) { vol |= 0x80; b |= 0x40; } + if (command) + { + S3MSaveConvert(&command, ¶m, FALSE); + if (command) b |= 0x80; + } + if (b & 0xE0) + { + buffer[len++] = b; + if (b & 0x20) + { + buffer[len++] = note; + buffer[len++] = m->instr; + } + if (b & 0x40) + { + buffer[len++] = vol; + } + if (b & 0x80) + { + buffer[len++] = command; + buffer[len++] = param; + } + if (len > sizeof(buffer) - 20) break; + } + } + buffer[len++] = 0; + if (len > sizeof(buffer) - 20) break; + } + } + buffer[0] = (len - 2) & 0xFF; + buffer[1] = (len - 2) >> 8; + len = (len+15) & (~0x0F); + fwrite(buffer, len, 1, f); + ofs += len; + } + // Writing samples + for (i=1; i<=nbi; i++) + { + MODINSTRUMENT *pins = &Ins[i]; + if (m_nInstruments) + { + pins = Ins; + if (Headers[i]) + { + for (UINT j=0; j<128; j++) + { + UINT n = Headers[i]->Keyboard[j]; + if ((n) && (n < MAX_INSTRUMENTS)) + { + pins = &Ins[n]; + break; + } + } + } + } + memcpy(insex[i-1].dosname, pins->name, 12); + memcpy(insex[i-1].name, m_szNames[i], 28); + memcpy(insex[i-1].scrs, "SCRS", 4); + insex[i-1].hmem = (BYTE)((DWORD)ofs >> 20); + insex[i-1].memseg = (WORD)((DWORD)ofs >> 4); + if (pins->pSample) + { + insex[i-1].type = 1; + insex[i-1].length = pins->nLength; + insex[i-1].loopbegin = pins->nLoopStart; + insex[i-1].loopend = pins->nLoopEnd; + insex[i-1].vol = pins->nVolume / 4; + insex[i-1].flags = (pins->uFlags & CHN_LOOP) ? 1 : 0; + if (pins->nC4Speed) + insex[i-1].finetune = pins->nC4Speed; + else + insex[i-1].finetune = TransposeToFrequency(pins->RelativeTone, pins->nFineTune); + UINT flags = RS_PCM8U; +#ifndef NO_PACKING + if (nPacking) + { + if ((!(pins->uFlags & (CHN_16BIT|CHN_STEREO))) + && (CanPackSample((char *)pins->pSample, pins->nLength, nPacking))) + { + insex[i-1].pack = 4; + flags = RS_ADPCM4; + } + } else +#endif // NO_PACKING + { + if (pins->uFlags & CHN_16BIT) + { + insex[i-1].flags |= 4; + flags = RS_PCM16U; + } + if (pins->uFlags & CHN_STEREO) + { + insex[i-1].flags |= 2; + flags = (pins->uFlags & CHN_16BIT) ? RS_STPCM16U : RS_STPCM8U; + } + } + DWORD len = WriteSample(f, pins, flags); + if (len & 0x0F) + { + fwrite(S3MFiller, 0x10 - (len & 0x0F), 1, f); + } + ofs += (len + 15) & (~0x0F); + } else + { + insex[i-1].length = 0; + } + } + // Updating parapointers + fseek(f, ofs0, SEEK_SET); + fwrite(insptr, nbi, 2, f); + fwrite(patptr, nbp, 2, f); + fseek(f, ofs1, SEEK_SET); + fwrite(insex, 0x50, nbi, f); + fclose(f); + return TRUE; +} + +#ifdef _MSC_VER +#pragma warning(default:4100) +#endif + +#endif // MODPLUG_NO_FILESAVE + diff --git a/lib/libmodplug/src/load_stm.cpp b/lib/libmodplug/src/load_stm.cpp new file mode 100644 index 0000000000..294ecd8f4f --- /dev/null +++ b/lib/libmodplug/src/load_stm.cpp @@ -0,0 +1,186 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +#include "stdafx.h" +#include "sndfile.h" + +//#pragma warning(disable:4244) + +#pragma pack(1) + +typedef struct tagSTMNOTE +{ + BYTE note; + BYTE insvol; + BYTE volcmd; + BYTE cmdinf; +} STMNOTE; + + +// Raw STM sampleinfo struct: +typedef struct tagSTMSAMPLE +{ + CHAR filename[14]; // Can't have long comments - just filename comments :) + WORD reserved; // ISA in memory when in ST 2 + WORD length; // Sample length + WORD loopbeg; // Loop start point + WORD loopend; // Loop end point + BYTE volume; // Volume + BYTE reserved2; // More reserved crap + WORD c2spd; // Good old c2spd + BYTE reserved3[6]; // Yet more of PSi's reserved crap +} STMSAMPLE; + + +// Raw STM header struct: +typedef struct tagSTMHEADER +{ + char songname[20]; // changed from CHAR + char trackername[8]; // !SCREAM! for ST 2.xx // changed from CHAR + CHAR unused; // 0x1A + CHAR filetype; // 1=song, 2=module (only 2 is supported, of course) :) + CHAR ver_major; // Like 2 + CHAR ver_minor; // "ditto" + BYTE inittempo; // initspeed= stm inittempo>>4 + BYTE numpat; // number of patterns + BYTE globalvol; // <- WoW! a RiGHT TRiANGLE =8*) + BYTE reserved[13]; // More of PSi's internal crap + STMSAMPLE sample[31]; // STM sample data + BYTE patorder[128]; // Docs say 64 - actually 128 +} STMHEADER; + +#pragma pack() + + + +BOOL CSoundFile::ReadSTM(const BYTE *lpStream, DWORD dwMemLength) +//--------------------------------------------------------------- +{ + STMHEADER *phdr = (STMHEADER *)lpStream; + DWORD dwMemPos = 0; + + if ((!lpStream) || (dwMemLength < sizeof(STMHEADER))) return FALSE; + if ((phdr->filetype != 2) || (phdr->unused != 0x1A) + || ((strnicmp(phdr->trackername, "!SCREAM!", 8)) + && (strnicmp(phdr->trackername, "BMOD2STM", 8)))) return FALSE; + memcpy(m_szNames[0], phdr->songname, 20); + // Read STM header + m_nType = MOD_TYPE_STM; + m_nSamples = 31; + m_nChannels = 4; + m_nInstruments = 0; + m_nMinPeriod = 64; + m_nMaxPeriod = 0x7FFF; + m_nDefaultSpeed = phdr->inittempo >> 4; + if (m_nDefaultSpeed < 1) m_nDefaultSpeed = 1; + m_nDefaultTempo = 125; + m_nDefaultGlobalVolume = phdr->globalvol << 2; + if (m_nDefaultGlobalVolume > 256) m_nDefaultGlobalVolume = 256; + memcpy(Order, phdr->patorder, 128); + // Setting up channels + for (UINT nSet=0; nSet<4; nSet++) + { + ChnSettings[nSet].dwFlags = 0; + ChnSettings[nSet].nVolume = 64; + ChnSettings[nSet].nPan = (nSet & 1) ? 0x40 : 0xC0; + } + // Reading samples + for (UINT nIns=0; nIns<31; nIns++) + { + MODINSTRUMENT *pIns = &Ins[nIns+1]; + STMSAMPLE *pStm = &phdr->sample[nIns]; // STM sample data + memcpy(pIns->name, pStm->filename, 13); + memcpy(m_szNames[nIns+1], pStm->filename, 12); + pIns->nC4Speed = bswapLE16(pStm->c2spd); + pIns->nGlobalVol = 64; + pIns->nVolume = pStm->volume << 2; + if (pIns->nVolume > 256) pIns->nVolume = 256; + pIns->nLength = bswapLE16(pStm->length); + if ((pIns->nLength < 4) || (!pIns->nVolume)) pIns->nLength = 0; + pIns->nLoopStart = bswapLE16(pStm->loopbeg); + pIns->nLoopEnd = bswapLE16(pStm->loopend); + if ((pIns->nLoopEnd > pIns->nLoopStart) && (pIns->nLoopEnd != 0xFFFF)) pIns->uFlags |= CHN_LOOP; + } + dwMemPos = sizeof(STMHEADER); + for (UINT nOrd=0; nOrd<MAX_ORDERS; nOrd++) if (Order[nOrd] >= 99) Order[nOrd] = 0xFF; + UINT nPatterns = phdr->numpat; + for (UINT nPat=0; nPat<nPatterns; nPat++) + { + if (dwMemPos + 64*4*4 > dwMemLength) return TRUE; + PatternSize[nPat] = 64; + if ((Patterns[nPat] = AllocatePattern(64, m_nChannels)) == NULL) return TRUE; + MODCOMMAND *m = Patterns[nPat]; + STMNOTE *p = (STMNOTE *)(lpStream + dwMemPos); + for (UINT n=0; n<64*4; n++, p++, m++) + { + UINT note,ins,vol,cmd; + // extract the various information from the 4 bytes that + // make up a single note + note = p->note; + ins = p->insvol >> 3; + vol = (p->insvol & 0x07) + (p->volcmd >> 1); + cmd = p->volcmd & 0x0F; + if ((ins) && (ins < 32)) m->instr = ins; + // special values of [SBYTE0] are handled here -> + // we have no idea if these strange values will ever be encountered + // but it appears as though stms sound correct. + if ((note == 0xFE) || (note == 0xFC)) m->note = 0xFE; else + // if note < 251, then all three bytes are stored in the file + if (note < 0xFC) m->note = (note >> 4)*12 + (note&0xf) + 37; + if (vol <= 64) { m->volcmd = VOLCMD_VOLUME; m->vol = vol; } + m->param = p->cmdinf; + switch(cmd) + { + // Axx set speed to xx + case 1: m->command = CMD_SPEED; m->param >>= 4; break; + // Bxx position jump + case 2: m->command = CMD_POSITIONJUMP; break; + // Cxx patternbreak to row xx + case 3: m->command = CMD_PATTERNBREAK; m->param = (m->param & 0xF0) * 10 + (m->param & 0x0F); break; + // Dxy volumeslide + case 4: m->command = CMD_VOLUMESLIDE; break; + // Exy toneslide down + case 5: m->command = CMD_PORTAMENTODOWN; break; + // Fxy toneslide up + case 6: m->command = CMD_PORTAMENTOUP; break; + // Gxx Tone portamento,speed xx + case 7: m->command = CMD_TONEPORTAMENTO; break; + // Hxy vibrato + case 8: m->command = CMD_VIBRATO; break; + // Ixy tremor, ontime x, offtime y + case 9: m->command = CMD_TREMOR; break; + // Jxy arpeggio + case 10: m->command = CMD_ARPEGGIO; break; + // Kxy Dual command H00 & Dxy + case 11: m->command = CMD_VIBRATOVOL; break; + // Lxy Dual command G00 & Dxy + case 12: m->command = CMD_TONEPORTAVOL; break; + // Xxx amiga command 8xx + case 0x18: m->command = CMD_PANNING8; break; + default: + m->command = m->param = 0; + } + } + dwMemPos += 64*4*4; + } + // Reading Samples + for (UINT nSmp=1; nSmp<=31; nSmp++) + { + MODINSTRUMENT *pIns = &Ins[nSmp]; + dwMemPos = (dwMemPos + 15) & (~15); + if (pIns->nLength) + { + UINT nPos = ((UINT)phdr->sample[nSmp-1].reserved) << 4; + if ((nPos >= sizeof(STMHEADER)) && (nPos+pIns->nLength <= dwMemLength)) dwMemPos = nPos; + if (dwMemPos < dwMemLength) + { + dwMemPos += ReadSample(pIns, RS_PCM8S, (LPSTR)(lpStream+dwMemPos),dwMemLength-dwMemPos); + } + } + } + return TRUE; +} + diff --git a/lib/libmodplug/src/load_ult.cpp b/lib/libmodplug/src/load_ult.cpp new file mode 100644 index 0000000000..91e1b30402 --- /dev/null +++ b/lib/libmodplug/src/load_ult.cpp @@ -0,0 +1,222 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +#include "stdafx.h" +#include "sndfile.h" + +//#pragma warning(disable:4244) + +#define ULT_16BIT 0x04 +#define ULT_LOOP 0x08 +#define ULT_BIDI 0x10 + +#pragma pack(1) + +// Raw ULT header struct: +typedef struct tagULTHEADER +{ + char id[15]; // changed from CHAR + char songtitle[32]; // changed from CHAR + BYTE reserved; +} ULTHEADER; + + +// Raw ULT sampleinfo struct: +typedef struct tagULTSAMPLE +{ + CHAR samplename[32]; + CHAR dosname[12]; + LONG loopstart; + LONG loopend; + LONG sizestart; + LONG sizeend; + BYTE volume; + BYTE flags; + WORD finetune; +} ULTSAMPLE; + +#pragma pack() + + +BOOL CSoundFile::ReadUlt(const BYTE *lpStream, DWORD dwMemLength) +//--------------------------------------------------------------- +{ + ULTHEADER *pmh = (ULTHEADER *)lpStream; + ULTSAMPLE *pus; + UINT nos, nop; + DWORD dwMemPos = 0; + + // try to read module header + if ((!lpStream) || (dwMemLength < 0x100)) return FALSE; + if (strncmp(pmh->id,"MAS_UTrack_V00",14)) return FALSE; + // Warning! Not supported ULT format, trying anyway + // if ((pmh->id[14] < '1') || (pmh->id[14] > '4')) return FALSE; + m_nType = MOD_TYPE_ULT; + m_nDefaultSpeed = 6; + m_nDefaultTempo = 125; + memcpy(m_szNames[0], pmh->songtitle, 32); + // read songtext + dwMemPos = sizeof(ULTHEADER); + if ((pmh->reserved) && (dwMemPos + pmh->reserved * 32 < dwMemLength)) + { + UINT len = pmh->reserved * 32; + m_lpszSongComments = new char[len + 1 + pmh->reserved]; + if (m_lpszSongComments) + { + for (UINT l=0; l<pmh->reserved; l++) + { + memcpy(m_lpszSongComments+l*33, lpStream+dwMemPos+l*32, 32); + m_lpszSongComments[l*33+32] = 0x0D; + } + m_lpszSongComments[len] = 0; + } + dwMemPos += len; + } + if (dwMemPos >= dwMemLength) return TRUE; + nos = lpStream[dwMemPos++]; + m_nSamples = nos; + if (m_nSamples >= MAX_SAMPLES) m_nSamples = MAX_SAMPLES-1; + UINT smpsize = 64; + if (pmh->id[14] >= '4') smpsize += 2; + if (dwMemPos + nos*smpsize + 256 + 2 > dwMemLength) return TRUE; + for (UINT ins=1; ins<=nos; ins++, dwMemPos+=smpsize) if (ins<=m_nSamples) + { + pus = (ULTSAMPLE *)(lpStream+dwMemPos); + MODINSTRUMENT *pins = &Ins[ins]; + memcpy(m_szNames[ins], pus->samplename, 32); + memcpy(pins->name, pus->dosname, 12); + pins->nLoopStart = pus->loopstart; + pins->nLoopEnd = pus->loopend; + pins->nLength = pus->sizeend - pus->sizestart; + pins->nVolume = pus->volume; + pins->nGlobalVol = 64; + pins->nC4Speed = 8363; + if (pmh->id[14] >= '4') + { + pins->nC4Speed = pus->finetune; + } + if (pus->flags & ULT_LOOP) pins->uFlags |= CHN_LOOP; + if (pus->flags & ULT_BIDI) pins->uFlags |= CHN_PINGPONGLOOP; + if (pus->flags & ULT_16BIT) + { + pins->uFlags |= CHN_16BIT; + pins->nLoopStart >>= 1; + pins->nLoopEnd >>= 1; + } + } + memcpy(Order, lpStream+dwMemPos, 256); + dwMemPos += 256; + m_nChannels = lpStream[dwMemPos] + 1; + nop = lpStream[dwMemPos+1] + 1; + dwMemPos += 2; + if (m_nChannels > 32) m_nChannels = 32; + // Default channel settings + for (UINT nSet=0; nSet<m_nChannels; nSet++) + { + ChnSettings[nSet].nVolume = 64; + ChnSettings[nSet].nPan = (nSet & 1) ? 0x40 : 0xC0; + } + // read pan position table for v1.5 and higher + if(pmh->id[14]>='3') + { + if (dwMemPos + m_nChannels > dwMemLength) return TRUE; + for(UINT t=0; t<m_nChannels; t++) + { + ChnSettings[t].nPan = (lpStream[dwMemPos++] << 4) + 8; + if (ChnSettings[t].nPan > 256) ChnSettings[t].nPan = 256; + } + } + // Allocating Patterns + for (UINT nAllocPat=0; nAllocPat<nop; nAllocPat++) + { + if (nAllocPat < MAX_PATTERNS) + { + PatternSize[nAllocPat] = 64; + Patterns[nAllocPat] = AllocatePattern(64, m_nChannels); + } + } + // Reading Patterns + for (UINT nChn=0; nChn<m_nChannels; nChn++) + { + for (UINT nPat=0; nPat<nop; nPat++) + { + MODCOMMAND *pat = NULL; + + if (nPat < MAX_PATTERNS) + { + pat = Patterns[nPat]; + if (pat) pat += nChn; + } + UINT row = 0; + while (row < 64) + { + if (dwMemPos + 6 > dwMemLength) return TRUE; + UINT rep = 1; + UINT note = lpStream[dwMemPos++]; + if (note == 0xFC) + { + rep = lpStream[dwMemPos]; + note = lpStream[dwMemPos+1]; + dwMemPos += 2; + } + UINT instr = lpStream[dwMemPos++]; + UINT eff = lpStream[dwMemPos++]; + UINT dat1 = lpStream[dwMemPos++]; + UINT dat2 = lpStream[dwMemPos++]; + UINT cmd1 = eff & 0x0F; + UINT cmd2 = eff >> 4; + if (cmd1 == 0x0C) dat1 >>= 2; else + if (cmd1 == 0x0B) { cmd1 = dat1 = 0; } + if (cmd2 == 0x0C) dat2 >>= 2; else + if (cmd2 == 0x0B) { cmd2 = dat2 = 0; } + while ((rep != 0) && (row < 64)) + { + if (pat) + { + pat->instr = instr; + if (note) pat->note = note + 36; + if (cmd1 | dat1) + { + if (cmd1 == 0x0C) + { + pat->volcmd = VOLCMD_VOLUME; + pat->vol = dat1; + } else + { + pat->command = cmd1; + pat->param = dat1; + ConvertModCommand(pat); + } + } + if (cmd2 == 0x0C) + { + pat->volcmd = VOLCMD_VOLUME; + pat->vol = dat2; + } else + if ((cmd2 | dat2) && (!pat->command)) + { + pat->command = cmd2; + pat->param = dat2; + ConvertModCommand(pat); + } + pat += m_nChannels; + } + row++; + rep--; + } + } + } + } + // Reading Instruments + for (UINT smp=1; smp<=m_nSamples; smp++) if (Ins[smp].nLength) + { + if (dwMemPos >= dwMemLength) return TRUE; + UINT flags = (Ins[smp].uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S; + dwMemPos += ReadSample(&Ins[smp], flags, (LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos); + } + return TRUE; +} + diff --git a/lib/libmodplug/src/load_umx.cpp b/lib/libmodplug/src/load_umx.cpp new file mode 100644 index 0000000000..8913c740ff --- /dev/null +++ b/lib/libmodplug/src/load_umx.cpp @@ -0,0 +1,53 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +#include "stdafx.h" +#include "sndfile.h" + +#define MODMAGIC_OFFSET (20+31*30+130) + + +BOOL CSoundFile::ReadUMX(const BYTE *lpStream, DWORD dwMemLength) +//--------------------------------------------------------------- +{ + if ((!lpStream) || (dwMemLength < 0x800)) return FALSE; + // Rip Mods from UMX + if ((bswapLE32(*((DWORD *)(lpStream+0x20))) < dwMemLength) + && (bswapLE32(*((DWORD *)(lpStream+0x18))) <= dwMemLength - 0x10) + && (bswapLE32(*((DWORD *)(lpStream+0x18))) >= dwMemLength - 0x200)) + { + for (UINT uscan=0x40; uscan<0x500; uscan++) + { + DWORD dwScan = bswapLE32(*((DWORD *)(lpStream+uscan))); + // IT + if (dwScan == 0x4D504D49) + { + DWORD dwRipOfs = uscan; + return ReadIT(lpStream + dwRipOfs, dwMemLength - dwRipOfs); + } + // S3M + if (dwScan == 0x4D524353) + { + DWORD dwRipOfs = uscan - 44; + return ReadS3M(lpStream + dwRipOfs, dwMemLength - dwRipOfs); + } + // XM + if (!strnicmp((LPCSTR)(lpStream+uscan), "Extended Module", 15)) + { + DWORD dwRipOfs = uscan; + return ReadXM(lpStream + dwRipOfs, dwMemLength - dwRipOfs); + } + // MOD + if ((uscan > MODMAGIC_OFFSET) && (dwScan == 0x2e4b2e4d)) + { + DWORD dwRipOfs = uscan - MODMAGIC_OFFSET; + return ReadMod(lpStream+dwRipOfs, dwMemLength-dwRipOfs); + } + } + } + return FALSE; +} + diff --git a/lib/libmodplug/src/load_wav.cpp b/lib/libmodplug/src/load_wav.cpp new file mode 100644 index 0000000000..3bcd132865 --- /dev/null +++ b/lib/libmodplug/src/load_wav.cpp @@ -0,0 +1,220 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +#include "stdafx.h" +#include "sndfile.h" + +#ifndef WAVE_FORMAT_EXTENSIBLE +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE +#endif + +///////////////////////////////////////////////////////////// +// WAV file support + +BOOL CSoundFile::ReadWav(const BYTE *lpStream, DWORD dwMemLength) +//--------------------------------------------------------------- +{ + DWORD dwMemPos = 0; + WAVEFILEHEADER *phdr = (WAVEFILEHEADER *)lpStream; + WAVEFORMATHEADER *pfmt = (WAVEFORMATHEADER *)(lpStream + sizeof(WAVEFILEHEADER)); + if ((!lpStream) || (dwMemLength < (DWORD)sizeof(WAVEFILEHEADER))) return FALSE; + if ((phdr->id_RIFF != IFFID_RIFF) || (phdr->id_WAVE != IFFID_WAVE) + || (pfmt->id_fmt != IFFID_fmt)) return FALSE; + dwMemPos = sizeof(WAVEFILEHEADER) + 8 + pfmt->hdrlen; + if ((dwMemPos + 8 >= dwMemLength) + || ((pfmt->format != WAVE_FORMAT_PCM) && (pfmt->format != WAVE_FORMAT_EXTENSIBLE)) + || (pfmt->channels > 4) + || (!pfmt->channels) + || (!pfmt->freqHz) + || (pfmt->bitspersample & 7) + || (pfmt->bitspersample < 8) + || (pfmt->bitspersample > 32)) return FALSE; + WAVEDATAHEADER *pdata; + for (;;) + { + pdata = (WAVEDATAHEADER *)(lpStream + dwMemPos); + if (pdata->id_data == IFFID_data) break; + dwMemPos += pdata->length + 8; + if (dwMemPos + 8 >= dwMemLength) return FALSE; + } + m_nType = MOD_TYPE_WAV; + m_nSamples = 0; + m_nInstruments = 0; + m_nChannels = 4; + m_nDefaultSpeed = 8; + m_nDefaultTempo = 125; + m_dwSongFlags |= SONG_LINEARSLIDES; // For no resampling + Order[0] = 0; + Order[1] = 0xFF; + PatternSize[0] = PatternSize[1] = 64; + if ((Patterns[0] = AllocatePattern(64, 4)) == NULL) return TRUE; + if ((Patterns[1] = AllocatePattern(64, 4)) == NULL) return TRUE; + UINT samplesize = (pfmt->channels * pfmt->bitspersample) >> 3; + UINT len = pdata->length, bytelen; + if (dwMemPos + len > dwMemLength - 8) len = dwMemLength - dwMemPos - 8; + len /= samplesize; + bytelen = len; + if (pfmt->bitspersample >= 16) bytelen *= 2; + if (len > MAX_SAMPLE_LENGTH) len = MAX_SAMPLE_LENGTH; + if (!len) return TRUE; + // Setting up module length + DWORD dwTime = ((len * 50) / pfmt->freqHz) + 1; + DWORD framesperrow = (dwTime + 63) / 63; + if (framesperrow < 4) framesperrow = 4; + UINT norders = 1; + while (framesperrow >= 0x20) + { + Order[norders++] = 1; + Order[norders] = 0xFF; + framesperrow = (dwTime + (64 * norders - 1)) / (64 * norders); + if (norders >= MAX_ORDERS-1) break; + } + m_nDefaultSpeed = framesperrow; + for (UINT iChn=0; iChn<4; iChn++) + { + ChnSettings[iChn].nPan = (iChn & 1) ? 256 : 0; + ChnSettings[iChn].nVolume = 64; + ChnSettings[iChn].dwFlags = 0; + } + // Setting up speed command + MODCOMMAND *pcmd = Patterns[0]; + pcmd[0].command = CMD_SPEED; + pcmd[0].param = (BYTE)m_nDefaultSpeed; + pcmd[0].note = 5*12+1; + pcmd[0].instr = 1; + pcmd[1].note = pcmd[0].note; + pcmd[1].instr = pcmd[0].instr; + m_nSamples = pfmt->channels; + // Support for Multichannel Wave + for (UINT nChn=0; nChn<m_nSamples; nChn++) + { + MODINSTRUMENT *pins = &Ins[nChn+1]; + pcmd[nChn].note = pcmd[0].note; + pcmd[nChn].instr = (BYTE)(nChn+1); + pins->nLength = len; + pins->nC4Speed = pfmt->freqHz; + pins->nVolume = 256; + pins->nPan = 128; + pins->nGlobalVol = 64; + pins->uFlags = (WORD)((pfmt->bitspersample >= 16) ? CHN_16BIT : 0); + pins->uFlags |= CHN_PANNING; + if (m_nSamples > 1) + { + switch(nChn) + { + case 0: pins->nPan = 0; break; + case 1: pins->nPan = 256; break; + case 2: pins->nPan = (WORD)((m_nSamples == 3) ? 128 : 64); pcmd[nChn].command = CMD_S3MCMDEX; pcmd[nChn].param = 0x91; break; + case 3: pins->nPan = 192; pcmd[nChn].command = CMD_S3MCMDEX; pcmd[nChn].param = 0x91; break; + default: pins->nPan = 128; break; + } + } + if ((pins->pSample = AllocateSample(bytelen+8)) == NULL) return TRUE; + if (pfmt->bitspersample >= 16) + { + int slsize = pfmt->bitspersample >> 3; + signed short *p = (signed short *)pins->pSample; + signed char *psrc = (signed char *)(lpStream+dwMemPos+8+nChn*slsize+slsize-2); + for (UINT i=0; i<len; i++) + { + p[i] = *((signed short *)psrc); + psrc += samplesize; + } + p[len+1] = p[len] = p[len-1]; + } else + { + signed char *p = (signed char *)pins->pSample; + signed char *psrc = (signed char *)(lpStream+dwMemPos+8+nChn); + for (UINT i=0; i<len; i++) + { + p[i] = (signed char)((*psrc) + 0x80); + psrc += samplesize; + } + p[len+1] = p[len] = p[len-1]; + } + } + return TRUE; +} + + +//////////////////////////////////////////////////////////////////////// +// IMA ADPCM Support + +#pragma pack(1) + +typedef struct IMAADPCMBLOCK +{ + WORD sample; + BYTE index; + BYTE Reserved; +} DVI_ADPCMBLOCKHEADER; + +#pragma pack() + +static const int gIMAUnpackTable[90] = +{ + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767, 0 +}; + + +BOOL IMAADPCMUnpack16(signed short *pdest, UINT nLen, LPBYTE psrc, DWORD dwBytes, UINT pkBlkAlign) +//------------------------------------------------------------------------------------------------ +{ + static const int gIMAIndexTab[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; + UINT nPos; + int value; + + if ((nLen < 4) || (!pdest) || (!psrc) + || (pkBlkAlign < 5) || (pkBlkAlign > dwBytes)) return FALSE; + nPos = 0; + while ((nPos < nLen) && (dwBytes > 4)) + { + int nIndex; + value = *((short int *)psrc); + nIndex = psrc[2]; + psrc += 4; + dwBytes -= 4; + pdest[nPos++] = (short int)value; + for (UINT i=0; ((i<(pkBlkAlign-4)*2) && (nPos < nLen) && (dwBytes)); i++) + { + BYTE delta; + if (i & 1) + { + delta = (BYTE)(((*(psrc++)) >> 4) & 0x0F); + dwBytes--; + } else + { + delta = (BYTE)((*psrc) & 0x0F); + } + int v = gIMAUnpackTable[nIndex] >> 3; + if (delta & 1) v += gIMAUnpackTable[nIndex] >> 2; + if (delta & 2) v += gIMAUnpackTable[nIndex] >> 1; + if (delta & 4) v += gIMAUnpackTable[nIndex]; + if (delta & 8) value -= v; else value += v; + nIndex += gIMAIndexTab[delta & 7]; + if (nIndex < 0) nIndex = 0; else + if (nIndex > 88) nIndex = 88; + if (value > 32767) value = 32767; else + if (value < -32768) value = -32768; + pdest[nPos++] = (short int)value; + } + } + return TRUE; +} + + + diff --git a/lib/libmodplug/src/load_xm.cpp b/lib/libmodplug/src/load_xm.cpp new file mode 100644 index 0000000000..ee71b79265 --- /dev/null +++ b/lib/libmodplug/src/load_xm.cpp @@ -0,0 +1,892 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net>, + * Adam Goode <adam@evdebs.org> (endian and char fixes for PPC) +*/ + +#include "stdafx.h" +#include "sndfile.h" + +//////////////////////////////////////////////////////// +// FastTracker II XM file support + +#ifdef MSC_VER +#pragma warning(disable:4244) +#endif + +#pragma pack(1) +typedef struct tagXMFILEHEADER +{ + DWORD size; + WORD norder; + WORD restartpos; + WORD channels; + WORD patterns; + WORD instruments; + WORD flags; + WORD speed; + WORD tempo; + BYTE order[256]; +} XMFILEHEADER; + + +typedef struct tagXMINSTRUMENTHEADER +{ + DWORD size; + CHAR name[22]; + BYTE type; + BYTE samples; + BYTE samplesh; +} XMINSTRUMENTHEADER; + + +typedef struct tagXMSAMPLEHEADER +{ + DWORD shsize; + BYTE snum[96]; + WORD venv[24]; + WORD penv[24]; + BYTE vnum, pnum; + BYTE vsustain, vloops, vloope, psustain, ploops, ploope; + BYTE vtype, ptype; + BYTE vibtype, vibsweep, vibdepth, vibrate; + WORD volfade; + WORD res; + BYTE reserved1[20]; +} XMSAMPLEHEADER; + +typedef struct tagXMSAMPLESTRUCT +{ + DWORD samplen; + DWORD loopstart; + DWORD looplen; + BYTE vol; + signed char finetune; + BYTE type; + BYTE pan; + signed char relnote; + BYTE res; + char name[22]; +} XMSAMPLESTRUCT; +#pragma pack() + + +BOOL CSoundFile::ReadXM(const BYTE *lpStream, DWORD dwMemLength) +//-------------------------------------------------------------- +{ + XMSAMPLEHEADER xmsh; + XMSAMPLESTRUCT xmss; + DWORD dwMemPos, dwHdrSize; + WORD norders=0, restartpos=0, channels=0, patterns=0, instruments=0; + WORD xmflags=0, deftempo=125, defspeed=6; + BOOL InstUsed[256]; + BYTE channels_used[MAX_CHANNELS]; + BYTE pattern_map[256]; + BOOL samples_used[MAX_SAMPLES]; + UINT unused_samples; + + m_nChannels = 0; + if ((!lpStream) || (dwMemLength < 0x200)) return FALSE; + if (strnicmp((LPCSTR)lpStream, "Extended Module", 15)) return FALSE; + + memcpy(m_szNames[0], lpStream+17, 20); + dwHdrSize = bswapLE32(*((DWORD *)(lpStream+60))); + norders = bswapLE16(*((WORD *)(lpStream+64))); + if ((!norders) || (norders > MAX_ORDERS)) return FALSE; + restartpos = bswapLE16(*((WORD *)(lpStream+66))); + channels = bswapLE16(*((WORD *)(lpStream+68))); + if ((!channels) || (channels > 64)) return FALSE; + m_nType = MOD_TYPE_XM; + m_nMinPeriod = 27; + m_nMaxPeriod = 54784; + m_nChannels = channels; + if (restartpos < norders) m_nRestartPos = restartpos; + patterns = bswapLE16(*((WORD *)(lpStream+70))); + if (patterns > 256) patterns = 256; + instruments = bswapLE16(*((WORD *)(lpStream+72))); + if (instruments >= MAX_INSTRUMENTS) instruments = MAX_INSTRUMENTS-1; + m_nInstruments = instruments; + m_nSamples = 0; + memcpy(&xmflags, lpStream+74, 2); + xmflags = bswapLE16(xmflags); + if (xmflags & 1) m_dwSongFlags |= SONG_LINEARSLIDES; + if (xmflags & 0x1000) m_dwSongFlags |= SONG_EXFILTERRANGE; + defspeed = bswapLE16(*((WORD *)(lpStream+76))); + deftempo = bswapLE16(*((WORD *)(lpStream+78))); + if ((deftempo >= 32) && (deftempo < 256)) m_nDefaultTempo = deftempo; + if ((defspeed > 0) && (defspeed < 40)) m_nDefaultSpeed = defspeed; + memcpy(Order, lpStream+80, norders); + memset(InstUsed, 0, sizeof(InstUsed)); + if (patterns > MAX_PATTERNS) + { + UINT i, j; + for (i=0; i<norders; i++) + { + if (Order[i] < patterns) InstUsed[Order[i]] = TRUE; + } + j = 0; + for (i=0; i<256; i++) + { + if (InstUsed[i]) pattern_map[i] = j++; + } + for (i=0; i<256; i++) + { + if (!InstUsed[i]) + { + pattern_map[i] = (j < MAX_PATTERNS) ? j : 0xFE; + j++; + } + } + for (i=0; i<norders; i++) + { + Order[i] = pattern_map[Order[i]]; + } + } else + { + for (UINT i=0; i<256; i++) pattern_map[i] = i; + } + memset(InstUsed, 0, sizeof(InstUsed)); + dwMemPos = dwHdrSize + 60; + if (dwMemPos + 8 >= dwMemLength) return TRUE; + // Reading patterns + memset(channels_used, 0, sizeof(channels_used)); + for (UINT ipat=0; ipat<patterns; ipat++) + { + UINT ipatmap = pattern_map[ipat]; + DWORD dwSize = 0; + WORD rows=64, packsize=0; + dwSize = bswapLE32(*((DWORD *)(lpStream+dwMemPos))); + while ((dwMemPos + dwSize >= dwMemLength) || (dwSize & 0xFFFFFF00)) + { + if (dwMemPos + 4 >= dwMemLength) break; + dwMemPos++; + dwSize = bswapLE32(*((DWORD *)(lpStream+dwMemPos))); + } + rows = bswapLE16(*((WORD *)(lpStream+dwMemPos+5))); + if ((!rows) || (rows > 256)) rows = 64; + packsize = bswapLE16(*((WORD *)(lpStream+dwMemPos+7))); + if (dwMemPos + dwSize + 4 > dwMemLength) return TRUE; + dwMemPos += dwSize; + if (dwMemPos + packsize + 4 > dwMemLength) return TRUE; + MODCOMMAND *p; + if (ipatmap < MAX_PATTERNS) + { + PatternSize[ipatmap] = rows; + if ((Patterns[ipatmap] = AllocatePattern(rows, m_nChannels)) == NULL) return TRUE; + if (!packsize) continue; + p = Patterns[ipatmap]; + } else p = NULL; + const BYTE *src = lpStream+dwMemPos; + UINT j=0; + for (UINT row=0; row<rows; row++) + { + for (UINT chn=0; chn<m_nChannels; chn++) + { + if ((p) && (j < packsize)) + { + BYTE b = src[j++]; + UINT vol = 0; + if (b & 0x80) + { + if (b & 1) p->note = src[j++]; + if (b & 2) p->instr = src[j++]; + if (b & 4) vol = src[j++]; + if (b & 8) p->command = src[j++]; + if (b & 16) p->param = src[j++]; + } else + { + p->note = b; + p->instr = src[j++]; + vol = src[j++]; + p->command = src[j++]; + p->param = src[j++]; + } + if (p->note == 97) p->note = 0xFF; else + if ((p->note) && (p->note < 97)) p->note += 12; + if (p->note) channels_used[chn] = 1; + if (p->command | p->param) ConvertModCommand(p); + if (p->instr == 0xff) p->instr = 0; + if (p->instr) InstUsed[p->instr] = TRUE; + if ((vol >= 0x10) && (vol <= 0x50)) + { + p->volcmd = VOLCMD_VOLUME; + p->vol = vol - 0x10; + } else + if (vol >= 0x60) + { + UINT v = vol & 0xF0; + vol &= 0x0F; + p->vol = vol; + switch(v) + { + // 60-6F: Volume Slide Down + case 0x60: p->volcmd = VOLCMD_VOLSLIDEDOWN; break; + // 70-7F: Volume Slide Up: + case 0x70: p->volcmd = VOLCMD_VOLSLIDEUP; break; + // 80-8F: Fine Volume Slide Down + case 0x80: p->volcmd = VOLCMD_FINEVOLDOWN; break; + // 90-9F: Fine Volume Slide Up + case 0x90: p->volcmd = VOLCMD_FINEVOLUP; break; + // A0-AF: Set Vibrato Speed + case 0xA0: p->volcmd = VOLCMD_VIBRATOSPEED; break; + // B0-BF: Vibrato + case 0xB0: p->volcmd = VOLCMD_VIBRATO; break; + // C0-CF: Set Panning + case 0xC0: p->volcmd = VOLCMD_PANNING; p->vol = (vol << 2) + 2; break; + // D0-DF: Panning Slide Left + case 0xD0: p->volcmd = VOLCMD_PANSLIDELEFT; break; + // E0-EF: Panning Slide Right + case 0xE0: p->volcmd = VOLCMD_PANSLIDERIGHT; break; + // F0-FF: Tone Portamento + case 0xF0: p->volcmd = VOLCMD_TONEPORTAMENTO; break; + } + } + p++; + } else + if (j < packsize) + { + BYTE b = src[j++]; + if (b & 0x80) + { + if (b & 1) j++; + if (b & 2) j++; + if (b & 4) j++; + if (b & 8) j++; + if (b & 16) j++; + } else j += 4; + } else break; + } + } + dwMemPos += packsize; + } + // Wrong offset check + while (dwMemPos + 4 < dwMemLength) + { + DWORD d = bswapLE32(*((DWORD *)(lpStream+dwMemPos))); + if (d < 0x300) break; + dwMemPos++; + } + memset(samples_used, 0, sizeof(samples_used)); + unused_samples = 0; + // Reading instruments + for (UINT iIns=1; iIns<=instruments; iIns++) + { + XMINSTRUMENTHEADER *pih; + BYTE flags[32]; + DWORD samplesize[32]; + UINT samplemap[32]; + WORD nsamples; + + if (dwMemPos + sizeof(XMINSTRUMENTHEADER) >= dwMemLength) return TRUE; + pih = (XMINSTRUMENTHEADER *)(lpStream+dwMemPos); + if (dwMemPos + bswapLE32(pih->size) > dwMemLength) return TRUE; + if ((Headers[iIns] = new INSTRUMENTHEADER) == NULL) continue; + memset(Headers[iIns], 0, sizeof(INSTRUMENTHEADER)); + memcpy(Headers[iIns]->name, pih->name, 22); + if ((nsamples = pih->samples) > 0) + { + if (dwMemPos + sizeof(XMSAMPLEHEADER) > dwMemLength) return TRUE; + memcpy(&xmsh, lpStream+dwMemPos+sizeof(XMINSTRUMENTHEADER), sizeof(XMSAMPLEHEADER)); + xmsh.shsize = bswapLE32(xmsh.shsize); + for (int i = 0; i < 24; ++i) { + xmsh.venv[i] = bswapLE16(xmsh.venv[i]); + xmsh.penv[i] = bswapLE16(xmsh.penv[i]); + } + xmsh.volfade = bswapLE16(xmsh.volfade); + xmsh.res = bswapLE16(xmsh.res); + dwMemPos += bswapLE32(pih->size); + } else + { + if (bswapLE32(pih->size)) dwMemPos += bswapLE32(pih->size); + else dwMemPos += sizeof(XMINSTRUMENTHEADER); + continue; + } + memset(samplemap, 0, sizeof(samplemap)); + if (nsamples > 32) return TRUE; + UINT newsamples = m_nSamples; + for (UINT nmap=0; nmap<nsamples; nmap++) + { + UINT n = m_nSamples+nmap+1; + if (n >= MAX_SAMPLES) + { + n = m_nSamples; + while (n > 0) + { + if (!Ins[n].pSample) + { + for (UINT xmapchk=0; xmapchk < nmap; xmapchk++) + { + if (samplemap[xmapchk] == n) goto alreadymapped; + } + for (UINT clrs=1; clrs<iIns; clrs++) if (Headers[clrs]) + { + INSTRUMENTHEADER *pks = Headers[clrs]; + for (UINT ks=0; ks<128; ks++) + { + if (pks->Keyboard[ks] == n) pks->Keyboard[ks] = 0; + } + } + break; + } + alreadymapped: + n--; + } +#ifndef MODPLUG_FASTSOUNDLIB + // Damn! more than 200 samples: look for duplicates + if (!n) + { + if (!unused_samples) + { + unused_samples = DetectUnusedSamples(samples_used); + if (!unused_samples) unused_samples = 0xFFFF; + } + if ((unused_samples) && (unused_samples != 0xFFFF)) + { + for (UINT iext=m_nSamples; iext>=1; iext--) if (!samples_used[iext]) + { + unused_samples--; + samples_used[iext] = TRUE; + DestroySample(iext); + n = iext; + for (UINT mapchk=0; mapchk<nmap; mapchk++) + { + if (samplemap[mapchk] == n) samplemap[mapchk] = 0; + } + for (UINT clrs=1; clrs<iIns; clrs++) if (Headers[clrs]) + { + INSTRUMENTHEADER *pks = Headers[clrs]; + for (UINT ks=0; ks<128; ks++) + { + if (pks->Keyboard[ks] == n) pks->Keyboard[ks] = 0; + } + } + memset(&Ins[n], 0, sizeof(Ins[0])); + break; + } + } + } +#endif // MODPLUG_FASTSOUNDLIB + } + if (newsamples < n) newsamples = n; + samplemap[nmap] = n; + } + m_nSamples = newsamples; + // Reading Volume Envelope + INSTRUMENTHEADER *penv = Headers[iIns]; + penv->nMidiProgram = pih->type; + penv->nFadeOut = xmsh.volfade; + penv->nPan = 128; + penv->nPPC = 5*12; + if (xmsh.vtype & 1) penv->dwFlags |= ENV_VOLUME; + if (xmsh.vtype & 2) penv->dwFlags |= ENV_VOLSUSTAIN; + if (xmsh.vtype & 4) penv->dwFlags |= ENV_VOLLOOP; + if (xmsh.ptype & 1) penv->dwFlags |= ENV_PANNING; + if (xmsh.ptype & 2) penv->dwFlags |= ENV_PANSUSTAIN; + if (xmsh.ptype & 4) penv->dwFlags |= ENV_PANLOOP; + if (xmsh.vnum > 12) xmsh.vnum = 12; + if (xmsh.pnum > 12) xmsh.pnum = 12; + penv->nVolEnv = xmsh.vnum; + if (!xmsh.vnum) penv->dwFlags &= ~ENV_VOLUME; + if (!xmsh.pnum) penv->dwFlags &= ~ENV_PANNING; + penv->nPanEnv = xmsh.pnum; + penv->nVolSustainBegin = penv->nVolSustainEnd = xmsh.vsustain; + if (xmsh.vsustain >= 12) penv->dwFlags &= ~ENV_VOLSUSTAIN; + penv->nVolLoopStart = xmsh.vloops; + penv->nVolLoopEnd = xmsh.vloope; + if (penv->nVolLoopEnd >= 12) penv->nVolLoopEnd = 0; + if (penv->nVolLoopStart >= penv->nVolLoopEnd) penv->dwFlags &= ~ENV_VOLLOOP; + penv->nPanSustainBegin = penv->nPanSustainEnd = xmsh.psustain; + if (xmsh.psustain >= 12) penv->dwFlags &= ~ENV_PANSUSTAIN; + penv->nPanLoopStart = xmsh.ploops; + penv->nPanLoopEnd = xmsh.ploope; + if (penv->nPanLoopEnd >= 12) penv->nPanLoopEnd = 0; + if (penv->nPanLoopStart >= penv->nPanLoopEnd) penv->dwFlags &= ~ENV_PANLOOP; + penv->nGlobalVol = 64; + for (UINT ienv=0; ienv<12; ienv++) + { + penv->VolPoints[ienv] = (WORD)xmsh.venv[ienv*2]; + penv->VolEnv[ienv] = (BYTE)xmsh.venv[ienv*2+1]; + penv->PanPoints[ienv] = (WORD)xmsh.penv[ienv*2]; + penv->PanEnv[ienv] = (BYTE)xmsh.penv[ienv*2+1]; + if (ienv) + { + if (penv->VolPoints[ienv] < penv->VolPoints[ienv-1]) + { + penv->VolPoints[ienv] &= 0xFF; + penv->VolPoints[ienv] += penv->VolPoints[ienv-1] & 0xFF00; + if (penv->VolPoints[ienv] < penv->VolPoints[ienv-1]) penv->VolPoints[ienv] += 0x100; + } + if (penv->PanPoints[ienv] < penv->PanPoints[ienv-1]) + { + penv->PanPoints[ienv] &= 0xFF; + penv->PanPoints[ienv] += penv->PanPoints[ienv-1] & 0xFF00; + if (penv->PanPoints[ienv] < penv->PanPoints[ienv-1]) penv->PanPoints[ienv] += 0x100; + } + } + } + for (UINT j=0; j<96; j++) + { + penv->NoteMap[j+12] = j+1+12; + if (xmsh.snum[j] < nsamples) + penv->Keyboard[j+12] = samplemap[xmsh.snum[j]]; + } + // Reading samples + for (UINT ins=0; ins<nsamples; ins++) + { + if ((dwMemPos + sizeof(xmss) > dwMemLength) + || (dwMemPos + xmsh.shsize > dwMemLength)) return TRUE; + memcpy(&xmss, lpStream+dwMemPos, sizeof(xmss)); + xmss.samplen = bswapLE32(xmss.samplen); + xmss.loopstart = bswapLE32(xmss.loopstart); + xmss.looplen = bswapLE32(xmss.looplen); + dwMemPos += xmsh.shsize; + flags[ins] = (xmss.type & 0x10) ? RS_PCM16D : RS_PCM8D; + if (xmss.type & 0x20) flags[ins] = (xmss.type & 0x10) ? RS_STPCM16D : RS_STPCM8D; + samplesize[ins] = xmss.samplen; + if (!samplemap[ins]) continue; + if (xmss.type & 0x10) + { + xmss.looplen >>= 1; + xmss.loopstart >>= 1; + xmss.samplen >>= 1; + } + if (xmss.type & 0x20) + { + xmss.looplen >>= 1; + xmss.loopstart >>= 1; + xmss.samplen >>= 1; + } + if (xmss.samplen > MAX_SAMPLE_LENGTH) xmss.samplen = MAX_SAMPLE_LENGTH; + if (xmss.loopstart >= xmss.samplen) xmss.type &= ~3; + xmss.looplen += xmss.loopstart; + if (xmss.looplen > xmss.samplen) xmss.looplen = xmss.samplen; + if (!xmss.looplen) xmss.type &= ~3; + UINT imapsmp = samplemap[ins]; + memcpy(m_szNames[imapsmp], xmss.name, 22); + m_szNames[imapsmp][22] = 0; + MODINSTRUMENT *pins = &Ins[imapsmp]; + pins->nLength = (xmss.samplen > MAX_SAMPLE_LENGTH) ? MAX_SAMPLE_LENGTH : xmss.samplen; + pins->nLoopStart = xmss.loopstart; + pins->nLoopEnd = xmss.looplen; + if (pins->nLoopEnd > pins->nLength) pins->nLoopEnd = pins->nLength; + if (pins->nLoopStart >= pins->nLoopEnd) + { + pins->nLoopStart = pins->nLoopEnd = 0; + } + if (xmss.type & 3) pins->uFlags |= CHN_LOOP; + if (xmss.type & 2) pins->uFlags |= CHN_PINGPONGLOOP; + pins->nVolume = xmss.vol << 2; + if (pins->nVolume > 256) pins->nVolume = 256; + pins->nGlobalVol = 64; + if ((xmss.res == 0xAD) && (!(xmss.type & 0x30))) + { + flags[ins] = RS_ADPCM4; + samplesize[ins] = (samplesize[ins]+1)/2 + 16; + } + pins->nFineTune = xmss.finetune; + pins->RelativeTone = (int)xmss.relnote; + pins->nPan = xmss.pan; + pins->uFlags |= CHN_PANNING; + pins->nVibType = xmsh.vibtype; + pins->nVibSweep = xmsh.vibsweep; + pins->nVibDepth = xmsh.vibdepth; + pins->nVibRate = xmsh.vibrate; + memcpy(pins->name, xmss.name, 22); + pins->name[21] = 0; + } +#if 0 + if ((xmsh.reserved2 > nsamples) && (xmsh.reserved2 <= 16)) + { + dwMemPos += (((UINT)xmsh.reserved2) - nsamples) * xmsh.shsize; + } +#endif + for (UINT ismpd=0; ismpd<nsamples; ismpd++) + { + if ((samplemap[ismpd]) && (samplesize[ismpd]) && (dwMemPos < dwMemLength)) + { + ReadSample(&Ins[samplemap[ismpd]], flags[ismpd], (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos); + } + dwMemPos += samplesize[ismpd]; + if (dwMemPos >= dwMemLength) break; + } + } + // Read song comments: "TEXT" + if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x74786574)) + { + UINT len = *((DWORD *)(lpStream+dwMemPos+4)); + dwMemPos += 8; + if ((dwMemPos + len <= dwMemLength) && (len < 16384)) + { + m_lpszSongComments = new char[len+1]; + if (m_lpszSongComments) + { + memcpy(m_lpszSongComments, lpStream+dwMemPos, len); + m_lpszSongComments[len] = 0; + } + dwMemPos += len; + } + } + // Read midi config: "MIDI" + if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x4944494D)) + { + UINT len = *((DWORD *)(lpStream+dwMemPos+4)); + dwMemPos += 8; + if (len == sizeof(MODMIDICFG)) + { + memcpy(&m_MidiCfg, lpStream+dwMemPos, len); + m_dwSongFlags |= SONG_EMBEDMIDICFG; + } + } + // Read pattern names: "PNAM" + if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x4d414e50)) + { + UINT len = *((DWORD *)(lpStream+dwMemPos+4)); + dwMemPos += 8; + if ((dwMemPos + len <= dwMemLength) && (len <= MAX_PATTERNS*MAX_PATTERNNAME) && (len >= MAX_PATTERNNAME)) + { + m_lpszPatternNames = new char[len]; + + if (m_lpszPatternNames) + { + m_nPatternNames = len / MAX_PATTERNNAME; + memcpy(m_lpszPatternNames, lpStream+dwMemPos, len); + } + dwMemPos += len; + } + } + // Read channel names: "CNAM" + if ((dwMemPos + 8 < dwMemLength) && (bswapLE32(*((DWORD *)(lpStream+dwMemPos))) == 0x4d414e43)) + { + UINT len = *((DWORD *)(lpStream+dwMemPos+4)); + dwMemPos += 8; + if ((dwMemPos + len <= dwMemLength) && (len <= MAX_BASECHANNELS*MAX_CHANNELNAME)) + { + UINT n = len / MAX_CHANNELNAME; + for (UINT i=0; i<n; i++) + { + memcpy(ChnSettings[i].szName, (lpStream+dwMemPos+i*MAX_CHANNELNAME), MAX_CHANNELNAME); + ChnSettings[i].szName[MAX_CHANNELNAME-1] = 0; + } + dwMemPos += len; + } + } + // Read mix plugins information + if (dwMemPos + 8 < dwMemLength) + { + dwMemPos += LoadMixPlugins(lpStream+dwMemPos, dwMemLength-dwMemPos); + } + return TRUE; +} + + +#ifndef MODPLUG_NO_FILESAVE + +BOOL CSoundFile::SaveXM(LPCSTR lpszFileName, UINT nPacking) +//--------------------------------------------------------- +{ + BYTE s[64*64*5]; + XMFILEHEADER header; + XMINSTRUMENTHEADER xmih; + XMSAMPLEHEADER xmsh; + XMSAMPLESTRUCT xmss; + BYTE smptable[32]; + BYTE xmph[9]; + FILE *f; + int i; + + if ((!m_nChannels) || (!lpszFileName)) return FALSE; + if ((f = fopen(lpszFileName, "wb")) == NULL) return FALSE; + fwrite("Extended Module: ", 17, 1, f); + fwrite(m_szNames[0], 20, 1, f); + s[0] = 0x1A; + lstrcpy((LPSTR)&s[1], (nPacking) ? "MOD Plugin packed " : "FastTracker v2.00 "); + s[21] = 0x04; + s[22] = 0x01; + fwrite(s, 23, 1, f); + // Writing song header + memset(&header, 0, sizeof(header)); + header.size = sizeof(XMFILEHEADER); + header.norder = 0; + header.restartpos = m_nRestartPos; + header.channels = m_nChannels; + header.patterns = 0; + for (i=0; i<MAX_ORDERS; i++) + { + if (Order[i] == 0xFF) break; + header.norder++; + if ((Order[i] >= header.patterns) && (Order[i] < MAX_PATTERNS)) header.patterns = Order[i]+1; + } + header.instruments = m_nInstruments; + if (!header.instruments) header.instruments = m_nSamples; + header.flags = (m_dwSongFlags & SONG_LINEARSLIDES) ? 0x01 : 0x00; + if (m_dwSongFlags & SONG_EXFILTERRANGE) header.flags |= 0x1000; + header.tempo = m_nDefaultTempo; + header.speed = m_nDefaultSpeed; + memcpy(header.order, Order, header.norder); + fwrite(&header, 1, sizeof(header), f); + // Writing patterns + for (i=0; i<header.patterns; i++) if (Patterns[i]) + { + MODCOMMAND *p = Patterns[i]; + UINT len = 0; + + memset(&xmph, 0, sizeof(xmph)); + xmph[0] = 9; + xmph[5] = (BYTE)(PatternSize[i] & 0xFF); + xmph[6] = (BYTE)(PatternSize[i] >> 8); + for (UINT j=m_nChannels*PatternSize[i]; j; j--,p++) + { + UINT note = p->note; + UINT param = ModSaveCommand(p, TRUE); + UINT command = param >> 8; + param &= 0xFF; + if (note >= 0xFE) note = 97; else + if ((note <= 12) || (note > 96+12)) note = 0; else + note -= 12; + UINT vol = 0; + if (p->volcmd) + { + UINT volcmd = p->volcmd; + switch(volcmd) + { + case VOLCMD_VOLUME: vol = 0x10 + p->vol; break; + case VOLCMD_VOLSLIDEDOWN: vol = 0x60 + (p->vol & 0x0F); break; + case VOLCMD_VOLSLIDEUP: vol = 0x70 + (p->vol & 0x0F); break; + case VOLCMD_FINEVOLDOWN: vol = 0x80 + (p->vol & 0x0F); break; + case VOLCMD_FINEVOLUP: vol = 0x90 + (p->vol & 0x0F); break; + case VOLCMD_VIBRATOSPEED: vol = 0xA0 + (p->vol & 0x0F); break; + case VOLCMD_VIBRATO: vol = 0xB0 + (p->vol & 0x0F); break; + case VOLCMD_PANNING: vol = 0xC0 + (p->vol >> 2); if (vol > 0xCF) vol = 0xCF; break; + case VOLCMD_PANSLIDELEFT: vol = 0xD0 + (p->vol & 0x0F); break; + case VOLCMD_PANSLIDERIGHT: vol = 0xE0 + (p->vol & 0x0F); break; + case VOLCMD_TONEPORTAMENTO: vol = 0xF0 + (p->vol & 0x0F); break; + } + } + if ((note) && (p->instr) && (vol > 0x0F) && (command) && (param)) + { + s[len++] = note; + s[len++] = p->instr; + s[len++] = vol; + s[len++] = command; + s[len++] = param; + } else + { + BYTE b = 0x80; + if (note) b |= 0x01; + if (p->instr) b |= 0x02; + if (vol >= 0x10) b |= 0x04; + if (command) b |= 0x08; + if (param) b |= 0x10; + s[len++] = b; + if (b & 1) s[len++] = note; + if (b & 2) s[len++] = p->instr; + if (b & 4) s[len++] = vol; + if (b & 8) s[len++] = command; + if (b & 16) s[len++] = param; + } + if (len > sizeof(s) - 5) break; + } + xmph[7] = (BYTE)(len & 0xFF); + xmph[8] = (BYTE)(len >> 8); + fwrite(xmph, 1, 9, f); + fwrite(s, 1, len, f); + } else + { + memset(&xmph, 0, sizeof(xmph)); + xmph[0] = 9; + xmph[5] = (BYTE)(PatternSize[i] & 0xFF); + xmph[6] = (BYTE)(PatternSize[i] >> 8); + fwrite(xmph, 1, 9, f); + } + // Writing instruments + for (i=1; i<=header.instruments; i++) + { + MODINSTRUMENT *pins; + BYTE flags[32]; + + memset(&xmih, 0, sizeof(xmih)); + memset(&xmsh, 0, sizeof(xmsh)); + xmih.size = sizeof(xmih) + sizeof(xmsh); + memcpy(xmih.name, m_szNames[i], 22); + xmih.type = 0; + xmih.samples = 0; + if (m_nInstruments) + { + INSTRUMENTHEADER *penv = Headers[i]; + if (penv) + { + memcpy(xmih.name, penv->name, 22); + xmih.type = penv->nMidiProgram; + xmsh.volfade = penv->nFadeOut; + xmsh.vnum = (BYTE)penv->nVolEnv; + xmsh.pnum = (BYTE)penv->nPanEnv; + if (xmsh.vnum > 12) xmsh.vnum = 12; + if (xmsh.pnum > 12) xmsh.pnum = 12; + for (UINT ienv=0; ienv<12; ienv++) + { + xmsh.venv[ienv*2] = penv->VolPoints[ienv]; + xmsh.venv[ienv*2+1] = penv->VolEnv[ienv]; + xmsh.penv[ienv*2] = penv->PanPoints[ienv]; + xmsh.penv[ienv*2+1] = penv->PanEnv[ienv]; + } + if (penv->dwFlags & ENV_VOLUME) xmsh.vtype |= 1; + if (penv->dwFlags & ENV_VOLSUSTAIN) xmsh.vtype |= 2; + if (penv->dwFlags & ENV_VOLLOOP) xmsh.vtype |= 4; + if (penv->dwFlags & ENV_PANNING) xmsh.ptype |= 1; + if (penv->dwFlags & ENV_PANSUSTAIN) xmsh.ptype |= 2; + if (penv->dwFlags & ENV_PANLOOP) xmsh.ptype |= 4; + xmsh.vsustain = (BYTE)penv->nVolSustainBegin; + xmsh.vloops = (BYTE)penv->nVolLoopStart; + xmsh.vloope = (BYTE)penv->nVolLoopEnd; + xmsh.psustain = (BYTE)penv->nPanSustainBegin; + xmsh.ploops = (BYTE)penv->nPanLoopStart; + xmsh.ploope = (BYTE)penv->nPanLoopEnd; + for (UINT j=0; j<96; j++) if (penv->Keyboard[j+12]) + { + UINT k; + for (k=0; k<xmih.samples; k++) if (smptable[k] == penv->Keyboard[j+12]) break; + if (k == xmih.samples) + { + smptable[xmih.samples++] = penv->Keyboard[j+12]; + } + if (xmih.samples >= 32) break; + xmsh.snum[j] = k; + } +// xmsh.reserved2 = xmih.samples; + } + } else + { + xmih.samples = 1; +// xmsh.reserved2 = 1; + smptable[0] = i; + } + xmsh.shsize = (xmih.samples) ? 40 : 0; + fwrite(&xmih, 1, sizeof(xmih), f); + if (smptable[0]) + { + MODINSTRUMENT *pvib = &Ins[smptable[0]]; + xmsh.vibtype = pvib->nVibType; + xmsh.vibsweep = pvib->nVibSweep; + xmsh.vibdepth = pvib->nVibDepth; + xmsh.vibrate = pvib->nVibRate; + } + fwrite(&xmsh, 1, xmih.size - sizeof(xmih), f); + if (!xmih.samples) continue; + for (UINT ins=0; ins<xmih.samples; ins++) + { + memset(&xmss, 0, sizeof(xmss)); + if (smptable[ins]) memcpy(xmss.name, m_szNames[smptable[ins]], 22); + pins = &Ins[smptable[ins]]; + xmss.samplen = pins->nLength; + xmss.loopstart = pins->nLoopStart; + xmss.looplen = pins->nLoopEnd - pins->nLoopStart; + xmss.vol = pins->nVolume / 4; + xmss.finetune = (char)pins->nFineTune; + xmss.type = 0; + if (pins->uFlags & CHN_LOOP) xmss.type = (pins->uFlags & CHN_PINGPONGLOOP) ? 2 : 1; + flags[ins] = RS_PCM8D; +#ifndef NO_PACKING + if (nPacking) + { + if ((!(pins->uFlags & (CHN_16BIT|CHN_STEREO))) + && (CanPackSample((char *)pins->pSample, pins->nLength, nPacking))) + { + flags[ins] = RS_ADPCM4; + xmss.res = 0xAD; + } + } else +#endif + { + if (pins->uFlags & CHN_16BIT) + { + flags[ins] = RS_PCM16D; + xmss.type |= 0x10; + xmss.looplen *= 2; + xmss.loopstart *= 2; + xmss.samplen *= 2; + } + if (pins->uFlags & CHN_STEREO) + { + flags[ins] = (pins->uFlags & CHN_16BIT) ? RS_STPCM16D : RS_STPCM8D; + xmss.type |= 0x20; + xmss.looplen *= 2; + xmss.loopstart *= 2; + xmss.samplen *= 2; + } + } + xmss.pan = 255; + if (pins->nPan < 256) xmss.pan = (BYTE)pins->nPan; + xmss.relnote = (signed char)pins->RelativeTone; + fwrite(&xmss, 1, xmsh.shsize, f); + } + for (UINT ismpd=0; ismpd<xmih.samples; ismpd++) + { + pins = &Ins[smptable[ismpd]]; + if (pins->pSample) + { +#ifndef NO_PACKING + if ((flags[ismpd] == RS_ADPCM4) && (xmih.samples>1)) CanPackSample((char *)pins->pSample, pins->nLength, nPacking); +#endif // NO_PACKING + WriteSample(f, pins, flags[ismpd]); + } + } + } + // Writing song comments + if ((m_lpszSongComments) && (m_lpszSongComments[0])) + { + DWORD d = 0x74786574; + fwrite(&d, 1, 4, f); + d = strlen(m_lpszSongComments); + fwrite(&d, 1, 4, f); + fwrite(m_lpszSongComments, 1, d, f); + } + // Writing midi cfg + if (m_dwSongFlags & SONG_EMBEDMIDICFG) + { + DWORD d = 0x4944494D; + fwrite(&d, 1, 4, f); + d = sizeof(MODMIDICFG); + fwrite(&d, 1, 4, f); + fwrite(&m_MidiCfg, 1, sizeof(MODMIDICFG), f); + } + // Writing Pattern Names + if ((m_nPatternNames) && (m_lpszPatternNames)) + { + DWORD dwLen = m_nPatternNames * MAX_PATTERNNAME; + while ((dwLen >= MAX_PATTERNNAME) && (!m_lpszPatternNames[dwLen-MAX_PATTERNNAME])) dwLen -= MAX_PATTERNNAME; + if (dwLen >= MAX_PATTERNNAME) + { + DWORD d = 0x4d414e50; + fwrite(&d, 1, 4, f); + fwrite(&dwLen, 1, 4, f); + fwrite(m_lpszPatternNames, 1, dwLen, f); + } + } + // Writing Channel Names + { + UINT nChnNames = 0; + for (UINT inam=0; inam<m_nChannels; inam++) + { + if (ChnSettings[inam].szName[0]) nChnNames = inam+1; + } + // Do it! + if (nChnNames) + { + DWORD dwLen = nChnNames * MAX_CHANNELNAME; + DWORD d = 0x4d414e43; + fwrite(&d, 1, 4, f); + fwrite(&dwLen, 1, 4, f); + for (UINT inam=0; inam<nChnNames; inam++) + { + fwrite(ChnSettings[inam].szName, 1, MAX_CHANNELNAME, f); + } + } + } + // Save mix plugins information + SaveMixPlugins(f); + fclose(f); + return TRUE; +} + +#endif // MODPLUG_NO_FILESAVE diff --git a/lib/libmodplug/src/mmcmp.cpp b/lib/libmodplug/src/mmcmp.cpp new file mode 100644 index 0000000000..200ee3b93d --- /dev/null +++ b/lib/libmodplug/src/mmcmp.cpp @@ -0,0 +1,406 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +#include "stdafx.h" +#include "sndfile.h" + +BOOL PP20_Unpack(LPCBYTE *ppMemFile, LPDWORD pdwMemLength); + +typedef struct MMCMPFILEHEADER +{ + DWORD id_ziRC; // "ziRC" + DWORD id_ONia; // "ONia" + WORD hdrsize; +} MMCMPFILEHEADER, *LPMMCMPFILEHEADER; + +typedef struct MMCMPHEADER +{ + WORD version; + WORD nblocks; + DWORD filesize; + DWORD blktable; + BYTE glb_comp; + BYTE fmt_comp; +} MMCMPHEADER, *LPMMCMPHEADER; + +typedef struct MMCMPBLOCK +{ + DWORD unpk_size; + DWORD pk_size; + DWORD xor_chk; + WORD sub_blk; + WORD flags; + WORD tt_entries; + WORD num_bits; +} MMCMPBLOCK, *LPMMCMPBLOCK; + +typedef struct MMCMPSUBBLOCK +{ + DWORD unpk_pos; + DWORD unpk_size; +} MMCMPSUBBLOCK, *LPMMCMPSUBBLOCK; + +#define MMCMP_COMP 0x0001 +#define MMCMP_DELTA 0x0002 +#define MMCMP_16BIT 0x0004 +#define MMCMP_STEREO 0x0100 +#define MMCMP_ABS16 0x0200 +#define MMCMP_ENDIAN 0x0400 + +typedef struct MMCMPBITBUFFER +{ + UINT bitcount; + DWORD bitbuffer; + LPCBYTE pSrc; + LPCBYTE pEnd; + + DWORD GetBits(UINT nBits); +} MMCMPBITBUFFER; + + +DWORD MMCMPBITBUFFER::GetBits(UINT nBits) +//--------------------------------------- +{ + DWORD d; + if (!nBits) return 0; + while (bitcount < 24) + { + bitbuffer |= ((pSrc < pEnd) ? *pSrc++ : 0) << bitcount; + bitcount += 8; + } + d = bitbuffer & ((1 << nBits) - 1); + bitbuffer >>= nBits; + bitcount -= nBits; + return d; +} + +//#define MMCMP_LOG + +#ifdef MMCMP_LOG +extern void Log(LPCSTR s, ...); +#endif + +const DWORD MMCMP8BitCommands[8] = +{ + 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF8 +}; + +const UINT MMCMP8BitFetch[8] = +{ + 3, 3, 3, 3, 2, 1, 0, 0 +}; + +const DWORD MMCMP16BitCommands[16] = +{ + 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF0, + 0x1F0, 0x3F0, 0x7F0, 0xFF0, 0x1FF0, 0x3FF0, 0x7FF0, 0xFFF0 +}; + +const UINT MMCMP16BitFetch[16] = +{ + 4, 4, 4, 4, 3, 2, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + + +BOOL MMCMP_Unpack(LPCBYTE *ppMemFile, LPDWORD pdwMemLength) +//--------------------------------------------------------- +{ + DWORD dwMemLength = *pdwMemLength; + LPCBYTE lpMemFile = *ppMemFile; + LPBYTE pBuffer; + LPMMCMPFILEHEADER pmfh = (LPMMCMPFILEHEADER)(lpMemFile); + LPMMCMPHEADER pmmh = (LPMMCMPHEADER)(lpMemFile+10); + LPDWORD pblk_table; + DWORD dwFileSize; + + if (PP20_Unpack(ppMemFile, pdwMemLength)) + { + return TRUE; + } + if ((dwMemLength < 256) || (!pmfh) || (pmfh->id_ziRC != 0x4352697A) || (pmfh->id_ONia != 0x61694e4f) || (pmfh->hdrsize < 14) + || (!pmmh->nblocks) || (pmmh->filesize < 16) || (pmmh->filesize > 0x8000000) + || (pmmh->blktable >= dwMemLength) || (pmmh->blktable + 4*pmmh->nblocks > dwMemLength)) return FALSE; + dwFileSize = pmmh->filesize; + if ((pBuffer = (LPBYTE)GlobalAllocPtr(GHND, (dwFileSize + 31) & ~15)) == NULL) return FALSE; + pblk_table = (LPDWORD)(lpMemFile+pmmh->blktable); + for (UINT nBlock=0; nBlock<pmmh->nblocks; nBlock++) + { + DWORD dwMemPos = pblk_table[nBlock]; + LPMMCMPBLOCK pblk = (LPMMCMPBLOCK)(lpMemFile+dwMemPos); + LPMMCMPSUBBLOCK psubblk = (LPMMCMPSUBBLOCK)(lpMemFile+dwMemPos+20); + + if ((dwMemPos + 20 >= dwMemLength) || (dwMemPos + 20 + pblk->sub_blk*8 >= dwMemLength)) break; + dwMemPos += 20 + pblk->sub_blk*8; +#ifdef MMCMP_LOG + Log("block %d: flags=%04X sub_blocks=%d", nBlock, (UINT)pblk->flags, (UINT)pblk->sub_blk); + Log(" pksize=%d unpksize=%d", pblk->pk_size, pblk->unpk_size); + Log(" tt_entries=%d num_bits=%d\n", pblk->tt_entries, pblk->num_bits); +#endif + // Data is not packed + if (!(pblk->flags & MMCMP_COMP)) + { + for (UINT i=0; i<pblk->sub_blk; i++) + { + if ((psubblk->unpk_pos > dwFileSize) || (psubblk->unpk_pos + psubblk->unpk_size > dwFileSize)) break; +#ifdef MMCMP_LOG + Log(" Unpacked sub-block %d: offset %d, size=%d\n", i, psubblk->unpk_pos, psubblk->unpk_size); +#endif + memcpy(pBuffer+psubblk->unpk_pos, lpMemFile+dwMemPos, psubblk->unpk_size); + dwMemPos += psubblk->unpk_size; + psubblk++; + } + } else + // Data is 16-bit packed + if (pblk->flags & MMCMP_16BIT) + { + MMCMPBITBUFFER bb; + LPWORD pDest = (LPWORD)(pBuffer + psubblk->unpk_pos); + DWORD dwSize = psubblk->unpk_size >> 1; + DWORD dwPos = 0; + UINT numbits = pblk->num_bits; + UINT subblk = 0, oldval = 0; + +#ifdef MMCMP_LOG + Log(" 16-bit block: pos=%d size=%d ", psubblk->unpk_pos, psubblk->unpk_size); + if (pblk->flags & MMCMP_DELTA) Log("DELTA "); + if (pblk->flags & MMCMP_ABS16) Log("ABS16 "); + Log("\n"); +#endif + bb.bitcount = 0; + bb.bitbuffer = 0; + bb.pSrc = lpMemFile+dwMemPos+pblk->tt_entries; + bb.pEnd = lpMemFile+dwMemPos+pblk->pk_size; + while (subblk < pblk->sub_blk) + { + UINT newval = 0x10000; + DWORD d = bb.GetBits(numbits+1); + + if (d >= MMCMP16BitCommands[numbits]) + { + UINT nFetch = MMCMP16BitFetch[numbits]; + UINT newbits = bb.GetBits(nFetch) + ((d - MMCMP16BitCommands[numbits]) << nFetch); + if (newbits != numbits) + { + numbits = newbits & 0x0F; + } else + { + if ((d = bb.GetBits(4)) == 0x0F) + { + if (bb.GetBits(1)) break; + newval = 0xFFFF; + } else + { + newval = 0xFFF0 + d; + } + } + } else + { + newval = d; + } + if (newval < 0x10000) + { + newval = (newval & 1) ? (UINT)(-(LONG)((newval+1) >> 1)) : (UINT)(newval >> 1); + if (pblk->flags & MMCMP_DELTA) + { + newval += oldval; + oldval = newval; + } else + if (!(pblk->flags & MMCMP_ABS16)) + { + newval ^= 0x8000; + } + pDest[dwPos++] = (WORD)newval; + } + if (dwPos >= dwSize) + { + subblk++; + dwPos = 0; + dwSize = psubblk[subblk].unpk_size >> 1; + pDest = (LPWORD)(pBuffer + psubblk[subblk].unpk_pos); + } + } + } else + // Data is 8-bit packed + { + MMCMPBITBUFFER bb; + LPBYTE pDest = pBuffer + psubblk->unpk_pos; + DWORD dwSize = psubblk->unpk_size; + DWORD dwPos = 0; + UINT numbits = pblk->num_bits; + UINT subblk = 0, oldval = 0; + LPCBYTE ptable = lpMemFile+dwMemPos; + + bb.bitcount = 0; + bb.bitbuffer = 0; + bb.pSrc = lpMemFile+dwMemPos+pblk->tt_entries; + bb.pEnd = lpMemFile+dwMemPos+pblk->pk_size; + while (subblk < pblk->sub_blk) + { + UINT newval = 0x100; + DWORD d = bb.GetBits(numbits+1); + + if (d >= MMCMP8BitCommands[numbits]) + { + UINT nFetch = MMCMP8BitFetch[numbits]; + UINT newbits = bb.GetBits(nFetch) + ((d - MMCMP8BitCommands[numbits]) << nFetch); + if (newbits != numbits) + { + numbits = newbits & 0x07; + } else + { + if ((d = bb.GetBits(3)) == 7) + { + if (bb.GetBits(1)) break; + newval = 0xFF; + } else + { + newval = 0xF8 + d; + } + } + } else + { + newval = d; + } + if (newval < 0x100) + { + int n = ptable[newval]; + if (pblk->flags & MMCMP_DELTA) + { + n += oldval; + oldval = n; + } + pDest[dwPos++] = (BYTE)n; + } + if (dwPos >= dwSize) + { + subblk++; + dwPos = 0; + dwSize = psubblk[subblk].unpk_size; + pDest = pBuffer + psubblk[subblk].unpk_pos; + } + } + } + } + *ppMemFile = pBuffer; + *pdwMemLength = dwFileSize; + return TRUE; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// PowerPack PP20 Unpacker +// + +typedef struct _PPBITBUFFER +{ + UINT bitcount; + ULONG bitbuffer; + LPCBYTE pStart; + LPCBYTE pSrc; + + ULONG GetBits(UINT n); +} PPBITBUFFER; + + +ULONG PPBITBUFFER::GetBits(UINT n) +{ + ULONG result = 0; + + for (UINT i=0; i<n; i++) + { + if (!bitcount) + { + bitcount = 8; + if (pSrc != pStart) pSrc--; + bitbuffer = *pSrc; + } + result = (result<<1) | (bitbuffer&1); + bitbuffer >>= 1; + bitcount--; + } + return result; +} + + +VOID PP20_DoUnpack(const BYTE *pSrc, UINT nSrcLen, BYTE *pDst, UINT nDstLen) +{ + PPBITBUFFER BitBuffer; + ULONG nBytesLeft; + + BitBuffer.pStart = pSrc; + BitBuffer.pSrc = pSrc + nSrcLen - 4; + BitBuffer.bitbuffer = 0; + BitBuffer.bitcount = 0; + BitBuffer.GetBits(pSrc[nSrcLen-1]); + nBytesLeft = nDstLen; + while (nBytesLeft > 0) + { + if (!BitBuffer.GetBits(1)) + { + UINT n = 1; + while (n < nBytesLeft) + { + UINT code = BitBuffer.GetBits(2); + n += code; + if (code != 3) break; + } + for (UINT i=0; i<n; i++) + { + pDst[--nBytesLeft] = (BYTE)BitBuffer.GetBits(8); + } + if (!nBytesLeft) break; + } + { + UINT n = BitBuffer.GetBits(2)+1; + UINT nbits = pSrc[n-1]; + UINT nofs; + if (n==4) + { + nofs = BitBuffer.GetBits( (BitBuffer.GetBits(1)) ? nbits : 7 ); + while (n < nBytesLeft) + { + UINT code = BitBuffer.GetBits(3); + n += code; + if (code != 7) break; + } + } else + { + nofs = BitBuffer.GetBits(nbits); + } + for (UINT i=0; i<=n; i++) + { + pDst[nBytesLeft-1] = (nBytesLeft+nofs < nDstLen) ? pDst[nBytesLeft+nofs] : 0; + if (!--nBytesLeft) break; + } + } + } +} + + +BOOL PP20_Unpack(LPCBYTE *ppMemFile, LPDWORD pdwMemLength) +{ + DWORD dwMemLength = *pdwMemLength; + LPCBYTE lpMemFile = *ppMemFile; + DWORD dwDstLen; + LPBYTE pBuffer; + + if ((!lpMemFile) || (dwMemLength < 256) || (*(DWORD *)lpMemFile != 0x30325050)) return FALSE; + dwDstLen = (lpMemFile[dwMemLength-4]<<16) | (lpMemFile[dwMemLength-3]<<8) | (lpMemFile[dwMemLength-2]); + //Log("PP20 detected: Packed length=%d, Unpacked length=%d\n", dwMemLength, dwDstLen); + if ((dwDstLen < 512) || (dwDstLen > 0x400000) || (dwDstLen > 16*dwMemLength)) return FALSE; + if ((pBuffer = (LPBYTE)GlobalAllocPtr(GHND, (dwDstLen + 31) & ~15)) == NULL) return FALSE; + PP20_DoUnpack(lpMemFile+4, dwMemLength-4, pBuffer, dwDstLen); + *ppMemFile = pBuffer; + *pdwMemLength = dwDstLen; + return TRUE; +} + + + + + diff --git a/lib/libmodplug/src/modplug.cpp b/lib/libmodplug/src/modplug.cpp new file mode 100644 index 0000000000..b53de00b41 --- /dev/null +++ b/lib/libmodplug/src/modplug.cpp @@ -0,0 +1,271 @@ +/* + * This source code is public domain. + * + * Authors: Kenton Varda <temporal@gauge3d.org> (C interface wrapper) + */ + +#include "modplug.h" +#include "stdafx.h" +#include "sndfile.h" + +struct _ModPlugFile +{ + CSoundFile mSoundFile; +}; + +namespace ModPlug +{ + ModPlug_Settings gSettings = + { + MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION, + + 2, + 16, + 44100, + MODPLUG_RESAMPLE_LINEAR, + + 0, + 0, + 0, + 0, + 0, + 0, + 0 + }; + + int gSampleSize; + + void UpdateSettings(bool updateBasicConfig) + { + if(gSettings.mFlags & MODPLUG_ENABLE_REVERB) + { + CSoundFile::SetReverbParameters(gSettings.mReverbDepth, + gSettings.mReverbDelay); + } + + if(gSettings.mFlags & MODPLUG_ENABLE_MEGABASS) + { + CSoundFile::SetXBassParameters(gSettings.mBassAmount, + gSettings.mBassRange); + } + else // modplug seems to ignore the SetWaveConfigEx() setting for bass boost + CSoundFile::SetXBassParameters(0, 0); + + if(gSettings.mFlags & MODPLUG_ENABLE_SURROUND) + { + CSoundFile::SetSurroundParameters(gSettings.mSurroundDepth, + gSettings.mSurroundDelay); + } + + if(updateBasicConfig) + { + CSoundFile::SetWaveConfig(gSettings.mFrequency, + gSettings.mBits, + gSettings.mChannels); + + gSampleSize = gSettings.mBits / 8 * gSettings.mChannels; + } + + CSoundFile::SetWaveConfigEx(gSettings.mFlags & MODPLUG_ENABLE_SURROUND, + !(gSettings.mFlags & MODPLUG_ENABLE_OVERSAMPLING), + gSettings.mFlags & MODPLUG_ENABLE_REVERB, + true, + gSettings.mFlags & MODPLUG_ENABLE_MEGABASS, + gSettings.mFlags & MODPLUG_ENABLE_NOISE_REDUCTION, + false); + CSoundFile::SetResamplingMode(gSettings.mResamplingMode); + } +} + +ModPlugFile* ModPlug_Load(const void* data, int size) +{ + ModPlugFile* result = new ModPlugFile; + ModPlug::UpdateSettings(true); + if(result->mSoundFile.Create((const BYTE*)data, size)) + { + result->mSoundFile.SetRepeatCount(ModPlug::gSettings.mLoopCount); + return result; + } + else + { + delete result; + return NULL; + } +} + +void ModPlug_Unload(ModPlugFile* file) +{ + file->mSoundFile.Destroy(); + delete file; +} + +int ModPlug_Read(ModPlugFile* file, void* buffer, int size) +{ + return file->mSoundFile.Read(buffer, size) * ModPlug::gSampleSize; +} + +const char* ModPlug_GetName(ModPlugFile* file) +{ + return file->mSoundFile.GetTitle(); +} + +int ModPlug_GetLength(ModPlugFile* file) +{ + return file->mSoundFile.GetSongTime() * 1000; +} + +void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) +{ + file->mSoundFile.gpSndMixHook = (LPSNDMIXHOOKPROC)proc ; + return; +} + +void ModPlug_UnloadMixerCallback(ModPlugFile* file) +{ + file->mSoundFile.gpSndMixHook = NULL; + return ; +} + +unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) +{ + return (unsigned int)file->mSoundFile.m_nMasterVolume; +} + +void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) +{ + (void)file->mSoundFile.SetMasterVolume( (UINT)cvol, + FALSE ); + return ; +} + +int ModPlug_GetCurrentSpeed(ModPlugFile* file) +{ + return file->mSoundFile.m_nMusicSpeed; +} + +int ModPlug_GetCurrentTempo(ModPlugFile* file) +{ + return file->mSoundFile.m_nMusicTempo; +} + +int ModPlug_GetCurrentOrder(ModPlugFile* file) +{ + return file->mSoundFile.GetCurrentOrder(); +} + +int ModPlug_GetCurrentPattern(ModPlugFile* file) +{ + return file->mSoundFile.GetCurrentPattern(); +} + +int ModPlug_GetCurrentRow(ModPlugFile* file) +{ + return file->mSoundFile.m_nRow; +} + +int ModPlug_GetPlayingChannels(ModPlugFile* file) +{ + return ( file->mSoundFile.m_nMixChannels < file->mSoundFile.m_nMaxMixChannels ? file->mSoundFile.m_nMixChannels : file->mSoundFile.m_nMaxMixChannels ); +} + +void ModPlug_SeekOrder(ModPlugFile* file,int order) +{ + file->mSoundFile.SetCurrentOrder(order); +} + +int ModPlug_GetModuleType(ModPlugFile* file) +{ + return file->mSoundFile.m_nType; +} + +char* ModPlug_GetMessage(ModPlugFile* file) +{ + return file->mSoundFile.m_lpszSongComments; +} + +#ifndef MODPLUG_NO_FILESAVE +char ModPlug_ExportS3M(ModPlugFile* file,const char* filepath) +{ + return (char)file->mSoundFile.SaveS3M(filepath,0); +} + +char ModPlug_ExportXM(ModPlugFile* file,const char* filepath) +{ + return (char)file->mSoundFile.SaveXM(filepath,0); +} + +char ModPlug_ExportMOD(ModPlugFile* file,const char* filepath) +{ + return (char)file->mSoundFile.SaveMod(filepath,0); +} + +char ModPlug_ExportIT(ModPlugFile* file,const char* filepath) +{ + return (char)file->mSoundFile.SaveIT(filepath,0); +} +#endif // MODPLUG_NO_FILESAVE + +unsigned int ModPlug_NumInstruments(ModPlugFile* file) +{ + return file->mSoundFile.m_nInstruments; +} + +unsigned int ModPlug_NumSamples(ModPlugFile* file) +{ + return file->mSoundFile.m_nSamples; +} + +unsigned int ModPlug_NumPatterns(ModPlugFile* file) +{ + return file->mSoundFile.GetNumPatterns(); +} + +unsigned int ModPlug_NumChannels(ModPlugFile* file) +{ + return file->mSoundFile.GetNumChannels(); +} + +unsigned int ModPlug_SampleName(ModPlugFile* file,unsigned int qual,char* buff) +{ + return file->mSoundFile.GetSampleName(qual,buff); +} + +unsigned int ModPlug_InstrumentName(ModPlugFile* file,unsigned int qual,char* buff) +{ + return file->mSoundFile.GetInstrumentName(qual,buff); +} + +ModPlugNote* ModPlug_GetPattern(ModPlugFile* file,int pattern,unsigned int* numrows) { + if ( pattern<MAX_PATTERNS ) { + if (file->mSoundFile.Patterns[pattern]) { + if (numrows) *numrows=(unsigned int)file->mSoundFile.PatternSize[pattern]; + return (ModPlugNote*)file->mSoundFile.Patterns[pattern]; + } + } + return NULL; +} + +void ModPlug_Seek(ModPlugFile* file, int millisecond) +{ + int maxpos; + int maxtime = file->mSoundFile.GetSongTime() * 1000; + float postime; + + if(millisecond > maxtime) + millisecond = maxtime; + maxpos = file->mSoundFile.GetMaxPosition(); + postime = (float)maxpos / (float)maxtime; + + file->mSoundFile.SetCurrentPos((int)(millisecond * postime)); +} + +void ModPlug_GetSettings(ModPlug_Settings* settings) +{ + memcpy(settings, &ModPlug::gSettings, sizeof(ModPlug_Settings)); +} + +void ModPlug_SetSettings(const ModPlug_Settings* settings) +{ + memcpy(&ModPlug::gSettings, settings, sizeof(ModPlug_Settings)); + ModPlug::UpdateSettings(false); // do not update basic config. +} diff --git a/lib/libmodplug/src/modplug.h b/lib/libmodplug/src/modplug.h new file mode 100644 index 0000000000..8e02ad8a86 --- /dev/null +++ b/lib/libmodplug/src/modplug.h @@ -0,0 +1,168 @@ +/* + * This source code is public domain. + * + * Authors: Kenton Varda <temporal@gauge3d.org> (C interface wrapper) + */ + +#ifndef MODPLUG_H__INCLUDED +#define MODPLUG_H__INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +struct _ModPlugFile; +typedef struct _ModPlugFile ModPlugFile; + +struct _ModPlugNote { + unsigned char Note; + unsigned char Instrument; + unsigned char VolumeEffect; + unsigned char Effect; + unsigned char Volume; + unsigned char Parameter; +}; +typedef struct _ModPlugNote ModPlugNote; + +typedef void (*ModPlugMixerProc)(int*, unsigned long, unsigned long); + +/* Load a mod file. [data] should point to a block of memory containing the complete + * file, and [size] should be the size of that block. + * Return the loaded mod file on success, or NULL on failure. */ +ModPlugFile* ModPlug_Load(const void* data, int size); +/* Unload a mod file. */ +void ModPlug_Unload(ModPlugFile* file); + +/* Read sample data into the buffer. Returns the number of bytes read. If the end + * of the mod has been reached, zero is returned. */ +int ModPlug_Read(ModPlugFile* file, void* buffer, int size); + +/* Get the name of the mod. The returned buffer is stored within the ModPlugFile + * structure and will remain valid until you unload the file. */ +const char* ModPlug_GetName(ModPlugFile* file); + +/* Get the length of the mod, in milliseconds. Note that this result is not always + * accurate, especially in the case of mods with loops. */ +int ModPlug_GetLength(ModPlugFile* file); + +/* Seek to a particular position in the song. Note that seeking and MODs don't mix very + * well. Some mods will be missing instruments for a short time after a seek, as ModPlug + * does not scan the sequence backwards to find out which instruments were supposed to be + * playing at that time. (Doing so would be difficult and not very reliable.) Also, + * note that seeking is not very exact in some mods -- especially those for which + * ModPlug_GetLength() does not report the full length. */ +void ModPlug_Seek(ModPlugFile* file, int millisecond); + +enum _ModPlug_Flags +{ + MODPLUG_ENABLE_OVERSAMPLING = 1 << 0, /* Enable oversampling (*highly* recommended) */ + MODPLUG_ENABLE_NOISE_REDUCTION = 1 << 1, /* Enable noise reduction */ + MODPLUG_ENABLE_REVERB = 1 << 2, /* Enable reverb */ + MODPLUG_ENABLE_MEGABASS = 1 << 3, /* Enable megabass */ + MODPLUG_ENABLE_SURROUND = 1 << 4 /* Enable surround sound. */ +}; + +enum _ModPlug_ResamplingMode +{ + MODPLUG_RESAMPLE_NEAREST = 0, /* No interpolation (very fast, extremely bad sound quality) */ + MODPLUG_RESAMPLE_LINEAR = 1, /* Linear interpolation (fast, good quality) */ + MODPLUG_RESAMPLE_SPLINE = 2, /* Cubic spline interpolation (high quality) */ + MODPLUG_RESAMPLE_FIR = 3 /* 8-tap fir filter (extremely high quality) */ +}; + +typedef struct _ModPlug_Settings +{ + int mFlags; /* One or more of the MODPLUG_ENABLE_* flags above, bitwise-OR'ed */ + + /* Note that ModPlug always decodes sound at 44100kHz, 32 bit, stereo and then + * down-mixes to the settings you choose. */ + int mChannels; /* Number of channels - 1 for mono or 2 for stereo */ + int mBits; /* Bits per sample - 8, 16, or 32 */ + int mFrequency; /* Sampling rate - 11025, 22050, or 44100 */ + int mResamplingMode; /* One of MODPLUG_RESAMPLE_*, above */ + + int mReverbDepth; /* Reverb level 0(quiet)-100(loud) */ + int mReverbDelay; /* Reverb delay in ms, usually 40-200ms */ + int mBassAmount; /* XBass level 0(quiet)-100(loud) */ + int mBassRange; /* XBass cutoff in Hz 10-100 */ + int mSurroundDepth; /* Surround level 0(quiet)-100(heavy) */ + int mSurroundDelay; /* Surround delay in ms, usually 5-40ms */ + int mLoopCount; /* Number of times to loop. Zero prevents looping. + -1 loops forever. */ +} ModPlug_Settings; + +/* Get and set the mod decoder settings. All options, except for channels, bits-per-sample, + * sampling rate, and loop count, will take effect immediately. Those options which don't + * take effect immediately will take effect the next time you load a mod. */ +void ModPlug_GetSettings(ModPlug_Settings* settings); +void ModPlug_SetSettings(const ModPlug_Settings* settings); + +/* New ModPlug API Functions */ +/* NOTE: Master Volume (1-512) */ +unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) ; +void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) ; + +int ModPlug_GetCurrentSpeed(ModPlugFile* file); +int ModPlug_GetCurrentTempo(ModPlugFile* file); +int ModPlug_GetCurrentOrder(ModPlugFile* file); +int ModPlug_GetCurrentPattern(ModPlugFile* file); +int ModPlug_GetCurrentRow(ModPlugFile* file); +int ModPlug_GetPlayingChannels(ModPlugFile* file); + +void ModPlug_SeekOrder(ModPlugFile* file,int order); +int ModPlug_GetModuleType(ModPlugFile* file); +char* ModPlug_GetMessage(ModPlugFile* file); + + +#ifndef MODPLUG_NO_FILESAVE +/* + * EXPERIMENTAL Export Functions + */ +/*Export to a Scream Tracker 3 S3M module. EXPERIMENTAL (only works on Little-Endian platforms)*/ +char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath); + +/*Export to a Extended Module (XM). EXPERIMENTAL (only works on Little-Endian platforms)*/ +char ModPlug_ExportXM(ModPlugFile* file, const char* filepath); + +/*Export to a Amiga MOD file. EXPERIMENTAL.*/ +char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath); + +/*Export to a Impulse Tracker IT file. Should work OK in Little-Endian & Big-Endian platforms :-) */ +char ModPlug_ExportIT(ModPlugFile* file, const char* filepath); +#endif // MODPLUG_NO_FILESAVE + +unsigned int ModPlug_NumInstruments(ModPlugFile* file); +unsigned int ModPlug_NumSamples(ModPlugFile* file); +unsigned int ModPlug_NumPatterns(ModPlugFile* file); +unsigned int ModPlug_NumChannels(ModPlugFile* file); +unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff); +unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff); + +/* + * Retrieve pattern note-data + */ +ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows); + +/* + * ================= + * Mixer callback + * ================= + * + * Use this callback if you want to 'modify' the mixed data of LibModPlug. + * + * void proc(int* buffer,unsigned long channels,unsigned long nsamples) ; + * + * 'buffer': A buffer of mixed samples + * 'channels': N. of channels in the buffer + * 'nsamples': N. of samples in the buffeer (without taking care of n.channels) + * + * (Samples are signed 32-bit integers) + */ +void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ; +void ModPlug_UnloadMixerCallback(ModPlugFile* file) ; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/lib/libmodplug/src/snd_dsp.cpp b/lib/libmodplug/src/snd_dsp.cpp new file mode 100644 index 0000000000..ab5a30584a --- /dev/null +++ b/lib/libmodplug/src/snd_dsp.cpp @@ -0,0 +1,485 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +#include "stdafx.h" +#include "sndfile.h" + +#ifdef MODPLUG_FASTSOUNDLIB +#define MODPLUG_NO_REVERB +#endif + + +// Delayed Surround Filters +#ifndef MODPLUG_FASTSOUNDLIB +#define nDolbyHiFltAttn 6 +#define nDolbyHiFltMask 3 +#define DOLBYATTNROUNDUP 31 +#else +#define nDolbyHiFltAttn 3 +#define nDolbyHiFltMask 3 +#define DOLBYATTNROUNDUP 3 +#endif + +// Bass Expansion +#define XBASS_DELAY 14 // 2.5 ms + +// Buffer Sizes +#define XBASSBUFFERSIZE 64 // 2 ms at 50KHz +#define FILTERBUFFERSIZE 64 // 1.25 ms +#define SURROUNDBUFFERSIZE ((MAX_SAMPLE_RATE * 50) / 1000) +#define REVERBBUFFERSIZE ((MAX_SAMPLE_RATE * 200) / 1000) +#define REVERBBUFFERSIZE2 ((REVERBBUFFERSIZE*13) / 17) +#define REVERBBUFFERSIZE3 ((REVERBBUFFERSIZE*7) / 13) +#define REVERBBUFFERSIZE4 ((REVERBBUFFERSIZE*7) / 19) + + +// DSP Effects: PUBLIC members +UINT CSoundFile::m_nXBassDepth = 6; +UINT CSoundFile::m_nXBassRange = XBASS_DELAY; +UINT CSoundFile::m_nReverbDepth = 1; +UINT CSoundFile::m_nReverbDelay = 100; +UINT CSoundFile::m_nProLogicDepth = 12; +UINT CSoundFile::m_nProLogicDelay = 20; + +//////////////////////////////////////////////////////////////////// +// DSP Effects internal state + +// Bass Expansion: low-pass filter +static LONG nXBassSum = 0; +static LONG nXBassBufferPos = 0; +static LONG nXBassDlyPos = 0; +static LONG nXBassMask = 0; + +// Noise Reduction: simple low-pass filter +static LONG nLeftNR = 0; +static LONG nRightNR = 0; + +// Surround Encoding: 1 delay line + low-pass filter + high-pass filter +static LONG nSurroundSize = 0; +static LONG nSurroundPos = 0; +static LONG nDolbyDepth = 0; +static LONG nDolbyLoDlyPos = 0; +static LONG nDolbyLoFltPos = 0; +static LONG nDolbyLoFltSum = 0; +static LONG nDolbyHiFltPos = 0; +static LONG nDolbyHiFltSum = 0; + +// Reverb: 4 delay lines + high-pass filter + low-pass filter +#ifndef MODPLUG_NO_REVERB +static LONG nReverbSize = 0; +static LONG nReverbBufferPos = 0; +static LONG nReverbSize2 = 0; +static LONG nReverbBufferPos2 = 0; +static LONG nReverbSize3 = 0; +static LONG nReverbBufferPos3 = 0; +static LONG nReverbSize4 = 0; +static LONG nReverbBufferPos4 = 0; +static LONG nReverbLoFltSum = 0; +static LONG nReverbLoFltPos = 0; +static LONG nReverbLoDlyPos = 0; +static LONG nFilterAttn = 0; +static LONG gRvbLowPass[8]; +static LONG gRvbLPPos = 0; +static LONG gRvbLPSum = 0; +static LONG ReverbLoFilterBuffer[XBASSBUFFERSIZE]; +static LONG ReverbLoFilterDelay[XBASSBUFFERSIZE]; +static LONG ReverbBuffer[REVERBBUFFERSIZE]; +static LONG ReverbBuffer2[REVERBBUFFERSIZE2]; +static LONG ReverbBuffer3[REVERBBUFFERSIZE3]; +static LONG ReverbBuffer4[REVERBBUFFERSIZE4]; +#endif +static LONG XBassBuffer[XBASSBUFFERSIZE]; +static LONG XBassDelay[XBASSBUFFERSIZE]; +static LONG DolbyLoFilterBuffer[XBASSBUFFERSIZE]; +static LONG DolbyLoFilterDelay[XBASSBUFFERSIZE]; +static LONG DolbyHiFilterBuffer[FILTERBUFFERSIZE]; +static LONG SurroundBuffer[SURROUNDBUFFERSIZE]; + +// Access the main temporary mix buffer directly: avoids an extra pointer +extern int MixSoundBuffer[MIXBUFFERSIZE*2]; +//cextern int MixReverbBuffer[MIXBUFFERSIZE*2]; +extern int MixReverbBuffer[MIXBUFFERSIZE*2]; + +static UINT GetMaskFromSize(UINT len) +//----------------------------------- +{ + UINT n = 2; + while (n <= len) n <<= 1; + return ((n >> 1) - 1); +} + + +void CSoundFile::InitializeDSP(BOOL bReset) +//----------------------------------------- +{ + if (!m_nReverbDelay) m_nReverbDelay = 100; + if (!m_nXBassRange) m_nXBassRange = XBASS_DELAY; + if (!m_nProLogicDelay) m_nProLogicDelay = 20; + if (m_nXBassDepth > 8) m_nXBassDepth = 8; + if (m_nXBassDepth < 2) m_nXBassDepth = 2; + if (bReset) + { + // Noise Reduction + nLeftNR = nRightNR = 0; + } + // Pro-Logic Surround + nSurroundPos = nSurroundSize = 0; + nDolbyLoFltPos = nDolbyLoFltSum = nDolbyLoDlyPos = 0; + nDolbyHiFltPos = nDolbyHiFltSum = 0; + if (gdwSoundSetup & SNDMIX_SURROUND) + { + memset(DolbyLoFilterBuffer, 0, sizeof(DolbyLoFilterBuffer)); + memset(DolbyHiFilterBuffer, 0, sizeof(DolbyHiFilterBuffer)); + memset(DolbyLoFilterDelay, 0, sizeof(DolbyLoFilterDelay)); + memset(SurroundBuffer, 0, sizeof(SurroundBuffer)); + nSurroundSize = (gdwMixingFreq * m_nProLogicDelay) / 1000; + if (nSurroundSize > SURROUNDBUFFERSIZE) nSurroundSize = SURROUNDBUFFERSIZE; + if (m_nProLogicDepth < 8) nDolbyDepth = (32 >> m_nProLogicDepth) + 32; + else nDolbyDepth = (m_nProLogicDepth < 16) ? (8 + (m_nProLogicDepth - 8) * 7) : 64; + nDolbyDepth >>= 2; + } + // Reverb Setup +#ifndef MODPLUG_NO_REVERB + if (gdwSoundSetup & SNDMIX_REVERB) + { + UINT nrs = (gdwMixingFreq * m_nReverbDelay) / 1000; + UINT nfa = m_nReverbDepth+1; + if (nrs > REVERBBUFFERSIZE) nrs = REVERBBUFFERSIZE; + if ((bReset) || (nrs != (UINT)nReverbSize) || (nfa != (UINT)nFilterAttn)) + { + nFilterAttn = nfa; + nReverbSize = nrs; + nReverbBufferPos = nReverbBufferPos2 = nReverbBufferPos3 = nReverbBufferPos4 = 0; + nReverbLoFltSum = nReverbLoFltPos = nReverbLoDlyPos = 0; + gRvbLPSum = gRvbLPPos = 0; + nReverbSize2 = (nReverbSize * 13) / 17; + if (nReverbSize2 > REVERBBUFFERSIZE2) nReverbSize2 = REVERBBUFFERSIZE2; + nReverbSize3 = (nReverbSize * 7) / 13; + if (nReverbSize3 > REVERBBUFFERSIZE3) nReverbSize3 = REVERBBUFFERSIZE3; + nReverbSize4 = (nReverbSize * 7) / 19; + if (nReverbSize4 > REVERBBUFFERSIZE4) nReverbSize4 = REVERBBUFFERSIZE4; + memset(ReverbLoFilterBuffer, 0, sizeof(ReverbLoFilterBuffer)); + memset(ReverbLoFilterDelay, 0, sizeof(ReverbLoFilterDelay)); + memset(ReverbBuffer, 0, sizeof(ReverbBuffer)); + memset(ReverbBuffer2, 0, sizeof(ReverbBuffer2)); + memset(ReverbBuffer3, 0, sizeof(ReverbBuffer3)); + memset(ReverbBuffer4, 0, sizeof(ReverbBuffer4)); + memset(gRvbLowPass, 0, sizeof(gRvbLowPass)); + } + } else nReverbSize = 0; +#endif + BOOL bResetBass = FALSE; + // Bass Expansion Reset + if (gdwSoundSetup & SNDMIX_MEGABASS) + { + UINT nXBassSamples = (gdwMixingFreq * m_nXBassRange) / 10000; + if (nXBassSamples > XBASSBUFFERSIZE) nXBassSamples = XBASSBUFFERSIZE; + UINT mask = GetMaskFromSize(nXBassSamples); + if ((bReset) || (mask != (UINT)nXBassMask)) + { + nXBassMask = mask; + bResetBass = TRUE; + } + } else + { + nXBassMask = 0; + bResetBass = TRUE; + } + if (bResetBass) + { + nXBassSum = nXBassBufferPos = nXBassDlyPos = 0; + memset(XBassBuffer, 0, sizeof(XBassBuffer)); + memset(XBassDelay, 0, sizeof(XBassDelay)); + } +} + + +void CSoundFile::ProcessStereoDSP(int count) +//------------------------------------------ +{ +#ifndef MODPLUG_NO_REVERB + // Reverb + if (gdwSoundSetup & SNDMIX_REVERB) + { + int *pr = MixSoundBuffer, *pin = MixReverbBuffer, rvbcount = count; + do + { + int echo = ReverbBuffer[nReverbBufferPos] + ReverbBuffer2[nReverbBufferPos2] + + ReverbBuffer3[nReverbBufferPos3] + ReverbBuffer4[nReverbBufferPos4]; // echo = reverb signal + // Delay line and remove Low Frequencies // v = original signal + int echodly = ReverbLoFilterDelay[nReverbLoDlyPos]; // echodly = delayed signal + ReverbLoFilterDelay[nReverbLoDlyPos] = echo >> 1; + nReverbLoDlyPos++; + nReverbLoDlyPos &= 0x1F; + int n = nReverbLoFltPos; + nReverbLoFltSum -= ReverbLoFilterBuffer[n]; + int tmp = echo / 128; + ReverbLoFilterBuffer[n] = tmp; + nReverbLoFltSum += tmp; + echodly -= nReverbLoFltSum; + nReverbLoFltPos = (n + 1) & 0x3F; + // Reverb + int v = (pin[0]+pin[1]) >> nFilterAttn; + pr[0] += pin[0] + echodly; + pr[1] += pin[1] + echodly; + v += echodly >> 2; + ReverbBuffer3[nReverbBufferPos3] = v; + ReverbBuffer4[nReverbBufferPos4] = v; + v += echodly >> 4; + v >>= 1; + gRvbLPSum -= gRvbLowPass[gRvbLPPos]; + gRvbLPSum += v; + gRvbLowPass[gRvbLPPos] = v; + gRvbLPPos++; + gRvbLPPos &= 7; + int vlp = gRvbLPSum >> 2; + ReverbBuffer[nReverbBufferPos] = vlp; + ReverbBuffer2[nReverbBufferPos2] = vlp; + if (++nReverbBufferPos >= nReverbSize) nReverbBufferPos = 0; + if (++nReverbBufferPos2 >= nReverbSize2) nReverbBufferPos2 = 0; + if (++nReverbBufferPos3 >= nReverbSize3) nReverbBufferPos3 = 0; + if (++nReverbBufferPos4 >= nReverbSize4) nReverbBufferPos4 = 0; + pr += 2; + pin += 2; + } while (--rvbcount); + } +#endif + // Dolby Pro-Logic Surround + if (gdwSoundSetup & SNDMIX_SURROUND) + { + int *pr = MixSoundBuffer, n = nDolbyLoFltPos; + for (int r=count; r; r--) + { + int v = (pr[0]+pr[1]+DOLBYATTNROUNDUP) >> (nDolbyHiFltAttn+1); +#ifndef MODPLUG_FASTSOUNDLIB + v *= (int)nDolbyDepth; +#endif + // Low-Pass Filter + nDolbyHiFltSum -= DolbyHiFilterBuffer[nDolbyHiFltPos]; + DolbyHiFilterBuffer[nDolbyHiFltPos] = v; + nDolbyHiFltSum += v; + v = nDolbyHiFltSum; + nDolbyHiFltPos++; + nDolbyHiFltPos &= nDolbyHiFltMask; + // Surround + int secho = SurroundBuffer[nSurroundPos]; + SurroundBuffer[nSurroundPos] = v; + // Delay line and remove low frequencies + v = DolbyLoFilterDelay[nDolbyLoDlyPos]; // v = delayed signal + DolbyLoFilterDelay[nDolbyLoDlyPos] = secho; // secho = signal + nDolbyLoDlyPos++; + nDolbyLoDlyPos &= 0x1F; + nDolbyLoFltSum -= DolbyLoFilterBuffer[n]; + int tmp = secho / 64; + DolbyLoFilterBuffer[n] = tmp; + nDolbyLoFltSum += tmp; + v -= nDolbyLoFltSum; + n++; + n &= 0x3F; + // Add echo + pr[0] += v; + pr[1] -= v; + if (++nSurroundPos >= nSurroundSize) nSurroundPos = 0; + pr += 2; + } + nDolbyLoFltPos = n; + } + // Bass Expansion + if (gdwSoundSetup & SNDMIX_MEGABASS) + { + int *px = MixSoundBuffer; + int xba = m_nXBassDepth+1, xbamask = (1 << xba) - 1; + int n = nXBassBufferPos; + for (int x=count; x; x--) + { + nXBassSum -= XBassBuffer[n]; + int tmp0 = px[0] + px[1]; + int tmp = (tmp0 + ((tmp0 >> 31) & xbamask)) >> xba; + XBassBuffer[n] = tmp; + nXBassSum += tmp; + int v = XBassDelay[nXBassDlyPos]; + XBassDelay[nXBassDlyPos] = px[0]; + px[0] = v + nXBassSum; + v = XBassDelay[nXBassDlyPos+1]; + XBassDelay[nXBassDlyPos+1] = px[1]; + px[1] = v + nXBassSum; + nXBassDlyPos = (nXBassDlyPos + 2) & nXBassMask; + px += 2; + n++; + n &= nXBassMask; + } + nXBassBufferPos = n; + } + // Noise Reduction + if (gdwSoundSetup & SNDMIX_NOISEREDUCTION) + { + int n1 = nLeftNR, n2 = nRightNR; + int *pnr = MixSoundBuffer; + for (int nr=count; nr; nr--) + { + int vnr = pnr[0] >> 1; + pnr[0] = vnr + n1; + n1 = vnr; + vnr = pnr[1] >> 1; + pnr[1] = vnr + n2; + n2 = vnr; + pnr += 2; + } + nLeftNR = n1; + nRightNR = n2; + } +} + + +void CSoundFile::ProcessMonoDSP(int count) +//---------------------------------------- +{ +#ifndef MODPLUG_NO_REVERB + // Reverb + if (gdwSoundSetup & SNDMIX_REVERB) + { + int *pr = MixSoundBuffer, rvbcount = count, *pin = MixReverbBuffer; + do + { + int echo = ReverbBuffer[nReverbBufferPos] + ReverbBuffer2[nReverbBufferPos2] + + ReverbBuffer3[nReverbBufferPos3] + ReverbBuffer4[nReverbBufferPos4]; // echo = reverb signal + // Delay line and remove Low Frequencies // v = original signal + int echodly = ReverbLoFilterDelay[nReverbLoDlyPos]; // echodly = delayed signal + ReverbLoFilterDelay[nReverbLoDlyPos] = echo >> 1; + nReverbLoDlyPos++; + nReverbLoDlyPos &= 0x1F; + int n = nReverbLoFltPos; + nReverbLoFltSum -= ReverbLoFilterBuffer[n]; + int tmp = echo / 128; + ReverbLoFilterBuffer[n] = tmp; + nReverbLoFltSum += tmp; + echodly -= nReverbLoFltSum; + nReverbLoFltPos = (n + 1) & 0x3F; + // Reverb + int v = pin[0] >> (nFilterAttn-1); + *pr++ += pin[0] + echodly; + pin++; + v += echodly >> 2; + ReverbBuffer3[nReverbBufferPos3] = v; + ReverbBuffer4[nReverbBufferPos4] = v; + v += echodly >> 4; + v >>= 1; + gRvbLPSum -= gRvbLowPass[gRvbLPPos]; + gRvbLPSum += v; + gRvbLowPass[gRvbLPPos] = v; + gRvbLPPos++; + gRvbLPPos &= 7; + int vlp = gRvbLPSum >> 2; + ReverbBuffer[nReverbBufferPos] = vlp; + ReverbBuffer2[nReverbBufferPos2] = vlp; + if (++nReverbBufferPos >= nReverbSize) nReverbBufferPos = 0; + if (++nReverbBufferPos2 >= nReverbSize2) nReverbBufferPos2 = 0; + if (++nReverbBufferPos3 >= nReverbSize3) nReverbBufferPos3 = 0; + if (++nReverbBufferPos4 >= nReverbSize4) nReverbBufferPos4 = 0; + } while (--rvbcount); + } +#endif + // Bass Expansion + if (gdwSoundSetup & SNDMIX_MEGABASS) + { + int *px = MixSoundBuffer; + int xba = m_nXBassDepth, xbamask = (1 << xba)-1; + int n = nXBassBufferPos; + for (int x=count; x; x--) + { + nXBassSum -= XBassBuffer[n]; + int tmp0 = *px; + int tmp = (tmp0 + ((tmp0 >> 31) & xbamask)) >> xba; + XBassBuffer[n] = tmp; + nXBassSum += tmp; + int v = XBassDelay[nXBassDlyPos]; + XBassDelay[nXBassDlyPos] = *px; + *px++ = v + nXBassSum; + nXBassDlyPos = (nXBassDlyPos + 2) & nXBassMask; + n++; + n &= nXBassMask; + } + nXBassBufferPos = n; + } + // Noise Reduction + if (gdwSoundSetup & SNDMIX_NOISEREDUCTION) + { + int n = nLeftNR; + int *pnr = MixSoundBuffer; + for (int nr=count; nr; pnr++, nr--) + { + int vnr = *pnr >> 1; + *pnr = vnr + n; + n = vnr; + } + nLeftNR = n; + } +} + + +///////////////////////////////////////////////////////////////// +// Clean DSP Effects interface + +// [Reverb level 0(quiet)-100(loud)], [delay in ms, usually 40-200ms] +BOOL CSoundFile::SetReverbParameters(UINT nDepth, UINT nDelay) +//------------------------------------------------------------ +{ + if (nDepth > 100) nDepth = 100; + UINT gain = nDepth / 20; + if (gain > 4) gain = 4; + m_nReverbDepth = 4 - gain; + if (nDelay < 40) nDelay = 40; + if (nDelay > 250) nDelay = 250; + m_nReverbDelay = nDelay; + return TRUE; +} + + +// [XBass level 0(quiet)-100(loud)], [cutoff in Hz 20-100] +BOOL CSoundFile::SetXBassParameters(UINT nDepth, UINT nRange) +//----------------------------------------------------------- +{ + if (nDepth > 100) nDepth = 100; + UINT gain = nDepth / 20; + if (gain > 4) gain = 4; + m_nXBassDepth = 8 - gain; // filter attenuation 1/256 .. 1/16 + UINT range = nRange / 5; + if (range > 5) range -= 5; else range = 0; + if (nRange > 16) nRange = 16; + m_nXBassRange = 21 - range; // filter average on 0.5-1.6ms + return TRUE; +} + + +// [Surround level 0(quiet)-100(heavy)] [delay in ms, usually 5-50ms] +BOOL CSoundFile::SetSurroundParameters(UINT nDepth, UINT nDelay) +//-------------------------------------------------------------- +{ + UINT gain = (nDepth * 16) / 100; + if (gain > 16) gain = 16; + if (gain < 1) gain = 1; + m_nProLogicDepth = gain; + if (nDelay < 4) nDelay = 4; + if (nDelay > 50) nDelay = 50; + m_nProLogicDelay = nDelay; + return TRUE; +} + +BOOL CSoundFile::SetWaveConfigEx(BOOL bSurround,BOOL bNoOverSampling,BOOL bReverb,BOOL hqido,BOOL bMegaBass,BOOL bNR,BOOL bEQ) +//---------------------------------------------------------------------------------------------------------------------------- +{ + DWORD d = gdwSoundSetup & ~(SNDMIX_SURROUND | SNDMIX_NORESAMPLING | SNDMIX_REVERB | SNDMIX_HQRESAMPLER | SNDMIX_MEGABASS | SNDMIX_NOISEREDUCTION | SNDMIX_EQ); + if (bSurround) d |= SNDMIX_SURROUND; + if (bNoOverSampling) d |= SNDMIX_NORESAMPLING; + if (bReverb) d |= SNDMIX_REVERB; + if (hqido) d |= SNDMIX_HQRESAMPLER; + if (bMegaBass) d |= SNDMIX_MEGABASS; + if (bNR) d |= SNDMIX_NOISEREDUCTION; + if (bEQ) d |= SNDMIX_EQ; + gdwSoundSetup = d; + InitPlayer(FALSE); + return TRUE; +} diff --git a/lib/libmodplug/src/snd_flt.cpp b/lib/libmodplug/src/snd_flt.cpp new file mode 100644 index 0000000000..0865e59770 --- /dev/null +++ b/lib/libmodplug/src/snd_flt.cpp @@ -0,0 +1,101 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +#include "stdafx.h" +#include "sndfile.h" + +// AWE32: cutoff = reg[0-255] * 31.25 + 100 -> [100Hz-8060Hz] +// EMU10K1 docs: cutoff = reg[0-127]*62+100 +#define FILTER_PRECISION 8192 + +#ifndef NO_FILTER + +#ifdef MSC_VER +#define _ASM_MATH +#endif + +#ifdef _ASM_MATH + +// pow(a,b) returns a^^b -> 2^^(b.log2(a)) +static float pow(float a, float b) +{ + long tmpint; + float result; + _asm { + fld b // Load b + fld a // Load a + fyl2x // ST(0) = b.log2(a) + fist tmpint // Store integer exponent + fisub tmpint // ST(0) = -1 <= (b*log2(a)) <= 1 + f2xm1 // ST(0) = 2^(x)-1 + fild tmpint // load integer exponent + fld1 // Load 1 + fscale // ST(0) = 2^ST(1) + fstp ST(1) // Remove the integer from the stack + fmul ST(1), ST(0) // multiply with fractional part + faddp ST(1), ST(0) // add integer_part + fstp result // Store the result + } + return result; +} + + +#else + +#include <math.h> + +#endif // _ASM_MATH + + +DWORD CSoundFile::CutOffToFrequency(UINT nCutOff, int flt_modifier) const +//----------------------------------------------------------------------- +{ + float Fc; + + if (m_dwSongFlags & SONG_EXFILTERRANGE) + Fc = 110.0f * pow(2.0f, 0.25f + ((float)(nCutOff*(flt_modifier+256)))/(21.0f*512.0f)); + else + Fc = 110.0f * pow(2.0f, 0.25f + ((float)(nCutOff*(flt_modifier+256)))/(24.0f*512.0f)); + LONG freq = (LONG)Fc; + if (freq < 120) return 120; + if (freq > 10000) return 10000; + if (freq*2 > (LONG)gdwMixingFreq) freq = gdwMixingFreq>>1; + return (DWORD)freq; +} + + +// Simple 2-poles resonant filter +void CSoundFile::SetupChannelFilter(MODCHANNEL *pChn, BOOL bReset, int flt_modifier) const +//---------------------------------------------------------------------------------------- +{ + float fc = (float)CutOffToFrequency(pChn->nCutOff, flt_modifier); + float fs = (float)gdwMixingFreq; + float fg, fb0, fb1; + + fc *= (float)(2.0*3.14159265358/fs); + float dmpfac = pow(10.0f, -((24.0f / 128.0f)*(float)pChn->nResonance) / 20.0f); + float d = (1.0f-2.0f*dmpfac)* fc; + if (d>2.0) d = 2.0; + d = (2.0f*dmpfac - d)/fc; + float e = pow(1.0f/fc,2.0); + + fg=1/(1+d+e); + fb0=(d+e+e)/(1+d+e); + fb1=-e/(1+d+e); + + pChn->nFilter_A0 = (int)(fg * FILTER_PRECISION); + pChn->nFilter_B0 = (int)(fb0 * FILTER_PRECISION); + pChn->nFilter_B1 = (int)(fb1 * FILTER_PRECISION); + + if (bReset) + { + pChn->nFilter_Y1 = pChn->nFilter_Y2 = 0; + pChn->nFilter_Y3 = pChn->nFilter_Y4 = 0; + } + pChn->dwFlags |= CHN_FILTER; +} + +#endif // NO_FILTER diff --git a/lib/libmodplug/src/snd_fx.cpp b/lib/libmodplug/src/snd_fx.cpp new file mode 100644 index 0000000000..a0287b2127 --- /dev/null +++ b/lib/libmodplug/src/snd_fx.cpp @@ -0,0 +1,2378 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +#include "stdafx.h" +#include "sndfile.h" +#include "tables.h" + +#ifdef MSC_VER +#pragma warning(disable:4244) +#endif + +//////////////////////////////////////////////////////////// +// Length + +DWORD CSoundFile::GetLength(BOOL bAdjust, BOOL bTotal) +//---------------------------------------------------- +{ + UINT dwElapsedTime=0, nRow=0, nCurrentPattern=0, nNextPattern=0, nPattern=Order[0]; + UINT nMusicSpeed=m_nDefaultSpeed, nMusicTempo=m_nDefaultTempo, nNextRow=0; + UINT nMaxRow = 0, nMaxPattern = 0; + LONG nGlbVol = m_nDefaultGlobalVolume, nOldGlbVolSlide = 0; + BYTE samples[MAX_CHANNELS]; + BYTE instr[MAX_CHANNELS]; + BYTE notes[MAX_CHANNELS]; + BYTE vols[MAX_CHANNELS]; + BYTE oldparam[MAX_CHANNELS]; + BYTE chnvols[MAX_CHANNELS]; + DWORD patloop[MAX_CHANNELS]; + + memset(instr, 0, sizeof(instr)); + memset(notes, 0, sizeof(notes)); + memset(vols, 0xFF, sizeof(vols)); + memset(patloop, 0, sizeof(patloop)); + memset(oldparam, 0, sizeof(oldparam)); + memset(chnvols, 64, sizeof(chnvols)); + memset(samples, 0, sizeof(samples)); + for (UINT icv=0; icv<m_nChannels; icv++) chnvols[icv] = ChnSettings[icv].nVolume; + nMaxRow = m_nNextRow; + nMaxPattern = m_nNextPattern; + nCurrentPattern = nNextPattern = 0; + nPattern = Order[0]; + nRow = nNextRow = 0; + for (;;) + { + UINT nSpeedCount = 0; + nRow = nNextRow; + nCurrentPattern = nNextPattern; + // Check if pattern is valid + nPattern = Order[nCurrentPattern]; + while (nPattern >= MAX_PATTERNS) + { + // End of song ? + if ((nPattern == 0xFF) || (nCurrentPattern >= MAX_ORDERS)) + { + goto EndMod; + } else + { + nCurrentPattern++; + nPattern = (nCurrentPattern < MAX_ORDERS) ? Order[nCurrentPattern] : 0xFF; + } + nNextPattern = nCurrentPattern; + } + // Weird stuff? + if ((nPattern >= MAX_PATTERNS) || (!Patterns[nPattern])) break; + // Should never happen + if (nRow >= PatternSize[nPattern]) nRow = 0; + // Update next position + nNextRow = nRow + 1; + if (nNextRow >= PatternSize[nPattern]) + { + nNextPattern = nCurrentPattern + 1; + nNextRow = 0; + } + if (!nRow) + { + for (UINT ipck=0; ipck<m_nChannels; ipck++) patloop[ipck] = dwElapsedTime; + } + if (!bTotal) + { + if ((nCurrentPattern > nMaxPattern) || ((nCurrentPattern == nMaxPattern) && (nRow >= nMaxRow))) + { + if (bAdjust) + { + m_nMusicSpeed = nMusicSpeed; + m_nMusicTempo = nMusicTempo; + } + break; + } + } + MODCHANNEL *pChn = Chn; + MODCOMMAND *p = Patterns[nPattern] + nRow * m_nChannels; + for (UINT nChn=0; nChn<m_nChannels; p++,pChn++, nChn++) if (*((DWORD *)p)) + { + UINT command = p->command; + UINT param = p->param; + UINT note = p->note; + if (p->instr) { instr[nChn] = p->instr; notes[nChn] = 0; vols[nChn] = 0xFF; } + if ((note) && (note <= NOTE_MAX)) notes[nChn] = note; + if (p->volcmd == VOLCMD_VOLUME) { vols[nChn] = p->vol; } + if (command) switch (command) + { + // Position Jump + case CMD_POSITIONJUMP: + if (param <= nCurrentPattern) goto EndMod; + nNextPattern = param; + nNextRow = 0; + if (bAdjust) + { + pChn->nPatternLoopCount = 0; + pChn->nPatternLoop = 0; + } + break; + // Pattern Break + case CMD_PATTERNBREAK: + nNextRow = param; + nNextPattern = nCurrentPattern + 1; + if (bAdjust) + { + pChn->nPatternLoopCount = 0; + pChn->nPatternLoop = 0; + } + break; + // Set Speed + case CMD_SPEED: + if (!param) break; + if ((param <= 0x20) || (m_nType != MOD_TYPE_MOD)) + { + if (param < 128) nMusicSpeed = param; + } + break; + // Set Tempo + case CMD_TEMPO: + if ((bAdjust) && (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT))) + { + if (param) pChn->nOldTempo = param; else param = pChn->nOldTempo; + } + if (param >= 0x20) nMusicTempo = param; else + // Tempo Slide + if ((param & 0xF0) == 0x10) + { + nMusicTempo += param & 0x0F; + if (nMusicTempo > 255) nMusicTempo = 255; + } else + { + nMusicTempo -= param & 0x0F; + if (nMusicTempo < 32) nMusicTempo = 32; + } + break; + // Pattern Delay + case CMD_S3MCMDEX: + if ((param & 0xF0) == 0x60) { nSpeedCount = param & 0x0F; break; } else + if ((param & 0xF0) == 0xB0) { param &= 0x0F; param |= 0x60; } + case CMD_MODCMDEX: + if ((param & 0xF0) == 0xE0) nSpeedCount = (param & 0x0F) * nMusicSpeed; else + if ((param & 0xF0) == 0x60) + { + if (param & 0x0F) dwElapsedTime += (dwElapsedTime - patloop[nChn]) * (param & 0x0F); + else patloop[nChn] = dwElapsedTime; + } + break; + } + if (!bAdjust) continue; + switch(command) + { + // Portamento Up/Down + case CMD_PORTAMENTOUP: + case CMD_PORTAMENTODOWN: + if (param) pChn->nOldPortaUpDown = param; + break; + // Tone-Portamento + case CMD_TONEPORTAMENTO: + if (param) pChn->nPortamentoSlide = param << 2; + break; + // Offset + case CMD_OFFSET: + if (param) pChn->nOldOffset = param; + break; + // Volume Slide + case CMD_VOLUMESLIDE: + case CMD_TONEPORTAVOL: + case CMD_VIBRATOVOL: + if (param) pChn->nOldVolumeSlide = param; + break; + // Set Volume + case CMD_VOLUME: + vols[nChn] = param; + break; + // Global Volume + case CMD_GLOBALVOLUME: + if (!(m_nType & (MOD_TYPE_IT))) param <<= 1; + if (param > 128) param = 128; + nGlbVol = param << 1; + break; + // Global Volume Slide + case CMD_GLOBALVOLSLIDE: + if (param) nOldGlbVolSlide = param; else param = nOldGlbVolSlide; + if (((param & 0x0F) == 0x0F) && (param & 0xF0)) + { + param >>= 4; + if (m_nType != MOD_TYPE_IT) param <<= 1; + nGlbVol += param << 1; + } else + if (((param & 0xF0) == 0xF0) && (param & 0x0F)) + { + param = (param & 0x0F) << 1; + if (m_nType != MOD_TYPE_IT) param <<= 1; + nGlbVol -= param; + } else + if (param & 0xF0) + { + param >>= 4; + param <<= 1; + if (m_nType != MOD_TYPE_IT) param <<= 1; + nGlbVol += param * nMusicSpeed; + } else + { + param = (param & 0x0F) << 1; + if (m_nType != MOD_TYPE_IT) param <<= 1; + nGlbVol -= param * nMusicSpeed; + } + if (nGlbVol < 0) nGlbVol = 0; + if (nGlbVol > 256) nGlbVol = 256; + break; + case CMD_CHANNELVOLUME: + if (param <= 64) chnvols[nChn] = param; + break; + case CMD_CHANNELVOLSLIDE: + if (param) oldparam[nChn] = param; else param = oldparam[nChn]; + pChn->nOldChnVolSlide = param; + if (((param & 0x0F) == 0x0F) && (param & 0xF0)) + { + param = (param >> 4) + chnvols[nChn]; + } else + if (((param & 0xF0) == 0xF0) && (param & 0x0F)) + { + if (chnvols[nChn] > (int)(param & 0x0F)) param = chnvols[nChn] - (param & 0x0F); + else param = 0; + } else + if (param & 0x0F) + { + param = (param & 0x0F) * nMusicSpeed; + param = (chnvols[nChn] > param) ? chnvols[nChn] - param : 0; + } else param = ((param & 0xF0) >> 4) * nMusicSpeed + chnvols[nChn]; + if (param > 64) param = 64; + chnvols[nChn] = param; + break; + } + } + nSpeedCount += nMusicSpeed; + dwElapsedTime += (2500 * nSpeedCount) / nMusicTempo; + } +EndMod: + if ((bAdjust) && (!bTotal)) + { + m_nGlobalVolume = nGlbVol; + m_nOldGlbVolSlide = nOldGlbVolSlide; + for (UINT n=0; n<m_nChannels; n++) + { + Chn[n].nGlobalVol = chnvols[n]; + if (notes[n]) Chn[n].nNewNote = notes[n]; + if (instr[n]) Chn[n].nNewIns = instr[n]; + if (vols[n] != 0xFF) + { + if (vols[n] > 64) vols[n] = 64; + Chn[n].nVolume = vols[n] << 2; + } + } + } + return (dwElapsedTime+500) / 1000; +} + + +////////////////////////////////////////////////////////////////////////////////////////////////// +// Effects + +void CSoundFile::InstrumentChange(MODCHANNEL *pChn, UINT instr, BOOL bPorta, BOOL bUpdVol, BOOL bResetEnv) +//-------------------------------------------------------------------------------------------------------- +{ + BOOL bInstrumentChanged = FALSE; + + if (instr >= MAX_INSTRUMENTS) return; + INSTRUMENTHEADER *penv = Headers[instr]; + MODINSTRUMENT *psmp = &Ins[instr]; + UINT note = pChn->nNewNote; + if ((penv) && (note) && (note <= 128)) + { + if (penv->NoteMap[note-1] >= 0xFE) return; + UINT n = penv->Keyboard[note-1]; + psmp = ((n) && (n < MAX_SAMPLES)) ? &Ins[n] : NULL; + } else + if (m_nInstruments) + { + if (note >= 0xFE) return; + psmp = NULL; + } + // Update Volume + if (bUpdVol) pChn->nVolume = (psmp) ? psmp->nVolume : 0; + // bInstrumentChanged is used for IT carry-on env option + if (penv != pChn->pHeader) + { + bInstrumentChanged = TRUE; + pChn->pHeader = penv; + } else + { + // Special XM hack + if ((bPorta) && (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) && (penv) + && (pChn->pInstrument) && (psmp != pChn->pInstrument)) + { + // FT2 doesn't change the sample in this case, + // but still uses the sample info from the old one (bug?) + return; + } + } + // Instrument adjust + pChn->nNewIns = 0; + if (psmp) + { + if (penv) + { + pChn->nInsVol = (psmp->nGlobalVol * penv->nGlobalVol) >> 6; + if (penv->dwFlags & ENV_SETPANNING) pChn->nPan = penv->nPan; + pChn->nNNA = penv->nNNA; + } else + { + pChn->nInsVol = psmp->nGlobalVol; + } + if (psmp->uFlags & CHN_PANNING) pChn->nPan = psmp->nPan; + } + // Reset envelopes + if (bResetEnv) + { + if ((!bPorta) || (!(m_nType & MOD_TYPE_IT)) || (m_dwSongFlags & SONG_ITCOMPATMODE) + || (!pChn->nLength) || ((pChn->dwFlags & CHN_NOTEFADE) && (!pChn->nFadeOutVol))) + { + pChn->dwFlags |= CHN_FASTVOLRAMP; + if ((m_nType & MOD_TYPE_IT) && (!bInstrumentChanged) && (penv) && (!(pChn->dwFlags & (CHN_KEYOFF|CHN_NOTEFADE)))) + { + if (!(penv->dwFlags & ENV_VOLCARRY)) pChn->nVolEnvPosition = 0; + if (!(penv->dwFlags & ENV_PANCARRY)) pChn->nPanEnvPosition = 0; + if (!(penv->dwFlags & ENV_PITCHCARRY)) pChn->nPitchEnvPosition = 0; + } else + { + pChn->nVolEnvPosition = 0; + pChn->nPanEnvPosition = 0; + pChn->nPitchEnvPosition = 0; + } + pChn->nAutoVibDepth = 0; + pChn->nAutoVibPos = 0; + } else + if ((penv) && (!(penv->dwFlags & ENV_VOLUME))) + { + pChn->nVolEnvPosition = 0; + pChn->nAutoVibDepth = 0; + pChn->nAutoVibPos = 0; + } + } + // Invalid sample ? + if (!psmp) + { + pChn->pInstrument = NULL; + pChn->nInsVol = 0; + return; + } + // Tone-Portamento doesn't reset the pingpong direction flag + if ((bPorta) && (psmp == pChn->pInstrument)) + { + if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)) return; + pChn->dwFlags &= ~(CHN_KEYOFF|CHN_NOTEFADE); + pChn->dwFlags = (pChn->dwFlags & (0xFFFFFF00 | CHN_PINGPONGFLAG)) | (psmp->uFlags); + } else + { + pChn->dwFlags &= ~(CHN_KEYOFF|CHN_NOTEFADE|CHN_VOLENV|CHN_PANENV|CHN_PITCHENV); + pChn->dwFlags = (pChn->dwFlags & 0xFFFFFF00) | (psmp->uFlags); + if (penv) + { + if (penv->dwFlags & ENV_VOLUME) pChn->dwFlags |= CHN_VOLENV; + if (penv->dwFlags & ENV_PANNING) pChn->dwFlags |= CHN_PANENV; + if (penv->dwFlags & ENV_PITCH) pChn->dwFlags |= CHN_PITCHENV; + if ((penv->dwFlags & ENV_PITCH) && (penv->dwFlags & ENV_FILTER)) + { + if (!pChn->nCutOff) pChn->nCutOff = 0x7F; + } + if (penv->nIFC & 0x80) pChn->nCutOff = penv->nIFC & 0x7F; + if (penv->nIFR & 0x80) pChn->nResonance = penv->nIFR & 0x7F; + } + pChn->nVolSwing = pChn->nPanSwing = 0; + } + pChn->pInstrument = psmp; + pChn->nLength = psmp->nLength; + pChn->nLoopStart = psmp->nLoopStart; + pChn->nLoopEnd = psmp->nLoopEnd; + pChn->nC4Speed = psmp->nC4Speed; + pChn->pSample = psmp->pSample; + pChn->nTranspose = psmp->RelativeTone; + pChn->nFineTune = psmp->nFineTune; + if (pChn->dwFlags & CHN_SUSTAINLOOP) + { + pChn->nLoopStart = psmp->nSustainStart; + pChn->nLoopEnd = psmp->nSustainEnd; + pChn->dwFlags |= CHN_LOOP; + if (pChn->dwFlags & CHN_PINGPONGSUSTAIN) pChn->dwFlags |= CHN_PINGPONGLOOP; + } + if ((pChn->dwFlags & CHN_LOOP) && (pChn->nLoopEnd < pChn->nLength)) pChn->nLength = pChn->nLoopEnd; +} + + +void CSoundFile::NoteChange(UINT nChn, int note, BOOL bPorta, BOOL bResetEnv) +//--------------------------------------------------------------------------- +{ + if (note < 1) return; + MODCHANNEL * const pChn = &Chn[nChn]; + MODINSTRUMENT *pins = pChn->pInstrument; + INSTRUMENTHEADER *penv = pChn->pHeader; + if ((penv) && (note <= 0x80)) + { + UINT n = penv->Keyboard[note - 1]; + if ((n) && (n < MAX_SAMPLES)) pins = &Ins[n]; + note = penv->NoteMap[note-1]; + } + // Key Off + if (note >= 0x80) // 0xFE or invalid note => key off + { + // Key Off + KeyOff(nChn); + // Note Cut + if (note == 0xFE) + { + pChn->dwFlags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP); + if ((!(m_nType & MOD_TYPE_IT)) || (m_nInstruments)) pChn->nVolume = 0; + pChn->nFadeOutVol = 0; + } + return; + } + if (!pins) return; + if ((!bPorta) && (m_nType & (MOD_TYPE_XM|MOD_TYPE_MED|MOD_TYPE_MT2))) + { + pChn->nTranspose = pins->RelativeTone; + pChn->nFineTune = pins->nFineTune; + } + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2|MOD_TYPE_MED)) note += pChn->nTranspose; + if (note < 1) note = 1; + if (note > 132) note = 132; + pChn->nNote = note; + if ((!bPorta) || (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT))) pChn->nNewIns = 0; + UINT period = GetPeriodFromNote(note, pChn->nFineTune, pChn->nC4Speed); + if (period) + { + if ((!bPorta) || (!pChn->nPeriod)) pChn->nPeriod = period; + pChn->nPortamentoDest = period; + if ((!bPorta) || ((!pChn->nLength) && (!(m_nType & MOD_TYPE_S3M)))) + { + pChn->pInstrument = pins; + pChn->pSample = pins->pSample; + pChn->nLength = pins->nLength; + pChn->nLoopEnd = pins->nLength; + pChn->nLoopStart = 0; + pChn->dwFlags = (pChn->dwFlags & 0xFFFFFF00) | (pins->uFlags); + if (pChn->dwFlags & CHN_SUSTAINLOOP) + { + pChn->nLoopStart = pins->nSustainStart; + pChn->nLoopEnd = pins->nSustainEnd; + pChn->dwFlags &= ~CHN_PINGPONGLOOP; + pChn->dwFlags |= CHN_LOOP; + if (pChn->dwFlags & CHN_PINGPONGSUSTAIN) pChn->dwFlags |= CHN_PINGPONGLOOP; + if (pChn->nLength > pChn->nLoopEnd) pChn->nLength = pChn->nLoopEnd; + } else + if (pChn->dwFlags & CHN_LOOP) + { + pChn->nLoopStart = pins->nLoopStart; + pChn->nLoopEnd = pins->nLoopEnd; + if (pChn->nLength > pChn->nLoopEnd) pChn->nLength = pChn->nLoopEnd; + } + pChn->nPos = 0; + pChn->nPosLo = 0; + if (pChn->nVibratoType < 4) pChn->nVibratoPos = ((m_nType & MOD_TYPE_IT) && (!(m_dwSongFlags & SONG_ITOLDEFFECTS))) ? 0x10 : 0; + if (pChn->nTremoloType < 4) pChn->nTremoloPos = 0; + } + if (pChn->nPos >= pChn->nLength) pChn->nPos = pChn->nLoopStart; + } else bPorta = FALSE; + if ((!bPorta) || (!(m_nType & MOD_TYPE_IT)) + || ((pChn->dwFlags & CHN_NOTEFADE) && (!pChn->nFadeOutVol)) + || ((m_dwSongFlags & SONG_ITCOMPATMODE) && (pChn->nRowInstr))) + { + if ((m_nType & MOD_TYPE_IT) && (pChn->dwFlags & CHN_NOTEFADE) && (!pChn->nFadeOutVol)) + { + pChn->nVolEnvPosition = 0; + pChn->nPanEnvPosition = 0; + pChn->nPitchEnvPosition = 0; + pChn->nAutoVibDepth = 0; + pChn->nAutoVibPos = 0; + pChn->dwFlags &= ~CHN_NOTEFADE; + pChn->nFadeOutVol = 65536; + } + if ((!bPorta) || (!(m_dwSongFlags & SONG_ITCOMPATMODE)) || (pChn->nRowInstr)) + { + if ((!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) || (pChn->nRowInstr)) + { + pChn->dwFlags &= ~CHN_NOTEFADE; + pChn->nFadeOutVol = 65536; + } + } + } + pChn->dwFlags &= ~(CHN_EXTRALOUD|CHN_KEYOFF); + // Enable Ramping + if (!bPorta) + { + pChn->nVUMeter = 0x100; + pChn->nLeftVU = pChn->nRightVU = 0xFF; + pChn->dwFlags &= ~CHN_FILTER; + pChn->dwFlags |= CHN_FASTVOLRAMP; + pChn->nRetrigCount = 0; + pChn->nTremorCount = 0; + if (bResetEnv) + { + pChn->nVolSwing = pChn->nPanSwing = 0; + if (penv) + { + if (!(penv->dwFlags & ENV_VOLCARRY)) pChn->nVolEnvPosition = 0; + if (!(penv->dwFlags & ENV_PANCARRY)) pChn->nPanEnvPosition = 0; + if (!(penv->dwFlags & ENV_PITCHCARRY)) pChn->nPitchEnvPosition = 0; + if (m_nType & MOD_TYPE_IT) + { + // Volume Swing + if (penv->nVolSwing) + { + int d = ((LONG)penv->nVolSwing*(LONG)((rand() & 0xFF) - 0x7F)) / 128; + pChn->nVolSwing = (signed short)((d * pChn->nVolume + 1)/128); + } + // Pan Swing + if (penv->nPanSwing) + { + int d = ((LONG)penv->nPanSwing*(LONG)((rand() & 0xFF) - 0x7F)) / 128; + pChn->nPanSwing = (signed short)d; + } + } + } + pChn->nAutoVibDepth = 0; + pChn->nAutoVibPos = 0; + } + pChn->nLeftVol = pChn->nRightVol = 0; + BOOL bFlt = (m_dwSongFlags & SONG_MPTFILTERMODE) ? FALSE : TRUE; + // Setup Initial Filter for this note + if (penv) + { + if (penv->nIFR & 0x80) { pChn->nResonance = penv->nIFR & 0x7F; bFlt = TRUE; } + if (penv->nIFC & 0x80) { pChn->nCutOff = penv->nIFC & 0x7F; bFlt = TRUE; } + } else + { + pChn->nVolSwing = pChn->nPanSwing = 0; + } +#ifndef NO_FILTER + if ((pChn->nCutOff < 0x7F) && (bFlt)) SetupChannelFilter(pChn, TRUE); +#endif // NO_FILTER + } +} + + +UINT CSoundFile::GetNNAChannel(UINT nChn) const +//--------------------------------------------- +{ + const MODCHANNEL *pChn = &Chn[nChn]; + // Check for empty channel + const MODCHANNEL *pi = &Chn[m_nChannels]; + for (UINT i=m_nChannels; i<MAX_CHANNELS; i++, pi++) if (!pi->nLength) return i; + if (!pChn->nFadeOutVol) return 0; + // All channels are used: check for lowest volume + UINT result = 0; + DWORD vol = 64*65536; // 25% + DWORD envpos = 0xFFFFFF; + const MODCHANNEL *pj = &Chn[m_nChannels]; + for (UINT j=m_nChannels; j<MAX_CHANNELS; j++, pj++) + { + if (!pj->nFadeOutVol) return j; + DWORD v = pj->nVolume; + if (pj->dwFlags & CHN_NOTEFADE) + v = v * pj->nFadeOutVol; + else + v <<= 16; + if (pj->dwFlags & CHN_LOOP) v >>= 1; + if ((v < vol) || ((v == vol) && (pj->nVolEnvPosition > envpos))) + { + envpos = pj->nVolEnvPosition; + vol = v; + result = j; + } + } + return result; +} + + +void CSoundFile::CheckNNA(UINT nChn, UINT instr, int note, BOOL bForceCut) +//------------------------------------------------------------------------ +{ + MODCHANNEL *pChn = &Chn[nChn]; + INSTRUMENTHEADER *penv = pChn->pHeader, *pHeader = 0; + signed char *pSample; + if (note > 0x80) note = 0; + if (note < 1) return; + // Always NNA cut - using + if ((!(m_nType & (MOD_TYPE_IT|MOD_TYPE_MT2))) || (!m_nInstruments) || (bForceCut)) + { + if ((m_dwSongFlags & SONG_CPUVERYHIGH) + || (!pChn->nLength) || (pChn->dwFlags & CHN_MUTE) + || ((!pChn->nLeftVol) && (!pChn->nRightVol))) return; + UINT n = GetNNAChannel(nChn); + if (!n) return; + MODCHANNEL *p = &Chn[n]; + // Copy Channel + *p = *pChn; + p->dwFlags &= ~(CHN_VIBRATO|CHN_TREMOLO|CHN_PANBRELLO|CHN_MUTE|CHN_PORTAMENTO); + p->nMasterChn = nChn+1; + p->nCommand = 0; + // Cut the note + p->nFadeOutVol = 0; + p->dwFlags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP); + // Stop this channel + pChn->nLength = pChn->nPos = pChn->nPosLo = 0; + pChn->nROfs = pChn->nLOfs = 0; + pChn->nLeftVol = pChn->nRightVol = 0; + return; + } + if (instr >= MAX_INSTRUMENTS) instr = 0; + pSample = pChn->pSample; + pHeader = pChn->pHeader; + if ((instr) && (note)) + { + pHeader = Headers[instr]; + if (pHeader) + { + UINT n = 0; + if (note <= 0x80) + { + n = pHeader->Keyboard[note-1]; + note = pHeader->NoteMap[note-1]; + if ((n) && (n < MAX_SAMPLES)) pSample = Ins[n].pSample; + } + } else pSample = NULL; + } + if (!penv) return; + MODCHANNEL *p = pChn; + for (UINT i=nChn; i<MAX_CHANNELS; p++, i++) + if ((i >= m_nChannels) || (p == pChn)) + { + if (((p->nMasterChn == nChn+1) || (p == pChn)) && (p->pHeader)) + { + BOOL bOk = FALSE; + // Duplicate Check Type + switch(p->pHeader->nDCT) + { + // Note + case DCT_NOTE: + if ((note) && (p->nNote == note) && (pHeader == p->pHeader)) bOk = TRUE; + break; + // Sample + case DCT_SAMPLE: + if ((pSample) && (pSample == p->pSample)) bOk = TRUE; + break; + // Instrument + case DCT_INSTRUMENT: + if (pHeader == p->pHeader) bOk = TRUE; + break; + } + // Duplicate Note Action + if (bOk) + { + switch(p->pHeader->nDNA) + { + // Cut + case DNA_NOTECUT: + KeyOff(i); + p->nVolume = 0; + break; + // Note Off + case DNA_NOTEOFF: + KeyOff(i); + break; + // Note Fade + case DNA_NOTEFADE: + p->dwFlags |= CHN_NOTEFADE; + break; + } + if (!p->nVolume) + { + p->nFadeOutVol = 0; + p->dwFlags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP); + } + } + } + } + if (pChn->dwFlags & CHN_MUTE) return; + // New Note Action + if ((pChn->nVolume) && (pChn->nLength)) + { + UINT n = GetNNAChannel(nChn); + if (n) + { + MODCHANNEL *p = &Chn[n]; + // Copy Channel + *p = *pChn; + p->dwFlags &= ~(CHN_VIBRATO|CHN_TREMOLO|CHN_PANBRELLO|CHN_MUTE|CHN_PORTAMENTO); + p->nMasterChn = nChn+1; + p->nCommand = 0; + // Key Off the note + switch(pChn->nNNA) + { + case NNA_NOTEOFF: KeyOff(n); break; + case NNA_NOTECUT: + p->nFadeOutVol = 0; + case NNA_NOTEFADE: p->dwFlags |= CHN_NOTEFADE; break; + } + if (!p->nVolume) + { + p->nFadeOutVol = 0; + p->dwFlags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP); + } + // Stop this channel + pChn->nLength = pChn->nPos = pChn->nPosLo = 0; + pChn->nROfs = pChn->nLOfs = 0; + } + } +} + + +BOOL CSoundFile::ProcessEffects() +//------------------------------- +{ + MODCHANNEL *pChn = Chn; + int nBreakRow = -1, nPosJump = -1, nPatLoopRow = -1; + + for (UINT nChn=0; nChn<m_nChannels; nChn++, pChn++) + { + UINT instr = pChn->nRowInstr; + UINT volcmd = pChn->nRowVolCmd; + UINT vol = pChn->nRowVolume; + UINT cmd = pChn->nRowCommand; + UINT param = pChn->nRowParam; + bool bPorta = ((cmd != CMD_TONEPORTAMENTO) && (cmd != CMD_TONEPORTAVOL) && (volcmd != VOLCMD_TONEPORTAMENTO)) ? FALSE : TRUE; + UINT nStartTick = 0; + + pChn->dwFlags &= ~CHN_FASTVOLRAMP; + // Process special effects (note delay, pattern delay, pattern loop) + if ((cmd == CMD_MODCMDEX) || (cmd == CMD_S3MCMDEX)) + { + if ((!param) && (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT))) param = pChn->nOldCmdEx; else pChn->nOldCmdEx = param; + // Note Delay ? + if ((param & 0xF0) == 0xD0) + { + nStartTick = param & 0x0F; + } else + if (!m_nTickCount) + { + // Pattern Loop ? + if ((((param & 0xF0) == 0x60) && (cmd == CMD_MODCMDEX)) + || (((param & 0xF0) == 0xB0) && (cmd == CMD_S3MCMDEX))) + { + int nloop = PatternLoop(pChn, param & 0x0F); + if (nloop >= 0) nPatLoopRow = nloop; + } else + // Pattern Delay + if ((param & 0xF0) == 0xE0) + { + m_nPatternDelay = param & 0x0F; + } + } + } + + // Handles note/instrument/volume changes + if (m_nTickCount == nStartTick) // can be delayed by a note delay effect + { + UINT note = pChn->nRowNote; + if (instr) pChn->nNewIns = instr; + // XM: Key-Off + Sample == Note Cut + if (m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if ((note == 0xFF) && ((!pChn->pHeader) || (!(pChn->pHeader->dwFlags & ENV_VOLUME)))) + { + pChn->dwFlags |= CHN_FASTVOLRAMP; + pChn->nVolume = 0; + note = instr = 0; + } + } + if ((!note) && (instr)) + { + if (m_nInstruments) + { + if (pChn->pInstrument) pChn->nVolume = pChn->pInstrument->nVolume; + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + pChn->dwFlags |= CHN_FASTVOLRAMP; + pChn->nVolEnvPosition = 0; + pChn->nPanEnvPosition = 0; + pChn->nPitchEnvPosition = 0; + pChn->nAutoVibDepth = 0; + pChn->nAutoVibPos = 0; + pChn->dwFlags &= ~CHN_NOTEFADE; + pChn->nFadeOutVol = 65536; + } + } else + { + if (instr < MAX_SAMPLES) pChn->nVolume = Ins[instr].nVolume; + } + if (!(m_nType & MOD_TYPE_IT)) instr = 0; + } + // Invalid Instrument ? + if (instr >= MAX_INSTRUMENTS) instr = 0; + // Note Cut/Off => ignore instrument + if (note >= 0xFE) instr = 0; + if ((note) && (note <= 128)) pChn->nNewNote = note; + // New Note Action ? + if ((note) && (note <= 128) && (!bPorta)) + { + CheckNNA(nChn, instr, note, FALSE); + } + // Instrument Change ? + if (instr) + { + MODINSTRUMENT *psmp = pChn->pInstrument; + InstrumentChange(pChn, instr, bPorta, TRUE); + pChn->nNewIns = 0; + // Special IT case: portamento+note causes sample change -> ignore portamento + if ((m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)) + && (psmp != pChn->pInstrument) && (note) && (note < 0x80)) + { + bPorta = FALSE; + } + } + // New Note ? + if (note) + { + if ((!instr) && (pChn->nNewIns) && (note < 0x80)) + { + InstrumentChange(pChn, pChn->nNewIns, bPorta, FALSE, (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) ? FALSE : TRUE); + pChn->nNewIns = 0; + } + NoteChange(nChn, note, bPorta, (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) ? FALSE : TRUE); + if ((bPorta) && (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) && (instr)) + { + pChn->dwFlags |= CHN_FASTVOLRAMP; + pChn->nVolEnvPosition = 0; + pChn->nPanEnvPosition = 0; + pChn->nPitchEnvPosition = 0; + pChn->nAutoVibDepth = 0; + pChn->nAutoVibPos = 0; + } + } + // Tick-0 only volume commands + if (volcmd == VOLCMD_VOLUME) + { + if (vol > 64) vol = 64; + pChn->nVolume = vol << 2; + pChn->dwFlags |= CHN_FASTVOLRAMP; + } else + if (volcmd == VOLCMD_PANNING) + { + if (vol > 64) vol = 64; + pChn->nPan = vol << 2; + pChn->dwFlags |= CHN_FASTVOLRAMP; + } + } + + // Volume Column Effect (except volume & panning) + if ((volcmd > VOLCMD_PANNING) && (m_nTickCount >= nStartTick)) + { + if (volcmd == VOLCMD_TONEPORTAMENTO) + { + if (m_nType & MOD_TYPE_IT) + TonePortamento(pChn, ImpulseTrackerPortaVolCmd[vol & 0x0F]); + else + TonePortamento(pChn, vol * 16); + } else + { + if (vol) pChn->nOldVolParam = vol; else vol = pChn->nOldVolParam; + switch(volcmd) + { + case VOLCMD_VOLSLIDEUP: + VolumeSlide(pChn, vol << 4); + break; + + case VOLCMD_VOLSLIDEDOWN: + VolumeSlide(pChn, vol); + break; + + case VOLCMD_FINEVOLUP: + if (m_nType & MOD_TYPE_IT) + { + if (m_nTickCount == nStartTick) VolumeSlide(pChn, (vol << 4) | 0x0F); + } else + FineVolumeUp(pChn, vol); + break; + + case VOLCMD_FINEVOLDOWN: + if (m_nType & MOD_TYPE_IT) + { + if (m_nTickCount == nStartTick) VolumeSlide(pChn, 0xF0 | vol); + } else + FineVolumeDown(pChn, vol); + break; + + case VOLCMD_VIBRATOSPEED: + Vibrato(pChn, vol << 4); + break; + + case VOLCMD_VIBRATO: + Vibrato(pChn, vol); + break; + + case VOLCMD_PANSLIDELEFT: + PanningSlide(pChn, vol); + break; + + case VOLCMD_PANSLIDERIGHT: + PanningSlide(pChn, vol << 4); + break; + + case VOLCMD_PORTAUP: + PortamentoUp(pChn, vol << 2); + break; + + case VOLCMD_PORTADOWN: + PortamentoDown(pChn, vol << 2); + break; + } + } + } + + // Effects + if (cmd) switch (cmd) + { + // Set Volume + case CMD_VOLUME: + if (!m_nTickCount) + { + pChn->nVolume = (param < 64) ? param*4 : 256; + pChn->dwFlags |= CHN_FASTVOLRAMP; + } + break; + + // Portamento Up + case CMD_PORTAMENTOUP: + if ((!param) && (m_nType & MOD_TYPE_MOD)) break; + PortamentoUp(pChn, param); + break; + + // Portamento Down + case CMD_PORTAMENTODOWN: + if ((!param) && (m_nType & MOD_TYPE_MOD)) break; + PortamentoDown(pChn, param); + break; + + // Volume Slide + case CMD_VOLUMESLIDE: + if ((param) || (m_nType != MOD_TYPE_MOD)) VolumeSlide(pChn, param); + break; + + // Tone-Portamento + case CMD_TONEPORTAMENTO: + TonePortamento(pChn, param); + break; + + // Tone-Portamento + Volume Slide + case CMD_TONEPORTAVOL: + if ((param) || (m_nType != MOD_TYPE_MOD)) VolumeSlide(pChn, param); + TonePortamento(pChn, 0); + break; + + // Vibrato + case CMD_VIBRATO: + Vibrato(pChn, param); + break; + + // Vibrato + Volume Slide + case CMD_VIBRATOVOL: + if ((param) || (m_nType != MOD_TYPE_MOD)) VolumeSlide(pChn, param); + Vibrato(pChn, 0); + break; + + // Set Speed + case CMD_SPEED: + if (!m_nTickCount) SetSpeed(param); + break; + + // Set Tempo + case CMD_TEMPO: + if (!m_nTickCount) + { + if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)) + { + if (param) pChn->nOldTempo = param; else param = pChn->nOldTempo; + } + SetTempo(param); + } + break; + + // Set Offset + case CMD_OFFSET: + if (m_nTickCount) break; + if (param) pChn->nOldOffset = param; else param = pChn->nOldOffset; + param <<= 8; + param |= (UINT)(pChn->nOldHiOffset) << 16; + if ((pChn->nRowNote) && (pChn->nRowNote < 0x80)) + { + if (bPorta) + pChn->nPos = param; + else + pChn->nPos += param; + if (pChn->nPos >= pChn->nLength) + { + if (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) + { + pChn->nPos = pChn->nLoopStart; + if ((m_dwSongFlags & SONG_ITOLDEFFECTS) && (pChn->nLength > 4)) + { + pChn->nPos = pChn->nLength - 2; + } + } + } + } else + if ((param < pChn->nLength) && (m_nType & (MOD_TYPE_MTM|MOD_TYPE_DMF))) + { + pChn->nPos = param; + } + break; + + // Arpeggio + case CMD_ARPEGGIO: + if ((m_nTickCount) || (!pChn->nPeriod) || (!pChn->nNote)) break; + if ((!param) && (!(m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)))) break; + pChn->nCommand = CMD_ARPEGGIO; + if (param) pChn->nArpeggio = param; + break; + + // Retrig + case CMD_RETRIG: + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if (!(param & 0xF0)) param |= pChn->nRetrigParam & 0xF0; + if (!(param & 0x0F)) param |= pChn->nRetrigParam & 0x0F; + param |= 0x100; // increment retrig count on first row + } + if (param) pChn->nRetrigParam = (BYTE)(param & 0xFF); else param = pChn->nRetrigParam; + RetrigNote(nChn, param); + break; + + // Tremor + case CMD_TREMOR: + if (m_nTickCount) break; + pChn->nCommand = CMD_TREMOR; + if (param) pChn->nTremorParam = param; + break; + + // Set Global Volume + case CMD_GLOBALVOLUME: + if (m_nTickCount) break; + if (m_nType != MOD_TYPE_IT) param <<= 1; + if (param > 128) param = 128; + m_nGlobalVolume = param << 1; + break; + + // Global Volume Slide + case CMD_GLOBALVOLSLIDE: + GlobalVolSlide(param); + break; + + // Set 8-bit Panning + case CMD_PANNING8: + if (m_nTickCount) break; + if (!(m_dwSongFlags & SONG_SURROUNDPAN)) pChn->dwFlags &= ~CHN_SURROUND; + if (m_nType & (MOD_TYPE_IT|MOD_TYPE_XM|MOD_TYPE_MT2)) + { + pChn->nPan = param; + } else + if (param <= 0x80) + { + pChn->nPan = param << 1; + } else + if (param == 0xA4) + { + pChn->dwFlags |= CHN_SURROUND; + pChn->nPan = 0x80; + } + pChn->dwFlags |= CHN_FASTVOLRAMP; + break; + + // Panning Slide + case CMD_PANNINGSLIDE: + PanningSlide(pChn, param); + break; + + // Tremolo + case CMD_TREMOLO: + Tremolo(pChn, param); + break; + + // Fine Vibrato + case CMD_FINEVIBRATO: + FineVibrato(pChn, param); + break; + + // MOD/XM Exx Extended Commands + case CMD_MODCMDEX: + ExtendedMODCommands(nChn, param); + break; + + // S3M/IT Sxx Extended Commands + case CMD_S3MCMDEX: + ExtendedS3MCommands(nChn, param); + break; + + // Key Off + case CMD_KEYOFF: + if (!m_nTickCount) KeyOff(nChn); + break; + + // Extra-fine porta up/down + case CMD_XFINEPORTAUPDOWN: + switch(param & 0xF0) + { + case 0x10: ExtraFinePortamentoUp(pChn, param & 0x0F); break; + case 0x20: ExtraFinePortamentoDown(pChn, param & 0x0F); break; + // Modplug XM Extensions + case 0x50: + case 0x60: + case 0x70: + case 0x90: + case 0xA0: ExtendedS3MCommands(nChn, param); break; + } + break; + + // Set Channel Global Volume + case CMD_CHANNELVOLUME: + if (m_nTickCount) break; + if (param <= 64) + { + pChn->nGlobalVol = param; + pChn->dwFlags |= CHN_FASTVOLRAMP; + } + break; + + // Channel volume slide + case CMD_CHANNELVOLSLIDE: + ChannelVolSlide(pChn, param); + break; + + // Panbrello (IT) + case CMD_PANBRELLO: + Panbrello(pChn, param); + break; + + // Set Envelope Position + case CMD_SETENVPOSITION: + if (!m_nTickCount) + { + pChn->nVolEnvPosition = param; + pChn->nPanEnvPosition = param; + pChn->nPitchEnvPosition = param; + if (pChn->pHeader) + { + INSTRUMENTHEADER *penv = pChn->pHeader; + if ((pChn->dwFlags & CHN_PANENV) && (penv->nPanEnv) && (param > penv->PanPoints[penv->nPanEnv-1])) + { + pChn->dwFlags &= ~CHN_PANENV; + } + } + } + break; + + // Position Jump + case CMD_POSITIONJUMP: + nPosJump = param; + break; + + // Pattern Break + case CMD_PATTERNBREAK: + nBreakRow = param; + break; + + // Midi Controller + case CMD_MIDI: + if (m_nTickCount) break; + if (param < 0x80){ + ProcessMidiMacro(nChn, &m_MidiCfg.szMidiSFXExt[pChn->nActiveMacro << 5], param); + } else { + ProcessMidiMacro(nChn, &m_MidiCfg.szMidiZXXExt[(param & 0x7F) << 5], 0); + } + break; + } + } + + // Navigation Effects + if (!m_nTickCount) + { + // Pattern Loop + if (nPatLoopRow >= 0) + { + m_nNextPattern = m_nCurrentPattern; + m_nNextRow = nPatLoopRow; + if (m_nPatternDelay) m_nNextRow++; + } else + // Pattern Break / Position Jump only if no loop running + if ((nBreakRow >= 0) || (nPosJump >= 0)) + { + BOOL bNoLoop = FALSE; + if (nPosJump < 0) nPosJump = m_nCurrentPattern+1; + if (nBreakRow < 0) nBreakRow = 0; + // Modplug Tracker & ModPlugin allow backward jumps + #ifndef MODPLUG_FASTSOUNDLIB + if ((nPosJump < (int)m_nCurrentPattern) + || ((nPosJump == (int)m_nCurrentPattern) && (nBreakRow <= (int)m_nRow))) + { + if (!IsValidBackwardJump(m_nCurrentPattern, m_nRow, nPosJump, nBreakRow)) + { + if (m_nRepeatCount) + { + if (m_nRepeatCount > 0) m_nRepeatCount--; + } else + { + #ifdef MODPLUG_TRACKER + if (gdwSoundSetup & SNDMIX_NOBACKWARDJUMPS) + #endif + // Backward jump disabled + bNoLoop = TRUE; + //reset repeat count incase there are multiple loops. + //(i.e. Unreal tracks) + m_nRepeatCount = m_nInitialRepeatCount; + } + } + } + #endif // MODPLUG_FASTSOUNDLIB + if (((!bNoLoop) && (nPosJump < MAX_ORDERS)) + && ((nPosJump != (int)m_nCurrentPattern) || (nBreakRow != (int)m_nRow))) + { + if (nPosJump != (int)m_nCurrentPattern) + { + for (UINT i=0; i<m_nChannels; i++) Chn[i].nPatternLoopCount = 0; + } + m_nNextPattern = nPosJump; + m_nNextRow = (UINT)nBreakRow; + } + } + } + return TRUE; +} + + +//////////////////////////////////////////////////////////// +// Channels effects + +void CSoundFile::PortamentoUp(MODCHANNEL *pChn, UINT param) +//--------------------------------------------------------- +{ + if (param) pChn->nOldPortaUpDown = param; else param = pChn->nOldPortaUpDown; + if ((m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM)) && ((param & 0xF0) >= 0xE0)) + { + if (param & 0x0F) + { + if ((param & 0xF0) == 0xF0) + { + FinePortamentoUp(pChn, param & 0x0F); + } else + if ((param & 0xF0) == 0xE0) + { + ExtraFinePortamentoUp(pChn, param & 0x0F); + } + } + return; + } + // Regular Slide + if (!(m_dwSongFlags & SONG_FIRSTTICK) || (m_nMusicSpeed == 1)) //rewbs.PortaA01fix + { + DoFreqSlide(pChn, -(int)(param * 4)); + } +} + + +void CSoundFile::PortamentoDown(MODCHANNEL *pChn, UINT param) +//----------------------------------------------------------- +{ + if (param) pChn->nOldPortaUpDown = param; else param = pChn->nOldPortaUpDown; + if ((m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM)) && ((param & 0xF0) >= 0xE0)) + { + if (param & 0x0F) + { + if ((param & 0xF0) == 0xF0) + { + FinePortamentoDown(pChn, param & 0x0F); + } else + if ((param & 0xF0) == 0xE0) + { + ExtraFinePortamentoDown(pChn, param & 0x0F); + } + } + return; + } + if (!(m_dwSongFlags & SONG_FIRSTTICK) || (m_nMusicSpeed == 1)) { //rewbs.PortaA01fix + DoFreqSlide(pChn, (int)(param << 2)); + } +} + + +void CSoundFile::FinePortamentoUp(MODCHANNEL *pChn, UINT param) +//------------------------------------------------------------- +{ + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; + } + if (m_dwSongFlags & SONG_FIRSTTICK) + { + if ((pChn->nPeriod) && (param)) + { + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) + { + pChn->nPeriod = _muldivr(pChn->nPeriod, LinearSlideDownTable[param & 0x0F], 65536); + } else + { + pChn->nPeriod -= (int)(param * 4); + } + if (pChn->nPeriod < 1) pChn->nPeriod = 1; + } + } +} + + +void CSoundFile::FinePortamentoDown(MODCHANNEL *pChn, UINT param) +//--------------------------------------------------------------- +{ + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; + } + if (m_dwSongFlags & SONG_FIRSTTICK) + { + if ((pChn->nPeriod) && (param)) + { + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) + { + pChn->nPeriod = _muldivr(pChn->nPeriod, LinearSlideUpTable[param & 0x0F], 65536); + } else + { + pChn->nPeriod += (int)(param * 4); + } + if (pChn->nPeriod > 0xFFFF) pChn->nPeriod = 0xFFFF; + } + } +} + + +void CSoundFile::ExtraFinePortamentoUp(MODCHANNEL *pChn, UINT param) +//------------------------------------------------------------------ +{ + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; + } + if (m_dwSongFlags & SONG_FIRSTTICK) + { + if ((pChn->nPeriod) && (param)) + { + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) + { + pChn->nPeriod = _muldivr(pChn->nPeriod, FineLinearSlideDownTable[param & 0x0F], 65536); + } else + { + pChn->nPeriod -= (int)(param); + } + if (pChn->nPeriod < 1) pChn->nPeriod = 1; + } + } +} + + +void CSoundFile::ExtraFinePortamentoDown(MODCHANNEL *pChn, UINT param) +//-------------------------------------------------------------------- +{ + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; + } + if (m_dwSongFlags & SONG_FIRSTTICK) + { + if ((pChn->nPeriod) && (param)) + { + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) + { + pChn->nPeriod = _muldivr(pChn->nPeriod, FineLinearSlideUpTable[param & 0x0F], 65536); + } else + { + pChn->nPeriod += (int)(param); + } + if (pChn->nPeriod > 0xFFFF) pChn->nPeriod = 0xFFFF; + } + } +} + + +// Portamento Slide +void CSoundFile::TonePortamento(MODCHANNEL *pChn, UINT param) +//----------------------------------------------------------- +{ + if (param) pChn->nPortamentoSlide = param * 4; + pChn->dwFlags |= CHN_PORTAMENTO; + if ((pChn->nPeriod) && (pChn->nPortamentoDest) && (!(m_dwSongFlags & SONG_FIRSTTICK))) + { + if (pChn->nPeriod < pChn->nPortamentoDest) + { + LONG delta = (int)pChn->nPortamentoSlide; + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) + { + UINT n = pChn->nPortamentoSlide >> 2; + if (n > 255) n = 255; + delta = _muldivr(pChn->nPeriod, LinearSlideUpTable[n], 65536) - pChn->nPeriod; + if (delta < 1) delta = 1; + } + pChn->nPeriod += delta; + if (pChn->nPeriod > pChn->nPortamentoDest) pChn->nPeriod = pChn->nPortamentoDest; + } else + if (pChn->nPeriod > pChn->nPortamentoDest) + { + LONG delta = - (int)pChn->nPortamentoSlide; + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) + { + UINT n = pChn->nPortamentoSlide >> 2; + if (n > 255) n = 255; + delta = _muldivr(pChn->nPeriod, LinearSlideDownTable[n], 65536) - pChn->nPeriod; + if (delta > -1) delta = -1; + } + pChn->nPeriod += delta; + if (pChn->nPeriod < pChn->nPortamentoDest) pChn->nPeriod = pChn->nPortamentoDest; + } + } +} + + +void CSoundFile::Vibrato(MODCHANNEL *p, UINT param) +//------------------------------------------------- +{ + if (param & 0x0F) p->nVibratoDepth = (param & 0x0F) * 4; + if (param & 0xF0) p->nVibratoSpeed = (param >> 4) & 0x0F; + p->dwFlags |= CHN_VIBRATO; +} + + +void CSoundFile::FineVibrato(MODCHANNEL *p, UINT param) +//----------------------------------------------------- +{ + if (param & 0x0F) p->nVibratoDepth = param & 0x0F; + if (param & 0xF0) p->nVibratoSpeed = (param >> 4) & 0x0F; + p->dwFlags |= CHN_VIBRATO; +} + + +void CSoundFile::Panbrello(MODCHANNEL *p, UINT param) +//--------------------------------------------------- +{ + if (param & 0x0F) p->nPanbrelloDepth = param & 0x0F; + if (param & 0xF0) p->nPanbrelloSpeed = (param >> 4) & 0x0F; + p->dwFlags |= CHN_PANBRELLO; +} + + +void CSoundFile::VolumeSlide(MODCHANNEL *pChn, UINT param) +//-------------------------------------------------------- +{ + if (param) pChn->nOldVolumeSlide = param; else param = pChn->nOldVolumeSlide; + LONG newvolume = pChn->nVolume; + if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM|MOD_TYPE_AMF)) + { + if ((param & 0x0F) == 0x0F) + { + if (param & 0xF0) + { + FineVolumeUp(pChn, (param >> 4)); + return; + } else + { + if ((m_dwSongFlags & SONG_FIRSTTICK) && (!(m_dwSongFlags & SONG_FASTVOLSLIDES))) + { + newvolume -= 0x0F * 4; + } + } + } else + if ((param & 0xF0) == 0xF0) + { + if (param & 0x0F) + { + FineVolumeDown(pChn, (param & 0x0F)); + return; + } else + { + if ((m_dwSongFlags & SONG_FIRSTTICK) && (!(m_dwSongFlags & SONG_FASTVOLSLIDES))) + { + newvolume += 0x0F * 4; + } + } + } + } + if ((!(m_dwSongFlags & SONG_FIRSTTICK)) || (m_dwSongFlags & SONG_FASTVOLSLIDES)) + { + if (param & 0x0F) newvolume -= (int)((param & 0x0F) * 4); + else newvolume += (int)((param & 0xF0) >> 2); + if (m_nType & MOD_TYPE_MOD) pChn->dwFlags |= CHN_FASTVOLRAMP; + } + if (newvolume < 0) newvolume = 0; + if (newvolume > 256) newvolume = 256; + pChn->nVolume = newvolume; +} + + +void CSoundFile::PanningSlide(MODCHANNEL *pChn, UINT param) +//--------------------------------------------------------- +{ + LONG nPanSlide = 0; + if (param) pChn->nOldPanSlide = param; else param = pChn->nOldPanSlide; + if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM)) + { + if (((param & 0x0F) == 0x0F) && (param & 0xF0)) + { + if (m_dwSongFlags & SONG_FIRSTTICK) + { + param = (param & 0xF0) >> 2; + nPanSlide = - (int)param; + } + } else + if (((param & 0xF0) == 0xF0) && (param & 0x0F)) + { + if (m_dwSongFlags & SONG_FIRSTTICK) + { + nPanSlide = (param & 0x0F) << 2; + } + } else + { + if (!(m_dwSongFlags & SONG_FIRSTTICK)) + { + if (param & 0x0F) nPanSlide = (int)((param & 0x0F) << 2); + else nPanSlide = -(int)((param & 0xF0) >> 2); + } + } + } else + { + if (!(m_dwSongFlags & SONG_FIRSTTICK)) + { + if (param & 0x0F) nPanSlide = -(int)((param & 0x0F) << 2); + else nPanSlide = (int)((param & 0xF0) >> 2); + } + } + if (nPanSlide) + { + nPanSlide += pChn->nPan; + if (nPanSlide < 0) nPanSlide = 0; + if (nPanSlide > 256) nPanSlide = 256; + pChn->nPan = nPanSlide; + } +} + + +void CSoundFile::FineVolumeUp(MODCHANNEL *pChn, UINT param) +//--------------------------------------------------------- +{ + if (param) pChn->nOldFineVolUpDown = param; else param = pChn->nOldFineVolUpDown; + if (m_dwSongFlags & SONG_FIRSTTICK) + { + pChn->nVolume += param * 4; + if (pChn->nVolume > 256) pChn->nVolume = 256; + if (m_nType & MOD_TYPE_MOD) pChn->dwFlags |= CHN_FASTVOLRAMP; + } +} + + +void CSoundFile::FineVolumeDown(MODCHANNEL *pChn, UINT param) +//----------------------------------------------------------- +{ + if (param) pChn->nOldFineVolUpDown = param; else param = pChn->nOldFineVolUpDown; + if (m_dwSongFlags & SONG_FIRSTTICK) + { + pChn->nVolume -= param * 4; + if (pChn->nVolume < 0) pChn->nVolume = 0; + if (m_nType & MOD_TYPE_MOD) pChn->dwFlags |= CHN_FASTVOLRAMP; + } +} + + +void CSoundFile::Tremolo(MODCHANNEL *p, UINT param) +//------------------------------------------------- +{ + if (param & 0x0F) p->nTremoloDepth = (param & 0x0F) << 2; + if (param & 0xF0) p->nTremoloSpeed = (param >> 4) & 0x0F; + p->dwFlags |= CHN_TREMOLO; +} + + +void CSoundFile::ChannelVolSlide(MODCHANNEL *pChn, UINT param) +//------------------------------------------------------------ +{ + LONG nChnSlide = 0; + if (param) pChn->nOldChnVolSlide = param; else param = pChn->nOldChnVolSlide; + if (((param & 0x0F) == 0x0F) && (param & 0xF0)) + { + if (m_dwSongFlags & SONG_FIRSTTICK) nChnSlide = param >> 4; + } else + if (((param & 0xF0) == 0xF0) && (param & 0x0F)) + { + if (m_dwSongFlags & SONG_FIRSTTICK) nChnSlide = - (int)(param & 0x0F); + } else + { + if (!(m_dwSongFlags & SONG_FIRSTTICK)) + { + if (param & 0x0F) nChnSlide = -(int)(param & 0x0F); + else nChnSlide = (int)((param & 0xF0) >> 4); + } + } + if (nChnSlide) + { + nChnSlide += pChn->nGlobalVol; + if (nChnSlide < 0) nChnSlide = 0; + if (nChnSlide > 64) nChnSlide = 64; + pChn->nGlobalVol = nChnSlide; + } +} + + +void CSoundFile::ExtendedMODCommands(UINT nChn, UINT param) +//--------------------------------------------------------- +{ + MODCHANNEL *pChn = &Chn[nChn]; + UINT command = param & 0xF0; + param &= 0x0F; + switch(command) + { + // E0x: Set Filter + // E1x: Fine Portamento Up + case 0x10: if ((param) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) FinePortamentoUp(pChn, param); break; + // E2x: Fine Portamento Down + case 0x20: if ((param) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) FinePortamentoDown(pChn, param); break; + // E3x: Set Glissando Control + case 0x30: pChn->dwFlags &= ~CHN_GLISSANDO; if (param) pChn->dwFlags |= CHN_GLISSANDO; break; + // E4x: Set Vibrato WaveForm + case 0x40: pChn->nVibratoType = param & 0x07; break; + // E5x: Set FineTune + case 0x50: if (m_nTickCount) break; + pChn->nC4Speed = S3MFineTuneTable[param]; + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + pChn->nFineTune = param*2; + else + pChn->nFineTune = MOD2XMFineTune(param); + if (pChn->nPeriod) pChn->nPeriod = GetPeriodFromNote(pChn->nNote, pChn->nFineTune, pChn->nC4Speed); + break; + // E6x: Pattern Loop + // E7x: Set Tremolo WaveForm + case 0x70: pChn->nTremoloType = param & 0x07; break; + // E8x: Set 4-bit Panning + case 0x80: if (!m_nTickCount) { pChn->nPan = (param << 4) + 8; pChn->dwFlags |= CHN_FASTVOLRAMP; } break; + // E9x: Retrig + case 0x90: RetrigNote(nChn, param); break; + // EAx: Fine Volume Up + case 0xA0: if ((param) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) FineVolumeUp(pChn, param); break; + // EBx: Fine Volume Down + case 0xB0: if ((param) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) FineVolumeDown(pChn, param); break; + // ECx: Note Cut + case 0xC0: NoteCut(nChn, param); break; + // EDx: Note Delay + // EEx: Pattern Delay + // EFx: MOD: Invert Loop, XM: Set Active Midi Macro + case 0xF0: pChn->nActiveMacro = param; break; + } +} + + +void CSoundFile::ExtendedS3MCommands(UINT nChn, UINT param) +//--------------------------------------------------------- +{ + MODCHANNEL *pChn = &Chn[nChn]; + UINT command = param & 0xF0; + param &= 0x0F; + switch(command) + { + // S0x: Set Filter + // S1x: Set Glissando Control + case 0x10: pChn->dwFlags &= ~CHN_GLISSANDO; if (param) pChn->dwFlags |= CHN_GLISSANDO; break; + // S2x: Set FineTune + case 0x20: if (m_nTickCount) break; + pChn->nC4Speed = S3MFineTuneTable[param & 0x0F]; + pChn->nFineTune = MOD2XMFineTune(param); + if (pChn->nPeriod) pChn->nPeriod = GetPeriodFromNote(pChn->nNote, pChn->nFineTune, pChn->nC4Speed); + break; + // S3x: Set Vibrato WaveForm + case 0x30: pChn->nVibratoType = param & 0x07; break; + // S4x: Set Tremolo WaveForm + case 0x40: pChn->nTremoloType = param & 0x07; break; + // S5x: Set Panbrello WaveForm + case 0x50: pChn->nPanbrelloType = param & 0x07; break; + // S6x: Pattern Delay for x frames + case 0x60: m_nFrameDelay = param; break; + // S7x: Envelope Control + case 0x70: if (m_nTickCount) break; + switch(param) + { + case 0: + case 1: + case 2: + { + MODCHANNEL *bkp = &Chn[m_nChannels]; + for (UINT i=m_nChannels; i<MAX_CHANNELS; i++, bkp++) + { + if (bkp->nMasterChn == nChn+1) + { + if (param == 1) KeyOff(i); else + if (param == 2) bkp->dwFlags |= CHN_NOTEFADE; else + { bkp->dwFlags |= CHN_NOTEFADE; bkp->nFadeOutVol = 0; } + } + } + } + break; + case 3: pChn->nNNA = NNA_NOTECUT; break; + case 4: pChn->nNNA = NNA_CONTINUE; break; + case 5: pChn->nNNA = NNA_NOTEOFF; break; + case 6: pChn->nNNA = NNA_NOTEFADE; break; + case 7: pChn->dwFlags &= ~CHN_VOLENV; break; + case 8: pChn->dwFlags |= CHN_VOLENV; break; + case 9: pChn->dwFlags &= ~CHN_PANENV; break; + case 10: pChn->dwFlags |= CHN_PANENV; break; + case 11: pChn->dwFlags &= ~CHN_PITCHENV; break; + case 12: pChn->dwFlags |= CHN_PITCHENV; break; + } + break; + // S8x: Set 4-bit Panning + case 0x80: if (!m_nTickCount) { pChn->nPan = (param << 4) + 8; pChn->dwFlags |= CHN_FASTVOLRAMP; } break; + // S9x: Set Surround + case 0x90: ExtendedChannelEffect(pChn, param & 0x0F); break; + // SAx: Set 64k Offset + case 0xA0: if (!m_nTickCount) + { + pChn->nOldHiOffset = param; + if ((pChn->nRowNote) && (pChn->nRowNote < 0x80)) + { + DWORD pos = param << 16; + if (pos < pChn->nLength) pChn->nPos = pos; + } + } + break; + // SBx: Pattern Loop + // SCx: Note Cut + case 0xC0: NoteCut(nChn, param); break; + // SDx: Note Delay + // case 0xD0: break; + // SEx: Pattern Delay for x rows + // SFx: S3M: Funk Repeat, IT: Set Active Midi Macro + case 0xF0: pChn->nActiveMacro = param; break; + } +} + + +void CSoundFile::ExtendedChannelEffect(MODCHANNEL *pChn, UINT param) +//------------------------------------------------------------------ +{ + // S9x and X9x commands (S3M/XM/IT only) + if (m_nTickCount) return; + switch(param & 0x0F) + { + // S90: Surround Off + case 0x00: pChn->dwFlags &= ~CHN_SURROUND; break; + // S91: Surround On + case 0x01: pChn->dwFlags |= CHN_SURROUND; pChn->nPan = 128; break; + //////////////////////////////////////////////////////////// + // Modplug Extensions + // S98: Reverb Off + case 0x08: + pChn->dwFlags &= ~CHN_REVERB; + pChn->dwFlags |= CHN_NOREVERB; + break; + // S99: Reverb On + case 0x09: + pChn->dwFlags &= ~CHN_NOREVERB; + pChn->dwFlags |= CHN_REVERB; + break; + // S9A: 2-Channels surround mode + case 0x0A: + m_dwSongFlags &= ~SONG_SURROUNDPAN; + break; + // S9B: 4-Channels surround mode + case 0x0B: + m_dwSongFlags |= SONG_SURROUNDPAN; + break; + // S9C: IT Filter Mode + case 0x0C: + m_dwSongFlags &= ~SONG_MPTFILTERMODE; + break; + // S9D: MPT Filter Mode + case 0x0D: + m_dwSongFlags |= SONG_MPTFILTERMODE; + break; + // S9E: Go forward + case 0x0E: + pChn->dwFlags &= ~(CHN_PINGPONGFLAG); + break; + // S9F: Go backward (set position at the end for non-looping samples) + case 0x0F: + if ((!(pChn->dwFlags & CHN_LOOP)) && (!pChn->nPos) && (pChn->nLength)) + { + pChn->nPos = pChn->nLength - 1; + pChn->nPosLo = 0xFFFF; + } + pChn->dwFlags |= CHN_PINGPONGFLAG; + break; + } +} + + +void CSoundFile::ProcessMidiMacro(UINT nChn, LPCSTR pszMidiMacro, UINT param) +//--------------------------------------------------------------------------- +{ + MODCHANNEL *pChn = &Chn[nChn]; + DWORD dwMacro = (*((LPDWORD)pszMidiMacro)) & 0x7F5F7F5F; + // Not Internal Device ? + if (dwMacro != 0x30463046 && dwMacro != 0x31463046) + { + UINT pos = 0, nNib = 0, nBytes = 0; + DWORD dwMidiCode = 0, dwByteCode = 0; + while (pos+6 <= 32) + { + CHAR cData = pszMidiMacro[pos++]; + if (!cData) break; + if ((cData >= '0') && (cData <= '9')) { dwByteCode = (dwByteCode<<4) | (cData-'0'); nNib++; } else + if ((cData >= 'A') && (cData <= 'F')) { dwByteCode = (dwByteCode<<4) | (cData-'A'+10); nNib++; } else + if ((cData >= 'a') && (cData <= 'f')) { dwByteCode = (dwByteCode<<4) | (cData-'a'+10); nNib++; } else + if ((cData == 'z') || (cData == 'Z')) { dwByteCode = param & 0x7f; nNib = 2; } else + if ((cData == 'x') || (cData == 'X')) { dwByteCode = param & 0x70; nNib = 2; } else + if ((cData == 'y') || (cData == 'Y')) { dwByteCode = (param & 0x0f)<<3; nNib = 2; } else + if (nNib >= 2) + { + nNib = 0; + dwMidiCode |= dwByteCode << (nBytes*8); + dwByteCode = 0; + nBytes++; + if (nBytes >= 3) + { + UINT nMasterCh = (nChn < m_nChannels) ? nChn+1 : pChn->nMasterChn; + if ((nMasterCh) && (nMasterCh <= m_nChannels)) + { + UINT nPlug = ChnSettings[nMasterCh-1].nMixPlugin; + if ((nPlug) && (nPlug <= MAX_MIXPLUGINS)) + { + IMixPlugin *pPlugin = m_MixPlugins[nPlug-1].pMixPlugin; + if ((pPlugin) && (m_MixPlugins[nPlug-1].pMixState)) + { + pPlugin->MidiSend(dwMidiCode); + } + } + } + nBytes = 0; + dwMidiCode = 0; + } + } + + } + return; + } + // Internal device + pszMidiMacro += 4; + // Filter ? + if (pszMidiMacro[0] == '0') + { + CHAR cData1 = pszMidiMacro[2]; + DWORD dwParam = 0; + if ((cData1 == 'z') || (cData1 == 'Z')) + { + dwParam = param; + } else + { + CHAR cData2 = pszMidiMacro[3]; + if ((cData1 >= '0') && (cData1 <= '9')) dwParam += (cData1 - '0') << 4; else + if ((cData1 >= 'A') && (cData1 <= 'F')) dwParam += (cData1 - 'A' + 0x0A) << 4; + if ((cData2 >= '0') && (cData2 <= '9')) dwParam += (cData2 - '0'); else + if ((cData2 >= 'A') && (cData2 <= 'F')) dwParam += (cData2 - 'A' + 0x0A); + } + switch(pszMidiMacro[1]) + { + // F0.F0.00.xx: Set CutOff + case '0': + { + int oldcutoff = pChn->nCutOff; + if (dwParam < 0x80) pChn->nCutOff = dwParam; +#ifndef NO_FILTER + oldcutoff -= pChn->nCutOff; + + if (oldcutoff < 0) oldcutoff = -oldcutoff; + if ((pChn->nVolume > 0) || (oldcutoff < 0x10) + || (!(pChn->dwFlags & CHN_FILTER)) || (!(pChn->nLeftVol|pChn->nRightVol))) + SetupChannelFilter(pChn, (pChn->dwFlags & CHN_FILTER) ? FALSE : TRUE); +#endif // NO_FILTER + } + break; + + // F0.F0.01.xx: Set Resonance + case '1': + if (dwParam < 0x80) pChn->nResonance = dwParam; +#ifndef NO_FILTER + SetupChannelFilter(pChn, (pChn->dwFlags & CHN_FILTER) ? FALSE : TRUE); +#endif // NO_FILTER + + break; + } + + } +} + + +void CSoundFile::RetrigNote(UINT nChn, UINT param) +//------------------------------------------------ +{ + // Retrig: bit 8 is set if it's the new XM retrig + MODCHANNEL *pChn = &Chn[nChn]; + UINT nRetrigSpeed = param & 0x0F; + UINT nRetrigCount = pChn->nRetrigCount; + BOOL bDoRetrig = FALSE; + + if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)) + { + if (!nRetrigSpeed) nRetrigSpeed = 1; + if ((nRetrigCount) && (!(nRetrigCount % nRetrigSpeed))) bDoRetrig = TRUE; + nRetrigCount++; + } else + { + UINT realspeed = nRetrigSpeed; + if ((param & 0x100) && (pChn->nRowVolCmd == VOLCMD_VOLUME) && (pChn->nRowParam & 0xF0)) realspeed++; + if ((m_nTickCount) || (param & 0x100)) + { + if (!realspeed) realspeed = 1; + if ((!(param & 0x100)) && (m_nMusicSpeed) && (!(m_nTickCount % realspeed))) bDoRetrig = TRUE; + nRetrigCount++; + } else if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) nRetrigCount = 0; + if (nRetrigCount >= realspeed) + { + if ((m_nTickCount) || ((param & 0x100) && (!pChn->nRowNote))) bDoRetrig = TRUE; + } + } + if (bDoRetrig) + { + UINT dv = (param >> 4) & 0x0F; + if (dv) + { + int vol = pChn->nVolume; + if (retrigTable1[dv]) + vol = (vol * retrigTable1[dv]) >> 4; + else + vol += ((int)retrigTable2[dv]) << 2; + if (vol < 0) vol = 0; + if (vol > 256) vol = 256; + pChn->nVolume = vol; + pChn->dwFlags |= CHN_FASTVOLRAMP; + } + UINT nNote = pChn->nNewNote; + LONG nOldPeriod = pChn->nPeriod; + if ((nNote) && (nNote <= NOTE_MAX) && (pChn->nLength)) CheckNNA(nChn, 0, nNote, TRUE); + BOOL bResetEnv = FALSE; + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if ((pChn->nRowInstr) && (param < 0x100)) { InstrumentChange(pChn, pChn->nRowInstr, FALSE, FALSE); bResetEnv = TRUE; } + if (param < 0x100) bResetEnv = TRUE; + } + NoteChange(nChn, nNote, FALSE, bResetEnv); + if ((m_nType & MOD_TYPE_IT) && (!pChn->nRowNote) && (nOldPeriod)) pChn->nPeriod = nOldPeriod; + if (!(m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT))) nRetrigCount = 0; + } + pChn->nRetrigCount = (BYTE)nRetrigCount; +} + + +void CSoundFile::DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide) +//------------------------------------------------------------- +{ + // IT Linear slides + if (!pChn->nPeriod) return; + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) + { + if (nFreqSlide < 0) + { + UINT n = (- nFreqSlide) >> 2; + if (n > 255) n = 255; + pChn->nPeriod = _muldivr(pChn->nPeriod, LinearSlideDownTable[n], 65536); + } else + { + UINT n = (nFreqSlide) >> 2; + + if (n > 255) n = 255; + pChn->nPeriod = _muldivr(pChn->nPeriod, LinearSlideUpTable[n], 65536); + } + } else + { + pChn->nPeriod += nFreqSlide; + } + if (pChn->nPeriod < 1) + { + pChn->nPeriod = 1; + if (m_nType & MOD_TYPE_IT) + { + pChn->dwFlags |= CHN_NOTEFADE; + pChn->nFadeOutVol = 0; + } + } +} + + +void CSoundFile::NoteCut(UINT nChn, UINT nTick) +//--------------------------------------------- +{ + if (m_nTickCount == nTick) + { + MODCHANNEL *pChn = &Chn[nChn]; + // if (m_nInstruments) KeyOff(pChn); ? + pChn->nVolume = 0; + pChn->dwFlags |= CHN_FASTVOLRAMP; + } +} + + +void CSoundFile::KeyOff(UINT nChn) +//-------------------------------- +{ + MODCHANNEL *pChn = &Chn[nChn]; + BOOL bKeyOn = (pChn->dwFlags & CHN_KEYOFF) ? FALSE : TRUE; + pChn->dwFlags |= CHN_KEYOFF; + //if ((!pChn->pHeader) || (!(pChn->dwFlags & CHN_VOLENV))) + if ((pChn->pHeader) && (!(pChn->dwFlags & CHN_VOLENV))) + { + pChn->dwFlags |= CHN_NOTEFADE; + } + if (!pChn->nLength) return; + if ((pChn->dwFlags & CHN_SUSTAINLOOP) && (pChn->pInstrument) && (bKeyOn)) + { + MODINSTRUMENT *psmp = pChn->pInstrument; + if (psmp->uFlags & CHN_LOOP) + { + if (psmp->uFlags & CHN_PINGPONGLOOP) + pChn->dwFlags |= CHN_PINGPONGLOOP; + else + pChn->dwFlags &= ~(CHN_PINGPONGLOOP|CHN_PINGPONGFLAG); + pChn->dwFlags |= CHN_LOOP; + pChn->nLength = psmp->nLength; + pChn->nLoopStart = psmp->nLoopStart; + pChn->nLoopEnd = psmp->nLoopEnd; + if (pChn->nLength > pChn->nLoopEnd) pChn->nLength = pChn->nLoopEnd; + } else + { + pChn->dwFlags &= ~(CHN_LOOP|CHN_PINGPONGLOOP|CHN_PINGPONGFLAG); + pChn->nLength = psmp->nLength; + } + } + if (pChn->pHeader) + { + INSTRUMENTHEADER *penv = pChn->pHeader; + if (((penv->dwFlags & ENV_VOLLOOP) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) && (penv->nFadeOut)) + pChn->dwFlags |= CHN_NOTEFADE; + } +} + + +////////////////////////////////////////////////////////// +// CSoundFile: Global Effects + + +void CSoundFile::SetSpeed(UINT param) +//----------------------------------- +{ + UINT max = (m_nType == MOD_TYPE_IT) ? 256 : 128; + // Modplug Tracker and Mod-Plugin don't do this check +#ifndef MODPLUG_TRACKER +#ifndef MODPLUG_FASTSOUNDLIB + // Big Hack!!! + if ((!param) || (param >= 0x80) || ((m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM|MOD_TYPE_MT2)) && (param >= 0x1E))) + { + if (IsSongFinished(m_nCurrentPattern, m_nRow+1)) + { + GlobalFadeSong(1000); + } + } +#endif // MODPLUG_FASTSOUNDLIB +#endif // MODPLUG_TRACKER + if ((m_nType & MOD_TYPE_S3M) && (param > 0x80)) param -= 0x80; + if ((param) && (param <= max)) m_nMusicSpeed = param; +} + + +void CSoundFile::SetTempo(UINT param) +//----------------------------------- +{ + if (param < 0x20) + { + // Tempo Slide + if ((param & 0xF0) == 0x10) + { + m_nMusicTempo += (param & 0x0F) * 2; + if (m_nMusicTempo > 255) m_nMusicTempo = 255; + } else + { + m_nMusicTempo -= (param & 0x0F) * 2; + if ((LONG)m_nMusicTempo < 32) m_nMusicTempo = 32; + } + } else + { + m_nMusicTempo = param; + } +} + + +int CSoundFile::PatternLoop(MODCHANNEL *pChn, UINT param) +//------------------------------------------------------- +{ + if (param) + { + if (pChn->nPatternLoopCount) + { + pChn->nPatternLoopCount--; + if (!pChn->nPatternLoopCount) return -1; + } else + { + MODCHANNEL *p = Chn; + for (UINT i=0; i<m_nChannels; i++, p++) if (p != pChn) + { + // Loop already done + if (p->nPatternLoopCount) return -1; + } + pChn->nPatternLoopCount = param; + } + return pChn->nPatternLoop; + } else + { + pChn->nPatternLoop = m_nRow; + } + return -1; +} + + +void CSoundFile::GlobalVolSlide(UINT param) +//----------------------------------------- +{ + LONG nGlbSlide = 0; + if (param) m_nOldGlbVolSlide = param; else param = m_nOldGlbVolSlide; + if (((param & 0x0F) == 0x0F) && (param & 0xF0)) + { + if (m_dwSongFlags & SONG_FIRSTTICK) nGlbSlide = (param >> 4) * 2; + } else + if (((param & 0xF0) == 0xF0) && (param & 0x0F)) + { + if (m_dwSongFlags & SONG_FIRSTTICK) nGlbSlide = - (int)((param & 0x0F) * 2); + } else + { + if (!(m_dwSongFlags & SONG_FIRSTTICK)) + { + if (param & 0xF0) nGlbSlide = (int)((param & 0xF0) >> 4) * 2; + else nGlbSlide = -(int)((param & 0x0F) * 2); + } + } + if (nGlbSlide) + { + if (m_nType != MOD_TYPE_IT) nGlbSlide *= 2; + nGlbSlide += m_nGlobalVolume; + if (nGlbSlide < 0) nGlbSlide = 0; + if (nGlbSlide > 256) nGlbSlide = 256; + m_nGlobalVolume = nGlbSlide; + } +} + + +DWORD CSoundFile::IsSongFinished(UINT nStartOrder, UINT nStartRow) const +//---------------------------------------------------------------------- +{ + UINT nOrd; + + for (nOrd=nStartOrder; nOrd<MAX_ORDERS; nOrd++) + { + UINT nPat = Order[nOrd]; + if (nPat != 0xFE) + { + MODCOMMAND *p; + + if (nPat >= MAX_PATTERNS) break; + p = Patterns[nPat]; + if (p) + { + UINT len = PatternSize[nPat] * m_nChannels; + UINT pos = (nOrd == nStartOrder) ? nStartRow : 0; + pos *= m_nChannels; + while (pos < len) + { + UINT cmd; + if ((p[pos].note) || (p[pos].volcmd)) return 0; + cmd = p[pos].command; + if (cmd == CMD_MODCMDEX) + { + UINT cmdex = p[pos].param & 0xF0; + if ((!cmdex) || (cmdex == 0x60) || (cmdex == 0xE0) || (cmdex == 0xF0)) cmd = 0; + } + if ((cmd) && (cmd != CMD_SPEED) && (cmd != CMD_TEMPO)) return 0; + pos++; + } + } + } + } + return (nOrd < MAX_ORDERS) ? nOrd : MAX_ORDERS-1; +} + + +BOOL CSoundFile::IsValidBackwardJump(UINT nStartOrder, UINT nStartRow, UINT nJumpOrder, UINT nJumpRow) const +//---------------------------------------------------------------------------------------------------------- +{ + while ((nJumpOrder < MAX_PATTERNS) && (Order[nJumpOrder] == 0xFE)) nJumpOrder++; + if ((nStartOrder >= MAX_PATTERNS) || (nJumpOrder >= MAX_PATTERNS)) return FALSE; + // Treat only case with jumps in the same pattern + if (nJumpOrder > nStartOrder) return TRUE; + if ((nJumpOrder < nStartOrder) || (nJumpRow >= PatternSize[nStartOrder]) + || (!Patterns[nStartOrder]) || (nStartRow >= 256) || (nJumpRow >= 256)) return FALSE; + // See if the pattern is being played backward + BYTE row_hist[256]; + memset(row_hist, 0, sizeof(row_hist)); + UINT nRows = PatternSize[nStartOrder], row = nJumpRow; + if (nRows > 256) nRows = 256; + row_hist[nStartRow] = TRUE; + while ((row < 256) && (!row_hist[row])) + { + if (row >= nRows) return TRUE; + row_hist[row] = TRUE; + MODCOMMAND *p = Patterns[nStartOrder] + row * m_nChannels; + row++; + int breakrow = -1, posjump = 0; + for (UINT i=0; i<m_nChannels; i++, p++) + { + if (p->command == CMD_POSITIONJUMP) + { + if (p->param < nStartOrder) return FALSE; + if (p->param > nStartOrder) return TRUE; + posjump = TRUE; + } else + if (p->command == CMD_PATTERNBREAK) + { + breakrow = p->param; + } + } + if (breakrow >= 0) + { + if (!posjump) return TRUE; + row = breakrow; + } + if (row >= nRows) return TRUE; + } + return FALSE; +} + + +////////////////////////////////////////////////////// +// Note/Period/Frequency functions + +UINT CSoundFile::GetNoteFromPeriod(UINT period) const +//--------------------------------------------------- +{ + if (!period) return 0; + if (m_nType & (MOD_TYPE_MED|MOD_TYPE_MOD|MOD_TYPE_MTM|MOD_TYPE_669|MOD_TYPE_OKT|MOD_TYPE_AMF0)) + { + period >>= 2; + for (UINT i=0; i<6*12; i++) + { + if (period >= ProTrackerPeriodTable[i]) + { + if ((period != ProTrackerPeriodTable[i]) && (i)) + { + UINT p1 = ProTrackerPeriodTable[i-1]; + UINT p2 = ProTrackerPeriodTable[i]; + if (p1 - period < (period - p2)) return i+36; + } + return i+1+36; + } + } + return 6*12+36; + } else + { + for (UINT i=1; i<NOTE_MAX; i++) + { + LONG n = GetPeriodFromNote(i, 0, 0); + if ((n > 0) && (n <= (LONG)period)) return i; + } + return NOTE_MAX; + } +} + + + +UINT CSoundFile::GetPeriodFromNote(UINT note, int nFineTune, UINT nC4Speed) const +//------------------------------------------------------------------------------- +{ + if ((!note) || (note > 0xF0)) return 0; + if (m_nType & (MOD_TYPE_IT|MOD_TYPE_S3M|MOD_TYPE_STM|MOD_TYPE_MDL|MOD_TYPE_ULT|MOD_TYPE_WAV + |MOD_TYPE_FAR|MOD_TYPE_DMF|MOD_TYPE_PTM|MOD_TYPE_AMS|MOD_TYPE_DBM|MOD_TYPE_AMF|MOD_TYPE_PSM)) + { + note--; + if (m_dwSongFlags & SONG_LINEARSLIDES) + { + return (FreqS3MTable[note % 12] << 5) >> (note / 12); + } else + { + if (!nC4Speed) nC4Speed = 8363; + return _muldiv(8363, (FreqS3MTable[note % 12] << 5), nC4Speed << (note / 12)); + } + } else + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if (note < 13) note = 13; + note -= 13; + if (m_dwSongFlags & SONG_LINEARSLIDES) + { + LONG l = ((NOTE_MAX - note) << 6) - (nFineTune / 2); + if (l < 1) l = 1; + return (UINT)l; + } else + { + int finetune = nFineTune; + UINT rnote = (note % 12) << 3; + UINT roct = note / 12; + int rfine = finetune / 16; + int i = rnote + rfine + 8; + if (i < 0) i = 0; + if (i >= 104) i = 103; + UINT per1 = XMPeriodTable[i]; + if ( finetune < 0 ) + { + rfine--; + finetune = -finetune; + } else rfine++; + i = rnote+rfine+8; + if (i < 0) i = 0; + if (i >= 104) i = 103; + UINT per2 = XMPeriodTable[i]; + rfine = finetune & 0x0F; + per1 *= 16-rfine; + per2 *= rfine; + return ((per1 + per2) << 1) >> roct; + } + } else + { + note--; + nFineTune = XM2MODFineTune(nFineTune); + if ((nFineTune) || (note < 36) || (note >= 36+6*12)) + return (ProTrackerTunedPeriods[nFineTune*12 + note % 12] << 5) >> (note / 12); + else + return (ProTrackerPeriodTable[note-36] << 2); + } +} + + +UINT CSoundFile::GetFreqFromPeriod(UINT period, UINT nC4Speed, int nPeriodFrac) const +//----------------------------------------------------------------------------------- +{ + if (!period) return 0; + if (m_nType & (MOD_TYPE_MED|MOD_TYPE_MOD|MOD_TYPE_MTM|MOD_TYPE_669|MOD_TYPE_OKT|MOD_TYPE_AMF0)) + { + return (3546895L*4) / period; + } else + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if (m_dwSongFlags & SONG_LINEARSLIDES) + return XMLinearTable[period % 768] >> (period / 768); + else + return 8363 * 1712L / period; + } else + { + if (m_dwSongFlags & SONG_LINEARSLIDES) + { + if (!nC4Speed) nC4Speed = 8363; + return _muldiv(nC4Speed, 1712L << 8, (period << 8)+nPeriodFrac); + } else + { + return _muldiv(8363, 1712L << 8, (period << 8)+nPeriodFrac); + } + } +} + + diff --git a/lib/libmodplug/src/sndfile.cpp b/lib/libmodplug/src/sndfile.cpp new file mode 100644 index 0000000000..88baf2ebca --- /dev/null +++ b/lib/libmodplug/src/sndfile.cpp @@ -0,0 +1,1885 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net>, + * Adam Goode <adam@evdebs.org> (endian and char fixes for PPC) +*/ + +#include <math.h> //for GCCFIX +#include <libmodplug/stdafx.h> +#include <libmodplug/sndfile.h> + +#define MMCMP_SUPPORT + +#ifdef MMCMP_SUPPORT +extern BOOL MMCMP_Unpack(LPCBYTE *ppMemFile, LPDWORD pdwMemLength); +#endif + +// External decompressors +extern void AMSUnpack(const char *psrc, UINT inputlen, char *pdest, UINT dmax, char packcharacter); +extern WORD MDLReadBits(DWORD &bitbuf, UINT &bitnum, LPBYTE &ibuf, CHAR n); +extern int DMFUnpack(LPBYTE psample, LPBYTE ibuf, LPBYTE ibufmax, UINT maxlen); +extern DWORD ITReadBits(DWORD &bitbuf, UINT &bitnum, LPBYTE &ibuf, CHAR n); +extern void ITUnpack8Bit(signed char *pSample, DWORD dwLen, LPBYTE lpMemFile, DWORD dwMemLength, BOOL b215); +extern void ITUnpack16Bit(signed char *pSample, DWORD dwLen, LPBYTE lpMemFile, DWORD dwMemLength, BOOL b215); + + +#define MAX_PACK_TABLES 3 + + +// Compression table +static const signed char UnpackTable[MAX_PACK_TABLES][16] = +//-------------------------------------------- +{ + // CPU-generated dynamic table + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + // u-Law table + {0, 1, 2, 4, 8, 16, 32, 64, + -1, -2, -4, -8, -16, -32, -48, -64}, + // Linear table + {0, 1, 2, 3, 5, 7, 12, 19, + -1, -2, -3, -5, -7, -12, -19, -31} +}; + + +////////////////////////////////////////////////////////// +// CSoundFile + +CSoundFile::CSoundFile() +//---------------------- +{ + m_nType = MOD_TYPE_NONE; + m_dwSongFlags = 0; + m_nChannels = 0; + m_nMixChannels = 0; + m_nSamples = 0; + m_nInstruments = 0; + m_nPatternNames = 0; + m_lpszPatternNames = NULL; + m_lpszSongComments = NULL; + m_nFreqFactor = m_nTempoFactor = 128; + m_nMasterVolume = 128; + m_nMinPeriod = 0x20; + m_nMaxPeriod = 0x7FFF; + m_nRepeatCount = 0; + memset(Chn, 0, sizeof(Chn)); + memset(ChnMix, 0, sizeof(ChnMix)); + memset(Ins, 0, sizeof(Ins)); + memset(ChnSettings, 0, sizeof(ChnSettings)); + memset(Headers, 0, sizeof(Headers)); + memset(Order, 0xFF, sizeof(Order)); + memset(Patterns, 0, sizeof(Patterns)); + memset(m_szNames, 0, sizeof(m_szNames)); + memset(m_MixPlugins, 0, sizeof(m_MixPlugins)); +} + + +CSoundFile::~CSoundFile() +//----------------------- +{ + Destroy(); +} + + +BOOL CSoundFile::Create(LPCBYTE lpStream, DWORD dwMemLength) +//---------------------------------------------------------- +{ + int i; + + m_nType = MOD_TYPE_NONE; + m_dwSongFlags = 0; + m_nChannels = 0; + m_nMixChannels = 0; + m_nSamples = 0; + m_nInstruments = 0; + m_nFreqFactor = m_nTempoFactor = 128; + m_nMasterVolume = 128; + m_nDefaultGlobalVolume = 256; + m_nGlobalVolume = 256; + m_nOldGlbVolSlide = 0; + m_nDefaultSpeed = 6; + m_nDefaultTempo = 125; + m_nPatternDelay = 0; + m_nFrameDelay = 0; + m_nNextRow = 0; + m_nRow = 0; + m_nPattern = 0; + m_nCurrentPattern = 0; + m_nNextPattern = 0; + m_nRestartPos = 0; + m_nMinPeriod = 16; + m_nMaxPeriod = 32767; + m_nSongPreAmp = 0x30; + m_nPatternNames = 0; + m_nMaxOrderPosition = 0; + m_lpszPatternNames = NULL; + m_lpszSongComments = NULL; + memset(Ins, 0, sizeof(Ins)); + memset(ChnMix, 0, sizeof(ChnMix)); + memset(Chn, 0, sizeof(Chn)); + memset(Headers, 0, sizeof(Headers)); + memset(Order, 0xFF, sizeof(Order)); + memset(Patterns, 0, sizeof(Patterns)); + memset(m_szNames, 0, sizeof(m_szNames)); + memset(m_MixPlugins, 0, sizeof(m_MixPlugins)); + ResetMidiCfg(); + for (UINT npt=0; npt<MAX_PATTERNS; npt++) PatternSize[npt] = 64; + for (UINT nch=0; nch<MAX_BASECHANNELS; nch++) + { + ChnSettings[nch].nPan = 128; + ChnSettings[nch].nVolume = 64; + ChnSettings[nch].dwFlags = 0; + ChnSettings[nch].szName[0] = 0; + } + if (lpStream) + { +#ifdef MMCMP_SUPPORT + BOOL bMMCmp = MMCMP_Unpack(&lpStream, &dwMemLength); +#endif + if ((!ReadXM(lpStream, dwMemLength)) + && (!ReadS3M(lpStream, dwMemLength)) + && (!ReadIT(lpStream, dwMemLength)) + && (!ReadWav(lpStream, dwMemLength)) +#ifndef MODPLUG_BASIC_SUPPORT +/* Sequencer File Format Support */ + && (!ReadABC(lpStream, dwMemLength)) + && (!ReadMID(lpStream, dwMemLength)) + && (!ReadPAT(lpStream, dwMemLength)) + && (!ReadSTM(lpStream, dwMemLength)) + && (!ReadMed(lpStream, dwMemLength)) + && (!ReadMTM(lpStream, dwMemLength)) + && (!ReadMDL(lpStream, dwMemLength)) + && (!ReadDBM(lpStream, dwMemLength)) + && (!Read669(lpStream, dwMemLength)) + && (!ReadFAR(lpStream, dwMemLength)) + && (!ReadAMS(lpStream, dwMemLength)) + && (!ReadOKT(lpStream, dwMemLength)) + && (!ReadPTM(lpStream, dwMemLength)) + && (!ReadUlt(lpStream, dwMemLength)) + && (!ReadDMF(lpStream, dwMemLength)) + && (!ReadDSM(lpStream, dwMemLength)) + && (!ReadUMX(lpStream, dwMemLength)) + && (!ReadAMF(lpStream, dwMemLength)) + && (!ReadPSM(lpStream, dwMemLength)) + && (!ReadMT2(lpStream, dwMemLength)) +#endif // MODPLUG_BASIC_SUPPORT + && (!ReadMod(lpStream, dwMemLength))) m_nType = MOD_TYPE_NONE; +#ifdef MMCMP_SUPPORT + if (bMMCmp) + { + GlobalFreePtr(lpStream); + lpStream = NULL; + } +#endif + } + // Adjust song names + for (i=0; i<MAX_SAMPLES; i++) + { + LPSTR p = m_szNames[i]; + int j = 31; + p[j] = 0; + while ((j>=0) && (p[j]<=' ')) p[j--] = 0; + while (j>=0) + { + if (((BYTE)p[j]) < ' ') p[j] = ' '; + j--; + } + } + // Adjust channels + for (i=0; i<MAX_BASECHANNELS; i++) + { + if (ChnSettings[i].nVolume > 64) ChnSettings[i].nVolume = 64; + if (ChnSettings[i].nPan > 256) ChnSettings[i].nPan = 128; + Chn[i].nPan = ChnSettings[i].nPan; + Chn[i].nGlobalVol = ChnSettings[i].nVolume; + Chn[i].dwFlags = ChnSettings[i].dwFlags; + Chn[i].nVolume = 256; + Chn[i].nCutOff = 0x7F; + } + // Checking instruments + MODINSTRUMENT *pins = Ins; + + for (i=0; i<MAX_INSTRUMENTS; i++, pins++) + { + if (pins->pSample) + { + if (pins->nLoopEnd > pins->nLength) pins->nLoopEnd = pins->nLength; + if (pins->nLoopStart + 3 >= pins->nLoopEnd) + { + pins->nLoopStart = 0; + pins->nLoopEnd = 0; + } + if (pins->nSustainEnd > pins->nLength) pins->nSustainEnd = pins->nLength; + if (pins->nSustainStart + 3 >= pins->nSustainEnd) + { + pins->nSustainStart = 0; + pins->nSustainEnd = 0; + } + } else + { + pins->nLength = 0; + pins->nLoopStart = 0; + pins->nLoopEnd = 0; + pins->nSustainStart = 0; + pins->nSustainEnd = 0; + } + if (!pins->nLoopEnd) pins->uFlags &= ~CHN_LOOP; + if (!pins->nSustainEnd) pins->uFlags &= ~CHN_SUSTAINLOOP; + if (pins->nGlobalVol > 64) pins->nGlobalVol = 64; + } + // Check invalid instruments + while ((m_nInstruments > 0) && (!Headers[m_nInstruments])) + m_nInstruments--; + // Set default values + if (m_nSongPreAmp < 0x20) m_nSongPreAmp = 0x20; + if (m_nDefaultTempo < 32) m_nDefaultTempo = 125; + if (!m_nDefaultSpeed) m_nDefaultSpeed = 6; + m_nMusicSpeed = m_nDefaultSpeed; + m_nMusicTempo = m_nDefaultTempo; + m_nGlobalVolume = m_nDefaultGlobalVolume; + m_nNextPattern = 0; + m_nCurrentPattern = 0; + m_nPattern = 0; + m_nBufferCount = 0; + m_nTickCount = m_nMusicSpeed; + m_nNextRow = 0; + m_nRow = 0; + if ((m_nRestartPos >= MAX_ORDERS) || (Order[m_nRestartPos] >= MAX_PATTERNS)) m_nRestartPos = 0; + // Load plugins + if (gpMixPluginCreateProc) + { + for (UINT iPlug=0; iPlug<MAX_MIXPLUGINS; iPlug++) + { + if ((m_MixPlugins[iPlug].Info.dwPluginId1) + || (m_MixPlugins[iPlug].Info.dwPluginId2)) + { + gpMixPluginCreateProc(&m_MixPlugins[iPlug]); + if (m_MixPlugins[iPlug].pMixPlugin) + { + m_MixPlugins[iPlug].pMixPlugin->RestoreAllParameters(); + } + } + } + } + if (m_nType) + { + UINT maxpreamp = 0x10+(m_nChannels*8); + if (maxpreamp > 100) maxpreamp = 100; + if (m_nSongPreAmp > maxpreamp) m_nSongPreAmp = maxpreamp; + return TRUE; + } + return FALSE; +} + + +BOOL CSoundFile::Destroy() + +//------------------------ +{ + int i; + for (i=0; i<MAX_PATTERNS; i++) if (Patterns[i]) + { + FreePattern(Patterns[i]); + Patterns[i] = NULL; + } + m_nPatternNames = 0; + if (m_lpszPatternNames) + { + delete m_lpszPatternNames; + m_lpszPatternNames = NULL; + } + if (m_lpszSongComments) + { + delete m_lpszSongComments; + m_lpszSongComments = NULL; + } + for (i=1; i<MAX_SAMPLES; i++) + { + MODINSTRUMENT *pins = &Ins[i]; + if (pins->pSample) + { + FreeSample(pins->pSample); + pins->pSample = NULL; + } + } + for (i=0; i<MAX_INSTRUMENTS; i++) + { + if (Headers[i]) + { + delete Headers[i]; + Headers[i] = NULL; + } + } + for (i=0; i<MAX_MIXPLUGINS; i++) + { + if ((m_MixPlugins[i].nPluginDataSize) && (m_MixPlugins[i].pPluginData)) + { + m_MixPlugins[i].nPluginDataSize = 0; + delete [] (signed char*)m_MixPlugins[i].pPluginData; + m_MixPlugins[i].pPluginData = NULL; + } + m_MixPlugins[i].pMixState = NULL; + if (m_MixPlugins[i].pMixPlugin) + { + m_MixPlugins[i].pMixPlugin->Release(); + m_MixPlugins[i].pMixPlugin = NULL; + } + } + m_nType = MOD_TYPE_NONE; + m_nChannels = m_nSamples = m_nInstruments = 0; + return TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +// Memory Allocation + +MODCOMMAND *CSoundFile::AllocatePattern(UINT rows, UINT nchns) +//------------------------------------------------------------ +{ + MODCOMMAND *p = new MODCOMMAND[rows*nchns]; + if (p) memset(p, 0, rows*nchns*sizeof(MODCOMMAND)); + return p; +} + + +void CSoundFile::FreePattern(LPVOID pat) +//-------------------------------------- +{ + if (pat) delete [] (signed char*)pat; +} + + +signed char* CSoundFile::AllocateSample(UINT nbytes) +//------------------------------------------- +{ + signed char * p = (signed char *)GlobalAllocPtr(GHND, (nbytes+39) & ~7); + if (p) p += 16; + return p; +} + + +void CSoundFile::FreeSample(LPVOID p) +//----------------------------------- +{ + if (p) + { + GlobalFreePtr(((LPSTR)p)-16); + } +} + + +////////////////////////////////////////////////////////////////////////// +// Misc functions + +void CSoundFile::ResetMidiCfg() +//----------------------------- +{ + memset(&m_MidiCfg, 0, sizeof(m_MidiCfg)); + lstrcpy(&m_MidiCfg.szMidiGlb[MIDIOUT_START*32], "FF"); + lstrcpy(&m_MidiCfg.szMidiGlb[MIDIOUT_STOP*32], "FC"); + lstrcpy(&m_MidiCfg.szMidiGlb[MIDIOUT_NOTEON*32], "9c n v"); + lstrcpy(&m_MidiCfg.szMidiGlb[MIDIOUT_NOTEOFF*32], "9c n 0"); + lstrcpy(&m_MidiCfg.szMidiGlb[MIDIOUT_PROGRAM*32], "Cc p"); + lstrcpy(&m_MidiCfg.szMidiSFXExt[0], "F0F000z"); + for (int iz=0; iz<16; iz++) wsprintf(&m_MidiCfg.szMidiZXXExt[iz*32], "F0F001%02X", iz*8); +} + + +UINT CSoundFile::GetNumChannels() const +//------------------------------------- +{ + UINT n = 0; + for (UINT i=0; i<m_nChannels; i++) if (ChnSettings[i].nVolume) n++; + return n; +} + + +UINT CSoundFile::GetSongComments(LPSTR s, UINT len, UINT linesize) +//---------------------------------------------------------------- +{ + LPCSTR p = m_lpszSongComments; + if (!p) return 0; + UINT i = 2, ln=0; + if ((len) && (s)) s[0] = '\x0D'; + if ((len > 1) && (s)) s[1] = '\x0A'; + while ((*p) && (i+2 < len)) + { + BYTE c = (BYTE)*p++; + if ((c == 0x0D) || ((c == ' ') && (ln >= linesize))) + { if (s) { s[i++] = '\x0D'; s[i++] = '\x0A'; } else i+= 2; ln=0; } + else + if (c >= 0x20) { if (s) s[i++] = c; else i++; ln++; } + } + if (s) s[i] = 0; + return i; +} + + +UINT CSoundFile::GetRawSongComments(LPSTR s, UINT len, UINT linesize) +//------------------------------------------------------------------- +{ + LPCSTR p = m_lpszSongComments; + if (!p) return 0; + UINT i = 0, ln=0; + while ((*p) && (i < len-1)) + { + BYTE c = (BYTE)*p++; + if ((c == 0x0D) || (c == 0x0A)) + { + if (ln) + { + while (ln < linesize) { if (s) s[i] = ' '; i++; ln++; } + ln = 0; + } + } else + if ((c == ' ') && (!ln)) + { + UINT k=0; + while ((p[k]) && (p[k] >= ' ')) k++; + if (k <= linesize) + { + if (s) s[i] = ' '; + i++; + ln++; + } + } else + { + if (s) s[i] = c; + i++; + ln++; + if (ln == linesize) ln = 0; + } + } + if (ln) + { + while ((ln < linesize) && (i < len)) + { + if (s) s[i] = ' '; + i++; + ln++; + } + } + if (s) s[i] = 0; + return i; +} + + +BOOL CSoundFile::SetWaveConfig(UINT nRate,UINT nBits,UINT nChannels,BOOL bMMX) +//---------------------------------------------------------------------------- +{ + BOOL bReset = FALSE; + DWORD d = gdwSoundSetup & ~SNDMIX_ENABLEMMX; + if (bMMX) d |= SNDMIX_ENABLEMMX; + if ((gdwMixingFreq != nRate) || (gnBitsPerSample != nBits) || (gnChannels != nChannels) || (d != gdwSoundSetup)) bReset = TRUE; + gnChannels = nChannels; + gdwSoundSetup = d; + gdwMixingFreq = nRate; + gnBitsPerSample = nBits; + InitPlayer(bReset); + return TRUE; +} + + +BOOL CSoundFile::SetResamplingMode(UINT nMode) +//-------------------------------------------- +{ + DWORD d = gdwSoundSetup & ~(SNDMIX_NORESAMPLING|SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); + switch(nMode) + { + case SRCMODE_NEAREST: d |= SNDMIX_NORESAMPLING; break; + case SRCMODE_LINEAR: break; + case SRCMODE_SPLINE: d |= SNDMIX_HQRESAMPLER; break; + case SRCMODE_POLYPHASE: d |= (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE); break; + default: + return FALSE; + } + gdwSoundSetup = d; + return TRUE; +} + + +BOOL CSoundFile::SetMasterVolume(UINT nVol, BOOL bAdjustAGC) +//---------------------------------------------------------- +{ + if (nVol < 1) nVol = 1; + if (nVol > 0x200) nVol = 0x200; // x4 maximum + if ((nVol < m_nMasterVolume) && (nVol) && (gdwSoundSetup & SNDMIX_AGC) && (bAdjustAGC)) + { + gnAGC = gnAGC * m_nMasterVolume / nVol; + if (gnAGC > AGC_UNITY) gnAGC = AGC_UNITY; + } + m_nMasterVolume = nVol; + return TRUE; +} + + +void CSoundFile::SetAGC(BOOL b) +//----------------------------- +{ + if (b) + { + if (!(gdwSoundSetup & SNDMIX_AGC)) + { + gdwSoundSetup |= SNDMIX_AGC; + gnAGC = AGC_UNITY; + } + } else gdwSoundSetup &= ~SNDMIX_AGC; +} + + +UINT CSoundFile::GetNumPatterns() const +//------------------------------------- +{ + UINT i = 0; + while ((i < MAX_ORDERS) && (Order[i] < 0xFF)) i++; + return i; +} + + +UINT CSoundFile::GetNumInstruments() const +//---------------------------------------- +{ + UINT n=0; + for (UINT i=0; i<MAX_INSTRUMENTS; i++) if (Ins[i].pSample) n++; + return n; +} + + +UINT CSoundFile::GetMaxPosition() const +//------------------------------------- +{ + UINT max = 0; + UINT i = 0; + + while ((i < MAX_ORDERS) && (Order[i] != 0xFF)) + { + if (Order[i] < MAX_PATTERNS) max += PatternSize[Order[i]]; + i++; + } + return max; +} + + +UINT CSoundFile::GetCurrentPos() const +//------------------------------------ +{ + UINT pos = 0; + + for (UINT i=0; i<m_nCurrentPattern; i++) if (Order[i] < MAX_PATTERNS) + pos += PatternSize[Order[i]]; + return pos + m_nRow; +} + + +void CSoundFile::SetCurrentPos(UINT nPos) +//--------------------------------------- +{ + UINT i, nPattern; + + for (i=0; i<MAX_CHANNELS; i++) + { + Chn[i].nNote = Chn[i].nNewNote = Chn[i].nNewIns = 0; + Chn[i].pInstrument = NULL; + Chn[i].pHeader = NULL; + Chn[i].nPortamentoDest = 0; + Chn[i].nCommand = 0; + Chn[i].nPatternLoopCount = 0; + Chn[i].nPatternLoop = 0; + Chn[i].nFadeOutVol = 0; + Chn[i].dwFlags |= CHN_KEYOFF|CHN_NOTEFADE; + Chn[i].nTremorCount = 0; + } + if (!nPos) + { + for (i=0; i<MAX_CHANNELS; i++) + { + Chn[i].nPeriod = 0; + Chn[i].nPos = Chn[i].nLength = 0; + Chn[i].nLoopStart = 0; + Chn[i].nLoopEnd = 0; + Chn[i].nROfs = Chn[i].nLOfs = 0; + Chn[i].pSample = NULL; + Chn[i].pInstrument = NULL; + Chn[i].pHeader = NULL; + Chn[i].nCutOff = 0x7F; + Chn[i].nResonance = 0; + Chn[i].nLeftVol = Chn[i].nRightVol = 0; + Chn[i].nNewLeftVol = Chn[i].nNewRightVol = 0; + Chn[i].nLeftRamp = Chn[i].nRightRamp = 0; + Chn[i].nVolume = 256; + if (i < MAX_BASECHANNELS) + { + Chn[i].dwFlags = ChnSettings[i].dwFlags; + Chn[i].nPan = ChnSettings[i].nPan; + Chn[i].nGlobalVol = ChnSettings[i].nVolume; + } else + { + Chn[i].dwFlags = 0; + Chn[i].nPan = 128; + Chn[i].nGlobalVol = 64; + } + } + m_nGlobalVolume = m_nDefaultGlobalVolume; + m_nMusicSpeed = m_nDefaultSpeed; + m_nMusicTempo = m_nDefaultTempo; + } + m_dwSongFlags &= ~(SONG_PATTERNLOOP|SONG_CPUVERYHIGH|SONG_FADINGSONG|SONG_ENDREACHED|SONG_GLOBALFADE); + for (nPattern = 0; nPattern < MAX_ORDERS; nPattern++) + { + UINT ord = Order[nPattern]; + if (ord == 0xFE) continue; + if (ord == 0xFF) break; + if (ord < MAX_PATTERNS) + { + if (nPos < (UINT)PatternSize[ord]) break; + nPos -= PatternSize[ord]; + } + } + // Buggy position ? + if ((nPattern >= MAX_ORDERS) + || (Order[nPattern] >= MAX_PATTERNS) + || (nPos >= PatternSize[Order[nPattern]])) + { + nPos = 0; + nPattern = 0; + } + UINT nRow = nPos; + if ((nRow) && (Order[nPattern] < MAX_PATTERNS)) + { + MODCOMMAND *p = Patterns[Order[nPattern]]; + if ((p) && (nRow < PatternSize[Order[nPattern]])) + { + BOOL bOk = FALSE; + while ((!bOk) && (nRow > 0)) + { + UINT n = nRow * m_nChannels; + for (UINT k=0; k<m_nChannels; k++, n++) + { + if (p[n].note) + { + bOk = TRUE; + break; + } + } + if (!bOk) nRow--; + } + } + } + m_nNextPattern = nPattern; + m_nNextRow = nRow; + m_nTickCount = m_nMusicSpeed; + m_nBufferCount = 0; + m_nPatternDelay = 0; + m_nFrameDelay = 0; +} + + +void CSoundFile::SetCurrentOrder(UINT nPos) +//----------------------------------------- +{ + while ((nPos < MAX_ORDERS) && (Order[nPos] == 0xFE)) nPos++; + if ((nPos >= MAX_ORDERS) || (Order[nPos] >= MAX_PATTERNS)) return; + for (UINT j=0; j<MAX_CHANNELS; j++) + { + Chn[j].nPeriod = 0; + Chn[j].nNote = 0; + Chn[j].nPortamentoDest = 0; + Chn[j].nCommand = 0; + Chn[j].nPatternLoopCount = 0; + Chn[j].nPatternLoop = 0; + Chn[j].nTremorCount = 0; + } + if (!nPos) + { + SetCurrentPos(0); + } else + { + m_nNextPattern = nPos; + m_nRow = m_nNextRow = 0; + m_nPattern = 0; + m_nTickCount = m_nMusicSpeed; + m_nBufferCount = 0; + m_nTotalCount = 0; + m_nPatternDelay = 0; + m_nFrameDelay = 0; + } + m_dwSongFlags &= ~(SONG_PATTERNLOOP|SONG_CPUVERYHIGH|SONG_FADINGSONG|SONG_ENDREACHED|SONG_GLOBALFADE); +} + + +void CSoundFile::ResetChannels() +//------------------------------ +{ + m_dwSongFlags &= ~(SONG_CPUVERYHIGH|SONG_FADINGSONG|SONG_ENDREACHED|SONG_GLOBALFADE); + m_nBufferCount = 0; + for (UINT i=0; i<MAX_CHANNELS; i++) + { + Chn[i].nROfs = Chn[i].nLOfs = 0; + } +} + + +void CSoundFile::LoopPattern(int nPat, int nRow) +//---------------------------------------------- +{ + if ((nPat < 0) || (nPat >= MAX_PATTERNS) || (!Patterns[nPat])) + { + m_dwSongFlags &= ~SONG_PATTERNLOOP; + } else + { + if ((nRow < 0) || (nRow >= PatternSize[nPat])) nRow = 0; + m_nPattern = nPat; + m_nRow = m_nNextRow = nRow; + m_nTickCount = m_nMusicSpeed; + m_nPatternDelay = 0; + m_nFrameDelay = 0; + m_nBufferCount = 0; + m_dwSongFlags |= SONG_PATTERNLOOP; + } +} + + +UINT CSoundFile::GetBestSaveFormat() const +//---------------------------------------- +{ + if ((!m_nSamples) || (!m_nChannels)) return MOD_TYPE_NONE; + if (!m_nType) return MOD_TYPE_NONE; + if (m_nType & (MOD_TYPE_MOD|MOD_TYPE_OKT)) + return MOD_TYPE_MOD; + if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_STM|MOD_TYPE_ULT|MOD_TYPE_FAR|MOD_TYPE_PTM)) + return MOD_TYPE_S3M; + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MED|MOD_TYPE_MTM|MOD_TYPE_MT2)) + return MOD_TYPE_XM; + return MOD_TYPE_IT; +} + + +UINT CSoundFile::GetSaveFormats() const +//------------------------------------- +{ + UINT n = 0; + if ((!m_nSamples) || (!m_nChannels) || (m_nType == MOD_TYPE_NONE)) return 0; + switch(m_nType) + { + case MOD_TYPE_MOD: n = MOD_TYPE_MOD; + case MOD_TYPE_S3M: n = MOD_TYPE_S3M; + } + n |= MOD_TYPE_XM | MOD_TYPE_IT; + if (!m_nInstruments) + { + if (m_nSamples < 32) n |= MOD_TYPE_MOD; + n |= MOD_TYPE_S3M; + } + return n; +} + + +UINT CSoundFile::GetSampleName(UINT nSample,LPSTR s) const +//-------------------------------------------------------- +{ + char sztmp[40] = ""; // changed from CHAR + memcpy(sztmp, m_szNames[nSample],32); + sztmp[31] = 0; + if (s) strcpy(s, sztmp); + return strlen(sztmp); +} + + +UINT CSoundFile::GetInstrumentName(UINT nInstr,LPSTR s) const +//----------------------------------------------------------- +{ + char sztmp[40] = ""; // changed from CHAR + if ((nInstr >= MAX_INSTRUMENTS) || (!Headers[nInstr])) + { + if (s) *s = 0; + return 0; + } + INSTRUMENTHEADER *penv = Headers[nInstr]; + memcpy(sztmp, penv->name, 32); + sztmp[31] = 0; + if (s) strcpy(s, sztmp); + return strlen(sztmp); +} + + +#ifndef NO_PACKING +UINT CSoundFile::PackSample(int &sample, int next) +//------------------------------------------------ +{ + UINT i = 0; + int delta = next - sample; + if (delta >= 0) + { + for (i=0; i<7; i++) if (delta <= (int)CompressionTable[i+1]) break; + } else + { + for (i=8; i<15; i++) if (delta >= (int)CompressionTable[i+1]) break; + } + sample += (int)CompressionTable[i]; + return i; +} + + +BOOL CSoundFile::CanPackSample(LPSTR pSample, UINT nLen, UINT nPacking, BYTE *result) +//----------------------------------------------------------------------------------- +{ + int pos, old, oldpos, besttable = 0; + DWORD dwErr, dwTotal, dwResult; + int i,j; + + if (result) *result = 0; + if ((!pSample) || (nLen < 1024)) return FALSE; + // Try packing with different tables + dwResult = 0; + for (j=1; j<MAX_PACK_TABLES; j++) + { + memcpy(CompressionTable, UnpackTable[j], 16); + dwErr = 0; + dwTotal = 1; + old = pos = oldpos = 0; + for (i=0; i<(int)nLen; i++) + { + int s = (int)pSample[i]; + PackSample(pos, s); + dwErr += abs(pos - oldpos); + dwTotal += abs(s - old); + old = s; + oldpos = pos; + } + dwErr = _muldiv(dwErr, 100, dwTotal); + if (dwErr >= dwResult) + { + dwResult = dwErr; + besttable = j; + } + } + memcpy(CompressionTable, UnpackTable[besttable], 16); + if (result) + { + if (dwResult > 100) *result = 100; else *result = (BYTE)dwResult; + } + return (dwResult >= nPacking) ? TRUE : FALSE; +} +#endif // NO_PACKING + +#ifndef MODPLUG_NO_FILESAVE + +UINT CSoundFile::WriteSample(FILE *f, MODINSTRUMENT *pins, UINT nFlags, UINT nMaxLen) +//----------------------------------------------------------------------------------- +{ + UINT len = 0, bufcount; + signed char buffer[4096]; + signed char *pSample = (signed char *)pins->pSample; + UINT nLen = pins->nLength; + + if ((nMaxLen) && (nLen > nMaxLen)) nLen = nMaxLen; + if ((!pSample) || (f == NULL) || (!nLen)) return 0; + switch(nFlags) + { +#ifndef NO_PACKING + // 3: 4-bit ADPCM data + case RS_ADPCM4: + { + int pos; + len = (nLen + 1) / 2; + fwrite(CompressionTable, 16, 1, f); + bufcount = 0; + pos = 0; + for (UINT j=0; j<len; j++) + { + BYTE b; + // Sample #1 + b = PackSample(pos, (int)pSample[j*2]); + // Sample #2 + b |= PackSample(pos, (int)pSample[j*2+1]) << 4; + buffer[bufcount++] = (signed char)b; + if (bufcount >= sizeof(buffer)) + { + fwrite(buffer, 1, bufcount, f); + bufcount = 0; + } + } + if (bufcount) fwrite(buffer, 1, bufcount, f); + len += 16; + } + break; +#endif // NO_PACKING + + // 16-bit samples + case RS_PCM16U: + case RS_PCM16D: + case RS_PCM16S: + { + int16_t *p = (int16_t *)pSample; + int s_old = 0, s_ofs; + len = nLen * 2; + bufcount = 0; + s_ofs = (nFlags == RS_PCM16U) ? 0x8000 : 0; + for (UINT j=0; j<nLen; j++) + { + int s_new = *p; + p++; + if (pins->uFlags & CHN_STEREO) + { + s_new = (s_new + (*p) + 1) >> 1; + p++; + } + if (nFlags == RS_PCM16D) + { + int16_t temp = bswapLE16((int16_t)(s_new - s_old)); + *((int16_t*)(&buffer[bufcount])) = temp; + s_old = s_new; + } else + { + int16_t temp = bswapLE16((int16_t)(s_new + s_ofs)); + *((int16_t *)(&buffer[bufcount])) = temp; + } + bufcount += 2; + if (bufcount >= sizeof(buffer) - 1) + { + fwrite(buffer, 1, bufcount, f); + bufcount = 0; + } + } + if (bufcount) fwrite(buffer, 1, bufcount, f); + } + break; + + + // 8-bit Stereo samples (not interleaved) + case RS_STPCM8S: + case RS_STPCM8U: + case RS_STPCM8D: + { + int s_ofs = (nFlags == RS_STPCM8U) ? 0x80 : 0; + for (UINT iCh=0; iCh<2; iCh++) + { + signed char *p = pSample + iCh; + int s_old = 0; + + bufcount = 0; + for (UINT j=0; j<nLen; j++) + { + int s_new = *p; + p += 2; + if (nFlags == RS_STPCM8D) + { + buffer[bufcount++] = (signed char)(s_new - s_old); + s_old = s_new; + } else + { + buffer[bufcount++] = (signed char)(s_new + s_ofs); + } + if (bufcount >= sizeof(buffer)) + { + fwrite(buffer, 1, bufcount, f); + bufcount = 0; + } + } + if (bufcount) fwrite(buffer, 1, bufcount, f); + } + } + len = nLen * 2; + break; + + // 16-bit Stereo samples (not interleaved) + case RS_STPCM16S: + case RS_STPCM16U: + case RS_STPCM16D: + { + int s_ofs = (nFlags == RS_STPCM16U) ? 0x8000 : 0; + for (UINT iCh=0; iCh<2; iCh++) + { + int16_t *p = ((int16_t *)pSample) + iCh; + int s_old = 0; + + bufcount = 0; + for (UINT j=0; j<nLen; j++) + { + int s_new = *p; + p += 2; + if (nFlags == RS_STPCM16D) + { + int16_t temp = bswapLE16((int16_t)(s_new - s_old)); + *((int16_t *)(&buffer[bufcount])) = temp; + s_old = s_new; + } else + { + int16_t temp = bswapLE16((int16_t)(s_new - s_ofs)); + *((int16_t*)(&buffer[bufcount])) = temp; + } + bufcount += 2; + if (bufcount >= sizeof(buffer)) + { + fwrite(buffer, 1, bufcount, f); + bufcount = 0; + } + } + if (bufcount) fwrite(buffer, 1, bufcount, f); + } + } + len = nLen*4; + break; + + // Stereo signed interleaved + case RS_STIPCM8S: + case RS_STIPCM16S: + len = nLen * 2; + if (nFlags == RS_STIPCM16S) len *= 2; + fwrite(pSample, 1, len, f); + break; + + // Default: assume 8-bit PCM data + default: + len = nLen; + bufcount = 0; + { + signed char *p = pSample; + int sinc = (pins->uFlags & CHN_16BIT) ? 2 : 1; + int s_old = 0, s_ofs = (nFlags == RS_PCM8U) ? 0x80 : 0; + if (pins->uFlags & CHN_16BIT) p++; + for (UINT j=0; j<len; j++) + { + int s_new = (signed char)(*p); + p += sinc; + if (pins->uFlags & CHN_STEREO) + { + s_new = (s_new + ((int)*p) + 1) >> 1; + p += sinc; + } + if (nFlags == RS_PCM8D) + { + buffer[bufcount++] = (signed char)(s_new - s_old); + s_old = s_new; + } else + { + buffer[bufcount++] = (signed char)(s_new + s_ofs); + } + if (bufcount >= sizeof(buffer)) + { + fwrite(buffer, 1, bufcount, f); + bufcount = 0; + } + } + if (bufcount) fwrite(buffer, 1, bufcount, f); + } + } + return len; +} + +#endif // MODPLUG_NO_FILESAVE + + +// Flags: +// 0 = signed 8-bit PCM data (default) +// 1 = unsigned 8-bit PCM data +// 2 = 8-bit ADPCM data with linear table +// 3 = 4-bit ADPCM data +// 4 = 16-bit ADPCM data with linear table +// 5 = signed 16-bit PCM data +// 6 = unsigned 16-bit PCM data + + +UINT CSoundFile::ReadSample(MODINSTRUMENT *pIns, UINT nFlags, LPCSTR lpMemFile, DWORD dwMemLength) +//------------------------------------------------------------------------------ +{ + UINT len = 0, mem = pIns->nLength+6; + + // Disable >2Gb samples,(preventing buffer overflow in AllocateSample) + if ((!pIns) || ((int)pIns->nLength < 4) || (!lpMemFile)) return 0; + if (pIns->nLength > MAX_SAMPLE_LENGTH) pIns->nLength = MAX_SAMPLE_LENGTH; + pIns->uFlags &= ~(CHN_16BIT|CHN_STEREO); + if (nFlags & RSF_16BIT) + { + mem *= 2; + pIns->uFlags |= CHN_16BIT; + } + if (nFlags & RSF_STEREO) + { + mem *= 2; + pIns->uFlags |= CHN_STEREO; + } + if ((pIns->pSample = AllocateSample(mem)) == NULL) + { + pIns->nLength = 0; + return 0; + } + switch(nFlags) + { + // 1: 8-bit unsigned PCM data + case RS_PCM8U: + { + len = pIns->nLength; + if (len > dwMemLength) len = pIns->nLength = dwMemLength; + signed char *pSample = pIns->pSample; + for (UINT j=0; j<len; j++) pSample[j] = (signed char)(lpMemFile[j] - 0x80); + } + break; + + // 2: 8-bit ADPCM data with linear table + case RS_PCM8D: + { + len = pIns->nLength; + if (len > dwMemLength) break; + signed char *pSample = pIns->pSample; + const signed char *p = (const signed char *)lpMemFile; + int delta = 0; + + for (UINT j=0; j<len; j++) + { + delta += p[j]; + *pSample++ = (signed char)delta; + } + } + break; + + // 3: 4-bit ADPCM data + case RS_ADPCM4: + { + len = (pIns->nLength + 1) / 2; + if (len > dwMemLength - 16) break; + memcpy(CompressionTable, lpMemFile, 16); + lpMemFile += 16; + signed char *pSample = pIns->pSample; + signed char delta = 0; + for (UINT j=0; j<len; j++) + { + BYTE b0 = (BYTE)lpMemFile[j]; + BYTE b1 = (BYTE)(lpMemFile[j] >> 4); + delta = (signed char)GetDeltaValue((int)delta, b0); + pSample[0] = delta; + delta = (signed char)GetDeltaValue((int)delta, b1); + pSample[1] = delta; + pSample += 2; + } + len += 16; + } + break; + + // 4: 16-bit ADPCM data with linear table + case RS_PCM16D: + { + len = pIns->nLength * 2; + if (len > dwMemLength) break; + int16_t *pSample = (int16_t *)pIns->pSample; + int16_t *p = (int16_t *)lpMemFile; + int delta16 = 0; + for (UINT j=0; j<len; j+=2) + { + delta16 += bswapLE16(*p++); + *pSample++ = (int16_t )delta16; + } + } + break; + + // 5: 16-bit signed PCM data + case RS_PCM16S: + { + len = pIns->nLength * 2; + if (len <= dwMemLength) memcpy(pIns->pSample, lpMemFile, len); + int16_t *pSample = (int16_t *)pIns->pSample; + for (UINT j=0; j<len; j+=2) + { + int16_t rawSample = *pSample; + *pSample++ = bswapLE16(rawSample); + } + } + break; + + // 16-bit signed mono PCM motorola byte order + case RS_PCM16M: + len = pIns->nLength * 2; + if (len > dwMemLength) len = dwMemLength & ~1; + if (len > 1) + { + signed char *pSample = (signed char *)pIns->pSample; + signed char *pSrc = (signed char *)lpMemFile; + for (UINT j=0; j<len; j+=2) + { + // pSample[j] = pSrc[j+1]; + // pSample[j+1] = pSrc[j]; + *((uint16_t *)(pSample+j)) = bswapBE16(*((uint16_t *)(pSrc+j))); + } + } + break; + + // 6: 16-bit unsigned PCM data + case RS_PCM16U: + { + len = pIns->nLength * 2; + if (len > dwMemLength) break; + int16_t *pSample = (int16_t *)pIns->pSample; + int16_t *pSrc = (int16_t *)lpMemFile; + for (UINT j=0; j<len; j+=2) *pSample++ = bswapLE16(*(pSrc++)) - 0x8000; + } + break; + + // 16-bit signed stereo big endian + case RS_STPCM16M: + len = pIns->nLength * 2; + if (len*2 <= dwMemLength) + { + signed char *pSample = (signed char *)pIns->pSample; + signed char *pSrc = (signed char *)lpMemFile; + for (UINT j=0; j<len; j+=2) + { + // pSample[j*2] = pSrc[j+1]; + // pSample[j*2+1] = pSrc[j]; + // pSample[j*2+2] = pSrc[j+1+len]; + // pSample[j*2+3] = pSrc[j+len]; + *((uint16_t *)(pSample+j*2)) = bswapBE16(*((uint16_t *)(pSrc+j))); + *((uint16_t *)(pSample+j*2+2)) = bswapBE16(*((uint16_t *)(pSrc+j+len))); + } + len *= 2; + } + break; + + // 8-bit stereo samples + case RS_STPCM8S: + case RS_STPCM8U: + case RS_STPCM8D: + { + int iadd_l = 0, iadd_r = 0; + if (nFlags == RS_STPCM8U) { iadd_l = iadd_r = -128; } + len = pIns->nLength; + signed char *psrc = (signed char *)lpMemFile; + signed char *pSample = (signed char *)pIns->pSample; + if (len*2 > dwMemLength) break; + for (UINT j=0; j<len; j++) + { + pSample[j*2] = (signed char)(psrc[0] + iadd_l); + pSample[j*2+1] = (signed char)(psrc[len] + iadd_r); + psrc++; + if (nFlags == RS_STPCM8D) + { + iadd_l = pSample[j*2]; + iadd_r = pSample[j*2+1]; + } + } + len *= 2; + } + break; + + // 16-bit stereo samples + case RS_STPCM16S: + case RS_STPCM16U: + case RS_STPCM16D: + { + int iadd_l = 0, iadd_r = 0; + if (nFlags == RS_STPCM16U) { iadd_l = iadd_r = -0x8000; } + len = pIns->nLength; + int16_t *psrc = (int16_t *)lpMemFile; + int16_t *pSample = (int16_t *)pIns->pSample; + if (len*4 > dwMemLength) break; + for (UINT j=0; j<len; j++) + { + pSample[j*2] = (int16_t) (bswapLE16(psrc[0]) + iadd_l); + pSample[j*2+1] = (int16_t) (bswapLE16(psrc[len]) + iadd_r); + psrc++; + if (nFlags == RS_STPCM16D) + { + iadd_l = pSample[j*2]; + iadd_r = pSample[j*2+1]; + } + } + len *= 4; + } + break; + + // IT 2.14 compressed samples + case RS_IT2148: + case RS_IT21416: + case RS_IT2158: + case RS_IT21516: + len = dwMemLength; + if (len < 4) break; + if ((nFlags == RS_IT2148) || (nFlags == RS_IT2158)) + ITUnpack8Bit(pIns->pSample, pIns->nLength, (LPBYTE)lpMemFile, dwMemLength, (nFlags == RS_IT2158)); + else + ITUnpack16Bit(pIns->pSample, pIns->nLength, (LPBYTE)lpMemFile, dwMemLength, (nFlags == RS_IT21516)); + break; + +#ifndef MODPLUG_BASIC_SUPPORT +#ifndef MODPLUG_FASTSOUNDLIB + // 8-bit interleaved stereo samples + case RS_STIPCM8S: + case RS_STIPCM8U: + { + int iadd = 0; + if (nFlags == RS_STIPCM8U) { iadd = -0x80; } + len = pIns->nLength; + if (len*2 > dwMemLength) len = dwMemLength >> 1; + LPBYTE psrc = (LPBYTE)lpMemFile; + LPBYTE pSample = (LPBYTE)pIns->pSample; + for (UINT j=0; j<len; j++) + { + pSample[j*2] = (signed char)(psrc[0] + iadd); + pSample[j*2+1] = (signed char)(psrc[1] + iadd); + psrc+=2; + } + len *= 2; + } + break; + + // 16-bit interleaved stereo samples + case RS_STIPCM16S: + case RS_STIPCM16U: + { + int iadd = 0; + if (nFlags == RS_STIPCM16U) iadd = -32768; + len = pIns->nLength; + if (len*4 > dwMemLength) len = dwMemLength >> 2; + int16_t *psrc = (int16_t *)lpMemFile; + int16_t *pSample = (int16_t *)pIns->pSample; + for (UINT j=0; j<len; j++) + { + pSample[j*2] = (int16_t)(bswapLE16(psrc[0]) + iadd); + pSample[j*2+1] = (int16_t)(bswapLE16(psrc[1]) + iadd); + psrc += 2; + } + len *= 4; + } + break; + + // AMS compressed samples + case RS_AMS8: + case RS_AMS16: + len = 9; + if (dwMemLength > 9) + { + const char *psrc = lpMemFile; + char packcharacter = lpMemFile[8], *pdest = (char *)pIns->pSample; + len += bswapLE32(*((LPDWORD)(lpMemFile+4))); + if (len > dwMemLength) len = dwMemLength; + UINT dmax = pIns->nLength; + if (pIns->uFlags & CHN_16BIT) dmax <<= 1; + AMSUnpack(psrc+9, len-9, pdest, dmax, packcharacter); + } + break; + + // PTM 8bit delta to 16-bit sample + case RS_PTM8DTO16: + { + len = pIns->nLength * 2; + if (len > dwMemLength) break; + int8_t *pSample = (int8_t *)pIns->pSample; + int8_t delta8 = 0; + for (UINT j=0; j<len; j++) + { + delta8 += lpMemFile[j]; + *pSample++ = delta8; + } + uint16_t *pSampleW = (uint16_t *)pIns->pSample; + for (UINT j=0; j<len; j+=2) // swaparoni! + { + uint16_t rawSample = *pSampleW; + *pSampleW++ = bswapLE16(rawSample); + } + } + break; + + // Huffman MDL compressed samples + case RS_MDL8: + case RS_MDL16: + len = dwMemLength; + if (len >= 4) + { + LPBYTE pSample = (LPBYTE)pIns->pSample; + LPBYTE ibuf = (LPBYTE)lpMemFile; + DWORD bitbuf = bswapLE32(*((DWORD *)ibuf)); + UINT bitnum = 32; + BYTE dlt = 0, lowbyte = 0; + ibuf += 4; + for (UINT j=0; j<pIns->nLength; j++) + { + BYTE hibyte; + BYTE sign; + if (nFlags == RS_MDL16) lowbyte = (BYTE)MDLReadBits(bitbuf, bitnum, ibuf, 8); + sign = (BYTE)MDLReadBits(bitbuf, bitnum, ibuf, 1); + if (MDLReadBits(bitbuf, bitnum, ibuf, 1)) + { + hibyte = (BYTE)MDLReadBits(bitbuf, bitnum, ibuf, 3); + } else + { + hibyte = 8; + while (!MDLReadBits(bitbuf, bitnum, ibuf, 1)) hibyte += 0x10; + hibyte += MDLReadBits(bitbuf, bitnum, ibuf, 4); + } + if (sign) hibyte = ~hibyte; + dlt += hibyte; + if (nFlags != RS_MDL16) + pSample[j] = dlt; + else + { + pSample[j<<1] = lowbyte; + pSample[(j<<1)+1] = dlt; + } + } + } + break; + + case RS_DMF8: + case RS_DMF16: + len = dwMemLength; + if (len >= 4) + { + UINT maxlen = pIns->nLength; + if (pIns->uFlags & CHN_16BIT) maxlen <<= 1; + LPBYTE ibuf = (LPBYTE)lpMemFile, ibufmax = (LPBYTE)(lpMemFile+dwMemLength); + len = DMFUnpack((LPBYTE)pIns->pSample, ibuf, ibufmax, maxlen); + } + break; + +#ifdef MODPLUG_TRACKER + // PCM 24-bit signed -> load sample, and normalize it to 16-bit + case RS_PCM24S: + case RS_PCM32S: + len = pIns->nLength * 3; + if (nFlags == RS_PCM32S) len += pIns->nLength; + if (len > dwMemLength) break; + if (len > 4*8) + { + UINT slsize = (nFlags == RS_PCM32S) ? 4 : 3; + LPBYTE pSrc = (LPBYTE)lpMemFile; + LONG max = 255; + if (nFlags == RS_PCM32S) pSrc++; + for (UINT j=0; j<len; j+=slsize) + { + LONG l = ((((pSrc[j+2] << 8) + pSrc[j+1]) << 8) + pSrc[j]) << 8; + l /= 256; + if (l > max) max = l; + if (-l > max) max = -l; + } + max = (max / 128) + 1; + int16_t *pDest = (int16_t *)pIns->pSample; + for (UINT k=0; k<len; k+=slsize) + { + LONG l = ((((pSrc[k+2] << 8) + pSrc[k+1]) << 8) + pSrc[k]) << 8; + *pDest++ = (uint16_t)(l / max); + } + } + break; + + // Stereo PCM 24-bit signed -> load sample, and normalize it to 16-bit + case RS_STIPCM24S: + case RS_STIPCM32S: + len = pIns->nLength * 6; + if (nFlags == RS_STIPCM32S) len += pIns->nLength * 2; + if (len > dwMemLength) break; + if (len > 8*8) + { + UINT slsize = (nFlags == RS_STIPCM32S) ? 4 : 3; + LPBYTE pSrc = (LPBYTE)lpMemFile; + LONG max = 255; + if (nFlags == RS_STIPCM32S) pSrc++; + for (UINT j=0; j<len; j+=slsize) + { + LONG l = ((((pSrc[j+2] << 8) + pSrc[j+1]) << 8) + pSrc[j]) << 8; + l /= 256; + if (l > max) max = l; + if (-l > max) max = -l; + } + max = (max / 128) + 1; + int16_t *pDest = (int16_t *)pIns->pSample; + for (UINT k=0; k<len; k+=slsize) + { + LONG lr = ((((pSrc[k+2] << 8) + pSrc[k+1]) << 8) + pSrc[k]) << 8; + k += slsize; + LONG ll = ((((pSrc[k+2] << 8) + pSrc[k+1]) << 8) + pSrc[k]) << 8; + pDest[0] = (int16_t)ll; + pDest[1] = (int16_t)lr; + pDest += 2; + } + } + break; + + // 16-bit signed big endian interleaved stereo + case RS_STIPCM16M: + { + len = pIns->nLength; + if (len*4 > dwMemLength) len = dwMemLength >> 2; + LPCBYTE psrc = (LPCBYTE)lpMemFile; + int16_t *pSample = (int16_t *)pIns->pSample; + for (UINT j=0; j<len; j++) + { + pSample[j*2] = (int16_t)(((UINT)psrc[0] << 8) | (psrc[1])); + pSample[j*2+1] = (int16_t)(((UINT)psrc[2] << 8) | (psrc[3])); + psrc += 4; + } + len *= 4; + } + break; + +#endif // MODPLUG_TRACKER +#endif // !MODPLUG_FASTSOUNDLIB +#endif // !MODPLUG_BASIC_SUPPORT + + // Default: 8-bit signed PCM data + default: + len = pIns->nLength; + if (len > dwMemLength) len = pIns->nLength = dwMemLength; + memcpy(pIns->pSample, lpMemFile, len); + } + if (len > dwMemLength) + { + if (pIns->pSample) + { + pIns->nLength = 0; + FreeSample(pIns->pSample); + pIns->pSample = NULL; + } + return 0; + } + AdjustSampleLoop(pIns); + return len; +} + + +void CSoundFile::AdjustSampleLoop(MODINSTRUMENT *pIns) +//---------------------------------------------------- +{ + if (!pIns->pSample) return; + if (pIns->nLoopEnd > pIns->nLength) pIns->nLoopEnd = pIns->nLength; + if (pIns->nLoopStart+2 >= pIns->nLoopEnd) + { + pIns->nLoopStart = pIns->nLoopEnd = 0; + pIns->uFlags &= ~CHN_LOOP; + } + UINT len = pIns->nLength; + if (pIns->uFlags & CHN_16BIT) + { + int16_t *pSample = (int16_t *)pIns->pSample; + // Adjust end of sample + if (pIns->uFlags & CHN_STEREO) + { + pSample[len*2+6] = pSample[len*2+4] = pSample[len*2+2] = pSample[len*2] = 0; + pSample[len*2+7] = pSample[len*2+5] = pSample[len*2+3] = pSample[len*2+1] = 0; + } else + { + pSample[len+4] = pSample[len+3] = pSample[len+2] = pSample[len+1] = pSample[len] = 0; + } + if ((pIns->uFlags & (CHN_LOOP|CHN_PINGPONGLOOP|CHN_STEREO)) == CHN_LOOP) + { + // Fix bad loops + if ((pIns->nLoopEnd+3 >= pIns->nLength) || (m_nType & MOD_TYPE_S3M)) + { + pSample[pIns->nLoopEnd] = pSample[pIns->nLoopStart]; + pSample[pIns->nLoopEnd+1] = pSample[pIns->nLoopStart+1]; + pSample[pIns->nLoopEnd+2] = pSample[pIns->nLoopStart+2]; + pSample[pIns->nLoopEnd+3] = pSample[pIns->nLoopStart+3]; + pSample[pIns->nLoopEnd+4] = pSample[pIns->nLoopStart+4]; + } + } + } else + { + signed char *pSample = pIns->pSample; +#ifndef MODPLUG_FASTSOUNDLIB + // Crappy samples (except chiptunes) ? + if ((pIns->nLength > 0x100) && (m_nType & (MOD_TYPE_MOD|MOD_TYPE_S3M)) + && (!(pIns->uFlags & CHN_STEREO))) + { + int smpend = pSample[pIns->nLength-1], smpfix = 0, kscan; + for (kscan=pIns->nLength-1; kscan>0; kscan--) + { + smpfix = pSample[kscan-1]; + if (smpfix != smpend) break; + } + int delta = smpfix - smpend; + if (((!(pIns->uFlags & CHN_LOOP)) || (kscan > (int)pIns->nLoopEnd)) + && ((delta < -8) || (delta > 8))) + { + while (kscan<(int)pIns->nLength) + { + if (!(kscan & 7)) + { + if (smpfix > 0) smpfix--; + if (smpfix < 0) smpfix++; + } + pSample[kscan] = (signed char)smpfix; + kscan++; + } + } + } +#endif + // Adjust end of sample + if (pIns->uFlags & CHN_STEREO) + { + pSample[len*2+6] = pSample[len*2+4] = pSample[len*2+2] = pSample[len*2] = 0; + pSample[len*2+7] = pSample[len*2+5] = pSample[len*2+3] = pSample[len*2+1] = 0; + + } else + { + pSample[len+4] = pSample[len+3] = pSample[len+2] = pSample[len+1] = pSample[len] = 0; + } + if ((pIns->uFlags & (CHN_LOOP|CHN_PINGPONGLOOP|CHN_STEREO)) == CHN_LOOP) + { + if ((pIns->nLoopEnd+3 >= pIns->nLength) || (m_nType & (MOD_TYPE_MOD|MOD_TYPE_S3M))) + { + pSample[pIns->nLoopEnd] = pSample[pIns->nLoopStart]; + pSample[pIns->nLoopEnd+1] = pSample[pIns->nLoopStart+1]; + pSample[pIns->nLoopEnd+2] = pSample[pIns->nLoopStart+2]; + pSample[pIns->nLoopEnd+3] = pSample[pIns->nLoopStart+3]; + pSample[pIns->nLoopEnd+4] = pSample[pIns->nLoopStart+4]; + } + } + } +} + + +///////////////////////////////////////////////////////////// +// Transpose <-> Frequency conversions + +// returns 8363*2^((transp*128+ftune)/(12*128)) +DWORD CSoundFile::TransposeToFrequency(int transp, int ftune) +//----------------------------------------------------------- +{ + //---GCCFIX: Removed assembly. + return (DWORD)(8363*pow(2, (transp*128+ftune)/(1536))); + +#ifdef MSC_VER + const float _fbase = 8363; + const float _factor = 1.0f/(12.0f*128.0f); + int result; + DWORD freq; + + transp = (transp << 7) + ftune; + _asm { + fild transp + fld _factor + fmulp st(1), st(0) + fist result + fisub result + f2xm1 + fild result + fld _fbase + fscale + fstp st(1) + fmul st(1), st(0) + faddp st(1), st(0) + fistp freq + } + UINT derr = freq % 11025; + if (derr <= 8) freq -= derr; + if (derr >= 11015) freq += 11025-derr; + derr = freq % 1000; + if (derr <= 5) freq -= derr; + if (derr >= 995) freq += 1000-derr; + return freq; +#endif +} + + +// returns 12*128*log2(freq/8363) +int CSoundFile::FrequencyToTranspose(DWORD freq) +//---------------------------------------------- +{ + //---GCCFIX: Removed assembly. + return int(1536*(log(freq/8363)/log(2))); + +#ifdef MSC_VER + const float _f1_8363 = 1.0f / 8363.0f; + const float _factor = 128 * 12; + LONG result; + + if (!freq) return 0; + _asm { + fld _factor + fild freq + fld _f1_8363 + fmulp st(1), st(0) + fyl2x + fistp result + } + return result; +#endif +} + + +void CSoundFile::FrequencyToTranspose(MODINSTRUMENT *psmp) +//-------------------------------------------------------- +{ + int f2t = FrequencyToTranspose(psmp->nC4Speed); + int transp = f2t >> 7; + int ftune = f2t & 0x7F; + if (ftune > 80) + { + transp++; + ftune -= 128; + } + if (transp > 127) transp = 127; + if (transp < -127) transp = -127; + psmp->RelativeTone = transp; + psmp->nFineTune = ftune; +} + + +void CSoundFile::CheckCPUUsage(UINT nCPU) +//--------------------------------------- +{ + if (nCPU > 100) nCPU = 100; + gnCPUUsage = nCPU; + if (nCPU < 90) + { + m_dwSongFlags &= ~SONG_CPUVERYHIGH; + } else + if ((m_dwSongFlags & SONG_CPUVERYHIGH) && (nCPU >= 94)) + { + UINT i=MAX_CHANNELS; + while (i >= 8) + { + i--; + if (Chn[i].nLength) + { + Chn[i].nLength = Chn[i].nPos = 0; + nCPU -= 2; + if (nCPU < 94) break; + } + } + } else + if (nCPU > 90) + { + m_dwSongFlags |= SONG_CPUVERYHIGH; + } +} + + +BOOL CSoundFile::SetPatternName(UINT nPat, LPCSTR lpszName) +//--------------------------------------------------------- +{ + char szName[MAX_PATTERNNAME] = ""; // changed from CHAR + if (nPat >= MAX_PATTERNS) return FALSE; + if (lpszName) lstrcpyn(szName, lpszName, MAX_PATTERNNAME); + szName[MAX_PATTERNNAME-1] = 0; + if (!m_lpszPatternNames) m_nPatternNames = 0; + if (nPat >= m_nPatternNames) + { + if (!lpszName[0]) return TRUE; + UINT len = (nPat+1)*MAX_PATTERNNAME; + char *p = new char[len]; // changed from CHAR + if (!p) return FALSE; + memset(p, 0, len); + if (m_lpszPatternNames) + { + memcpy(p, m_lpszPatternNames, m_nPatternNames * MAX_PATTERNNAME); + delete m_lpszPatternNames; + m_lpszPatternNames = NULL; + } + m_lpszPatternNames = p; + m_nPatternNames = nPat + 1; + } + memcpy(m_lpszPatternNames + nPat * MAX_PATTERNNAME, szName, MAX_PATTERNNAME); + return TRUE; +} + + +BOOL CSoundFile::GetPatternName(UINT nPat, LPSTR lpszName, UINT cbSize) const +//--------------------------------------------------------------------------- +{ + if ((!lpszName) || (!cbSize)) return FALSE; + lpszName[0] = 0; + if (cbSize > MAX_PATTERNNAME) cbSize = MAX_PATTERNNAME; + if ((m_lpszPatternNames) && (nPat < m_nPatternNames)) + { + memcpy(lpszName, m_lpszPatternNames + nPat * MAX_PATTERNNAME, cbSize); + lpszName[cbSize-1] = 0; + return TRUE; + } + return FALSE; +} + + +#ifndef MODPLUG_FASTSOUNDLIB + +UINT CSoundFile::DetectUnusedSamples(BOOL *pbIns) +//----------------------------------------------- +{ + UINT nExt = 0; + + if (!pbIns) return 0; + if (m_nInstruments) + { + memset(pbIns, 0, MAX_SAMPLES * sizeof(BOOL)); + for (UINT ipat=0; ipat<MAX_PATTERNS; ipat++) + { + MODCOMMAND *p = Patterns[ipat]; + if (p) + { + UINT jmax = PatternSize[ipat] * m_nChannels; + for (UINT j=0; j<jmax; j++, p++) + { + if ((p->note) && (p->note <= NOTE_MAX)) + { + if ((p->instr) && (p->instr < MAX_INSTRUMENTS)) + { + INSTRUMENTHEADER *penv = Headers[p->instr]; + if (penv) + { + UINT n = penv->Keyboard[p->note-1]; + if (n < MAX_SAMPLES) pbIns[n] = TRUE; + } + } else + { + for (UINT k=1; k<=m_nInstruments; k++) + { + INSTRUMENTHEADER *penv = Headers[k]; + if (penv) + { + UINT n = penv->Keyboard[p->note-1]; + if (n < MAX_SAMPLES) pbIns[n] = TRUE; + } + } + } + } + } + } + } + for (UINT ichk=1; ichk<=m_nSamples; ichk++) + { + if ((!pbIns[ichk]) && (Ins[ichk].pSample)) nExt++; + } + } + return nExt; +} + + +BOOL CSoundFile::RemoveSelectedSamples(BOOL *pbIns) +//------------------------------------------------- +{ + if (!pbIns) return FALSE; + for (UINT j=1; j<MAX_SAMPLES; j++) + { + if ((!pbIns[j]) && (Ins[j].pSample)) + { + DestroySample(j); + if ((j == m_nSamples) && (j > 1)) m_nSamples--; + } + } + return TRUE; +} + + +BOOL CSoundFile::DestroySample(UINT nSample) +//------------------------------------------ +{ + if ((!nSample) || (nSample >= MAX_SAMPLES)) return FALSE; + if (!Ins[nSample].pSample) return TRUE; + MODINSTRUMENT *pins = &Ins[nSample]; + signed char *pSample = pins->pSample; + pins->pSample = NULL; + pins->nLength = 0; + pins->uFlags &= ~(CHN_16BIT); + for (UINT i=0; i<MAX_CHANNELS; i++) + { + if (Chn[i].pSample == pSample) + { + Chn[i].nPos = Chn[i].nLength = 0; + Chn[i].pSample = Chn[i].pCurrentSample = NULL; + } + } + FreeSample(pSample); + return TRUE; +} + +#endif // MODPLUG_FASTSOUNDLIB + diff --git a/lib/libmodplug/src/sndmix.cpp b/lib/libmodplug/src/sndmix.cpp new file mode 100644 index 0000000000..a2078d76ee --- /dev/null +++ b/lib/libmodplug/src/sndmix.cpp @@ -0,0 +1,1237 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +#include <libmodplug/stdafx.h> +#include <libmodplug/sndfile.h> +#include "tables.h" + +#ifdef MODPLUG_TRACKER +#define ENABLE_STEREOVU +#endif + +// Volume ramp length, in 1/10 ms +#define VOLUMERAMPLEN 146 // 1.46ms = 64 samples at 44.1kHz + +// VU-Meter +#define VUMETER_DECAY 4 + +// SNDMIX: These are global flags for playback control +UINT CSoundFile::m_nStereoSeparation = 128; +LONG CSoundFile::m_nStreamVolume = 0x8000; +UINT CSoundFile::m_nMaxMixChannels = 32; +// Mixing Configuration (SetWaveConfig) +DWORD CSoundFile::gdwSysInfo = 0; +DWORD CSoundFile::gnChannels = 1; +DWORD CSoundFile::gdwSoundSetup = 0; +DWORD CSoundFile::gdwMixingFreq = 44100; +DWORD CSoundFile::gnBitsPerSample = 16; +// Mixing data initialized in +UINT CSoundFile::gnAGC = AGC_UNITY; +UINT CSoundFile::gnVolumeRampSamples = 64; +UINT CSoundFile::gnVUMeter = 0; +UINT CSoundFile::gnCPUUsage = 0; +LPSNDMIXHOOKPROC CSoundFile::gpSndMixHook = NULL; +PMIXPLUGINCREATEPROC CSoundFile::gpMixPluginCreateProc = NULL; +LONG gnDryROfsVol = 0; +LONG gnDryLOfsVol = 0; +LONG gnRvbROfsVol = 0; +LONG gnRvbLOfsVol = 0; +int gbInitPlugins = 0; + +typedef DWORD (MPPASMCALL * LPCONVERTPROC)(LPVOID, int *, DWORD, LPLONG, LPLONG); + +extern DWORD MPPASMCALL X86_Convert32To8(LPVOID lpBuffer, int *, DWORD nSamples, LPLONG, LPLONG); +extern DWORD MPPASMCALL X86_Convert32To16(LPVOID lpBuffer, int *, DWORD nSamples, LPLONG, LPLONG); +extern DWORD MPPASMCALL X86_Convert32To24(LPVOID lpBuffer, int *, DWORD nSamples, LPLONG, LPLONG); +extern DWORD MPPASMCALL X86_Convert32To32(LPVOID lpBuffer, int *, DWORD nSamples, LPLONG, LPLONG); +extern UINT MPPASMCALL X86_AGC(int *pBuffer, UINT nSamples, UINT nAGC); +extern VOID MPPASMCALL X86_Dither(int *pBuffer, UINT nSamples, UINT nBits); +extern VOID MPPASMCALL X86_InterleaveFrontRear(int *pFrontBuf, int *pRearBuf, DWORD nSamples); +extern VOID MPPASMCALL X86_StereoFill(int *pBuffer, UINT nSamples, LPLONG lpROfs, LPLONG lpLOfs); +extern VOID MPPASMCALL X86_MonoFromStereo(int *pMixBuf, UINT nSamples); + +extern int MixSoundBuffer[MIXBUFFERSIZE*4]; +extern int MixRearBuffer[MIXBUFFERSIZE*2]; +UINT gnReverbSend; + + +// Log tables for pre-amp +// We don't want the tracker to get too loud +const UINT PreAmpTable[16] = +{ + 0x60, 0x60, 0x60, 0x70, // 0-7 + 0x80, 0x88, 0x90, 0x98, // 8-15 + 0xA0, 0xA4, 0xA8, 0xB0, // 16-23 + 0xB4, 0xB8, 0xBC, 0xC0, // 24-31 +}; + +const UINT PreAmpAGCTable[16] = +{ + 0x60, 0x60, 0x60, 0x60, + 0x68, 0x70, 0x78, 0x80, + 0x84, 0x88, 0x8C, 0x90, + 0x94, 0x98, 0x9C, 0xA0, +}; + + +// Return (a*b)/c - no divide error +int _muldiv(long a, long b, long c) +{ +#ifdef MSC_VER + int sign, result; + _asm { + mov eax, a + mov ebx, b + or eax, eax + mov edx, eax + jge aneg + neg eax +aneg: + xor edx, ebx + or ebx, ebx + mov ecx, c + jge bneg + neg ebx +bneg: + xor edx, ecx + or ecx, ecx + mov sign, edx + jge cneg + neg ecx +cneg: + mul ebx + cmp edx, ecx + jae diverr + div ecx + jmp ok +diverr: + mov eax, 0x7fffffff +ok: + mov edx, sign + or edx, edx + jge rneg + neg eax +rneg: + mov result, eax + } + return result; +#else + return ((unsigned long long) a * (unsigned long long) b ) / c; +#endif +} + + +// Return (a*b+c/2)/c - no divide error +int _muldivr(long a, long b, long c) +{ +#ifdef MSC_VER + int sign, result; + _asm { + mov eax, a + mov ebx, b + or eax, eax + mov edx, eax + jge aneg + neg eax +aneg: + xor edx, ebx + or ebx, ebx + mov ecx, c + jge bneg + neg ebx +bneg: + xor edx, ecx + or ecx, ecx + mov sign, edx + jge cneg + neg ecx +cneg: + mul ebx + mov ebx, ecx + shr ebx, 1 + add eax, ebx + adc edx, 0 + cmp edx, ecx + jae diverr + div ecx + jmp ok +diverr: + mov eax, 0x7fffffff +ok: + mov edx, sign + or edx, edx + jge rneg + neg eax +rneg: + mov result, eax + } + return result; +#else + return ((unsigned long long) a * (unsigned long long) b + (c >> 1)) / c; +#endif +} + + +BOOL CSoundFile::InitPlayer(BOOL bReset) +//-------------------------------------- +{ + if (m_nMaxMixChannels > MAX_CHANNELS) m_nMaxMixChannels = MAX_CHANNELS; + if (gdwMixingFreq < 4000) gdwMixingFreq = 4000; + if (gdwMixingFreq > MAX_SAMPLE_RATE) gdwMixingFreq = MAX_SAMPLE_RATE; + gnVolumeRampSamples = (gdwMixingFreq * VOLUMERAMPLEN) / 100000; + if (gnVolumeRampSamples < 8) gnVolumeRampSamples = 8; + gnDryROfsVol = gnDryLOfsVol = 0; + gnRvbROfsVol = gnRvbLOfsVol = 0; + if (bReset) + { + gnVUMeter = 0; + gnCPUUsage = 0; + } + gbInitPlugins = (bReset) ? 3 : 1; + InitializeDSP(bReset); + return TRUE; +} + + +BOOL CSoundFile::FadeSong(UINT msec) +//---------------------------------- +{ + LONG nsamples = _muldiv(msec, gdwMixingFreq, 1000); + if (nsamples <= 0) return FALSE; + if (nsamples > 0x100000) nsamples = 0x100000; + m_nBufferCount = nsamples; + LONG nRampLength = m_nBufferCount; + // Ramp everything down + for (UINT noff=0; noff < m_nMixChannels; noff++) + { + MODCHANNEL *pramp = &Chn[ChnMix[noff]]; + if (!pramp) continue; + pramp->nNewLeftVol = pramp->nNewRightVol = 0; + pramp->nRightRamp = (-pramp->nRightVol << VOLUMERAMPPRECISION) / nRampLength; + pramp->nLeftRamp = (-pramp->nLeftVol << VOLUMERAMPPRECISION) / nRampLength; + pramp->nRampRightVol = pramp->nRightVol << VOLUMERAMPPRECISION; + pramp->nRampLeftVol = pramp->nLeftVol << VOLUMERAMPPRECISION; + pramp->nRampLength = nRampLength; + pramp->dwFlags |= CHN_VOLUMERAMP; + } + m_dwSongFlags |= SONG_FADINGSONG; + return TRUE; +} + + +BOOL CSoundFile::GlobalFadeSong(UINT msec) +//---------------------------------------- +{ + if (m_dwSongFlags & SONG_GLOBALFADE) return FALSE; + m_nGlobalFadeMaxSamples = _muldiv(msec, gdwMixingFreq, 1000); + m_nGlobalFadeSamples = m_nGlobalFadeMaxSamples; + m_dwSongFlags |= SONG_GLOBALFADE; + return TRUE; +} + + +UINT CSoundFile::Read(LPVOID lpDestBuffer, UINT cbBuffer) +//------------------------------------------------------- +{ + LPBYTE lpBuffer = (LPBYTE)lpDestBuffer; + LPCONVERTPROC pCvt = X86_Convert32To8; + UINT lRead, lMax, lSampleSize, lCount, lSampleCount, nStat=0; + LONG nVUMeterMin = 0x7FFFFFFF, nVUMeterMax = -0x7FFFFFFF; + UINT nMaxPlugins; + + { + nMaxPlugins = MAX_MIXPLUGINS; + while ((nMaxPlugins > 0) && (!m_MixPlugins[nMaxPlugins-1].pMixPlugin)) nMaxPlugins--; + } + m_nMixStat = 0; + lSampleSize = gnChannels; + if (gnBitsPerSample == 16) { lSampleSize *= 2; pCvt = X86_Convert32To16; } +#ifndef MODPLUG_FASTSOUNDLIB + else if (gnBitsPerSample == 24) { lSampleSize *= 3; pCvt = X86_Convert32To24; } + else if (gnBitsPerSample == 32) { lSampleSize *= 4; pCvt = X86_Convert32To32; } +#endif + lMax = cbBuffer / lSampleSize; + if ((!lMax) || (!lpBuffer) || (!m_nChannels)) return 0; + lRead = lMax; + if (m_dwSongFlags & SONG_ENDREACHED) goto MixDone; + while (lRead > 0) + { + // Update Channel Data + if (!m_nBufferCount) + { +#ifndef MODPLUG_FASTSOUNDLIB + if (m_dwSongFlags & SONG_FADINGSONG) + { + m_dwSongFlags |= SONG_ENDREACHED; + m_nBufferCount = lRead; + } else +#endif + if (!ReadNote()) + { +#ifndef MODPLUG_FASTSOUNDLIB + if (!FadeSong(FADESONGDELAY)) +#endif + { + m_dwSongFlags |= SONG_ENDREACHED; + if (lRead == lMax) goto MixDone; + m_nBufferCount = lRead; + } + } + } + lCount = m_nBufferCount; + if (lCount > MIXBUFFERSIZE) lCount = MIXBUFFERSIZE; + if (lCount > lRead) lCount = lRead; + if (!lCount) break; + lSampleCount = lCount; +#ifndef MODPLUG_NO_REVERB + gnReverbSend = 0; +#endif + // Resetting sound buffer + X86_StereoFill(MixSoundBuffer, lSampleCount, &gnDryROfsVol, &gnDryLOfsVol); + if (gnChannels >= 2) + { + lSampleCount *= 2; + m_nMixStat += CreateStereoMix(lCount); + ProcessStereoDSP(lCount); + } else + { + m_nMixStat += CreateStereoMix(lCount); + if (nMaxPlugins) ProcessPlugins(lCount); + ProcessStereoDSP(lCount); + X86_MonoFromStereo(MixSoundBuffer, lCount); + } + nStat++; +#ifndef NO_AGC + // Automatic Gain Control + if (gdwSoundSetup & SNDMIX_AGC) ProcessAGC(lSampleCount); +#endif + UINT lTotalSampleCount = lSampleCount; +#ifndef MODPLUG_FASTSOUNDLIB + // Multichannel + if (gnChannels > 2) + { + X86_InterleaveFrontRear(MixSoundBuffer, MixRearBuffer, lSampleCount); + lTotalSampleCount *= 2; + } + // Hook Function + if (gpSndMixHook) + { + gpSndMixHook(MixSoundBuffer, lTotalSampleCount, gnChannels); + } +#endif + // Perform clipping + VU-Meter + lpBuffer += pCvt(lpBuffer, MixSoundBuffer, lTotalSampleCount, &nVUMeterMin, &nVUMeterMax); + // Buffer ready + lRead -= lCount; + m_nBufferCount -= lCount; + } +MixDone: + if (lRead) memset(lpBuffer, (gnBitsPerSample == 8) ? 0x80 : 0, lRead * lSampleSize); + // VU-Meter + nVUMeterMin >>= (24-MIXING_ATTENUATION); + nVUMeterMax >>= (24-MIXING_ATTENUATION); + if (nVUMeterMax < nVUMeterMin) nVUMeterMax = nVUMeterMin; + if ((gnVUMeter = (UINT)(nVUMeterMax - nVUMeterMin)) > 0xFF) gnVUMeter = 0xFF; + if (nStat) { m_nMixStat += nStat-1; m_nMixStat /= nStat; } + return lMax - lRead; +} + + + +///////////////////////////////////////////////////////////////////////////// +// Handles navigation/effects + +BOOL CSoundFile::ProcessRow() +//--------------------------- +{ + if (++m_nTickCount >= m_nMusicSpeed * (m_nPatternDelay+1) + m_nFrameDelay) + { + m_nPatternDelay = 0; + m_nFrameDelay = 0; + m_nTickCount = 0; + m_nRow = m_nNextRow; + // Reset Pattern Loop Effect + if (m_nCurrentPattern != m_nNextPattern) m_nCurrentPattern = m_nNextPattern; + // Check if pattern is valid + if (!(m_dwSongFlags & SONG_PATTERNLOOP)) + { + m_nPattern = (m_nCurrentPattern < MAX_ORDERS) ? Order[m_nCurrentPattern] : 0xFF; + if ((m_nPattern < MAX_PATTERNS) && (!Patterns[m_nPattern])) m_nPattern = 0xFE; + while (m_nPattern >= MAX_PATTERNS) + { + // End of song ? + if ((m_nPattern == 0xFF) || (m_nCurrentPattern >= MAX_ORDERS)) + { + //if (!m_nRepeatCount) + return FALSE; //never repeat entire song + if (!m_nRestartPos) + { + m_nMusicSpeed = m_nDefaultSpeed; + m_nMusicTempo = m_nDefaultTempo; + m_nGlobalVolume = m_nDefaultGlobalVolume; + for (UINT i=0; i<MAX_CHANNELS; i++) + { + Chn[i].dwFlags |= CHN_NOTEFADE | CHN_KEYOFF; + Chn[i].nFadeOutVol = 0; + if (i < m_nChannels) + { + Chn[i].nGlobalVol = ChnSettings[i].nVolume; + Chn[i].nVolume = ChnSettings[i].nVolume; + Chn[i].nPan = ChnSettings[i].nPan; + Chn[i].nPanSwing = Chn[i].nVolSwing = 0; + Chn[i].nOldVolParam = 0; + Chn[i].nOldOffset = 0; + Chn[i].nOldHiOffset = 0; + Chn[i].nPortamentoDest = 0; + if (!Chn[i].nLength) + { + Chn[i].dwFlags = ChnSettings[i].dwFlags; + Chn[i].nLoopStart = 0; + Chn[i].nLoopEnd = 0; + Chn[i].pHeader = NULL; + Chn[i].pSample = NULL; + Chn[i].pInstrument = NULL; + } + } + } + } +// if (m_nRepeatCount > 0) m_nRepeatCount--; + m_nCurrentPattern = m_nRestartPos; + m_nRow = 0; + if ((Order[m_nCurrentPattern] >= MAX_PATTERNS) || (!Patterns[Order[m_nCurrentPattern]])) return FALSE; + } else + { + m_nCurrentPattern++; + } + m_nPattern = (m_nCurrentPattern < MAX_ORDERS) ? Order[m_nCurrentPattern] : 0xFF; + if ((m_nPattern < MAX_PATTERNS) && (!Patterns[m_nPattern])) m_nPattern = 0xFE; + } + m_nNextPattern = m_nCurrentPattern; + } + // Weird stuff? + if ((m_nPattern >= MAX_PATTERNS) || (!Patterns[m_nPattern])) return FALSE; + // Should never happen + if (m_nRow >= PatternSize[m_nPattern]) m_nRow = 0; + m_nNextRow = m_nRow + 1; + if (m_nNextRow >= PatternSize[m_nPattern]) + { + if (!(m_dwSongFlags & SONG_PATTERNLOOP)) m_nNextPattern = m_nCurrentPattern + 1; + m_nNextRow = 0; + } + // Reset channel values + MODCHANNEL *pChn = Chn; + MODCOMMAND *m = Patterns[m_nPattern] + m_nRow * m_nChannels; + for (UINT nChn=0; nChn<m_nChannels; pChn++, nChn++, m++) + { + pChn->nRowNote = m->note; + pChn->nRowInstr = m->instr; + pChn->nRowVolCmd = m->volcmd; + pChn->nRowVolume = m->vol; + pChn->nRowCommand = m->command; + pChn->nRowParam = m->param; + + pChn->nLeftVol = pChn->nNewLeftVol; + pChn->nRightVol = pChn->nNewRightVol; + pChn->dwFlags &= ~(CHN_PORTAMENTO | CHN_VIBRATO | CHN_TREMOLO | CHN_PANBRELLO); + pChn->nCommand = 0; + } + } + // Should we process tick0 effects? + if (!m_nMusicSpeed) m_nMusicSpeed = 1; + m_dwSongFlags |= SONG_FIRSTTICK; + if (m_nTickCount) + { + m_dwSongFlags &= ~SONG_FIRSTTICK; + if ((!(m_nType & MOD_TYPE_XM)) && (m_nTickCount < m_nMusicSpeed * (1 + m_nPatternDelay))) + { + if (!(m_nTickCount % m_nMusicSpeed)) m_dwSongFlags |= SONG_FIRSTTICK; + } + + } + // Update Effects + return ProcessEffects(); +} + + +//////////////////////////////////////////////////////////////////////////////////////////// +// Handles envelopes & mixer setup + +BOOL CSoundFile::ReadNote() +//------------------------- +{ + if (!ProcessRow()) return FALSE; + //////////////////////////////////////////////////////////////////////////////////// + m_nTotalCount++; + if (!m_nMusicTempo) return FALSE; + m_nBufferCount = (gdwMixingFreq * 5 * m_nTempoFactor) / (m_nMusicTempo << 8); + // Master Volume + Pre-Amplification / Attenuation setup + DWORD nMasterVol; + { + int nchn32 = (m_nChannels < 32) ? m_nChannels : 31; + if ((m_nType & MOD_TYPE_IT) && (m_nInstruments) && (nchn32 < 6)) nchn32 = 6; + int realmastervol = m_nMasterVolume; + if (realmastervol > 0x80) + { + realmastervol = 0x80 + ((realmastervol - 0x80) * (nchn32+4)) / 16; + } + UINT attenuation = (gdwSoundSetup & SNDMIX_AGC) ? PreAmpAGCTable[nchn32>>1] : PreAmpTable[nchn32>>1]; + DWORD mastervol = (realmastervol * (m_nSongPreAmp + 0x10)) >> 6; + if (mastervol > 0x200) mastervol = 0x200; + if ((m_dwSongFlags & SONG_GLOBALFADE) && (m_nGlobalFadeMaxSamples)) + { + mastervol = _muldiv(mastervol, m_nGlobalFadeSamples, m_nGlobalFadeMaxSamples); + } + nMasterVol = (mastervol << 7) / attenuation; + if (nMasterVol > 0x180) nMasterVol = 0x180; + } + //////////////////////////////////////////////////////////////////////////////////// + // Update channels data + m_nMixChannels = 0; + MODCHANNEL *pChn = Chn; + for (UINT nChn=0; nChn<MAX_CHANNELS; nChn++,pChn++) + { + if ((pChn->dwFlags & CHN_NOTEFADE) && (!(pChn->nFadeOutVol|pChn->nRightVol|pChn->nLeftVol))) + { + pChn->nLength = 0; + pChn->nROfs = pChn->nLOfs = 0; + } + // Check for unused channel + if ((pChn->dwFlags & CHN_MUTE) || ((nChn >= m_nChannels) && (!pChn->nLength))) + { + pChn->nVUMeter = 0; +#ifdef ENABLE_STEREOVU + pChn->nLeftVU = pChn->nRightVU = 0; +#endif + continue; + } + // Reset channel data + pChn->nInc = 0; + pChn->nRealVolume = 0; + pChn->nRealPan = pChn->nPan + pChn->nPanSwing; + if (pChn->nRealPan < 0) pChn->nRealPan = 0; + if (pChn->nRealPan > 256) pChn->nRealPan = 256; + pChn->nRampLength = 0; + // Calc Frequency + if ((pChn->nPeriod) && (pChn->nLength)) + { + int vol = pChn->nVolume + pChn->nVolSwing; + + if (vol < 0) vol = 0; + if (vol > 256) vol = 256; + // Tremolo + if (pChn->dwFlags & CHN_TREMOLO) + { + UINT trempos = pChn->nTremoloPos & 0x3F; + if (vol > 0) + { + int tremattn = (m_nType & MOD_TYPE_XM) ? 5 : 6; + switch (pChn->nTremoloType & 0x03) + { + case 1: + vol += (ModRampDownTable[trempos] * (int)pChn->nTremoloDepth) >> tremattn; + break; + case 2: + vol += (ModSquareTable[trempos] * (int)pChn->nTremoloDepth) >> tremattn; + break; + case 3: + vol += (ModRandomTable[trempos] * (int)pChn->nTremoloDepth) >> tremattn; + break; + default: + vol += (ModSinusTable[trempos] * (int)pChn->nTremoloDepth) >> tremattn; + } + } + if ((m_nTickCount) || ((m_nType & (MOD_TYPE_STM|MOD_TYPE_S3M|MOD_TYPE_IT)) && (!(m_dwSongFlags & SONG_ITOLDEFFECTS)))) + { + pChn->nTremoloPos = (trempos + pChn->nTremoloSpeed) & 0x3F; + } + } + // Tremor + if (pChn->nCommand == CMD_TREMOR) + { + UINT n = (pChn->nTremorParam >> 4) + (pChn->nTremorParam & 0x0F); + UINT ontime = pChn->nTremorParam >> 4; + if ((!(m_nType & MOD_TYPE_IT)) || (m_dwSongFlags & SONG_ITOLDEFFECTS)) { n += 2; ontime++; } + UINT tremcount = (UINT)pChn->nTremorCount; + if (tremcount >= n) tremcount = 0; + if ((m_nTickCount) || (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT))) + { + if (tremcount >= ontime) vol = 0; + pChn->nTremorCount = (BYTE)(tremcount + 1); + } + pChn->dwFlags |= CHN_FASTVOLRAMP; + } + // Clip volume + if (vol < 0) vol = 0; + if (vol > 0x100) vol = 0x100; + vol <<= 6; + // Process Envelopes + if (pChn->pHeader) + { + INSTRUMENTHEADER *penv = pChn->pHeader; + // Volume Envelope + if ((pChn->dwFlags & CHN_VOLENV) && (penv->nVolEnv)) + { + int envpos = pChn->nVolEnvPosition; + UINT pt = penv->nVolEnv - 1; + for (UINT i=0; i<(UINT)(penv->nVolEnv-1); i++) + { + if (envpos <= penv->VolPoints[i]) + { + pt = i; + break; + } + } + int x2 = penv->VolPoints[pt]; + int x1, envvol; + if (envpos >= x2) + { + envvol = penv->VolEnv[pt] << 2; + x1 = x2; + } else + if (pt) + { + envvol = penv->VolEnv[pt-1] << 2; + x1 = penv->VolPoints[pt-1]; + } else + { + envvol = 0; + x1 = 0; + } + if (envpos > x2) envpos = x2; + if ((x2 > x1) && (envpos > x1)) + { + envvol += ((envpos - x1) * (((int)penv->VolEnv[pt]<<2) - envvol)) / (x2 - x1); + } + if (envvol < 0) envvol = 0; + if (envvol > 256) envvol = 256; + vol = (vol * envvol) >> 8; + } + // Panning Envelope + if ((pChn->dwFlags & CHN_PANENV) && (penv->nPanEnv)) + { + int envpos = pChn->nPanEnvPosition; + UINT pt = penv->nPanEnv - 1; + for (UINT i=0; i<(UINT)(penv->nPanEnv-1); i++) + { + if (envpos <= penv->PanPoints[i]) + { + pt = i; + break; + } + } + int x2 = penv->PanPoints[pt], y2 = penv->PanEnv[pt]; + int x1, envpan; + if (envpos >= x2) + { + envpan = y2; + x1 = x2; + } else + if (pt) + { + envpan = penv->PanEnv[pt-1]; + x1 = penv->PanPoints[pt-1]; + } else + { + envpan = 128; + x1 = 0; + } + if ((x2 > x1) && (envpos > x1)) + { + envpan += ((envpos - x1) * (y2 - envpan)) / (x2 - x1); + } + if (envpan < 0) envpan = 0; + if (envpan > 64) envpan = 64; + int pan = pChn->nPan; + if (pan >= 128) + { + pan += ((envpan - 32) * (256 - pan)) / 32; + } else + { + pan += ((envpan - 32) * (pan)) / 32; + } + if (pan < 0) pan = 0; + if (pan > 256) pan = 256; + pChn->nRealPan = pan; + } + // FadeOut volume + if (pChn->dwFlags & CHN_NOTEFADE) + { + UINT fadeout = penv->nFadeOut; + if (fadeout) + { + pChn->nFadeOutVol -= fadeout << 1; + if (pChn->nFadeOutVol <= 0) pChn->nFadeOutVol = 0; + vol = (vol * pChn->nFadeOutVol) >> 16; + } else + if (!pChn->nFadeOutVol) + { + vol = 0; + } + } + // Pitch/Pan separation + if ((penv->nPPS) && (pChn->nRealPan) && (pChn->nNote)) + { + int pandelta = (int)pChn->nRealPan + (int)((int)(pChn->nNote - penv->nPPC - 1) * (int)penv->nPPS) / (int)8; + if (pandelta < 0) pandelta = 0; + if (pandelta > 256) pandelta = 256; + pChn->nRealPan = pandelta; + } + } else + { + // No Envelope: key off => note cut + if (pChn->dwFlags & CHN_NOTEFADE) // 1.41-: CHN_KEYOFF|CHN_NOTEFADE + { + pChn->nFadeOutVol = 0; + vol = 0; + } + } + // vol is 14-bits + if (vol) + { + // IMPORTANT: pChn->nRealVolume is 14 bits !!! + // -> _muldiv( 14+8, 6+6, 18); => RealVolume: 14-bit result (22+12-20) + pChn->nRealVolume = _muldiv(vol * m_nGlobalVolume, pChn->nGlobalVol * pChn->nInsVol, 1 << 20); + } + if (pChn->nPeriod < m_nMinPeriod) pChn->nPeriod = m_nMinPeriod; + int period = pChn->nPeriod; + if ((pChn->dwFlags & (CHN_GLISSANDO|CHN_PORTAMENTO)) == (CHN_GLISSANDO|CHN_PORTAMENTO)) + { + period = GetPeriodFromNote(GetNoteFromPeriod(period), pChn->nFineTune, pChn->nC4Speed); + } + + // Arpeggio ? + if (pChn->nCommand == CMD_ARPEGGIO) + { + switch(m_nTickCount % 3) + { + case 1: period = GetPeriodFromNote(pChn->nNote + (pChn->nArpeggio >> 4), pChn->nFineTune, pChn->nC4Speed); break; + case 2: period = GetPeriodFromNote(pChn->nNote + (pChn->nArpeggio & 0x0F), pChn->nFineTune, pChn->nC4Speed); break; + } + } + + if (m_dwSongFlags & SONG_AMIGALIMITS) + { + if (period < 113*4) period = 113*4; + if (period > 856*4) period = 856*4; + } + + // Pitch/Filter Envelope + if ((pChn->pHeader) && (pChn->dwFlags & CHN_PITCHENV) && (pChn->pHeader->nPitchEnv)) + { + INSTRUMENTHEADER *penv = pChn->pHeader; + int envpos = pChn->nPitchEnvPosition; + UINT pt = penv->nPitchEnv - 1; + for (UINT i=0; i<(UINT)(penv->nPitchEnv-1); i++) + { + if (envpos <= penv->PitchPoints[i]) + { + pt = i; + break; + } + } + int x2 = penv->PitchPoints[pt]; + int x1, envpitch; + if (envpos >= x2) + { + envpitch = (((int)penv->PitchEnv[pt]) - 32) * 8; + x1 = x2; + } else + if (pt) + { + envpitch = (((int)penv->PitchEnv[pt-1]) - 32) * 8; + x1 = penv->PitchPoints[pt-1]; + } else + { + envpitch = 0; + x1 = 0; + } + if (envpos > x2) envpos = x2; + if ((x2 > x1) && (envpos > x1)) + { + int envpitchdest = (((int)penv->PitchEnv[pt]) - 32) * 8; + envpitch += ((envpos - x1) * (envpitchdest - envpitch)) / (x2 - x1); + } + if (envpitch < -256) envpitch = -256; + if (envpitch > 256) envpitch = 256; + // Filter Envelope: controls cutoff frequency + if (penv->dwFlags & ENV_FILTER) + { +#ifndef NO_FILTER + SetupChannelFilter(pChn, (pChn->dwFlags & CHN_FILTER) ? FALSE : TRUE, envpitch); +#endif // NO_FILTER + } else + // Pitch Envelope + { + int l = envpitch; + if (l < 0) + { + l = -l; + if (l > 255) l = 255; + period = _muldiv(period, LinearSlideUpTable[l], 0x10000); + } else + { + if (l > 255) l = 255; + period = _muldiv(period, LinearSlideDownTable[l], 0x10000); + } + } + } + + // Vibrato + if (pChn->dwFlags & CHN_VIBRATO) + { + UINT vibpos = pChn->nVibratoPos; + LONG vdelta; + switch (pChn->nVibratoType & 0x03) + { + case 1: + vdelta = ModRampDownTable[vibpos]; + break; + case 2: + vdelta = ModSquareTable[vibpos]; + break; + case 3: + vdelta = ModRandomTable[vibpos]; + break; + default: + vdelta = ModSinusTable[vibpos]; + } + UINT vdepth = ((m_nType != MOD_TYPE_IT) || (m_dwSongFlags & SONG_ITOLDEFFECTS)) ? 6 : 7; + vdelta = (vdelta * (int)pChn->nVibratoDepth) >> vdepth; + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (m_nType & MOD_TYPE_IT)) + { + LONG l = vdelta; + if (l < 0) + { + l = -l; + vdelta = _muldiv(period, LinearSlideDownTable[l >> 2], 0x10000) - period; + if (l & 0x03) vdelta += _muldiv(period, FineLinearSlideDownTable[l & 0x03], 0x10000) - period; + + } else + { + vdelta = _muldiv(period, LinearSlideUpTable[l >> 2], 0x10000) - period; + if (l & 0x03) vdelta += _muldiv(period, FineLinearSlideUpTable[l & 0x03], 0x10000) - period; + + } + } + period += vdelta; + if ((m_nTickCount) || ((m_nType & MOD_TYPE_IT) && (!(m_dwSongFlags & SONG_ITOLDEFFECTS)))) + { + pChn->nVibratoPos = (vibpos + pChn->nVibratoSpeed) & 0x3F; + } + } + // Panbrello + if (pChn->dwFlags & CHN_PANBRELLO) + { + UINT panpos = ((pChn->nPanbrelloPos+0x10) >> 2) & 0x3F; + LONG pdelta; + switch (pChn->nPanbrelloType & 0x03) + { + case 1: + pdelta = ModRampDownTable[panpos]; + break; + case 2: + pdelta = ModSquareTable[panpos]; + break; + case 3: + pdelta = ModRandomTable[panpos]; + break; + default: + pdelta = ModSinusTable[panpos]; + } + pChn->nPanbrelloPos += pChn->nPanbrelloSpeed; + pdelta = ((pdelta * (int)pChn->nPanbrelloDepth) + 2) >> 3; + pdelta += pChn->nRealPan; + if (pdelta < 0) pdelta = 0; + if (pdelta > 256) pdelta = 256; + pChn->nRealPan = pdelta; + } + int nPeriodFrac = 0; + // Instrument Auto-Vibrato + if ((pChn->pInstrument) && (pChn->pInstrument->nVibDepth)) + { + MODINSTRUMENT *pins = pChn->pInstrument; + if (pins->nVibSweep == 0) + { + pChn->nAutoVibDepth = pins->nVibDepth << 8; + } else + { + if (m_nType & MOD_TYPE_IT) + { + pChn->nAutoVibDepth += pins->nVibSweep << 3; + } else + if (!(pChn->dwFlags & CHN_KEYOFF)) + { + pChn->nAutoVibDepth += (pins->nVibDepth << 8) / pins->nVibSweep; + } + if ((pChn->nAutoVibDepth >> 8) > pins->nVibDepth) + pChn->nAutoVibDepth = pins->nVibDepth << 8; + } + pChn->nAutoVibPos += pins->nVibRate; + int val; + switch(pins->nVibType) + { + case 4: // Random + val = ModRandomTable[pChn->nAutoVibPos & 0x3F]; + pChn->nAutoVibPos++; + break; + case 3: // Ramp Down + val = ((0x40 - (pChn->nAutoVibPos >> 1)) & 0x7F) - 0x40; + break; + case 2: // Ramp Up + val = ((0x40 + (pChn->nAutoVibPos >> 1)) & 0x7f) - 0x40; + break; + case 1: // Square + val = (pChn->nAutoVibPos & 128) ? +64 : -64; + break; + default: // Sine + val = ft2VibratoTable[pChn->nAutoVibPos & 255]; + } + int n = ((val * pChn->nAutoVibDepth) >> 8); + if (m_nType & MOD_TYPE_IT) + { + int df1, df2; + if (n < 0) + { + n = -n; + UINT n1 = n >> 8; + df1 = LinearSlideUpTable[n1]; + df2 = LinearSlideUpTable[n1+1]; + } else + { + UINT n1 = n >> 8; + df1 = LinearSlideDownTable[n1]; + df2 = LinearSlideDownTable[n1+1]; + } + n >>= 2; + period = _muldiv(period, df1 + ((df2-df1)*(n&0x3F)>>6), 256); + nPeriodFrac = period & 0xFF; + period >>= 8; + } else + { + period += (n >> 6); + } + } + // Final Period + if (period <= m_nMinPeriod) + { + if (m_nType & MOD_TYPE_S3M) pChn->nLength = 0; + period = m_nMinPeriod; + } + if (period > m_nMaxPeriod) + { + if ((m_nType & MOD_TYPE_IT) || (period >= 0x100000)) + { + pChn->nFadeOutVol = 0; + pChn->dwFlags |= CHN_NOTEFADE; + pChn->nRealVolume = 0; + } + period = m_nMaxPeriod; + nPeriodFrac = 0; + } + UINT freq = GetFreqFromPeriod(period, pChn->nC4Speed, nPeriodFrac); + if ((m_nType & MOD_TYPE_IT) && (freq < 256)) + { + pChn->nFadeOutVol = 0; + pChn->dwFlags |= CHN_NOTEFADE; + pChn->nRealVolume = 0; + } + UINT ninc = _muldiv(freq, 0x10000, gdwMixingFreq); + if ((ninc >= 0xFFB0) && (ninc <= 0x10090)) ninc = 0x10000; + if (m_nFreqFactor != 128) ninc = (ninc * m_nFreqFactor) >> 7; + if (ninc > 0xFF0000) ninc = 0xFF0000; + pChn->nInc = (ninc+1) & ~3; + } + + // Increment envelope position + if (pChn->pHeader) + { + INSTRUMENTHEADER *penv = pChn->pHeader; + // Volume Envelope + if (pChn->dwFlags & CHN_VOLENV) + { + // Increase position + pChn->nVolEnvPosition++; + // Volume Loop ? + if (penv->dwFlags & ENV_VOLLOOP) + { + UINT volloopend = penv->VolPoints[penv->nVolLoopEnd]; + if (m_nType != MOD_TYPE_XM) volloopend++; + if (pChn->nVolEnvPosition == volloopend) + { + pChn->nVolEnvPosition = penv->VolPoints[penv->nVolLoopStart]; + if ((penv->nVolLoopEnd == penv->nVolLoopStart) && (!penv->VolEnv[penv->nVolLoopStart]) + && ((!(m_nType & MOD_TYPE_XM)) || (penv->nVolLoopEnd+1 == penv->nVolEnv))) + { + pChn->dwFlags |= CHN_NOTEFADE; + pChn->nFadeOutVol = 0; + } + } + } + // Volume Sustain ? + if ((penv->dwFlags & ENV_VOLSUSTAIN) && (!(pChn->dwFlags & CHN_KEYOFF))) + { + if (pChn->nVolEnvPosition == (UINT)penv->VolPoints[penv->nVolSustainEnd]+1) + pChn->nVolEnvPosition = penv->VolPoints[penv->nVolSustainBegin]; + } else + // End of Envelope ? + if (pChn->nVolEnvPosition > penv->VolPoints[penv->nVolEnv - 1]) + { + if ((m_nType & MOD_TYPE_IT) || (pChn->dwFlags & CHN_KEYOFF)) pChn->dwFlags |= CHN_NOTEFADE; + pChn->nVolEnvPosition = penv->VolPoints[penv->nVolEnv - 1]; + if ((!penv->VolEnv[penv->nVolEnv-1]) && ((nChn >= m_nChannels) || (m_nType & MOD_TYPE_IT))) + { + pChn->dwFlags |= CHN_NOTEFADE; + pChn->nFadeOutVol = 0; + + pChn->nRealVolume = 0; + } + } + } + // Panning Envelope + if (pChn->dwFlags & CHN_PANENV) + { + pChn->nPanEnvPosition++; + if (penv->dwFlags & ENV_PANLOOP) + { + UINT panloopend = penv->PanPoints[penv->nPanLoopEnd]; + if (m_nType != MOD_TYPE_XM) panloopend++; + if (pChn->nPanEnvPosition == panloopend) + pChn->nPanEnvPosition = penv->PanPoints[penv->nPanLoopStart]; + } + // Panning Sustain ? + if ((penv->dwFlags & ENV_PANSUSTAIN) && (pChn->nPanEnvPosition == (UINT)penv->PanPoints[penv->nPanSustainEnd]+1) + && (!(pChn->dwFlags & CHN_KEYOFF))) + { + // Panning sustained + pChn->nPanEnvPosition = penv->PanPoints[penv->nPanSustainBegin]; + } else + { + if (pChn->nPanEnvPosition > penv->PanPoints[penv->nPanEnv - 1]) + pChn->nPanEnvPosition = penv->PanPoints[penv->nPanEnv - 1]; + } + } + // Pitch Envelope + if (pChn->dwFlags & CHN_PITCHENV) + { + // Increase position + pChn->nPitchEnvPosition++; + // Pitch Loop ? + if (penv->dwFlags & ENV_PITCHLOOP) + { + if (pChn->nPitchEnvPosition >= penv->PitchPoints[penv->nPitchLoopEnd]) + pChn->nPitchEnvPosition = penv->PitchPoints[penv->nPitchLoopStart]; + } + // Pitch Sustain ? + if ((penv->dwFlags & ENV_PITCHSUSTAIN) && (!(pChn->dwFlags & CHN_KEYOFF))) + { + if (pChn->nPitchEnvPosition == (UINT)penv->PitchPoints[penv->nPitchSustainEnd]+1) + pChn->nPitchEnvPosition = penv->PitchPoints[penv->nPitchSustainBegin]; + } else + { + if (pChn->nPitchEnvPosition > penv->PitchPoints[penv->nPitchEnv - 1]) + pChn->nPitchEnvPosition = penv->PitchPoints[penv->nPitchEnv - 1]; + } + } + } +#ifdef MODPLUG_PLAYER + // Limit CPU -> > 80% -> don't ramp + if ((gnCPUUsage >= 80) && (!pChn->nRealVolume)) + { + pChn->nLeftVol = pChn->nRightVol = 0; + } +#endif // MODPLUG_PLAYER + // Volume ramping + pChn->dwFlags &= ~CHN_VOLUMERAMP; + if ((pChn->nRealVolume) || (pChn->nLeftVol) || (pChn->nRightVol)) + pChn->dwFlags |= CHN_VOLUMERAMP; +#ifdef MODPLUG_PLAYER + // Decrease VU-Meter + if (pChn->nVUMeter > VUMETER_DECAY) pChn->nVUMeter -= VUMETER_DECAY; else pChn->nVUMeter = 0; +#endif // MODPLUG_PLAYER +#ifdef ENABLE_STEREOVU + if (pChn->nLeftVU > VUMETER_DECAY) pChn->nLeftVU -= VUMETER_DECAY; else pChn->nLeftVU = 0; + if (pChn->nRightVU > VUMETER_DECAY) pChn->nRightVU -= VUMETER_DECAY; else pChn->nRightVU = 0; +#endif + // Check for too big nInc + if (((pChn->nInc >> 16) + 1) >= (LONG)(pChn->nLoopEnd - pChn->nLoopStart)) pChn->dwFlags &= ~CHN_LOOP; + pChn->nNewRightVol = pChn->nNewLeftVol = 0; + pChn->pCurrentSample = ((pChn->pSample) && (pChn->nLength) && (pChn->nInc)) ? pChn->pSample : NULL; + if (pChn->pCurrentSample) + { + // Update VU-Meter (nRealVolume is 14-bit) +#ifdef MODPLUG_PLAYER + UINT vutmp = pChn->nRealVolume >> (14 - 8); + if (vutmp > 0xFF) vutmp = 0xFF; + if (pChn->nVUMeter >= 0x100) pChn->nVUMeter = vutmp; + vutmp >>= 1; + if (pChn->nVUMeter < vutmp) pChn->nVUMeter = vutmp; +#endif // MODPLUG_PLAYER +#ifdef ENABLE_STEREOVU + UINT vul = (pChn->nRealVolume * pChn->nRealPan) >> 14; + if (vul > 127) vul = 127; + if (pChn->nLeftVU > 127) pChn->nLeftVU = (BYTE)vul; + vul >>= 1; + if (pChn->nLeftVU < vul) pChn->nLeftVU = (BYTE)vul; + UINT vur = (pChn->nRealVolume * (256-pChn->nRealPan)) >> 14; + if (vur > 127) vur = 127; + if (pChn->nRightVU > 127) pChn->nRightVU = (BYTE)vur; + vur >>= 1; + if (pChn->nRightVU < vur) pChn->nRightVU = (BYTE)vur; +#endif +#ifdef MODPLUG_TRACKER + UINT kChnMasterVol = (pChn->dwFlags & CHN_EXTRALOUD) ? 0x100 : nMasterVol; +#else +#define kChnMasterVol nMasterVol +#endif // MODPLUG_TRACKER + // Adjusting volumes + if (gnChannels >= 2) + { + int pan = ((int)pChn->nRealPan) - 128; + pan *= (int)m_nStereoSeparation; + pan /= 128; + pan += 128; + + if (pan < 0) pan = 0; + if (pan > 256) pan = 256; +#ifndef MODPLUG_FASTSOUNDLIB + if (gdwSoundSetup & SNDMIX_REVERSESTEREO) pan = 256 - pan; +#endif + LONG realvol = (pChn->nRealVolume * kChnMasterVol) >> (8-1); + if (gdwSoundSetup & SNDMIX_SOFTPANNING) + { + if (pan < 128) + { + pChn->nNewLeftVol = (realvol * pan) >> 8; + pChn->nNewRightVol = (realvol * 128) >> 8; + } else + { + pChn->nNewLeftVol = (realvol * 128) >> 8; + pChn->nNewRightVol = (realvol * (256 - pan)) >> 8; + } + } else + { + pChn->nNewLeftVol = (realvol * pan) >> 8; + pChn->nNewRightVol = (realvol * (256 - pan)) >> 8; + } + } else + { + pChn->nNewRightVol = (pChn->nRealVolume * kChnMasterVol) >> 8; + pChn->nNewLeftVol = pChn->nNewRightVol; + } + // Clipping volumes + if (pChn->nNewRightVol > 0xFFFF) pChn->nNewRightVol = 0xFFFF; + if (pChn->nNewLeftVol > 0xFFFF) pChn->nNewLeftVol = 0xFFFF; + // Check IDO + if (gdwSoundSetup & SNDMIX_NORESAMPLING) + { + pChn->dwFlags |= CHN_NOIDO; + } else + { + pChn->dwFlags &= ~(CHN_NOIDO|CHN_HQSRC); + if( pChn->nInc == 0x10000 ) + { pChn->dwFlags |= CHN_NOIDO; + } + else + { if( ((gdwSoundSetup & SNDMIX_HQRESAMPLER) == 0) && ((gdwSoundSetup & SNDMIX_ULTRAHQSRCMODE) == 0) ) + { if (pChn->nInc >= 0xFF00) pChn->dwFlags |= CHN_NOIDO; + } + } + } + pChn->nNewRightVol >>= MIXING_ATTENUATION; + pChn->nNewLeftVol >>= MIXING_ATTENUATION; + pChn->nRightRamp = pChn->nLeftRamp = 0; + // Dolby Pro-Logic Surround + if ((pChn->dwFlags & CHN_SURROUND) && (gnChannels <= 2)) pChn->nNewLeftVol = - pChn->nNewLeftVol; + // Checking Ping-Pong Loops + if (pChn->dwFlags & CHN_PINGPONGFLAG) pChn->nInc = -pChn->nInc; + // Setting up volume ramp + if ((pChn->dwFlags & CHN_VOLUMERAMP) + && ((pChn->nRightVol != pChn->nNewRightVol) + || (pChn->nLeftVol != pChn->nNewLeftVol))) + { + LONG nRampLength = gnVolumeRampSamples; + LONG nRightDelta = ((pChn->nNewRightVol - pChn->nRightVol) << VOLUMERAMPPRECISION); + LONG nLeftDelta = ((pChn->nNewLeftVol - pChn->nLeftVol) << VOLUMERAMPPRECISION); +#ifndef MODPLUG_FASTSOUNDLIB + if ((gdwSoundSetup & SNDMIX_DIRECTTODISK) + || ((gdwSysInfo & (SYSMIX_ENABLEMMX|SYSMIX_FASTCPU)) + && (gdwSoundSetup & SNDMIX_HQRESAMPLER) && (gnCPUUsage <= 20))) + { + if ((pChn->nRightVol|pChn->nLeftVol) && (pChn->nNewRightVol|pChn->nNewLeftVol) && (!(pChn->dwFlags & CHN_FASTVOLRAMP))) + { + nRampLength = m_nBufferCount; + if (nRampLength > (1 << (VOLUMERAMPPRECISION-1))) nRampLength = (1 << (VOLUMERAMPPRECISION-1)); + if (nRampLength < (LONG)gnVolumeRampSamples) nRampLength = gnVolumeRampSamples; + } + } +#endif + pChn->nRightRamp = nRightDelta / nRampLength; + pChn->nLeftRamp = nLeftDelta / nRampLength; + pChn->nRightVol = pChn->nNewRightVol - ((pChn->nRightRamp * nRampLength) >> VOLUMERAMPPRECISION); + pChn->nLeftVol = pChn->nNewLeftVol - ((pChn->nLeftRamp * nRampLength) >> VOLUMERAMPPRECISION); + if (pChn->nRightRamp|pChn->nLeftRamp) + { + pChn->nRampLength = nRampLength; + } else + { + pChn->dwFlags &= ~CHN_VOLUMERAMP; + pChn->nRightVol = pChn->nNewRightVol; + pChn->nLeftVol = pChn->nNewLeftVol; + } + } else + { + pChn->dwFlags &= ~CHN_VOLUMERAMP; + pChn->nRightVol = pChn->nNewRightVol; + pChn->nLeftVol = pChn->nNewLeftVol; + } + pChn->nRampRightVol = pChn->nRightVol << VOLUMERAMPPRECISION; + pChn->nRampLeftVol = pChn->nLeftVol << VOLUMERAMPPRECISION; + // Adding the channel in the channel list + ChnMix[m_nMixChannels++] = nChn; + if (m_nMixChannels >= MAX_CHANNELS) break; + } else + { +#ifdef ENABLE_STEREOVU + // Note change but no sample + if (pChn->nLeftVU > 128) pChn->nLeftVU = 0; + if (pChn->nRightVU > 128) pChn->nRightVU = 0; +#endif + if (pChn->nVUMeter > 0xFF) pChn->nVUMeter = 0; + pChn->nLeftVol = pChn->nRightVol = 0; + pChn->nLength = 0; + } + } + // Checking Max Mix Channels reached: ordering by volume + if ((m_nMixChannels >= m_nMaxMixChannels) && (!(gdwSoundSetup & SNDMIX_DIRECTTODISK))) + { + for (UINT i=0; i<m_nMixChannels; i++) + { + UINT j=i; + while ((j+1<m_nMixChannels) && (Chn[ChnMix[j]].nRealVolume < Chn[ChnMix[j+1]].nRealVolume)) + { + UINT n = ChnMix[j]; + ChnMix[j] = ChnMix[j+1]; + ChnMix[j+1] = n; + j++; + } + } + } + if (m_dwSongFlags & SONG_GLOBALFADE) + { + if (!m_nGlobalFadeSamples) + { + m_dwSongFlags |= SONG_ENDREACHED; + return FALSE; + } + if (m_nGlobalFadeSamples > m_nBufferCount) + m_nGlobalFadeSamples -= m_nBufferCount; + else + m_nGlobalFadeSamples = 0; + } + return TRUE; +} + + diff --git a/lib/libmodplug/src/tables.h b/lib/libmodplug/src/tables.h new file mode 100755 index 0000000000..ebe5ceadb0 --- /dev/null +++ b/lib/libmodplug/src/tables.h @@ -0,0 +1,376 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> + */ + +#include <libmodplug/stdafx.h> +#include <libmodplug/sndfile.h> + +#ifndef MODPLUG_FASTSOUNDLIB +//#pragma data_seg(".tables") +#endif + +static const BYTE ImpulseTrackerPortaVolCmd[16] = +{ + 0x00, 0x01, 0x04, 0x08, 0x10, 0x20, 0x40, 0x60, + 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +// Period table for Protracker octaves 0-5: +static const WORD ProTrackerPeriodTable[6*12] = +{ + 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907, + 856,808,762,720,678,640,604,570,538,508,480,453, + 428,404,381,360,339,320,302,285,269,254,240,226, + 214,202,190,180,170,160,151,143,135,127,120,113, + 107,101,95,90,85,80,75,71,67,63,60,56, + 53,50,47,45,42,40,37,35,33,31,30,28 +}; + + +static const WORD ProTrackerTunedPeriods[16*12] = +{ + 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907, + 1700,1604,1514,1430,1348,1274,1202,1134,1070,1010,954,900, + 1688,1592,1504,1418,1340,1264,1194,1126,1064,1004,948,894, + 1676,1582,1492,1408,1330,1256,1184,1118,1056,996,940,888, + 1664,1570,1482,1398,1320,1246,1176,1110,1048,990,934,882, + 1652,1558,1472,1388,1310,1238,1168,1102,1040,982,926,874, + 1640,1548,1460,1378,1302,1228,1160,1094,1032,974,920,868, + 1628,1536,1450,1368,1292,1220,1150,1086,1026,968,914,862, + 1814,1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960, + 1800,1700,1604,1514,1430,1350,1272,1202,1134,1070,1010,954, + 1788,1688,1592,1504,1418,1340,1264,1194,1126,1064,1004,948, + 1774,1676,1582,1492,1408,1330,1256,1184,1118,1056,996,940, + 1762,1664,1570,1482,1398,1320,1246,1176,1110,1048,988,934, + 1750,1652,1558,1472,1388,1310,1238,1168,1102,1040,982,926, + 1736,1640,1548,1460,1378,1302,1228,1160,1094,1032,974,920, + 1724,1628,1536,1450,1368,1292,1220,1150,1086,1026,968,914 +}; + + +// S3M C-4 periods +static const WORD FreqS3MTable[16] = +{ + 1712,1616,1524,1440,1356,1280, + 1208,1140,1076,1016,960,907, + 0,0,0,0 +}; + + +// S3M FineTune frequencies +static const WORD S3MFineTuneTable[16] = +{ + 7895,7941,7985,8046,8107,8169,8232,8280, + 8363,8413,8463,8529,8581,8651,8723,8757, // 8363*2^((i-8)/(12*8)) +}; + + +// Sinus table +static const int16_t ModSinusTable[64] = +{ + 0,12,25,37,49,60,71,81,90,98,106,112,117,122,125,126, + 127,126,125,122,117,112,106,98,90,81,71,60,49,37,25,12, + 0,-12,-25,-37,-49,-60,-71,-81,-90,-98,-106,-112,-117,-122,-125,-126, + -127,-126,-125,-122,-117,-112,-106,-98,-90,-81,-71,-60,-49,-37,-25,-12 +}; + +// Triangle wave table (ramp down) +static const int16_t ModRampDownTable[64] = +{ + 0,-4,-8,-12,-16,-20,-24,-28,-32,-36,-40,-44,-48,-52,-56,-60, + -64,-68,-72,-76,-80,-84,-88,-92,-96,-100,-104,-108,-112,-116,-120,-124, + 127,123,119,115,111,107,103,99,95,91,87,83,79,75,71,67, + 63,59,55,51,47,43,39,35,31,27,23,19,15,11,7,3 +}; + +// Square wave table +static const int16_t ModSquareTable[64] = +{ + 127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127, + 127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127, + -127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127, + -127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127,-127 +}; + +// Random wave table +static const int16_t ModRandomTable[64] = +{ + 98,-127,-43,88,102,41,-65,-94,125,20,-71,-86,-70,-32,-16,-96, + 17,72,107,-5,116,-69,-62,-40,10,-61,65,109,-18,-38,-13,-76, + -23,88,21,-94,8,106,21,-112,6,109,20,-88,-30,9,-127,118, + 42,-34,89,-4,-51,-72,21,-29,112,123,84,-101,-92,98,-54,-95 +}; + + +// volume fade tables for Retrig Note: +static const int8_t retrigTable1[16] = +{ 0, 0, 0, 0, 0, 0, 10, 8, 0, 0, 0, 0, 0, 0, 24, 32 }; + +static const int8_t retrigTable2[16] = +{ 0, -1, -2, -4, -8, -16, 0, 0, 0, 1, 2, 4, 8, 16, 0, 0 }; + + + +static const WORD XMPeriodTable[104] = +{ + 907,900,894,887,881,875,868,862,856,850,844,838,832,826,820,814, + 808,802,796,791,785,779,774,768,762,757,752,746,741,736,730,725, + 720,715,709,704,699,694,689,684,678,675,670,665,660,655,651,646, + 640,636,632,628,623,619,614,610,604,601,597,592,588,584,580,575, + 570,567,563,559,555,551,547,543,538,535,532,528,524,520,516,513, + 508,505,502,498,494,491,487,484,480,477,474,470,467,463,460,457, + 453,450,447,443,440,437,434,431 +}; + + +static const uint32_t XMLinearTable[768] = +{ + 535232,534749,534266,533784,533303,532822,532341,531861, + 531381,530902,530423,529944,529466,528988,528511,528034, + 527558,527082,526607,526131,525657,525183,524709,524236, + 523763,523290,522818,522346,521875,521404,520934,520464, + 519994,519525,519057,518588,518121,517653,517186,516720, + 516253,515788,515322,514858,514393,513929,513465,513002, + 512539,512077,511615,511154,510692,510232,509771,509312, + 508852,508393,507934,507476,507018,506561,506104,505647, + 505191,504735,504280,503825,503371,502917,502463,502010, + 501557,501104,500652,500201,499749,499298,498848,498398, + 497948,497499,497050,496602,496154,495706,495259,494812, + 494366,493920,493474,493029,492585,492140,491696,491253, + 490809,490367,489924,489482,489041,488600,488159,487718, + 487278,486839,486400,485961,485522,485084,484647,484210, + 483773,483336,482900,482465,482029,481595,481160,480726, + 480292,479859,479426,478994,478562,478130,477699,477268, + 476837,476407,475977,475548,475119,474690,474262,473834, + 473407,472979,472553,472126,471701,471275,470850,470425, + 470001,469577,469153,468730,468307,467884,467462,467041, + 466619,466198,465778,465358,464938,464518,464099,463681, + 463262,462844,462427,462010,461593,461177,460760,460345, + 459930,459515,459100,458686,458272,457859,457446,457033, + 456621,456209,455797,455386,454975,454565,454155,453745, + 453336,452927,452518,452110,451702,451294,450887,450481, + 450074,449668,449262,448857,448452,448048,447644,447240, + 446836,446433,446030,445628,445226,444824,444423,444022, + 443622,443221,442821,442422,442023,441624,441226,440828, + 440430,440033,439636,439239,438843,438447,438051,437656, + 437261,436867,436473,436079,435686,435293,434900,434508, + 434116,433724,433333,432942,432551,432161,431771,431382, + 430992,430604,430215,429827,429439,429052,428665,428278, + 427892,427506,427120,426735,426350,425965,425581,425197, + 424813,424430,424047,423665,423283,422901,422519,422138, + 421757,421377,420997,420617,420237,419858,419479,419101, + 418723,418345,417968,417591,417214,416838,416462,416086, + 415711,415336,414961,414586,414212,413839,413465,413092, + 412720,412347,411975,411604,411232,410862,410491,410121, + 409751,409381,409012,408643,408274,407906,407538,407170, + 406803,406436,406069,405703,405337,404971,404606,404241, + 403876,403512,403148,402784,402421,402058,401695,401333, + 400970,400609,400247,399886,399525,399165,398805,398445, + 398086,397727,397368,397009,396651,396293,395936,395579, + 395222,394865,394509,394153,393798,393442,393087,392733, + 392378,392024,391671,391317,390964,390612,390259,389907, + 389556,389204,388853,388502,388152,387802,387452,387102, + 386753,386404,386056,385707,385359,385012,384664,384317, + 383971,383624,383278,382932,382587,382242,381897,381552, + 381208,380864,380521,380177,379834,379492,379149,378807, + + 378466,378124,377783,377442,377102,376762,376422,376082, + 375743,375404,375065,374727,374389,374051,373714,373377, + 373040,372703,372367,372031,371695,371360,371025,370690, + 370356,370022,369688,369355,369021,368688,368356,368023, + 367691,367360,367028,366697,366366,366036,365706,365376, + 365046,364717,364388,364059,363731,363403,363075,362747, + 362420,362093,361766,361440,361114,360788,360463,360137, + 359813,359488,359164,358840,358516,358193,357869,357547, + 357224,356902,356580,356258,355937,355616,355295,354974, + 354654,354334,354014,353695,353376,353057,352739,352420, + 352103,351785,351468,351150,350834,350517,350201,349885, + 349569,349254,348939,348624,348310,347995,347682,347368, + 347055,346741,346429,346116,345804,345492,345180,344869, + 344558,344247,343936,343626,343316,343006,342697,342388, + 342079,341770,341462,341154,340846,340539,340231,339924, + 339618,339311,339005,338700,338394,338089,337784,337479, + 337175,336870,336566,336263,335959,335656,335354,335051, + 334749,334447,334145,333844,333542,333242,332941,332641, + 332341,332041,331741,331442,331143,330844,330546,330247, + 329950,329652,329355,329057,328761,328464,328168,327872, + 327576,327280,326985,326690,326395,326101,325807,325513, + 325219,324926,324633,324340,324047,323755,323463,323171, + 322879,322588,322297,322006,321716,321426,321136,320846, + 320557,320267,319978,319690,319401,319113,318825,318538, + 318250,317963,317676,317390,317103,316817,316532,316246, + 315961,315676,315391,315106,314822,314538,314254,313971, + 313688,313405,313122,312839,312557,312275,311994,311712, + 311431,311150,310869,310589,310309,310029,309749,309470, + 309190,308911,308633,308354,308076,307798,307521,307243, + 306966,306689,306412,306136,305860,305584,305308,305033, + 304758,304483,304208,303934,303659,303385,303112,302838, + 302565,302292,302019,301747,301475,301203,300931,300660, + 300388,300117,299847,299576,299306,299036,298766,298497, + 298227,297958,297689,297421,297153,296884,296617,296349, + 296082,295815,295548,295281,295015,294749,294483,294217, + 293952,293686,293421,293157,292892,292628,292364,292100, + 291837,291574,291311,291048,290785,290523,290261,289999, + 289737,289476,289215,288954,288693,288433,288173,287913, + 287653,287393,287134,286875,286616,286358,286099,285841, + 285583,285326,285068,284811,284554,284298,284041,283785, + 283529,283273,283017,282762,282507,282252,281998,281743, + 281489,281235,280981,280728,280475,280222,279969,279716, + 279464,279212,278960,278708,278457,278206,277955,277704, + 277453,277203,276953,276703,276453,276204,275955,275706, + 275457,275209,274960,274712,274465,274217,273970,273722, + 273476,273229,272982,272736,272490,272244,271999,271753, + 271508,271263,271018,270774,270530,270286,270042,269798, + 269555,269312,269069,268826,268583,268341,268099,267857 +}; + + +static const int8_t ft2VibratoTable[256] = +{ + 0,-2,-3,-5,-6,-8,-9,-11,-12,-14,-16,-17,-19,-20,-22,-23, + -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42, + -43,-44,-45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56, + -56,-57,-58,-59,-59,-60,-60,-61,-61,-62,-62,-62,-63,-63, + -63,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-63,-63, + -63,-62,-62,-62,-61,-61,-60,-60,-59,-59,-58,-57,-56,-56, + -55,-54,-53,-52,-51,-50,-49,-48,-47,-46,-45,-44,-43,-42, + -41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26,-24,-23, + -22,-20,-19,-17,-16,-14,-12,-11,-9,-8,-6,-5,-3,-2,0, + 2,3,5,6,8,9,11,12,14,16,17,19,20,22,23,24,26,27,29,30, + 32,33,34,36,37,38,39,41,42,43,44,45,46,47,48,49,50,51, + 52,53,54,55,56,56,57,58,59,59,60,60,61,61,62,62,62,63, + 63,63,64,64,64,64,64,64,64,64,64,64,64,63,63,63,62,62, + 62,61,61,60,60,59,59,58,57,56,56,55,54,53,52,51,50,49, + 48,47,46,45,44,43,42,41,39,38,37,36,34,33,32,30,29,27, + 26,24,23,22,20,19,17,16,14,12,11,9,8,6,5,3,2 +}; + + + +static const DWORD FineLinearSlideUpTable[16] = +{ + 65536, 65595, 65654, 65714, 65773, 65832, 65892, 65951, + 66011, 66071, 66130, 66190, 66250, 66309, 66369, 66429 +}; + + +static const DWORD FineLinearSlideDownTable[16] = +{ + 65535, 65477, 65418, 65359, 65300, 65241, 65182, 65123, + 65065, 65006, 64947, 64888, 64830, 64772, 64713, 64645 +}; + + +static const DWORD LinearSlideUpTable[256] = +{ + 65536, 65773, 66010, 66249, 66489, 66729, 66971, 67213, + 67456, 67700, 67945, 68190, 68437, 68685, 68933, 69182, + 69432, 69684, 69936, 70189, 70442, 70697, 70953, 71209, + 71467, 71725, 71985, 72245, 72507, 72769, 73032, 73296, + 73561, 73827, 74094, 74362, 74631, 74901, 75172, 75444, + 75717, 75991, 76265, 76541, 76818, 77096, 77375, 77655, + 77935, 78217, 78500, 78784, 79069, 79355, 79642, 79930, + 80219, 80509, 80800, 81093, 81386, 81680, 81976, 82272, + 82570, 82868, 83168, 83469, 83771, 84074, 84378, 84683, + 84989, 85297, 85605, 85915, 86225, 86537, 86850, 87164, + 87480, 87796, 88113, 88432, 88752, 89073, 89395, 89718, + 90043, 90369, 90695, 91023, 91353, 91683, 92015, 92347, + 92681, 93017, 93353, 93691, 94029, 94370, 94711, 95053, + 95397, 95742, 96088, 96436, 96785, 97135, 97486, 97839, + 98193, 98548, 98904, 99262, 99621, 99981, 100343, 100706, + 101070, 101435, 101802, 102170, 102540, 102911, 103283, 103657, + 104031, 104408, 104785, 105164, 105545, 105926, 106309, 106694, + 107080, 107467, 107856, 108246, 108637, 109030, 109425, 109820, + 110217, 110616, 111016, 111418, 111821, 112225, 112631, 113038, + 113447, 113857, 114269, 114682, 115097, 115514, 115931, 116351, + 116771, 117194, 117618, 118043, 118470, 118898, 119328, 119760, + 120193, 120628, 121064, 121502, 121941, 122382, 122825, 123269, + 123715, 124162, 124611, 125062, 125514, 125968, 126424, 126881, + 127340, 127801, 128263, 128727, 129192, 129660, 130129, 130599, + 131072, 131546, 132021, 132499, 132978, 133459, 133942, 134426, + 134912, 135400, 135890, 136381, 136875, 137370, 137866, 138365, + 138865, 139368, 139872, 140378, 140885, 141395, 141906, 142419, + 142935, 143451, 143970, 144491, 145014, 145538, 146064, 146593, + 147123, 147655, 148189, 148725, 149263, 149803, 150344, 150888, + 151434, 151982, 152531, 153083, 153637, 154192, 154750, 155310, + 155871, 156435, 157001, 157569, 158138, 158710, 159284, 159860, + 160439, 161019, 161601, 162186, 162772, 163361, 163952, 164545, +}; + + +static const DWORD LinearSlideDownTable[256] = +{ + 65536, 65299, 65064, 64830, 64596, 64363, 64131, 63900, + 63670, 63440, 63212, 62984, 62757, 62531, 62305, 62081, + 61857, 61634, 61412, 61191, 60970, 60751, 60532, 60314, + 60096, 59880, 59664, 59449, 59235, 59021, 58809, 58597, + 58385, 58175, 57965, 57757, 57548, 57341, 57134, 56928, + 56723, 56519, 56315, 56112, 55910, 55709, 55508, 55308, + 55108, 54910, 54712, 54515, 54318, 54123, 53928, 53733, + + 53540, 53347, 53154, 52963, 52772, 52582, 52392, 52204, + 52015, 51828, 51641, 51455, 51270, 51085, 50901, 50717, + 50535, 50353, 50171, 49990, 49810, 49631, 49452, 49274, + 49096, 48919, 48743, 48567, 48392, 48218, 48044, 47871, + 47698, 47526, 47355, 47185, 47014, 46845, 46676, 46508, + 46340, 46173, 46007, 45841, 45676, 45511, 45347, 45184, + 45021, 44859, 44697, 44536, 44376, 44216, 44056, 43898, + 43740, 43582, 43425, 43268, 43112, 42957, 42802, 42648, + 42494, 42341, 42189, 42037, 41885, 41734, 41584, 41434, + 41285, 41136, 40988, 40840, 40693, 40546, 40400, 40254, + 40109, 39965, 39821, 39677, 39534, 39392, 39250, 39108, + 38967, 38827, 38687, 38548, 38409, 38270, 38132, 37995, + 37858, 37722, 37586, 37450, 37315, 37181, 37047, 36913, + 36780, 36648, 36516, 36384, 36253, 36122, 35992, 35862, + 35733, 35604, 35476, 35348, 35221, 35094, 34968, 34842, + 34716, 34591, 34466, 34342, 34218, 34095, 33972, 33850, + 33728, 33606, 33485, 33364, 33244, 33124, 33005, 32886, + 32768, 32649, 32532, 32415, 32298, 32181, 32065, 31950, + 31835, 31720, 31606, 31492, 31378, 31265, 31152, 31040, + 30928, 30817, 30706, 30595, 30485, 30375, 30266, 30157, + 30048, 29940, 29832, 29724, 29617, 29510, 29404, 29298, + 29192, 29087, 28982, 28878, 28774, 28670, 28567, 28464, + 28361, 28259, 28157, 28056, 27955, 27854, 27754, 27654, + 27554, 27455, 27356, 27257, 27159, 27061, 26964, 26866, + 26770, 26673, 26577, 26481, 26386, 26291, 26196, 26102, +}; + + +static const int SpectrumSinusTable[256*2] = +{ + 0, 1, 1, 2, 3, 3, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11, + 12, 13, 14, 14, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22, 22, 23, + 24, 25, 25, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 34, + 35, 36, 36, 37, 38, 38, 39, 39, 40, 41, 41, 42, 42, 43, 44, 44, + 45, 45, 46, 46, 47, 47, 48, 48, 49, 49, 50, 50, 51, 51, 52, 52, + 53, 53, 53, 54, 54, 55, 55, 55, 56, 56, 57, 57, 57, 58, 58, 58, + 59, 59, 59, 59, 60, 60, 60, 60, 61, 61, 61, 61, 61, 62, 62, 62, + 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 62, 62, + 62, 62, 62, 62, 61, 61, 61, 61, 61, 60, 60, 60, 60, 59, 59, 59, + 59, 58, 58, 58, 57, 57, 57, 56, 56, 55, 55, 55, 54, 54, 53, 53, + 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, 48, 47, 47, 46, 46, 45, + 45, 44, 44, 43, 42, 42, 41, 41, 40, 39, 39, 38, 38, 37, 36, 36, + 35, 34, 34, 33, 32, 32, 31, 30, 30, 29, 28, 28, 27, 26, 25, 25, + 24, 23, 22, 22, 21, 20, 20, 19, 18, 17, 17, 16, 15, 14, 14, 13, + 12, 11, 10, 10, 9, 8, 7, 7, 6, 5, 4, 3, 3, 2, 1, 0, + 0, -1, -1, -2, -3, -3, -4, -5, -6, -7, -7, -8, -9, -10, -10, -11, + -12, -13, -14, -14, -15, -16, -17, -17, -18, -19, -20, -20, -21, -22, -22, -23, + -24, -25, -25, -26, -27, -28, -28, -29, -30, -30, -31, -32, -32, -33, -34, -34, + -35, -36, -36, -37, -38, -38, -39, -39, -40, -41, -41, -42, -42, -43, -44, -44, + -45, -45, -46, -46, -47, -47, -48, -48, -49, -49, -50, -50, -51, -51, -52, -52, + -53, -53, -53, -54, -54, -55, -55, -55, -56, -56, -57, -57, -57, -58, -58, -58, + -59, -59, -59, -59, -60, -60, -60, -60, -61, -61, -61, -61, -61, -62, -62, -62, + -62, -62, -62, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, + -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, -62, -62, + -62, -62, -62, -62, -61, -61, -61, -61, -61, -60, -60, -60, -60, -59, -59, -59, + -59, -58, -58, -58, -57, -57, -57, -56, -56, -55, -55, -55, -54, -54, -53, -53, + -53, -52, -52, -51, -51, -50, -50, -49, -49, -48, -48, -47, -47, -46, -46, -45, + -45, -44, -44, -43, -42, -42, -41, -41, -40, -39, -39, -38, -38, -37, -36, -36, + -35, -34, -34, -33, -32, -32, -31, -30, -30, -29, -28, -28, -27, -26, -25, -25, + -24, -23, -22, -22, -21, -20, -20, -19, -18, -17, -17, -16, -15, -14, -14, -13, + -12, -11, -10, -10, -9, -8, -7, -7, -6, -5, -4, -3, -3, -2, -1, 0, +}; + |